@shopify/oxygen-cli 1.2.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -68,7 +68,7 @@ environment:
68
68
  OXYGEN_DEPLOYMENT_TOKEN: ${YOUR_ENV_VARIABLE}
69
69
  ```
70
70
 
71
- When run from a supported CI/CD environment, the tool will automatically retrieve metadata from relevant environment variables. Metadata describes the author making the deployment (user), the version (commit Sha) and the deployment URL (workflow URL).
71
+ When run from a supported CI/CD environment, the tool will automatically retrieve metadata from relevant environment variables. Metadata describes the author making the deployment (user), the version (commit Sha) and the deployment URL (workflow URL). This metadata will be saved and used to show details about the Oxygen deployment in the Hydrogen channel within the Shopify Admin.
72
72
 
73
73
  The following environment variables are relevant, depending on your environment:
74
74
 
@@ -78,9 +78,9 @@ The following environment variables are relevant, depending on your environment:
78
78
  | Version | BITBUCKET_COMMIT | CIRCLE_SHA1 | GITHUB_SHA | CI_COMMIT_SHA |
79
79
  | URL | BITBUCKET_BUILD_URL | CIRCLE_BUILD_URL | `${GITHUB_SERVER_URL}${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}` | CI_PROJECT_URL |
80
80
 
81
- These values can be overruled by the command line flags `metadataUser`, `metadataUrl` and `metadataVersion` respectively.
81
+ These values can be overridden by the command line flags `metadataUser`, `metadataUrl` and `metadataVersion` respectively.
82
82
 
83
- The tag of the environment to deploy to, will be derived from the following variables:
83
+ The environment tag to deploy to will be derived from the following variables:
84
84
 
85
85
  | Service | Branch |
86
86
  | ------- | ------ |
@@ -89,4 +89,4 @@ The tag of the environment to deploy to, will be derived from the following vari
89
89
  | Github | GITHUB_REF_NAME |
90
90
  | Gitlab | CI_COMMIT_REF_NAME |
91
91
 
92
- The `environmentTag` command line option can be used to override this, and is required when the tool is run from outside a supported CI environment.
92
+ The `environmentTag` command line option can be used to override this. If not provided, or there is no environment with the specified tag, the deployment will be deployed to the default environment for the app (if one exists).
@@ -67,17 +67,17 @@ class Deploy extends Command {
67
67
  default: false
68
68
  }),
69
69
  metadataUrl: Flags.string({
70
- description: "URL that links to the deployment",
70
+ description: "URL that links to the deployment. Will be saved and displayed in the Shopify admin",
71
71
  required: false,
72
72
  env: "OXYGEN_METADATA_URL"
73
73
  }),
74
74
  metadataUser: Flags.string({
75
- description: "User that initiated the deployment",
75
+ description: "User that initiated the deployment. Will be saved and displayed in the Shopify admin",
76
76
  required: false,
77
77
  env: "OXYGEN_METADATA_USER"
78
78
  }),
79
79
  metadataVersion: Flags.string({
80
- description: "A version identifier for the deployment",
80
+ description: "A version identifier for the deployment. Will be saved and displayed in the Shopify admin",
81
81
  required: false,
82
82
  env: "OXYGEN_METADATA_VERSION"
83
83
  })
@@ -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, errorHandler } from '../utils/utils.js';
4
+ import { Header, stderrLogger, errorHandler } from '../utils/utils.js';
5
5
  import { BuildCancelQuery } from './graphql/build-cancel.js';
6
6
 
7
7
  async function buildCancel(config, buildId, reason) {
@@ -25,7 +25,7 @@ async function buildCancel(config, buildId, reason) {
25
25
  `Failed to cancel build: ${response.buildCancel.userErrors[0]?.message}`
26
26
  );
27
27
  }
28
- outputInfo(`Build with id ${buildId} cancelled.`);
28
+ outputInfo(`Build with id ${buildId} cancelled.`, stderrLogger);
29
29
  return response.buildCancel;
30
30
  } catch (error) {
31
31
  errorHandler(error);
@@ -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 { outputCompleted } from '@shopify/cli-kit/node/output';
4
- import { Header, errorHandler } from '../utils/utils.js';
4
+ import { Header, stderrLogger, errorHandler } from '../utils/utils.js';
5
5
  import { BuildInitiateQuery } from './graphql/build-initiate.js';
6
6
 
7
7
  async function buildInitiate(config, environment, labels = []) {
@@ -26,7 +26,8 @@ async function buildInitiate(config, environment, labels = []) {
26
26
  );
27
27
  }
28
28
  outputCompleted(
29
- `Build initiated successfully with id ${response.buildInitiate.build.id}.`
29
+ `Build initiated successfully with id ${response.buildInitiate.build.id}.`,
30
+ stderrLogger
30
31
  );
31
32
  return response.buildInitiate;
32
33
  } 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 } from '../utils/utils.js';
5
+ import { Header, stderrLogger } from '../utils/utils.js';
6
6
  import { createTestConfig } from '../utils/test-helper.js';
7
7
  import { buildInitiate } from './build-initiate.js';
8
8
 
@@ -39,7 +39,8 @@ describe("BuildInitiate", () => {
39
39
  }
40
40
  });
41
41
  expect(outputCompleted).toHaveBeenCalledWith(
42
- "Build initiated successfully with id build-1."
42
+ "Build initiated successfully with id build-1.",
43
+ stderrLogger
43
44
  );
44
45
  });
45
46
  test("should throw AbortError when build initiation fails due to user errors", async () => {
@@ -4,7 +4,7 @@ async function buildProject(config, assetPath) {
4
4
  const assetPathEnvironment = assetPath ? { HYDROGEN_ASSET_BASE_URL: assetPath } : {};
5
5
  await new Promise((resolve, reject) => {
6
6
  const buildCommand = spawn(config.buildCommand, [], {
7
- stdio: "inherit",
7
+ stdio: ["inherit", "pipe", "inherit"],
8
8
  env: {
9
9
  // eslint-disable-next-line no-process-env
10
10
  ...process.env,
@@ -13,11 +13,7 @@ async function buildProject(config, assetPath) {
13
13
  cwd: config.rootPath,
14
14
  shell: true
15
15
  });
16
- if (buildCommand.stdout) {
17
- buildCommand.stdout.on("data", (data) => {
18
- console.log(`stdout: ${data}`);
19
- });
20
- }
16
+ buildCommand.stdout.pipe(process.stderr);
21
17
  buildCommand.on("close", (code) => {
22
18
  if (code !== 0) {
23
19
  reject(code);
@@ -10,6 +10,8 @@ vi.mock("child_process", () => {
10
10
  return {
11
11
  stdout: {
12
12
  on: () => {
13
+ },
14
+ pipe: () => {
13
15
  }
14
16
  },
15
17
  on: (event, callback) => {
@@ -32,7 +34,7 @@ test("BuildProject builds the project successfully", async () => {
32
34
  expect(spawn).toBeCalledWith("npm run build", [], {
33
35
  cwd: "rootFolder",
34
36
  shell: true,
35
- stdio: "inherit",
37
+ stdio: ["inherit", "pipe", "inherit"],
36
38
  env: {
37
39
  // eslint-disable-next-line no-process-env
38
40
  ...process.env,
@@ -1,6 +1,10 @@
1
1
  import { DeployConfig } from './types.js';
2
2
  import { DeploymentCancelResponse } from './graphql/deployment-cancel.js';
3
3
 
4
- declare function deploymentCancel(config: DeployConfig, deploymentId: string, reason: string): Promise<DeploymentCancelResponse>;
4
+ declare enum DeploymentCancelReason {
5
+ Failed = "FAILED",
6
+ Cancelled = "CANCELLED"
7
+ }
8
+ declare function deploymentCancel(config: DeployConfig, deploymentId: string, reason: DeploymentCancelReason): Promise<DeploymentCancelResponse>;
5
9
 
6
- export { deploymentCancel };
10
+ export { DeploymentCancelReason, deploymentCancel };
@@ -1,9 +1,14 @@
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, errorHandler } from '../utils/utils.js';
4
+ import { Header, stderrLogger, errorHandler } from '../utils/utils.js';
5
5
  import { DeploymentCancelQuery } from './graphql/deployment-cancel.js';
6
6
 
7
+ var DeploymentCancelReason = /* @__PURE__ */ ((DeploymentCancelReason2) => {
8
+ DeploymentCancelReason2["Failed"] = "FAILED";
9
+ DeploymentCancelReason2["Cancelled"] = "CANCELLED";
10
+ return DeploymentCancelReason2;
11
+ })(DeploymentCancelReason || {});
7
12
  async function deploymentCancel(config, deploymentId, reason) {
8
13
  const variables = {
9
14
  deploymentId,
@@ -25,7 +30,7 @@ async function deploymentCancel(config, deploymentId, reason) {
25
30
  `Failed to cancel deployment: ${response.deploymentCancel.userErrors[0]?.message}`
26
31
  );
27
32
  }
28
- outputInfo(`Deployment with id ${deploymentId} cancelled.`);
33
+ outputInfo(`Deployment with id ${deploymentId} cancelled.`, stderrLogger);
29
34
  return response.deploymentCancel;
30
35
  } catch (error) {
31
36
  errorHandler(error);
@@ -33,4 +38,4 @@ async function deploymentCancel(config, deploymentId, reason) {
33
38
  }
34
39
  }
35
40
 
36
- export { deploymentCancel };
41
+ export { DeploymentCancelReason, deploymentCancel };
@@ -3,7 +3,7 @@ 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
5
  import { Header } from '../utils/utils.js';
6
- import { deploymentCancel } from './deployment-cancel.js';
6
+ import { deploymentCancel, DeploymentCancelReason } from './deployment-cancel.js';
7
7
 
8
8
  vi.mock("@shopify/cli-kit/node/api/graphql");
9
9
  vi.mock("@shopify/cli-kit/node/output");
@@ -22,7 +22,7 @@ describe("DeploymentComplete", () => {
22
22
  const completeResponse = await deploymentCancel(
23
23
  testConfig,
24
24
  "deployment-1",
25
- "some reason"
25
+ DeploymentCancelReason.Failed
26
26
  );
27
27
  expect(completeResponse).toEqual(response.deploymentCancel);
28
28
  expect(graphqlRequest).toHaveBeenCalledWith({
@@ -32,7 +32,7 @@ describe("DeploymentComplete", () => {
32
32
  token: testConfig.deploymentToken.accessToken,
33
33
  variables: {
34
34
  deploymentId: "deployment-1",
35
- reason: "some reason"
35
+ reason: DeploymentCancelReason.Failed
36
36
  },
37
37
  addedHeaders: {
38
38
  [Header.OxygenNamespaceHandle]: `${testConfig.deploymentToken.namespace}`
@@ -51,7 +51,11 @@ describe("DeploymentComplete", () => {
51
51
  };
52
52
  vi.mocked(graphqlRequest).mockResolvedValueOnce(response);
53
53
  await expect(
54
- deploymentCancel(testConfig, "deployment-1", "some reason")
54
+ deploymentCancel(
55
+ testConfig,
56
+ "deployment-1",
57
+ DeploymentCancelReason.Failed
58
+ )
55
59
  ).rejects.toThrow(
56
60
  new AbortError(
57
61
  `Failed to cancel deployment: ${response.deploymentCancel.userErrors[0]?.message}`
@@ -65,7 +69,11 @@ describe("DeploymentComplete", () => {
65
69
  vi.mocked(graphqlRequest).mockRejectedValueOnce(error);
66
70
  try {
67
71
  await expect(
68
- deploymentCancel(testConfig, "deployment-1", "some reason")
72
+ deploymentCancel(
73
+ testConfig,
74
+ "deployment-1",
75
+ DeploymentCancelReason.Failed
76
+ )
69
77
  ).rejects.toThrow(
70
78
  new AbortError(
71
79
  "You are not authorized to perform this action. Please check your deployment token."
@@ -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 { outputCompleted } from '@shopify/cli-kit/node/output';
4
- import { Header, errorHandler } from '../utils/utils.js';
4
+ import { Header, stderrLogger, errorHandler } from '../utils/utils.js';
5
5
  import { DeploymentInitiateQuery } from './graphql/deployment-initiate.js';
6
6
 
7
7
  async function deploymentInitiate(config, input) {
@@ -29,7 +29,8 @@ async function deploymentInitiate(config, input) {
29
29
  );
30
30
  }
31
31
  outputCompleted(
32
- `Deployment initiated, ${response.deploymentInitiate.deploymentTargets.length} files to upload.`
32
+ `Deployment initiated, ${response.deploymentInitiate.deploymentTargets.length} files to upload.`,
33
+ stderrLogger
33
34
  );
34
35
  return response.deploymentInitiate;
35
36
  } catch (error) {
@@ -3,7 +3,7 @@ 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
5
  import { createTestConfig } from '../utils/test-helper.js';
6
- import { Header } from '../utils/utils.js';
6
+ import { Header, stderrLogger } from '../utils/utils.js';
7
7
  import { deploymentInitiate } from './deployment-initiate.js';
8
8
 
9
9
  vi.mock("@shopify/cli-kit/node/api/graphql");
@@ -51,14 +51,16 @@ describe("DeploymentInitiate", () => {
51
51
  variables: {
52
52
  buildId: "build-1",
53
53
  environment: void 0,
54
- files: testManifest
54
+ files: testManifest,
55
+ isPrivate: true
55
56
  },
56
57
  addedHeaders: {
57
58
  [Header.OxygenNamespaceHandle]: `${testConfig.deploymentToken.namespace}`
58
59
  }
59
60
  });
60
61
  expect(outputCompleted).toHaveBeenCalledWith(
61
- `Deployment initiated, ${testManifest.length} files to upload.`
62
+ `Deployment initiated, ${testManifest.length} files to upload.`,
63
+ stderrLogger
62
64
  );
63
65
  });
64
66
  test("should initiate a deployment with an environmentName", async () => {
@@ -77,14 +79,16 @@ describe("DeploymentInitiate", () => {
77
79
  variables: {
78
80
  buildId: void 0,
79
81
  environment: { tag: "preview" },
80
- files: testManifest
82
+ files: testManifest,
83
+ isPrivate: true
81
84
  },
82
85
  addedHeaders: {
83
86
  [Header.OxygenNamespaceHandle]: `${testConfig.deploymentToken.namespace}`
84
87
  }
85
88
  });
86
89
  expect(outputCompleted).toHaveBeenCalledWith(
87
- `Deployment initiated, ${testManifest.length} files to upload.`
90
+ `Deployment initiated, ${testManifest.length} files to upload.`,
91
+ stderrLogger
88
92
  );
89
93
  });
90
94
  test("should throw AbortError when deployment initiation fails due to user errors", async () => {
@@ -1,6 +1,6 @@
1
1
  import { OxygenError } from '../types.js';
2
2
 
3
- declare const DeploymentCompleteQuery = "\n mutation DeploymentComplete($deploymentId: ID!) {\n deploymentComplete(id: $deploymentId) {\n deployment {\n id\n status\n url\n }\n userErrors {\n message\n }\n }\n }\n";
3
+ declare const DeploymentCompleteQuery = "\n mutation DeploymentComplete($deploymentId: ID!) {\n deploymentComplete(id: $deploymentId) {\n deployment {\n id\n url\n }\n userErrors {\n message\n }\n }\n }\n";
4
4
  interface DeploymentCompleteQueryData {
5
5
  deploymentComplete: DeploymentCompleteResponse;
6
6
  }
@@ -10,7 +10,6 @@ interface DeploymentCompleteResponse {
10
10
  }
11
11
  interface Deployment {
12
12
  id: string;
13
- status: string;
14
13
  url: string;
15
14
  }
16
15
 
@@ -3,7 +3,6 @@ const DeploymentCompleteQuery = `
3
3
  deploymentComplete(id: $deploymentId) {
4
4
  deployment {
5
5
  id
6
- status
7
6
  url
8
7
  }
9
8
  userErrors {
@@ -1,6 +1,6 @@
1
1
  import { OxygenError } from '../types.js';
2
2
 
3
- declare const DeploymentInitiateQuery = "\n mutation DeploymentInitiate($buildId: ID, $environment: EnvironmentSelectorInput, $labels: [String!], $files: [FileInput!]!, $isPrivate: Boolean) {\n deploymentInitiate(buildId: $buildId, environment: $environment, labels: $labels, files: $files, isPrivate: $isPrivate) {\n deployment {\n id\n status\n }\n deploymentTargets {\n filePath\n fileSize\n uploadUrl\n fileType\n parameters {\n name\n value\n }\n }\n userErrors {\n message\n }\n }\n }\n";
3
+ declare const DeploymentInitiateQuery = "\n mutation DeploymentInitiate($buildId: ID, $environment: EnvironmentSelectorInput, $labels: [String!], $files: [FileInput!]!, $isPrivate: Boolean) {\n deploymentInitiate(buildId: $buildId, environment: $environment, labels: $labels, files: $files, isPrivate: $isPrivate) {\n deployment {\n id\n }\n deploymentTargets {\n filePath\n fileSize\n uploadUrl\n fileType\n parameters {\n name\n value\n }\n }\n userErrors {\n message\n }\n }\n }\n";
4
4
  interface DeploymentInitiateQueryData {
5
5
  deploymentInitiate: DeploymentInitiateResponse;
6
6
  }
@@ -18,7 +18,6 @@ interface DeploymentTargetResponse {
18
18
  }
19
19
  interface Deployment {
20
20
  id: string;
21
- status: string;
22
21
  }
23
22
  interface DeploymentInitiateParameters {
24
23
  name: string;
@@ -3,7 +3,6 @@ const DeploymentInitiateQuery = `
3
3
  deploymentInitiate(buildId: $buildId, environment: $environment, labels: $labels, files: $files, isPrivate: $isPrivate) {
4
4
  deployment {
5
5
  id
6
- status
7
6
  }
8
7
  deploymentTargets {
9
8
  filePath
@@ -1,11 +1,11 @@
1
- import { outputSuccess, outputWarn, consoleError } from '@shopify/cli-kit/node/output';
2
- import { verifyConfig } from '../utils/utils.js';
1
+ import { outputSuccess, outputInfo, outputWarn, consoleError } from '@shopify/cli-kit/node/output';
2
+ import { verifyConfig, stderrLogger } from '../utils/utils.js';
3
3
  import { buildInitiate } from './build-initiate.js';
4
4
  import { buildCancel } from './build-cancel.js';
5
5
  import { getUploadFiles } from './get-upload-files.js';
6
6
  import { deploymentInitiate } from './deployment-initiate.js';
7
7
  import { deploymentComplete } from './deployment-complete.js';
8
- import { deploymentCancel } from './deployment-cancel.js';
8
+ import { deploymentCancel, DeploymentCancelReason } from './deployment-cancel.js';
9
9
  import { uploadFiles } from './upload-files.js';
10
10
  import { buildProject } from './build-project.js';
11
11
  import { getMetadata, createLabels, getEnvironmentInput } from './metadata.js';
@@ -43,31 +43,44 @@ async function createDeploy(config) {
43
43
  const urlMessage = config.publicDeployment ? "Public" : "Private";
44
44
  outputSuccess(
45
45
  `Deployment complete.
46
- ${urlMessage} preview URL: ${deploymentCompleteOp.deployment.url}`
46
+ ${urlMessage} preview URL: ${deploymentCompleteOp.deployment.url}`,
47
+ stderrLogger
47
48
  );
49
+ if (metadata.name !== "none") {
50
+ outputInfo(deploymentCompleteOp.deployment.url);
51
+ }
48
52
  } catch (error) {
49
53
  if (!(error instanceof Error)) {
50
54
  console.error("Unknown error", error);
51
55
  return;
52
56
  }
53
57
  if (build.id && !buildCompleted) {
54
- outputWarn(`Build failed with: ${error.message}, cancelling build.`);
58
+ outputWarn(
59
+ `Build failed with: ${error.message}, cancelling build.`,
60
+ stderrLogger
61
+ );
55
62
  await buildCancel(config, build.id, error.message).catch((err) => {
56
63
  if (err instanceof Error) {
57
- outputWarn(`Failed to cancel build: ${err.message}`);
64
+ outputWarn(`Failed to cancel build: ${err.message}`, stderrLogger);
58
65
  }
59
66
  });
60
67
  } else if (deployment?.deployment.id) {
61
68
  outputWarn(
62
- `Deployment failed with: ${error.message}, cancelling deployment.`
69
+ `Deployment failed with: ${error.message}, cancelling deployment.`,
70
+ stderrLogger
63
71
  );
64
- await deploymentCancel(config, deployment.deployment.id, "failed").catch(
65
- (err) => {
66
- if (err instanceof Error) {
67
- outputWarn(`Failed to cancel deployment: ${err.message}`);
68
- }
72
+ await deploymentCancel(
73
+ config,
74
+ deployment.deployment.id,
75
+ DeploymentCancelReason.Failed
76
+ ).catch((err) => {
77
+ if (err instanceof Error) {
78
+ outputWarn(
79
+ `Failed to cancel deployment: ${err.message}`,
80
+ stderrLogger
81
+ );
69
82
  }
70
- );
83
+ });
71
84
  }
72
85
  consoleError(error.message);
73
86
  }
@@ -1,7 +1,7 @@
1
1
  import { ciPlatform } from '@shopify/cli-kit/node/context/local';
2
2
  import { getLatestGitCommit } from '@shopify/cli-kit/node/git';
3
3
  import { outputWarn } from '@shopify/cli-kit/node/output';
4
- import { maxLabelLength } from '../utils/utils.js';
4
+ import { stderrLogger, maxLabelLength } from '../utils/utils.js';
5
5
 
6
6
  async function getMetadata(config) {
7
7
  const ciInfo = ciPlatform();
@@ -19,11 +19,11 @@ async function getMetadata(config) {
19
19
  commitMessage: gitCommit.message
20
20
  };
21
21
  } catch (error) {
22
- outputWarn("No CI metadata loaded from environment");
22
+ outputWarn("No CI metadata loaded from environment", stderrLogger);
23
23
  }
24
24
  }
25
25
  return {
26
- name: ciInfo.isCI ? ciInfo.name : "unknown",
26
+ name: ciInfo.isCI ? ciInfo.name : "none",
27
27
  ...metadata,
28
28
  actor: config.metadata.user ?? metadata.actor,
29
29
  commitSha: config.metadata.version ?? metadata.commitSha,
@@ -64,7 +64,7 @@ describe("getMetadata", () => {
64
64
  const metadataResult = await getMetadata(testConfig);
65
65
  expect(metadataResult.actor).toBe("gh_author");
66
66
  expect(metadataResult.commitSha).toBe("gh_hash");
67
- expect(metadataResult.name).toBe("unknown");
67
+ expect(metadataResult.name).toBe("none");
68
68
  expect(metadataResult.url).toBe(void 0);
69
69
  });
70
70
  });
