climaybe 1.5.2 → 1.6.1
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 +45 -18
- package/bin/version.txt +1 -1
- package/package.json +16 -2
- package/src/commands/add-cursor-skill.js +17 -0
- package/src/commands/init.js +23 -0
- package/src/commands/setup-commitlint.js +21 -0
- package/src/index.js +12 -0
- package/src/lib/commit-tooling.js +110 -0
- package/src/lib/config.js +16 -0
- package/src/lib/prompts.js +28 -0
- package/src/workflows/build/build-pipeline.yml +15 -15
- package/src/workflows/multi/main-to-staging-stores.yml +66 -79
- package/src/workflows/multi/multistore-hotfix-to-main.yml +15 -4
- package/src/workflows/multi/root-to-stores.yml +63 -16
- package/src/workflows/multi/stores-to-root.yml +77 -43
- package/src/workflows/shared/version-bump.yml +39 -9
- package/src/workflows/single/nightly-hotfix.yml +43 -23
- package/src/workflows/single/post-merge-tag.yml +67 -89
- package/src/workflows/single/release-pr-check.yml +30 -4
package/README.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
Shopify CI/CD CLI — scaffolds GitHub Actions workflows, branch strategy, and store config for single-store and multi-store theme repositories.
|
|
4
4
|
|
|
5
|
+
**Commit linting and AI-assisted commits are available as optional setup steps:**
|
|
6
|
+
|
|
7
|
+
- **Conventional commit linting:** During `climaybe init`, you can choose to automatically install and configure [commitlint](https://commitlint.js.org/) and [Husky](https://typicode.github.io/husky) to enforce [Conventional Commits](https://www.conventionalcommits.org/) in your theme repository.
|
|
8
|
+
- **Cursor AI commit skill:** You can also opt-in to installing the [Cursor AI commit skill](https://cursor.so/) (`.cursor/skills/commit/SKILL.md`) for AI-assisted, conventional commit message support in your project.
|
|
9
|
+
|
|
10
|
+
Both options streamline commit message quality and team workflows but are fully optional during setup.
|
|
11
|
+
|
|
5
12
|
## Install
|
|
6
13
|
|
|
7
14
|
Install in your theme repo (project-only, no global install):
|
|
@@ -34,10 +41,13 @@ Interactive setup that configures your repo for CI/CD.
|
|
|
34
41
|
3. Asks if you want to add more stores
|
|
35
42
|
4. Asks whether to enable optional **preview + cleanup** workflows
|
|
36
43
|
5. Asks whether to enable optional **build + Lighthouse** workflows
|
|
37
|
-
6.
|
|
38
|
-
7.
|
|
39
|
-
8.
|
|
40
|
-
9.
|
|
44
|
+
6. Asks whether to enable **commitlint + Husky** (enforce [conventional commits](https://www.conventionalcommits.org/) on `git commit`)
|
|
45
|
+
7. Asks whether to add **Cursor commit skill** to the project (`.cursor/skills/commit/SKILL.md`) for AI-assisted conventional commits
|
|
46
|
+
8. Based on store count, sets up **single-store** or **multi-store** mode
|
|
47
|
+
9. Writes `package.json` config
|
|
48
|
+
10. Scaffolds GitHub Actions workflows
|
|
49
|
+
11. Creates git branches and store directories (multi-store)
|
|
50
|
+
12. Optionally installs commitlint, Husky, and the Cursor skill
|
|
41
51
|
|
|
42
52
|
### `climaybe add-store`
|
|
43
53
|
|
|
@@ -91,6 +101,22 @@ npx climaybe update-workflows
|
|
|
91
101
|
|
|
92
102
|
Useful after updating the CLI to get the latest workflow improvements.
|
|
93
103
|
|
|
104
|
+
### `climaybe setup-commitlint`
|
|
105
|
+
|
|
106
|
+
Set up **only** commitlint + Husky (conventional commits enforced on `git commit`). Use this if you skipped it at init or want to add it later.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npx climaybe setup-commitlint
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### `climaybe add-cursor-skill`
|
|
113
|
+
|
|
114
|
+
Add **only** the Cursor commit skill to this project (`.cursor/skills/commit/SKILL.md`). Use this if you skipped it at init or want to add it later.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
npx climaybe add-cursor-skill
|
|
118
|
+
```
|
|
119
|
+
|
|
94
120
|
## Configuration
|
|
95
121
|
|
|
96
122
|
The CLI writes config into the `config` field of your `package.json`:
|
|
@@ -102,6 +128,8 @@ The CLI writes config into the `config` field of your `package.json`:
|
|
|
102
128
|
"default_store": "voldt-staging.myshopify.com",
|
|
103
129
|
"preview_workflows": true,
|
|
104
130
|
"build_workflows": true,
|
|
131
|
+
"commitlint": true,
|
|
132
|
+
"cursor_skills": true,
|
|
105
133
|
"stores": {
|
|
106
134
|
"voldt-staging": "voldt-staging.myshopify.com",
|
|
107
135
|
"voldt-norway": "voldt-norway.myshopify.com"
|
|
@@ -149,19 +177,19 @@ Direct pushes to `staging-<store>` or `live-<store>` are automatically synced ba
|
|
|
149
177
|
|
|
150
178
|
| Workflow | Trigger | What it does |
|
|
151
179
|
|----------|---------|-------------|
|
|
152
|
-
| `release-pr-check.yml` | PR from `staging` to `main` |
|
|
153
|
-
| `post-merge-tag.yml` | Push to `main` (merged PR) | Staging→main: minor bump (e.g. v3.2.0).
|
|
154
|
-
| `nightly-hotfix.yml` | Cron 02:00 US Eastern |
|
|
180
|
+
| `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 |
|
|
181
|
+
| `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 |
|
|
182
|
+
| `nightly-hotfix.yml` | Cron 02:00 US Eastern | Collects commits since latest tag (incl. hotfix backports), AI changelog, patch bump and tag |
|
|
155
183
|
|
|
156
184
|
### Multi-store (additional)
|
|
157
185
|
|
|
158
186
|
| Workflow | Trigger | What it does |
|
|
159
187
|
|----------|---------|-------------|
|
|
160
|
-
| `main-to-staging-stores.yml` (main-to-staging-<store>) | Push to `main` |
|
|
161
|
-
| `stores-to-root.yml` | Push to `staging-*` |
|
|
188
|
+
| `main-to-staging-stores.yml` (main-to-staging-<store>) | Push to `main` | Merges main into each `staging-<alias>`; root JSONs ignored. For hotfix-backport, skips the store that sent the hotfix; other stores get the merge. Skips only on pure store-sync. |
|
|
189
|
+
| `stores-to-root.yml` | Push to `staging-*` | From main merge: stores→root. From elsewhere (e.g. Shopify): root→stores |
|
|
162
190
|
| `pr-to-live.yml` | After stores-to-root | Opens PR from `staging-<alias>` to `live-<alias>` |
|
|
163
|
-
| `root-to-stores.yml` | Push to `live-*`
|
|
164
|
-
| `multistore-hotfix-to-main.yml` | Push to `staging-*` or `live-*` (and after root-to-stores) |
|
|
191
|
+
| `root-to-stores.yml` | Push to `live-*` | From main merge: stores→root. From elsewhere: root→stores (same as stores-to-root on staging-*) |
|
|
192
|
+
| `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) |
|
|
165
193
|
|
|
166
194
|
### Optional preview + cleanup package
|
|
167
195
|
|
|
@@ -189,13 +217,12 @@ Enabled via `climaybe init` prompt (`Enable build + Lighthouse workflows?`).
|
|
|
189
217
|
|
|
190
218
|
## Versioning
|
|
191
219
|
|
|
192
|
-
- **Version format**: Always three-part (e.g. `v3.2.0`). No
|
|
193
|
-
- **
|
|
194
|
-
- **
|
|
195
|
-
- **
|
|
196
|
-
- **
|
|
197
|
-
|
|
198
|
-
All version bumps update `config/settings_schema.json` automatically.
|
|
220
|
+
- **Version format**: Always three-part (e.g. `v3.2.0`). No version in code or PR title; the system infers from tags.
|
|
221
|
+
- **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.
|
|
222
|
+
- **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).
|
|
223
|
+
- **Non-staging to main** (hotfix backports, direct commits): **Patch** bump only, via **nightly workflow** at 02:00 US Eastern (not at commit time).
|
|
224
|
+
- **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). When the push is a hotfix from one store (e.g. live-norway), that store’s staging branch is skipped; other stores’ staging branches still get the merge.
|
|
225
|
+
- Version bumps update `config/settings_schema.json` and, when present, `package.json` `version`.
|
|
199
226
|
|
|
200
227
|
**Full specification:** For detailed versioning rules, local dev flow, hotfix behavior, and alignment with the external CI/CD doc, see **[CI/CD Reference](docs/CI_CD_REFERENCE.md)**.
|
|
201
228
|
|
package/bin/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.6.1
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "climaybe",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.1",
|
|
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": {
|
|
@@ -39,7 +39,21 @@
|
|
|
39
39
|
"main"
|
|
40
40
|
],
|
|
41
41
|
"plugins": [
|
|
42
|
-
|
|
42
|
+
[
|
|
43
|
+
"@semantic-release/commit-analyzer",
|
|
44
|
+
{
|
|
45
|
+
"releaseRules": [
|
|
46
|
+
{
|
|
47
|
+
"type": "ci",
|
|
48
|
+
"release": "patch"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"type": "chore",
|
|
52
|
+
"release": "patch"
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
],
|
|
43
57
|
"@semantic-release/release-notes-generator",
|
|
44
58
|
[
|
|
45
59
|
"@semantic-release/npm",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { writeConfig } from '../lib/config.js';
|
|
3
|
+
import { scaffoldCursorCommitSkill } from '../lib/commit-tooling.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Add only the Cursor commit skill to this project (.cursor/skills/commit/SKILL.md).
|
|
7
|
+
* Can be run standalone or after init without having chosen Cursor skills at init.
|
|
8
|
+
*/
|
|
9
|
+
export async function addCursorSkillCommand() {
|
|
10
|
+
console.log(pc.bold('\n climaybe — Add Cursor commit skill\n'));
|
|
11
|
+
|
|
12
|
+
writeConfig({ cursor_skills: true });
|
|
13
|
+
|
|
14
|
+
scaffoldCursorCommitSkill();
|
|
15
|
+
console.log(pc.green(' Cursor commit skill added to .cursor/skills/commit/SKILL.md'));
|
|
16
|
+
console.log(pc.dim(' Use "commit" or "group and commit" in Cursor to get conventional-commit assistance.\n'));
|
|
17
|
+
}
|
package/src/commands/init.js
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
promptStoreLoop,
|
|
5
5
|
promptPreviewWorkflows,
|
|
6
6
|
promptBuildWorkflows,
|
|
7
|
+
promptCommitlint,
|
|
8
|
+
promptCursorSkills,
|
|
7
9
|
promptConfigureCISecrets,
|
|
8
10
|
promptUpdateExistingSecrets,
|
|
9
11
|
promptSecretValue,
|
|
@@ -13,6 +15,7 @@ import { readConfig, writeConfig } from '../lib/config.js';
|
|
|
13
15
|
import { ensureGitRepo, ensureInitialCommit, ensureStagingBranch, createStoreBranches, getSuggestedTagForRelease } from '../lib/git.js';
|
|
14
16
|
import { scaffoldWorkflows } from '../lib/workflows.js';
|
|
15
17
|
import { createStoreDirectories } from '../lib/store-sync.js';
|
|
18
|
+
import { scaffoldCommitlint, scaffoldCursorCommitSkill } from '../lib/commit-tooling.js';
|
|
16
19
|
import {
|
|
17
20
|
isGhAvailable,
|
|
18
21
|
hasGitHubRemote,
|
|
@@ -38,6 +41,8 @@ async function runInitFlow() {
|
|
|
38
41
|
const mode = stores.length > 1 ? 'multi' : 'single';
|
|
39
42
|
const enablePreviewWorkflows = await promptPreviewWorkflows();
|
|
40
43
|
const enableBuildWorkflows = await promptBuildWorkflows();
|
|
44
|
+
const enableCommitlint = await promptCommitlint();
|
|
45
|
+
const enableCursorSkills = await promptCursorSkills();
|
|
41
46
|
|
|
42
47
|
console.log(pc.dim(`\n Mode: ${mode}-store (${stores.length} store(s))`));
|
|
43
48
|
|
|
@@ -47,6 +52,8 @@ async function runInitFlow() {
|
|
|
47
52
|
default_store: stores[0].domain,
|
|
48
53
|
preview_workflows: enablePreviewWorkflows,
|
|
49
54
|
build_workflows: enableBuildWorkflows,
|
|
55
|
+
commitlint: enableCommitlint,
|
|
56
|
+
cursor_skills: enableCursorSkills,
|
|
50
57
|
stores: {},
|
|
51
58
|
};
|
|
52
59
|
|
|
@@ -80,6 +87,20 @@ async function runInitFlow() {
|
|
|
80
87
|
includeBuild: enableBuildWorkflows,
|
|
81
88
|
});
|
|
82
89
|
|
|
90
|
+
// 7. Optional commitlint + Husky and Cursor commit skill
|
|
91
|
+
if (enableCommitlint) {
|
|
92
|
+
console.log(pc.dim(' Setting up commitlint + Husky...'));
|
|
93
|
+
if (scaffoldCommitlint()) {
|
|
94
|
+
console.log(pc.green(' commitlint + Husky installed (conventional commits enforced on git commit).'));
|
|
95
|
+
} else {
|
|
96
|
+
console.log(pc.yellow(' commitlint setup failed or skipped (run npm install manually).'));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (enableCursorSkills) {
|
|
100
|
+
scaffoldCursorCommitSkill();
|
|
101
|
+
console.log(pc.green(' Cursor commit skill added to .cursor/skills/commit/SKILL.md'));
|
|
102
|
+
}
|
|
103
|
+
|
|
83
104
|
// Done
|
|
84
105
|
console.log(pc.bold(pc.green('\n Setup complete!\n')));
|
|
85
106
|
|
|
@@ -95,6 +116,8 @@ async function runInitFlow() {
|
|
|
95
116
|
}
|
|
96
117
|
console.log(pc.dim(` Preview workflows: ${enablePreviewWorkflows ? 'enabled' : 'disabled'}`));
|
|
97
118
|
console.log(pc.dim(` Build workflows: ${enableBuildWorkflows ? 'enabled' : 'disabled'}`));
|
|
119
|
+
console.log(pc.dim(` commitlint + Husky: ${enableCommitlint ? 'enabled' : 'disabled'}`));
|
|
120
|
+
console.log(pc.dim(` Cursor commit skill: ${enableCursorSkills ? 'added' : 'skipped'}`));
|
|
98
121
|
|
|
99
122
|
const suggestedTag = getSuggestedTagForRelease();
|
|
100
123
|
const tagLabel = suggestedTag === 'v1.0.0' ? 'Tag your first release' : 'Tag your next release';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { writeConfig } from '../lib/config.js';
|
|
3
|
+
import { scaffoldCommitlint } from '../lib/commit-tooling.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Set up commitlint + Husky only (conventional commits on git commit).
|
|
7
|
+
* Can be run standalone or after init without having chosen commitlint at init.
|
|
8
|
+
*/
|
|
9
|
+
export async function setupCommitlintCommand() {
|
|
10
|
+
console.log(pc.bold('\n climaybe — Setup commitlint + Husky\n'));
|
|
11
|
+
|
|
12
|
+
writeConfig({ commitlint: true });
|
|
13
|
+
|
|
14
|
+
console.log(pc.dim(' Installing commitlint + Husky (conventional commits enforced on git commit)...'));
|
|
15
|
+
const skipInstall = process.env.CLIMAYBE_SKIP_INSTALL === '1';
|
|
16
|
+
if (scaffoldCommitlint(process.cwd(), skipInstall ? { skipInstall: true } : {})) {
|
|
17
|
+
console.log(pc.green('\n commitlint + Husky are set up. Use conventional commits (e.g. feat: add X, fix: resolve Y).\n'));
|
|
18
|
+
} else {
|
|
19
|
+
console.log(pc.yellow('\n Installation failed or skipped. Run npm install in this repo and try again.\n'));
|
|
20
|
+
}
|
|
21
|
+
}
|
package/src/index.js
CHANGED
|
@@ -5,6 +5,8 @@ import { switchCommand } from './commands/switch.js';
|
|
|
5
5
|
import { syncCommand } from './commands/sync.js';
|
|
6
6
|
import { updateWorkflowsCommand } from './commands/update-workflows.js';
|
|
7
7
|
import { ensureBranchesCommand } from './commands/ensure-branches.js';
|
|
8
|
+
import { setupCommitlintCommand } from './commands/setup-commitlint.js';
|
|
9
|
+
import { addCursorSkillCommand } from './commands/add-cursor-skill.js';
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Create the CLI program (for testing and for run).
|
|
@@ -57,6 +59,16 @@ export function createProgram(version = '0.0.0', packageDir = '') {
|
|
|
57
59
|
.description('Create missing staging and per-store branches from current HEAD (then push)')
|
|
58
60
|
.action(ensureBranchesCommand);
|
|
59
61
|
|
|
62
|
+
program
|
|
63
|
+
.command('setup-commitlint')
|
|
64
|
+
.description('Set up only commitlint + Husky (conventional commits on git commit)')
|
|
65
|
+
.action(setupCommitlintCommand);
|
|
66
|
+
|
|
67
|
+
program
|
|
68
|
+
.command('add-cursor-skill')
|
|
69
|
+
.description('Add only the Cursor commit skill to this project (.cursor/skills/commit)')
|
|
70
|
+
.action(addCursorSkillCommand);
|
|
71
|
+
|
|
60
72
|
return program;
|
|
61
73
|
}
|
|
62
74
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
import { readPkg, writePkg } from './config.js';
|
|
5
|
+
|
|
6
|
+
const COMMITLINT_DEPS = {
|
|
7
|
+
'@commitlint/cli': '^20.4.4',
|
|
8
|
+
'@commitlint/config-conventional': '^20.4.4',
|
|
9
|
+
husky: '^9.1.7',
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const COMMITLINT_CONFIG = `/** @type {import('@commitlint/types').UserConfig} */
|
|
13
|
+
module.exports = {
|
|
14
|
+
extends: ['@commitlint/config-conventional'],
|
|
15
|
+
rules: {
|
|
16
|
+
'type-enum': [
|
|
17
|
+
2,
|
|
18
|
+
'always',
|
|
19
|
+
['feat', 'fix', 'docs', 'style', 'refactor', 'perf', 'test', 'build', 'ci', 'chore'],
|
|
20
|
+
],
|
|
21
|
+
'header-max-length': [2, 'always', 100],
|
|
22
|
+
'body-max-line-length': [2, 'always', 200],
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const HUSKY_COMMIT_MSG = `npx --no-install commitlint --edit "$1"
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
const CURSOR_COMMIT_SKILL = `---
|
|
31
|
+
name: commit
|
|
32
|
+
description: Groups working-tree changes into logical commits and commits them using conventional commit rules (type, optional scope, subject; commitlint). Use when the user asks to commit, group commits, stage and commit, or write conventional commits.
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
# Commit (group + conventional)
|
|
36
|
+
|
|
37
|
+
Group changes by purpose, then commit each group with a valid conventional message so commitlint and semantic-release stay happy.
|
|
38
|
+
|
|
39
|
+
## Workflow
|
|
40
|
+
|
|
41
|
+
1. **Inspect** — Get full picture of changes (git status, git diff).
|
|
42
|
+
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 will reject invalid messages.
|
|
45
|
+
|
|
46
|
+
## Message rules (commitlint)
|
|
47
|
+
|
|
48
|
+
- **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
|
+
- **Imperative, present tense:** "add feature" not "added feature".
|
|
51
|
+
|
|
52
|
+
## Examples
|
|
53
|
+
|
|
54
|
+
feat(init): add store alias prompt
|
|
55
|
+
fix(sync): prevent overwrite of unchanged files
|
|
56
|
+
docs: add conventional commit section to CONTRIBUTING
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Scaffold commitlint config, Husky commit-msg hook, and add deps to package.json.
|
|
61
|
+
* Runs npm install so husky prepare runs and hooks are installed (unless options.skipInstall).
|
|
62
|
+
* @param {string} [cwd] - Working directory (default process.cwd())
|
|
63
|
+
* @param {{ skipInstall?: boolean }} [options] - skipInstall: true to skip npm install (e.g. in tests)
|
|
64
|
+
* @returns {boolean} - true if scaffolded, false if skipped/failed
|
|
65
|
+
*/
|
|
66
|
+
export function scaffoldCommitlint(cwd = process.cwd(), options = {}) {
|
|
67
|
+
const pkg = readPkg(cwd);
|
|
68
|
+
if (!pkg) return false;
|
|
69
|
+
|
|
70
|
+
const updated = { ...pkg };
|
|
71
|
+
updated.scripts = { ...pkg.scripts };
|
|
72
|
+
if (!updated.scripts.prepare) {
|
|
73
|
+
updated.scripts.prepare = 'husky';
|
|
74
|
+
} else if (!updated.scripts.prepare.includes('husky')) {
|
|
75
|
+
updated.scripts.prepare = `husky && ${updated.scripts.prepare}`;
|
|
76
|
+
}
|
|
77
|
+
updated.devDependencies = { ...pkg.devDependencies, ...COMMITLINT_DEPS };
|
|
78
|
+
writePkg(updated, cwd);
|
|
79
|
+
|
|
80
|
+
writeFileSync(join(cwd, 'commitlint.config.js'), COMMITLINT_CONFIG, 'utf-8');
|
|
81
|
+
|
|
82
|
+
const huskyDir = join(cwd, '.husky');
|
|
83
|
+
if (!existsSync(huskyDir)) {
|
|
84
|
+
mkdirSync(huskyDir, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
writeFileSync(join(huskyDir, 'commit-msg'), HUSKY_COMMIT_MSG, 'utf-8');
|
|
87
|
+
|
|
88
|
+
if (options.skipInstall) return true;
|
|
89
|
+
try {
|
|
90
|
+
execSync('npm install', { cwd, stdio: 'inherit' });
|
|
91
|
+
} catch {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Scaffold Cursor commit skill into .cursor/skills/commit/SKILL.md.
|
|
99
|
+
* @param {string} [cwd] - Working directory (default process.cwd())
|
|
100
|
+
* @returns {boolean} - true if written
|
|
101
|
+
*/
|
|
102
|
+
export function scaffoldCursorCommitSkill(cwd = process.cwd()) {
|
|
103
|
+
const skillDir = join(cwd, '.cursor', 'skills', 'commit');
|
|
104
|
+
if (!existsSync(skillDir)) {
|
|
105
|
+
mkdirSync(skillDir, { recursive: true });
|
|
106
|
+
}
|
|
107
|
+
const skillPath = join(skillDir, 'SKILL.md');
|
|
108
|
+
writeFileSync(skillPath, CURSOR_COMMIT_SKILL, 'utf-8');
|
|
109
|
+
return true;
|
|
110
|
+
}
|
package/src/lib/config.js
CHANGED
|
@@ -97,6 +97,22 @@ export function isBuildWorkflowsEnabled(cwd = process.cwd()) {
|
|
|
97
97
|
return config?.build_workflows === true;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Whether commitlint + Husky was enabled at init.
|
|
102
|
+
*/
|
|
103
|
+
export function isCommitlintEnabled(cwd = process.cwd()) {
|
|
104
|
+
const config = readConfig(cwd);
|
|
105
|
+
return config?.commitlint === true;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Whether Cursor commit skill was added at init.
|
|
110
|
+
*/
|
|
111
|
+
export function isCursorSkillsEnabled(cwd = process.cwd()) {
|
|
112
|
+
const config = readConfig(cwd);
|
|
113
|
+
return config?.cursor_skills === true;
|
|
114
|
+
}
|
|
115
|
+
|
|
100
116
|
/**
|
|
101
117
|
* Add a store entry to the config.
|
|
102
118
|
* Returns the updated config.
|
package/src/lib/prompts.js
CHANGED
|
@@ -148,6 +148,34 @@ export async function promptBuildWorkflows() {
|
|
|
148
148
|
return !!enableBuildWorkflows;
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Ask whether to set up commitlint + Husky (conventional commits enforced on git commit).
|
|
153
|
+
*/
|
|
154
|
+
export async function promptCommitlint() {
|
|
155
|
+
const { enableCommitlint } = await prompts({
|
|
156
|
+
type: 'confirm',
|
|
157
|
+
name: 'enableCommitlint',
|
|
158
|
+
message: 'Enable commitlint + Husky? (enforce conventional commits on git commit)',
|
|
159
|
+
initial: true,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return !!enableCommitlint;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Ask whether to add Cursor commit skill to the project (.cursor/skills/commit).
|
|
167
|
+
*/
|
|
168
|
+
export async function promptCursorSkills() {
|
|
169
|
+
const { enableCursorSkills } = await prompts({
|
|
170
|
+
type: 'confirm',
|
|
171
|
+
name: 'enableCursorSkills',
|
|
172
|
+
message: 'Add Cursor commit skill to this project? (AI-assisted conventional commits)',
|
|
173
|
+
initial: true,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return !!enableCursorSkills;
|
|
177
|
+
}
|
|
178
|
+
|
|
151
179
|
/**
|
|
152
180
|
* Prompt for a single new store (used by add-store command).
|
|
153
181
|
* Takes existing aliases to prevent duplicates.
|
|
@@ -10,43 +10,43 @@ jobs:
|
|
|
10
10
|
build:
|
|
11
11
|
uses: ./.github/workflows/reusable-build.yml
|
|
12
12
|
|
|
13
|
-
lighthouse-
|
|
13
|
+
lighthouse-gate:
|
|
14
14
|
runs-on: ubuntu-latest
|
|
15
15
|
needs: [build]
|
|
16
|
+
outputs:
|
|
17
|
+
run_lighthouse: ${{ steps.check.outputs.run_lighthouse }}
|
|
16
18
|
env:
|
|
17
19
|
SHOPIFY_STORE_URL: ${{ secrets.SHOPIFY_STORE_URL }}
|
|
18
20
|
SHOP_ACCESS_TOKEN: ${{ secrets.SHOP_ACCESS_TOKEN }}
|
|
19
21
|
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
|
|
20
22
|
steps:
|
|
21
|
-
- name: Check
|
|
23
|
+
- name: Check if Lighthouse CI should run
|
|
24
|
+
id: check
|
|
22
25
|
run: |
|
|
23
26
|
SKIP_REASONS=()
|
|
24
|
-
|
|
25
27
|
if [ "${{ github.ref }}" != "refs/heads/main" ] && [ "${{ github.ref }}" != "refs/heads/staging" ]; then
|
|
26
28
|
SKIP_REASONS+=("Not on main or staging branch (current: ${{ github.ref }})")
|
|
27
29
|
fi
|
|
28
|
-
|
|
29
30
|
if [ -z "$SHOPIFY_STORE_URL" ] || [ -z "$SHOP_ACCESS_TOKEN" ] || [ -z "$LHCI_GITHUB_APP_TOKEN" ]; then
|
|
30
31
|
SKIP_REASONS+=("Missing required secrets: SHOPIFY_STORE_URL, SHOP_ACCESS_TOKEN, or LHCI_GITHUB_APP_TOKEN")
|
|
31
32
|
fi
|
|
32
|
-
|
|
33
33
|
if [ ${#SKIP_REASONS[@]} -gt 0 ]; then
|
|
34
|
-
echo "
|
|
35
|
-
echo "
|
|
36
|
-
for reason in "${SKIP_REASONS[@]}"; do
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
exit 0
|
|
34
|
+
echo "run_lighthouse=false" >> $GITHUB_OUTPUT
|
|
35
|
+
echo "Lighthouse CI skipped:"
|
|
36
|
+
for reason in "${SKIP_REASONS[@]}"; do echo " - $reason"; done
|
|
37
|
+
else
|
|
38
|
+
echo "run_lighthouse=true" >> $GITHUB_OUTPUT
|
|
40
39
|
fi
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
lighthouse-ci:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
needs: [build, lighthouse-gate]
|
|
44
|
+
if: needs.lighthouse-gate.outputs.run_lighthouse == 'true'
|
|
45
|
+
steps:
|
|
44
46
|
- name: Checkout code
|
|
45
|
-
if: env.SHOPIFY_STORE_URL != '' && env.SHOP_ACCESS_TOKEN != '' && env.LHCI_GITHUB_APP_TOKEN != '' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
|
|
46
47
|
uses: actions/checkout@v4
|
|
47
48
|
|
|
48
49
|
- name: Lighthouse
|
|
49
|
-
if: env.SHOPIFY_STORE_URL != '' && env.SHOP_ACCESS_TOKEN != '' && env.LHCI_GITHUB_APP_TOKEN != '' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging')
|
|
50
50
|
uses: shopify/lighthouse-ci-action@v1
|
|
51
51
|
with:
|
|
52
52
|
store: ${{ secrets.SHOPIFY_STORE_URL }}
|