@shopify/oxygen-cli 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -35,14 +35,14 @@ oxygen:deploy [options]
35
35
  ### Options
36
36
 
37
37
  - -t, --token <token>: (required) Oxygen deployment token. Can also be set using the `OXYGEN_DEPLOYMENT_TOKEN` environment variable (see below).
38
- - -r, --rootPath <rootPath>: Root path (defaults to the current working directory).
38
+ - -p, --path <rootPath>: Root path (defaults to the current working directory).
39
39
  - -e, --environmentTag <environmentTag>: Tag of the environment to deploy to. Defaults to branch name in supported CI environments (see below).
40
40
  - -w, --workerFolder <workerFolder>: Worker folder (default: `dist/worker/`).
41
41
  - -a, --assetsFolder <assetsFolder>: Assets folder (default: `dist/client/`).
42
42
  - -o, --workerOnly: Worker only deployment.
43
43
  - -s, --skipBuild: Skip running build command.
44
- - -p, --publicDeployment: set the deployment to be publicly accessible.
45
44
  - -b, --buildCommand <buildCommand>: Build command (default: `yarn build`).
45
+ - --publicDeployment: set the deployment to be publicly accessible.
46
46
  - --metadataUrl <metadataUrl>: URL that links to the deployment.
47
47
  - --metadataUser <metadataUser>: User that initiated the deployment.
48
48
  - --metadataVersion <metadataVersion>: A version identifier for the deployment.
@@ -52,7 +52,7 @@ oxygen:deploy [options]
52
52
  ### Example:
53
53
 
54
54
  ```
55
- oxygen:deploy -t my-deployment-token -e staging --rootPath="/my-project" --workerOnly
55
+ oxygen:deploy -t my-deployment-token -e staging --path="/my-project" --workerOnly
56
56
  ```
57
57
 
58
58
  This command will deploy your Oxygen project to the staging environment with the provided deployment token and root path. No static assets will be uploaded.
