@shopify/oxygen-cli 1.4.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. package/README.md +2 -0
  2. package/dist/commands/oxygen/deploy.d.ts +9 -9
  3. package/dist/commands/oxygen/deploy.js +58 -43
  4. package/dist/deploy/build-cancel.d.ts +9 -2
  5. package/dist/deploy/build-cancel.js +4 -3
  6. package/dist/deploy/build-cancel.test.js +21 -4
  7. package/dist/deploy/build-initiate.d.ts +9 -2
  8. package/dist/deploy/build-initiate.js +4 -3
  9. package/dist/deploy/build-initiate.test.js +19 -8
  10. package/dist/deploy/build-project.d.ts +7 -2
  11. package/dist/deploy/build-project.js +34 -19
  12. package/dist/deploy/build-project.test.js +14 -8
  13. package/dist/deploy/deployment-cancel.d.ts +9 -2
  14. package/dist/deploy/deployment-cancel.js +4 -3
  15. package/dist/deploy/deployment-cancel.test.js +19 -16
  16. package/dist/deploy/deployment-complete.d.ts +2 -2
  17. package/dist/deploy/deployment-initiate.d.ts +10 -4
  18. package/dist/deploy/deployment-initiate.js +4 -3
  19. package/dist/deploy/deployment-initiate.test.js +26 -10
  20. package/dist/deploy/get-upload-files.d.ts +2 -2
  21. package/dist/deploy/health-check.d.ts +12 -0
  22. package/dist/deploy/health-check.js +44 -0
  23. package/dist/deploy/health-check.test.d.ts +2 -0
  24. package/dist/deploy/health-check.test.js +92 -0
  25. package/dist/deploy/index.d.ts +10 -3
  26. package/dist/deploy/index.js +54 -26
  27. package/dist/deploy/metadata.d.ts +4 -3
  28. package/dist/deploy/metadata.js +3 -3
  29. package/dist/deploy/metadata.test.js +4 -4
  30. package/dist/deploy/types.d.ts +17 -2
  31. package/dist/deploy/types.js +3 -1
  32. package/dist/deploy/upload-files.d.ts +9 -2
  33. package/dist/deploy/upload-files.js +7 -4
  34. package/dist/deploy/upload-files.test.js +37 -18
  35. package/dist/utils/test-helper.d.ts +2 -2
  36. package/dist/utils/test-helper.js +3 -0
  37. package/dist/utils/utils.d.ts +3 -3
  38. package/dist/utils/utils.js +1 -5
  39. package/oclif.manifest.json +50 -33
  40. package/package.json +8 -8
package/README.md CHANGED
@@ -42,6 +42,8 @@ oxygen:deploy [options]
42
42
  - -o, --workerOnly: Worker only deployment.
43
43
  - -s, --skipBuild: Skip running build command.
44
44
  - -b, --buildCommand <buildCommand>: Build command (default: `yarn build`).
45
+ - -h, --skipHealthCheck: Skip running the health check on the deployment
46
+ - -d, --healthCheckMaxDuration: The maximum duration (in seconds) that the health check is allowed to run before it is considered failed. Accepts values between 10 and 300.
45
47
  - --publicDeployment: set the deployment to be publicly accessible.
46
48
  - --metadataUrl <metadataUrl>: URL that links to the deployment.
47
49
  - --metadataUser <metadataUser>: User that initiated the deployment.
@@ -1,20 +1,21 @@
1
1
  import * as _oclif_core_lib_interfaces_parser_js from '@oclif/core/lib/interfaces/parser.js';
2
2
  import { Command } from '@oclif/core';
3
- import { DeployConfig } from '../../deploy/types.js';
4
3
 
