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.
Files changed (69) hide show
  1. data/boxen.gemspec +12 -12
  2. data/lib/boxen/check.rb +8 -39
  3. data/lib/boxen/cli.rb +19 -45
  4. data/lib/boxen/command.rb +142 -0
  5. data/lib/boxen/command/help.rb +40 -0
  6. data/lib/boxen/command/preflight.rb +38 -0
  7. data/lib/boxen/command/project.rb +49 -0
  8. data/lib/boxen/command/project/install.rb +33 -0
  9. data/lib/boxen/command/run.rb +199 -0
  10. data/lib/boxen/command/service.rb +61 -0
  11. data/lib/boxen/command/service/disable.rb +15 -0
  12. data/lib/boxen/command/service/enable.rb +15 -0
  13. data/lib/boxen/command/service/restart.rb +24 -0
  14. data/lib/boxen/command/version.rb +29 -0
  15. data/lib/boxen/command_status.rb +15 -0
  16. data/lib/boxen/config.rb +43 -61
  17. data/lib/boxen/hook.rb +15 -8
  18. data/lib/boxen/keychain.rb +1 -1
  19. data/lib/boxen/postflight/env.rb +1 -1
  20. data/lib/boxen/postflight/github_issue.rb +124 -0
  21. data/lib/boxen/postflight/hooks.rb +16 -0
  22. data/lib/boxen/postflight/web_hook.rb +63 -0
  23. data/lib/boxen/preflight.rb +4 -7
  24. data/lib/boxen/preflight/creds.rb +8 -47
  25. data/lib/boxen/preflight/facts.rb +36 -0
  26. data/lib/boxen/preflight/homebrew.rb +13 -0
  27. data/lib/boxen/preflight/identity.rb +2 -0
  28. data/lib/boxen/preflight/offline.rb +33 -0
  29. data/lib/boxen/preflight/os.rb +1 -1
  30. data/lib/boxen/preflight/update.rb +109 -0
  31. data/lib/boxen/util/logging.rb +59 -0
  32. data/lib/boxen/version.rb +3 -0
  33. data/script/bootstrap +1 -1
  34. data/script/tests +1 -0
  35. data/test/boxen/test.rb +1 -1
  36. data/test/boxen_cli_test.rb +8 -31
  37. data/test/boxen_command_test.rb +93 -0
  38. data/test/boxen_config_test.rb +1 -31
  39. data/test/boxen_directories_test.rb +4 -4
  40. data/test/boxen_hook_test.rb +25 -0
  41. data/test/command/help_test.rb +49 -0
  42. data/test/command/project/install_test.rb +34 -0
  43. data/test/command/project_test.rb +32 -0
  44. data/test/command/run_test.rb +21 -0
  45. data/test/command/service/disable_test.rb +49 -0
  46. data/test/command/service/enable_test.rb +49 -0
  47. data/test/command/service/restart_test.rb +53 -0
  48. data/test/command/service_test.rb +55 -0
  49. data/test/command/version_test.rb +15 -0
  50. data/test/{boxen_postflight_active_test.rb → postflight/boxen_postflight_active_test.rb} +3 -3
  51. data/test/{boxen_postflight_env_test.rb → postflight/boxen_postflight_env_test.rb} +0 -0
  52. data/test/{boxen_hook_github_issue_test.rb → postflight/boxen_postflight_github_issue_test.rb} +72 -82
  53. data/test/{boxen_hook_web_test.rb → postflight/boxen_postflight_web_hook_test.rb} +12 -11
  54. data/test/preflight/boxen_preflight_creds_test.rb +82 -0
  55. data/test/{boxen_preflight_etc_my_cnf_test.rb → preflight/boxen_preflight_etc_my_cnf_test.rb} +1 -1
  56. data/test/preflight/boxen_preflight_homebrew_test.rb +10 -0
  57. data/test/{boxen_preflight_rvm_test.rb → preflight/boxen_preflight_rvm_test.rb} +1 -1
  58. metadata +247 -171
  59. checksums.yaml +0 -7
  60. data/lib/boxen/flags.rb +0 -282
  61. data/lib/boxen/hook/github_issue.rb +0 -120
  62. data/lib/boxen/hook/web.rb +0 -56
  63. data/lib/boxen/puppeteer.rb +0 -121
  64. data/lib/boxen/runner.rb +0 -149
  65. data/test/boxen_check_test.rb +0 -55
  66. data/test/boxen_flags_test.rb +0 -217
  67. data/test/boxen_preflight_creds_test.rb +0 -177
  68. data/test/boxen_puppeteer_test.rb +0 -101
  69. data/test/boxen_runner_test.rb +0 -171
