@zuplo/cli 7.4.4 → 7.4.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/dist/common/settings.d.ts +1 -0
  2. package/dist/common/settings.d.ts.map +1 -1
  3. package/dist/common/settings.js +3 -0
  4. package/dist/common/settings.js.map +1 -1
  5. package/dist/deploy/classify-build-probe.d.ts +14 -0
  6. package/dist/deploy/classify-build-probe.d.ts.map +1 -0
  7. package/dist/deploy/classify-build-probe.js +35 -0
  8. package/dist/deploy/classify-build-probe.js.map +1 -0
  9. package/dist/deploy/classify-build-probe.test.d.ts +2 -0
  10. package/dist/deploy/classify-build-probe.test.d.ts.map +1 -0
  11. package/dist/deploy/classify-build-probe.test.js +51 -0
  12. package/dist/deploy/classify-build-probe.test.js.map +1 -0
  13. package/dist/deploy/handler.js +20 -5
  14. package/dist/deploy/handler.js.map +1 -1
  15. package/dist/deploy/poll-deployment.d.ts +8 -4
  16. package/dist/deploy/poll-deployment.d.ts.map +1 -1
  17. package/dist/deploy/poll-deployment.js +32 -4
  18. package/dist/deploy/poll-deployment.js.map +1 -1
  19. package/dist/deploy/poll-deployment.test.d.ts +2 -0
  20. package/dist/deploy/poll-deployment.test.d.ts.map +1 -0
  21. package/dist/deploy/poll-deployment.test.js +158 -0
  22. package/dist/deploy/poll-deployment.test.js.map +1 -0
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/node_modules/@posthog/core/dist/logs/index.js +1 -1
  25. package/node_modules/@posthog/core/dist/logs/index.mjs +1 -1
  26. package/node_modules/@posthog/core/dist/logs/logs-utils.d.ts +4 -13
  27. package/node_modules/@posthog/core/dist/logs/logs-utils.d.ts.map +1 -1
  28. package/node_modules/@posthog/core/dist/logs/logs-utils.js +160 -26
  29. package/node_modules/@posthog/core/dist/logs/logs-utils.mjs +160 -26
  30. package/node_modules/@posthog/core/dist/metrics/index.js +1 -1
  31. package/node_modules/@posthog/core/dist/metrics/index.mjs +1 -1
  32. package/node_modules/@posthog/core/dist/posthog-core.d.ts.map +1 -1
  33. package/node_modules/@posthog/core/dist/posthog-core.js +12 -7
  34. package/node_modules/@posthog/core/dist/posthog-core.mjs +12 -7
  35. package/node_modules/@posthog/core/dist/utils/index.d.ts +1 -1
  36. package/node_modules/@posthog/core/dist/utils/index.d.ts.map +1 -1
  37. package/node_modules/@posthog/core/dist/utils/index.js +14 -24
  38. package/node_modules/@posthog/core/dist/utils/index.mjs +2 -2
  39. package/node_modules/@posthog/core/dist/utils/json-utils.d.ts +15 -0
  40. package/node_modules/@posthog/core/dist/utils/json-utils.d.ts.map +1 -1
  41. package/node_modules/@posthog/core/dist/utils/json-utils.js +24 -0
  42. package/node_modules/@posthog/core/dist/utils/json-utils.mjs +1 -1
  43. package/node_modules/@posthog/core/package.json +2 -2
  44. package/node_modules/@posthog/core/src/logs/index.ts +1 -1
  45. package/node_modules/@posthog/core/src/logs/logs-utils.spec.ts +314 -9
  46. package/node_modules/@posthog/core/src/logs/logs-utils.ts +215 -27
  47. package/node_modules/@posthog/core/src/metrics/index.spec.ts +15 -1
  48. package/node_modules/@posthog/core/src/metrics/index.ts +1 -1
  49. package/node_modules/@posthog/core/src/posthog-core.ts +19 -12
  50. package/node_modules/@posthog/core/src/utils/index.ts +3 -1
  51. package/node_modules/@posthog/core/src/utils/json-utils.ts +15 -8
  52. package/node_modules/@posthog/types/dist/capture-log.d.ts +10 -1
  53. package/node_modules/@posthog/types/dist/capture-log.d.ts.map +1 -1
  54. package/node_modules/@posthog/types/package.json +1 -1
  55. package/node_modules/@posthog/types/src/capture-log.ts +8 -1
  56. package/node_modules/@zuplo/core/package.json +1 -1
  57. package/node_modules/@zuplo/graphql/package.json +1 -1
  58. package/node_modules/@zuplo/openapi-tools/package.json +1 -1
  59. package/node_modules/@zuplo/otel/package.json +1 -1
  60. package/node_modules/@zuplo/runtime/package.json +1 -1
  61. package/package.json +11 -11
