climaybe 1.6.1 → 1.7.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 +4 -4
- package/bin/version.txt +1 -1
- package/package.json +3 -2
- package/src/lib/commit-tooling.js +6 -5
- package/src/lib/prompts.js +2 -2
- package/src/workflows/build/build-pipeline.yml +7 -0
- package/src/workflows/multi/main-to-staging-stores.yml +18 -0
- package/src/workflows/multi/multistore-hotfix-to-main.yml +48 -2
package/README.md
CHANGED
|
@@ -39,8 +39,8 @@ Interactive setup that configures your repo for CI/CD.
|
|
|
39
39
|
1. Prompts for your store URL (e.g., `voldt-staging.myshopify.com`)
|
|
40
40
|
2. Extracts subdomain as alias, lets you override
|
|
41
41
|
3. Asks if you want to add more stores
|
|
42
|
-
4. Asks whether to enable optional **preview + cleanup** workflows
|
|
43
|
-
5. Asks whether to enable optional **build + Lighthouse** workflows
|
|
42
|
+
4. Asks whether to enable optional **preview + cleanup** workflows (default: yes)
|
|
43
|
+
5. Asks whether to enable optional **build + Lighthouse** workflows (default: yes)
|
|
44
44
|
6. Asks whether to enable **commitlint + Husky** (enforce [conventional commits](https://www.conventionalcommits.org/) on `git commit`)
|
|
45
45
|
7. Asks whether to add **Cursor commit skill** to the project (`.cursor/skills/commit/SKILL.md`) for AI-assisted conventional commits
|
|
46
46
|
8. Based on store count, sets up **single-store** or **multi-store** mode
|
|
@@ -193,7 +193,7 @@ Direct pushes to `staging-<store>` or `live-<store>` are automatically synced ba
|
|
|
193
193
|
|
|
194
194
|
### Optional preview + cleanup package
|
|
195
195
|
|
|
196
|
-
Enabled via `climaybe init` prompt (`Enable preview + cleanup workflows
|
|
196
|
+
Enabled via `climaybe init` prompt (`Enable preview + cleanup workflows?`; default: yes).
|
|
197
197
|
|
|
198
198
|
| Workflow | Trigger | What it does |
|
|
199
199
|
|----------|---------|-------------|
|
|
@@ -207,7 +207,7 @@ Enabled via `climaybe init` prompt (`Enable preview + cleanup workflows?`).
|
|
|
207
207
|
|
|
208
208
|
### Optional build + Lighthouse package
|
|
209
209
|
|
|
210
|
-
Enabled via `climaybe init` prompt (`Enable build + Lighthouse workflows
|
|
210
|
+
Enabled via `climaybe init` prompt (`Enable build + Lighthouse workflows?`; default: yes).
|
|
211
211
|
|
|
212
212
|
| Workflow | Trigger | What it does |
|
|
213
213
|
|----------|---------|-------------|
|
package/bin/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.7.0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "climaybe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.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": {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
[
|
|
43
43
|
"@semantic-release/commit-analyzer",
|
|
44
44
|
{
|
|
45
|
+
"preset": "conventionalcommits",
|
|
45
46
|
"releaseRules": [
|
|
46
47
|
{
|
|
47
48
|
"type": "ci",
|
|
@@ -68,7 +69,7 @@
|
|
|
68
69
|
"package.json",
|
|
69
70
|
"package-lock.json"
|
|
70
71
|
],
|
|
71
|
-
"message": "chore(release): ${nextRelease.version}
|
|
72
|
+
"message": "chore(release): ${nextRelease.version}\n\n${nextRelease.notes}"
|
|
72
73
|
}
|
|
73
74
|
]
|
|
74
75
|
]
|
|
@@ -18,6 +18,7 @@ module.exports = {
|
|
|
18
18
|
'always',
|
|
19
19
|
['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore'],
|
|
20
20
|
],
|
|
21
|
+
'subject-empty': [0],
|
|
21
22
|
'header-max-length': [2, 'always', 100],
|
|
22
23
|
'body-max-line-length': [2, 'always', 200],
|
|
23
24
|
},
|
|
@@ -34,19 +35,19 @@ description: Groups working-tree changes into logical commits and commits them u
|
|
|
34
35
|
|
|
35
36
|
# Commit (group + conventional)
|
|
36
37
|
|
|
37
|
-
Group changes by purpose, then commit each group
|
|
38
|
+
Group changes by purpose, then commit each group. A conventional message is optional; when the user provides or wants one, use the format below so commitlint and semantic-release stay happy.
|
|
38
39
|
|
|
39
40
|
## Workflow
|
|
40
41
|
|
|
41
42
|
1. **Inspect** — Get full picture of changes (git status, git diff).
|
|
42
43
|
2. **Group** — Partition by type: feat, fix, docs, style, refactor, perf, test, build, ci, chore.
|
|
43
|
-
3. **Commit each group** — type(scope): subject, imperative, lowercase, no period, ≤100 chars.
|
|
44
|
-
4. **Validate** — commit-msg hook
|
|
44
|
+
3. **Commit each group** — If the user wants a message: type(scope): subject, imperative, lowercase, no period, ≤100 chars. If they don't, commit without message or with a minimal one (e.g. chore: wip).
|
|
45
|
+
4. **Validate** — commit-msg hook may reject invalid messages when a message is used.
|
|
45
46
|
|
|
46
|
-
## Message rules (commitlint)
|
|
47
|
+
## Message rules (commitlint, when a message is used)
|
|
47
48
|
|
|
48
49
|
- **Types:** feat, fix, docs, style, refactor, perf, test, build, ci, chore.
|
|
49
|
-
- **Header:** type(scope): subject — subject max 100 chars. Body lines max 200.
|
|
50
|
+
- **Header:** type(scope): subject — subject optional; max 100 chars when present. Body lines max 200.
|
|
50
51
|
- **Imperative, present tense:** "add feature" not "added feature".
|
|
51
52
|
|
|
52
53
|
## Examples
|
package/src/lib/prompts.js
CHANGED
|
@@ -128,7 +128,7 @@ export async function promptPreviewWorkflows() {
|
|
|
128
128
|
type: 'confirm',
|
|
129
129
|
name: 'enablePreviewWorkflows',
|
|
130
130
|
message: 'Enable preview + cleanup workflows?',
|
|
131
|
-
initial:
|
|
131
|
+
initial: true,
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
return !!enablePreviewWorkflows;
|
|
@@ -142,7 +142,7 @@ export async function promptBuildWorkflows() {
|
|
|
142
142
|
type: 'confirm',
|
|
143
143
|
name: 'enableBuildWorkflows',
|
|
144
144
|
message: 'Enable build + Lighthouse workflows?',
|
|
145
|
-
initial:
|
|
145
|
+
initial: true,
|
|
146
146
|
});
|
|
147
147
|
|
|
148
148
|
return !!enableBuildWorkflows;
|
|
@@ -8,6 +8,13 @@ on:
|
|
|
8
8
|
|
|
9
9
|
jobs:
|
|
10
10
|
build:
|
|
11
|
+
# Skip pure store-sync + hotfix-backport commits; those are infrastructure-only
|
|
12
|
+
# syncs from staging/live stores back to main and should not trigger heavy theme
|
|
13
|
+
# review / build pipelines.
|
|
14
|
+
if: >
|
|
15
|
+
!contains(github.event.head_commit.message, '[hotfix-backport]')
|
|
16
|
+
&& !contains(github.event.head_commit.message, '[stores-to-root]')
|
|
17
|
+
&& !contains(github.event.head_commit.message, '[root-to-stores]')
|
|
11
18
|
uses: ./.github/workflows/reusable-build.yml
|
|
12
19
|
|
|
13
20
|
lighthouse-gate:
|
|
@@ -5,12 +5,19 @@
|
|
|
5
5
|
# Runs on every push to main except pure store-sync commits ([stores-to-root], [root-to-stores]), so that:
|
|
6
6
|
# - Version-bump commits: staging-<alias> get the new version.
|
|
7
7
|
# - Hotfix backports (live or staging-<alias> → main): other staging-<alias> get those changes; the store that sent the hotfix is skipped (no need to merge back to the same branch).
|
|
8
|
+
#
|
|
9
|
+
# Also runs when Post-Merge Tag or Nightly Hotfix Tag complete: version-bump pushes with GITHUB_TOKEN,
|
|
10
|
+
# which does not trigger push-based workflows, so we explicitly run sync after those workflows finish.
|
|
8
11
|
|
|
9
12
|
name: Main to Staging Stores
|
|
10
13
|
|
|
11
14
|
on:
|
|
12
15
|
push:
|
|
13
16
|
branches: [main]
|
|
17
|
+
workflow_run:
|
|
18
|
+
workflows: ['Post-Merge Tag', 'Nightly Hotfix Tag']
|
|
19
|
+
types: [completed]
|
|
20
|
+
branches: [main]
|
|
14
21
|
|
|
15
22
|
# Prevent concurrent store pushes
|
|
16
23
|
concurrency:
|
|
@@ -19,8 +26,11 @@ concurrency:
|
|
|
19
26
|
|
|
20
27
|
jobs:
|
|
21
28
|
# Gate: skip only pure store-sync commits. When hotfix-backport, extract source branch alias so we skip merging back into that same store.
|
|
29
|
+
# When triggered by workflow_run (version-bump finished), run sync if the workflow succeeded.
|
|
22
30
|
gate:
|
|
23
31
|
runs-on: ubuntu-latest
|
|
32
|
+
# workflow_run: only sync when Post-Merge or Nightly Hotfix completed successfully (version-bump pushed)
|
|
33
|
+
if: github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success'
|
|
24
34
|
outputs:
|
|
25
35
|
should_run: ${{ steps.check.outputs.should_run }}
|
|
26
36
|
hotfix_source_alias: ${{ steps.check.outputs.hotfix_source_alias }}
|
|
@@ -28,6 +38,13 @@ jobs:
|
|
|
28
38
|
- name: Check commit message and hotfix source
|
|
29
39
|
id: check
|
|
30
40
|
run: |
|
|
41
|
+
if [ "${{ github.event_name }}" = "workflow_run" ]; then
|
|
42
|
+
echo "Triggered by workflow_run (version-bump finished); syncing main to staging."
|
|
43
|
+
echo "should_run=true" >> $GITHUB_OUTPUT
|
|
44
|
+
echo "hotfix_source_alias=" >> $GITHUB_OUTPUT
|
|
45
|
+
exit 0
|
|
46
|
+
fi
|
|
47
|
+
|
|
31
48
|
COMMIT_MSG="${{ github.event.head_commit.message }}"
|
|
32
49
|
|
|
33
50
|
if echo "$COMMIT_MSG" | grep -qE "\[skip-store-sync\]|\[stores-to-root\]|\[root-to-stores\]"; then
|
|
@@ -94,6 +111,7 @@ jobs:
|
|
|
94
111
|
|
|
95
112
|
- name: Sync main to staging-${{ matrix.store }} (root JSONs ignored)
|
|
96
113
|
run: |
|
|
114
|
+
git fetch origin main
|
|
97
115
|
BRANCH="staging-${{ matrix.store }}"
|
|
98
116
|
ALIAS="${{ matrix.store }}"
|
|
99
117
|
STORE_DIR="stores/${ALIAS}"
|
|
@@ -43,10 +43,35 @@ jobs:
|
|
|
43
43
|
fetch-depth: 0
|
|
44
44
|
token: ${{ secrets.GITHUB_TOKEN }}
|
|
45
45
|
|
|
46
|
+
- name: Resolve default store alias
|
|
47
|
+
id: default_store
|
|
48
|
+
run: |
|
|
49
|
+
DEFAULT_ALIAS=$(node -e "
|
|
50
|
+
const fs = require('fs');
|
|
51
|
+
const pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
52
|
+
const stores = pkg?.config?.stores || {};
|
|
53
|
+
const defaultStoreRaw = pkg?.config?.default_store;
|
|
54
|
+
const normalize = (v) => String(v || '').toLowerCase().replace(/^https?:\\/\\//, '').replace(/\/.*\$/, '');
|
|
55
|
+
const defaultStore = normalize(defaultStoreRaw);
|
|
56
|
+
let alias = '';
|
|
57
|
+
if (defaultStore) {
|
|
58
|
+
for (const [k, d] of Object.entries(stores)) {
|
|
59
|
+
if (normalize(d) === defaultStore) { alias = k; break; }
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (!alias) alias = Object.keys(stores)[0] || '';
|
|
63
|
+
process.stdout.write(alias);
|
|
64
|
+
")
|
|
65
|
+
echo "alias=$DEFAULT_ALIAS" >> $GITHUB_OUTPUT
|
|
66
|
+
echo "Default store alias: $DEFAULT_ALIAS"
|
|
67
|
+
|
|
46
68
|
- name: Check if backport is needed
|
|
47
69
|
id: check
|
|
48
70
|
run: |
|
|
49
71
|
SOURCE="${{ steps.ref.outputs.branch }}"
|
|
72
|
+
SOURCE_ALIAS="${SOURCE#staging-}"
|
|
73
|
+
SOURCE_ALIAS="${SOURCE_ALIAS#live-}"
|
|
74
|
+
DEFAULT_ALIAS="${{ steps.default_store.outputs.alias }}"
|
|
50
75
|
|
|
51
76
|
# Do not backport when this push was a merge FROM main (staging-* or live-*; would loop)
|
|
52
77
|
if [[ "$SOURCE" == staging-* || "$SOURCE" == live-* ]]; then
|
|
@@ -67,11 +92,19 @@ jobs:
|
|
|
67
92
|
exit 0
|
|
68
93
|
fi
|
|
69
94
|
|
|
70
|
-
|
|
71
|
-
|
|
95
|
+
# Default store: commits outside stores/ (root + code) trigger backport.
|
|
96
|
+
# Non-default store: only commits under stores/<alias>/ trigger backport; root JSON must not overwrite main.
|
|
97
|
+
if [ -n "$DEFAULT_ALIAS" ] && [ "$SOURCE_ALIAS" = "$DEFAULT_ALIAS" ]; then
|
|
98
|
+
COMMITS=$(git log --oneline ${MERGE_BASE}..origin/$SOURCE -- . ':!stores/' 2>/dev/null | \
|
|
99
|
+
grep -v "\[stores-to-root\]" | grep -v "\[root-to-stores\]" | grep -v "chore(release)" || true)
|
|
100
|
+
else
|
|
101
|
+
COMMITS=$(git log --oneline ${MERGE_BASE}..origin/$SOURCE -- "stores/${SOURCE_ALIAS}/" 2>/dev/null | \
|
|
102
|
+
grep -v "\[stores-to-root\]" | grep -v "\[root-to-stores\]" | grep -v "chore(release)" || true)
|
|
103
|
+
fi
|
|
72
104
|
|
|
73
105
|
if [ -n "$COMMITS" ]; then
|
|
74
106
|
echo "needs_backport=true" >> $GITHUB_OUTPUT
|
|
107
|
+
echo "is_default_store=$([ -n "$DEFAULT_ALIAS" ] && [ "$SOURCE_ALIAS" = "$DEFAULT_ALIAS" ] && echo true || echo false)" >> $GITHUB_OUTPUT
|
|
75
108
|
echo "Commits to sync to main:"
|
|
76
109
|
echo "$COMMITS"
|
|
77
110
|
else
|
|
@@ -83,6 +116,7 @@ jobs:
|
|
|
83
116
|
if: steps.check.outputs.needs_backport == 'true'
|
|
84
117
|
run: |
|
|
85
118
|
SOURCE="${{ steps.ref.outputs.branch }}"
|
|
119
|
+
IS_DEFAULT="${{ steps.check.outputs.is_default_store }}"
|
|
86
120
|
|
|
87
121
|
git config user.name "github-actions[bot]"
|
|
88
122
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
@@ -90,6 +124,18 @@ jobs:
|
|
|
90
124
|
git fetch origin
|
|
91
125
|
git checkout main
|
|
92
126
|
git merge origin/$SOURCE --no-ff -m "Merge $SOURCE into main [hotfix-backport]"
|
|
127
|
+
|
|
128
|
+
# Non-default store: do not bring root sync files to main (avoid conflicts).
|
|
129
|
+
# Keep only stores/<alias>/ and shared code; restore config/, templates/, sections/ from main.
|
|
130
|
+
if [ "$IS_DEFAULT" != "true" ]; then
|
|
131
|
+
git checkout origin/main -- config/ templates/ sections/ 2>/dev/null || true
|
|
132
|
+
git checkout HEAD -- config/settings_schema.json 2>/dev/null || true
|
|
133
|
+
if ! git diff --cached --quiet || ! git diff --quiet; then
|
|
134
|
+
git add config/ templates/ sections/
|
|
135
|
+
git commit -m "chore: keep root sync files from main for non-default store backport [hotfix-backport]"
|
|
136
|
+
fi
|
|
137
|
+
fi
|
|
138
|
+
|
|
93
139
|
git push origin main
|
|
94
140
|
|
|
95
141
|
echo "Synced $SOURCE → main"
|