@@ -0,0 +1,33 @@
1
+ require "boxen/command/project"
2
+
3
+ class Boxen::Command::Project::Install < Boxen::Command::Project
4
+ def self.detailed_help
5
+ <<-EOS
6
+
7
+ boxen project:install <project1> [<project2> ...]
8
+
9
+ Install one or more Boxen-managed projects.
10
+
11
+ EOS
12
+ end
13
+
14
+ def self.help
15
+ "Install one or more projects"
16
+ end
17
+
18
+ def run
19
+ File.open("#{config.repodir}/.projects", "w+") do |f|
20
+ f.truncate 0
21
+ f.write projects
22
+ end
23
+
24
+ Boxen::Command.invoke 'run', config
25
+ end
26
+
27
+ def projects
28
+ @args.join ','
29
+ end
30
+ end
31
+
32
+ Boxen::Command.register :"project:install", Boxen::Command::Project::Install
33
+ Boxen::Command.register :"projects:install", Boxen::Command::Project::Install
@@ -0,0 +1,199 @@
1
+ require "boxen/command"
2
+
3
+ class Boxen::Command::Run < Boxen::Command
4
+ preflight \
5
+ Boxen::Preflight::OS,
6
+ Boxen::Preflight::Directories,
7
+ Boxen::Preflight::EtcMyCnf,
8
+ Boxen::Preflight::Homebrew,
9
+ Boxen::Preflight::Rbenv,
10
+ Boxen::Preflight::RVM,
11
+ Boxen::Preflight::Offline,
12
+ Boxen::Preflight::Creds,
13
+ Boxen::Preflight::Identity,
14
+ Boxen::Preflight::Update,
15
+ Boxen::Preflight::Facts
16
+
17
+ postflight \
18
+ Boxen::Postflight::Active,
19
+ Boxen::Postflight::Hooks,
20
+ Boxen::Postflight::GithubIssue,
21
+ Boxen::Postflight::Env
22
+
23
+ def self.help
24
+ "run Boxen's managed puppet environment"
25
+ end
26
+
27
+ def self.detailed_help
28
+ <<-EOS
29
+
30
+ boxen run [options]
31
+
32
+ Runs Puppet via Boxen's environment.
33
+
34
+ Some options you may find useful:
35
+
36
+ --debug Be really, really verbose. Like incredibly verbose.
37
+ --no-issue Don't file an issue if the Boxen run fails.
38
+ --no-color Don't output any colored text to the tty.
39
+ --report Generate graphs and catalog data from Puppet.
40
+ --profile Display very high-level performance details from the Puppet run.
41
+ --future-parser Use Puppet's upcoming future parser
42
+ --graph Enable puppet dependency graph output
43
+
44
+ boxen run:noop [options]
45
+
46
+ The same thing as run, but acts as a dry run where no changes are made.
47
+
48
+ EOS
49
+ end
50
+
51
+ def run
52
+ info "Updating librarian-puppet modules"
53
+ create_clean_working_environment
54
+ run_librarian_puppet
55
+
56
+ warn command.join(" ") if debug?
57
+
58
+ info "Running puppet"
59
+ Boxen::Util.sudo(*command)
60
+
61
+ Boxen::CommandStatus.new($?.exitstatus, [0, 2])
62
+ end
63
+
64
+ def noop
65
+ false
66
+ end
67
+
68
+ def report?
69
+ @args.include? "--report"
70
+ end
71
+
72
+ def profile?
73
+ @args.include? "--profile"
74
+ end
75
+
76
+ def future_parser?
77
+ @args.include? "--future-parser"
78
+ end
79
+
80
+ def graph?
81
+ @args.include? "--graph"
82
+ end
83
+
84
+ def debug?
85
+ @args.include? "--debug"
86
+ end
87
+
88
+ def no_color?
89
+ @args.include? "--no-color"
90
+ end
91
+
92
+ private
93
+ def command
94
+ [
95
+ "#{config.repodir}/bin/puppet",
96
+ "apply",
97
+ puppet_flags,
98
+ "#{config.repodir}/manifests/site.pp"
99
+ ].flatten
100
+ end
101
+
102
+ def run_librarian_puppet
103
+ if File.file? "#{config.repodir}/Puppetfile"
104
+ librarian = "#{config.repodir}/bin/librarian-puppet"
105
+
106
+ unless config.enterprise?
107
+ ENV["GITHUB_API_TOKEN"] = config.token
108
+ end
109
+
110
+ librarian_command = [librarian, "install", "--path=#{config.repodir}/shared"]
111
+ librarian_command << "--verbose" if debug?
112
+
113
+ warn librarian_command.join(" ") if debug?
114
+ unless system(*librarian_command)
115
+ abort "Can't run Puppet, fetching dependencies with librarian failed."
116
+ end
117
+ end
118
+ end
119
+
120
+ def create_clean_working_environment
121
+ FileUtils.mkdir_p config.puppetdir
122
+
123
+ FileUtils.rm_f config.logfile
124
+
125
+ FileUtils.rm_rf "#{config.puppetdir}/var/reports" if report?
126
+
127
+ FileUtils.mkdir_p File.dirname config.logfile
128
+ FileUtils.touch config.logfile
129
+ end
130
+
131
+ def hiera_config
132
+ hiera_yaml = "#{config.repodir}/config/hiera.yaml"
133
+
134
+ File.exists?(hiera_yaml) ? hiera_yaml : "/dev/null"
135
+ end
136
+
137
+ def puppet_flags
138
+ _flags = []
139
+ root = File.expand_path "../../..", __FILE__
140
+
141
+ _flags << ["--group", "admin"]
142
+ _flags << ["--confdir", "#{config.puppetdir}/conf"]
143
+ _flags << ["--vardir", "#{config.puppetdir}/var"]
144
+ _flags << ["--libdir", "#{config.repodir}/lib"]#:#{root}/lib"]
145
+ _flags << ["--libdir", "#{root}/lib"]
146
+ _flags << ["--manifestdir", "#{config.repodir}/manifests"]
147
+ _flags << ["--modulepath", "#{config.repodir}/modules:#{config.repodir}/shared"]
148
+ _flags << ["--hiera_config", hiera_config]
149
+ _flags << ["--logdest", "#{config.repodir}/log/puppet.log"]
150
+ _flags << ["--logdest", "console"]
151
+ _flags << ["--pluginfactdest", "#{config.homedir}/facts.d"]
152
+
153
+ _flags << "--no-report" unless report?
154
+ _flags << "--detailed-exitcodes"
155
+
156
+ _flags << "--show_diff"
157
+
158
+ if profile?
159
+ _flags << "--evaltrace"
160
+ _flags << "--summarize"
161
+ end
162
+
163
+ _flags << "--parser=future" if future_parser?
164
+ _flags << "--graph" if graph?
165
+
166
+ _flags << "--debug" if debug?
167
+ _flags << "--noop" if noop
168
+
169
+ _flags << "--color=false" if no_color?
170
+
171
+ _flags.flatten
172
+ end
173
+ end
174
+
175
+ class Boxen::Command::Run::Noop < Boxen::Command::Run
176
+ preflight \
177
+ Boxen::Preflight::OS,
178
+ Boxen::Preflight::Directories,
179
+ Boxen::Preflight::EtcMyCnf,
180
+ Boxen::Preflight::Homebrew,
181
+ Boxen::Preflight::Rbenv,
182
+ Boxen::Preflight::RVM,
183
+ Boxen::Preflight::Offline,
184
+ Boxen::Preflight::Creds,
185
+ Boxen::Preflight::Identity,
186
+ Boxen::Preflight::Update,
187
+ Boxen::Preflight::Facts
188
+
189
+ postflight \
190
+ Boxen::Postflight::Active,
191
+ Boxen::Postflight::Env
192
+
193
+ def noop
194
+ true
195
+ end
196
+ end
197
+
198
+ Boxen::Command.register :run, Boxen::Command::Run
199
+ Boxen::Command.register :"run:noop", Boxen::Command::Run::Noop
@@ -0,0 +1,61 @@
1
+ require "boxen/command"
2
+ require "boxen/service"
3
+
4
+ class Boxen::Command::Service < Boxen::Command
5
+ def self.detailed_help
6
+ <<-EOS
7
+
8
+ boxen service
9
+
10
+ Display all services Boxen knows about.
11
+
12
+ boxen service:enable <service1> [<service2> ...]
13
+
14
+ Enable and start a Boxen-managed service. If none are given,
15
+ enables and starts all Boxen-managed services.
16
+
17
+ boxen service:disable <service1> [<service2> ...]
18
+
19
+ Disable and stop a Boxen-managed service. If none are given,
20
+ disables and stops all Boxen-managed services.
21
+
22
+ boxen service:restart [<service1> <service2> ...]
23
+
24
+ Restart a Boxen-managed service. If none are given, restarts all
25
+ Boxen-managed services.
26
+
27
+ NOTE: 'boxen service' is aliased to 'services' for convenience
28
+
29
+ EOS
30
+ end
31
+
32
+ def self.help
33
+ "Show and manage Boxen services."
34
+ end
35
+
36
+ def run
37
+ @args = [] # we don't care about args here
38
+
39
+ puts "Boxen manages the following services:\n\n"
40
+
41
+ services.each do |service|
42
+ puts " #{service.name}"
43
+ end
44
+
45
+ Boxen::CommandStatus.new(0)
46
+ end
47
+
48
+ def services
49
+ @services ||= if @args.any?
50
+ @args.map { |s| Boxen::Service.new(s) }
51
+ else
52
+ Boxen::Service.list
53
+ end
54
+ end
55
+ end
56
+
57
+ require "boxen/command/service/enable"
58
+ require "boxen/command/service/disable"
59
+ require "boxen/command/service/restart"
60
+
61
+ Boxen::Command.register :service, Boxen::Command::Service, :services
@@ -0,0 +1,15 @@
1
+ require "boxen/command/service"
2
+
3
+ class Boxen::Command::Service::Disable < Boxen::Command::Service
4
+ def run
5
+ services.each do |service|
6
+ puts "Disabling service: #{service.name}"
7
+ service.disable
8
+ end
9
+
10
+ Boxen::CommandStatus.new(0)
11
+ end
12
+ end
13
+
14
+ Boxen::Command.register :"service:disable", Boxen::Command::Service::Disable
15
+ Boxen::Command.register :"services:disable", Boxen::Command::Service::Disable
@@ -0,0 +1,15 @@
1
+ require "boxen/command/service"
2
+
3
+ class Boxen::Command::Service::Enable < Boxen::Command::Service
4
+ def run
5
+ services.each do |service|
6
+ puts "Enabling service: #{service.name}"
7
+ service.enable
8
+ end
9
+
10
+ Boxen::CommandStatus.new(0)
11
+ end
12
+ end
13
+
14
+ Boxen::Command.register :"service:enable", Boxen::Command::Service::Enable
15
+ Boxen::Command.register :"services:enable", Boxen::Command::Service::Enable
@@ -0,0 +1,24 @@
1
+ require "boxen/command/service"
2
+
3
+ class Boxen::Command::Service::Restart < Boxen::Command::Service
4
+ def run
5
+ services.each do |service|
6
+ puts "Restarting service: #{service.name}"
7
+ service.disable
8
+ service.enable
9
+ end
10
+
11
+ Boxen::CommandStatus.new(0)
12
+ end
13
+
14
+ def services
15
+ @services ||= if @args.any?
16
+ @args.map { |s| Boxen::Service.new(s) }
17
+ else
18
+ Boxen::Service.list_enabled
19
+ end
20
+ end
21
+ end
22
+
23
+ Boxen::Command.register :"service:restart", Boxen::Command::Service::Restart
24
+ Boxen::Command.register :"services:restart", Boxen::Command::Service::Restart
@@ -0,0 +1,29 @@
1
+ require "boxen/command"
2
+ require "boxen/version"
3
+
4
+ class Boxen::Command::Version < Boxen::Command
5
+ def self.help
6
+ "Displays the current version of Boxen"
7
+ end
8
+
9
+ def self.detailed_help
10
+ <<-EOS
11
+
12
+ boxen version
13
+
14
+ Display the current version of the Boxen gem.
15
+
16
+ EOS
17
+ end
18
+
19
+ def run
20
+ puts "Boxen #{version}"
21
+ Boxen::CommandStatus.new(0)
22
+ end
23
+
24
+ def version
25
+ Boxen::VERSION
26
+ end
27
+ end
28
+
29
+ Boxen::Command.register :version, Boxen::Command::Version, :"-v", :"--version"
@@ -0,0 +1,15 @@
1
+ module Boxen
2
+ class CommandStatus
3
+ attr_reader :code, :successful_on
4
+
5
+ def initialize(code, successful_on = [0])
6
+ @successful_on = successful_on
7
+ @code = code
8
+ end
9
+
10
+ def success?
11
+ successful_on.member? code
12
+ end
13
+
14
+ end
15
+ end
@@ -25,9 +25,6 @@ module Boxen
25
25
  end
