Add a model for config

This commit is contained in:
John Barnette
2012-10-02 09:39:44 -07:00
parent 8d6454559c
commit 6f6cf25d49
5 changed files with 404 additions and 0 deletions

View File

@@ -2,14 +2,31 @@ PATH
remote: .
specs:
boxen (0.0.0)
json_pure
octokit
GEM
remote: http://rubygems.org/
specs:
addressable (2.3.2)
faraday (0.8.4)
multipart-post (~> 1.1)
faraday_middleware (0.8.8)
faraday (>= 0.7.4, < 0.9)
hashie (1.2.0)
json_pure (1.7.5)
metaclass (0.0.1)
minitest (3.5.0)
mocha (0.12.6)
metaclass (~> 0.0.1)
multi_json (1.3.6)
multipart-post (1.1.5)
octokit (1.15.1)
addressable (~> 2.2)
faraday (~> 0.8)
faraday_middleware (~> 0.8)
hashie (~> 1.2)
multi_json (~> 1.3)
PLATFORMS
ruby

View File

@@ -13,6 +13,9 @@ Gem::Specification.new do |gem|
gem.test_files = gem.files.grep /^test/
gem.require_paths = ["lib"]
gem.add_dependency "json_pure"
gem.add_dependency "octokit"
gem.add_development_dependency "minitest", "3.5.0"
gem.add_development_dependency "mocha"
end

235
lib/boxen/config.rb Normal file
View File

@@ -0,0 +1,235 @@
require "fileutils"
require "json"
require "octokit"
require "boxen/project"
module Boxen
# All configuration for Boxen, whether it's loaded from command-line
# args, environment variables, config files, or the keychain.
class Config
# The service name to use when loading/saving config in the Keychain.
KEYCHAIN_SERVICE = "Boxen"
# Load config. Yields config if `block` is given.
def self.load(&block)
new do |config|
home = ENV["GH_HOME"] || "/opt/boxen"
file = "#{home}/config/boxen/defaults.json"
if File.file? file
attrs = JSON.parse File.read file
attrs.each do |key, value|
if value && config.respond_to?(selector = "#{key}=")
config.send selector, value
end
end
end
cmd = "security find-generic-password " +
"-a #{config.user} -s '#{KEYCHAIN_SERVICE}' -w 2>/dev/null"
password = `#{cmd}`.strip
password = nil unless $?.success?
config.password = password
yield config if block_given?
end
end
# Save `config`. Returns `config`. Note that this only saves data,
# not flags. For example, `login` will be saved, but `stealth?`
# won't.
def self.save(config)
attrs = {
:email => config.email,
:homedir => config.homedir,
:login => config.login,
:name => config.name,
:srcdir => config.srcdir,
:user => config.user
}
file = "#{config.homedir}/config/boxen/defaults.json"
FileUtils.mkdir_p File.dirname file
File.open file, "wb" do |f|
f.write JSON.generate Hash[attrs.reject { |k, v| v.nil? }]
end
cmd = ["security", "add-generic-password",
"-a", config.user, "-s", KEYCHAIN_SERVICE, "-U", "-w", config.password]
unless system *cmd
raise Boxen::Error, "Can't save config in the Keychain."
end
config
end
# Create a new instance. Yields `self` if `block` is given.
def initialize(&block)
@fde = true
@pull = true
yield self if block_given?
end
# Create an API instance using the current user creds. A new
# instance is created any time `login` or `password` change.
def api
@api ||= Octokit::Client.new :login => login, :password => password
end
# Spew a bunch of debug logging? Default is `false`.
def debug?
!!@debug
end
attr_writer :debug
# A GitHub user's public email.
attr_accessor :email
# The shell script that loads Boxen's environment.
def envfile
"#{homedir}/env.sh"
end
# Is full disk encryption required? Default is `true`. Respects
# the `BOXEN_NO_FDE` environment variable.
def fde?
!ENV["BOXEN_NO_FDE"] && @fde
end
attr_writer :fde
# Boxen's home directory. Default is `"/opt/boxen"`. Respects the
# `BOXEN_HOME` environment variable.
def homedir
@homedir || ENV["BOXEN_HOME"] || "/opt/boxen"
end
attr_writer :homedir
# Boxen's log file. Default is `"/tmp/boxen.log"`. Respects the
# `BOXEN_LOG_FILE` environment variable.
def logfile
@logfile || ENV["BOXEN_LOG_FILE"] || "/tmp/boxen.log"
end
attr_writer :logfile
# A GitHub user login. Default is `nil`.
attr_reader :login
def login=(login)
@api = nil
@login = login
end
# Is Boxen running on the `master` branch?
def master?
`git symbolic-ref HEAD`.chomp == "refs/heads/master"
end
# A GitHub user's profile name.
attr_accessor :name
# A GitHub user password. Default is `nil`.
attr_reader :password
def password=(password)
@api = nil
@password = password
end
# Just go through the motions? Default is `false`.
def pretend?
!!@pretend
end
attr_writer :pretend
# Run a profiler on Puppet? Default is `false`.
def profile?
!!@profile
end
attr_writer :profile
# Dirty tree?
def dirty?
changes.empty?
end
def changes
`git status --porcelain`.strip
end
# An Array of Boxen::Project entries, one for each project Boxen
# knows how to manage.
#
# FIX: Revisit this once we restructure template projects. It's
# broken for several reasons: It assumes paths that won't be
# right, and it assumes projects live in the same repo as this
# file.
def projects
root = File.expand_path "../../..", __FILE__
files = Dir["modules/github/manifests/projects/*.pp"]
names = (files.map { |m| File.basename m, ".pp" } - %w(all)).sort
names.map do |name|
Boxen::Project.new "#{srcdir}/#{name}"
end
end
# The directory where repos live. Default is
# `"/Users/#{user}/src"`.
def srcdir
@srcdir || "/Users/#{user}/src"
end
attr_writer :srcdir
# Don't auto-create issues on failure? Default is `false`.
# Respects the `BOXEN_NO_ISSUE` environment variable.
def stealth?
!!ENV["BOXEN_NO_ISSUE"] || @stealth
end
attr_writer :stealth
# A local user login. Default is the `USER` environment variable.
def user
@user || ENV["USER"]
end
attr_writer :user
end
end

