@shopify/cli-hydrogen 5.4.0 → 5.4.2
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/dist/commands/hydrogen/deploy.js +85 -26
- package/dist/commands/hydrogen/deploy.test.js +86 -34
- package/dist/generator-templates/starter/app/components/Search.tsx +1 -1
- package/dist/generator-templates/starter/app/routes/discount.$code.tsx +6 -1
- package/dist/generator-templates/starter/package.json +3 -3
- package/dist/lib/{get-oxygen-token.js → get-oxygen-deployment-data.js} +5 -5
- package/dist/lib/{get-oxygen-token.test.js → get-oxygen-deployment-data.test.js} +31 -17
- package/dist/lib/graphql/admin/{oxygen-token.js → get-oxygen-data.js} +8 -4
- package/oclif.manifest.json +25 -9
- package/package.json +3 -3
|
@@ -3,11 +3,13 @@ import Command from '@shopify/cli-kit/node/base-command';
|
|
|
3
3
|
import colors from '@shopify/cli-kit/node/colors';
|
|
4
4
|
import { outputWarn, outputInfo, outputContent } from '@shopify/cli-kit/node/output';
|
|
5
5
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
6
|
+
import { getLatestGitCommit } from '@shopify/cli-kit/node/git';
|
|
6
7
|
import { resolvePath } from '@shopify/cli-kit/node/path';
|
|
7
|
-
import { renderFatalError, renderSuccess, renderTasks } from '@shopify/cli-kit/node/ui';
|
|
8
|
+
import { renderFatalError, renderSelectPrompt, renderSuccess, renderTasks } from '@shopify/cli-kit/node/ui';
|
|
9
|
+
import { ciPlatform } from '@shopify/cli-kit/node/context/local';
|
|
8
10
|
import { parseToken, createDeploy } from '@shopify/oxygen-cli/deploy';
|
|
9
|
-
import { commonFlags } from '../../lib/flags.js';
|
|
10
|
-
import {
|
|
11
|
+
import { commonFlags, flagsToCamelObject } from '../../lib/flags.js';
|
|
12
|
+
import { getOxygenDeploymentData } from '../../lib/get-oxygen-deployment-data.js';
|
|
11
13
|
import { runBuild } from './build.js';
|
|
12
14
|
|
|
13
15
|
const deploymentLogger = (message, level = "info") => {
|
|
@@ -17,25 +19,36 @@ const deploymentLogger = (message, level = "info") => {
|
|
|
17
19
|
};
|
|
18
20
|
class Deploy extends Command {
|
|
19
21
|
static flags = {
|
|
22
|
+
"env-branch": Flags.string({
|
|
23
|
+
char: "e",
|
|
24
|
+
description: "Environment branch (tag) for environment to deploy to",
|
|
25
|
+
required: false
|
|
26
|
+
}),
|
|
20
27
|
path: commonFlags.path,
|
|
21
28
|
shop: commonFlags.shop,
|
|
22
|
-
|
|
29
|
+
"public-deployment": Flags.boolean({
|
|
23
30
|
env: "SHOPIFY_HYDROGEN_FLAG_PUBLIC_DEPLOYMENT",
|
|
24
31
|
description: "Marks a preview deployment as publicly accessible.",
|
|
25
32
|
required: false,
|
|
26
33
|
default: false
|
|
27
34
|
}),
|
|
28
|
-
|
|
35
|
+
token: Flags.string({
|
|
36
|
+
char: "t",
|
|
37
|
+
description: "Oxygen deployment token",
|
|
38
|
+
env: "SHOPIFY_HYDROGEN_DEPLOYMENT_TOKEN",
|
|
39
|
+
required: false
|
|
40
|
+
}),
|
|
41
|
+
"metadata-url": Flags.string({
|
|
29
42
|
description: "URL that links to the deployment. Will be saved and displayed in the Shopify admin",
|
|
30
43
|
required: false,
|
|
31
44
|
env: "SHOPIFY_HYDROGEN_FLAG_METADATA_URL"
|
|
32
45
|
}),
|
|
33
|
-
|
|
46
|
+
"metadata-user": Flags.string({
|
|
34
47
|
description: "User that initiated the deployment. Will be saved and displayed in the Shopify admin",
|
|
35
48
|
required: false,
|
|
36
49
|
env: "SHOPIFY_HYDROGEN_FLAG_METADATA_USER"
|
|
37
50
|
}),
|
|
38
|
-
|
|
51
|
+
"metadata-version": Flags.string({
|
|
39
52
|
description: "A version identifier for the deployment. Will be saved and displayed in the Shopify admin",
|
|
40
53
|
required: false,
|
|
41
54
|
env: "SHOPIFY_HYDROGEN_FLAG_METADATA_VERSION"
|
|
@@ -44,24 +57,26 @@ class Deploy extends Command {
|
|
|
44
57
|
static hidden = true;
|
|
45
58
|
async run() {
|
|
46
59
|
const { flags } = await this.parse(Deploy);
|
|
47
|
-
const
|
|
48
|
-
await oxygenDeploy({
|
|
49
|
-
path: actualPath,
|
|
50
|
-
shop: flags.shop,
|
|
51
|
-
publicDeployment: flags.publicDeployment,
|
|
52
|
-
metadataUrl: flags.metadataUrl,
|
|
53
|
-
metadataUser: flags.metadataUser,
|
|
54
|
-
metadataVersion: flags.metadataVersion
|
|
55
|
-
}).catch((error) => {
|
|
60
|
+
const deploymentOptions = this.flagsToOxygenDeploymentOptions(flags);
|
|
61
|
+
await oxygenDeploy(deploymentOptions).catch((error) => {
|
|
56
62
|
renderFatalError(error);
|
|
57
63
|
process.exit(1);
|
|
58
64
|
}).finally(() => {
|
|
59
65
|
process.exit(0);
|
|
60
66
|
});
|
|
61
67
|
}
|
|
68
|
+
flagsToOxygenDeploymentOptions(flags) {
|
|
69
|
+
const camelFlags = flagsToCamelObject(flags);
|
|
70
|
+
return {
|
|
71
|
+
...camelFlags,
|
|
72
|
+
environmentTag: flags["env-branch"],
|
|
73
|
+
path: flags.path ? resolvePath(flags.path) : process.cwd()
|
|
74
|
+
};
|
|
75
|
+
}
|
|
62
76
|
}
|
|
63
77
|
async function oxygenDeploy(options) {
|
|
64
78
|
const {
|
|
79
|
+
environmentTag,
|
|
65
80
|
path,
|
|
66
81
|
shop,
|
|
67
82
|
publicDeployment,
|
|
@@ -69,25 +84,69 @@ async function oxygenDeploy(options) {
|
|
|
69
84
|
metadataUser,
|
|
70
85
|
metadataVersion
|
|
71
86
|
} = options;
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
87
|
+
const ci = ciPlatform();
|
|
88
|
+
let token = options.token;
|
|
89
|
+
let branch;
|
|
90
|
+
let deploymentData;
|
|
91
|
+
let deploymentEnvironmentTag = void 0;
|
|
92
|
+
let gitCommit;
|
|
93
|
+
try {
|
|
94
|
+
gitCommit = await getLatestGitCommit(path);
|
|
95
|
+
branch = (/HEAD -> ([^,]*)/.exec(gitCommit.refs) || [])[1];
|
|
96
|
+
} catch (error) {
|
|
97
|
+
outputWarn("Could not retrieve Git history.");
|
|
98
|
+
branch = void 0;
|
|
99
|
+
}
|
|
100
|
+
if (!ci.isCI) {
|
|
101
|
+
deploymentData = await getOxygenDeploymentData({
|
|
102
|
+
root: path,
|
|
103
|
+
flagShop: shop
|
|
104
|
+
});
|
|
105
|
+
if (!deploymentData) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
token = token || deploymentData.oxygenDeploymentToken;
|
|
109
|
+
}
|
|
76
110
|
if (!token) {
|
|
77
|
-
|
|
111
|
+
const errMessage = ci.isCI ? [
|
|
112
|
+
"No deployment token provided. Use the ",
|
|
113
|
+
{ command: "--token" },
|
|
114
|
+
" flag to provide a token."
|
|
115
|
+
] : `Could not obtain an Oxygen deployment token, please try again or contact Shopify support.`;
|
|
116
|
+
throw new AbortError(errMessage);
|
|
117
|
+
}
|
|
118
|
+
if (!ci.isCI && !environmentTag && deploymentData?.environments) {
|
|
119
|
+
if (deploymentData.environments.length > 1) {
|
|
120
|
+
const choices = [
|
|
121
|
+
...deploymentData.environments.map(({ name, branch: branch2 }) => ({
|
|
122
|
+
label: name,
|
|
123
|
+
value: branch2
|
|
124
|
+
}))
|
|
125
|
+
];
|
|
126
|
+
deploymentEnvironmentTag = await renderSelectPrompt({
|
|
127
|
+
message: "Select an environment to deploy to",
|
|
128
|
+
choices,
|
|
129
|
+
defaultValue: branch
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
outputInfo(
|
|
133
|
+
`Using current checked out branch ${branch} as environment tag`
|
|
134
|
+
);
|
|
135
|
+
}
|
|
78
136
|
}
|
|
79
137
|
const config = {
|
|
80
138
|
assetsDir: "dist/client",
|
|
81
139
|
deploymentUrl: "https://oxygen.shopifyapps.com",
|
|
82
140
|
deploymentToken: parseToken(token),
|
|
83
|
-
|
|
141
|
+
environmentTag: environmentTag || deploymentEnvironmentTag || branch,
|
|
142
|
+
verificationMaxDuration: 180,
|
|
84
143
|
metadata: {
|
|
85
144
|
...metadataUrl ? { url: metadataUrl } : {},
|
|
86
145
|
...metadataUser ? { user: metadataUser } : {},
|
|
87
146
|
...metadataVersion ? { version: metadataVersion } : {}
|
|
88
147
|
},
|
|
89
148
|
publicDeployment,
|
|
90
|
-
|
|
149
|
+
skipVerification: false,
|
|
91
150
|
rootPath: path,
|
|
92
151
|
skipBuild: false,
|
|
93
152
|
workerOnly: false,
|
|
@@ -120,10 +179,10 @@ async function oxygenDeploy(options) {
|
|
|
120
179
|
useCodegen: false
|
|
121
180
|
});
|
|
122
181
|
},
|
|
123
|
-
|
|
182
|
+
onVerificationComplete: () => resolveHealthCheck(),
|
|
124
183
|
onUploadFilesStart: () => uploadStart(),
|
|
125
184
|
onUploadFilesComplete: () => resolveUpload(),
|
|
126
|
-
|
|
185
|
+
onVerificationError: (error) => {
|
|
127
186
|
deployError = new AbortError(
|
|
128
187
|
error.message,
|
|
129
188
|
"Please verify the deployment status in the Shopify Admin and retry deploying if necessary."
|
|
@@ -146,7 +205,7 @@ async function oxygenDeploy(options) {
|
|
|
146
205
|
task: async () => await uploadPromise
|
|
147
206
|
},
|
|
148
207
|
{
|
|
149
|
-
title: "
|
|
208
|
+
title: "Verifying deployment",
|
|
150
209
|
task: async () => await healthCheckPromise
|
|
151
210
|
}
|
|
152
211
|
]);
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { vi, describe, beforeEach, afterEach, it
|
|
1
|
+
import { vi, describe, expect, beforeEach, afterEach, it } from 'vitest';
|
|
2
2
|
import { login } from '../../lib/auth.js';
|
|
3
3
|
import { getStorefronts } from '../../lib/graphql/admin/link-storefront.js';
|
|
4
4
|
import { AbortError } from '@shopify/cli-kit/node/error';
|
|
5
5
|
import { renderSelectPrompt, renderSuccess, renderFatalError } from '@shopify/cli-kit/node/ui';
|
|
6
|
+
import { getLatestGitCommit } from '@shopify/cli-kit/node/git';
|
|
6
7
|
import { oxygenDeploy, deploymentLogger } from './deploy.js';
|
|
7
|
-
import {
|
|
8
|
+
import { getOxygenDeploymentData } from '../../lib/get-oxygen-deployment-data.js';
|
|
8
9
|
import { createDeploy, parseToken } from '@shopify/oxygen-cli/deploy';
|
|
9
10
|
|
|
10
|
-
vi.mock("../../lib/get-oxygen-
|
|
11
|
+
vi.mock("../../lib/get-oxygen-deployment-data.js");
|
|
11
12
|
vi.mock("@shopify/oxygen-cli/deploy");
|
|
12
13
|
vi.mock("../../lib/auth.js");
|
|
13
14
|
vi.mock("../../lib/shopify-config.js");
|
|
@@ -19,6 +20,8 @@ vi.mock("@shopify/cli-kit/node/output", async () => {
|
|
|
19
20
|
return {
|
|
20
21
|
outputContent: () => ({ value: "" }),
|
|
21
22
|
outputInfo: () => {
|
|
23
|
+
},
|
|
24
|
+
outputWarn: () => {
|
|
22
25
|
}
|
|
23
26
|
};
|
|
24
27
|
});
|
|
@@ -30,6 +33,16 @@ vi.mock("@shopify/cli-kit/node/ui", async () => {
|
|
|
30
33
|
renderTasks: vi.fn()
|
|
31
34
|
};
|
|
32
35
|
});
|
|
36
|
+
vi.mock("@shopify/cli-kit/node/git", async () => {
|
|
37
|
+
return {
|
|
38
|
+
getLatestGitCommit: vi.fn()
|
|
39
|
+
};
|
|
40
|
+
});
|
|
41
|
+
vi.mock("@shopify/cli-kit/node/context/local", async () => {
|
|
42
|
+
return {
|
|
43
|
+
ciPlatform: () => ({ isCI: false })
|
|
44
|
+
};
|
|
45
|
+
});
|
|
33
46
|
describe("deploy", () => {
|
|
34
47
|
const ADMIN_SESSION = {
|
|
35
48
|
token: "abc123",
|
|
@@ -66,6 +79,31 @@ describe("deploy", () => {
|
|
|
66
79
|
namespace: "some-namespace",
|
|
67
80
|
namespaceId: "1"
|
|
68
81
|
};
|
|
82
|
+
const expectedConfig = {
|
|
83
|
+
assetsDir: "dist/client",
|
|
84
|
+
deploymentUrl: "https://oxygen.shopifyapps.com",
|
|
85
|
+
deploymentToken: mockToken,
|
|
86
|
+
verificationMaxDuration: 180,
|
|
87
|
+
metadata: {
|
|
88
|
+
url: deployParams.metadataUrl,
|
|
89
|
+
user: deployParams.metadataUser,
|
|
90
|
+
version: deployParams.metadataVersion
|
|
91
|
+
},
|
|
92
|
+
publicDeployment: deployParams.publicDeployment,
|
|
93
|
+
skipVerification: false,
|
|
94
|
+
rootPath: deployParams.path,
|
|
95
|
+
skipBuild: false,
|
|
96
|
+
workerOnly: false,
|
|
97
|
+
workerDir: "dist/worker"
|
|
98
|
+
};
|
|
99
|
+
const expectedHooks = {
|
|
100
|
+
buildFunction: expect.any(Function),
|
|
101
|
+
onVerificationComplete: expect.any(Function),
|
|
102
|
+
onUploadFilesStart: expect.any(Function),
|
|
103
|
+
onUploadFilesComplete: expect.any(Function),
|
|
104
|
+
onVerificationError: expect.any(Function),
|
|
105
|
+
onUploadFilesError: expect.any(Function)
|
|
106
|
+
};
|
|
69
107
|
beforeEach(async () => {
|
|
70
108
|
process.exit = vi.fn();
|
|
71
109
|
vi.mocked(login).mockResolvedValue({
|
|
@@ -83,54 +121,68 @@ describe("deploy", () => {
|
|
|
83
121
|
vi.mocked(createDeploy).mockResolvedValue(
|
|
84
122
|
"https://a-lovely-deployment.com"
|
|
85
123
|
);
|
|
86
|
-
vi.mocked(
|
|
124
|
+
vi.mocked(getOxygenDeploymentData).mockResolvedValue({
|
|
125
|
+
oxygenDeploymentToken: "some-encoded-token",
|
|
126
|
+
environments: []
|
|
127
|
+
});
|
|
87
128
|
vi.mocked(parseToken).mockReturnValue(mockToken);
|
|
88
129
|
});
|
|
89
130
|
afterEach(() => {
|
|
90
131
|
vi.resetAllMocks();
|
|
91
132
|
process.exit = originalExit;
|
|
92
133
|
});
|
|
93
|
-
it("calls
|
|
134
|
+
it("calls getOxygenDeploymentData with the correct parameters", async () => {
|
|
94
135
|
await oxygenDeploy(deployParams);
|
|
95
|
-
expect(
|
|
136
|
+
expect(getOxygenDeploymentData).toHaveBeenCalledWith({
|
|
96
137
|
root: "./",
|
|
97
138
|
flagShop: "snowdevil.myshopify.com"
|
|
98
139
|
});
|
|
99
|
-
expect(
|
|
140
|
+
expect(getOxygenDeploymentData).toHaveBeenCalledTimes(1);
|
|
100
141
|
});
|
|
101
142
|
it("calls createDeploy with the correct parameters", async () => {
|
|
102
143
|
await oxygenDeploy(deployParams);
|
|
103
|
-
const expectedConfig = {
|
|
104
|
-
assetsDir: "dist/client",
|
|
105
|
-
deploymentUrl: "https://oxygen.shopifyapps.com",
|
|
106
|
-
deploymentToken: mockToken,
|
|
107
|
-
healthCheckMaxDuration: 180,
|
|
108
|
-
metadata: {
|
|
109
|
-
url: deployParams.metadataUrl,
|
|
110
|
-
user: deployParams.metadataUser,
|
|
111
|
-
version: deployParams.metadataVersion
|
|
112
|
-
},
|
|
113
|
-
publicDeployment: deployParams.publicDeployment,
|
|
114
|
-
skipHealthCheck: false,
|
|
115
|
-
rootPath: deployParams.path,
|
|
116
|
-
skipBuild: false,
|
|
117
|
-
workerOnly: false,
|
|
118
|
-
workerDir: "dist/worker"
|
|
119
|
-
};
|
|
120
144
|
expect(vi.mocked(createDeploy)).toHaveBeenCalledWith({
|
|
121
145
|
config: expectedConfig,
|
|
122
|
-
hooks:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
146
|
+
hooks: expectedHooks,
|
|
147
|
+
logger: deploymentLogger
|
|
148
|
+
});
|
|
149
|
+
expect(vi.mocked(renderSuccess)).toHaveBeenCalled;
|
|
150
|
+
});
|
|
151
|
+
it("calls createDeploy with the checked out branch name", async () => {
|
|
152
|
+
vi.mocked(getLatestGitCommit).mockResolvedValue({
|
|
153
|
+
hash: "123",
|
|
154
|
+
message: "test commit",
|
|
155
|
+
date: "2021-01-01",
|
|
156
|
+
author_name: "test author",
|
|
157
|
+
author_email: "test@author.com",
|
|
158
|
+
body: "test body",
|
|
159
|
+
refs: "HEAD -> main"
|
|
160
|
+
});
|
|
161
|
+
await oxygenDeploy(deployParams);
|
|
162
|
+
expect(vi.mocked(createDeploy)).toHaveBeenCalledWith({
|
|
163
|
+
config: { ...expectedConfig, environmentTag: "main" },
|
|
164
|
+
hooks: expectedHooks,
|
|
130
165
|
logger: deploymentLogger
|
|
131
166
|
});
|
|
132
167
|
expect(vi.mocked(renderSuccess)).toHaveBeenCalled;
|
|
133
168
|
});
|
|
169
|
+
it("calls renderSelectPrompt when there are multiple environments", async () => {
|
|
170
|
+
vi.mocked(getOxygenDeploymentData).mockResolvedValue({
|
|
171
|
+
oxygenDeploymentToken: "some-encoded-token",
|
|
172
|
+
environments: [
|
|
173
|
+
{ name: "production", branch: "main" },
|
|
174
|
+
{ name: "preview", branch: "staging" }
|
|
175
|
+
]
|
|
176
|
+
});
|
|
177
|
+
await oxygenDeploy(deployParams);
|
|
178
|
+
expect(vi.mocked(renderSelectPrompt)).toHaveBeenCalledWith({
|
|
179
|
+
message: "Select an environment to deploy to",
|
|
180
|
+
choices: [
|
|
181
|
+
{ label: "production", value: "main" },
|
|
182
|
+
{ label: "preview", value: "staging" }
|
|
183
|
+
]
|
|
184
|
+
});
|
|
185
|
+
});
|
|
134
186
|
it("handles error during uploadFiles", async () => {
|
|
135
187
|
const mockRenderFatalError = vi.fn();
|
|
136
188
|
vi.mocked(renderFatalError).mockImplementation(mockRenderFatalError);
|
|
@@ -156,14 +208,14 @@ describe("deploy", () => {
|
|
|
156
208
|
}
|
|
157
209
|
}
|
|
158
210
|
});
|
|
159
|
-
it("handles error during
|
|
211
|
+
it("handles error during deployment verification", async () => {
|
|
160
212
|
const mockRenderFatalError = vi.fn();
|
|
161
213
|
vi.mocked(renderFatalError).mockImplementation(mockRenderFatalError);
|
|
162
214
|
const error = new Error("Cloudflare is down!");
|
|
163
215
|
vi.mocked(createDeploy).mockImplementation((options) => {
|
|
164
216
|
options.hooks?.onUploadFilesStart?.();
|
|
165
217
|
options.hooks?.onUploadFilesComplete?.();
|
|
166
|
-
options.hooks?.
|
|
218
|
+
options.hooks?.onVerificationError?.(error);
|
|
167
219
|
return new Promise((_resolve, reject) => {
|
|
168
220
|
reject(error);
|
|
169
221
|
});
|
|
@@ -151,7 +151,7 @@ export function SearchResults({
|
|
|
151
151
|
function SearchResultsProductsGrid({products}: Pick<SearchQuery, 'products'>) {
|
|
152
152
|
return (
|
|
153
153
|
<div className="search-result">
|
|
154
|
-
<
|
|
154
|
+
<h2>Products</h2>
|
|
155
155
|
<Pagination connection={products}>
|
|
156
156
|
{({nodes, isLoading, NextLink, PreviousLink}) => {
|
|
157
157
|
const itemsMarkup = nodes.map((product) => (
|
|
@@ -17,9 +17,14 @@ export async function loader({request, context, params}: LoaderArgs) {
|
|
|
17
17
|
|
|
18
18
|
const url = new URL(request.url);
|
|
19
19
|
const searchParams = new URLSearchParams(url.search);
|
|
20
|
-
|
|
20
|
+
let redirectParam =
|
|
21
21
|
searchParams.get('redirect') || searchParams.get('return_to') || '/';
|
|
22
22
|
|
|
23
|
+
if (redirectParam.includes('//')) {
|
|
24
|
+
// Avoid redirecting to external URLs to prevent phishing attacks
|
|
25
|
+
redirectParam = '/';
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
searchParams.delete('redirect');
|
|
24
29
|
searchParams.delete('return_to');
|
|
25
30
|
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@remix-run/react": "1.19.1",
|
|
17
17
|
"@shopify/cli": "3.49.2",
|
|
18
|
-
"@shopify/cli-hydrogen": "^5.4.
|
|
19
|
-
"@shopify/hydrogen": "^2023.7.
|
|
20
|
-
"@shopify/remix-oxygen": "^1.1.
|
|
18
|
+
"@shopify/cli-hydrogen": "^5.4.2",
|
|
19
|
+
"@shopify/hydrogen": "^2023.7.10",
|
|
20
|
+
"@shopify/remix-oxygen": "^1.1.6",
|
|
21
21
|
"graphql": "^16.6.0",
|
|
22
22
|
"graphql-tag": "^2.12.6",
|
|
23
23
|
"isbot": "^3.6.6",
|
|
@@ -4,9 +4,9 @@ import { linkStorefront } from '../commands/hydrogen/link.js';
|
|
|
4
4
|
import { login } from './auth.js';
|
|
5
5
|
import { getCliCommand } from './shell.js';
|
|
6
6
|
import { renderMissingLink, renderMissingStorefront } from './render-errors.js';
|
|
7
|
-
import {
|
|
7
|
+
import { getOxygenData } from './graphql/admin/get-oxygen-data.js';
|
|
8
8
|
|
|
9
|
-
async function
|
|
9
|
+
async function getOxygenDeploymentData({
|
|
10
10
|
root
|
|
11
11
|
}) {
|
|
12
12
|
const [{ session, config }, cliCommand] = await Promise.all([
|
|
@@ -28,7 +28,7 @@ async function getOxygenDeploymentToken({
|
|
|
28
28
|
if (!config.storefront) {
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
-
const { storefront } = await
|
|
31
|
+
const { storefront } = await getOxygenData(session, config.storefront.id);
|
|
32
32
|
if (!storefront) {
|
|
33
33
|
renderMissingStorefront({
|
|
34
34
|
session,
|
|
@@ -41,7 +41,7 @@ async function getOxygenDeploymentToken({
|
|
|
41
41
|
outputWarn(`Could not retrieve a deployment token.`);
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
-
return storefront
|
|
44
|
+
return storefront;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
export {
|
|
47
|
+
export { getOxygenDeploymentData };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { vi, describe, beforeEach, afterEach, it, expect } from 'vitest';
|
|
2
2
|
import { renderConfirmationPrompt } from '@shopify/cli-kit/node/ui';
|
|
3
|
-
import {
|
|
3
|
+
import { getOxygenDeploymentData } from './get-oxygen-deployment-data.js';
|
|
4
4
|
import { login } from './auth.js';
|
|
5
5
|
import { getConfig } from './shopify-config.js';
|
|
6
6
|
import { renderMissingLink, renderMissingStorefront } from './render-errors.js';
|
|
7
7
|
import { linkStorefront } from '../commands/hydrogen/link.js';
|
|
8
|
-
import {
|
|
8
|
+
import { getOxygenData } from './graphql/admin/get-oxygen-data.js';
|
|
9
9
|
|
|
10
10
|
vi.mock("@shopify/cli-kit/node/ui", async () => {
|
|
11
11
|
const original = await vi.importActual("@shopify/cli-kit/node/ui");
|
|
@@ -19,9 +19,19 @@ vi.mock("./admin-session.js");
|
|
|
19
19
|
vi.mock("./shopify-config.js");
|
|
20
20
|
vi.mock("./render-errors.js");
|
|
21
21
|
vi.mock("../commands/hydrogen/link.js");
|
|
22
|
-
vi.mock("./graphql/admin/oxygen-
|
|
23
|
-
describe("
|
|
22
|
+
vi.mock("./graphql/admin/get-oxygen-data.js");
|
|
23
|
+
describe("getOxygenDeploymentData", () => {
|
|
24
24
|
const OXYGEN_DEPLOYMENT_TOKEN = "a-lovely-token";
|
|
25
|
+
const environments = [
|
|
26
|
+
{
|
|
27
|
+
name: "production",
|
|
28
|
+
branch: "main"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "preview",
|
|
32
|
+
branch: "staging"
|
|
33
|
+
}
|
|
34
|
+
];
|
|
25
35
|
beforeEach(() => {
|
|
26
36
|
vi.mocked(login).mockResolvedValue({
|
|
27
37
|
session: {
|
|
@@ -41,16 +51,20 @@ describe("getOxygenDeploymentToken", () => {
|
|
|
41
51
|
vi.mocked(getConfig).mockResolvedValue({
|
|
42
52
|
storefront: { id: "storefront-id", title: "Existing Link" }
|
|
43
53
|
});
|
|
44
|
-
vi.mocked(
|
|
45
|
-
storefront: {
|
|
54
|
+
vi.mocked(getOxygenData).mockResolvedValue({
|
|
55
|
+
storefront: {
|
|
56
|
+
oxygenDeploymentToken: OXYGEN_DEPLOYMENT_TOKEN,
|
|
57
|
+
environments
|
|
58
|
+
}
|
|
46
59
|
});
|
|
47
60
|
});
|
|
48
61
|
afterEach(() => {
|
|
49
62
|
vi.resetAllMocks();
|
|
50
63
|
});
|
|
51
|
-
it("returns the oxygen deployment token", async () => {
|
|
52
|
-
const
|
|
53
|
-
expect(
|
|
64
|
+
it("returns the oxygen deployment token and environments", async () => {
|
|
65
|
+
const data = await getOxygenDeploymentData({ root: "test-root" });
|
|
66
|
+
expect(data?.oxygenDeploymentToken).toBe(OXYGEN_DEPLOYMENT_TOKEN);
|
|
67
|
+
expect(data?.environments).toEqual(environments);
|
|
54
68
|
});
|
|
55
69
|
describe("when there is no linked storefront", () => {
|
|
56
70
|
beforeEach(() => {
|
|
@@ -69,36 +83,36 @@ describe("getOxygenDeploymentToken", () => {
|
|
|
69
83
|
});
|
|
70
84
|
it("calls renderMissingLink and prompts the user to create a link", async () => {
|
|
71
85
|
vi.mocked(renderConfirmationPrompt).mockResolvedValue(true);
|
|
72
|
-
await
|
|
86
|
+
await getOxygenDeploymentData({ root: "test-root" });
|
|
73
87
|
expect(renderMissingLink).toHaveBeenCalled();
|
|
74
88
|
expect(renderConfirmationPrompt).toHaveBeenCalled();
|
|
75
89
|
expect(linkStorefront).toHaveBeenCalled();
|
|
76
90
|
});
|
|
77
91
|
it("returns nothing if the user does not create a new link", async () => {
|
|
78
92
|
vi.mocked(renderConfirmationPrompt).mockResolvedValue(false);
|
|
79
|
-
const token = await
|
|
93
|
+
const token = await getOxygenDeploymentData({ root: "test-root" });
|
|
80
94
|
expect(token).toEqual(void 0);
|
|
81
95
|
});
|
|
82
96
|
});
|
|
83
97
|
describe("when there is no matching storefront in the shop", () => {
|
|
84
98
|
beforeEach(() => {
|
|
85
|
-
vi.mocked(
|
|
99
|
+
vi.mocked(getOxygenData).mockResolvedValue({ storefront: null });
|
|
86
100
|
});
|
|
87
101
|
it("calls renderMissingStorefront and returns nothing", async () => {
|
|
88
|
-
const token = await
|
|
102
|
+
const token = await getOxygenDeploymentData({ root: "test-root" });
|
|
89
103
|
expect(renderMissingStorefront).toHaveBeenCalled();
|
|
90
104
|
expect(token).toEqual(void 0);
|
|
91
105
|
});
|
|
92
106
|
});
|
|
93
107
|
describe("when the storefront does not have an oxygen deployment token", () => {
|
|
94
108
|
beforeEach(() => {
|
|
95
|
-
vi.mocked(
|
|
96
|
-
storefront: { oxygenDeploymentToken: "" }
|
|
109
|
+
vi.mocked(getOxygenData).mockResolvedValue({
|
|
110
|
+
storefront: { oxygenDeploymentToken: "", environments: [] }
|
|
97
111
|
});
|
|
98
112
|
});
|
|
99
113
|
it("returns nothing", async () => {
|
|
100
|
-
const
|
|
101
|
-
expect(
|
|
114
|
+
const data = await getOxygenDeploymentData({ root: "test-root" });
|
|
115
|
+
expect(data).toEqual(void 0);
|
|
102
116
|
});
|
|
103
117
|
});
|
|
104
118
|
});
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { adminRequest } from './client.js';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const GetDeploymentDataQuery = `#graphql
|
|
4
4
|
query GetDeploymentToken($id: ID!) {
|
|
5
5
|
hydrogenStorefront(id: $id) {
|
|
6
6
|
oxygenDeploymentToken
|
|
7
|
+
environments {
|
|
8
|
+
name
|
|
9
|
+
branch
|
|
10
|
+
}
|
|
7
11
|
}
|
|
8
12
|
}
|
|
9
13
|
`;
|
|
10
|
-
async function
|
|
14
|
+
async function getOxygenData(adminSession, storefrontId) {
|
|
11
15
|
const { hydrogenStorefront } = await adminRequest(
|
|
12
|
-
|
|
16
|
+
GetDeploymentDataQuery,
|
|
13
17
|
adminSession,
|
|
14
18
|
{
|
|
15
19
|
id: storefrontId
|
|
@@ -18,4 +22,4 @@ async function getOxygenToken(adminSession, storefrontId) {
|
|
|
18
22
|
return { storefront: hydrogenStorefront };
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
export {
|
|
25
|
+
export { GetDeploymentDataQuery, getOxygenData };
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "5.4.
|
|
2
|
+
"version": "5.4.2",
|
|
3
3
|
"commands": {
|
|
4
4
|
"hydrogen:build": {
|
|
5
5
|
"id": "hydrogen:build",
|
|
@@ -153,6 +153,14 @@
|
|
|
153
153
|
"hidden": true,
|
|
154
154
|
"aliases": [],
|
|
155
155
|
"flags": {
|
|
156
|
+
"env-branch": {
|
|
157
|
+
"name": "env-branch",
|
|
158
|
+
"type": "option",
|
|
159
|
+
"char": "e",
|
|
160
|
+
"description": "Environment branch (tag) for environment to deploy to",
|
|
161
|
+
"required": false,
|
|
162
|
+
"multiple": false
|
|
163
|
+
},
|
|
156
164
|
"path": {
|
|
157
165
|
"name": "path",
|
|
158
166
|
"type": "option",
|
|
@@ -166,29 +174,37 @@
|
|
|
166
174
|
"description": "Shop URL. It can be the shop prefix (janes-apparel) or the full myshopify.com URL (janes-apparel.myshopify.com, https://janes-apparel.myshopify.com).",
|
|
167
175
|
"multiple": false
|
|
168
176
|
},
|
|
169
|
-
"
|
|
170
|
-
"name": "
|
|
177
|
+
"public-deployment": {
|
|
178
|
+
"name": "public-deployment",
|
|
171
179
|
"type": "boolean",
|
|
172
180
|
"description": "Marks a preview deployment as publicly accessible.",
|
|
173
181
|
"required": false,
|
|
174
182
|
"allowNo": false
|
|
175
183
|
},
|
|
176
|
-
"
|
|
177
|
-
"name": "
|
|
184
|
+
"token": {
|
|
185
|
+
"name": "token",
|
|
186
|
+
"type": "option",
|
|
187
|
+
"char": "t",
|
|
188
|
+
"description": "Oxygen deployment token",
|
|
189
|
+
"required": false,
|
|
190
|
+
"multiple": false
|
|
191
|
+
},
|
|
192
|
+
"metadata-url": {
|
|
193
|
+
"name": "metadata-url",
|
|
178
194
|
"type": "option",
|
|
179
195
|
"description": "URL that links to the deployment. Will be saved and displayed in the Shopify admin",
|
|
180
196
|
"required": false,
|
|
181
197
|
"multiple": false
|
|
182
198
|
},
|
|
183
|
-
"
|
|
184
|
-
"name": "
|
|
199
|
+
"metadata-user": {
|
|
200
|
+
"name": "metadata-user",
|
|
185
201
|
"type": "option",
|
|
186
202
|
"description": "User that initiated the deployment. Will be saved and displayed in the Shopify admin",
|
|
187
203
|
"required": false,
|
|
188
204
|
"multiple": false
|
|
189
205
|
},
|
|
190
|
-
"
|
|
191
|
-
"name": "
|
|
206
|
+
"metadata-version": {
|
|
207
|
+
"name": "metadata-version",
|
|
192
208
|
"type": "option",
|
|
193
209
|
"description": "A version identifier for the deployment. Will be saved and displayed in the Shopify admin",
|
|
194
210
|
"required": false,
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public",
|
|
5
5
|
"@shopify:registry": "https://registry.npmjs.org"
|
|
6
6
|
},
|
|
7
|
-
"version": "5.4.
|
|
7
|
+
"version": "5.4.2",
|
|
8
8
|
"license": "MIT",
|
|
9
9
|
"type": "module",
|
|
10
10
|
"scripts": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"@shopify/cli-kit": "3.49.2",
|
|
39
39
|
"@shopify/hydrogen-codegen": "^0.0.2",
|
|
40
40
|
"@shopify/mini-oxygen": "^2.2.1",
|
|
41
|
-
"@shopify/oxygen-cli": "^
|
|
41
|
+
"@shopify/oxygen-cli": "^2.0.0",
|
|
42
42
|
"ansi-escapes": "^6.2.0",
|
|
43
43
|
"diff": "^5.1.0",
|
|
44
44
|
"fs-extra": "^11.1.0",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"@remix-run/dev": "1.19.1",
|
|
58
58
|
"@remix-run/react": "1.19.1",
|
|
59
59
|
"@shopify/hydrogen-react": "^2023.7.4",
|
|
60
|
-
"@shopify/remix-oxygen": "^1.1.
|
|
60
|
+
"@shopify/remix-oxygen": "^1.1.6"
|
|
61
61
|
},
|
|
62
62
|
"peerDependenciesMeta": {
|
|
63
63
|
"@remix-run/dev": {
|