ace-git-github 0.4.0 → 0.5.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: baa6f879ef34125b1099214ec6387d091ebbca80116f51c7ea4ea37fc9fc08a9
4
- data.tar.gz: b7f37f202295ba924168e9b3e2f639c221bf93c63a8d2f6112a2fc243cdb8888
3
+ metadata.gz: 7712428a17c056a1d6ae36c9bad2c4ef2d90d083b03eed913ea16d83d9b99c6d
4
+ data.tar.gz: 76aa065c65412840d64a1cc3fc75c8c7b1e0649a7c9bcce8947b477a3e476c7b
5
5
  SHA512:
6
- metadata.gz: 1f49e0c270b7235b7db93ecbeebef80a78ca5a9791959fae5f13b3d6e06c29576583c8f6a270654ffb7815f0703e5be5aaf41cae1c2e123dc80167d4fcf4e1ce
7
- data.tar.gz: 770eec189bf46c9502bc405fbf0e5b1258c154e3a49a1467df4479f71c2118d29a2910c9830e2d78c5fbeb81e3239fd1c824aefa78f4b8cad8a9e99a7fca3513
6
+ metadata.gz: 702969a6a3841f315d52008046ce20323e18667ff035750be93f7305e5187ef529e879ec635daec64957a82b4b6d7a53ed7ce6b1bb7610f23b583d21af2439d3
7
+ data.tar.gz: 17331c5365f2fb16ec22464903c4342ad7e66a7d0342003844aca46c0b11c7826cd59a793a6351a945a35772489ee8a3481745cbff9014ee6a52171bae23cfe2
data/CHANGELOG.md CHANGED
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.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
+ - Prove PAT/App user/App installation actors through authenticated GraphQL viewer instead of REST /user; classify unverifiable viewers and integration access refusals truthfully before approval.
20
+
10
21
  ## [0.4.0] - 2026-10-03
11
22
 
12
23
  ### Added
@@ -3,6 +3,7 @@
3
3
  require "digest"
4
4
  require "open3"
5
5
  require "json"
6
+ require "ace/git/providers/formal_approval"
6
7
  require "uri"
7
8
 
8
9
  module Ace
@@ -510,6 +511,14 @@ end
510
511
  raise Ace::Git::ProviderMalformedOutputError, "Malformed GitHub repository file content: #{e.message}"
511
512
  end
512
513
 
514
+ def approve_pull_request(number:, expected_head:, body:, correlation:)
515
+ formal_approval.approve(number: number, expected_head: expected_head, body: body, correlation: correlation)
516
+ end
517
+
518
+ def pull_request_approval(number:, expected_head:, review_id:)
519
+ formal_approval.read(number: number, expected_head: expected_head, review_id: review_id)
520
+ end
521
+
513
522
  def create_pull_request_comment(number:, expected_head:, body:, correlation:)
514
523
  pr = verify_expected_head!(pull_request(number: number), expected_head)
515
524
  require_open_pr!(pr)
@@ -894,6 +903,41 @@ end
894
903
  )
895
904
  end
896
905
 
