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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +12 -0
  5. data/CONTRIBUTING.md +59 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +21 -0
  8. data/NEWS.md +638 -0
  9. data/README.md +225 -0
  10. data/RELEASING.md +18 -0
  11. data/Rakefile +8 -0
  12. data/USAGE +13 -0
  13. data/bin/bulldozer +23 -0
  14. data/bin/rake +16 -0
  15. data/bin/rspec +16 -0
  16. data/bin/setup +13 -0
  17. data/bulldozer.gemspec +35 -0
  18. data/docker-compose.yml +8 -0
  19. data/dockerfile +11 -0
  20. data/lib/bulldozer.rb +24 -0
  21. data/lib/bulldozer/actions.rb +85 -0
  22. data/lib/bulldozer/adapters/heroku.rb +123 -0
  23. data/lib/bulldozer/app_builder.rb +321 -0
  24. data/lib/bulldozer/generators/analytics_generator.rb +24 -0
  25. data/lib/bulldozer/generators/app_generator.rb +231 -0
  26. data/lib/bulldozer/generators/base.rb +20 -0
  27. data/lib/bulldozer/generators/ci_generator.rb +22 -0
  28. data/lib/bulldozer/generators/db_optimizations_generator.rb +30 -0
  29. data/lib/bulldozer/generators/factories_generator.rb +22 -0
  30. data/lib/bulldozer/generators/forms_generator.rb +18 -0
  31. data/lib/bulldozer/generators/jobs_generator.rb +38 -0
  32. data/lib/bulldozer/generators/js_driver_generator.rb +15 -0
  33. data/lib/bulldozer/generators/json_generator.rb +10 -0
  34. data/lib/bulldozer/generators/lint_generator.rb +9 -0
  35. data/lib/bulldozer/generators/production/deployment_generator.rb +27 -0
  36. data/lib/bulldozer/generators/production/email_generator.rb +37 -0
  37. data/lib/bulldozer/generators/production/force_tls_generator.rb +11 -0
  38. data/lib/bulldozer/generators/production/manifest_generator.rb +24 -0
  39. data/lib/bulldozer/generators/production/timeout_generator.rb +21 -0
  40. data/lib/bulldozer/generators/staging/pull_requests_generator.rb +33 -0
  41. data/lib/bulldozer/generators/static_generator.rb +10 -0
  42. data/lib/bulldozer/generators/stylesheet_base_generator.rb +31 -0
  43. data/lib/bulldozer/generators/testing_generator.rb +55 -0
  44. data/lib/bulldozer/generators/views_generator.rb +30 -0
  45. data/lib/bulldozer/version.rb +8 -0
  46. data/spec/adapters/heroku_spec.rb +72 -0
  47. data/spec/fakes/bin/heroku +5 -0
  48. data/spec/fakes/bin/hub +5 -0
  49. data/spec/features/api_spec.rb +18 -0
  50. data/spec/features/cli_help_spec.rb +36 -0
  51. data/spec/features/github_spec.rb +16 -0
  52. data/spec/features/heroku_spec.rb +71 -0
  53. data/spec/features/json_spec.rb +15 -0
  54. data/spec/features/new_project_spec.rb +341 -0
  55. data/spec/features/production/deployment_spec.rb +22 -0
  56. data/spec/features/production/email_spec.rb +47 -0
  57. data/spec/features/production/manifest_spec.rb +35 -0
  58. data/spec/features/staging/pull_requests_spec.rb +22 -0
  59. data/spec/spec_helper.rb +21 -0
  60. data/spec/support/be_executable_matcher.rb +7 -0
  61. data/spec/support/bulldozer.rb +182 -0
  62. data/spec/support/contain_json_matcher.rb +24 -0
  63. data/spec/support/exist_as_a_file_matcher.rb +7 -0
  64. data/spec/support/fake_github.rb +21 -0
  65. data/spec/support/fake_heroku.rb +53 -0
  66. data/spec/support/generators.rb +5 -0
  67. data/spec/support/match_contents_matcher.rb +6 -0
  68. data/spec/support/project_files.rb +13 -0
  69. data/templates/Gemfile.erb +51 -0
  70. data/templates/Procfile +2 -0
  71. data/templates/README.md.erb +28 -0
  72. data/templates/_analytics.html.erb +8 -0
  73. data/templates/_css_overrides.html.erb +7 -0
  74. data/templates/_flashes.html.erb +7 -0
  75. data/templates/_javascript.html.erb +3 -0
  76. data/templates/action_mailer.rb +5 -0
  77. data/templates/active_job.rb +13 -0
  78. data/templates/application.scss +8 -0
  79. data/templates/bin_deploy +12 -0
  80. data/templates/bin_setup +28 -0
  81. data/templates/bin_setup_review_app.erb +22 -0
  82. data/templates/browserslist +3 -0
  83. data/templates/bulldozer_gitignore +18 -0
  84. data/templates/bulldozer_layout.html.erb.erb +21 -0
  85. data/templates/bundler_audit.rake +4 -0
  86. data/templates/chromedriver.rb +17 -0
  87. data/templates/circle.yml.erb +6 -0
  88. data/templates/config_locales_en.yml.erb +19 -0
  89. data/templates/dev.rake +12 -0
  90. data/templates/dotfiles/.ctags +2 -0
  91. data/templates/dotfiles/.env +13 -0
  92. data/templates/email.rb +3 -0
  93. data/templates/errors.rb +34 -0
  94. data/templates/factories.rb +2 -0
  95. data/templates/factory_bot_rspec.rb +5 -0
  96. data/templates/flashes_helper.rb +5 -0
  97. data/templates/hound.yml +14 -0
  98. data/templates/i18n.rb +3 -0
  99. data/templates/json_encoding.rb +1 -0
  100. data/templates/postgresql_database.yml.erb +19 -0
  101. data/templates/puma.rb +28 -0
  102. data/templates/rack_mini_profiler.rb +5 -0
  103. data/templates/rails_helper.rb +22 -0
  104. data/templates/secrets.yml +8 -0
  105. data/templates/shoulda_matchers_config_rspec.rb +6 -0
  106. data/templates/smtp.rb +9 -0
  107. data/templates/spec_helper.rb +28 -0
  108. 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
@@ -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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :be_executable do
4
+ match do |filename|
5
+ File.stat(File.join(project_path, filename)).executable?
6
+ end
7
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec::Matchers.define :exist_as_a_file do
4
+ match do |filename|
5
+ File.exist?(File.join(project_path, filename))
6
+ end
7
+ 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,5 @@
1
+ RSpec.configure do |config|
2
+ config.before(:each, type: :generator) do
3
+ remove_project_directory
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ RSpec::Matchers.define :match_contents do |regexp|
2
+ match do |filename|
3
+ contents = IO.read(File.join(project_path, filename))
4
+ contents =~ regexp
5
+ end
6
+ 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]
@@ -0,0 +1,2 @@
1
+ web: bundle exec puma -p $PORT -C ./config/puma.rb
2
+ worker: bundle exec rails jobs:work
@@ -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)