suspenders 20230113.0 → 20250317.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 (170) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +98 -170
  3. data/Rakefile +17 -0
  4. data/lib/generators/suspenders/accessibility_generator.rb +24 -0
  5. data/lib/generators/suspenders/advisories_generator.rb +32 -0
  6. data/lib/generators/suspenders/ci_generator.rb +54 -0
  7. data/lib/generators/suspenders/email_generator.rb +56 -0
  8. data/lib/generators/suspenders/environments/development_generator.rb +50 -0
  9. data/lib/generators/suspenders/environments/production_generator.rb +27 -0
  10. data/lib/generators/suspenders/environments/test_generator.rb +39 -0
  11. data/lib/generators/suspenders/factories_generator.rb +64 -0
  12. data/lib/generators/suspenders/inline_svg_generator.rb +24 -0
  13. data/lib/generators/suspenders/install/web_generator.rb +85 -0
  14. data/lib/generators/suspenders/jobs_generator.rb +34 -0
  15. data/lib/generators/suspenders/lint_generator.rb +94 -0
  16. data/lib/generators/suspenders/prerequisites_generator.rb +19 -0
  17. data/lib/generators/suspenders/rake_generator.rb +25 -0
  18. data/lib/generators/suspenders/setup_generator.rb +14 -0
  19. data/lib/generators/suspenders/styles_generator.rb +84 -0
  20. data/lib/generators/suspenders/tasks_generator.rb +20 -0
  21. data/lib/generators/suspenders/testing_generator.rb +113 -0
  22. data/lib/generators/suspenders/views_generator.rb +38 -0
  23. data/lib/generators/templates/ci/ci.yml.tt +148 -0
  24. data/lib/generators/templates/email/email_interceptor.rb +11 -0
  25. data/lib/generators/templates/factories/factories_spec.rb +7 -0
  26. data/lib/generators/templates/factories/factories_test.rb +9 -0
  27. data/lib/generators/templates/install/web/CONTRIBUTING.md +94 -0
  28. data/lib/generators/templates/lint/config_better_html.yml +2 -0
  29. data/lib/generators/templates/lint/config_initializers_better_html.rb +9 -0
  30. data/lib/generators/templates/lint/erb-lint.yml +63 -0
  31. data/lib/generators/templates/lint/erblint.rake +47 -0
  32. data/lib/generators/templates/lint/eslintrc.json +7 -0
  33. data/lib/generators/templates/lint/package.json +4 -0
  34. data/lib/generators/templates/lint/prettierignore +1 -0
  35. data/lib/generators/templates/lint/prettierrc +11 -0
  36. data/lib/generators/templates/lint/rubocop.yml.tt +7 -0
  37. data/lib/generators/templates/prerequisites/node-version.tt +1 -0
  38. data/lib/generators/templates/setup/bin_setup.rb +39 -0
  39. data/lib/generators/templates/styles/postcss.config.js +11 -0
  40. data/lib/generators/templates/testing/driver.rb +5 -0
  41. data/lib/generators/templates/views/flashes.html.erb +7 -0
  42. data/lib/install/web.rb +70 -0
  43. data/lib/suspenders/cleanup/generate_readme.rb +165 -0
  44. data/lib/suspenders/cleanup/organize_gemfile.rb +134 -0
  45. data/lib/suspenders/engine.rb +5 -0
  46. data/lib/suspenders/generators.rb +126 -0
  47. data/lib/suspenders/railtie.rb +4 -0
  48. data/lib/suspenders/version.rb +4 -6
  49. data/lib/suspenders.rb +9 -33
  50. data/lib/tasks/suspenders.rake +37 -0
  51. metadata +81 -173
  52. data/.ruby-version +0 -1
  53. data/CONTRIBUTING.md +0 -59
  54. data/GOALS.md +0 -65
  55. data/LICENSE +0 -21
  56. data/NEWS.md +0 -738
  57. data/RELEASING.md +0 -18
  58. data/bin/suspenders +0 -50
  59. data/docs/heroku_deploy.md +0 -19
  60. data/docs/rails_7.md +0 -5
  61. data/lib/suspenders/actions/strip_comments_action.rb +0 -254
  62. data/lib/suspenders/actions.rb +0 -106
  63. data/lib/suspenders/adapters/heroku.rb +0 -136
  64. data/lib/suspenders/app_builder.rb +0 -267
  65. data/lib/suspenders/exit_on_failure.rb +0 -19
  66. data/lib/suspenders/generators/accessibility_generator.rb +0 -12
  67. data/lib/suspenders/generators/advisories_generator.rb +0 -15
  68. data/lib/suspenders/generators/analytics_generator.rb +0 -24
  69. data/lib/suspenders/generators/app_generator.rb +0 -215
  70. data/lib/suspenders/generators/base.rb +0 -60
  71. data/lib/suspenders/generators/ci_generator.rb +0 -32
  72. data/lib/suspenders/generators/db_optimizations_generator.rb +0 -18
  73. data/lib/suspenders/generators/factories_generator.rb +0 -22
  74. data/lib/suspenders/generators/forms_generator.rb +0 -18
  75. data/lib/suspenders/generators/inline_svg_generator.rb +0 -14
  76. data/lib/suspenders/generators/jobs_generator.rb +0 -37
  77. data/lib/suspenders/generators/js_driver_generator.rb +0 -18
  78. data/lib/suspenders/generators/json_generator.rb +0 -14
  79. data/lib/suspenders/generators/lint_generator.rb +0 -14
  80. data/lib/suspenders/generators/production/compression_generator.rb +0 -14
  81. data/lib/suspenders/generators/production/deployment_generator.rb +0 -16
  82. data/lib/suspenders/generators/production/email_generator.rb +0 -34
  83. data/lib/suspenders/generators/production/force_tls_generator.rb +0 -11
  84. data/lib/suspenders/generators/production/manifest_generator.rb +0 -25
  85. data/lib/suspenders/generators/production/single_redirect.rb +0 -15
  86. data/lib/suspenders/generators/production/timeout_generator.rb +0 -22
  87. data/lib/suspenders/generators/profiler_generator.rb +0 -35
  88. data/lib/suspenders/generators/runner_generator.rb +0 -48
  89. data/lib/suspenders/generators/staging/pull_requests_generator.rb +0 -25
  90. data/lib/suspenders/generators/static_generator.rb +0 -14
  91. data/lib/suspenders/generators/stylelint_generator.rb +0 -71
  92. data/lib/suspenders/generators/stylesheet_base_generator.rb +0 -15
  93. data/lib/suspenders/generators/testing_generator.rb +0 -43
  94. data/lib/suspenders/generators/views_generator.rb +0 -25
  95. data/templates/Gemfile.erb +0 -49
  96. data/templates/Procfile +0 -2
  97. data/templates/README.md.erb +0 -28
  98. data/templates/_analytics.html.erb +0 -8
  99. data/templates/_css_overrides.html.erb +0 -7
  100. data/templates/_flashes.html.erb +0 -7
  101. data/templates/active_job.rb +0 -14
  102. data/templates/application.postcss.css +0 -1
  103. data/templates/bin_auto_migrate +0 -5
  104. data/templates/bin_deploy +0 -10
  105. data/templates/bin_setup +0 -28
  106. data/templates/bin_setup_review_app.erb +0 -21
  107. data/templates/bin_yarn +0 -18
  108. data/templates/bundler_audit.rake +0 -4
  109. data/templates/capybara_silence_puma.rb +0 -1
  110. data/templates/chromedriver.rb +0 -27
  111. data/templates/circle.yml.erb +0 -6
  112. data/templates/config_locales_en.yml.erb +0 -19
  113. data/templates/descriptions/advisories.md +0 -5
  114. data/templates/descriptions/analytics.md +0 -4
  115. data/templates/descriptions/ci.md +0 -4
  116. data/templates/descriptions/compression.md +0 -4
  117. data/templates/descriptions/db_optimizations.md +0 -2
  118. data/templates/descriptions/deployment.md +0 -5
  119. data/templates/descriptions/email.md +0 -9
  120. data/templates/descriptions/factories.md +0 -12
  121. data/templates/descriptions/force_tls.md +0 -1
  122. data/templates/descriptions/forms.md +0 -1
  123. data/templates/descriptions/inline_svg.md +0 -2
  124. data/templates/descriptions/jobs.md +0 -3
  125. data/templates/descriptions/js_driver.md +0 -4
  126. data/templates/descriptions/json.md +0 -1
  127. data/templates/descriptions/lint.md +0 -3
  128. data/templates/descriptions/manifest.md +0 -2
  129. data/templates/descriptions/profiler.md +0 -7
  130. data/templates/descriptions/pull_requests.md +0 -4
  131. data/templates/descriptions/runner.md +0 -10
  132. data/templates/descriptions/single_redirect.md +0 -1
  133. data/templates/descriptions/static.md +0 -5
  134. data/templates/descriptions/stylelint.md +0 -3
  135. data/templates/descriptions/stylesheet_base.md +0 -1
  136. data/templates/descriptions/testing.md +0 -9
  137. data/templates/descriptions/timeout.md +0 -4
  138. data/templates/descriptions/views.md +0 -8
  139. data/templates/email.rb +0 -3
  140. data/templates/errors.rb +0 -35
  141. data/templates/flashes_helper.rb +0 -5
  142. data/templates/hound.yml +0 -15
  143. data/templates/json_encoding.rb +0 -1
  144. data/templates/oj.rb +0 -3
  145. data/templates/partials/ci_simplecov.rb +0 -14
  146. data/templates/partials/db_optimizations_configuration.rb +0 -7
  147. data/templates/partials/deployment_readme.md +0 -8
  148. data/templates/partials/email_smtp.rb +0 -2
  149. data/templates/partials/profiler_readme.md +0 -8
  150. data/templates/partials/pull_requests_config.rb +0 -5
  151. data/templates/partials/runner_readme.md +0 -31
  152. data/templates/partials/runner_setup.rb +0 -2
  153. data/templates/postcss.config.js +0 -8
  154. data/templates/postgresql_database.yml.erb +0 -20
  155. data/templates/rack_mini_profiler.rb +0 -7
  156. data/templates/rails_helper.rb +0 -25
  157. data/templates/sample_env +0 -12
  158. data/templates/secrets.yml +0 -8
  159. data/templates/smtp.rb +0 -9
  160. data/templates/spec_helper.rb +0 -25
  161. data/templates/suspenders_gitignore +0 -18
  162. data/templates/suspenders_layout.html.erb.erb +0 -23
  163. /data/{templates → lib/generators/templates/factories}/factories.rb +0 -0
  164. /data/{templates → lib/generators/templates/factories}/factory_bot_rspec.rb +0 -0
  165. /data/{templates → lib/generators/templates/inline_svg}/inline_svg.rb +0 -0
  166. /data/{templates → lib/generators/templates/lint}/stylelintrc.json +0 -0
  167. /data/{templates → lib/generators/templates/tasks}/dev.rake +0 -0
  168. /data/{templates → lib/generators/templates/testing}/action_mailer.rb +0 -0
  169. /data/{templates → lib/generators/templates/testing}/i18n.rb +0 -0
  170. /data/{templates/shoulda_matchers_config_rspec.rb → lib/generators/templates/testing/shoulda_matchers.rb} +0 -0
