tap_dance 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/Guardfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +72 -0
- data/Rakefile +1 -0
- data/bin/dance +15 -0
- data/examples/01_brewfile.rb +7 -0
- data/ext/array.rb +18 -0
- data/ext/hash.rb +42 -0
- data/lib/tap_dance/brew.rb +109 -0
- data/lib/tap_dance/brew_cli.rb +99 -0
- data/lib/tap_dance/cli.rb +83 -0
- data/lib/tap_dance/definition.rb +47 -0
- data/lib/tap_dance/dsl.rb +55 -0
- data/lib/tap_dance/shell_result.rb +61 -0
- data/lib/tap_dance/tap.rb +36 -0
- data/lib/tap_dance/ui.rb +159 -0
- data/lib/tap_dance/version.rb +3 -0
- data/lib/tap_dance.rb +21 -0
- data/spec/bin/dance_spec.rb +24 -0
- data/spec/lib/brew_cli_spec.rb +13 -0
- data/spec/lib/brew_spec.rb +133 -0
- data/spec/lib/cli_spec.rb +23 -0
- data/spec/lib/dsl_spec.rb +7 -0
- data/spec/lib/shell_result_spec.rb +78 -0
- data/spec/lib/tap_spec.rb +28 -0
- data/spec/spec_helper.rb +0 -0
- data/spec/tap_dance_spec.rb +9 -0
- data/tap_dance.gemspec +27 -0
- metadata +174 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MmRkZjkyMDQ0NWQ0NGE1MmNjMDRhYTBhY2EwMDQ5ZmUyMDMxNDk2MA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YzM1YTQzODBjYTk4OTFiZmRmMjE2ODAzODcwM2U2MTdhNWFlMmUxZQ==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NDg0NGJhZjE0OTA3ZTYyN2E1NzFkOTA3NWE2YjNiOGVlNTY1YmRlZmIxOGIw
|
10
|
+
ZmFmMTNmZDU0NTdjYjY3MjdmMzI5YjcxNjdkMWMzZWNkYjY0M2VkMzlkODQx
|
11
|
+
N2NkZGQzYzRhMzQ5MjJkZDU4NWJiYTE5OWExNmVkMWRjMjEyZjA=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NWJhMTcwNWNlMjU0ODc0ZDYwMWMwZWRiNDQ5MDJjYzAwZGMyYjUzNzZhYzRj
|
14
|
+
MDA4MDUyZWNmMzRhNzNjMTNhMDhmZmI3NDczYjEzZjU5NmNiNjJjNmYyMjlm
|
15
|
+
ZTZkZTA1MDAwYzVjMzQyMDNmNmIyOGRmMDI4MDFkYzZlNDU4YWQ=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jonathan Martin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/tap_dance.png)](http://badge.fury.io/rb/tap_dance)
|
2
|
+
[![Dependency Status](https://gemnasium.com/nybblr/tap_dance.png)](https://gemnasium.com/nybblr/tap_dance)
|
3
|
+
[![Code Climate](https://codeclimate.com/github/nybblr/tap_dance.png)](https://codeclimate.com/github/nybblr/tap_dance)
|
4
|
+
|
5
|
+
Tap Dance: Homebrew meets Bundler
|
6
|
+
===============================
|
7
|
+
|
8
|
+
Homebrew rocks, but managing system level dependencies can become a pain to track across multiple machines. Bundler was designed to make explicit declaration of Ruby dependencies consistent and painless. Tap Dance aims to do the same for system binaries by alleviating such issues as:
|
9
|
+
- "What did I `brew install` to make my vim plugins work?"
|
10
|
+
- "With what extra arguments did I run my python installation?"
|
11
|
+
- "Did I install that item in `brew list` for a reason I don't remember, or is it an automatically installed dependency?"
|
12
|
+
- "Ugh, I just want to pull my latest dotfiles and have everything work."
|
13
|
+
|
14
|
+
Installation
|
15
|
+
------------
|
16
|
+
|
17
|
+
The "best" way to install tap dance is by including it in your system-wide Gemfile (e.g. a Gemfile in your dotfiles).
|
18
|
+
|
19
|
+
Add this line to your Gemfile:
|
20
|
+
|
21
|
+
gem 'tap_dance'
|
22
|
+
|
23
|
+
And then execute:
|
24
|
+
|
25
|
+
$ bundle
|
26
|
+
|
27
|
+
If you don't version-control your dots (which you should), install it yourself as:
|
28
|
+
|
29
|
+
$ gem install tap_dance
|
30
|
+
|
31
|
+
Usage
|
32
|
+
-----
|
33
|
+
|
34
|
+
TODO: Write usage instructions here
|
35
|
+
|
36
|
+
What's up with the name?
|
37
|
+
------------------------
|
38
|
+
|
39
|
+
Because declaring system and project deps with an awesome ruby gem makes me want to dance. Although I actually prefer swing and waltz.
|
40
|
+
|
41
|
+
Features
|
42
|
+
--------
|
43
|
+
|
44
|
+
Limit by:
|
45
|
+
- matching computer name
|
46
|
+
- environment variable
|
47
|
+
- shell script
|
48
|
+
- if installed
|
49
|
+
|
50
|
+
Groups
|
51
|
+
|
52
|
+
Credit where due!
|
53
|
+
-----------------
|
54
|
+
|
55
|
+
Huge thanks to Yehuda Katz for his awesome work on Bundler. I shamelessly stole a lot of the feature ideas and Gemfile evaluation magic from it. Oh, and obviously since the gem is built around Homebrew, a big thanks to Max and the community around brew: it has redefined my machine setup workflow.
|
56
|
+
|
57
|
+
Contributing
|
58
|
+
------------
|
59
|
+
|
60
|
+
I love contributions: give me a hand by including an in-depth description of the problem fixed or feature implemented, with usage information if applicable.
|
61
|
+
|
62
|
+
Most importantly, please include tests: if the request fixes a bug, include one or more failing tests which cover the issue. For new features, try to cover the basic functionalities, making sure the tests can pass on any properly configured OSX workstation.
|
63
|
+
|
64
|
+
**Steps to contribution awesomeness:**
|
65
|
+
|
66
|
+
1. Fork it
|
67
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
68
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
69
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
70
|
+
5. Create new Pull Request
|
71
|
+
|
72
|
+
Although you could just make your commits on the master branch, it's easier to inspect merge histories with actual feature branches. It also makes the bounds of your contribution obvious. Did I mention it looks better in `git log --oneline --graph`
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/dance
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Add lib, ext to load path
|
4
|
+
root = File.expand_path '../..', __FILE__
|
5
|
+
[:lib, :ext].each do |dir|
|
6
|
+
path = File.join root, dir.to_s
|
7
|
+
$:.unshift(path) unless $:.include?(path)
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rubygems'
|
11
|
+
require 'thor'
|
12
|
+
|
13
|
+
require "tap_dance"
|
14
|
+
|
15
|
+
TapDance::CLI.start
|
data/ext/array.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
class Array
|
2
|
+
# Simple implementation of hash
|
3
|
+
# squash for Hash#squash. See the
|
4
|
+
# Hash notes for more explanation.
|
5
|
+
#
|
6
|
+
# For Array, we simply call squash
|
7
|
+
# on each item.
|
8
|
+
|
9
|
+
def squash(separator=" ")
|
10
|
+
map do |m|
|
11
|
+
if m.respond_to? :squash
|
12
|
+
m.squash(separator)
|
13
|
+
else
|
14
|
+
m.to_s
|
15
|
+
end
|
16
|
+
end.flatten
|
17
|
+
end
|
18
|
+
end
|
data/ext/hash.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
class Hash
|
2
|
+
# Converts a deeply nested hash
|
3
|
+
# of stringish keys and values
|
4
|
+
# to an array of flattened branches
|
5
|
+
#
|
6
|
+
# For example, consider:
|
7
|
+
#
|
8
|
+
# hash = {
|
9
|
+
# :with => {
|
10
|
+
# :b => [1, 2]
|
11
|
+
# },
|
12
|
+
# :without => "c"
|
13
|
+
# }
|
14
|
+
#
|
15
|
+
# hash.squash("_") =>
|
16
|
+
# [
|
17
|
+
# "with_b_1",
|
18
|
+
# "with_b_2",
|
19
|
+
# "without_c"
|
20
|
+
# ]
|
21
|
+
#
|
22
|
+
# This is useful for readable hash options, like:
|
23
|
+
#
|
24
|
+
# magic :with => [ "Jack", "Giant" => { :in => "sky", :on => [ "ground", "harp" ] } ]
|
25
|
+
|
26
|
+
def squash(separator=" ")
|
27
|
+
inject([]) do |memo, (k, v)|
|
28
|
+
key = if k.respond_to?(:squash) then k.squash else [ k.to_s ] end
|
29
|
+
val = if v.respond_to?(:squash) then v.squash else [ v.to_s ] end
|
30
|
+
|
31
|
+
# Loop over all combos
|
32
|
+
for sk in key do
|
33
|
+
for sv in val do
|
34
|
+
memo << (sk + separator + sv)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
memo
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'tap_dance/ui'
|
2
|
+
require 'tap_dance/brew_cli'
|
3
|
+
|
4
|
+
module TapDance
|
5
|
+
class Brew
|
6
|
+
attr_accessor :name
|
7
|
+
attr_accessor :tap
|
8
|
+
|
9
|
+
def initialize(name, opts={})
|
10
|
+
@opts = opts.dup
|
11
|
+
@name = name.to_s
|
12
|
+
@tap = @opts[:tap]
|
13
|
+
@flags = @opts[:flags]
|
14
|
+
@flags ||= []
|
15
|
+
|
16
|
+
# Get actual tap object
|
17
|
+
unless @tap.nil?
|
18
|
+
case @tap
|
19
|
+
when String, Symbol
|
20
|
+
@tap = @opts[:definition].tap_named @tap.to_s
|
21
|
+
when TapDance::Tap
|
22
|
+
# Good to go!
|
23
|
+
end
|
24
|
+
@opts[:tap] = @tap
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
def install
|
30
|
+
if brewable?
|
31
|
+
# Do magic here
|
32
|
+
BrewCLI.install canonical, @flags
|
33
|
+
else
|
34
|
+
TapDance.ui.error "No available formula for \"#{canonical}\""
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def upgrade
|
39
|
+
if installed?
|
40
|
+
# Do magic here
|
41
|
+
BrewCLI.upgrade canonical, @flags
|
42
|
+
else
|
43
|
+
TapDance.ui.error "You haven't installed \"#{canonical}\""
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def canonical
|
48
|
+
unless @tap.nil?
|
49
|
+
"#{@tap.url}/#{@name}"
|
50
|
+
else
|
51
|
+
@name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Does the formula exist?
|
56
|
+
def brewable?
|
57
|
+
BrewCLI.formula_info(canonical).okay?
|
58
|
+
end
|
59
|
+
|
60
|
+
def formula_version
|
61
|
+
res = BrewCLI.formula_info(canonical)
|
62
|
+
return nil unless res.okay?
|
63
|
+
|
64
|
+
res.out.split($/).first.match(/#{name}: stable ([^\s,]+)/)[1]
|
65
|
+
end
|
66
|
+
|
67
|
+
def formula_versions
|
68
|
+
# Takes a long time to get; cache return
|
69
|
+
if @formula_versions.nil?
|
70
|
+
@formula_versions = BrewCLI.formula_versions(canonical).
|
71
|
+
out.chomp.split($/).map do |v|
|
72
|
+
s = v.split(/\s+/)
|
73
|
+
version = s[0]
|
74
|
+
commit = s[3]
|
75
|
+
path = s[4]
|
76
|
+
[ version, commit, path ]
|
77
|
+
end
|
78
|
+
else
|
79
|
+
return @formula_versions
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def installed_versions
|
84
|
+
# Would love to use StringScanner...but we don't need to!
|
85
|
+
versions = BrewCLI.list_versions(canonical).out.strip.split(/\s+/)
|
86
|
+
unless versions.empty?
|
87
|
+
return versions[1..-1]
|
88
|
+
else
|
89
|
+
return []
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Is a version installed?
|
94
|
+
def installed?
|
95
|
+
!installed_versions.empty?
|
96
|
+
end
|
97
|
+
|
98
|
+
def latest_version
|
99
|
+
# Works unless the versions have
|
100
|
+
# strings in them, like alpha/beta
|
101
|
+
installed_versions.sort.last if installed?
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s
|
105
|
+
return canonical
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'tap_dance'
|
2
|
+
require 'tap_dance/shell_result'
|
3
|
+
|
4
|
+
# Load in extensions
|
5
|
+
require 'hash'
|
6
|
+
require 'array'
|
7
|
+
|
8
|
+
module TapDance
|
9
|
+
module BrewCLI
|
10
|
+
class << self
|
11
|
+
# One offing terminal commands makes it easy to stub.
|
12
|
+
attr_accessor :dry_run
|
13
|
+
attr_writer :prefix
|
14
|
+
|
15
|
+
def prefix
|
16
|
+
@prefix ||= exec("--prefix").strip
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
exec "update", true
|
21
|
+
end
|
22
|
+
|
23
|
+
def install(name, flags="")
|
24
|
+
exec "install #{name} #{flag_string flags}", true
|
25
|
+
end
|
26
|
+
|
27
|
+
def upgrade(name, flags="")
|
28
|
+
exec "upgrade #{name} #{flag_string flags}", true
|
29
|
+
end
|
30
|
+
|
31
|
+
def list_versions(name)
|
32
|
+
exec "list --versions #{name}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def formula_info(name)
|
36
|
+
exec "info #{name}"
|
37
|
+
end
|
38
|
+
|
39
|
+
def formula_versions(name)
|
40
|
+
exec "versions #{name}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def tap(url)
|
44
|
+
exec "tap #{url}", true
|
45
|
+
end
|
46
|
+
|
47
|
+
def untap(url)
|
48
|
+
exec "untap #{url}", true
|
49
|
+
end
|
50
|
+
|
51
|
+
def tap_list
|
52
|
+
exec "tap"
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Private because we want
|
58
|
+
# all brew commands wrapped
|
59
|
+
# for testing.
|
60
|
+
def exec(cmd, volatile=false)
|
61
|
+
# If a command is volatile (causes system changes),
|
62
|
+
# we want to offer a dry-run option for testing
|
63
|
+
# and the CLI.
|
64
|
+
cmd.strip!
|
65
|
+
|
66
|
+
if volatile and @dry_run
|
67
|
+
TapDance.ui.warn "Would run: brew #{cmd}"
|
68
|
+
return nil
|
69
|
+
else
|
70
|
+
ShellResult.of "brew #{cmd}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Sanitize flags with standard unix
|
75
|
+
# conventions and generate a string
|
76
|
+
def flag_string(flags)
|
77
|
+
case flags
|
78
|
+
when String
|
79
|
+
return flags
|
80
|
+
else
|
81
|
+
return flags.squash("-").map {|m|
|
82
|
+
case m
|
83
|
+
when /^--/
|
84
|
+
m
|
85
|
+
when /^-[^\-]/
|
86
|
+
m
|
87
|
+
else
|
88
|
+
if m =~ /^.[ =]/
|
89
|
+
"-#{m}"
|
90
|
+
else
|
91
|
+
"--#{m}"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
}.join(" ")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'tap_dance'
|
2
|
+
require 'tap_dance/brew'
|
3
|
+
require 'tap_dance/tap'
|
4
|
+
require 'tap_dance/dsl'
|
5
|
+
|
6
|
+
require 'thor'
|
7
|
+
|
8
|
+
module TapDance
|
9
|
+
class CLI < Thor
|
10
|
+
package_name "TapDance"
|
11
|
+
default_task "install"
|
12
|
+
|
13
|
+
class_option "verbose",
|
14
|
+
:type => :boolean,
|
15
|
+
:banner => "Enable verbose output mode",
|
16
|
+
:aliases => "-v"
|
17
|
+
class_option "brewfile",
|
18
|
+
:type => :string,
|
19
|
+
:banner => "Specify an alternate path to a Brewfile",
|
20
|
+
:aliases => "-B"
|
21
|
+
class_option "dry-run",
|
22
|
+
:type => :boolean,
|
23
|
+
:banner => "Print out what volatile commands would be run"
|
24
|
+
|
25
|
+
attr_accessor :brewfile
|
26
|
+
attr_accessor :definition
|
27
|
+
|
28
|
+
def initialize(*args, &block)
|
29
|
+
super(*args, &block)
|
30
|
+
|
31
|
+
# Activate shell output
|
32
|
+
TapDance.ui = UI::Shell.new(options)
|
33
|
+
TapDance.ui.level = "debug" if options["verbose"]
|
34
|
+
|
35
|
+
# Make sure homebrew is installed
|
36
|
+
unless command?(:brew)
|
37
|
+
TapDance.ui.error "You haven't installed homebrew, or it isn't in your path."
|
38
|
+
exit 1
|
39
|
+
end
|
40
|
+
|
41
|
+
@definition = nil
|
42
|
+
|
43
|
+
# Activate dry-run mode
|
44
|
+
TapDance::BrewCLI.dry_run = options["dry-run"] if options["dry-run"]
|
45
|
+
|
46
|
+
# Find brewfile
|
47
|
+
@brewfile = options["brewfile"]
|
48
|
+
@brewfile ||= "./Brewfile"
|
49
|
+
@brewfile = File.expand_path @brewfile
|
50
|
+
|
51
|
+
TapDance.ui.info "Brewing from #{@brewfile}"
|
52
|
+
|
53
|
+
unless File.exist? @brewfile
|
54
|
+
TapDance.ui.error "Nothing to brew! No Brewfile found."
|
55
|
+
else
|
56
|
+
TapDance.ui.info "Running `brew update` for good measure."
|
57
|
+
BrewCLI.update
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
desc "install", "install all the brews in your Brewfile"
|
62
|
+
def install
|
63
|
+
return unless File.exist? @brewfile
|
64
|
+
@definition = TapDance::DSL.evaluate @brewfile
|
65
|
+
@definition.execute
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "update", "update all the brews in your Brewfile"
|
69
|
+
def update(name=nil)
|
70
|
+
return unless File.exist? @brewfile
|
71
|
+
@definition = TapDance::DSL.evaluate @brewfile
|
72
|
+
@definition.execute true
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
# Note: susceptible to code injection!
|
78
|
+
def command?(name)
|
79
|
+
system "which #{name.to_s} > /dev/null 2>&1"
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'tap_dance/brew'
|
2
|
+
require 'tap_dance/tap'
|
3
|
+
require 'tap_dance/ui'
|
4
|
+
require 'tap_dance'
|
5
|
+
|
6
|
+
module TapDance
|
7
|
+
class Definition
|
8
|
+
attr_accessor :taps
|
9
|
+
attr_accessor :brews
|
10
|
+
|
11
|
+
def initialize(taps=[], brews=[])
|
12
|
+
@taps = taps
|
13
|
+
@brews = brews
|
14
|
+
end
|
15
|
+
|
16
|
+
def tap(name, url, opts={})
|
17
|
+
@taps << Tap.new(name, url, opts.merge(:definition => self))
|
18
|
+
@taps.last
|
19
|
+
end
|
20
|
+
|
21
|
+
def brew(name, opts={})
|
22
|
+
@brews << Brew.new(name, opts.merge(:definition => self))
|
23
|
+
@brews.last
|
24
|
+
end
|
25
|
+
|
26
|
+
def tap_named(name)
|
27
|
+
@taps.find { |t| t.name.to_s == name.to_s }
|
28
|
+
end
|
29
|
+
|
30
|
+
def execute(upgrade=false)
|
31
|
+
for tap in @taps do
|
32
|
+
TapDance.ui.confirm "Tapping \"#{tap}\""
|
33
|
+
TapDance.ui.detail tap.entap
|
34
|
+
end
|
35
|
+
|
36
|
+
for brew in @brews do
|
37
|
+
unless upgrade
|
38
|
+
TapDance.ui.confirm "Installing \"#{brew}\""
|
39
|
+
TapDance.ui.detail brew.install
|
40
|
+
else
|
41
|
+
TapDance.ui.confirm "Upgrading \"#{brew}\""
|
42
|
+
TapDance.ui.detail brew.upgrade
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'tap_dance/definition'
|
2
|
+
require 'tap_dance/brew'
|
3
|
+
require 'tap_dance/tap'
|
4
|
+
require 'tap_dance/ui'
|
5
|
+
|
6
|
+
class BrewfileError < RuntimeError; end
|
7
|
+
module TapDance
|
8
|
+
class DSL
|
9
|
+
attr_reader :definition
|
10
|
+
|
11
|
+
def self.evaluate(brewfile, lockfile=nil, unlock=nil)
|
12
|
+
builder = new
|
13
|
+
builder.eval_brewfile(brewfile)
|
14
|
+
builder.definition
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@groups = [] # Arg/filter groups
|
19
|
+
@tap = nil # Current tap scope
|
20
|
+
|
21
|
+
@definition = Definition.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def eval_brewfile(brewfile, contents = nil)
|
25
|
+
contents ||= File.read File.expand_path(brewfile.to_s)
|
26
|
+
instance_eval contents, brewfile.to_s, 1
|
27
|
+
rescue SyntaxError => e
|
28
|
+
bt = e.message.split("\n")[1..-1]
|
29
|
+
raise BrewfileError, ["Brewfile syntax error:", *bt].join("\n")
|
30
|
+
rescue ScriptError, RegexpError, NameError, ArgumentError => e
|
31
|
+
e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
|
32
|
+
TapDance.ui.warn e.backtrace.join("\n ")
|
33
|
+
raise BrewfileError, "There was an error in your Brewfile," \
|
34
|
+
" and TapDance cannot continue."
|
35
|
+
end
|
36
|
+
|
37
|
+
### DSL commands
|
38
|
+
def tap(name, url, opts={})
|
39
|
+
# Can't nest taps; doesn't make sense
|
40
|
+
raise BrewfileError, "You cannot nest taps!" unless @tap.nil?
|
41
|
+
old_tap = @tap
|
42
|
+
|
43
|
+
@tap = @definition.tap name, url, opts
|
44
|
+
|
45
|
+
yield if block_given?
|
46
|
+
|
47
|
+
@tap = old_tap
|
48
|
+
end
|
49
|
+
|
50
|
+
def brew(name, opts={})
|
51
|
+
@definition.brew name, { :tap => @tap }.merge(opts)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module TapDance
|
4
|
+
class ShellResult
|
5
|
+
attr_reader :out
|
6
|
+
attr_reader :err
|
7
|
+
attr_reader :status
|
8
|
+
|
9
|
+
def self.of(cmd)
|
10
|
+
out, err, sts = Open3.capture3 cmd
|
11
|
+
new out, err, sts.exitstatus
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(out, err=nil, status=0)
|
15
|
+
@out = out.to_s
|
16
|
+
@err = err.to_s
|
17
|
+
@status = status
|
18
|
+
end
|
19
|
+
|
20
|
+
def okay?
|
21
|
+
!error?
|
22
|
+
end
|
23
|
+
|
24
|
+
def error?
|
25
|
+
@status != 0 || erred(@out) || erred(@err)
|
26
|
+
end
|
27
|
+
|
28
|
+
def stdout?
|
29
|
+
@out.strip != ""
|
30
|
+
end
|
31
|
+
|
32
|
+
def stderr?
|
33
|
+
@err.strip != ""
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
if @out.strip == "" then @err else @out end
|
38
|
+
end
|
39
|
+
|
40
|
+
### We likes, but disable for refactoring
|
41
|
+
# def method_missing(method, *args, &block)
|
42
|
+
# # Let people treat it like a string
|
43
|
+
# if String.new.respond_to? method
|
44
|
+
# to_s.send(method, *args, &block)
|
45
|
+
# else
|
46
|
+
# super
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
|
50
|
+
# def respond_to_missing?(method, include_private = false)
|
51
|
+
# String.new.respond_to?(method) || super
|
52
|
+
# end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def erred(string)
|
57
|
+
string[0..4] == 'Error'
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|