26
26
  end
27
27
 
28
- keychain = Boxen::Keychain.new config.user
29
- config.token = keychain.token
30
-
31
28
  if config.enterprise?
32
29
  # configure to talk to GitHub Enterprise
33
30
  Octokit.configure do |c|
@@ -80,10 +77,19 @@ module Boxen
80
77
  def initialize(&block)
81
78
  @fde = true
82
79
  @pull = true
80
+ @debug = false
83
81
 
84
82
  yield self if block_given?
85
83
  end
86
84
 
85
+ def keychain
86
+ @keychain ||= Boxen::Keychain.new self.user
87
+ end
88
+
89
+ def token
90
+ @token ||= keychain.token
91
+ end
92
+
87
93
  # Create an API instance using the current user creds. A new
88
94
  # instance is created any time `token` changes.
89
95
 
@@ -94,11 +100,32 @@ module Boxen
94
100
  # Spew a bunch of debug logging? Default is `false`.
95
101
 
96
102
  def debug?
103
+ # TODO: fix this
97
104
  !!@debug
98
105
  end
99
106
 
100
107
  attr_writer :debug
101
108
 
109
+ def offline?
110
+ @offline ||= false
111
+ end
112
+
113
+ attr_writer :offline
114
+
115
+ def report?
116
+ # TODO: Actually make this a thing
117
+ @report = false
118
+ end
119
+
120
+ attr_writer :report
121
+
122
+ def profile?
123
+ #TODO: Actually make this a thing
124
+ @profile = true
125
+ end
126
+
127
+ attr_writer :profile
128
+
102
129
  # A GitHub user's public email.
