aboutsummaryrefslogtreecommitdiffstats
path: root/Library/Contributions/cmd
diff options
context:
space:
mode:
authorMike McQuaid2013-02-17 13:32:15 +0000
committerMike McQuaid2013-02-18 10:42:44 +0000
commit4a281cd5cf8f722d73a4f50612e8c460f57fddd7 (patch)
tree765c472cc951590398a52651b59051af2cdd2b59 /Library/Contributions/cmd
parente9d58c410052157487f96a77dd318cbda641806e (diff)
downloadbrew-4a281cd5cf8f722d73a4f50612e8c460f57fddd7.tar.bz2
Rename Library/Contributions/cmds -> cmd.
Diffstat (limited to 'Library/Contributions/cmd')
-rwxr-xr-xLibrary/Contributions/cmd/brew-aspell-dictionaries51
-rwxr-xr-xLibrary/Contributions/cmd/brew-beer.rb179
-rwxr-xr-xLibrary/Contributions/cmd/brew-dirty.rb7
-rwxr-xr-xLibrary/Contributions/cmd/brew-graph311
-rwxr-xr-xLibrary/Contributions/cmd/brew-grep3
-rwxr-xr-xLibrary/Contributions/cmd/brew-leaves.rb27
-rwxr-xr-xLibrary/Contributions/cmd/brew-linkapps.rb33
-rwxr-xr-xLibrary/Contributions/cmd/brew-ls-taps.rb18
-rwxr-xr-xLibrary/Contributions/cmd/brew-man51
-rwxr-xr-xLibrary/Contributions/cmd/brew-mirror-check.rb56
-rwxr-xr-xLibrary/Contributions/cmd/brew-pull.rb89
-rwxr-xr-xLibrary/Contributions/cmd/brew-readall.rb14
-rwxr-xr-xLibrary/Contributions/cmd/brew-server208
-rwxr-xr-xLibrary/Contributions/cmd/brew-services.rb369
-rwxr-xr-xLibrary/Contributions/cmd/brew-switch.rb40
-rwxr-xr-xLibrary/Contributions/cmd/brew-test-bot.rb296
-rwxr-xr-xLibrary/Contributions/cmd/brew-tests.rb11
-rwxr-xr-xLibrary/Contributions/cmd/brew-unpack.rb110
-rwxr-xr-xLibrary/Contributions/cmd/brew-which.rb49
-rwxr-xr-xLibrary/Contributions/cmd/git51
l---------Library/Contributions/cmd/svn1
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 &ldquo;#{q}&rdquo;</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>&larr; <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>&larr; <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