ace-git-forgejo 0.6.1 → 0.7.0

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: b31bdebd863b7bee391783037e9aef4dab2a1ac4bede0b2b897e8442ac297b7f
4
- data.tar.gz: db3a546c09effeda3781370316a691cb893af860cb0212ef982320a521ab9cc0
3
+ metadata.gz: ec7d89b79d4c95330a4610f04ce163e8dcd402f3ff72bb468b27dac082d086ea
4
+ data.tar.gz: b9a7a5d29fa5fbd4b559ef532018d18e07d9a3b8de20dbd5860938408158d6e9
5
5
  SHA512:
6
- metadata.gz: b4323793a3d0b8b0f217bf7acca41d31d89dc9316b14c51509352edc903760d6811e7412497d1189bf9339cd774154ac6fbc70b506d4c0587a017a3bc8dce2f9
7
- data.tar.gz: e501d7e7d7ddf48e3194eb6a110c29df8984ba5f873295c0d6bbbbaa621987dcd814f50ede1fb594eb7f18c605dd1b8bc98edb2ca95a3cb6837db8f7a829d305
6
+ metadata.gz: f7a145c24c9720d4748f32cc921f42983f2dc20c56dc0c007de0536972b4b318bba6d35c52b2b360ece6846b24cf4cd6d5b819493a7471d14e13bd59e08dc060
7
+ data.tar.gz: e308d391be1d1057de4176e58a698cfef161ed9474e46bb29dd1ab0a5ef83efb13af1f51b8d4bccb53d8322c0ffce89db71438f777762f49a6cadb07bab9880a
data/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.7.0] - 2026-10-16
11
+
12
+ ### Added
13
+
14
+ - Repository-bound API surface for scoped service identities: `HttpClient`/`PullRequestApi` accept an explicit `token:` (a host-installed merge receiver credential) with the `fj` keys-file lookup unchanged as the fallback, and `PullRequestApi#create_comment` attaches one PR-thread comment (the issue-served Forgejo endpoint) with the same `MutationOutcome` refusal classification as the other lifecycle mutations.
15
+
16
+ ## [0.6.2] - 2026-10-08
17
+
18
+ ### Fixed
19
+
20
+ - Recheck ready/merge acceptance and selected repository provenance; retain the exact expected head and classify unconfirmed effects without replay.
21
+
10
22
  ## [0.6.1] - 2026-10-04
11
23
 
12
24
  ### Fixed
@@ -20,10 +20,15 @@ module Ace
20
20
  # limited to the safe GET verb — a mutation is sent at most once per
21
21
  # call, so a lost response stays an unknown outcome.
22
22
  class HttpClient
23
- def initialize(server:, timeout:, runner: nil)
23
+ # `token:` injects an explicit credential for deployments whose scoped
24
+ # service identity is host-installed (e.g. a merge receiver reading its
25
+ # token from root-owned configuration) instead of the `fj` CLI keys
26
+ # file; when absent the fj credential governs as before.
27
+ def initialize(server:, timeout:, runner: nil, token: nil)
24
28
  @target = RepositoryBinding::Target.resolve(server.url)
25
29
  @timeout = timeout
26
30
  @runner = runner
31
+ @token = token.is_a?(String) && !token.empty? ? token : nil
27
32
  end
28
33
 
29
34
  def paginate(suffix)
@@ -145,6 +150,8 @@ module Ace
145
150
  end
146
151
 
147
152
  def access_token!
153
+ return @token if @token
154
+
148
155
  path = RepositoryBinding.default_keys_path
149
156
  data = path && JSON.parse(File.read(path))
150
157
  # fj stores credentials under the bare host (or host:port)
@@ -33,6 +33,16 @@ module Ace
33
33
  WIP_PREFIXES = ["WIP:", "[WIP]"].freeze
34
34
 
35
35
  class << self
36
+ def pull_request_title(title:, draft:)
37
+ wip = WIP_PREFIXES.any? { |prefix| title.to_s.upcase.start_with?(prefix) }
38
+ return wip ? title : "WIP: #{title}" if draft
39
+ if wip
40
+ raise Ace::Git::ProviderConflictingMatchesError,
41
+ "Requested draft:false conflicts with the WIP-prefixed title"
42
+ end
43
+ title
44
+ end
45
+
36
46
  def display_name
37
47
  "Forgejo"
38
48
  end