103
130
 
104
131
  attr_accessor :email
@@ -122,7 +149,7 @@ module Boxen
122
149
  # `BOXEN_HOME` environment variable.
123
150
 
124
151
  def homedir
125
- @homedir || ENV["BOXEN_HOME"] || "/opt/boxen"
152
+ @homedir ||= (ENV["BOXEN_HOME"] || "/opt/boxen")
126
153
  end
127
154
 
128
155
  attr_writer :homedir
@@ -132,7 +159,7 @@ module Boxen
132
159
  # overwritten on every run.
133
160
 
134
161
  def logfile
135
- @logfile || ENV["BOXEN_LOG_FILE"] || "#{repodir}/log/boxen.log"
162
+ @logfile ||= (ENV["BOXEN_LOG_FILE"] || "#{repodir}/log/boxen.log")
136
163
  end
137
164
 
138
165
  attr_writer :logfile
@@ -145,46 +172,6 @@ module Boxen
145
172
 
146
173
  attr_accessor :name
147
174
 
148
- # Just go through the motions? Default is `false`.
149
-
150
- def pretend?
151
- !!@pretend
152
- end
153
-
154
- attr_writer :pretend
155
-
156
- # Run a profiler on Puppet? Default is `false`.
157
-
158
- def profile?
159
- !!@profile
160
- end
161
-
162
- attr_writer :profile
163
-
164
- # Enable the Puppet future parser? Default is `false`.
165
-
166
- def future_parser?
167
- !!@future_parser
168
- end
169
-
170
- attr_writer :future_parser
171
-
172
- # Enable puppet reports ? Default is `false`.
173
-
174
- def report?
175
- !!@report
176
- end
177
-
178
- attr_writer :report
179
-
180
- # Enable generation of dependency graphs.
181
-
182
- def graph?
183
- !!@graph
184
- end
185
-
186
- attr_writer :graph
187
-
188
175
  # An Array of Boxen::Project entries, one for each project Boxen
