boxen 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +49 -0
- data/LICENSE +20 -0
- data/README.md +7 -0
- data/boxen.gemspec +24 -0
- data/lib/boxen/check.rb +72 -0
- data/lib/boxen/cli.rb +79 -0
- data/lib/boxen/config.rb +251 -0
- data/lib/boxen/error.rb +4 -0
- data/lib/boxen/flags.rb +166 -0
- data/lib/boxen/postflight.rb +13 -0
- data/lib/boxen/postflight/active.rb +16 -0
- data/lib/boxen/postflight/env.rb +29 -0
- data/lib/boxen/preflight.rb +13 -0
- data/lib/boxen/preflight/creds.rb +34 -0
- data/lib/boxen/preflight/etc_my_cnf.rb +12 -0
- data/lib/boxen/preflight/homebrew.rb +13 -0
- data/lib/boxen/preflight/identity.rb +16 -0
- data/lib/boxen/preflight/os.rb +11 -0
- data/lib/boxen/preflight/rbenv.rb +12 -0
- data/lib/boxen/project.rb +24 -0
- data/lib/boxen/puppeteer.rb +68 -0
- data/lib/boxen/util.rb +17 -0
- data/script/bootstrap +9 -0
- data/script/tests +10 -0
- data/test/boxen/test.rb +7 -0
- data/test/boxen_check_test.rb +55 -0
- data/test/boxen_cli_test.rb +15 -0
- data/test/boxen_config_test.rb +157 -0
- data/test/boxen_flags_test.rb +182 -0
- data/test/boxen_postflight_active_test.rb +29 -0
- data/test/boxen_postflight_env_test.rb +6 -0
- data/test/boxen_project_test.rb +21 -0
- data/test/boxen_puppeteer_test.rb +77 -0
- data/test/boxen_util_test.rb +21 -0
- data/test/fixtures/repo/modules/projects/manifests/first-project.pp +0 -0
- data/test/fixtures/repo/modules/projects/manifests/second-project.pp +0 -0
- data/test/system_timer.rb +10 -0
- metadata +211 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
boxen (0.0.0)
|
5
|
+
ansi (~> 1.4)
|
6
|
+
highline (~> 1.6)
|
7
|
+
json_pure (~> 1.7)
|
8
|
+
octokit (~> 1.15)
|
9
|
+
puppet (~> 3.0)
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: http://rubygems.org/
|
13
|
+
specs:
|
14
|
+
addressable (2.3.2)
|
15
|
+
ansi (1.4.3)
|
16
|
+
facter (1.6.12)
|
17
|
+
faraday (0.8.4)
|
18
|
+
multipart-post (~> 1.1)
|
19
|
+
faraday_middleware (0.8.8)
|
20
|
+
faraday (>= 0.7.4, < 0.9)
|
21
|
+
hashie (1.2.0)
|
22
|
+
hiera (1.1.0.rc1)
|
23
|
+
json
|
24
|
+
highline (1.6.15)
|
25
|
+
json (1.7.5)
|
26
|
+
json_pure (1.7.5)
|
27
|
+
metaclass (0.0.1)
|
28
|
+
minitest (3.5.0)
|
29
|
+
mocha (0.12.6)
|
30
|
+
metaclass (~> 0.0.1)
|
31
|
+
multi_json (1.3.6)
|
32
|
+
multipart-post (1.1.5)
|
33
|
+
octokit (1.15.1)
|
34
|
+
addressable (~> 2.2)
|
35
|
+
faraday (~> 0.8)
|
36
|
+
faraday_middleware (~> 0.8)
|
37
|
+
hashie (~> 1.2)
|
38
|
+
multi_json (~> 1.3)
|
39
|
+
puppet (3.0.0)
|
40
|
+
facter (>= 1.6.11)
|
41
|
+
hiera (>= 1.0.0rc)
|
42
|
+
|
43
|
+
PLATFORMS
|
44
|
+
ruby
|
45
|
+
|
46
|
+
DEPENDENCIES
|
47
|
+
boxen!
|
48
|
+
minitest (= 3.5.0)
|
49
|
+
mocha (~> 0.12)
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 GitHub, Inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
17
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
18
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
19
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
20
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
data/boxen.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "boxen"
|
5
|
+
gem.version = "0.0.0"
|
6
|
+
gem.authors = ["John Barnette", "Will Farrington"]
|
7
|
+
gem.email = ["jbarnette@github.com", "wfarr@github.com"]
|
8
|
+
gem.description = "Manage Mac development boxes with love (and Puppet)."
|
9
|
+
gem.summary = "You know, for laptops and stuff."
|
10
|
+
gem.homepage = "https://github.com/boxen/boxen"
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split $/
|
13
|
+
gem.test_files = gem.files.grep /^test/
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
|
16
|
+
gem.add_dependency "ansi", "~> 1.4"
|
17
|
+
gem.add_dependency "highline", "~> 1.6"
|
18
|
+
gem.add_dependency "json_pure", "~> 1.7"
|
19
|
+
gem.add_dependency "octokit", "~> 1.15"
|
20
|
+
gem.add_dependency "puppet", "~> 3.0"
|
21
|
+
|
22
|
+
gem.add_development_dependency "minitest", "3.5.0" # pinned for mocha
|
23
|
+
gem.add_development_dependency "mocha", "~> 0.12"
|
24
|
+
end
|
data/lib/boxen/check.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require "ansi"
|
2
|
+
|
3
|
+
module Boxen
|
4
|
+
|
5
|
+
# The superclass for preflight and postflight sanity checks.
|
6
|
+
|
7
|
+
class Check
|
8
|
+
|
9
|
+
# A collection of preflight instances for `config`. An instance is
|
10
|
+
# created for every constant under `self` that's also a
|
11
|
+
# subclass of `self`.
|
12
|
+
|
13
|
+
def self.checks(config)
|
14
|
+
constants.map { |n| const_get n }.
|
15
|
+
select { |c| c < self }.
|
16
|
+
map { |c| c.new config }
|
17
|
+
end
|
18
|
+
|
19
|
+
# Search `dir` and load all Ruby files under it.
|
20
|
+
|
21
|
+
def self.register(dir)
|
22
|
+
Dir["#{dir}/*.rb"].sort.each { |f| load f }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Check each instance against `config`.
|
26
|
+
|
27
|
+
def self.run(config)
|
28
|
+
checks(config).each { |check| check.run unless check.ok? }
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :config
|
32
|
+
|
33
|
+
def initialize(config)
|
34
|
+
@config = config
|
35
|
+
end
|
36
|
+
|
37
|
+
# Is everything good to go? Implemented by subclasses.
|
38
|
+
|
39
|
+
def ok?
|
40
|
+
raise "Subclasses must implement this method."
|
41
|
+
end
|
42
|
+
|
43
|
+
# Warn, fix, or abort. Implemented by subclasses.
|
44
|
+
|
45
|
+
def run
|
46
|
+
raise "Subclasses must implement this method."
|
47
|
+
end
|
48
|
+
|
49
|
+
# A fancier `abort` and `warn`. This will probably really annoy
|
50
|
+
# someone at some point because it's overriding a Kernel method,
|
51
|
+
# but it's limited to checks.
|
52
|
+
|
53
|
+
def abort(message, *extras)
|
54
|
+
extras << { :color => :red }
|
55
|
+
warn message, *extras
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
|
59
|
+
def warn(message, *extras)
|
60
|
+
options = Hash === extras.last ? extras.pop : {}
|
61
|
+
color = options[:color] || :yellow
|
62
|
+
|
63
|
+
$stderr.puts ANSI.send(color) { "--> #{message}" }
|
64
|
+
|
65
|
+
unless extras.empty?
|
66
|
+
extras.each { |line| $stderr.puts " #{line}" }
|
67
|
+
end
|
68
|
+
|
69
|
+
$stderr.puts
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/boxen/cli.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require "boxen/config"
|
2
|
+
require "boxen/flags"
|
3
|
+
require "boxen/postflight"
|
4
|
+
require "boxen/preflight"
|
5
|
+
require "boxen/puppeteer"
|
6
|
+
|
7
|
+
module Boxen
|
8
|
+
class CLI
|
9
|
+
attr_reader :config
|
10
|
+
attr_reader :flags
|
11
|
+
attr_reader :puppet
|
12
|
+
|
13
|
+
def initialize(config, flags)
|
14
|
+
@config = config
|
15
|
+
@flags = flags
|
16
|
+
@puppet = Boxen::Puppeteer.new @config
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
|
21
|
+
# --env prints out the current BOXEN_ env vars.
|
22
|
+
|
23
|
+
exec "env | grep ^BOXEN_ | sort" if flags.env?
|
24
|
+
|
25
|
+
# --help prints some CLI help and exits.
|
26
|
+
|
27
|
+
abort "#{flags}\n" if flags.help?
|
28
|
+
|
29
|
+
# --projects prints a list of available projects and exits.
|
30
|
+
|
31
|
+
if flags.projects?
|
32
|
+
config.projects.each do |project|
|
33
|
+
prefix = project.installed? ? "*" : " "
|
34
|
+
puts "#{prefix} #{project.name}"
|
35
|
+
end
|
36
|
+
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
status = puppet.run
|
41
|
+
|
42
|
+
return status
|
43
|
+
end
|
44
|
+
|
45
|
+
# Run Boxen by wiring together the command-line flags, config,
|
46
|
+
# preflights, Puppet execution, and postflights. Returns Puppet's
|
47
|
+
# exit code.
|
48
|
+
|
49
|
+
def self.run(*args)
|
50
|
+
config = Boxen::Config.load
|
51
|
+
flags = Boxen::Flags.new args
|
52
|
+
|
53
|
+
# Apply command-line flags to the config in case we're changing or
|
54
|
+
# overriding anything.
|
55
|
+
|
56
|
+
flags.apply config
|
57
|
+
|
58
|
+
# Run the preflight checks.
|
59
|
+
|
60
|
+
Boxen::Preflight.run config
|
61
|
+
|
62
|
+
# Save the config for Puppet (and next time).
|
63
|
+
|
64
|
+
Boxen::Config.save config
|
65
|
+
|
66
|
+
# Make the magic happen.
|
67
|
+
|
68
|
+
status = Boxen::CLI.new(config, flags).run
|
69
|
+
|
70
|
+
# Run the postflight checks.
|
71
|
+
|
72
|
+
Boxen::Postflight.run config if status.zero?
|
73
|
+
|
74
|
+
# Return Puppet's exit status.
|
75
|
+
|
76
|
+
return status
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/boxen/config.rb
ADDED
@@ -0,0 +1,251 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "json"
|
3
|
+
require "octokit"
|
4
|
+
require "boxen/project"
|
5
|
+
|
6
|
+
module Boxen
|
7
|
+
|
8
|
+
# All configuration for Boxen, whether it's loaded from command-line
|
9
|
+
# args, environment variables, config files, or the keychain.
|
10
|
+
|
11
|
+
class Config
|
12
|
+
|
13
|
+
# The service name to use when loading/saving config in the Keychain.
|
14
|
+
|
15
|
+
KEYCHAIN_SERVICE = "Boxen"
|
16
|
+
|
17
|
+
# Load config. Yields config if `block` is given.
|
18
|
+
|
19
|
+
def self.load(&block)
|
20
|
+
new do |config|
|
21
|
+
file = "#{config.homedir}/config/boxen/defaults.json"
|
22
|
+
|
23
|
+
if File.file? file
|
24
|
+
attrs = JSON.parse File.read file
|
25
|
+
|
26
|
+
attrs.each do |key, value|
|
27
|
+
if value && config.respond_to?(selector = "#{key}=")
|
28
|
+
config.send selector, value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
cmd = "security find-generic-password " +
|
34
|
+
"-a #{config.user} -s '#{KEYCHAIN_SERVICE}' -w 2>/dev/null"
|
35
|
+
|
36
|
+
password = `#{cmd}`.strip
|
37
|
+
password = nil unless $?.success?
|
38
|
+
|
39
|
+
config.password = password
|
40
|
+
|
41
|
+
yield config if block_given?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Save `config`. Returns `config`. Note that this only saves data,
|
46
|
+
# not flags. For example, `login` will be saved, but `stealth?`
|
47
|
+
# won't.
|
48
|
+
|
49
|
+
def self.save(config)
|
50
|
+
attrs = {
|
51
|
+
:email => config.email,
|
52
|
+
:homedir => config.homedir,
|
53
|
+
:login => config.login,
|
54
|
+
:name => config.name,
|
55
|
+
:puppetdir => config.puppetdir,
|
56
|
+
:repodir => config.repodir,
|
57
|
+
:srcdir => config.srcdir,
|
58
|
+
:user => config.user
|
59
|
+
}
|
60
|
+
|
61
|
+
file = "#{config.homedir}/config/boxen/defaults.json"
|
62
|
+
FileUtils.mkdir_p File.dirname file
|
63
|
+
|
64
|
+
File.open file, "wb" do |f|
|
65
|
+
f.write JSON.generate Hash[attrs.reject { |k, v| v.nil? }]
|
66
|
+
end
|
67
|
+
|
68
|
+
cmd = ["security", "add-generic-password",
|
69
|
+
"-a", config.user, "-s", KEYCHAIN_SERVICE, "-U", "-w", config.password]
|
70
|
+
|
71
|
+
unless system *cmd
|
72
|
+
raise Boxen::Error, "Can't save config in the Keychain."
|
73
|
+
end
|
74
|
+
|
75
|
+
config
|
76
|
+
end
|
77
|
+
|
78
|
+
# Create a new instance. Yields `self` if `block` is given.
|
79
|
+
|
80
|
+
def initialize(&block)
|
81
|
+
@fde = true
|
82
|
+
@pull = true
|
83
|
+
|
84
|
+
yield self if block_given?
|
85
|
+
end
|
86
|
+
|
87
|
+
# Create an API instance using the current user creds. A new
|
88
|
+
# instance is created any time `login` or `password` change.
|
89
|
+
|
90
|
+
def api
|
91
|
+
@api ||= Octokit::Client.new :login => login, :password => password
|
92
|
+
end
|
93
|
+
|
94
|
+
# Spew a bunch of debug logging? Default is `false`.
|
95
|
+
|
96
|
+
def debug?
|
97
|
+
!!@debug
|
98
|
+
end
|
99
|
+
|
100
|
+
attr_writer :debug
|
101
|
+
|
102
|
+
def dirty?
|
103
|
+
`git status --porcelain`.strip.empty?
|
104
|
+
end
|
105
|
+
|
106
|
+
# A GitHub user's public email.
|
107
|
+
|
108
|
+
attr_accessor :email
|
109
|
+
|
110
|
+
# The shell script that loads Boxen's environment.
|
111
|
+
|
112
|
+
def envfile
|
113
|
+
"#{homedir}/env.sh"
|
114
|
+
end
|
115
|
+
|
116
|
+
# Is full disk encryption required? Default is `true`. Respects
|
117
|
+
# the `BOXEN_NO_FDE` environment variable.
|
118
|
+
|
119
|
+
def fde?
|
120
|
+
!ENV["BOXEN_NO_FDE"] && @fde
|
121
|
+
end
|
122
|
+
|
123
|
+
attr_writer :fde
|
124
|
+
|
125
|
+
# Boxen's home directory. Default is `"/opt/boxen"`. Respects the
|
126
|
+
# `BOXEN_HOME` environment variable.
|
127
|
+
|
128
|
+
def homedir
|
129
|
+
@homedir || ENV["BOXEN_HOME"] || "/opt/boxen"
|
130
|
+
end
|
131
|
+
|
132
|
+
attr_writer :homedir
|
133
|
+
|
134
|
+
# Boxen's log file. Default is `"#{repodir}/log/boxen.log"`.
|
135
|
+
# Respects the `BOXEN_LOG_FILE` environment variable. The log is
|
136
|
+
# overwritten on every run.
|
137
|
+
|
138
|
+
def logfile
|
139
|
+
@logfile || ENV["BOXEN_LOG_FILE"] || "#{repodir}/log/boxen.log"
|
140
|
+
end
|
141
|
+
|
142
|
+
attr_writer :logfile
|
143
|
+
|
144
|
+
# A GitHub user login. Default is `nil`.
|
145
|
+
|
146
|
+
attr_reader :login
|
147
|
+
|
148
|
+
def login=(login)
|
149
|
+
@api = nil
|
150
|
+
@login = login
|
151
|
+
end
|
152
|
+
|
153
|
+
# Is Boxen running on the `master` branch?
|
154
|
+
|
155
|
+
def master?
|
156
|
+
`git symbolic-ref HEAD`.chomp == "refs/heads/master"
|
157
|
+
end
|
158
|
+
|
159
|
+
# A GitHub user's profile name.
|
160
|
+
|
161
|
+
attr_accessor :name
|
162
|
+
|
163
|
+
# A GitHub user password. Default is `nil`.
|
164
|
+
|
165
|
+
attr_reader :password
|
166
|
+
|
167
|
+
def password=(password)
|
168
|
+
@api = nil
|
169
|
+
@password = password
|
170
|
+
end
|
171
|
+
|
172
|
+
# Just go through the motions? Default is `false`.
|
173
|
+
|
174
|
+
def pretend?
|
175
|
+
!!@pretend
|
176
|
+
end
|
177
|
+
|
178
|
+
attr_writer :pretend
|
179
|
+
|
180
|
+
# Run a profiler on Puppet? Default is `false`.
|
181
|
+
|
182
|
+
def profile?
|
183
|
+
!!@profile
|
184
|
+
end
|
185
|
+
|
186
|
+
attr_writer :profile
|
187
|
+
|
188
|
+
# An Array of Boxen::Project entries, one for each project Boxen
|
189
|
+
# knows how to manage.
|
190
|
+
#
|
191
|
+
# FIX: Revisit this once we restructure template projects. It's
|
192
|
+
# broken for several reasons: It assumes paths that won't be
|
193
|
+
# right, and it assumes projects live in the same repo as this
|
194
|
+
# file.
|
195
|
+
|
196
|
+
def projects
|
197
|
+
files = Dir["#{repodir}/modules/projects/manifests/*.pp"]
|
198
|
+
names = files.map { |m| File.basename m, ".pp" }.sort
|
199
|
+
|
200
|
+
names.map do |name|
|
201
|
+
Boxen::Project.new "#{srcdir}/#{name}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# The directory where Puppet expects configuration (which we don't
|
206
|
+
# use) and runtime information (which we generally don't care
|
207
|
+
# about). Default is `/tmp/boxen/puppet`. Respects the
|
208
|
+
# `BOXEN_PUPPET_DIR` environment variable.
|
209
|
+
|
210
|
+
def puppetdir
|
211
|
+
@puppetdir || ENV["BOXEN_PUPPET_DIR"] || "/tmp/boxen/puppet"
|
212
|
+
end
|
213
|
+
|
214
|
+
attr_writer :puppetdir
|
215
|
+
|
216
|
+
# The directory of the custom Boxen repo for an org. Default is
|
217
|
+
# `Dir.pwd`. Respects the `BOXEN_REPO_DIR` environment variable.
|
218
|
+
|
219
|
+
def repodir
|
220
|
+
@repodir || ENV["BOXEN_REPO_DIR"] || Dir.pwd
|
221
|
+
end
|
222
|
+
|
223
|
+
attr_writer :repodir
|
224
|
+
|
225
|
+
# The directory where repos live. Default is
|
226
|
+
# `"/Users/#{user}/src"`.
|
227
|
+
|
228
|
+
def srcdir
|
229
|
+
@srcdir || "/Users/#{user}/src"
|
230
|
+
end
|
231
|
+
|
232
|
+
attr_writer :srcdir
|
233
|
+
|
234
|
+
# Don't auto-create issues on failure? Default is `false`.
|
235
|
+
# Respects the `BOXEN_NO_ISSUE` environment variable.
|
236
|
+
|
237
|
+
def stealth?
|
238
|
+
!!ENV["BOXEN_NO_ISSUE"] || @stealth
|
239
|
+
end
|
240
|
+
|
241
|
+
attr_writer :stealth
|
242
|
+
|
243
|
+
# A local user login. Default is the `USER` environment variable.
|
244
|
+
|
245
|
+
def user
|
246
|
+
@user || ENV["USER"]
|
247
|
+
end
|
248
|
+
|
249
|
+
attr_writer :user
|
250
|
+
end
|
251
|
+
end
|