@@ -2,6 +2,7 @@ declare class Settings {
2
2
  get ZUPLO_DEVELOPER_API_ENDPOINT(): string;
3
3
  get MAX_POLL_RETRIES(): number;
4
4
  get POLL_INTERVAL(): number;
5
+ get DEPLOY_INIT_ERROR_FAIL_FAST(): boolean;
5
6
  get MAX_PROVISIONING_RETRIES(): number;
6
7
  get PROVISIONING_POLL_INTERVAL(): number;
7
8
  get AUTH0_DOMAIN(): string;
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/common/settings.ts"],"names":[],"mappings":"AAGA,cAAM,QAAQ;IACZ,IAAI,4BAA4B,IAAI,MAAM,CAEzC;IAED,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,IAAI,wBAAwB,IAAI,MAAM,CAErC;IAED,IAAI,0BAA0B,IAAI,MAAM,CAEvC;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;CACF;AAED,QAAA,MAAM,QAAQ,UAAiB,CAAC;AAChC,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/common/settings.ts"],"names":[],"mappings":"AAGA,cAAM,QAAQ;IACZ,IAAI,4BAA4B,IAAI,MAAM,CAEzC;IAED,IAAI,gBAAgB,IAAI,MAAM,CAE7B;IAED,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,IAAI,2BAA2B,IAAI,OAAO,CAEzC;IAED,IAAI,wBAAwB,IAAI,MAAM,CAErC;IAED,IAAI,0BAA0B,IAAI,MAAM,CAEvC;IAED,IAAI,YAAY,IAAI,MAAM,CAEzB;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;CACF;AAED,QAAA,MAAM,QAAQ,UAAiB,CAAC;AAChC,eAAe,QAAQ,CAAC"}
@@ -8,6 +8,9 @@ class Settings {
8
8
  get POLL_INTERVAL() {
9
9
  return parseInt(process.env.POLL_INTERVAL ?? "1000", 10);
10
10
  }
11
+ get DEPLOY_INIT_ERROR_FAIL_FAST() {
12
+ return process.env.DEPLOY_INIT_ERROR_FAIL_FAST !== "false";
13
+ }
11
14
  get MAX_PROVISIONING_RETRIES() {
12
15
  return parseInt(process.env.MAX_PROVISIONING_RETRIES ?? "60", 10);
13
16
  }
@@ -1 +1 @@
1
- {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/common/settings.ts"],"names":[],"mappings":"AAGA,MAAM,QAAQ;IACZ,IAAI,4BAA4B;QAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,uBAAuB,CAAC;IAC7E,CAAC;IAED,IAAI,gBAAgB;QAClB,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,aAAa;QACf,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,wBAAwB;QAC1B,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,0BAA0B;QAC5B,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,YAAY;QACd,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,gBAAgB,CAAC;IACtD,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,kCAAkC,CAAC;IAC3E,CAAC;CACF;AAED,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;AAChC,eAAe,QAAQ,CAAC","sourcesContent":["/** biome-ignore-all lint/style/useNamingConvention: Environment variables */\n/** biome-ignore-all lint/style/noProcessEnv: Environment variables */\n\nclass Settings {\n get ZUPLO_DEVELOPER_API_ENDPOINT(): string {\n return process.env.ZUPLO_DEVELOPER_API_ENDPOINT ?? \"https://dev.zuplo.com\";\n }\n\n get MAX_POLL_RETRIES(): number {\n return parseInt(process.env.MAX_POLL_RETRIES ?? \"250\", 10);\n }\n\n get POLL_INTERVAL(): number {\n return parseInt(process.env.POLL_INTERVAL ?? \"1000\", 10);\n }\n\n get MAX_PROVISIONING_RETRIES(): number {\n return parseInt(process.env.MAX_PROVISIONING_RETRIES ?? \"60\", 10);\n }\n\n get PROVISIONING_POLL_INTERVAL(): number {\n return parseInt(process.env.PROVISIONING_INTERVAL ?? \"15000\", 10);\n }\n\n get AUTH0_DOMAIN(): string {\n return process.env.AUTH0_DOMAIN ?? \"auth.zuplo.com\";\n }\n\n get AUTH0_CLIENT_ID(): string {\n return process.env.AUTH0_CLIENT_ID ?? \"mYLGcH7kB4P0pw0HAk6GH7raRwYhSlW4\";\n }\n}\n\nconst settings = new Settings();\nexport default settings;\n"]}
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/common/settings.ts"],"names":[],"mappings":"AAGA,MAAM,QAAQ;IACZ,IAAI,4BAA4B;QAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,4BAA4B,IAAI,uBAAuB,CAAC;IAC7E,CAAC;IAED,IAAI,gBAAgB;QAClB,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7D,CAAC;IAED,IAAI,aAAa;QACf,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,MAAM,EAAE,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,2BAA2B;QAC7B,OAAO,OAAO,CAAC,GAAG,CAAC,2BAA2B,KAAK,OAAO,CAAC;IAC7D,CAAC;IAED,IAAI,wBAAwB;QAC1B,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,0BAA0B;QAC5B,OAAO,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,IAAI,YAAY;QACd,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,gBAAgB,CAAC;IACtD,CAAC;IAED,IAAI,eAAe;QACjB,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,kCAAkC,CAAC;IAC3E,CAAC;CACF;AAED,MAAM,QAAQ,GAAG,IAAI,QAAQ,EAAE,CAAC;AAChC,eAAe,QAAQ,CAAC","sourcesContent":["/** biome-ignore-all lint/style/useNamingConvention: Environment variables */\n/** biome-ignore-all lint/style/noProcessEnv: Environment variables */\n\nclass Settings {\n get ZUPLO_DEVELOPER_API_ENDPOINT(): string {\n return process.env.ZUPLO_DEVELOPER_API_ENDPOINT ?? \"https://dev.zuplo.com\";\n }\n\n get MAX_POLL_RETRIES(): number {\n return parseInt(process.env.MAX_POLL_RETRIES ?? \"250\", 10);\n }\n\n get POLL_INTERVAL(): number {\n return parseInt(process.env.POLL_INTERVAL ?? \"1000\", 10);\n }\n\n get DEPLOY_INIT_ERROR_FAIL_FAST(): boolean {\n return process.env.DEPLOY_INIT_ERROR_FAIL_FAST !== \"false\";\n }\n\n get MAX_PROVISIONING_RETRIES(): number {\n return parseInt(process.env.MAX_PROVISIONING_RETRIES ?? \"60\", 10);\n }\n\n get PROVISIONING_POLL_INTERVAL(): number {\n return parseInt(process.env.PROVISIONING_INTERVAL ?? \"15000\", 10);\n }\n\n get AUTH0_DOMAIN(): string {\n return process.env.AUTH0_DOMAIN ?? \"auth.zuplo.com\";\n }\n\n get AUTH0_CLIENT_ID(): string {\n return process.env.AUTH0_CLIENT_ID ?? \"mYLGcH7kB4P0pw0HAk6GH7raRwYhSlW4\";\n }\n}\n\nconst settings = new Settings();\nexport default settings;\n"]}
@@ -0,0 +1,14 @@
1
+ export type BuildProbeObservation =
2
+ | {
3
+ type: "init-error";
4
+ buildId: string | undefined;
5
+ detail: string;
6
+ }
7
+ | {
8
+ type: "inconclusive";
9
+ };
10
+ export declare function classifyBuildProbeResponse(
11
+ status: number,
12
+ rawBody: string | undefined
13
+ ): BuildProbeObservation;
14
+ //# sourceMappingURL=classify-build-probe.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify-build-probe.d.ts","sourceRoot":"","sources":["../../src/deploy/classify-build-probe.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACnE;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,CAAC;AAc7B,wBAAgB,0BAA0B,CACxC,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,qBAAqB,CAgCvB"}
@@ -0,0 +1,35 @@
1
+ const GATEWAY_INITIALIZATION_ERROR_TITLE = "Gateway Initialization Error";
2
+ const FALLBACK_INIT_ERROR_DETAIL = "The gateway failed to initialize after deployment. Check the deployment logs for details.";
3
+ function isRecord(value) {
4
+ return typeof value === "object" && value !== null && !Array.isArray(value);
5
+ }
6
+ export function classifyBuildProbeResponse(status, rawBody) {
7
+ if (!rawBody) {
8
+ return { type: "inconclusive" };
9
+ }
10
+ let body;
11
+ try {
12
+ const parsed = JSON.parse(rawBody);
13
+ if (!isRecord(parsed)) {
14
+ return { type: "inconclusive" };
15
+ }
16
+ body = parsed;
17
+ }
18
+ catch {
19
+ return { type: "inconclusive" };
20
+ }
21
+ if (status !== 500 || body.title !== GATEWAY_INITIALIZATION_ERROR_TITLE) {
22
+ return { type: "inconclusive" };
23
+ }
24
+ const tracedBuildId = isRecord(body.trace) ? body.trace.buildId : undefined;
25
+ return {
26
+ type: "init-error",
27
+ buildId: typeof tracedBuildId === "string" && tracedBuildId.length > 0
28
+ ? tracedBuildId
29
+ : undefined,
30
+ detail: typeof body.detail === "string" && body.detail.length > 0
31
+ ? body.detail
32
+ : FALLBACK_INIT_ERROR_DETAIL,
33
+ };
34
+ }
35
+ //# sourceMappingURL=classify-build-probe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify-build-probe.js","sourceRoot":"","sources":["../../src/deploy/classify-build-probe.ts"],"names":[],"mappings":"AAAA,MAAM,kCAAkC,GAAG,8BAA8B,CAAC;AAC1E,MAAM,0BAA0B,GAC9B,2FAA2F,CAAC;AAM9F,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAUD,MAAM,UAAU,0BAA0B,CACxC,MAAc,EACd,OAA2B;IAE3B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAClC,CAAC;IAED,IAAI,IAA6B,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;QAClC,CAAC;QACD,IAAI,GAAG,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAClC,CAAC;IAED,IAAI,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,KAAK,KAAK,kCAAkC,EAAE,CAAC;QACxE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC;IAClC,CAAC;IAED,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAC5E,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,OAAO,EACL,OAAO,aAAa,KAAK,QAAQ,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC;YAC3D,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,SAAS;QACf,MAAM,EACJ,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;YACvD,CAAC,CAAC,IAAI,CAAC,MAAM;YACb,CAAC,CAAC,0BAA0B;KACjC,CAAC;AACJ,CAAC","sourcesContent":["const GATEWAY_INITIALIZATION_ERROR_TITLE = \"Gateway Initialization Error\";\nconst FALLBACK_INIT_ERROR_DETAIL =\n \"The gateway failed to initialize after deployment. Check the deployment logs for details.\";\n\nexport type BuildProbeObservation =\n | { type: \"init-error\"; buildId: string | undefined; detail: string }\n | { type: \"inconclusive\" };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/**\n * Classifies the CLI's post-deployment build probe response.\n *\n * Keep this aligned with the problem produced by `handleError` in\n * `packages/runtime/src/handler.ts` and with `classifyPingResponse` in\n * `packages/deploy/src/api/tasks/poll-deployment-liveness.ts`. The CLI cannot\n * import the deployer copy because it does not depend on `@zuplo/deploy`.\n */\nexport function classifyBuildProbeResponse(\n status: number,\n rawBody: string | undefined\n): BuildProbeObservation {\n if (!rawBody) {\n return { type: \"inconclusive\" };\n }\n\n let body: Record<string, unknown>;\n try {\n const parsed: unknown = JSON.parse(rawBody);\n if (!isRecord(parsed)) {\n return { type: \"inconclusive\" };\n }\n body = parsed;\n } catch {\n return { type: \"inconclusive\" };\n }\n\n if (status !== 500 || body.title !== GATEWAY_INITIALIZATION_ERROR_TITLE) {\n return { type: \"inconclusive\" };\n }\n\n const tracedBuildId = isRecord(body.trace) ? body.trace.buildId : undefined;\n return {\n type: \"init-error\",\n buildId:\n typeof tracedBuildId === \"string\" && tracedBuildId.length > 0\n ? tracedBuildId\n : undefined,\n detail:\n typeof body.detail === \"string\" && body.detail.length > 0\n ? body.detail\n : FALLBACK_INIT_ERROR_DETAIL,\n };\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=classify-build-probe.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify-build-probe.test.d.ts","sourceRoot":"","sources":["../../src/deploy/classify-build-probe.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,51 @@
1
+ import { describe, it } from "node:test";
2
+ import { assert } from "chai";
3
+ import { classifyBuildProbeResponse } from "./classify-build-probe.js";
4
+ const FALLBACK_INIT_ERROR_DETAIL = "The gateway failed to initialize after deployment. Check the deployment logs for details.";
5
+ function initErrorBody(overrides = {}) {
6
+ return JSON.stringify({
7
+ status: 500,
8
+ title: "Gateway Initialization Error",
9
+ type: "https://httpproblems.com/http-status/500",
10
+ detail: "Value of 'allowedOrigins' on custom cors policies is invalid.",
11
+ trace: { timestamp: 0, buildId: "build-new" },
12
+ ...overrides,
13
+ });
14
+ }
15
+ describe("classifyBuildProbeResponse", () => {
16
+ it("classifies a gateway initialization error with its build ID and detail", () => {
17
+ assert.deepEqual(classifyBuildProbeResponse(500, initErrorBody()), {
18
+ type: "init-error",
19
+ buildId: "build-new",
20
+ detail: "Value of 'allowedOrigins' on custom cors policies is invalid.",
21
+ });
22
+ });
23
+ it("uses a fallback detail when the problem omits detail", () => {
24
+ assert.deepEqual(classifyBuildProbeResponse(500, initErrorBody({ detail: undefined })), {
25
+ type: "init-error",
26
+ buildId: "build-new",
27
+ detail: FALLBACK_INIT_ERROR_DETAIL,
28
+ });
29
+ });
30
+ it("returns an undefined build ID when the trace omits it", () => {
31
+ assert.deepEqual(classifyBuildProbeResponse(500, initErrorBody({ trace: {} })), {
32
+ type: "init-error",
33
+ buildId: undefined,
34
+ detail: "Value of 'allowedOrigins' on custom cors policies is invalid.",
35
+ });
36
+ });
37
+ for (const [name, status, body] of [
38
+ ["a different error title", 500, initErrorBody({ title: "Other Error" })],
39
+ ["a non-500 response", 503, initErrorBody()],
40
+ ["non-JSON HTML", 500, "<html>unavailable</html>"],
41
+ ["an empty body", 500, ""],
42
+ ["an undefined body", 500, undefined],
43
+ ]) {
44
+ it(`treats ${name} as inconclusive`, () => {
45
+ assert.deepEqual(classifyBuildProbeResponse(status, body), {
46
+ type: "inconclusive",
47
+ });
48
+ });
49
+ }
50
+ });
51
+ //# sourceMappingURL=classify-build-probe.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classify-build-probe.test.js","sourceRoot":"","sources":["../../src/deploy/classify-build-probe.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAC9B,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AAEvE,MAAM,0BAA0B,GAC9B,2FAA2F,CAAC;AAE9F,SAAS,aAAa,CAAC,YAAqC,EAAE;IAC5D,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,MAAM,EAAE,GAAG;QACX,KAAK,EAAE,8BAA8B;QACrC,IAAI,EAAE,0CAA0C;QAChD,MAAM,EAAE,+DAA+D;QACvE,KAAK,EAAE,EAAE,SAAS,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,EAAE;QAC7C,GAAG,SAAS;KACb,CAAC,CAAC;AACL,CAAC;AAED,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,CAAC,SAAS,CAAC,0BAA0B,CAAC,GAAG,EAAE,aAAa,EAAE,CAAC,EAAE;YACjE,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,WAAW;YACpB,MAAM,EAAE,+DAA+D;SACxE,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;QAC9D,MAAM,CAAC,SAAS,CACd,0BAA0B,CAAC,GAAG,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,EACrE;YACE,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,WAAW;YACpB,MAAM,EAAE,0BAA0B;SACnC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uDAAuD,EAAE,GAAG,EAAE;QAC/D,MAAM,CAAC,SAAS,CACd,0BAA0B,CAAC,GAAG,EAAE,aAAa,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,EAC7D;YACE,IAAI,EAAE,YAAY;YAClB,OAAO,EAAE,SAAS;YAClB,MAAM,EAAE,+DAA+D;SACxE,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,KAAK,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI;QACjC,CAAC,yBAAyB,EAAE,GAAG,EAAE,aAAa,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,CAAC,CAAC;QACzE,CAAC,oBAAoB,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC;QAC5C,CAAC,eAAe,EAAE,GAAG,EAAE,0BAA0B,CAAC;QAClD,CAAC,eAAe,EAAE,GAAG,EAAE,EAAE,CAAC;QAC1B,CAAC,mBAAmB,EAAE,GAAG,EAAE,SAAS,CAAC;KAC7B,EAAE,CAAC;QACX,EAAE,CAAC,UAAU,IAAI,kBAAkB,EAAE,GAAG,EAAE;YACxC,MAAM,CAAC,SAAS,CAAC,0BAA0B,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE;gBACzD,IAAI,EAAE,cAAc;aACrB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC","sourcesContent":["import { describe, it } from \"node:test\";\nimport { assert } from \"chai\";\nimport { classifyBuildProbeResponse } from \"./classify-build-probe.js\";\n\nconst FALLBACK_INIT_ERROR_DETAIL =\n \"The gateway failed to initialize after deployment. Check the deployment logs for details.\";\n\nfunction initErrorBody(overrides: Record<string, unknown> = {}): string {\n return JSON.stringify({\n status: 500,\n title: \"Gateway Initialization Error\",\n type: \"https://httpproblems.com/http-status/500\",\n detail: \"Value of 'allowedOrigins' on custom cors policies is invalid.\",\n trace: { timestamp: 0, buildId: \"build-new\" },\n ...overrides,\n });\n}\n\ndescribe(\"classifyBuildProbeResponse\", () => {\n it(\"classifies a gateway initialization error with its build ID and detail\", () => {\n assert.deepEqual(classifyBuildProbeResponse(500, initErrorBody()), {\n type: \"init-error\",\n buildId: \"build-new\",\n detail: \"Value of 'allowedOrigins' on custom cors policies is invalid.\",\n });\n });\n\n it(\"uses a fallback detail when the problem omits detail\", () => {\n assert.deepEqual(\n classifyBuildProbeResponse(500, initErrorBody({ detail: undefined })),\n {\n type: \"init-error\",\n buildId: \"build-new\",\n detail: FALLBACK_INIT_ERROR_DETAIL,\n }\n );\n });\n\n it(\"returns an undefined build ID when the trace omits it\", () => {\n assert.deepEqual(\n classifyBuildProbeResponse(500, initErrorBody({ trace: {} })),\n {\n type: \"init-error\",\n buildId: undefined,\n detail: \"Value of 'allowedOrigins' on custom cors policies is invalid.\",\n }\n );\n });\n\n for (const [name, status, body] of [\n [\"a different error title\", 500, initErrorBody({ title: \"Other Error\" })],\n [\"a non-500 response\", 503, initErrorBody()],\n [\"non-JSON HTML\", 500, \"<html>unavailable</html>\"],\n [\"an empty body\", 500, \"\"],\n [\"an undefined body\", 500, undefined],\n ] as const) {\n it(`treats ${name} as inconclusive`, () => {\n assert.deepEqual(classifyBuildProbeResponse(status, body), {\n type: \"inconclusive\",\n });\n });\n }\n});\n"]}
@@ -83,13 +83,28 @@ async function deployToSaas(argv) {
83
83
  logger.debug(`Upload response: ${uploadResponse.status}`);
84
84
  if (uploadResponse.ok) {
85
85
  const spinner = printSpinnerToConsole(`Deploying the '${payload.environment}' environment to '${project}' on account '${account}'...`);
86
- const { url, logUrl } = await pollDeployment(argv, deploymentId, account, project, spinner);
87
- if (url) {
88
- await printResultToConsoleAndExitGracefully(`Deployed to ${url}`, spinner);
86
+ const result = await pollDeployment(argv, deploymentId, account, project, spinner);
87
+ if (result.url) {
88
+ await printResultToConsoleAndExitGracefully(`Deployed to ${result.url}`, spinner);
89
+ }
90
+ else if (result.initError) {
91
+ await printCriticalFailureToConsoleAndExit([
92
+ `The environment ${archiveMetadata.metadata.branch} in project ${project} was deployed, but the gateway failed to initialize:`,
93
+ "",
94
+ ` ${result.initError.detail}`,
95
+ "",
96
+ "Every request to this environment will return a 500 error until a fixed version is deployed.",
97
+ `Deployment ID: ${deploymentId}`,
98
+ `Build logs: ${result.logUrl}`,
99
+ ].join("\n"), spinner);
89
100
  }
90
101
  else {
91
- await printCriticalFailureToConsoleAndExit(`Failed to deploy the environment ${archiveMetadata.metadata.branch} to project ${project} on account ${account}.\nDeployment ID: ${deploymentId}\nFor more information, check the deployment logs in the Zuplo dashboard.\n
92
- ${logUrl}`, spinner);
102
+ await printCriticalFailureToConsoleAndExit([
103
+ `Failed to deploy the environment ${archiveMetadata.metadata.branch} to project ${project} on account ${account}.`,
104
+ `Deployment ID: ${deploymentId}`,
105
+ "For more information, check the deployment logs in the Zuplo dashboard.",
106
+ result.logUrl,
107
+ ].join("\n"), spinner);
93
108
  }
94
109
  }
95
110
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/deploy/handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { MtlsCertificate } from \"@zuplo/types\";\nimport { ZUPLO_SYSTEM_ENV_VAR } from \"../common/constants.js\";\nimport { logger } from \"../common/logger.js\";\nimport {\n printCriticalFailureToConsoleAndExit,\n printDiagnosticsToConsole,\n printResultToConsoleAndExitGracefully,\n printSpinnerToConsole,\n printWarningToConsole,\n} from \"../common/output.js\";\nimport { pullSystemConfig } from \"../common/populate.js\";\nimport settings from \"../common/settings.js\";\nimport { getPrettyBranch } from \"../common/utils/branch.js\";\nimport { RequiredProperties } from \"../common/utils/types.js\";\nimport { normalizeUrl } from \"../common/utils/urls.js\";\nimport { archive, generateMetadata } from \"./archive.js\";\nimport {\n retrieveOrCreateEnvironment,\n UnableToAutoLinkToExistingProject,\n} from \"./environments.js\";\nimport { upload } from \"./file-upload.js\";\nimport { pollBuild, pollDeployment } from \"./poll-deployment.js\";\n\nexport interface Arguments {\n account: string;\n project: string;\n dir: string;\n environment?: string;\n authToken: string;\n \"verify-remote\"?: boolean;\n \"self-hosted-endpoint\"?: string;\n \"override-repo-url\"?: string;\n \"mtls-certificates-dir\"?: string;\n}\n\nexport type SelfHostedArgs = RequiredProperties<\n Arguments,\n \"self-hosted-endpoint\"\n>;\n\nexport async function deploy(argv: Arguments) {\n if (argv[\"self-hosted-endpoint\"]) {\n const args = argv as SelfHostedArgs;\n await deployToSelfHosted(args);\n } else {\n await deployToSaas(argv);\n }\n}\n\nfunction addMtlsCertificatesToFormData(\n formData: FormData,\n mtlsCertificatesDir: string\n) {\n const resolvedMtlsCertificatesDir = resolve(\n process.cwd(),\n mtlsCertificatesDir\n );\n\n let certNames: string[];\n try {\n certNames = readdirSync(resolvedMtlsCertificatesDir, {\n withFileTypes: true,\n })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort((a, b) => a.localeCompare(b));\n } catch (cause) {\n throw new Error(\n `Unable to read mTLS certificates directory '${mtlsCertificatesDir}'`,\n { cause }\n );\n }\n\n const certificates: MtlsCertificate[] = certNames.map((certName) => {\n const certificateDirectory = join(resolvedMtlsCertificatesDir, certName);\n const certPath = join(certificateDirectory, \"tls.crt\");\n const keyPath = join(certificateDirectory, \"tls.key\");\n\n try {\n return {\n certName,\n mtlsCrt: readFileSync(certPath, \"utf-8\"),\n mtlsKey: readFileSync(keyPath, \"utf-8\"),\n };\n } catch (cause) {\n throw new Error(\n `Unable to read mTLS certificate files for '${certName}' in '${mtlsCertificatesDir}'`,\n { cause }\n );\n }\n });\n\n for (const certificate of certificates) {\n formData.append(\"mtlsCertificate\", JSON.stringify(certificate));\n }\n}\n\nasync function deployToSaas(argv: Arguments) {\n // 1. Create the tarball locally\n const archiveMetadata = await archive(argv);\n logger.debug(`Tarball created locally at ${archiveMetadata.tarball}`);\n\n // 2. Build uploadUrl request\n const { account, project } = argv;\n\n const payload = {\n accountName: account,\n projectName: project,\n environment: archiveMetadata.metadata.branch,\n repositoryUrl: archiveMetadata.metadata.repoUrl,\n sha: archiveMetadata.metadata.sha,\n };\n\n const uploadUrlResponse = await fetch(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/deployments/source-url`,\n {\n method: \"POST\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n body: JSON.stringify(payload),\n }\n );\n\n logger.debug(`Upload URL response: ${uploadUrlResponse.status}`);\n\n if (uploadUrlResponse.ok) {\n // 3. Upload to request URL\n const { uploadUrl, deploymentId } = await uploadUrlResponse.json();\n\n const uploadResponse = await upload({\n tarballPath: archiveMetadata.tarball,\n uploadUrl,\n deploymentId,\n ...payload,\n });\n\n logger.debug(`Upload response: ${uploadResponse.status}`);\n\n if (uploadResponse.ok) {\n const spinner = printSpinnerToConsole(\n `Deploying the '${payload.environment}' environment to '${project}' on account '${account}'...`\n );\n\n const { url, logUrl } = await pollDeployment(\n argv,\n deploymentId,\n account,\n project,\n spinner\n );\n if (url) {\n await printResultToConsoleAndExitGracefully(\n `Deployed to ${url}`,\n spinner\n );\n } else {\n await printCriticalFailureToConsoleAndExit(\n `Failed to deploy the environment ${\n archiveMetadata.metadata.branch\n } to project ${project} on account ${account}.\\nDeployment ID: ${deploymentId}\\nFor more information, check the deployment logs in the Zuplo dashboard.\\n\n ${logUrl}`,\n spinner\n );\n }\n } else {\n logger.error(\n {\n status: uploadResponse.status,\n statusText: uploadResponse.statusText,\n },\n \"Failed to upload source to cloud storage\"\n );\n printDiagnosticsToConsole(\n \"Error: Failed to upload deployment source. Please try again.\"\n );\n }\n } else {\n logger.error(\n {\n status: uploadUrlResponse.status,\n statusText: uploadUrlResponse.statusText,\n },\n \"Failed to retrieve uploadUrl\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to upload deployment source. Please try again.\"\n );\n }\n}\n\nasync function deployToSelfHosted(argv: SelfHostedArgs) {\n const { account, project } = argv;\n\n let existingZuploEnv: string | undefined;\n\n try {\n // 0. Finagle the URL first\n const endpoint = normalizeUrl(argv[\"self-hosted-endpoint\"]);\n\n // 1. Perform the link on-behalf-of-the-user\n // Store the current .env.zuplo if there is one and restore it later\n existingZuploEnv = retrieveExistingZuploEnv(argv);\n const branch = (await generateMetadata(argv)).branch;\n try {\n const environmentToAutoLink = await retrieveOrCreateEnvironment(\n branch,\n argv\n );\n await pullSystemConfig({\n dir: argv.dir,\n environment: environmentToAutoLink.name,\n authToken: argv.authToken,\n });\n } catch (error) {\n if (\n error instanceof UnableToAutoLinkToExistingProject &&\n // biome-ignore lint/style/noProcessEnv: Migrated from ESLint\n process.env.ZUPLO_ALLOW_DEPLOY_WITH_EMPTY_VARS\n ) {\n printWarningToConsole(\n `We are unable to fetch the environment variables from Zuplo for this project. \nDeployment will proceed because ZUPLO_ALLOW_DEPLOY_WITH_EMPTY_VARS is set. \nHowever, the environment variables will not be available. \nTo fix this, check that the project, ${argv.project} exists and this api-key has access to it.`\n );\n } else if (error instanceof UnableToAutoLinkToExistingProject) {\n await printCriticalFailureToConsoleAndExit(\n `We are unable to fetch the environment variables from Zuplo for this project. \nTo fix this, check that the project, ${argv.project} exists and this api-key has access to it.\nIf you want to force deployment without the environment variables, set ZUPLO_ALLOW_DEPLOY_WITH_EMPTY_VARS to true.`\n );\n } else {\n logger.error(error, \"Failed to fetch environment variables\");\n await printCriticalFailureToConsoleAndExit(\n `We are unable to fetch the environment variables from Zuplo for this project. \nTo fix this, check that the project, ${argv.project} exists and this api-key has access to it.`\n );\n }\n }\n\n // 2. Create the tarball locally\n const archiveMetadata = await archive(argv);\n logger.debug(`Tarball created locally at ${archiveMetadata.tarball}`);\n\n // 3. Build uploadUrl request\n const form = new FormData();\n const deploymentName = `${project}-${getPrettyBranch(\n archiveMetadata.metadata.branch\n )}`;\n form.set(\n \"file\",\n new Blob([readFileSync(archiveMetadata.tarball)], {\n type: \"application/gzip\",\n })\n );\n form.set(\"projectName\", project);\n form.set(\"deploymentName\", deploymentName);\n if (argv[\"mtls-certificates-dir\"]) {\n logger.debug(\n `Adding mTLS certificates from ${argv[\"mtls-certificates-dir\"]} to deployment`\n );\n addMtlsCertificatesToFormData(form, argv[\"mtls-certificates-dir\"]);\n }\n\n const uploadUrlResponse = await fetch(`${endpoint}/v1/deployments/build`, {\n method: \"POST\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n body: form,\n });\n\n logger.debug(`Upload URL response: ${uploadUrlResponse.status}`);\n\n if (uploadUrlResponse.ok) {\n const spinner = printSpinnerToConsole(\n `Deploying the current branch ${archiveMetadata.metadata.branch} to project ${project} on account ${account}...`\n );\n\n // 4. Poll for build\n const { buildName } = await uploadUrlResponse.json();\n\n logger.debug(`Deployment started for ${buildName}`);\n\n const buildResult = await pollBuild(argv, endpoint, buildName, spinner);\n\n if (buildResult.conditionType === \"Complete\") {\n // Retrieve the deployment\n const deploymentResponse = await fetch(\n `${endpoint}/v1/deployments/${deploymentName}`,\n {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n const deploymentJSON: { deploymentUrl: string } =\n await deploymentResponse.json();\n restoreExistingZuploEnvAsNecessary(argv, existingZuploEnv);\n\n await printResultToConsoleAndExitGracefully(\n `Deployed to ${deploymentJSON.deploymentUrl}`,\n spinner\n );\n } else {\n restoreExistingZuploEnvAsNecessary(argv, existingZuploEnv);\n await printCriticalFailureToConsoleAndExit(\n `Failed to deploy the current environment. Here's the diagnostics: ${JSON.stringify(\n buildResult\n )}`,\n spinner\n );\n }\n } else {\n logger.error(\n { response: await uploadUrlResponse.text() },\n \"Failed to upload to self-hosted build endpoint\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to upload to self-hosted build endpoint\"\n );\n }\n } catch (error) {\n logger.error(error);\n } finally {\n restoreExistingZuploEnvAsNecessary(argv, existingZuploEnv);\n }\n}\n\nfunction retrieveExistingZuploEnv(argv: SelfHostedArgs): string | undefined {\n const normalizedDir = resolve(argv.dir);\n const envFilePath = join(normalizedDir, ZUPLO_SYSTEM_ENV_VAR);\n if (existsSync(envFilePath)) {\n const envFileContent = readFileSync(envFilePath, \"utf-8\");\n return envFileContent;\n } else {\n return undefined;\n }\n}\n\nfunction restoreExistingZuploEnvAsNecessary(\n argv: SelfHostedArgs,\n originalValue: string | undefined\n) {\n if (originalValue) {\n const normalizedDir = resolve(argv.dir);\n const envFilePath = join(normalizedDir, ZUPLO_SYSTEM_ENV_VAR);\n writeFileSync(envFilePath, originalValue);\n }\n}\n"]}
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/deploy/handler.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, readdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join, resolve } from \"node:path\";\nimport { MtlsCertificate } from \"@zuplo/types\";\nimport { ZUPLO_SYSTEM_ENV_VAR } from \"../common/constants.js\";\nimport { logger } from \"../common/logger.js\";\nimport {\n printCriticalFailureToConsoleAndExit,\n printDiagnosticsToConsole,\n printResultToConsoleAndExitGracefully,\n printSpinnerToConsole,\n printWarningToConsole,\n} from \"../common/output.js\";\nimport { pullSystemConfig } from \"../common/populate.js\";\nimport settings from \"../common/settings.js\";\nimport { getPrettyBranch } from \"../common/utils/branch.js\";\nimport { RequiredProperties } from \"../common/utils/types.js\";\nimport { normalizeUrl } from \"../common/utils/urls.js\";\nimport { archive, generateMetadata } from \"./archive.js\";\nimport {\n retrieveOrCreateEnvironment,\n UnableToAutoLinkToExistingProject,\n} from \"./environments.js\";\nimport { upload } from \"./file-upload.js\";\nimport { pollBuild, pollDeployment } from \"./poll-deployment.js\";\n\nexport interface Arguments {\n account: string;\n project: string;\n dir: string;\n environment?: string;\n authToken: string;\n \"verify-remote\"?: boolean;\n \"self-hosted-endpoint\"?: string;\n \"override-repo-url\"?: string;\n \"mtls-certificates-dir\"?: string;\n}\n\nexport type SelfHostedArgs = RequiredProperties<\n Arguments,\n \"self-hosted-endpoint\"\n>;\n\nexport async function deploy(argv: Arguments) {\n if (argv[\"self-hosted-endpoint\"]) {\n const args = argv as SelfHostedArgs;\n await deployToSelfHosted(args);\n } else {\n await deployToSaas(argv);\n }\n}\n\nfunction addMtlsCertificatesToFormData(\n formData: FormData,\n mtlsCertificatesDir: string\n) {\n const resolvedMtlsCertificatesDir = resolve(\n process.cwd(),\n mtlsCertificatesDir\n );\n\n let certNames: string[];\n try {\n certNames = readdirSync(resolvedMtlsCertificatesDir, {\n withFileTypes: true,\n })\n .filter((entry) => entry.isDirectory())\n .map((entry) => entry.name)\n .sort((a, b) => a.localeCompare(b));\n } catch (cause) {\n throw new Error(\n `Unable to read mTLS certificates directory '${mtlsCertificatesDir}'`,\n { cause }\n );\n }\n\n const certificates: MtlsCertificate[] = certNames.map((certName) => {\n const certificateDirectory = join(resolvedMtlsCertificatesDir, certName);\n const certPath = join(certificateDirectory, \"tls.crt\");\n const keyPath = join(certificateDirectory, \"tls.key\");\n\n try {\n return {\n certName,\n mtlsCrt: readFileSync(certPath, \"utf-8\"),\n mtlsKey: readFileSync(keyPath, \"utf-8\"),\n };\n } catch (cause) {\n throw new Error(\n `Unable to read mTLS certificate files for '${certName}' in '${mtlsCertificatesDir}'`,\n { cause }\n );\n }\n });\n\n for (const certificate of certificates) {\n formData.append(\"mtlsCertificate\", JSON.stringify(certificate));\n }\n}\n\nasync function deployToSaas(argv: Arguments) {\n // 1. Create the tarball locally\n const archiveMetadata = await archive(argv);\n logger.debug(`Tarball created locally at ${archiveMetadata.tarball}`);\n\n // 2. Build uploadUrl request\n const { account, project } = argv;\n\n const payload = {\n accountName: account,\n projectName: project,\n environment: archiveMetadata.metadata.branch,\n repositoryUrl: archiveMetadata.metadata.repoUrl,\n sha: archiveMetadata.metadata.sha,\n };\n\n const uploadUrlResponse = await fetch(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/deployments/source-url`,\n {\n method: \"POST\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n body: JSON.stringify(payload),\n }\n );\n\n logger.debug(`Upload URL response: ${uploadUrlResponse.status}`);\n\n if (uploadUrlResponse.ok) {\n // 3. Upload to request URL\n const { uploadUrl, deploymentId } = await uploadUrlResponse.json();\n\n const uploadResponse = await upload({\n tarballPath: archiveMetadata.tarball,\n uploadUrl,\n deploymentId,\n ...payload,\n });\n\n logger.debug(`Upload response: ${uploadResponse.status}`);\n\n if (uploadResponse.ok) {\n const spinner = printSpinnerToConsole(\n `Deploying the '${payload.environment}' environment to '${project}' on account '${account}'...`\n );\n\n const result = await pollDeployment(\n argv,\n deploymentId,\n account,\n project,\n spinner\n );\n if (result.url) {\n await printResultToConsoleAndExitGracefully(\n `Deployed to ${result.url}`,\n spinner\n );\n } else if (result.initError) {\n await printCriticalFailureToConsoleAndExit(\n [\n `The environment ${archiveMetadata.metadata.branch} in project ${project} was deployed, but the gateway failed to initialize:`,\n \"\",\n ` ${result.initError.detail}`,\n \"\",\n \"Every request to this environment will return a 500 error until a fixed version is deployed.\",\n `Deployment ID: ${deploymentId}`,\n `Build logs: ${result.logUrl}`,\n ].join(\"\\n\"),\n spinner\n );\n } else {\n await printCriticalFailureToConsoleAndExit(\n [\n `Failed to deploy the environment ${archiveMetadata.metadata.branch} to project ${project} on account ${account}.`,\n `Deployment ID: ${deploymentId}`,\n \"For more information, check the deployment logs in the Zuplo dashboard.\",\n result.logUrl,\n ].join(\"\\n\"),\n spinner\n );\n }\n } else {\n logger.error(\n {\n status: uploadResponse.status,\n statusText: uploadResponse.statusText,\n },\n \"Failed to upload source to cloud storage\"\n );\n printDiagnosticsToConsole(\n \"Error: Failed to upload deployment source. Please try again.\"\n );\n }\n } else {\n logger.error(\n {\n status: uploadUrlResponse.status,\n statusText: uploadUrlResponse.statusText,\n },\n \"Failed to retrieve uploadUrl\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to upload deployment source. Please try again.\"\n );\n }\n}\n\nasync function deployToSelfHosted(argv: SelfHostedArgs) {\n const { account, project } = argv;\n\n let existingZuploEnv: string | undefined;\n\n try {\n // 0. Finagle the URL first\n const endpoint = normalizeUrl(argv[\"self-hosted-endpoint\"]);\n\n // 1. Perform the link on-behalf-of-the-user\n // Store the current .env.zuplo if there is one and restore it later\n existingZuploEnv = retrieveExistingZuploEnv(argv);\n const branch = (await generateMetadata(argv)).branch;\n try {\n const environmentToAutoLink = await retrieveOrCreateEnvironment(\n branch,\n argv\n );\n await pullSystemConfig({\n dir: argv.dir,\n environment: environmentToAutoLink.name,\n authToken: argv.authToken,\n });\n } catch (error) {\n if (\n error instanceof UnableToAutoLinkToExistingProject &&\n // biome-ignore lint/style/noProcessEnv: Migrated from ESLint\n process.env.ZUPLO_ALLOW_DEPLOY_WITH_EMPTY_VARS\n ) {\n printWarningToConsole(\n `We are unable to fetch the environment variables from Zuplo for this project. \nDeployment will proceed because ZUPLO_ALLOW_DEPLOY_WITH_EMPTY_VARS is set. \nHowever, the environment variables will not be available. \nTo fix this, check that the project, ${argv.project} exists and this api-key has access to it.`\n );\n } else if (error instanceof UnableToAutoLinkToExistingProject) {\n await printCriticalFailureToConsoleAndExit(\n `We are unable to fetch the environment variables from Zuplo for this project. \nTo fix this, check that the project, ${argv.project} exists and this api-key has access to it.\nIf you want to force deployment without the environment variables, set ZUPLO_ALLOW_DEPLOY_WITH_EMPTY_VARS to true.`\n );\n } else {\n logger.error(error, \"Failed to fetch environment variables\");\n await printCriticalFailureToConsoleAndExit(\n `We are unable to fetch the environment variables from Zuplo for this project. \nTo fix this, check that the project, ${argv.project} exists and this api-key has access to it.`\n );\n }\n }\n\n // 2. Create the tarball locally\n const archiveMetadata = await archive(argv);\n logger.debug(`Tarball created locally at ${archiveMetadata.tarball}`);\n\n // 3. Build uploadUrl request\n const form = new FormData();\n const deploymentName = `${project}-${getPrettyBranch(\n archiveMetadata.metadata.branch\n )}`;\n form.set(\n \"file\",\n new Blob([readFileSync(archiveMetadata.tarball)], {\n type: \"application/gzip\",\n })\n );\n form.set(\"projectName\", project);\n form.set(\"deploymentName\", deploymentName);\n if (argv[\"mtls-certificates-dir\"]) {\n logger.debug(\n `Adding mTLS certificates from ${argv[\"mtls-certificates-dir\"]} to deployment`\n );\n addMtlsCertificatesToFormData(form, argv[\"mtls-certificates-dir\"]);\n }\n\n const uploadUrlResponse = await fetch(`${endpoint}/v1/deployments/build`, {\n method: \"POST\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n body: form,\n });\n\n logger.debug(`Upload URL response: ${uploadUrlResponse.status}`);\n\n if (uploadUrlResponse.ok) {\n const spinner = printSpinnerToConsole(\n `Deploying the current branch ${archiveMetadata.metadata.branch} to project ${project} on account ${account}...`\n );\n\n // 4. Poll for build\n const { buildName } = await uploadUrlResponse.json();\n\n logger.debug(`Deployment started for ${buildName}`);\n\n const buildResult = await pollBuild(argv, endpoint, buildName, spinner);\n\n if (buildResult.conditionType === \"Complete\") {\n // Retrieve the deployment\n const deploymentResponse = await fetch(\n `${endpoint}/v1/deployments/${deploymentName}`,\n {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n const deploymentJSON: { deploymentUrl: string } =\n await deploymentResponse.json();\n restoreExistingZuploEnvAsNecessary(argv, existingZuploEnv);\n\n await printResultToConsoleAndExitGracefully(\n `Deployed to ${deploymentJSON.deploymentUrl}`,\n spinner\n );\n } else {\n restoreExistingZuploEnvAsNecessary(argv, existingZuploEnv);\n await printCriticalFailureToConsoleAndExit(\n `Failed to deploy the current environment. Here's the diagnostics: ${JSON.stringify(\n buildResult\n )}`,\n spinner\n );\n }\n } else {\n logger.error(\n { response: await uploadUrlResponse.text() },\n \"Failed to upload to self-hosted build endpoint\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to upload to self-hosted build endpoint\"\n );\n }\n } catch (error) {\n logger.error(error);\n } finally {\n restoreExistingZuploEnvAsNecessary(argv, existingZuploEnv);\n }\n}\n\nfunction retrieveExistingZuploEnv(argv: SelfHostedArgs): string | undefined {\n const normalizedDir = resolve(argv.dir);\n const envFilePath = join(normalizedDir, ZUPLO_SYSTEM_ENV_VAR);\n if (existsSync(envFilePath)) {\n const envFileContent = readFileSync(envFilePath, \"utf-8\");\n return envFileContent;\n } else {\n return undefined;\n }\n}\n\nfunction restoreExistingZuploEnvAsNecessary(\n argv: SelfHostedArgs,\n originalValue: string | undefined\n) {\n if (originalValue) {\n const normalizedDir = resolve(argv.dir);\n const envFilePath = join(normalizedDir, ZUPLO_SYSTEM_ENV_VAR);\n writeFileSync(envFilePath, originalValue);\n }\n}\n"]}
@@ -1,15 +1,19 @@
1
1
  import { Ora } from "ora";
