@shopify/oxygen-cli 2.1.3-unstable.202310231557.0 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- package/README.md +1 -1
- package/dist/commands/oxygen/deploy.js +2 -1
- package/dist/commands/oxygen/deploy.js.map +1 -1
- package/dist/deploy/build-project.d.ts +2 -0
- package/dist/deploy/build-project.js +16 -5
- package/dist/deploy/build-project.js.map +1 -1
- package/dist/deploy/get-upload-files.d.ts +2 -1
- package/dist/deploy/get-upload-files.js +5 -1
- package/dist/deploy/get-upload-files.js.map +1 -1
- package/dist/deploy/index.js +11 -3
- package/dist/deploy/index.js.map +1 -1
- package/dist/deploy/upload-files.d.ts +2 -0
- package/dist/deploy/upload-files.js +7 -0
- package/dist/deploy/upload-files.js.map +1 -1
- package/dist/utils/bugsnag.js +1 -10
- package/dist/utils/bugsnag.js.map +1 -1
- package/dist/utils/metrics-exporter.d.ts +28 -0
- package/dist/utils/metrics-exporter.js +71 -0
- package/dist/utils/metrics-exporter.js.map +1 -0
- package/dist/utils/utils.d.ts +2 -1
- package/dist/utils/utils.js +22 -4
- package/dist/utils/utils.js.map +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
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
|
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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
|
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
|
-
|
10
|
-
|
11
|
-
|
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;
|
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,
|
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"]}
|
package/dist/deploy/index.js
CHANGED
@@ -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,
|
package/dist/deploy/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/deploy/index.ts"],"names":[],"mappings":"AAAA;AAAA,EAEE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEP,SAAQ,cAAc,oBAAmB;AACzC,SAAQ,YAAY,yBAAwB;
|
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":"AACA,SAAQ,aAAY;AAEpB,SAAQ,OAAO,gBAAe;AAC9B,SAAQ,4BAA2B;AACnC;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AACP,SAAQ,gBAAe;AACvB,SAAQ,gBAAe;AAEvB,SAAQ,sBAAqB;AAC7B,SAAyB,kBAAiB;AAwB1C,eAAsB,YAAY,SAA4C;AAC5E,QAAM,EAAC,QAAQ,QAAQ,SAAS,MAAK,IAAI;AACzC,aAAW,aAAa,QAAQ,MAAM,aAAa,MAAM;AAEzD,QAAM,kBACJ,QAAQ,OAAO,CAAC,OAAO,WAAW,QAAQ,OAAO,UAAU,CAAC,IAAI;AAClE,UAAQ,iBAAiB,IAAI,WAAW,mBAAmB,eAAe;AAC1E,UAAQ,iBAAiB,IAAI,WAAW,kBAAkB,QAAQ,MAAM;AAExE,QAAM,QAAQ,IAAI,MAAM,EAAC,WAAW,KAAI,CAAC;AACzC,SAAO,qBAAqB;AAC5B,QAAM,YAAY,YAAY,IAAI;AAClC,SAAO,SAAS,SAAS,GAAG,OAAO,WAAqC;AACtE,UAAM,WAAW,QAAQ,QAAQ,KAAK;AAAA,EACxC,CAAC,EACE,KAAK,MAAM;AACV,UAAM,UAAU,YAAY,IAAI;AAChC,WAAO,wBAAwB;AAC/B,YAAQ,iBAAiB,IAAI,WAAW,YAAY,UAAU,SAAS;AACvE,oBAAgB,+BAA+B,MAAM;AAAA,EACvD,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,WAAO,qBAAqB,GAAG;AAC/B,UAAM;AAAA,EACR,CAAC,EACA,QAAQ,MAAM;AACb,UAAM,QAAQ;AAAA,EAChB,CAAC;AACL;AAEA,eAAe,WACb,QACA,QACA,OACA;AACA,QAAM,kBACJ,OAAO,aAAa,WAChB,SAAS,OAAO,UAAW,OAAO,SAAU,IAC5C,SAAS,OAAO,UAAW,OAAO,SAAU;AAElD,MAAI,OAAO,eAAe,QAAQ,OAAO,WAAW,SAAS,GAAG;AAE9D,UAAM,OAAO,SAAS;AACtB,WAAO,WAAW,QAAQ,CAAC,UAAU;AACnC,WAAK,OAAO,MAAM,MAAM,MAAM,KAAK;AAAA,IACrC,CAAC;AACD,SAAK;AAAA,MACH;AAAA,MACA,qBAAqB,SAAS,iBAAiB,OAAO,QAAQ,CAAC;AAAA,IACjE;AACA,UAAM,WAAW,MAAM,QAAQ,KAAK;AAAA,EACtC,OAAO;AAEL,UAAM,WAAW,MAAM,wBAAwB,QAAQ,KAAK;AAC5D,UAAM;AAAA,MACJ,SAAS,iBAAiB,OAAO,QAAQ;AAAA,MACzC;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,WACb,MACA,QACA,OACA,gBAAgB,GAChB;AACA,MAAI;AACF,UAAM,kBAAkB;AACxB,UAAM,aAAa,IAAI,gBAAgB;AACvC,UAAM,UAAU,WAAW,MAAM;AAC/B,iBAAW,MAAM;AAAA,IACnB,GAAG,eAAe;AAElB,UAAM,WAAW,MAAM,MAAM,OAAO,WAAW;AAAA,MAC7C,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,QAAQ,WAAW;AAAA,MACnB,SAAS;AAAA,QACP,YAAY;AAAA,MACd;AAAA,MACA;AAAA,IACF,CAAC;AACD,iBAAa,OAAO;AAEpB,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,GAAG,SAAS,MAAM,EAAE;AAAA,IACtC;AAAA,EACF,SAAS,KAAK;AACZ,QAAI,YAAY,KAAK,QAAQ,GAAG;AAC9B,YAAM,IAAI,MAAM,mBAAmB,OAAO,QAAQ,EAAE;AAAA,IACtD;AAEA,QAAI,gBAAgB,OAAO,eAAe,iBAAiB,GAAG;AAC5D,YAAM,WAAW,MAAM,QAAQ,OAAO,gBAAgB,CAAC;AAAA,IACzD,OAAO;AACL,UAAI,eAAe,SAAS,IAAI,SAAS,cAAc;AACrD,cAAM,IAAI,MAAM,oCAAoC,OAAO,QAAQ,EAAE;AAAA,MACvE;AACA,YAAM,IAAI,MAAM,yBAAyB,OAAO,QAAQ,EAAE;AAAA,IAC5D;AAAA,EACF;AACF;AAEA,eAAe,wBACb,QACA,OACoC;AACpC,SAAO,MAAM,OAAO,WAAW;AAAA,IAC7B,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,oBAAoB;AAAA,MACpB,+BAA+B,KAAK,OAAO,QAAQ;AAAA,MACnD,cAAc;AAAA,IAChB;AAAA,IACA;AAAA,EACF,CAAC,EACE,KAAK,CAAC,QAAQ;AACb,WAAO;AAAA,MACL,YAAY,IAAI,QAAQ,IAAI,sBAAsB;AAAA,MAClD,UAAU,IAAI,QAAQ,IAAI,UAAU;AAAA,MACpC;AAAA,IACF;AAAA,EACF,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,UAAM,IAAI;AAAA,MACR,gDAAgD,OAAO,QAAQ,iBAAiB,IAAI,UAAU;AAAA,IAChG;AAAA,EACF,CAAC;AACL;AAEA,eAAe,uBACb,eACA,UACA,OACA,YAAY,GACZ,gBAAgB,GAChB;AACA,QAAM;AAAA,IACJ,SAAS;AAAA,IACT;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,MAAM,OAAO,QAAQ;AACrB,QAAI,YAAY,KAAK,QAAQ,GAAG;AAC9B,YAAM,IAAI,MAAM,mBAAmB,SAAS,OAAO,QAAQ,EAAE;AAAA,IAC/D;AAEA,QACE,OACA,iBAAiB,OAAO,eAAe,yBAAyB,GAChE;AACA,YAAM,IAAI;AAAA,QACR,yBAAyB,SAAS,OAAO,QAAQ,UAAU,eAAe,yBAAyB;AAAA,MACrG;AAAA,IACF;AACA,UAAM,SAAS,MAAM;AAAA,MACnB,SAAS;AAAA,MACT,SAAS,OAAO;AAAA,MAChB;AAAA,IACF;AACA,QAAI,CAAC,OAAO,UAAU;AACpB,YAAM,uBAAuB,OAAO;AACpC,YAAM,UAAU,gBAAgB;AAChC,YAAM;AAAA,QACJ;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,eAAe,gBACb,UACA,UACA,kBACA,OACiB;AACjB,QAAM,OAAO,qBAAqB,UAAU,EAAC,OAAO,iBAAgB,CAAC;AACrE,SAAO,MAAM,UAAU;AAAA,IACrB,QAAQ;AAAA,IACR,MAAM;AAAA,IACN;AAAA,EACF,CAAC,EAAE,KAAK,CAAC,QAAQ;AACf,WAAO,IAAI;AAAA,EACb,CAAC;AACH;AAEA,eAAe,sBACb,UACA,UACA,OACgC;AAChC,QAAM,cAAc,CAAC,UAAiC;AACpD,QAAI,CAAC,SAAS,MAAM,MAAM,GAAG,EAAE,WAAW;AAAG,aAAO;AACpD,UAAM,aAAa,MAAM,MAAM,GAAG;AAClC,WAAO,SAAS,WAAW,CAAC,GAAI,EAAE;AAAA,EACpC;AAEA,SAAO,MAAM,UAAU;AAAA,IACrB,QAAQ;AAAA,IACR,SAAS;AAAA,MACP,kBAAkB;AAAA,MAClB,iBAAiB,WAAW,QAAQ;AAAA,IACtC;AAAA,IACA;AAAA,EACF,CAAC,EACE,KAAK,CAAC,QAAQ;AACb,WAAO;AAAA,MACL,UAAU,IAAI,WAAW;AAAA,MACzB,kBAAkB,YAAY,IAAI,QAAQ,IAAI,OAAO,CAAC;AAAA,IACxD;AAAA,EACF,CAAC,EACA,MAAM,CAAC,QAAQ;AAEd,YAAQ,MAAM,GAAG;AACjB,WAAO;AAAA,MACL,UAAU;AAAA,MACV,kBAAkB;AAAA,IACpB;AAAA,EACF,CAAC;AACL;AAEA,SAAS,YAAY,KAAc,MAAuB;AACxD,SAAO,eAAe,SAAS,UAAU,OAAO,IAAI,SAAS;AAC/D","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"]}
|
package/dist/utils/bugsnag.js
CHANGED
@@ -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,
|
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"]}
|
package/dist/utils/utils.d.ts
CHANGED
@@ -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 };
|
package/dist/utils/utils.js
CHANGED
@@ -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(
|
109
|
-
if (!await fileExists(
|
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: ${
|
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
|
package/dist/utils/utils.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../src/utils/utils.ts"],"names":["Header"],"mappings":"AAAA,SAAQ,YAAY,sBAAqB;AACzC,SAAQ,YAAY,kBAAiB;AACrC,SAAQ,
|
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"]}
|
package/oclif.manifest.json
CHANGED
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.
|
8
|
+
"version": "2.2.0",
|
9
9
|
"type": "module",
|
10
10
|
"scripts": {
|
11
11
|
"build": "tsup --sourcemap --clean --config ./tsup.config.ts && oclif manifest",
|
@@ -39,7 +39,7 @@
|
|
39
39
|
"@bugsnag/js": "^7.21.0",
|
40
40
|
"@bugsnag/node": "^7.19.0",
|
41
41
|
"@oclif/core": "3.0.3",
|
42
|
-
"@shopify/cli-kit": "^3.
|
42
|
+
"@shopify/cli-kit": "^3.50.0",
|
43
43
|
"async": "^3.2.4"
|
44
44
|
},
|
45
45
|
"devDependencies": {
|
@@ -49,7 +49,7 @@
|
|
49
49
|
"@shopify/prettier-config": "^1.1.2",
|
50
50
|
"@types/async": "^3.2.21",
|
51
51
|
"@types/node": "^20.6.5",
|
52
|
-
"eslint": "^8.
|
52
|
+
"eslint": "^8.51.0",
|
53
53
|
"eslint-plugin-prettier": "^5.0.1",
|
54
54
|
"node-fetch": "^3.3.2",
|
55
55
|
"oclif": "^4",
|