summaryrefslogtreecommitdiff
path: root/Hutch/Networking/RepositoryDeployKeyService.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-08-07 16:29:04 -0500
committerChristian Cleberg <[email protected]>2026-08-07 16:29:04 -0500
commit07156180789b02d4f22a87f22a37c912e7543190 (patch)
tree6d943fc410b37d3dc7dfec72406688dbd97ac724 /Hutch/Networking/RepositoryDeployKeyService.swift
parent18b1c475983ae9e6a52fcedb2e2992193130035c (diff)
downloadhutch-07156180789b02d4f22a87f22a37c912e7543190.tar.gz
hutch-07156180789b02d4f22a87f22a37c912e7543190.tar.bz2
hutch-07156180789b02d4f22a87f22a37c912e7543190.zip
Deploy keys: manage git.sr.ht repository deploy keys (v3.10.0)
Wire git.sr.ht createDeployKey / deleteDeployKey and Repository.deployKeys into an owner-only Deploy Keys sheet in the repository actions menu, next to ACLs: list keys (fingerprint, comment, access), add an SSH public key with RO/RW access, and delete behind a confirmation. Verified live against the ~hutch account. The createDeployKey response returns an empty access value, so create selects only rid and the list is reloaded rather than decoding the partial key. Bumps to 3.10.0 (build 95); roadmap marks deploy keys shipped.
Diffstat (limited to 'Hutch/Networking/RepositoryDeployKeyService.swift')
-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 }
+ }
+ """
+}