climaybe 2.4.0 → 2.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/README.md +4 -4
- package/bin/version.txt +1 -1
- package/package.json +1 -1
- package/src/workflows/build/build-scripts.js +35 -14
- package/src/workflows/multi/main-to-staging-stores.yml +8 -2
- package/src/workflows/multi/multistore-hotfix-to-main.yml +9 -0
- package/src/workflows/single/nightly-hotfix.yml +6 -0
package/README.md
CHANGED
|
@@ -205,17 +205,17 @@ Direct pushes to `staging-<store>` or `live-<store>` are automatically synced ba
|
|
|
205
205
|
|----------|---------|-------------|
|
|
206
206
|
| `release-pr-check.yml` | PR from `staging` to `main` | Finds latest tag on main, AI changelog to PR head, creates pre-release patch tag (e.g. v3.1.13) to lock state; posts changelog comment |
|
|
207
207
|
| `post-merge-tag.yml` | Push to `main` (merged PR) | Staging→main only: minor bump from latest tag (e.g. v3.1.13 → v3.2.0). No version in PR title |
|
|
208
|
-
| `nightly-hotfix.yml` | Cron 02:00 US Eastern | Collects commits since latest tag (incl. hotfix backports), AI changelog, patch bump and tag |
|
|
208
|
+
| `nightly-hotfix.yml` | Cron 02:00 US Eastern | Collects commits since latest tag (incl. hotfix backports), ignores no-op/empty-tree commits, generates AI changelog, patch bump and tag |
|
|
209
209
|
|
|
210
210
|
### Multi-store (additional)
|
|
211
211
|
|
|
212
212
|
| Workflow | Trigger | What it does |
|
|
213
213
|
|----------|---------|-------------|
|
|
214
|
-
| `main-to-staging-stores.yml` (main-to-staging-<store>) | Push to `main` | Merges main into each `staging-<alias>`; root JSONs ignored. For hotfix-backport: if source is `staging-<alias>`, that same staging branch is skipped; if source is `live-<alias>`, `staging-<alias>` is also synced. Skips only on pure store-sync. |
|
|
214
|
+
| `main-to-staging-stores.yml` (main-to-staging-<store>) | Push to `main` | Merges main into each `staging-<alias>`; root JSONs ignored. Skips no-op sync when branch tree already matches main. For hotfix-backport: if source is `staging-<alias>`, that same staging branch is skipped; if source is `live-<alias>`, `staging-<alias>` is also synced. Skips only on pure store-sync. |
|
|
215
215
|
| `stores-to-root.yml` | Push to `staging-*` | From main merge: stores→root. From elsewhere (e.g. Shopify): root→stores |
|
|
216
216
|
| `pr-to-live.yml` | After stores-to-root | Opens PR from `staging-<alias>` to `live-<alias>` |
|
|
217
217
|
| `root-to-stores.yml` | Push to `live-*` | From main merge: stores→root. From elsewhere: root→stores (same as stores-to-root on staging-*) |
|
|
218
|
-
| `multistore-hotfix-to-main.yml` | Push to `staging-*` or `live-*` (and after root-to-stores) | Merges store branch into main (no PR). Skips when push is a merge from main (avoids loop) |
|
|
218
|
+
| `multistore-hotfix-to-main.yml` | Push to `staging-*` or `live-*` (and after root-to-stores) | Merges store branch into main (no PR). Skips when push is a merge from main (avoids loop) and skips no-op backports when source and main trees are identical |
|
|
219
219
|
|
|
220
220
|
### Optional preview + cleanup package
|
|
221
221
|
|
|
@@ -268,7 +268,7 @@ You can install/update this later with:
|
|
|
268
268
|
- **Version format**: Always three-part (e.g. `v3.2.0`). No version in code or PR title; the system infers from tags.
|
|
269
269
|
- **No tags yet?** The system uses `theme_version` from `config/settings_schema.json` (`theme_info`), creates that tag on main (e.g. `v1.0.0`), and continues from there.
|
|
270
270
|
- **Staging → main**: On PR, a pre-release patch tag (e.g. v3.1.13) locks the current minor line; on merge, **minor** bump (e.g. v3.1.13 → v3.2.0).
|
|
271
|
-
- **Non-staging to main** (hotfix backports, direct commits): **Patch** bump only, via **nightly workflow** at 02:00 US Eastern (not at commit time).
|
|
271
|
+
- **Non-staging to main** (hotfix backports, direct commits): **Patch** bump only, via **nightly workflow** at 02:00 US Eastern (not at commit time). No-op/empty-tree commits are ignored.
|
|
272
272
|
- **Version bump runs only on main** (post-merge-tag and nightly-hotfix). Main-to-staging-stores merges main into each `staging-<alias>` on every push (version bumps and hotfixes). For hotfix-backport, only a `staging-<alias> -> main` source skips syncing back to the same staging branch; a `live-<alias> -> main` source still syncs into `staging-<alias>`.
|
|
273
273
|
- Version bumps update `config/settings_schema.json` and, when present, `package.json` `version`.
|
|
274
274
|
- **Safety**: The version-bump workflow fails if the new tag would not be **strictly higher** than the latest merged release tag (semver), so the release line cannot step backward.
|
package/bin/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.4.
|
|
1
|
+
2.4.2
|
package/package.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
|
-
const
|
|
3
|
+
const CLIMAYBE_DIR = process.cwd();
|
|
4
|
+
const SCRIPTS_DIR = path.join(CLIMAYBE_DIR, '_scripts');
|
|
4
5
|
|
|
5
6
|
function extractImports(content) {
|
|
6
7
|
const imports = [];
|
|
7
|
-
|
|
8
|
-
|
|
8
|
+
// Supports compact imports (import{a}from"./x"), multiline forms,
|
|
9
|
+
// and import attributes (with { type: "json" }).
|
|
10
|
+
const fromImportRegex =
|
|
11
|
+
/(^|\n)\s*import(?:\s+type)?\s*[\s\S]*?\s*\bfrom\b\s*['"]([^'"]+)['"](?:\s+with\s*\{[\s\S]*?\})?\s*;?/g;
|
|
12
|
+
const sideEffectImportRegex = /(^|\n)\s*import\s*['"]([^'"]+)['"](?:\s+with\s*\{[\s\S]*?\})?\s*;?/g;
|
|
9
13
|
let match;
|
|
10
14
|
|
|
11
15
|
while ((match = fromImportRegex.exec(content)) !== null) {
|
|
@@ -18,6 +22,27 @@ function extractImports(content) {
|
|
|
18
22
|
return imports;
|
|
19
23
|
}
|
|
20
24
|
|
|
25
|
+
function stripModuleSyntax(content) {
|
|
26
|
+
// Remove import statements (including multiline/compact forms and import attributes).
|
|
27
|
+
let cleaned = content.replace(
|
|
28
|
+
/(^|\n)\s*import(?:\s+type)?\s*[\s\S]*?\s*\bfrom\b\s*['"][^'"]+['"](?:\s+with\s*\{[\s\S]*?\})?\s*;?/g,
|
|
29
|
+
'$1'
|
|
30
|
+
);
|
|
31
|
+
cleaned = cleaned.replace(/(^|\n)\s*import\s*['"][^'"]+['"](?:\s+with\s*\{[\s\S]*?\})?\s*;?/g, '$1');
|
|
32
|
+
|
|
33
|
+
// Fallback: ensure no standalone import declarations leak into bundle output.
|
|
34
|
+
cleaned = cleaned.replace(/^[ \t]*import\s*['"][^'"]+['"][ \t]*;?[ \t]*$/gm, '');
|
|
35
|
+
cleaned = cleaned.replace(
|
|
36
|
+
/^[ \t]*import(?:\s+type)?[ \t]*[^;\n\r]*\bfrom\b[ \t]*['"][^'"]+['"][ \t]*(?:with[ \t]*\{[^}\n\r]*\})?[ \t]*;?[ \t]*$/gm,
|
|
37
|
+
''
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
cleaned = cleaned.replace(/^\s*export\s+default\s+/gm, '');
|
|
41
|
+
cleaned = cleaned.replace(/^\s*export\s+\{[^}]*\}\s*;?\s*$/gm, '');
|
|
42
|
+
cleaned = cleaned.replace(/^\s*export\s+(?=(const|let|var|function|class)\b)/gm, '');
|
|
43
|
+
return cleaned;
|
|
44
|
+
}
|
|
45
|
+
|
|
21
46
|
function processScriptFile(filePath, processedFiles = new Set()) {
|
|
22
47
|
if (processedFiles.has(filePath)) {
|
|
23
48
|
return '';
|
|
@@ -25,7 +50,7 @@ function processScriptFile(filePath, processedFiles = new Set()) {
|
|
|
25
50
|
|
|
26
51
|
processedFiles.add(filePath);
|
|
27
52
|
|
|
28
|
-
const fullPath = path.join(
|
|
53
|
+
const fullPath = path.join(SCRIPTS_DIR, filePath);
|
|
29
54
|
|
|
30
55
|
if (!fs.existsSync(fullPath)) {
|
|
31
56
|
console.warn(`Warning: File ${filePath} not found`);
|
|
@@ -40,12 +65,7 @@ function processScriptFile(filePath, processedFiles = new Set()) {
|
|
|
40
65
|
importedContent += processScriptFile(importPath, processedFiles);
|
|
41
66
|
}
|
|
42
67
|
|
|
43
|
-
|
|
44
|
-
content = content.replace(/(^|\n)\s*import\s+[\s\S]*?\s+from\s+['"][^'"]+['"]\s*;?/g, '$1');
|
|
45
|
-
content = content.replace(/(^|\n)\s*import\s+['"][^'"]+['"]\s*;?/g, '$1');
|
|
46
|
-
content = content.replace(/^\s*export\s+default\s+/gm, '');
|
|
47
|
-
content = content.replace(/^\s*export\s+\{[^}]*\}\s*;?\s*$/gm, '');
|
|
48
|
-
content = content.replace(/^\s*export\s+(?=(const|let|var|function|class)\b)/gm, '');
|
|
68
|
+
content = stripModuleSyntax(content);
|
|
49
69
|
|
|
50
70
|
if (process.env.NODE_ENV === 'production') {
|
|
51
71
|
content = content.replace(/\/\*\*[\s\S]*?\*\//g, '');
|
|
@@ -61,12 +81,13 @@ function buildScripts() {
|
|
|
61
81
|
try {
|
|
62
82
|
if (global.gc) global.gc();
|
|
63
83
|
|
|
64
|
-
const mainPath = path.join(
|
|
84
|
+
const mainPath = path.join(SCRIPTS_DIR, 'main.js');
|
|
65
85
|
fs.readFileSync(mainPath, 'utf8');
|
|
66
86
|
|
|
67
87
|
const processedFiles = new Set();
|
|
68
|
-
|
|
69
|
-
|
|
88
|
+
let finalContent = processScriptFile('main.js', processedFiles);
|
|
89
|
+
finalContent = stripModuleSyntax(finalContent);
|
|
90
|
+
const outputPath = path.join(CLIMAYBE_DIR, 'assets', 'index.js');
|
|
70
91
|
fs.writeFileSync(outputPath, finalContent.trim() + '\n');
|
|
71
92
|
|
|
72
93
|
const fileCount = processedFiles.size;
|
|
@@ -83,4 +104,4 @@ if (require.main === module) {
|
|
|
83
104
|
buildScripts();
|
|
84
105
|
}
|
|
85
106
|
|
|
86
|
-
module.exports = { buildScripts };
|
|
107
|
+
module.exports = { buildScripts };
|
|
@@ -133,8 +133,14 @@ jobs:
|
|
|
133
133
|
|
|
134
134
|
git fetch origin "$BRANCH"
|
|
135
135
|
git checkout "$BRANCH"
|
|
136
|
-
git
|
|
137
|
-
|
|
136
|
+
BRANCH_TREE=$(git rev-parse HEAD^{tree} 2>/dev/null || echo "")
|
|
137
|
+
MAIN_TREE=$(git rev-parse origin/main^{tree} 2>/dev/null || echo "")
|
|
138
|
+
if [ -n "$BRANCH_TREE" ] && [ "$BRANCH_TREE" = "$MAIN_TREE" ]; then
|
|
139
|
+
echo "$BRANCH is already in sync with main (no-op), skipping."
|
|
140
|
+
exit 0
|
|
141
|
+
fi
|
|
142
|
+
|
|
143
|
+
if ! git merge origin/main --no-ff -m "Sync main → $BRANCH"; then
|
|
138
144
|
echo "Merge had conflicts; aborting. Manual resolution may be needed."
|
|
139
145
|
git merge --abort 2>/dev/null || true
|
|
140
146
|
exit 1
|
|
@@ -103,6 +103,15 @@ jobs:
|
|
|
103
103
|
fi
|
|
104
104
|
|
|
105
105
|
if [ -n "$COMMITS" ]; then
|
|
106
|
+
# Skip no-op history merges when source and main already have identical content.
|
|
107
|
+
MAIN_TREE=$(git rev-parse origin/main^{tree} 2>/dev/null || echo "")
|
|
108
|
+
SOURCE_TREE=$(git rev-parse origin/$SOURCE^{tree} 2>/dev/null || echo "")
|
|
109
|
+
if [ -n "$MAIN_TREE" ] && [ "$MAIN_TREE" = "$SOURCE_TREE" ]; then
|
|
110
|
+
echo "needs_backport=false" >> $GITHUB_OUTPUT
|
|
111
|
+
echo "No-op backport: origin/main and origin/$SOURCE trees are identical."
|
|
112
|
+
exit 0
|
|
113
|
+
fi
|
|
114
|
+
|
|
106
115
|
echo "needs_backport=true" >> $GITHUB_OUTPUT
|
|
107
116
|
echo "is_default_store=$([ -n "$DEFAULT_ALIAS" ] && [ "$SOURCE_ALIAS" = "$DEFAULT_ALIAS" ] && echo true || echo false)" >> $GITHUB_OUTPUT
|
|
108
117
|
echo "Commits to sync to main:"
|
|
@@ -90,6 +90,12 @@ jobs:
|
|
|
90
90
|
while IFS=$'\t' read -r SHA SUBJECT; do
|
|
91
91
|
[ -z "$SHA" ] && continue
|
|
92
92
|
|
|
93
|
+
# Ignore metadata-only commits (e.g. no-op merges) so they do not trigger bumps.
|
|
94
|
+
if git diff-tree --quiet --no-commit-id -r "$SHA"; then
|
|
95
|
+
echo "Skipping no-op commit (empty tree diff): $SHA"
|
|
96
|
+
continue
|
|
97
|
+
fi
|
|
98
|
+
|
|
93
99
|
HEAD_REF=$(gh api \
|
|
94
100
|
repos/${{ github.repository }}/commits/$SHA/pulls \
|
|
95
101
|
--jq '.[0].head.ref // ""' 2>/dev/null || true)
|