@shopify/oxygen-cli 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,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 { DeploymentCancelQuery } from './graphql/deployment-cancel.js';
6
6
 
7
7
  async function deploymentCancel(config, deploymentId, reason) {
@@ -25,7 +25,7 @@ async function deploymentCancel(config, deploymentId, reason) {
25
25
  `Failed to cancel deployment: ${response.deploymentCancel.userErrors[0]?.message}`
26
26
  );
27
27
  }
28
- outputInfo(`Deployment with id ${deploymentId} cancelled.`);
28
+ outputInfo(`Deployment with id ${deploymentId} cancelled.`, stderrLogger);
29
29
  return response.deploymentCancel;
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 { 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,5 +1,5 @@
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';
@@ -43,28 +43,39 @@ 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
72
  await deploymentCancel(config, deployment.deployment.id, "failed").catch(
65
73
  (err) => {
66
74
  if (err instanceof Error) {
67
- outputWarn(`Failed to cancel deployment: ${err.message}`);
75
+ outputWarn(
76
+ `Failed to cancel deployment: ${err.message}`,
77
+ stderrLogger
78
+ );
68
79
  }
69
80
  }
70
81
  );
@@ -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 {
@@ -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.0",
3
3
  "commands": {
4
4
  "oxygen:deploy": {
5
5
  "id": "oxygen:deploy",
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.0",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "build": "tsup --clean --config ./tsup.config.ts && oclif manifest",