2
2
  import { Arguments } from "./handler.js";
3
+ export interface PollDeploymentResult {
4
+ url?: string;
5
+ logUrl: string;
6
+ initError?: {
7
+ detail: string;
8
+ };
9
+ }
3
10
  export declare function pollDeployment(
4
11
  argv: Arguments,
5
12
  fileId: string,
6
13
  account: string,
7
14
  project: string,
8
15
  spinner: Ora
9
- ): Promise<{
10
- url?: string;
11
- logUrl: string;
12
- }>;
16
+ ): Promise<PollDeploymentResult>;
13
17
  export declare function pollBuild(
14
18
  argv: Arguments,
15
19
  endpoint: string,
@@ -1 +1 @@
1
- {"version":3,"file":"poll-deployment.d.ts","sourceRoot":"","sources":["../../src/deploy/poll-deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAO1B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAMzC,wBAAsB,cAAc,CAClC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,GAAG,GACX,OAAO,CAAC;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAoG3C;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,GAAG,GACX,OAAO,CAAC;IACT,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC,CAsDD"}
1
+ {"version":3,"file":"poll-deployment.d.ts","sourceRoot":"","sources":["../../src/deploy/poll-deployment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAQ1B,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAMzC,MAAM,WAAW,oBAAoB;IACnC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IAEf,SAAS,CAAC,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;CAChC;AAED,wBAAsB,cAAc,CAClC,IAAI,EAAE,SAAS,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,GAAG,GACX,OAAO,CAAC,oBAAoB,CAAC,CAsI/B;AAED,wBAAsB,SAAS,CAC7B,IAAI,EAAE,SAAS,EACf,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,GAAG,GACX,OAAO,CAAC;IACT,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC,CAsDD"}
@@ -1,12 +1,16 @@
1
1
  import { logger } from "../common/logger.js";
2
2
  import { printCriticalFailureToConsoleAndExit, textOrJson, } from "../common/output.js";
3
3
  import settings from "../common/settings.js";
4
+ import { classifyBuildProbeResponse } from "./classify-build-probe.js";
4
5
  function wait(duration = settings.POLL_INTERVAL) {
5
6
  return new Promise((resolve) => setTimeout(resolve, duration));
6
7
  }
7
8
  export async function pollDeployment(argv, fileId, account, project, spinner) {
8
9
  const maxRetryAttemptsForServerUpdates = 5;
10
+ const requiredConsecutiveInitErrors = 2;
9
11
  let currentRetryAttemptsForServerUpdates = 0;
12
+ let consecutiveMatchingInitErrors = 0;
13
+ let lastLogUrl;
10
14
  for (let pollRetry = 0; pollRetry < settings.MAX_POLL_RETRIES; pollRetry++) {
11
15
  spinner.suffixText = `(${pollRetry}/${settings.MAX_POLL_RETRIES})`;
12
16
  const response = await fetch(`${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts/${account}/projects/${project}/deployment-status/${fileId}`, {
@@ -17,13 +21,17 @@ export async function pollDeployment(argv, fileId, account, project, spinner) {
17
21
  });
18
22
  if (response.ok) {
19
23
  const rawJSON = await response.text();
20
- const { status, url, logUrl } = JSON.parse(rawJSON);
24
+ const { status, url, logUrl, buildId } = JSON.parse(rawJSON);
25
+ lastLogUrl = logUrl;
21
26
  switch (status) {
22
27
  case "IN_PROGRESS":
23
28
  await wait();
24
29
  continue;
25
30
  case "SUCCESS": {
26
31
  logger.debug(`Checking if API is live: ${url}`);
32
+ if (!url) {
33
+ return { logUrl };
34
+ }
27
35
  const parsedUrl = new URL(url);
28
36
  if (parsedUrl.hostname.includes(".zuplo.app")) {
29
37
  try {
@@ -36,18 +44,38 @@ export async function pollDeployment(argv, fileId, account, project, spinner) {
36
44
  }
37
45
  else {
38
46
  logger.debug({ status: zupResponse.status }, "Error while polling deployment");
47
+ let rawBody;
39
48
  try {
40
- const result = await zupResponse.text();
41
- logger.debug(result);
49
+ rawBody = await zupResponse.text();
50
+ logger.debug(rawBody);
42
51
  }
43
52
  catch {
44
53
  }
54
+ const observation = classifyBuildProbeResponse(zupResponse.status, rawBody);
55
+ if (settings.DEPLOY_INIT_ERROR_FAIL_FAST &&
56
+ observation.type === "init-error" &&
57
+ typeof buildId === "string" &&
58
+ buildId.length > 0 &&
59
+ observation.buildId === buildId) {
60
+ consecutiveMatchingInitErrors++;
61
+ if (consecutiveMatchingInitErrors >=
62
+ requiredConsecutiveInitErrors) {
63
+ return {
64
+ logUrl,
65
+ initError: { detail: observation.detail },
66
+ };
67
+ }
68
+ }
69
+ else {
70
+ consecutiveMatchingInitErrors = 0;
71
+ }
45
72
  await wait();
46
73
  continue;
47
74
  }
48
75
  }
49
76
  catch (error) {
50
77
  logger.debug("Error while polling for zuplo build", error);
78
+ consecutiveMatchingInitErrors = 0;
51
79
  await wait();
52
80
  continue;
53
81
  }
@@ -79,7 +107,7 @@ export async function pollDeployment(argv, fileId, account, project, spinner) {
79
107
  }
80
108
  }
81
109
  return {
82
- logUrl: "https://portal.zuplo.com",
110
+ logUrl: lastLogUrl ?? "https://portal.zuplo.com",
83
111
  };
84
112
  }
85
113
  export async function pollBuild(argv, endpoint, buildName, spinner) {
@@ -1 +1 @@
1
- {"version":3,"file":"poll-deployment.js","sourceRoot":"","sources":["../../src/deploy/poll-deployment.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ora } from \"ora\";\nimport { logger } from \"../common/logger.js\";\nimport {\n printCriticalFailureToConsoleAndExit,\n textOrJson,\n} from \"../common/output.js\";\nimport settings from \"../common/settings.js\";\nimport { Arguments } from \"./handler.js\";\n\nfunction wait(duration: number = settings.POLL_INTERVAL): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, duration));\n}\n\nexport async function pollDeployment(\n argv: Arguments,\n fileId: string,\n account: string,\n project: string,\n spinner: Ora\n): Promise<{ url?: string; logUrl: string }> {\n const maxRetryAttemptsForServerUpdates = 5;\n let currentRetryAttemptsForServerUpdates = 0;\n\n for (let pollRetry = 0; pollRetry < settings.MAX_POLL_RETRIES; pollRetry++) {\n spinner.suffixText = `(${pollRetry}/${settings.MAX_POLL_RETRIES})`;\n const response = await fetch(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts/${account}/projects/${project}/deployment-status/${fileId}`,\n {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n if (response.ok) {\n const rawJSON = await response.text();\n const { status, url, logUrl } = JSON.parse(rawJSON);\n\n switch (status) {\n case \"IN_PROGRESS\":\n await wait();\n continue;\n case \"SUCCESS\": {\n // If the return URL is zuplo.app we can check to make sure the zup is\n // fully deployed. If it is not, it might be a private URL so we skip\n // the check.\n logger.debug(`Checking if API is live: ${url}`);\n const parsedUrl = new URL(url);\n if (parsedUrl.hostname.includes(\".zuplo.app\")) {\n // Let's do some other check here to ensure that the zup is fully deployed\n try {\n const zupResponse = await fetch(\n `${parsedUrl.origin}/__zuplo/build`\n );\n if (zupResponse.ok) {\n return {\n url,\n logUrl,\n };\n } else {\n logger.debug(\n { status: zupResponse.status },\n \"Error while polling deployment\"\n );\n try {\n const result = await zupResponse.text();\n logger.debug(result);\n } catch {\n // Ignore\n }\n // Let's do another round of polling\n await wait();\n continue;\n }\n } catch (error) {\n // Log the error in debug but let's continue polling.\n // On first deploy to dedicated-managed, sometimes the URL has not resolved yet.\n logger.debug(\"Error while polling for zuplo build\", error);\n await wait();\n continue;\n }\n } else {\n return {\n url,\n logUrl,\n };\n }\n }\n case \"ERROR\":\n return {\n logUrl,\n };\n }\n } else if (response.status === 404) {\n // This means that it has not posted yet\n await wait();\n } else if (\n response.status === 500 &&\n currentRetryAttemptsForServerUpdates < maxRetryAttemptsForServerUpdates\n ) {\n currentRetryAttemptsForServerUpdates++;\n await wait();\n } else {\n logger.error(\n `Unexpected error from server while polling for deployment: ${\n response.status\n } ${response.statusText} ${textOrJson(await response.text())}`\n );\n throw new Error(\n `Unexpected response from server while polling for deployment: ${response.status} ${response.statusText}`\n );\n }\n }\n\n return {\n // Fallback url for the user to check the logs\n logUrl: \"https://portal.zuplo.com\",\n };\n}\n\nexport async function pollBuild(\n argv: Arguments,\n endpoint: string,\n buildName: string,\n spinner: Ora\n): Promise<{\n conditionType?: string;\n status?: string;\n reason?: string;\n message?: string;\n}> {\n const MaxRetryAttemptsForServerUpdates = 5;\n let CurrentRetryAttemptsForServerUpdates = 0;\n\n for (let pollRetry = 0; pollRetry < settings.MAX_POLL_RETRIES; pollRetry++) {\n spinner.suffixText = `(${pollRetry}/${settings.MAX_POLL_RETRIES})`;\n\n const response = await fetch(\n `${endpoint}/v1/deployments/build/${buildName}`,\n {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n if (response.ok) {\n const { conditionType, status, reason, message } = await response.json();\n\n switch (conditionType) {\n case \"Complete\":\n case \"Failed\":\n return {\n conditionType,\n status,\n reason,\n message,\n };\n default:\n await wait();\n continue;\n }\n } else if (response.status === 404) {\n // This means that it has not posted yet\n await wait();\n } else if (\n response.status === 500 &&\n CurrentRetryAttemptsForServerUpdates < MaxRetryAttemptsForServerUpdates\n ) {\n CurrentRetryAttemptsForServerUpdates++;\n await wait();\n } else {\n logger.error(\n { response: JSON.stringify(await response.json(), null, 2) },\n \"Failed to upload to self-hosted build endpoint\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to upload to self-hosted build endpoint\"\n );\n }\n }\n\n return {};\n}\n"]}
1
+ {"version":3,"file":"poll-deployment.js","sourceRoot":"","sources":["../../src/deploy/poll-deployment.ts"],"names":[],"mappings":"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","sourcesContent":["import { Ora } from \"ora\";\nimport { logger } from \"../common/logger.js\";\nimport {\n printCriticalFailureToConsoleAndExit,\n textOrJson,\n} from \"../common/output.js\";\nimport settings from \"../common/settings.js\";\nimport { classifyBuildProbeResponse } from \"./classify-build-probe.js\";\nimport { Arguments } from \"./handler.js\";\n\nfunction wait(duration: number = settings.POLL_INTERVAL): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, duration));\n}\n\nexport interface PollDeploymentResult {\n url?: string;\n logUrl: string;\n /** Set when the deploy succeeded but the gateway failed to initialize. */\n initError?: { detail: string };\n}\n\nexport async function pollDeployment(\n argv: Arguments,\n fileId: string,\n account: string,\n project: string,\n spinner: Ora\n): Promise<PollDeploymentResult> {\n const maxRetryAttemptsForServerUpdates = 5;\n const requiredConsecutiveInitErrors = 2;\n let currentRetryAttemptsForServerUpdates = 0;\n let consecutiveMatchingInitErrors = 0;\n let lastLogUrl: string | undefined;\n\n for (let pollRetry = 0; pollRetry < settings.MAX_POLL_RETRIES; pollRetry++) {\n spinner.suffixText = `(${pollRetry}/${settings.MAX_POLL_RETRIES})`;\n const response = await fetch(\n `${settings.ZUPLO_DEVELOPER_API_ENDPOINT}/v1/accounts/${account}/projects/${project}/deployment-status/${fileId}`,\n {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n if (response.ok) {\n const rawJSON = await response.text();\n const { status, url, logUrl, buildId } = JSON.parse(rawJSON);\n lastLogUrl = logUrl;\n\n switch (status) {\n case \"IN_PROGRESS\":\n await wait();\n continue;\n case \"SUCCESS\": {\n // If the return URL is zuplo.app we can check to make sure the zup is\n // fully deployed. If it is not, it might be a private URL so we skip\n // the check.\n logger.debug(`Checking if API is live: ${url}`);\n if (!url) {\n return { logUrl };\n }\n const parsedUrl = new URL(url);\n if (parsedUrl.hostname.includes(\".zuplo.app\")) {\n // Let's do some other check here to ensure that the zup is fully deployed\n try {\n const zupResponse = await fetch(\n `${parsedUrl.origin}/__zuplo/build`\n );\n if (zupResponse.ok) {\n return {\n url,\n logUrl,\n };\n } else {\n logger.debug(\n { status: zupResponse.status },\n \"Error while polling deployment\"\n );\n let rawBody: string | undefined;\n try {\n rawBody = await zupResponse.text();\n logger.debug(rawBody);\n } catch {\n // Ignore\n }\n\n const observation = classifyBuildProbeResponse(\n zupResponse.status,\n rawBody\n );\n if (\n settings.DEPLOY_INIT_ERROR_FAIL_FAST &&\n observation.type === \"init-error\" &&\n typeof buildId === \"string\" &&\n buildId.length > 0 &&\n observation.buildId === buildId\n ) {\n consecutiveMatchingInitErrors++;\n if (\n consecutiveMatchingInitErrors >=\n requiredConsecutiveInitErrors\n ) {\n return {\n logUrl,\n initError: { detail: observation.detail },\n };\n }\n } else {\n consecutiveMatchingInitErrors = 0;\n }\n // Let's do another round of polling\n await wait();\n continue;\n }\n } catch (error) {\n // Log the error in debug but let's continue polling.\n // On first deploy to dedicated-managed, sometimes the URL has not resolved yet.\n logger.debug(\"Error while polling for zuplo build\", error);\n consecutiveMatchingInitErrors = 0;\n await wait();\n continue;\n }\n } else {\n return {\n url,\n logUrl,\n };\n }\n }\n case \"ERROR\":\n return {\n logUrl,\n };\n }\n } else if (response.status === 404) {\n // This means that it has not posted yet\n await wait();\n } else if (\n response.status === 500 &&\n currentRetryAttemptsForServerUpdates < maxRetryAttemptsForServerUpdates\n ) {\n currentRetryAttemptsForServerUpdates++;\n await wait();\n } else {\n logger.error(\n `Unexpected error from server while polling for deployment: ${\n response.status\n } ${response.statusText} ${textOrJson(await response.text())}`\n );\n throw new Error(\n `Unexpected response from server while polling for deployment: ${response.status} ${response.statusText}`\n );\n }\n }\n\n return {\n // Fallback url for the user to check the logs\n logUrl: lastLogUrl ?? \"https://portal.zuplo.com\",\n };\n}\n\nexport async function pollBuild(\n argv: Arguments,\n endpoint: string,\n buildName: string,\n spinner: Ora\n): Promise<{\n conditionType?: string;\n status?: string;\n reason?: string;\n message?: string;\n}> {\n const MaxRetryAttemptsForServerUpdates = 5;\n let CurrentRetryAttemptsForServerUpdates = 0;\n\n for (let pollRetry = 0; pollRetry < settings.MAX_POLL_RETRIES; pollRetry++) {\n spinner.suffixText = `(${pollRetry}/${settings.MAX_POLL_RETRIES})`;\n\n const response = await fetch(\n `${endpoint}/v1/deployments/build/${buildName}`,\n {\n method: \"GET\",\n headers: {\n // biome-ignore lint/style/useNamingConvention: External API property\n Authorization: `Bearer ${argv.authToken}`,\n },\n }\n );\n if (response.ok) {\n const { conditionType, status, reason, message } = await response.json();\n\n switch (conditionType) {\n case \"Complete\":\n case \"Failed\":\n return {\n conditionType,\n status,\n reason,\n message,\n };\n default:\n await wait();\n continue;\n }\n } else if (response.status === 404) {\n // This means that it has not posted yet\n await wait();\n } else if (\n response.status === 500 &&\n CurrentRetryAttemptsForServerUpdates < MaxRetryAttemptsForServerUpdates\n ) {\n CurrentRetryAttemptsForServerUpdates++;\n await wait();\n } else {\n logger.error(\n { response: JSON.stringify(await response.json(), null, 2) },\n \"Failed to upload to self-hosted build endpoint\"\n );\n await printCriticalFailureToConsoleAndExit(\n \"Error: Failed to upload to self-hosted build endpoint\"\n );\n }\n }\n\n return {};\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=poll-deployment.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"poll-deployment.test.d.ts","sourceRoot":"","sources":["../../src/deploy/poll-deployment.test.ts"],"names":[],"mappings":""}