climaybe 1.8.2 → 1.9.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 +36 -16
- package/bin/version.txt +1 -1
- package/package.json +2 -2
- package/src/commands/add-store.js +12 -2
- package/src/commands/app-init.js +89 -0
- package/src/commands/ensure-branches.js +4 -1
- package/src/commands/init.js +21 -6
- package/src/commands/switch.js +4 -1
- package/src/commands/sync.js +4 -1
- package/src/commands/update-workflows.js +4 -1
- package/src/index.js +42 -20
- package/src/lib/config.js +39 -2
- package/src/lib/theme-guard.js +13 -0
- package/src/workflows/build/build-pipeline.yml +1 -1
package/README.md
CHANGED
|
@@ -1,38 +1,54 @@
|
|
|
1
1
|
# climaybe
|
|
2
2
|
|
|
3
|
-
Shopify CI/CD
|
|
3
|
+
Shopify CLI for **theme CI/CD** (GitHub Actions, branches, multi-store config) and light **app repo** setup. Same install works in theme or app repositories.
|
|
4
4
|
|
|
5
|
-
**Commit linting and
|
|
5
|
+
**Commit linting and Cursor bundle (optional in both flows):**
|
|
6
6
|
|
|
7
|
-
- **Conventional commit linting:** During `climaybe init`, you can
|
|
8
|
-
- **Cursor rules + skills:**
|
|
7
|
+
- **Conventional commit linting:** During `climaybe theme init` or `climaybe app init`, you can install [commitlint](https://commitlint.js.org/) and [Husky](https://typicode.github.io/husky) for [Conventional Commits](https://www.conventionalcommits.org/).
|
|
8
|
+
- **Cursor rules + skills:** Opt in to Electric Maybe’s bundled [Cursor](https://cursor.com/) rules and skills under `.cursor/rules/` and `.cursor/skills/` (themes, JS, a11y, commits, changelog, Linear, etc.).
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
## Command layout (Shopify CLI–style)
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
- **`climaybe theme <command>`** — canonical commands for theme repos (workflows, stores, branches).
|
|
13
|
+
- **Same commands at the top level** — `climaybe init` is the same as `climaybe theme init` (backward compatible).
|
|
14
|
+
- **`climaybe app init`** — app repos only: writes `project_type: "app"` in `package.json` config, optional commitlint + Cursor bundle. Does **not** install theme GitHub Actions or store/branch setup.
|
|
15
|
+
- **`climaybe setup-commitlint`** and **`climaybe add-cursor`** — always at the top level (stack-agnostic).
|
|
16
|
+
|
|
17
|
+
Theme-only commands refuse to run when `package.json` → `config.project_type` is **`app`**.
|
|
13
18
|
|
|
14
|
-
Install
|
|
19
|
+
## Install
|
|
15
20
|
|
|
16
21
|
```bash
|
|
17
|
-
cd your-shopify-theme-repo
|
|
22
|
+
cd your-shopify-theme-repo # or app repo
|
|
18
23
|
npm install -D climaybe
|
|
19
24
|
```
|
|
20
25
|
|
|
21
26
|
Run commands with `npx climaybe` (or add scripts to your `package.json`).
|
|
22
27
|
|
|
23
|
-
## Quick Start
|
|
28
|
+
## Quick Start (theme)
|
|
24
29
|
|
|
25
30
|
```bash
|
|
26
31
|
cd your-shopify-theme-repo
|
|
27
32
|
npm install -D climaybe
|
|
28
33
|
npx climaybe init
|
|
34
|
+
# equivalent: npx climaybe theme init
|
|
29
35
|
```
|
|
30
36
|
|
|
31
37
|
The interactive setup will ask for your store URL(s) and configure everything automatically.
|
|
32
38
|
|
|
39
|
+
## Quick Start (app)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cd your-shopify-app-repo
|
|
43
|
+
npm install -D climaybe
|
|
44
|
+
npx climaybe app init
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Installs optional commitlint/Husky and Cursor rules/skills only. Use [Shopify CLI](https://shopify.dev/docs/api/shopify-cli) for app development and deployment.
|
|
48
|
+
|
|
33
49
|
## Commands
|
|
34
50
|
|
|
35
|
-
### `climaybe init`
|
|
51
|
+
### `climaybe init` / `climaybe theme init`
|
|
36
52
|
|
|
37
53
|
Interactive setup that configures your repo for CI/CD.
|
|
38
54
|
|
|
@@ -49,7 +65,11 @@ Interactive setup that configures your repo for CI/CD.
|
|
|
49
65
|
11. Creates git branches and store directories (multi-store)
|
|
50
66
|
12. Optionally installs commitlint, Husky, and the Cursor bundle (rules + skills)
|
|
51
67
|
|
|
52
|
-
### `climaybe
|
|
68
|
+
### `climaybe app init`
|
|
69
|
+
|
|
70
|
+
Interactive setup for a **Shopify app** repository: optional commitlint + Husky, optional Cursor bundle, and `project_type: "app"` in `package.json` `config`. No theme workflows, stores, or staging/live branches.
|
|
71
|
+
|
|
72
|
+
### `climaybe add-store` / `climaybe theme add-store`
|
|
53
73
|
|
|
54
74
|
Add a new store to an existing setup.
|
|
55
75
|
|
|
@@ -62,7 +82,7 @@ npx climaybe add-store
|
|
|
62
82
|
- Creates `stores/<alias>/` directory structure
|
|
63
83
|
- If store count goes from 1 to 2+, automatically migrates from single to multi-store mode
|
|
64
84
|
|
|
65
|
-
### `climaybe switch <alias>`
|
|
85
|
+
### `climaybe switch <alias>` / `climaybe theme switch`
|
|
66
86
|
|
|
67
87
|
Switch your local dev environment to a specific store (multi-store only).
|
|
68
88
|
|
|
@@ -72,7 +92,7 @@ npx climaybe switch voldt-norway
|
|
|
72
92
|
|
|
73
93
|
Copies `stores/<alias>/` JSON files to the repo root so you can preview that store locally.
|
|
74
94
|
|
|
75
|
-
### `climaybe sync [alias]`
|
|
95
|
+
### `climaybe sync [alias]` / `climaybe theme sync`
|
|
76
96
|
|
|
77
97
|
Sync root JSON files back to a store directory (multi-store only).
|
|
78
98
|
|
|
@@ -82,7 +102,7 @@ npx climaybe sync voldt-norway
|
|
|
82
102
|
|
|
83
103
|
If no alias is given, syncs to the default store.
|
|
84
104
|
|
|
85
|
-
### `climaybe ensure-branches`
|
|
105
|
+
### `climaybe ensure-branches` / `climaybe theme ensure-branches`
|
|
86
106
|
|
|
87
107
|
Create missing `staging` and per-store branches (`staging-<alias>`, `live-<alias>`) from your current branch (usually `main`). Use when the repo only has `main` (e.g. after a fresh clone) so the main → staging-<store> sync can run.
|
|
88
108
|
|
|
@@ -91,7 +111,7 @@ npx climaybe ensure-branches
|
|
|
91
111
|
git push origin --all
|
|
92
112
|
```
|
|
93
113
|
|
|
94
|
-
### `climaybe update-workflows`
|
|
114
|
+
### `climaybe update-workflows` / `climaybe theme update-workflows`
|
|
95
115
|
|
|
96
116
|
Refresh GitHub Actions workflows from the latest bundled templates.
|
|
97
117
|
|
|
@@ -213,7 +233,7 @@ Enabled via `climaybe init` prompt (`Enable build + Lighthouse workflows?`; defa
|
|
|
213
233
|
|
|
214
234
|
| Workflow | Trigger | What it does |
|
|
215
235
|
|----------|---------|-------------|
|
|
216
|
-
| `build-pipeline.yml` | Push to
|
|
236
|
+
| `build-pipeline.yml` | Push to any branch | Runs reusable build and Lighthouse checks (when required secrets exist) |
|
|
217
237
|
| `reusable-build.yml` | workflow_call | Runs Node build + Tailwind compile, then commits compiled assets when changed |
|
|
218
238
|
| `create-release.yml` | Push tag `v*` | Builds release archive and creates GitHub Release using `release-notes.md` |
|
|
219
239
|
|
package/bin/version.txt
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.
|
|
1
|
+
1.9.1
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "climaybe",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "Shopify
|
|
3
|
+
"version": "1.9.1",
|
|
4
|
+
"description": "Shopify CLI — theme CI/CD (workflows, branches, stores) and app repo helpers; also: commitlint and Cursor rules/skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"climaybe": "bin/cli.js"
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import pc from 'picocolors';
|
|
2
2
|
import { promptNewStore, promptConfigureCISecrets, promptUpdateExistingSecrets, promptSecretValue, promptTestThemeToken } from '../lib/prompts.js';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
readConfig,
|
|
5
|
+
addStoreToConfig,
|
|
6
|
+
getStoreAliases,
|
|
7
|
+
getMode,
|
|
8
|
+
isPreviewWorkflowsEnabled,
|
|
9
|
+
isBuildWorkflowsEnabled,
|
|
10
|
+
} from '../lib/config.js';
|
|
11
|
+
import { requireThemeProject } from '../lib/theme-guard.js';
|
|
4
12
|
import { createStoreBranches } from '../lib/git.js';
|
|
5
13
|
import { scaffoldWorkflows } from '../lib/workflows.js';
|
|
6
14
|
import { createStoreDirectories } from '../lib/store-sync.js';
|
|
@@ -21,10 +29,12 @@ import {
|
|
|
21
29
|
export async function addStoreCommand() {
|
|
22
30
|
console.log(pc.bold('\n climaybe — Add Store\n'));
|
|
23
31
|
|
|
32
|
+
if (!requireThemeProject()) return;
|
|
33
|
+
|
|
24
34
|
// Guard: config must exist
|
|
25
35
|
const config = readConfig();
|
|
26
36
|
if (!config?.stores) {
|
|
27
|
-
console.log(pc.red(' No climaybe config found. Run "climaybe init" first.\n'));
|
|
37
|
+
console.log(pc.red(' No climaybe config found. Run "climaybe theme init" (or "climaybe init") first.\n'));
|
|
28
38
|
return;
|
|
29
39
|
}
|
|
30
40
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import pc from 'picocolors';
|
|
3
|
+
import { promptCommitlint, promptCursorSkills } from '../lib/prompts.js';
|
|
4
|
+
import {
|
|
5
|
+
readConfig,
|
|
6
|
+
writeConfig,
|
|
7
|
+
getProjectType,
|
|
8
|
+
isThemeProjectForAppInit,
|
|
9
|
+
} from '../lib/config.js';
|
|
10
|
+
import { ensureGitRepo, ensureInitialCommit } from '../lib/git.js';
|
|
11
|
+
import { scaffoldCommitlint } from '../lib/commit-tooling.js';
|
|
12
|
+
import { scaffoldCursorBundle } from '../lib/cursor-bundle.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Minimal Shopify app repo setup: commitlint, Cursor bundle, project_type in config.
|
|
16
|
+
* No theme stores, branches, or GitHub Actions theme workflows.
|
|
17
|
+
*/
|
|
18
|
+
async function runAppInitFlow() {
|
|
19
|
+
const enableCommitlint = await promptCommitlint();
|
|
20
|
+
const enableCursorSkills = await promptCursorSkills();
|
|
21
|
+
|
|
22
|
+
const config = {
|
|
23
|
+
project_type: 'app',
|
|
24
|
+
commitlint: enableCommitlint,
|
|
25
|
+
cursor_skills: enableCursorSkills,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
writeConfig(config, process.cwd(), { defaultPackageName: 'shopify-app' });
|
|
29
|
+
console.log(pc.green(' Updated package.json config.'));
|
|
30
|
+
|
|
31
|
+
ensureGitRepo();
|
|
32
|
+
ensureInitialCommit();
|
|
33
|
+
|
|
34
|
+
if (enableCommitlint) {
|
|
35
|
+
console.log(pc.dim(' Setting up commitlint + Husky...'));
|
|
36
|
+
if (scaffoldCommitlint()) {
|
|
37
|
+
console.log(pc.green(' commitlint + Husky installed (conventional commits enforced on git commit).'));
|
|
38
|
+
} else {
|
|
39
|
+
console.log(pc.yellow(' commitlint setup failed or skipped (run npm install manually).'));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (enableCursorSkills) {
|
|
43
|
+
const cursorOk = scaffoldCursorBundle();
|
|
44
|
+
if (cursorOk) {
|
|
45
|
+
console.log(pc.green(' Electric Maybe Cursor rules + skills → .cursor/rules, .cursor/skills'));
|
|
46
|
+
} else {
|
|
47
|
+
console.log(pc.yellow(' Cursor bundle not found in package (skipped).'));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
console.log(pc.bold(pc.green('\n App setup complete!\n')));
|
|
52
|
+
console.log(pc.dim(' commitlint + Husky: ' + (enableCommitlint ? 'enabled' : 'disabled')));
|
|
53
|
+
console.log(pc.dim(' Cursor rules + skills: ' + (enableCursorSkills ? 'installed' : 'skipped')));
|
|
54
|
+
console.log(pc.dim('\n Next steps:'));
|
|
55
|
+
console.log(pc.dim(' Use Shopify CLI (`shopify app dev`, etc.) for app development.'));
|
|
56
|
+
console.log(pc.dim(' Theme CI/CD workflows are not installed; add your own deployment as needed.\n'));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function appInitCommand() {
|
|
60
|
+
console.log(pc.bold('\n climaybe — Shopify app setup\n'));
|
|
61
|
+
|
|
62
|
+
if (isThemeProjectForAppInit()) {
|
|
63
|
+
console.log(
|
|
64
|
+
pc.red(
|
|
65
|
+
' This repo looks like a theme project (stores in config or project_type: theme).'
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
console.log(pc.dim(' Use: npx climaybe theme init (or: npx climaybe init)\n'));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const existing = readConfig();
|
|
73
|
+
const hasConfig = existing != null && typeof existing === 'object';
|
|
74
|
+
|
|
75
|
+
if (hasConfig && getProjectType() === 'app') {
|
|
76
|
+
const { reinit } = await prompts({
|
|
77
|
+
type: 'confirm',
|
|
78
|
+
name: 'reinit',
|
|
79
|
+
message: 'This repo already has climaybe app config. Re-run setup (merge config and reinstall optional tooling)?',
|
|
80
|
+
initial: false,
|
|
81
|
+
});
|
|
82
|
+
if (!reinit) {
|
|
83
|
+
console.log(pc.dim(' Use "climaybe setup-commitlint" or "climaybe add-cursor" to refresh tooling.\n'));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
await runAppInitFlow();
|
|
89
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import pc from 'picocolors';
|
|
2
2
|
import { readConfig } from '../lib/config.js';
|
|
3
|
+
import { requireThemeProject } from '../lib/theme-guard.js';
|
|
3
4
|
import {
|
|
4
5
|
isGitRepo,
|
|
5
6
|
currentBranch,
|
|
@@ -14,9 +15,11 @@ import {
|
|
|
14
15
|
export async function ensureBranchesCommand() {
|
|
15
16
|
console.log(pc.bold('\n climaybe — Ensure Branches\n'));
|
|
16
17
|
|
|
18
|
+
if (!requireThemeProject()) return;
|
|
19
|
+
|
|
17
20
|
const config = readConfig();
|
|
18
21
|
if (!config?.stores) {
|
|
19
|
-
console.log(pc.red(' No climaybe config found. Run "climaybe init" first.\n'));
|
|
22
|
+
console.log(pc.red(' No climaybe config found. Run "climaybe theme init" (or "climaybe init") first.\n'));
|
|
20
23
|
return;
|
|
21
24
|
}
|
|
22
25
|
|
package/src/commands/init.js
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
promptSecretValue,
|
|
12
12
|
promptTestThemeToken,
|
|
13
13
|
} from '../lib/prompts.js';
|
|
14
|
-
import { readConfig, writeConfig } from '../lib/config.js';
|
|
14
|
+
import { readConfig, writeConfig, getProjectType } from '../lib/config.js';
|
|
15
15
|
import { ensureGitRepo, ensureInitialCommit, ensureStagingBranch, createStoreBranches, getSuggestedTagForRelease } from '../lib/git.js';
|
|
16
16
|
import { scaffoldWorkflows } from '../lib/workflows.js';
|
|
17
17
|
import { createStoreDirectories } from '../lib/store-sync.js';
|
|
@@ -49,6 +49,7 @@ async function runInitFlow() {
|
|
|
49
49
|
|
|
50
50
|
// 2. Build config
|
|
51
51
|
const config = {
|
|
52
|
+
project_type: 'theme',
|
|
52
53
|
port: 9295,
|
|
53
54
|
default_store: stores[0].domain,
|
|
54
55
|
preview_workflows: enablePreviewWorkflows,
|
|
@@ -236,7 +237,14 @@ async function runInitFlow() {
|
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
export async function initCommand() {
|
|
239
|
-
console.log(pc.bold('\n climaybe — Shopify CI/CD
|
|
240
|
+
console.log(pc.bold('\n climaybe — Shopify theme CI/CD setup\n'));
|
|
241
|
+
|
|
242
|
+
if (getProjectType() === 'app') {
|
|
243
|
+
console.log(pc.red(' This repo is configured as a Shopify app (project_type: app).'));
|
|
244
|
+
console.log(pc.dim(' Use: npx climaybe app init'));
|
|
245
|
+
console.log(pc.dim(' To switch to theme CI/CD, remove or edit package.json → config first.\n'));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
240
248
|
|
|
241
249
|
const existing = readConfig();
|
|
242
250
|
const hasConfig = existing != null && typeof existing === 'object';
|
|
@@ -249,9 +257,9 @@ export async function initCommand() {
|
|
|
249
257
|
initial: false,
|
|
250
258
|
});
|
|
251
259
|
if (!reinit) {
|
|
252
|
-
console.log(pc.dim(' Use "climaybe add-store" to add more stores.'));
|
|
253
|
-
console.log(pc.dim(' Use "climaybe update-workflows" to refresh workflows.'));
|
|
254
|
-
console.log(pc.dim(' Use "climaybe reinit" to reinitialize from scratch.\n'));
|
|
260
|
+
console.log(pc.dim(' Use "climaybe theme add-store" (or "climaybe add-store") to add more stores.'));
|
|
261
|
+
console.log(pc.dim(' Use "climaybe theme update-workflows" (or "climaybe update-workflows") to refresh workflows.'));
|
|
262
|
+
console.log(pc.dim(' Use "climaybe theme reinit" (or "climaybe reinit") to reinitialize from scratch.\n'));
|
|
255
263
|
return;
|
|
256
264
|
}
|
|
257
265
|
}
|
|
@@ -260,6 +268,13 @@ export async function initCommand() {
|
|
|
260
268
|
}
|
|
261
269
|
|
|
262
270
|
export async function reinitCommand() {
|
|
263
|
-
console.log(pc.bold('\n climaybe — Reinitialize CI/CD
|
|
271
|
+
console.log(pc.bold('\n climaybe — Reinitialize theme CI/CD setup\n'));
|
|
272
|
+
|
|
273
|
+
if (getProjectType() === 'app') {
|
|
274
|
+
console.log(pc.red(' This repo is a Shopify app (project_type: app).'));
|
|
275
|
+
console.log(pc.dim(' Use: npx climaybe app init\n'));
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
264
279
|
await runInitFlow();
|
|
265
280
|
}
|
package/src/commands/switch.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import pc from 'picocolors';
|
|
2
2
|
import { getStoreAliases, getMode } from '../lib/config.js';
|
|
3
3
|
import { storesToRoot } from '../lib/store-sync.js';
|
|
4
|
+
import { requireThemeProject } from '../lib/theme-guard.js';
|
|
4
5
|
|
|
5
6
|
export async function switchCommand(alias) {
|
|
6
7
|
console.log(pc.bold('\n climaybe — Switch Store\n'));
|
|
7
8
|
|
|
9
|
+
if (!requireThemeProject()) return;
|
|
10
|
+
|
|
8
11
|
const mode = getMode();
|
|
9
12
|
if (mode !== 'multi') {
|
|
10
13
|
console.log(pc.yellow(' Switch is only available in multi-store mode.\n'));
|
|
@@ -22,6 +25,6 @@ export async function switchCommand(alias) {
|
|
|
22
25
|
if (ok) {
|
|
23
26
|
console.log(pc.bold(pc.green(`\n Switched to store: ${alias}\n`)));
|
|
24
27
|
console.log(pc.dim(' Root JSON files now reflect this store\'s data.'));
|
|
25
|
-
console.log(pc.dim(' Use "climaybe sync" to write changes back.\n'));
|
|
28
|
+
console.log(pc.dim(' Use "climaybe theme sync" (or "climaybe sync") to write changes back.\n'));
|
|
26
29
|
}
|
|
27
30
|
}
|
package/src/commands/sync.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import pc from 'picocolors';
|
|
2
2
|
import { getStoreAliases, getMode, readConfig } from '../lib/config.js';
|
|
3
3
|
import { rootToStores } from '../lib/store-sync.js';
|
|
4
|
+
import { requireThemeProject } from '../lib/theme-guard.js';
|
|
4
5
|
|
|
5
6
|
export async function syncCommand(alias) {
|
|
6
7
|
console.log(pc.bold('\n climaybe — Sync to Store\n'));
|
|
7
8
|
|
|
9
|
+
if (!requireThemeProject()) return;
|
|
10
|
+
|
|
8
11
|
const mode = getMode();
|
|
9
12
|
if (mode !== 'multi') {
|
|
10
13
|
console.log(pc.yellow(' Sync is only available in multi-store mode.\n'));
|
|
@@ -22,7 +25,7 @@ export async function syncCommand(alias) {
|
|
|
22
25
|
|
|
23
26
|
if (!alias) {
|
|
24
27
|
console.log(pc.red(' No alias provided and no default store found.'));
|
|
25
|
-
console.log(pc.dim(` Usage: climaybe sync <alias>`));
|
|
28
|
+
console.log(pc.dim(` Usage: climaybe theme sync <alias>`));
|
|
26
29
|
console.log(pc.dim(` Available: ${aliases.join(', ')}\n`));
|
|
27
30
|
return;
|
|
28
31
|
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import pc from 'picocolors';
|
|
2
2
|
import { getMode, isBuildWorkflowsEnabled, isPreviewWorkflowsEnabled, readConfig } from '../lib/config.js';
|
|
3
3
|
import { scaffoldWorkflows } from '../lib/workflows.js';
|
|
4
|
+
import { requireThemeProject } from '../lib/theme-guard.js';
|
|
4
5
|
|
|
5
6
|
export async function updateWorkflowsCommand() {
|
|
6
7
|
console.log(pc.bold('\n climaybe — Update Workflows\n'));
|
|
7
8
|
|
|
9
|
+
if (!requireThemeProject()) return;
|
|
10
|
+
|
|
8
11
|
const config = readConfig();
|
|
9
12
|
if (!config?.stores) {
|
|
10
|
-
console.log(pc.red(' No climaybe config found. Run "climaybe init" first.\n'));
|
|
13
|
+
console.log(pc.red(' No climaybe config found. Run "climaybe theme init" (or "climaybe init") first.\n'));
|
|
11
14
|
return;
|
|
12
15
|
}
|
|
13
16
|
|
package/src/index.js
CHANGED
|
@@ -7,57 +7,79 @@ import { updateWorkflowsCommand } from './commands/update-workflows.js';
|
|
|
7
7
|
import { ensureBranchesCommand } from './commands/ensure-branches.js';
|
|
8
8
|
import { setupCommitlintCommand } from './commands/setup-commitlint.js';
|
|
9
9
|
import { addCursorSkillCommand } from './commands/add-cursor-skill.js';
|
|
10
|
+
import { appInitCommand } from './commands/app-init.js';
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
|
-
*
|
|
13
|
-
* @param {
|
|
14
|
-
* @param {string} [packageDir] - Package root dir (shown with --version so user can see which install is running).
|
|
13
|
+
* Register theme CI/CD commands on a Commander instance (root or `theme` subgroup).
|
|
14
|
+
* @param {import('commander').Command} cmd
|
|
15
15
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const versionDisplay = packageDir ? `${version}\n from: ${packageDir}` : version;
|
|
19
|
-
|
|
20
|
-
program
|
|
21
|
-
.name('climaybe')
|
|
22
|
-
.description('Shopify CI/CD CLI — scaffolds workflows, branch strategy, and store config')
|
|
23
|
-
.version(versionDisplay);
|
|
24
|
-
|
|
25
|
-
program
|
|
16
|
+
function registerThemeCommands(cmd) {
|
|
17
|
+
cmd
|
|
26
18
|
.command('init')
|
|
27
19
|
.description('Initialize CI/CD setup for a Shopify theme repo')
|
|
28
20
|
.action(initCommand);
|
|
29
21
|
|
|
30
|
-
|
|
22
|
+
cmd
|
|
31
23
|
.command('reinit')
|
|
32
|
-
.description('Reinitialize CI/CD
|
|
24
|
+
.description('Reinitialize theme CI/CD (re-scaffold workflows from templates)')
|
|
33
25
|
.action(reinitCommand);
|
|
34
26
|
|
|
35
|
-
|
|
27
|
+
cmd
|
|
36
28
|
.command('add-store')
|
|
37
29
|
.description('Add a new store to an existing multi-store config')
|
|
38
30
|
.action(addStoreCommand);
|
|
39
31
|
|
|
40
|
-
|
|
32
|
+
cmd
|
|
41
33
|
.command('switch')
|
|
42
34
|
.argument('<alias>', 'Store alias to switch to')
|
|
43
35
|
.description('Switch local dev environment to a specific store')
|
|
44
36
|
.action(switchCommand);
|
|
45
37
|
|
|
46
|
-
|
|
38
|
+
cmd
|
|
47
39
|
.command('sync')
|
|
48
40
|
.argument('[alias]', 'Store alias to sync to (defaults to active store)')
|
|
49
41
|
.description('Sync root JSON files back to a store directory')
|
|
50
42
|
.action(syncCommand);
|
|
51
43
|
|
|
52
|
-
|
|
44
|
+
cmd
|
|
53
45
|
.command('update-workflows')
|
|
54
46
|
.description('Refresh GitHub Actions workflows from latest bundled templates')
|
|
55
47
|
.action(updateWorkflowsCommand);
|
|
56
48
|
|
|
57
|
-
|
|
49
|
+
cmd
|
|
58
50
|
.command('ensure-branches')
|
|
59
51
|
.description('Create missing staging and per-store branches from current HEAD (then push)')
|
|
60
52
|
.action(ensureBranchesCommand);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create the CLI program (for testing and for run).
|
|
57
|
+
* @param {string} [version] - Version string (from bin/cli.js when run as CLI; from package.json in tests).
|
|
58
|
+
* @param {string} [packageDir] - Package root dir (shown with --version so user can see which install is running).
|
|
59
|
+
*/
|
|
60
|
+
export function createProgram(version = '0.0.0', packageDir = '') {
|
|
61
|
+
const program = new Command();
|
|
62
|
+
const versionDisplay = packageDir ? `${version}\n from: ${packageDir}` : version;
|
|
63
|
+
|
|
64
|
+
program
|
|
65
|
+
.name('climaybe')
|
|
66
|
+
.description(
|
|
67
|
+
'Shopify CLI — theme CI/CD (workflows, branches, stores) and app repo helpers (commitlint, Cursor bundle)'
|
|
68
|
+
)
|
|
69
|
+
.version(versionDisplay);
|
|
70
|
+
|
|
71
|
+
const theme = program
|
|
72
|
+
.command('theme')
|
|
73
|
+
.description('Shopify theme CI/CD: workflows, branches, and multi-store config');
|
|
74
|
+
|
|
75
|
+
registerThemeCommands(theme);
|
|
76
|
+
registerThemeCommands(program);
|
|
77
|
+
|
|
78
|
+
const app = program.command('app').description('Shopify app repo helpers (no theme workflows)');
|
|
79
|
+
app
|
|
80
|
+
.command('init')
|
|
81
|
+
.description('Set up commitlint, Cursor rules/skills, and project_type: app in package.json')
|
|
82
|
+
.action(appInitCommand);
|
|
61
83
|
|
|
62
84
|
program
|
|
63
85
|
.command('setup-commitlint')
|
package/src/lib/config.js
CHANGED
|
@@ -39,11 +39,20 @@ export function readConfig(cwd = process.cwd()) {
|
|
|
39
39
|
return pkg?.config ?? null;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
* @typedef {object} WriteConfigOptions
|
|
44
|
+
* @property {string} [defaultPackageName] - When creating package.json, set `name` (default: shopify-theme).
|
|
45
|
+
*/
|
|
46
|
+
|
|
42
47
|
/**
|
|
43
48
|
* Write/merge the climaybe config section into package.json.
|
|
44
49
|
* Creates package.json if it doesn't exist.
|
|
50
|
+
* @param {object} config
|
|
51
|
+
* @param {string} [cwd]
|
|
52
|
+
* @param {WriteConfigOptions} [options]
|
|
45
53
|
*/
|
|
46
|
-
export function writeConfig(config, cwd = process.cwd()) {
|
|
54
|
+
export function writeConfig(config, cwd = process.cwd(), options = {}) {
|
|
55
|
+
const defaultPackageName = options.defaultPackageName ?? 'shopify-theme';
|
|
47
56
|
let pkg = readPkg(cwd);
|
|
48
57
|
if (!pkg) {
|
|
49
58
|
let version = '1.0.0';
|
|
@@ -54,7 +63,7 @@ export function writeConfig(config, cwd = process.cwd()) {
|
|
|
54
63
|
// not a git repo or no tags
|
|
55
64
|
}
|
|
56
65
|
pkg = {
|
|
57
|
-
name:
|
|
66
|
+
name: defaultPackageName,
|
|
58
67
|
version,
|
|
59
68
|
private: true,
|
|
60
69
|
config: {},
|
|
@@ -64,6 +73,34 @@ export function writeConfig(config, cwd = process.cwd()) {
|
|
|
64
73
|
writePkg(pkg, cwd);
|
|
65
74
|
}
|
|
66
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Resolved project kind for guards and init flows.
|
|
78
|
+
* - `app` only when config explicitly sets project_type: app.
|
|
79
|
+
* - Otherwise `theme` (including missing config and legacy theme repos without project_type).
|
|
80
|
+
* @param {string} [cwd]
|
|
81
|
+
* @returns {'theme' | 'app'}
|
|
82
|
+
*/
|
|
83
|
+
export function getProjectType(cwd = process.cwd()) {
|
|
84
|
+
const config = readConfig(cwd);
|
|
85
|
+
if (config?.project_type === 'app') return 'app';
|
|
86
|
+
return 'theme';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* True if this repo is clearly a theme project (explicit project_type or legacy stores).
|
|
91
|
+
* Used to refuse `app init` on theme repos.
|
|
92
|
+
* @param {string} [cwd]
|
|
93
|
+
*/
|
|
94
|
+
export function isThemeProjectForAppInit(cwd = process.cwd()) {
|
|
95
|
+
const config = readConfig(cwd);
|
|
96
|
+
if (!config) return false;
|
|
97
|
+
if (config.project_type === 'theme') return true;
|
|
98
|
+
if (config.stores && typeof config.stores === 'object' && Object.keys(config.stores).length > 0) {
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
|
|
67
104
|
/**
|
|
68
105
|
* Get the list of store aliases from config.
|
|
69
106
|
*/
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import pc from 'picocolors';
|
|
2
|
+
import { getProjectType } from './config.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @param {string} [cwd]
|
|
6
|
+
* @returns {boolean} false if the repo is an app project (caller should return early).
|
|
7
|
+
*/
|
|
8
|
+
export function requireThemeProject(cwd = process.cwd()) {
|
|
9
|
+
if (getProjectType(cwd) !== 'app') return true;
|
|
10
|
+
console.log(pc.red(' This command is for theme repos only. This project has project_type: app.'));
|
|
11
|
+
console.log(pc.dim(' Use climaybe app init for app setup; theme stores and workflows do not apply here.\n'));
|
|
12
|
+
return false;
|
|
13
|
+
}
|