History

feat(source-control): add Bitbucket & Azure Devops providers (#2473)

Julius Marminge committed 6 months agov0.0.22-nightly.20260504.196

Commit 91a03e01 parent d501ebe59 files · +5834 −2327Browse files

Showing the changes to apps/server/src/sourceControl/GitLabSourceControlProvider.ts (1 of 59 files). Show all

Unified · Split

apps/server/src/sourceControl/GitLabSourceControlProvider.ts+53−19
@@ -1,10 +1,14 @@
1import { Effect, Layer, Option } from "effect";1import { Effect, Layer, Option } from "effect";
2import { SourceControlProviderError, type ChangeRequest } from "@t3tools/contracts";2import { SourceControlProviderError, type ChangeRequest } from "@t3tools/contracts";
3 3
4import { GitLabCli, type GitLabCliError, type GitLabMergeRequestSummary } from "./GitLabCli.ts";4import * as GitLabCli from "./GitLabCli.ts";
5import { SourceControlProvider, type SourceControlRefSelector } from "./SourceControlProvider.ts";5import * as SourceControlProvider from "./SourceControlProvider.ts";
6import * as SourceControlProviderDiscovery from "./SourceControlProviderDiscovery.ts";
6 7
7function providerError(operation: string, cause: GitLabCliError): SourceControlProviderError {8function 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
16function toChangeRequest(summary: GitLabMergeRequestSummary): ChangeRequest {20function 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
38function sourceFromInput(input: {42function 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
74export 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
52export const make = Effect.fn("makeGitLabSourceControlProvider")(function* () {86export 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 gitlab93 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 gitlab113 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
110export const layer = Layer.effect(SourceControlProvider, make());144export const layer = Layer.effect(SourceControlProvider.SourceControlProvider, make());