@@ -3,14 +3,14 @@ import { createFileReadStream } from '@shopify/cli-kit/node/fs';
3
3
  import { outputInfo, outputCompleted } from '@shopify/cli-kit/node/output';
4
4
  import { joinPath } from '@shopify/cli-kit/node/path';
5
5
  import { mapLimit } from 'async';
6
- import { deployDefaults } from '../utils/utils.js';
6
+ import { stderrLogger, deployDefaults } from '../utils/utils.js';
7
7
 
8
8
  async function uploadFiles(config, targets) {
9
- outputInfo(`Uploading ${targets.length} files...`);
9
+ outputInfo(`Uploading ${targets.length} files...`, stderrLogger);
10
10
  return mapLimit(targets, 6, async (target) => {
11
11
  await uploadFile(config, target);
12
12
  }).then(() => {
13
- outputCompleted(`Files uploaded successfully`);
13
+ outputCompleted(`Files uploaded successfully`, stderrLogger);
14
14
  });
15
15
  }
16
16
  async function uploadFile(config, target) {
@@ -9,6 +9,7 @@ declare enum Header {
9
9
  OxygenNamespaceHandle = "X-Oxygen-Namespace-Handle"
10
10
  }
11
11
  declare function isClientError(error: unknown): error is ClientError;
12
+ declare function stderrLogger(log: string): void;
12
13
  declare const maxLabelLength = 90;
13
14
  declare function parseToken(inputToken: string): DeploymentToken;
14
15
  interface VerifyConfigParams {
@@ -17,4 +18,4 @@ interface VerifyConfigParams {
17
18
  }
18
19
  declare function verifyConfig({ config, performedBuild, }: VerifyConfigParams): Promise<void>;
19
20
 
20
- export { Header, deployDefaults, errorHandler, getBuildCommandFromLockFile, isClientError, maxLabelLength, parseToken, verifyConfig };
21
+ export { Header, deployDefaults, errorHandler, getBuildCommandFromLockFile, isClientError, maxLabelLength, parseToken, stderrLogger, verifyConfig };
@@ -43,13 +43,17 @@ function getBuildCommandFromLockFile(config) {
43
43
  }
44
44
  if (foundLockFiles.length > 1) {
45
45
  const lockFilesList = foundLockFiles.map(({ lockFileName }) => lockFileName).join(", ");
46
- outputWarn(`Warning: Multiple lock files found: (${lockFilesList}).`);
46
+ outputWarn(
47
+ `Warning: Multiple lock files found: (${lockFilesList}).`,
48
+ stderrLogger
49
+ );
47
50
  }
48
51
  if (foundLockFiles.length > 0) {
49
52
  const { lockFileName, buildCommand } = foundLockFiles[0];
50
53
  const infoMsg = foundLockFiles.length > 1 ? "" : `Found: ${lockFileName}. `;
51
54
  outputInfo(
52
- `${infoMsg}Assuming "${buildCommand}" as build command. Use the buildCommand flag to override.`
55
+ `${infoMsg}Assuming "${buildCommand}" as build command. Use the buildCommand flag to override.`,
56
+ stderrLogger
53
57
  );
54
58
  return buildCommand;
55
59
  }
@@ -62,6 +66,10 @@ var Header = /* @__PURE__ */ ((Header2) => {
62
66
  function isClientError(error) {
63
67
  return typeof error === "object" && error !== null && "statusCode" in error;
64
68
  }
69
+ function stderrLogger(log) {
70
+ process.stderr.write(`${log}
71
+ `);
72
+ }
65
73
  const maxLabelLength = 90;
66
74
  function parseToken(inputToken) {
67
75
  try {
@@ -91,7 +99,7 @@ async function verifyConfig({
91
99
  for (const pathType of Object.keys(checkPaths)) {
92
100
  await checkPath(checkPaths[pathType], pathType);
93
101
  }
94
- const addressRegex = /^(http|https):\/\/(?:[\w-]+\.)*[\w-]+|(?:http|https):\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
102
+ const addressRegex = /^https:\/\/(?:[\w-]+\.)*[\w-]+|^https:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
95
103
  if (!addressRegex.test(config.deploymentUrl)) {
96
104
  throw new Error(`Invalid deployment service URL: ${config.deploymentUrl}`);
97
105
  }
@@ -100,7 +108,8 @@ async function checkPath(path, pathType) {
100
108
  if (!await fileExists(path)) {
101
109
  if (pathType === "assets") {
102
110
  outputWarn(
103
- `Use the "workerOnly" flag to perform a worker-only deployment.`
111
+ `Use the "workerOnly" flag to perform a worker-only deployment.`,
112
+ stderrLogger
104
113
  );
105
114
  }
106
115
  throw new Error(`Path not found: ${path}`);
@@ -123,4 +132,4 @@ function convertKeysToCamelCase(obj) {
123
132
  return obj;
124
133
  }
125
134
 
126
- export { Header, deployDefaults, errorHandler, getBuildCommandFromLockFile, isClientError, maxLabelLength, parseToken, verifyConfig };
135
+ export { Header, deployDefaults, errorHandler, getBuildCommandFromLockFile, isClientError, maxLabelLength, parseToken, stderrLogger, verifyConfig };
@@ -29,7 +29,8 @@ describe("getBuildCommandFromLockfile", () => {
29
29
  const buildCommand = utils.getBuildCommandFromLockFile(testConfig);
30
30
  expect(buildCommand).toBe("pnpm run build");
31
31
  expect(outputInfo).toHaveBeenCalledWith(
32
- 'Found: pnpm-lock.yaml. Assuming "pnpm run build" as build command. Use the buildCommand flag to override.'
32
+ 'Found: pnpm-lock.yaml. Assuming "pnpm run build" as build command. Use the buildCommand flag to override.',
33
+ utils.stderrLogger
33
34
  );
34
35
  });
35
36
  test("getBuildCommandFromLockfile the first build command and warns when multiple lockfiles are found", async () => {
@@ -37,10 +38,12 @@ describe("getBuildCommandFromLockfile", () => {
37
38
  const buildCommand = utils.getBuildCommandFromLockFile(testConfig);
38
39
  expect(buildCommand).toBe("pnpm run build");
39
40
  expect(outputInfo).toHaveBeenCalledWith(
40
- 'Assuming "pnpm run build" as build command. Use the buildCommand flag to override.'
41
+ 'Assuming "pnpm run build" as build command. Use the buildCommand flag to override.',
42
+ utils.stderrLogger
41
43
  );
42
44
  expect(outputWarn).toHaveBeenCalledWith(
43
- "Warning: Multiple lock files found: (pnpm-lock.yaml, yarn.lock)."
45
+ "Warning: Multiple lock files found: (pnpm-lock.yaml, yarn.lock).",
46
+ utils.stderrLogger
44
47
  );
45
48
  });
46
49
  });
@@ -94,7 +97,8 @@ describe("verifyConfig", () => {
94
97
  )
95
98
  );
96
99
  expect(outputWarn).toHaveBeenCalledWith(
97
- `Use the "workerOnly" flag to perform a worker-only deployment.`
100
+ `Use the "workerOnly" flag to perform a worker-only deployment.`,
101
+ utils.stderrLogger
98
102
  );
99
103
  });
100
104
  test("verifyConfig throws when the workerDir cannot be resolved after a build", async () => {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.2.0",
2
+ "version": "1.3.1",
3
3
  "commands": {
4
4
  "oxygen:deploy": {
5
5
  "id": "oxygen:deploy",
@@ -90,21 +90,21 @@
90
90
  "metadataUrl": {
91
91
  "name": "metadataUrl",
92
92
  "type": "option",
93
- "description": "URL that links to the deployment",
93
+ "description": "URL that links to the deployment. Will be saved and displayed in the Shopify admin",
94
94
  "required": false,
95
95
  "multiple": false
96
96
  },
97
97
  "metadataUser": {
98
98
  "name": "metadataUser",
99
99
  "type": "option",
100
- "description": "User that initiated the deployment",
100
+ "description": "User that initiated the deployment. Will be saved and displayed in the Shopify admin",
101
101
  "required": false,
102
102
  "multiple": false
103
103
  },
104
104
  "metadataVersion": {
105
105
  "name": "metadataVersion",
106
106
  "type": "option",
107
- "description": "A version identifier for the deployment",
107
+ "description": "A version identifier for the deployment. Will be saved and displayed in the Shopify admin",
108
108
  "required": false,
109
109
  "multiple": false
110
110
  }
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "@shopify:registry": "https://registry.npmjs.org"
6
6
  },
7
7
  "license": "MIT",
8
- "version": "1.2.0",
8
+ "version": "1.3.1",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "build": "tsup --clean --config ./tsup.config.ts && oclif manifest",
@@ -13,7 +13,7 @@
13
13
  "dev": "tsup --watch --config ./tsup.config.ts",
14
14
  "typecheck": "tsc --noEmit",
15
15
  "lint": "eslint --ext .js,.ts --max-warnings 0 src",
16
- "lint:format": "npm run lint --fix",
16
+ "lint:format": "prettier --write \"**/*.*\" && npm run lint --fix",
17
17
  "generate:manifest": "oclif manifest",
18
18
  "prepack": "npm install && npm run build",
19
19
  "release": "npm run build && npx changeset publish",
@@ -31,7 +31,7 @@
31
31
  "/oclif.manifest.json"
32
32
  ],
33
33
  "dependencies": {
34
- "@oclif/core": "2.1.4",
34
+ "@oclif/core": "2.8.6",
35
35
  "@shopify/cli-kit": "^3.46.0",
36
36
  "async": "^3.2.4"
37
37
  },
@@ -41,14 +41,14 @@
41
41
  "@shopify/prettier-config": "^1.1.2",
42
42
  "@types/async": "^3.2.18",
43
43
  "@types/node": "^18.15.11",
44
- "@types/prettier": "^2.7.2",
45
- "eslint": "^8.37.0",
44
+ "@types/prettier": "^2.7.3",
45
+ "eslint": "^8.42.0",
46
46
  "node-fetch": "^3.3.1",
47
47
  "oclif": "^3",
48
48
  "tsup": "^6.7.0",
49
- "typescript": "^5.0.3",
50
- "vite": "^4.2.1",
51
- "vitest": "^0.29.8"
49
+ "typescript": "^5.1.3",
50
+ "vite": "^4.3.9",
51
+ "vitest": "^0.32.0"
52
52
  },
53
53
  "prettier": "@shopify/prettier-config",
54
54
  "oclif": {