Showing the changes to apps/server/src/sourceControl/GitLabSourceControlProvider.ts (1 of 59 files). Show all
Unified · Split
| @@ -1,10 +1,14 @@ | |||
| 1 | import { Effect, Layer, Option } from "effect"; | 1 | import { Effect, Layer, Option } from "effect"; |
| 2 | import { SourceControlProviderError, type ChangeRequest } from "@t3tools/contracts"; | 2 | import { SourceControlProviderError, type ChangeRequest } from "@t3tools/contracts"; |
| 3 | 3 | ||
| 4 | import { GitLabCli, type GitLabCliError, type GitLabMergeRequestSummary } from "./GitLabCli.ts"; | 4 | import * as GitLabCli from "./GitLabCli.ts"; |
| 5 | import { SourceControlProvider, type SourceControlRefSelector } from "./SourceControlProvider.ts"; | 5 | import * as SourceControlProvider from "./SourceControlProvider.ts"; |
| 6 | import * as SourceControlProviderDiscovery from "./SourceControlProviderDiscovery.ts"; | ||
| 6 | 7 | ||
| 7 | function providerError(operation: string, cause: GitLabCliError): SourceControlProviderError { | 8 | function providerError( |
| 9 | operation: string, | ||
| 10 | cause: GitLabCli.GitLabCliError, | ||
| 11 | ): SourceControlProviderError { | ||
| 8 | return new SourceControlProviderError({ | 12 | return new SourceControlProviderError({ |
| 9 | provider: "gitlab", | 13 | provider: "gitlab", |
| 10 | operation, | 14 | operation, |
| @@ -13,7 +17,7 @@ function providerError(operation: string, cause: GitLabCliError): SourceControlP · 2 unmodified lines | |||
| 13 | }); | 17 | }); |
| 14 | } | 18 | } |
| 15 | 19 | ||
| 16 | function toChangeRequest(summary: GitLabMergeRequestSummary): ChangeRequest { | 20 | function toChangeRequest(summary: GitLabCli.GitLabMergeRequestSummary): ChangeRequest { |
| 17 | return { | 21 | return { |
| 18 | provider: "gitlab", | 22 | provider: "gitlab", |
| 19 | number: summary.number, | 23 | number: summary.number, |
| @@ -35,27 +39,57 @@ function toChangeRequest(summary: GitLabMergeRequestSummary): ChangeRequest { · 15 unmodified lines | |||
| 35 | }; | 39 | }; |
| 36 | } | 40 | } |
| 37 | 41 | ||
| 38 | function sourceFromInput(input: { | 42 | function parseGitLabAuth(input: SourceControlProviderDiscovery.SourceControlAuthProbeInput) { |
| 39 | readonly headSelector: string; | 43 | const output = SourceControlProviderDiscovery.combinedAuthOutput(input); |
| 40 | readonly source?: SourceControlRefSelector; | 44 | const account = SourceControlProviderDiscovery.matchFirst(output, [ |
| 41 | }): SourceControlRefSelector | undefined { | 45 | /Logged in to .* as\s+([^\s(]+)/iu, |
| 42 | if (input.source) { | 46 | /Logged in to .* account\s+([^\s(]+)/iu, |
| 43 | return input.source; | 47 | /account:\s*([^\s(]+)/iu, |
| 48 | ]); | ||
| 49 | const host = SourceControlProviderDiscovery.parseCliHost(output); | ||
| 50 | |||
| 51 | if (input.exitCode !== 0) { | ||
| 52 | return SourceControlProviderDiscovery.providerAuth({ | ||
| 53 | status: "unauthenticated", | ||
| 54 | host, | ||
| 55 | detail: | ||
| 56 | SourceControlProviderDiscovery.firstSafeAuthLine(output) ?? | ||
| 57 | "Run `glab auth login` to authenticate GitLab CLI.", | ||
| 58 | }); | ||
| 59 | } | ||
| 60 | |||
| 61 | if (account) { | ||
| 62 | return SourceControlProviderDiscovery.providerAuth({ status: "authenticated", account, host }); | ||
| 44 | } | 63 | } |
| 45 | 64 | ||
| 46 | const match = /^([^:/\s]+):(.+)$/u.exec(input.headSelector.trim()); | 65 | return SourceControlProviderDiscovery.providerAuth({ |
| 47 | const owner = match?.[1]?.trim(); | 66 | status: "unknown", |
| 48 | const refName = match?.[2]?.trim(); | 67 | host, |
| 49 | return owner && refName ? { owner, refName } : undefined; | 68 | detail: |
| 69 | SourceControlProviderDiscovery.firstSafeAuthLine(output) ?? | ||
| 70 | "GitLab CLI auth status could not be parsed.", | ||
| 71 | }); | ||
| 50 | } | 72 | } |
| 51 | 73 | ||
| 74 | export const discovery = { | ||
| 75 | type: "cli", | ||
| 76 | kind: "gitlab", | ||
| 77 | label: "GitLab", | ||
| 78 | executable: "glab", | ||
| 79 | versionArgs: ["--version"], | ||
| 80 | authArgs: ["auth", "status"], | ||
| 81 | parseAuth: parseGitLabAuth, | ||
| 82 | installHint: | ||
| 83 | "Install the GitLab command-line tool (`glab`) from https://gitlab.com/gitlab-org/cli or your package manager (for example `brew install glab`).", | ||
| 84 | } satisfies SourceControlProviderDiscovery.SourceControlCliDiscoverySpec; | ||
| 85 | |||
| 52 | export const make = Effect.fn("makeGitLabSourceControlProvider")(function* () { | 86 | export const make = Effect.fn("makeGitLabSourceControlProvider")(function* () { |
| 53 | const gitlab = yield* GitLabCli; | 87 | const gitlab = yield* GitLabCli.GitLabCli; |
| 54 | 88 | ||
| 55 | return SourceControlProvider.of({ | 89 | return SourceControlProvider.SourceControlProvider.of({ |
| 56 | kind: "gitlab", | 90 | kind: "gitlab", |
| 57 | listChangeRequests: (input) => { | 91 | listChangeRequests: (input) => { |
| 58 | const source = sourceFromInput(input); | 92 | const source = SourceControlProvider.sourceControlRefFromInput(input); |
| 59 | return gitlab | 93 | return gitlab |
| 60 | .listMergeRequests({ | 94 | .listMergeRequests({ |
| 61 | cwd: input.cwd, | 95 | cwd: input.cwd, |
| @@ -75,7 +109,7 @@ export const make = Effect.fn("makeGitLabSourceControlProvider")(function* () { · 13 unmodified lines | |||
| 75 | Effect.mapError((error) => providerError("getChangeRequest", error)), | 109 | Effect.mapError((error) => providerError("getChangeRequest", error)), |
| 76 | ), | 110 | ), |
| 77 | createChangeRequest: (input) => { | 111 | createChangeRequest: (input) => { |
| 78 | const source = sourceFromInput(input); | 112 | const source = SourceControlProvider.sourceControlRefFromInput(input); |
| 79 | return gitlab | 113 | return gitlab |
| 80 | .createMergeRequest({ | 114 | .createMergeRequest({ |
| 81 | cwd: input.cwd, | 115 | cwd: input.cwd, |
| @@ -107,4 +141,4 @@ export const make = Effect.fn("makeGitLabSourceControlProvider")(function* () { · 25 unmodified lines | |||
| 107 | }); | 141 | }); |
| 108 | }); | 142 | }); |
| 109 | 143 | ||
| 110 | export const layer = Layer.effect(SourceControlProvider, make()); | 144 | export const layer = Layer.effect(SourceControlProvider.SourceControlProvider, make()); |