bulldozer 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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)