summaryrefslogtreecommitdiff
path: root/Hutch/Networking
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-08-07 16:35:16 -0500
committerGitHub <[email protected]>2026-08-07 16:35:16 -0500
commit9671eac8eeb9d06e25563f51bf81889b5537feeb (patch)
tree6d943fc410b37d3dc7dfec72406688dbd97ac724 /Hutch/Networking
parent18b1c475983ae9e6a52fcedb2e2992193130035c (diff)
parent07156180789b02d4f22a87f22a37c912e7543190 (diff)
downloadhutch-9671eac8eeb9d06e25563f51bf81889b5537feeb.tar.gz
hutch-9671eac8eeb9d06e25563f51bf81889b5537feeb.tar.bz2
hutch-9671eac8eeb9d06e25563f51bf81889b5537feeb.zip
Merge pull request #36 from krazywarez/deploy-keysv3.10.0
Deploy keys: manage git.sr.ht repository deploy keys (v3.10.0)
Diffstat (limited to 'Hutch/Networking')
-rw-r--r--Hutch/Networking/RepositoryDeployKeyService.swift100
1 files changed, 100 insertions, 0 deletions
diff --git a/Hutch/Networking/RepositoryDeployKeyService.swift b/Hutch/Networking/RepositoryDeployKeyService.swift
new file mode 100644
index 0000000..39e2da1
--- /dev/null
+++ b/Hutch/Networking/RepositoryDeployKeyService.swift
@@ -0,0 +1,100 @@
+import Foundation
+
+/// A per-repository deploy key (git.sr.ht `SSHKey` under `Repository.deployKeys`).
+struct RepositoryDeployKey: Decodable, Sendable, Identifiable, Hashable {
+ let rid: String
+ let keyType: String
+ let fingerprintSHA256: String
+ let comment: String?
+ let access: AccessMode
+
+ var id: String { rid }
+}
+
+private struct DeployKeysQueryResponse: Decodable, Sendable {
+ let repository: DeployKeysRepository?
+}
+
+private struct DeployKeysRepository: Decodable, Sendable {
+ let deployKeys: DeployKeysPage
+}
+
+private struct DeployKeysPage: Decodable, Sendable {
+ let results: [RepositoryDeployKey]
+}
+
+/// `createDeployKey`'s response returns an empty `access` (the stored value is
+/// correct — the list query reports it), so we select only `rid` here and let
+/// callers reload rather than decode the partial key.
+private struct CreateDeployKeyResponse: Decodable, Sendable {}
+
+/// Delete returns the removed key; only success matters here.
+private struct DeleteDeployKeyResponse: Decodable, Sendable {}
+
+/// Deploy keys are a git.sr.ht capability (`createDeployKey` / `deleteDeployKey`),
+/// owner-only, alongside repository ACLs.
+struct RepositoryDeployKeyService: Sendable {
+ private let client: SRHTClient
+
+ init(client: SRHTClient) {
+ self.client = client
+ }
+
+ func fetchDeployKeys(repositoryRid: String) async throws -> [RepositoryDeployKey] {
+ let response = try await client.execute(
+ service: .git,
+ query: Self.deployKeysQuery,
+ variables: ["rid": repositoryRid],
+ responseType: DeployKeysQueryResponse.self
+ )
+ return response.repository?.deployKeys.results ?? []
+ }
+
+ func createDeployKey(repositoryRid: String, mode: AccessMode, key: String) async throws {
+ _ = try await client.execute(
+ service: .git,
+ query: Self.createDeployKeyMutation,
+ variables: ["repo": repositoryRid, "mode": mode.rawValue, "key": key],
+ responseType: CreateDeployKeyResponse.self
+ )
+ }
+
+ func deleteDeployKey(rid: String) async throws {
+ _ = try await client.execute(
+ service: .git,
+ query: Self.deleteDeployKeyMutation,
+ variables: ["rid": rid],
+ responseType: DeleteDeployKeyResponse.self
+ )
+ }
+}
+
+private extension RepositoryDeployKeyService {
+ static let deployKeysQuery = """
+ query repositoryDeployKeys($rid: ID!) {
+ repository(rid: $rid) {
+ deployKeys {
+ results {
+ rid
+ keyType
+ fingerprintSHA256
+ comment
+ access
+ }
+ }
+ }
+ }
+ """
+
+ static let createDeployKeyMutation = """
+ mutation createDeployKey($repo: ID!, $mode: AccessMode!, $key: String!) {
+ createDeployKey(repo: $repo, mode: $mode, key: $key) { rid }
+ }
+ """
+
+ static let deleteDeployKeyMutation = """
+ mutation deleteDeployKey($rid: ID!) {
+ deleteDeployKey(rid: $rid) { rid }
+ }
+ """
+}