discharger 0.2.7 → 0.2.9
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/lib/discharger/procedure.rb +117 -0
- data/lib/discharger/railway.rb +111 -0
- data/lib/discharger/task.rb +4 -2
- data/lib/discharger/version.rb +1 -1
- metadata +5 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d98bb157c8cb3b219d105f4c6f1b18715cba0706a6cedf93f1991852fc11a04
|
4
|
+
data.tar.gz: 1115667f6e8c9e94662b7ba7d76769e522b3fa26961b6217b78420fe9b095baa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85870fbc537b4d4c3c86326c3c6cfc0f06adeeba24a831ff65f7ab858004e068baa6f1b69ec46945dbbdbfd42094f2614614402f931369815f72c13c970d38d6
|
7
|
+
data.tar.gz: 4ac3f985792dc65968030f09319e08e41a840df0c96113791bdee1afa8c8aa989ec347b1ebb7412cdbb42b1331c411403f620948dc3ae14a3e78bf5adce667cd
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Discharger
|
2
|
+
class Procedure
|
3
|
+
include Railway::Steps
|
4
|
+
|
5
|
+
def initialize(name, system_command: nil, **data)
|
6
|
+
@name = name
|
7
|
+
@data = data
|
8
|
+
@system_command = system_command || method(:syscall)
|
9
|
+
@steps = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def call
|
13
|
+
result = run_steps(
|
14
|
+
*step_procs,
|
15
|
+
branches: {
|
16
|
+
permission_denied: :handle_permissions,
|
17
|
+
network_error: :retry_network_operation,
|
18
|
+
any_failure: ->(_) {
|
19
|
+
puts "An error occurred but we'll try to recover"
|
20
|
+
Success()
|
21
|
+
}
|
22
|
+
}
|
23
|
+
)
|
24
|
+
|
25
|
+
if result.failure?
|
26
|
+
puts "Failed: #{result.error}".bg(:red).black
|
27
|
+
return false
|
28
|
+
end
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def step_procs
|
33
|
+
steps.map do |step|
|
34
|
+
->(_) { @system_command.call(step) }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Run a multiple system commands and return a Result
|
39
|
+
# If any command fails, returns a Failure result
|
40
|
+
def syscall(*cmd, output: $stdout, error: $stderr)
|
41
|
+
puts cmd.join(" ").bg(:green).black
|
42
|
+
stdout, stderr, status = Open3.capture3(*cmd)
|
43
|
+
|
44
|
+
if status.success?
|
45
|
+
output.puts stdout
|
46
|
+
Success(stdout)
|
47
|
+
else
|
48
|
+
error.puts stderr
|
49
|
+
|
50
|
+
# Different failure types based on the error
|
51
|
+
if stderr.include?("Permission denied")
|
52
|
+
Failure(stderr, :permission_denied)
|
53
|
+
elsif stderr.include?("Network")
|
54
|
+
Failure(stderr, :network_error)
|
55
|
+
else
|
56
|
+
Failure(stderr)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
rescue => e
|
60
|
+
Failure("Exception: #{e.message}")
|
61
|
+
end
|
62
|
+
|
63
|
+
# Version that supports evaluating the result with a block
|
64
|
+
def syscall_with_block(*cmd, output: $stdout, error: $stderr, &block)
|
65
|
+
result = syscall(*cmd, output: output, error: error)
|
66
|
+
|
67
|
+
if result.success? && block_given?
|
68
|
+
stdout, stderr, status = result.value, "", $?
|
69
|
+
block_result = !!yield(stdout, stderr, status)
|
70
|
+
|
71
|
+
# Handle the bypassed rule case
|
72
|
+
if !block_result && stderr.match?(/bypassed rule violations/i)
|
73
|
+
block_result = true
|
74
|
+
end
|
75
|
+
|
76
|
+
return block_result ? Success(stdout) : Failure(stderr)
|
77
|
+
end
|
78
|
+
|
79
|
+
result
|
80
|
+
end
|
81
|
+
|
82
|
+
# Handle permission errors
|
83
|
+
def handle_permissions(result)
|
84
|
+
puts "Permission error: #{result.error}"
|
85
|
+
# Try to fix permissions
|
86
|
+
if fix_permissions
|
87
|
+
Success("Permissions fixed")
|
88
|
+
else
|
89
|
+
result # Return the original failure if we couldn't fix it
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Retry network operations
|
94
|
+
def retry_network_operation(result)
|
95
|
+
3.times do |i|
|
96
|
+
puts "Network error, retrying (#{i + 1}/3)..."
|
97
|
+
sleep 2
|
98
|
+
result = retry_last_command
|
99
|
+
return result if result.success?
|
100
|
+
end
|
101
|
+
Failure("Network operation failed after 3 retries")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
class Build < Procedure
|
106
|
+
def steps
|
107
|
+
@steps ||= [
|
108
|
+
["fetch origin #{working_branch}"],
|
109
|
+
["checkout #{working_branch}"],
|
110
|
+
["reset --hard origin/#{working_branch}"],
|
111
|
+
["branch -D #{staging_branch} 2>/dev/null || true"],
|
112
|
+
["checkout -b #{staging_branch}"],
|
113
|
+
["push origin #{staging_branch} --force"]
|
114
|
+
]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Discharger
|
2
|
+
module Railway
|
3
|
+
# Represents the result of an operation - either Success or Failure
|
4
|
+
class Result
|
5
|
+
attr_reader :value, :error, :status
|
6
|
+
|
7
|
+
def initialize(status, value = nil, error = nil)
|
8
|
+
@status = status
|
9
|
+
@value = value
|
10
|
+
@error = error
|
11
|
+
end
|
12
|
+
|
13
|
+
def success?
|
14
|
+
@status == :success
|
15
|
+
end
|
16
|
+
|
17
|
+
def failure?
|
18
|
+
!success?
|
19
|
+
end
|
20
|
+
|
21
|
+
# Get the failure type/status
|
22
|
+
def failure_type
|
23
|
+
failure? ? @status : nil
|
24
|
+
end
|
25
|
+
|
26
|
+
# Factory method for creating success results
|
27
|
+
def self.success(value = nil)
|
28
|
+
new(:success, value)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Factory method for creating failure results
|
32
|
+
def self.failure(error = nil, type = :error)
|
33
|
+
new(type, nil, error)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Allows chaining operations if this result is a success
|
37
|
+
def then
|
38
|
+
return self if failure?
|
39
|
+
yield(value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Module to include in classes that want to use railway flows
|
44
|
+
module Steps
|
45
|
+
def Success(value = nil)
|
46
|
+
Result.success(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def Failure(error = nil, type = :error)
|
50
|
+
Result.failure(error, type)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Execute a series of steps with branching logic
|
54
|
+
def run_steps(*steps, branches: {})
|
55
|
+
result = Success()
|
56
|
+
step_index = 0
|
57
|
+
|
58
|
+
while step_index < steps.length
|
59
|
+
step = steps[step_index]
|
60
|
+
|
61
|
+
# Execute the current step
|
62
|
+
result = execute_step(step, result.value)
|
63
|
+
|
64
|
+
if result.failure?
|
65
|
+
# Check if there's a branch for this failure type
|
66
|
+
branch = branches[result.status] || branches[:any_failure]
|
67
|
+
|
68
|
+
if branch
|
69
|
+
if branch.is_a?(Integer)
|
70
|
+
# Jump to the specified step index
|
71
|
+
step_index = branch
|
72
|
+
next
|
73
|
+
elsif branch.is_a?(Proc)
|
74
|
+
# Execute the branch handler
|
75
|
+
branch_result = branch.call(result)
|
76
|
+
return branch_result if branch_result.is_a?(Result)
|
77
|
+
# If the branch handler doesn't return a Result, continue with the next step
|
78
|
+
elsif branch.is_a?(Symbol)
|
79
|
+
# Execute the named method
|
80
|
+
branch_result = send(branch, result)
|
81
|
+
return branch_result if branch_result.is_a?(Result)
|
82
|
+
elsif branch == :continue
|
83
|
+
# Continue to the next step despite the failure
|
84
|
+
else
|
85
|
+
return result # Default: stop processing on failure
|
86
|
+
end
|
87
|
+
else
|
88
|
+
return result # No branch defined, stop processing
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
step_index += 1
|
93
|
+
end
|
94
|
+
|
95
|
+
result
|
96
|
+
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def execute_step(step, input)
|
101
|
+
if step.is_a?(Proc)
|
102
|
+
step.call(input)
|
103
|
+
elsif step.is_a?(Symbol)
|
104
|
+
send(step, input)
|
105
|
+
else
|
106
|
+
raise ArgumentError, "Step must be a Proc or Symbol, got #{step.class}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/discharger/task.rb
CHANGED
@@ -147,7 +147,7 @@ module Discharger
|
|
147
147
|
["git push origin #{production_branch}:#{production_branch} v#{current_version}:v#{current_version}"],
|
148
148
|
["git push origin v#{current_version}"]
|
149
149
|
) do
|
150
|
-
tasker["#{name}:slack"].invoke("Released #{app_name} #{current_version} to production.", release_message_channel, ":chipmunk:")
|
150
|
+
tasker["#{name}:slack"].invoke("Released #{app_name} #{current_version} (#{commit_identifier.call}) to production.", release_message_channel, ":chipmunk:")
|
151
151
|
if last_message_ts.present?
|
152
152
|
text = File.read(Rails.root.join(changelog_file))
|
153
153
|
tasker["#{name}:slack"].reenable
|
@@ -206,11 +206,13 @@ module Discharger
|
|
206
206
|
syscall(
|
207
207
|
["git fetch origin #{working_branch}"],
|
208
208
|
["git checkout #{working_branch}"],
|
209
|
+
["git reset --hard origin/#{working_branch}"],
|
209
210
|
["git branch -D #{staging_branch} 2>/dev/null || true"],
|
210
211
|
["git checkout -b #{staging_branch}"],
|
211
212
|
["git push origin #{staging_branch} --force"]
|
212
213
|
) do
|
213
|
-
|
214
|
+
current_version = Object.const_get(version_constant)
|
215
|
+
tasker["#{name}:slack"].invoke("Building #{app_name} #{current_version} (#{commit_identifier.call}) on #{staging_branch}.", release_message_channel)
|
214
216
|
syscall ["git checkout #{working_branch}"]
|
215
217
|
end
|
216
218
|
end
|
data/lib/discharger/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: discharger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Gay
|
8
8
|
- Savannah Moore
|
9
|
-
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2025-
|
11
|
+
date: 2025-03-03 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: open3
|
@@ -92,7 +91,9 @@ files:
|
|
92
91
|
- README.md
|
93
92
|
- Rakefile
|
94
93
|
- lib/discharger.rb
|
94
|
+
- lib/discharger/procedure.rb
|
95
95
|
- lib/discharger/railtie.rb
|
96
|
+
- lib/discharger/railway.rb
|
96
97
|
- lib/discharger/task.rb
|
97
98
|
- lib/discharger/version.rb
|
98
99
|
- lib/generators/discharger/install/install_generator.rb
|
@@ -103,7 +104,6 @@ metadata:
|
|
103
104
|
homepage_uri: https://github.com/SOFware/discharger
|
104
105
|
source_code_uri: https://github.com/SOFware/discharger.git
|
105
106
|
changelog_uri: https://github.com/SOFware/discharger/blob/main/CHANGELOG.md
|
106
|
-
post_install_message:
|
107
107
|
rdoc_options: []
|
108
108
|
require_paths:
|
109
109
|
- lib
|
@@ -118,8 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
118
118
|
- !ruby/object:Gem::Version
|
119
119
|
version: '0'
|
120
120
|
requirements: []
|
121
|
-
rubygems_version: 3.
|
122
|
-
signing_key:
|
121
|
+
rubygems_version: 3.6.3
|
123
122
|
specification_version: 4
|
124
123
|
summary: Tasks for discharging an application for deployment.
|
125
124
|
test_files: []
|