boxen-linux 2.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -0
  3. data/.travis.yml +7 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +20 -0
  6. data/README.md +50 -0
  7. data/boxen.gemspec +26 -0
  8. data/lib/boxen/check.rb +72 -0
  9. data/lib/boxen/checkout.rb +25 -0
  10. data/lib/boxen/cli.rb +63 -0
  11. data/lib/boxen/config.rb +330 -0
  12. data/lib/boxen/error.rb +4 -0
  13. data/lib/boxen/flags.rb +272 -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 +63 -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 +34 -0
  21. data/lib/boxen/preflight.rb +13 -0
  22. data/lib/boxen/preflight/creds.rb +108 -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/homebrew.rb +13 -0
  26. data/lib/boxen/preflight/identity.rb +16 -0
  27. data/lib/boxen/preflight/os.rb +33 -0
  28. data/lib/boxen/preflight/rbenv.rb +12 -0
  29. data/lib/boxen/preflight/rvm.rb +12 -0
  30. data/lib/boxen/project.rb +20 -0
  31. data/lib/boxen/puppeteer.rb +122 -0
  32. data/lib/boxen/runner.rb +149 -0
  33. data/lib/boxen/service.rb +58 -0
  34. data/lib/boxen/util.rb +17 -0
  35. data/lib/facter/boxen.rb +34 -0
  36. data/lib/system_timer.rb +13 -0
  37. data/script/Boxen +0 -0
  38. data/script/Boxen-linux +0 -0
  39. data/script/bootstrap +7 -0
  40. data/script/build-keychain-helper +6 -0
  41. data/script/build-keyring-helper +9 -0
  42. data/script/release +38 -0
  43. data/script/tests +10 -0
  44. data/src/keychain-helper.c +85 -0
  45. data/src/keyring-helper.c +86 -0
  46. data/test/boxen/test.rb +7 -0
  47. data/test/boxen_check_test.rb +55 -0
  48. data/test/boxen_checkout_test.rb +42 -0
  49. data/test/boxen_cli_test.rb +39 -0
  50. data/test/boxen_config_test.rb +393 -0
  51. data/test/boxen_directories_test.rb +40 -0
  52. data/test/boxen_flags_test.rb +217 -0
  53. data/test/boxen_hook_github_issue_test.rb +294 -0
  54. data/test/boxen_hook_web_test.rb +58 -0
  55. data/test/boxen_keychain_test.rb +24 -0
  56. data/test/boxen_postflight_active_test.rb +29 -0
  57. data/test/boxen_postflight_env_test.rb +6 -0
  58. data/test/boxen_preflight_creds_test.rb +80 -0
  59. data/test/boxen_preflight_etc_my_cnf_test.rb +10 -0
  60. data/test/boxen_preflight_homebrew_test.rb +10 -0
  61. data/test/boxen_preflight_rvm_test.rb +10 -0
  62. data/test/boxen_project_test.rb +14 -0
  63. data/test/boxen_puppeteer_test.rb +101 -0
  64. data/test/boxen_runner_test.rb +171 -0
  65. data/test/boxen_service_test.rb +39 -0
  66. data/test/boxen_util_test.rb +21 -0
  67. data/test/fixtures/repo/modules/projects/manifests/first-project.pp +0 -0
  68. data/test/fixtures/repo/modules/projects/manifests/second-project.pp +0 -0
  69. data/test/system_timer.rb +10 -0
  70. metadata +279 -0