139
test/boxen_config_test.rb Normal file
View File

@@ -0,0 +1,139 @@
require "boxen/test"
require "boxen/config"
class BoxenConfigTest < Boxen::Test
def setup
@config = Boxen::Config.new
end
def test_debug?
refute @config.debug?
@config.debug = true
assert @config.debug?
end
def test_email
assert_nil @config.email
@config.email = "foo"
assert_equal "foo", @config.email
end
def test_fde?
assert @config.fde?
@config.fde = false
refute @config.fde?
end
def test_fde_env_var
ENV.expects(:[]).with("BOXEN_NO_FDE").returns "1"
refute @config.fde?
end
def test_homedir
assert_equal "/opt/boxen", @config.homedir
@config.homedir = "foo"
assert_equal "foo", @config.homedir
end
def test_homedir_env_var_boxen_home
ENV.expects(:[]).with("BOXEN_HOME").returns "foo"
assert_equal "foo", @config.homedir
end
def test_initialize
config = Boxen::Config.new do |c|
c.homedir = "foo"
end
assert_equal "foo", config.homedir
end
def test_logfile
assert_equal "/tmp/boxen.log", @config.logfile
@config.logfile = "foo"
assert_equal "foo", @config.logfile
end
def test_logfile_env_var
ENV.expects(:[]).with("BOXEN_LOG_FILE").returns "foo"
assert_equal "foo", @config.logfile
end
def test_login
assert_nil @config.login
@config.login = "foo"
assert_equal "foo", @config.login
end
def test_name
assert_nil @config.name
@config.name = "foo"
assert_equal "foo", @config.name
end
def test_password
assert_nil @config.password
@config.password = "foo"
assert_equal "foo", @config.password
end
def test_pretend?
refute @config.pretend?
@config.pretend = true
assert @config.pretend?
end
def test_profile?
refute @config.profile?
@config.profile = true
assert @config.profile?
end
def test_projects
root = File.expand_path "../..", __FILE__
files = Dir["#{root}/modules/github/manifests/projects/*.pp"]
# all.pp is autogenerated
files.reject! { |f| File.basename(f) == "all.pp" }
assert_equal files.size, @config.projects.size
end
def test_srcdir
@config.expects(:user).returns "foo"
assert_equal "/Users/foo/src", @config.srcdir
@config.srcdir = "elsewhere"
assert_equal "elsewhere", @config.srcdir
end
def test_stealth?
refute @config.stealth?
@config.stealth = true
assert @config.stealth?
end
def test_stealth_env_var
ENV.expects(:[]).with("BOXEN_NO_ISSUE").returns "1"
assert @config.stealth?
end
def test_user
ENV.expects(:[]).with("USER").returns "foo"
assert_equal "foo", @config.user
@config.user = "bar"
assert_equal "bar", @config.user
end
end

10
test/system_timer.rb Normal file
View File

@@ -0,0 +1,10 @@
# THIS IS SUCH HAX. Faraday helpfully reminds you to install
# `system_timer` if you're running Ruby 1.8, since Timeout can give
# unreliable results. We can't do this during first-time runs, since
# there's no C compiler available.
#
# To squash the message and stop confusing people, this shim just
# exposes Timeout as SystemTimer. I'm a bad person.
require "timeout"
SystemTimer = Timeout