boxen 2.9.0 → 3.0.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|