189
176
  # knows how to manage.
190
177
  #
@@ -208,7 +195,7 @@ module Boxen
208
195
  # `BOXEN_PUPPET_DIR` environment variable.
209
196
 
210
197
  def puppetdir
211
- @puppetdir || ENV["BOXEN_PUPPET_DIR"] || "/tmp/boxen/puppet"
198
+ @puppetdir ||= (ENV["BOXEN_PUPPET_DIR"] || "/tmp/boxen/puppet")
212
199
  end
213
200
 
214
201
  attr_writer :puppetdir
@@ -217,7 +204,7 @@ module Boxen
217
204
  # `Dir.pwd`. Respects the `BOXEN_REPO_DIR` environment variable.
218
205
 
219
206
  def repodir
220
- @repodir || ENV["BOXEN_REPO_DIR"] || Dir.pwd
207
+ @repodir ||= (ENV["BOXEN_REPO_DIR"] || Dir.pwd)
221
208
  end
222
209
 
223
210
  attr_writer :repodir
@@ -228,7 +215,7 @@ module Boxen
228
215
  # Respects the `BOXEN_REPO_NAME` environment variable.
229
216
 
230
217
  def reponame
231
- override = @reponame || ENV["BOXEN_REPO_NAME"]
218
+ override = @reponame ||= ENV["BOXEN_REPO_NAME"]
232
219
  return override unless override.nil?
