railstart 0.3.0 → 0.4.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 +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +22 -0
- data/lib/railstart/cli.rb +13 -0
- data/lib/railstart/config.rb +27 -6
- data/lib/railstart/errors.rb +3 -0
- data/lib/railstart/generator.rb +39 -7
- data/lib/railstart/template_runner.rb +77 -0
- data/lib/railstart/version.rb +1 -1
- data/lib/railstart.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 675d33f74310aa870ad6a167cbd585cc643b3e37096d255732bcf900fa7ab65d
|
|
4
|
+
data.tar.gz: 460628c266ba316d4ad19e148b9e8044912865c98f6c091dd8a1be25653dab71
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 344c0860107725a5a56e88bf108a7e7a73891da7f0f7678850af5c47469ed63f91c8c22de71743dc68747557f7d34aac2d001814033dee29533f5b343d8b7e0d
|
|
7
|
+
data.tar.gz: 2252beeb778c40a048e1156aebd1786a904fa7cf5edec89fd89d924265b223ff742024ceee8491d5b2a5b459a8a560047be60e43a85c13d771ee96db6edd978a
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.0] - 2025-11-22
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- CLI `--preset` flag now accepts explicit `.yaml`/`.yml` file paths in addition to preset names.
|
|
12
|
+
- **Template post-actions**: New `type: template` post-action type for executing full Rails application templates
|
|
13
|
+
- **TemplateRunner**: New `Railstart::TemplateRunner` class wraps Rails' AppGenerator to run templates with proper context
|
|
14
|
+
- **Template variables**: Template actions support `variables` hash for injecting custom instance variables into templates
|
|
15
|
+
- **Built-in variables**: Templates automatically receive `@app_name` and `@answers` instance variables
|
|
16
|
+
- **Template DSL support**: Full access to Rails template DSL (`gem`, `route`, `initializer`, `after_bundle`, etc.)
|
|
17
|
+
- **Error handling**: New `Railstart::TemplateError` for template execution failures with proper error wrapping
|
|
18
|
+
- **Config validation**: Validation for template post-actions (requires `source`, validates `variables` as Hash)
|
|
19
|
+
- **Documentation**: README section explaining template post-actions vs command actions with security guidance
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- **Post-action processing**: Refactored to support both command and template execution types
|
|
23
|
+
- **Directory context**: `run_post_actions` now uses block form of `Dir.chdir` for proper scoping
|
|
24
|
+
- **Config validation**: Enhanced `validate_post_action_entry` to handle multiple action types
|
|
25
|
+
|
|
26
|
+
### Technical
|
|
27
|
+
- New file: `lib/railstart/template_runner.rb` (77 lines, full YARD docs)
|
|
28
|
+
- New test file: `test/template_runner_test.rb` (comprehensive coverage with mocks)
|
|
29
|
+
- Enhanced `lib/railstart/generator.rb` with template execution flow
|
|
30
|
+
- Enhanced `lib/railstart/config.rb` with template action validation
|
|
31
|
+
- Version bump: 0.3.0 → 0.4.0
|
|
32
|
+
- All tests pass (39 runs, 111 assertions, 0 failures)
|
|
33
|
+
- RuboCop clean (20 files inspected, no offenses)
|
|
8
34
|
|
|
9
35
|
## [0.3.0] - 2025-11-22
|
|
10
36
|
|
data/README.md
CHANGED
|
@@ -309,6 +309,28 @@ post_actions:
|
|
|
309
309
|
equals: value # or includes: [array, values]
|
|
310
310
|
```
|
|
311
311
|
|
|
312
|
+
#### Template Post-Actions
|
|
313
|
+
|
|
314
|
+
Post-actions can now execute full Rails application templates (including [RailsBytes scripts](https://railsbytes.com)) instead of plain shell commands.
|
|
315
|
+
|
|
316
|
+
```yaml
|
|
317
|
+
post_actions:
|
|
318
|
+
- id: apply_tailwind_dash
|
|
319
|
+
name: "Apply Tailwind dashboard template"
|
|
320
|
+
type: template
|
|
321
|
+
enabled: false # keep disabled unless you trust the source
|
|
322
|
+
prompt: "Run the sample template?"
|
|
323
|
+
source: "https://railsbytes.com/script/zAasQK"
|
|
324
|
+
variables:
|
|
325
|
+
app_label: "internal-tools" # optional instance variables available inside template
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Key differences from `command` actions:
|
|
329
|
+
|
|
330
|
+
- Set `type: template` and provide a `source` (local path or URL). Railstart streams that template into Rails' own `apply` helper, so all standard DSL commands (`gem`, `route`, `after_bundle`, etc.) are available.
|
|
331
|
+
- `variables` is optional; when present, its keys become instance variables accessible from the template (e.g., `@app_label`). Railstart always exposes `@app_name` and `@answers` for convenience.
|
|
332
|
+
- Template actions still honor `prompt`, `default`, and `if` just like command actions. Keep remote templates disabled by default unless you explicitly trust them.
|
|
333
|
+
|
|
312
334
|
## Development
|
|
313
335
|
|
|
314
336
|
### Setup
|
data/lib/railstart/cli.rb
CHANGED
|
@@ -237,6 +237,10 @@ module Railstart
|
|
|
237
237
|
end
|
|
238
238
|
|
|
239
239
|
def preset_file_for(name)
|
|
240
|
+
if (direct_path = explicit_preset_path(name))
|
|
241
|
+
return direct_path
|
|
242
|
+
end
|
|
243
|
+
|
|
240
244
|
# Check user presets first
|
|
241
245
|
user_path = File.join(PRESET_DIR, "#{name}.yaml")
|
|
242
246
|
return user_path if File.exist?(user_path)
|
|
@@ -319,5 +323,14 @@ module Railstart
|
|
|
319
323
|
enabled: true
|
|
320
324
|
YAML
|
|
321
325
|
end
|
|
326
|
+
|
|
327
|
+
def explicit_preset_path(name)
|
|
328
|
+
return unless name&.match?(/\.ya?ml\z/)
|
|
329
|
+
|
|
330
|
+
expanded = File.expand_path(name)
|
|
331
|
+
return expanded if File.exist?(expanded)
|
|
332
|
+
|
|
333
|
+
raise Railstart::ConfigLoadError, "Preset file '#{name}' not found"
|
|
334
|
+
end
|
|
322
335
|
end
|
|
323
336
|
end
|
data/lib/railstart/config.rb
CHANGED
|
@@ -197,12 +197,7 @@ module Railstart
|
|
|
197
197
|
|
|
198
198
|
issues.concat(validate_question_choices(entry, id || index)) if CHOICE_REQUIRED_TYPES.include?(type)
|
|
199
199
|
elsif name == "post_actions"
|
|
200
|
-
if entry.fetch("enabled", true)
|
|
201
|
-
command = entry["command"] || entry[:command]
|
|
202
|
-
if command.nil? || command.to_s.strip.empty?
|
|
203
|
-
issues << "Post-action #{id || index} is enabled but missing a command"
|
|
204
|
-
end
|
|
205
|
-
end
|
|
200
|
+
issues.concat(validate_post_action_entry(entry, id || index)) if entry.fetch("enabled", true)
|
|
206
201
|
|
|
207
202
|
if_condition = entry["if"] || entry[:if]
|
|
208
203
|
if if_condition.is_a?(Hash)
|
|
@@ -246,6 +241,32 @@ module Railstart
|
|
|
246
241
|
issues
|
|
247
242
|
end
|
|
248
243
|
|
|
244
|
+
def validate_post_action_entry(entry, identifier)
|
|
245
|
+
action_type = (entry["type"] || entry[:type] || "command").to_s
|
|
246
|
+
|
|
247
|
+
case action_type
|
|
248
|
+
when "command"
|
|
249
|
+
command = entry["command"] || entry[:command]
|
|
250
|
+
if command.nil? || command.to_s.strip.empty?
|
|
251
|
+
["Post-action #{identifier} is enabled but missing a command"]
|
|
252
|
+
else
|
|
253
|
+
[]
|
|
254
|
+
end
|
|
255
|
+
when "template"
|
|
256
|
+
issues = []
|
|
257
|
+
source = entry["source"] || entry[:source]
|
|
258
|
+
if source.nil? || source.to_s.strip.empty?
|
|
259
|
+
issues << "Post-action #{identifier} is a template but missing a source"
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
variables = entry["variables"] || entry[:variables]
|
|
263
|
+
issues << "Post-action #{identifier} template variables must be a Hash" if variables && !variables.is_a?(Hash)
|
|
264
|
+
issues
|
|
265
|
+
else
|
|
266
|
+
["Post-action #{identifier} has unsupported type '#{action_type}'"]
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
249
270
|
def deep_dup(value)
|
|
250
271
|
case value
|
|
251
272
|
when Hash
|
data/lib/railstart/errors.rb
CHANGED
data/lib/railstart/generator.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "tty-prompt"
|
|
4
4
|
require_relative "ui"
|
|
5
|
+
require_relative "template_runner"
|
|
5
6
|
|
|
6
7
|
module Railstart
|
|
7
8
|
# Orchestrates the interactive Rails app generation flow.
|
|
@@ -221,20 +222,30 @@ module Railstart
|
|
|
221
222
|
end
|
|
222
223
|
|
|
223
224
|
def run_post_actions
|
|
224
|
-
Dir.chdir(@app_name)
|
|
225
|
+
Dir.chdir(@app_name) do
|
|
226
|
+
template_runner = nil
|
|
225
227
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
228
|
+
Array(@config["post_actions"]).each do |action|
|
|
229
|
+
template_runner ||= TemplateRunner.new(app_path: Dir.pwd) if template_action?(action)
|
|
230
|
+
process_post_action(action, template_runner)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
puts
|
|
234
|
+
UI.success("Rails app created successfully at ./#{@app_name}")
|
|
235
|
+
end
|
|
229
236
|
rescue Errno::ENOENT
|
|
230
237
|
UI.warning("Could not change to app directory. Post-actions skipped.")
|
|
231
238
|
end
|
|
232
239
|
|
|
233
|
-
def process_post_action(action)
|
|
240
|
+
def process_post_action(action, template_runner)
|
|
234
241
|
return unless should_run_action?(action)
|
|
235
242
|
return unless confirm_action?(action)
|
|
236
243
|
|
|
237
|
-
|
|
244
|
+
if template_action?(action)
|
|
245
|
+
run_template_action(action, template_runner)
|
|
246
|
+
else
|
|
247
|
+
run_command_action(action)
|
|
248
|
+
end
|
|
238
249
|
end
|
|
239
250
|
|
|
240
251
|
def confirm_action?(action)
|
|
@@ -243,12 +254,33 @@ module Railstart
|
|
|
243
254
|
@prompt.yes?(action["prompt"], default: action.fetch("default", true))
|
|
244
255
|
end
|
|
245
256
|
|
|
246
|
-
def
|
|
257
|
+
def run_command_action(action)
|
|
247
258
|
UI.info(action["name"].to_s)
|
|
248
259
|
success = system(action["command"])
|
|
249
260
|
UI.warning("Post-action '#{action["name"]}' failed. Continuing anyway.") unless success
|
|
250
261
|
end
|
|
251
262
|
|
|
263
|
+
def run_template_action(action, template_runner)
|
|
264
|
+
return unless template_runner
|
|
265
|
+
|
|
266
|
+
UI.info(action["name"].to_s)
|
|
267
|
+
source = action["source"]
|
|
268
|
+
variables = template_variables(action)
|
|
269
|
+
template_runner.apply(source, variables: variables)
|
|
270
|
+
rescue TemplateError => e
|
|
271
|
+
UI.warning("Post-action '#{action["name"]}' failed. #{e.message}")
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def template_variables(action)
|
|
275
|
+
base = { app_name: @app_name, answers: @answers }
|
|
276
|
+
extras = action["variables"].is_a?(Hash) ? action["variables"].transform_keys(&:to_sym) : {}
|
|
277
|
+
base.merge(extras)
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def template_action?(action)
|
|
281
|
+
action["type"].to_s == "template"
|
|
282
|
+
end
|
|
283
|
+
|
|
252
284
|
def should_run_action?(action)
|
|
253
285
|
return false unless action.fetch("enabled", true)
|
|
254
286
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "thor"
|
|
4
|
+
require_relative "errors"
|
|
5
|
+
|
|
6
|
+
module Railstart
|
|
7
|
+
# Executes Rails application templates (including RailsBytes scripts)
|
|
8
|
+
# inside a generated application directory.
|
|
9
|
+
#
|
|
10
|
+
# Wraps Rails' own AppGenerator so existing template DSL helpers such as
|
|
11
|
+
# `gem`, `initializer`, `route`, etc. are available without reimplementing
|
|
12
|
+
# them in Railstart.
|
|
13
|
+
class TemplateRunner
|
|
14
|
+
# @param app_path [String] absolute path to the Rails application
|
|
15
|
+
# @param generator_factory [#call] optional factory for injecting a
|
|
16
|
+
# generator (mainly used in tests)
|
|
17
|
+
# @param shell [Thor::Shell] Thor shell instance for output
|
|
18
|
+
def initialize(app_path:, generator_factory: nil, shell: Thor::Base.shell.new)
|
|
19
|
+
@app_path = app_path
|
|
20
|
+
@shell = shell
|
|
21
|
+
@generator_factory = generator_factory
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Apply a Rails template located at +source+.
|
|
25
|
+
#
|
|
26
|
+
# @param source [String] file path or URL
|
|
27
|
+
# @param variables [Hash] instance variables injected into the template
|
|
28
|
+
# @return [void]
|
|
29
|
+
# @raise [Railstart::TemplateError] when Rails cannot be loaded or
|
|
30
|
+
# template execution fails
|
|
31
|
+
def apply(source, variables: {})
|
|
32
|
+
raise TemplateError, "Template source must be provided" if source.to_s.strip.empty?
|
|
33
|
+
|
|
34
|
+
generator = build_generator
|
|
35
|
+
assign_variables(generator, variables)
|
|
36
|
+
generator.apply(source)
|
|
37
|
+
rescue TemplateError
|
|
38
|
+
raise
|
|
39
|
+
rescue LoadError => e
|
|
40
|
+
raise TemplateError, "Rails must be installed to run template post-actions: #{e.message}"
|
|
41
|
+
rescue StandardError => e
|
|
42
|
+
raise TemplateError, "Failed to apply template #{source}: #{e.message}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def assign_variables(generator, variables)
|
|
48
|
+
Array(variables).each do |key, value|
|
|
49
|
+
generator.instance_variable_set(:"@#{key}", value)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def build_generator
|
|
54
|
+
generator = generator_factory.call(@app_path)
|
|
55
|
+
if generator.respond_to?(:destination_root=)
|
|
56
|
+
generator.destination_root = @app_path
|
|
57
|
+
elsif generator.respond_to?(:destination_root)
|
|
58
|
+
generator.instance_variable_set(:@destination_root, @app_path)
|
|
59
|
+
end
|
|
60
|
+
generator
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def generator_factory
|
|
64
|
+
@generator_factory ||= default_generator_factory
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def default_generator_factory
|
|
68
|
+
require "rails/generators"
|
|
69
|
+
require "rails/generators/rails/app/app_generator"
|
|
70
|
+
|
|
71
|
+
shell = @shell
|
|
72
|
+
lambda do |_app_path|
|
|
73
|
+
Rails::Generators::AppGenerator.new([], {}, shell: shell)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
data/lib/railstart/version.rb
CHANGED
data/lib/railstart.rb
CHANGED
|
@@ -5,6 +5,7 @@ require_relative "railstart/errors"
|
|
|
5
5
|
require_relative "railstart/config"
|
|
6
6
|
require_relative "railstart/command_builder"
|
|
7
7
|
require_relative "railstart/generator"
|
|
8
|
+
require_relative "railstart/template_runner"
|
|
8
9
|
require_relative "railstart/cli"
|
|
9
10
|
|
|
10
11
|
# Main namespace for the Railstart gem.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: railstart
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- dpaluy
|
|
@@ -78,6 +78,7 @@ files:
|
|
|
78
78
|
- lib/railstart/config.rb
|
|
79
79
|
- lib/railstart/errors.rb
|
|
80
80
|
- lib/railstart/generator.rb
|
|
81
|
+
- lib/railstart/template_runner.rb
|
|
81
82
|
- lib/railstart/ui.rb
|
|
82
83
|
- lib/railstart/version.rb
|
|
83
84
|
homepage: https://github.com/dpaluy/railstart
|