boxen23 3.1.3a

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +20 -0
  6. data/README.md +50 -0
  7. data/boxen.gemspec +29 -0
  8. data/lib/boxen/check.rb +72 -0
  9. data/lib/boxen/checkout.rb +25 -0
  10. data/lib/boxen/cli.rb +61 -0
  11. data/lib/boxen/config.rb +345 -0
  12. data/lib/boxen/error.rb +4 -0
  13. data/lib/boxen/flags.rb +291 -0
  14. data/lib/boxen/hook.rb +47 -0
  15. data/lib/boxen/hook/github_issue.rb +120 -0
  16. data/lib/boxen/hook/web.rb +56 -0
  17. data/lib/boxen/keychain.rb +61 -0
  18. data/lib/boxen/postflight.rb +13 -0
  19. data/lib/boxen/postflight/active.rb +16 -0
  20. data/lib/boxen/postflight/env.rb +29 -0
  21. data/lib/boxen/preflight.rb +13 -0
  22. data/lib/boxen/preflight/creds.rb +149 -0
  23. data/lib/boxen/preflight/directories.rb +32 -0
  24. data/lib/boxen/preflight/etc_my_cnf.rb +12 -0
  25. data/lib/boxen/preflight/identity.rb +16 -0
  26. data/lib/boxen/preflight/os.rb +33 -0
  27. data/lib/boxen/preflight/rbenv.rb +12 -0
  28. data/lib/boxen/preflight/rvm.rb +12 -0
  29. data/lib/boxen/project.rb +20 -0
  30. data/lib/boxen/puppeteer.rb +122 -0
  31. data/lib/boxen/runner.rb +149 -0
  32. data/lib/boxen/service.rb +58 -0
  33. data/lib/boxen/util.rb +19 -0
  34. data/lib/facter/boxen.rb +34 -0
  35. data/script/Boxen +0 -0
  36. data/script/bootstrap +7 -0
  37. data/script/build-keychain-helper +6 -0
  38. data/script/release +38 -0
  39. data/script/tests +10 -0
  40. data/src/keychain-helper.c +85 -0
  41. data/test/boxen/test.rb +7 -0
  42. data/test/boxen_check_test.rb +55 -0
  43. data/test/boxen_checkout_test.rb +42 -0
  44. data/test/boxen_cli_test.rb +39 -0
  45. data/test/boxen_config_test.rb +400 -0
  46. data/test/boxen_directories_test.rb +40 -0
  47. data/test/boxen_flags_test.rb +223 -0
  48. data/test/boxen_hook_github_issue_test.rb +294 -0
  49. data/test/boxen_hook_web_test.rb +58 -0
  50. data/test/boxen_keychain_test.rb +21 -0
  51. data/test/boxen_postflight_active_test.rb +29 -0
  52. data/test/boxen_postflight_env_test.rb +6 -0
  53. data/test/boxen_preflight_creds_test.rb +177 -0
  54. data/test/boxen_preflight_etc_my_cnf_test.rb +10 -0
  55. data/test/boxen_preflight_rvm_test.rb +10 -0
  56. data/test/boxen_project_test.rb +14 -0
  57. data/test/boxen_puppeteer_test.rb +101 -0
  58. data/test/boxen_runner_test.rb +171 -0
  59. data/test/boxen_service_test.rb +39 -0
  60. data/test/boxen_util_test.rb +21 -0
  61. data/test/fixtures/repo/modules/projects/manifests/first-project.pp +0 -0
  62. data/test/fixtures/repo/modules/projects/manifests/second-project.pp +0 -0
  63. metadata +257 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6bb9a890428b37ca3a51cd2d89c0338f9c499f437eca543fdb397ce78a402541
