hephaestus 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +13 -0
- data/LICENSE.txt +9 -0
- data/README.md +19 -0
- data/bin/hephaestus +55 -0
- data/lib/hephaestus/actions/strip_comments_action.rb +263 -0
- data/lib/hephaestus/actions.rb +116 -0
- data/lib/hephaestus/app_builder.rb +168 -0
- data/lib/hephaestus/exit_on_failure.rb +22 -0
- data/lib/hephaestus/generators/app_generator.rb +158 -0
- data/lib/hephaestus/generators/base.rb +65 -0
- data/lib/hephaestus/generators/config_generator.rb +102 -0
- data/lib/hephaestus/generators/core_generator.rb +50 -0
- data/lib/hephaestus/generators/deployment_generator.rb +18 -0
- data/lib/hephaestus/generators/lib_generator.rb +16 -0
- data/lib/hephaestus/generators/license_generator.rb +16 -0
- data/lib/hephaestus/generators/rubocop_generator.rb +18 -0
- data/lib/hephaestus/generators/sorbet_generator.rb +16 -0
- data/lib/hephaestus/version.rb +11 -0
- data/lib/hephaestus.rb +21 -0
- data/templates/Gemfile.erb +121 -0
- data/templates/Procfile.debug +2 -0
- data/templates/Procfile.dev +2 -0
- data/templates/README.md.erb +1 -0
- data/templates/app/controllers/application_controller.rb +107 -0
- data/templates/app/controllers/concerns/authable.rb +32 -0
- data/templates/app/controllers/root_controller.rb +11 -0
- data/templates/app/controllers/settings_controller.rb +7 -0
- data/templates/app/controllers/staff_controller.rb +15 -0
- data/templates/app/controllers/yetto_controller.rb +30 -0
- data/templates/app/jobs/application_job.rb +10 -0
- data/templates/app/jobs/update_yetto_job.rb +27 -0
- data/templates/app/lib/body_parameter/yetto_parameters.rb +8 -0
- data/templates/app/lib/body_parameter.rb +6 -0
- data/templates/app/lib/constants/app.rb +8 -0
- data/templates/app/lib/headers/yetto.rb +17 -0
- data/templates/app/lib/headers.rb +5 -0
- data/templates/app/lib/path_parameter/yetto_parameters.rb +25 -0
- data/templates/app/lib/path_parameter.rb +8 -0
- data/templates/app/lib/plug_app/http.rb +34 -0
- data/templates/app/lib/plug_app/middleware/malformed_request.rb +110 -0
- data/templates/app/lib/plug_app/middleware/not_found.rb +41 -0
- data/templates/app/lib/plug_app/middleware/openapi_validation.rb +54 -0
- data/templates/app/lib/plug_app/middleware/tracing_attributes.rb +42 -0
- data/templates/app/lib/query_parameter.rb +6 -0
- data/templates/app/serializers/error_serializer.rb +16 -0
- data/templates/app/services/yetto_service.rb +61 -0
- data/templates/app/views/settings/index.json.jbuilder +15 -0
- data/templates/config/initializers/cors.rb +18 -0
- data/templates/config/initializers/environment.rb +30 -0
- data/templates/config/initializers/filter_parameter_logging.rb +22 -0
- data/templates/config/initializers/inflections.rb +20 -0
- data/templates/config/initializers/lograge.rb +25 -0
- data/templates/config/initializers/open_telemetry.rb +27 -0
- data/templates/config/initializers/sidekiq.rb +11 -0
- data/templates/config/initializers/slack_webhook_logger.rb +17 -0
- data/templates/config/sidekiq.yml +18 -0
- data/templates/hephaestus_gitignore +296 -0
- data/templates/lib/plug_app/schemas/api/2023-03-06/components/parameters/headers/yetto.json +42 -0
- data/templates/lib/plug_app/schemas/api/2023-03-06/components/parameters/plugInstallation.json +12 -0
- data/templates/lib/plug_app/schemas/api/2023-03-06/components/schemas/plug.json +9 -0
- data/templates/lib/plug_app/schemas/api/2023-03-06/components/schemas/responses.json +64 -0
- data/templates/lib/plug_app/schemas/api/2023-03-06/components/schemas/yetto.json +1 -0
- data/templates/lib/plug_app/schemas/api/2023-03-06/openapi.json +27 -0
- data/templates/lib/plug_app/schemas/api/2023-03-06/paths/plug.json +91 -0
- data/templates/lib/plug_app/schemas/api/2023-03-06/paths/yetto/after_create_message.json +41 -0
- data/templates/lib/plug_app/schemas/api/2023-03-06/paths/yetto/after_create_plug_installation.json +41 -0
- data/templates/lib/tasks/test_tasks.rake +10 -0
- data/templates/script/ci +7 -0
- data/templates/script/hmac_text +22 -0
- data/templates/script/licenses +51 -0
- data/templates/script/ngrok +5 -0
- data/templates/script/security_checks/brakeman +5 -0
- data/templates/script/security_checks/bundle-audit +5 -0
- data/templates/script/server +5 -0
- data/templates/script/server-debug +5 -0
- data/templates/script/test +5 -0
- data/templates/script/typecheck +42 -0
- data/templates/sorbet/custom.rbi +14 -0
- data/templates/test/controllers/root_controller_test.rb +12 -0
- data/templates/test/controllers/settings_controller_test.rb +27 -0
- data/templates/test/controllers/yetto_controller_test.rb +130 -0
- data/templates/test/jobs/update_yetto_job_test.rb +41 -0
- data/templates/test/support/api.rb +74 -0
- data/templates/test/support/rails.rb +39 -0
- data/templates/test/support/webmocks/slack_webmock.rb +24 -0
- data/templates/test/support/webmocks/yetto.rb +94 -0
- data/templates/test/support/webmocks.rb +5 -0
- data/templates/test/test_helper.rb +24 -0
- metadata +209 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 371b5a8acd1794fe2317f9f6e6c94de073e3790198d3f9f5871b149df137602e
|
4
|
+
data.tar.gz: ba0f8d0dfe79c043782610058fcebbe74f8c05d2c3edc472200841b769aeaf6a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a3b51205a5048ca0c67d5121fa477bb3b5053125968c0c31dd04386f5b224016e3a02dd08f5fcd74390b87f0dccbb253304e13f6c8da0f79584b137aa988231d
|
7
|
+
data.tar.gz: 1d59384fc58c249d0b964a1dbe0d26a3c29109febaebd2ba12f9cea745c34d889fe674a992bfb42611c6a27b9b5fef6226b827399d65c7a75e128367684c2bce
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.1.1
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
## [v0.0.1](https://github.com/yettoapp/hephaestus/tree/v0.0.1) (2023-02-28)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/yettoapp/hephaestus/compare/b33ebb8ab3188b8689b10d4649d3f29ea7ca1f2a...v0.0.1)
|
6
|
+
|
7
|
+
**Merged pull requests:**
|
8
|
+
|
9
|
+
- Add a Rails application template for plugs [\#1](https://github.com/yettoapp/hephaestus/pull/1) ([gjtorikian](https://github.com/gjtorikian))
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2023 Garen Torikian
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Hephaestus
|
2
|
+
|
3
|
+
A plug template for Yetto.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
```
|
8
|
+
rails new plug-app --api -m hephaestus/template.rb
|
9
|
+
```
|
10
|
+
|
11
|
+
If you're working on updating this locally, you'll also probably want:
|
12
|
+
|
13
|
+
```
|
14
|
+
rm -rf plug-app && DEBUG=1 rails new plug-app --api -m hephaestus/template.rb
|
15
|
+
```
|
16
|
+
|
17
|
+
## Acknowledgements
|
18
|
+
|
19
|
+
This project was heavily based on [thoughtbot/suspenders](https://github.com/thoughtbot/suspenders).
|
data/bin/hephaestus
ADDED
@@ -0,0 +1,55 @@
|
|
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 "Hephaestus error: Unable to find Rails version #{rails_version}"
|
24
|
+
end
|
25
|
+
else
|
26
|
+
require "hephaestus/version"
|
27
|
+
|
28
|
+
spec = Gem::Specification.find_by_name("rails", Hephaestus::RAILS_VERSION)
|
29
|
+
|
30
|
+
activate_rails_version.call(spec.version.to_s)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
require "hephaestus"
|
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 Hephaestus::VERSION
|
43
|
+
exit 0
|
44
|
+
end
|
45
|
+
|
46
|
+
templates_root = File.expand_path(File.join("..", "templates"), File.dirname(__FILE__))
|
47
|
+
Hephaestus::AppGenerator.source_root templates_root
|
48
|
+
Hephaestus::AppGenerator.source_paths << Rails::Generators::AppGenerator.source_root << templates_root
|
49
|
+
|
50
|
+
ARGV.push("--minimal")
|
51
|
+
ARGV.push("--no-skip-active-job")
|
52
|
+
ARGV.push("--skip-asset-pipeline")
|
53
|
+
ARGV.push("--skip-active-record")
|
54
|
+
ARGV.push("--skip-active-record")
|
55
|
+
Hephaestus::AppGenerator.start
|
@@ -0,0 +1,263 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "parser/current"
|
5
|
+
|
6
|
+
module Hephaestus
|
7
|
+
module Actions
|
8
|
+
class StripCommentsAction
|
9
|
+
class << self
|
10
|
+
def call(source)
|
11
|
+
parser = Parser::CurrentRuby.new
|
12
|
+
|
13
|
+
source
|
14
|
+
.then { |s| strip_comments(s, parser) }
|
15
|
+
.then { |s| strip_trailing_whitespace(s) }
|
16
|
+
.then { |s| strip_dup_newlines(s) }
|
17
|
+
.then { |s| strip_leading_scope_newlines(s, parser) }
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def strip_comments(source, parser)
|
23
|
+
StripComments.call(source, parser.reset)
|
24
|
+
end
|
25
|
+
|
26
|
+
def strip_trailing_whitespace(source)
|
27
|
+
source.gsub(/[[:blank:]]+$/, "")
|
28
|
+
end
|
29
|
+
|
30
|
+
def strip_dup_newlines(source)
|
31
|
+
source.gsub(/\n{2,}/, "\n\n")
|
32
|
+
end
|
33
|
+
|
34
|
+
def strip_leading_scope_newlines(source, parser)
|
35
|
+
StripLeadingScopeNewlines.call(source, parser.reset)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Strips full-line and inline comments from a buffer but does
|
40
|
+
# not remove whitespaces or newlines after the fact. Example
|
41
|
+
# input:
|
42
|
+
#
|
43
|
+
# MyGem.application.configure do |config|
|
44
|
+
# # Full-line comment
|
45
|
+
# config.option1 = :value # Inline comment
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# The output is:
|
49
|
+
#
|
50
|
+
# MyGem.application.configure do |config|
|
51
|
+
#
|
52
|
+
# config.option1 = :value
|
53
|
+
# end
|
54
|
+
class StripComments
|
55
|
+
class << self
|
56
|
+
def call(source, parser)
|
57
|
+
buffer = Parser::Source::Buffer.new(nil, source: source)
|
58
|
+
rewriter = Parser::Source::TreeRewriter.new(buffer)
|
59
|
+
|
60
|
+
_, comments = parser.parse_with_comments(buffer)
|
61
|
+
|
62
|
+
comments.each do |comment|
|
63
|
+
strip_comment(comment, buffer, rewriter)
|
64
|
+
end
|
65
|
+
|
66
|
+
rewriter.process
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def strip_comment(comment, buffer, rewriter)
|
72
|
+
expr = comment.location.expression
|
73
|
+
|
74
|
+
if full_line_comment?(expr)
|
75
|
+
expr = full_line_comment_expr(expr, buffer)
|
76
|
+
end
|
77
|
+
|
78
|
+
rewriter.remove(expr)
|
79
|
+
end
|
80
|
+
|
81
|
+
def full_line_comment_expr(expr, buffer)
|
82
|
+
pos = BackwardStringScanner.beginning_of_line_pos(expr, expr.begin_pos)
|
83
|
+
|
84
|
+
Parser::Source::Range.new(buffer, pos, expr.end_pos + 1)
|
85
|
+
end
|
86
|
+
|
87
|
+
def full_line_comment?(expr)
|
88
|
+
expr.source == expr.source_line.lstrip
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# A tiny, non-stateful backward string scanner somewhat inspired
|
94
|
+
# by Ruby's StringScanner. Ruby's StringScanner is unable to
|
95
|
+
# seek backward on a string.
|
96
|
+
class BackwardStringScanner
|
97
|
+
class << self
|
98
|
+
def beginning_of_line_pos(expr, initial_pos)
|
99
|
+
skip_before(expr, initial_pos) { |char| char == "\n" }
|
100
|
+
end
|
101
|
+
|
102
|
+
def skip_before(expr, initial_pos, &block)
|
103
|
+
skip_until(expr, initial_pos, -1, &block)
|
104
|
+
end
|
105
|
+
|
106
|
+
def skip_until(expr, initial_pos, lookup_inc = 0)
|
107
|
+
pos = initial_pos
|
108
|
+
|
109
|
+
loop do
|
110
|
+
break if pos.zero?
|
111
|
+
|
112
|
+
char = expr.source_buffer.source[pos + lookup_inc]
|
113
|
+
break if yield(char)
|
114
|
+
|
115
|
+
pos -= 1
|
116
|
+
end
|
117
|
+
|
118
|
+
pos
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# The intent of this class is purely aesthetic: remove leading
|
124
|
+
# newlines inside of code scopes like blocks and begin/end.
|
125
|
+
# Example input:
|
126
|
+
#
|
127
|
+
# module MyGem
|
128
|
+
#
|
129
|
+
# MyGem.application.configure do |config|
|
130
|
+
#
|
131
|
+
# config.option1 = true
|
132
|
+
#
|
133
|
+
# config.option2 = false
|
134
|
+
# end
|
135
|
+
# end
|
136
|
+
#
|
137
|
+
# The output is:
|
138
|
+
#
|
139
|
+
# module MyGem
|
140
|
+
# MyGem.application.configure do |config|
|
141
|
+
# config.option1 = true
|
142
|
+
#
|
143
|
+
# config.option2 = false
|
144
|
+
# end
|
145
|
+
# end
|
146
|
+
class StripLeadingScopeNewlines
|
147
|
+
class << self
|
148
|
+
def call(source, parser)
|
149
|
+
buffer = Parser::Source::Buffer.new(nil, source: source)
|
150
|
+
ast = parser.parse(buffer)
|
151
|
+
|
152
|
+
LeadingNewlineStripRewriter.new.rewrite(buffer, ast).lstrip
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class LeadingNewlineStripRewriter < Parser::TreeRewriter
|
157
|
+
def on_module(node)
|
158
|
+
strip_newline_before(node.children[1])
|
159
|
+
strip_newline_after(node.children.last)
|
160
|
+
|
161
|
+
super
|
162
|
+
end
|
163
|
+
|
164
|
+
def on_class(node)
|
165
|
+
strip_newline_before(node.children[2])
|
166
|
+
strip_newline_after(node.children.last)
|
167
|
+
|
168
|
+
super
|
169
|
+
end
|
170
|
+
|
171
|
+
def on_begin(node)
|
172
|
+
handle_begin(node)
|
173
|
+
|
174
|
+
super
|
175
|
+
end
|
176
|
+
|
177
|
+
def on_kwbegin(node)
|
178
|
+
strip_newline_before(node.children[0])
|
179
|
+
strip_newline_after(node.children.last)
|
180
|
+
|
181
|
+
handle_begin(node)
|
182
|
+
|
183
|
+
super
|
184
|
+
end
|
185
|
+
|
186
|
+
def on_block(node)
|
187
|
+
strip_newline_before(node.children[2])
|
188
|
+
strip_newline_after(node.children.last)
|
189
|
+
|
190
|
+
super
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def handle_begin(node)
|
196
|
+
strip_blank_lines_between_setter_calls(node.children)
|
197
|
+
|
198
|
+
node.children.each do |child_node|
|
199
|
+
send("on_#{child_node.type}", child_node)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
def strip_blank_lines_between_setter_calls(children)
|
204
|
+
pairs = children.each_cons(2).to_a
|
205
|
+
|
206
|
+
pairs.each do |(node_before, node_after)|
|
207
|
+
if setter_call?(node_before) && setter_call?(node_after)
|
208
|
+
strip_newline_before(node_after)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def setter_call?(node)
|
214
|
+
node.children[1].to_s.end_with?("=")
|
215
|
+
end
|
216
|
+
|
217
|
+
def strip_newline_before(node)
|
218
|
+
return if node.nil?
|
219
|
+
|
220
|
+
expr = node.location.expression
|
221
|
+
end_pos = find_end_pos(expr, expr.begin_pos)
|
222
|
+
begin_pos = find_begin_pos(expr, end_pos)
|
223
|
+
|
224
|
+
strip_source_range(expr, begin_pos, end_pos)
|
225
|
+
end
|
226
|
+
|
227
|
+
def strip_newline_after(node)
|
228
|
+
return if node.nil?
|
229
|
+
|
230
|
+
expr = node.location.expression
|
231
|
+
source = expr.source_buffer.source
|
232
|
+
|
233
|
+
if source[expr.end_pos + 1] == "\n"
|
234
|
+
strip_source_range(expr, expr.end_pos, expr.end_pos + 1)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def find_end_pos(expr, begin_pos)
|
239
|
+
BackwardStringScanner.skip_until(expr, begin_pos) do |char|
|
240
|
+
char == "\n"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def find_begin_pos(expr, end_pos)
|
245
|
+
BackwardStringScanner.skip_before(expr, end_pos) do |char|
|
246
|
+
char != "\n" && char != " "
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def strip_source_range(expr, begin_pos, end_pos)
|
251
|
+
remove(
|
252
|
+
Parser::Source::Range.new(
|
253
|
+
expr.source_buffer,
|
254
|
+
begin_pos,
|
255
|
+
end_pos,
|
256
|
+
),
|
257
|
+
)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Hephaestus
|
5
|
+
module Actions
|
6
|
+
def replace_in_file(relative_path, find, replace)
|
7
|
+
path = File.join(destination_root, relative_path)
|
8
|
+
contents = File.read(path)
|
9
|
+
contents.gsub!(find, replace)
|
10
|
+
File.write(path, contents)
|
11
|
+
end
|
12
|
+
|
13
|
+
def replace_in_files(relative_path, find, replace)
|
14
|
+
Dir.glob("#{relative_path}/**/*").each do |path|
|
15
|
+
next if File.directory?(path)
|
16
|
+
|
17
|
+
path = Pathname.new(path).relative_path_from(relative_path).to_s
|
18
|
+
replace_in_file(path, find, replace)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def action_mailer_host(rails_env, host)
|
23
|
+
config = "config.action_mailer.default_url_options = { host: #{host} }"
|
24
|
+
configure_environment(rails_env, config)
|
25
|
+
end
|
26
|
+
|
27
|
+
def action_mailer_asset_host(rails_env, host)
|
28
|
+
config = "config.action_mailer.asset_host = #{host}"
|
29
|
+
configure_environment(rails_env, config)
|
30
|
+
end
|
31
|
+
|
32
|
+
def configure_environment(rails_env, config)
|
33
|
+
inject_into_file(
|
34
|
+
"config/environments/#{rails_env}.rb",
|
35
|
+
"\n #{config}",
|
36
|
+
before: "\nend",
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def expand_json(file, data)
|
41
|
+
action(ExpandJson.new(destination_root, file, data))
|
42
|
+
end
|
43
|
+
|
44
|
+
def gem(*args)
|
45
|
+
options = args.extract_options!
|
46
|
+
name, *versions = args
|
47
|
+
|
48
|
+
parts = [quote(name)]
|
49
|
+
|
50
|
+
if (versions = versions.any? ? versions : options.delete(:version))
|
51
|
+
versions = Array(versions)
|
52
|
+
versions.each do |version|
|
53
|
+
parts << quote(version)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
parts << quote(options) unless options.empty?
|
58
|
+
|
59
|
+
str = []
|
60
|
+
str << indentation
|
61
|
+
str << "gem #{parts.join(", ")}"
|
62
|
+
append_file("Gemfile", %(\n#{str.join}\n), verbose: false)
|
63
|
+
end
|
64
|
+
|
65
|
+
class ExpandJson
|
66
|
+
def initialize(destination_root, file, data)
|
67
|
+
@destination_root = destination_root
|
68
|
+
@file = file
|
69
|
+
@data = data
|
70
|
+
end
|
71
|
+
|
72
|
+
def invoke!
|
73
|
+
write_out { |existing_json| existing_json.deep_merge(data) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def revoke!
|
77
|
+
write_out { |existing_json| hash_unmerge(existing_json, data) }
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
attr_reader :destination_root, :file, :data
|
83
|
+
|
84
|
+
def write_out
|
85
|
+
new_json = yield(existing_json)
|
86
|
+
File.write(destination_file, JSON.pretty_generate(new_json))
|
87
|
+
end
|
88
|
+
|
89
|
+
def destination_file
|
90
|
+
File.join(destination_root, file)
|
91
|
+
end
|
92
|
+
|
93
|
+
def existing_json
|
94
|
+
JSON.parse(File.read(destination_file), symbolize_names: true)
|
95
|
+
rescue Errno::ENOENT
|
96
|
+
{}
|
97
|
+
end
|
98
|
+
|
99
|
+
def hash_unmerge(hash, subhash)
|
100
|
+
subhash.reduce(hash) do |acc, (k, v)|
|
101
|
+
if hash.key?(k)
|
102
|
+
if v == hash[k]
|
103
|
+
acc.except(k)
|
104
|
+
elsif v.is_a?(Hash)
|
105
|
+
acc.merge(k => hash_unmerge(hash[k], v))
|
106
|
+
else
|
107
|
+
acc
|
108
|
+
end
|
109
|
+
else
|
110
|
+
acc
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "forwardable"
|
5
|
+
|
6
|
+
module Hephaestus
|
7
|
+
class AppBuilder < ::Rails::AppBuilder
|
8
|
+
include Hephaestus::Actions
|
9
|
+
extend Forwardable
|
10
|
+
|
11
|
+
def readme
|
12
|
+
template("README.md.erb", "README.md")
|
13
|
+
end
|
14
|
+
|
15
|
+
def gitignore
|
16
|
+
copy_file("hephaestus_gitignore", ".gitignore")
|
17
|
+
end
|
18
|
+
|
19
|
+
def gemfile
|
20
|
+
template("Gemfile.erb", "Gemfile")
|
21
|
+
end
|
22
|
+
|
23
|
+
def raise_on_delivery_errors
|
24
|
+
replace_in_file(
|
25
|
+
"config/environments/development.rb",
|
26
|
+
"raise_delivery_errors = false",
|
27
|
+
"raise_delivery_errors = true",
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def copy_setup_scripts
|
32
|
+
source = File.join(Hephaestus::AppGenerator.source_root, "script")
|
33
|
+
directory(source, "script")
|
34
|
+
Dir.glob("script/**/*").each do |file|
|
35
|
+
next if File.directory?(file)
|
36
|
+
|
37
|
+
chmod(file, 0o755)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def configure_dev_hosting
|
42
|
+
config = <<~EOD
|
43
|
+
# Let dev server run on GitHub Codespaces
|
44
|
+
config.hosts << /[a-z0-9-]+.githubpreview.dev/
|
45
|
+
|
46
|
+
# Let dev server run on ngrok domains
|
47
|
+
config.hosts << /[a-z0-9-]+.ngrok.io/
|
48
|
+
EOD
|
49
|
+
|
50
|
+
configure_environment("development", config)
|
51
|
+
end
|
52
|
+
|
53
|
+
def setup_asset_host
|
54
|
+
config = <<~EOD
|
55
|
+
config.public_file_server.headers = {
|
56
|
+
"Cache-Control" => "public, max-age=31557600",
|
57
|
+
}
|
58
|
+
EOD
|
59
|
+
|
60
|
+
configure_environment("production", config)
|
61
|
+
end
|
62
|
+
|
63
|
+
def setup_background_worker
|
64
|
+
config = <<~EOD
|
65
|
+
# Use a real queuing backend for Active Job (and separate queues per environment).
|
66
|
+
config.active_job.queue_adapter = :sidekiq
|
67
|
+
EOD
|
68
|
+
configure_environment("production", config)
|
69
|
+
configure_environment("staging", config)
|
70
|
+
end
|
71
|
+
|
72
|
+
def setup_slack_logger
|
73
|
+
config = <<~EOD
|
74
|
+
config.after_initialize do
|
75
|
+
Rails.logger.extend(ActiveSupport::Logger.broadcast(SlackWebhookLogger.logger))
|
76
|
+
end
|
77
|
+
EOD
|
78
|
+
configure_environment("production", config)
|
79
|
+
configure_environment("staging", config)
|
80
|
+
configure_environment("test", config)
|
81
|
+
end
|
82
|
+
|
83
|
+
def setup_staging_environment
|
84
|
+
FileUtils.cp(File.join(destination_root, "config/environments/production.rb"), File.join(destination_root, "config/environments/staging.rb"))
|
85
|
+
end
|
86
|
+
|
87
|
+
def replace_gemfile
|
88
|
+
template("Gemfile.erb", "Gemfile", force: true) do |content|
|
89
|
+
if development_env?
|
90
|
+
content.gsub(/gem .hephaestus./) { |s| %(#{s}, path: "#{root_path}") }
|
91
|
+
else
|
92
|
+
content
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def ruby_version
|
98
|
+
create_file(".ruby-version", "#{Hephaestus::RUBY_VERSION}\n")
|
99
|
+
end
|
100
|
+
|
101
|
+
def configure_time_formats
|
102
|
+
replace_in_file("config/application.rb", /# config.time_zone = .*/, "config.time_zone = \"UTC\"")
|
103
|
+
end
|
104
|
+
|
105
|
+
def create_github_repo(repo_name)
|
106
|
+
run("gh repo create #{repo_name}")
|
107
|
+
end
|
108
|
+
|
109
|
+
def remove_config_comment_lines
|
110
|
+
config_files = [
|
111
|
+
"application.rb",
|
112
|
+
"environment.rb",
|
113
|
+
"environments/development.rb",
|
114
|
+
"environments/production.rb",
|
115
|
+
"environments/test.rb",
|
116
|
+
]
|
117
|
+
|
118
|
+
config_files.each do |config_file|
|
119
|
+
path = Pathname(destination_root).join("config", config_file)
|
120
|
+
source = Actions::StripCommentsAction.call(path.read)
|
121
|
+
|
122
|
+
path.write(source)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def remove_routes_comment_lines
|
127
|
+
replace_in_file(
|
128
|
+
"config/routes.rb",
|
129
|
+
/Rails\.application\.routes\.draw do.*end/m,
|
130
|
+
"Rails.application.routes.draw do\nend",
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
def setup_test_environment
|
135
|
+
remove_dir("test")
|
136
|
+
source = File.join(Hephaestus::AppGenerator.source_root, "test")
|
137
|
+
directory(source, "test")
|
138
|
+
end
|
139
|
+
|
140
|
+
def replace_generic_variables
|
141
|
+
replace_in_files(destination_root, "${APP}", app_name.titlecase)
|
142
|
+
replace_in_files(destination_root, "PlugApp", app_name.underscore.camelcase)
|
143
|
+
replace_in_files(destination_root, "plug-app", app_name.dasherize)
|
144
|
+
replace_in_files(destination_root, "PLUG_APP", app_name.underscore.upcase)
|
145
|
+
replace_in_files(destination_root, "plug_app", app_name.underscore)
|
146
|
+
end
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def root_path
|
151
|
+
@root_path ||= Pathname(__dir__).join("..", "..").expand_path
|
152
|
+
end
|
153
|
+
|
154
|
+
def development_env?
|
155
|
+
root_path.join("hephaestus.gemspec").exist?
|
156
|
+
end
|
157
|
+
|
158
|
+
def raise_on_missing_translations_in(environment)
|
159
|
+
config = "config.i18n.raise_on_missing_translations = true"
|
160
|
+
|
161
|
+
uncomment_lines("config/environments/#{environment}.rb", config)
|
162
|
+
end
|
163
|
+
|
164
|
+
def heroku_adapter
|
165
|
+
@heroku_adapter ||= Adapters::Heroku.new(self)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# typed: false
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "active_support/concern"
|
5
|
+
require "English"
|
6
|
+
|
7
|
+
module Hephaestus
|
8
|
+
module ExitOnFailure
|
9
|
+
extend ActiveSupport::Concern
|
10
|
+
|
11
|
+
# def bundle_command(*args)
|
12
|
+
# run("bundle", args)
|
13
|
+
# exit(false) if $CHILD_STATUS.exitstatus.nonzero?
|
14
|
+
# end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
def exit_on_failure?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|