@@ -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).
@@ -6,8 +6,8 @@ declare class Deploy extends Command {
6
6
  static description: string;
7
7
  static hidden: boolean;
8
8
  static flags: {
9
- token: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
10
- rootPath: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
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
11
  environmentTag: _oclif_core_lib_interfaces_parser_js.OptionFlag<string | undefined, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
12
12
  workerFolder: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
13
13
  assetsFolder: _oclif_core_lib_interfaces_parser_js.OptionFlag<string, _oclif_core_lib_interfaces_parser_js.CustomOptions>;
@@ -12,10 +12,10 @@ class Deploy extends Command {
12
12
  char: "t",
13
13
  description: "Oxygen deployment token",
14
14
  env: "OXYGEN_DEPLOYMENT_TOKEN",
15
- required: true
15
+ required: false
16
16
  }),
17
- rootPath: Flags.string({
18
- char: "r",
17
+ path: Flags.string({
18
+ char: "p",
19
19
  description: "Root path",
20
20
  default: "./",
21
21
  required: false
@@ -60,24 +60,23 @@ class Deploy extends Command {
60
60
  }
61
61
  }),
62
62
  publicDeployment: Flags.boolean({
63
- char: "p",
64
63
  env: "OXYGEN_PUBLIC_DEPLOYMENT",
65
64
  description: "Marks a preview deployment as publicly accessible.",
66
65
  required: false,
67
66
  default: false
68
67
  }),
69
68
  metadataUrl: Flags.string({
70
- description: "URL that links to the deployment",
69
+ description: "URL that links to the deployment. Will be saved and displayed in the Shopify admin",
71
70
  required: false,
72
71
  env: "OXYGEN_METADATA_URL"
73
72
  }),
74
73
  metadataUser: Flags.string({
75
- description: "User that initiated the deployment",
74
+ description: "User that initiated the deployment. Will be saved and displayed in the Shopify admin",
76
75
  required: false,
77
76
  env: "OXYGEN_METADATA_USER"
78
77
  }),
79
78
  metadataVersion: Flags.string({
80
- description: "A version identifier for the deployment",
79
+ description: "A version identifier for the deployment. Will be saved and displayed in the Shopify admin",
81
80
  required: false,
82
81
  env: "OXYGEN_METADATA_VERSION"
83
82
  })
@@ -102,7 +101,7 @@ class Deploy extends Command {
102
101
  version: flags.metadataVersion
103
102
  },
104
103
  publicDeployment: flags.publicDeployment,
105
- rootPath: normalizePath(flags.rootPath),
104
+ rootPath: normalizePath(flags.path),
106
105
  skipBuild: flags.skipBuild,
107
106
  workerDir: normalizePath(flags.workerFolder),
108
107
  workerOnly: flags.workerOnly
@@ -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 };
@@ -4,6 +4,11 @@ import { outputInfo } from '@shopify/cli-kit/node/output';
4
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,
@@ -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,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
@@ -5,7 +5,7 @@ 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';
@@ -69,16 +69,18 @@ ${urlMessage} preview URL: ${deploymentCompleteOp.deployment.url}`,
69
69
  `Deployment failed with: ${error.message}, cancelling deployment.`,
70
70
  stderrLogger
71
71
  );
72
- await deploymentCancel(config, deployment.deployment.id, "failed").catch(
73
- (err) => {
74
- if (err instanceof Error) {
75
- outputWarn(
76
- `Failed to cancel deployment: ${err.message}`,
77
- stderrLogger
78
- );
79
- }
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
+ );
80
82
  }
81
- );
83
+ });
82
84
  }
83
85
  consoleError(error.message);
84
86
  }
@@ -72,6 +72,11 @@ function stderrLogger(log) {
72
72
  }
73
73
  const maxLabelLength = 90;
74
74
  function parseToken(inputToken) {
75
+ if (!inputToken) {
76
+ throw new Error(
77
+ "No deployment token provided. Use the --token flag to set a deployment token."
78
+ );
79
+ }
75
80
  try {
76
81
  const decodedToken = Buffer.from(inputToken, "base64").toString("utf-8");
77
82
  const rawToken = JSON.parse(decodedToken);
@@ -99,7 +104,7 @@ async function verifyConfig({
99
104
  for (const pathType of Object.keys(checkPaths)) {
100
105
  await checkPath(checkPaths[pathType], pathType);
101
106
  }
102
- const addressRegex = /^(http|https):\/\/(?:[\w-]+\.)*[\w-]+|(?:http|https):\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
107
+ const addressRegex = /^https:\/\/(?:[\w-]+\.)*[\w-]+|^https:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/;
103
108
  if (!addressRegex.test(config.deploymentUrl)) {
104
109
  throw new Error(`Invalid deployment service URL: ${config.deploymentUrl}`);
105
110
  }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3.0",
2
+ "version": "1.4.0",
3
3
  "commands": {
4
4
  "oxygen:deploy": {
5
5
  "id": "oxygen:deploy",
@@ -16,13 +16,13 @@
16
16
  "type": "option",
17
17
  "char": "t",
18
18
  "description": "Oxygen deployment token",
19
- "required": true,
19
+ "required": false,
20
20
  "multiple": false
21
21
  },
22
- "rootPath": {
23
- "name": "rootPath",
22
+ "path": {
23
+ "name": "path",
24
24
  "type": "option",
25
- "char": "r",
25
+ "char": "p",
26
26
  "description": "Root path",
27
27
  "required": false,
28
28
  "multiple": false,
@@ -82,7 +82,6 @@
82
82
  "publicDeployment": {
83
83
  "name": "publicDeployment",
84
84
  "type": "boolean",
85
- "char": "p",
86
85
  "description": "Marks a preview deployment as publicly accessible.",
87
86
  "required": false,
88
87
  "allowNo": false
@@ -90,21 +89,21 @@
90
89
  "metadataUrl": {
91
90
  "name": "metadataUrl",
92
91
  "type": "option",
93
- "description": "URL that links to the deployment",
92
+ "description": "URL that links to the deployment. Will be saved and displayed in the Shopify admin",
94
93
  "required": false,
95
94
  "multiple": false
96
95
  },
97
96
  "metadataUser": {
98
97
  "name": "metadataUser",
99
98
  "type": "option",
100
- "description": "User that initiated the deployment",
99
+ "description": "User that initiated the deployment. Will be saved and displayed in the Shopify admin",
101
100
  "required": false,
102
101
  "multiple": false
103
102
  },
104
103
  "metadataVersion": {
105
104
  "name": "metadataVersion",
106
105
  "type": "option",
107
- "description": "A version identifier for the deployment",
106
+ "description": "A version identifier for the deployment. Will be saved and displayed in the Shopify admin",
108
107
  "required": false,
109
108
  "multiple": false
110
109
  }
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.3.0",
8
+ "version": "1.4.0",
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,8 +31,8 @@
31
31
  "/oclif.manifest.json"
32
32
  ],
33
33
  "dependencies": {
34
- "@oclif/core": "2.1.4",
35
- "@shopify/cli-kit": "^3.46.0",
34
+ "@oclif/core": "2.8.7",
35
+ "@shopify/cli-kit": "^3.46.5",
36
36
  "async": "^3.2.4"
37
37
  },
38
38
  "devDependencies": {
@@ -40,15 +40,15 @@
40
40
  "@shopify/eslint-plugin": "^42.1.0",
41
41
  "@shopify/prettier-config": "^1.1.2",
42
42
  "@types/async": "^3.2.18",
43
- "@types/node": "^18.15.11",
44
- "@types/prettier": "^2.7.2",
45
- "eslint": "^8.37.0",
43
+ "@types/node": "^20.3.1",
44
+ "@types/prettier": "^2.7.3",
45
+ "eslint": "^8.43.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.2"
52
52
  },
53
53
  "prettier": "@shopify/prettier-config",
54
54
  "oclif": {