afterlife 1.7.2 → 1.7.3
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 +20 -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 +41 -0
- data/lib/afterlife/clickup/get_ids_from_text.rb +26 -0
- data/lib/afterlife/clickup/get_range_commits.rb +17 -0
- data/lib/afterlife/clickup/status_precedence_checker.rb +40 -0
- data/lib/afterlife/clickup/update_task_status.rb +45 -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: 04142460aa12c27177dd71d0450a10930e27370ab91b753f107ace751735ebbd
|
4
|
+
data.tar.gz: a9f0f37abc39566e8c7a5660b5a86108a7e1dcc88f87936e882f0364f00687c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c503b855bd1257b9266c010ca4364fc894d26aefaccb65f2debaba46859bf7af51e661265f148bb0119ea1432e3edc7fd21d5f89ba546e8be719c7f26e1c960c
|
7
|
+
data.tar.gz: 9e046b5c9bb75f1ffd71edf220935260423c5d4d7d5fffabaedffc82a849944e0850458ac1e90885ccb96ad5072d802cac06bfa1851139f27295287386b42f1f
|
data/README.md
CHANGED
@@ -20,6 +20,26 @@ 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
|
+
|
23
43
|
## Development
|
24
44
|
|
25
45
|
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.
|
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,41 @@
|
|
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
|
+
def extract_ids(text = nil)
|
16
|
+
text ||= $stdin.read
|
17
|
+
ids = GetIdsFromText.call(text)
|
18
|
+
ids.each { |id| puts id }
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'get-commits FROM_REVISION [TO_REVISION]',
|
22
|
+
'Get commits between revisions. The revision can be a short or long version of the commit sha.'
|
23
|
+
def get_commits(from_revision, to_revision = 'HEAD')
|
24
|
+
messages = GetRangeCommits.call(from_revision, to_revision)
|
25
|
+
puts messages
|
26
|
+
rescue StandardError => e
|
27
|
+
fatal!(e.message)
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'update-status TARGET_STATUS TASK_ID [TASK_ID...]',
|
31
|
+
"Update status of ClickUp tasks. Possible values for TARGET_STATUS: #{CLICKUP_STATUS_OPTIONS}"
|
32
|
+
def update_status(target_status, *task_ids)
|
33
|
+
task_ids = $stdin.read.strip.split("\n") if task_ids.empty?
|
34
|
+
|
35
|
+
UpdateTaskStatus.call(task_ids, target_status)
|
36
|
+
rescue StandardError => e
|
37
|
+
fatal!(e.message)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,26 @@
|
|
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)
|
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
|
+
ids.to_a
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
|
5
|
+
module Afterlife
|
6
|
+
module Clickup
|
7
|
+
class GetRangeCommits
|
8
|
+
def self.call(from_revision, to_revision = 'HEAD')
|
9
|
+
range = "#{from_revision}..#{to_revision}"
|
10
|
+
commit_messages = `git log --pretty=format:"%s%n%b" #{range}`.strip
|
11
|
+
fail "Error getting commits for range #{range}" unless $CHILD_STATUS.success?
|
12
|
+
|
13
|
+
commit_messages
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
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,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Afterlife
|
4
|
+
module Clickup
|
5
|
+
class UpdateTaskStatus
|
6
|
+
attr_reader :target_status, :precedence_checker, :api
|
7
|
+
|
8
|
+
def self.call(task_ids, target_status)
|
9
|
+
new.call(task_ids, target_status)
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
token = ENV['CLICKUP_TOKEN'] || fail('CLICKUP_TOKEN required, put `export CLICKUP_TOKEN=...` or the equivalent in your shell') # rubocop:disable Layout/LineLength
|
14
|
+
@api = Api.new(token: token)
|
15
|
+
@precedence_checker = StatusPrecedenceChecker.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(task_ids, target_status)
|
19
|
+
if task_ids.empty?
|
20
|
+
puts 'No tasks to update'
|
21
|
+
return []
|
22
|
+
end
|
23
|
+
|
24
|
+
puts "Updating #{task_ids.length} tasks to '#{target_status}'"
|
25
|
+
moved_task_ids = task_ids.map { |task_id| move_task(task_id, target_status) }.compact
|
26
|
+
puts "Moved #{moved_task_ids.length} tasks, with ids: #{moved_task_ids.join(', ')}"
|
27
|
+
moved_task_ids
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def move_task(task_id, target_status)
|
33
|
+
current_status = api.get_task(task_id).dig('status', 'status')&.downcase
|
34
|
+
|
35
|
+
return puts "WARNING: Task #{task_id} not found" if current_status.nil?
|
36
|
+
|
37
|
+
should_skip_update = precedence_checker.should_skip_update?(current_status, target_status)
|
38
|
+
return puts "Skipping task #{task_id} because it's already at '#{current_status}'" if should_skip_update
|
39
|
+
|
40
|
+
api.update_task(task_id, { status: target_status })
|
41
|
+
task_id
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
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.3
|
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-29 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
|