climaybe 3.1.3 → 3.2.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
|
@@ -243,14 +243,14 @@ When enabled, builds are **resilient**:
|
|
|
243
243
|
- If `_scripts/*.js` or `_styles/main.css` are missing, the build workflow **skips** those steps and continues.
|
|
244
244
|
- `init` may offer to create entrypoints; **default answer is No**.
|
|
245
245
|
- Script bundling preserves comments/spacing and emits bundles only for root entry files (files imported by other top-level `_scripts/*.js` are inlined, not emitted separately).
|
|
246
|
-
-
|
|
246
|
+
- Script bundles are written to `assets/*.js` (readable by default; use `climaybe build-scripts --minify` if you want minified output).
|
|
247
247
|
- Live minified `assets/*` changes are intentionally excluded from hotfix backports to `main` (no branch-specific `.gitignore` split required).
|
|
248
248
|
|
|
249
249
|
Build workflows install deps with `npm ci` and run `npx --no-install climaybe build-scripts` plus `npx --no-install climaybe build`, so CI uses lockfile-pinned versions (no `@latest` drift).
|
|
250
250
|
|
|
251
251
|
| Workflow | Trigger | What it does |
|
|
252
252
|
|----------|---------|-------------|
|
|
253
|
-
| `build-pipeline.yml` | Push to any branch (ignores docs-only/tooling-only paths; see CI/CD reference) | Runs reusable build; Lighthouse only on branch **`staging`**, when a build ran, and secrets allow |
|
|
253
|
+
| `build-pipeline.yml` | Push to any branch (ignores docs-only/tooling-only paths; see CI/CD reference) | Runs reusable build; skips entirely on **`live-*`** when the pusher is a GitHub **`[bot]`** user; Lighthouse only on branch **`staging`**, when a build ran, and secrets allow |
|
|
254
254
|
| `reusable-build.yml` | workflow_call | Path-filtered `build-scripts` / Tailwind (`climaybe build`), then commits compiled assets when changed |
|
|
255
255
|
| `create-release.yml` | Push tag `v*`, or **workflow_run** after Post-Merge Tag / Nightly Hotfix Tag succeed on `main` | Builds release archive and creates GitHub Release notes from commits since the previous tag. It filters repetitive automation subjects (main→staging syncs, store/root sync chores, bot merge noise) before generating notes. If remaining subjects are low-signal and `GEMINI_API_KEY` exists, it uses Gemini to generate cleaner merchant-facing notes. Also covers tags created by workflows with `GITHUB_TOKEN`, which may not trigger tag-push workflows. |
|
|
256
256
|
|
package/bin/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.
|
|
1
|
+
3.2.0
|
package/package.json
CHANGED
package/src/lib/dev-runtime.js
CHANGED
|
@@ -232,8 +232,14 @@ export function serveAssets({ cwd = process.cwd(), includeThemeCheck = false } =
|
|
|
232
232
|
writeTaggedLine('tailwind', pc.blue, 'watching _styles/main.css -> assets/style.css');
|
|
233
233
|
}
|
|
234
234
|
|
|
235
|
-
// Optional dev MCP (non-blocking if missing)
|
|
236
|
-
|
|
235
|
+
// Optional dev MCP (non-blocking if missing). @shopify/dev-mcp pulls hydrogen (peers react-router 7.12)
|
|
236
|
+
// alongside react-router 7.13 — npm warns with ERESOLVE unless legacy peer resolution is enabled.
|
|
237
|
+
const devMcpEnv = { ...process.env, npm_config_legacy_peer_deps: 'true' };
|
|
238
|
+
const devMcp = spawnLogged('npx', ['-y', '@shopify/dev-mcp@latest'], {
|
|
239
|
+
name: 'dev-mcp',
|
|
240
|
+
cwd,
|
|
241
|
+
env: devMcpEnv,
|
|
242
|
+
});
|
|
237
243
|
|
|
238
244
|
const scriptsDir = join(cwd, '_scripts');
|
|
239
245
|
if (existsSync(scriptsDir)) {
|
|
@@ -22,11 +22,14 @@ jobs:
|
|
|
22
22
|
# Skip pure store-sync + hotfix-backport commits; those are infrastructure-only
|
|
23
23
|
# syncs from staging/live stores back to main and should not trigger heavy theme
|
|
24
24
|
# review / build pipelines.
|
|
25
|
+
# On live-<alias>, skip when the pusher is a GitHub bot (actor name contains "[bot]")
|
|
26
|
+
# so automated commits do not run script/Tailwind compilation.
|
|
25
27
|
if: >
|
|
26
28
|
!contains(github.event.head_commit.message, '[hotfix-backport]')
|
|
27
29
|
&& !contains(github.event.head_commit.message, '[stores-to-root]')
|
|
28
30
|
&& !contains(github.event.head_commit.message, '[root-to-stores]')
|
|
29
31
|
&& !(github.actor == 'github-actions[bot]' && contains(github.event.head_commit.message, 'chore(assets): update compiled javascript and css'))
|
|
32
|
+
&& !(startsWith(github.ref_name, 'live-') && contains(github.actor, '[bot]'))
|
|
30
33
|
uses: ./.github/workflows/reusable-build.yml
|
|
31
34
|
|
|
32
35
|
lighthouse-gate:
|
|
@@ -15,6 +15,7 @@ jobs:
|
|
|
15
15
|
resolve-store:
|
|
16
16
|
runs-on: ubuntu-latest
|
|
17
17
|
outputs:
|
|
18
|
+
store_alias: ${{ steps.resolve.outputs.alias }}
|
|
18
19
|
store_alias_secret: ${{ steps.resolve.outputs.alias_secret }}
|
|
19
20
|
steps:
|
|
20
21
|
- name: Checkout code
|
|
@@ -80,7 +81,7 @@ jobs:
|
|
|
80
81
|
secrets: inherit
|
|
81
82
|
|
|
82
83
|
comment-on-pr:
|
|
83
|
-
needs: cleanup-theme
|
|
84
|
+
needs: [cleanup-theme, resolve-store]
|
|
84
85
|
runs-on: ubuntu-latest
|
|
85
86
|
steps:
|
|
86
87
|
- name: Comment on PR about cleanup
|
|
@@ -88,6 +89,7 @@ jobs:
|
|
|
88
89
|
with:
|
|
89
90
|
script: |
|
|
90
91
|
const prNumber = context.payload.pull_request.number;
|
|
92
|
+
const storeAlias = '${{ needs.resolve-store.outputs.store_alias }}' || '';
|
|
91
93
|
const deletedCount = parseInt('${{ needs.cleanup-theme.outputs.deleted_count }}') || 0;
|
|
92
94
|
const deletedThemesRaw = `${{ needs.cleanup-theme.outputs.deleted_themes }}`.trim();
|
|
93
95
|
|
|
@@ -98,8 +100,9 @@ jobs:
|
|
|
98
100
|
'',
|
|
99
101
|
`**Branch:** ${context.payload.pull_request.head.ref}`,
|
|
100
102
|
`**PR Status:** ${context.payload.pull_request.state}`,
|
|
103
|
+
storeAlias ? `**Store:** ${storeAlias}` : null,
|
|
101
104
|
`**Deleted Themes:** ${deletedCount}`
|
|
102
|
-
];
|
|
105
|
+
].filter(Boolean);
|
|
103
106
|
|
|
104
107
|
if (deletedCount > 0 && deletedThemesRaw) {
|
|
105
108
|
const deletedThemeLines = deletedThemesRaw
|
|
@@ -18,6 +18,18 @@ on:
|
|
|
18
18
|
deleted_themes:
|
|
19
19
|
description: "Deleted theme names, one per line"
|
|
20
20
|
value: ${{ jobs.cleanup.outputs.deleted_themes }}
|
|
21
|
+
matched_count:
|
|
22
|
+
description: "Number of themes matching this PR (before delete)"
|
|
23
|
+
value: ${{ jobs.cleanup.outputs.matched_count }}
|
|
24
|
+
matched_themes:
|
|
25
|
+
description: "Matched theme names, one per line (before delete)"
|
|
26
|
+
value: ${{ jobs.cleanup.outputs.matched_themes }}
|
|
27
|
+
skipped_reason:
|
|
28
|
+
description: "Why cleanup was skipped (empty if not skipped)"
|
|
29
|
+
value: ${{ jobs.cleanup.outputs.skipped_reason }}
|
|
30
|
+
store_hint:
|
|
31
|
+
description: "Redacted store host (best-effort; may be masked)"
|
|
32
|
+
value: ${{ jobs.cleanup.outputs.store_hint }}
|
|
21
33
|
|
|
22
34
|
jobs:
|
|
23
35
|
cleanup:
|
|
@@ -25,6 +37,10 @@ jobs:
|
|
|
25
37
|
outputs:
|
|
26
38
|
deleted_count: ${{ steps.cleanup.outputs.deleted_count }}
|
|
27
39
|
deleted_themes: ${{ steps.cleanup.outputs.deleted_themes }}
|
|
40
|
+
matched_count: ${{ steps.cleanup.outputs.matched_count }}
|
|
41
|
+
matched_themes: ${{ steps.cleanup.outputs.matched_themes }}
|
|
42
|
+
skipped_reason: ${{ steps.cleanup.outputs.skipped_reason }}
|
|
43
|
+
store_hint: ${{ steps.cleanup.outputs.store_hint }}
|
|
28
44
|
steps:
|
|
29
45
|
- name: Install Shopify CLI
|
|
30
46
|
run: npm install -g @shopify/cli @shopify/theme
|
|
@@ -35,20 +51,86 @@ jobs:
|
|
|
35
51
|
SHOPIFY_STORE_URL: ${{ inputs.store_alias_secret && secrets[format('SHOPIFY_STORE_URL_{0}', inputs.store_alias_secret)] || secrets.SHOPIFY_STORE_URL }}
|
|
36
52
|
SHOPIFY_THEME_ACCESS_TOKEN: ${{ inputs.store_alias_secret && secrets[format('SHOPIFY_THEME_ACCESS_TOKEN_{0}', inputs.store_alias_secret)] || secrets.SHOPIFY_THEME_ACCESS_TOKEN }}
|
|
37
53
|
PR_NUMBER: ${{ inputs.pr_number }}
|
|
54
|
+
STORE_ALIAS_SECRET: ${{ inputs.store_alias_secret }}
|
|
38
55
|
run: |
|
|
39
|
-
|
|
56
|
+
set -euo pipefail
|
|
57
|
+
|
|
58
|
+
STORE_HOST="${SHOPIFY_STORE_URL#https://}"
|
|
59
|
+
STORE_HOST="${STORE_HOST#http://}"
|
|
60
|
+
STORE_HOST="${STORE_HOST%%/*}"
|
|
61
|
+
|
|
62
|
+
{
|
|
63
|
+
echo "### 🧹 Cleanup preview themes"
|
|
64
|
+
echo ""
|
|
65
|
+
echo "- **PR**: \`${PR_NUMBER}\`"
|
|
66
|
+
echo "- **Store alias secret**: \`${STORE_ALIAS_SECRET:-<default>}\`"
|
|
67
|
+
echo "- **Store (host)**: \`${STORE_HOST:-<missing>}\`"
|
|
68
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
69
|
+
|
|
70
|
+
echo "PR=${PR_NUMBER}"
|
|
71
|
+
echo "Store alias secret=${STORE_ALIAS_SECRET:-<default>}"
|
|
72
|
+
echo "Store host=${STORE_HOST:-<missing>}"
|
|
73
|
+
|
|
74
|
+
if [ -z "${SHOPIFY_STORE_URL:-}" ] || [ -z "${SHOPIFY_THEME_ACCESS_TOKEN:-}" ]; then
|
|
40
75
|
echo "Missing Shopify secrets; skipping theme cleanup (no themes deleted)."
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
76
|
+
|
|
77
|
+
{
|
|
78
|
+
echo ""
|
|
79
|
+
echo "#### Result"
|
|
80
|
+
echo "- **Skipped**: yes (missing Shopify secrets)"
|
|
81
|
+
echo "- **Matched themes**: 0"
|
|
82
|
+
echo "- **Deleted themes**: 0"
|
|
83
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
84
|
+
|
|
85
|
+
echo "skipped_reason=missing_secrets" >> "$GITHUB_OUTPUT"
|
|
86
|
+
echo "store_hint=${STORE_HOST:-}" >> "$GITHUB_OUTPUT"
|
|
87
|
+
echo "matched_count=0" >> "$GITHUB_OUTPUT"
|
|
88
|
+
echo "matched_themes<<MATCHED_EOF" >> "$GITHUB_OUTPUT"
|
|
89
|
+
echo "MATCHED_EOF" >> "$GITHUB_OUTPUT"
|
|
90
|
+
echo "deleted_count=0" >> "$GITHUB_OUTPUT"
|
|
91
|
+
echo "deleted_themes<<DELETED_EOF" >> "$GITHUB_OUTPUT"
|
|
92
|
+
echo "DELETED_EOF" >> "$GITHUB_OUTPUT"
|
|
44
93
|
exit 0
|
|
45
94
|
fi
|
|
46
95
|
|
|
47
|
-
|
|
96
|
+
echo "store_hint=${STORE_HOST:-}" >> "$GITHUB_OUTPUT"
|
|
97
|
+
echo "skipped_reason=" >> "$GITHUB_OUTPUT"
|
|
98
|
+
|
|
99
|
+
THEME_LIST_ERR="$(mktemp)"
|
|
100
|
+
THEME_LIST_JSON="$(mktemp)"
|
|
101
|
+
|
|
102
|
+
if ! shopify theme list \
|
|
48
103
|
--store "$SHOPIFY_STORE_URL" \
|
|
49
104
|
--password "$SHOPIFY_THEME_ACCESS_TOKEN" \
|
|
50
|
-
--json
|
|
105
|
+
--json > "$THEME_LIST_JSON" 2> "$THEME_LIST_ERR"; then
|
|
106
|
+
echo "shopify theme list failed."
|
|
107
|
+
echo "Error output (first 120 lines):"
|
|
108
|
+
sed -n '1,120p' "$THEME_LIST_ERR" || true
|
|
109
|
+
{
|
|
110
|
+
echo ""
|
|
111
|
+
echo "#### Result"
|
|
112
|
+
echo "- **Error**: \`shopify theme list\` failed"
|
|
113
|
+
echo ""
|
|
114
|
+
echo "<details><summary>CLI error output</summary>"
|
|
115
|
+
echo ""
|
|
116
|
+
echo "\`\`\`"
|
|
117
|
+
sed -n '1,240p' "$THEME_LIST_ERR" || true
|
|
118
|
+
echo "\`\`\`"
|
|
119
|
+
echo "</details>"
|
|
120
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
121
|
+
exit 1
|
|
122
|
+
fi
|
|
123
|
+
|
|
124
|
+
# Defensive: if CLI returned empty, treat as an empty list (instead of jq failure).
|
|
125
|
+
if [ ! -s "$THEME_LIST_JSON" ]; then
|
|
126
|
+
echo "[]" > "$THEME_LIST_JSON"
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
ALL_COUNT="$(jq -r 'length' "$THEME_LIST_JSON" 2>/dev/null || echo "0")"
|
|
130
|
+
echo "Found ${ALL_COUNT} total theme(s) on store."
|
|
51
131
|
|
|
132
|
+
MATCHED_COUNT=0
|
|
133
|
+
MATCHED_THEMES=""
|
|
52
134
|
DELETED_COUNT=0
|
|
53
135
|
DELETED_THEMES=""
|
|
54
136
|
|
|
@@ -57,22 +139,61 @@ jobs:
|
|
|
57
139
|
[ -z "$THEME_NAME" ] && continue
|
|
58
140
|
|
|
59
141
|
if printf "%s" "$THEME_NAME" | grep -q "PR${PR_NUMBER}"; then
|
|
142
|
+
MATCHED_COUNT=$((MATCHED_COUNT + 1))
|
|
143
|
+
MATCHED_THEMES="${MATCHED_THEMES}${THEME_NAME}\n"
|
|
144
|
+
echo "Matched: $THEME_NAME ($THEME_ID)"
|
|
145
|
+
|
|
146
|
+
DELETE_ERR="$(mktemp)"
|
|
60
147
|
if shopify theme delete \
|
|
61
148
|
--store "$SHOPIFY_STORE_URL" \
|
|
62
149
|
--password "$SHOPIFY_THEME_ACCESS_TOKEN" \
|
|
63
150
|
--force \
|
|
64
|
-
--theme "$THEME_ID"
|
|
151
|
+
--theme "$THEME_ID" 1>/dev/null 2>"$DELETE_ERR"; then
|
|
65
152
|
DELETED_COUNT=$((DELETED_COUNT + 1))
|
|
66
153
|
DELETED_THEMES="${DELETED_THEMES}${THEME_NAME}\n"
|
|
154
|
+
echo "Deleted: $THEME_NAME ($THEME_ID)"
|
|
67
155
|
else
|
|
68
|
-
echo "Failed to delete
|
|
156
|
+
echo "Failed to delete: $THEME_NAME ($THEME_ID)"
|
|
157
|
+
echo "Delete error output (first 80 lines):"
|
|
158
|
+
sed -n '1,80p' "$DELETE_ERR" || true
|
|
69
159
|
fi
|
|
70
160
|
fi
|
|
71
|
-
done < <(
|
|
161
|
+
done < <(jq -r '.[] | [.id, .name] | @tsv' "$THEME_LIST_JSON")
|
|
162
|
+
|
|
163
|
+
{
|
|
164
|
+
echo ""
|
|
165
|
+
echo "#### Result"
|
|
166
|
+
echo "- **Total themes on store**: ${ALL_COUNT}"
|
|
167
|
+
echo "- **Matched (PR${PR_NUMBER})**: ${MATCHED_COUNT}"
|
|
168
|
+
echo "- **Deleted**: ${DELETED_COUNT}"
|
|
169
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
170
|
+
|
|
171
|
+
if [ "$MATCHED_COUNT" -gt 0 ]; then
|
|
172
|
+
{
|
|
173
|
+
echo ""
|
|
174
|
+
echo "#### Matched theme names"
|
|
175
|
+
printf "%b" "$MATCHED_THEMES" | sed -e 's/^/- /'
|
|
176
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
177
|
+
fi
|
|
178
|
+
|
|
179
|
+
if [ "$DELETED_COUNT" -gt 0 ]; then
|
|
180
|
+
{
|
|
181
|
+
echo ""
|
|
182
|
+
echo "#### Deleted theme names"
|
|
183
|
+
printf "%b" "$DELETED_THEMES" | sed -e 's/^/- /'
|
|
184
|
+
} >> "$GITHUB_STEP_SUMMARY"
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
echo "matched_count=$MATCHED_COUNT" >> "$GITHUB_OUTPUT"
|
|
188
|
+
{
|
|
189
|
+
echo "matched_themes<<MATCHED_EOF"
|
|
190
|
+
printf "%b" "$MATCHED_THEMES"
|
|
191
|
+
echo "MATCHED_EOF"
|
|
192
|
+
} >> "$GITHUB_OUTPUT"
|
|
72
193
|
|
|
73
|
-
echo "deleted_count=$DELETED_COUNT" >> $GITHUB_OUTPUT
|
|
194
|
+
echo "deleted_count=$DELETED_COUNT" >> "$GITHUB_OUTPUT"
|
|
74
195
|
{
|
|
75
196
|
echo "deleted_themes<<DELETED_EOF"
|
|
76
197
|
printf "%b" "$DELETED_THEMES"
|
|
77
198
|
echo "DELETED_EOF"
|
|
78
|
-
} >> $GITHUB_OUTPUT
|
|
199
|
+
} >> "$GITHUB_OUTPUT"
|