diff --git a/.editorconfig b/.editorconfig index 79ac465..94d365b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# This file is managed by chef, changes will be overwritten +# This file is managed by infrastructure, changes will be overwritten root = true diff --git a/.gemspec b/.gemspec deleted file mode 100644 index f55b751..0000000 --- a/.gemspec +++ /dev/null @@ -1,16 +0,0 @@ -require 'pathname' - -Gem::Specification.new do |s| - s.name = 'infrastructure' - s.version = File.read(Pathname.new(__FILE__).parent + 'version').strip - s.summary = 'infrastructure' - s.authors = ['The Way Development Team'] - s.add_runtime_dependency 'berkshelf' - s.add_runtime_dependency 'chefspec' - s.add_runtime_dependency 'colorize' - s.add_runtime_dependency 'foodcritic' - s.add_runtime_dependency 'rake' - s.add_runtime_dependency 'rspec' - s.add_runtime_dependency 'rubocop' - s.add_runtime_dependency 'serverspec' -end diff --git a/.gitignore b/.gitignore index 72a5b91..7930476 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,4 @@ .vagrant -# Bundler -.bundle/* -bin/* -Gemfile.lock - -# Berks -Berksfile.lock - # Sublime Text *.sublime-workspace diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..afbc6f8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/bats"] + path = vendor/bats + url = https://github.com/sstephenson/bats.git diff --git a/.rspec b/.rspec deleted file mode 100644 index 114486d..0000000 --- a/.rspec +++ /dev/null @@ -1 +0,0 @@ ---color --format documentation diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index 617d0ea..0000000 --- a/.rubocop.yml +++ /dev/null @@ -1,5 +0,0 @@ -# This file is managed by chef, changes will be overwritten - -# Max line length is 90 -Metrics/LineLength: - Max: 90 diff --git a/.travis.yml b/.travis.yml index c025572..5384f95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,8 @@ sudo: true dist: trusty -language: ruby -addons: - apt: - sources: - - chef-current-trusty - packages: - - chefdk -before_install: -- | - eval "$(/opt/chefdk/bin/chef shell-init bash)" - which ruby - ruby --version - which bundle - bundle --version - which gem - gem --version +language: c +install: bash scripts/provisioning/travis +script: make travis deploy: provider: releases api_key: diff --git a/Berksfile b/Berksfile deleted file mode 100644 index 7e71817..0000000 --- a/Berksfile +++ /dev/null @@ -1,3 +0,0 @@ -source 'https://supermarket.chef.io' -metadata -cookbook 'apt', '~> 2.9.2' diff --git a/Gemfile b/Gemfile deleted file mode 100644 index bc13464..0000000 --- a/Gemfile +++ /dev/null @@ -1,3 +0,0 @@ -source 'https://rubygems.org' -gemspec -gem 'travis' diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..bf696c5 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +SHELL = /bin/bash + +default: tests + +travis: tests + +tests: integration + +integration: + vendor/bats/bin/bats test/integration diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 3a89459..0000000 --- a/Rakefile +++ /dev/null @@ -1,6 +0,0 @@ -desc 'Run all linting and tests' -task :default - -# Grab everything in the tasks directory -Dir['vendor/infrastructure/tasks/**/*.rb'].sort.each { |file| require_relative file } -Dir['tasks/**/*.rb'].sort.each { |file| require_relative file } diff --git a/Vagrantfile b/Vagrantfile index a7c10ff..ef02e3e 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -1,6 +1,3 @@ -require 'ostruct' -require 'pathname' - Vagrant.configure('2') do |config| if Vagrant.has_plugin?('vagrant-cachier') config.cache.scope = :box # http://fgrehm.viewdocs.io/vagrant-cachier/usage/ @@ -13,6 +10,7 @@ Vagrant.configure('2') do |config| %w(vmware_fusion vmware_workstation).each do |vmware| config.vm.provider vmware do |provider, override| provider.name = 'infrastructure' + provider.vmx['tools.syncTime'] = true override.vm.box = 'netsensia/ubuntu-trusty64' end end @@ -25,20 +23,13 @@ Vagrant.configure('2') do |config| config.vm.provision :file, source: '~/.gitconfig', destination: '.gitconfig' end - config.vm.provision :shell, inline: %( -which chef || wget -qO - https://www.chef.io/chef/install.sh | bash -s -- -P chefdk -berks vendor -b /vagrant/Berksfile /tmp/vagrant-chef/cookbooks -) - - config.vm.provision :chef_solo do |chef_solo| - chef_solo.install = false - chef_solo.binary_path = '/opt/chefdk/bin' - chef_solo.add_recipe 'infrastructure::vagrant' - end - - # If Vagrantfile.custom exists, read and eval it in this context - vagrant_custom = Pathname.new(__FILE__).parent + 'Vagrantfile.custom' - if File.exist?(vagrant_custom) - OpenStruct.new(config: config).instance_eval File.read(vagrant_custom) + config.vm.provision :shell do |shell| + shell.privileged = false + shell.keep_color = true + shell.path = 'scripts/provisioning/vagrant' + shell.env = { + PROJECT_DIR: '/vagrant', + PROJECT_NAME: 'infrastructure' + } end end diff --git a/attributes/default.rb b/attributes/default.rb deleted file mode 100644 index d6c17b4..0000000 --- a/attributes/default.rb +++ /dev/null @@ -1,4 +0,0 @@ -# Where the project will be installed to -default['infrastructure']['project_name'] = node['hostname'] -default['infrastructure']['project_root'] = '/vagrant' -default['infrastructure']['project_user'] = 'vagrant' diff --git a/chefignore b/chefignore deleted file mode 100644 index c92322f..0000000 --- a/chefignore +++ /dev/null @@ -1,106 +0,0 @@ -# Put files/directories that should be ignored in this file when uploading -# or sharing to the community site. -# Lines that start with '# ' are comments. - -# Git Submodules # -################## -vendor/* - -# OS generated files # -###################### -.DS_Store -Icon? -nohup.out -ehthumbs.db -Thumbs.db - -# SASS # -######## -.sass-cache - -# EDITORS # -########### -\#* -.#* -*~ -*.sw[a-z] -*.bak -REVISION -TAGS* -tmtags -*_flymake.* -*_flymake -*.tmproj -.project -.settings -mkmf.log -*.sublime-project -*.sublime-workspace - -## COMPILED ## -############## -a.out -*.o -*.pyc -*.so -*.com -*.class -*.dll -*.exe -*/rdoc/ - -# Testing # -########### -.watchr -.rspec -spec/* -spec/fixtures/* -test/* -features/* -examples/* -Guardfile -Procfile -test/* -spec/* - -# SCM # -####### -.git -*/.git -.gitignore -.gitmodules -.gitconfig -.gitattributes -.svn -*/.bzr/* -*/.hg/* -*/.svn/* - -# Berkshelf # -############# -Berksfile -Berksfile.lock -cookbooks/* -tmp - -# Cookbooks # -############# -CONTRIBUTING -CHANGELOG* - -# Strainer # -############ -Colanderfile -Strainerfile -.colander -.strainer - -# Vagrant # -########### -.vagrant -Vagrantfile - -# Travis # -########## -.travis.yml -.travis/* diff --git a/install b/install index 12a4327..ed042e3 100755 --- a/install +++ b/install @@ -3,8 +3,8 @@ # Bootstrap a new Way repo. Run this in a new repo directory on the OS host. # This script can be run without vagrant or tags for testing purposes, but -# DO NOT RUN IT OUTSIDE OF VAGRANT OR TRAVIS IN THIS WAY as it will fire up a sudo'd -# chef client. +# DO NOT RUN IT OUTSIDE OF VAGRANT OR TRAVIS IN THIS WAY as it will fire up the sudo'd +# provisioning process # To enable test mode, set the TEST environment variable and run it by name: # TEST=true ~/infrastructure/install @@ -13,12 +13,14 @@ # By default it is removed on exit, but for debugging it can be kept by setting KEEP: # TEST=true KEEP=true ~/infrastructure/install -# Fail on error -set -e +set -e -u -o pipefail + +TEST="${TEST:-}" +KEEP="${KEEP:-}" # A little color can really spruce up that dreary log spam function green { - if [[ -z "$NO_COLOR" ]] + if [ -z "${NO_COLOR:-}" ] then echo -e "\033[1;32m$@\033[0m" else @@ -27,7 +29,7 @@ function green { } function red { - if [[ -z "$NO_COLOR" ]] + if [ -z "${NO_COLOR:-}" ] then echo -e "\033[1;31m$@\033[0m" else @@ -41,10 +43,10 @@ function error { false } -# We need git and either a vagrant or a chefdk for test +# We need git and either a vagrant or a ruby for test which git >/dev/null || error 'git is not available' -if [[ -z "$TEST" ]] +if [ -z "$TEST" ] then which vagrant >/dev/null || error 'vagrant is not available' else @@ -52,21 +54,21 @@ else fi # A directory to hold generated files. An inherited value is used by the exec trampoline -if [[ -z "$INSTALL_WORK_DIR" ]] +if [ -z "${INSTALL_WORK_DIR:-}" ] then export INSTALL_WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}"/infrastructure-install-XXXX) fi # Keep the original location of this script for testing -if [[ -z "$ORIGINAL_INSTALL_LOCATION" ]] && [[ -n "$TEST" ]] +if [ -z "${ORIGINAL_INSTALL_LOCATION:-}" ] && [ -n "$TEST" ] then export ORIGINAL_INSTALL_LOCATION="${BASH_SOURCE[0]}" fi -if [[ -z "$KEEP" ]] +if [ -z "$KEEP" ] then # Clean it up on exit - if [[ -z "$TEST" ]] + if [ -z "$TEST" ] then trap "rm -rf \"$INSTALL_WORK_DIR\"" EXIT else @@ -75,7 +77,8 @@ then fi # The generated project name is taken from the current directory -export PROJECT_NAME=$(basename $(pwd)) +export PROJECT_DIR=$(pwd) +export PROJECT_NAME=$(basename "$PROJECT_DIR") # Run a heredoc as ruby. # @@ -91,25 +94,25 @@ function ruby_stdin { # This consumes the function's stdin cat > "$ruby_path" - if [[ -z "$TEST" ]] + if [ -z "$TEST" ] then # Point vagrant at it and run VAGRANT_CWD="$INSTALL_WORK_DIR" VAGRANT_VAGRANTFILE="$ruby_file" vagrant status else - # Use the ruby in chefdk + # Use the local ruby ruby "$ruby_path" fi } # Fetch the latest release (or this one for testing), unless we already have -if [[ "$0" != "$INSTALL_WORK_DIR/install" ]] +if [ "$0" != "$INSTALL_WORK_DIR/install" ] then ruby_stdin < e warn "Error: Could not bootstrap vagrant (#{e})" exit 1 @@ -219,47 +231,12 @@ else end RUBY -if [[ -z "$TEST" ]] +if [ -z "$TEST" ] then # Fire up the real vagrant to generate the rest vagrant up --no-color else - chef_solo_dir="$INSTALL_WORK_DIR/chef-solo" - mkdir -p "$chef_solo_dir"/{cache,backup,cookbooks} - berks vendor -b Berksfile "$chef_solo_dir/cookbooks" - - cat > "$chef_solo_dir/config.rb" < "$chef_solo_dir/attributes.json" <&2 echo "$@" + false +} + +# Render and write a file to the named path from a template. +# Files are only written if they do not exist or if CLOBBER is set. +function render { + local path + local template_path + + path="$1" + shift 1 + + if test -f "templates/$path" + then + template_path="templates/$path" + elif test -f "$INFRASTRUCTURE/templates/$path" + then + template_path="$INFRASTRUCTURE/templates/$path" + else + die "Can't find template for $path" + fi + + if test "${CLOBBER:-}" == "" && test -f "$path" + then + return 0 + fi + + sed "s|@PROJECT_NAME@|$PROJECT_NAME|g" < "$template_path" > "$path" + + return $? +} + +mkdir -p scripts/provisioning +render scripts/provisioning/common +render scripts/provisioning/vagrant +render .editorconfig +render .gitignore +render .sublime-project +render Makefile +render README.md +render Vagrantfile +render version diff --git a/scripts/provisioning/client-vagrant b/scripts/provisioning/client-vagrant new file mode 100644 index 0000000..b57e500 --- /dev/null +++ b/scripts/provisioning/client-vagrant @@ -0,0 +1,63 @@ +#! /bin/bash + +set -e -u -o pipefail +set -x + +sudo apt-get update + +if [ -z "${TEST:-}" ] +then + # Only upgrade if we're not under test. It's expensive and brittle in CI. + sudo apt-get upgrade -y +fi + +sudo apt-get install -y software-properties-common + +sudo add-apt-repository -y ppa:git-core +sudo apt-get update + +. "$(dirname ${BASH_SOURCE[0]})/client-common" + +sudo apt-get install -y \ + bash-completion \ + curl \ + dstat \ + git \ + iftop \ + iotop \ + iperf \ + jq \ + lsof \ + make \ + man-db \ + mlocate \ + nano \ + strace \ + sysstat \ + unzip \ + wget \ + zip + +# Color prompts +sudo sed -i 's|#force_color|force_color|' "$HOME/.bashrc" + +# UTC timezone +sudo timedatectl set-timezone UTC + +# Softlink to code +[ "$PROJECT_DIR" = "$HOME/$PROJECT_NAME" ] || + sudo ln -sfT "$PROJECT_DIR" "$HOME/$PROJECT_NAME" + +# # Create a spec/vagrant_spec.rb if none exists +# template "#{project_root}/spec/vagrant_spec.rb" do +# source 'vagrant/spec/vagrant_spec.rb.erb' +# variables name: project_name +# not_if { File.exist? "#{project_root}/spec/vagrant_spec.rb" } +# end + +# # Run the serverspec suite enforcing that client-controlled files are up to date +# execute 'rspec-project-spec' do +# cookbook_path = Pathname.new(__FILE__).parent.parent.to_s +# command "rspec --format documentation #{cookbook_path}/spec/project_spec.rb" +# cwd project_root +# end diff --git a/scripts/provisioning/common b/scripts/provisioning/common new file mode 100644 index 0000000..2c345d9 --- /dev/null +++ b/scripts/provisioning/common @@ -0,0 +1,4 @@ +#! /bin/bash + +set -e -u -o pipefail +set -x diff --git a/scripts/provisioning/travis b/scripts/provisioning/travis new file mode 100644 index 0000000..2f70a71 --- /dev/null +++ b/scripts/provisioning/travis @@ -0,0 +1,7 @@ +#! /bin/bash + +set -e -u -o pipefail +set -x + +. "$TRAVIS_BUILD_DIR/scripts/provisioning/client-common" +. "$TRAVIS_BUILD_DIR/scripts/provisioning/common" diff --git a/scripts/provisioning/vagrant b/scripts/provisioning/vagrant new file mode 100644 index 0000000..fa8e885 --- /dev/null +++ b/scripts/provisioning/vagrant @@ -0,0 +1,13 @@ +#! /bin/bash + +set -e -u -o pipefail +set -x + +. "$PROJECT_DIR/scripts/provisioning/client-vagrant" +. "$PROJECT_DIR/scripts/provisioning/common" + +sudo add-apt-repository -y ppa:brightbox/ruby-ng +sudo apt-get update + +sudo apt-get install -y \ + ruby2.2 diff --git a/scripts/tag-version b/scripts/tag-version new file mode 100755 index 0000000..8fef0a5 --- /dev/null +++ b/scripts/tag-version @@ -0,0 +1,70 @@ +#! /bin/bash + +set -e -u -o pipefail + +function die { + >&2 echo "$@" + false +} + +function ensure_clean_workspace { + git diff --quiet || die 'There are unstaged changes!' + git diff --staged --quiet || die 'There are uncommitted changes!' +} + +function increment_version { + local bumped + local component + local -a components + local zeroed + + components=($(tr . ' ')) # consumes stdin + component="${1:-}" + + case "$component" in + major) + bumped=0 + ;; + minor) + bumped=1 + ;; + patch) + bumped=2 + ;; + *) + die "Usage: $0 major|minor|patch" + ;; + esac + + components[$bumped]=$(( components[$bumped] + 1 )) + + for (( zeroed = bumped + 1; zeroed < ${#components[@]}; zeroed += 1 )) + do + components[$zeroed]=0 + done + + echo ${components[@]} | tr ' ' . +} + +function commit_and_tag { + local new_version + + new_version="$1" + + git commit -am "Committing version $new_version" + git tag -a -m "Tagging $new_version" "v$new_version" +} + +function tag_version { + local new_version + + ensure_clean_workspace + + new_version=$(increment_version "$@" < version) + echo $new_version > version + + commit_and_tag "$new_version" + echo "Tagged version $new_version" +} + +tag_version "$@" diff --git a/spec/common_spec.rb b/spec/common_spec.rb deleted file mode 100644 index 2bd31e1..0000000 --- a/spec/common_spec.rb +++ /dev/null @@ -1,65 +0,0 @@ -require 'spec_helper' - -describe 'infrastructure::common' do - let(:hostname) { 'the-hostname' } - let(:project_root) { 'the-project-root' } - - let(:chef_run) do - ChefSpec::SoloRunner.new do |node| - node.automatic['hostname'] = hostname - node.set['infrastructure']['project_root'] = project_root - end.converge(described_recipe) - end - - it 'includes apt' do - expect(chef_run).to include_recipe('apt') - end - - it 'adds the git apt repository' do - code_name = chef_run.node['lsb']['codename'] - expect(chef_run).to add_apt_repository('git-ppa').with( - uri: 'http://ppa.launchpad.net/git-core/ppa/ubuntu', - distribution: code_name, - components: %w(main), - key: 'E1DF1F24', - keyserver: 'keyserver.ubuntu.com') - end - - %w( - bash-completion - curl - dstat - git - iftop - iotop - iperf - jq - lsof - man-db - mlocate - nano - strace - sysstat - unzip - wget - zip - ).each do |package| - it "installs #{package}" do - expect(chef_run).to install_package(package) - end - end - - it 'installs bundler dependencies if Gemfile exists' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/Gemfile").and_return(true) - expect(chef_run).to run_execute('bundle-install').with( - command: "bundle install --gemfile=#{project_root}/Gemfile", - user: 'vagrant') - end - - it 'does not install bundler dependencies if Gemfile is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/Gemfile").and_return(false) - expect(chef_run).to_not run_execute('bundle-install') - end -end diff --git a/spec/install_spec.rb b/spec/install_spec.rb deleted file mode 100644 index 2a346b5..0000000 --- a/spec/install_spec.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'tmpdir' -require 'serverspec' - -set :backend, :exec - -directory = Dir.mktmpdir 'install-spec-' - -describe command "cd #{directory} && TEST=true $OLDPWD/install 2>&1" do - it('has output') { puts subject.stdout } if ENV['CI'] - its(:exit_status) { should eq 0 } -end - -# It should stage all generated files -describe command "cd #{directory} && git diff --exit-code" do - its(:exit_status) { should eq 0 } -end - -describe command "rm -rf #{directory}" do - its(:exit_status) { should eq 0 } -end diff --git a/spec/project_spec.rb b/spec/project_spec.rb deleted file mode 100644 index a465125..0000000 --- a/spec/project_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -require 'serverspec' - -set :backend, :exec - -describe file 'Vagrantfile' do - it { should exist } -end - -describe file 'version' do - it { should exist } - its(:content) { should match(/\d+\.\d+\.\d+/) } -end - -describe file 'README.md' do - it { should exist } -end - -describe file 'Gemfile' do - it { should exist } -end - -describe file 'Berksfile' do - it { should exist } -end - -describe file 'Rakefile' do - it { should exist } - - its(:content) do - should include "Dir['vendor/infrastructure/tasks/**/*.rb']"\ - '.sort.each { |file| require_relative file }' - end - - its(:content) do - should include "Dir['tasks/**/*.rb'].sort.each { |file| require_relative file }" - end -end - -describe file 'metadata.rb' do - it { should exist } - - its(:content) do - should include "version File.read(Pathname.new(__FILE__).parent + 'version').strip" - end -end - -describe file 'chefignore' do - it { should exist } - its(:content) { should include 'vendor/*' } -end - -describe file '.gitignore' do - it { should exist } - its(:content) { should include '.vagrant' } - its(:content) { should include 'Gemfile.lock' } - its(:content) { should include 'Berksfile.lock' } -end - -describe file '.rubocop.yml' do - it { should exist } -end - -describe file '.editorconfig' do - it { should exist } -end - -describe file '.sublime-project' do - it { should exist } -end - -describe file 'recipes/vagrant.rb' do - it { should exist } -end - -describe file 'spec/vagrant_spec.rb' do - it { should exist } -end - -describe file 'spec/spec_helper.rb' do - it { should exist } -end - -describe file '.gitmodules' do - its(:content) { should_not include 'git@github.com' } -end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb deleted file mode 100644 index f0a0c23..0000000 --- a/spec/spec_helper.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'chefspec' -require 'chefspec/berkshelf' - -RSpec.configure do |config| - config.platform = 'ubuntu' - config.version = '14.04' -end diff --git a/spec/vagrant_spec.rb b/spec/vagrant_spec.rb deleted file mode 100644 index 74b6070..0000000 --- a/spec/vagrant_spec.rb +++ /dev/null @@ -1,259 +0,0 @@ -require 'spec_helper' - -describe 'infrastructure::vagrant' do - let(:hostname) { 'the-hostname' } - let(:project_root) { 'the-project-root' } - - let(:chef_run) do - ChefSpec::SoloRunner.new do |node| - node.automatic['hostname'] = hostname - node.set['infrastructure']['project_root'] = project_root - end.converge(described_recipe) - end - - it 'creates ./Vagrantfile' do - expect(chef_run).to create_template("#{project_root}/Vagrantfile").with( - source: 'vagrant/Vagrantfile.erb', - variables: { name: hostname }) - end - - it 'creates ./version if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/version").and_return(false) - expect(chef_run).to create_template("#{project_root}/version").with( - source: 'vagrant/version.erb') - end - - it "doesn't create ./version if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/version").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/version") - end - - it 'creates ./README.md if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/README.md").and_return(false) - expect(chef_run).to create_template("#{project_root}/README.md").with( - source: 'vagrant/README.md.erb', - variables: { name: hostname }) - end - - it "doesn't create ./README.md if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/README.md").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/README.md") - end - - it 'creates ./.gemspec if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/.gemspec").and_return(false) - expect(chef_run).to create_template("#{project_root}/.gemspec").with( - source: 'vagrant/.gemspec.erb', - variables: { name: hostname }) - end - - it "doesn't create ./.gemspec if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/.gemspec").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/.gemspec") - end - - it 'creates ./Gemfile if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/Gemfile").and_return(false) - expect(chef_run).to create_template("#{project_root}/Gemfile").with( - source: 'vagrant/Gemfile.erb') - end - - it "doesn't create ./Gemfile if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/Gemfile").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/Gemfile") - end - - it 'creates ./Berksfile if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/Berksfile").and_return(false) - expect(chef_run).to create_template("#{project_root}/Berksfile").with( - source: 'vagrant/Berksfile.erb') - end - - it "doesn't create ./Berksfile if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/Berksfile").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/Berksfile") - end - - it 'creates ./Rakefile' do - expect(chef_run).to create_template("#{project_root}/Rakefile").with( - source: 'vagrant/Rakefile.erb') - end - - it 'creates ./metadata.rb if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/metadata.rb").and_return(false) - expect(chef_run).to create_template("#{project_root}/metadata.rb").with( - source: 'vagrant/metadata.rb.erb', - variables: { name: hostname }) - end - - it "doesn't create ./metadata.rb if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/metadata.rb").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/metadata.rb") - end - - it 'creates ./chefignore if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/chefignore").and_return(false) - expect(chef_run).to create_template("#{project_root}/chefignore").with( - source: 'vagrant/chefignore.erb') - end - - it "doesn't create ./chefignore if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/chefignore").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/chefignore") - end - - it 'creates ./.gitignore if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/.gitignore").and_return(false) - expect(chef_run).to create_template("#{project_root}/.gitignore").with( - source: 'vagrant/.gitignore.erb') - end - - it "doesn't create ./.gitignore if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/.gitignore").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/.gitignore") - end - - it 'creates ./.rspec' do - expect(chef_run).to create_file("#{project_root}/.rspec").with( - content: "--color --format documentation\n") - end - - it 'creates ./.rubocop.yml' do - expect(chef_run).to create_template("#{project_root}/.rubocop.yml").with( - source: 'vagrant/.rubocop.yml.erb') - end - - it 'creates ./.editorconfig' do - expect(chef_run).to create_template("#{project_root}/.editorconfig").with( - source: 'vagrant/.editorconfig.erb') - end - - it 'creates ./.sublime-project if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?) - .with("#{project_root}/.sublime-project").and_return(false) - expect(chef_run).to create_template("#{project_root}/.sublime-project").with( - source: 'vagrant/.sublime-project.erb') - end - - it "doesn't create ./.sublime-project if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?) - .with("#{project_root}/.sublime-project").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/.sublime-project") - end - - it 'creates ./recipes' do - expect(chef_run).to create_directory("#{project_root}/recipes").with( - owner: 'vagrant', - group: 'vagrant') - end - - it 'creates ./recipes/vagrant.rb if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?) - .with("#{project_root}/recipes/vagrant.rb").and_return(false) - expect(chef_run).to create_template("#{project_root}/recipes/vagrant.rb").with( - source: 'vagrant/recipes/vagrant.rb.erb') - end - - it "doesn't create ./recipes/vagrant.rb if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?) - .with("#{project_root}/recipes/vagrant.rb").and_return(true) - expect(chef_run).to_not create_template("#{project_root}/recipes/vagrant.rb") - end - - it 'creates ./spec' do - expect(chef_run).to create_directory("#{project_root}/spec").with( - owner: 'vagrant', - group: 'vagrant') - end - - it 'creates ./spec/vagrant_spec.rb if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/spec/vagrant_spec.rb") - .and_return(false) - expect(chef_run).to create_template("#{project_root}/spec/vagrant_spec.rb").with( - source: 'vagrant/spec/vagrant_spec.rb.erb', - variables: { name: hostname }) - end - - it "doesn't create ./spec/vagrant_spec.rb if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/spec/vagrant_spec.rb") - .and_return(true) - expect(chef_run).to_not create_template("#{project_root}/spec/vagrant_spec.rb") - end - - it 'creates ./spec/spec_helper.rb if it is absent' do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/spec/spec_helper.rb") - .and_return(false) - expect(chef_run).to create_template("#{project_root}/spec/spec_helper.rb").with( - source: 'vagrant/spec/spec_helper.rb.erb') - end - - it "doesn't create ./spec/spec_helper.rb if it exists" do - allow(File).to receive(:exist?).and_call_original - allow(File).to receive(:exist?).with("#{project_root}/spec/spec_helper.rb") - .and_return(true) - expect(chef_run).to_not create_template("#{project_root}/spec/spec_helper.rb") - end - - it 'forces color shell prompts' do - expect(chef_run).to run_execute( - "sed -i 's|#force_color|force_color|' /home/vagrant/.bashrc") - end - - it 'creates a softlink to the project root named after the project' do - expect(chef_run).to create_link("/home/vagrant/#{hostname}") - .with(to: project_root) - end - - describe 'when the softlink would lead to itself' do - let(:project_root) { "/home/vagrant/#{hostname}" } - - it "doesn't creates the softlink" do - expect(chef_run).to_not create_link("/home/vagrant/#{hostname}") - end - end - - it 'creates /home/vagrant/.irbrc' do - expect(chef_run).to create_template('/home/vagrant/.irbrc').with( - source: 'home/vagrant/.irbrc.erb') - end - - it 'initializes chefdk for bash with a /etc/profile.d entry' do - expect(chef_run).to create_template('/etc/profile.d/chefdk.sh').with( - source: 'etc/profile.d/chefdk.sh.erb') - end - - it 'includes common' do - expect(chef_run).to include_recipe('infrastructure::common') - end - - it 'runs the project spec' do - cookbook_path = - chef_run.run_context.cookbook_collection['infrastructure'].root_paths.first - expect(chef_run).to run_execute('rspec-project-spec').with( - command: "rspec --format documentation #{cookbook_path}/spec/project_spec.rb", - cwd: project_root) - end -end diff --git a/tasks/foodcritic.rb b/tasks/foodcritic.rb deleted file mode 100644 index 900aa82..0000000 --- a/tasks/foodcritic.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'foodcritic' - -FoodCritic::Rake::LintTask.new do |foodcritic| - foodcritic.options[:tags] = %w(correctness ~FC055 ~FC056) -end - -task default: :foodcritic diff --git a/tasks/rubocop.rb b/tasks/rubocop.rb deleted file mode 100644 index 3c215e4..0000000 --- a/tasks/rubocop.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'rubocop/rake_task' - -RuboCop::RakeTask.new do |rubocop_task| - rubocop_task.options = %w(--display-cop-names) -end - -task default: :rubocop diff --git a/tasks/spec.rb b/tasks/spec.rb deleted file mode 100644 index f26a9b9..0000000 --- a/tasks/spec.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'rspec/core/rake_task' - -RSpec::Core::RakeTask.new do |rspec_task| - rspec_task.pattern += ',vendor/infrastructure/spec/project_spec.rb' -end - -task default: :spec diff --git a/templates/default/vagrant/.editorconfig.erb b/templates/.editorconfig similarity index 71% rename from templates/default/vagrant/.editorconfig.erb rename to templates/.editorconfig index 79ac465..94d365b 100644 --- a/templates/default/vagrant/.editorconfig.erb +++ b/templates/.editorconfig @@ -1,4 +1,4 @@ -# This file is managed by chef, changes will be overwritten +# This file is managed by infrastructure, changes will be overwritten root = true diff --git a/templates/.gitignore b/templates/.gitignore new file mode 100644 index 0000000..7930476 --- /dev/null +++ b/templates/.gitignore @@ -0,0 +1,4 @@ +.vagrant + +# Sublime Text +*.sublime-workspace diff --git a/templates/default/vagrant/.sublime-project.erb b/templates/.sublime-project similarity index 100% rename from templates/default/vagrant/.sublime-project.erb rename to templates/.sublime-project diff --git a/templates/Makefile b/templates/Makefile new file mode 100644 index 0000000..6221292 --- /dev/null +++ b/templates/Makefile @@ -0,0 +1,7 @@ +SHELL = /bin/bash + +default: tests + +tests: infrastructure-tests + +include vendor/infrastructure/make/common diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 0000000..fd7fe24 --- /dev/null +++ b/templates/README.md @@ -0,0 +1 @@ +# @PROJECT_NAME@ diff --git a/templates/Vagrantfile b/templates/Vagrantfile new file mode 100644 index 0000000..9d57d81 --- /dev/null +++ b/templates/Vagrantfile @@ -0,0 +1,35 @@ +Vagrant.configure('2') do |config| + if Vagrant.has_plugin?('vagrant-cachier') + config.cache.scope = :box # http://fgrehm.viewdocs.io/vagrant-cachier/usage/ + end + + config.vm.box = 'ubuntu/trusty64' + config.vm.hostname = '@PROJECT_NAME@' + config.ssh.forward_agent = true + + %w(vmware_fusion vmware_workstation).each do |vmware| + config.vm.provider vmware do |provider, override| + provider.name = '@PROJECT_NAME@' + provider.vmx['tools.syncTime'] = true + override.vm.box = 'netsensia/ubuntu-trusty64' + end + end + + config.vm.provider 'virtualbox' do |provider, _override| + provider.name = '@PROJECT_NAME@' + end + + if File.exist? "#{ENV['HOME']}/.gitconfig" + config.vm.provision :file, source: '~/.gitconfig', destination: '.gitconfig' + end + + config.vm.provision :shell do |shell| + shell.privileged = false + shell.keep_color = true + shell.path = 'scripts/provisioning/vagrant' + shell.env = { + PROJECT_DIR: '/vagrant', + PROJECT_NAME: '@PROJECT_NAME@' + } + end +end diff --git a/templates/default/etc/profile.d/chefdk.sh.erb b/templates/default/etc/profile.d/chefdk.sh.erb deleted file mode 100644 index b947807..0000000 --- a/templates/default/etc/profile.d/chefdk.sh.erb +++ /dev/null @@ -1,2 +0,0 @@ -# This file is managed by chef, changes will be overwritten -eval "$(/opt/chefdk/bin/chef shell-init bash)" diff --git a/templates/default/home/vagrant/.irbrc.erb b/templates/default/home/vagrant/.irbrc.erb deleted file mode 100644 index b1d6737..0000000 --- a/templates/default/home/vagrant/.irbrc.erb +++ /dev/null @@ -1 +0,0 @@ -IRB.conf[:PROMPT_MODE] = :SIMPLE diff --git a/templates/default/vagrant/.gemspec.erb b/templates/default/vagrant/.gemspec.erb deleted file mode 100644 index 3d5b1a8..0000000 --- a/templates/default/vagrant/.gemspec.erb +++ /dev/null @@ -1,8 +0,0 @@ -require 'pathname' - -Gem::Specification.new do |s| - s.name = '<%= @name %>' - s.version = File.read(Pathname.new(__FILE__).parent + 'version').strip - s.summary = '<%= @name %>' - s.authors = ['The Way Development Team'] -end diff --git a/templates/default/vagrant/.gitignore.erb b/templates/default/vagrant/.gitignore.erb deleted file mode 100644 index 72a5b91..0000000 --- a/templates/default/vagrant/.gitignore.erb +++ /dev/null @@ -1,12 +0,0 @@ -.vagrant - -# Bundler -.bundle/* -bin/* -Gemfile.lock - -# Berks -Berksfile.lock - -# Sublime Text -*.sublime-workspace diff --git a/templates/default/vagrant/.rubocop.yml.erb b/templates/default/vagrant/.rubocop.yml.erb deleted file mode 100644 index 617d0ea..0000000 --- a/templates/default/vagrant/.rubocop.yml.erb +++ /dev/null @@ -1,5 +0,0 @@ -# This file is managed by chef, changes will be overwritten - -# Max line length is 90 -Metrics/LineLength: - Max: 90 diff --git a/templates/default/vagrant/Berksfile.erb b/templates/default/vagrant/Berksfile.erb deleted file mode 100644 index 95dbbd2..0000000 --- a/templates/default/vagrant/Berksfile.erb +++ /dev/null @@ -1,3 +0,0 @@ -source 'https://supermarket.chef.io' -metadata -cookbook 'infrastructure', path: 'vendor/infrastructure' diff --git a/templates/default/vagrant/Gemfile.erb b/templates/default/vagrant/Gemfile.erb deleted file mode 100644 index 8d7446d..0000000 --- a/templates/default/vagrant/Gemfile.erb +++ /dev/null @@ -1,3 +0,0 @@ -source 'https://rubygems.org' -gemspec -gem 'infrastructure', path: 'vendor/infrastructure' diff --git a/templates/default/vagrant/README.md.erb b/templates/default/vagrant/README.md.erb deleted file mode 100644 index f7bbe11..0000000 --- a/templates/default/vagrant/README.md.erb +++ /dev/null @@ -1 +0,0 @@ -# <%= @name %> diff --git a/templates/default/vagrant/Rakefile.erb b/templates/default/vagrant/Rakefile.erb deleted file mode 100644 index 3a89459..0000000 --- a/templates/default/vagrant/Rakefile.erb +++ /dev/null @@ -1,6 +0,0 @@ -desc 'Run all linting and tests' -task :default - -# Grab everything in the tasks directory -Dir['vendor/infrastructure/tasks/**/*.rb'].sort.each { |file| require_relative file } -Dir['tasks/**/*.rb'].sort.each { |file| require_relative file } diff --git a/templates/default/vagrant/Vagrantfile.erb b/templates/default/vagrant/Vagrantfile.erb deleted file mode 100644 index 6b964c9..0000000 --- a/templates/default/vagrant/Vagrantfile.erb +++ /dev/null @@ -1,44 +0,0 @@ -require 'ostruct' -require 'pathname' - -Vagrant.configure('2') do |config| - if Vagrant.has_plugin?('vagrant-cachier') - config.cache.scope = :box # http://fgrehm.viewdocs.io/vagrant-cachier/usage/ - end - - config.vm.box = 'ubuntu/trusty64' - config.vm.hostname = '<%= @name %>' - config.ssh.forward_agent = true - - %w(vmware_fusion vmware_workstation).each do |vmware| - config.vm.provider vmware do |provider, override| - provider.name = '<%= @name %>' - override.vm.box = 'netsensia/ubuntu-trusty64' - end - end - - config.vm.provider 'virtualbox' do |provider, _override| - provider.name = '<%= @name %>' - end - - if File.exist? "#{ENV['HOME']}/.gitconfig" - config.vm.provision :file, source: '~/.gitconfig', destination: '.gitconfig' - end - - config.vm.provision :shell, inline: %( -which chef || wget -qO - https://www.chef.io/chef/install.sh | bash -s -- -P chefdk -berks vendor -b /vagrant/Berksfile /tmp/vagrant-chef/cookbooks -) - - config.vm.provision :chef_solo do |chef_solo| - chef_solo.install = false - chef_solo.binary_path = '/opt/chefdk/bin' - chef_solo.add_recipe '<%= @name %>::vagrant' - end - - # If Vagrantfile.custom exists, read and eval it in this context - vagrant_custom = Pathname.new(__FILE__).parent + 'Vagrantfile.custom' - if File.exist?(vagrant_custom) - OpenStruct.new(config: config).instance_eval File.read(vagrant_custom) - end -end diff --git a/templates/default/vagrant/chefignore.erb b/templates/default/vagrant/chefignore.erb deleted file mode 100644 index c92322f..0000000 --- a/templates/default/vagrant/chefignore.erb +++ /dev/null @@ -1,106 +0,0 @@ -# Put files/directories that should be ignored in this file when uploading -# or sharing to the community site. -# Lines that start with '# ' are comments. - -# Git Submodules # -################## -vendor/* - -# OS generated files # -###################### -.DS_Store -Icon? -nohup.out -ehthumbs.db -Thumbs.db - -# SASS # -######## -.sass-cache - -# EDITORS # -########### -\#* -.#* -*~ -*.sw[a-z] -*.bak -REVISION -TAGS* -tmtags -*_flymake.* -*_flymake -*.tmproj -.project -.settings -mkmf.log -*.sublime-project -*.sublime-workspace - -## COMPILED ## -############## -a.out -*.o -*.pyc -*.so -*.com -*.class -*.dll -*.exe -*/rdoc/ - -# Testing # -########### -.watchr -.rspec -spec/* -spec/fixtures/* -test/* -features/* -examples/* -Guardfile -Procfile -test/* -spec/* - -# SCM # -####### -.git -*/.git -.gitignore -.gitmodules -.gitconfig -.gitattributes -.svn -*/.bzr/* -*/.hg/* -*/.svn/* - -# Berkshelf # -############# -Berksfile -Berksfile.lock -cookbooks/* -tmp - -# Cookbooks # -############# -CONTRIBUTING -CHANGELOG* - -# Strainer # -############ -Colanderfile -Strainerfile -.colander -.strainer - -# Vagrant # -########### -.vagrant -Vagrantfile - -# Travis # -########## -.travis.yml -.travis/* diff --git a/templates/default/vagrant/metadata.rb.erb b/templates/default/vagrant/metadata.rb.erb deleted file mode 100644 index 8b63d26..0000000 --- a/templates/default/vagrant/metadata.rb.erb +++ /dev/null @@ -1,5 +0,0 @@ -require 'pathname' - -name '<%= @name %>' -version File.read(Pathname.new(__FILE__).parent + 'version').strip -depends 'infrastructure' diff --git a/templates/default/vagrant/recipes/vagrant.rb.erb b/templates/default/vagrant/recipes/vagrant.rb.erb deleted file mode 100644 index 4580fa9..0000000 --- a/templates/default/vagrant/recipes/vagrant.rb.erb +++ /dev/null @@ -1 +0,0 @@ -include_recipe 'infrastructure::vagrant' diff --git a/templates/default/vagrant/spec/spec_helper.rb.erb b/templates/default/vagrant/spec/spec_helper.rb.erb deleted file mode 100644 index f0a0c23..0000000 --- a/templates/default/vagrant/spec/spec_helper.rb.erb +++ /dev/null @@ -1,7 +0,0 @@ -require 'chefspec' -require 'chefspec/berkshelf' - -RSpec.configure do |config| - config.platform = 'ubuntu' - config.version = '14.04' -end diff --git a/templates/default/vagrant/spec/vagrant_spec.rb.erb b/templates/default/vagrant/spec/vagrant_spec.rb.erb deleted file mode 100644 index c1fc211..0000000 --- a/templates/default/vagrant/spec/vagrant_spec.rb.erb +++ /dev/null @@ -1,15 +0,0 @@ -require 'spec_helper' - -describe '<%= @name %>::vagrant' do - let(:hostname) { 'the-hostname' } - - let(:chef_run) do - ChefSpec::SoloRunner.new do |node| - node.automatic['hostname'] = hostname - end.converge(described_recipe) - end - - it 'includes infrastructure::vagrant' do - expect(chef_run).to include_recipe('infrastructure::vagrant') - end -end diff --git a/templates/scripts/provisioning/common b/templates/scripts/provisioning/common new file mode 100644 index 0000000..2c345d9 --- /dev/null +++ b/templates/scripts/provisioning/common @@ -0,0 +1,4 @@ +#! /bin/bash + +set -e -u -o pipefail +set -x diff --git a/templates/scripts/provisioning/vagrant b/templates/scripts/provisioning/vagrant new file mode 100644 index 0000000..1227901 --- /dev/null +++ b/templates/scripts/provisioning/vagrant @@ -0,0 +1,7 @@ +#! /bin/bash + +set -e -u -o pipefail +set -x + +. "$PROJECT_DIR/vendor/infrastructure/scripts/provisioning/client-vagrant" +. "$PROJECT_DIR/scripts/provisioning/common" diff --git a/templates/default/vagrant/version.erb b/templates/version similarity index 100% rename from templates/default/vagrant/version.erb rename to templates/version diff --git a/test/integration/install.bats b/test/integration/install.bats new file mode 100644 index 0000000..c0b9fc6 --- /dev/null +++ b/test/integration/install.bats @@ -0,0 +1,28 @@ +#! /usr/bin/env bats + +function setup { + export ROOT=$(pwd) + export EXAMPLE_PROJECT=$(mktemp --tmpdir -d example-project-XXXX) + export TEST=true + export SOFTLINK=~/$(basename $EXAMPLE_PROJECT) + cd $EXAMPLE_PROJECT +} + +@test 'install runs' { + $ROOT/install +} + +@test 'install stages all generated files' { + run $ROOT/install + git diff --exit-code +} + +@test 'install output passes infrastructure tests' { + run $ROOT/install + make infrastructure-tests +} + +function teardown { + rm -rf $EXAMPLE_PROJECT + test -L $SOFTLINK && rm $SOFTLINK +} diff --git a/test/integration/layout.bats b/test/integration/layout.bats new file mode 100644 index 0000000..a9c06ad --- /dev/null +++ b/test/integration/layout.bats @@ -0,0 +1,59 @@ +#! /usr/bin/env bats + +@test 'scripts/provisioning is a directory' { + test -d scripts/provisioning +} + +@test 'scripts/provisioning/common is a file' { + test -f scripts/provisioning/common +} + +@test 'scripts/provisioning/vagrant is a file' { + test -f scripts/provisioning/vagrant +} + +@test '.editorconfig is a file' { + test -f .editorconfig +} + +@test '.gitignore is a file' { + test -f .gitignore +} + +@test '.gitignore ignores vagrant dir' { + egrep -q '\.vagrant' .gitignore +} + +@test '.gitmodules does not include github ssh urls' { + # It doesn't have to exist + test ! -e .gitmodules || ! egrep -q 'git@github\.com' .gitmodules +} + +@test '.sublime-project is a file' { + test -f .sublime-project +} + +@test 'Makefile is a file' { + test -f Makefile +} + +@test 'README.md is a file' { + test -f README.md +} + +@test 'Vagrantfile is a file' { + test -f Vagrantfile +} + +@test 'version is a file' { + test -f version +} + +@test 'version is one line' { + run wc -l version + test "$output" = '1 version' +} + +@test 'version content is dotted numeric triple' { + egrep -q '^[0-9]+\.[0-9]+\.[0-9]+$' version +} diff --git a/vendor/bats b/vendor/bats new file mode 160000 index 0000000..7b032e4 --- /dev/null +++ b/vendor/bats @@ -0,0 +1 @@ +Subproject commit 7b032e4b232666ee24f150338bad73de65c7b99d