climaybe 3.0.5 → 3.0.7
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 +8 -3
- package/bin/version.txt +1 -1
- package/package.json +1 -1
- package/src/commands/add-store.js +13 -7
- package/src/commands/init.js +20 -13
- package/src/index.js +6 -6
- package/src/lib/dev-runtime.js +2 -2
- package/src/lib/github-secrets.js +36 -10
package/README.md
CHANGED
|
@@ -258,6 +258,9 @@ dev config defaults (`.theme-check.yml`, `.shopifyignore`, `.prettierrc`,
|
|
|
258
258
|
`.lighthouserc.js`), writes `climaybe.config.json`, appends a managed `.gitignore` block, and optionally adds
|
|
259
259
|
`.vscode/tasks.json` (default: yes) wired to run `climaybe` dev commands.
|
|
260
260
|
|
|
261
|
+
Local serve commands keep Theme Check disabled by default for faster startup. Enable it explicitly with
|
|
262
|
+
`climaybe serve --theme-check` or `climaybe serve:assets --theme-check`.
|
|
263
|
+
|
|
261
264
|
You can create optional build entrypoints later with:
|
|
262
265
|
|
|
263
266
|
`climaybe create-entrypoints`
|
|
@@ -304,14 +307,16 @@ Add the following secrets to your GitHub repository (or use **GitLab CI/CD varia
|
|
|
304
307
|
|
|
305
308
|
| Secret | Required | Description |
|
|
306
309
|
|--------|----------|-------------|
|
|
307
|
-
| `GEMINI_API_KEY` |
|
|
310
|
+
| `GEMINI_API_KEY` | Optional | Google Gemini API key for AI-generated release notes fallback |
|
|
308
311
|
| `SHOPIFY_STORE_URL` | Set from config | Store URL is set automatically from the store domain(s) you add during init (no prompt). |
|
|
309
|
-
| `SHOPIFY_THEME_ACCESS_TOKEN` |
|
|
312
|
+
| `SHOPIFY_THEME_ACCESS_TOKEN` | Optional* | Theme access token for preview workflows (needed only when you want preview theme publish/cleanup to run). |
|
|
310
313
|
| `SHOP_ACCESS_TOKEN` | Optional* | Required only when optional build workflows are enabled (Lighthouse) |
|
|
311
314
|
| `LHCI_GITHUB_APP_TOKEN` | Optional* | Required only when optional build workflows are enabled (Lighthouse) |
|
|
312
315
|
| `SHOP_PASSWORD` | Optional | Used by Lighthouse action when your store requires password auth |
|
|
313
316
|
|
|
314
|
-
**
|
|
317
|
+
**Prompting behavior:** During `climaybe init` (or `add-store`), every GitHub/GitLab secret prompt is skippable. Add values later in CI settings if you prefer.
|
|
318
|
+
|
|
319
|
+
**Store URL:** During `climaybe init` (or `add-store`), store URL secret(s) are set from your configured store domain(s); theme tokens are optional prompts.
|
|
315
320
|
|
|
316
321
|
**Multi-store:** Per-store secrets `SHOPIFY_STORE_URL_<ALIAS>` and `SHOPIFY_THEME_ACCESS_TOKEN_<ALIAS>` — the URL is set from config; you must provide the theme token per store. `<ALIAS>` is uppercase with hyphens as underscores (e.g. `voldt-norway` → `SHOPIFY_STORE_URL_VOLDT_NORWAY`). Preview and cleanup: for PRs targeting **main**, **staging**, or **develop** the workflows use the **default store** (from `config.default_store` or first in `config.stores`); for PRs targeting **staging-<alias>** or **live-<alias>** they use that store’s credentials. Set either the plain `SHOPIFY_*` secrets or the `_<ALIAS>` pair for each store you use in preview.
|
|
317
322
|
|
package/bin/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.0.
|
|
1
|
+
3.0.7
|
package/package.json
CHANGED
|
@@ -88,7 +88,13 @@ export async function addStoreCommand() {
|
|
|
88
88
|
: { check: isGlabAvailable, checkRemote: hasGitLabRemote, set: setGitLabVariable, name: 'GitLab' };
|
|
89
89
|
|
|
90
90
|
if (!setter.check()) {
|
|
91
|
-
console.log(
|
|
91
|
+
console.log(
|
|
92
|
+
pc.yellow(
|
|
93
|
+
ciHost === 'github'
|
|
94
|
+
? ' GitHub CLI is not available (tried gh and npx gh) or not logged in. Add secrets manually in repo Settings.'
|
|
95
|
+
: ` ${setter.name} CLI is not installed or not logged in. Add secrets manually in repo Settings.`
|
|
96
|
+
)
|
|
97
|
+
);
|
|
92
98
|
} else if (!setter.checkRemote()) {
|
|
93
99
|
console.log(pc.yellow(' No ' + setter.name + ' remote (origin). Add secrets manually after pushing.'));
|
|
94
100
|
} else {
|
|
@@ -116,22 +122,22 @@ export async function addStoreCommand() {
|
|
|
116
122
|
}
|
|
117
123
|
}
|
|
118
124
|
const total = secretsToPrompt.length;
|
|
119
|
-
console.log(pc.cyan(`\n Configure ${total} secret(s) for store "${store.alias}" (
|
|
125
|
+
console.log(pc.cyan(`\n Configure ${total} secret(s) for store "${store.alias}" (all optional).\n`));
|
|
120
126
|
for (let i = 0; i < secretsToPrompt.length; i++) {
|
|
121
127
|
const secret = secretsToPrompt[i];
|
|
122
128
|
const isThemeToken = secret.name === 'SHOPIFY_THEME_ACCESS_TOKEN' || secret.name.startsWith('SHOPIFY_THEME_ACCESS_TOKEN_');
|
|
123
129
|
if (isThemeToken && store.domain) {
|
|
124
|
-
// Theme tokens are
|
|
130
|
+
// Theme tokens are optional; if provided, keep prompting until valid + set.
|
|
125
131
|
while (true) {
|
|
126
132
|
const value = await promptSecretValue(secret, i, total);
|
|
127
133
|
if (!value) {
|
|
128
|
-
console.log(pc.
|
|
129
|
-
|
|
134
|
+
console.log(pc.dim(` Skipped ${secret.name}.`));
|
|
135
|
+
break;
|
|
130
136
|
}
|
|
131
137
|
const result = await validateThemeAccessToken(store.domain, value);
|
|
132
138
|
if (!result.ok) {
|
|
133
139
|
console.log(pc.red(` Token test failed: ${result.error}`));
|
|
134
|
-
console.log(pc.dim('
|
|
140
|
+
console.log(pc.dim(' Enter a valid token, or leave blank to skip.'));
|
|
135
141
|
continue;
|
|
136
142
|
}
|
|
137
143
|
console.log(pc.green(' Token validated against store.'));
|
|
@@ -142,7 +148,7 @@ export async function addStoreCommand() {
|
|
|
142
148
|
break;
|
|
143
149
|
} catch (err) {
|
|
144
150
|
console.log(pc.red(` Failed to set ${secret.name}: ${err.message}`));
|
|
145
|
-
console.log(pc.dim('
|
|
151
|
+
console.log(pc.dim(' Enter again to retry, or leave blank to skip.'));
|
|
146
152
|
}
|
|
147
153
|
}
|
|
148
154
|
continue;
|
package/src/commands/init.js
CHANGED
|
@@ -223,7 +223,13 @@ async function runInitFlow() {
|
|
|
223
223
|
|
|
224
224
|
if (!setter.check()) {
|
|
225
225
|
const installUrl = ciHost === 'github' ? 'https://cli.github.com/' : 'https://gitlab.com/gitlab-org/cli';
|
|
226
|
-
console.log(
|
|
226
|
+
console.log(
|
|
227
|
+
pc.yellow(
|
|
228
|
+
ciHost === 'github'
|
|
229
|
+
? ' GitHub CLI is not available (tried gh and npx gh) or not logged in.'
|
|
230
|
+
: ` ${setter.name} CLI is not installed or not logged in.`
|
|
231
|
+
)
|
|
232
|
+
);
|
|
227
233
|
console.log(pc.dim(` Install: ${installUrl} — then run ${ciHost === 'github' ? 'gh' : 'glab'} auth login`));
|
|
228
234
|
console.log(
|
|
229
235
|
pc.dim(
|
|
@@ -283,23 +289,24 @@ async function runInitFlow() {
|
|
|
283
289
|
|
|
284
290
|
if (isThemeToken) {
|
|
285
291
|
if (!storeUrl) {
|
|
286
|
-
console.log(pc.
|
|
287
|
-
continue;
|
|
292
|
+
console.log(pc.yellow(` Could not resolve store URL for ${secret.name}, skipping token validation.`));
|
|
288
293
|
}
|
|
289
|
-
// Theme tokens are
|
|
294
|
+
// Theme tokens are optional during setup; if provided, keep prompting until valid + set.
|
|
290
295
|
while (true) {
|
|
291
296
|
const value = await promptSecretValue(secret, i, totalToPrompt);
|
|
292
297
|
if (!value) {
|
|
293
|
-
console.log(pc.
|
|
294
|
-
|
|
298
|
+
console.log(pc.dim(` Skipped ${secret.name}.`));
|
|
299
|
+
break;
|
|
295
300
|
}
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
+
if (storeUrl) {
|
|
302
|
+
const result = await validateThemeAccessToken(storeUrl, value);
|
|
303
|
+
if (!result.ok) {
|
|
304
|
+
console.log(pc.red(` Token test failed: ${result.error}`));
|
|
305
|
+
console.log(pc.dim(' Enter a valid token, or leave blank to skip.'));
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
console.log(pc.green(' Token validated against store.'));
|
|
301
309
|
}
|
|
302
|
-
console.log(pc.green(' Token validated against store.'));
|
|
303
310
|
try {
|
|
304
311
|
await setter.set(secret.name, value);
|
|
305
312
|
console.log(pc.green(` Set ${secret.name}.`));
|
|
@@ -307,7 +314,7 @@ async function runInitFlow() {
|
|
|
307
314
|
break;
|
|
308
315
|
} catch (err) {
|
|
309
316
|
console.log(pc.red(` Failed to set ${secret.name}: ${err.message}`));
|
|
310
|
-
console.log(pc.dim('
|
|
317
|
+
console.log(pc.dim(' Enter again to retry, or leave blank to skip.'));
|
|
311
318
|
}
|
|
312
319
|
}
|
|
313
320
|
continue;
|
package/src/index.js
CHANGED
|
@@ -61,15 +61,15 @@ function registerThemeCommands(cmd) {
|
|
|
61
61
|
|
|
62
62
|
cmd
|
|
63
63
|
.command('serve')
|
|
64
|
-
.description('Run local theme dev (Shopify + assets
|
|
65
|
-
.option('--
|
|
66
|
-
.action((opts) => serveAll({ includeThemeCheck: opts.themeCheck
|
|
64
|
+
.description('Run local theme dev (Shopify + assets; Theme Check off by default)')
|
|
65
|
+
.option('--theme-check', 'Enable Theme Check watcher')
|
|
66
|
+
.action((opts) => serveAll({ includeThemeCheck: opts.themeCheck === true }));
|
|
67
67
|
cmd.command('serve:shopify').description('Run Shopify theme dev server').action(() => serveShopify());
|
|
68
68
|
cmd
|
|
69
69
|
.command('serve:assets')
|
|
70
|
-
.description('Run assets watch (Tailwind + scripts
|
|
71
|
-
.option('--
|
|
72
|
-
.action((opts) => serveAssets({ includeThemeCheck: opts.themeCheck
|
|
70
|
+
.description('Run assets watch (Tailwind + scripts; Theme Check off by default)')
|
|
71
|
+
.option('--theme-check', 'Enable Theme Check watcher')
|
|
72
|
+
.action((opts) => serveAssets({ includeThemeCheck: opts.themeCheck === true }));
|
|
73
73
|
|
|
74
74
|
cmd.command('lint').description('Run theme linting (liquid, js, css)').action(() => lintAll());
|
|
75
75
|
|
package/src/lib/dev-runtime.js
CHANGED
|
@@ -217,7 +217,7 @@ export function serveShopify({ cwd = process.cwd() } = {}) {
|
|
|
217
217
|
return runShopify(args, { cwd, name: 'shopify' });
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
-
export function serveAssets({ cwd = process.cwd(), includeThemeCheck =
|
|
220
|
+
export function serveAssets({ cwd = process.cwd(), includeThemeCheck = false } = {}) {
|
|
221
221
|
printServeStartupHeader();
|
|
222
222
|
const env = { ...process.env, NODE_ENV: 'production' };
|
|
223
223
|
const styleEntrypoint = join(cwd, '_styles', 'main.css');
|
|
@@ -312,7 +312,7 @@ export function serveAssets({ cwd = process.cwd(), includeThemeCheck = true } =
|
|
|
312
312
|
return { tailwind, devMcp, scriptsWatch, themeCheckWatch, cleanup };
|
|
313
313
|
}
|
|
314
314
|
|
|
315
|
-
export function serveAll({ cwd = process.cwd(), includeThemeCheck =
|
|
315
|
+
export function serveAll({ cwd = process.cwd(), includeThemeCheck = false } = {}) {
|
|
316
316
|
// Start assets first, then bring up Shopify after a short delay.
|
|
317
317
|
const assets = serveAssets({ cwd, includeThemeCheck });
|
|
318
318
|
let shopify = null;
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { spawn, spawnSync, execSync } from 'node:child_process';
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
|
|
4
|
+
function resolveGhInvocation(cwd = process.cwd()) {
|
|
5
|
+
const direct = spawnSync('gh', ['--version'], { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
6
|
+
if (direct.status === 0) return { command: 'gh', prefix: [] };
|
|
7
|
+
|
|
8
|
+
const viaNpx = spawnSync('npx', ['--yes', 'gh', '--version'], {
|
|
9
|
+
cwd,
|
|
10
|
+
encoding: 'utf-8',
|
|
11
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
12
|
+
});
|
|
13
|
+
if (viaNpx.status === 0) return { command: 'npx', prefix: ['--yes', 'gh'] };
|
|
14
|
+
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
|
|
4
18
|
/**
|
|
5
19
|
* Secret/variable definitions for CI (GitHub Actions or GitLab CI).
|
|
6
20
|
* condition: 'always' = required for core workflows; 'preview' | 'build' = only when that feature is enabled.
|
|
@@ -8,7 +22,7 @@ import pc from 'picocolors';
|
|
|
8
22
|
export const SECRET_DEFINITIONS = [
|
|
9
23
|
{
|
|
10
24
|
name: 'GEMINI_API_KEY',
|
|
11
|
-
required:
|
|
25
|
+
required: false,
|
|
12
26
|
condition: 'always',
|
|
13
27
|
description: 'Google Gemini API key for AI-generated changelogs on release',
|
|
14
28
|
whereToGet:
|
|
@@ -24,7 +38,7 @@ export const SECRET_DEFINITIONS = [
|
|
|
24
38
|
},
|
|
25
39
|
{
|
|
26
40
|
name: 'SHOPIFY_THEME_ACCESS_TOKEN',
|
|
27
|
-
required:
|
|
41
|
+
required: false,
|
|
28
42
|
condition: 'preview',
|
|
29
43
|
description: 'Theme access token so CI can push preview themes (password from Shopify Theme Access app)',
|
|
30
44
|
whereToGet:
|
|
@@ -60,8 +74,13 @@ export const SECRET_DEFINITIONS = [
|
|
|
60
74
|
*/
|
|
61
75
|
export function isGhAvailable() {
|
|
62
76
|
try {
|
|
63
|
-
|
|
64
|
-
return
|
|
77
|
+
const gh = resolveGhInvocation();
|
|
78
|
+
if (!gh) return false;
|
|
79
|
+
const result = spawnSync(gh.command, [...gh.prefix, 'auth', 'status'], {
|
|
80
|
+
encoding: 'utf-8',
|
|
81
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
82
|
+
});
|
|
83
|
+
return result.status === 0;
|
|
65
84
|
} catch {
|
|
66
85
|
return false;
|
|
67
86
|
}
|
|
@@ -99,10 +118,12 @@ export function getGitHubRepoSpec(cwd = process.cwd()) {
|
|
|
99
118
|
*/
|
|
100
119
|
export function listGitHubSecrets(cwd = process.cwd()) {
|
|
101
120
|
try {
|
|
121
|
+
const gh = resolveGhInvocation(cwd);
|
|
122
|
+
if (!gh) return [];
|
|
102
123
|
const repo = getGitHubRepoSpec(cwd);
|
|
103
|
-
const args = ['secret', 'list', '--json', 'name'];
|
|
124
|
+
const args = [...gh.prefix, 'secret', 'list', '--json', 'name'];
|
|
104
125
|
if (repo) args.push('-R', repo);
|
|
105
|
-
const result = spawnSync(
|
|
126
|
+
const result = spawnSync(gh.command, args, { cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
106
127
|
const out = (result.stdout || '').trim();
|
|
107
128
|
const data = JSON.parse(out || '[]');
|
|
108
129
|
return Array.isArray(data) ? data.map((s) => s.name).filter(Boolean) : [];
|
|
@@ -131,10 +152,15 @@ export function listGitLabVariables(cwd = process.cwd()) {
|
|
|
131
152
|
*/
|
|
132
153
|
export function setSecret(name, value, cwd = process.cwd()) {
|
|
133
154
|
return new Promise((resolve, reject) => {
|
|
155
|
+
const gh = resolveGhInvocation(cwd);
|
|
156
|
+
if (!gh) {
|
|
157
|
+
reject(new Error('GitHub CLI is not available (tried gh and npx gh)'));
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
134
160
|
const repo = getGitHubRepoSpec(cwd);
|
|
135
|
-
const args = ['secret', 'set', name];
|
|
161
|
+
const args = [...gh.prefix, 'secret', 'set', name];
|
|
136
162
|
if (repo) args.push('-R', repo);
|
|
137
|
-
const child = spawn(
|
|
163
|
+
const child = spawn(gh.command, args, {
|
|
138
164
|
stdio: ['pipe', 'inherit', 'inherit'],
|
|
139
165
|
cwd,
|
|
140
166
|
});
|
|
@@ -295,7 +321,7 @@ export function getSecretsToPrompt({ enablePreviewWorkflows, enableBuildWorkflow
|
|
|
295
321
|
if (enablePreviewWorkflows) {
|
|
296
322
|
list.push({
|
|
297
323
|
name: `SHOPIFY_THEME_ACCESS_TOKEN_${suffix}`,
|
|
298
|
-
required:
|
|
324
|
+
required: false,
|
|
299
325
|
description: `Store ${store.alias}: Theme access token (password from Theme Access app)`,
|
|
300
326
|
whereToGet: 'Theme Access app in Shopify — the password it gives is the token for this store.',
|
|
301
327
|
});
|
|
@@ -338,7 +364,7 @@ export function getSecretsToPromptForNewStore(store) {
|
|
|
338
364
|
return [
|
|
339
365
|
{
|
|
340
366
|
name: `SHOPIFY_THEME_ACCESS_TOKEN_${suffix}`,
|
|
341
|
-
required:
|
|
367
|
+
required: false,
|
|
342
368
|
description: `Store ${store.alias}: Theme access token (password from Theme Access app)`,
|
|
343
369
|
whereToGet: 'Theme Access app in Shopify — the password it gives is the token for this store.',
|
|
344
370
|
},
|