1import * as Data from "effect/Data";
2import type { PlatformError } from "effect/PlatformError";
3
4export class DockerRegistryBlobUnknown extends Data.TaggedError(
5 "DockerRegistryBlobUnknown",
6)<{ readonly cause: PlatformError }> {
7 override get message() {
8 return this.cause.message;
9 }
10}
11
12export class DockerRegistryUnavailable extends Data.TaggedError(
13 "DockerRegistryUnavailable",
14)<{ readonly cause: PlatformError }> {
15 override get message() {
16 return this.cause.message;
17 }
18}
19
20export type DockerImagePublicationError =
21 | PlatformError
22 | DockerRegistryBlobUnknown
23 | DockerRegistryUnavailable;
24
25export const classifyDockerRegistryError = (
26 error: PlatformError,
27): DockerImagePublicationError => {
28 // Build logs can contain arbitrary Dockerfile output before the final error.
29 const message =
30 error.reason.description
31 ?.split("\n")
32 .map((line) => line.trim())
33 .filter(
34 (line) =>
35 line.length > 0 &&
36 !line.startsWith(
37 "View build details: docker-desktop://dashboard/build/",
38 ),
39 )
40 .at(-1) ?? "";
41 if (/(?:^|:\s*)(?:unknown:\s*)?blob unknown to registry$/i.test(message)) {
42 return new DockerRegistryBlobUnknown({ cause: error });
43 }
44 if (
45 /(?:unexpected (?:http )?status(?:[^\n]*:\s*|\s+)5\d\d(?:\s|$)|(?:^|:\s*)(?:500 internal server error|502 bad gateway|503 service unavailable|504 gateway timeout)$)/i.test(
46 message,
47 )
48 ) {
49 return new DockerRegistryUnavailable({ cause: error });
50 }
51 return error;
52};