diff options
| author | Mike McQuaid | 2013-02-17 13:32:15 +0000 |
|---|---|---|
| committer | Mike McQuaid | 2013-02-18 10:42:44 +0000 |
| commit | 4a281cd5cf8f722d73a4f50612e8c460f57fddd7 (patch) | |
| tree | 765c472cc951590398a52651b59051af2cdd2b59 /Library/Contributions/cmd | |
| parent | e9d58c410052157487f96a77dd318cbda641806e (diff) | |
| download | brew-4a281cd5cf8f722d73a4f50612e8c460f57fddd7.tar.bz2 | |
Rename Library/Contributions/cmds -> cmd.
Diffstat (limited to 'Library/Contributions/cmd')
21 files changed, 1974 insertions, 0 deletions
diff --git a/Library/Contributions/cmd/brew-aspell-dictionaries b/Library/Contributions/cmd/brew-aspell-dictionaries new file mode 100755 index 000000000..a5cdb1ac3 --- /dev/null +++ b/Library/Contributions/cmd/brew-aspell-dictionaries @@ -0,0 +1,51 @@ +#!/bin/sh +# +# brew-aspell-dictionaries - update aspell formula to include latest dictionaries +# This script fetches the current index for the aspell dictionaries gnu server, +# it parses the html to retrieve the URL to the dictionary archive for each +# available language. +# The script then calculates the sha1 for each dictionary archive and +# generates a brew formula for each language. +# The result can then to be merged into the aspell formula, to update +# the available dictionary formulae. + +dictionaries_url=http://ftpmirror.gnu.org/aspell/dict +dictionaries_mirror=http://ftp.gnu.org/gnu/aspell/dict +tmp_file=`mktemp -t brew_aspell_dictionaries` +brew_formulae_tmp_file=`mktemp -t brew_aspell_dictionaries_formulae` + +echo "Downloading aspell dictionaries Index" +curl -sL ${dictionaries_url}/0index.html \ + | egrep '^(<tr><td><a|</table)' \ + | cut -d\" -f2,4 \ + > $tmp_file + +echo "# BEGIN generated with `basename $0`" > $brew_formulae_tmp_file +langs="" +for dict in `cat $tmp_file`; do + [ "${dict}" = "</table>" ] && break # only read the entries in the first table, which lists the dictionaries for aspell 0.60 + lang=`echo $dict | awk -F\" '{ gsub("-", "_", $1); print $1 }'` + url="${dictionaries_url}/"`echo $dict | awk -F\" '{ print $2 }'` + mirror="${dictionaries_mirror}/"`echo $dict | awk -F\" '{ print $2 }'` + langs="${langs} ${lang}" + echo "Calculating sha1 for formula: ${lang}" + sha1=`curl -sL "${url}" | shasum | awk '{print $1}'` + cat <<EOF >> $brew_formulae_tmp_file +class Aspell_${lang} < AspellLang + url '${url}' + mirror '${mirror}' + sha1 '${sha1}' +end +EOF +done + +cat <<EOF >> $brew_formulae_tmp_file +def available_languages + %w(${langs}) +end +# END generated with `basename $0` +EOF + +rm $tmp_file + +echo "The formulae for the aspell dictionaries have been written to\n$brew_formulae_tmp_file" diff --git a/Library/Contributions/cmd/brew-beer.rb b/Library/Contributions/cmd/brew-beer.rb new file mode 100755 index 000000000..4ce1f9b9d --- /dev/null +++ b/Library/Contributions/cmd/brew-beer.rb @@ -0,0 +1,179 @@ +HOMEBREW_BEER = <<-EOS +Recipe stolen from: http://allrecipes.com/howto/beer-brewing-for-beginners/ + +**The Key Ingredients** +Before beginning the brewing process, you must first understand the four key +ingredients necessary to brew a batch of beer: water, fermentable sugar, hops, +and yeast. Each ingredient is integral to the recipe and must be cooked in a +certain way to yield a successful batch of brew. Understanding their basic +qualities and how each ingredient is meant to react with the others is an +important aspect of beer brewing. + +Water: Water is the primary ingredient in beer, so it is very important the +water tastes good. If the tap water at your house tastes good to you, then it +is fine to use for beer brewing. If you don't like the way your tap water +tastes, then you can use bottled or distilled water instead. If you use tap +water, boil it first to evaporate the chlorine and other chemicals that may +interfere with the brewing process. Let the water cool before using. + +Fermented Sugar: Malted barley is the ingredient commonly used to fill the +sugar quota in a home brew recipe. Some brewers will substitute a percentage +of corn, rice, wheat, or other grains to add a lighter flavor to the beer. +Beginning brewers should purchase a ready-to-use form of malted barley called +malt syrup or malt extract, rather than attempting to malt the grain from +scratch, as it is a very complex and touchy process. Using a malt extract will +guarantee the fermented sugar is prepared in just the right manner and will +act as it needs to throughout the beer brewing process. + +Hops: Hops are cone-like flowers found on a hop vine. They lend the bitter +flavor to beer that balances out sweetness. Hops also inhibit spoilage and +help keep the "head" (the frothy top when a beer is poured) around longer. + +Yeast: First things first: Do not use bread yeast for beer brewing! Beer yeast +is cultivated especially for use in brewing. There are two broad categories of +beer yeast: ale and lager. Ale yeasts are top-fermenting, which means they +tend to hang out at the top of the carboy while fermenting and rest at the +bottom after the majority of fermenting has occurred. Ale yeasts will not +actively ferment below 50 degrees F (20 degrees C). Lager yeasts are +bottom-fermenters and are best used at a temperature ranging from 55 degrees F +(25 degrees C) down to 32 degrees F (0 degrees C). As their names suggest, the +type of yeast used plays an important part in influencing the type of beer +that will be made. Do not rely on the yeast to define the beer, however, as +all of the ingredients play a part in the taste and type of beer you will +create. + +**Ready to Brew?** +We've opted to use a simple ale recipe to guide you through the process. The +first cooking step in brewing is to make the wort, a soupy mixture of malt and +sugar that is boiled before fermentation. Malt and sugar form the perfect food +for yeast to grown in--thus making the all-important process of fermentation +possible. All of the ingredients for beer-making can be found at your local +brew supply store, or at any number of beer outfitters. Once you've got all +the necessary equipment and ingredients, you're ready to begin the beer-making +process by properly sanitizing your equipment, making and cooling the wort, +fermenting the wort, and bottling your brew. + +Ingredients: + +1.5 gallons water +6 pounds canned pre-hopped light malt syrup +1 ounce hop pellets (choose your flavor) +Ice poured into a water bath (do not use store-bought ice) +3 gallons cool water +2 (7-gram) packets ale yeast +1 cup warm water (about 90 degrees F or 35 degrees C) +3/4 cup liquid corn syrup (or 4 ounces dry corn syrup) +1 (4-ounce) container iodine solution +1 tablespoon bleach + +A bottle of household bleach or an iodine solution that can be bought at your +local home brew shop to sanitize all of your materials or use will be +necessary. (Make a bleach disinfecting solution with 1 tablespoon bleach to 1 +gallon water.) Be sure to rinse the equipment well with boiling water before +using it. + +Part I: Make and Cool the Wort + +Sanitize the pot, stirring spoon and fermenter with the sanitizing solution. +Rinse everything in boiling water. + +Bring 1.5 gallons of water to a boil. When the water begins to boil, remove it +from the heat and stir in the malt syrup until it dissolves. Do not allow any +syrup to stick to the bottom or sides of the pot, as it will burn and taste +awful. Return the pot to the heat and bring the mixture to a boil for 50 +minutes, stir frequently and watch constantly to prevent boil-overs. If the +mixture threatens to boil over, reduce the heat. + +After 50 minutes have elapsed, stir in the hop pellets. Hops will create a +foam on the top of the liquid--so if the pot is very full, the hops may cause +a boil-over. You want to avoid this at all costs by lowering the heat or +spraying the foam down with a water bottle (sanitized, of course). Let the +hops cook for 10 to 20 minutes. + +While the wort is being made, prep the yeast by placing 1 packet of yeast in 1 +cup of warm water (90 degrees F or 35 degrees C; stir and cover for 10 +minutes. If the yeast does not react (form foam), discard the yeast solution +and try again with the second yeast packet. + +At about the time hops are added to the wort, you should prepare an ice-cold +water bath in either a large sink or tub to quick-cool the wort. Once the wort +is finished cooking, float the pot in the water bath. Stir the wort while it +is sitting in the bath so that the maximum amount of wort reaches the pot's +sides where it can cool quickly. If the water bath heats up, add more ice to +keep the water bath cold. It should take approximately 20 minutes to cool the +wort to approximately 80 degrees F (27 degrees C). + + +Part II: Ferment + +Pour the 3 gallons cool water into your sanitized carboy. Funnel in the warm +wort. Sprinkle the prepared yeast into the carboy. Cover the carboy's mouth +with plastic wrap and cap it with a lid. Holding your hand tight over the lid, +shake the bottle up and down to distribute the yeast. Remove the plastic wrap, +wipe any wort around the carboy's mouth off and place the fermentation lock +(with a little water added into its top) on. + +Store the carboy in a cool (60 to 75 degrees F or 15 to 24 degrees C) safe +place without direct sunlight where you will be able to easily clean up or +drain any foam that escapes. A bathtub is an excellent place to store your +fermenter if there are no windows in the room. If the temperature in the +storage room drops and bubbling in the carboy's airlock stops, move the carboy +to a warmer room. The fermenting will resume. Fermentation should begin within +24 hours. A clear sign of fermentation is the production of foam and air +bubbles in the fermentation lock. + +When fermentation begins, it produces a slow trickle of bubbles that will +increase in amount for a few days, and then reduce to a slow trickle again. +Let the beer ferment for approximately 14 days when the primary fermentation +has taken place. If the fermenting process pops the fermentation lock out of +the carboy, re-sanitize it and place it back into the carboy. + + +Part III: Bottle + +Sanitize all of your bottles by soaking them in the sanitizing solution (make +sure to hold them under the solution so the water gets inside of the bottles) +for 1 hour. Rinse the bottles with boiling water. Also sanitize a small +cooking pot, bottling bucket, siphon and racking cane. Follow the instructions +that came with the bottle caps to sanitize them. Let everything air dry. +Combine the corn syrup and 1 cup water in the sanitized cooking pot. Let boil +10 minutes. Pour mixture into the bottling bucket. Be careful not to add too +much corn syrup to the bottling bucket, because this will over-carbonate the +beer and cause bottles to explode! Place the fermenter full of beer on the +kitchen counter and the bottling bucket on the ground below it. Attach the +racking cane to the siphon. Prepare the siphon by filling it with tap water. +Pinch both ends of the siphon to prevent the water from running out. Place one +end of the racking cane and siphon into the iodine solution and one end into +an empty jar. When the solution has run into the siphon and expelled all of +the water into the jar, pinch both ends and let the iodine sit in the siphon +for 5 minutes to re-sanitize the siphon. (Resist the temptation to blow into +the siphon with your mouth to encourage the flow of iodine solution.) + +Place one end of the sanitized siphon into the fermenter and the other end +into the jar; once the beer has begun flowing through the siphon, transfer its +end to the bottling bucket. Monitor the speed that the beer transfers into the +bottling bucket by pinching and releasing the siphon with your fingers (or use +a specialty clamp). The beer should not splash into the bucket; it should +gently rush into it. Once all of the beer has been siphoned into the bucket, +cover it (with a sanitized cover ) and wait 30 minutes for the sediment to +settle at the bottom of the bucket. + +Place the bottling bucket on the counter, attach the siphon and run the other +end of the siphon into a bottle. Fill each bottle with beer to 3/4 inch from +the top of the bottle. Cap each bottle with the bottle-capper. Check and +double-check that the caps are secure. Sure Signs of Infection: + +Keep your eyes peeled for strands of slime in the beer and a milky layer at +the top and/or residue bumps clinging to the air space in the bottleneck. If +the beer has strands, it most likely has a lacto infection and should be +discarded. The milky layer is a sign of a micro-derm infection; this beer +should also be discarded. + +Age the bottles at room temperature for up to two months, but for at least two +weeks, before cracking one open, proposing a toast to yourself and impressing +your friends! Ready to expand your brewing prowess? + +Thanks for brewin' +EOS + +puts HOMEBREW_BEER diff --git a/Library/Contributions/cmd/brew-dirty.rb b/Library/Contributions/cmd/brew-dirty.rb new file mode 100755 index 000000000..e22ad75c6 --- /dev/null +++ b/Library/Contributions/cmd/brew-dirty.rb @@ -0,0 +1,7 @@ +# See: http://github.com/mxcl/homebrew/issues/issue/1359 + +to_list = HOMEBREW_CELLAR.children.select { |pn| pn.directory? } +to_list.each do |d| + versions = d.children.select { |pn| pn.directory? }.collect { |pn| pn.basename.to_s } + puts "#{d.basename} (#{versions.join(', ')})" if versions.size > 1 +end diff --git a/Library/Contributions/cmd/brew-graph b/Library/Contributions/cmd/brew-graph new file mode 100755 index 000000000..27c80af83 --- /dev/null +++ b/Library/Contributions/cmd/brew-graph @@ -0,0 +1,311 @@ +#!/usr/bin/env python +""" +$ brew install graphviz +$ brew graph | dot -Tsvg -ohomebrew.svg +$ open homebrew.svg +""" +from __future__ import with_statement + +from contextlib import contextmanager +import re +from subprocess import Popen, PIPE +import sys + + +def run(command, print_command=False): + "Run a command, returning the exit code and output." + if print_command: print command + p = Popen(command, stdout=PIPE) + output, errput = p.communicate() + return p.returncode, output + + +def _quote_id(id): + return '"' + id.replace('"', '\"') + '"' + + +def format_attribs(attrib): + if len(attrib) == 0: + return '' + + values = ['%s="%s"' % (k, attrib[k]) for k in attrib] + return '[' + ','.join(values) + ']' + + +class Output(object): + def __init__(self, fd=sys.stdout, tabstyle=" "): + self.fd = fd + self.tabstyle = tabstyle + self.tablevel = 0 + + def close(self): + self.fd = None + + def out(self, s): + self.tabout() + self.fd.write(s) + + def outln(self, s=None): + if s is not None: + self.tabout() + self.fd.write(s) + self.fd.write('\n') + + @contextmanager + def indented(self): + self.indent() + yield self + self.dedent() + + def indent(self): + self.tablevel += 1 + + def dedent(self): + if self.tablevel == 0: + raise Exception('No existing indent level.') + self.tablevel -= 1 + + def tabout(self): + if self.tablevel: + self.fd.write(self.tabstyle * self.tablevel) + + +class NodeContainer(object): + def __init__(self): + self.nodes = list() + self.node_defaults = dict() + # Stack of node attribs + self._node_styles = list() + + def _node_style(self): + if (len(self._node_styles) > 0): + return self._node_styles[-1] + else: + return dict() + + def _push_node_style(self, attrib): + self._node_styles.append(attrib) + + def _pop_node_style(self): + return self._node_styles.pop() + + @contextmanager + def node_styles(self, attrib): + self._push_node_style(attrib) + yield + self._pop_node_style() + + def node(self, nodeid, label, attrib=None): + _attrib = dict(self._node_style()) + if attrib is not None: + _attrib.update(attrib) + + n = Node(nodeid, label, _attrib) + self.nodes.append(n) + return n + + def nodes_to_dot(self, out): + if len(self.node_defaults) > 0: + out.outln("node " + format_attribs(self.node_defaults) + ";") + + if len(self.nodes) == 0: + return + + id_width = max([len(_quote_id(n.id)) for n in self.nodes]) + for node in self.nodes: + node.to_dot(out, id_width) + + +class Node(object): + def __init__(self, nodeid, label, attrib=None): + self.id = nodeid + self.label = label + self.attrib = attrib if attrib is not None else dict() + + def as_dot(self, id_width=1): + _attribs = dict(self.attrib) + _attribs['label'] = self.label + + return '%-*s %s' % (id_width, _quote_id(self.id), format_attribs(_attribs)) + + + def to_dot(self, out, id_width=1): + out.outln(self.as_dot(id_width)) + + +class ClusterContainer(object): + def __init__(self): + self.clusters = list() + + def cluster(self, clusterid, label, attrib=None): + c = Cluster(clusterid, label, self, attrib) + self.clusters.append(c) + return c + + +class Cluster(NodeContainer, ClusterContainer): + def __init__(self, clusterid, label, parentcluster=None, attrib=None): + NodeContainer.__init__(self) + ClusterContainer.__init__(self) + + self.id = clusterid + self.label = label + self.attrib = attrib if attrib is not None else dict() + self.parentcluster = parentcluster + + def cluster_id(self): + return _quote_id("cluster_" + self.id) + + def to_dot(self, out): + out.outln("subgraph %s {" % self.cluster_id()) + with out.indented(): + out.outln('label = "%s"' % self.label) + for k in self.attrib: + out.outln('%s = "%s"' % (k, self.attrib[k])) + + for cluster in self.clusters: + cluster.to_dot(out) + + self.nodes_to_dot(out) + out.outln("}") + + +class Edge(object): + def __init__(self, source, target, attrib=None): + if attrib is None: + attrib = dict() + + self.source = source + self.target = target + self.attrib = attrib + + def to_dot(self, out): + out.outln(self.as_dot()) + + def as_dot(self): + return " ".join((_quote_id(self.source), "->", _quote_id(self.target), format_attribs(self.attrib))) + + +class EdgeContainer(object): + def __init__(self): + self.edges = list() + self.edge_defaults = dict() + # Stack of edge attribs + self._edge_styles = list() + + def _edge_style(self): + if (len(self._edge_styles) > 0): + return self._edge_styles[-1] + else: + return dict() + + def _push_edge_style(self, attrib): + self._edge_styles.append(attrib) + + def _pop_edge_style(self): + return self._edge_styles.pop() + + @contextmanager + def edge_styles(self, attrib): + self._push_edge_style(attrib) + yield + self._pop_edge_style() + + def link(self, source, target, attrib=None): + _attrib = dict(self._edge_style()) + if attrib is not None: + _attrib.update(attrib) + + e = Edge(source, target, _attrib) + self.edges.append(e) + return e + + def edges_to_dot(self, out): + if len(self.edge_defaults) > 0: + out.outln("edge " + format_attribs(self.edge_defaults) + ";") + + if len(self.edges) == 0: + return + + for edge in self.edges: + edge.to_dot(out) + + +class Graph(NodeContainer, EdgeContainer, ClusterContainer): + """ + Contains the nodes, edges, and subgraph definitions for a graph to be + turned into a Graphviz DOT file. + """ + + def __init__(self, label=None, attrib=None): + NodeContainer.__init__(self) + EdgeContainer.__init__(self) + ClusterContainer.__init__(self) + + self.label = label if label is not None else "Default Label" + self.attrib = attrib if attrib is not None else dict() + + def dot(self, fd=sys.stdout): + try: + self.o = Output(fd) + self._dot() + finally: + self.o.close() + + def _dot(self): + self.o.outln("digraph G {") + + with self.o.indented(): + self.o.outln('label = "%s"' % self.label) + for k in self.attrib: + self.o.outln('%s = "%s"' % (k, self.attrib[k])) + + self.nodes_to_dot(self.o) + + for cluster in self.clusters: + self.o.outln() + cluster.to_dot(self.o) + + self.o.outln() + self.edges_to_dot(self.o) + + self.o.outln("}") + + +def main(): + cmd = ["brew", "deps"] + cmd.extend(sys.argv[1:] or ["--all"]) + code, output = run(cmd) + output = output.strip() + depgraph = list() + + for f in output.split("\n"): + stuff = f.split(":",2) + name = stuff[0] + deps = stuff[1].strip() + if not deps: + deps = list() + else: + deps = deps.split(" ") + depgraph.append((name, deps)) + + hb = Graph("Homebrew Dependencies", attrib={'labelloc':'b', 'rankdir':'LR', 'ranksep':'5'}) + + used = set() + for f in depgraph: + for d in f[1]: + used.add(f[0]) + used.add(d) + + for f in depgraph: + if f[0] not in used: + continue + n = hb.node(f[0], f[0]) + for d in f[1]: + hb.link(d, f[0]) + + hb.dot() + + +if __name__ == "__main__": + main() diff --git a/Library/Contributions/cmd/brew-grep b/Library/Contributions/cmd/brew-grep new file mode 100755 index 000000000..027f97c4c --- /dev/null +++ b/Library/Contributions/cmd/brew-grep @@ -0,0 +1,3 @@ +#!/bin/bash + +grep $@ $HOMEBREW_REPOSITORY/Library/Formula/* diff --git a/Library/Contributions/cmd/brew-leaves.rb b/Library/Contributions/cmd/brew-leaves.rb new file mode 100755 index 000000000..f94809c9d --- /dev/null +++ b/Library/Contributions/cmd/brew-leaves.rb @@ -0,0 +1,27 @@ +# Outputs formulae that are installed but are not a dependency for +# any other installed formula. +# See: http://github.com/mxcl/homebrew/issues/issue/1438 + +require 'formula' + +def get_used_by + used_by = {} + Formula.each do |f| + next if f.deps == nil + + f.deps.each do |dep| + _deps = used_by[dep.to_s] || [] + _deps << f.name unless _deps.include? f.name + used_by[dep.to_s] = _deps + end + end + + return used_by +end + +deps_graph = get_used_by() +installed = HOMEBREW_CELLAR.children.select { |pn| pn.directory? }.collect { |pn| pn.basename.to_s } +installed.each do |name| + deps = deps_graph[name] || [] + puts name unless deps.any? { |dep| installed.include? dep.to_s } +end diff --git a/Library/Contributions/cmd/brew-linkapps.rb b/Library/Contributions/cmd/brew-linkapps.rb new file mode 100755 index 000000000..92e1f6890 --- /dev/null +++ b/Library/Contributions/cmd/brew-linkapps.rb @@ -0,0 +1,33 @@ +# Links any Applications (.app) found in installed prefixes to ~/Applications +require "formula" + +HOME_APPS = File.expand_path("~/Applications") + +unless File.exist? HOME_APPS + opoo "#{HOME_APPS} does not exist, stopping." + puts "Run `mkdir ~/Applications` first." + exit 1 +end + +HOMEBREW_CELLAR.subdirs.each do |keg| + next unless keg.subdirs + name = keg.basename.to_s + + if ((f = Formula.factory(name)).installed? rescue false) + Dir["#{f.installed_prefix}/*.app", "#{f.installed_prefix}/bin/*.app", "#{f.installed_prefix}/libexec/*.app"].each do |p| + puts "Linking #{p}" + appname = File.basename(p) + target = HOME_APPS+"/"+appname + if File.exist? target + if File.symlink? target + system "rm", target + else + onoe "#{target} already exists, skipping." + end + end + system "ln", "-s", p, HOME_APPS + end + end +end + +puts "Finished linking. Find the links under ~/Applications." diff --git a/Library/Contributions/cmd/brew-ls-taps.rb b/Library/Contributions/cmd/brew-ls-taps.rb new file mode 100755 index 000000000..f6c92ebc0 --- /dev/null +++ b/Library/Contributions/cmd/brew-ls-taps.rb @@ -0,0 +1,18 @@ +require 'open-uri' +require 'vendor/multi_json' + +begin + open "https://api.github.com/legacy/repos/search/homebrew" do |f| + MultiJson.decode(f.read)["repositories"].each do |repo| + if repo['name'] =~ /^homebrew-(\S+)$/ + puts tap = if repo['username'] == "Homebrew" + "homebrew/#{$1}" + else + repo['username']+"/"+$1 + end + end + end + end +rescue + nil +end diff --git a/Library/Contributions/cmd/brew-man b/Library/Contributions/cmd/brew-man new file mode 100755 index 000000000..a646427dc --- /dev/null +++ b/Library/Contributions/cmd/brew-man @@ -0,0 +1,51 @@ +#!/bin/bash + +set -e +shopt -s nullglob + +SOURCE_PATH="$HOMEBREW_REPOSITORY/Library/Contributions/manpages" +TARGET_PATH="$HOMEBREW_REPOSITORY/share/man/man1" +LINKED_PATH="$HOMEBREW_PREFIX/share/man/man1" + + +die (){ + echo $1 + exit 1 +} + +test "$1" = '--link' || \ +test "$1" = '-l' && { + [[ $TARGET_PATH == $LINKED_PATH ]] && exit 0 + + for page in "$TARGET_PATH"/*.1 + do + ln -s $page $LINKED_PATH + done + exit 0 +} + +/usr/bin/which ronn &>/dev/null || die "You need to \"gem install ronn\" and put it in your path." + +test "$1" = '--server' || \ +test "$1" = '-s' && { + echo "Man page test server: http://localhost:1207/" + echo "Control-C to exit." + ronn --server $SOURCE_PATH/* + exit 0 +} + +echo "Writing manpages to $TARGET_PATH" + +for i in "$SOURCE_PATH"/*.md +do + # Get the filename only, without the .md extension + j=`basename $i` + target_file="$TARGET_PATH/${j%\.md}" + + ronn --roff --pipe --organization='Homebrew' --manual='brew' $i > $target_file +done + +if test "$1" = '--verbose' || test "$1" = '-v' +then + man brew +fi diff --git a/Library/Contributions/cmd/brew-mirror-check.rb b/Library/Contributions/cmd/brew-mirror-check.rb new file mode 100755 index 000000000..821a62526 --- /dev/null +++ b/Library/Contributions/cmd/brew-mirror-check.rb @@ -0,0 +1,56 @@ +require 'formula' + +class Formula + def test_mirror mirror + # Checksum verification is done against @active_spec, so we need only + # populate the stub spec object with the mirror URL. + spec = SoftwareSpec.new(mirror) + downloader = download_strategy.new(name, spec) + + # Force the downloader to attempt the download by removing the tarball if + # it is allready cached. + tarball_path = downloader.tarball_path + tarball_path.unlink if tarball_path.exist? + + begin + fetched = downloader.fetch + rescue DownloadError => e + opoo "Failed to fetch from URL: #{url}" + return + end + + verify_download_integrity fetched if fetched.kind_of? Pathname + end +end + +module Homebrew extend self + def check_mirrors + mirror_check_usage = <<-EOS +Usage: brew mirror-check <formulae ...> + +Cycle through mirror lists for each formula, attempt a download and validate file hashes. + EOS + + if ARGV.empty? + puts mirror_check_usage + exit 0 + end + + formulae = ARGV.formulae + raise FormulaUnspecifiedError if formulae.empty? + + formulae.each do |f| + if f.mirrors.empty? + opoo "#{f.name} has no mirrors" + next + else + oh1 "Testing mirrors for #{f.name}" + f.mirrors.each{ |m| f.test_mirror m } + end + end + end +end + +# Here is the actual code that gets run when `brew` loads this external +# command. +Homebrew.check_mirrors diff --git a/Library/Contributions/cmd/brew-pull.rb b/Library/Contributions/cmd/brew-pull.rb new file mode 100755 index 000000000..6a055f77b --- /dev/null +++ b/Library/Contributions/cmd/brew-pull.rb @@ -0,0 +1,89 @@ +# Gets a patch from a GitHub commit or pull request and applies it to Homebrew. +# Optionally, installs it too. + +require 'utils' +require 'formula' + +def tap arg + match = arg.match(%r[homebrew-(\w+)/]) + match[1].downcase if match +end + +if ARGV.empty? + onoe 'This command requires at least one argument containing a URL or pull request number' +end + +if ARGV[0] == '--rebase' + onoe 'You meant `git pull --rebase`.' +end + +ARGV.named.each do|arg| + if arg.to_i > 0 + url = 'https://github.com/mxcl/homebrew/pull/' + arg + else + url_match = arg.match HOMEBREW_PULL_URL_REGEX + unless url_match + ohai 'Ignoring URL:', "Not a GitHub pull request or commit: #{arg}" + next + end + + url = url_match[0] + end + + if tap url + Dir.chdir HOMEBREW_REPOSITORY/"Library/Taps/#{url_match[1].downcase}-#{tap url}" + else + Dir.chdir HOMEBREW_REPOSITORY + end + + # GitHub provides commits'/pull-requests' raw patches using this URL. + url += '.patch' + + # The cache directory seems like a good place to put patches. + HOMEBREW_CACHE.mkpath + patchpath = HOMEBREW_CACHE + File.basename(url) + curl url, '-o', patchpath + + # Store current revision + revision = `git rev-parse --short HEAD`.strip + + ohai 'Applying patch' + patch_args = ['am'] + patch_args << '--signoff' unless ARGV.include? '--clean' + # Normally we don't want whitespace errors, but squashing them can break + # patches so an option is provided to skip this step. + patch_args << '--whitespace=fix' unless ARGV.include? '--ignore-whitespace' or ARGV.include? '--clean' + patch_args << patchpath + + safe_system 'git', *patch_args + + issue = arg.to_i > 0 ? arg.to_i : url_match[4] + if issue and not ARGV.include? '--clean' + ohai "Patch closes issue ##{issue}" + message = `git log HEAD^.. --format=%B` + + # If this is a pull request, append a close message. + unless message.include? 'Closes #' + issueline = "Closes ##{issue}." + signed = 'Signed-off-by:' + message = message.gsub signed, issueline + "\n\n" + signed + safe_system 'git', 'commit', '--amend', '-q', '-m', message + end + end + + ohai 'Patch changed:' + safe_system 'git', '--no-pager', 'diff', "#{revision}..", '--stat' + + if ARGV.include? '--install' + `git diff #{revision}.. --name-status`.each_line do |line| + status, filename = line.split + # Don't try and do anything to removed files. + if (status == 'A' or status == 'M') and filename.include? '/Formula/' or tap url + formula = File.basename(filename, '.rb') + ohai "Installing #{formula}" + install = Formula.factory(formula).installed? ? 'upgrade' : 'install' + safe_system 'brew', install, '--debug', '--fresh', formula + end + end + end +end diff --git a/Library/Contributions/cmd/brew-readall.rb b/Library/Contributions/cmd/brew-readall.rb new file mode 100755 index 000000000..d04de565d --- /dev/null +++ b/Library/Contributions/cmd/brew-readall.rb @@ -0,0 +1,14 @@ +# `brew readall` tries to import all formulae one-by-one. +# This can be useful for debugging issues across all formulae +# when making significant changes to formula.rb, +# or to determine if any current formulae have Ruby issues + +require 'formula' +Formula.names.each do |n| + begin + f = Formula.factory(n) + rescue Exception => e + onoe "problem in #{Formula.path(n)}" + puts e + end +end diff --git a/Library/Contributions/cmd/brew-server b/Library/Contributions/cmd/brew-server new file mode 100755 index 000000000..a87322409 --- /dev/null +++ b/Library/Contributions/cmd/brew-server @@ -0,0 +1,208 @@ +#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby + +## brew server: Run a local webserver for browsing available and installed brews. +# Note: this external command is ruby, but set up as a shell script, so that it gets exec'd. +# This is required for sinatra's run-loop to take over. + +$:.unshift(ENV['HOMEBREW_LIBRARY_PATH']) + +require 'global' +require 'formula' + +require 'rubygems' + +begin + require 'sinatra' +rescue LoadError + onoe 'Sinatra required but not found' + puts 'To install: /usr/bin/gem install sinatra' + exit 1 +end + +require 'cgi' + + +def link_to_formula name + "<a href=\"/formula/#{CGI.escape(name)}\">#{name}</a>" +end + +def css_style + "" # No CSS defined yet. +end + +def search_form + <<-EOS + <form action="/search"> + Search: <input name="q" type="text"> <input type="submit"> + </form> + EOS +end + +def html_page + body = <<-HTML + <html> + <head> + <title>Homebrew Menu</title> + #{css_style} + </head> + <body> + <div id="wrap"> + <div id="header"> + <h1><a href="./">Homebrew</a></h1> + <p id="subtitle"><strong>The missing package manager for OS X</strong></p> + <p id="installed"><a href="/installed">Show installed packages</a></p> + </div> + + <div id="informations"> + HTML + yield body + body += <<-HTML + </div> + </div> + </body> + </html> + HTML + return body +end + +get '/' do + return html_page do |s| + s << <<-HTML + <div class="row">#{search_form}</div> + <div class="row"> + <ul> + HTML + Formula.names do |name| + s << "<li>#{link_to_formula(name)}</li>" + end + s << <<-HTML + </ul> + </div> + HTML + end +end + +get '/search' do + q = params['q'] + results = search_brews(q) + + s = <<-HTML + <html> + <head> + <title>Search Results</title> + #{css_style} + </head> + <body> + <h1>Results</h1> + #{search_form} + <h4>Searched for “#{q}”</h4> + <ul> + HTML + + results.each do |name| + s << "<li>#{link_to_formula(name)}</li>" + end + + s += <<-HTML + </ul> + </body> + </html> + HTML + + return s +end + +get '/formula/:name' do + klass = Formula.factory(params[:name]) + + installed = klass.installed? ? "Installed at" : "Not installed." + installed_dd = klass.installed? ? "<a href=\"file://#{klass.prefix}\">#{klass.prefix}</a>" : "" + + s = "" + s << <<-HTML + <html> + <head> + <title>Formula: #{klass.name}</title> + #{css_style} + </head> + <body> + <div>← <a href="/">Back to menu</a></div> + <h1>#{klass.name}</h1> + <dl> + <dt>Version</dt> + <dd>#{klass.version}</dd> + + <dt>Homepage</dt> + <dd><a href="#{klass.homepage}">#{klass.homepage}</a></dd> + + <dt>Download</dt> + <dd><a href="#{klass.url}">#{klass.url}</a></dd> + + <dt>#{installed}</dt> + <dd>#{installed_dd}</dd> + HTML + + unless klass.deps.count == 0 + s << <<-HTML + <dt>Depends on</td> + HTML + klass.deps.each do |dep| + s << "<dd>#{link_to_formula(dep.name)}</dd>" + end + end + + used_by = Formula.select{|ff| ff.deps.include?(klass.name)}.map{|f| f.name}.flatten.uniq.sort + unless used_by.empty? + s << <<-HTML + <dt>Used by</td> + HTML + if used_by != nil + used_by.each do |name| + s << "<dd>#{link_to_formula(name)}</dd>" + end + end + end + + s += <<-HTML + </dl> + </body> + </html> + HTML + + return s +end + + +def installed_formulae + Formula.select{|formula| formula.installed?} +end + +get '/installed' do + + s = <<-HTML + <html> + <head> + <title>Installed formulae</title> + #{css_style} + </head> + <body> + <h1>Installed Fomulas</h1> + <ul> + HTML + + installed_formulae.each do |formula| + s << "<li>#{link_to_formula(formula.name)}</li>" + end + + s += <<-HTML + </ul> + <div>← <a href="/">Back to menu</a></div> + </body> + </html> + HTML + + return s +end + + +puts "View our tasting menu at http://localhost:4567/\nUse \"Control-C\" to exit.\n\n" diff --git a/Library/Contributions/cmd/brew-services.rb b/Library/Contributions/cmd/brew-services.rb new file mode 100755 index 000000000..4891ae510 --- /dev/null +++ b/Library/Contributions/cmd/brew-services.rb @@ -0,0 +1,369 @@ +#!/usr/bin/env ruby -w + +# brew-services(1) - Easily start and stop formulae via launchctl +# =============================================================== +# +# ## SYNOPSIS +# +# [<sudo>] `brew services` `list`<br> +# [<sudo>] `brew services` `restart` <formula><br> +# [<sudo>] `brew services` `start` <formula> [<plist>]<br> +# [<sudo>] `brew services` `stop` <formula><br> +# [<sudo>] `brew services` `cleanup`<br> +# +# ## DESCRIPTION +# +# Integrates homebrew formulae with MacOS X' `launchctl` manager. Services +# can either be added to `/Library/LaunchDaemons` or `~/Library/LaunchAgents`. +# Basically items added to `/Library/LaunchDaemons` are started at boot, +# those in `~/Library/LaunchAgents` at login. +# +# When started with `sudo` it operates on `/Library/LaunchDaemons`, else +# in the user space. +# +# Basically on `start` the plist file is generated and written to a `Tempfile`, +# then copied to the launch path (existing plists are overwritten). +# +# ## OPTIONS +# +# To access everything quickly, some aliases have been added: +# +# * `rm`: +# Shortcut for `cleanup`, because that's basically whats being done. +# +# * `ls`: +# Because `list` is too much to type :) +# +# * `reload', 'r': +# Alias for `restart`, which gracefully restarts selected service. +# +# * `load`, `s`: +# Alias for `start`, guess what it does... +# +# * `unload`, `term`, `t`: +# Alias for `stop`, stops and unloads selected service. +# +# ## SYNTAX +# +# Several existing formulae (like mysql, nginx) already write custom plist +# files to the formulae prefix. Most of these implement `#startup_plist` +# which then in turn returns a neat-o plist file as string. +# +# `brew services` operates on `#startup_plist` as well and requires +# supporting formulae to implement it. This method should either string +# containing the generated XML file, or return a `Pathname` instance which +# points to a plist template, or a hash like: +# +# { :url => "https://gist.github.com/raw/534777/63c4698872aaef11fe6e6c0c5514f35fd1b1687b/nginx.plist.xml" } +# +# Some simple template parsing is performed, all variables like `{{name}}` are +# replaced by basically doing: +# `formula.send('name').to_s if formula.respond_to?('name')`, a bit like +# mustache. So any variable in the `Formula` is available as template +# variable, like `{{var}}`, `{{bin}}` usw. +# +# ## EXAMPLES +# +# Install and start service mysql at boot: +# +# $ brew install mysql +# $ sudo brew services start mysql +# +# Stop service mysql (when launched at boot): +# +# $ sudo brew services stop mysql +# +# Start memcached at login: +# +# $ brew install memcached +# $ brew services start memcached +# +# List all running services for current user, and root: +# +# $ brew services list +# $ sudo brew services list +# +# ## BUGS +# +# `brew-services.rb` might not handle all edge cases, though it tries +# to fix problems by running `brew services cleanup`. +# +module ServicesCli + class << self + # Binary name. + def bin; "brew services" end + + # Path to launchctl binary. + def launchctl; "/bin/launchctl" end + + # Wohoo, we are root dude! + def root?; Process.uid == 0 end + + # Current user, i.e. owner of `HOMEBREW_CELLAR`. + def user; @user ||= %x{/usr/bin/stat -f '%Su' #{HOMEBREW_CELLAR} 2>/dev/null}.chomp || %x{/usr/bin/whoami}.chomp end + + # Run at boot. + def boot_path; Pathname.new("/Library/LaunchDaemons") end + + # Run at login. + def user_path; Pathname.new(ENV['HOME'] + '/Library/LaunchAgents') end + + # If root returns `boot_path` else `user_path`. + def path; root? ? boot_path : user_path end + + # Find all currently running services via launchctl list + def running; %x{#{launchctl} list | grep com.github.homebrew}.chomp.split("\n").map { |svc| $1 if svc =~ /(com\.github\.homebrew\..+)\z/ }.compact end + + # Check if running as homebre and load required libraries et al. + def homebrew! + abort("Runtime error: homebrew is required, please start via `#{bin} ...`") unless defined?(HOMEBREW_LIBRARY_PATH) + %w{fileutils pathname tempfile formula utils}.each { |req| require(req) } + self.send(:extend, ::FileUtils) + ::Formula.send(:include, Service::PlistSupport) + end + + # Access current service + def service; @service ||= Service.new(Formula.factory(Formula.canonical_name(@formula))) if @formula end + + # Print usage and `exit(...)` with supplied exit code, if code + # is set to `false`, then exit is ignored. + def usage(code = 0) + puts "usage: [sudo] #{bin} [--help] <command> [<formula>]" + puts + puts "Small wrapper around `launchctl` for supported formulae, commands available:" + puts " cleanup Get rid of stale services and unused plists" + puts " list List all services managed by `#{bin}`" + puts " restart Gracefully restart selected service" + puts " start Start selected service" + puts " stop Stop selected service" + puts + puts "Options, sudo and paths:" + puts + puts " sudo When run as root, operates on #{boot_path} (run at boot!)" + puts " Run at boot: #{boot_path}" + puts " Run at login: #{user_path}" + puts + exit(code) unless code == false + true + end + + # Run and start the command loop. + def run! + homebrew! + usage if ARGV.empty? || ARGV.include?('help') || ARGV.include?('--help') || ARGV.include?('-h') + + # parse arguments + @args = ARGV.reject { |arg| arg[0] == 45 }.map { |arg| arg.include?("/") ? arg : arg.downcase } # 45.chr == '-' + @cmd = @args.shift + @formula = @args.shift + + # dispatch commands and aliases + case @cmd + when 'cleanup', 'clean', 'cl', 'rm' then cleanup + when 'list', 'ls' then list + when 'restart', 'relaunch', 'reload', 'r' then check and restart + when 'start', 'launch', 'load', 's', 'l' then check and start + when 'stop', 'unload', 'terminate', 'term', 't', 'u' then check and stop + else + onoe "Unknown command `#{@cmd}`" + usage(1) + end + end + + # Check if formula has been found + def check + odie("Formula missing, please provide a formula name") unless service + true + end + + # List all running services with PID and status and path to plist file, if available + def list + opoo("No %s services controlled by `#{bin}` running..." % [root? ? 'root' : 'user-space']) and return if running.empty? + running.each do |label| + if svc = Service.from(label) + status = !svc.dest.file? ? "#{Tty.red}stale " : "#{Tty.white}started" + puts "%-10.10s %s#{Tty.reset} %7s %s" % [svc.name, status, svc.pid ? svc.pid.to_s : '-', svc.dest.file? ? svc.dest : label] + else + puts "%-10.10s #{Tty.red}unknown#{Tty.reset} %7s #{label}" % ["?", "-"] + end + end + end + + # Kill services without plist file and remove unused plists + def cleanup + cleaned = [] + + # 1. kill services which have no plist file + running.each do |label| + if svc = Service.from(label) + if !svc.dest.file? + puts "%-15.15s #{Tty.white}stale#{Tty.reset} => killing service..." % svc.name + kill(svc) + cleaned << label + end + else + opoo "Service #{label} not managed by `#{bin}` => skipping" + end + end + + # 2. remove unused plist files + Dir[path + 'com.github.homebrew.*.plist'].each do |file| + unless running.include?(File.basename(file).sub(/\.plist$/i, '')) + puts "Removing unused plist #{file}" + rm file + cleaned << file + end + end + + puts "All #{root? ? 'root' : 'user-space'} services OK, nothing cleaned..." if cleaned.empty? + end + + # Stop if loaded, then start again + def restart + stop if service.loaded? + start + end + + # Start a service + def start + odie "Service `#{service.name}` already started, use `#{bin} restart #{service.name}`" if service.loaded? + + custom_plist = @args.first + if custom_plist + if custom_plist =~ %r{\Ahttps?://.+} + custom_plist = { :url => custom_plist } + elsif File.exist?(custom_plist) + custom_plist = Pathname.new(custom_plist) + else + odie "#{custom_plist} is not a url or exising file" + end + end + + odie "Formula `#{service.name}` not installed, #startup_plist not implemented or no plist file found" if !custom_plist && !service.plist? + + temp = Tempfile.new(service.label) + temp << service.generate_plist(custom_plist) + temp.flush + + rm service.dest if service.dest.exist? + cp temp.path, service.dest + + # clear tempfile + temp.close + + safe_system launchctl, "load", "-w", service.dest.to_s + $?.to_i != 0 ? odie("Failed to start `#{service.name}`") : ohai("Successfully started `#{service.name}` (label: #{service.label})") + end + + # Stop a service or kill if no plist file available... + def stop + unless service.loaded? + rm service.dest if service.dest.exist? # get rid of installed plist anyway, dude + odie "Service `#{service.name}` not running, wanna start it? Try `#{bin} start #{service.name}`" + end + + if service.dest.exist? + puts "Stopping `#{service.name}`... (might take a while)" + safe_system launchctl, "unload", "-w", service.dest.to_s + $?.to_i != 0 ? odie("Failed to stop `#{service.name}`") : ohai("Successfully stopped `#{service.name}` (label: #{service.label})") + else + puts "Stopping stale service `#{service.name}`... (might take a while)" + kill(service) + end + rm service.dest if service.dest.exist? + end + + # Kill service without plist file by issuing a `launchctl remove` command + def kill(svc) + safe_system launchctl, "remove", svc.label + odie("Failed to remove `#{svc.name}`, try again?") unless $?.to_i == 0 + while svc.loaded? + puts " ...checking status" + sleep(5) + end + ohai "Successfully stopped `#{svc.name}` via #{svc.label}" + end + end +end + +# Wrapper for a formula to handle service related stuff like parsing +# and generating the plist file. +class Service + + # Support module which will be used to extend Formula with a method :) + module PlistSupport + # As a replacement value for `<key>UserName</key>`. + def startup_user; ServicesCli.user end + end + + # Access the `Formula` instance + attr_reader :formula + + # Create a new `Service` instance from either a path or label. + def self.from(path_or_label) + return nil unless path_or_label =~ /com\.github\.homebrew\.([^\.]+)(\.plist)?\z/ + new(Formula.factory(Formula.canonical_name($1))) rescue nil + end + + # Initialize new `Service` instance with supplied formula. + def initialize(formula); @formula = formula end + + # Delegate access to `formula.name`. + def name; @name ||= formula.name end + + # Label delegates to formula.plist_name, e.g `homebrew.mxcl.<formula>`. + def label; @label ||= formula.plist_name end + + # Path to a static plist file, this is always `com.github.homebrew.<formula>.plist`. + def plist; @plist ||= formula.prefix + "#{label}.plist" end + + # Path to destination plist, if run as root it's in `boot_path`, else `user_path`. + def dest; (ServicesCli.root? ? ServicesCli.boot_path : ServicesCli.user_path) + "#{label}.plist" end + + # Returns `true` if formula implements #startup_plist or file exists. + def plist?; formula.installed? && (plist.file? || formula.respond_to?(:startup_plist)) end + + # Returns `true` if service is loaded, else false. + def loaded?; %x{#{ServicesCli.launchctl} list | grep #{label} 2>/dev/null}.chomp =~ /#{label}\z/ end + + # Get current PID of daemon process from launchctl. + def pid + status = %x{#{ServicesCli.launchctl} list | grep #{label} 2>/dev/null}.chomp + return $1.to_i if status =~ /\A([\d]+)\s+.+#{label}\z/ + end + + # Generate that plist file, dude. + def generate_plist(data = nil) + data ||= plist.file? ? plist : formula.startup_plist + + if data.respond_to?(:file?) && data.file? + data = data.read + elsif data.respond_to?(:keys) && data.keys.include?(:url) + require 'open-uri' + data = open(data).read + end + + # replace "template" variables and ensure label is always, always com.github.homebrew.<formula> + data = data.to_s.gsub(/\{\{([a-z][a-z0-9_]*)\}\}/i) { |m| formula.send($1).to_s if formula.respond_to?($1) }. + gsub(%r{(<key>Label</key>\s*<string>)[^<]*(</string>)}, '\1' + label + '\2') + + # and force fix UserName, if necessary + if formula.startup_user != "root" && data =~ %r{<key>UserName</key>\s*<string>root</string>} + data = data.gsub(%r{(<key>UserName</key>\s*<string>)[^<]*(</string>)}, '\1' + formula.startup_user + '\2') + elsif ServicesCli.root? && formula.startup_user != "root" && data !~ %r{<key>UserName</key>} + data = data.gsub(%r{(</dict>\s*</plist>)}, " <key>UserName</key><string>#{formula.startup_user}</string>\n\\1") + end + + if ARGV.verbose? + ohai "Generated plist for #{formula.name}:" + puts " " + data.gsub("\n", "\n ") + puts + end + + data + end +end + +# Start the cli dispatch stuff. +# +ServicesCli.run! diff --git a/Library/Contributions/cmd/brew-switch.rb b/Library/Contributions/cmd/brew-switch.rb new file mode 100755 index 000000000..3b51f941c --- /dev/null +++ b/Library/Contributions/cmd/brew-switch.rb @@ -0,0 +1,40 @@ +require 'formula' +require 'keg' + +if ARGV.named.length != 2 + onoe "Usage: brew switch <formula> <version>" + exit 1 +end + +name = ARGV.shift +version = ARGV.shift + +# Does this formula have any versions? +f = Formula.factory(name.downcase) +cellar = f.prefix.parent +unless cellar.directory? + onoe "#{name} not found in the Cellar." + exit 2 +end + +# Does the target version exist? +unless (cellar+version).directory? + onoe "#{name} does not have a version \"#{version}\" in the Cellar." + + versions = cellar.children.select { |pn| pn.directory? }.collect { |pn| pn.basename.to_s } + puts "Versions available: #{versions.join(', ')}" + + exit 3 +end + +# Unlink all existing versions +cellar.children.select { |pn| pn.directory? }.each do |v| + keg = Keg.new(v) + puts "Cleaning #{keg}" + keg.unlink +end + +# Link new version + +keg = Keg.new(cellar+version) +puts "#{keg.link} links created for #{keg}"
\ No newline at end of file diff --git a/Library/Contributions/cmd/brew-test-bot.rb b/Library/Contributions/cmd/brew-test-bot.rb new file mode 100755 index 000000000..58e4f5e6c --- /dev/null +++ b/Library/Contributions/cmd/brew-test-bot.rb @@ -0,0 +1,296 @@ +# Comprehensively test a formula or pull request. +# +# Usage: brew test-bot [options...] <pull-request|formula> +# +# Options: +# --keep-logs: Write and keep log files under ./brewbot/ +# --cleanup: Clean the Homebrew directory. Very dangerous. Use with care. +# --skip-setup: Don't check the local system is setup correctly. + +require 'formula' +require 'utils' +require 'date' + +HOMEBREW_CONTRIBUTED_CMDS = HOMEBREW_REPOSITORY + "Library/Contributions/cmd/" + +class Step + attr_reader :command, :repository + attr_accessor :status + + def initialize test, command + @test = test + @category = test.category + @command = command + @name = command.split[1].delete '-' + @status = :running + @repository = HOMEBREW_REPOSITORY + @test.steps << self + end + + def log_file_path full_path=true + file = "#{@category}.#{@name}.txt" + return file unless @test.log_root and full_path + @test.log_root + file + end + + def status_colour + case @status + when :passed then "green" + when :running then "orange" + when :failed then "red" + end + end + + def status_upcase + @status.to_s.upcase + end + + def puts_command + print "#{Tty.blue}==>#{Tty.white} #{@command}#{Tty.reset}" + tabs = (80 - "PASSED".length + 1 - @command.length) / 8 + tabs.times{ print "\t" } + $stdout.flush + end + + def puts_result + puts "#{Tty.send status_colour}#{status_upcase}#{Tty.reset}" + end + + def self.run test, command, puts_output_on_success = false + step = new test, command + step.puts_command + + command = "#{step.command} &>#{step.log_file_path}" + + output = nil + if command.start_with? 'git ' + Dir.chdir step.repository do + output = `#{command}` + end + else + output = `#{command}` + end + output = IO.read(step.log_file_path) + + success = $?.success? + step.status = success ? :passed : :failed + step.puts_result + if output and output.any? and (not success or puts_output_on_success) + puts output + end + FileUtils.rm step.log_file_path unless ARGV.include? "--keep-logs" + end +end + +class Test + attr_reader :log_root, :category, :name + attr_reader :core_changed, :formulae + attr_accessor :steps + + def initialize argument + @hash = nil + @url = nil + @formulae = [] + + url_match = argument.match HOMEBREW_PULL_URL_REGEX + formula = Formula.factory argument rescue FormulaUnavailableError + git "rev-parse --verify #{argument} &>/dev/null" + if $?.success? + @hash = argument + elsif url_match + @url = url_match[0] + elsif formula + @formulae = [argument] + else + odie "#{argument} is not a pull request URL, commit URL or formula name." + end + + @category = __method__ + @steps = [] + @core_changed = false + @brewbot_root = Pathname.pwd + "brewbot" + FileUtils.mkdir_p @brewbot_root + end + + def git arguments + Dir.chdir HOMEBREW_REPOSITORY do + `git #{arguments}` + end + end + + def download + def current_sha1 + git('rev-parse --short HEAD').strip + end + + def current_branch + git('symbolic-ref HEAD').gsub('refs/heads/', '').strip + end + + @category = __method__ + @start_branch = current_branch + + if @hash or @url + diff_start_sha1 = current_sha1 + test "brew update" if current_branch == "master" + diff_end_sha1 = current_sha1 + end + + if @hash == 'HEAD' + @name = "#{diff_start_sha1}-#{diff_end_sha1}" + elsif @hash + test "git checkout #{@hash}" + diff_start_sha1 = "#{@hash}^" + diff_end_sha1 = @hash + @name = @hash + elsif @url + test "git checkout #{current_sha1}" + test "brew pull --clean #{@url}" + diff_end_sha1 = current_sha1 + @name = "#{@url}-#{diff_end_sha1}" + else + diff_start_sha1 = diff_end_sha1 = current_sha1 + @name = "#{@formulae.first}-#{diff_end_sha1}" + end + + @log_root = @brewbot_root + @name + FileUtils.mkdir_p @log_root + + return unless diff_start_sha1 != diff_end_sha1 + return if @url and steps.last.status != :passed + + diff_stat = git "diff #{diff_start_sha1}..#{diff_end_sha1} --name-status" + diff_stat.each_line do |line| + status, filename = line.split + # Don't try and do anything to removed files. + if (status == 'A' or status == 'M') + if filename.include? '/Formula/' + @formulae << File.basename(filename, '.rb') + end + end + if filename.include? '/Homebrew/' or filename.include? '/ENV/' \ + or filename.include? 'bin/brew' + @core_changed = true + end + end + end + + def setup + @category = __method__ + + test "brew doctor" + test "brew --env" + test "brew --config" + end + + def formula formula + @category = __method__.to_s + ".#{formula}" + + dependencies = `brew deps #{formula}`.split("\n") + dependencies -= `brew list`.split("\n") + dependencies = dependencies.join(' ') + formula_object = Formula.factory(formula) + + test "brew audit #{formula}" + test "brew fetch #{dependencies}" unless dependencies.empty? + test "brew fetch --build-bottle #{formula}" + test "brew install --verbose #{dependencies}" unless dependencies.empty? + test "brew install --verbose --build-bottle #{formula}" + return unless steps.last.status == :passed + test "brew bottle #{formula}", true + bottle_version = bottle_new_version(formula_object) + bottle_filename = bottle_filename(formula_object, bottle_version) + test "brew uninstall #{formula}" + test "brew install #{bottle_filename}" + test "brew test #{formula}" if formula_object.test_defined? + test "brew uninstall #{formula}" + test "brew uninstall #{dependencies}" unless dependencies.empty? + end + + def homebrew + @category = __method__ + test "brew tests" + test "brew readall" + end + + def cleanup_before + @category = __method__ + return unless ARGV.include? '--cleanup' + git 'stash' + git 'am --abort 2>/dev/null' + git 'rebase --abort 2>/dev/null' + git 'reset --hard' + git 'clean --force -dx' + end + + def cleanup_after + @category = __method__ + force_flag = '' + if ARGV.include? '--cleanup' + test 'brew cleanup' + test 'git clean --force -dx' + force_flag = '-f' + end + + if ARGV.include? '--cleanup' or @url or @hash + test "git checkout #{force_flag} #{@start_branch}" + end + + if ARGV.include? '--cleanup' + test 'git reset --hard' + test 'git gc' + git 'stash pop 2>/dev/null' + end + + FileUtils.rm_rf @brewbot_root unless ARGV.include? "--keep-logs" + end + + def test cmd, puts_output_on_success = false + Step.run self, cmd, puts_output_on_success + end + + def check_results + message = "All tests passed and raring to brew." + + status = :passed + steps.each do |step| + case step.status + when :passed then next + when :running then raise + when :failed then + if status == :passed + status = :failed + message = "" + end + message += "#{step.command}: #{step.status.to_s.upcase}\n" + end + end + status == :passed + end + + def self.run argument + test = new argument + test.cleanup_before + test.download + test.setup unless ARGV.include? "--skip-setup" + test.formulae.each do |formula| + test.formula formula + end + test.homebrew if test.core_changed + test.cleanup_after + test.check_results + end +end + +if Pathname.pwd == HOMEBREW_PREFIX and ARGV.include? "--cleanup" + odie 'cannot use --cleanup from HOMEBREW_PREFIX as it will delete all output.' +end + +any_errors = false +if ARGV.named.empty? + # With no arguments just build the most recent commit. + any_errors = Test.run 'HEAD' +else + ARGV.named.each { |argument| any_errors = Test.run(argument) or any_errors } +end +exit any_errors ? 0 : 1 diff --git a/Library/Contributions/cmd/brew-tests.rb b/Library/Contributions/cmd/brew-tests.rb new file mode 100755 index 000000000..0a2fb78e7 --- /dev/null +++ b/Library/Contributions/cmd/brew-tests.rb @@ -0,0 +1,11 @@ +module Homebrew extend self + def tests + (HOMEBREW_LIBRARY/'Homebrew/test').cd do + ENV['TESTOPTS'] = '-v' if ARGV.verbose? + system "rake", "test" + exit $?.exitstatus + end + end +end + +Homebrew.tests diff --git a/Library/Contributions/cmd/brew-unpack.rb b/Library/Contributions/cmd/brew-unpack.rb new file mode 100755 index 000000000..eb00d2fff --- /dev/null +++ b/Library/Contributions/cmd/brew-unpack.rb @@ -0,0 +1,110 @@ +require 'formula' + +require 'stringio' +module ScriptDataReader + # This module contains a method for extracting the contents of DATA from a + # Ruby file other than the script containing the currently executing + # function. Many thanks to Glenn Jackman's Stackoverflow answer which + # provided this code: + # + # http://stackoverflow.com/questions/2156629/can-i-access-the-data-from-a-required-script-in-ruby/2157556#2157556 + def self.load(filename) + data = StringIO.new + File.open(filename) do |f| + begin + line = f.gets + end until line.nil? or line.match(/^__END__$/) + while line = f.gets + data << line + end + end + data.rewind + data + end +end + +# otherwise we may unpack bottles +ENV['HOMEBREW_BUILD_FROM_SOURCE'] = '1' + +# Need to tweak the Formula class slightly so that patching is option and `DATA` +# patches work correctly. +class Formula + # Create a reference to the original Formula.patch method and then override + # so that paching only happens if the user asks. + alias do_patch patch + def patch + if ARGV.flag? '--patch' + # Yes Ruby, we are about to redefine a constant. Just breathe. + orig_v = $VERBOSE; $VERBOSE = nil + Formula.const_set 'DATA', ScriptDataReader.load(path) + $VERBOSE = orig_v + + do_patch + end + end + + # handle_llvm_failure() requires extend/ENV, so let's never fail + # with llvm since we don't particularly care in this context. + def fails_with_llvm?; false; end +end + +module Homebrew extend self + def unpack + unpack_usage = <<-EOS +Usage: brew unpack [-pg] [--destdir=path/to/extract/in] <formulae ...> + +Unpack formulae source code for inspection. + +Formulae archives will be extracted to subfolders inside the current working +directory or a directory specified by `--destdir`. If the `-p` option is +supplied, patches will also be downloaded and applied. If the `-g` option is +specified a git repository is created and all files added so that you can diff +changes. + EOS + + abort unpack_usage if ARGV.empty? + + formulae = ARGV.formulae + raise FormulaUnspecifiedError if formulae.empty? + + unpack_dir = ARGV.options_only.select {|o| o.start_with? "--destdir="} + if unpack_dir.empty? + unpack_dir = Pathname.new Dir.getwd + else + unpack_dir = Pathname.new(unpack_dir.first.split('=')[1]).realpath + unpack_dir.mkpath unless unpack_dir.exist? + end + + raise "Cannot write to #{unpack_dir}" unless unpack_dir.writable_real? + + formulae.each do |f| + # Create a nice name for the stage folder. + stage_dir = unpack_dir + [f.name, f.version].join('-') + + if stage_dir.exist? + raise "Destination #{stage_dir} allready exists!" unless ARGV.force? + rm_rf stage_dir + end + + oh1 "Unpacking #{f.name} to: #{stage_dir}" + ENV['VERBOSE'] = '1' # show messages about tar + f.brew do + cd Dir['*'][0] if Dir['*'].one? + cp_r getwd, stage_dir + end + ENV['VERBOSE'] = nil + + if ARGV.switch? 'g' + ohai "Setting up git repository" + cd stage_dir + system "git init -q" + system "git add -A" + system 'git commit -qm"Vanilla"' + end + end + end +end + +# Here is the actual code that gets run when `brew` loads this external +# command. +Homebrew.unpack diff --git a/Library/Contributions/cmd/brew-which.rb b/Library/Contributions/cmd/brew-which.rb new file mode 100755 index 000000000..b3318ee76 --- /dev/null +++ b/Library/Contributions/cmd/brew-which.rb @@ -0,0 +1,49 @@ +require 'extend/pathname' + + +module Homebrew extend self + def which_versions which_brews=nil + brew_links = Array.new + version_map = Hash.new + + real_cellar = HOMEBREW_CELLAR.realpath + + paths=%w[bin sbin lib].collect {|d| HOMEBREW_PREFIX+d} + + paths.each do |path| + path.find do |path| + next unless path.symlink? && path.resolved_path_exists? + brew_links << Pathname.new(path.realpath) + end + end + + brew_links = brew_links.collect{|p|p.relative_path_from(real_cellar).to_s}.reject{|p|p.start_with?("../")} + + brew_links.each do |p| + parts = p.split("/") + next if parts.count < 2 # Shouldn't happen for normally installed brews + brew = parts.shift + version = parts.shift + + next unless which_brews.include? brew if which_brews + + versions = version_map[brew] || [] + versions << version unless versions.include? version + version_map[brew] = versions + end + + return version_map + end + + def which + which_brews = ARGV.named.empty? ? nil : ARGV.named + + brews = which_versions which_brews + brews.keys.sort.each do |b| + puts "#{b}: #{brews[b].sort*' '}" + end + puts + end +end + +Homebrew.which diff --git a/Library/Contributions/cmd/git b/Library/Contributions/cmd/git new file mode 100755 index 000000000..7539ecfc0 --- /dev/null +++ b/Library/Contributions/cmd/git @@ -0,0 +1,51 @@ +#!/usr/bin/ruby -W0 +# This script because we support $GIT, $HOMEBREW_SVN, etc. and Xcode-only +# configurations. Order is careful to be what the user would want. + +F = File.basename(__FILE__).freeze +D = File.expand_path(File.dirname(__FILE__)).freeze + +def exec *args + # prevent fork-bombs + arg0 = if args.size == 1 + args.first.split(' ') + else + args + end.first + return if arg0 =~ /^#{F}/i + return if File.expand_path(arg0) == File.expand_path(__FILE__) + + if args[1] == '-print-path' and File.executable? args[0] + puts args[0] + exit 0 + else + Kernel.exec *args + end +end + +case F.downcase + when 'git' then %W{HOMEBREW_GIT GIT} + when 'svn' then "HOMEBREW_SVN" + else [] +end.each do |key| + exec ENV[key], *ARGV if ENV[key] and File.executable? ENV[key] +end + +brew_version = File.expand_path("#{D}/../../../bin/#{F}") +exec brew_version, *ARGV if File.executable? brew_version + +`/usr/bin/which -a #{F} 2>/dev/null`.split("\n").each do |path| + exec path, *ARGV +end + +# xcrun hangs if xcode-select is set to "/" +path = `/usr/bin/xcode-select -print-path 2>/dev/null`.chomp +if path != "/" + path = `/usr/bin/xcrun -find #{F} 2>/dev/null`.chomp + exec path, *ARGV if File.executable? path +end + +path = "/Applications/Xcode.app/Contents/Developer/usr/bin/#{F}" +exec path, *ARGV if File.executable? path + +abort "You must: brew install #{F}" diff --git a/Library/Contributions/cmd/svn b/Library/Contributions/cmd/svn new file mode 120000 index 000000000..0899c2993 --- /dev/null +++ b/Library/Contributions/cmd/svn @@ -0,0 +1 @@ +git
\ No newline at end of file |
