climaybe 1.3.1 → 1.4.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 CHANGED
@@ -210,14 +210,14 @@ Add the following secrets to your GitHub repository (or use **GitLab CI/CD varia
210
210
  |--------|----------|-------------|
211
211
  | `GEMINI_API_KEY` | Yes | Google Gemini API key for changelog generation |
212
212
  | `SHOPIFY_STORE_URL` | Set from config | Store URL is set automatically from the store domain(s) you add during init (no prompt). |
213
- | `SHOPIFY_CLI_THEME_TOKEN` | Yes* | Theme access token for preview workflows (required when preview is enabled). |
213
+ | `SHOPIFY_THEME_ACCESS_TOKEN` | Yes* | Theme access token for preview workflows (required when preview is enabled). |
214
214
  | `SHOP_ACCESS_TOKEN` | Optional* | Required only when optional build workflows are enabled (Lighthouse) |
215
215
  | `LHCI_GITHUB_APP_TOKEN` | Optional* | Required only when optional build workflows are enabled (Lighthouse) |
216
216
  | `SHOP_PASSWORD` | Optional | Used by Lighthouse action when your store requires password auth |
217
217
 
218
218
  **Store URL:** During `climaybe init` (or `add-store`), store URL secret(s) are set from your configured store domain(s); you are only prompted for the theme token.
219
219
 
220
- **Multi-store:** Per-store secrets `SHOPIFY_STORE_URL_<ALIAS>` and `SHOPIFY_CLI_THEME_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`).
220
+ **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`).
221
221
 
222
222
  ## Directory Structure (Multi-store)
223
223
 
@@ -246,7 +246,7 @@ Add the following secrets to your GitHub repository (or use **GitLab CI/CD varia
246
246
 
247
247
  - **Branch:** Single default branch `main`. Feature branches open as PRs into `main`.
248
248
  - **Versioning:** [SemVer](https://semver.org/). Versions are **bumped automatically** when PRs are merged to `main` using [conventional commits](https://www.conventionalcommits.org/): `fix:` → patch, `feat:` → minor, `BREAKING CHANGE` or `feat!:` → major.
249
- - **Flow:** Merge to `main` → [Release version](.github/workflows/release-version.yml) runs semantic-release (bumps `package.json`, pushes tag) → tag push triggers [Release](.github/workflows/release.yml) (tests + publish to npm). Requires `NPM_TOKEN` secret for npm publish.
249
+ - **Flow:** Merge to `main` → [Release version](.github/workflows/release-version.yml) runs semantic-release (bumps `package.json`, pushes tag) → tag push triggers [Release](.github/workflows/release.yml) (tests + publish to npm). Requires `NPM_TOKEN` secret for npm publish. Do not create tags manually; only the Release version workflow creates tags so that tag and package version stay in sync.
250
250
  - **CI:** Every PR and push to `main` runs tests on Node 20 and 22 ([CI workflow](.github/workflows/ci.yml)).
251
251
 
252
252
  See [CONTRIBUTING.md](CONTRIBUTING.md) for branch, PR, and conventional-commit details.
package/bin/cli.js CHANGED
@@ -1,5 +1,13 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { createRequire } from 'node:module';
4
+ import { dirname, join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
3
6
  import { run } from '../src/index.js';
4
7
 
5
- run(process.argv);
8
+ // Resolve version from package.json next to this bin (works with npm link / global install)
9
+ const require = createRequire(import.meta.url);
10
+ const binDir = dirname(fileURLToPath(import.meta.url));
11
+ const pkg = require(join(binDir, '..', 'package.json'));
12
+
13
+ run(process.argv, pkg.version);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "climaybe",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "Shopify CI/CD CLI — scaffolds workflows, branch strategy, and store config for single-store and multi-store theme repos",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,5 +1,5 @@
1
1
  import pc from 'picocolors';
2
- import { promptNewStore, promptConfigureCISecrets, promptUpdateExistingSecrets, promptSecretValue } from '../lib/prompts.js';
2
+ import { promptNewStore, promptConfigureCISecrets, promptUpdateExistingSecrets, promptSecretValue, promptTestThemeToken } from '../lib/prompts.js';
3
3
  import { readConfig, addStoreToConfig, getStoreAliases, getMode, isPreviewWorkflowsEnabled, isBuildWorkflowsEnabled } from '../lib/config.js';
4
4
  import { createStoreBranches } from '../lib/git.js';
5
5
  import { scaffoldWorkflows } from '../lib/workflows.js';
@@ -13,6 +13,7 @@ import {
13
13
  listGitLabVariables,
14
14
  getStoreUrlSecretForNewStore,
15
15
  getSecretsToPromptForNewStore,
16
+ validateThemeAccessToken,
16
17
  setSecret,
17
18
  setGitLabVariable,
18
19
  } from '../lib/github-secrets.js';
@@ -109,15 +110,29 @@ export async function addStoreCommand() {
109
110
  for (let i = 0; i < secretsToPrompt.length; i++) {
110
111
  const secret = secretsToPrompt[i];
111
112
  const value = await promptSecretValue(secret, i, total);
112
- if (value) {
113
- try {
114
- await setter.set(secret.name, value);
115
- console.log(pc.green(` Set ${secret.name}.`));
116
- setCount++;
117
- } catch (err) {
118
- console.log(pc.red(` Failed to set ${secret.name}: ${err.message}`));
113
+ if (!value) continue;
114
+
115
+ const isThemeToken = secret.name === 'SHOPIFY_THEME_ACCESS_TOKEN' || secret.name.startsWith('SHOPIFY_THEME_ACCESS_TOKEN_');
116
+ if (isThemeToken && store.domain) {
117
+ const doTest = await promptTestThemeToken();
118
+ if (doTest) {
119
+ const result = await validateThemeAccessToken(store.domain, value);
120
+ if (!result.ok) {
121
+ console.log(pc.red(` Token test failed: ${result.error}`));
122
+ console.log(pc.dim(' Secret not set. You can add it later in repo Settings → Secrets.'));
123
+ continue;
124
+ }
125
+ console.log(pc.green(' Token validated against store.'));
119
126
  }
120
127
  }
128
+
129
+ try {
130
+ await setter.set(secret.name, value);
131
+ console.log(pc.green(` Set ${secret.name}.`));
132
+ setCount++;
133
+ } catch (err) {
134
+ console.log(pc.red(` Failed to set ${secret.name}: ${err.message}`));
135
+ }
121
136
  }
122
137
  if (setCount > 0) {
123
138
  console.log(pc.green(`\n Done. ${setCount} secret(s) set for store "${store.alias}".\n`));
@@ -7,9 +7,10 @@ import {
7
7
  promptConfigureCISecrets,
8
8
  promptUpdateExistingSecrets,
9
9
  promptSecretValue,
10
+ promptTestThemeToken,
10
11
  } from '../lib/prompts.js';
11
12
  import { readConfig, writeConfig } from '../lib/config.js';
12
- import { ensureGitRepo, ensureInitialCommit, ensureStagingBranch, createStoreBranches } from '../lib/git.js';
13
+ import { ensureGitRepo, ensureInitialCommit, ensureStagingBranch, createStoreBranches, getSuggestedTagForRelease } from '../lib/git.js';
13
14
  import { scaffoldWorkflows } from '../lib/workflows.js';
14
15
  import { createStoreDirectories } from '../lib/store-sync.js';
15
16
  import {
@@ -21,6 +22,8 @@ import {
21
22
  listGitLabVariables,
22
23
  getStoreUrlSecretsFromConfig,
23
24
  getSecretsToPrompt,
25
+ getStoreUrlForThemeTokenSecret,
26
+ validateThemeAccessToken,
24
27
  setSecret,
25
28
  setGitLabVariable,
26
29
  } from '../lib/github-secrets.js';
@@ -93,10 +96,12 @@ async function runInitFlow() {
93
96
  console.log(pc.dim(` Preview workflows: ${enablePreviewWorkflows ? 'enabled' : 'disabled'}`));
94
97
  console.log(pc.dim(` Build workflows: ${enableBuildWorkflows ? 'enabled' : 'disabled'}`));
95
98
 
99
+ const suggestedTag = getSuggestedTagForRelease();
100
+ const tagLabel = suggestedTag === 'v1.0.0' ? 'Tag your first release' : 'Tag your next release';
96
101
  console.log(pc.dim('\n Next steps:'));
97
102
  console.log(pc.dim(' 1. Add GEMINI_API_KEY to your CI secrets (or configure below)'));
98
103
  console.log(pc.dim(' 2. Push to GitHub/GitLab and start using the branching workflow'));
99
- console.log(pc.dim(' 3. Tag your first release: git tag v1.0.0\n'));
104
+ console.log(pc.dim(` 3. ${tagLabel}: git tag ${suggestedTag}\n`));
100
105
 
101
106
  const ciHost = await promptConfigureCISecrets();
102
107
  if (ciHost === 'skip') return;
@@ -167,15 +172,32 @@ async function runInitFlow() {
167
172
  for (let i = 0; i < secretsToPrompt.length; i++) {
168
173
  const secret = secretsToPrompt[i];
169
174
  const value = await promptSecretValue(secret, i, total);
170
- if (value) {
171
- try {
172
- await setter.set(secret.name, value);
173
- console.log(pc.green(` Set ${secret.name}.`));
174
- setCount++;
175
- } catch (err) {
176
- console.log(pc.red(` Failed to set ${secret.name}: ${err.message}`));
175
+ if (!value) continue;
176
+
177
+ const isThemeToken =
178
+ secret.name === 'SHOPIFY_THEME_ACCESS_TOKEN' || secret.name.startsWith('SHOPIFY_THEME_ACCESS_TOKEN_');
179
+ const storeUrl = isThemeToken ? getStoreUrlForThemeTokenSecret(secret.name, stores) : null;
180
+
181
+ if (storeUrl) {
182
+ const doTest = await promptTestThemeToken();
183
+ if (doTest) {
184
+ const result = await validateThemeAccessToken(storeUrl, value);
185
+ if (!result.ok) {
186
+ console.log(pc.red(` Token test failed: ${result.error}`));
187
+ console.log(pc.dim(' Secret not set. You can add it later in repo Settings → Secrets.'));
188
+ continue;
189
+ }
190
+ console.log(pc.green(' Token validated against store.'));
177
191
  }
178
192
  }
193
+
194
+ try {
195
+ await setter.set(secret.name, value);
196
+ console.log(pc.green(` Set ${secret.name}.`));
197
+ setCount++;
198
+ } catch (err) {
199
+ console.log(pc.red(` Failed to set ${secret.name}: ${err.message}`));
200
+ }
179
201
  }
180
202
  if (setCount > 0) {
181
203
  console.log(pc.green(`\n Done. ${setCount} secret(s) set for this repository.\n`));
package/src/index.js CHANGED
@@ -1,6 +1,3 @@
1
- import { createRequire } from 'node:module';
2
- import { dirname, join } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
1
  import { Command } from 'commander';
5
2
  import { initCommand, reinitCommand } from './commands/init.js';
6
3
  import { addStoreCommand } from './commands/add-store.js';
@@ -8,14 +5,11 @@ import { switchCommand } from './commands/switch.js';
8
5
  import { syncCommand } from './commands/sync.js';
9
6
  import { updateWorkflowsCommand } from './commands/update-workflows.js';
10
7
 
11
- const require = createRequire(import.meta.url);
12
- const __dirname = dirname(fileURLToPath(import.meta.url));
13
- const { version } = require(join(__dirname, '..', 'package.json'));
14
-
15
8
  /**
16
9
  * Create the CLI program (for testing and for run).
10
+ * @param {string} [version] - Version string (from bin/cli.js when run as CLI; from package.json in tests).
17
11
  */
18
- export function createProgram() {
12
+ export function createProgram(version = '0.0.0') {
19
13
  const program = new Command();
20
14
 
21
15
  program
@@ -58,6 +52,6 @@ export function createProgram() {
58
52
  return program;
59
53
  }
60
54
 
61
- export function run(argv) {
62
- createProgram().parse(argv);
55
+ export function run(argv, version) {
56
+ createProgram(version).parse(argv);
63
57
  }
package/src/lib/config.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { readFileSync, writeFileSync, existsSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
+ import { getLatestTagVersion } from './git.js';
3
4
 
4
5
  const PKG = 'package.json';
5
6
 
@@ -45,9 +46,16 @@ export function readConfig(cwd = process.cwd()) {
45
46
  export function writeConfig(config, cwd = process.cwd()) {
46
47
  let pkg = readPkg(cwd);
47
48
  if (!pkg) {
49
+ let version = '1.0.0';
50
+ try {
51
+ const fromTags = getLatestTagVersion(cwd);
52
+ if (fromTags) version = fromTags;
53
+ } catch {
54
+ // not a git repo or no tags
55
+ }
48
56
  pkg = {
49
57
  name: 'shopify-theme',
50
- version: '1.0.0',
58
+ version,
51
59
  private: true,
52
60
  config: {},
53
61
  };
package/src/lib/git.js CHANGED
@@ -106,3 +106,31 @@ export function ensureGitRepo(cwd = process.cwd()) {
106
106
  console.log(pc.green(' Initialized git repository.'));
107
107
  }
108
108
  }
109
+
110
+ /**
111
+ * Get the latest tag version (e.g. "1.2.3") from v* tags, or null if none.
112
+ * Sorts by version so v2.0.0 > v1.9.9.
113
+ */
114
+ export function getLatestTagVersion(cwd = process.cwd()) {
115
+ try {
116
+ const out = exec('git tag -l "v*" --sort=-v:refname', cwd);
117
+ const first = out.split(/\n/)[0]?.trim();
118
+ if (!first || !first.startsWith('v')) return null;
119
+ const match = first.replace(/^v/, '').match(/^(\d+)\.(\d+)\.(\d+)(?:-|$)/);
120
+ return match ? `${match[1]}.${match[2]}.${match[3]}` : null;
121
+ } catch {
122
+ return null;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Suggested tag for next release: v1.0.0 if no tags, else next patch (e.g. v1.2.3 → v1.2.4).
128
+ */
129
+ export function getSuggestedTagForRelease(cwd = process.cwd()) {
130
+ const latest = getLatestTagVersion(cwd);
131
+ if (!latest) return 'v1.0.0';
132
+ const parts = latest.split('.').map(Number);
133
+ if (parts.length < 3) return 'v1.0.0';
134
+ parts[2] += 1;
135
+ return `v${parts[0]}.${parts[1]}.${parts[2]}`;
136
+ }
@@ -23,12 +23,12 @@ export const SECRET_DEFINITIONS = [
23
23
  'Your theme’s store URL in Shopify Admin → Settings → Domains, or use the .myshopify.com URL.',
24
24
  },
25
25
  {
26
- name: 'SHOPIFY_CLI_THEME_TOKEN',
26
+ name: 'SHOPIFY_THEME_ACCESS_TOKEN',
27
27
  required: true,
28
28
  condition: 'preview',
29
- description: 'Theme access token so CI can push preview themes to your store',
29
+ description: 'Theme access token so CI can push preview themes (password from Shopify Theme Access app)',
30
30
  whereToGet:
31
- 'Shopify Partners: your app → Theme library access Create theme access token. Or: Shopify Admin Apps Develop apps → your app API credentials → Theme access.',
31
+ 'Install the Theme Access app from Shopify: it gives you a password that password is your theme access token.',
32
32
  },
33
33
  {
34
34
  name: 'SHOP_ACCESS_TOKEN',
@@ -197,6 +197,44 @@ export function aliasToSecretSuffix(alias) {
197
197
  return String(alias).replace(/-/g, '_').toUpperCase();
198
198
  }
199
199
 
200
+ /**
201
+ * Get store domain for a theme token secret name (single or SHOPIFY_THEME_ACCESS_TOKEN_<ALIAS>).
202
+ * Returns store domain or null if not found.
203
+ */
204
+ export function getStoreUrlForThemeTokenSecret(secretName, stores = []) {
205
+ if (!stores.length) return null;
206
+ if (secretName === 'SHOPIFY_THEME_ACCESS_TOKEN') return stores[0]?.domain ?? null;
207
+ if (!secretName.startsWith('SHOPIFY_THEME_ACCESS_TOKEN_')) return null;
208
+ const suffix = secretName.replace('SHOPIFY_THEME_ACCESS_TOKEN_', '');
209
+ const store = stores.find((s) => aliasToSecretSuffix(s.alias) === suffix);
210
+ return store?.domain ?? null;
211
+ }
212
+
213
+ /**
214
+ * Test theme access token against the store (read-only list). Never logs or persists the token.
215
+ * Returns { ok: true } or { ok: false, error: string }. Safe to call; token only sent to Shopify CLI.
216
+ */
217
+ export function validateThemeAccessToken(storeUrl, token) {
218
+ if (!storeUrl || !token) return { ok: false, error: 'Missing store URL or token' };
219
+ return new Promise((resolve) => {
220
+ const child = spawn('shopify', ['theme', 'list', '--store', storeUrl, '--password', token], {
221
+ stdio: ['ignore', 'pipe', 'pipe'],
222
+ env: { ...process.env },
223
+ });
224
+ let stderr = '';
225
+ child.stderr?.on('data', (chunk) => {
226
+ stderr += String(chunk);
227
+ });
228
+ child.on('error', (err) => {
229
+ resolve({ ok: false, error: err.code === 'ENOENT' ? 'Shopify CLI not installed (npm install -g @shopify/cli @shopify/theme)' : err.message });
230
+ });
231
+ child.on('close', (code) => {
232
+ if (code === 0) resolve({ ok: true });
233
+ else resolve({ ok: false, error: stderr.trim() || `Exit code ${code}` });
234
+ });
235
+ });
236
+ }
237
+
200
238
  /**
201
239
  * Store URL secrets are set from config (store domains added during init), not prompted.
202
240
  * Returns [{ name, value }] for SHOPIFY_STORE_URL and/or SHOPIFY_STORE_URL_<ALIAS>.
@@ -225,7 +263,7 @@ export function getStoreUrlSecretsFromConfig({ enablePreviewWorkflows, enableBui
225
263
 
226
264
  /**
227
265
  * Get secrets we need to prompt for (excludes store URLs; those are set from config).
228
- * Theme token(s) are required when preview is enabled.
266
+ * Theme token(s) are required when preview is enabled. In multi-store, all Shopify tokens are per-store.
229
267
  */
230
268
  export function getSecretsToPrompt({ enablePreviewWorkflows, enableBuildWorkflows, mode = 'single', stores = [] }) {
231
269
  const isMulti = mode === 'multi' && stores.length > 1;
@@ -239,23 +277,46 @@ export function getSecretsToPrompt({ enablePreviewWorkflows, enableBuildWorkflow
239
277
  return false;
240
278
  });
241
279
 
242
- const dropPreviewGeneric =
243
- isMulti && enablePreviewWorkflows
244
- ? (s) => s.name !== 'SHOPIFY_CLI_THEME_TOKEN'
280
+ // Multi-store: drop generic Shopify tokens; we prompt per-store below
281
+ const dropForMulti =
282
+ isMulti
283
+ ? (s) =>
284
+ s.name !== 'SHOPIFY_THEME_ACCESS_TOKEN' &&
285
+ s.name !== 'SHOP_ACCESS_TOKEN' &&
286
+ s.name !== 'SHOP_PASSWORD'
245
287
  : () => true;
246
288
 
247
- let list = base.filter(dropPreviewGeneric);
289
+ let list = base.filter(dropForMulti);
248
290
 
249
- if (isMulti && enablePreviewWorkflows) {
291
+ // Multi-store: prompt per store (theme token, then access token + password if build) so user configures one store at a time
292
+ if (isMulti) {
250
293
  for (const store of stores) {
251
294
  const suffix = aliasToSecretSuffix(store.alias);
252
- list.push({
253
- name: `SHOPIFY_CLI_THEME_TOKEN_${suffix}`,
254
- required: true,
255
- description: `Store ${store.alias}: Theme access token for CI (staging/live use this for ${store.alias})`,
256
- whereToGet:
257
- 'Shopify Partners or Admin Apps Develop apps your app Theme access for this store.',
258
- });
295
+ if (enablePreviewWorkflows) {
296
+ list.push({
297
+ name: `SHOPIFY_THEME_ACCESS_TOKEN_${suffix}`,
298
+ required: true,
299
+ description: `Store ${store.alias}: Theme access token (password from Theme Access app)`,
300
+ whereToGet: 'Theme Access app in Shopify the password it gives is the token for this store.',
301
+ });
302
+ }
303
+ if (enableBuildWorkflows) {
304
+ list.push(
305
+ {
306
+ name: `SHOP_ACCESS_TOKEN_${suffix}`,
307
+ required: false,
308
+ description: `Store ${store.alias}: API access token for Lighthouse`,
309
+ whereToGet:
310
+ 'Shopify Admin → Develop apps → your app → API credentials (Admin/storefront access).',
311
+ },
312
+ {
313
+ name: `SHOP_PASSWORD_${suffix}`,
314
+ required: false,
315
+ description: `Store ${store.alias}: Storefront password if protected (optional)`,
316
+ whereToGet: 'Storefront password in Shopify Admin for this store.',
317
+ }
318
+ );
319
+ }
259
320
  }
260
321
  }
261
322
 
@@ -270,17 +331,28 @@ export function getStoreUrlSecretForNewStore(store) {
270
331
  }
271
332
 
272
333
  /**
273
- * Per-store secret to prompt for when adding a store (theme token only; URL is set from store.domain).
334
+ * Per-store secrets to prompt for when adding a store (URL is set from store.domain).
274
335
  */
275
336
  export function getSecretsToPromptForNewStore(store) {
276
337
  const suffix = aliasToSecretSuffix(store.alias);
277
338
  return [
278
339
  {
279
- name: `SHOPIFY_CLI_THEME_TOKEN_${suffix}`,
340
+ name: `SHOPIFY_THEME_ACCESS_TOKEN_${suffix}`,
280
341
  required: true,
281
- description: `Store ${store.alias}: Theme access token for CI (staging/live use this for ${store.alias})`,
282
- whereToGet:
283
- 'Shopify Partners or Admin → Apps → Develop apps → your app → Theme access for this store.',
342
+ description: `Store ${store.alias}: Theme access token (password from Theme Access app)`,
343
+ whereToGet: 'Theme Access app in Shopify — the password it gives is the token for this store.',
344
+ },
345
+ {
346
+ name: `SHOP_ACCESS_TOKEN_${suffix}`,
347
+ required: false,
348
+ description: `Store ${store.alias}: API access token for Lighthouse (if using build workflows)`,
349
+ whereToGet: 'Shopify Admin → Develop apps → your app → API credentials.',
350
+ },
351
+ {
352
+ name: `SHOP_PASSWORD_${suffix}`,
353
+ required: false,
354
+ description: `Store ${store.alias}: Storefront password if protected (optional)`,
355
+ whereToGet: 'Storefront password in Shopify Admin for this store.',
284
356
  },
285
357
  ];
286
358
  }
@@ -225,3 +225,16 @@ export async function promptSecretValue(secret, index, total) {
225
225
  if (!trimmed && !secret.required) return null;
226
226
  return trimmed || null;
227
227
  }
228
+
229
+ /**
230
+ * Ask whether to test the theme token against the store now. Returns true to test, false to skip.
231
+ */
232
+ export async function promptTestThemeToken() {
233
+ const { test } = await prompts({
234
+ type: 'confirm',
235
+ name: 'test',
236
+ message: 'Test this token against the store now?',
237
+ initial: true,
238
+ });
239
+ return !!test;
240
+ }
@@ -22,7 +22,7 @@ jobs:
22
22
  pull-requests: write
23
23
  env:
24
24
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
25
- SHOPIFY_CLI_THEME_TOKEN: ${{ secrets.SHOPIFY_CLI_THEME_TOKEN }}
25
+ SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
26
26
  steps:
27
27
  - uses: actions/checkout@v4
28
28
 
@@ -72,8 +72,8 @@ jobs:
72
72
 
73
73
  - name: Create PR to live branch
74
74
  env:
75
- SHOPIFY_CLI_THEME_TOKEN_SCOPED: ${{ secrets[format('SHOPIFY_CLI_THEME_TOKEN_{0}', steps.alias.outputs.alias_secret)] }}
76
- SHOPIFY_CLI_THEME_TOKEN_DEFAULT: ${{ secrets.SHOPIFY_CLI_THEME_TOKEN }}
75
+ SHOPIFY_THEME_ACCESS_TOKEN_SCOPED: ${{ secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', steps.alias.outputs.alias_secret)] }}
76
+ SHOPIFY_THEME_ACCESS_TOKEN_DEFAULT: ${{ secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
77
77
  run: |
78
78
  STAGING="${{ steps.alias.outputs.staging_branch }}"
79
79
  LIVE="${{ steps.alias.outputs.live_branch }}"
@@ -81,7 +81,7 @@ jobs:
81
81
  DOMAIN="${{ steps.store.outputs.domain }}"
82
82
  STAGING_THEME_ID=""
83
83
  REPO_NAME="${GITHUB_REPOSITORY#*/}"
84
- SHOPIFY_TOKEN="${SHOPIFY_CLI_THEME_TOKEN_SCOPED:-$SHOPIFY_CLI_THEME_TOKEN_DEFAULT}"
84
+ SHOPIFY_TOKEN="${SHOPIFY_THEME_ACCESS_TOKEN_SCOPED:-$SHOPIFY_THEME_ACCESS_TOKEN_DEFAULT}"
85
85
 
86
86
  # Check if live branch exists
87
87
  if ! git ls-remote --heads origin "$LIVE" | grep -q "$LIVE"; then
@@ -69,19 +69,19 @@ jobs:
69
69
  - name: Validate Shopify credentials
70
70
  env:
71
71
  SHOPIFY_STORE_URL_SCOPED: ${{ secrets[format('SHOPIFY_STORE_URL_{0}', steps.resolve.outputs.alias_secret)] }}
72
- SHOPIFY_CLI_THEME_TOKEN_SCOPED: ${{ secrets[format('SHOPIFY_CLI_THEME_TOKEN_{0}', steps.resolve.outputs.alias_secret)] }}
72
+ SHOPIFY_THEME_ACCESS_TOKEN_SCOPED: ${{ secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', steps.resolve.outputs.alias_secret)] }}
73
73
  SHOPIFY_STORE_URL_DEFAULT: ${{ secrets.SHOPIFY_STORE_URL }}
74
- SHOPIFY_CLI_THEME_TOKEN_DEFAULT: ${{ secrets.SHOPIFY_CLI_THEME_TOKEN }}
74
+ SHOPIFY_THEME_ACCESS_TOKEN_DEFAULT: ${{ secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
75
75
  run: |
76
76
  SHOPIFY_STORE_URL="${SHOPIFY_STORE_URL_SCOPED:-$SHOPIFY_STORE_URL_DEFAULT}"
77
- SHOPIFY_CLI_THEME_TOKEN="${SHOPIFY_CLI_THEME_TOKEN_SCOPED:-$SHOPIFY_CLI_THEME_TOKEN_DEFAULT}"
77
+ SHOPIFY_THEME_ACCESS_TOKEN="${SHOPIFY_THEME_ACCESS_TOKEN_SCOPED:-$SHOPIFY_THEME_ACCESS_TOKEN_DEFAULT}"
78
78
 
79
79
  if [ -z "$SHOPIFY_STORE_URL" ]; then
80
80
  echo "No store URL secret found. Expected SHOPIFY_STORE_URL_${{ steps.resolve.outputs.alias_secret }} or SHOPIFY_STORE_URL."
81
81
  exit 1
82
82
  fi
83
- if [ -z "$SHOPIFY_CLI_THEME_TOKEN" ]; then
84
- echo "No theme token secret found. Expected SHOPIFY_CLI_THEME_TOKEN_${{ steps.resolve.outputs.alias_secret }} or SHOPIFY_CLI_THEME_TOKEN."
83
+ if [ -z "$SHOPIFY_THEME_ACCESS_TOKEN" ]; then
84
+ echo "No theme token secret found. Expected SHOPIFY_THEME_ACCESS_TOKEN_${{ steps.resolve.outputs.alias_secret }} or SHOPIFY_THEME_ACCESS_TOKEN."
85
85
  exit 1
86
86
  fi
87
87
  echo "Shopify credentials are configured for alias: ${{ steps.resolve.outputs.alias }}"
@@ -94,7 +94,7 @@ jobs:
94
94
  store_alias: ${{ needs.validate-environment.outputs.store_alias }}
95
95
  secrets:
96
96
  SHOPIFY_STORE_URL: ${{ secrets[format('SHOPIFY_STORE_URL_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
97
- SHOPIFY_CLI_THEME_TOKEN: ${{ secrets[format('SHOPIFY_CLI_THEME_TOKEN_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_CLI_THEME_TOKEN }}
97
+ SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
98
98
 
99
99
  rename-theme:
100
100
  needs: [share-theme, extract-pr-number, validate-environment]
@@ -106,7 +106,7 @@ jobs:
106
106
  store_alias: ${{ needs.validate-environment.outputs.store_alias }}
107
107
  secrets:
108
108
  SHOPIFY_STORE_URL: ${{ secrets[format('SHOPIFY_STORE_URL_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
109
- SHOPIFY_CLI_THEME_TOKEN: ${{ secrets[format('SHOPIFY_CLI_THEME_TOKEN_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_CLI_THEME_TOKEN }}
109
+ SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
110
110
 
111
111
  comment-on-pr:
112
112
  needs: [share-theme, rename-theme, extract-pr-number, validate-environment]
@@ -29,17 +29,17 @@ jobs:
29
29
  id: cleanup
30
30
  env:
31
31
  SHOPIFY_STORE_URL: ${{ secrets.SHOPIFY_STORE_URL }}
32
- SHOPIFY_CLI_THEME_TOKEN: ${{ secrets.SHOPIFY_CLI_THEME_TOKEN }}
32
+ SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
33
33
  PR_NUMBER: ${{ inputs.pr_number }}
34
34
  run: |
35
- if [ -z "$SHOPIFY_STORE_URL" ] || [ -z "$SHOPIFY_CLI_THEME_TOKEN" ]; then
35
+ if [ -z "$SHOPIFY_STORE_URL" ] || [ -z "$SHOPIFY_THEME_ACCESS_TOKEN" ]; then
36
36
  echo "Missing Shopify secrets."
37
37
  exit 1
38
38
  fi
39
39
 
40
40
  THEME_LIST=$(shopify theme list \
41
41
  --store "$SHOPIFY_STORE_URL" \
42
- --password "$SHOPIFY_CLI_THEME_TOKEN" \
42
+ --password "$SHOPIFY_THEME_ACCESS_TOKEN" \
43
43
  --json 2>/dev/null || echo "[]")
44
44
 
45
45
  DELETED_COUNT=0
@@ -52,7 +52,7 @@ jobs:
52
52
  if printf "%s" "$THEME_NAME" | grep -q "PR${PR_NUMBER}"; then
53
53
  if shopify theme delete \
54
54
  --store "$SHOPIFY_STORE_URL" \
55
- --password "$SHOPIFY_CLI_THEME_TOKEN" \
55
+ --password "$SHOPIFY_THEME_ACCESS_TOKEN" \
56
56
  --force \
57
57
  --theme "$THEME_ID" 2>/dev/null; then
58
58
  DELETED_COUNT=$((DELETED_COUNT + 1))
@@ -26,7 +26,7 @@ on:
26
26
  secrets:
27
27
  SHOPIFY_STORE_URL:
28
28
  required: false
29
- SHOPIFY_CLI_THEME_TOKEN:
29
+ SHOPIFY_THEME_ACCESS_TOKEN:
30
30
  required: false
31
31
 
32
32
  jobs:
@@ -39,7 +39,7 @@ jobs:
39
39
  - name: Rename theme
40
40
  env:
41
41
  SHOPIFY_STORE_URL: ${{ secrets.SHOPIFY_STORE_URL }}
42
- SHOPIFY_CLI_THEME_TOKEN: ${{ secrets.SHOPIFY_CLI_THEME_TOKEN }}
42
+ SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
43
43
  THEME_ID: ${{ inputs.theme_id }}
44
44
  THEME_NAME: ${{ inputs.theme_name }}
45
45
  PR_NUMBER: ${{ inputs.pr_number }}
@@ -48,7 +48,7 @@ jobs:
48
48
  echo "Missing theme_id/theme_name."
49
49
  exit 1
50
50
  fi
51
- if [ -z "$SHOPIFY_STORE_URL" ] || [ -z "$SHOPIFY_CLI_THEME_TOKEN" ]; then
51
+ if [ -z "$SHOPIFY_STORE_URL" ] || [ -z "$SHOPIFY_THEME_ACCESS_TOKEN" ]; then
52
52
  echo "Missing Shopify store URL/token for rename."
53
53
  exit 1
54
54
  fi
@@ -58,7 +58,7 @@ jobs:
58
58
 
59
59
  if shopify theme rename \
60
60
  --store "$SHOPIFY_STORE_URL" \
61
- --password "$SHOPIFY_CLI_THEME_TOKEN" \
61
+ --password "$SHOPIFY_THEME_ACCESS_TOKEN" \
62
62
  --theme "$THEME_ID" \
63
63
  --name "$NEW_THEME_NAME" 2>&1; then
64
64
  echo "Rename succeeded with password auth."
@@ -28,7 +28,7 @@ on:
28
28
  secrets:
29
29
  SHOPIFY_STORE_URL:
30
30
  required: false
31
- SHOPIFY_CLI_THEME_TOKEN:
31
+ SHOPIFY_THEME_ACCESS_TOKEN:
32
32
  required: false
33
33
 
34
34
  jobs:
@@ -56,16 +56,16 @@ jobs:
56
56
  id: share
57
57
  env:
58
58
  SHOPIFY_STORE_URL: ${{ secrets.SHOPIFY_STORE_URL }}
59
- SHOPIFY_CLI_THEME_TOKEN: ${{ secrets.SHOPIFY_CLI_THEME_TOKEN }}
59
+ SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
60
60
  run: |
61
- if [ -z "$SHOPIFY_STORE_URL" ] || [ -z "$SHOPIFY_CLI_THEME_TOKEN" ]; then
61
+ if [ -z "$SHOPIFY_STORE_URL" ] || [ -z "$SHOPIFY_THEME_ACCESS_TOKEN" ]; then
62
62
  echo "Missing Shopify secrets."
63
63
  exit 1
64
64
  fi
65
65
 
66
66
  OUTPUT=$(shopify theme share \
67
67
  --store "$SHOPIFY_STORE_URL" \
68
- --password "$SHOPIFY_CLI_THEME_TOKEN" 2>&1)
68
+ --password "$SHOPIFY_THEME_ACCESS_TOKEN" 2>&1)
69
69
  STATUS=$?
70
70
 
71
71
  echo "$OUTPUT"