boxen 2.9.0 → 3.0.0.beta1
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/boxen.gemspec +12 -12
- data/lib/boxen/check.rb +8 -39
- data/lib/boxen/cli.rb +19 -45
- data/lib/boxen/command.rb +142 -0
- data/lib/boxen/command/help.rb +40 -0
- data/lib/boxen/command/preflight.rb +38 -0
- data/lib/boxen/command/project.rb +49 -0
- data/lib/boxen/command/project/install.rb +33 -0
- data/lib/boxen/command/run.rb +199 -0
- data/lib/boxen/command/service.rb +61 -0
- data/lib/boxen/command/service/disable.rb +15 -0
- data/lib/boxen/command/service/enable.rb +15 -0
- data/lib/boxen/command/service/restart.rb +24 -0
- data/lib/boxen/command/version.rb +29 -0
- data/lib/boxen/command_status.rb +15 -0
- data/lib/boxen/config.rb +43 -61
- data/lib/boxen/hook.rb +15 -8
- data/lib/boxen/keychain.rb +1 -1
- data/lib/boxen/postflight/env.rb +1 -1
- data/lib/boxen/postflight/github_issue.rb +124 -0
- data/lib/boxen/postflight/hooks.rb +16 -0
- data/lib/boxen/postflight/web_hook.rb +63 -0
- data/lib/boxen/preflight.rb +4 -7
- data/lib/boxen/preflight/creds.rb +8 -47
- data/lib/boxen/preflight/facts.rb +36 -0
- data/lib/boxen/preflight/homebrew.rb +13 -0
- data/lib/boxen/preflight/identity.rb +2 -0
- data/lib/boxen/preflight/offline.rb +33 -0
- data/lib/boxen/preflight/os.rb +1 -1
- data/lib/boxen/preflight/update.rb +109 -0
- data/lib/boxen/util/logging.rb +59 -0
- data/lib/boxen/version.rb +3 -0
- data/script/bootstrap +1 -1
- data/script/tests +1 -0
- data/test/boxen/test.rb +1 -1
- data/test/boxen_cli_test.rb +8 -31
- data/test/boxen_command_test.rb +93 -0
- data/test/boxen_config_test.rb +1 -31
- data/test/boxen_directories_test.rb +4 -4
- data/test/boxen_hook_test.rb +25 -0
- data/test/command/help_test.rb +49 -0
- data/test/command/project/install_test.rb +34 -0
- data/test/command/project_test.rb +32 -0
- data/test/command/run_test.rb +21 -0
- data/test/command/service/disable_test.rb +49 -0
- data/test/command/service/enable_test.rb +49 -0
- data/test/command/service/restart_test.rb +53 -0
- data/test/command/service_test.rb +55 -0
- data/test/command/version_test.rb +15 -0
- data/test/{boxen_postflight_active_test.rb → postflight/boxen_postflight_active_test.rb} +3 -3
- data/test/{boxen_postflight_env_test.rb → postflight/boxen_postflight_env_test.rb} +0 -0
- data/test/{boxen_hook_github_issue_test.rb → postflight/boxen_postflight_github_issue_test.rb} +72 -82
- data/test/{boxen_hook_web_test.rb → postflight/boxen_postflight_web_hook_test.rb} +12 -11
- data/test/preflight/boxen_preflight_creds_test.rb +82 -0
- data/test/{boxen_preflight_etc_my_cnf_test.rb → preflight/boxen_preflight_etc_my_cnf_test.rb} +1 -1
- data/test/preflight/boxen_preflight_homebrew_test.rb +10 -0
- data/test/{boxen_preflight_rvm_test.rb → preflight/boxen_preflight_rvm_test.rb} +1 -1
- metadata +247 -171
- checksums.yaml +0 -7
- data/lib/boxen/flags.rb +0 -282
- data/lib/boxen/hook/github_issue.rb +0 -120
- data/lib/boxen/hook/web.rb +0 -56
- data/lib/boxen/puppeteer.rb +0 -121
- data/lib/boxen/runner.rb +0 -149
- data/test/boxen_check_test.rb +0 -55
- data/test/boxen_flags_test.rb +0 -217
- data/test/boxen_preflight_creds_test.rb +0 -177
- data/test/boxen_puppeteer_test.rb +0 -101
- data/test/boxen_runner_test.rb +0 -171
data/lib/boxen/hook.rb
CHANGED
@@ -2,7 +2,6 @@ module Boxen
|
|
2
2
|
class Hook
|
3
3
|
attr_reader :config
|
4
4
|
attr_reader :checkout
|
5
|
-
attr_reader :puppet
|
6
5
|
attr_reader :result
|
7
6
|
|
8
7
|
@hooks = []
|
@@ -16,13 +15,24 @@ module Boxen
|
|
16
15
|
end
|
17
16
|
|
18
17
|
def self.all
|
19
|
-
@hooks
|
18
|
+
@hooks
|
20
19
|
end
|
21
20
|
|
22
|
-
def
|
21
|
+
def self.register(klass)
|
22
|
+
unless defined? @hooks
|
23
|
+
@hooks = []
|
24
|
+
end
|
25
|
+
|
26
|
+
@hooks << klass
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.run
|
30
|
+
@hooks.each { |hook| hook.new(nil, nil, nil).run }
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(config, checkout, result)
|
23
34
|
@config = config
|
24
35
|
@checkout = checkout
|
25
|
-
@puppet = puppet
|
26
36
|
@result = result
|
27
37
|
end
|
28
38
|
|
@@ -34,7 +44,7 @@ module Boxen
|
|
34
44
|
end
|
35
45
|
|
36
46
|
def perform?
|
37
|
-
|
47
|
+
enabled?
|
38
48
|
end
|
39
49
|
|
40
50
|
def run
|
@@ -42,6 +52,3 @@ module Boxen
|
|
42
52
|
end
|
43
53
|
end
|
44
54
|
end
|
45
|
-
|
46
|
-
require "boxen/hook/github_issue"
|
47
|
-
require "boxen/hook/web"
|
data/lib/boxen/keychain.rb
CHANGED
data/lib/boxen/postflight/env.rb
CHANGED
@@ -0,0 +1,124 @@
|
|
1
|
+
require "boxen/postflight"
|
2
|
+
require "boxen/checkout"
|
3
|
+
|
4
|
+
# Checks to see if the basic environment is loaded.
|
5
|
+
|
6
|
+
class Boxen::Postflight::GithubIssue < Boxen::Postflight
|
7
|
+
def checkout
|
8
|
+
@checkout ||= Boxen::Checkout.new(@config)
|
9
|
+
end
|
10
|
+
|
11
|
+
def ok?
|
12
|
+
# Only run if we have credentials and we're on master
|
13
|
+
!enabled? || config.login.to_s.empty? || !checkout.master?
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
if command.success?
|
18
|
+
close_failures
|
19
|
+
else
|
20
|
+
warn "Sorry! Creating an issue on #{config.reponame}"
|
21
|
+
record_failure
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def compare_url
|
26
|
+
return unless config.reponame
|
27
|
+
"#{config.ghurl}/#{config.reponame}/compare/#{checkout.sha}...master"
|
28
|
+
end
|
29
|
+
|
30
|
+
def hostname
|
31
|
+
Socket.gethostname
|
32
|
+
end
|
33
|
+
|
34
|
+
def os
|
35
|
+
`sw_vers -productVersion`.strip
|
36
|
+
end
|
37
|
+
|
38
|
+
def shell
|
39
|
+
ENV["SHELL"]
|
40
|
+
end
|
41
|
+
|
42
|
+
def logfile
|
43
|
+
File.read config.logfile
|
44
|
+
end
|
45
|
+
|
46
|
+
def record_failure
|
47
|
+
return unless issues?
|
48
|
+
|
49
|
+
title = "Failed for #{config.user}"
|
50
|
+
config.api.create_issue config.reponame,
|
51
|
+
title,
|
52
|
+
failure_details,
|
53
|
+
:labels => [
|
54
|
+
failure_label
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
def close_failures
|
59
|
+
return unless issues?
|
60
|
+
|
61
|
+
comment = "Succeeded at version #{checkout.sha}."
|
62
|
+
failures.each do |issue|
|
63
|
+
config.api.add_comment(config.reponame, issue.number, comment)
|
64
|
+
config.api.close_issue(config.reponame, issue.number)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def failures
|
69
|
+
return [] unless issues?
|
70
|
+
|
71
|
+
issues = config.api.list_issues(config.reponame, :state => 'open',
|
72
|
+
:labels => failure_label, :creator => config.login)
|
73
|
+
issues.reject! {|i| i.labels.collect(&:name).include?(ongoing_label)}
|
74
|
+
issues
|
75
|
+
end
|
76
|
+
|
77
|
+
def failure_details
|
78
|
+
body = ''
|
79
|
+
body << "Running on `#{hostname}` (OS X #{os}) under `#{shell}`, "
|
80
|
+
body << "version #{checkout.sha} ([compare to master](#{compare_url}))."
|
81
|
+
body << "\n\n"
|
82
|
+
|
83
|
+
if checkout.dirty?
|
84
|
+
body << "### Changes"
|
85
|
+
body << "\n\n"
|
86
|
+
body << "```\n#{checkout.changes}\n```"
|
87
|
+
body << "\n\n"
|
88
|
+
end
|
89
|
+
|
90
|
+
body << "### Output (from #{config.logfile})"
|
91
|
+
body << "\n\n"
|
92
|
+
body << "```\n#{logfile}\n```\n"
|
93
|
+
|
94
|
+
body
|
95
|
+
end
|
96
|
+
|
97
|
+
attr_writer :failure_label
|
98
|
+
def failure_label
|
99
|
+
@failure_label ||= 'failure'
|
100
|
+
end
|
101
|
+
|
102
|
+
attr_writer :ongoing_label
|
103
|
+
def ongoing_label
|
104
|
+
@ongoing_label ||= 'ongoing'
|
105
|
+
end
|
106
|
+
|
107
|
+
def issues?
|
108
|
+
return unless config.reponame
|
109
|
+
return if config.reponame == 'boxen/our-boxen'
|
110
|
+
|
111
|
+
config.api.repository(config.reponame).has_issues
|
112
|
+
end
|
113
|
+
|
114
|
+
def required_environment_variables
|
115
|
+
['BOXEN_ISSUES_ENABLED']
|
116
|
+
end
|
117
|
+
|
118
|
+
def enabled?
|
119
|
+
required_vars = Array(required_environment_variables)
|
120
|
+
required_vars.any? && required_vars.all? do |var|
|
121
|
+
ENV[var] && !ENV[var].empty?
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "boxen/postflight"
|
2
|
+
require "boxen/hook"
|
3
|
+
|
4
|
+
# Prints deprecation notices for all pre-3.x style hooks
|
5
|
+
|
6
|
+
class Boxen::Postflight::Hooks < Boxen::Postflight
|
7
|
+
def ok?
|
8
|
+
!Boxen::Hook.all.any?
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
::Boxen::Hook.all.each do |hook|
|
13
|
+
warn "DEPRECATION WARNING: Boxen::Hook is deprecated (#{hook})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "boxen/postflight"
|
2
|
+
require "json"
|
3
|
+
require "net/http"
|
4
|
+
|
5
|
+
class Boxen::Postflight::WebHook < Boxen::Postflight
|
6
|
+
def ok?
|
7
|
+
!enabled?
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_writer :checkout
|
11
|
+
def checkout
|
12
|
+
@checkout ||= Boxen::Checkout.new(config)
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
payload = {
|
17
|
+
:login => config.user,
|
18
|
+
:sha => checkout.sha,
|
19
|
+
:status => command.success? ? 'success' : 'failure',
|
20
|
+
:time => "#{Time.now.utc.to_i}"
|
21
|
+
}
|
22
|
+
|
23
|
+
post_web_hook payload
|
24
|
+
end
|
25
|
+
|
26
|
+
def post_web_hook(payload)
|
27
|
+
headers = { 'Content-Type' => 'application/json' }
|
28
|
+
|
29
|
+
uri = URI.parse(URI.escape(ENV['BOXEN_WEB_HOOK_URL']))
|
30
|
+
|
31
|
+
user, pass, host, port, path = \
|
32
|
+
uri.user, uri.pass, uri.host, uri.port, uri.path
|
33
|
+
|
34
|
+
request = Net::HTTP::Post.new(path, headers)
|
35
|
+
|
36
|
+
if uri.scheme =~ /https/
|
37
|
+
http.use_ssl = true
|
38
|
+
end
|
39
|
+
|
40
|
+
if user && pass
|
41
|
+
request.basic_auth user, pass
|
42
|
+
end
|
43
|
+
|
44
|
+
request.body = payload.to_json
|
45
|
+
|
46
|
+
response = Net::HTTP.new(host, port).start do |http|
|
47
|
+
http.request(request)
|
48
|
+
end
|
49
|
+
|
50
|
+
response
|
51
|
+
end
|
52
|
+
|
53
|
+
def required_environment_variables
|
54
|
+
['BOXEN_WEB_HOOK_URL']
|
55
|
+
end
|
56
|
+
|
57
|
+
def enabled?
|
58
|
+
required_vars = Array(required_environment_variables)
|
59
|
+
required_vars.any? && required_vars.all? do |var|
|
60
|
+
ENV[var] && !ENV[var].empty?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/boxen/preflight.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
1
1
|
require "boxen/check"
|
2
2
|
|
3
3
|
module Boxen
|
4
|
-
|
5
|
-
# The superclass for preflight checks.
|
6
|
-
|
7
4
|
class Preflight < Boxen::Check
|
8
|
-
|
9
|
-
# Load all available preflight checks.
|
10
|
-
|
11
|
-
register File.expand_path("../preflight", __FILE__)
|
12
5
|
end
|
13
6
|
end
|
7
|
+
|
8
|
+
Dir["#{File.expand_path('../preflight', __FILE__)}/*"].each do |f|
|
9
|
+
require "boxen/preflight/#{File.basename f}"
|
10
|
+
end
|
@@ -1,8 +1,6 @@
|
|
1
1
|
require "boxen/preflight"
|
2
2
|
require "highline"
|
3
3
|
require "octokit"
|
4
|
-
require "digest"
|
5
|
-
require "socket"
|
6
4
|
|
7
5
|
# HACK: Unless this is `false`, HighLine has some really bizarre
|
8
6
|
# problems with empty/expended streams at bizarre intervals.
|
@@ -14,6 +12,8 @@ class Boxen::Preflight::Creds < Boxen::Preflight
|
|
14
12
|
attr :password
|
15
13
|
|
16
14
|
def ok?
|
15
|
+
return true if config.offline?
|
16
|
+
|
17
17
|
if config.token && config.api.user
|
18
18
|
# There was a period of time when login wasn't geting set on first run.
|
19
19
|
# This should correct that.
|
@@ -70,27 +70,12 @@ class Boxen::Preflight::Creds < Boxen::Preflight
|
|
70
70
|
fetch_login_and_password
|
71
71
|
tokens = get_tokens
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
tmp_api.delete_authorization(legacy_auth.id, :headers => headers) if legacy_auth
|
80
|
-
|
81
|
-
# The updated GitHub authorizations API, in order to improve security, no
|
82
|
-
# longer returns a plaintext `token` for existing authorizations. So, if an
|
83
|
-
# authorization already exists for this machine we need to first delete it
|
84
|
-
# so that we can create a new one.
|
85
|
-
auth = tokens.detect { |a| a.note == note && a.fingerprint == fingerprint }
|
86
|
-
tmp_api.delete_authorization(auth.id, :headers => headers) if auth
|
87
|
-
|
88
|
-
auth = tmp_api.create_authorization(
|
89
|
-
:note => note,
|
90
|
-
:scopes => %w(repo user),
|
91
|
-
:fingerprint => fingerprint,
|
92
|
-
:headers => headers
|
93
|
-
)
|
73
|
+
unless auth = tokens.detect { |a| a.note == "Boxen" }
|
74
|
+
auth = tmp_api.create_authorization \
|
75
|
+
:note => "Boxen",
|
76
|
+
:scopes => %w(repo user),
|
77
|
+
:headers => headers
|
78
|
+
end
|
94
79
|
|
95
80
|
config.token = auth.token
|
96
81
|
|
@@ -122,28 +107,4 @@ class Boxen::Preflight::Creds < Boxen::Preflight
|
|
122
107
|
warn "Oh, looks like you've provided your #{thing} as environmental variable..."
|
123
108
|
found
|
124
109
|
end
|
125
|
-
|
126
|
-
def fingerprint
|
127
|
-
@fingerprint ||= begin
|
128
|
-
# See Apple technical note TN1103, "Uniquely Identifying a Macintosh
|
129
|
-
# Computer."
|
130
|
-
serial_number_match_data = IO.popen(
|
131
|
-
["ioreg", "-c", "IOPlatformExpertDevice", "-d", "2"]
|
132
|
-
).read.match(/"IOPlatformSerialNumber" = "([[:alnum:]]+)"/)
|
133
|
-
if serial_number_match_data
|
134
|
-
# The fingerprint must be unique across all personal access tokens for a
|
135
|
-
# given user. We prefix the serial number with the application name to
|
136
|
-
# differentiate between any other personal access token that uses the
|
137
|
-
# Mac serial number for the fingerprint.
|
138
|
-
Digest::SHA256.hexdigest("Boxen: #{serial_number_match_data[1]}")
|
139
|
-
else
|
140
|
-
abort "Sorry, I was unable to obtain your Mac's serial number.",
|
141
|
-
"Boxen requires access to your Mac's serial number in order to generate a unique GitHub personal access token."
|
142
|
-
end
|
143
|
-
end
|
144
|
-
end
|
145
|
-
|
146
|
-
def note
|
147
|
-
@note ||= "Boxen: #{Socket.gethostname}"
|
148
|
-
end
|
149
110
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "fileutils"
|
2
|
+
require "pathname"
|
3
|
+
|
4
|
+
require "boxen/preflight"
|
5
|
+
|
6
|
+
class Boxen::Preflight::Facts < Boxen::Preflight
|
7
|
+
def ok?
|
8
|
+
write_facts
|
9
|
+
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def facter_d
|
19
|
+
Pathname.new("#{config.homedir}/facts.d")
|
20
|
+
end
|
21
|
+
|
22
|
+
def write_facts
|
23
|
+
FileUtils.mkdir_p facter_d
|
24
|
+
|
25
|
+
write_fact "offline", config.offline?
|
26
|
+
end
|
27
|
+
|
28
|
+
def write_fact(name, value)
|
29
|
+
File.open("#{facter_d}/#{name}.txt", "w") do |f|
|
30
|
+
f.write "#{name}=#{value}\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
debug "Setting global fact `#{name}` to `#{value}`"
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "boxen/preflight"
|
2
|
+
|
3
|
+
class Boxen::Preflight::Homebrew < Boxen::Preflight
|
4
|
+
def run
|
5
|
+
warn "You have an existing Homebrew install in /usr/local",
|
6
|
+
"The Boxen provides its own Homebrew, so consider deleting yours.",
|
7
|
+
"Keeping both will confuse many projects."
|
8
|
+
end
|
9
|
+
|
10
|
+
def ok?
|
11
|
+
!File.exist? "/usr/local/Library/Homebrew"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "timeout"
|
3
|
+
|
4
|
+
require "boxen/preflight"
|
5
|
+
|
6
|
+
class Boxen::Preflight::Offline < Boxen::Preflight
|
7
|
+
def ok?
|
8
|
+
config.offline = !google_reachable?
|
9
|
+
|
10
|
+
info "Running boxen in offline mode as we couldn't reach google." if config.offline?
|
11
|
+
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def google_reachable?
|
21
|
+
@online = begin
|
22
|
+
timeout(3) do
|
23
|
+
s = TCPSocket.new('google.com', 80)
|
24
|
+
s.close
|
25
|
+
end
|
26
|
+
true
|
27
|
+
rescue Errno::ECONNREFUSED
|
28
|
+
true
|
29
|
+
rescue Timeout::Error, StandardError
|
30
|
+
false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|