@@ -0,0 +1,63 @@
1
+ require "shellwords"
2
+
3
+ module Boxen
4
+ class Keychain
5
+
6
+ # The keychain proxy we use to provide isolation and a friendly
7
+ # message in security prompts.
8
+ if (/darwin/ =~ RUBY_PLATFORM) != nil
9
+ HELPER = File.expand_path "../../../script/Boxen", __FILE__
10
+ else
11
+ HELPER = File.expand_path "../../../script/Boxen-linux", __FILE__
12
+ end
13
+
14
+ # The service name to use when loading/saving passwords.
15
+
16
+ PASSWORD_SERVICE = "GitHub Password"
17
+
18
+ # The service name to use when loading/saving API keys.
19
+
20
+ TOKEN_SERVICE = "GitHub API Token"
21
+
22
+ def initialize(login)
23
+ @login = login
24
+ # Clear the password. We're storing tokens now.
25
+ set PASSWORD_SERVICE, ""
26
+ end
27
+
28
+ def token
29
+ get TOKEN_SERVICE
30
+ end
31
+
32
+ def token=(token)
33
+ set TOKEN_SERVICE, token
34
+ end
35
+
36
+ protected
37
+
38
+ attr_reader :login
39
+
40
+ def get(service)
41
+ print 'get', HELPER, service, login
42
+ cmd = shellescape(HELPER, service, login)
43
+
44
+ result = `#{cmd}`.strip
45
+ $?.success? ? result : nil
46
+ end
47
+
48
+ def set(service, token)
49
+ print 'set', HELPER, service, login, token
50
+ cmd = shellescape(HELPER, service, login, token)
51
+
52
+ unless system *cmd
53
+ raise Boxen::Error, "Can't save #{service} in the keychain."
54
+ end
55
+
56
+ token
57
+ end
58
+
59
+ def shellescape(*args)
60
+ args.map { |s| Shellwords.shellescape s }.join " "
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ require "boxen/check"
2
+
3
+ module Boxen
4
+
5
+ # The superclass for postflight checks.
6
+
7
+ class Postflight < Boxen::Check
8
+
9
+ # Load all available postflight checks.
10
+
11
+ register File.expand_path("../postflight", __FILE__)
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require "boxen/postflight"
2
+ require "boxen/util"
3
+
4
+ # Checks to see if the basic environment is loaded.
5
+
6
+ class Boxen::Postflight::Active < Boxen::Postflight
7
+ def ok?
8
+ Boxen::Util.active?
9
+ end
10
+
11
+ def run
12
+ warn "You haven't loaded Boxen's environment yet!",
13
+ "To permanently fix this, source #{config.envfile} at the end",
14
+ "of your shell's startup file."
15
+ end
16
+ end
@@ -0,0 +1,34 @@
1
+ require "boxen/postflight"
2
+
3
+ class Boxen::Postflight::Env < Boxen::Postflight
4
+
5
+ # Calculate an MD5 checksum for the current environment.
6
+
7
+ def self.checksum
8
+
9
+ # We can't get this from config 'cause it's static (gotta happen
10
+ # on load), and BOXEN_HOME might not be set.
11
+
12
+ home = ENV["BOXEN_HOME"] || "/opt/boxen"
13
+ return unless File.file? "#{home}/env.sh"
14
+ if RUBY_PLATFORM =~ /Darwin/
15
+ `find #{home}/env* -type f 2>&1 | sort | xargs /sbin/md5 | /sbin/md5 -q`.strip
16
+ elsif RUBY_PLATFORM =~ /Debian/
17
+ #TODO: figure out linux equivalent of above darwin line. maybe:
18
+ `find #{home}/env* -type f 2>&1 | sort | xargs /usr/bin/md5sum | /usr/bin/md5sum --quiet`.strip
19
+ end
20
+
21
+ end
22
+
23
+ # The checksum when this file was loaded.
24
+
25
+ CHECKSUM = self.checksum
26
+
27
+ def ok?
28
+ self.class.checksum == CHECKSUM
29
+ end
30
+
31
+ def run
32
+ warn "Source #{config.envfile} or restart your shell for new stuff!"
33
+ end
34
+ end
@@ -0,0 +1,13 @@
1
+ require "boxen/check"
2
+
3
+ module Boxen
4
+
5
+ # The superclass for preflight checks.
6
+
7
+ class Preflight < Boxen::Check
8
+
9
+ # Load all available preflight checks.
10
+
11
+ register File.expand_path("../preflight", __FILE__)
12
+ end
13
+ end
@@ -0,0 +1,108 @@
1
+ require "boxen/preflight"
2
+ require "highline"
3
+ require "octokit"
4
+
5
+ # HACK: Unless this is `false`, HighLine has some really bizarre
6
+ # problems with empty/expended streams at bizarre intervals.
7
+
8
+ HighLine.track_eof = false
9
+
10
+ class Boxen::Preflight::Creds < Boxen::Preflight
11
+ attr :otp
12
+ attr :password
13
+
14
+ def ok?
15
+ if config.token && config.api.user
16
+ # There was a period of time when login wasn't geting set on first run.
17
+ # This should correct that.
18
+ config.login = config.api.user.login
19
+ true
20
+ end
21
+ rescue
22
+ nil
23
+ end
24
+
25
+ def tmp_api
26
+ @tmp_api ||= Octokit::Client.new :login => config.login, :password => password, :auto_paginate => true
27
+ end
28
+
29
+ def headers
30
+ otp.nil? ? {} : {"X-GitHub-OTP" => otp}
31
+ end
32
+
33
+ def get_otp
34
+ console = HighLine.new
35
+
36
+ # junk API call to send OTP until we implement PUT
37
+ tmp_api.create_authorization rescue nil
38
+
39
+ @otp = console.ask "One time password (via SMS or device):" do |q|
40
+ q.echo = '*'
41
+ end
42
+ end
43
+
44
+ # Attempt to use the username+password to get a list of the user's OAuth
45
+ # authorizations from the API. If it fails because of 2FA, ask the user for
46
+ # her OTP and try again.
47
+ #
48
+ # Returns a list of authorizations
49
+ def get_tokens
50
+ begin
51
+ tmp_api.authorizations(:headers => headers)
52
+ rescue Octokit::Unauthorized
53
+ abort "Sorry, I can't auth you on GitHub.",
54
+ "Please check your credentials and teams and give it another try."
55
+ rescue Octokit::OneTimePasswordRequired
56
+ puts
57
+ if otp.nil?
58
+ warn "It looks like you have two-factor auth enabled."
59
+ else
60
+ warn "That one time password didn't work. Let's try again."
61
+ end
62
+ get_otp
63
+ get_tokens
64
+ end
65
+ end
66
+
67
+ def run
68
+ fetch_login_and_password
69
+ tokens = get_tokens
70
+
71
+ unless auth = tokens.detect { |a| a.note == "Boxen" }
72
+ auth = tmp_api.create_authorization \
73
+ :note => "Boxen",
74
+ :scopes => %w(repo user),
75
+ :headers => headers
76
+ end
77
+
78
+ config.token = auth.token
79
+
80
+ unless ok?
81
+ puts
82
+ abort "Something went terribly wrong.",
83
+ "I was able to get your OAuth token, but was unable to use it."
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ def fetch_login_and_password
90
+ console = HighLine.new
91
+
92
+ config.login = fetch_from_env("login") || console.ask("GitHub login: ") do |q|
93
+ q.default = config.login || config.user
94
+ q.validate = /\A[^@]+\Z/
95
+ end
96
+
97
+ @password = fetch_from_env("password") || console.ask("GitHub password: ") do |q|
98
+ q.echo = "*"
99
+ end
100
+ end
101
+
102
+ def fetch_from_env(thing)
103
+ key = "BOXEN_GITHUB_#{thing.upcase}"
104
+ return unless found = ENV[key]
105
+ warn "Oh, looks like you've provided your #{thing} as environmental variable..."
106
+ found
107
+ end
108
+ end
@@ -0,0 +1,32 @@
1
+ require "boxen/preflight"
2
+ require "boxen/util"
3
+
4
+ class Boxen::Preflight::Directories < Boxen::Preflight
5
+ def ok?
6
+ homedir_directory_exists? &&
7
+ homedir_owner == config.user &&
8
+ homedir_group == 'staff'
9
+ end
10
+
11
+ def run
12
+ Boxen::Util.sudo("/bin/mkdir", "-p", config.homedir) &&
13
+ Boxen::Util.sudo("/usr/sbin/chown", "#{config.user}:staff", config.homedir)
14
+ end
15
+
16
+ private
17
+ def homedir_directory_exists?
18
+ File.directory?(config.homedir)
19
+ end
20
+
21
+ def homedir_owner
22
+ Etc.getpwuid(homedir_stat.uid).name
23
+ end
24
+
25
+ def homedir_group
26
+ Etc.getgrgid(homedir_stat.gid).name
27
+ end
28
+
29
+ def homedir_stat
30
+ @homedir_stat ||= File.stat(config.homedir)
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ require "boxen/preflight"
2
+
3
+ class Boxen::Preflight::EtcMyCnf < Boxen::Preflight
4
+ def run
5
+ abort "You have an /etc/my.cnf file.",
6
+ "This will confuse Boxen's MySQL a lot. Please remove it."
7
+ end
8
+
9
+ def ok?
10
+ !File.file? "/etc/my.cnf"
11
+ end
12
+ 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,16 @@
1
+ require "boxen/preflight"
2
+
3
+ class Boxen::Preflight::Identity < Boxen::Preflight
4
+ def ok?
5
+ !user || (config.email && config.name)
6
+ end
7
+
8
+ def run
9
+ config.email = user.email
10
+ config.name = user.name
11
+ end
12
+
13
+ def user
14
+ @user ||= config.api.user rescue nil
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ require "boxen/preflight"
2
+
3
+ class Boxen::Preflight::OS < Boxen::Preflight
4
+ SUPPORTED_RELEASES = %w(10.8 10.9)
5
+
6
+ def ok?
7
+ (linux? || (osx? && supported_release?))
8
+ end
9
+
10
+ def run
11
+ abort "You must be running one of the following OS X versions: #{SUPPORTED_RELEASES.join(' ')}."
12
+ end
13
+
14
+ private
15
+
16
+ def osx?
17
+ `uname -s`.chomp == "Darwin"
18
+ end
19
+
20
+ def linux?
21
+ `uname -s`.chomp == "Linux"
22
+ end
23
+
24
+ def supported_release?
25
+ SUPPORTED_RELEASES.any? do |r|
26
+ current_release.start_with? r
27
+ end
28
+ end
29
+
30
+ def current_release
31
+ @current_release ||= `sw_vers -productVersion`
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ require "boxen/preflight"
2
+
3
+ class Boxen::Preflight::Rbenv < Boxen::Preflight
4
+ def run
5
+ warn "You have an existing rbenv installed in ~/.rbenv.",
6
+ "Boxen provides its own rbenv, so consider deleting yours."
7
+ end
8
+
9
+ def ok?
10
+ !File.exist? "#{ENV['HOME']}/.rbenv"
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ require "boxen/preflight"
2
+
3
+ class Boxen::Preflight::RVM < Boxen::Preflight
4
+ def run
5
+ abort "You have an rvm installed in ~/.rvm.",
6
+ "Boxen uses rbenv to install ruby, so please `rvm implode`"
7
+ end
8
+
9
+ def ok?
10
+ !File.exist? "#{ENV['HOME']}/.rvm"
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ module Boxen
2
+
3
+ # A project managed by Boxen.
4
+
5
+ class Project
6
+
7
+ # The directory where this project's repo should live.
8
+
9
+ attr_reader :dir
10
+
11
+ # The name of this project.
12
+
13
+ attr_reader :name
14
+
15
+ def initialize(dir)
16
+ @dir = dir
17
+ @name = File.basename @dir
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,122 @@
1
+ require "fileutils"
2
+ require "boxen/util"
3
+
4
+ module Boxen
5
+
6
+ # Manages an invocation of puppet.
7
+
8
+ class Puppeteer
9
+
10
+ class Status < Struct.new(:code)
11
+ # Puppet's detailed exit codes reserves 2 for a successful run with changes
12
+ def success?
13
+ [0,2].include?(code)
14
+ end
15
+ end
16
+
17
+ attr_reader :config
18
+
19
+ def initialize(config)
20
+ @config = config
21
+ end
22
+
23
+ def command
24
+ manifest = "#{config.repodir}/manifests/site.pp"
25
+ puppet = "#{config.repodir}/bin/puppet"
26
+
27
+ [puppet, "apply", flags, manifest].flatten
28
+ end
29
+
30
+ def hiera_config
31
+ if File.exist? "#{config.repodir}/config/hiera.yaml"
32
+ "#{config.repodir}/config/hiera.yaml"
33
+ else
34
+ "/dev/null"
35
+ end
36
+ end
37
+
38
+ def flags
39
+ flags = []
40
+ root = File.expand_path "../../..", __FILE__
41
+
42
+ flags << ["--group", "admin"]
43
+ flags << ["--confdir", "#{config.puppetdir}/conf"]
44
+ flags << ["--vardir", "#{config.puppetdir}/var"]
45
+ flags << ["--libdir", "#{config.repodir}/lib"]#:#{root}/lib"]
46
+ flags << ["--libdir", "#{root}/lib"]
47
+ flags << ["--manifestdir", "#{config.repodir}/manifests"]
48
+ flags << ["--modulepath", "#{config.repodir}/modules:#{config.repodir}/shared"]
49
+
50
+ # Don't ever complain about Hiera to me
51
+ flags << ["--hiera_config", hiera_config]
52
+
53
+ # Log to both the console and a file.
54
+
55
+ flags << ["--logdest", config.logfile]
56
+ flags << ["--logdest", "console"]
57
+
58
+ # For some reason Puppet tries to set up a bunch of rrd stuff
59
+ # (user, group) unless reports are completely disabled.
60
+
61
+ flags << "--no-report" unless config.report?
62
+ flags << "--detailed-exitcodes"
63
+
64
+ flags << "--graph" if config.graph?
65
+
66
+ flags << "--show_diff"
67
+
68
+ if config.profile?
69
+ flags << "--evaltrace"
70
+ flags << "--summarize"
71
+ end
72
+
73
+ if config.future_parser?
74
+ flags << "--parser=future"
75
+ end
76
+
77
+ flags << "--debug" if config.debug?
78
+ flags << "--noop" if config.pretend?
79
+
80
+ flags << "--color=false" unless config.color?
81
+
82
+ flags.flatten
83
+ end
84
+
85
+ def run
86
+ FileUtils.mkdir_p config.puppetdir
87
+
88
+ FileUtils.rm_f config.logfile
89
+
90
+ FileUtils.rm_rf "#{config.puppetdir}/var/reports" if config.report?
91
+
92
+ FileUtils.rm_rf "#{config.puppetdir}/var/state/graphs" if config.graph?
93
+
94
+ FileUtils.mkdir_p File.dirname config.logfile
95
+ FileUtils.touch config.logfile
96
+
97
+ if File.file? "Puppetfile"
98
+ librarian = "#{config.repodir}/bin/librarian-puppet"
99
+
100
+ unless config.enterprise?
101
+ # Set an environment variable for librarian-puppet's
102
+ # github_tarball source strategy.
103
+ ENV["GITHUB_API_TOKEN"] = config.token
104
+ end
105
+
106
+ librarian_command = [librarian, "install", "--path=#{config.repodir}/shared"]
107
+ librarian_command << "--verbose" if config.debug?
108
+
109
+ warn librarian_command.join(" ") if config.debug?
110
+ unless system *librarian_command
111
+ abort "Can't run Puppet, fetching dependencies with librarian failed."
112
+ end
113
+ end
114
+
115
+ warn command.join(" ") if config.debug?
116
+
117
+ Boxen::Util.sudo *command
118
+
119
+ Status.new($?.exitstatus)
120
+ end
121
+ end
122
+ end