afterlife 1.7.2 → 1.7.4
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/.vscode/settings.json.example +3 -0
- data/README.md +37 -0
- data/afterlife.gemspec +2 -0
- data/lib/afterlife/cli.rb +3 -0
- data/lib/afterlife/clickup/api.rb +36 -0
- data/lib/afterlife/clickup/cli.rb +49 -0
- data/lib/afterlife/clickup/get_ids_from_text.rb +30 -0
- data/lib/afterlife/clickup/get_range_commits.rb +21 -0
- data/lib/afterlife/clickup/status_precedence_checker.rb +40 -0
- data/lib/afterlife/clickup/update_task_status.rb +53 -0
- data/lib/afterlife/clickup.rb +6 -0
- data/lib/afterlife/deploy/kubernetes_deployment.rb +1 -1
- data/lib/afterlife/version.rb +1 -1
- metadata +39 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b3027a754b5aeaaa15a7dbbba4b5aa895ce11fb48402dd9a4348f30b3dc8aa7
|
4
|
+
data.tar.gz: 2253a73fd40b0e247cafb3b35b0bd0d75fb9b78d722b7887a7800b30d0f8faa9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 01a9c292bef075d314f0ce97ff3deda29705247f9e6e0227c82645292fe0ffe262a4d18d62b8abd896b6376325bcb5071a61d90f992ee47e42dba0a0828bba64
|
7
|
+
data.tar.gz: 2f7763617dbd485d863afb4ea226caac63fa3493b14ab3a0c35c186d11ff22f01a8b2e895ba0b7d12ca5cc30db45e140a4be0814e557840cdffd3ff085016b99
|
data/README.md
CHANGED
@@ -20,12 +20,49 @@ Logs can be found on `~/.afterlife/logs/cdn.log`
|
|
20
20
|
|
21
21
|
Running `afterlife cdn start` will run the CDN web server in the background. Running in foreground using `afterlife cdn start --foreground` will display errors and content normally found in logs in console instead.
|
22
22
|
|
23
|
+
# About Clickup module
|
24
|
+
Usage example:
|
25
|
+
```sh
|
26
|
+
afterlife clickup get-commits <revision> | afterlife clickup extract-ids | afterlife clickup update-status 'ready for qa'
|
27
|
+
```
|
28
|
+
This will output something like:
|
29
|
+
```sh
|
30
|
+
Updating 1 tasks to 'trunk merged'
|
31
|
+
Moved 1 tasks, with ids: 86dxbp9d6
|
32
|
+
```
|
33
|
+
|
34
|
+
The above line does the following:
|
35
|
+
1. Prints all the commits betweed the specified <revision> and HEAD.
|
36
|
+
2. Extracts the clickup ids from the previous output.
|
37
|
+
3. Moves the ids extracted from the previous output to 'ready for qa' status.
|
38
|
+
|
39
|
+
To not do this blindly, you can run just `afterlife clickup get-commits <revision> | afterlife
|
40
|
+
clickup extract-ids` to review if the tasks being moved are correct. But the `update-status` in the
|
41
|
+
full command will print the list of moved or ignored tasks anyway.
|
42
|
+
|
43
|
+
Also, to easily run these commands, is recommended to setup an alias:
|
44
|
+
|
45
|
+
For bash or zsh:
|
46
|
+
```bash
|
47
|
+
alias ci="afterlife clickup"
|
48
|
+
```
|
49
|
+
|
50
|
+
For fish:
|
51
|
+
```fish
|
52
|
+
alias ci 'afterlife clickup'
|
53
|
+
```
|
54
|
+
|
23
55
|
## Development
|
24
56
|
|
25
57
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
26
58
|
|
27
59
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
28
60
|
|
61
|
+
## Publishing
|
62
|
+
This gem is published on rubygems. To publish there, just:
|
63
|
+
1. Do a version bump in `lib/afterlife/version.rb`.
|
64
|
+
2. Execute `bundle exec rake release`. This will ask you for rubygems credentials. You'll access, ask Genaro for it.
|
65
|
+
|
29
66
|
## Contributing
|
30
67
|
|
31
68
|
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/afterlife.
|
data/afterlife.gemspec
CHANGED
@@ -28,6 +28,8 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
29
|
spec.require_paths = ['lib']
|
30
30
|
|
31
|
+
spec.add_dependency 'faraday'
|
32
|
+
spec.add_dependency 'racc' # rubocop dependency, cannot run `bundle exec rubocop` without it
|
31
33
|
spec.add_dependency 'thor'
|
32
34
|
spec.add_dependency 'zeitwerk'
|
33
35
|
|
data/lib/afterlife/cli.rb
CHANGED
@@ -47,6 +47,9 @@ module Afterlife
|
|
47
47
|
desc 'release', 'Releases management'
|
48
48
|
subcommand 'release', Release::Cli
|
49
49
|
|
50
|
+
desc 'clickup', 'ClickUp integration commands'
|
51
|
+
subcommand 'clickup', Clickup::Cli
|
52
|
+
|
50
53
|
map %w[-v --version] => :version
|
51
54
|
desc 'version', 'Prints afterlife version'
|
52
55
|
def version
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'faraday'
|
4
|
+
|
5
|
+
module Afterlife
|
6
|
+
module Clickup
|
7
|
+
class Api
|
8
|
+
BASE_URL = 'https://api.clickup.com'
|
9
|
+
attr_reader :connection
|
10
|
+
|
11
|
+
def initialize(token:)
|
12
|
+
@connection = Faraday.new(
|
13
|
+
url: BASE_URL,
|
14
|
+
headers: { 'Content-Type' => 'application/json', 'Authorization' => token },
|
15
|
+
) do |faraday|
|
16
|
+
faraday.request :json # encode request bodies as JSON
|
17
|
+
faraday.response :json # decode response bodies as JSON
|
18
|
+
faraday.response :raise_error # raise an error if the response is not successful
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def get_task(task_id)
|
23
|
+
connection.get("/api/v2/task/#{task_id}").body
|
24
|
+
rescue Faraday::Error
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def update_task(task_id, params)
|
29
|
+
connection.put("/api/v2/task/#{task_id}", params).body
|
30
|
+
rescue Faraday::Error => e
|
31
|
+
puts "Error updating task #{task_id}: #{e.message}"
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterlife
|
4
|
+
module Clickup
|
5
|
+
class Cli < Thor
|
6
|
+
include BaseCli
|
7
|
+
|
8
|
+
CLICKUP_STATUS_OPTIONS = StatusPrecedenceChecker::STATUS_PRECEDENCE.map { |s| "'#{s}'" }.join(', ')
|
9
|
+
|
10
|
+
def self.exit_on_failure?
|
11
|
+
true
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'extract-ids [TEXT]', 'Extract ClickUp task IDs from text or stdin'
|
15
|
+
option :link, type: :boolean, default: false, aliases: '-l',
|
16
|
+
desc: 'Convert task IDs to links'
|
17
|
+
def extract_ids(text = nil)
|
18
|
+
text ||= $stdin.read
|
19
|
+
ids = GetIdsFromText.call(text, options[:link])
|
20
|
+
ids.each { |id| puts id }
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'get-commits FROM_REVISION [TO_REVISION]',
|
24
|
+
'Get commits between revisions. The revision can be a short or long version of the commit sha.'
|
25
|
+
option :short, type: :boolean, default: false, aliases: '-s',
|
26
|
+
desc: 'Only prints a short version of the commit, do not use it on CI.'
|
27
|
+
def get_commits(from_revision, to_revision = 'HEAD')
|
28
|
+
messages = GetRangeCommits.call(from_revision, to_revision, short: options[:short])
|
29
|
+
puts messages
|
30
|
+
rescue StandardError => e
|
31
|
+
fatal!(e.message)
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'update-status TARGET_STATUS TASK_ID [TASK_ID...]',
|
35
|
+
"Update status of ClickUp tasks. Possible values for TARGET_STATUS: #{CLICKUP_STATUS_OPTIONS}"
|
36
|
+
option :force, type: :boolean, default: false, aliases: '-f',
|
37
|
+
desc: 'Force update status of tasks even if they would not be updated due to status precedence'
|
38
|
+
option :dry_run, type: :boolean, default: false, aliases: '-d',
|
39
|
+
desc: 'Do not actually update the status of the tasks, just print what would be done'
|
40
|
+
def update_status(target_status, *task_ids)
|
41
|
+
task_ids = $stdin.read.strip.split("\n") if task_ids.empty?
|
42
|
+
|
43
|
+
UpdateTaskStatus.call(task_ids, target_status, force: options[:force], dry_run: options[:dry_run])
|
44
|
+
rescue StandardError => e
|
45
|
+
fatal!(e.message)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterlife
|
4
|
+
module Clickup
|
5
|
+
class GetIdsFromText
|
6
|
+
ID_PATTERNS = [
|
7
|
+
/#CU-([a-z0-9]+)/i,
|
8
|
+
/#CU_([a-z0-9]+)/i,
|
9
|
+
/#([a-z0-9]{8,})/i,
|
10
|
+
/CU-([a-z0-9]+)/i,
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
def self.call(text, convert_to_link = false) # rubocop:disable Metrics/MethodLength
|
14
|
+
ids = Set.new
|
15
|
+
|
16
|
+
ID_PATTERNS.each do |pattern|
|
17
|
+
text.scan(pattern) do |match|
|
18
|
+
ids.add(match.first.downcase)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if convert_to_link
|
23
|
+
ids.map { |id| "https://app.clickup.com/t/#{id}" }
|
24
|
+
else
|
25
|
+
ids.to_a
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
|
5
|
+
module Afterlife
|
6
|
+
module Clickup
|
7
|
+
class GetRangeCommits
|
8
|
+
SHORT_PARAMS = "--graph --decorate --pretty=format:'%C(yellow)%h %C(cyan)%ad %C(cyan)%d %Creset%s %Cgreen[%an]' --date=relative" # rubocop:disable Layout/LineLength
|
9
|
+
DEFAULT_PARAMS = "--pretty=format:'%s%n%b'"
|
10
|
+
|
11
|
+
def self.call(from_revision, to_revision = 'HEAD', short: nil)
|
12
|
+
range = "#{from_revision}..#{to_revision}"
|
13
|
+
params = short ? SHORT_PARAMS : DEFAULT_PARAMS
|
14
|
+
commit_messages = `git log #{params} #{range}`.strip
|
15
|
+
fail "Error getting commits for range #{range}" unless $CHILD_STATUS.success?
|
16
|
+
|
17
|
+
commit_messages
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterlife
|
4
|
+
module Clickup
|
5
|
+
class StatusPrecedenceChecker
|
6
|
+
STATUS_PRECEDENCE = [
|
7
|
+
'backlog',
|
8
|
+
'defined',
|
9
|
+
'planned',
|
10
|
+
'in progress',
|
11
|
+
'needs review',
|
12
|
+
'needs rework',
|
13
|
+
'blocked',
|
14
|
+
'trunk merged',
|
15
|
+
'ready for qa',
|
16
|
+
'validated qa',
|
17
|
+
'completed',
|
18
|
+
].freeze
|
19
|
+
|
20
|
+
def should_skip_update?(current_status, target_status)
|
21
|
+
current_precedence = get_precedence(current_status)
|
22
|
+
target_precedence = get_precedence(target_status)
|
23
|
+
|
24
|
+
current_precedence >= target_precedence
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def get_precedence(status)
|
30
|
+
return -1 if status.nil? || status.strip.empty?
|
31
|
+
|
32
|
+
result = STATUS_PRECEDENCE.index(status.strip.downcase) || -1
|
33
|
+
|
34
|
+
puts "WARNING: status precedence not found: #{status}" if result == -1
|
35
|
+
|
36
|
+
result
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterlife
|
4
|
+
module Clickup
|
5
|
+
class UpdateTaskStatus
|
6
|
+
attr_reader :target_status, :precedence_checker, :api, :force, :dry_run
|
7
|
+
|
8
|
+
def self.call(task_ids, target_status, force: false, dry_run: false)
|
9
|
+
new(force: force, dry_run: dry_run).call(task_ids, target_status)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(force: false, dry_run: false)
|
13
|
+
@force = force
|
14
|
+
@dry_run = dry_run
|
15
|
+
token = ENV['CLICKUP_TOKEN'] || fail('CLICKUP_TOKEN required, put `export CLICKUP_TOKEN=...` or the equivalent in your shell') # rubocop:disable Layout/LineLength
|
16
|
+
@api = Api.new(token: token)
|
17
|
+
@precedence_checker = StatusPrecedenceChecker.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(task_ids, target_status) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
21
|
+
if task_ids.empty?
|
22
|
+
puts 'No tasks to update'
|
23
|
+
return []
|
24
|
+
end
|
25
|
+
|
26
|
+
puts 'WARNING: Forcing status updates...' if force
|
27
|
+
puts 'WARNING: Dry run, no tasks will be updated' if dry_run
|
28
|
+
puts "Updating #{task_ids.length} tasks to '#{target_status}'"
|
29
|
+
moved_task_ids = task_ids.map { |task_id| move_task(task_id, target_status) }.compact
|
30
|
+
puts "#{dry_run ? 'Would have moved' : 'Moved'} #{moved_task_ids.length} tasks:"
|
31
|
+
moved_task_ids.each do |task|
|
32
|
+
puts "- #{task[:link]} (from '#{task[:previous_status]}' to '#{task[:new_status]}')"
|
33
|
+
end
|
34
|
+
moved_task_ids
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def move_task(task_id, target_status)
|
40
|
+
current_status = api.get_task(task_id).dig('status', 'status')&.downcase
|
41
|
+
|
42
|
+
return puts "WARNING: Task #{task_id} not found" if current_status.nil?
|
43
|
+
|
44
|
+
should_skip_update = !force && precedence_checker.should_skip_update?(current_status, target_status)
|
45
|
+
task_link = "https://app.clickup.com/t/#{task_id}"
|
46
|
+
return puts "Skipping task #{task_link} because it's already at '#{current_status}'" if should_skip_update
|
47
|
+
|
48
|
+
api.update_task(task_id, { status: target_status }) unless dry_run
|
49
|
+
{ id: task_id, link: task_link, previous_status: current_status, new_status: target_status }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -43,7 +43,7 @@ module Afterlife
|
|
43
43
|
return if options['no-build']
|
44
44
|
|
45
45
|
<<-BASH
|
46
|
-
docker buildx bake -f docker-bake.hcl #{targets.join(' ')} #{local_stage? ? '--load' : '--push'}
|
46
|
+
docker buildx bake --allow=ssh -f docker-bake.hcl #{targets.join(' ')} #{local_stage? ? '--load' : '--push'}
|
47
47
|
BASH
|
48
48
|
end
|
49
49
|
|
data/lib/afterlife/version.rb
CHANGED
metadata
CHANGED
@@ -1,15 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: afterlife
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Genaro Madrid
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2025-07-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: faraday
|
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: racc
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
13
41
|
- !ruby/object:Gem::Dependency
|
14
42
|
name: thor
|
15
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -51,6 +79,7 @@ files:
|
|
51
79
|
- ".rubocop.yml"
|
52
80
|
- ".ruby-gemset"
|
53
81
|
- ".ruby-version"
|
82
|
+
- ".vscode/settings.json.example"
|
54
83
|
- CHANGELOG.md
|
55
84
|
- Gemfile
|
56
85
|
- README.md
|
@@ -65,6 +94,13 @@ files:
|
|
65
94
|
- lib/afterlife/cdn/cli.rb
|
66
95
|
- lib/afterlife/cdn/server.rb
|
67
96
|
- lib/afterlife/cli.rb
|
97
|
+
- lib/afterlife/clickup.rb
|
98
|
+
- lib/afterlife/clickup/api.rb
|
99
|
+
- lib/afterlife/clickup/cli.rb
|
100
|
+
- lib/afterlife/clickup/get_ids_from_text.rb
|
101
|
+
- lib/afterlife/clickup/get_range_commits.rb
|
102
|
+
- lib/afterlife/clickup/status_precedence_checker.rb
|
103
|
+
- lib/afterlife/clickup/update_task_status.rb
|
68
104
|
- lib/afterlife/config.rb
|
69
105
|
- lib/afterlife/config/cli.rb
|
70
106
|
- lib/afterlife/config/provider.rb
|
@@ -111,7 +147,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
111
147
|
- !ruby/object:Gem::Version
|
112
148
|
version: '0'
|
113
149
|
requirements: []
|
114
|
-
rubygems_version: 3.
|
150
|
+
rubygems_version: 3.5.16
|
115
151
|
signing_key:
|
116
152
|
specification_version: 4
|
117
153
|
summary: Devops utils
|