ace-git-forgejo 0.8.0 → 0.9.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: 5e06e7ebace5fc96a24f3e1ea11c26a858864acc02be4ff0e8da43823fdc4a01
4
- data.tar.gz: 98b6f9244d4d965302a22bd9a7ef0faf1ca91d55801043e961cc9409c66feb0f
3
+ metadata.gz: 2f9063026140a927fbd346c52d1bcad410b23dc0fbaa8e8d69eae206688afd72
4
+ data.tar.gz: d2731b929dc9c5adde5d1a21259bc071ad54771424be3d5f3a7e2465891a3eb7
5
5
  SHA512:
6
- metadata.gz: b07df628f1428cf4599ec991429c0767c03787196e866260ce8efeebb7dc948a0adfee15d22f290b8978c1ad46205147f7b01262929f02d78b135b5449b17527
7
- data.tar.gz: 3a4308e47ec2fe95cf9e2f15cc41562a3d627aab7b4466d5029334e0b0b33c280255dd7f6b0542edf8dd4d2c6f5cc1bcdffa2abde9d6b3f22a410f8dab6a75db
6
+ metadata.gz: b69e571f2eb13eff2a71f082d8630a5610fc4827c7ea2fe6e126cef0a04610257e9bc494053ca615b8fccf88ba49b956e51dbf16768e9c2dc990a55c6d9fde7d
7
+ data.tar.gz: 2bda0f62a97a58ef95c67495dabb2085401be57d3002f5cfb97fd5f7a6a92b173f8aebc5c5cde5519ca0f64cc03c49de307df3021f70a7fa6988e4a675589377
data/CHANGELOG.md CHANGED
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.9.0] - 2026-11-10
11
+
12
+ ### Added
13
+
14
+ - Observe repository-bound open issues including body and label changes. Support formal approval with repository-scoped credentials and verify the returned reviewer identity.
15
+
10
16
  ## [0.8.0] - 2026-10-23
11
17
 
12
18
  ### Added
@@ -25,6 +25,10 @@ module Ace
25
25
  request(:get, "issues/#{number}")
26
26
  end
27
27
 
28
+ def open_issues
29
+ paginate("issues?state=open", fail_on_repeated_page: true)
30
+ end
31
+
28
32
  def comments(number)
29
33
  paginate("issues/#{number}/comments")
30
34
  end
@@ -78,22 +82,28 @@ module Ace
78
82
 
79
83
  private
80
84
 
81
- def paginate(suffix)
85
+ def paginate(suffix, fail_on_repeated_page: false)
82
86
  page = 1
83
87
  entries = []
84
88
  seen = []
85
89
  loop do
86
- batch = request(:get, "#{suffix}?page=#{page}&limit=50")
90
+ separator = suffix.include?("?") ? "&" : "?"
91
+ batch = request(:get, "#{suffix}#{separator}page=#{page}&limit=50")
87
92
  unless batch.is_a?(Array)
88
93
  raise Ace::Git::ProviderMalformedOutputError, "Forgejo #{suffix} did not return a list"
89
94
  end
90
- before = entries.length
95
+ unless batch.all? { |entry| entry.is_a?(Hash) && entry["id"].is_a?(Integer) && entry["id"].positive? }
96
+ raise Ace::Git::ProviderMalformedOutputError, "Forgejo #{suffix} contains an invalid item identity"
97
+ end
91
98
  # Stop when a page adds no new items (unique ids): instances
92
99
  # may cap pages below the requested limit, so a short page alone
93
100
  # does not prove the end of the collection.
94
101
  fresh = batch.reject { |item| seen.include?(item["id"]) }
95
102
  entries.concat(fresh)
96
103
  fresh.each { |item| seen << item["id"] }
104
+ if fail_on_repeated_page && !batch.empty? && fresh.empty?
105
+ raise Ace::Git::ProviderMalformedOutputError, "Forgejo #{suffix} repeated a page; completeness cannot be proven"
106
+ end
97
107
  break if fresh.empty?
98
108
 
99
109
  page += 1
@@ -119,6 +119,39 @@ module Ace
119
119
  .first(limit)
120
120
  end
121
121
 