@@ -549,7 +559,10 @@ module Ace
549
559
  # @return [ProviderMutationReceipt] operation :update
550
560
  def update_pull_request(number:, expected_head:, title: nil, body: nil)
551
561
  number = request_number!(number)
552
- verify_expected_head!(pull_request(number: number), expected_head)
562
+ before = verify_expected_head!(pull_request(number: number), expected_head)
563
+ # Title edits are not a ready transition. Keep Forgejo's title-based
564
+ # draft state, just as providers with an explicit draft field do.
565
+ title = draft_title!(title, before.draft) if title
553
566
  if title || body
554
567
  pr_api.ensure_version_supported!
555
568
  outcome = send_lifecycle_mutation("PR ##{number} on #{server.name}, head #{expected_head}") do
@@ -560,9 +573,13 @@ module Ace
560
573
  "PR update refused for ##{number} (HTTP #{outcome.status}: #{outcome.message}); " \
561
574
  "reconcile before repeating"
562
575
  end
563
- verify_post_mutation_head!(number, expected_head, "update")
576
+ after = verify_post_mutation_head!(number, expected_head, "update")
577
+ unless after.draft == before.draft
578
+ raise Ace::Git::ProviderUnknownOutcomeError,
579
+ "PR update changed draft state for ##{number}; reconcile before repeating"
580
+ end
564
581
  end
565
- receipt(:update, pull_request(number: number), nil)
582
+ receipt(:update, after || before, nil)
566
583
  end
567
584
 
568
585
  # Mark a draft ready on the exact head. Forgejo derives draft state
@@ -715,15 +732,7 @@ module Ace
715
732
  # defaults, compared case-insensitively without trimming); the API
716
733
  # create form has no draft field.
717
734
  def draft_title!(title, draft)
718
- if draft
719
- work_in_progress_title?(title) ? title : "WIP: #{title}"
720
- elsif work_in_progress_title?(title)
721
- raise Ace::Git::ProviderConflictingMatchesError,
722
- "Requested draft:false conflicts with the WIP-prefixed title #{title.inspect}; " \
723
- "Forgejo would create the pull request as a draft"
724
- else
725
- title
726
- end
735
+ self.class.pull_request_title(title: title, draft: draft)
727
736
  end
728
737
 
729
738
  def work_in_progress_title?(title)
@@ -35,8 +35,8 @@ module Ace
35
35
  # success), and the parsed payload when the server returned one.
36
36
  MutationOutcome = Data.define(:status, :message, :payload)
37
37
 
38
- def initialize(server:, timeout:, runner: nil)
39
- @http = HttpClient.new(server: server, timeout: timeout, runner: runner)
38
+ def initialize(server:, timeout:, runner: nil, token: nil)
39
+ @http = HttpClient.new(server: server, timeout: timeout, runner: runner, token: token)
40
40
  end
41
41
 
42
42
  # Probe and validate the selected server's API version once per
@@ -161,6 +161,19 @@ module Ace
161
161
  MutationOutcome.new(status: status, message: message, payload: payload)
162
162
  end
163
163
 
164
+ # Attach one comment to the pull request thread (Forgejo serves PR
165
+ # comments on the issue endpoints) — e.g. a reviewer-produced evidence
166
+ # body handed over by a caller before the merge invocation.
167
+ #
168
+ # @return [MutationOutcome] 201 with the created comment payload, or a
169
+ # refusal outcome (404 absent pull request, 422 rejected body)
170
+ def create_comment(number, body)
171
+ status, payload, message = @http.exchange(:post, "issues/#{number}/comments",
172
+ body: {"body" => body.to_s})
173
+ classify_send!(status, "create pull request #{number} comment")
174
+ MutationOutcome.new(status: status, message: message, payload: payload)
175
+ end
176
+
164
177
  private
165
178
 
166
179
  # Authentication, absence, and transport failures classify directly
@@ -3,7 +3,7 @@
3
3
  module Ace
4
4
  module Git
5
5
  module Forgejo
6
- VERSION = "0.6.1"
6
+ VERSION = "0.7.0"
7
7
  end
8
8
  end
9
9
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ace-git-forgejo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-10-04 00:00:00.000000000 Z
10
+ date: 2026-10-16 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ace-git
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: '0.26'
18
+ version: '0.30'
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: '0.26'
25
+ version: '0.30'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: faraday
28
28
  requirement: !ruby/object:Gem::Requirement