@shopify/oxygen-cli 1.4.0 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- 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) {
|