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..d1d700f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,3 @@ +# From infrastructure .vagrant - -# Bundler -.bundle/* -bin/* -Gemfile.lock - -# Berks -Berksfile.lock - -# Sublime Text -*.sublime-workspace +bin 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/.sublime-project b/.sublime-project deleted file mode 100644 index 708bf5f..0000000 --- a/.sublime-project +++ /dev/null @@ -1,20 +0,0 @@ -{ - "folders": [ - { - "file_exclude_patterns": [ - "*.sublime-workspace" - ], - "folder_exclude_patterns": [ - ".vagrant", - "vendor" - ], - "path": "." - } - ], - "settings": { - "highlight_line": true, - "save_on_focus_lost": true, - "word_wrap": true, - "wrap_width": 90 - } -} diff --git a/.travis.yml b/.travis.yml index c025572..20b7a37 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 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..bca459e --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +include make/common + +default: tests + +travis: tests + +tests: integration + +integration: + bats test/integration diff --git a/README.md b/README.md index 7177520..625867c 100644 --- a/README.md +++ b/README.md @@ -10,33 +10,37 @@ Cross-repository Services ## About -Infrastructure's goal is to keep the Way development environments consistent across the repos of the organization. It aims to do so by extracting their provisioning to a single place. Hand in hand with this goal is automating the process of creating a new repo in the organization. +Infrastructure's goal is to keep the Way development environments consistent across the repositories of the organization. It aims to do so by moving common aspects of their provisioning to a single place. Hand in hand with this goal is automating common life cycle tasks, including creating a new repository in the organization. -There is a natural trade-off between similarity (for portable UX) and diversity (for achieving required function) of org repos. Infrastructure takes the following stance: -* All repos will avoid a thick host OS tool chain by sand-boxing development within vagrant. -* All repos will use ruby and its ecosystem (e.g. rake) for tooling where possible. -* All repos will automate development environment provisioning with chef. -* Infrastructure will clearly divide maintenance responsibility of common files, and all repos will respect this division of responsibility. +In organiaztion repositories there is a natural tension between similarity (for a portable developer experience) and diversity (for achieving required function). Infrastructure takes the following stance: +* All repositories will avoid a thick host OS tool chain by sand-boxing development within [vagrant][vagrant]. +* All repositories will automate development environment provisioning with bash. +* Infrastructure will clearly divide maintenance responsibility of common files, and all repositories will respect this division. The first point seeks to limit host OS dependencies to: -* git -* vagrant -* text editor of choice +* `bash` +* `gpg` or `gpg2` (only needed to tag releases) +* `git` +* `vagrant` +* [`virtualbox`][virtualbox] +* your text editor of choice -The goal of the last point is to avoid code rot introduced by projects being created at various times in the face of evolving project templates. When a significantly complex file changes in a way that all repos should consume, infrastructure will roll the change out (e.g. as an updated chef template.) +The goal of the last point is to avoid code rot introduced by projects being created at various times in the face of evolving project templates. When a significantly complex file changes in a way that all repositories should consume, infrastructure will roll the change out, e.g. as an updated template. -Infrastructure makes an effort to allow client repositories to retain ownership of most files. Infrastructure implements two strategies for managing the portions of those files that it owns. First, it tries to offer an inclusion mechanism of some sort either native to the file format or via a chef partial template. When inclusion is not appropriate, it falls back to validating client-owned files at provision time to ensure that required features are present (see `project_spec.rb`.) +Infrastructure makes an effort to allow client repositories to retain ownership of most files. Infrastructure implements two strategies for managing the portions of those files that it owns. First, it tries to offer an inclusion mechanism of some sort. When inclusion is not appropriate, it falls back to validating client-owned files during test to ensure that required features are present (see `layout.bats`). -Infrastructure explicitly does not require: -* Committing to a host OS (in theory) -* Committing to an editor, though [editorconfig][editorconfig] users will benefit. -* Committing to or automating the configuration of external services such as travis CI. +Infrastructure explicitly does not: +* Require a particular host OS (in theory) +* Require an editor, though [editorconfig][editorconfig] users will benefit. +* Require or automate the configuration of external services such as continuous integration. +[vagrant]: https://www.vagrantup.com/ +[virtualbox]: https://www.virtualbox.org/ [editorconfig]: http://editorconfig.org/ ## Creating a new Repository -First, make a new way repo in the Github UI. Then: +First, make a new way repository in the Github UI. Then: ``` git clone git@github.com:waylang/new_repo.git @@ -44,12 +48,12 @@ cd new_repo curl -s https://raw.githubusercontent.com/waylang/infrastructure/master/install | bash ``` -This fetches the latest [github release][github-releases] of infrastructure, which generates a Vagrantfile and drops into chef provisioning to create the rest of the project template. You will be left with some files to commit in your staging area, and a running vagrant instance. +This fetches the latest [github release][github-releases] of infrastructure, which generates a `Vagrantfile` and drops into provisioning to create the rest of the project template. You will be left with some files to commit in your staging area, and a running vagrant instance. [github-releases]: https://help.github.com/articles/about-releases/ ## Releasing a new Version -Use the `version:major`, `version:minor` and `version:patch` rake tasks to bump the given semantic version component. This will create a new commit (modifying the `version` file) and an annotated tag. The tag is not automatically pushed. +On the _host_ OS, use `./bin/bump-version` with `major`, `minor` or `patch` arguments to bump the given semantic version component. This will modify the `version` file in a new commit and add a gpg-signed, annotated tag. Neither the commit nor the tag is not automatically pushed. It is expected that the repository's CI service will trigger a build in response to pushing the tag and, if the build succeeds, release the new version using the appropriate continuous delivery process. 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..af0f532 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -1,44 +1,5 @@ -require 'ostruct' -require 'pathname' +common = File.expand_path('../vagrant/common', __FILE__) +load common if File.exists?(common) 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 = 'infrastructure' - config.ssh.forward_agent = true - - %w(vmware_fusion vmware_workstation).each do |vmware| - config.vm.provider vmware do |provider, override| - provider.name = 'infrastructure' - override.vm.box = 'netsensia/ubuntu-trusty64' - end - end - - config.vm.provider 'virtualbox' do |provider, _override| - provider.name = 'infrastructure' - 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 '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) - 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..3626de2 100755 --- a/install +++ b/install @@ -2,23 +2,28 @@ # 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. +# This script can be run in a "local" mode which runs provisioning in the calling host. +# It goes without saying DO NOT RUN IT OUTSIDE OF VAGRANT OR TRAVIS IN THIS WAY. -# To enable test mode, set the TEST environment variable and run it by name: +# To enable local mode, set the LOCAL environment variable and run it by name: +# LOCAL=true ~/infrastructure/install + +# There is also a test mode, which implies local. To enable it, set TEST: # TEST=true ~/infrastructure/install # The script makes a work directory in $TMPDIR or /tmp, named infrastructure-install.XXXX # 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:-}" +LOCAL="${LOCAL:-$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 +32,7 @@ function green { } function red { - if [[ -z "$NO_COLOR" ]] + if [ -z "${NO_COLOR:-}" ] then echo -e "\033[1;31m$@\033[0m" else @@ -38,13 +43,13 @@ function red { # Report an error to stderr and exit abnormally. function error { red Error: "$@" >&2 - false + exit 1 } -# We need git and either a vagrant or a chefdk for test +# We need git and either a vagrant or a ruby which git >/dev/null || error 'git is not available' -if [[ -z "$TEST" ]] +if [ -z "$LOCAL" ] then which vagrant >/dev/null || error 'vagrant is not available' else @@ -52,35 +57,31 @@ 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" ]] - then - trap "rm -rf \"$INSTALL_WORK_DIR\"" EXIT - else - trap "sudo rm -rf \"$INSTALL_WORK_DIR\"" EXIT - fi + trap "sudo rm -rf \"$INSTALL_WORK_DIR\"" EXIT 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. # # In live use this runs within vagrant's embedded ruby install. -# During test it's just the host's ruby. +# In local mode it's just the host's ruby. function ruby_stdin { local ruby_path local ruby_file @@ -91,25 +92,25 @@ function ruby_stdin { # This consumes the function's stdin cat > "$ruby_path" - if [[ -z "$TEST" ]] + if [ -z "$LOCAL" ] 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 +233,12 @@ else end RUBY -if [[ -z "$TEST" ]] +if [ -z "$LOCAL" ] 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 [ -f "templates/$path" ] + then + template_path="templates/$path" + elif [ -f "$INFRASTRUCTURE/templates/$path" ] + then + template_path="$INFRASTRUCTURE/templates/$path" + else + die "Can't find template for $path" + fi + + if [ "${CLOBBER:-}" == "" ] && [ -f "$path" ] + then + return 0 + fi + + if [ "$#" == '0' ] + then + cat < "$template_path" > "$path" + else + sed "$@" < "$template_path" > "$path" + fi + + return $? +} + +mkdir -p bin +ln -sfT "$INFRASTRUCTURE/vendor/bats/bin/bats" bin/bats +# It must be relative as bump-version expects to be called from the VM host +ln -sfT "../$RELATIVE_INFRASTRUCTURE/src/bump-version" bin/bump-version +mkdir -p provisioning +render provisioning/common +render provisioning/vagrant +render .editorconfig +render .gitignore +render Makefile +render README.md \ + -e "s|@PROJECT_NAME@|$PROJECT_NAME|g" \ + -e "s|@RELATIVE_INFRASTRUCTURE@|$RELATIVE_INFRASTRUCTURE|g" +render Vagrantfile \ + -e "s|@PROJECT_NAME@|$PROJECT_NAME|g" +render version diff --git a/provisioning/client-vagrant b/provisioning/client-vagrant new file mode 100644 index 0000000..4639280 --- /dev/null +++ b/provisioning/client-vagrant @@ -0,0 +1,75 @@ +#! /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 +sed -i 's|#force_color|force_color|' "$HOME/.bashrc" + +# ~/.bashrc.d/ +mkdir -p "$HOME/.bashrc.d" + +if ! grep -q .bashrc.d "$HOME/.bashrc" +then + cat >> "$HOME/.bashrc" <<'BASHRC' + +# Load ~/.bashrc.d/* +if (shopt -s nullglob dotglob; f=(~/.bashrc.d/*); ((${#f[@]}))) +then + for f in ~/.bashrc.d/* + do + . "$f" + done +fi +BASHRC +fi + +if [ -z "${TEST:-}" ] +then + # Add project bin to PATH + cat > "$HOME/.bashrc.d/05-project-bin-path" <&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/src/bump-version b/src/bump-version new file mode 100755 index 0000000..e058803 --- /dev/null +++ b/src/bump-version @@ -0,0 +1,94 @@ +#! /bin/bash + +# Commit and tag a version bump. + +# This expects to run from the host, not within vagrant, in order to sign tags. + +set -e -u -o pipefail + +function die { + >&2 echo "$@" + exit 1 +} + +function verify_staged_clean { + git diff --staged --quiet || die 'There are uncommitted changes!' +} + +function verify_unstaged_clean { + git diff --quiet || die 'There are unstaged changes!' +} + +# Increment a version (dotted triple) on stdin, write to stdout. +# The component to bump is given positionally as "major", "minor" or "patch". +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 ' ' . +} + +# Stage, commit and tag ./version. +# Die if we find unexpected dirty workspace state +# The new tag name is passed positionally without the leading "v". +function commit_and_tag { + local new_version + + new_version="$1" + + # Before adding version, there should be nothing staged + # After adding it, there should nothing unstaged + verify_staged_clean + git add version + verify_unstaged_clean + + git commit --message "Committing version $new_version" + git tag --annotate --sign --message "Tagging $new_version" "v$new_version" +} + +# Bump the version, commit and tag the result. +function tag_version { + local new_version + + # Refuse to run within vagrant + [ -e /vagrant ] && die 'This must be run from the host OS!' + + # Refuse to run without a clean workspace + verify_unstaged_clean + verify_staged_clean + + new_version=$(increment_version "$@" < version) + echo $new_version > version + + commit_and_tag "$new_version" + echo "Tagged version $new_version" +} + +tag_version "$@" 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/tasks/version.rb b/tasks/version.rb deleted file mode 100644 index f2d53a9..0000000 --- a/tasks/version.rb +++ /dev/null @@ -1,60 +0,0 @@ -require 'colorize' -require 'English' - -namespace :version do - def ensure_clean_workspace - `git diff --quiet` - unless $CHILD_STATUS.exitstatus == 0 - raise 'There are unstaged changes!'.colorize(color: :red, mode: :bold) - end - - `git diff --staged --quiet` - unless $CHILD_STATUS.exitstatus == 0 - raise 'There are uncommitted changes!'.colorize(color: :red, mode: :bold) - end - end - - def calculate_new_version(old_version, component_to_bump) - index_to_bump = %i(major minor patch).index(component_to_bump) - components = old_version.split('.').map(&:to_i) - components[index_to_bump] += 1 - ((index_to_bump + 1)...(components.length)).each { |i| components[i] = 0 } - components.join('.') - end - - def write_new_version(component_to_bump) - version_file = './version' - old_version = File.read(version_file).strip - new_version = calculate_new_version old_version, component_to_bump - File.open(version_file, 'w') { |f| f.write("#{new_version}\n") } - new_version - end - - def commit_and_tag(new_version) - # Use sh so that failures raise - sh "git commit -am 'Committing version #{new_version}'" - sh "git tag -a -m 'Tagging #{new_version}' 'v#{new_version}'" - end - - def bump_and_tag(component_to_bump) - ensure_clean_workspace - new_version = write_new_version(component_to_bump) - commit_and_tag(new_version) - puts "Tagged version #{new_version}".colorize(color: :green, mode: :bold) - end - - desc 'Tag a new major version release' - task :major do - bump_and_tag :major - end - - desc 'Tag a new minor version release' - task :minor do - bump_and_tag :minor - end - - desc 'Tag a new patch version release' - task :patch do - bump_and_tag :patch - end -end 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..d1d700f --- /dev/null +++ b/templates/.gitignore @@ -0,0 +1,3 @@ +# From infrastructure +.vagrant +bin diff --git a/templates/Makefile b/templates/Makefile new file mode 100644 index 0000000..544f435 --- /dev/null +++ b/templates/Makefile @@ -0,0 +1,5 @@ +include vendor/infrastructure/make/common + +default: tests + +tests: infrastructure-tests diff --git a/templates/README.md b/templates/README.md new file mode 100644 index 0000000..3fba301 --- /dev/null +++ b/templates/README.md @@ -0,0 +1,5 @@ +# @PROJECT_NAME@ + +For generic instructions on how to interact with this repository, including how to create a release, see [infrastructure's README][infrastructure-readme]. + +[infrastructure-readme]: @RELATIVE_INFRASTRUCTURE@/README.md diff --git a/templates/Vagrantfile b/templates/Vagrantfile new file mode 100644 index 0000000..f4f7ea2 --- /dev/null +++ b/templates/Vagrantfile @@ -0,0 +1,5 @@ +common = File.expand_path('../vendor/infrastructure/vagrant/common', __FILE__) +load common if File.exists?(common) + +Vagrant.configure('2') do |config| +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/.sublime-project.erb b/templates/default/vagrant/.sublime-project.erb deleted file mode 100644 index 708bf5f..0000000 --- a/templates/default/vagrant/.sublime-project.erb +++ /dev/null @@ -1,20 +0,0 @@ -{ - "folders": [ - { - "file_exclude_patterns": [ - "*.sublime-workspace" - ], - "folder_exclude_patterns": [ - ".vagrant", - "vendor" - ], - "path": "." - } - ], - "settings": { - "highlight_line": true, - "save_on_focus_lost": true, - "word_wrap": true, - "wrap_width": 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/provisioning/common b/templates/provisioning/common new file mode 100644 index 0000000..2c345d9 --- /dev/null +++ b/templates/provisioning/common @@ -0,0 +1,4 @@ +#! /bin/bash + +set -e -u -o pipefail +set -x diff --git a/templates/provisioning/vagrant b/templates/provisioning/vagrant new file mode 100644 index 0000000..6aac00b --- /dev/null +++ b/templates/provisioning/vagrant @@ -0,0 +1,7 @@ +#! /bin/bash + +set -e -u -o pipefail +set -x + +. "$PROJECT_DIR/vendor/infrastructure/provisioning/client-vagrant" +. "$PROJECT_DIR/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..cb5f845 --- /dev/null +++ b/test/integration/layout.bats @@ -0,0 +1,75 @@ +#! /usr/bin/env bats + +@test 'bin is a directory' { + test -d bin +} + +@test 'bin/bats is a soft link' { + test -L bin/bats +} + +@test 'bin/bats resolves' { + test -e bin/bats +} + +@test 'bin/bump-version is a soft link' { + test -L bin/bump-version +} + +@test 'bin/bump-version resolves' { + test -e bin/bump-version +} + +@test 'provisioning is a directory' { + test -d provisioning +} + +@test 'provisioning/common is a file' { + test -f provisioning/common +} + +@test 'provisioning/vagrant is a file' { + test -f 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 '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/vagrant/common b/vagrant/common new file mode 100644 index 0000000..4bf5083 --- /dev/null +++ b/vagrant/common @@ -0,0 +1,33 @@ +Vagrant.configure('2') do |config| + if Vagrant.has_plugin?('vagrant-cachier') + config.cache.scope = :box + config.cache.synced_folder_opts = { + owner: '_apt', + group: '_apt', + mount_options: %w(dmode=777 fmode=666) + } + end + + config.vm.box = 'ubuntu/xenial64' + config.vm.hostname = 'infrastructure' + config.ssh.forward_agent = true + + config.vm.provider 'virtualbox' do |provider, _override| + provider.name = 'infrastructure' + provider.customize ['modifyvm', :id, '--uartmode1', 'disconnected'] + 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 = 'provisioning/vagrant' + shell.env = { + PROJECT_DIR: '/vagrant', + PROJECT_NAME: 'infrastructure' + } + end +end 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