| ... | @@ -1,6 +1,7 @@ | ... | @@ -1,6 +1,7 @@ |
| 1 | import * as Context from "effect/Context"; | 1 | import * as Context from "effect/Context"; |
| 2 | import * as Effect from "effect/Effect"; | 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Layer from "effect/Layer"; | 3 | import * as Layer from "effect/Layer"; |
| | 4 | import * as Match from "effect/Match"; |
| 4 | import * as Option from "effect/Option"; | 5 | import * as Option from "effect/Option"; |
| 5 | import * as Result from "effect/Result"; | 6 | import * as Result from "effect/Result"; |
| 6 | import * as Schema from "effect/Schema"; | 7 | import * as Schema from "effect/Schema"; |
| ... | @@ -21,13 +22,56 @@ import type * as SourceControlProvider from "./SourceControlProvider.ts"; | ... | @@ -21,13 +22,56 @@ import type * as SourceControlProvider from "./SourceControlProvider.ts"; |
| 21 | | 22 | |
| 22 | const DEFAULT_TIMEOUT_MS = 30_000; | 23 | const DEFAULT_TIMEOUT_MS = 30_000; |
| 23 | | 24 | |
| 24 | export class GitLabCliError extends Schema.TaggedErrorClass<GitLabCliError>()("GitLabCliError", { | 25 | const gitLabCliExecutionErrorContext = { |
| 25 | operation: Schema.String, | 26 | operation: Schema.Literal("execute"), |
| 26 | command: Schema.String, | 27 | command: Schema.Literal("glab"), |
| 27 | cwd: Schema.String, | 28 | cwd: Schema.String, |
| 28 | detail: Schema.String, | 29 | cause: Schema.Defect(), |
| 29 | cause: Schema.optional(Schema.Defect()), | 30 | }; |
| 30 | }) { | 31 | |
| | 32 | const gitLabCliDecodeErrorContext = { |
| | 33 | command: Schema.Literal("glab"), |
| | 34 | cwd: Schema.String, |
| | 35 | cause: Schema.Defect(), |
| | 36 | }; |
| | 37 | |
| | 38 | export class GitLabCliUnavailableError extends Schema.TaggedErrorClass<GitLabCliUnavailableError>()( |
| | 39 | "GitLabCliUnavailableError", |
| | 40 | gitLabCliExecutionErrorContext, |
| | 41 | ) { |
| | 42 | get detail(): string { |
| | 43 | return "GitLab CLI (`glab`) is required but not available on PATH."; |
| | 44 | } |
| | 45 | |
| | 46 | override get message(): string { |
| | 47 | return `GitLab CLI failed in ${this.operation}: ${this.detail}`; |
| | 48 | } |
| | 49 | } |
| | 50 | |
| | 51 | export class GitLabCliAuthenticationError extends Schema.TaggedErrorClass<GitLabCliAuthenticationError>()( |
| | 52 | "GitLabCliAuthenticationError", |
| | 53 | gitLabCliExecutionErrorContext, |
| | 54 | ) { |
| | 55 | get detail(): string { |
| | 56 | return "GitLab CLI is not authenticated. Run `glab auth login` and retry."; |
| | 57 | } |
| | 58 | |
| | 59 | override get message(): string { |
| | 60 | return `GitLab CLI failed in ${this.operation}: ${this.detail}`; |
| | 61 | } |
| | 62 | } |
| | 63 | |
| | 64 | export class GitLabMergeRequestNotFoundError extends Schema.TaggedErrorClass<GitLabMergeRequestNotFoundError>()( |
| | 65 | "GitLabMergeRequestNotFoundError", |
| | 66 | { |
| | 67 | ...gitLabCliExecutionErrorContext, |
| | 68 | reference: Schema.String, |
| | 69 | }, |
| | 70 | ) { |
| | 71 | get detail(): string { |
| | 72 | return `Merge request ${this.reference} was not found. Check the MR number or URL and try again.`; |
| | 73 | } |
| | 74 | |
| 31 | override get message(): string { | 75 | override get message(): string { |
| 32 | return `GitLab CLI failed in ${this.operation}: ${this.detail}`; | 76 | return `GitLab CLI failed in ${this.operation}: ${this.detail}`; |
| 33 | } | 77 | } |
| ... | @@ -37,52 +81,145 @@ export class GitLabCliError extends Schema.TaggedErrorClass<GitLabCliError>()("G | ... | @@ -37,52 +81,145 @@ export class GitLabCliError extends Schema.TaggedErrorClass<GitLabCliError>()("G |
| 37 | readonly operation: "execute"; | 81 | readonly operation: "execute"; |
| 38 | readonly command: "glab"; | 82 | readonly command: "glab"; |
| 39 | readonly cwd: string; | 83 | readonly cwd: string; |
| | 84 | readonly reference: string; |
| 40 | }, | 85 | }, |
| 41 | error: VcsError | unknown, | 86 | error: VcsError, |
| 42 | ): GitLabCliError { | 87 | ): GitLabCliError { |
| 43 | const lower = errorText(error).toLowerCase(); | 88 | if (error._tag === "VcsProcessExitError" && error.failureKind === "not-found") { |
| 44 | | 89 | return new GitLabMergeRequestNotFoundError({ ...context, cause: error }); |
| 45 | if (lower.includes("command not found: glab") || isVcsProcessSpawnError(error)) { | | |
| 46 | return new GitLabCliError({ | | |
| 47 | ...context, | | |
| 48 | detail: "GitLab CLI (`glab`) is required but not available on PATH.", | | |
| 49 | cause: error, | | |
| 50 | }); | | |
| 51 | } | 90 | } |
| 52 | | 91 | |
| 53 | if ( | 92 | return GitLabCliCommandError.fromVcsError( |
| 54 | lower.includes("authentication failed") || | 93 | { |
| 55 | lower.includes("not logged in") || | 94 | operation: context.operation, |
| 56 | lower.includes("glab auth login") || | 95 | command: context.command, |
| 57 | lower.includes("token") | 96 | cwd: context.cwd, |
| 58 | ) { | 97 | }, |
| 59 | return new GitLabCliError({ | 98 | error, |
| 60 | ...context, | 99 | ); |
| 61 | detail: "GitLab CLI is not authenticated. Run `glab auth login` and retry.", | 100 | } |
| 62 | cause: error, | 101 | } |
| 63 | }); | | |
| 64 | } | | |
| 65 | | 102 | |
| 66 | if ( | 103 | export class GitLabCliCommandError extends Schema.TaggedErrorClass<GitLabCliCommandError>()( |
| 67 | lower.includes("merge request not found") || | 104 | "GitLabCliCommandError", |
| 68 | lower.includes("not found") || | 105 | gitLabCliExecutionErrorContext, |
| 69 | lower.includes("404") | 106 | ) { |
| 70 | ) { | 107 | get detail(): string { |
| 71 | return new GitLabCliError({ | 108 | return "GitLab CLI command failed."; |
| 72 | ...context, | 109 | } |
| 73 | detail: "Merge request not found. Check the MR number or URL and try again.", | | |
| 74 | cause: error, | | |
| 75 | }); | | |
| 76 | } | | |
| 77 | | 110 | |
| 78 | return new GitLabCliError({ | 111 | override get message(): string { |
| 79 | ...context, | 112 | return `GitLab CLI failed in ${this.operation}: ${this.detail}`; |
| 80 | detail: "GitLab CLI command failed.", | 113 | } |
| 81 | cause: error, | 114 | |
| | 115 | static fromVcsError( |
| | 116 | context: { |
| | 117 | readonly operation: "execute"; |
| | 118 | readonly command: "glab"; |
| | 119 | readonly cwd: string; |
| | 120 | }, |
| | 121 | error: VcsError, |
| | 122 | ): GitLabCliError { |
| | 123 | return Match.valueTags(error, { |
| | 124 | VcsProcessSpawnError: (cause) => new GitLabCliUnavailableError({ ...context, cause }), |
| | 125 | VcsProcessExitError: (cause) => { |
| | 126 | switch (cause.failureKind) { |
| | 127 | case "authentication": |
| | 128 | return new GitLabCliAuthenticationError({ ...context, cause }); |
| | 129 | case "not-found": |
| | 130 | case "command-failed": |
| | 131 | case undefined: |
| | 132 | return new GitLabCliCommandError({ ...context, cause }); |
| | 133 | } |
| | 134 | }, |
| | 135 | VcsProcessTimeoutError: (cause) => new GitLabCliCommandError({ ...context, cause }), |
| | 136 | VcsOutputDecodeError: (cause) => new GitLabCliCommandError({ ...context, cause }), |
| | 137 | VcsRepositoryDetectionError: (cause) => new GitLabCliCommandError({ ...context, cause }), |
| | 138 | VcsUnsupportedOperationError: (cause) => new GitLabCliCommandError({ ...context, cause }), |
| 82 | }); | 139 | }); |
| 83 | } | 140 | } |
| 84 | } | 141 | } |
| 85 | | 142 | |
| | 143 | export class GitLabMergeRequestListDecodeError extends Schema.TaggedErrorClass<GitLabMergeRequestListDecodeError>()( |
| | 144 | "GitLabMergeRequestListDecodeError", |
| | 145 | { |
| | 146 | ...gitLabCliDecodeErrorContext, |
| | 147 | operation: Schema.Literal("listMergeRequests"), |
| | 148 | }, |
| | 149 | ) { |
| | 150 | get detail(): string { |
| | 151 | return "GitLab CLI returned invalid MR list JSON."; |
| | 152 | } |
| | 153 | |
| | 154 | override get message(): string { |
| | 155 | return `GitLab CLI failed in ${this.operation}: ${this.detail}`; |
| | 156 | } |
| | 157 | } |
| | 158 | |
| | 159 | export class GitLabMergeRequestDecodeError extends Schema.TaggedErrorClass<GitLabMergeRequestDecodeError>()( |
| | 160 | "GitLabMergeRequestDecodeError", |
| | 161 | { |
| | 162 | ...gitLabCliDecodeErrorContext, |
| | 163 | operation: Schema.Literal("getMergeRequest"), |
| | 164 | reference: Schema.String, |
| | 165 | }, |
| | 166 | ) { |
| | 167 | get detail(): string { |
| | 168 | return "GitLab CLI returned invalid merge request JSON."; |
| | 169 | } |
| | 170 | |
| | 171 | override get message(): string { |
| | 172 | return `GitLab CLI failed in ${this.operation}: ${this.detail}`; |
| | 173 | } |
| | 174 | } |
| | 175 | |
| | 176 | export class GitLabRepositoryDecodeError extends Schema.TaggedErrorClass<GitLabRepositoryDecodeError>()( |
| | 177 | "GitLabRepositoryDecodeError", |
| | 178 | { |
| | 179 | ...gitLabCliDecodeErrorContext, |
| | 180 | operation: Schema.Literals(["getRepositoryCloneUrls", "createRepository", "getDefaultBranch"]), |
| | 181 | repository: Schema.optional(Schema.String), |
| | 182 | }, |
| | 183 | ) { |
| | 184 | get detail(): string { |
| | 185 | return "GitLab CLI returned invalid repository JSON."; |
| | 186 | } |
| | 187 | |
| | 188 | override get message(): string { |
| | 189 | return `GitLab CLI failed in ${this.operation}: ${this.detail}`; |
| | 190 | } |
| | 191 | } |
| | 192 | |
| | 193 | export class GitLabNamespaceDecodeError extends Schema.TaggedErrorClass<GitLabNamespaceDecodeError>()( |
| | 194 | "GitLabNamespaceDecodeError", |
| | 195 | { |
| | 196 | ...gitLabCliDecodeErrorContext, |
| | 197 | operation: Schema.Literal("createRepository"), |
| | 198 | namespacePath: Schema.String, |
| | 199 | }, |
| | 200 | ) { |
| | 201 | get detail(): string { |
| | 202 | return "GitLab CLI returned invalid namespace JSON."; |
| | 203 | } |
| | 204 | |
| | 205 | override get message(): string { |
| | 206 | return `GitLab CLI failed in ${this.operation}: ${this.detail}`; |
| | 207 | } |
| | 208 | } |
| | 209 | |
| | 210 | export const GitLabCliError = Schema.Union([ |
| | 211 | GitLabCliUnavailableError, |
| | 212 | GitLabCliAuthenticationError, |
| | 213 | GitLabMergeRequestNotFoundError, |
| | 214 | GitLabCliCommandError, |
| | 215 | GitLabMergeRequestListDecodeError, |
| | 216 | GitLabMergeRequestDecodeError, |
| | 217 | GitLabRepositoryDecodeError, |
| | 218 | GitLabNamespaceDecodeError, |
| | 219 | ]); |
| | 220 | export type GitLabCliError = typeof GitLabCliError.Type; |
| | 221 | export const isGitLabCliError = Schema.is(GitLabCliError); |
| | 222 | |
| 86 | export interface GitLabMergeRequestSummary { | 223 | export interface GitLabMergeRequestSummary { |
| 87 | readonly number: number; | 224 | readonly number: number; |
| 88 | readonly title: string; | 225 | readonly title: string; |
| ... | @@ -102,17 +239,6 @@ export interface GitLabRepositoryCloneUrls { | ... | @@ -102,17 +239,6 @@ export interface GitLabRepositoryCloneUrls { |
| 102 | readonly sshUrl: string; | 239 | readonly sshUrl: string; |
| 103 | } | 240 | } |
| 104 | | 241 | |
| 105 | function errorText(error: VcsError | unknown): string { | | |
| 106 | if (typeof error === "object" && error !== null) { | | |
| 107 | const tag = "_tag" in error && typeof error._tag === "string" ? error._tag : ""; | | |
| 108 | const detail = "detail" in error && typeof error.detail === "string" ? error.detail : ""; | | |
| 109 | const message = "message" in error && typeof error.message === "string" ? error.message : ""; | | |
| 110 | return [tag, detail, message].filter(Boolean).join("\n"); | | |
| 111 | } | | |
| 112 | | | |
| 113 | return String(error); | | |
| 114 | } | | |
| 115 | | | |
| 116 | export class GitLabCli extends Context.Service< | 242 | export class GitLabCli extends Context.Service< |
| 117 | GitLabCli, | 243 | GitLabCli, |
| 118 | { | 244 | { |
| ... | @@ -168,15 +294,6 @@ export class GitLabCli extends Context.Service< | ... | @@ -168,15 +294,6 @@ export class GitLabCli extends Context.Service< |
| 168 | } | 294 | } |
| 169 | >()("t3/sourceControl/GitLabCli") {} | 295 | >()("t3/sourceControl/GitLabCli") {} |
| 170 | | 296 | |
| 171 | function isVcsProcessSpawnError(error: unknown): boolean { | | |
| 172 | return ( | | |
| 173 | typeof error === "object" && | | |
| 174 | error !== null && | | |
| 175 | "_tag" in error && | | |
| 176 | error._tag === "VcsProcessSpawnError" | | |
| 177 | ); | | |
| 178 | } | | |
| 179 | | | |
| 180 | const RawGitLabRepositoryCloneUrlsSchema = Schema.Struct({ | 297 | const RawGitLabRepositoryCloneUrlsSchema = Schema.Struct({ |
| 181 | path_with_namespace: TrimmedNonEmptyString, | 298 | path_with_namespace: TrimmedNonEmptyString, |
| 182 | web_url: TrimmedNonEmptyString, | 299 | web_url: TrimmedNonEmptyString, |
| ... | @@ -192,6 +309,14 @@ const RawGitLabNamespaceSchema = Schema.Struct({ | ... | @@ -192,6 +309,14 @@ const RawGitLabNamespaceSchema = Schema.Struct({ |
| 192 | id: Schema.Number, | 309 | id: Schema.Number, |
| 193 | }); | 310 | }); |
| 194 | | 311 | |
| | 312 | const decodeGitLabRepositoryCloneUrls = Schema.decodeEffect( |
| | 313 | Schema.fromJsonString(RawGitLabRepositoryCloneUrlsSchema), |
| | 314 | ); |
| | 315 | const decodeGitLabDefaultBranch = Schema.decodeEffect( |
| | 316 | Schema.fromJsonString(RawGitLabDefaultBranchSchema), |
| | 317 | ); |
| | 318 | const decodeGitLabNamespace = Schema.decodeEffect(Schema.fromJsonString(RawGitLabNamespaceSchema)); |
| | 319 | |
| 195 | function normalizeRepositoryCloneUrls( | 320 | function normalizeRepositoryCloneUrls( |
| 196 | raw: Schema.Schema.Type<typeof RawGitLabRepositoryCloneUrlsSchema>, | 321 | raw: Schema.Schema.Type<typeof RawGitLabRepositoryCloneUrlsSchema>, |
| 197 | ): GitLabRepositoryCloneUrls { | 322 | ): GitLabRepositoryCloneUrls { |
| ... | @@ -202,27 +327,6 @@ function normalizeRepositoryCloneUrls( | ... | @@ -202,27 +327,6 @@ function normalizeRepositoryCloneUrls( |
| 202 | }; | 327 | }; |
| 203 | } | 328 | } |
| 204 | | 329 | |
| 205 | function decodeGitLabJson<S extends Schema.Top>( | | |
| 206 | raw: string, | | |
| 207 | schema: S, | | |
| 208 | operation: "getRepositoryCloneUrls" | "getDefaultBranch" | "createRepository", | | |
| 209 | invalidDetail: string, | | |
| 210 | cwd: string, | | |
| 211 | ): Effect.Effect<S["Type"], GitLabCliError, S["DecodingServices"]> { | | |
| 212 | return Schema.decodeEffect(Schema.fromJsonString(schema))(raw).pipe( | | |
| 213 | Effect.mapError( | | |
| 214 | (error) => | | |
| 215 | new GitLabCliError({ | | |
| 216 | operation, | | |
| 217 | command: "glab", | | |
| 218 | cwd, | | |
| 219 | detail: invalidDetail, | | |
| 220 | cause: error, | | |
| 221 | }), | | |
| 222 | ), | | |
| 223 | ); | | |
| 224 | } | | |
| 225 | | | |
| 226 | function stateArgs(state: "open" | "closed" | "merged" | "all"): ReadonlyArray<string> { | 330 | function stateArgs(state: "open" | "closed" | "merged" | "all"): ReadonlyArray<string> { |
| 227 | switch (state) { | 331 | switch (state) { |
| 228 | case "open": | 332 | case "open": |
| ... | @@ -283,7 +387,10 @@ function parseRepositoryPath(repository: string): { | ... | @@ -283,7 +387,10 @@ function parseRepositoryPath(repository: string): { |
| 283 | export const make = Effect.gen(function* () { | 387 | export const make = Effect.gen(function* () { |
| 284 | const process = yield* VcsProcess.VcsProcess; | 388 | const process = yield* VcsProcess.VcsProcess; |
| 285 | | 389 | |
| 286 | const execute: GitLabCli["Service"]["execute"] = (input) => | 390 | const run = ( |
| | 391 | input: Parameters<GitLabCli["Service"]["execute"]>[0], |
| | 392 | mapError: (error: VcsError) => GitLabCliError, |
| | 393 | ) => |
| 287 | process | 394 | process |
| 288 | .run({ | 395 | .run({ |
| 289 | operation: "GitLabCli.execute", | 396 | operation: "GitLabCli.execute", |
| ... | @@ -292,14 +399,32 @@ export const make = Effect.gen(function* () { | ... | @@ -292,14 +399,32 @@ export const make = Effect.gen(function* () { |
| 292 | cwd: input.cwd, | 399 | cwd: input.cwd, |
| 293 | timeoutMs: input.timeoutMs ?? DEFAULT_TIMEOUT_MS, | 400 | timeoutMs: input.timeoutMs ?? DEFAULT_TIMEOUT_MS, |
| 294 | }) | 401 | }) |
| 295 | .pipe( | 402 | .pipe(Effect.mapError(mapError)); |
| 296 | Effect.mapError((error) => | 403 | |
| 297 | GitLabCliError.fromVcsError( | 404 | const execute: GitLabCli["Service"]["execute"] = (input) => |
| 298 | { operation: "execute", command: "glab", cwd: input.cwd }, | 405 | run(input, (error) => |
| 299 | error, | 406 | GitLabCliCommandError.fromVcsError( |
| 300 | ), | 407 | { operation: "execute", command: "glab", cwd: input.cwd }, |
| 301 | ), | 408 | error, |
| 302 | ); | 409 | ), |
| | 410 | ); |
| | 411 | |
| | 412 | const executeMergeRequest = (input: { |
| | 413 | readonly cwd: string; |
| | 414 | readonly reference: string; |
| | 415 | readonly args: ReadonlyArray<string>; |
| | 416 | }) => |
| | 417 | run(input, (error) => |
| | 418 | GitLabMergeRequestNotFoundError.fromVcsError( |
| | 419 | { |
| | 420 | operation: "execute", |
| | 421 | command: "glab", |
| | 422 | cwd: input.cwd, |
| | 423 | reference: input.reference, |
| | 424 | }, |
| | 425 | error, |
| | 426 | ), |
| | 427 | ); |
| 303 | | 428 | |
| 304 | return GitLabCli.of({ | 429 | return GitLabCli.of({ |
| 305 | execute, | 430 | execute, |
| ... | @@ -326,11 +451,10 @@ export const make = Effect.gen(function* () { | ... | @@ -326,11 +451,10 @@ export const make = Effect.gen(function* () { |
| 326 | Effect.flatMap((decoded) => { | 451 | Effect.flatMap((decoded) => { |
| 327 | if (!Result.isSuccess(decoded)) { | 452 | if (!Result.isSuccess(decoded)) { |
| 328 | return Effect.fail( | 453 | return Effect.fail( |
| 329 | new GitLabCliError({ | 454 | new GitLabMergeRequestListDecodeError({ |
| 330 | operation: "listMergeRequests", | 455 | operation: "listMergeRequests", |
| 331 | command: "glab", | 456 | command: "glab", |
| 332 | cwd: input.cwd, | 457 | cwd: input.cwd, |
| 333 | detail: "GitLab CLI returned invalid MR list JSON.", | | |
| 334 | cause: decoded.failure, | 458 | cause: decoded.failure, |
| 335 | }), | 459 | }), |
| 336 | ); | 460 | ); |
| ... | @@ -342,8 +466,9 @@ export const make = Effect.gen(function* () { | ... | @@ -342,8 +466,9 @@ export const make = Effect.gen(function* () { |
| 342 | ), | 466 | ), |
| 343 | ), | 467 | ), |
| 344 | getMergeRequest: (input) => | 468 | getMergeRequest: (input) => |
| 345 | execute({ | 469 | executeMergeRequest({ |
| 346 | cwd: input.cwd, | 470 | cwd: input.cwd, |
| | 471 | reference: input.reference, |
| 347 | args: ["mr", "view", input.reference, "--output", "json"], | 472 | args: ["mr", "view", input.reference, "--output", "json"], |
| 348 | }).pipe( | 473 | }).pipe( |
| 349 | Effect.map((result) => result.stdout.trim()), | 474 | Effect.map((result) => result.stdout.trim()), |
| ... | @@ -352,11 +477,11 @@ export const make = Effect.gen(function* () { | ... | @@ -352,11 +477,11 @@ export const make = Effect.gen(function* () { |
| 352 | Effect.flatMap((decoded) => { | 477 | Effect.flatMap((decoded) => { |
| 353 | if (!Result.isSuccess(decoded)) { | 478 | if (!Result.isSuccess(decoded)) { |
| 354 | return Effect.fail( | 479 | return Effect.fail( |
| 355 | new GitLabCliError({ | 480 | new GitLabMergeRequestDecodeError({ |
| 356 | operation: "getMergeRequest", | 481 | operation: "getMergeRequest", |
| 357 | command: "glab", | 482 | command: "glab", |
| 358 | cwd: input.cwd, | 483 | cwd: input.cwd, |
| 359 | detail: "GitLab CLI returned invalid merge request JSON.", | 484 | reference: input.reference, |
| 360 | cause: decoded.failure, | 485 | cause: decoded.failure, |
| 361 | }), | 486 | }), |
| 362 | ); | 487 | ); |
| ... | @@ -374,12 +499,17 @@ export const make = Effect.gen(function* () { | ... | @@ -374,12 +499,17 @@ export const make = Effect.gen(function* () { |
| 374 | }).pipe( | 499 | }).pipe( |
| 375 | Effect.map((result) => result.stdout.trim()), | 500 | Effect.map((result) => result.stdout.trim()), |
| 376 | Effect.flatMap((raw) => | 501 | Effect.flatMap((raw) => |
| 377 | decodeGitLabJson( | 502 | decodeGitLabRepositoryCloneUrls(raw).pipe( |
| 378 | raw, | 503 | Effect.mapError( |
| 379 | RawGitLabRepositoryCloneUrlsSchema, | 504 | (cause) => |
| 380 | "getRepositoryCloneUrls", | 505 | new GitLabRepositoryDecodeError({ |
| 381 | "GitLab CLI returned invalid repository JSON.", | 506 | operation: "getRepositoryCloneUrls", |
| 382 | input.cwd, | 507 | command: "glab", |
| | 508 | cwd: input.cwd, |
| | 509 | repository: input.repository, |
| | 510 | cause, |
| | 511 | }), |
| | 512 | ), |
| 383 | ), | 513 | ), |
| 384 | ), | 514 | ), |
| 385 | Effect.map(normalizeRepositoryCloneUrls), | 515 | Effect.map(normalizeRepositoryCloneUrls), |
| ... | @@ -393,12 +523,17 @@ export const make = Effect.gen(function* () { | ... | @@ -393,12 +523,17 @@ export const make = Effect.gen(function* () { |
| 393 | }).pipe( | 523 | }).pipe( |
| 394 | Effect.map((result) => result.stdout.trim()), | 524 | Effect.map((result) => result.stdout.trim()), |
| 395 | Effect.flatMap((raw) => | 525 | Effect.flatMap((raw) => |
| 396 | decodeGitLabJson( | 526 | decodeGitLabNamespace(raw).pipe( |
| 397 | raw, | 527 | Effect.mapError( |
| 398 | RawGitLabNamespaceSchema, | 528 | (cause) => |
| 399 | "createRepository", | 529 | new GitLabNamespaceDecodeError({ |
| 400 | "GitLab CLI returned invalid namespace JSON.", | 530 | operation: "createRepository", |
| 401 | input.cwd, | 531 | command: "glab", |
| | 532 | cwd: input.cwd, |
| | 533 | namespacePath, |
| | 534 | cause, |
| | 535 | }), |
| | 536 | ), |
| 402 | ), | 537 | ), |
| 403 | ), | 538 | ), |
| 404 | Effect.map((namespace) => namespace.id), | 539 | Effect.map((namespace) => namespace.id), |
| ... | @@ -428,12 +563,17 @@ export const make = Effect.gen(function* () { | ... | @@ -428,12 +563,17 @@ export const make = Effect.gen(function* () { |
| 428 | ), | 563 | ), |
| 429 | Effect.map((result) => result.stdout.trim()), | 564 | Effect.map((result) => result.stdout.trim()), |
| 430 | Effect.flatMap((raw) => | 565 | Effect.flatMap((raw) => |
| 431 | decodeGitLabJson( | 566 | decodeGitLabRepositoryCloneUrls(raw).pipe( |
| 432 | raw, | 567 | Effect.mapError( |
| 433 | RawGitLabRepositoryCloneUrlsSchema, | 568 | (cause) => |
| 434 | "createRepository", | 569 | new GitLabRepositoryDecodeError({ |
| 435 | "GitLab CLI returned invalid repository JSON.", | 570 | operation: "createRepository", |
| 436 | input.cwd, | 571 | command: "glab", |
| | 572 | cwd: input.cwd, |
| | 573 | repository: input.repository, |
| | 574 | cause, |
| | 575 | }), |
| | 576 | ), |
| 437 | ), | 577 | ), |
| 438 | ), | 578 | ), |
| 439 | Effect.map(normalizeRepositoryCloneUrls), | 579 | Effect.map(normalizeRepositoryCloneUrls), |
| ... | @@ -467,19 +607,24 @@ export const make = Effect.gen(function* () { | ... | @@ -467,19 +607,24 @@ export const make = Effect.gen(function* () { |
| 467 | }).pipe( | 607 | }).pipe( |
| 468 | Effect.map((result) => result.stdout.trim()), | 608 | Effect.map((result) => result.stdout.trim()), |
| 469 | Effect.flatMap((raw) => | 609 | Effect.flatMap((raw) => |
| 470 | decodeGitLabJson( | 610 | decodeGitLabDefaultBranch(raw).pipe( |
| 471 | raw, | 611 | Effect.mapError( |
| 472 | RawGitLabDefaultBranchSchema, | 612 | (cause) => |
| 473 | "getDefaultBranch", | 613 | new GitLabRepositoryDecodeError({ |
| 474 | "GitLab CLI returned invalid repository JSON.", | 614 | operation: "getDefaultBranch", |
| 475 | input.cwd, | 615 | command: "glab", |
| | 616 | cwd: input.cwd, |
| | 617 | cause, |
| | 618 | }), |
| | 619 | ), |
| 476 | ), | 620 | ), |
| 477 | ), | 621 | ), |
| 478 | Effect.map((value) => value.default_branch ?? null), | 622 | Effect.map((value) => value.default_branch ?? null), |
| 479 | ), | 623 | ), |
| 480 | checkoutMergeRequest: (input) => | 624 | checkoutMergeRequest: (input) => |
| 481 | execute({ | 625 | executeMergeRequest({ |
| 482 | cwd: input.cwd, | 626 | cwd: input.cwd, |
| | 627 | reference: input.reference, |
| 483 | args: ["mr", "checkout", input.reference], | 628 | args: ["mr", "checkout", input.reference], |
| 484 | }).pipe(Effect.asVoid), | 629 | }).pipe(Effect.asVoid), |
| 485 | }); | 630 | }); |