climaybe 1.5.1 → 1.5.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/README.md CHANGED
@@ -169,8 +169,8 @@ Enabled via `climaybe init` prompt (`Enable preview + cleanup workflows?`).
169
169
 
170
170
  | Workflow | Trigger | What it does |
171
171
  |----------|---------|-------------|
172
- | `pr-update.yml` | PR opened/synchronize/reopened | Shares draft theme, renames with `-PR<number>`, comments preview + customize URLs |
173
- | `pr-close.yml` | PR closed | Deletes matching preview themes and comments deleted count + names |
172
+ | `pr-update.yml` | PR opened/synchronize/reopened (base: main, staging, develop, staging-*, live-*) | Shares draft theme, renames with `-PR<number>`, comments preview + customize URLs; uses default store for main/staging/develop, or the store for staging-&lt;alias&gt;/live-&lt;alias&gt; |
173
+ | `pr-close.yml` | PR closed (same branch set) | Deletes matching preview themes and comments deleted count + names |
174
174
  | `reusable-share-theme.yml` | workflow_call | Shares Shopify draft theme and returns `theme_id` |
175
175
  | `reusable-rename-theme.yml` | workflow_call | Renames shared theme to include `PR<number>` (fails job on rename failure) |
176
176
  | `reusable-comment-on-pr.yml` | workflow_call | Posts preview comment including Customize URL |
