gflows 0.1.7 → 0.1.9
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 +109 -77
- package/package.json +1 -1
- package/src/cli.ts +4 -0
- package/src/commands/delete.ts +5 -1
- package/src/commands/finish.ts +1 -1
- package/src/commands/help.ts +3 -1
- package/src/commands/init.ts +25 -5
- package/src/commands/list.ts +5 -1
- package/src/commands/start.ts +1 -1
- package/src/commands/status.ts +5 -1
- package/src/commands/switch.ts +5 -1
- package/src/config.ts +54 -34
- package/src/index.ts +1 -1
- package/src/types.ts +4 -0
package/README.md
CHANGED
|
@@ -40,29 +40,55 @@ git --version
|
|
|
40
40
|
|
|
41
41
|
## Installation
|
|
42
42
|
|
|
43
|
-
**
|
|
43
|
+
**As a dev dependency (recommended):**
|
|
44
|
+
|
|
45
|
+
**npm**
|
|
44
46
|
|
|
45
47
|
```bash
|
|
46
|
-
|
|
47
|
-
cd gflows
|
|
48
|
-
bun install
|
|
49
|
-
bun link # global `gflows` (or: npm link)
|
|
48
|
+
npm install --save-dev gflows
|
|
50
49
|
```
|
|
51
50
|
|
|
52
|
-
**
|
|
51
|
+
**Bun**
|
|
53
52
|
|
|
54
53
|
```bash
|
|
55
|
-
bun
|
|
56
|
-
# or
|
|
57
|
-
bun run src/cli.ts -- <command> ...
|
|
54
|
+
bun add --dev gflows
|
|
58
55
|
```
|
|
59
56
|
|
|
60
|
-
**
|
|
57
|
+
**JSR (npm / npx)**
|
|
61
58
|
|
|
62
59
|
```bash
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
60
|
+
npx jsr add --dev @alialnaghmoush/gflows
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**JSR (Bun / bunx)**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
bunx jsr add --dev @alialnaghmoush/gflows
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**JSR (Deno)**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
deno add --dev jsr:@alialnaghmoush/gflows
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Available on:
|
|
76
|
+
|
|
77
|
+
- [npm: gflows](https://www.npmjs.com/package/gflows)
|
|
78
|
+
- [JSR: @alialnaghmoush/gflows](https://jsr.io/@alialnaghmoush/gflows)
|
|
79
|
+
|
|
80
|
+
**Global install (optional):**
|
|
81
|
+
|
|
82
|
+
**npm**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm install --global gflows
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Bun**
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
bun add --global gflows
|
|
66
92
|
```
|
|
67
93
|
|
|
68
94
|
---
|
|
@@ -84,12 +110,17 @@ You can override branch names and prefixes via [configuration](#configuration).
|
|
|
84
110
|
|
|
85
111
|
## Quick start
|
|
86
112
|
|
|
87
|
-
**1. One-time setup**
|
|
113
|
+
**1. One-time setup** — In your repo, ensure `main` exists and create `dev`:
|
|
88
114
|
|
|
89
115
|
```bash
|
|
90
116
|
gflows init
|
|
91
|
-
|
|
92
|
-
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Optional: use `--push` to push `dev` to the remote, or `--dry-run` to preview. To use different branch or remote names, pass `--main`, `--dev`, and `--remote`; those values are written to `.gflows.json` for future runs.
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
gflows init --push # create dev, then push to origin
|
|
123
|
+
gflows init --main main --dev develop --remote origin # custom names, persisted to .gflows.json
|
|
93
124
|
```
|
|
94
125
|
|
|
95
126
|
**2. Daily development** (feature → dev):
|
|
@@ -125,19 +156,21 @@ gflows finish hotfix --push # merge to main, then dev; tag v1.3.1;
|
|
|
125
156
|
|
|
126
157
|
### Summary table
|
|
127
158
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
|
131
|
-
| `
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
135
|
-
| `
|
|
136
|
-
| `
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
159
|
+
|
|
160
|
+
| Command | Short | Description |
|
|
161
|
+
| ------------ | ----- | ------------------------------------------------------------------------- |
|
|
162
|
+
| `init` | `-I` | Ensure main exists; create dev from main. |
|
|
163
|
+
| `start` | `-S` | Create a workflow branch (requires type + name). |
|
|
164
|
+
| `finish` | `-F` | Merge branch into target(s), optional tag (release/hotfix), delete, push. |
|
|
165
|
+
| `switch` | `-W` | Switch to a workflow branch (picker or name). |
|
|
166
|
+
| `delete` | `-L` | Delete local workflow branch(es). Never main/dev. |
|
|
167
|
+
| `list` | `-l` | List workflow branches; optional type filter and remote. |
|
|
168
|
+
| `bump` | — | Bump or rollback package version (patch/minor/major). |
|
|
169
|
+
| `completion` | — | Print shell completion script (bash | zsh | fish). |
|
|
170
|
+
| `status` | `-t` | Show current branch, type, base, merge target(s), ahead/behind. |
|
|
171
|
+
| `help` | `-h` | Show usage and quick reference. |
|
|
172
|
+
| `version` | `-V` | Show version. |
|
|
173
|
+
|
|
141
174
|
|
|
142
175
|
**Branch types (for start/finish/list):** `feature` (`-f`), `bugfix` (`-b`), `chore` (`-c`), `release` (`-r`), `hotfix` (`-x`), `spike` (`-e`).
|
|
143
176
|
|
|
@@ -147,16 +180,19 @@ gflows finish hotfix --push # merge to main, then dev; tag v1.3.1;
|
|
|
147
180
|
|
|
148
181
|
Ensures the **main** branch exists (exits with error if not). Creates **dev** from main if it does not exist; does nothing if dev already exists. Does not rewrite or force-push.
|
|
149
182
|
|
|
183
|
+
You can set and persist config with `**--main`**, `**--dev**`, and `**-R`/`--remote**`. Any of these flags cause init to write or update `.gflows.json` with the given values (after a successful init; skipped with `--dry-run`).
|
|
184
|
+
|
|
150
185
|
**Examples:**
|
|
151
186
|
|
|
152
187
|
```bash
|
|
153
188
|
gflows init
|
|
154
189
|
gflows init --push # push dev to remote after creating
|
|
190
|
+
gflows init --main main --dev develop --remote origin # create dev branch "develop", persist to .gflows.json
|
|
155
191
|
gflows init -C ../other-repo # run in another directory
|
|
156
192
|
gflows init --dry-run # log intended actions only
|
|
157
193
|
```
|
|
158
194
|
|
|
159
|
-
**Flags:** `--push`, `-C`/`--path <dir>`, `--dry-run`, `-v`/`--verbose`, `-q`/`--quiet`.
|
|
195
|
+
**Flags:** `--push`, `--main <name>`, `--dev <name>`, `-R`/`--remote <name>` (main/dev/remote are persisted to `.gflows.json` when provided), `-C`/`--path <dir>`, `--dry-run`, `-v`/`--verbose`, `-q`/`--quiet`.
|
|
160
196
|
|
|
161
197
|
---
|
|
162
198
|
|
|
@@ -316,14 +352,16 @@ gflows -V
|
|
|
316
352
|
|
|
317
353
|
## Branch types in detail
|
|
318
354
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
|
322
|
-
|
|
|
323
|
-
|
|
|
324
|
-
|
|
|
325
|
-
|
|
|
326
|
-
|
|
|
355
|
+
|
|
356
|
+
| Type | Short | Base (default) | With `-o main` | Merge target(s) | Tag |
|
|
357
|
+
| ------- | ----- | -------------- | -------------- | -------------------------- | --- |
|
|
358
|
+
| feature | `-f` | dev | — | dev | no |
|
|
359
|
+
| bugfix | `-b` | dev | main | dev (or main if from main) | no |
|
|
360
|
+
| chore | `-c` | dev | — | dev | no |
|
|
361
|
+
| release | `-r` | dev | — | main, then dev | yes |
|
|
362
|
+
| hotfix | `-x` | main | — | main, then dev | yes |
|
|
363
|
+
| spike | `-e` | dev | — | dev | no |
|
|
364
|
+
|
|
327
365
|
|
|
328
366
|
- **feature** — New functionality; branches from dev, merges to dev.
|
|
329
367
|
- **bugfix** — Bug fixes. Usually from dev → dev. Use `-o main` when fixing a bug on production (branch from main, merge to main then dev).
|
|
@@ -343,9 +381,8 @@ Configuration is **optional**. Override branch names, remote, and branch **prefi
|
|
|
343
381
|
**Resolution order** (later overrides earlier):
|
|
344
382
|
|
|
345
383
|
1. Built-in defaults (`main`, `dev`, `origin`, and default prefixes).
|
|
346
|
-
2. Repo config file:
|
|
347
|
-
3.
|
|
348
|
-
4. CLI (e.g. `-R`/`--remote` for push).
|
|
384
|
+
2. Repo config file: `**.gflows.json`** in repo root, or `**gflows**` key in `**package.json**`.
|
|
385
|
+
3. CLI (e.g. `--main`, `--dev`, `-R`/`--remote`).
|
|
349
386
|
|
|
350
387
|
Only include keys you want to override; the rest stay default. Invalid or malformed config is ignored (with an optional warning when using `-v`).
|
|
351
388
|
|
|
@@ -401,21 +438,12 @@ Only include keys you want to override; the rest stay default. Invalid or malfor
|
|
|
401
438
|
}
|
|
402
439
|
```
|
|
403
440
|
|
|
404
|
-
### Environment variables
|
|
405
|
-
|
|
406
|
-
```bash
|
|
407
|
-
export GFLOWS_MAIN=main
|
|
408
|
-
export GFLOWS_DEV=develop
|
|
409
|
-
export GFLOWS_REMOTE=origin
|
|
410
|
-
gflows init
|
|
411
|
-
```
|
|
412
|
-
|
|
413
441
|
---
|
|
414
442
|
|
|
415
443
|
## Scripting and CI
|
|
416
444
|
|
|
417
445
|
- **Non-interactive:** When stdin is **not** a TTY, gflows does **not** show pickers (e.g. for switch, delete, or finish `-B`). You must pass branch names explicitly or the command exits with a clear message (exit code 1).
|
|
418
|
-
- **Skip confirmations:** Use
|
|
446
|
+
- **Skip confirmations:** Use `**-y`/`--yes`** to accept defaults (e.g. "Delete branch after finish?" → no delete unless you also passed `-D`).
|
|
419
447
|
- **Exit codes:** Use them in scripts: `0` success, `1` usage/validation, `2` Git/system error.
|
|
420
448
|
|
|
421
449
|
**Examples:**
|
|
@@ -445,11 +473,13 @@ gflows -C ./packages/api list
|
|
|
445
473
|
|
|
446
474
|
## Exit codes
|
|
447
475
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
|
451
|
-
| **
|
|
452
|
-
| **
|
|
476
|
+
|
|
477
|
+
| Code | Meaning | Typical causes |
|
|
478
|
+
| ----- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
479
|
+
| **0** | Success | Command completed without error. |
|
|
480
|
+
| **1** | Usage / validation | Missing type or name for `start`; invalid branch name or version format; wrong/missing positionals for non-TTY. |
|
|
481
|
+
| **2** | Git / system | Not a Git repo; branch not found; dirty working tree (start without `--force`); merge conflict on finish; rebase/merge in progress; detached HEAD; finish on main/dev; tag already exists; push failed after merge. |
|
|
482
|
+
|
|
453
483
|
|
|
454
484
|
**Validation (exit 1):** Invalid version (e.g. `start release foo`), invalid branch name (e.g. `feature/bad..name`), or missing required args in non-interactive mode.
|
|
455
485
|
|
|
@@ -459,19 +489,21 @@ gflows -C ./packages/api list
|
|
|
459
489
|
|
|
460
490
|
## Troubleshooting
|
|
461
491
|
|
|
462
|
-
| Situation | What to do |
|
|
463
|
-
|-----------|------------|
|
|
464
|
-
| **"Not a Git repository"** | Run from a directory that contains `.git`, or use `-C <path>` to point to the repo root. |
|
|
465
|
-
| **"Working tree has uncommitted changes"** | Commit or stash changes before `start`, or use `--force` (only when you intend to carry uncommitted work). |
|
|
466
|
-
| **"Merge conflict while merging into …"** | Resolve conflicts in your working tree, then run `git add` and `git merge --continue` (or `git merge --abort` to cancel). Re-run `gflows finish` after resolving if needed. |
|
|
467
|
-
| **"Tag v1.2.3 already exists"** | Use a new version for the release/hotfix, or delete/move the tag if you know what you’re doing. gflows does not overwrite tags. |
|
|
468
|
-
| **"Cannot finish the long-lived branch main/dev"** | You’re on main or dev. Checkout a workflow branch first, or use `-B <branch>` to finish another branch. |
|
|
469
|
-
| **"HEAD is detached"** | Checkout a branch (e.g. `git checkout dev`) before running `start` or `finish`. |
|
|
470
|
-
| **"A rebase or merge is in progress"** | Run `git rebase --abort` or `git merge --abort`, or complete the operation, then retry gflows. |
|
|
471
|
-
| **Picker not showing / "requires branch name"** | Without a TTY, gflows does not show interactive pickers. Pass the branch name explicitly (e.g. `-B feature/xyz` or `gflows switch feature/xyz`). |
|
|
472
|
-
| **Wrong remote or branch names** | Set `GFLOWS_MAIN`, `GFLOWS_DEV`, `GFLOWS_REMOTE` or use `.gflows.json` / `package.json` "gflows" key. Use `-R` for one-off remote override. |
|
|
473
492
|
|
|
474
|
-
|
|
493
|
+
| Situation | What to do |
|
|
494
|
+
| -------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
495
|
+
| **"Not a Git repository"** | Run from a directory that contains `.git`, or use `-C <path>` to point to the repo root. |
|
|
496
|
+
| **"Working tree has uncommitted changes"** | Commit or stash changes before `start`, or use `--force` (only when you intend to carry uncommitted work). |
|
|
497
|
+
| **"Merge conflict while merging into …"** | Resolve conflicts in your working tree, then run `git add` and `git merge --continue` (or `git merge --abort` to cancel). Re-run `gflows finish` after resolving if needed. |
|
|
498
|
+
| **"Tag v1.2.3 already exists"** | Use a new version for the release/hotfix, or delete/move the tag if you know what you’re doing. gflows does not overwrite tags. |
|
|
499
|
+
| **"Cannot finish the long-lived branch main/dev"** | You’re on main or dev. Checkout a workflow branch first, or use `-B <branch>` to finish another branch. |
|
|
500
|
+
| **"HEAD is detached"** | Checkout a branch (e.g. `git checkout dev`) before running `start` or `finish`. |
|
|
501
|
+
| **"A rebase or merge is in progress"** | Run `git rebase --abort` or `git merge --abort`, or complete the operation, then retry gflows. |
|
|
502
|
+
| **Picker not showing / "requires branch name"** | Without a TTY, gflows does not show interactive pickers. Pass the branch name explicitly (e.g. `-B feature/xyz` or `gflows switch feature/xyz`). |
|
|
503
|
+
| **Wrong remote or branch names** | Use `.gflows.json` or `package.json` "gflows" key, or `gflows init --main … --dev … --remote …`. Use `-R` for one-off remote override. |
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
Use `**-v`/`--verbose`** to see git commands and extra diagnostics; combine with the error message to pinpoint the cause.
|
|
475
507
|
|
|
476
508
|
---
|
|
477
509
|
|
|
@@ -525,22 +557,22 @@ bun run publish:all -- --force # skip pre-publish checks (clean tree, branch m
|
|
|
525
557
|
|
|
526
558
|
1. Ensure you’re on **main** with a clean working tree (or use `--force` when intentional).
|
|
527
559
|
2. Bump version and tag in Git yourself, or use gflows bump + your own commit:
|
|
528
|
-
|
|
560
|
+
```bash
|
|
529
561
|
gflows bump up minor --dry-run # confirm
|
|
530
562
|
gflows bump up minor
|
|
531
563
|
git add package.json jsr.json && git commit -m "chore: bump to 1.4.0"
|
|
532
|
-
|
|
564
|
+
```
|
|
533
565
|
3. Run the publish script:
|
|
534
|
-
|
|
566
|
+
```bash
|
|
535
567
|
bun run publish:all -- --dry-run # verify
|
|
536
568
|
bun run publish:all
|
|
537
|
-
|
|
569
|
+
```
|
|
538
570
|
4. Optionally push main and tags:
|
|
539
|
-
|
|
571
|
+
```bash
|
|
540
572
|
git push origin main --tags
|
|
541
|
-
|
|
573
|
+
```
|
|
542
574
|
|
|
543
|
-
**Version sync:** The script reads `version` from **package.json** and writes it to **jsr.json** before publishing so the two registries never drift. Use
|
|
575
|
+
**Version sync:** The script reads `version` from **package.json** and writes it to **jsr.json** before publishing so the two registries never drift. Use `**gflows bump`** to change the version; the script does not bump for you.
|
|
544
576
|
|
|
545
577
|
### JSR score (100%)
|
|
546
578
|
|
|
@@ -548,10 +580,10 @@ To get a 100% score on [JSR](https://jsr.io/@alialnaghmoush/gflows/score):
|
|
|
548
580
|
|
|
549
581
|
1. **Description** — Set in `jsr.json` (already added). If the score still shows 0/1, set the description in [package settings](https://jsr.io/@alialnaghmoush/gflows/settings) on JSR.
|
|
550
582
|
2. **Runtime compatibility** — In [package settings](https://jsr.io/@alialnaghmoush/gflows/settings), open “Runtime compatibility” and mark at least **Bun** and **Node.js** (or others) as **Supported**.
|
|
551
|
-
3. **Provenance** — The repo includes [
|
|
583
|
+
3. **Provenance** — The repo includes `[.github/workflows/publish.yml](.github/workflows/publish.yml)` (test, lint, then publish to npm and JSR). In JSR package settings, **link** the package to this GitHub repository. Add `NPM_TOKEN` in repo Secrets for npm. After that, pushes to `main` run CI and publish both registries; JSR records provenance.
|
|
552
584
|
|
|
553
585
|
---
|
|
554
586
|
|
|
555
587
|
## License
|
|
556
588
|
|
|
557
|
-
See [LICENSE](LICENSE) in this repository.
|
|
589
|
+
See [LICENSE](LICENSE) in this repository.
|
package/package.json
CHANGED
package/src/cli.ts
CHANGED
|
@@ -78,6 +78,8 @@ function buildParseArgsOptions() {
|
|
|
78
78
|
// Common
|
|
79
79
|
push: { type: "boolean" as const, short: "p" },
|
|
80
80
|
noPush: { type: "boolean" as const, short: "P" },
|
|
81
|
+
main: { type: "string" as const },
|
|
82
|
+
dev: { type: "string" as const },
|
|
81
83
|
remote: { type: "string" as const, short: "R" },
|
|
82
84
|
from: { type: "string" as const, short: "o" },
|
|
83
85
|
branch: { type: "string" as const, short: "B" },
|
|
@@ -276,6 +278,8 @@ export function parse(argv: string[] = Bun.argv.slice(2)): ParsedArgs {
|
|
|
276
278
|
bumpType,
|
|
277
279
|
push: v.push === true,
|
|
278
280
|
noPush: v.noPush === true,
|
|
281
|
+
main: typeof v.main === "string" && v.main.trim() !== "" ? v.main.trim() : undefined,
|
|
282
|
+
dev: typeof v.dev === "string" && v.dev.trim() !== "" ? v.dev.trim() : undefined,
|
|
279
283
|
remote: typeof v.remote === "string" ? v.remote : undefined,
|
|
280
284
|
branch: typeof v.branch === "string" ? v.branch : undefined,
|
|
281
285
|
yes: v.yes === true,
|
package/src/commands/delete.ts
CHANGED
|
@@ -50,7 +50,11 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
50
50
|
if (err instanceof NotRepoError) throw err;
|
|
51
51
|
throw err;
|
|
52
52
|
});
|
|
53
|
-
const config = resolveConfig(root
|
|
53
|
+
const config = resolveConfig(root, {
|
|
54
|
+
main: args.main,
|
|
55
|
+
dev: args.dev,
|
|
56
|
+
remote: args.remote,
|
|
57
|
+
});
|
|
54
58
|
const { main, dev, prefixes } = config;
|
|
55
59
|
|
|
56
60
|
const fromPositionals = (rawBranchNames ?? [])
|
package/src/commands/finish.ts
CHANGED
|
@@ -91,7 +91,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
91
91
|
const repoRoot = await resolveRepoRoot(args.cwd);
|
|
92
92
|
const config = resolveConfig(
|
|
93
93
|
repoRoot,
|
|
94
|
-
{ remote: args.remote },
|
|
94
|
+
{ main: args.main, dev: args.dev, remote: args.remote },
|
|
95
95
|
{ verbose: args.verbose }
|
|
96
96
|
);
|
|
97
97
|
|
package/src/commands/help.ts
CHANGED
|
@@ -33,7 +33,9 @@ Types: feature (-f), bugfix (-b), chore (-c), release (-r), hotfix (-x), spike (
|
|
|
33
33
|
Common flags:
|
|
34
34
|
-p, --push Push after init/start/finish
|
|
35
35
|
-P, --no-push Do not push
|
|
36
|
-
|
|
36
|
+
--main <name> Main branch (init: persist to .gflows.json)
|
|
37
|
+
--dev <name> Dev branch (init: persist to .gflows.json)
|
|
38
|
+
-R, --remote <name> Remote for push (init: persist to .gflows.json)
|
|
37
39
|
-o, --from <branch> Base branch override (e.g. -o main for bugfix)
|
|
38
40
|
-B, --branch <name> Branch name (finish: branch to finish)
|
|
39
41
|
-y, --yes Skip confirmations
|
package/src/commands/init.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Init command: ensure main exists, create dev from main, optional push and dry-run.
|
|
3
|
+
* Can persist main, dev, and remote to .gflows.json via --main, --dev, --remote.
|
|
3
4
|
* @module commands/init
|
|
4
5
|
*/
|
|
5
6
|
|
|
6
|
-
import { resolveConfig } from "../config.js";
|
|
7
|
+
import { resolveConfig, writeConfigFile } from "../config.js";
|
|
7
8
|
import { BranchNotFoundError, NotRepoError } from "../errors.js";
|
|
8
9
|
import {
|
|
9
10
|
branchList,
|
|
@@ -18,14 +19,21 @@ import type { ParsedArgs } from "../types.js";
|
|
|
18
19
|
* Runs the init command: ensure main exists, create dev from main if missing, optional push.
|
|
19
20
|
* Pre-check: cwd (or -C) must be a git repo; main branch must exist (exit 2 otherwise).
|
|
20
21
|
* Skips creating dev if it already exists. Supports --dry-run and --push.
|
|
22
|
+
* When --main, --dev, or --remote are passed, writes or updates .gflows.json with those values.
|
|
21
23
|
*
|
|
22
|
-
* @param args - Parsed CLI args (cwd, dryRun, push, noPush, remote, verbose, quiet).
|
|
24
|
+
* @param args - Parsed CLI args (cwd, dryRun, push, noPush, main, dev, remote, verbose, quiet).
|
|
23
25
|
*/
|
|
24
26
|
export async function run(args: ParsedArgs): Promise<void> {
|
|
25
27
|
const repoRoot = await resolveRepoRoot(args.cwd);
|
|
26
|
-
const config = resolveConfig(
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
const config = resolveConfig(
|
|
29
|
+
repoRoot,
|
|
30
|
+
{
|
|
31
|
+
main: args.main,
|
|
32
|
+
dev: args.dev,
|
|
33
|
+
remote: args.remote,
|
|
34
|
+
},
|
|
35
|
+
{ verbose: args.verbose }
|
|
36
|
+
);
|
|
29
37
|
|
|
30
38
|
const opts = {
|
|
31
39
|
dryRun: args.dryRun,
|
|
@@ -67,4 +75,16 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
67
75
|
console.error(`gflows: pushed '${config.dev}' to '${config.remote}'.`);
|
|
68
76
|
}
|
|
69
77
|
}
|
|
78
|
+
|
|
79
|
+
const hasConfigFlags = args.main !== undefined || args.dev !== undefined || args.remote !== undefined;
|
|
80
|
+
if (!args.dryRun && hasConfigFlags) {
|
|
81
|
+
writeConfigFile(repoRoot, {
|
|
82
|
+
...(args.main !== undefined && { main: args.main }),
|
|
83
|
+
...(args.dev !== undefined && { dev: args.dev }),
|
|
84
|
+
...(args.remote !== undefined && { remote: args.remote }),
|
|
85
|
+
});
|
|
86
|
+
if (!args.quiet) {
|
|
87
|
+
console.error("gflows: updated .gflows.json with provided options.");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
70
90
|
}
|
package/src/commands/list.ts
CHANGED
|
@@ -57,7 +57,11 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
57
57
|
throw err;
|
|
58
58
|
});
|
|
59
59
|
|
|
60
|
-
const config = resolveConfig(
|
|
60
|
+
const config = resolveConfig(
|
|
61
|
+
root,
|
|
62
|
+
{ main: args.main, dev: args.dev, remote: args.remote },
|
|
63
|
+
{ verbose: !!verbose }
|
|
64
|
+
);
|
|
61
65
|
|
|
62
66
|
if (includeRemote && !dryRun) {
|
|
63
67
|
await fetch(root, config.remote, {
|
package/src/commands/start.ts
CHANGED
|
@@ -53,7 +53,7 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
53
53
|
const repoRoot = await resolveRepoRoot(args.cwd);
|
|
54
54
|
const config = resolveConfig(
|
|
55
55
|
repoRoot,
|
|
56
|
-
{ remote: args.remote },
|
|
56
|
+
{ main: args.main, dev: args.dev, remote: args.remote },
|
|
57
57
|
{ verbose: args.verbose }
|
|
58
58
|
);
|
|
59
59
|
|
package/src/commands/status.ts
CHANGED
|
@@ -74,7 +74,11 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
74
74
|
throw err;
|
|
75
75
|
});
|
|
76
76
|
|
|
77
|
-
const config = resolveConfig(
|
|
77
|
+
const config = resolveConfig(
|
|
78
|
+
root,
|
|
79
|
+
{ main: args.main, dev: args.dev, remote: args.remote },
|
|
80
|
+
{ verbose: !!verbose }
|
|
81
|
+
);
|
|
78
82
|
const current = await getCurrentBranch(root, {
|
|
79
83
|
dryRun: !!dryRun,
|
|
80
84
|
verbose: !!verbose,
|
package/src/commands/switch.ts
CHANGED
|
@@ -49,7 +49,11 @@ export async function run(args: ParsedArgs): Promise<void> {
|
|
|
49
49
|
if (err instanceof NotRepoError) throw err;
|
|
50
50
|
throw err;
|
|
51
51
|
});
|
|
52
|
-
const config = resolveConfig(root
|
|
52
|
+
const config = resolveConfig(root, {
|
|
53
|
+
main: args.main,
|
|
54
|
+
dev: args.dev,
|
|
55
|
+
remote: args.remote,
|
|
56
|
+
});
|
|
53
57
|
|
|
54
58
|
const branchName = (branch?.trim() || name?.trim() || "").trim() || undefined;
|
|
55
59
|
|
package/src/config.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Config resolution for gflows: defaults → repo config file →
|
|
2
|
+
* Config resolution for gflows: defaults → repo config file → CLI overrides.
|
|
3
3
|
* Exposes resolved main, dev, remote, and branch type prefixes for use by commands.
|
|
4
4
|
* @module config
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import type {
|
|
10
10
|
BranchPrefixes,
|
|
@@ -24,11 +24,7 @@ const CONFIG_FILE = ".gflows.json";
|
|
|
24
24
|
const PACKAGE_JSON = "package.json";
|
|
25
25
|
const GFLOWS_KEY = "gflows";
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
const ENV_DEV = "GFLOWS_DEV";
|
|
29
|
-
const ENV_REMOTE = "GFLOWS_REMOTE";
|
|
30
|
-
|
|
31
|
-
/** CLI overrides for main, dev, and remote (e.g. from -R/--remote or future flags). */
|
|
27
|
+
/** CLI overrides for main, dev, and remote (e.g. --main, --dev, -R/--remote). */
|
|
32
28
|
export interface ConfigCliOverrides {
|
|
33
29
|
main?: string;
|
|
34
30
|
dev?: string;
|
|
@@ -121,26 +117,6 @@ function normalizeConfigFile(data: unknown): GflowsConfigFile | null {
|
|
|
121
117
|
return out;
|
|
122
118
|
}
|
|
123
119
|
|
|
124
|
-
/**
|
|
125
|
-
* Returns config overrides from environment (GFLOWS_MAIN, GFLOWS_DEV, GFLOWS_REMOTE).
|
|
126
|
-
*/
|
|
127
|
-
export function getEnvConfigOverrides(): ConfigCliOverrides {
|
|
128
|
-
const overrides: ConfigCliOverrides = {};
|
|
129
|
-
const main = process.env[ENV_MAIN];
|
|
130
|
-
if (typeof main === "string" && main.trim() !== "") {
|
|
131
|
-
overrides.main = main.trim();
|
|
132
|
-
}
|
|
133
|
-
const dev = process.env[ENV_DEV];
|
|
134
|
-
if (typeof dev === "string" && dev.trim() !== "") {
|
|
135
|
-
overrides.dev = dev.trim();
|
|
136
|
-
}
|
|
137
|
-
const remote = process.env[ENV_REMOTE];
|
|
138
|
-
if (typeof remote === "string" && remote.trim() !== "") {
|
|
139
|
-
overrides.remote = remote.trim();
|
|
140
|
-
}
|
|
141
|
-
return overrides;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
120
|
/**
|
|
145
121
|
* Merges prefix overrides into a full Required<BranchPrefixes> (defaults + overrides).
|
|
146
122
|
*/
|
|
@@ -157,11 +133,11 @@ function mergePrefixes(overrides?: BranchPrefixes): Required<BranchPrefixes> {
|
|
|
157
133
|
}
|
|
158
134
|
|
|
159
135
|
/**
|
|
160
|
-
* Resolves full config for the given directory: defaults → file →
|
|
136
|
+
* Resolves full config for the given directory: defaults → file → CLI.
|
|
161
137
|
* Uses dir as the repo root for locating .gflows.json and package.json.
|
|
162
138
|
*
|
|
163
139
|
* @param dir - Directory to read config from (e.g. cwd or resolved -C path).
|
|
164
|
-
* @param cliOverrides - Optional overrides from CLI (e.g. --remote).
|
|
140
|
+
* @param cliOverrides - Optional overrides from CLI (e.g. --main, --dev, --remote).
|
|
165
141
|
* @param options - Optional { verbose } to warn when config file is missing or invalid.
|
|
166
142
|
* @returns Resolved config with main, dev, remote, and full prefixes.
|
|
167
143
|
*/
|
|
@@ -189,11 +165,6 @@ export function resolveConfig(
|
|
|
189
165
|
if (file.prefixes !== undefined) prefixes = mergePrefixes(file.prefixes);
|
|
190
166
|
}
|
|
191
167
|
|
|
192
|
-
const envOverrides = getEnvConfigOverrides();
|
|
193
|
-
if (envOverrides.main !== undefined) main = envOverrides.main;
|
|
194
|
-
if (envOverrides.dev !== undefined) dev = envOverrides.dev;
|
|
195
|
-
if (envOverrides.remote !== undefined) remote = envOverrides.remote;
|
|
196
|
-
|
|
197
168
|
if (cliOverrides?.main !== undefined) main = cliOverrides.main;
|
|
198
169
|
if (cliOverrides?.dev !== undefined) dev = cliOverrides.dev;
|
|
199
170
|
if (cliOverrides?.remote !== undefined) remote = cliOverrides.remote;
|
|
@@ -201,6 +172,55 @@ export function resolveConfig(
|
|
|
201
172
|
return { main, dev, remote, prefixes };
|
|
202
173
|
}
|
|
203
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Writes or updates .gflows.json in dir with the given partial config.
|
|
177
|
+
* Merges with existing .gflows.json if present; only provided keys are updated.
|
|
178
|
+
* Skips keys with empty string values.
|
|
179
|
+
*
|
|
180
|
+
* @param dir - Repo root directory.
|
|
181
|
+
* @param partial - Keys to set (main, dev, remote, prefixes); omitted keys are left unchanged.
|
|
182
|
+
*/
|
|
183
|
+
export function writeConfigFile(
|
|
184
|
+
dir: string,
|
|
185
|
+
partial: Partial<GflowsConfigFile>
|
|
186
|
+
): void {
|
|
187
|
+
const path = join(dir, CONFIG_FILE);
|
|
188
|
+
let existing: GflowsConfigFile = {};
|
|
189
|
+
if (existsSync(path)) {
|
|
190
|
+
try {
|
|
191
|
+
const raw = readFileSync(path, "utf-8");
|
|
192
|
+
const data = JSON.parse(raw) as unknown;
|
|
193
|
+
const normalized = normalizeConfigFile(data);
|
|
194
|
+
if (normalized) existing = normalized;
|
|
195
|
+
} catch {
|
|
196
|
+
// overwrite invalid file
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const merged: GflowsConfigFile = { ...existing };
|
|
200
|
+
if (typeof partial.main === "string" && partial.main.trim() !== "") {
|
|
201
|
+
merged.main = partial.main.trim();
|
|
202
|
+
}
|
|
203
|
+
if (typeof partial.dev === "string" && partial.dev.trim() !== "") {
|
|
204
|
+
merged.dev = partial.dev.trim();
|
|
205
|
+
}
|
|
206
|
+
if (typeof partial.remote === "string" && partial.remote.trim() !== "") {
|
|
207
|
+
merged.remote = partial.remote.trim();
|
|
208
|
+
}
|
|
209
|
+
if (partial.prefixes !== undefined && partial.prefixes !== null && typeof partial.prefixes === "object" && !Array.isArray(partial.prefixes)) {
|
|
210
|
+
const prefs = partial.prefixes as Record<string, unknown>;
|
|
211
|
+
const prefixes: BranchPrefixes = { ...(merged.prefixes ?? {}) };
|
|
212
|
+
const keys: (keyof BranchPrefixes)[] = ["feature", "bugfix", "chore", "release", "hotfix", "spike"];
|
|
213
|
+
for (const k of keys) {
|
|
214
|
+
const v = prefs[k];
|
|
215
|
+
if (typeof v === "string" && v.trim() !== "") {
|
|
216
|
+
prefixes[k] = v.trim();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
merged.prefixes = prefixes;
|
|
220
|
+
}
|
|
221
|
+
writeFileSync(path, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
222
|
+
}
|
|
223
|
+
|
|
204
224
|
/**
|
|
205
225
|
* Returns the branch name prefix for a given branch type from resolved config.
|
|
206
226
|
*/
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -101,6 +101,10 @@ export interface ParsedArgs {
|
|
|
101
101
|
// Common flags
|
|
102
102
|
push: boolean;
|
|
103
103
|
noPush: boolean;
|
|
104
|
+
/** Main branch override (e.g. from --main; init persists to .gflows.json). */
|
|
105
|
+
main: string | undefined;
|
|
106
|
+
/** Dev branch override (e.g. from --dev; init persists to .gflows.json). */
|
|
107
|
+
dev: string | undefined;
|
|
104
108
|
remote: string | undefined;
|
|
105
109
|
branch: string | undefined;
|
|
106
110
|
yes: boolean;
|