4
+ data.tar.gz: 4ef6d21ec943420af24918e68c9743920b84354f24f2441065b2d186a994d51c
5
+ SHA512:
6
+ metadata.gz: 629e944de0253d6bb554d22b58c8714088c13b2ea1c938e07a348f0cc56e811483c3e5892bbdb6b0a9c12db0c1cd95bf4f2cc3a8ed844fff9b0a4c4678d2c7ab
7
+ data.tar.gz: eb5ab9e2ad44962ef06a0823cfff10f52573e52aa751267754b3b4021b21e501bcb06712f3debf165c28e2617b4f253eb2cd8198413e345caaffc39477c88f29
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle
2
+ /.env.local.rb
3
+ /.projects
4
+ /.rbenv-version
5
+ /Gemfile.lock
6
+ /bin
7
+ /boxen-*.gem
8
+ /log
9
+ /puppet
10
+ /script/Boxen.dSYM
11
+ /.ruby-version
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ script: "./script/tests"
3
+ gemfile: "this/does/not/exist"
4
+ rvm:
5
+ - 2.0.0
6
+ - 2.3.3
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
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
@@ -0,0 +1,50 @@
1
+ # Boxen [![Build Status](https://travis-ci.org/boxen/boxen.png?branch=master)](https://travis-ci.org/boxen/boxen)
2
+
3
+ Manage Mac development boxes with love (and Puppet).
4
+
5
+ ## Rules for Services
6
+
7
+ 0. Run on a nonstandard port, usually default port + 1000 or 10000.
8
+
9
+ 0. Install with a custom Boxen homebrew formula.
10
+
11
+ 0. Suffix the Homebrew package's version, starting with `-boxen1`.
12
+
13
+ 0. Run as a launchd service in the `dev` namespace, e.g.,
14
+ `dev.dnsmasq`.
15
+
16
+ 0. Store config, data, and log files in
17
+ `$BOXEN_HOME/{config,data,log}`. This will normally require
18
+ customization of a service's Homebrew formula.
19
+
20
+ Sometimes it's not possible to follow these rules, but try hard.
21
+
22
+ ## Projects from the CLI
23
+
24
+ We use a totally awful hack to do from-the-cli project installs of projects.
25
+ We create a file in "$BOXEN_HOME/repodir" called .projects, with a single line.
26
+ That line is made up of projects separated by commas.
27
+ We then read that into a Puppet fact in Puppet-land, and that checks for
28
+ classes that match those project names, and includes them in the catalog.
29
+
30
+ We can't pass a `FACTER_` env var because sudo has `env_reset`
31
+ and we can't just modify the sudoers file due to a chicken-egg problem.
32
+
33
+ ## Hooks
34
+
35
+ 0. All hooks must be in the namespace `Boxen::Hook::MyThing`.
36
+
37
+ 0. All hooks must subclass from `Boxen::Hook`
38
+
39
+ 0. All hooks must provide a private instance method `required_environment_variables` that returns an array with at least one entry.
40
+
41
+ 0. All hooks must provide a private instance method `#call`.
42
+
43
+ ## Contributing
44
+
45
+ Use the OS X system Ruby (2.0 or newer). Run `script/tests` often. Open PR's.
46
+ Use the CI.
47
+
48
+ ## Halp!
49
+
50
+ Use Issues or #boxen on irc.freenode.net.
data/boxen.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "boxen23"
5
+ gem.version = "3.1.3a"
6
+ # Thanks go out to the previous maintainers John Barnette, Will
7
+ # Farrington, David Goodlad and Mike McQuaid for getting this project
8
+ # to where it is today.
9
+ gem.authors = ["Jacob Bednarz"]
10
+ gem.email = ["jacob.bednarz@gmail.com"]
11
+ gem.description = "Manage Mac development boxes with love (and Puppet). for Ruby 2.3"
12
+ gem.summary = "You know, for laptops and stuff."
13
+ gem.homepage = "https://github.com/boxen/boxen"
14
+ gem.license = 'MIT'
15
+
16
+ gem.files = `git ls-files`.split $/
17
+ gem.test_files = gem.files.grep /^test/
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "ansi", "~> 1.5"
21
+ gem.add_dependency "hiera", "~> 3.4.2"
22
+ gem.add_dependency "highline", "~> 1.7.8"
23
+ gem.add_dependency "json_pure", [">= 1.8.3", "< 2.0"]
24
+ gem.add_dependency "octokit", ["~> 3.8", ">= 3.8.0"]
25
+ gem.add_dependency "puppet", "~> 4.10"
26
+
27
+ gem.add_development_dependency "minitest", "4.4.0" # pinned for mocha
28
+ gem.add_development_dependency "mocha", ["~> 1.2", "< 1.2.1"]
29
+ end
@@ -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
@@ -0,0 +1,25 @@
1
+ module Boxen
2
+ class Checkout
3
+ attr_reader :config
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ end
8
+
9
+ def sha
10
+ Dir.chdir(config.repodir) { `git rev-parse HEAD`.strip }
11
+ end
12
+
13
+ def master?
14
+ Dir.chdir(config.repodir) { `git symbolic-ref HEAD`.strip == 'refs/heads/master' }
15
+ end
16
+
17
+ def dirty?
18
+ !changes.empty?
19
+ end
20
+
21
+ def changes
22
+ Dir.chdir(config.repodir) { `git status --porcelain`.strip }
23
+ end
24
+ end
25
+ end
data/lib/boxen/cli.rb ADDED
@@ -0,0 +1,61 @@
1
+ require "boxen/config"
2
+ require "boxen/flags"
3
+ require "boxen/postflight"
4
+ require "boxen/preflight"
5
+ require "boxen/runner"
6
+ require "boxen/util"
7
+
8
+ module Boxen
9
+ class CLI
10
+ attr_reader :config
11
+ attr_reader :flags
12
+ attr_reader :runner
13
+
14
+ def initialize(config, flags)
15
+ @config = config
16
+ @flags = flags
17
+ @runner = Boxen::Runner.new(@config, @flags)
18
+ end
19
+
20
+ def run
21
+ if flags.help?
22
+ puts flags
23
+ exit
24
+ end
25
+
26
+ runner.run
27
+ end
28
+
29
+ # Run Boxen by wiring together the command-line flags, config,
30
+ # preflights, Puppet execution, and postflights. Returns Puppet's
31
+ # exit code.
32
+
33
+ def self.run(*args)
34
+ config = Boxen::Config.load
35
+ flags = Boxen::Flags.new args
36
+
37
+ # Apply command-line flags to the config in case we're changing or
38
+ # overriding anything.
39
+ flags.apply config
40
+
41
+ if flags.run?
42
+ # Run the preflight checks.
43
+ Boxen::Preflight.run config
44
+
45
+ # Save the config for Puppet (and next time).
46
+ Boxen::Config.save config
47
+ end
48
+
49
+ # Make the magic happen.
50
+ status = Boxen::CLI.new(config, flags).run
51
+
52
+ if flags.run?
53
+ # Run the postflight checks.
54
+ Boxen::Postflight.run config if status.success?
55
+ end
56
+
57
+ # Return Puppet's exit status.
58
+ return status.code
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,345 @@
1
+ require "boxen/keychain"
2
+ require "boxen/project"
3
+ require "boxen/util"
4
+ require "fileutils"
5
+ require "json"
6
+ require "octokit"
7
+ require "shellwords"
8
+
9
+ module Boxen
10
+
11
+ # All configuration for Boxen, whether it's loaded from command-line
12
+ # args, environment variables, config files, or the keychain.
13
+
14
+ class Config
15
+ def self.load(&block)
16
+ new do |config|
17
+ file = "#{config.homedir}/config/boxen/defaults.json"
18
+
19
+ if File.file? file
20
+ attrs = JSON.parse File.read file
21
+
22
+ attrs.each do |key, value|
23
+ if !value.nil? && config.respond_to?(selector = "#{key}=")
24
+ config.send selector, value
25
+ end
26
+ end
27
+ end
28
+
29
+ if Boxen::Util.osx?
30
+ keychain = Boxen::Keychain.new config.user
31
+ config.token = keychain.token
32
+ else
33
+ config.token = ''
34
+ end
35
+
36
+ if config.enterprise?
37
+ # configure to talk to GitHub Enterprise
38
+ Octokit.configure do |c|
39
+ c.api_endpoint = "#{config.ghurl}/api/v3"
40
+ c.web_endpoint = config.ghurl
41
+ end
42
+ end
43
+
44
+ yield config if block_given?
45
+ end
46
+ end
47
+
48
+ # Save `config`. Returns `config`. Note that this only saves data,
49
+ # not flags. For example, `login` will be saved, but `stealth?`
50
+ # won't.
51
+
52
+ def self.save(config)
53
+ attrs = {
54
+ :email => config.email,
55
+ :fde => config.fde?,
56
+ :homedir => config.homedir,
57
+ :login => config.login,
58
+ :name => config.name,
59
+ :puppetdir => config.puppetdir,
60
+ :repodir => config.repodir,
61
+ :reponame => config.reponame,
62
+ :ghurl => config.ghurl,
63
+ :srcdir => config.srcdir,
64
+ :user => config.user,
65
+ :repotemplate => config.repotemplate,
66
+ :s3host => config.s3host,
67
+ :s3bucket => config.s3bucket
68
+ }
69
+
70
+ file = "#{config.homedir}/config/boxen/defaults.json"
71
+ FileUtils.mkdir_p File.dirname file
72
+
73
+ File.open file, "wb" do |f|
74
+ f.write JSON.generate Hash[attrs.reject { |k, v| v.nil? }]
75
+ end
76
+
77
+ if Boxen::Util.osx?
78
+ keychain = Boxen::Keychain.new config.user
79
+ keychain.token = config.token
80
+ end
81
+
82
+ config
83
+ end
84
+
85
+ # Create a new instance. Yields `self` if `block` is given.
86
+
87
+ def initialize(&block)
88
+ @fde = true
89
+ @pull = true
90
+
91
+ yield self if block_given?
92
+ end
93
+
94
+ # Create an API instance using the current user creds. A new
95
+ # instance is created any time `token` changes.
96
+
97
+ def api
98
+ @api ||= Octokit::Client.new :login => token, :password => 'x-oauth-basic'
99
+ end
100
+
101
+ # Spew a bunch of debug logging? Default is `false`.
102
+
103
+ def debug?
104
+ !!@debug
105
+ end
106
+
107
+ attr_writer :debug
108
+
109
+ # A GitHub user's public email.
110
+
111
+ attr_accessor :email
112
+
113
+ # The shell script that loads Boxen's environment.
114
+
115
+ def envfile
116
+ "#{homedir}/env.sh"
117
+ end
118
+
119
+ # Is full disk encryption required? Default is `true`. Respects
120
+ # the `BOXEN_NO_FDE` environment variable.
121
+
122
+ def fde?
123
+ !ENV["BOXEN_NO_FDE"] && @fde
124
+ end
125
+
126
+ attr_writer :fde
127
+
128
+ # Boxen's home directory. Default is `"/opt/boxen"`. Respects the
129
+ # `BOXEN_HOME` environment variable.
130
+
131
+ def homedir
132
+ @homedir || ENV["BOXEN_HOME"] || "/opt/boxen"
133
+ end
134
+
135
+ attr_writer :homedir
136
+
137
+ # Boxen's log file. Default is `"#{repodir}/log/boxen.log"`.
138
+ # Respects the `BOXEN_LOG_FILE` environment variable. The log is
139
+ # overwritten on every run.
140
+
141
+ def logfile
142
+ @logfile || ENV["BOXEN_LOG_FILE"] || "#{repodir}/log/boxen.log"
143
+ end
144
+
145
+ attr_writer :logfile
146
+
147
+ # A GitHub user login. Default is `nil`.
148
+
149
+ attr_accessor :login
150
+
151
+ # A GitHub user's profile name.
152
+
153
+ attr_accessor :name
154
+
155
+ # Just go through the motions? Default is `false`.
156
+
157
+ def pretend?
158
+ !!@pretend
159
+ end
160
+
161
+ attr_writer :pretend
162
+
163
+ # Run a profiler on Puppet? Default is `false`.
164
+
165
+ def profile?
166
+ !!@profile
167
+ end
168
+
169
+ attr_writer :profile
170
+
171
+ # Enable the Puppet future parser? Default is `false`.
172
+
173
+ def future_parser?
174
+ !!@future_parser
175
+ end
176
+
177
+ attr_writer :future_parser
178
+
179
+ # Skip installing librarian-puppet
180
+
181
+ def skip_puppetfile?
182
+ !!@skip_puppetfile
183
+ end
184
+
185
+ attr_writer :skip_puppetfile
186
+
187
+ # Enable puppet reports ? Default is `false`.
188
+
189
+ def report?
190
+ !!@report
191
+ end
192
+
193
+ attr_writer :report
194
+
195
+ # Enable generation of dependency graphs.
196
+
197
+ def graph?
198
+ !!@graph
199
+ end
200
+
201
+ attr_writer :graph
202
+
203
+ # An Array of Boxen::Project entries, one for each project Boxen
204
+ # knows how to manage.
205
+ #
206
+ # FIX: Revisit this once we restructure template projects. It's
207
+ # broken for several reasons: It assumes paths that won't be
208
+ # right, and it assumes projects live in the same repo as this
209
+ # file.
210
+
211
+ def projects
212
+ files = Dir["#{repodir}/modules/projects/manifests/*.pp"]
213
+ names = files.map { |m| File.basename m, ".pp" }.sort
214
+
215
+ names.map do |name|
216
+ Boxen::Project.new "#{srcdir}/#{name}"
217
+ end
218
+ end
219
+
220
+ # The directory where Puppet expects configuration (which we don't
221
+ # use) and runtime information (which we generally don't care
222
+ # about). Default is `/tmp/boxen/puppet`. Respects the
223
+ # `BOXEN_PUPPET_DIR` environment variable.
224
+
225
+ def puppetdir
226
+ @puppetdir || ENV["BOXEN_PUPPET_DIR"] || "/tmp/boxen/puppet"
227
+ end
228
+
229
+ attr_writer :puppetdir
230
+
231
+ # The directory of the custom Boxen repo for an org. Default is
232
+ # `Dir.pwd`. Respects the `BOXEN_REPO_DIR` environment variable.
233
+
234
+ def repodir
235
+ @repodir || ENV["BOXEN_REPO_DIR"] || Dir.pwd
236
+ end
237
+
238
+ attr_writer :repodir
239
+
240
+ # The repo on GitHub to use for error reports and automatic
241
+ # updates, in `owner/repo` format. Default is the `origin` of a
242
+ # Git repo in `repodir`, if it exists and points at GitHub.
243
+ # Respects the `BOXEN_REPO_NAME` environment variable.
244
+
245
+ def reponame
246
+ override = @reponame || ENV["BOXEN_REPO_NAME"]
247
+ return override unless override.nil?
248
+
249
+ if File.directory? repodir
250
+ ghuri = URI(ghurl)
251
+ url = Dir.chdir(repodir) { `git config remote.origin.url`.strip }
252
+
253
+ # find the path and strip off the .git suffix
254
+ repo_exp = Regexp.new Regexp.escape(ghuri.host) + "[/:]([^/]+/[^/]+)"
255
+ if $?.success? && repo_exp.match(url)
256
+ @reponame = $1.sub /\.git$/, ""
257
+ end
258
+ end
259
+ end
260
+
261
+ attr_writer :reponame
262
+
263
+ # GitHub location (public or GitHub Enterprise)
264
+
265
+ def ghurl
266
+ @ghurl || ENV["BOXEN_GITHUB_ENTERPRISE_URL"] || "https://github.com"
267
+ end
268
+
269
+ attr_writer :ghurl
270
+
271
+ # Repository URL template (required for GitHub Enterprise)
272
+
273
+ def repotemplate
274
+ default = 'https://github.com/%s'
275
+ @repotemplate || ENV["BOXEN_REPO_URL_TEMPLATE"] || default
276
+ end
277
+
278
+ attr_writer :repotemplate
279
+
280
+ # Does this Boxen use a GitHub Enterprise instance?
281
+
282
+ def enterprise?
283
+ ghurl != "https://github.com"
284
+ end
285
+
286
+ # The directory where repos live. Default is
287
+ # `"/Users/#{user}/src"`.
288
+
289
+ def srcdir
290
+ @srcdir || ENV["BOXEN_SRC_DIR"] || "/Users/#{user}/src"
291
+ end
292
+
293
+ attr_writer :srcdir
294
+
295
+ # Don't auto-create issues on failure? Default is `false`.
296
+ # Respects the `BOXEN_NO_ISSUE` environment variable.
297
+
298
+ def stealth?
299
+ !!ENV["BOXEN_NO_ISSUE"] || @stealth
300
+ end
301
+
302
+ attr_writer :stealth
303
+
304
+ # A GitHub OAuth token. Default is `nil`.
305
+
306
+ attr_reader :token
307
+
308
+ def token=(token)
309
+ @token = token
310
+ @api = nil
311
+ end
312
+
313
+ # A local user login. Default is the `USER` environment variable.
314
+
315
+ def user
316
+ @user || ENV["USER"]
317
+ end
318
+
319
+ attr_writer :user
320
+
321
+ def color?
322
+ @color
323
+ end
324
+
325
+ attr_writer :color
326
+
327
+ # The S3 host name. Default is `"s3.amazonaws.com"`.
328
+ # Respects the `BOXEN_S3_HOST` environment variable.
329
+
330
+ def s3host
331
+ @s3host || ENV["BOXEN_S3_HOST"] || "s3.amazonaws.com"
332
+ end
333
+
334
+ attr_writer :s3host
335
+
336
+ # The S3 bucket name. Default is `"boxen-downloads"`.
337
+ # Respects the `BOXEN_S3_BUCKET` environment variable.
338
+
339
+ def s3bucket
340
+ @s3bucket || ENV["BOXEN_S3_BUCKET"] || "boxen-downloads"
341
+ end
342
+
343
+ attr_writer :s3bucket
344
+ end
345
+ end