@shopify/oxygen-cli 1.4.0 → 1.6.0

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 (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 };