@@ -232,7 +232,7 @@ Add the following secrets to your GitHub repository (or use **GitLab CI/CD varia
232
232
 
233
233
  **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.
234
234
 
235
- **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 workflows use the **default store** (from `config.default_store` or first store in `config.stores`), so set either the plain `SHOPIFY_*` secrets or the `_<ALIAS>` pair for that default store.
235
+ **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-&lt;alias&gt;** or **live-&lt;alias&gt;** they use that store’s credentials. Set either the plain `SHOPIFY_*` secrets or the `_<ALIAS>` pair for each store you use in preview.
236
236
 
237
237
  ## Directory Structure (Multi-store)
238
238
 
package/bin/version.txt CHANGED
@@ -1 +1 @@
1
- 1.5.1
1
+ 1.5.2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "climaybe",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
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": {
@@ -6,7 +6,7 @@ name: PR Close
6
6
  on:
7
7
  pull_request:
8
8
  types: [closed]
9
- branches: [main, staging, develop]
9
+ branches: [main, staging, develop, 'staging-*', 'live-*']
10
10
 
11
11
  jobs:
12
12
  extract-pr-number:
@@ -20,43 +20,50 @@ jobs:
20
20
  - name: Checkout code
21
21
  uses: actions/checkout@v4
22
22
 
23
- - name: Resolve default store alias
23
+ - name: Resolve store alias from base branch
24
24
  id: resolve
25
+ env:
26
+ BASE_REF: ${{ github.event.pull_request.base.ref }}
25
27
  run: |
26
- ALIAS=$(node -e "
27
- const fs = require('fs');
28
- const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
29
- const stores = pkg?.config?.stores || {};
30
- const defaultStoreRaw = pkg?.config?.default_store;
31
- const normalize = (v) => String(v || '')
32
- .toLowerCase()
33
- .replace(/^https?:\\/\\//, '')
34
- .replace(/\\/.*$/, '');
35
- const defaultStore = normalize(defaultStoreRaw);
36
- let alias = '';
37
- if (defaultStore) {
38
- for (const [k, d] of Object.entries(stores)) {
39
- if (normalize(d) === defaultStore) {
40
- alias = k;
41
- break;
28
+ if [[ -n "$BASE_REF" && ("$BASE_REF" == staging-* || "$BASE_REF" == live-*) ]]; then
29
+ ALIAS="${BASE_REF#staging-}"
30
+ ALIAS="${ALIAS#live-}"
31
+ else
32
+ ALIAS=$(node -e "
33
+ const fs = require('fs');
34
+ const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
35
+ const stores = pkg?.config?.stores || {};
36
+ const defaultStoreRaw = pkg?.config?.default_store;
37
+ const normalize = (v) => String(v || '')
38
+ .toLowerCase()
39
+ .replace(/^https?:\\/\\//, '')
40
+ .replace(/\\/.*$/, '');
41
+ const defaultStore = normalize(defaultStoreRaw);
42
+ let alias = '';
43
+ if (defaultStore) {
44
+ for (const [k, d] of Object.entries(stores)) {
45
+ if (normalize(d) === defaultStore) {
46
+ alias = k;
47
+ break;
48
+ }
42
49
  }
43
50
  }
44
- }
45
- if (!alias) {
46
- alias = Object.keys(stores)[0] || '';
47
- }
48
- if (!alias && defaultStore) {
49
- const subdomain = defaultStore.split('.')[0] || 'default';
50
- alias = subdomain.replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-') || 'default';
51
- }
52
- if (!alias) {
53
- alias = 'default';
54
- }
55
- process.stdout.write(alias);
56
- ")
51
+ if (!alias) {
52
+ alias = Object.keys(stores)[0] || '';
53
+ }
54
+ if (!alias && defaultStore) {
55
+ const subdomain = defaultStore.split('.')[0] || 'default';
56
+ alias = subdomain.replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-') || 'default';
57
+ }
58
+ if (!alias) {
59
+ alias = 'default';
60
+ }
61
+ process.stdout.write(alias);
62
+ ")
63
+ fi
57
64
 
58
65
  if [ -z "$ALIAS" ]; then
59
- echo "Could not resolve default store alias from package.json config."
66
+ echo "Could not resolve store alias from package.json config or base branch."
60
67
  exit 1
61
68
  fi
62
69
 
@@ -69,9 +76,8 @@ jobs:
69
76
  uses: ./.github/workflows/reusable-cleanup-themes.yml
70
77
  with:
71
78
  pr_number: ${{ needs.extract-pr-number.outputs.pr_number }}
72
- secrets:
73
- SHOPIFY_STORE_URL: ${{ secrets[format('SHOPIFY_STORE_URL_{0}', needs.resolve-store.outputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
74
- SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', needs.resolve-store.outputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
79
+ store_alias_secret: ${{ needs.resolve-store.outputs.store_alias_secret }}
80
+ secrets: inherit
75
81
 
76
82
  comment-on-pr:
77
83
  needs: cleanup-theme
@@ -7,7 +7,7 @@ name: PR Update
7
7
  on:
8
8
  pull_request:
9
9
  types: [opened, synchronize, reopened]
10
- branches: [main, staging, develop]
10
+ branches: [main, staging, develop, 'staging-*', 'live-*']
11
11
 
12
12
  jobs:
13
13
  extract-pr-number:
@@ -22,40 +22,48 @@ jobs:
22
22
  - name: Checkout code
23
23
  uses: actions/checkout@v4
24
24
 
25
- - name: Resolve default store alias
25
+ - name: Resolve store alias from base branch
26
26
  id: resolve
27
+ env:
28
+ BASE_REF: ${{ github.event.pull_request.base.ref }}
27
29
  run: |
28
- ALIAS=$(node -e "
29
- const fs = require('fs');
30
- const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
31
- const stores = pkg?.config?.stores || {};
32
- const defaultStoreRaw = pkg?.config?.default_store;
33
- const normalize = (v) => String(v || '')
34
- .toLowerCase()
35
- .replace(/^https?:\\/\\//, '')
36
- .replace(/\\/.*$/, '');
37
- const defaultStore = normalize(defaultStoreRaw);
38
- let alias = '';
39
- if (defaultStore) {
40
- for (const [k, d] of Object.entries(stores)) {
41
- if (normalize(d) === defaultStore) {
42
- alias = k;
43
- break;
30
+ # On staging-<alias> or live-<alias> PRs, use that store; otherwise default store
31
+ if [[ -n "$BASE_REF" && ("$BASE_REF" == staging-* || "$BASE_REF" == live-*) ]]; then
32
+ ALIAS="${BASE_REF#staging-}"
33
+ ALIAS="${ALIAS#live-}"
34
+ else
35
+ ALIAS=$(node -e "
36
+ const fs = require('fs');
37
+ const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
38
+ const stores = pkg?.config?.stores || {};
39
+ const defaultStoreRaw = pkg?.config?.default_store;
40
+ const normalize = (v) => String(v || '')
41
+ .toLowerCase()
42
+ .replace(/^https?:\\/\\//, '')
43
+ .replace(/\\/.*$/, '');
44
+ const defaultStore = normalize(defaultStoreRaw);
45
+ let alias = '';
46
+ if (defaultStore) {
47
+ for (const [k, d] of Object.entries(stores)) {
48
+ if (normalize(d) === defaultStore) {
49
+ alias = k;
50
+ break;
51
+ }
44
52
  }
45
53
  }
46
- }
47
- if (!alias) {
48
- alias = Object.keys(stores)[0] || '';
49
- }
50
- if (!alias && defaultStore) {
51
- const subdomain = defaultStore.split('.')[0] || 'default';
52
- alias = subdomain.replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-') || 'default';
53
- }
54
- if (!alias) {
55
- alias = 'default';
56
- }
57
- process.stdout.write(alias);
58
- ")
54
+ if (!alias) {
55
+ alias = Object.keys(stores)[0] || '';
56
+ }
57
+ if (!alias && defaultStore) {
58
+ const subdomain = defaultStore.split('.')[0] || 'default';
59
+ alias = subdomain.replace(/[^a-z0-9-]/g, '-').replace(/-+/g, '-') || 'default';
60
+ }
61
+ if (!alias) {
62
+ alias = 'default';
63
+ }
64
+ process.stdout.write(alias);
65
+ ")
66
+ fi
59
67
 
60
68
  if [ -z "$ALIAS" ]; then
61
69
  echo "Could not resolve default store alias from package.json config."
@@ -91,9 +99,8 @@ jobs:
91
99
  uses: ./.github/workflows/reusable-cleanup-themes.yml
92
100
  with:
93
101
  pr_number: ${{ needs.extract-pr-number.outputs.pr_number }}
94
- secrets:
95
- SHOPIFY_STORE_URL: ${{ secrets[format('SHOPIFY_STORE_URL_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
96
- SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
102
+ store_alias_secret: ${{ needs.validate-environment.outputs.store_alias_secret }}
103
+ secrets: inherit
97
104
 
98
105
  share-theme:
99
106
  needs: [validate-environment, extract-pr-number, cleanup-themes]
@@ -101,9 +108,8 @@ jobs:
101
108
  with:
102
109
  pr_number: ${{ needs.extract-pr-number.outputs.pr_number }}
103
110
  store_alias: ${{ needs.validate-environment.outputs.store_alias }}
104
- secrets:
105
- SHOPIFY_STORE_URL: ${{ secrets[format('SHOPIFY_STORE_URL_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
106
- SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
111
+ store_alias_secret: ${{ needs.validate-environment.outputs.store_alias_secret }}
112
+ secrets: inherit
107
113
 
108
114
  rename-theme:
109
115
  needs: [share-theme, extract-pr-number, validate-environment]
@@ -113,9 +119,8 @@ jobs:
113
119
  theme_name: ${{ needs.share-theme.outputs.theme_name }}
114
120
  pr_number: ${{ needs.extract-pr-number.outputs.pr_number }}
115
121
  store_alias: ${{ needs.validate-environment.outputs.store_alias }}
116
- secrets:
117
- SHOPIFY_STORE_URL: ${{ secrets[format('SHOPIFY_STORE_URL_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
118
- SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
122
+ store_alias_secret: ${{ needs.validate-environment.outputs.store_alias_secret }}
123
+ secrets: inherit
119
124
 
120
125
  comment-on-pr:
121
126
  needs: [share-theme, rename-theme, extract-pr-number, validate-environment]
@@ -125,5 +130,5 @@ jobs:
125
130
  share_output: ${{ needs.share-theme.outputs.share_output }}
126
131
  pr_number: ${{ needs.extract-pr-number.outputs.pr_number_unpadded }}
127
132
  store_alias: ${{ needs.validate-environment.outputs.store_alias }}
128
- secrets:
129
- SHOPIFY_STORE_URL: ${{ secrets[format('SHOPIFY_STORE_URL_{0}', needs.validate-environment.outputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
133
+ store_alias_secret: ${{ needs.validate-environment.outputs.store_alias_secret }}
134
+ secrets: inherit
@@ -7,6 +7,10 @@ on:
7
7
  required: true
8
8
  type: string
9
9
  description: "PR number to clean up themes for"
10
+ store_alias_secret:
11
+ required: false
12
+ type: string
13
+ description: "Upper snake-case alias for scoped secret (e.g. VOLDT_STAGING). If set, uses SHOPIFY_*_<this>; else uses SHOPIFY_STORE_URL / SHOPIFY_THEME_ACCESS_TOKEN."
10
14
  outputs:
11
15
  deleted_count:
12
16
  description: "Number of deleted themes"
@@ -28,8 +32,8 @@ jobs:
28
32
  - name: Cleanup PR preview themes
29
33
  id: cleanup
30
34
  env:
31
- SHOPIFY_STORE_URL: ${{ secrets.SHOPIFY_STORE_URL }}
32
- SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
35
+ SHOPIFY_STORE_URL: ${{ inputs.store_alias_secret && secrets[format('SHOPIFY_STORE_URL_{0}', inputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
36
+ SHOPIFY_THEME_ACCESS_TOKEN: ${{ inputs.store_alias_secret && secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', inputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
33
37
  PR_NUMBER: ${{ inputs.pr_number }}
34
38
  run: |
35
39
  if [ -z "$SHOPIFY_STORE_URL" ] || [ -z "$SHOPIFY_THEME_ACCESS_TOKEN" ]; then
@@ -18,9 +18,7 @@ on:
18
18
  store_alias_secret:
19
19
  required: false
20
20
  type: string
21
- secrets:
22
- SHOPIFY_STORE_URL:
23
- required: false
21
+ description: "Upper snake-case alias for scoped secret (e.g. VOLDT_STAGING). If set, uses SHOPIFY_STORE_URL_<this>; else uses SHOPIFY_STORE_URL."
24
22
 
25
23
  jobs:
26
24
  comment:
@@ -29,7 +27,7 @@ jobs:
29
27
  - name: Post preview comment
30
28
  uses: actions/github-script@v7
31
29
  env:
32
- SHOPIFY_STORE_URL: ${{ secrets.SHOPIFY_STORE_URL }}
30
+ SHOPIFY_STORE_URL: ${{ inputs.store_alias_secret && secrets[format('SHOPIFY_STORE_URL_{0}', inputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
33
31
  THEME_ID: ${{ inputs.theme_id }}
34
32
  SHARE_OUTPUT: ${{ inputs.share_output }}
35
33
  PR_NUMBER: ${{ inputs.pr_number }}
@@ -22,12 +22,7 @@ on:
22
22
  store_alias_secret:
23
23
  required: false
24
24
  type: string
25
- description: "Upper snake-case alias for scoped secret lookup"
26
- secrets:
27
- SHOPIFY_STORE_URL:
28
- required: false
29
- SHOPIFY_THEME_ACCESS_TOKEN:
30
- required: false
25
+ description: "Upper snake-case alias for scoped secret (e.g. VOLDT_STAGING). If set, uses SHOPIFY_*_<this>; else uses SHOPIFY_STORE_URL / SHOPIFY_THEME_ACCESS_TOKEN."
31
26
 
32
27
  jobs:
33
28
  rename:
@@ -38,8 +33,8 @@ jobs:
38
33
 
39
34
  - name: Rename theme
40
35
  env:
41
- SHOPIFY_STORE_URL: ${{ secrets.SHOPIFY_STORE_URL }}
42
- SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
36
+ SHOPIFY_STORE_URL: ${{ inputs.store_alias_secret && secrets[format('SHOPIFY_STORE_URL_{0}', inputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
37
+ SHOPIFY_THEME_ACCESS_TOKEN: ${{ inputs.store_alias_secret && secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', inputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
43
38
  THEME_ID: ${{ inputs.theme_id }}
44
39
  THEME_NAME: ${{ inputs.theme_name }}
45
40
  PR_NUMBER: ${{ inputs.pr_number }}
@@ -14,7 +14,7 @@ on:
14
14
  store_alias_secret:
15
15
  required: false
16
16
  type: string
17
- description: "Upper snake-case alias for scoped secret lookup"
17
+ description: "Upper snake-case alias for scoped secret (e.g. VOLDT_STAGING). If set, uses SHOPIFY_*_<this>; else uses SHOPIFY_STORE_URL / SHOPIFY_THEME_ACCESS_TOKEN."
18
18
  outputs:
19
19
  theme_id:
20
20
  description: "Shared theme ID"
@@ -25,11 +25,6 @@ on:
25
25
  share_output:
26
26
  description: "Raw share command output"
27
27
  value: ${{ jobs.share.outputs.share_output }}
28
- secrets:
29
- SHOPIFY_STORE_URL:
30
- required: false
31
- SHOPIFY_THEME_ACCESS_TOKEN:
32
- required: false
33
28
 
34
29
  jobs:
35
30
  share:
@@ -55,8 +50,8 @@ jobs:
55
50
  - name: Share theme
56
51
  id: share
57
52
  env:
58
- SHOPIFY_STORE_URL: ${{ secrets.SHOPIFY_STORE_URL }}
59
- SHOPIFY_THEME_ACCESS_TOKEN: ${{ secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
53
+ SHOPIFY_STORE_URL: ${{ inputs.store_alias_secret && secrets[format('SHOPIFY_STORE_URL_{0}', inputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
54
+ SHOPIFY_THEME_ACCESS_TOKEN: ${{ inputs.store_alias_secret && secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', inputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
60
55
  run: |
61
56
  if [ -z "$SHOPIFY_STORE_URL" ] || [ -z "$SHOPIFY_THEME_ACCESS_TOKEN" ]; then
62
57
  echo "Missing Shopify secrets."