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 +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/ace/git/forgejo/issue_api.rb +13 -3
- data/lib/ace/git/forgejo/provider.rb +46 -1
- data/lib/ace/git/forgejo/version.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2f9063026140a927fbd346c52d1bcad410b23dc0fbaa8e8d69eae206688afd72
|
|
4
|
+
data.tar.gz: d2731b929dc9c5adde5d1a21259bc071ad54771424be3d5f3a7e2465891a3eb7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
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.
|
|
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
|
|
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
|