data/RELEASING.md DELETED
@@ -1,18 +0,0 @@
1
- # Releasing
2
-
3
- 1. Update `NEWS.md` to reflect the changes since last release.
4
- 2. Update `lib/suspenders/version.rb` file accordingly.
5
- 3. Commit changes. There shouldn't be code changes, and thus CI doesn't need to
6
- run; you can add `[ci skip]` to the commit message.
7
- 4. Tag the release: `git tag vVERSION -a -s`. The tag message should contain the
8
- appropriate `NEWS.md` subsection.
9
- 5. Push changes: `git push --tags`
10
- 6. Build and publish to rubygems:
11
- ```sh
12
- gem build suspenders.gemspec
13
- gem push suspenders-*.gem
14
- ```
15
- 7. Add a new GitHub release:
16
- https://github.com/thoughtbot/suspenders/releases/new?tag=vVERSION
17
- 8. Announce the new release, making sure to thank the contributors who helped
18
- shape this version!
data/bin/suspenders DELETED
@@ -1,50 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require 'pathname'
3
-
4
- source_path = (Pathname.new(__FILE__).dirname + '../lib').expand_path
5
- $LOAD_PATH << source_path
6
-
7
- activate_rails_version = ->(rails_version) do
8
- rails_bin_path = Gem.activate_bin_path("railties", "rails", rails_version)
9
- rails_path = File.expand_path("../..", rails_bin_path)
10
- $LOAD_PATH.unshift(rails_path)
11
- end
12
-
13
- if str = ARGV.first
14
- str = str.b[/\A_(.*)_\z/, 1]
15
-
16
- if str && Gem::Version.correct?(str)
17
- rails_version = str
18
- ARGV.shift
19
-
20
- begin
21
- activate_rails_version.call(rails_version)
22
- rescue Gem::GemNotFoundException
23
- abort "Suspenders error: Unable to find Rails version #{rails_version}"
24
- end
25
- else
26
- require "suspenders/version"
27
-
28
- spec = Gem::Specification.find_by_name("rails", Suspenders::RAILS_VERSION)
29
-
30
- activate_rails_version.call(spec.version.to_s)
31
- end
32
- end
33
-
34
- require "suspenders"
35
-
36
- if ARGV.empty?
37
- puts "Please provide a path for the new application"
38
- puts
39
- puts "See --help for more info"
40
- exit 0
41
- elsif ["-v", "--version"].include? ARGV[0]
42
- puts Suspenders::VERSION
43
- exit 0
44
- end
45
-
46
- templates_root = File.expand_path(File.join("..", "templates"), File.dirname(__FILE__))
47
- Suspenders::AppGenerator.source_root templates_root
48
- Suspenders::AppGenerator.source_paths << Rails::Generators::AppGenerator.source_root << templates_root
49
-
50
- Suspenders::AppGenerator.start
@@ -1,19 +0,0 @@
1
- # Deploying to Heroku
2
-
3
- ## Setup
4
-
5
- Deploying to Heroku requires two additional steps:
6
-
7
- 1. Set the following environment variables:
8
-
9
- - `ASSET_HOST`: `siteURL.herokuapp.com`
10
- - `APPLICATION_HOST`: `siteURL.herokuapp.com`
11
- - `SMTP_ADDRESS`: `smtp.example.com`
12
- - `SMTP_DOMAIN`: `example.com`
13
- - `SMTP_USERNAME`: `username`
14
- - `SMTP_PASSWORD`: `password`
15
- - `AUTO_MIGRATE_DB`: `true`
16
-
17
- ## Execution
18
-
19
- - Use the `./bin/deploy` convention for deploying to Heroku
data/docs/rails_7.md DELETED
@@ -1,5 +0,0 @@
1
- # Rails 7
2
-
3
- To create a new Rails 7 app with Suspenders, you need to have `node >= 14.6`.
4
-
5
- After the app is created, run the server with `bin/dev`.
@@ -1,254 +0,0 @@
1
- require "parser/current"
2
-
3
- module Suspenders
4
- module Actions
5
- class StripCommentsAction
6
- class << self
7
- def call(source)
8
- parser = Parser::CurrentRuby.new
9
-
10
- source
11
- .then { |s| strip_comments(s, parser) }
12
- .then { |s| strip_trailing_whitespace(s) }
13
- .then { |s| strip_dup_newlines(s) }
14
- .then { |s| strip_leading_scope_newlines(s, parser) }
15
- end
16
-
17
- private
18
-
19
- def strip_comments(source, parser)
20
- StripComments.call(source, parser.reset)
21
- end
22
-
23
- def strip_trailing_whitespace(source)
24
- source.gsub(/[[:blank:]]+$/, "")
25
- end
26
-
27
- def strip_dup_newlines(source)
28
- source.gsub(/\n{2,}/, "\n\n")
29
- end
30
-
31
- def strip_leading_scope_newlines(source, parser)
32
- StripLeadingScopeNewlines.call(source, parser.reset)
33
- end
34
- end
35
-
36
- # Strips full-line and inline comments from a buffer but does
37
- # not remove whitespaces or newlines after the fact. Example
38
- # input:
39
- #
40
- # MyGem.application.configure do |config|
41
- # # Full-line comment
42
- # config.option1 = :value # Inline comment
43
- # end
44
- #
45
- # The output is:
46
- #
47
- # MyGem.application.configure do |config|
48
- #
49
- # config.option1 = :value
50
- # end
51
- class StripComments
52
- class << self
53
- def call(source, parser)
54
- buffer = Parser::Source::Buffer.new(nil, source: source)
55
- rewriter = Parser::Source::TreeRewriter.new(buffer)
56
-
57
- _, comments = parser.parse_with_comments(buffer)
58
-
59
- comments.each do |comment|
60
- strip_comment(comment, buffer, rewriter)
61
- end
62
-
63
- rewriter.process
64
- end
65
-
66
- private
67
-
68
- def strip_comment(comment, buffer, rewriter)
69
- expr = comment.location.expression
70
-
71
- if full_line_comment?(expr)
72
- expr = full_line_comment_expr(expr, buffer)
73
- end
74
-
75
- rewriter.remove(expr)
76
- end
77
-
78
- def full_line_comment_expr(expr, buffer)
79
- pos = BackwardStringScanner.beginning_of_line_pos(expr, expr.begin_pos)
80
-
81
- Parser::Source::Range.new(buffer, pos, expr.end_pos + 1)
82
- end
83
-
84
- def full_line_comment?(expr)
85
- expr.source == expr.source_line.lstrip
86
- end
87
- end
88
- end
89
-
90
- # A tiny, non-stateful backward string scanner somewhat inspired
91
- # by Ruby's StringScanner. Ruby's StringScanner is unable to
92
- # seek backward on a string.
93
- class BackwardStringScanner
94
- def self.beginning_of_line_pos(expr, initial_pos)
95
- skip_before(expr, initial_pos) { |char| char == "\n" }
96
- end
97
-
98
- def self.skip_before(expr, initial_pos, &block)
99
- skip_until(expr, initial_pos, -1, &block)
100
- end
101
-
102
- def self.skip_until(expr, initial_pos, lookup_inc = 0)
103
- pos = initial_pos
104
-
105
- loop do
106
- break if pos.zero?
107
- char = expr.source_buffer.source[pos + lookup_inc]
108
- break if yield(char)
109
- pos -= 1
110
- end
111
-
112
- pos
113
- end
114
- end
115
-
116
- # The intent of this class is purely aesthetic: remove leading
117
- # newlines inside of code scopes like blocks and begin/end.
118
- # Example input:
119
- #
120
- # module MyGem
121
- #
122
- # MyGem.application.configure do |config|
123
- #
124
- # config.option1 = true
125
- #
126
- # config.option2 = false
127
- # end
128
- # end
129
- #
130
- # The output is:
131
- #
132
- # module MyGem
133
- # MyGem.application.configure do |config|
134
- # config.option1 = true
135
- #
136
- # config.option2 = false
137
- # end
138
- # end
139
- class StripLeadingScopeNewlines
140
- def self.call(source, parser)
141
- buffer = Parser::Source::Buffer.new(nil, source: source)
142
- ast = parser.parse(buffer)
143
-
144
- LeadingNewlineStripRewriter.new.rewrite(buffer, ast).lstrip
145
- end
146
-
147
- class LeadingNewlineStripRewriter < Parser::TreeRewriter
148
- def on_module(node)
149
- strip_newline_before(node.children[1])
150
- strip_newline_after(node.children.last)
151
-
152
- super
153
- end
154
-
155
- def on_class(node)
156
- strip_newline_before(node.children[2])
157
- strip_newline_after(node.children.last)
158
-
159
- super
160
- end
161
-
162
- def on_begin(node)
163
- handle_begin(node)
164
-
165
- super
166
- end
167
-
168
- def on_kwbegin(node)
169
- strip_newline_before(node.children[0])
170
- strip_newline_after(node.children.last)
171
-
172
- handle_begin(node)
173
-
174
- super
175
- end
176
-
177
- def on_block(node)
178
- strip_newline_before(node.children[2])
179
- strip_newline_after(node.children.last)
180
-
181
- super
182
- end
183
-
184
- private
185
-
186
- def handle_begin(node)
187
- strip_blank_lines_between_setter_calls(node.children)
188
-
189
- node.children.each do |child_node|
190
- send("on_#{child_node.type}", child_node)
191
- end
192
- end
193
-
194
- def strip_blank_lines_between_setter_calls(children)
195
- pairs = children.each_cons(2).to_a
196
-
197
- pairs.each do |(node_before, node_after)|
198
- if setter_call?(node_before) && setter_call?(node_after)
199
- strip_newline_before(node_after)
200
- end
201
- end
202
- end
203
-
204
- def setter_call?(node)
205
- node.children[1].to_s.end_with?("=")
206
- end
207
-
208
- def strip_newline_before(node)
209
- return if node.nil?
210
-
211
- expr = node.location.expression
212
- end_pos = find_end_pos(expr, expr.begin_pos)
213
- begin_pos = find_begin_pos(expr, end_pos)
214
-
215
- strip_source_range(expr, begin_pos, end_pos)
216
- end
217
-
218
- def strip_newline_after(node)
219
- return if node.nil?
220
-
221
- expr = node.location.expression
222
- source = expr.source_buffer.source
223
-
224
- if source[expr.end_pos + 1] == "\n"
225
- strip_source_range(expr, expr.end_pos, expr.end_pos + 1)
226
- end
227
- end
228
-
229
- def find_end_pos(expr, begin_pos)
230
- BackwardStringScanner.skip_until(expr, begin_pos) do |char|
231
- char == "\n"
232
- end
233
- end
234
-
235
- def find_begin_pos(expr, end_pos)
236
- BackwardStringScanner.skip_before(expr, end_pos) do |char|
237
- char != "\n" && char != " "
238
- end
239
- end
240
-
241
- def strip_source_range(expr, begin_pos, end_pos)
242
- remove(
243
- Parser::Source::Range.new(
244
- expr.source_buffer,
245
- begin_pos,
246
- end_pos
247
- )
248
- )
249
- end
250
- end
251
- end
252
- end
253
- end
254
- end
@@ -1,106 +0,0 @@
1
- module Suspenders
2
- module Actions
3
- def replace_in_file(relative_path, find, replace)
4
- path = File.join(destination_root, relative_path)
5
- contents = IO.read(path)
6
- unless contents.gsub!(find, replace)
7
- raise "#{find.inspect} not found in #{relative_path}"
8
- end
9
- File.write(path, contents)
10
- end
11
-
12
- def action_mailer_host(rails_env, host)
13
- config = "config.action_mailer.default_url_options = { host: #{host} }"
14
- configure_environment(rails_env, config)
15
- end
16
-
17
- def action_mailer_asset_host(rails_env, host)
18
- config = "config.action_mailer.asset_host = #{host}"
19
- configure_environment(rails_env, config)
20
- end
21
-
22
- def configure_environment(rails_env, config)
23
- inject_into_file(
24
- "config/environments/#{rails_env}.rb",
25
- "\n #{config}",
26
- before: "\nend"
27
- )
28
- end
29
-
30
- def expand_json(file, data)
31
- action ExpandJson.new(destination_root, file, data)
32
- end
33
-
34
- def gem(*args)
35
- options = args.extract_options!
36
- name, *versions = args
37
-
38
- parts = [quote(name)]
39
-
40
- if (versions = versions.any? ? versions : options.delete(:version))
41
- versions = Array(versions)
42
- versions.each do |version|
43
- parts << quote(version)
44
- end
45
- end
46
-
47
- parts << quote(options) unless options.empty?
48
-
49
- str = []
50
- str << indentation
51
- str << "gem #{parts.join(", ")}"
52
- append_file "Gemfile", %(\n#{str.join}\n), verbose: false
53
- end
54
-
55
- class ExpandJson
56
- def initialize(destination_root, file, data)
57
- @destination_root = destination_root
58
- @file = file
59
- @data = data
60
- end
61
-
62
- def invoke!
63
- write_out { |existing_json| existing_json.deep_merge(data) }
64
- end
65
-
66
- def revoke!
67
- write_out { |existing_json| hash_unmerge(existing_json, data) }
68
- end
69
-
70
- private
71
-
72
- attr_reader :destination_root, :file, :data
73
-
74
- def write_out
75
- new_json = yield(existing_json)
76
- IO.write(destination_file, JSON.pretty_generate(new_json))
77
- end
78
-
79
- def destination_file
80
- File.join(destination_root, file)
81
- end
82
-
83
- def existing_json
84
- JSON.parse(IO.read(destination_file), symbolize_names: true)
85
- rescue Errno::ENOENT
86
- {}
87
- end
88
-
89
- def hash_unmerge(hash, subhash)
90
- subhash.reduce(hash) do |acc, (k, v)|
91
- if hash.has_key?(k)
92
- if v == hash[k]
93
- acc.except(k)
94
- elsif v.is_a?(Hash)
95
- acc.merge(k => hash_unmerge(hash[k], v))
96
- else
97
- acc
98
- end
99
- else
100
- acc
101
- end
102
- end
103
- end
104
- end
105
- end
106
- end
@@ -1,136 +0,0 @@
1
- module Suspenders
2
- module Adapters
3
- class Heroku
4
- def initialize(app_builder)
5
- @app_builder = app_builder
6
- end
7
-
8
- def set_heroku_remotes
9
- remotes = <<~SHELL
10
- #{command_to_join_heroku_app("staging")}
11
- #{command_to_join_heroku_app("production")}
12
-
13
- git config heroku.remote staging
14
- SHELL
15
-
16
- app_builder.append_file "bin/setup", remotes
17
- end
18
-
19
- def create_staging_heroku_app(flags)
20
- app_name = heroku_app_name_for("staging")
21
-
22
- run_toolbelt_command "create #{app_name} #{flags}", "staging"
23
- end
24
-
25
- def create_production_heroku_app(flags)
26
- app_name = heroku_app_name_for("production")
27
-
28
- run_toolbelt_command "create #{app_name} #{flags}", "production"
29
- end
30
-
31
- def set_heroku_rails_secrets
32
- %w[staging production].each do |environment|
33
- run_toolbelt_command(
34
- "config:add SECRET_KEY_BASE=#{generate_secret}",
35
- environment
36
- )
37
- end
38
- end
39
-
40
- def set_heroku_honeybadger_env
41
- %w[staging production].each do |environment|
42
- run_toolbelt_command(
43
- "config:add HONEYBADGER_ENV=#{environment}",
44
- environment
45
- )
46
- end
47
- end
48
-
49
- def set_heroku_backup_schedule
50
- %w[staging production].each do |environment|
51
- run_toolbelt_command(
52
- "pg:backups:schedule DATABASE_URL --at '10:00 UTC'",
53
- environment
54
- )
55
- end
56
- end
57
-
58
- def create_heroku_pipeline
59
- pipelines_plugin = `heroku help | grep pipelines`
60
- if pipelines_plugin.empty?
61
- puts "You need heroku pipelines plugin. Run: brew upgrade heroku-toolbelt"
62
- exit 1
63
- end
64
-
65
- run_toolbelt_command(
66
- "pipelines:create #{heroku_app_name} \
67
- -a #{heroku_app_name}-staging --stage staging",
68
- "staging"
69
- )
70
-
71
- run_toolbelt_command(
72
- "pipelines:add #{heroku_app_name} \
73
- -a #{heroku_app_name}-production --stage production",
74
- "production"
75
- )
76
- end
77
-
78
- def set_heroku_application_host
79
- %w[staging production].each do |environment|
80
- run_toolbelt_command(
81
- "config:add APPLICATION_HOST=#{heroku_app_name}-#{environment}.herokuapp.com",
82
- environment
83
- )
84
- end
85
- end
86
-
87
- def set_heroku_buildpacks
88
- %w[staging production].each do |environment|
89
- run_toolbelt_command(
90
- "buildpacks:add --index 1 heroku/nodejs",
91
- environment
92
- )
93
- run_toolbelt_command(
94
- "buildpacks:add --index 2 heroku/ruby",
95
- environment
96
- )
97
- end
98
- end
99
-
100
- private
101
-
102
- attr_reader :app_builder
103
-
104
- def command_to_join_heroku_app(environment)
105
- heroku_app_name = heroku_app_name_for(environment)
106
- <<~SHELL
107
-
108
- if heroku apps:info --app #{heroku_app_name} > /dev/null 2>&1; then
109
- git remote add #{environment} git@heroku.com:#{heroku_app_name}.git || true
110
- printf 'You are a collaborator on the "#{heroku_app_name}" Heroku app\n'
111
- else
112
- printf 'Ask for access to the "#{heroku_app_name}" Heroku app\n'
113
- fi
114
- SHELL
115
- end
116
-
117
- def heroku_app_name
118
- app_builder.app_name.dasherize
119
- end
120
-
121
- def heroku_app_name_for(environment)
122
- "#{heroku_app_name}-#{environment}"
123
- end
124
-
125
- def generate_secret
126
- SecureRandom.hex(64)
127
- end
128
-
129
- def run_toolbelt_command(command, environment)
130
- app_builder.run(
131
- "heroku #{command} --remote #{environment}"
132
- )
133
- end
134
- end
135
- end
136
- end