discharger 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +7 -0
- data/README.md +58 -0
- data/Rakefile +12 -0
- data/lib/discharger/railtie.rb +9 -0
- data/lib/discharger/task.rb +299 -0
- data/lib/discharger/version.rb +3 -0
- data/lib/discharger.rb +5 -0
- metadata +122 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 48d8ec34c0e97892c1dd06c2a23e5d5f61028949d04638d848e62258c1c9f89c
|
4
|
+
data.tar.gz: d7a1d3338e2fe6bbbad2088e29017c1cefd6c5dac70b4dc45fc3f9429417f581
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ab11db3d3ebfbaf567bafa691bf390a5c7b663f4d7b3d935e0996d057fe68472dc24e06b9b1c53fe1a41ec5de4007a78307c6243fff9f8eb9d71f22c86faf9f5
|
7
|
+
data.tar.gz: dc349824f12833069806e1b1a6fdad4fd1b17e53f349f155724a1c42a220500c6670cbcce43c90b64a331c6cd26cc804f1c84eeac5dd18bb04b656e6413cbaab
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2024 SOFware LLC
|
2
|
+
|
3
|
+
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:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
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,58 @@
|
|
1
|
+
# Discharger
|
2
|
+
Code supporting tasks that discharge code for deployment.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
|
6
|
+
Add `require "discharger/task"` to your Rakefile.
|
7
|
+
|
8
|
+
Then build the discharger task
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
require "discharger/task"
|
12
|
+
|
13
|
+
Discharger::Task.create do |task|
|
14
|
+
task.version_file = "config/application.rb"
|
15
|
+
task.release_message_channel = "#some-slack-channel"
|
16
|
+
task.version_constant = "MyApp::VERSION"
|
17
|
+
task.app_name = "My App name"
|
18
|
+
task.commit_identifier = -> { `git rev-parse HEAD`.strip }
|
19
|
+
task.pull_request_url = "https://github.com/SOFware/some-app"
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
It will make Rake tasks available to push code to branches and notify Slack channels.
|
24
|
+
|
25
|
+
```bash
|
26
|
+
$ rake -T release
|
27
|
+
rake release # ---------- STEP 3 ----------
|
28
|
+
rake release:build # Release the current version to stage
|
29
|
+
rake release:prepare # ---------- STEP 1 ----------
|
30
|
+
rake release:slack[text,channel,emoji] # Send a message to Slack
|
31
|
+
rake release:stage # ---------- STEP 2 ----------
|
32
|
+
```
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
Add this line to your application's Gemfile:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
gem "discharger"
|
39
|
+
```
|
40
|
+
|
41
|
+
And then execute:
|
42
|
+
```bash
|
43
|
+
$ bundle
|
44
|
+
```
|
45
|
+
|
46
|
+
Or install it yourself as:
|
47
|
+
```bash
|
48
|
+
$ gem install discharger
|
49
|
+
```
|
50
|
+
|
51
|
+
## Contributing
|
52
|
+
|
53
|
+
This gem is managed with [Reissue](https://github.com/SOFware/reissue).
|
54
|
+
|
55
|
+
Bug reports and pull requests are welcome on GitHub.
|
56
|
+
|
57
|
+
## License
|
58
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,299 @@
|
|
1
|
+
require "rake/tasklib"
|
2
|
+
|
3
|
+
module Discharger
|
4
|
+
class Task < Rake::TaskLib
|
5
|
+
def self.create(name = :release, &block)
|
6
|
+
task = new(name)
|
7
|
+
task.instance_eval(&block) if block
|
8
|
+
task.define
|
9
|
+
task
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_accessor :name
|
13
|
+
|
14
|
+
attr_accessor :description
|
15
|
+
|
16
|
+
attr_accessor :working_branch
|
17
|
+
attr_accessor :staging_branch
|
18
|
+
attr_accessor :production_branch
|
19
|
+
|
20
|
+
attr_accessor :release_message_channel
|
21
|
+
attr_accessor :version_constant
|
22
|
+
|
23
|
+
attr_accessor :chat_token
|
24
|
+
attr_accessor :app_name
|
25
|
+
attr_accessor :commit_identifier
|
26
|
+
attr_accessor :pull_request_url
|
27
|
+
|
28
|
+
# Reissue settings
|
29
|
+
attr_accessor :version_file
|
30
|
+
attr_accessor :version_limit
|
31
|
+
attr_accessor :version_redo_proc
|
32
|
+
attr_accessor :changelog_file
|
33
|
+
attr_accessor :updated_paths
|
34
|
+
attr_accessor :commit
|
35
|
+
attr_accessor :commit_finalize
|
36
|
+
|
37
|
+
def initialize(name = :release)
|
38
|
+
@name = name
|
39
|
+
@working_branch = "develop"
|
40
|
+
@staging_branch = "stage"
|
41
|
+
@production_branch = "main"
|
42
|
+
@description = "Release the current version to #{staging_branch}"
|
43
|
+
end
|
44
|
+
|
45
|
+
# Run a multiple system commands and return true if all commands succeed
|
46
|
+
# If any command fails, the method will return false and stop executing
|
47
|
+
# any further commands.
|
48
|
+
#
|
49
|
+
# Provide a block to evaluate the output of the command and return true
|
50
|
+
# if the command was successful. If the block returns false, the method
|
51
|
+
# will return false and stop executing any further commands.
|
52
|
+
#
|
53
|
+
# @param *steps [Array<Array<String>>] an array of commands to run
|
54
|
+
# @param block [Proc] a block to evaluate the output of the command
|
55
|
+
# @return [Boolean] true if all commands succeed, false otherwise
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# syscall(
|
59
|
+
# ["echo", "Hello, World!"],
|
60
|
+
# ["ls", "-l"]
|
61
|
+
# )
|
62
|
+
def syscall(*steps)
|
63
|
+
success = false
|
64
|
+
stdout, stderr, status = nil
|
65
|
+
steps.each do |*cmd|
|
66
|
+
puts cmd.join(" ").bg(:green).black
|
67
|
+
stdout, stderr, status = Open3.capture3(*cmd)
|
68
|
+
if status.success?
|
69
|
+
puts stdout
|
70
|
+
else
|
71
|
+
puts stderr
|
72
|
+
success = false
|
73
|
+
abort(stderr)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
if block_given?
|
77
|
+
success = !!yield(stdout, stderr, status)
|
78
|
+
# If the error reports that a rule was bypassed, consider the command successful
|
79
|
+
# because we are bypassing the rule intentionally when merging the release branch
|
80
|
+
# to the production branch.
|
81
|
+
success = true if stderr.match?(/bypassed rule violations/i)
|
82
|
+
abort(stderr) unless success
|
83
|
+
end
|
84
|
+
success
|
85
|
+
end
|
86
|
+
|
87
|
+
# Echo a message to the console
|
88
|
+
#
|
89
|
+
# @param message [String] the message to echo
|
90
|
+
# return [TrueClass]
|
91
|
+
def sysecho(message)
|
92
|
+
system "echo", message
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
def define
|
97
|
+
require "slack-ruby-client"
|
98
|
+
Slack.configure do |config|
|
99
|
+
config.token = chat_token
|
100
|
+
end
|
101
|
+
|
102
|
+
desc <<~DESC
|
103
|
+
---------- STEP 3 ----------
|
104
|
+
Release the current version to production
|
105
|
+
|
106
|
+
This task rebases the production branch on the staging branch and tags the
|
107
|
+
current version. The production branch and the tag will be pushed to the
|
108
|
+
remote repository.
|
109
|
+
|
110
|
+
After the release is complete, a new branch will be created to bump the
|
111
|
+
version for the next release.
|
112
|
+
DESC
|
113
|
+
task "#{name}": [:environment] do
|
114
|
+
current_version = Object.const_get(version_constant)
|
115
|
+
sysecho <<~MSG
|
116
|
+
Releasing version #{current_version} to production.
|
117
|
+
|
118
|
+
This will tag the current version and push it to the production branch.
|
119
|
+
MSG
|
120
|
+
sysecho "Are you ready to continue? (Press Enter to continue, Type 'x' and Enter to exit)".bg(:yellow).black
|
121
|
+
input = $stdin.gets
|
122
|
+
exit if input.chomp.match?(/^x/i)
|
123
|
+
|
124
|
+
continue = syscall(
|
125
|
+
"git checkout #{working_branch}",
|
126
|
+
"git branch -D #{staging_branch} 2> /dev/null || true",
|
127
|
+
"git branch -D #{production_branch} 2> /dev/null || true",
|
128
|
+
"git fetch origin #{staging_branch}:#{staging_branch} #{production_branch}:#{production_branch}",
|
129
|
+
"git checkout #{production_branch}",
|
130
|
+
"git rebase #{staging_branch}",
|
131
|
+
"git tag -a v#{current_version} -m 'Release #{current_version}'",
|
132
|
+
"git push origin #{production_branch}; git push origin v#{current_version}"
|
133
|
+
) do
|
134
|
+
Rake::Task["slack"].invoke("Released #{Qualify.name} #{current_version} to production.", release_message_channel, ":chipmunk:")
|
135
|
+
syscall "git checkout #{working_branch}"
|
136
|
+
end
|
137
|
+
|
138
|
+
abort "Release failed." unless continue
|
139
|
+
|
140
|
+
sysecho <<~MSG
|
141
|
+
Version #{current_version} released to production.
|
142
|
+
|
143
|
+
Preparing to bump the version for the next release.
|
144
|
+
|
145
|
+
MSG
|
146
|
+
|
147
|
+
new_version_branch = "bump/begin-#{current_version.tr(".", "-")}"
|
148
|
+
|
149
|
+
continue = syscall("git checkout -b #{new_version_branch}") do
|
150
|
+
Rake::Task["reissue"].invoke
|
151
|
+
end
|
152
|
+
|
153
|
+
abort "Bump failed." unless continue
|
154
|
+
|
155
|
+
pr_url = "#{pull_request_url}/compare/#{working_branch}...#{new_version_branch}?expand=1&title=Begin%20#{current_version}"
|
156
|
+
|
157
|
+
syscall("git push origin #{new_version_branch} --force") do
|
158
|
+
sysecho <<~MSG
|
159
|
+
Branch #{new_version_branch} created.
|
160
|
+
|
161
|
+
Open a PR to #{working_branch} to mark the version and update the chaneglog
|
162
|
+
for the next release.
|
163
|
+
|
164
|
+
Opening PR: #{pr_url}
|
165
|
+
MSG
|
166
|
+
end.then do |success|
|
167
|
+
syscall "open #{pr_url}" if success
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
namespace name do
|
172
|
+
desc description
|
173
|
+
task build: :environment do
|
174
|
+
syscall(
|
175
|
+
"git fetch origin #{working_branch}",
|
176
|
+
"git checkout #{working_branch}",
|
177
|
+
"git branch -D #{staging_branch} 2> /dev/null || true",
|
178
|
+
"git checkout -b #{staging_branch}",
|
179
|
+
"git push origin #{staging_branch} --force"
|
180
|
+
) do
|
181
|
+
Rake::Task["slack"].invoke("Building #{app_name} #{commit_identifier.call} on #{staging_branch}.", release_message_channel)
|
182
|
+
syscall "git checkout #{working_branch}"
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
desc "Send a message to Slack."
|
187
|
+
task :slack, [:text, :channel, :emoji] => :environment do |_, args|
|
188
|
+
args.with_defaults(
|
189
|
+
channel: release_message_channel,
|
190
|
+
emoji: nil
|
191
|
+
)
|
192
|
+
client = Slack::Web::Client.new
|
193
|
+
options = args.to_h
|
194
|
+
options[:icon_emoji] = options.delete(:emoji) if options[:emoji]
|
195
|
+
|
196
|
+
sysecho "Sending message to Slack:".bg(:green).black + " #{args[:text]}"
|
197
|
+
result = client.chat_postMessage(**options)
|
198
|
+
sysecho %(Message sent: #{result["ts"]})
|
199
|
+
end
|
200
|
+
|
201
|
+
desc <<~DESC
|
202
|
+
---------- STEP 1 ----------
|
203
|
+
Prepare the current version for release to production (#{production_branch})
|
204
|
+
|
205
|
+
This task will create a new branch to prepare the release. The CHANGELOG
|
206
|
+
will be updated and the version will be bumped. The branch will be pushed
|
207
|
+
to the remote repository.
|
208
|
+
|
209
|
+
After the branch is created, open a PR to #{working_branch} to finalize
|
210
|
+
the release.
|
211
|
+
DESC
|
212
|
+
task prepare: [:environment] do
|
213
|
+
current_version = Object.const_get(version_constant)
|
214
|
+
finish_branch = "bump/finish-#{current_version.tr(".", "-")}"
|
215
|
+
|
216
|
+
syscall "git fetch origin #{working_branch}",
|
217
|
+
"git checkout #{working_branch}",
|
218
|
+
"git checkout -b #{finish_branch}"
|
219
|
+
|
220
|
+
sysecho <<~MSG
|
221
|
+
Branch #{finish_branch} created.
|
222
|
+
|
223
|
+
Check the contents of the CHANGELOG and ensure that the text is correct.
|
224
|
+
|
225
|
+
If you need to make changes, edit the CHANGELOG and save the file.
|
226
|
+
Then return here to continue with this commit.
|
227
|
+
MSG
|
228
|
+
sysecho "Are you ready to continue? (Press Enter to continue, Type 'x' and Enter to exit)".bg(:yellow).black
|
229
|
+
input = $stdin.gets
|
230
|
+
exit if input.chomp.match?(/^x/i)
|
231
|
+
|
232
|
+
Rake::Task["reissue:finalize"].invoke
|
233
|
+
|
234
|
+
params = {
|
235
|
+
expand: 1,
|
236
|
+
title: "Finish version #{current_version}",
|
237
|
+
body: <<~BODY
|
238
|
+
Completing development for #{current_version}.
|
239
|
+
BODY
|
240
|
+
}
|
241
|
+
|
242
|
+
pr_url = "#{pull_request_url}/compare/#{finish_branch}?#{params.to_query}"
|
243
|
+
|
244
|
+
continue = syscall "git push origin #{finish_branch} --force" do
|
245
|
+
sysecho <<~MSG
|
246
|
+
Branch #{finish_branch} created.
|
247
|
+
Open a PR to #{working_branch} to finalize the release.
|
248
|
+
|
249
|
+
#{pr_url}
|
250
|
+
|
251
|
+
Once the PR is merged, pull down #{working_branch} and run
|
252
|
+
'rake #{name}:stage'
|
253
|
+
to stage the release branch.
|
254
|
+
MSG
|
255
|
+
end
|
256
|
+
if continue
|
257
|
+
syscall "git checkout #{working_branch}",
|
258
|
+
"open #{pr_url}"
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
desc <<~DESC
|
263
|
+
---------- STEP 2 ----------
|
264
|
+
Stage the release branch
|
265
|
+
|
266
|
+
This task will update Stage, open a PR, and instruct you on the next steps.
|
267
|
+
|
268
|
+
NOTE: If you just want to update the stage environment but aren't ready to release, run:
|
269
|
+
|
270
|
+
bin/rails #{name}:build
|
271
|
+
DESC
|
272
|
+
task stage: [:environment] do
|
273
|
+
Rake::Task["build"].invoke
|
274
|
+
current_version = Object.const_get(version_constant)
|
275
|
+
|
276
|
+
params = {
|
277
|
+
expand: 1,
|
278
|
+
title: "Release #{current_version} to production",
|
279
|
+
body: <<~BODY
|
280
|
+
Deploy #{current_version} to production.
|
281
|
+
BODY
|
282
|
+
}
|
283
|
+
|
284
|
+
pr_url = "#{pull_request_url}/compare/#{production_branch}...#{staging_branch}?#{params.to_query}"
|
285
|
+
|
286
|
+
sysecho <<~MSG
|
287
|
+
Branch #{staging_branch} updated.
|
288
|
+
Open a PR to #{production_branch} to release the version.
|
289
|
+
|
290
|
+
Opening PR: #{pr_url}
|
291
|
+
|
292
|
+
Once the PR is **approved**, run 'rake release' to release the version.
|
293
|
+
MSG
|
294
|
+
syscall "open #{pr_url}"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
data/lib/discharger.rb
ADDED
metadata
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: discharger
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jim Gay
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-09-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: open3
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 7.2.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 7.2.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rainbow
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: reissue
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: slack-ruby-client
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Code supporting deployments.
|
84
|
+
email:
|
85
|
+
- jim@saturnflyer.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- LICENSE
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- lib/discharger.rb
|
94
|
+
- lib/discharger/railtie.rb
|
95
|
+
- lib/discharger/task.rb
|
96
|
+
- lib/discharger/version.rb
|
97
|
+
homepage: https://github.com/SOFware/discharger
|
98
|
+
licenses: []
|
99
|
+
metadata:
|
100
|
+
homepage_uri: https://github.com/SOFware/discharger
|
101
|
+
source_code_uri: https://github.com/SOFware/discharger.git
|
102
|
+
changelog_uri: https://github.com/SOFware/discharger/blob/main/CHANGELOG.md
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options: []
|
105
|
+
require_paths:
|
106
|
+
- lib
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ">="
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '0'
|
117
|
+
requirements: []
|
118
|
+
rubygems_version: 3.5.9
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Tasks for discharging an application for deployment.
|
122
|
+
test_files: []
|