906
+ def formal_approval
907
+ request = lambda do |method, path, payload|
908
+ if path == :actor
909
+ formal_review_actor
910
+ else
911
+ gh_api(path, method: method, payload: payload)
912
+ end
913
+ end
914
+ Ace::Git::Providers::FormalApproval.new(provider: self, request: request,
915
+ list: ->(path) { gh_api_pages(path) })
916
+ end
917
+
918
+ # GitHub documents installation-token GraphQL viewer queries. Use the
919
+ # same configured credential for PAT/App user/App installation identities;
920
+ # REST /user is not an installation-token actor endpoint.
921
+ def formal_review_actor
922
+ result = gh_api("graphql", method: :post, global: true,
923
+ payload: {"query" => "query { viewer { login databaseId } }"})
924
+ viewer = result.is_a?(Hash) && result.dig("data", "viewer")
925
+ errors = result.is_a?(Hash) && result["errors"]
926
+ unless (!errors || errors == []) && viewer.is_a?(Hash) &&
927
+ viewer["databaseId"].is_a?(Integer) && viewer["databaseId"].positive? &&
928
+ viewer["login"].is_a?(String) && !viewer["login"].empty?
929
+ raise Ace::Git::ProviderUnsupportedCapabilityError,
930
+ "Configured GitHub credential cannot provide a verified GraphQL viewer actor; no approval performed"
931
+ end
932
+ {"id" => viewer["databaseId"], "login" => viewer["login"]}
933
+ rescue Ace::Git::ProviderAuthenticationError => error
934
+ if error.message.match?(/resource not accessible by integration/i)
935
+ raise Ace::Git::ProviderUnsupportedCapabilityError,
936
+ "Configured GitHub integration credential cannot verify its GraphQL viewer actor; no approval performed"
937
+ end
938
+ raise
939
+ end
940
+
897
941
  def gh_api_pages(suffix)
898
942
  data = gh_api(suffix, paginate: true)
899
943
  pages = data.is_a?(Array) ? data : nil
@@ -903,13 +947,13 @@ end
903
947
  pages.flatten(1)
904
948
  end
905
949
 
906
- def gh_api(suffix, method: :get, body: nil, paginate: false)
950
+ def gh_api(suffix, method: :get, body: nil, paginate: false, payload: nil, global: false)
907
951
  target = Ace::Git::Atoms::ServerUrl.normalize(server.url)
908
952
  host, repository = target.split("/", 2)
909
953
  unless repository&.match?(%r{\A[^/]+/[^/]+\z})
910
954
  raise Ace::Git::ConfigError, "Invalid selected GitHub repository #{server.url}"
911
955
  end
912
- args = ["repos/#{repository}/#{suffix}", "--hostname", host]
956
+ args = [global ? suffix : "repos/#{repository}/#{suffix}", "--hostname", host]
913
957
  args += ["--paginate", "--slurp"] if paginate
914
958
  stdin = nil
915
959
  if %i[post patch].include?(method)
@@ -917,7 +961,7 @@ end
917
961
  # command argument: argv leaks into process listings and
918
962
  # timeout errors, and huge review bodies exceed arg limits.
919
963
  args += ["-X", method.to_s.upcase, "--input", "-"]
920
- stdin = JSON.generate(body: body)
964
+ stdin = JSON.generate(payload || {body: body})
921
965
  end
922
966
  result = CliExecutor.execute("api", args, timeout: timeout, runner: runner, stdin: stdin)
923
967
  classify_failure(result[:stderr], context: "api #{suffix}") unless result[:success]
@@ -1168,7 +1212,7 @@ end
1168
1212
  message = stderr.to_s
1169
1213
  if message.match?(PrFetcher::PR_NOT_FOUND_PATTERN)
1170
1214
  raise Ace::Git::ProviderObjectNotFoundError, "Object not found: #{context}: #{message}"
1171
- elsif message.match?(PrFetcher::AUTH_ERROR_PATTERN)
1215
+ elsif message.match?(PrFetcher::AUTH_ERROR_PATTERN) || message.match?(/HTTP 40[13]|resource not accessible by integration/i)
1172
1216
  raise Ace::Git::ProviderAuthenticationError, "Not authenticated with GitHub: #{message}"
1173
1217
  elsif message.match?(/match[- ]head[- ]commit|head commit/i)
1174
1218
  raise Ace::Git::ProviderExpectedHeadConflictError,
@@ -3,7 +3,7 @@
3
3
  module Ace
4
4
  module Git
5
5
  module Github
6
- VERSION = "0.4.0"
6
+ VERSION = "0.5.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-github
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-10-03 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.26'
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.26'
25
+ version: '0.31'
26
26
  description: 'Implements the shared ace-git provider contract for GitHub: all `gh`
27
27
  CLI invocation, output parsing, authentication verification, and classified failure
28
28
  handling live behind one normalized evidence boundary.'