discharger 0.2.10 → 0.2.12
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/README.md +75 -26
- data/Rakefile +3 -1
- data/lib/discharger/setup_runner/command_factory.rb +56 -0
- data/lib/discharger/setup_runner/command_registry.rb +62 -0
- data/lib/discharger/setup_runner/commands/asdf_command.rb +61 -0
- data/lib/discharger/setup_runner/commands/base_command.rb +185 -0
- data/lib/discharger/setup_runner/commands/brew_command.rb +26 -0
- data/lib/discharger/setup_runner/commands/bundler_command.rb +25 -0
- data/lib/discharger/setup_runner/commands/config_command.rb +51 -0
- data/lib/discharger/setup_runner/commands/custom_command.rb +41 -0
- data/lib/discharger/setup_runner/commands/database_command.rb +111 -0
- data/lib/discharger/setup_runner/commands/docker_command.rb +100 -0
- data/lib/discharger/setup_runner/commands/env_command.rb +42 -0
- data/lib/discharger/setup_runner/commands/git_command.rb +45 -0
- data/lib/discharger/setup_runner/commands/yarn_command.rb +45 -0
- data/lib/discharger/setup_runner/condition_evaluator.rb +130 -0
- data/lib/discharger/setup_runner/configuration.rb +71 -0
- data/lib/discharger/setup_runner/runner.rb +111 -0
- data/lib/discharger/setup_runner/version.rb +7 -0
- data/lib/discharger/setup_runner.rb +55 -0
- data/lib/discharger/task.rb +2 -0
- data/lib/discharger/version.rb +1 -1
- data/lib/discharger.rb +1 -0
- data/lib/generators/discharger/install/install_generator.rb +14 -0
- data/lib/generators/discharger/install/templates/setup +36 -0
- data/lib/generators/discharger/install/templates/setup.yml +60 -0
- metadata +47 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0556c953dac93a704d652f69824bd24da384f42c204487340512beafde360f95
|
4
|
+
data.tar.gz: f3224ed2f8bc399bc7a9e33014334843058a4e44540fc8a14e34fa47091611d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 33ace7f51a07a0c126ba03285cde6695bdb5b38a84b796ca1588a16695dac532f9222340ca683ed5b2b7ae5e61d1abc3704f444db15ee2ba73a60f6e2e95f6a4
|
7
|
+
data.tar.gz: f0c3821be4631d3a804a71f68df53a56b7788f7d56598361d540027b7118f79466ba8e82a082ddac1bd2c124c8e4002f37e5f5cb3aa8da62ffbe7a4da97ee842
|
data/README.md
CHANGED
@@ -1,26 +1,80 @@
|
|
1
1
|
# Discharger
|
2
|
-
Code supporting tasks that discharge code for deployment.
|
3
2
|
|
4
|
-
|
3
|
+
A Ruby gem that provides Rake tasks for managing code deployment workflows with automated versioning, changelog management, and Slack notifications.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem "discharger"
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
```bash
|
15
|
+
$ bundle install
|
16
|
+
```
|
17
|
+
|
18
|
+
Then run the install generator:
|
19
|
+
```bash
|
20
|
+
$ rails generate discharger:install
|
21
|
+
```
|
5
22
|
|
6
|
-
|
23
|
+
Or install it yourself as:
|
24
|
+
```bash
|
25
|
+
$ gem install discharger
|
26
|
+
```
|
7
27
|
|
8
|
-
|
28
|
+
## Usage
|
29
|
+
|
30
|
+
Add `require "discharger/task"` to your Rakefile, then configure the discharger task:
|
9
31
|
|
10
32
|
```ruby
|
11
33
|
require "discharger/task"
|
12
34
|
|
13
35
|
Discharger::Task.create do |task|
|
36
|
+
# Version management
|
14
37
|
task.version_file = "config/application.rb"
|
15
|
-
task.release_message_channel = "#some-slack-channel"
|
16
38
|
task.version_constant = "MyApp::VERSION"
|
17
|
-
|
39
|
+
|
40
|
+
# Slack integration
|
41
|
+
task.release_message_channel = "#some-slack-channel"
|
42
|
+
task.app_name = "My App Name"
|
43
|
+
|
44
|
+
# Git integration
|
18
45
|
task.commit_identifier = -> { `git rev-parse HEAD`.strip }
|
19
46
|
task.pull_request_url = "https://github.com/SOFware/some-app"
|
47
|
+
|
48
|
+
# Changelog management (optional)
|
49
|
+
task.fragment_directory = "changelog.d" # Directory for changelog fragments
|
50
|
+
end
|
51
|
+
```
|
52
|
+
|
53
|
+
### Changelog Fragments
|
54
|
+
|
55
|
+
Discharger supports changelog fragment management through the `fragment_directory` setting. When set, Discharger will look for changelog fragments in the specified directory and automatically combine them into the main changelog during releases.
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
Discharger::Task.create do |task|
|
59
|
+
# ... other configuration ...
|
60
|
+
task.fragment_directory = "changelog.d" # Default: nil (disabled)
|
20
61
|
end
|
21
62
|
```
|
22
63
|
|
23
|
-
|
64
|
+
With fragments enabled, you can create individual changelog files in the `changelog.d/` directory:
|
65
|
+
|
66
|
+
```
|
67
|
+
changelog.d/
|
68
|
+
├── 123-fix-login-bug.md
|
69
|
+
├── 124-add-user-profile.md
|
70
|
+
└── 125-update-dependencies.md
|
71
|
+
```
|
72
|
+
|
73
|
+
Each fragment file should contain the changelog entry for a specific change or feature.
|
74
|
+
|
75
|
+
## Available Tasks
|
76
|
+
|
77
|
+
The gem creates several Rake tasks for managing your deployment workflow:
|
24
78
|
|
25
79
|
```bash
|
26
80
|
$ rake -T release
|
@@ -32,33 +86,28 @@ rake release:slack[text,channel,emoji] # Send a message to Slack
|
|
32
86
|
rake release:stage # ---------- STEP 2 ----------
|
33
87
|
```
|
34
88
|
|
35
|
-
|
36
|
-
Add this line to your application's Gemfile:
|
89
|
+
### Workflow Steps
|
37
90
|
|
38
|
-
|
39
|
-
|
40
|
-
|
91
|
+
1. **Prepare** (`rake release:prepare`): Create a new branch to prepare the release, update the changelog, and bump the version
|
92
|
+
2. **Stage** (`rake release:stage`): Update the staging branch and create a PR to production
|
93
|
+
3. **Release** (`rake release`): Release the current version to production by tagging and pushing to the production branch
|
41
94
|
|
42
|
-
|
43
|
-
```bash
|
44
|
-
$ bundle
|
45
|
-
```
|
95
|
+
## Contributing
|
46
96
|
|
47
|
-
|
48
|
-
```bash
|
49
|
-
$ rails generate discharger:install
|
50
|
-
```
|
97
|
+
This gem is managed with [Reissue](https://github.com/SOFware/reissue).
|
51
98
|
|
52
|
-
|
53
|
-
```bash
|
54
|
-
$ gem install discharger
|
55
|
-
```
|
99
|
+
### Releasing
|
56
100
|
|
57
|
-
|
101
|
+
Releases are automated via GitHub Actions:
|
58
102
|
|
59
|
-
|
103
|
+
1. Go to Actions → "Prepare Release" → Run workflow
|
104
|
+
2. Select version type (major, minor, patch, or custom)
|
105
|
+
3. Review the created PR with version bumps and changelog updates
|
106
|
+
4. Add the `approved-release` label and merge
|
107
|
+
5. The gem will be automatically published to RubyGems.org
|
60
108
|
|
61
109
|
Bug reports and pull requests are welcome on GitHub.
|
62
110
|
|
63
111
|
## License
|
112
|
+
|
64
113
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
@@ -7,7 +7,9 @@ require "reissue/gem"
|
|
7
7
|
|
8
8
|
Reissue::Task.create :reissue do |task|
|
9
9
|
task.version_file = "lib/discharger/version.rb"
|
10
|
-
task.commit =
|
10
|
+
task.commit = !ENV["GITHUB_ACTIONS"]
|
11
|
+
task.commit_finalize = !ENV["GITHUB_ACTIONS"]
|
12
|
+
task.push_finalize = :branch
|
11
13
|
end
|
12
14
|
|
13
15
|
Rake::TestTask.new(:test) do |t|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "command_registry"
|
4
|
+
|
5
|
+
module Discharger
|
6
|
+
module SetupRunner
|
7
|
+
class CommandFactory
|
8
|
+
attr_reader :config, :app_root, :logger
|
9
|
+
|
10
|
+
def initialize(config, app_root, logger)
|
11
|
+
@config = config
|
12
|
+
@app_root = app_root
|
13
|
+
@logger = logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def create_command(name)
|
17
|
+
command_class = CommandRegistry.get(name)
|
18
|
+
return nil unless command_class
|
19
|
+
|
20
|
+
command_class.new(config, app_root, logger)
|
21
|
+
rescue => e
|
22
|
+
logger&.warn "Failed to create command #{name}: #{e.message}"
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def create_all_commands
|
27
|
+
commands = []
|
28
|
+
|
29
|
+
# Create built-in commands from steps
|
30
|
+
if config.steps.any?
|
31
|
+
config.steps.each do |step|
|
32
|
+
command = create_command(step)
|
33
|
+
commands << command if command
|
34
|
+
end
|
35
|
+
else
|
36
|
+
# If no steps specified, create all registered commands
|
37
|
+
CommandRegistry.names.each do |name|
|
38
|
+
command = create_command(name)
|
39
|
+
commands << command if command
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Create custom commands
|
44
|
+
if config.respond_to?(:custom_steps) && config.custom_steps.any?
|
45
|
+
require_relative "commands/custom_command"
|
46
|
+
config.custom_steps.each do |step_config|
|
47
|
+
command = Commands::CustomCommand.new(config, app_root, logger, step_config)
|
48
|
+
commands << command
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
commands
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Discharger
|
4
|
+
module SetupRunner
|
5
|
+
class CommandRegistry
|
6
|
+
class << self
|
7
|
+
def register(name, command_class)
|
8
|
+
commands[name.to_s] = command_class
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(name)
|
12
|
+
commands[name.to_s]
|
13
|
+
end
|
14
|
+
|
15
|
+
def all
|
16
|
+
commands.values
|
17
|
+
end
|
18
|
+
|
19
|
+
def names
|
20
|
+
commands.keys
|
21
|
+
end
|
22
|
+
|
23
|
+
def clear
|
24
|
+
commands.clear
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_commands
|
28
|
+
# Load base command first
|
29
|
+
require_relative "commands/base_command"
|
30
|
+
|
31
|
+
# Load all command files from the commands directory
|
32
|
+
commands_dir = File.expand_path("commands", __dir__)
|
33
|
+
Dir.glob(File.join(commands_dir, "*_command.rb")).each do |file|
|
34
|
+
require file
|
35
|
+
end
|
36
|
+
|
37
|
+
# Auto-register commands based on naming convention
|
38
|
+
Commands.constants.each do |const_name|
|
39
|
+
next unless const_name.to_s.end_with?("Command")
|
40
|
+
|
41
|
+
command_class = Commands.const_get(const_name)
|
42
|
+
next unless command_class < Commands::BaseCommand
|
43
|
+
next if command_class == Commands::BaseCommand
|
44
|
+
|
45
|
+
# Convert class name to command name (e.g., AsdfCommand -> asdf)
|
46
|
+
command_name = const_name.to_s.sub(/Command$/, "").gsub(/([A-Z])/, '_\1').downcase.sub(/^_/, "")
|
47
|
+
register(command_name, command_class)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def commands
|
54
|
+
@commands ||= {}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Load and register all built-in commands
|
59
|
+
load_commands
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_command"
|
4
|
+
|
5
|
+
module Discharger
|
6
|
+
module SetupRunner
|
7
|
+
module Commands
|
8
|
+
class AsdfCommand < BaseCommand
|
9
|
+
def execute
|
10
|
+
log "Install tool-versions dependencies via ASDF"
|
11
|
+
|
12
|
+
unless system_quiet("which asdf")
|
13
|
+
log "asdf not installed. Run `brew install asdf` if you want bin/setup to ensure versions are up-to-date"
|
14
|
+
return
|
15
|
+
end
|
16
|
+
|
17
|
+
tools_versions_file = File.join(app_root, ".tool-versions")
|
18
|
+
return log("No .tool-versions file found") unless File.exist?(tools_versions_file)
|
19
|
+
|
20
|
+
dependencies = File.read(tools_versions_file).split("\n")
|
21
|
+
installables = []
|
22
|
+
|
23
|
+
# Check for nodejs plugin
|
24
|
+
unless system_quiet("asdf plugin list | grep nodejs")
|
25
|
+
node_deps = dependencies.select { |item| item.match?(/node/) }
|
26
|
+
if node_deps.any?
|
27
|
+
ask_to_install "asdf to manage Node JS" do
|
28
|
+
installables.concat(node_deps)
|
29
|
+
system! "asdf plugin add nodejs https://github.com/asdf-vm/asdf-nodejs.git"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Check for ruby plugin
|
35
|
+
unless system_quiet("asdf plugin list | grep ruby")
|
36
|
+
ruby_deps = dependencies.select { |item| item.match?(/ruby/) }
|
37
|
+
if ruby_deps.any?
|
38
|
+
ask_to_install "asdf to manage Ruby" do
|
39
|
+
installables.concat(ruby_deps)
|
40
|
+
system! "asdf plugin add ruby https://github.com/asdf-vm/asdf-ruby.git"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Install all versions
|
46
|
+
installables.each do |name_version|
|
47
|
+
system! "asdf install #{name_version}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def can_execute?
|
52
|
+
File.exist?(File.join(app_root, ".tool-versions"))
|
53
|
+
end
|
54
|
+
|
55
|
+
def description
|
56
|
+
"Install tool versions with asdf"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Discharger
|
4
|
+
module SetupRunner
|
5
|
+
module Commands
|
6
|
+
class BaseCommand
|
7
|
+
attr_reader :config, :app_root, :logger
|
8
|
+
|
9
|
+
def initialize(config, app_root, logger)
|
10
|
+
@config = config
|
11
|
+
@app_root = app_root
|
12
|
+
@logger = logger
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute
|
16
|
+
raise NotImplementedError, "#{self.class} must implement #execute"
|
17
|
+
end
|
18
|
+
|
19
|
+
def can_execute?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def description
|
24
|
+
class_name = self.class.name || "AnonymousCommand"
|
25
|
+
class_name.demodulize.underscore.humanize
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def log(message, emoji: nil)
|
31
|
+
return unless logger
|
32
|
+
class_name = self.class.name || "AnonymousCommand"
|
33
|
+
prefix = emoji ? "#{emoji} " : ""
|
34
|
+
logger.info "#{prefix}[#{class_name.demodulize}] #{message}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def with_spinner(message)
|
38
|
+
if ENV["CI"] || ENV["NO_SPINNER"] || !$stdout.tty? || ENV["QUIET_SETUP"]
|
39
|
+
result = yield
|
40
|
+
# Handle error case when spinner is disabled
|
41
|
+
if result.is_a?(Hash) && !result[:success] && result[:raise_error] != false
|
42
|
+
raise result[:error]
|
43
|
+
end
|
44
|
+
return result
|
45
|
+
end
|
46
|
+
|
47
|
+
require "rainbow"
|
48
|
+
spinner_chars = %w[⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏]
|
49
|
+
spinner_thread = nil
|
50
|
+
stop_spinner = false
|
51
|
+
|
52
|
+
begin
|
53
|
+
# Print initial message
|
54
|
+
print Rainbow("◯ #{message}").cyan
|
55
|
+
$stdout.flush
|
56
|
+
|
57
|
+
# Start spinner in background thread
|
58
|
+
spinner_thread = Thread.new do
|
59
|
+
i = 0
|
60
|
+
until stop_spinner
|
61
|
+
print "\r#{Rainbow(spinner_chars[i % spinner_chars.length]).cyan} #{Rainbow(message).cyan}"
|
62
|
+
$stdout.flush
|
63
|
+
sleep 0.1
|
64
|
+
i += 1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Execute the block
|
69
|
+
result = yield
|
70
|
+
|
71
|
+
# Stop spinner
|
72
|
+
stop_spinner = true
|
73
|
+
spinner_thread&.join(0.1)
|
74
|
+
|
75
|
+
# Clear line and print result
|
76
|
+
if result.is_a?(Hash)
|
77
|
+
if result[:success]
|
78
|
+
puts "\r#{Rainbow(result[:message] || "✓").green} #{message}"
|
79
|
+
else
|
80
|
+
puts "\r#{Rainbow(result[:message] || "✗").red} #{message}"
|
81
|
+
raise result[:error] if result[:error] && result[:raise_error] != false
|
82
|
+
end
|
83
|
+
else
|
84
|
+
puts "\r#{Rainbow("✓").green} #{message}"
|
85
|
+
end
|
86
|
+
|
87
|
+
result
|
88
|
+
rescue
|
89
|
+
stop_spinner = true
|
90
|
+
spinner_thread&.join(0.1)
|
91
|
+
puts "\r#{Rainbow("✗").red} #{message}"
|
92
|
+
raise
|
93
|
+
ensure
|
94
|
+
stop_spinner = true
|
95
|
+
spinner_thread&.kill if spinner_thread&.alive?
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def simple_action(message)
|
100
|
+
return yield if ENV["CI"] || ENV["NO_SPINNER"] || !$stdout.tty? || ENV["QUIET_SETUP"]
|
101
|
+
|
102
|
+
require "rainbow"
|
103
|
+
print Rainbow(" → #{message}...").cyan
|
104
|
+
$stdout.flush
|
105
|
+
|
106
|
+
begin
|
107
|
+
result = yield
|
108
|
+
puts Rainbow(" ✓").green
|
109
|
+
result
|
110
|
+
rescue
|
111
|
+
puts Rainbow(" ✗").red
|
112
|
+
raise
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def system!(*args)
|
117
|
+
require "open3"
|
118
|
+
command_str = args.join(" ")
|
119
|
+
|
120
|
+
# Create a more readable message for the spinner
|
121
|
+
spinner_message = if command_str.length > 80
|
122
|
+
if args.first.is_a?(Hash)
|
123
|
+
# Skip env hash in display
|
124
|
+
cmd_args = args[1..]
|
125
|
+
base_cmd = cmd_args.take(3).join(" ")
|
126
|
+
else
|
127
|
+
base_cmd = args.take(3).join(" ")
|
128
|
+
end
|
129
|
+
"Executing #{base_cmd}..."
|
130
|
+
else
|
131
|
+
"Executing #{command_str}"
|
132
|
+
end
|
133
|
+
|
134
|
+
result = with_spinner(spinner_message) do
|
135
|
+
stdout, stderr, status = Open3.capture3(*args)
|
136
|
+
|
137
|
+
if status.success?
|
138
|
+
# Log output if there is any (for debugging)
|
139
|
+
logger&.debug("Output: #{stdout}") if stdout && !stdout.empty?
|
140
|
+
{success: true, message: "✓"}
|
141
|
+
elsif args.first.to_s.include?("docker")
|
142
|
+
logger&.debug("Error: #{stderr}") if stderr && !stderr.empty?
|
143
|
+
{success: false, message: "✗ (Docker command failed)", raise_error: false}
|
144
|
+
else
|
145
|
+
{success: false, message: "✗", error: "#{command_str} failed: #{stderr}"}
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Handle the case when spinner is disabled
|
150
|
+
if result.is_a?(Hash) && !result[:success] && result[:raise_error] != false
|
151
|
+
raise result[:error]
|
152
|
+
end
|
153
|
+
|
154
|
+
result
|
155
|
+
end
|
156
|
+
|
157
|
+
def system_quiet(*args)
|
158
|
+
require "open3"
|
159
|
+
stdout, _stderr, status = Open3.capture3(*args)
|
160
|
+
logger&.debug("Quietly executed #{args.join(" ")} - success: #{status.success?}")
|
161
|
+
logger&.debug("Output: #{stdout}") if stdout && !stdout.empty? && logger
|
162
|
+
status.success?
|
163
|
+
end
|
164
|
+
|
165
|
+
def ask_to_install(description)
|
166
|
+
unless ENV["QUIET_SETUP"] || ENV["DISABLE_OUTPUT"]
|
167
|
+
puts "You do not currently use #{description}.\n ===> If you want to, type Y\nOtherwise hit any key to ignore."
|
168
|
+
end
|
169
|
+
if gets.chomp == "Y"
|
170
|
+
yield
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def proceed_with(task)
|
175
|
+
unless ENV["QUIET_SETUP"] || ENV["DISABLE_OUTPUT"]
|
176
|
+
puts "Proceed with #{task}?\n ===> Type Y to proceed\nOtherwise hit any key to ignore."
|
177
|
+
end
|
178
|
+
if gets.chomp == "Y"
|
179
|
+
yield
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_command"
|
4
|
+
|
5
|
+
module Discharger
|
6
|
+
module SetupRunner
|
7
|
+
module Commands
|
8
|
+
class BrewCommand < BaseCommand
|
9
|
+
def execute
|
10
|
+
proceed_with "brew bundle" do
|
11
|
+
log "Ensuring brew dependencies"
|
12
|
+
system! "brew bundle"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def can_execute?
|
17
|
+
File.exist?("Brewfile")
|
18
|
+
end
|
19
|
+
|
20
|
+
def description
|
21
|
+
"Install Homebrew dependencies"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_command"
|
4
|
+
|
5
|
+
module Discharger
|
6
|
+
module SetupRunner
|
7
|
+
module Commands
|
8
|
+
class BundlerCommand < BaseCommand
|
9
|
+
def execute
|
10
|
+
log "Installing dependencies"
|
11
|
+
system! "gem install bundler --conservative"
|
12
|
+
system_quiet("bundle check") || system!("bundle install")
|
13
|
+
end
|
14
|
+
|
15
|
+
def can_execute?
|
16
|
+
File.exist?("Gemfile")
|
17
|
+
end
|
18
|
+
|
19
|
+
def description
|
20
|
+
"Install Ruby dependencies"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base_command"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
module Discharger
|
7
|
+
module SetupRunner
|
8
|
+
module Commands
|
9
|
+
class ConfigCommand < BaseCommand
|
10
|
+
def execute
|
11
|
+
log "Ensuring configuration files are present"
|
12
|
+
|
13
|
+
# Copy database.yml if needed
|
14
|
+
database_yml = File.join(app_root, "config/database.yml")
|
15
|
+
database_yml_example = File.join(app_root, "config/database.yml.example")
|
16
|
+
|
17
|
+
if !File.exist?(database_yml) && File.exist?(database_yml_example)
|
18
|
+
FileUtils.cp(database_yml_example, database_yml)
|
19
|
+
log "Copied config/database.yml.example to config/database.yml"
|
20
|
+
end
|
21
|
+
|
22
|
+
# Copy Procfile.dev to Procfile if needed
|
23
|
+
procfile = File.join(app_root, "Procfile")
|
24
|
+
procfile_dev = File.join(app_root, "Procfile.dev")
|
25
|
+
|
26
|
+
if !File.exist?(procfile) && File.exist?(procfile_dev)
|
27
|
+
FileUtils.cp(procfile_dev, procfile)
|
28
|
+
log "Copied Procfile.dev to Procfile"
|
29
|
+
end
|
30
|
+
|
31
|
+
# Copy any other example config files
|
32
|
+
Dir.glob(File.join(app_root, "config/**/*.example")).each do |example_file|
|
33
|
+
config_file = example_file.sub(/\.example$/, "")
|
34
|
+
unless File.exist?(config_file)
|
35
|
+
FileUtils.cp(example_file, config_file)
|
36
|
+
log "Copied #{example_file.sub(app_root + "/", "")} to #{config_file.sub(app_root + "/", "")}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def can_execute?
|
42
|
+
true
|
43
|
+
end
|
44
|
+
|
45
|
+
def description
|
46
|
+
"Setup configuration files"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../condition_evaluator"
|
4
|
+
|
5
|
+
module Discharger
|
6
|
+
module SetupRunner
|
7
|
+
module Commands
|
8
|
+
class CustomCommand < BaseCommand
|
9
|
+
attr_reader :step_config
|
10
|
+
|
11
|
+
def initialize(config, app_root, logger, step_config)
|
12
|
+
super(config, app_root, logger)
|
13
|
+
@step_config = step_config
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
command = step_config["command"]
|
18
|
+
description = step_config["description"] || command
|
19
|
+
condition = step_config["condition"]
|
20
|
+
|
21
|
+
# Check condition if provided using safe evaluator
|
22
|
+
if condition && !ConditionEvaluator.evaluate(condition)
|
23
|
+
log "Skipping #{description} (condition not met)"
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
log "Running: #{description}"
|
28
|
+
system!(command)
|
29
|
+
end
|
30
|
+
|
31
|
+
def can_execute?
|
32
|
+
step_config["command"].present?
|
33
|
+
end
|
34
|
+
|
35
|
+
def description
|
36
|
+
step_config["description"] || "Custom command: #{step_config["command"]}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|