@shopify/oxygen-cli 2.1.3-unstable.202310231610.0 → 2.2.1-unstable.202310241146.0

Sign up to get free protection for your applications and to get access to all the features.
package/README.md CHANGED
@@ -109,7 +109,7 @@ The `environmentTag` command line option can be used to override this. If not pr
109
109
 
110
110
  ## Error reporting with Bugsnag
111
111
 
112
- `oxygen-cli` Uses Bugsnag to collect errors that occur during the deployment process. This helps Shopify diagnose and fix issues more efficiently, improving the reliability of the tool.
112
+ `oxygen-cli` Uses Bugsnag to collect errors that occur during the deployment process. This helps Shopify diagnose and fix issues more efficiently, improving the reliability of the tool. In addition, `oxygen-cli` collects anonymous usage data. This data is used to understand the performance of the tool and to improve its efficiency.
113
113
 
114
114
  Bugsnag collects error reports which include stack traces, the type of error, and other relevant data that can help in diagnosing the issue. Please be aware that as part of the error reporting process, some user data related to your Continuous Integration (CI) environment may be logged. This data is used solely for the purpose of diagnosing and resolving issues.
115
115
 
@@ -111,6 +111,7 @@ class Deploy extends Command {
111
111
  async run() {
112
112
  try {
113
113
  const { flags } = await this.parse(Deploy);
114
+ const rootPath = normalizePath(flags.path);
114
115
  initializeBugsnag(flags.disableBugsnag);
115
116
  const Bugsnag = getBugsnag();
116
117
  const deploymentUrl = (
@@ -136,7 +137,7 @@ class Deploy extends Command {
136
137
  version: flags.metadataVersion
137
138
  },
138
139
  publicDeployment: flags.publicDeployment,
139
- rootPath: normalizePath(flags.path),
140
+ rootPath,
140
141
  skipBuild: flags.skipBuild,
141
142
  skipVerification: flags.skipVerification,
142
143
  workerDir: normalizePath(flags.workerFolder),
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/oxygen/deploy.ts"],"names":[],"mappings":"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","sourcesContent":["import {Command, Flags} from '@oclif/core';\nimport {consoleError} from '@shopify/cli-kit/node/output';\nimport {normalizePath} from '@shopify/cli-kit/node/path';\n\nimport {getBugsnag, initializeBugsnag} from '../../utils/bugsnag.js';\nimport {createDeploy} from '../../deploy/index.js';\nimport {\n deployDefaults,\n getBuildCommandFromLockFile,\n parseToken,\n verifyConfig,\n} from '../../utils/utils.js';\nimport type {DeploymentConfig} from '../../deploy/types.js';\nimport {VerificationError} from '../../deploy/types.js';\n\nexport class Deploy extends Command {\n static description = 'Creates a deployment to Oxygen';\n static hidden = false;\n static flags = {\n assetsFolder: Flags.string({\n char: 'a',\n description: 'Assets folder',\n default: String(deployDefaults.assetsDirDefault),\n required: false,\n }),\n buildCommand: Flags.string({\n char: 'b',\n description: 'Build command',\n required: false,\n default: String(deployDefaults.buildCommandDefault!),\n parse: (input): Promise<string> => {\n this.hasCustomBuildCommand = true;\n return Promise.resolve(input);\n },\n }),\n disableBugsnag: Flags.boolean({\n description: 'Disable Bugsnag error reporting',\n required: false,\n default: false,\n }),\n environmentTag: Flags.string({\n char: 'e',\n description: 'Tag of the environment to deploy to',\n required: false,\n }),\n verificationMaxDuration: Flags.integer({\n char: 'd',\n description:\n 'the maximum duration (in seconds) that the deployment verification step is allowed to run before it is considered failed.',\n min: 10,\n max: 300,\n required: false,\n default: deployDefaults.verificationMaxDurationDefault as number,\n }),\n path: Flags.string({\n char: 'p',\n description: 'Root path',\n default: './',\n required: false,\n }),\n publicDeployment: Flags.boolean({\n env: 'OXYGEN_PUBLIC_DEPLOYMENT',\n description: 'Marks a preview deployment as publicly accessible.',\n required: false,\n default: false,\n }),\n skipBuild: Flags.boolean({\n char: 's',\n description: 'Skip running build command',\n required: false,\n default: false,\n }),\n skipVerification: Flags.boolean({\n char: 'v',\n description: 'Skip running deployment verification step',\n required: false,\n default: false,\n }),\n token: Flags.string({\n char: 't',\n description: 'Oxygen deployment token',\n env: 'OXYGEN_DEPLOYMENT_TOKEN',\n required: true,\n }),\n workerFolder: Flags.string({\n char: 'w',\n description: 'Worker folder',\n default: String(deployDefaults.workerDirDefault),\n required: false,\n }),\n workerOnly: Flags.boolean({\n char: 'o',\n description: 'Worker only deployment',\n default: false,\n required: false,\n }),\n metadataDescription: Flags.string({\n description:\n 'Description of the deployment. Will be saved and displayed in the Shopify admin',\n required: false,\n env: 'OXYGEN_METADATA_DESCRIPTION',\n }),\n metadataUrl: Flags.string({\n description:\n 'URL that links to the deployment. Will be saved and displayed in the Shopify admin',\n required: false,\n env: 'OXYGEN_METADATA_URL',\n }),\n metadataUser: Flags.string({\n description:\n 'User that initiated the deployment. Will be saved and displayed in the Shopify admin',\n required: false,\n env: 'OXYGEN_METADATA_USER',\n }),\n metadataVersion: Flags.string({\n description:\n 'A version identifier for the deployment. Will be saved and displayed in the Shopify admin',\n required: false,\n env: 'OXYGEN_METADATA_VERSION',\n }),\n };\n\n static hasCustomBuildCommand = false;\n\n async run() {\n try {\n const {flags} = await this.parse(Deploy);\n\n initializeBugsnag(flags.disableBugsnag);\n const Bugsnag = getBugsnag();\n\n const deploymentUrl =\n // eslint-disable-next-line no-process-env\n process.env.UNSAFE_OXYGEN_DEPLOYMENT_URL ||\n 'https://oxygen.shopifyapps.com';\n Bugsnag?.addMetadata('flags', {\n flags: {...flags, token: '***'},\n deploymentUrl,\n });\n\n const config: DeploymentConfig = {\n assetsDir: normalizePath(flags.assetsFolder),\n bugsnag: !flags.disableBugsnag,\n buildCommand: flags.buildCommand!,\n deploymentToken: parseToken(flags.token!),\n environmentTag: flags.environmentTag,\n deploymentUrl,\n verificationMaxDuration: flags.verificationMaxDuration,\n metadata: {\n description: flags.metadataDescription,\n url: flags.metadataUrl,\n user: flags.metadataUser,\n version: flags.metadataVersion,\n },\n publicDeployment: flags.publicDeployment,\n rootPath: normalizePath(flags.path),\n skipBuild: flags.skipBuild,\n skipVerification: flags.skipVerification,\n workerDir: normalizePath(flags.workerFolder),\n workerOnly: flags.workerOnly,\n };\n\n await verifyConfig({config});\n if (!Deploy.hasCustomBuildCommand && !config.skipBuild) {\n config.buildCommand = getBuildCommandFromLockFile(config);\n }\n await createDeploy({config});\n } catch (error) {\n const Bugsnag = getBugsnag();\n if (Bugsnag && (error instanceof Error || typeof error === 'string')) {\n Bugsnag.notify(error);\n }\n\n if (!(error instanceof Error)) {\n consoleError(error as string);\n } else if (!(error instanceof VerificationError)) {\n consoleError(error.message);\n }\n\n this.exit(1);\n }\n }\n}\n"]}
1
+ {"version":3,"sources":["../../../src/commands/oxygen/deploy.ts"],"names":[],"mappings":"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","sourcesContent":["import {Command, Flags} from '@oclif/core';\nimport {consoleError} from '@shopify/cli-kit/node/output';\nimport {normalizePath} from '@shopify/cli-kit/node/path';\n\nimport {getBugsnag, initializeBugsnag} from '../../utils/bugsnag.js';\nimport {createDeploy} from '../../deploy/index.js';\nimport {\n deployDefaults,\n getBuildCommandFromLockFile,\n parseToken,\n verifyConfig,\n} from '../../utils/utils.js';\nimport type {DeploymentConfig} from '../../deploy/types.js';\nimport {VerificationError} from '../../deploy/types.js';\n\nexport class Deploy extends Command {\n static description = 'Creates a deployment to Oxygen';\n static hidden = false;\n static flags = {\n assetsFolder: Flags.string({\n char: 'a',\n description: 'Assets folder',\n default: String(deployDefaults.assetsDirDefault),\n required: false,\n }),\n buildCommand: Flags.string({\n char: 'b',\n description: 'Build command',\n required: false,\n default: String(deployDefaults.buildCommandDefault!),\n parse: (input): Promise<string> => {\n this.hasCustomBuildCommand = true;\n return Promise.resolve(input);\n },\n }),\n disableBugsnag: Flags.boolean({\n description: 'Disable Bugsnag error reporting',\n required: false,\n default: false,\n }),\n environmentTag: Flags.string({\n char: 'e',\n description: 'Tag of the environment to deploy to',\n required: false,\n }),\n verificationMaxDuration: Flags.integer({\n char: 'd',\n description:\n 'the maximum duration (in seconds) that the deployment verification step is allowed to run before it is considered failed.',\n min: 10,\n max: 300,\n required: false,\n default: deployDefaults.verificationMaxDurationDefault as number,\n }),\n path: Flags.string({\n char: 'p',\n description: 'Root path',\n default: './',\n required: false,\n }),\n publicDeployment: Flags.boolean({\n env: 'OXYGEN_PUBLIC_DEPLOYMENT',\n description: 'Marks a preview deployment as publicly accessible.',\n required: false,\n default: false,\n }),\n skipBuild: Flags.boolean({\n char: 's',\n description: 'Skip running build command',\n required: false,\n default: false,\n }),\n skipVerification: Flags.boolean({\n char: 'v',\n description: 'Skip running deployment verification step',\n required: false,\n default: false,\n }),\n token: Flags.string({\n char: 't',\n description: 'Oxygen deployment token',\n env: 'OXYGEN_DEPLOYMENT_TOKEN',\n required: true,\n }),\n workerFolder: Flags.string({\n char: 'w',\n description: 'Worker folder',\n default: String(deployDefaults.workerDirDefault),\n required: false,\n }),\n workerOnly: Flags.boolean({\n char: 'o',\n description: 'Worker only deployment',\n default: false,\n required: false,\n }),\n metadataDescription: Flags.string({\n description:\n 'Description of the deployment. Will be saved and displayed in the Shopify admin',\n required: false,\n env: 'OXYGEN_METADATA_DESCRIPTION',\n }),\n metadataUrl: Flags.string({\n description:\n 'URL that links to the deployment. Will be saved and displayed in the Shopify admin',\n required: false,\n env: 'OXYGEN_METADATA_URL',\n }),\n metadataUser: Flags.string({\n description:\n 'User that initiated the deployment. Will be saved and displayed in the Shopify admin',\n required: false,\n env: 'OXYGEN_METADATA_USER',\n }),\n metadataVersion: Flags.string({\n description:\n 'A version identifier for the deployment. Will be saved and displayed in the Shopify admin',\n required: false,\n env: 'OXYGEN_METADATA_VERSION',\n }),\n };\n\n static hasCustomBuildCommand = false;\n\n async run() {\n try {\n const {flags} = await this.parse(Deploy);\n const rootPath = normalizePath(flags.path);\n\n initializeBugsnag(flags.disableBugsnag);\n const Bugsnag = getBugsnag();\n\n const deploymentUrl =\n // eslint-disable-next-line no-process-env\n process.env.UNSAFE_OXYGEN_DEPLOYMENT_URL ||\n 'https://oxygen.shopifyapps.com';\n Bugsnag?.addMetadata('flags', {\n flags: {...flags, token: '***'},\n deploymentUrl,\n });\n\n const config: DeploymentConfig = {\n assetsDir: normalizePath(flags.assetsFolder),\n bugsnag: !flags.disableBugsnag,\n buildCommand: flags.buildCommand!,\n deploymentToken: parseToken(flags.token!),\n environmentTag: flags.environmentTag,\n deploymentUrl,\n verificationMaxDuration: flags.verificationMaxDuration,\n metadata: {\n description: flags.metadataDescription,\n url: flags.metadataUrl,\n user: flags.metadataUser,\n version: flags.metadataVersion,\n },\n publicDeployment: flags.publicDeployment,\n rootPath,\n skipBuild: flags.skipBuild,\n skipVerification: flags.skipVerification,\n workerDir: normalizePath(flags.workerFolder),\n workerOnly: flags.workerOnly,\n };\n\n await verifyConfig({config});\n if (!Deploy.hasCustomBuildCommand && !config.skipBuild) {\n config.buildCommand = getBuildCommandFromLockFile(config);\n }\n await createDeploy({config});\n } catch (error) {\n const Bugsnag = getBugsnag();\n if (Bugsnag && (error instanceof Error || typeof error === 'string')) {\n Bugsnag.notify(error);\n }\n\n if (!(error instanceof Error)) {\n consoleError(error as string);\n } else if (!(error instanceof VerificationError)) {\n consoleError(error.message);\n }\n\n this.exit(1);\n }\n }\n}\n"]}
@@ -1,9 +1,11 @@
1
+ import { MetricsExporter } from '../utils/metrics-exporter.js';
1
2
  import { DeploymentConfig, DeploymentHooks } from './types.js';
2
3
 
3
4
  interface BuildProjectOptions {
4
5
  config: DeploymentConfig;
5
6
  assetPath?: string;
6
7
  hooks?: DeploymentHooks;
8
+ metricsExporter?: MetricsExporter;
7
9
  }
8
10
  declare function buildProject(options: BuildProjectOptions): Promise<void>;
9
11
 
@@ -1,17 +1,21 @@
1
1
  import { spawn } from 'child_process';
2
2
  import { PassThrough } from 'stream';
3
3
  import { getBugsnag } from '../utils/bugsnag.js';
4
+ import { MetricName } from '../utils/metrics-exporter.js';
4
5
 
5
6
  async function buildProject(options) {
6
7
  const { config, assetPath, hooks } = options;
7
8
  const Bugsnag = getBugsnag();
8
9
  if (hooks?.buildFunction) {
9
- try {
10
- await hooks.buildFunction(assetPath);
11
- } catch (error) {
10
+ const startTime = performance.now();
11
+ return hooks.buildFunction(assetPath).then(() => {
12
+ options.metricsExporter?.add(
13
+ MetricName.BuildTime,
14
+ performance.now() - startTime
15
+ );
16
+ }).catch((error) => {
12
17
  throw new Error(`Build function failed with error: ${error}`);
13
- }
14
- return;
18
+ });
15
19
  }
16
20
  const assetPathEnvironment = assetPath ? { HYDROGEN_ASSET_BASE_URL: assetPath } : {};
17
21
  try {
@@ -22,6 +26,7 @@ async function buildProject(options) {
22
26
  stderr += chunk;
23
27
  process.stderr.write(chunk);
24
28
  });
29
+ const startTime = performance.now();
25
30
  const buildCommand = spawn(config.buildCommand, [], {
26
31
  stdio: ["inherit", "pipe", "pipe"],
27
32
  env: {
@@ -35,6 +40,12 @@ async function buildProject(options) {
35
40
  buildCommand.stderr?.pipe(stderrStream);
36
41
  buildCommand.stdout?.pipe(stderrStream);
37
42
  buildCommand.on("close", (code) => {
43
+ if (code === 0) {
44
+ options.metricsExporter?.add(
45
+ MetricName.BuildTime,
46
+ performance.now() - startTime
47
+ );
48
+ }
38
49
  Bugsnag?.addMetadata("buildCommand", {
39
50
  command: config.buildCommand,
40
51
  stderr,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/deploy/build-project.ts"],"names":[],"mappings":"AAAA,SAAQ,aAAY;AACpB,SAAQ,mBAA4B;AAEpC,SAAQ,kBAAiB;AAUzB,eAAsB,aAAa,SAA8B;AAC/D,QAAM,EAAC,QAAQ,WAAW,MAAK,IAAI;AACnC,QAAM,UAAU,WAAW;AAE3B,MAAI,OAAO,eAAe;AACxB,QAAI;AACF,YAAM,MAAM,cAAc,SAAS;AAAA,IACrC,SAAS,OAAO;AACd,YAAM,IAAI,MAAM,qCAAqC,KAAK,EAAE;AAAA,IAC9D;AACA;AAAA,EACF;AAEA,QAAM,uBAAuB,YACzB,EAAC,yBAAyB,UAAS,IACnC,CAAC;AACL,MAAI;AACF,UAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AACrC,UAAI,SAAS;AACb,YAAM,eAAe,IAAI,YAAY;AAErC,mBAAa,GAAG,QAAQ,CAAC,UAAU;AACjC,kBAAU;AACV,gBAAQ,OAAO,MAAM,KAAK;AAAA,MAC5B,CAAC;AAED,YAAM,eAAe,MAAM,OAAO,cAAe,CAAC,GAAG;AAAA,QACnD,OAAO,CAAC,WAAW,QAAQ,MAAM;AAAA,QACjC,KAAK;AAAA;AAAA,UAEH,GAAG,QAAQ;AAAA,UACX,GAAG;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,OAAO;AAAA,MACT,CAAC;AAED,MAAC,aAAa,QAA4B,KAAK,YAAY;AAC3D,MAAC,aAAa,QAA4B,KAAK,YAAY;AAE3D,mBAAa,GAAG,SAAS,CAAC,SAAS;AACjC,iBAAS,YAAY,gBAAgB;AAAA,UACnC,SAAS,OAAO;AAAA,UAChB;AAAA,UACA;AAAA,QACF,CAAC;AACD,YAAI,SAAS,GAAG;AACd,iBAAO,IAAI;AACX;AAAA,QACF;AACA,gBAAQ,IAAI;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,iCAAiC,KAAK,EAAE;AAAA,EAC1D;AACF","sourcesContent":["import {spawn} from 'child_process';\nimport {PassThrough, Readable} from 'stream';\n\nimport {getBugsnag} from '../utils/bugsnag.js';\n\nimport {DeploymentConfig, DeploymentHooks} from './types.js';\n\ninterface BuildProjectOptions {\n config: DeploymentConfig;\n assetPath?: string;\n hooks?: DeploymentHooks;\n}\n\nexport async function buildProject(options: BuildProjectOptions) {\n const {config, assetPath, hooks} = options;\n const Bugsnag = getBugsnag();\n\n if (hooks?.buildFunction) {\n try {\n await hooks.buildFunction(assetPath);\n } catch (error) {\n throw new Error(`Build function failed with error: ${error}`);\n }\n return;\n }\n\n const assetPathEnvironment = assetPath\n ? {HYDROGEN_ASSET_BASE_URL: assetPath}\n : {};\n try {\n await new Promise((resolve, reject) => {\n let stderr = '';\n const stderrStream = new PassThrough();\n\n stderrStream.on('data', (chunk) => {\n stderr += chunk;\n process.stderr.write(chunk);\n });\n\n const buildCommand = spawn(config.buildCommand!, [], {\n stdio: ['inherit', 'pipe', 'pipe'],\n env: {\n // eslint-disable-next-line no-process-env\n ...process.env,\n ...assetPathEnvironment,\n },\n cwd: config.rootPath,\n shell: true,\n });\n\n (buildCommand.stderr as Readable | null)?.pipe(stderrStream);\n (buildCommand.stdout as Readable | null)?.pipe(stderrStream);\n\n buildCommand.on('close', (code) => {\n Bugsnag?.addMetadata('buildCommand', {\n command: config.buildCommand,\n stderr,\n code,\n });\n if (code !== 0) {\n reject(code);\n return;\n }\n resolve(code);\n });\n });\n } catch (error) {\n throw new Error(`Build failed with error code: ${error}`);\n }\n}\n"]}
1
+ {"version":3,"sources":["../../src/deploy/build-project.ts"],"names":[],"mappings":"AAAA,SAAQ,aAAY;AACpB,SAAQ,mBAA4B;AAEpC,SAAQ,kBAAiB;AACzB,SAAyB,kBAAiB;AAW1C,eAAsB,aAAa,SAA8B;AAC/D,QAAM,EAAC,QAAQ,WAAW,MAAK,IAAI;AACnC,QAAM,UAAU,WAAW;AAE3B,MAAI,OAAO,eAAe;AACxB,UAAM,YAAY,YAAY,IAAI;AAClC,WAAO,MACJ,cAAc,SAAS,EACvB,KAAK,MAAM;AACV,cAAQ,iBAAiB;AAAA,QACvB,WAAW;AAAA,QACX,YAAY,IAAI,IAAI;AAAA,MACtB;AAAA,IACF,CAAC,EACA,MAAM,CAAC,UAAU;AAChB,YAAM,IAAI,MAAM,qCAAqC,KAAK,EAAE;AAAA,IAC9D,CAAC;AAAA,EACL;AAEA,QAAM,uBAAuB,YACzB,EAAC,yBAAyB,UAAS,IACnC,CAAC;AACL,MAAI;AACF,UAAM,IAAI,QAAQ,CAAC,SAAS,WAAW;AACrC,UAAI,SAAS;AACb,YAAM,eAAe,IAAI,YAAY;AAErC,mBAAa,GAAG,QAAQ,CAAC,UAAU;AACjC,kBAAU;AACV,gBAAQ,OAAO,MAAM,KAAK;AAAA,MAC5B,CAAC;AAED,YAAM,YAAY,YAAY,IAAI;AAClC,YAAM,eAAe,MAAM,OAAO,cAAe,CAAC,GAAG;AAAA,QACnD,OAAO,CAAC,WAAW,QAAQ,MAAM;AAAA,QACjC,KAAK;AAAA;AAAA,UAEH,GAAG,QAAQ;AAAA,UACX,GAAG;AAAA,QACL;AAAA,QACA,KAAK,OAAO;AAAA,QACZ,OAAO;AAAA,MACT,CAAC;AAED,MAAC,aAAa,QAA4B,KAAK,YAAY;AAC3D,MAAC,aAAa,QAA4B,KAAK,YAAY;AAE3D,mBAAa,GAAG,SAAS,CAAC,SAAS;AACjC,YAAI,SAAS,GAAG;AACd,kBAAQ,iBAAiB;AAAA,YACvB,WAAW;AAAA,YACX,YAAY,IAAI,IAAI;AAAA,UACtB;AAAA,QACF;AACA,iBAAS,YAAY,gBAAgB;AAAA,UACnC,SAAS,OAAO;AAAA,UAChB;AAAA,UACA;AAAA,QACF,CAAC;AACD,YAAI,SAAS,GAAG;AACd,iBAAO,IAAI;AACX;AAAA,QACF;AACA,gBAAQ,IAAI;AAAA,MACd,CAAC;AAAA,IACH,CAAC;AAAA,EACH,SAAS,OAAO;AACd,UAAM,IAAI,MAAM,iCAAiC,KAAK,EAAE;AAAA,EAC1D;AACF","sourcesContent":["import {spawn} from 'child_process';\nimport {PassThrough, Readable} from 'stream';\n\nimport {getBugsnag} from '../utils/bugsnag.js';\nimport {MetricsExporter, MetricName} from '../utils/metrics-exporter.js';\n\nimport {DeploymentConfig, DeploymentHooks} from './types.js';\n\ninterface BuildProjectOptions {\n config: DeploymentConfig;\n assetPath?: string;\n hooks?: DeploymentHooks;\n metricsExporter?: MetricsExporter;\n}\n\nexport async function buildProject(options: BuildProjectOptions) {\n const {config, assetPath, hooks} = options;\n const Bugsnag = getBugsnag();\n\n if (hooks?.buildFunction) {\n const startTime = performance.now();\n return hooks\n .buildFunction(assetPath)\n .then(() => {\n options.metricsExporter?.add(\n MetricName.BuildTime,\n performance.now() - startTime,\n );\n })\n .catch((error) => {\n throw new Error(`Build function failed with error: ${error}`);\n });\n }\n\n const assetPathEnvironment = assetPath\n ? {HYDROGEN_ASSET_BASE_URL: assetPath}\n : {};\n try {\n await new Promise((resolve, reject) => {\n let stderr = '';\n const stderrStream = new PassThrough();\n\n stderrStream.on('data', (chunk) => {\n stderr += chunk;\n process.stderr.write(chunk);\n });\n\n const startTime = performance.now();\n const buildCommand = spawn(config.buildCommand!, [], {\n stdio: ['inherit', 'pipe', 'pipe'],\n env: {\n // eslint-disable-next-line no-process-env\n ...process.env,\n ...assetPathEnvironment,\n },\n cwd: config.rootPath,\n shell: true,\n });\n\n (buildCommand.stderr as Readable | null)?.pipe(stderrStream);\n (buildCommand.stdout as Readable | null)?.pipe(stderrStream);\n\n buildCommand.on('close', (code) => {\n if (code === 0) {\n options.metricsExporter?.add(\n MetricName.BuildTime,\n performance.now() - startTime,\n );\n }\n Bugsnag?.addMetadata('buildCommand', {\n command: config.buildCommand,\n stderr,\n code,\n });\n if (code !== 0) {\n reject(code);\n return;\n }\n resolve(code);\n });\n });\n } catch (error) {\n throw new Error(`Build failed with error code: ${error}`);\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
+ import { MetricsExporter } from '../utils/metrics-exporter.js';
1
2
  import { DeploymentConfig, DeploymentManifestFile } from './types.js';
2
3
 
3
- declare function getUploadFiles(config: DeploymentConfig): Promise<DeploymentManifestFile[]>;
4
+ declare function getUploadFiles(config: DeploymentConfig, metricsExporter?: MetricsExporter): Promise<DeploymentManifestFile[]>;
4
5
 
5
6
  export { getUploadFiles };
@@ -2,9 +2,10 @@ import { glob, fileSizeSync, readFileSync } from '@shopify/cli-kit/node/fs';
2
2
  import { fileHash } from '@shopify/cli-kit/node/crypto';
3
3
  import { joinPath, relativePath } from '@shopify/cli-kit/node/path';
4
4
  import { lookupMimeType } from '@shopify/cli-kit/node/mimes';
5
+ import { MetricName } from '../utils/metrics-exporter.js';
5
6
  import { FileType } from './types.js';
6
7
 
7
- async function getUploadFiles(config) {
8
+ async function getUploadFiles(config, metricsExporter) {
8
9
  const workerPath = joinPath(config.rootPath, config.workerDir);
9
10
  const workerFiles = await glob(`${workerPath}/**`);
10
11
  let manifest = createManifestEntries({
@@ -26,6 +27,9 @@ async function getUploadFiles(config) {
26
27
  })
27
28
  ];
28
29
  }
30
+ metricsExporter?.add(MetricName.NumFilesRequested, manifest.length);
31
+ const totalSize = manifest.reduce((total, file) => total + file.fileSize, 0) / 1024;
32
+ metricsExporter?.add(MetricName.TotalSizeRequested, totalSize);
29
33
  return manifest;
30
34
  }
31
35
  function createHash(file) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/deploy/get-upload-files.ts"],"names":[],"mappings":"AAAA,SAAQ,MAAM,cAAc,oBAAmB;AAC/C,SAAQ,gBAAe;AACvB,SAAQ,UAAU,oBAAmB;AACrC,SAAQ,sBAAqB;AAE7B,SAAkD,gBAAe;AAEjE,eAAsB,eACpB,QACmC;AACnC,QAAM,aAAa,SAAS,OAAO,UAAW,OAAO,SAAU;AAC/D,QAAM,cAAc,MAAM,KAAK,GAAG,UAAU,KAAK;AACjD,MAAI,WAAW,sBAAsB;AAAA,IACnC,OAAO;AAAA,IACP,UAAU;AAAA,IACV,MAAM,SAAS;AAAA,IACf,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,CAAC,OAAO,YAAY;AACtB,UAAM,YAAY,SAAS,OAAO,UAAW,OAAO,SAAU;AAC9D,UAAM,aAAa,MAAM,KAAK,GAAG,SAAS,KAAK;AAC/C,eAAW;AAAA,MACT,GAAG;AAAA,MACH,GAAG,sBAAsB;AAAA,QACvB,OAAO;AAAA,QACP,UAAU;AAAA,QACV,MAAM,SAAS;AAAA,QACf,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,WAAW,MAAsB;AACxC,QAAM,OAAO,aAAa,IAAI;AAC9B,SAAO,SAAS,IAAI;AACtB;AASA,SAAS,sBACP,QAC0B;AAC1B,QAAM,EAAC,OAAO,UAAU,MAAM,OAAM,IAAI;AACxC,QAAM,WAAqC,CAAC;AAC5C,QAAM,QAAQ,CAAC,SAAiB;AAC9B,UAAM,WAAW,aAAa,UAAU,IAAI;AAC5C,QAAI,UAAU,CAAC,OAAO,QAAQ,GAAG;AAC/B;AAAA,IACF;AAEA,aAAS,KAAK;AAAA,MACZ;AAAA,MACA,UAAU,aAAa,IAAI;AAAA,MAC3B,UAAU,eAAe,IAAI;AAAA,MAC7B,UAAU;AAAA,MACV,UAAU,WAAW,IAAI;AAAA,IAC3B,CAAC;AAAA,EACH,CAAC;AACD,SAAO;AACT;AAEA,SAAS,iBAAiB,UAA2B;AACnD,QAAM,oBAAoB,CAAC,WAAW,YAAY,QAAQ,OAAO,MAAM;AACvE,QAAM,mBAAmB,CAAC,OAAO;AACjC,QAAM,cAAc,KAAK,iBAAiB;AAAA,IACxC;AAAA,EACF,CAAC,KAAK,kBAAkB,KAAK,GAAG,CAAC;AACjC,QAAM,QAAQ,IAAI,OAAO,WAAW;AACpC,SAAO,MAAM,KAAK,SAAS,YAAY,CAAC;AAC1C;AAEA,SAAS,gBAAgB,UAA2B;AAClD,QAAM,uBAAuB,CAAC,MAAM;AACpC,QAAM,cAAc,IAAI,qBAAqB,KAAK,GAAG,CAAC;AACtD,QAAM,QAAQ,IAAI,OAAO,WAAW;AACpC,SAAO,CAAC,MAAM,KAAK,SAAS,YAAY,CAAC;AAC3C","sourcesContent":["import {glob, fileSizeSync, readFileSync} from '@shopify/cli-kit/node/fs';\nimport {fileHash} from '@shopify/cli-kit/node/crypto';\nimport {joinPath, relativePath} from '@shopify/cli-kit/node/path';\nimport {lookupMimeType} from '@shopify/cli-kit/node/mimes';\n\nimport {DeploymentConfig, DeploymentManifestFile, FileType} from './types.js';\n\nexport async function getUploadFiles(\n config: DeploymentConfig,\n): Promise<DeploymentManifestFile[]> {\n const workerPath = joinPath(config.rootPath!, config.workerDir!);\n const workerFiles = await glob(`${workerPath}/**`);\n let manifest = createManifestEntries({\n files: workerFiles,\n basePath: workerPath,\n type: FileType.Worker,\n filter: workerFileFilter,\n });\n\n if (!config.workerOnly) {\n const assetPath = joinPath(config.rootPath!, config.assetsDir!);\n const assetFiles = await glob(`${assetPath}/**`);\n manifest = [\n ...manifest,\n ...createManifestEntries({\n files: assetFiles,\n basePath: assetPath,\n type: FileType.Asset,\n filter: assetFileFilter,\n }),\n ];\n }\n\n return manifest;\n}\n\nfunction createHash(file: string): string {\n const buff = readFileSync(file);\n return fileHash(buff);\n}\n\ninterface CreateManifestEntriesParams {\n files: string[];\n basePath: string;\n type: FileType;\n filter?: (arg0: string) => boolean;\n}\n\nfunction createManifestEntries(\n params: CreateManifestEntriesParams,\n): DeploymentManifestFile[] {\n const {files, basePath, type, filter} = params;\n const manifest: DeploymentManifestFile[] = [];\n files.forEach((file: string) => {\n const filePath = relativePath(basePath, file);\n if (filter && !filter(filePath)) {\n return;\n }\n\n manifest.push({\n filePath,\n fileSize: fileSizeSync(file),\n mimeType: lookupMimeType(file)!,\n fileType: type,\n fileHash: createHash(file),\n });\n });\n return manifest;\n}\n\nfunction workerFileFilter(fileName: string): boolean {\n const allowedExtensions = ['.js.map', '.mjs.map', '.map', '.js', '.mjs'];\n const allowedFilenames = ['index'];\n const regexString = `^(${allowedFilenames.join(\n '|',\n )})(${allowedExtensions.join('|')})$`;\n const regex = new RegExp(regexString);\n return regex.test(fileName.toLowerCase());\n}\n\nfunction assetFileFilter(fileName: string): boolean {\n const disallowedExtensions = ['.map'];\n const regexString = `(${disallowedExtensions.join('|')})$`;\n const regex = new RegExp(regexString);\n return !regex.test(fileName.toLowerCase());\n}\n"]}
1
+ {"version":3,"sources":["../../src/deploy/get-upload-files.ts"],"names":[],"mappings":"AAAA,SAAQ,MAAM,cAAc,oBAAmB;AAC/C,SAAQ,gBAAe;AACvB,SAAQ,UAAU,oBAAmB;AACrC,SAAQ,sBAAqB;AAE7B,SAAyB,kBAAiB;AAE1C,SAAkD,gBAAe;AAEjE,eAAsB,eACpB,QACA,iBACmC;AACnC,QAAM,aAAa,SAAS,OAAO,UAAW,OAAO,SAAU;AAC/D,QAAM,cAAc,MAAM,KAAK,GAAG,UAAU,KAAK;AACjD,MAAI,WAAW,sBAAsB;AAAA,IACnC,OAAO;AAAA,IACP,UAAU;AAAA,IACV,MAAM,SAAS;AAAA,IACf,QAAQ;AAAA,EACV,CAAC;AAED,MAAI,CAAC,OAAO,YAAY;AACtB,UAAM,YAAY,SAAS,OAAO,UAAW,OAAO,SAAU;AAC9D,UAAM,aAAa,MAAM,KAAK,GAAG,SAAS,KAAK;AAC/C,eAAW;AAAA,MACT,GAAG;AAAA,MACH,GAAG,sBAAsB;AAAA,QACvB,OAAO;AAAA,QACP,UAAU;AAAA,QACV,MAAM,SAAS;AAAA,QACf,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AACA,mBAAiB,IAAI,WAAW,mBAAmB,SAAS,MAAM;AAClE,QAAM,YACJ,SAAS,OAAO,CAAC,OAAO,SAAS,QAAQ,KAAK,UAAU,CAAC,IAAI;AAC/D,mBAAiB,IAAI,WAAW,oBAAoB,SAAS;AAE7D,SAAO;AACT;AAEA,SAAS,WAAW,MAAsB;AACxC,QAAM,OAAO,aAAa,IAAI;AAC9B,SAAO,SAAS,IAAI;AACtB;AASA,SAAS,sBACP,QAC0B;AAC1B,QAAM,EAAC,OAAO,UAAU,MAAM,OAAM,IAAI;AACxC,QAAM,WAAqC,CAAC;AAC5C,QAAM,QAAQ,CAAC,SAAiB;AAC9B,UAAM,WAAW,aAAa,UAAU,IAAI;AAC5C,QAAI,UAAU,CAAC,OAAO,QAAQ,GAAG;AAC/B;AAAA,IACF;AAEA,aAAS,KAAK;AAAA,MACZ;AAAA,MACA,UAAU,aAAa,IAAI;AAAA,MAC3B,UAAU,eAAe,IAAI;AAAA,MAC7B,UAAU;AAAA,MACV,UAAU,WAAW,IAAI;AAAA,IAC3B,CAAC;AAAA,EACH,CAAC;AACD,SAAO;AACT;AAEA,SAAS,iBAAiB,UAA2B;AACnD,QAAM,oBAAoB,CAAC,WAAW,YAAY,QAAQ,OAAO,MAAM;AACvE,QAAM,mBAAmB,CAAC,OAAO;AACjC,QAAM,cAAc,KAAK,iBAAiB;AAAA,IACxC;AAAA,EACF,CAAC,KAAK,kBAAkB,KAAK,GAAG,CAAC;AACjC,QAAM,QAAQ,IAAI,OAAO,WAAW;AACpC,SAAO,MAAM,KAAK,SAAS,YAAY,CAAC;AAC1C;AAEA,SAAS,gBAAgB,UAA2B;AAClD,QAAM,uBAAuB,CAAC,MAAM;AACpC,QAAM,cAAc,IAAI,qBAAqB,KAAK,GAAG,CAAC;AACtD,QAAM,QAAQ,IAAI,OAAO,WAAW;AACpC,SAAO,CAAC,MAAM,KAAK,SAAS,YAAY,CAAC;AAC3C","sourcesContent":["import {glob, fileSizeSync, readFileSync} from '@shopify/cli-kit/node/fs';\nimport {fileHash} from '@shopify/cli-kit/node/crypto';\nimport {joinPath, relativePath} from '@shopify/cli-kit/node/path';\nimport {lookupMimeType} from '@shopify/cli-kit/node/mimes';\n\nimport {MetricsExporter, MetricName} from '../utils/metrics-exporter.js';\n\nimport {DeploymentConfig, DeploymentManifestFile, FileType} from './types.js';\n\nexport async function getUploadFiles(\n config: DeploymentConfig,\n metricsExporter?: MetricsExporter,\n): Promise<DeploymentManifestFile[]> {\n const workerPath = joinPath(config.rootPath!, config.workerDir!);\n const workerFiles = await glob(`${workerPath}/**`);\n let manifest = createManifestEntries({\n files: workerFiles,\n basePath: workerPath,\n type: FileType.Worker,\n filter: workerFileFilter,\n });\n\n if (!config.workerOnly) {\n const assetPath = joinPath(config.rootPath!, config.assetsDir!);\n const assetFiles = await glob(`${assetPath}/**`);\n manifest = [\n ...manifest,\n ...createManifestEntries({\n files: assetFiles,\n basePath: assetPath,\n type: FileType.Asset,\n filter: assetFileFilter,\n }),\n ];\n }\n metricsExporter?.add(MetricName.NumFilesRequested, manifest.length);\n const totalSize =\n manifest.reduce((total, file) => total + file.fileSize, 0) / 1024;\n metricsExporter?.add(MetricName.TotalSizeRequested, totalSize);\n\n return manifest;\n}\n\nfunction createHash(file: string): string {\n const buff = readFileSync(file);\n return fileHash(buff);\n}\n\ninterface CreateManifestEntriesParams {\n files: string[];\n basePath: string;\n type: FileType;\n filter?: (arg0: string) => boolean;\n}\n\nfunction createManifestEntries(\n params: CreateManifestEntriesParams,\n): DeploymentManifestFile[] {\n const {files, basePath, type, filter} = params;\n const manifest: DeploymentManifestFile[] = [];\n files.forEach((file: string) => {\n const filePath = relativePath(basePath, file);\n if (filter && !filter(filePath)) {\n return;\n }\n\n manifest.push({\n filePath,\n fileSize: fileSizeSync(file),\n mimeType: lookupMimeType(file)!,\n fileType: type,\n fileHash: createHash(file),\n });\n });\n return manifest;\n}\n\nfunction workerFileFilter(fileName: string): boolean {\n const allowedExtensions = ['.js.map', '.mjs.map', '.map', '.js', '.mjs'];\n const allowedFilenames = ['index'];\n const regexString = `^(${allowedFilenames.join(\n '|',\n )})(${allowedExtensions.join('|')})$`;\n const regex = new RegExp(regexString);\n return regex.test(fileName.toLowerCase());\n}\n\nfunction assetFileFilter(fileName: string): boolean {\n const disallowedExtensions = ['.map'];\n const regexString = `(${disallowedExtensions.join('|')})$`;\n const regex = new RegExp(regexString);\n return !regex.test(fileName.toLowerCase());\n}\n"]}
@@ -2,6 +2,7 @@ import { outputSuccess, outputInfo, outputWarn } from '@shopify/cli-kit/node/out
2
2
  import { stderrLogger, verifyConfig } from '../utils/utils.js';
3
3
  export { parseToken } from '../utils/utils.js';
4
4
  import { initializeBugsnag, getBugsnag } from '../utils/bugsnag.js';
5
+ import { MetricsExporter } from '../utils/metrics-exporter.js';
5
6
  import { buildInitiate } from './build-initiate.js';
6
7
  import { buildCancel } from './build-cancel.js';
7
8
  import { getUploadFiles } from './get-upload-files.js';
@@ -35,6 +36,10 @@ async function createDeploy(options) {
35
36
  labels,
36
37
  metadata
37
38
  });
39
+ const metricsExporter = new MetricsExporter({
40
+ ciProvider: metadata.name || "unknown",
41
+ rootPath: config.rootPath
42
+ });
38
43
  if (!config.workerOnly && !config.skipBuild) {
39
44
  const buildInitiateResponse = await buildInitiate({
40
45
  config,
@@ -49,12 +54,13 @@ async function createDeploy(options) {
49
54
  await buildProject({
50
55
  config,
51
56
  assetPath: build.assetPath,
52
- hooks
57
+ hooks,
58
+ metricsExporter
53
59
  });
54
60
  verifyConfig({ config, performedBuild: true });
55
61
  }
56
62
  buildCompleted = true;
57
- const manifest = await getUploadFiles(config);
63
+ const manifest = await getUploadFiles(config, metricsExporter);
58
64
  Bugsnag?.addMetadata("manifest", manifest);
59
65
  const deploymentInitiateInput = build.id ? { buildId: build.id, manifest } : { environment, manifest, labels };
60
66
  deployment = await deploymentInitiate({
@@ -67,12 +73,14 @@ async function createDeploy(options) {
67
73
  config,
68
74
  targets: deployment.deploymentTargets,
69
75
  hooks,
70
- logger
76
+ logger,
77
+ metricsExporter
71
78
  });
72
79
  const deploymentCompleteOp = await deploymentComplete(
73
80
  config,
74
81
  deployment.deployment.id
75
82
  );
83
+ await metricsExporter.export();
76
84
  if (!config.skipVerification) {
77
85
  await verifyDeployment({
78
86
  config,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/deploy/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Logger,\n outputInfo,\n outputSuccess,\n outputWarn,\n} from '@shopify/cli-kit/node/output';\n\nimport {stderrLogger, verifyConfig} from '../utils/utils.js';\nimport {getBugsnag, initializeBugsnag} from '../utils/bugsnag.js';\n\nimport {buildInitiate} from './build-initiate.js';\nimport {buildCancel} from './build-cancel.js';\nimport {getUploadFiles} from './get-upload-files.js';\nimport {deploymentInitiate} from './deployment-initiate.js';\nimport {deploymentComplete} from './deployment-complete.js';\nimport {verifyDeployment} from './verify-deployment.js';\nimport {deploymentCancel} from './deployment-cancel.js';\nimport {uploadFiles} from './upload-files.js';\nimport {\n Build,\n DeploymentConfig,\n DeploymentHooks,\n VerificationError,\n} from './types.js';\nimport {buildProject} from './build-project.js';\nimport {createLabels, getMetadata, getEnvironmentInput} from './metadata.js';\n\ninterface CreateDeployOptions {\n config: DeploymentConfig;\n hooks?: DeploymentHooks;\n logger?: Logger;\n}\n\nexport async function createDeploy(\n options: CreateDeployOptions,\n): Promise<string | undefined> {\n initializeBugsnag(!options.config.bugsnag);\n const Bugsnag = getBugsnag();\n Bugsnag?.addMetadata('config', {\n ...options.config,\n deploymentToken: {...options.config.deploymentToken, accessToken: '***'},\n });\n\n const {config, hooks} = options;\n const logger = options.logger ?? stderrLogger;\n const build = {} as Build;\n let buildCompleted;\n let deployment;\n\n try {\n const metadata = await getMetadata(config, logger);\n const labels = createLabels(metadata);\n const environment = getEnvironmentInput(config, metadata);\n Bugsnag?.addMetadata('metadata', {\n environment,\n labels,\n metadata,\n });\n\n if (!config.workerOnly && !config.skipBuild) {\n const buildInitiateResponse = await buildInitiate({\n config,\n environment,\n labels,\n logger,\n });\n build.id = buildInitiateResponse.build.id;\n build.assetPath = buildInitiateResponse.build.assetPath;\n }\n\n if (!config.skipBuild) {\n await buildProject({\n config,\n assetPath: build.assetPath,\n hooks,\n });\n verifyConfig({config, performedBuild: true});\n }\n buildCompleted = true;\n\n const manifest = await getUploadFiles(config);\n Bugsnag?.addMetadata('manifest', manifest);\n const deploymentInitiateInput = build.id\n ? {buildId: build.id, manifest}\n : {environment, manifest, labels};\n\n deployment = await deploymentInitiate({\n config,\n input: deploymentInitiateInput,\n logger,\n });\n Bugsnag?.addMetadata('targets', deployment.deploymentTargets);\n await uploadFiles({\n config,\n targets: deployment.deploymentTargets,\n hooks,\n logger,\n });\n const deploymentCompleteOp = await deploymentComplete(\n config,\n deployment.deployment.id,\n );\n\n if (!config.skipVerification) {\n await verifyDeployment({\n config,\n url: deploymentCompleteOp.deployment.url,\n logger,\n hooks,\n });\n }\n\n const urlMessage = config.publicDeployment ? 'public' : 'private';\n outputSuccess(\n `\\nThe deployment can be reached at the ${deploymentCompleteOp.deployment.url} ${urlMessage} preview URL`,\n logger,\n );\n // in CI environments, we want to output the URL to stdout\n if (metadata.name !== 'none') {\n outputInfo(deploymentCompleteOp.deployment.url);\n }\n\n return deploymentCompleteOp.deployment.url;\n } catch (error) {\n if (\n config.bugsnag &&\n Bugsnag &&\n (error instanceof Error || typeof error === 'string')\n ) {\n Bugsnag.notify(error);\n }\n\n if (!(error instanceof Error)) {\n // eslint-disable-next-line no-console\n console.error('Unknown error', error);\n return Promise.reject(new Error('Unknown error'));\n }\n\n if (error instanceof VerificationError) {\n outputWarn(error.message, logger);\n } else if (build.id && !buildCompleted) {\n outputWarn(\n `Build failed with: ${error.message}, cancelling build.`,\n logger,\n );\n await buildCancel({\n config,\n buildId: build.id!,\n reason: error.message,\n logger,\n }).catch((err) => {\n if (err instanceof Error) {\n outputWarn(`Failed to cancel build: ${err.message}`, logger);\n }\n });\n } else if (deployment?.deployment.id) {\n outputWarn(\n `Deployment failed with: ${error.message}, cancelling deployment.`,\n logger,\n );\n await deploymentCancel({\n config,\n deploymentId: deployment.deployment.id,\n reason: error.message,\n logger,\n }).catch((err) => {\n if (err instanceof Error) {\n outputWarn(`Failed to cancel deployment: ${err.message}`, logger);\n }\n });\n }\n return Promise.reject(error);\n }\n}\n\nexport type {DeploymentConfig, DeploymentHooks};\nexport {parseToken} from '../utils/utils.js';\n"]}
1
+ {"version":3,"sources":["../../src/deploy/index.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n Logger,\n outputInfo,\n outputSuccess,\n outputWarn,\n} from '@shopify/cli-kit/node/output';\n\nimport {stderrLogger, verifyConfig} from '../utils/utils.js';\nimport {getBugsnag, initializeBugsnag} from '../utils/bugsnag.js';\nimport {MetricsExporter, MetricName} from '../utils/metrics-exporter.js';\n\nimport {buildInitiate} from './build-initiate.js';\nimport {buildCancel} from './build-cancel.js';\nimport {getUploadFiles} from './get-upload-files.js';\nimport {deploymentInitiate} from './deployment-initiate.js';\nimport {deploymentComplete} from './deployment-complete.js';\nimport {verifyDeployment} from './verify-deployment.js';\nimport {deploymentCancel} from './deployment-cancel.js';\nimport {uploadFiles} from './upload-files.js';\nimport {\n Build,\n DeploymentConfig,\n DeploymentHooks,\n VerificationError,\n} from './types.js';\nimport {buildProject} from './build-project.js';\nimport {createLabels, getMetadata, getEnvironmentInput} from './metadata.js';\n\ninterface CreateDeployOptions {\n config: DeploymentConfig;\n hooks?: DeploymentHooks;\n logger?: Logger;\n}\n\nexport async function createDeploy(\n options: CreateDeployOptions,\n): Promise<string | undefined> {\n initializeBugsnag(!options.config.bugsnag);\n const Bugsnag = getBugsnag();\n Bugsnag?.addMetadata('config', {\n ...options.config,\n deploymentToken: {...options.config.deploymentToken, accessToken: '***'},\n });\n\n const {config, hooks} = options;\n const logger = options.logger ?? stderrLogger;\n const build = {} as Build;\n let buildCompleted;\n let deployment;\n\n try {\n const metadata = await getMetadata(config, logger);\n const labels = createLabels(metadata);\n const environment = getEnvironmentInput(config, metadata);\n Bugsnag?.addMetadata('metadata', {\n environment,\n labels,\n metadata,\n });\n const metricsExporter = new MetricsExporter({\n ciProvider: metadata.name || 'unknown',\n rootPath: config.rootPath!,\n });\n\n if (!config.workerOnly && !config.skipBuild) {\n const buildInitiateResponse = await buildInitiate({\n config,\n environment,\n labels,\n logger,\n });\n build.id = buildInitiateResponse.build.id;\n build.assetPath = buildInitiateResponse.build.assetPath;\n }\n\n if (!config.skipBuild) {\n await buildProject({\n config,\n assetPath: build.assetPath,\n hooks,\n metricsExporter,\n });\n verifyConfig({config, performedBuild: true});\n }\n buildCompleted = true;\n\n const manifest = await getUploadFiles(config, metricsExporter);\n Bugsnag?.addMetadata('manifest', manifest);\n const deploymentInitiateInput = build.id\n ? {buildId: build.id, manifest}\n : {environment, manifest, labels};\n\n deployment = await deploymentInitiate({\n config,\n input: deploymentInitiateInput,\n logger,\n });\n Bugsnag?.addMetadata('targets', deployment.deploymentTargets);\n\n await uploadFiles({\n config,\n targets: deployment.deploymentTargets,\n hooks,\n logger,\n metricsExporter,\n });\n const deploymentCompleteOp = await deploymentComplete(\n config,\n deployment.deployment.id,\n );\n await metricsExporter.export();\n\n if (!config.skipVerification) {\n await verifyDeployment({\n config,\n url: deploymentCompleteOp.deployment.url,\n logger,\n hooks,\n });\n }\n\n const urlMessage = config.publicDeployment ? 'public' : 'private';\n outputSuccess(\n `\\nThe deployment can be reached at the ${deploymentCompleteOp.deployment.url} ${urlMessage} preview URL`,\n logger,\n );\n // in CI environments, we want to output the URL to stdout\n if (metadata.name !== 'none') {\n outputInfo(deploymentCompleteOp.deployment.url);\n }\n\n return deploymentCompleteOp.deployment.url;\n } catch (error) {\n if (\n config.bugsnag &&\n Bugsnag &&\n (error instanceof Error || typeof error === 'string')\n ) {\n Bugsnag.notify(error);\n }\n\n if (!(error instanceof Error)) {\n // eslint-disable-next-line no-console\n console.error('Unknown error', error);\n return Promise.reject(new Error('Unknown error'));\n }\n\n if (error instanceof VerificationError) {\n outputWarn(error.message, logger);\n } else if (build.id && !buildCompleted) {\n outputWarn(\n `Build failed with: ${error.message}, cancelling build.`,\n logger,\n );\n await buildCancel({\n config,\n buildId: build.id!,\n reason: error.message,\n logger,\n }).catch((err) => {\n if (err instanceof Error) {\n outputWarn(`Failed to cancel build: ${err.message}`, logger);\n }\n });\n } else if (deployment?.deployment.id) {\n outputWarn(\n `Deployment failed with: ${error.message}, cancelling deployment.`,\n logger,\n );\n await deploymentCancel({\n config,\n deploymentId: deployment.deployment.id,\n reason: error.message,\n logger,\n }).catch((err) => {\n if (err instanceof Error) {\n outputWarn(`Failed to cancel deployment: ${err.message}`, logger);\n }\n });\n }\n return Promise.reject(error);\n }\n}\n\nexport type {DeploymentConfig, DeploymentHooks};\nexport {parseToken} from '../utils/utils.js';\n"]}
@@ -1,4 +1,5 @@
1
1
  import { Logger } from '@shopify/cli-kit/node/output';
2
+ import { MetricsExporter } from '../utils/metrics-exporter.js';
2
3
  import { DeploymentConfig, DeploymentHooks } from './types.js';
3
4
  import { DeploymentTargetResponse } from './graphql/deployment-initiate.js';
4
5
 
@@ -6,6 +7,7 @@ interface UploadFilesOptions {
6
7
  config: DeploymentConfig;
7
8
  hooks?: DeploymentHooks;
8
9
  logger: Logger;
10
+ metricsExporter?: MetricsExporter;
9
11
  targets: DeploymentTargetResponse[];
10
12
  }
11
13
  declare function uploadFiles(options: UploadFilesOptions): Promise<void>;
@@ -5,16 +5,23 @@ import { outputInfo, outputCompleted } from '@shopify/cli-kit/node/output';
5
5
  import { joinPath } from '@shopify/cli-kit/node/path';
6
6
  import { mapLimit } from 'async';
7
7
  import { deployDefaults } from '../utils/utils.js';
8
+ import { MetricName } from '../utils/metrics-exporter.js';
8
9
 
9
10
  async function uploadFiles(options) {
10
11
  const { config, logger, targets, hooks } = options;
11
12
  outputInfo(`Uploading ${targets.length} files...`, logger);
13
+ const totalUploadSize = targets.reduce((total, target) => total + target.fileSize, 0) / 1024;
14
+ options.metricsExporter?.add(MetricName.TotalSizeUploaded, totalUploadSize);
15
+ options.metricsExporter?.add(MetricName.NumFilesUploaded, targets.length);
12
16
  const agent = new Agent({ keepAlive: true });
13
17
  hooks?.onUploadFilesStart?.();
18
+ const startTime = performance.now();
14
19
  return mapLimit(targets, 6, async (target) => {
15
20
  await uploadFile(config, target, agent);
16
21
  }).then(() => {
22
+ const endTime = performance.now();
17
23
  hooks?.onUploadFilesComplete?.();
24
+ options.metricsExporter?.add(MetricName.UploadTime, endTime - startTime);
18
25
  outputCompleted(`Files uploaded successfully`, logger);
19
26
  }).catch((err) => {
20
27
  hooks?.onUploadFilesError?.(err);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/deploy/upload-files.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/naming-convention */\nimport {Agent} from 'https';\n\nimport {fetch, formData} from '@shopify/cli-kit/node/http';\nimport {createFileReadStream} from '@shopify/cli-kit/node/fs';\nimport {\n Logger,\n outputCompleted,\n outputInfo,\n} from '@shopify/cli-kit/node/output';\nimport {joinPath} from '@shopify/cli-kit/node/path';\nimport {mapLimit} from 'async';\n\nimport {deployDefaults} from '../utils/utils.js';\n\nimport {DeploymentConfig, DeploymentHooks} from './types.js';\nimport {DeploymentTargetResponse} from './graphql/deployment-initiate.js';\n\ninterface InitiateResumableResponse {\n target: DeploymentTargetResponse;\n sessionUri: string;\n location: string;\n}\n\ninterface ResumableUploadStatus {\n complete: boolean;\n lastReceivedByte: number;\n}\n\ninterface UploadFilesOptions {\n config: DeploymentConfig;\n hooks?: DeploymentHooks;\n logger: Logger;\n targets: DeploymentTargetResponse[];\n}\n\nexport async function uploadFiles(options: UploadFilesOptions): Promise<void> {\n const {config, logger, targets, hooks} = options;\n outputInfo(`Uploading ${targets.length} files...`, logger);\n const agent = new Agent({keepAlive: true});\n hooks?.onUploadFilesStart?.();\n return mapLimit(targets, 6, async (target: DeploymentTargetResponse) => {\n await uploadFile(config, target, agent);\n })\n .then(() => {\n hooks?.onUploadFilesComplete?.();\n outputCompleted(`Files uploaded successfully`, logger);\n })\n .catch((err) => {\n hooks?.onUploadFilesError?.(err);\n throw err;\n })\n .finally(() => {\n agent.destroy();\n });\n}\n\nasync function uploadFile(\n config: DeploymentConfig,\n target: DeploymentTargetResponse,\n agent: Agent,\n) {\n const localFolderPath =\n target.fileType === 'WORKER'\n ? joinPath(config.rootPath!, config.workerDir!)\n : joinPath(config.rootPath!, config.assetsDir!);\n\n if (target.parameters !== null && target.parameters.length > 0) {\n // If parameters exist perform a form upload\n const form = formData();\n target.parameters.forEach((param) => {\n form.append(param.name, param.value);\n });\n form.append(\n 'file',\n createFileReadStream(joinPath(localFolderPath, target.filePath)),\n );\n await formUpload(form, target, agent);\n } else {\n // If no parameters exist perform a resumable upload\n const initData = await initiateResumableUpload(target, agent);\n await performResumableUpload(\n joinPath(localFolderPath, target.filePath),\n initData,\n agent,\n );\n }\n}\n\nasync function formUpload(\n form: ReturnType<typeof formData>,\n target: DeploymentTargetResponse,\n agent: Agent,\n attemptNumber = 0,\n) {\n try {\n const timeoutDuration = 120000;\n const controller = new AbortController();\n const timeout = setTimeout(() => {\n controller.abort();\n }, timeoutDuration);\n\n const response = await fetch(target.uploadUrl, {\n method: 'POST',\n body: form,\n signal: controller.signal,\n headers: {\n Connection: 'keep-alive',\n },\n agent,\n });\n clearTimeout(timeout);\n\n if (!response.ok) {\n throw new Error(`${response.status}`);\n }\n } catch (err) {\n if (isErrorCode(err, 'ENOENT')) {\n throw new Error(`File not found: ${target.filePath}`);\n }\n\n if (attemptNumber < Number(deployDefaults.maxUploadAttempts)) {\n await formUpload(form, target, agent, attemptNumber + 1);\n } else {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new Error(`Request timeout whilst uploading ${target.filePath}`);\n }\n throw new Error(`Failed to upload file ${target.filePath}`);\n }\n }\n}\n\nasync function initiateResumableUpload(\n target: DeploymentTargetResponse,\n agent: Agent,\n): Promise<InitiateResumableResponse> {\n return fetch(target.uploadUrl, {\n method: 'POST',\n headers: {\n 'x-goog-resumable': 'start',\n 'X-Goog-Content-Length-Range': `0,${target.fileSize}`,\n 'User-Agent': 'oxygen-cli',\n },\n agent,\n })\n .then((res) => {\n return {\n sessionUri: res.headers.get('x-guploader-uploadid')!,\n location: res.headers.get('location')!,\n target,\n };\n })\n .catch((err) => {\n throw new Error(\n `Failed to initiate resumable upload for file ${target.filePath} (status code ${err.statusCode})`,\n );\n });\n}\n\nasync function performResumableUpload(\n localFilePath: string,\n initData: InitiateResumableResponse,\n agent: Agent,\n startByte = 0,\n attemptNumber = 0,\n) {\n await uploadResumable(\n initData.location,\n localFilePath,\n startByte,\n agent,\n ).catch(async (err) => {\n if (isErrorCode(err, 'ENOENT')) {\n throw new Error(`File not found: ${initData.target.filePath}`);\n }\n\n if (\n err &&\n attemptNumber >= Number(deployDefaults.maxResumabeUploadAttempts)\n ) {\n throw new Error(\n `Failed to upload file ${initData.target.filePath} after ${deployDefaults.maxResumabeUploadAttempts} attempts`,\n );\n }\n const status = await resumableUploadStatus(\n initData.location,\n initData.target.fileSize,\n agent,\n );\n if (!status.complete) {\n const nextAttemptStartByte = status.lastReceivedByte;\n const attempt = attemptNumber + 1;\n await performResumableUpload(\n localFilePath,\n initData,\n agent,\n nextAttemptStartByte,\n attempt,\n );\n }\n });\n}\n\nasync function uploadResumable(\n location: string,\n filePath: string,\n lastReceivedByte: number,\n agent: Agent,\n): Promise<number> {\n const file = createFileReadStream(filePath, {start: lastReceivedByte});\n return fetch(location, {\n method: 'PUT',\n body: file,\n agent,\n }).then((res) => {\n return res.status;\n });\n}\n\nasync function resumableUploadStatus(\n location: string,\n fileSize: number,\n agent: Agent,\n): Promise<ResumableUploadStatus> {\n const getLastByte = (range: string | null): number => {\n if (!range || range.split('-').length !== 2) return 0;\n const rangeParts = range.split('-');\n return parseInt(rangeParts[1]!, 10);\n };\n\n return fetch(location, {\n method: 'PUT',\n headers: {\n 'Content-Length': '0',\n 'Content-Range': `bytes */${fileSize}`,\n },\n agent,\n })\n .then((res) => {\n return {\n complete: res.status === 200,\n lastReceivedByte: getLastByte(res.headers.get('range')),\n };\n })\n .catch((err) => {\n // eslint-disable-next-line no-console\n console.error(err);\n return {\n complete: false,\n lastReceivedByte: 0,\n };\n });\n}\n\nfunction isErrorCode(err: unknown, code: string): boolean {\n return err instanceof Error && 'code' in err && err.code === code;\n}\n"]}
1
+ {"version":3,"sources":["../../src/deploy/upload-files.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/naming-convention */\nimport {Agent} from 'https';\n\nimport {fetch, formData} from '@shopify/cli-kit/node/http';\nimport {createFileReadStream} from '@shopify/cli-kit/node/fs';\nimport {\n Logger,\n outputCompleted,\n outputInfo,\n} from '@shopify/cli-kit/node/output';\nimport {joinPath} from '@shopify/cli-kit/node/path';\nimport {mapLimit} from 'async';\n\nimport {deployDefaults} from '../utils/utils.js';\nimport {MetricsExporter, MetricName} from '../utils/metrics-exporter.js';\n\nimport {DeploymentConfig, DeploymentHooks} from './types.js';\nimport {DeploymentTargetResponse} from './graphql/deployment-initiate.js';\n\ninterface InitiateResumableResponse {\n target: DeploymentTargetResponse;\n sessionUri: string;\n location: string;\n}\n\ninterface ResumableUploadStatus {\n complete: boolean;\n lastReceivedByte: number;\n}\n\ninterface UploadFilesOptions {\n config: DeploymentConfig;\n hooks?: DeploymentHooks;\n logger: Logger;\n metricsExporter?: MetricsExporter;\n targets: DeploymentTargetResponse[];\n}\n\nexport async function uploadFiles(options: UploadFilesOptions): Promise<void> {\n const {config, logger, targets, hooks} = options;\n outputInfo(`Uploading ${targets.length} files...`, logger);\n\n const totalUploadSize =\n targets.reduce((total, target) => total + target.fileSize, 0) / 1024;\n options.metricsExporter?.add(MetricName.TotalSizeUploaded, totalUploadSize);\n options.metricsExporter?.add(MetricName.NumFilesUploaded, targets.length);\n\n const agent = new Agent({keepAlive: true});\n hooks?.onUploadFilesStart?.();\n const startTime = performance.now();\n return mapLimit(targets, 6, async (target: DeploymentTargetResponse) => {\n await uploadFile(config, target, agent);\n })\n .then(() => {\n const endTime = performance.now();\n hooks?.onUploadFilesComplete?.();\n options.metricsExporter?.add(MetricName.UploadTime, endTime - startTime);\n outputCompleted(`Files uploaded successfully`, logger);\n })\n .catch((err) => {\n hooks?.onUploadFilesError?.(err);\n throw err;\n })\n .finally(() => {\n agent.destroy();\n });\n}\n\nasync function uploadFile(\n config: DeploymentConfig,\n target: DeploymentTargetResponse,\n agent: Agent,\n) {\n const localFolderPath =\n target.fileType === 'WORKER'\n ? joinPath(config.rootPath!, config.workerDir!)\n : joinPath(config.rootPath!, config.assetsDir!);\n\n if (target.parameters !== null && target.parameters.length > 0) {\n // If parameters exist perform a form upload\n const form = formData();\n target.parameters.forEach((param) => {\n form.append(param.name, param.value);\n });\n form.append(\n 'file',\n createFileReadStream(joinPath(localFolderPath, target.filePath)),\n );\n await formUpload(form, target, agent);\n } else {\n // If no parameters exist perform a resumable upload\n const initData = await initiateResumableUpload(target, agent);\n await performResumableUpload(\n joinPath(localFolderPath, target.filePath),\n initData,\n agent,\n );\n }\n}\n\nasync function formUpload(\n form: ReturnType<typeof formData>,\n target: DeploymentTargetResponse,\n agent: Agent,\n attemptNumber = 0,\n) {\n try {\n const timeoutDuration = 120000;\n const controller = new AbortController();\n const timeout = setTimeout(() => {\n controller.abort();\n }, timeoutDuration);\n\n const response = await fetch(target.uploadUrl, {\n method: 'POST',\n body: form,\n signal: controller.signal,\n headers: {\n Connection: 'keep-alive',\n },\n agent,\n });\n clearTimeout(timeout);\n\n if (!response.ok) {\n throw new Error(`${response.status}`);\n }\n } catch (err) {\n if (isErrorCode(err, 'ENOENT')) {\n throw new Error(`File not found: ${target.filePath}`);\n }\n\n if (attemptNumber < Number(deployDefaults.maxUploadAttempts)) {\n await formUpload(form, target, agent, attemptNumber + 1);\n } else {\n if (err instanceof Error && err.name === 'AbortError') {\n throw new Error(`Request timeout whilst uploading ${target.filePath}`);\n }\n throw new Error(`Failed to upload file ${target.filePath}`);\n }\n }\n}\n\nasync function initiateResumableUpload(\n target: DeploymentTargetResponse,\n agent: Agent,\n): Promise<InitiateResumableResponse> {\n return fetch(target.uploadUrl, {\n method: 'POST',\n headers: {\n 'x-goog-resumable': 'start',\n 'X-Goog-Content-Length-Range': `0,${target.fileSize}`,\n 'User-Agent': 'oxygen-cli',\n },\n agent,\n })\n .then((res) => {\n return {\n sessionUri: res.headers.get('x-guploader-uploadid')!,\n location: res.headers.get('location')!,\n target,\n };\n })\n .catch((err) => {\n throw new Error(\n `Failed to initiate resumable upload for file ${target.filePath} (status code ${err.statusCode})`,\n );\n });\n}\n\nasync function performResumableUpload(\n localFilePath: string,\n initData: InitiateResumableResponse,\n agent: Agent,\n startByte = 0,\n attemptNumber = 0,\n) {\n await uploadResumable(\n initData.location,\n localFilePath,\n startByte,\n agent,\n ).catch(async (err) => {\n if (isErrorCode(err, 'ENOENT')) {\n throw new Error(`File not found: ${initData.target.filePath}`);\n }\n\n if (\n err &&\n attemptNumber >= Number(deployDefaults.maxResumabeUploadAttempts)\n ) {\n throw new Error(\n `Failed to upload file ${initData.target.filePath} after ${deployDefaults.maxResumabeUploadAttempts} attempts`,\n );\n }\n const status = await resumableUploadStatus(\n initData.location,\n initData.target.fileSize,\n agent,\n );\n if (!status.complete) {\n const nextAttemptStartByte = status.lastReceivedByte;\n const attempt = attemptNumber + 1;\n await performResumableUpload(\n localFilePath,\n initData,\n agent,\n nextAttemptStartByte,\n attempt,\n );\n }\n });\n}\n\nasync function uploadResumable(\n location: string,\n filePath: string,\n lastReceivedByte: number,\n agent: Agent,\n): Promise<number> {\n const file = createFileReadStream(filePath, {start: lastReceivedByte});\n return fetch(location, {\n method: 'PUT',\n body: file,\n agent,\n }).then((res) => {\n return res.status;\n });\n}\n\nasync function resumableUploadStatus(\n location: string,\n fileSize: number,\n agent: Agent,\n): Promise<ResumableUploadStatus> {\n const getLastByte = (range: string | null): number => {\n if (!range || range.split('-').length !== 2) return 0;\n const rangeParts = range.split('-');\n return parseInt(rangeParts[1]!, 10);\n };\n\n return fetch(location, {\n method: 'PUT',\n headers: {\n 'Content-Length': '0',\n 'Content-Range': `bytes */${fileSize}`,\n },\n agent,\n })\n .then((res) => {\n return {\n complete: res.status === 200,\n lastReceivedByte: getLastByte(res.headers.get('range')),\n };\n })\n .catch((err) => {\n // eslint-disable-next-line no-console\n console.error(err);\n return {\n complete: false,\n lastReceivedByte: 0,\n };\n });\n}\n\nfunction isErrorCode(err: unknown, code: string): boolean {\n return err instanceof Error && 'code' in err && err.code === code;\n}\n"]}
@@ -1,15 +1,6 @@
1
- import fs from 'fs';
2
- import path, { dirname } from 'path';
3
- import { fileURLToPath } from 'url';
4
1
  import Bugsnag from '@bugsnag/node';
2
+ import { loadPackageJson } from './utils.js';
5
3
 
6
- function loadPackageJson() {
7
- const __filename = fileURLToPath(import.meta.url);
8
- const __dirname = dirname(__filename);
9
- const packageJsonPath = path.resolve(__dirname, "../../package.json");
10
- const packageJsonData = fs.readFileSync(packageJsonPath, "utf8");
11
- return JSON.parse(packageJsonData);
12
- }
13
4
  let BugsnagClient;
14
5
  function setupBugsnag(disableBugsnag) {
15
6
  if (disableBugsnag) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/bugsnag.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ;AACf,OAAO,QAAO,eAAc;AAC5B,SAAQ,qBAAoB;AAE5B,OAAO,aAAa;AAGpB,SAAS,kBAAkB;AACzB,QAAM,aAAa,cAAc,YAAY,GAAG;AAChD,QAAM,YAAY,QAAQ,UAAU;AACpC,QAAM,kBAAkB,KAAK,QAAQ,WAAW,oBAAoB;AACpE,QAAM,kBAAkB,GAAG,aAAa,iBAAiB,MAAM;AAC/D,SAAO,KAAK,MAAM,eAAe;AACnC;AAEA,IAAI;AAEJ,SAAS,aAAa,gBAAyB;AAC7C,MAAI,gBAAgB;AAClB;AAAA,EACF,WAAY,QAAgB,SAAS;AACnC,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,gBAAgB;AAEpC,QAAM,uBAA2C;AAAA,IAC/C,QAAQ;AAAA,IACR,kBAAkB;AAAA,IAClB,YAAY,YAAY;AAAA,IACxB,QAAQ;AAAA,EACV;AAEA,SAAQ,QAAgB,MAAM,oBAAoB;AACpD;AAEO,SAAS,kBAAkB,gBAAyB;AACzD,kBAAgB,aAAa,cAAc;AAC7C;AAEO,SAAS,aAAa;AAC3B,SAAO;AACT","sourcesContent":["import fs from 'fs';\nimport path, {dirname} from 'path';\nimport {fileURLToPath} from 'url';\n\nimport Bugsnag from '@bugsnag/node';\nimport * as BugsnagCore from '@bugsnag/core';\n\nfunction loadPackageJson() {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const packageJsonPath = path.resolve(__dirname, '../../package.json');\n const packageJsonData = fs.readFileSync(packageJsonPath, 'utf8');\n return JSON.parse(packageJsonData);\n}\n\nlet BugsnagClient: BugsnagCore.Client | undefined;\n\nfunction setupBugsnag(disableBugsnag: boolean) {\n if (disableBugsnag) {\n return;\n } else if ((Bugsnag as any)._client) {\n return Bugsnag;\n }\n\n const packageJson = loadPackageJson();\n\n const bugsnagServerOptions: BugsnagCore.Config = {\n apiKey: '21fd37fe87ead4b9c5a8eed90752b455',\n autoDetectErrors: true,\n appVersion: packageJson.version,\n logger: null,\n };\n\n return (Bugsnag as any).start(bugsnagServerOptions);\n}\n\nexport function initializeBugsnag(disableBugsnag: boolean) {\n BugsnagClient = setupBugsnag(disableBugsnag);\n}\n\nexport function getBugsnag() {\n return BugsnagClient;\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/bugsnag.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa;AAGpB,SAAQ,uBAAsB;AAE9B,IAAI;AAEJ,SAAS,aAAa,gBAAyB;AAC7C,MAAI,gBAAgB;AAClB;AAAA,EACF,WAAY,QAAgB,SAAS;AACnC,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,gBAAgB;AAEpC,QAAM,uBAA2C;AAAA,IAC/C,QAAQ;AAAA,IACR,kBAAkB;AAAA,IAClB,YAAY,YAAY;AAAA,IACxB,QAAQ;AAAA,EACV;AAEA,SAAQ,QAAgB,MAAM,oBAAoB;AACpD;AAEO,SAAS,kBAAkB,gBAAyB;AACzD,kBAAgB,aAAa,cAAc;AAC7C;AAEO,SAAS,aAAa;AAC3B,SAAO;AACT","sourcesContent":["import Bugsnag from '@bugsnag/node';\nimport * as BugsnagCore from '@bugsnag/core';\n\nimport {loadPackageJson} from './utils.js';\n\nlet BugsnagClient: BugsnagCore.Client | undefined;\n\nfunction setupBugsnag(disableBugsnag: boolean) {\n if (disableBugsnag) {\n return;\n } else if ((Bugsnag as any)._client) {\n return Bugsnag;\n }\n\n const packageJson = loadPackageJson();\n\n const bugsnagServerOptions: BugsnagCore.Config = {\n apiKey: '21fd37fe87ead4b9c5a8eed90752b455',\n autoDetectErrors: true,\n appVersion: packageJson.version,\n logger: null,\n };\n\n return (Bugsnag as any).start(bugsnagServerOptions);\n}\n\nexport function initializeBugsnag(disableBugsnag: boolean) {\n BugsnagClient = setupBugsnag(disableBugsnag);\n}\n\nexport function getBugsnag() {\n return BugsnagClient;\n}\n"]}
@@ -0,0 +1,28 @@
1
+ interface MetricsExporterOptions {
2
+ ciProvider: string;
3
+ rootPath: string;
4
+ }
5
+ declare enum MetricName {
6
+ BuildTime = "build_time",
7
+ NumFilesRequested = "num_files_requested",
8
+ NumFilesUploaded = "num_files_uploaded",
9
+ TotalSizeUploaded = "total_size_uploaded",
10
+ TotalSizeRequested = "total_size_requested",
11
+ UploadTime = "upload_time"
12
+ }
13
+ declare class MetricsExporter {
14
+ private metrics;
15
+ private readonly metricPrefix;
16
+ private readonly defaultTags;
17
+ private readonly exportUrl;
18
+ constructor(options: MetricsExporterOptions);
19
+ add(name: MetricName, value: number, tags?: {
20
+ [key: string]: string;
21
+ }): void;
22
+ formatTags(tags: {
23
+ [key: string]: string;
24
+ }): string[];
25
+ export(): Promise<void>;
26
+ }
27
+
28
+ export { MetricName, MetricsExporter };
@@ -0,0 +1,71 @@
1
+ import { fetch } from '@shopify/cli-kit/node/http';
2
+ import { getBugsnag } from './bugsnag.js';
3
+ import { loadPackageJson } from './utils.js';
4
+
5
+ var MetricName = /* @__PURE__ */ ((MetricName2) => {
6
+ MetricName2["BuildTime"] = "build_time";
7
+ MetricName2["NumFilesRequested"] = "num_files_requested";
8
+ MetricName2["NumFilesUploaded"] = "num_files_uploaded";
9
+ MetricName2["TotalSizeUploaded"] = "total_size_uploaded";
10
+ MetricName2["TotalSizeRequested"] = "total_size_requested";
11
+ MetricName2["UploadTime"] = "upload_time";
12
+ return MetricName2;
13
+ })(MetricName || {});
14
+ class MetricsExporter {
15
+ metrics = {};
16
+ metricPrefix = "oxygen.cli";
17
+ defaultTags;
18
+ exportUrl;
19
+ constructor(options) {
20
+ const { version } = loadPackageJson();
21
+ const { dependencies = {} } = loadPackageJson(options.rootPath);
22
+ this.defaultTags = {
23
+ ci_provider: options.ciProvider,
24
+ cli_version: `oxygen-cli@${version || "unknown"}`,
25
+ hydrogen_version: dependencies["@shopify/hydrogen"] || "unknown"
26
+ };
27
+ this.exportUrl = "https://metrics.myshopify.dev";
28
+ }
29
+ add(name, value, tags = {}) {
30
+ const metric = {
31
+ value,
32
+ tags
33
+ };
34
+ if (this.metrics[name]) {
35
+ this.metrics[name].push(metric);
36
+ } else {
37
+ this.metrics[name] = [metric];
38
+ }
39
+ }
40
+ formatTags(tags) {
41
+ return Object.entries(tags).map(([key, value]) => `${key}:${value}`);
42
+ }
43
+ async export() {
44
+ const metricsToExport = Object.entries(this.metrics).map(([metricName, values]) => {
45
+ const name = `${this.metricPrefix}.${metricName}`;
46
+ return values.map(({ value, tags }) => {
47
+ return {
48
+ name,
49
+ type: "gauge",
50
+ value,
51
+ tags: this.formatTags({ ...tags, ...this.defaultTags })
52
+ };
53
+ });
54
+ }).flat();
55
+ try {
56
+ await fetch(this.exportUrl, {
57
+ method: "POST",
58
+ body: JSON.stringify({ events: metricsToExport })
59
+ });
60
+ } catch (error) {
61
+ const Bugsnag = getBugsnag();
62
+ if (Bugsnag && (error instanceof Error || typeof error === "string")) {
63
+ Bugsnag.notify(error);
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ export { MetricName, MetricsExporter };
70
+ //# sourceMappingURL=out.js.map
71
+ //# sourceMappingURL=metrics-exporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/metrics-exporter.ts"],"names":["MetricName"],"mappings":"AACA,SAAQ,aAAY;AAEpB,SAAQ,kBAAiB;AACzB,SAAQ,uBAAsB;AAkBvB,IAAK,aAAL,kBAAKA,gBAAL;AACL,EAAAA,YAAA,eAAY;AACZ,EAAAA,YAAA,uBAAoB;AACpB,EAAAA,YAAA,sBAAmB;AACnB,EAAAA,YAAA,uBAAoB;AACpB,EAAAA,YAAA,wBAAqB;AACrB,EAAAA,YAAA,gBAAa;AANH,SAAAA;AAAA,GAAA;AASL,MAAM,gBAAgB;AAAA,EACnB,UAA4B,CAAC;AAAA,EACpB,eAAuB;AAAA,EACvB;AAAA,EACA;AAAA,EAEjB,YAAY,SAAiC;AAC3C,UAAM,EAAC,QAAO,IAAI,gBAAgB;AAClC,UAAM,EAAC,eAAe,CAAC,EAAC,IAAI,gBAAgB,QAAQ,QAAQ;AAC5D,SAAK,cAAc;AAAA,MACjB,aAAa,QAAQ;AAAA,MACrB,aAAa,cAAc,WAAW,SAAS;AAAA,MAC/C,kBAAkB,aAAa,mBAAmB,KAAK;AAAA,IACzD;AACA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEA,IACE,MACA,OACA,OAAgC,CAAC,GAC3B;AACN,UAAM,SAAS;AAAA,MACb;AAAA,MACA;AAAA,IACF;AAEA,QAAI,KAAK,QAAQ,IAAI,GAAG;AACtB,WAAK,QAAQ,IAAI,EAAG,KAAK,MAAM;AAAA,IACjC,OAAO;AACL,WAAK,QAAQ,IAAI,IAAI,CAAC,MAAM;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,WAAW,MAAyC;AAClD,WAAO,OAAO,QAAQ,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,IAAI,KAAK,EAAE;AAAA,EACrE;AAAA,EAEA,MAAM,SAAwB;AAC5B,UAAM,kBAAgC,OAAO,QAAQ,KAAK,OAAO,EAC9D,IAAI,CAAC,CAAC,YAAY,MAAM,MAAM;AAC7B,YAAM,OAAO,GAAG,KAAK,YAAY,IAAI,UAAU;AAE/C,aAAO,OAAO,IAAI,CAAC,EAAC,OAAO,KAAI,MAAM;AACnC,eAAO;AAAA,UACL;AAAA,UACA,MAAM;AAAA,UACN;AAAA,UACA,MAAM,KAAK,WAAW,EAAC,GAAG,MAAM,GAAG,KAAK,YAAW,CAAC;AAAA,QACtD;AAAA,MACF,CAAC;AAAA,IACH,CAAC,EACA,KAAK;AAER,QAAI;AACF,YAAM,MAAM,KAAK,WAAW;AAAA,QAC1B,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU,EAAC,QAAQ,gBAAe,CAAC;AAAA,MAChD,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,UAAU,WAAW;AAC3B,UAAI,YAAY,iBAAiB,SAAS,OAAO,UAAU,WAAW;AACpE,gBAAQ,OAAO,KAAK;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AACF","sourcesContent":["/* eslint-disable @typescript-eslint/naming-convention */\nimport {fetch} from '@shopify/cli-kit/node/http';\n\nimport {getBugsnag} from './bugsnag.js';\nimport {loadPackageJson} from './utils.js';\n\ninterface GodogEvent {\n name: string;\n type: string;\n value: number;\n tags: string[];\n}\n\ninterface MetricCollection {\n [metricName: string]: [{value: number; tags: {[key: string]: string}}];\n}\n\ninterface MetricsExporterOptions {\n ciProvider: string;\n rootPath: string;\n}\n\nexport enum MetricName {\n BuildTime = 'build_time',\n NumFilesRequested = 'num_files_requested',\n NumFilesUploaded = 'num_files_uploaded',\n TotalSizeUploaded = 'total_size_uploaded',\n TotalSizeRequested = 'total_size_requested',\n UploadTime = 'upload_time',\n}\n\nexport class MetricsExporter {\n private metrics: MetricCollection = {};\n private readonly metricPrefix: string = 'oxygen.cli';\n private readonly defaultTags: {[key: string]: string};\n private readonly exportUrl: string;\n\n constructor(options: MetricsExporterOptions) {\n const {version} = loadPackageJson();\n const {dependencies = {}} = loadPackageJson(options.rootPath);\n this.defaultTags = {\n ci_provider: options.ciProvider,\n cli_version: `oxygen-cli@${version || 'unknown'}`,\n hydrogen_version: dependencies['@shopify/hydrogen'] || 'unknown',\n };\n this.exportUrl = 'https://metrics.myshopify.dev';\n }\n\n add(\n name: MetricName,\n value: number,\n tags: {[key: string]: string} = {},\n ): void {\n const metric = {\n value,\n tags,\n };\n\n if (this.metrics[name]) {\n this.metrics[name]!.push(metric);\n } else {\n this.metrics[name] = [metric];\n }\n }\n\n formatTags(tags: {[key: string]: string}): string[] {\n return Object.entries(tags).map(([key, value]) => `${key}:${value}`);\n }\n\n async export(): Promise<void> {\n const metricsToExport: GodogEvent[] = Object.entries(this.metrics)\n .map(([metricName, values]) => {\n const name = `${this.metricPrefix}.${metricName}`;\n\n return values.map(({value, tags}) => {\n return {\n name,\n type: 'gauge',\n value,\n tags: this.formatTags({...tags, ...this.defaultTags}),\n };\n });\n })\n .flat();\n\n try {\n await fetch(this.exportUrl, {\n method: 'POST',\n body: JSON.stringify({events: metricsToExport}),\n });\n } catch (error) {\n const Bugsnag = getBugsnag();\n if (Bugsnag && (error instanceof Error || typeof error === 'string')) {\n Bugsnag.notify(error);\n }\n }\n }\n}\n"]}
@@ -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 loadPackageJson(projectFolder?: string): any;
12
13
  declare function stderrLogger(log: string): void;
13
14
  declare const maxLabelLength = 375;
14
15
  declare function parseToken(inputToken: string): DeploymentToken;
@@ -18,4 +19,4 @@ interface VerifyConfigParams {
18
19
  }
19
20
  declare function verifyConfig({ config, performedBuild, }: VerifyConfigParams): Promise<void>;
20
21
 
21
- export { Header, deployDefaults, errorHandler, getBuildCommandFromLockFile, isClientError, maxLabelLength, parseToken, stderrLogger, verifyConfig };
22
+ export { Header, deployDefaults, errorHandler, getBuildCommandFromLockFile, isClientError, loadPackageJson, maxLabelLength, parseToken, stderrLogger, verifyConfig };
@@ -1,3 +1,6 @@
1
+ import fs from 'fs';
2
+ import path, { dirname } from 'path';
3
+ import { fileURLToPath } from 'url';
1
4
  import { fileExistsSync, fileExists } from '@shopify/cli-kit/node/fs';
2
5
  import { outputWarn, outputInfo } from '@shopify/cli-kit/node/output';
3
6
  import { joinPath } from '@shopify/cli-kit/node/path';
@@ -67,6 +70,21 @@ var Header = /* @__PURE__ */ ((Header2) => {
67
70
  function isClientError(error) {
68
71
  return typeof error === "object" && error !== null && "statusCode" in error;
69
72
  }
73
+ function loadPackageJson(projectFolder) {
74
+ let jsonPath;
75
+ if (projectFolder) {
76
+ jsonPath = path.join(projectFolder, "package.json");
77
+ } else {
78
+ const __filename = fileURLToPath(import.meta.url);
79
+ const __dirname = dirname(__filename);
80
+ jsonPath = path.join(__dirname, "../../package.json");
81
+ }
82
+ if (!fs.existsSync(jsonPath)) {
83
+ return {};
84
+ }
85
+ const packageJsonData = fs.readFileSync(jsonPath, "utf8");
86
+ return JSON.parse(packageJsonData);
87
+ }
70
88
  function stderrLogger(log) {
71
89
  process.stderr.write(`${log}
72
90
  `);
@@ -105,15 +123,15 @@ async function verifyConfig({
105
123
  throw new Error(`Invalid deployment service URL: ${config.deploymentUrl}`);
106
124
  }
107
125
  }
108
- async function checkPath(path, pathType) {
109
- if (!await fileExists(path)) {
126
+ async function checkPath(path2, pathType) {
127
+ if (!await fileExists(path2)) {
110
128
  if (pathType === "assets") {
111
129
  outputWarn(
112
130
  `Use the "workerOnly" flag to perform a worker-only deployment.`,
113
131
  stderrLogger
114
132
  );
115
133
  }
116
- throw new Error(`Path not found: ${path}`);
134
+ throw new Error(`Path not found: ${path2}`);
117
135
  }
118
136
  }
119
137
  function convertKeysToCamelCase(obj) {
@@ -133,6 +151,6 @@ function convertKeysToCamelCase(obj) {
133
151
  return obj;
134
152
  }
135
153
 
136
- export { Header, deployDefaults, errorHandler, getBuildCommandFromLockFile, isClientError, maxLabelLength, parseToken, stderrLogger, verifyConfig };
154
+ export { Header, deployDefaults, errorHandler, getBuildCommandFromLockFile, isClientError, loadPackageJson, maxLabelLength, parseToken, stderrLogger, verifyConfig };
137
155
  //# sourceMappingURL=out.js.map
138
156
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/utils.ts"],"names":["Header"],"mappings":"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","sourcesContent":["import {fileExists, fileExistsSync} from '@shopify/cli-kit/node/fs';\nimport {outputInfo, outputWarn} from '@shopify/cli-kit/node/output';\nimport {joinPath, normalizePath} from '@shopify/cli-kit/node/path';\nimport {AbortError} from '@shopify/cli-kit/node/error';\n\nimport {\n ClientError,\n DeploymentConfig,\n DeploymentToken,\n} from '../deploy/types.js';\n\nexport const deployDefaults: {[key: string]: string | number} = {\n assetsDirDefault: 'dist/client/',\n buildCommandDefault: 'yarn build',\n verificationDurationDefault: 180,\n maxUploadAttempts: 3,\n maxResumabeUploadAttempts: 9,\n workerDirDefault: 'dist/worker/',\n};\n\nexport function errorHandler(error: any) {\n if (isClientError(error)) {\n if (error.statusCode === 401) {\n throw new AbortError(\n 'You are not authorized to perform this action. Please check your deployment token.',\n );\n }\n\n if (error.statusCode === 429) {\n throw new AbortError(\n \"You've made too many requests. Please try again later\",\n );\n }\n }\n\n if (error instanceof AbortError && error.message.includes('503')) {\n throw new AbortError(\n 'The server is currently unavailable. Please try again later.',\n );\n }\n}\n\nexport function getBuildCommandFromLockFile(config: DeploymentConfig): string {\n const lockFileBuildCommands = new Map([\n ['package-lock.json', 'npm run build'],\n ['pnpm-lock.yaml', 'pnpm run build'],\n ['yarn.lock', 'yarn build'],\n ]);\n\n const foundLockFiles = [];\n\n for (const [lockFileName, buildCommand] of lockFileBuildCommands) {\n if (fileExistsSync(joinPath(config.rootPath!, lockFileName))) {\n foundLockFiles.push({lockFileName, buildCommand});\n }\n }\n\n if (foundLockFiles.length > 1) {\n const lockFilesList = foundLockFiles\n .map(({lockFileName}) => lockFileName)\n .join(', ');\n outputWarn(\n `Warning: Multiple lock files found: (${lockFilesList}).`,\n stderrLogger,\n );\n }\n\n if (foundLockFiles.length > 0) {\n const {lockFileName, buildCommand} = foundLockFiles[0] as {\n lockFileName: string;\n buildCommand: string;\n };\n\n const infoMsg = foundLockFiles.length > 1 ? '' : `Found: ${lockFileName}. `;\n\n outputInfo(\n `${infoMsg}Assuming \"${buildCommand}\" as build command. Use the buildCommand flag to override.`,\n stderrLogger,\n );\n return buildCommand;\n }\n\n return String(deployDefaults.buildCommandDefault);\n}\n\nexport enum Header {\n OxygenNamespaceHandle = 'X-Oxygen-Namespace-Handle',\n}\n\nexport function isClientError(error: unknown): error is ClientError {\n return typeof error === 'object' && error !== null && 'statusCode' in error;\n}\n\nexport function stderrLogger(log: string): void {\n process.stderr.write(`${log}\\n`);\n}\n\nexport const maxLabelLength = 375;\n\nexport function parseToken(inputToken: string): DeploymentToken {\n try {\n const decodedToken = Buffer.from(inputToken, 'base64').toString('utf-8');\n const rawToken = JSON.parse(decodedToken);\n return convertKeysToCamelCase(rawToken) as DeploymentToken;\n } catch (error) {\n throw new Error(\n `Error processing deployment token. Please check your token and try again.`,\n );\n }\n}\n\ninterface VerifyConfigParams {\n config: DeploymentConfig;\n performedBuild?: boolean;\n}\n\nexport async function verifyConfig({\n config,\n performedBuild = false,\n}: VerifyConfigParams) {\n const {rootPath, workerDir, assetsDir, skipBuild, workerOnly} = config;\n const checkPaths: {[pathType: string]: string} = {\n root: rootPath!,\n };\n\n if (skipBuild || performedBuild) {\n checkPaths.worker = joinPath(rootPath!, workerDir!);\n if (!workerOnly) {\n checkPaths.assets = joinPath(rootPath!, assetsDir!);\n }\n }\n\n for (const pathType of Object.keys(checkPaths)) {\n await checkPath(checkPaths[pathType]!, pathType);\n }\n\n const addressRegex =\n /^https:\\/\\/(?:[\\w-]+\\.)*[\\w-]+|^https:\\/\\/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/;\n if (!addressRegex.test(config.deploymentUrl)) {\n throw new Error(`Invalid deployment service URL: ${config.deploymentUrl}`);\n }\n}\n\nasync function checkPath(path: string, pathType: string) {\n if (!(await fileExists(path))) {\n if (pathType === 'assets') {\n outputWarn(\n `Use the \"workerOnly\" flag to perform a worker-only deployment.`,\n stderrLogger,\n );\n }\n throw new Error(`Path not found: ${path}`);\n }\n}\n\ninterface TokenType {\n [x: string]: string | TokenType;\n}\n\nfunction convertKeysToCamelCase(\n obj: TokenType | string,\n): DeploymentToken | string {\n if (typeof obj === 'object') {\n return Object.keys(obj).reduce((result: any, key: string) => {\n const camelCaseKey = key.replace(/([-_][a-z])/gi, ($1) =>\n $1.toUpperCase().replace('-', '').replace('_', ''),\n );\n if (obj[key] === undefined) {\n throw new Error(`Invalid token: ${key} is undefined`);\n }\n result[camelCaseKey] = convertKeysToCamelCase(obj[key] as TokenType);\n return result;\n }, {} as DeploymentToken);\n }\n return obj;\n}\n"]}
1
+ {"version":3,"sources":["../../src/utils/utils.ts"],"names":["Header","path"],"mappings":"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","sourcesContent":["import fs from 'fs';\nimport path, {dirname} from 'path';\nimport {fileURLToPath} from 'url';\n\nimport {fileExists, fileExistsSync} from '@shopify/cli-kit/node/fs';\nimport {outputInfo, outputWarn} from '@shopify/cli-kit/node/output';\nimport {joinPath} from '@shopify/cli-kit/node/path';\nimport {AbortError} from '@shopify/cli-kit/node/error';\n\nimport {\n ClientError,\n DeploymentConfig,\n DeploymentToken,\n} from '../deploy/types.js';\n\nexport const deployDefaults: {[key: string]: string | number} = {\n assetsDirDefault: 'dist/client/',\n buildCommandDefault: 'yarn build',\n verificationDurationDefault: 180,\n maxUploadAttempts: 3,\n maxResumabeUploadAttempts: 9,\n workerDirDefault: 'dist/worker/',\n};\n\nexport function errorHandler(error: any) {\n if (isClientError(error)) {\n if (error.statusCode === 401) {\n throw new AbortError(\n 'You are not authorized to perform this action. Please check your deployment token.',\n );\n }\n\n if (error.statusCode === 429) {\n throw new AbortError(\n \"You've made too many requests. Please try again later\",\n );\n }\n }\n\n if (error instanceof AbortError && error.message.includes('503')) {\n throw new AbortError(\n 'The server is currently unavailable. Please try again later.',\n );\n }\n}\n\nexport function getBuildCommandFromLockFile(config: DeploymentConfig): string {\n const lockFileBuildCommands = new Map([\n ['package-lock.json', 'npm run build'],\n ['pnpm-lock.yaml', 'pnpm run build'],\n ['yarn.lock', 'yarn build'],\n ]);\n\n const foundLockFiles = [];\n\n for (const [lockFileName, buildCommand] of lockFileBuildCommands) {\n if (fileExistsSync(joinPath(config.rootPath!, lockFileName))) {\n foundLockFiles.push({lockFileName, buildCommand});\n }\n }\n\n if (foundLockFiles.length > 1) {\n const lockFilesList = foundLockFiles\n .map(({lockFileName}) => lockFileName)\n .join(', ');\n outputWarn(\n `Warning: Multiple lock files found: (${lockFilesList}).`,\n stderrLogger,\n );\n }\n\n if (foundLockFiles.length > 0) {\n const {lockFileName, buildCommand} = foundLockFiles[0] as {\n lockFileName: string;\n buildCommand: string;\n };\n\n const infoMsg = foundLockFiles.length > 1 ? '' : `Found: ${lockFileName}. `;\n\n outputInfo(\n `${infoMsg}Assuming \"${buildCommand}\" as build command. Use the buildCommand flag to override.`,\n stderrLogger,\n );\n return buildCommand;\n }\n\n return String(deployDefaults.buildCommandDefault);\n}\n\nexport enum Header {\n OxygenNamespaceHandle = 'X-Oxygen-Namespace-Handle',\n}\n\nexport function isClientError(error: unknown): error is ClientError {\n return typeof error === 'object' && error !== null && 'statusCode' in error;\n}\n\nexport function loadPackageJson(projectFolder?: string) {\n let jsonPath;\n if (projectFolder) {\n jsonPath = path.join(projectFolder, 'package.json');\n } else {\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n jsonPath = path.join(__dirname, '../../package.json');\n }\n\n if (!fs.existsSync(jsonPath)) {\n return {};\n }\n const packageJsonData = fs.readFileSync(jsonPath, 'utf8');\n return JSON.parse(packageJsonData);\n}\n\nexport function stderrLogger(log: string): void {\n process.stderr.write(`${log}\\n`);\n}\n\nexport const maxLabelLength = 375;\n\nexport function parseToken(inputToken: string): DeploymentToken {\n try {\n const decodedToken = Buffer.from(inputToken, 'base64').toString('utf-8');\n const rawToken = JSON.parse(decodedToken);\n return convertKeysToCamelCase(rawToken) as DeploymentToken;\n } catch (error) {\n throw new Error(\n `Error processing deployment token. Please check your token and try again.`,\n );\n }\n}\n\ninterface VerifyConfigParams {\n config: DeploymentConfig;\n performedBuild?: boolean;\n}\n\nexport async function verifyConfig({\n config,\n performedBuild = false,\n}: VerifyConfigParams) {\n const {rootPath, workerDir, assetsDir, skipBuild, workerOnly} = config;\n const checkPaths: {[pathType: string]: string} = {\n root: rootPath!,\n };\n\n if (skipBuild || performedBuild) {\n checkPaths.worker = joinPath(rootPath!, workerDir!);\n if (!workerOnly) {\n checkPaths.assets = joinPath(rootPath!, assetsDir!);\n }\n }\n\n for (const pathType of Object.keys(checkPaths)) {\n await checkPath(checkPaths[pathType]!, pathType);\n }\n\n const addressRegex =\n /^https:\\/\\/(?:[\\w-]+\\.)*[\\w-]+|^https:\\/\\/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}/;\n if (!addressRegex.test(config.deploymentUrl)) {\n throw new Error(`Invalid deployment service URL: ${config.deploymentUrl}`);\n }\n}\n\nasync function checkPath(path: string, pathType: string) {\n if (!(await fileExists(path))) {\n if (pathType === 'assets') {\n outputWarn(\n `Use the \"workerOnly\" flag to perform a worker-only deployment.`,\n stderrLogger,\n );\n }\n throw new Error(`Path not found: ${path}`);\n }\n}\n\ninterface TokenType {\n [x: string]: string | TokenType;\n}\n\nfunction convertKeysToCamelCase(\n obj: TokenType | string,\n): DeploymentToken | string {\n if (typeof obj === 'object') {\n return Object.keys(obj).reduce((result: any, key: string) => {\n const camelCaseKey = key.replace(/([-_][a-z])/gi, ($1) =>\n $1.toUpperCase().replace('-', '').replace('_', ''),\n );\n if (obj[key] === undefined) {\n throw new Error(`Invalid token: ${key} is undefined`);\n }\n result[camelCaseKey] = convertKeysToCamelCase(obj[key] as TokenType);\n return result;\n }, {} as DeploymentToken);\n }\n return obj;\n}\n"]}
@@ -168,5 +168,5 @@
168
168
  ]
169
169
  }
170
170
  },
171
- "version": "2.1.3-unstable.202310231610.0"
171
+ "version": "2.2.1-unstable.202310241146.0"
172
172
  }
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "@shopify:registry": "https://registry.npmjs.org"
6
6
  },
7
7
  "license": "MIT",
8
- "version": "2.1.3-unstable.202310231610.0",
8
+ "version": "2.2.1-unstable.202310241146.0",
9
9
  "type": "module",
10
10
  "scripts": {
11
11
  "build": "tsup --sourcemap --clean --config ./tsup.config.ts && oclif manifest",