233
220
 
234
221
  if File.directory? repodir
@@ -238,7 +225,7 @@ module Boxen
238
225
  # find the path and strip off the .git suffix
239
226
  repo_exp = Regexp.new Regexp.escape(ghuri.host) + "[/:]([^/]+/[^/]+)"
240
227
  if $?.success? && repo_exp.match(url)
241
- @reponame = $1.sub /\.git$/, ""
228
+ @reponame = $1.sub(/\.git$/, "")
242
229
  end
243
230
  end
244
231
  end
@@ -248,7 +235,7 @@ module Boxen
248
235
  # GitHub location (public or GitHub Enterprise)
249
236
 
250
237
  def ghurl
251
- @ghurl || ENV["BOXEN_GITHUB_ENTERPRISE_URL"] || "https://github.com"
238
+ @ghurl ||= (ENV["BOXEN_GITHUB_ENTERPRISE_URL"] || "https://github.com")
252
239
  end
253
240
 
254
241
  attr_writer :ghurl
@@ -256,8 +243,7 @@ module Boxen
256
243
  # Repository URL template (required for GitHub Enterprise)
257
244
 
258
245
  def repotemplate
259
- default = 'https://github.com/%s'
260
- @repotemplate || ENV["BOXEN_REPO_URL_TEMPLATE"] || default
246
+ @repotemplate ||= (ENV["BOXEN_REPO_URL_TEMPLATE"] || "https://github.com/%s")
261
247
  end
262
248
 
263
249
  attr_writer :repotemplate
@@ -272,7 +258,7 @@ module Boxen
272
258
  # `"/Users/#{user}/src"`.
273
259
 
274
260
  def srcdir
275
- @srcdir || ENV["BOXEN_SRC_DIR"] || "/Users/#{user}/src"
261
+ @srcdir ||= (ENV["BOXEN_SRC_DIR"] || "/Users/#{user}/src")
276
262
  end
277
263
 
278
264
  attr_writer :srcdir
@@ -281,15 +267,11 @@ module Boxen
281
267
  # Respects the `BOXEN_NO_ISSUE` environment variable.
282
268
 
283
269
  def stealth?
284
- !!ENV["BOXEN_NO_ISSUE"] || @stealth
270
+ @stealth ||= !!ENV["BOXEN_NO_ISSUE"]
285
271
  end
286
272
 
287
273
  attr_writer :stealth
288
274
 
289
- # A GitHub OAuth token. Default is `nil`.
290
-
291
- attr_reader :token
292
-
293
275
  def token=(token)
294
276
  @token = token
295
277
  @api = nil
@@ -298,7 +280,7 @@ module Boxen
298
280
  # A local user login. Default is the `USER` environment variable.
299
281
 
300
282
  def user
301
- @user || ENV["USER"]
283
+ @user ||= ENV["USER"]
302
284
  end
303
285
 
304
286
  attr_writer :user
@@ -313,7 +295,7 @@ module Boxen
313
295
  # Respects the `BOXEN_S3_HOST` environment variable.
314
296
 
315
297
  def s3host
316
- @s3host || ENV["BOXEN_S3_HOST"] || "s3.amazonaws.com"
298
+ @s3host ||= (ENV["BOXEN_S3_HOST"] || "s3.amazonaws.com")
317
299
  end
318
300
 
319
301
  attr_writer :s3host
@@ -322,7 +304,7 @@ module Boxen
322
304
  # Respects the `BOXEN_S3_BUCKET` environment variable.
323
305
 
324
306
  def s3bucket
325
- @s3bucket || ENV["BOXEN_S3_BUCKET"] || "boxen-downloads"
307
+ @s3bucket ||= (ENV["BOXEN_S3_BUCKET"] || "boxen-downloads")
326
308
  end
327
309
 
328
310
  attr_writer :s3bucket