122
+ # Open issue intake uses the selected Forgejo repository, and never
123
+ # substitutes pull requests or an unavailable listing for an empty one.
124
+ # @return [Array<ProviderIssue>] open issues, newest first
125
+ # @raise [Ace::Git::ProviderMalformedOutputError] for invalid evidence
126
+ # @raise [Ace::Git::ProviderIdentityMismatchError] for foreign evidence
127
+ def recent_issues(limit:)
128
+ limit = Integer(limit)
129
+ return [] if limit <= 0
130
+
131
+ issue_api.open_issues.filter_map do |entry|
132
+ next if entry["pull_request"]
133
+
134
+ number = entry["number"]
135
+ unless number.is_a?(Integer) && number.positive? && entry["title"].is_a?(String) &&
136
+ !entry["title"].strip.empty? && entry["state"] == "open"
137
+ raise Ace::Git::ProviderMalformedOutputError, "Forgejo issue listing contains an invalid issue"
138
+ end
139
+ unless entry["html_url"] == issue_url(number)
140
+ raise Ace::Git::ProviderIdentityMismatchError, "Forgejo issue listing contains foreign issue ##{number}"
141
+ end
142
+ unless entry["user"].nil? || entry["user"].is_a?(Hash)
143
+ raise Ace::Git::ProviderMalformedOutputError, "Forgejo issue listing contains an invalid author"
144
+ end
145
+ unless (entry["body"].nil? || entry["body"].is_a?(String)) &&
146
+ (entry["labels"].nil? || entry["labels"].is_a?(Array) && entry["labels"].all? { |label| label.is_a?(Hash) && label["name"].is_a?(String) })
147
+ raise Ace::Git::ProviderMalformedOutputError, "Forgejo issue listing contains invalid body or labels"
148
+ end
149
+ Ace::Git::ProviderIssue.new(server_name: server.name, number: number, title: entry["title"],
150
+ state: :open, author: entry.dig("user", "login"), url: entry["html_url"],
151
+ body: entry["body"], labels: Array(entry["labels"]).map { |label| label.fetch("name") })
152
+ end.sort_by { |issue| -issue.number }.first(limit)
153
+ end
154
+
122
155
  # @return [ProviderIssue] normalized issue evidence
123
156
  def issue(number:)
124
157
  view = fj(:issue_view, request_number!(number))
@@ -931,7 +964,19 @@ module Ace
931
964
  review_http.request(method, path, body: payload)
932
965
  end
933
966
  Ace::Git::Providers::FormalApproval.new(provider: self, request: request,
934
- list: method(:formal_review_rows), official: true, pending_reuse: true)
967
+ list: method(:formal_review_rows), official: true, pending_reuse: true,
968
+ expected_reviewer: leased_reviewer_account)
969
+ end
970
+
971
+ # Repository-scoped leases cannot call /api/v1/user. The issuer's
972
+ # account is an expectation; the forge review supplies the identity.
973
+ def leased_reviewer_account
974
+ return unless ENV["ACE_LAB_FORGEJO_ORIGIN"] == RepositoryBinding::Target.resolve(server.url).host_url
975
+
976
+ account = ENV["ACE_LAB_FORGEJO_ACCOUNT"]
977
+ return account if account&.match?(/\A[a-zA-Z0-9_.-]+\z/)
978
+
979
+ raise Ace::Git::ProviderAuthenticationError, "Forgejo lease has no valid reviewer account"
935
980
  end
936
981
 
937
982
  # Forgejo lists review requests alongside actual reviews. User and
@@ -3,7 +3,7 @@
3
3
  module Ace
4
4
  module Git
5
5
  module Forgejo
6
- VERSION = "0.8.0"
6
+ VERSION = "0.9.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.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-10-23 00:00:00.000000000 Z
10
+ date: 2026-11-10 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ace-git
@@ -16,6 +16,9 @@ dependencies:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
18
  version: '0.31'
19
+ - - ">="
20
+ - !ruby/object:Gem::Version
21
+ version: 0.33.0
19
22
  type: :runtime
20
23
  prerelease: false
21
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -23,6 +26,9 @@ dependencies:
23
26
  - - "~>"
24
27
  - !ruby/object:Gem::Version
25
28
  version: '0.31'
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 0.33.0
26
32
  - !ruby/object:Gem::Dependency
27
33
  name: faraday
28
34
  requirement: !ruby/object:Gem::Requirement