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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acd1912f0524e2efc93ab47e2f43fed17487fc5b05cff9c56f25e275c4501790
4
- data.tar.gz: 3f658d550f54904680eb77ab7333bcde9bfeea2b5dbe4f19b634132ac45276e4
3
+ metadata.gz: 04142460aa12c27177dd71d0450a10930e27370ab91b753f107ace751735ebbd
4
+ data.tar.gz: a9f0f37abc39566e8c7a5660b5a86108a7e1dcc88f87936e882f0364f00687c7
5
5
  SHA512:
6
- metadata.gz: a14e900ab52e37392e5a2ebe142257a72db3a0c79b47947975f28d1ac3c58f4c6085c22027935c3be4bd2fda9ac38b1464b04a8e7c60bc6ce8c141c723c9e1b8
7
- data.tar.gz: 316829b117b8b6f7b98d6582244e9bf269bc7e0ca59608c93dd50e54b1a10b25c0bed5272ab143d8261fb61349b97bdd236317ed41402710c55a68db2da3bbbd
6
+ metadata.gz: c503b855bd1257b9266c010ca4364fc894d26aefaccb65f2debaba46859bf7af51e661265f148bb0119ea1432e3edc7fd21d5f89ba546e8be719c7f26e1c960c
7
+ data.tar.gz: 9e046b5c9bb75f1ffd71edf220935260423c5d4d7d5fffabaedffc82a849944e0850458ac1e90885ccb96ad5072d802cac06bfa1851139f27295287386b42f1f
@@ -0,0 +1,3 @@
1
+ {
2
+ "vscode-run-rspec-file.custom-command": "bundle exec rspec --color --fail-fast"
3
+ }
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
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Afterlife
4
+ module Clickup
5
+ end
6
+ 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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Afterlife
4
- VERSION = '1.7.2'
4
+ VERSION = '1.7.3'
5
5
  end
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.2
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: 2024-10-07 00:00:00.000000000 Z
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.2.33
150
+ rubygems_version: 3.5.16
115
151
  signing_key:
116
152
  specification_version: 4
117
153
  summary: Devops utils