bulldozer 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.ruby-version +1 -0
- data/.travis.yml +12 -0
- data/CONTRIBUTING.md +59 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/NEWS.md +638 -0
- data/README.md +225 -0
- data/RELEASING.md +18 -0
- data/Rakefile +8 -0
- data/USAGE +13 -0
- data/bin/bulldozer +23 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/bin/setup +13 -0
- data/bulldozer.gemspec +35 -0
- data/docker-compose.yml +8 -0
- data/dockerfile +11 -0
- data/lib/bulldozer.rb +24 -0
- data/lib/bulldozer/actions.rb +85 -0
- data/lib/bulldozer/adapters/heroku.rb +123 -0
- data/lib/bulldozer/app_builder.rb +321 -0
- data/lib/bulldozer/generators/analytics_generator.rb +24 -0
- data/lib/bulldozer/generators/app_generator.rb +231 -0
- data/lib/bulldozer/generators/base.rb +20 -0
- data/lib/bulldozer/generators/ci_generator.rb +22 -0
- data/lib/bulldozer/generators/db_optimizations_generator.rb +30 -0
- data/lib/bulldozer/generators/factories_generator.rb +22 -0
- data/lib/bulldozer/generators/forms_generator.rb +18 -0
- data/lib/bulldozer/generators/jobs_generator.rb +38 -0
- data/lib/bulldozer/generators/js_driver_generator.rb +15 -0
- data/lib/bulldozer/generators/json_generator.rb +10 -0
- data/lib/bulldozer/generators/lint_generator.rb +9 -0
- data/lib/bulldozer/generators/production/deployment_generator.rb +27 -0
- data/lib/bulldozer/generators/production/email_generator.rb +37 -0
- data/lib/bulldozer/generators/production/force_tls_generator.rb +11 -0
- data/lib/bulldozer/generators/production/manifest_generator.rb +24 -0
- data/lib/bulldozer/generators/production/timeout_generator.rb +21 -0
- data/lib/bulldozer/generators/staging/pull_requests_generator.rb +33 -0
- data/lib/bulldozer/generators/static_generator.rb +10 -0
- data/lib/bulldozer/generators/stylesheet_base_generator.rb +31 -0
- data/lib/bulldozer/generators/testing_generator.rb +55 -0
- data/lib/bulldozer/generators/views_generator.rb +30 -0
- data/lib/bulldozer/version.rb +8 -0
- data/spec/adapters/heroku_spec.rb +72 -0
- data/spec/fakes/bin/heroku +5 -0
- data/spec/fakes/bin/hub +5 -0
- data/spec/features/api_spec.rb +18 -0
- data/spec/features/cli_help_spec.rb +36 -0
- data/spec/features/github_spec.rb +16 -0
- data/spec/features/heroku_spec.rb +71 -0
- data/spec/features/json_spec.rb +15 -0
- data/spec/features/new_project_spec.rb +341 -0
- data/spec/features/production/deployment_spec.rb +22 -0
- data/spec/features/production/email_spec.rb +47 -0
- data/spec/features/production/manifest_spec.rb +35 -0
- data/spec/features/staging/pull_requests_spec.rb +22 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/be_executable_matcher.rb +7 -0
- data/spec/support/bulldozer.rb +182 -0
- data/spec/support/contain_json_matcher.rb +24 -0
- data/spec/support/exist_as_a_file_matcher.rb +7 -0
- data/spec/support/fake_github.rb +21 -0
- data/spec/support/fake_heroku.rb +53 -0
- data/spec/support/generators.rb +5 -0
- data/spec/support/match_contents_matcher.rb +6 -0
- data/spec/support/project_files.rb +13 -0
- data/templates/Gemfile.erb +51 -0
- data/templates/Procfile +2 -0
- data/templates/README.md.erb +28 -0
- data/templates/_analytics.html.erb +8 -0
- data/templates/_css_overrides.html.erb +7 -0
- data/templates/_flashes.html.erb +7 -0
- data/templates/_javascript.html.erb +3 -0
- data/templates/action_mailer.rb +5 -0
- data/templates/active_job.rb +13 -0
- data/templates/application.scss +8 -0
- data/templates/bin_deploy +12 -0
- data/templates/bin_setup +28 -0
- data/templates/bin_setup_review_app.erb +22 -0
- data/templates/browserslist +3 -0
- data/templates/bulldozer_gitignore +18 -0
- data/templates/bulldozer_layout.html.erb.erb +21 -0
- data/templates/bundler_audit.rake +4 -0
- data/templates/chromedriver.rb +17 -0
- data/templates/circle.yml.erb +6 -0
- data/templates/config_locales_en.yml.erb +19 -0
- data/templates/dev.rake +12 -0
- data/templates/dotfiles/.ctags +2 -0
- data/templates/dotfiles/.env +13 -0
- data/templates/email.rb +3 -0
- data/templates/errors.rb +34 -0
- data/templates/factories.rb +2 -0
- data/templates/factory_bot_rspec.rb +5 -0
- data/templates/flashes_helper.rb +5 -0
- data/templates/hound.yml +14 -0
- data/templates/i18n.rb +3 -0
- data/templates/json_encoding.rb +1 -0
- data/templates/postgresql_database.yml.erb +19 -0
- data/templates/puma.rb +28 -0
- data/templates/rack_mini_profiler.rb +5 -0
- data/templates/rails_helper.rb +22 -0
- data/templates/secrets.yml +8 -0
- data/templates/shoulda_matchers_config_rspec.rb +6 -0
- data/templates/smtp.rb +9 -0
- data/templates/spec_helper.rb +28 -0
- metadata +197 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
RSpec.describe "bulldozer:staging:pull_requests", type: :generators do
|
4
|
+
it "generates the configuration for Heroku pipeline review apps" do
|
5
|
+
with_app { generate("bulldozer:staging:pull_requests") }
|
6
|
+
|
7
|
+
expect("config/environments/production.rb").to \
|
8
|
+
match_contents(%r{HEROKU_APP_NAME})
|
9
|
+
|
10
|
+
expect("bin/setup_review_app").to \
|
11
|
+
match_contents(%r{APP_NAME=dummy-app-staging-pr-\$1})
|
12
|
+
end
|
13
|
+
|
14
|
+
it "destroys the configuration for Heroku pipeline review apps" do
|
15
|
+
with_app { destroy("bulldozer:staging:pull_requests") }
|
16
|
+
|
17
|
+
expect("config/environments/production.rb").not_to \
|
18
|
+
match_contents(%r{APP_NAME=dummy-app-staging-pr-\$1})
|
19
|
+
|
20
|
+
expect("bin/setup_review_app").not_to exist_as_a_file
|
21
|
+
end
|
22
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
|
3
|
+
Bundler.require(:default, :test)
|
4
|
+
|
5
|
+
require (Pathname.new(__FILE__).dirname + '../lib/bulldozer').expand_path
|
6
|
+
|
7
|
+
Dir['./spec/support/**/*.rb'].each { |file| require file }
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.include BulldozerTestHelpers
|
11
|
+
config.include ProjectFiles
|
12
|
+
|
13
|
+
config.before(:all) do
|
14
|
+
add_fakes_to_path
|
15
|
+
create_tmp_directory
|
16
|
+
end
|
17
|
+
|
18
|
+
config.before(:each) do
|
19
|
+
FakeGithub.clear!
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module BulldozerTestHelpers
|
2
|
+
APP_NAME = "dummy_app"
|
3
|
+
|
4
|
+
def remove_project_directory
|
5
|
+
FileUtils.rm_rf(project_path)
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_tmp_directory
|
9
|
+
FileUtils.mkdir_p(tmp_path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run_bulldozer(arguments = nil)
|
13
|
+
arguments = "--path=#{root_path} #{arguments}"
|
14
|
+
run_in_tmp do
|
15
|
+
add_fakes_to_path
|
16
|
+
|
17
|
+
with_revision_for_honeybadger do
|
18
|
+
debug `#{bulldozer_bin} #{APP_NAME} #{arguments}`
|
19
|
+
end
|
20
|
+
|
21
|
+
Dir.chdir(APP_NAME) do
|
22
|
+
with_env("HOME", tmp_path) do
|
23
|
+
debug `git add .`
|
24
|
+
debug `git commit -m 'Initial commit'`
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def with_app
|
31
|
+
drop_dummy_database
|
32
|
+
remove_project_directory
|
33
|
+
rails_new
|
34
|
+
setup_app_dependencies
|
35
|
+
|
36
|
+
yield
|
37
|
+
end
|
38
|
+
|
39
|
+
def rails_new
|
40
|
+
run_in_tmp do
|
41
|
+
add_fakes_to_path
|
42
|
+
|
43
|
+
with_revision_for_honeybadger do
|
44
|
+
debug `#{system_rails_bin} new #{APP_NAME}`
|
45
|
+
end
|
46
|
+
|
47
|
+
Dir.chdir(APP_NAME) do
|
48
|
+
File.open("Gemfile", "a") do |file|
|
49
|
+
file.puts %{gem "bulldozer", path: #{root_path.inspect}}
|
50
|
+
end
|
51
|
+
|
52
|
+
with_env("HOME", tmp_path) do
|
53
|
+
debug `git add .`
|
54
|
+
debug `git commit -m 'Initial commit'`
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def generate(generator)
|
61
|
+
run_in_project do
|
62
|
+
with_revision_for_honeybadger do
|
63
|
+
debug `bin/spring stop`
|
64
|
+
debug `#{project_rails_bin} generate #{generator}`
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def destroy(generator)
|
70
|
+
run_in_project do
|
71
|
+
with_revision_for_honeybadger do
|
72
|
+
`bin/spring stop`
|
73
|
+
`#{project_rails_bin} destroy #{generator}`
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def bulldozer_help_command
|
79
|
+
run_in_tmp do
|
80
|
+
debug `#{bulldozer_bin} -h`
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def setup_app_dependencies
|
85
|
+
run_in_project do
|
86
|
+
debug `bundle check || bundle install`
|
87
|
+
end
|
88
|
+
rescue Errno::ENOENT
|
89
|
+
# The project_path might not exist, in which case we can skip this.
|
90
|
+
end
|
91
|
+
|
92
|
+
def drop_dummy_database
|
93
|
+
run_in_project do
|
94
|
+
debug `#{project_rails_bin} db:drop 2>&1`
|
95
|
+
end
|
96
|
+
rescue Errno::ENOENT
|
97
|
+
# The project_path might not exist, in which case we can skip this.
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_fakes_to_path
|
101
|
+
ENV["PATH"] = "#{support_bin}:#{ENV['PATH']}"
|
102
|
+
end
|
103
|
+
|
104
|
+
def project_path
|
105
|
+
@project_path ||= Pathname.new("#{tmp_path}/#{APP_NAME}")
|
106
|
+
end
|
107
|
+
|
108
|
+
def usage_file
|
109
|
+
@usage_path ||= File.join(root_path, "USAGE")
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def tmp_path
|
115
|
+
@tmp_path ||= Pathname.new("#{root_path}/tmp")
|
116
|
+
end
|
117
|
+
|
118
|
+
def bulldozer_bin
|
119
|
+
File.join(root_path, 'bin', 'bulldozer')
|
120
|
+
end
|
121
|
+
|
122
|
+
def system_rails_bin
|
123
|
+
"rails"
|
124
|
+
end
|
125
|
+
|
126
|
+
def project_rails_bin
|
127
|
+
"bin/rails"
|
128
|
+
end
|
129
|
+
|
130
|
+
def support_bin
|
131
|
+
File.join(root_path, "spec", "fakes", "bin")
|
132
|
+
end
|
133
|
+
|
134
|
+
def root_path
|
135
|
+
File.expand_path('../../../', __FILE__)
|
136
|
+
end
|
137
|
+
|
138
|
+
def with_env(name, new_value)
|
139
|
+
had_key = ENV.has_key?(name)
|
140
|
+
prior = ENV[name]
|
141
|
+
ENV[name] = new_value.to_s
|
142
|
+
|
143
|
+
yield
|
144
|
+
|
145
|
+
ensure
|
146
|
+
ENV.delete(name)
|
147
|
+
|
148
|
+
if had_key
|
149
|
+
ENV[name] = prior
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def with_revision_for_honeybadger
|
154
|
+
with_env("HEROKU_SLUG_COMMIT", 1) do
|
155
|
+
yield
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def run_in_tmp
|
160
|
+
Dir.chdir(tmp_path) do
|
161
|
+
Bundler.with_clean_env do
|
162
|
+
yield
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def run_in_project
|
168
|
+
Dir.chdir(project_path) do
|
169
|
+
Bundler.with_clean_env do
|
170
|
+
yield
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def debug(output)
|
176
|
+
if ENV["DEBUG"]
|
177
|
+
warn output
|
178
|
+
end
|
179
|
+
|
180
|
+
output
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
RSpec::Matchers.define :contain_json do
|
6
|
+
match do
|
7
|
+
sub_json = expected
|
8
|
+
filename = actual
|
9
|
+
|
10
|
+
filepath = File.join(project_path, filename)
|
11
|
+
json = JSON.parse(IO.read(filepath), symbolize_names: true)
|
12
|
+
sub_json <= json
|
13
|
+
end
|
14
|
+
|
15
|
+
failure_message do
|
16
|
+
sub_json = expected
|
17
|
+
filename = actual
|
18
|
+
|
19
|
+
filepath = File.join(project_path, filename)
|
20
|
+
json = JSON.parse(IO.read(filepath), symbolize_names: true)
|
21
|
+
|
22
|
+
"in #{filename}, expected to find\n#{sub_json.inspect}\nin\n#{json.inspect}"
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class FakeGithub
|
2
|
+
RECORDER = File.expand_path(File.join('..', '..', 'tmp', 'hub_commands'), File.dirname(__FILE__))
|
3
|
+
|
4
|
+
def initialize(args)
|
5
|
+
@args = args
|
6
|
+
end
|
7
|
+
|
8
|
+
def run!
|
9
|
+
File.open(RECORDER, 'a') do |file|
|
10
|
+
file.write @args.join(' ')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.clear!
|
15
|
+
FileUtils.rm_rf RECORDER
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.has_created_repo?(repo_name)
|
19
|
+
File.read(RECORDER) == "create #{repo_name}"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
class FakeHeroku
|
2
|
+
RECORDER = File.expand_path(File.join('..', '..', 'tmp', 'heroku_commands'), File.dirname(__FILE__))
|
3
|
+
|
4
|
+
def initialize(args)
|
5
|
+
@args = args
|
6
|
+
end
|
7
|
+
|
8
|
+
def run!
|
9
|
+
if @args.first == "help"
|
10
|
+
puts "pipelines # manage collections of apps in pipelines"
|
11
|
+
end
|
12
|
+
File.open(RECORDER, 'a') do |file|
|
13
|
+
file.puts @args.join(' ')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.clear!
|
18
|
+
FileUtils.rm_rf RECORDER
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.has_gem_included?(project_path, gem_name)
|
22
|
+
gemfile = File.open(File.join(project_path, 'Gemfile'), 'a')
|
23
|
+
|
24
|
+
File.foreach(gemfile).any? do |line|
|
25
|
+
line.match(/#{Regexp.quote(gem_name)}/)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.has_created_app_for?(environment, flags = nil)
|
30
|
+
app_name = "#{BulldozerTestHelpers::APP_NAME.dasherize}-#{environment}"
|
31
|
+
|
32
|
+
command = if flags
|
33
|
+
"create #{app_name} #{flags} --remote #{environment}\n"
|
34
|
+
else
|
35
|
+
"create #{app_name} --remote #{environment}\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
File.foreach(RECORDER).any? { |line| line == command }
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.has_configured_vars?(remote_name, var)
|
42
|
+
commands_ran =~ /^config:add #{var}=.+ --remote #{remote_name}\n/
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.has_setup_pipeline_for?(app_name)
|
46
|
+
commands_ran =~ /^pipelines:create #{app_name} -a #{app_name}-staging --stage staging/ &&
|
47
|
+
commands_ran =~ /^pipelines:add #{app_name} -a #{app_name}-production --stage production/
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.commands_ran
|
51
|
+
@commands_ran ||= File.read(RECORDER)
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ProjectFiles
|
2
|
+
def touch(filename)
|
3
|
+
path = File.join(project_path, filename)
|
4
|
+
dirname = File.dirname(path)
|
5
|
+
FileUtils.mkdir_p(dirname)
|
6
|
+
FileUtils.touch(path)
|
7
|
+
end
|
8
|
+
|
9
|
+
def rm(filename)
|
10
|
+
path = File.join(project_path, filename)
|
11
|
+
FileUtils.rm_rf(path)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
git_source(:github) do |repo_name|
|
4
|
+
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
|
5
|
+
"https://github.com/#{repo_name}.git"
|
6
|
+
end
|
7
|
+
|
8
|
+
ruby "<%= Bulldozer::RUBY_VERSION %>"
|
9
|
+
|
10
|
+
gem "autoprefixer-rails"
|
11
|
+
gem "honeybadger"
|
12
|
+
gem "pg"
|
13
|
+
gem "puma"
|
14
|
+
gem "rack-canonical-host"
|
15
|
+
gem "rails", "<%= Bulldozer::RAILS_VERSION %>"
|
16
|
+
gem "recipient_interceptor"
|
17
|
+
gem "sassc-rails"
|
18
|
+
gem "skylight"
|
19
|
+
gem "sprockets", ">= 3.0.0"
|
20
|
+
gem "title"
|
21
|
+
gem "tzinfo-data", platforms: [:mingw, :x64_mingw, :mswin, :jruby]
|
22
|
+
gem "uglifier"
|
23
|
+
gem "bootsnap", require: false
|
24
|
+
<% if options[:webpack] %>
|
25
|
+
gem "webpacker"
|
26
|
+
<% end %>
|
27
|
+
|
28
|
+
group :development do
|
29
|
+
gem "listen"
|
30
|
+
gem "rack-mini-profiler", require: false
|
31
|
+
gem "spring"
|
32
|
+
gem "web-console"
|
33
|
+
end
|
34
|
+
|
35
|
+
group :development, :test do
|
36
|
+
gem "awesome_print"
|
37
|
+
gem "bundler-audit", ">= 0.5.0", require: false
|
38
|
+
gem "dotenv-rails"
|
39
|
+
gem "pry-byebug"
|
40
|
+
gem "pry-rails"
|
41
|
+
end
|
42
|
+
|
43
|
+
group :test do
|
44
|
+
gem "formulaic"
|
45
|
+
gem "launchy"
|
46
|
+
gem "simplecov", require: false
|
47
|
+
gem "timecop"
|
48
|
+
gem "webmock"
|
49
|
+
end
|
50
|
+
|
51
|
+
gem "bulldozer", group: [:development, :test]
|
data/templates/Procfile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# <%= app_name.humanize %>
|
2
|
+
|
3
|
+
## Getting Started
|
4
|
+
|
5
|
+
After you have cloned this repo, run this setup script to set up your machine
|
6
|
+
with the necessary dependencies to run and test this app:
|
7
|
+
|
8
|
+
% ./bin/setup
|
9
|
+
|
10
|
+
It assumes you have a machine equipped with Ruby, Postgres, etc. If not, set up
|
11
|
+
your machine with [this script].
|
12
|
+
|
13
|
+
[this script]: https://github.com/SeasonedSoftware/laptop
|
14
|
+
|
15
|
+
After setting up, you can run the application using [Heroku Local]:
|
16
|
+
|
17
|
+
% heroku local
|
18
|
+
|
19
|
+
[Heroku Local]: https://devcenter.heroku.com/articles/heroku-local
|
20
|
+
|
21
|
+
## Guidelines
|
22
|
+
|
23
|
+
Use the following guides for getting things done, programming well, and
|
24
|
+
programming in style.
|
25
|
+
|
26
|
+
* [Protocol](http://github.com/SeasonedSoftware/guides/blob/master/protocol)
|
27
|
+
* [Best Practices](http://github.com/SeasonedSoftware/guides/blob/master/best-practices)
|
28
|
+
* [Style](http://github.com/SeasonedSoftware/guides/blob/master/style)
|