ace-git-forgejo 0.6.2 → 0.8.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: 670c1a5fa83a0de21364026001ad9b113742be293bf5930b9557ee56df019023
4
- data.tar.gz: 975f91389a01fa855025479f091a397ba074f43eb535c22887014d410ae0a481
3
+ metadata.gz: 5e06e7ebace5fc96a24f3e1ea11c26a858864acc02be4ff0e8da43823fdc4a01
4
+ data.tar.gz: 98b6f9244d4d965302a22bd9a7ef0faf1ca91d55801043e961cc9409c66feb0f
5
5
  SHA512:
6
- metadata.gz: ea952944afab9658f89959bf5da1f059a4808073c46e77bd6dfcde73017b35021538b353f54cc4c646e6c55937700ca1bc1c47af1c659c6e1f4ff801a7f58693
7
- data.tar.gz: f1228942a54b588673fa7ca90150fac10ad36af6334ce9b3d16a2fb2780d7cac4f836e414ccd87d6a1c7353506dec3317f6fc23828d5c3d103ed6f73b96ddc58
6
+ metadata.gz: b07df628f1428cf4599ec991429c0767c03787196e866260ce8efeebb7dc948a0adfee15d22f290b8978c1ad46205147f7b01262929f02d78b135b5449b17527
7
+ data.tar.gz: 3a4308e47ec2fe95cf9e2f15cc41562a3d627aab7b4466d5029334e0b0b33c280255dd7f6b0542edf8dd4d2c6f5cc1bcdffa2abde9d6b3f22a410f8dab6a75db
data/CHANGELOG.md CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.0] - 2026-10-23
11
+
12
+ ### Added
13
+
14
+ - Formal reviewer-owned, exact-head provider approval and read-only approval verification through the forge-neutral review flow; comments do not substitute for approval.
15
+
16
+
17
+ ### Fixed
18
+
19
+ - Publish formal approval directly at explicit commit_id instead of rebinding pending-submit; retain safe comment-free owned pending recovery, refuse unrelated pending, ignore request/team rows, and require the verified formal-review API floor.
20
+
21
+ ## [0.7.0] - 2026-10-16
22
+
23
+ ### Added
24
+
25
+ - 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.
26
+
10
27
  ## [0.6.2] - 2026-10-08
11
28
 
12
29
  ### 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)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "ace/git/providers/formal_approval"
4
5
  require "uri"
5
6
  require "ace/git/atoms/server_url"
6
7
  require_relative "cli_executor"
@@ -340,6 +341,14 @@ module Ace
340
341
  raise Ace::Git::ProviderMalformedOutputError, "Malformed Forgejo repository file content: #{e.message}"
341
342
  end
342
343
 
344
+ def approve_pull_request(number:, expected_head:, body:, correlation:)
345
+ formal_approval.approve(number: number, expected_head: expected_head, body: body, correlation: correlation)
346
+ end
347
+
348
+ def pull_request_approval(number:, expected_head:, review_id:)
349
+ formal_approval.read(number: number, expected_head: expected_head, review_id: review_id)
350
+ end
351
+
343
352
  def create_pull_request_comment(number:, expected_head:, body:, correlation:)
344
353
  number = request_number!(number)
345
354
  pr = verify_expected_head!(pull_request(number: number), expected_head)
@@ -914,6 +923,25 @@ module Ace
914
923
  "Cannot post review comment to PR ##{pr.number} in #{pr.state} state"
915
924
  end
916
925
 
926
+ def formal_approval
927
+ pr_api.ensure_formal_review_supported!
928
+ request = lambda do |method, path, payload|
929
+ path = "/api/v1/user" if path == :actor
930
+ payload = payload.merge("event" => "APPROVED") if method == :post
931
+ review_http.request(method, path, body: payload)
932
+ end
933
+ Ace::Git::Providers::FormalApproval.new(provider: self, request: request,
934
+ list: method(:formal_review_rows), official: true, pending_reuse: true)
935
+ end
936
+
937
+ # Forgejo lists review requests alongside actual reviews. User and
938
+ # team requests have no reviewed commit; they are not approval evidence.
939
+ def formal_review_rows(path)
940
+ review_http.paginate(path).reject do |row|
941
+ row.is_a?(Hash) && row["state"] == "REQUEST_REVIEW"
942
+ end
943
+ end
944
+
917
945
  def review_http
918
946
  @review_http ||= HttpClient.new(server: server, timeout: timeout, runner: runner)
919
947
  end
@@ -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
@@ -70,9 +70,21 @@ module Ace
70
70
  "Forgejo server reports #{version}; PR delivery requires #{MINIMUM_SERVER_VERSION} " \
71
71
  "(server-enforced expected-head merge is not documented below it)"
72
72
  end
73
+ @server_version = version
73
74
  @version_supported = true
74
75
  end
75
76
 
77
+ # Formal create-with-event preserves explicit commit_id in the
78
+ # inspected Forgejo v11.0.0 and v16.0.5 router/service/model sources.
79
+ # Pending submission does not: it rebinds to current HEAD and is unused.
80
+ def ensure_formal_review_supported!
81
+ ensure_version_supported!
82
+ unless @server_version >= Gem::Version.new("11.0.0")
83
+ raise Ace::Git::ProviderUnsupportedCapabilityError,
84
+ "Formal exact-commit approval requires Forgejo 11.0.0 or newer"
85
+ end
86
+ end
87
+
76
88
  # @return [Hash] the raw validated pull request payload
77
89
  # @raise [Ace::Git::ProviderObjectNotFoundError] when absent
78
90
  # @raise [Ace::Git::ProviderMalformedOutputError] on schema violation
@@ -161,6 +173,19 @@ module Ace
161
173
  MutationOutcome.new(status: status, message: message, payload: payload)
162
174
  end
163
175
 
176
+ # Attach one comment to the pull request thread (Forgejo serves PR
177
+ # comments on the issue endpoints) — e.g. a reviewer-produced evidence
178
+ # body handed over by a caller before the merge invocation.
179
+ #
180
+ # @return [MutationOutcome] 201 with the created comment payload, or a
181
+ # refusal outcome (404 absent pull request, 422 rejected body)
182
+ def create_comment(number, body)
183
+ status, payload, message = @http.exchange(:post, "issues/#{number}/comments",
184
+ body: {"body" => body.to_s})
185
+ classify_send!(status, "create pull request #{number} comment")
186
+ MutationOutcome.new(status: status, message: message, payload: payload)
187
+ end
188
+
164
189
  private
165
190
 
166
191
  # 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.2"
6
+ VERSION = "0.8.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.2
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-10-08 00:00:00.000000000 Z
10
+ date: 2026-10-23 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.30'
18
+ version: '0.31'
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.30'
25
+ version: '0.31'
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: faraday
28
28
  requirement: !ruby/object:Gem::Requirement