@shopify/oxygen-cli 1.4.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/commands/oxygen/deploy.d.ts +9 -9
- package/dist/commands/oxygen/deploy.js +58 -43
- package/dist/deploy/build-cancel.d.ts +9 -2
- package/dist/deploy/build-cancel.js +4 -3
- package/dist/deploy/build-cancel.test.js +21 -4
- package/dist/deploy/build-initiate.d.ts +9 -2
- package/dist/deploy/build-initiate.js +4 -3
- package/dist/deploy/build-initiate.test.js +19 -8
- package/dist/deploy/build-project.d.ts +7 -2
- package/dist/deploy/build-project.js +34 -19
- package/dist/deploy/build-project.test.js +14 -8
- package/dist/deploy/deployment-cancel.d.ts +9 -2
- package/dist/deploy/deployment-cancel.js +4 -3
- package/dist/deploy/deployment-cancel.test.js +19 -16
- package/dist/deploy/deployment-complete.d.ts +2 -2
- package/dist/deploy/deployment-initiate.d.ts +10 -4
- package/dist/deploy/deployment-initiate.js +4 -3
- package/dist/deploy/deployment-initiate.test.js +26 -10
- package/dist/deploy/get-upload-files.d.ts +2 -2
- package/dist/deploy/health-check.d.ts +12 -0
- package/dist/deploy/health-check.js +44 -0
- package/dist/deploy/health-check.test.d.ts +2 -0
- package/dist/deploy/health-check.test.js +92 -0
- package/dist/deploy/index.d.ts +10 -3
- package/dist/deploy/index.js +54 -26
- package/dist/deploy/metadata.d.ts +4 -3
- package/dist/deploy/metadata.js +3 -3
- package/dist/deploy/metadata.test.js +4 -4
- package/dist/deploy/types.d.ts +17 -2
- package/dist/deploy/types.js +3 -1
- package/dist/deploy/upload-files.d.ts +9 -2
- package/dist/deploy/upload-files.js +7 -4
- package/dist/deploy/upload-files.test.js +37 -18
- package/dist/utils/test-helper.d.ts +2 -2
- package/dist/utils/test-helper.js +3 -0
- package/dist/utils/utils.d.ts +3 -3
- package/dist/utils/utils.js +1 -5
- package/oclif.manifest.json +50 -33
- package/package.json +8 -8
@@ -1,10 +1,11 @@
|
|
1
1
|
import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
|
2
2
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
3
3
|
import { outputCompleted } from '@shopify/cli-kit/node/output';
|
4
|
-
import { Header,
|
4
|
+
import { Header, errorHandler } from '../utils/utils.js';
|
5
5
|
import { DeploymentInitiateQuery } from './graphql/deployment-initiate.js';
|
6
6
|
|
7
|
-
async function deploymentInitiate(
|
7
|
+
async function deploymentInitiate(options) {
|
8
|
+
const { config, input, logger } = options;
|
8
9
|
const variables = {
|
9
10
|
buildId: input.buildId,
|
10
11
|
environment: input.environment,
|
@@ -30,7 +31,7 @@ async function deploymentInitiate(config, input) {
|
|
30
31
|
}
|
31
32
|
outputCompleted(
|
32
33
|
`Deployment initiated, ${response.deploymentInitiate.deploymentTargets.length} files to upload.`,
|
33
|
-
|
34
|
+
logger
|
34
35
|
);
|
35
36
|
return response.deploymentInitiate;
|
36
37
|
} catch (error) {
|
@@ -3,7 +3,7 @@ import { graphqlRequest } from '@shopify/cli-kit/node/api/graphql';
|
|
3
3
|
import { outputCompleted } from '@shopify/cli-kit/node/output';
|
4
4
|
import { vi, describe, test, expect } from 'vitest';
|
5
5
|
import { createTestConfig } from '../utils/test-helper.js';
|
6
|
-
import {
|
6
|
+
import { stderrLogger, Header } from '../utils/utils.js';
|
7
7
|
import { deploymentInitiate } from './deployment-initiate.js';
|
8
8
|
|
9
9
|
vi.mock("@shopify/cli-kit/node/api/graphql");
|
@@ -38,9 +38,13 @@ const testResponse = {
|
|
38
38
|
describe("DeploymentInitiate", () => {
|
39
39
|
test("should initiate a deployment with a buildId", async () => {
|
40
40
|
vi.mocked(graphqlRequest).mockResolvedValueOnce(testResponse);
|
41
|
-
const initiateResponse = await deploymentInitiate(
|
42
|
-
|
43
|
-
|
41
|
+
const initiateResponse = await deploymentInitiate({
|
42
|
+
config: testConfig,
|
43
|
+
input: {
|
44
|
+
buildId: "build-1",
|
45
|
+
manifest: testManifest
|
46
|
+
},
|
47
|
+
logger: stderrLogger
|
44
48
|
});
|
45
49
|
expect(initiateResponse).toEqual(testResponse.deploymentInitiate);
|
46
50
|
expect(graphqlRequest).toHaveBeenCalledWith({
|
@@ -65,10 +69,14 @@ describe("DeploymentInitiate", () => {
|
|
65
69
|
});
|
66
70
|
test("should initiate a deployment with an environmentName", async () => {
|
67
71
|
vi.mocked(graphqlRequest).mockResolvedValueOnce(testResponse);
|
68
|
-
const initiateResponse = await deploymentInitiate(
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
+
const initiateResponse = await deploymentInitiate({
|
73
|
+
config: testConfig,
|
74
|
+
input: {
|
75
|
+
buildId: void 0,
|
76
|
+
environment: { tag: "preview" },
|
77
|
+
manifest: testManifest
|
78
|
+
},
|
79
|
+
logger: stderrLogger
|
72
80
|
});
|
73
81
|
expect(initiateResponse).toEqual(testResponse.deploymentInitiate);
|
74
82
|
expect(graphqlRequest).toHaveBeenCalledWith({
|
@@ -108,7 +116,11 @@ describe("DeploymentInitiate", () => {
|
|
108
116
|
manifest: testManifest
|
109
117
|
};
|
110
118
|
await expect(
|
111
|
-
deploymentInitiate(
|
119
|
+
deploymentInitiate({
|
120
|
+
config: testConfig,
|
121
|
+
input: deploymentInitData,
|
122
|
+
logger: stderrLogger
|
123
|
+
})
|
112
124
|
).rejects.toThrow(
|
113
125
|
new AbortError(
|
114
126
|
`Failed to create deployment. ${response.deploymentInitiate.userErrors[0]?.message}`
|
@@ -127,7 +139,11 @@ describe("DeploymentInitiate", () => {
|
|
127
139
|
manifest: testManifest
|
128
140
|
};
|
129
141
|
await expect(
|
130
|
-
deploymentInitiate(
|
142
|
+
deploymentInitiate({
|
143
|
+
config: testConfig,
|
144
|
+
input: deploymentInitData,
|
145
|
+
logger: stderrLogger
|
146
|
+
})
|
131
147
|
).rejects.toThrow(
|
132
148
|
new AbortError(
|
133
149
|
"You are not authorized to perform this action. Please check your deployment token."
|
@@ -1,5 +1,5 @@
|
|
1
|
-
import {
|
1
|
+
import { DeploymentConfig, DeploymentManifestFile } from './types.js';
|
2
2
|
|
3
|
-
declare function getUploadFiles(config:
|
3
|
+
declare function getUploadFiles(config: DeploymentConfig): Promise<DeploymentManifestFile[]>;
|
4
4
|
|
5
5
|
export { getUploadFiles };
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { Logger } from '@shopify/cli-kit/node/output';
|
2
|
+
import { DeploymentConfig, DeploymentHooks } from './types.js';
|
3
|
+
|
4
|
+
interface HealthCheckOptions {
|
5
|
+
config: DeploymentConfig;
|
6
|
+
hooks?: DeploymentHooks;
|
7
|
+
logger: Logger;
|
8
|
+
url: string;
|
9
|
+
}
|
10
|
+
declare function healthCheck(options: HealthCheckOptions): Promise<void>;
|
11
|
+
|
12
|
+
export { healthCheck };
|
@@ -0,0 +1,44 @@
|
|
1
|
+
import { fetch } from '@shopify/cli-kit/node/http';
|
2
|
+
import { outputInfo } from '@shopify/cli-kit/node/output';
|
3
|
+
import { HealthCheckError } from './types.js';
|
4
|
+
|
5
|
+
async function healthCheck(options) {
|
6
|
+
const { config, url, logger, hooks } = options;
|
7
|
+
hooks?.onHealthCheckStart?.();
|
8
|
+
outputInfo("Performing health check on the deployment...", logger);
|
9
|
+
let attempts = 0;
|
10
|
+
let delay = 0;
|
11
|
+
const startTime = Date.now();
|
12
|
+
const handleInterval = async () => {
|
13
|
+
if (attempts < 10) {
|
14
|
+
delay = 500;
|
15
|
+
} else if (attempts % 5 === 0) {
|
16
|
+
delay += 5e3;
|
17
|
+
}
|
18
|
+
const elapsedTime = (Date.now() - startTime) / 1e3;
|
19
|
+
if (elapsedTime + delay / 1e3 > config.healthCheckMaxDuration) {
|
20
|
+
const error = new HealthCheckError("Unable to verify deployment health.");
|
21
|
+
hooks?.onHealthCheckError?.(error);
|
22
|
+
throw error;
|
23
|
+
}
|
24
|
+
attempts++;
|
25
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
26
|
+
await check();
|
27
|
+
};
|
28
|
+
const check = async () => {
|
29
|
+
try {
|
30
|
+
const response = await fetch(`${url}/.oxygen/deployment`);
|
31
|
+
if (response.status === 200) {
|
32
|
+
outputInfo("Deployment health check passed", logger);
|
33
|
+
hooks?.onHealthCheckComplete?.();
|
34
|
+
return Promise.resolve();
|
35
|
+
}
|
36
|
+
await handleInterval();
|
37
|
+
} catch {
|
38
|
+
await handleInterval();
|
39
|
+
}
|
40
|
+
};
|
41
|
+
await check();
|
42
|
+
}
|
43
|
+
|
44
|
+
export { healthCheck };
|
@@ -0,0 +1,92 @@
|
|
1
|
+
import { vi, describe, beforeEach, it, expect } from 'vitest';
|
2
|
+
import { fetch } from '@shopify/cli-kit/node/http';
|
3
|
+
import { Response } from 'node-fetch';
|
4
|
+
import { stderrLogger } from '../utils/utils.js';
|
5
|
+
import { createTestConfig } from '../utils/test-helper.js';
|
6
|
+
import { HealthCheckError } from './types.js';
|
7
|
+
import { healthCheck } from './health-check.js';
|
8
|
+
|
9
|
+
vi.mock("@shopify/cli-kit/node/http", async () => {
|
10
|
+
const actual = await vi.importActual("@shopify/cli-kit/node/http");
|
11
|
+
return {
|
12
|
+
...actual,
|
13
|
+
fetch: vi.fn()
|
14
|
+
};
|
15
|
+
});
|
16
|
+
vi.mock("@shopify/cli-kit/node/output");
|
17
|
+
const testConfig = createTestConfig("rootFolder");
|
18
|
+
describe("healthCheck", () => {
|
19
|
+
let setTimeoutSpy;
|
20
|
+
const hooks = {
|
21
|
+
onHealthCheckError: vi.fn(),
|
22
|
+
onHealthCheckStart: vi.fn(),
|
23
|
+
onHealthCheckComplete: vi.fn()
|
24
|
+
};
|
25
|
+
const url = "http://example.com";
|
26
|
+
beforeEach(() => {
|
27
|
+
vi.mocked(fetch).mockReset();
|
28
|
+
setTimeoutSpy = vi.spyOn(global, "setTimeout").mockImplementation((cb, _delay) => {
|
29
|
+
cb();
|
30
|
+
return {};
|
31
|
+
});
|
32
|
+
});
|
33
|
+
it("resolves when URL is accessible", async () => {
|
34
|
+
const response = new Response();
|
35
|
+
vi.mocked(fetch).mockResolvedValueOnce(response);
|
36
|
+
await healthCheck({ config: testConfig, url, logger: stderrLogger, hooks });
|
37
|
+
expect(fetch).toHaveBeenCalledTimes(1);
|
38
|
+
expect(fetch).toHaveBeenCalledWith(`${url}/.oxygen/deployment`);
|
39
|
+
expect(hooks.onHealthCheckStart).toBeCalled();
|
40
|
+
expect(hooks.onHealthCheckComplete).toBeCalled();
|
41
|
+
});
|
42
|
+
it("repeats request until URL is accessible with progressive timeout", async () => {
|
43
|
+
const responseFail = new Response(null, { status: 404 });
|
44
|
+
const responseSuccess = new Response(null, { status: 200 });
|
45
|
+
let attempts = 0;
|
46
|
+
vi.mocked(fetch).mockImplementation(() => {
|
47
|
+
if (attempts === 30) {
|
48
|
+
return Promise.resolve(responseSuccess);
|
49
|
+
}
|
50
|
+
attempts++;
|
51
|
+
return Promise.resolve(responseFail);
|
52
|
+
});
|
53
|
+
await new Promise((resolve, reject) => {
|
54
|
+
healthCheck({ config: testConfig, url, logger: stderrLogger, hooks }).then(() => {
|
55
|
+
expect(fetch).toHaveBeenCalledTimes(attempts + 1);
|
56
|
+
expect(fetch).toHaveBeenCalledWith(`${url}/.oxygen/deployment`);
|
57
|
+
expect(setTimeoutSpy).toHaveBeenCalledTimes(attempts);
|
58
|
+
expect(hooks.onHealthCheckStart).toBeCalled();
|
59
|
+
expect(hooks.onHealthCheckComplete).toBeCalled();
|
60
|
+
let expectedDuration = 500;
|
61
|
+
setTimeoutSpy.mock.calls.forEach(
|
62
|
+
(call, index) => {
|
63
|
+
if (index >= 10 && index % 5 === 0) {
|
64
|
+
expectedDuration += 5e3;
|
65
|
+
}
|
66
|
+
expect(call[1]).toBe(expectedDuration);
|
67
|
+
}
|
68
|
+
);
|
69
|
+
resolve();
|
70
|
+
}).catch((error) => {
|
71
|
+
reject(error);
|
72
|
+
});
|
73
|
+
});
|
74
|
+
});
|
75
|
+
it("throws an error after max duration", async () => {
|
76
|
+
vi.useFakeTimers();
|
77
|
+
const responseFail = new Response(null, { status: 404 });
|
78
|
+
vi.mocked(fetch).mockResolvedValue(responseFail);
|
79
|
+
const healthCheckPromise = healthCheck({
|
80
|
+
config: testConfig,
|
81
|
+
url,
|
82
|
+
logger: stderrLogger,
|
83
|
+
hooks
|
84
|
+
});
|
85
|
+
vi.setSystemTime(Date.now() + testConfig.healthCheckMaxDuration * 1e3);
|
86
|
+
await expect(healthCheckPromise).rejects.toThrow(HealthCheckError);
|
87
|
+
expect(fetch).toHaveBeenCalledOnce();
|
88
|
+
expect(hooks.onHealthCheckStart).toBeCalled();
|
89
|
+
expect(hooks.onHealthCheckError).toBeCalled();
|
90
|
+
vi.useRealTimers();
|
91
|
+
});
|
92
|
+
});
|
package/dist/deploy/index.d.ts
CHANGED
@@ -1,5 +1,12 @@
|
|
1
|
-
import {
|
1
|
+
import { Logger } from '@shopify/cli-kit/node/output';
|
2
|
+
import { DeploymentConfig, DeploymentHooks } from './types.js';
|
3
|
+
export { parseToken } from '../utils/utils.js';
|
2
4
|
|
3
|
-
|
5
|
+
interface CreateDeployOptions {
|
6
|
+
config: DeploymentConfig;
|
7
|
+
hooks?: DeploymentHooks;
|
8
|
+
logger?: Logger;
|
9
|
+
}
|
10
|
+
declare function createDeploy(options: CreateDeployOptions): Promise<string | undefined>;
|
4
11
|
|
5
|
-
export { createDeploy };
|
12
|
+
export { DeploymentConfig, DeploymentHooks, createDeploy };
|
package/dist/deploy/index.js
CHANGED
@@ -1,88 +1,116 @@
|
|
1
|
-
import { outputSuccess, outputInfo, outputWarn
|
2
|
-
import {
|
1
|
+
import { outputSuccess, outputInfo, outputWarn } from '@shopify/cli-kit/node/output';
|
2
|
+
import { stderrLogger, verifyConfig } from '../utils/utils.js';
|
3
|
+
export { parseToken } from '../utils/utils.js';
|
3
4
|
import { buildInitiate } from './build-initiate.js';
|
4
5
|
import { buildCancel } from './build-cancel.js';
|
5
6
|
import { getUploadFiles } from './get-upload-files.js';
|
6
7
|
import { deploymentInitiate } from './deployment-initiate.js';
|
7
8
|
import { deploymentComplete } from './deployment-complete.js';
|
9
|
+
import { healthCheck } from './health-check.js';
|
8
10
|
import { deploymentCancel, DeploymentCancelReason } from './deployment-cancel.js';
|
9
11
|
import { uploadFiles } from './upload-files.js';
|
12
|
+
import { HealthCheckError } from './types.js';
|
10
13
|
import { buildProject } from './build-project.js';
|
11
14
|
import { getMetadata, createLabels, getEnvironmentInput } from './metadata.js';
|
12
15
|
|
13
|
-
async function createDeploy(
|
16
|
+
async function createDeploy(options) {
|
17
|
+
const { config, hooks } = options;
|
18
|
+
const logger = options.logger ?? stderrLogger;
|
14
19
|
const build = {};
|
15
20
|
let buildCompleted;
|
16
21
|
let deployment;
|
17
22
|
try {
|
18
|
-
const metadata = await getMetadata(config);
|
23
|
+
const metadata = await getMetadata(config, logger);
|
19
24
|
const labels = createLabels(metadata);
|
20
25
|
const environment = getEnvironmentInput(config, metadata);
|
21
26
|
if (!config.workerOnly && !config.skipBuild) {
|
22
|
-
const buildInitiateResponse = await buildInitiate(
|
27
|
+
const buildInitiateResponse = await buildInitiate({
|
23
28
|
config,
|
24
29
|
environment,
|
25
|
-
labels
|
26
|
-
|
30
|
+
labels,
|
31
|
+
logger
|
32
|
+
});
|
27
33
|
build.id = buildInitiateResponse.build.id;
|
28
34
|
build.assetPath = buildInitiateResponse.build.assetPath;
|
29
35
|
}
|
30
36
|
if (!config.skipBuild) {
|
31
|
-
await buildProject(
|
37
|
+
await buildProject({
|
38
|
+
config,
|
39
|
+
assetPath: build.assetPath,
|
40
|
+
hooks
|
41
|
+
});
|
32
42
|
verifyConfig({ config, performedBuild: true });
|
33
43
|
}
|
34
44
|
buildCompleted = true;
|
35
45
|
const manifest = await getUploadFiles(config);
|
36
46
|
const deploymentInitiateInput = build.id ? { buildId: build.id, manifest } : { environment, manifest, labels };
|
37
|
-
deployment = await deploymentInitiate(
|
38
|
-
|
47
|
+
deployment = await deploymentInitiate({
|
48
|
+
config,
|
49
|
+
input: deploymentInitiateInput,
|
50
|
+
logger
|
51
|
+
});
|
52
|
+
await uploadFiles({ config, targets: deployment.deploymentTargets, logger });
|
39
53
|
const deploymentCompleteOp = await deploymentComplete(
|
40
54
|
config,
|
41
55
|
deployment.deployment.id
|
42
56
|
);
|
57
|
+
if (!config.skipHealthCheck) {
|
58
|
+
await healthCheck({
|
59
|
+
config,
|
60
|
+
url: deploymentCompleteOp.deployment.url,
|
61
|
+
logger,
|
62
|
+
hooks
|
63
|
+
});
|
64
|
+
}
|
43
65
|
const urlMessage = config.publicDeployment ? "Public" : "Private";
|
44
66
|
outputSuccess(
|
45
67
|
`Deployment complete.
|
46
68
|
${urlMessage} preview URL: ${deploymentCompleteOp.deployment.url}`,
|
47
|
-
|
69
|
+
logger
|
48
70
|
);
|
49
71
|
if (metadata.name !== "none") {
|
50
72
|
outputInfo(deploymentCompleteOp.deployment.url);
|
51
73
|
}
|
74
|
+
return deploymentCompleteOp.deployment.url;
|
52
75
|
} catch (error) {
|
53
76
|
if (!(error instanceof Error)) {
|
54
77
|
console.error("Unknown error", error);
|
55
|
-
return;
|
78
|
+
return Promise.reject(new Error("Unknown error"));
|
56
79
|
}
|
57
|
-
if (
|
80
|
+
if (error instanceof HealthCheckError) {
|
81
|
+
outputWarn(error.message, logger);
|
82
|
+
} else if (build.id && !buildCompleted) {
|
58
83
|
outputWarn(
|
59
84
|
`Build failed with: ${error.message}, cancelling build.`,
|
60
|
-
|
85
|
+
logger
|
61
86
|
);
|
62
|
-
await buildCancel(
|
87
|
+
await buildCancel({
|
88
|
+
config,
|
89
|
+
buildId: build.id,
|
90
|
+
reason: error.message,
|
91
|
+
logger
|
92
|
+
}).catch((err) => {
|
63
93
|
if (err instanceof Error) {
|
64
|
-
outputWarn(`Failed to cancel build: ${err.message}`,
|
94
|
+
outputWarn(`Failed to cancel build: ${err.message}`, logger);
|
65
95
|
}
|
66
96
|
});
|
67
97
|
} else if (deployment?.deployment.id) {
|
68
98
|
outputWarn(
|
69
99
|
`Deployment failed with: ${error.message}, cancelling deployment.`,
|
70
|
-
|
100
|
+
logger
|
71
101
|
);
|
72
|
-
await deploymentCancel(
|
102
|
+
await deploymentCancel({
|
73
103
|
config,
|
74
|
-
deployment.deployment.id,
|
75
|
-
DeploymentCancelReason.Failed
|
76
|
-
|
104
|
+
deploymentId: deployment.deployment.id,
|
105
|
+
reason: DeploymentCancelReason.Failed,
|
106
|
+
logger
|
107
|
+
}).catch((err) => {
|
77
108
|
if (err instanceof Error) {
|
78
|
-
outputWarn(
|
79
|
-
`Failed to cancel deployment: ${err.message}`,
|
80
|
-
stderrLogger
|
81
|
-
);
|
109
|
+
outputWarn(`Failed to cancel deployment: ${err.message}`, logger);
|
82
110
|
}
|
83
111
|
});
|
84
112
|
}
|
85
|
-
|
113
|
+
return Promise.reject(error);
|
86
114
|
}
|
87
115
|
}
|
88
116
|
|
@@ -1,11 +1,12 @@
|
|
1
1
|
import { CIMetadata } from '@shopify/cli-kit/node/context/local';
|
2
|
-
import {
|
2
|
+
import { Logger } from '@shopify/cli-kit/node/output';
|
3
|
+
import { DeploymentConfig, EnvironmentInput } from './types.js';
|
3
4
|
|
4
5
|
type Metadata = CIMetadata & {
|
5
6
|
name: string;
|
6
7
|
};
|
7
|
-
declare function getMetadata(config:
|
8
|
-
declare function getEnvironmentInput(config:
|
8
|
+
declare function getMetadata(config: DeploymentConfig, logger: Logger): Promise<Metadata>;
|
9
|
+
declare function getEnvironmentInput(config: DeploymentConfig, metadata: CIMetadata): EnvironmentInput | undefined;
|
9
10
|
declare function createLabels(metadata: Metadata): string[];
|
10
11
|
|
11
12
|
export { createLabels, getEnvironmentInput, getMetadata };
|
package/dist/deploy/metadata.js
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
import { ciPlatform } from '@shopify/cli-kit/node/context/local';
|
2
2
|
import { getLatestGitCommit } from '@shopify/cli-kit/node/git';
|
3
3
|
import { outputWarn } from '@shopify/cli-kit/node/output';
|
4
|
-
import {
|
4
|
+
import { maxLabelLength } from '../utils/utils.js';
|
5
5
|
|
6
|
-
async function getMetadata(config) {
|
6
|
+
async function getMetadata(config, logger) {
|
7
7
|
const ciInfo = ciPlatform();
|
8
8
|
let metadata = {};
|
9
9
|
if (ciInfo.isCI && ciInfo.name !== "unknown") {
|
@@ -19,7 +19,7 @@ async function getMetadata(config) {
|
|
19
19
|
commitMessage: gitCommit.message
|
20
20
|
};
|
21
21
|
} catch (error) {
|
22
|
-
outputWarn("No CI metadata loaded from environment",
|
22
|
+
outputWarn("No CI metadata loaded from environment", logger);
|
23
23
|
}
|
24
24
|
}
|
25
25
|
return {
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import { getLatestGitCommit } from '@shopify/cli-kit/node/git';
|
2
2
|
import { ciPlatform } from '@shopify/cli-kit/node/context/local';
|
3
3
|
import { vi, describe, test, expect } from 'vitest';
|
4
|
-
import { maxLabelLength } from '../utils/utils.js';
|
4
|
+
import { stderrLogger, maxLabelLength } from '../utils/utils.js';
|
5
5
|
import { createTestConfig } from '../utils/test-helper.js';
|
6
6
|
import { getMetadata, getEnvironmentInput, createLabels } from './metadata.js';
|
7
7
|
|
@@ -19,7 +19,7 @@ describe("getMetadata", () => {
|
|
19
19
|
}
|
20
20
|
});
|
21
21
|
const testConfig = createTestConfig("/tmp/deploymentRoot/");
|
22
|
-
const metadataResult = await getMetadata(testConfig);
|
22
|
+
const metadataResult = await getMetadata(testConfig, stderrLogger);
|
23
23
|
expect(metadataResult.actor).toBe("circle_actor");
|
24
24
|
expect(metadataResult.commitSha).toBe("circle_sha");
|
25
25
|
expect(metadataResult.name).toBe("circle");
|
@@ -41,7 +41,7 @@ describe("getMetadata", () => {
|
|
41
41
|
version: "custom_version",
|
42
42
|
url: "custom_url"
|
43
43
|
};
|
44
|
-
const metadataResult = await getMetadata(testConfig);
|
44
|
+
const metadataResult = await getMetadata(testConfig, stderrLogger);
|
45
45
|
expect(metadataResult.actor).toBe("custom_user");
|
46
46
|
expect(metadataResult.commitSha).toBe("custom_version");
|
47
47
|
expect(metadataResult.name).toBe("circle");
|
@@ -61,7 +61,7 @@ describe("getMetadata", () => {
|
|
61
61
|
body: "gh_body"
|
62
62
|
});
|
63
63
|
const testConfig = createTestConfig("/tmp/deploymentRoot/");
|
64
|
-
const metadataResult = await getMetadata(testConfig);
|
64
|
+
const metadataResult = await getMetadata(testConfig, stderrLogger);
|
65
65
|
expect(metadataResult.actor).toBe("gh_author");
|
66
66
|
expect(metadataResult.commitSha).toBe("gh_hash");
|
67
67
|
expect(metadataResult.name).toBe("none");
|
package/dist/deploy/types.d.ts
CHANGED
@@ -5,12 +5,24 @@ interface Build {
|
|
5
5
|
interface ClientError extends Error {
|
6
6
|
statusCode: number;
|
7
7
|
}
|
8
|
-
interface
|
8
|
+
interface DeploymentHooks {
|
9
|
+
onBuildStart?: () => void;
|
10
|
+
onBuildComplete?: () => void;
|
11
|
+
onBuildError?: (error: Error) => void;
|
12
|
+
onHealthCheckStart?: () => void;
|
13
|
+
onHealthCheckComplete?: () => void;
|
14
|
+
onHealthCheckError?: (error: Error) => void;
|
15
|
+
onUploadFilesStart?: () => void;
|
16
|
+
onUploadFilesComplete?: () => void;
|
17
|
+
}
|
18
|
+
interface DeploymentConfig {
|
9
19
|
assetsDir?: string;
|
10
20
|
buildCommand: string;
|
21
|
+
buildOutput: boolean;
|
11
22
|
deploymentToken: DeploymentToken;
|
12
23
|
deploymentUrl: string;
|
13
24
|
environmentTag?: string;
|
25
|
+
healthCheckMaxDuration: number;
|
14
26
|
metadata: {
|
15
27
|
user?: string;
|
16
28
|
version?: string;
|
@@ -19,6 +31,7 @@ interface DeployConfig {
|
|
19
31
|
publicDeployment: boolean;
|
20
32
|
rootPath?: string;
|
21
33
|
skipBuild: boolean;
|
34
|
+
skipHealthCheck: boolean;
|
22
35
|
workerDir?: string;
|
23
36
|
workerOnly: boolean;
|
24
37
|
}
|
@@ -49,5 +62,7 @@ declare enum FileType {
|
|
49
62
|
interface OxygenError {
|
50
63
|
message: string;
|
51
64
|
}
|
65
|
+
declare class HealthCheckError extends Error {
|
66
|
+
}
|
52
67
|
|
53
|
-
export { Build, ClientError,
|
68
|
+
export { Build, ClientError, DeploymentConfig, DeploymentHooks, DeploymentManifestFile, DeploymentToken, EnvironmentInput, FileType, HealthCheckError, OxygenError };
|
package/dist/deploy/types.js
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
-
import {
|
1
|
+
import { Logger } from '@shopify/cli-kit/node/output';
|
2
|
+
import { DeploymentConfig, DeploymentHooks } from './types.js';
|
2
3
|
import { DeploymentTargetResponse } from './graphql/deployment-initiate.js';
|
3
4
|
|
4
|
-
|
5
|
+
interface UploadFilesOptions {
|
6
|
+
config: DeploymentConfig;
|
7
|
+
hooks?: DeploymentHooks;
|
8
|
+
logger: Logger;
|
9
|
+
targets: DeploymentTargetResponse[];
|
10
|
+
}
|
11
|
+
declare function uploadFiles(options: UploadFilesOptions): Promise<void>;
|
5
12
|
|
6
13
|
export { uploadFiles };
|
@@ -3,14 +3,17 @@ import { createFileReadStream } from '@shopify/cli-kit/node/fs';
|
|
3
3
|
import { outputInfo, outputCompleted } from '@shopify/cli-kit/node/output';
|
4
4
|
import { joinPath } from '@shopify/cli-kit/node/path';
|
5
5
|
import { mapLimit } from 'async';
|
6
|
-
import {
|
6
|
+
import { deployDefaults } from '../utils/utils.js';
|
7
7
|
|
8
|
-
async function uploadFiles(
|
9
|
-
|
8
|
+
async function uploadFiles(options) {
|
9
|
+
const { config, logger, targets, hooks } = options;
|
10
|
+
outputInfo(`Uploading ${targets.length} files...`, logger);
|
11
|
+
hooks?.onUploadFilesStart?.();
|
10
12
|
return mapLimit(targets, 6, async (target) => {
|
11
13
|
await uploadFile(config, target);
|
12
14
|
}).then(() => {
|
13
|
-
|
15
|
+
hooks?.onUploadFilesComplete?.();
|
16
|
+
outputCompleted(`Files uploaded successfully`, logger);
|
14
17
|
});
|
15
18
|
}
|
16
19
|
async function uploadFile(config, target) {
|