5
4
  declare class Deploy extends Command {
6
5
  static description: string;
7
6
  static hidden: boolean;
8
7
  static flags: {
9
- token: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
10
- path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
11
- environmentTag: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
12
- workerFolder: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
13
8
  assetsFolder: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
14
- workerOnly: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
15
- skipBuild: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
16
9
  buildCommand: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
10
+ environmentTag: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
11
+ healthCheckMaxDuration: _oclif_core_lib_interfaces_parser_js.OptionFlag<number, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
12
+ path: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
17
13
  publicDeployment: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
14
+ skipBuild: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
15
+ skipHealthCheck: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
16
+ token: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
17
+ workerFolder: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
18
+ workerOnly: _oclif_core_lib_interfaces_parser_js.BooleanFlag<boolean>;
18
19
  metadataUrl: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
19
20
  metadataUser: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
20
21
  metadataVersion: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -22,6 +23,5 @@ declare class Deploy extends Command {
22
23
  static hasCustomBuildCommand: boolean;
23
24
  run(): Promise<void>;
24
25
  }
25
- declare function runInit(options: DeployConfig): void;
26
26
 
27
- export { Deploy, runInit };
27
+ export { Deploy };
@@ -3,52 +3,18 @@ import { consoleError } from '@shopify/cli-kit/node/output';
3
3
  import { normalizePath } from '@shopify/cli-kit/node/path';
4
4
  import { createDeploy } from '../../deploy/index.js';
5
5
  import { deployDefaults, parseToken, verifyConfig, getBuildCommandFromLockFile } from '../../utils/utils.js';
6
+ import { HealthCheckError } from '../../deploy/types.js';
6
7
 
7
8
  class Deploy extends Command {
8
9
  static description = "Creates a deployment to Oxygen";
9
10
  static hidden = false;
10
11
  static flags = {
11
- token: Flags.string({
12
- char: "t",
13
- description: "Oxygen deployment token",
14
- env: "OXYGEN_DEPLOYMENT_TOKEN",
15
- required: false
16
- }),
17
- path: Flags.string({
18
- char: "p",
19
- description: "Root path",
20
- default: "./",
21
- required: false
22
- }),
23
- environmentTag: Flags.string({
24
- char: "e",
25
- description: "Tag of the environment to deploy to",
26
- required: false
27
- }),
28
- workerFolder: Flags.string({
29
- char: "w",
30
- description: "Worker folder",
31
- default: String(deployDefaults.workerDirDefault),
32
- required: false
33
- }),
34
12
  assetsFolder: Flags.string({
35
13
  char: "a",
36
14
  description: "Assets folder",
37
15
  default: String(deployDefaults.assetsDirDefault),
38
16
  required: false
39
17
  }),
40
- workerOnly: Flags.boolean({
41
- char: "o",
42
- description: "Worker only deployment",
43
- default: false,
44
- required: false
45
- }),
46
- skipBuild: Flags.boolean({
47
- char: "s",
48
- description: "Skip running build command",
49
- required: false,
50
- default: false
51
- }),
52
18
  buildCommand: Flags.string({
53
19
  char: "b",
54
20
  description: "Build command",
@@ -59,12 +25,61 @@ class Deploy extends Command {
59
25
  return Promise.resolve(input);
60
26
  }
61
27
  }),
28
+ environmentTag: Flags.string({
29
+ char: "e",
30
+ description: "Tag of the environment to deploy to",
31
+ required: false
32
+ }),
33
+ healthCheckMaxDuration: Flags.integer({
34
+ char: "d",
35
+ description: "the maximum duration (in seconds) that the health check is allowed to run before it is considered failed.",
36
+ min: 10,
37
+ max: 300,
38
+ required: false,
39
+ default: deployDefaults.healthCheckMaxDurationDefault
40
+ }),
41
+ path: Flags.string({
42
+ char: "p",
43
+ description: "Root path",
44
+ default: "./",
45
+ required: false
46
+ }),
62
47
  publicDeployment: Flags.boolean({
63
48
  env: "OXYGEN_PUBLIC_DEPLOYMENT",
64
49
  description: "Marks a preview deployment as publicly accessible.",
65
50
  required: false,
66
51
  default: false
67
52
  }),
53
+ skipBuild: Flags.boolean({
54
+ char: "s",
55
+ description: "Skip running build command",
56
+ required: false,
57
+ default: false
58
+ }),
59
+ skipHealthCheck: Flags.boolean({
60
+ char: "h",
61
+ description: "Skip running deployment health check",
62
+ required: false,
63
+ default: false
64
+ }),
65
+ token: Flags.string({
66
+ char: "t",
67
+ description: "Oxygen deployment token",
68
+ env: "OXYGEN_DEPLOYMENT_TOKEN",
69
+ required: true
70
+ }),
71
+ workerFolder: Flags.string({
72
+ char: "w",
73
+ description: "Worker folder",
74
+ default: String(deployDefaults.workerDirDefault),
75
+ required: false
76
+ }),
77
+ workerOnly: Flags.boolean({
78
+ char: "o",
79
+ description: "Worker only deployment",
80
+ default: false,
81
+ required: false
82
+ }),
68
83
  metadataUrl: Flags.string({
69
84
  description: "URL that links to the deployment. Will be saved and displayed in the Shopify admin",
70
85
  required: false,
@@ -92,9 +107,11 @@ class Deploy extends Command {
92
107
  const config = {
93
108
  assetsDir: normalizePath(flags.assetsFolder),
94
109
  buildCommand: flags.buildCommand,
110
+ buildOutput: true,
95
111
  deploymentToken: parseToken(flags.token),
96
112
  environmentTag: flags.environmentTag,
97
113
  deploymentUrl,
114
+ healthCheckMaxDuration: flags.healthCheckMaxDuration,
98
115
  metadata: {
99
116
  url: flags.metadataUrl,
100
117
  user: flags.metadataUser,
@@ -103,6 +120,7 @@ class Deploy extends Command {
103
120
  publicDeployment: flags.publicDeployment,
104
121
  rootPath: normalizePath(flags.path),
105
122
  skipBuild: flags.skipBuild,
123
+ skipHealthCheck: flags.skipHealthCheck,
106
124
  workerDir: normalizePath(flags.workerFolder),
107
125
  workerOnly: flags.workerOnly
108
126
  };
@@ -110,19 +128,16 @@ class Deploy extends Command {
110
128
  if (!Deploy.hasCustomBuildCommand && !config.skipBuild) {
111
129
  config.buildCommand = getBuildCommandFromLockFile(config);
112
130
  }
113
- runInit(config);
131
+ await createDeploy({ config });
114
132
  } catch (error) {
115
- if (error instanceof Error) {
133
+ if (!(error instanceof Error)) {
134
+ consoleError(error);
135
+ } else if (!(error instanceof HealthCheckError)) {
116
136
  consoleError(error.message);
117
- } else {
118
- console.error(error);
119
137
  }
120
138
  this.exit(1);
121
139
  }
122
140
  }
123
141
  }
124
- function runInit(options) {
125
- createDeploy(options);
126
- }
127
142
 
128
- export { Deploy, runInit };
143
+ export { Deploy };
@@ -1,6 +1,13 @@
1
- import { DeployConfig } from './types.js';
1
+ import { Logger } from '@shopify/cli-kit/node/output';
2
+ import { DeploymentConfig } from './types.js';
2
3
  import { BuildCancelResponse } from './graphql/build-cancel.js';
3
4
 
4
- declare function buildCancel(config: DeployConfig, buildId: string, reason: string): Promise<BuildCancelResponse>;
5
+ interface BuildCancelOptions {
6
+ config: DeploymentConfig;
7
+ buildId: string;
8
+ reason: string;
9
+ logger: Logger;
10
+ }
11
+ declare function buildCancel(options: BuildCancelOptions): Promise<BuildCancelResponse>;
5
12
 
6
13
  export { buildCancel };
@@ -1,10 +1,11 @@
1
1
  import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
2
2
  import { AbortError } from '@shopify/cli-kit/node/error';
3
3
  import { outputInfo } from '@shopify/cli-kit/node/output';
4
- import { Header, stderrLogger, errorHandler } from '../utils/utils.js';
4
+ import { Header, errorHandler } from '../utils/utils.js';
5
5
  import { BuildCancelQuery } from './graphql/build-cancel.js';
6
6
 
7
- async function buildCancel(config, buildId, reason) {
7
+ async function buildCancel(options) {
8
+ const { config, buildId, reason, logger } = options;
8
9
  const variables = {
9
10
  buildId,
10
11
  reason
@@ -25,7 +26,7 @@ async function buildCancel(config, buildId, reason) {
25
26
  `Failed to cancel build: ${response.buildCancel.userErrors[0]?.message}`
26
27
  );
27
28
  }
28
- outputInfo(`Build with id ${buildId} cancelled.`, stderrLogger);
29
+ outputInfo(`Build with id ${buildId} cancelled.`, logger);
29
30
  return response.buildCancel;
30
31
  } catch (error) {
31
32
  errorHandler(error);
@@ -2,7 +2,7 @@ import { AbortError } from '@shopify/cli-kit/node/error';
2
2
  import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
3
3
  import { vi, describe, test, expect } from 'vitest';
4
4
  import { createTestConfig } from '../utils/test-helper.js';
5
- import { Header } from '../utils/utils.js';
5
+ import { stderrLogger, Header } from '../utils/utils.js';
6
6
  import { buildCancel } from './build-cancel.js';
7
7
 
8
8
  vi.mock("@shopify/cli-kit/node/api/graphql");
@@ -20,7 +20,12 @@ describe("BuildCancel", () => {
20
20
  }
21
21
  };
22
22
  vi.mocked(graphqlRequest).mockResolvedValueOnce(response);
23
- const cancelResponse = await buildCancel(testConfig, "build-1", "because");
23
+ const cancelResponse = await buildCancel({
24
+ config: testConfig,
25
+ buildId: "build-1",
26
+ reason: "because",
27
+ logger: stderrLogger
28
+ });
24
29
  expect(cancelResponse).toEqual(response.buildCancel);
25
30
  expect(graphqlRequest).toHaveBeenCalledWith({
26
31
  query: expect.any(String),
@@ -47,7 +52,14 @@ describe("BuildCancel", () => {
47
52
  }
48
53
  };
49
54
  vi.mocked(graphqlRequest).mockResolvedValueOnce(response);
50
- await expect(buildCancel(testConfig, "build-1", "because")).rejects.toThrow(
55
+ await expect(
56
+ buildCancel({
57
+ config: testConfig,
58
+ buildId: "build-1",
59
+ reason: "because",
60
+ logger: stderrLogger
61
+ })
62
+ ).rejects.toThrow(
51
63
  new AbortError(
52
64
  `Failed to cancel build: ${response.buildCancel.userErrors[0]?.message}`
53
65
  )
@@ -60,7 +72,12 @@ describe("BuildCancel", () => {
60
72
  vi.mocked(graphqlRequest).mockRejectedValueOnce(error);
61
73
  try {
62
74
  await expect(
63
- buildCancel(testConfig, "build-1", "because")
75
+ buildCancel({
76
+ config: testConfig,
77
+ buildId: "build-1",
78
+ reason: "because",
79
+ logger: stderrLogger
80
+ })
64
81
  ).rejects.toThrow(
65
82
  new AbortError(
66
83
  "You are not authorized to perform this action. Please check your deployment token."
@@ -1,6 +1,13 @@
1
- import { DeployConfig, EnvironmentInput } from './types.js';
1
+ import { Logger } from '@shopify/cli-kit/node/output';
2
+ import { DeploymentConfig, EnvironmentInput } from './types.js';
2
3
  import { BuildInitiateResponse } from './graphql/build-initiate.js';
3
4
 
4
- declare function buildInitiate(config: DeployConfig, environment?: EnvironmentInput, labels?: string[]): Promise<BuildInitiateResponse>;
5
+ interface BuildInitiateOptions {
6
+ config: DeploymentConfig;
7
+ logger: Logger;
8
+ environment?: EnvironmentInput;
9
+ labels?: string[];
10
+ }
11
+ declare function buildInitiate(options: BuildInitiateOptions): Promise<BuildInitiateResponse>;
5
12
 
6
13
  export { buildInitiate };
@@ -1,10 +1,11 @@
1
1
  import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
2
2
  import { AbortError } from '@shopify/cli-kit/node/error';
3
3
  import { outputCompleted } from '@shopify/cli-kit/node/output';
4
- import { Header, stderrLogger, errorHandler } from '../utils/utils.js';
4
+ import { Header, errorHandler } from '../utils/utils.js';
5
5
  import { BuildInitiateQuery } from './graphql/build-initiate.js';
6
6
 
7
- async function buildInitiate(config, environment, labels = []) {
7
+ async function buildInitiate(options) {
8
+ const { config, logger, environment, labels = [] } = options;
8
9
  const variables = {
9
10
  environment,
10
11
  labels
@@ -27,7 +28,7 @@ async function buildInitiate(config, environment, labels = []) {
27
28
  }
28
29
  outputCompleted(
29
30
  `Build initiated successfully with id ${response.buildInitiate.build.id}.`,
30
- stderrLogger
31
+ logger
31
32
  );
32
33
  return response.buildInitiate;
33
34
  } catch (error) {
@@ -2,7 +2,7 @@ import { AbortError } from '@shopify/cli-kit/node/error';
2
2
  import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
3
3
  import { outputCompleted } from '@shopify/cli-kit/node/output';
4
4
  import { vi, describe, test, expect } from 'vitest';
5
- import { Header, stderrLogger } from '../utils/utils.js';
5
+ import { stderrLogger, Header } from '../utils/utils.js';
6
6
  import { createTestConfig } from '../utils/test-helper.js';
7
7
  import { buildInitiate } from './build-initiate.js';
8
8
 
@@ -20,13 +20,14 @@ describe("BuildInitiate", () => {
20
20
  }
21
21
  };
22
22
  vi.mocked(graphqlRequest).mockResolvedValueOnce(response);
23
- const initiateResponse = await buildInitiate(
24
- testConfig,
25
- {
23
+ const initiateResponse = await buildInitiate({
24
+ config: testConfig,
25
+ environment: {
26
26
  tag: testConfig.environmentTag
27
27
  },
28
- []
29
- );
28
+ labels: [],
29
+ logger: stderrLogger
30
+ });
30
31
  expect(initiateResponse).toEqual(response.buildInitiate);
31
32
  expect(graphqlRequest).toHaveBeenCalledWith({
32
33
  query: expect.any(String),
@@ -55,7 +56,12 @@ describe("BuildInitiate", () => {
55
56
  };
56
57
  vi.mocked(graphqlRequest).mockResolvedValueOnce(response);
57
58
  await expect(
58
- buildInitiate(testConfig, { tag: "preview" }, [])
59
+ buildInitiate({
60
+ config: testConfig,
61
+ environment: { tag: "preview" },
62
+ labels: [],
63
+ logger: stderrLogger
64
+ })
59
65
  ).rejects.toThrow(
60
66
  new AbortError(
61
67
  `Failed to create build. ${response.buildInitiate.userErrors[0]?.message}`
@@ -69,7 +75,12 @@ describe("BuildInitiate", () => {
69
75
  vi.mocked(graphqlRequest).mockRejectedValueOnce(error);
70
76
  try {
71
77
  await expect(
72
- buildInitiate(testConfig, { tag: "preview" }, [])
78
+ buildInitiate({
79
+ config: testConfig,
80
+ environment: { tag: "preview" },
81
+ labels: [],
82
+ logger: stderrLogger
83
+ })
73
84
  ).rejects.toThrow(
74
85
  new AbortError(
75
86
  "You are not authorized to perform this action. Please check your deployment token."
@@ -1,5 +1,10 @@
1
- import { DeployConfig } from './types.js';
1
+ import { DeploymentConfig, DeploymentHooks } from './types.js';
2
2
 
3
- declare function buildProject(config: DeployConfig, assetPath?: string): Promise<void>;
3
+ interface BuildProjectOptions {
4
+ config: DeploymentConfig;
5
+ assetPath?: string;
6
+ hooks?: DeploymentHooks;
7
+ }
8
+ declare function buildProject(options: BuildProjectOptions): Promise<void>;
4
9
 
5
10
  export { buildProject };
@@ -1,28 +1,43 @@
1
1
  import { spawn } from 'child_process';
2
2
 
3
- async function buildProject(config, assetPath) {
3
+ async function buildProject(options) {
4
+ const { config, assetPath, hooks } = options;
5
+ hooks?.onBuildStart?.();
4
6
  const assetPathEnvironment = assetPath ? { HYDROGEN_ASSET_BASE_URL: assetPath } : {};
5
- await new Promise((resolve, reject) => {
6
- const buildCommand = spawn(config.buildCommand, [], {
7
- stdio: ["inherit", "pipe", "inherit"],
8
- env: {
9
- // eslint-disable-next-line no-process-env
10
- ...process.env,
11
- ...assetPathEnvironment
12
- },
13
- cwd: config.rootPath,
14
- shell: true
15
- });
16
- buildCommand.stdout.pipe(process.stderr);
17
- buildCommand.on("close", (code) => {
18
- if (code !== 0) {
19
- reject(code);
7
+ try {
8
+ await new Promise((resolve, reject) => {
9
+ const buildCommand = spawn(config.buildCommand, [], {
10
+ stdio: config.buildOutput ? ["inherit", "pipe", "inherit"] : ["ignore", "ignore", "pipe"],
11
+ env: {
12
+ // eslint-disable-next-line no-process-env
13
+ ...process.env,
14
+ ...assetPathEnvironment
15
+ },
16
+ cwd: config.rootPath,
17
+ shell: true
18
+ });
19
+ let stderrOutput = "";
20
+ if (buildCommand.stderr) {
21
+ buildCommand.stderr.on("data", (data) => {
22
+ stderrOutput += data;
23
+ });
24
+ }
25
+ if (config.buildOutput && buildCommand.stdout) {
26
+ buildCommand.stdout.pipe(process.stderr);
20
27
  }
21
- resolve(code);
28
+ buildCommand.on("close", (code) => {
29
+ if (code !== 0) {
30
+ hooks?.onBuildError?.(new Error(stderrOutput));
31
+ reject(code);
32
+ return;
33
+ }
34
+ hooks?.onBuildComplete?.();
35
+ resolve(code);
36
+ });
22
37
  });
23
- }).catch((error) => {
38
+ } catch (error) {
24
39
  throw new Error(`Build failed with error code: ${error}`);
25
- });
40
+ }
26
41
  }
27
42
 
28
43
  export { buildProject };
@@ -30,7 +30,11 @@ test("BuildProject builds the project successfully", async () => {
30
30
  buildCommand: "npm run build"
31
31
  };
32
32
  const assetPath = "https://example.com/assets";
33
- await buildProject(config, assetPath);
33
+ const hooks = {
34
+ onBuildStart: vi.fn(),
35
+ onBuildComplete: vi.fn()
36
+ };
37
+ await buildProject({ config, assetPath, hooks });
34
38
  expect(spawn).toBeCalledWith("npm run build", [], {
35
39
  cwd: "rootFolder",
36
40
  shell: true,
@@ -41,15 +45,17 @@ test("BuildProject builds the project successfully", async () => {
41
45
  HYDROGEN_ASSET_BASE_URL: "https://example.com/assets"
42
46
  }
43
47
  });
48
+ expect(hooks.onBuildStart).toBeCalled();
49
+ expect(hooks.onBuildComplete).toBeCalled();
44
50
  });
45
51
  test("should throw error on build command failure", async () => {
46
52
  returnCode = 1;
47
- ({
48
- ...testConfig,
49
- buildCommand: "yarn build"
50
- });
53
+ const hooks = {
54
+ onBuildError: vi.fn()
55
+ };
51
56
  const assetPath = "https://example.com/assets";
52
- await expect(() => buildProject(testConfig, assetPath)).rejects.toThrow(
53
- "Build failed with error code: 1"
54
- );
57
+ await expect(
58
+ () => buildProject({ config: testConfig, assetPath, hooks })
59
+ ).rejects.toThrow("Build failed with error code: 1");
60
+ expect(hooks.onBuildError).toBeCalled();
55
61
  });
@@ -1,10 +1,17 @@
1
- import { DeployConfig } from './types.js';
1
+ import { Logger } from '@shopify/cli-kit/node/output';
2
+ import { DeploymentConfig } from './types.js';
2
3
  import { DeploymentCancelResponse } from './graphql/deployment-cancel.js';
3
4
 
4
5
  declare enum DeploymentCancelReason {
5
6
  Failed = "FAILED",
6
7
  Cancelled = "CANCELLED"
7
8
  }
8
- declare function deploymentCancel(config: DeployConfig, deploymentId: string, reason: DeploymentCancelReason): Promise<DeploymentCancelResponse>;
9
+ interface DeploymentCancelOptions {
10
+ config: DeploymentConfig;
11
+ deploymentId: string;
12
+ reason: DeploymentCancelReason;
13
+ logger: Logger;
14
+ }
15
+ declare function deploymentCancel(options: DeploymentCancelOptions): Promise<DeploymentCancelResponse>;
9
16
 
10
17
  export { DeploymentCancelReason, deploymentCancel };
@@ -1,7 +1,7 @@
1
1
  import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
2
2
  import { AbortError } from '@shopify/cli-kit/node/error';
3
3
  import { outputInfo } from '@shopify/cli-kit/node/output';
4
- import { Header, stderrLogger, errorHandler } from '../utils/utils.js';
4
+ import { Header, errorHandler } from '../utils/utils.js';
5
5
  import { DeploymentCancelQuery } from './graphql/deployment-cancel.js';
6
6
 
7
7
  var DeploymentCancelReason = /* @__PURE__ */ ((DeploymentCancelReason2) => {
@@ -9,7 +9,8 @@ var DeploymentCancelReason = /* @__PURE__ */ ((DeploymentCancelReason2) => {
9
9
  DeploymentCancelReason2["Cancelled"] = "CANCELLED";
10
10
  return DeploymentCancelReason2;
11
11
  })(DeploymentCancelReason || {});
12
- async function deploymentCancel(config, deploymentId, reason) {
12
+ async function deploymentCancel(options) {
13
+ const { config, deploymentId, reason, logger } = options;
13
14
  const variables = {
14
15
  deploymentId,
15
16
  reason
@@ -30,7 +31,7 @@ async function deploymentCancel(config, deploymentId, reason) {
30
31
  `Failed to cancel deployment: ${response.deploymentCancel.userErrors[0]?.message}`
31
32
  );
32
33
  }
33
- outputInfo(`Deployment with id ${deploymentId} cancelled.`, stderrLogger);
34
+ outputInfo(`Deployment with id ${deploymentId} cancelled.`, logger);
34
35
  return response.deploymentCancel;
35
36
  } catch (error) {
36
37
  errorHandler(error);
@@ -2,7 +2,7 @@ import { AbortError } from '@shopify/cli-kit/node/error';
2
2
  import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
3
3
  import { vi, describe, test, expect } from 'vitest';
4
4
  import { createTestConfig } from '../utils/test-helper.js';
5
- import { Header } from '../utils/utils.js';
5
+ import { stderrLogger, Header } from '../utils/utils.js';
6
6
  import { deploymentCancel, DeploymentCancelReason } from './deployment-cancel.js';
7
7
 
8
8
  vi.mock("@shopify/cli-kit/node/api/graphql");
@@ -19,11 +19,12 @@ describe("DeploymentComplete", () => {
19
19
  }
20
20
  };
21
21
  vi.mocked(graphqlRequest).mockResolvedValueOnce(response);
22
- const completeResponse = await deploymentCancel(
23
- testConfig,
24
- "deployment-1",
25
- DeploymentCancelReason.Failed
26
- );
22
+ const completeResponse = await deploymentCancel({
23
+ config: testConfig,
24
+ deploymentId: "deployment-1",
25
+ reason: DeploymentCancelReason.Failed,
26
+ logger: stderrLogger
27
+ });
27
28
  expect(completeResponse).toEqual(response.deploymentCancel);
28
29
  expect(graphqlRequest).toHaveBeenCalledWith({
29
30
  query: expect.any(String),
@@ -51,11 +52,12 @@ describe("DeploymentComplete", () => {
51
52
  };
52
53
  vi.mocked(graphqlRequest).mockResolvedValueOnce(response);
53
54
  await expect(
54
- deploymentCancel(
55
- testConfig,
56
- "deployment-1",
57
- DeploymentCancelReason.Failed
58
- )
55
+ deploymentCancel({
56
+ config: testConfig,
57
+ deploymentId: "deployment-1",
58
+ reason: DeploymentCancelReason.Failed,
59
+ logger: stderrLogger
60
+ })
59
61
  ).rejects.toThrow(
60
62
  new AbortError(
61
63
  `Failed to cancel deployment: ${response.deploymentCancel.userErrors[0]?.message}`
@@ -69,11 +71,12 @@ describe("DeploymentComplete", () => {
69
71
  vi.mocked(graphqlRequest).mockRejectedValueOnce(error);
70
72
  try {
71
73
  await expect(
72
- deploymentCancel(
73
- testConfig,
74
- "deployment-1",
75
- DeploymentCancelReason.Failed
76
- )
74
+ deploymentCancel({
75
+ config: testConfig,
76
+ deploymentId: "deployment-1",
77
+ reason: DeploymentCancelReason.Failed,
78
+ logger: stderrLogger
79
+ })
77
80
  ).rejects.toThrow(
78
81
  new AbortError(
79
82
  "You are not authorized to perform this action. Please check your deployment token."
@@ -1,6 +1,6 @@
1
1
  import { DeploymentCompleteResponse } from './graphql/deployment-complete.js';
2
- import { DeployConfig } from './types.js';
2
+ import { DeploymentConfig } from './types.js';
3
3
 
4
- declare function deploymentComplete(config: DeployConfig, deploymentId: string): Promise<DeploymentCompleteResponse>;
4
+ declare function deploymentComplete(config: DeploymentConfig, deploymentId: string): Promise<DeploymentCompleteResponse>;
5
5
 
6
6
  export { deploymentComplete };
@@ -1,17 +1,23 @@
1
- import { DeployConfig, DeploymentManifestFile, EnvironmentInput } from './types.js';
1
+ import { Logger } from '@shopify/cli-kit/node/output';
2
+ import { DeploymentConfig, DeploymentManifestFile, EnvironmentInput } from './types.js';
2
3
  import { DeploymentInitiateResponse } from './graphql/deployment-initiate.js';
3
4
 
4
5
  type DeploymentInitiateInput = {
5
6
  buildId: string;
6
7
  environment?: never;
7
- manifest: DeploymentManifestFile[];
8
8
  labels?: string[];
9
+ manifest: DeploymentManifestFile[];
9
10
  } | {
10
11
  environment?: EnvironmentInput;
11
12
  buildId?: never;
12
- manifest: DeploymentManifestFile[];
13
13
  labels?: string[];
14
+ manifest: DeploymentManifestFile[];
14
15
  };
15
- declare function deploymentInitiate(config: DeployConfig, input: DeploymentInitiateInput): Promise<DeploymentInitiateResponse>;
16
+ interface DeploymentInitiateOptions {
17
+ config: DeploymentConfig;
18
+ input: DeploymentInitiateInput;
19
+ logger: Logger;
20
+ }
21
+ declare function deploymentInitiate(options: DeploymentInitiateOptions): Promise<DeploymentInitiateResponse>;
16
22
 
17
23
  export { deploymentInitiate };