@quonfig/cli 0.0.15 → 0.0.17
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 +85 -34
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +18 -0
- package/dist/commands/migrate.js +48 -2
- package/dist/commands/pull.d.ts +14 -1
- package/dist/commands/pull.js +64 -4
- package/dist/commands/push.d.ts +53 -0
- package/dist/commands/push.js +370 -0
- package/dist/commands/workspace/bootstrap.d.ts +7 -0
- package/dist/commands/workspace/bootstrap.js +58 -2
- package/dist/migrate/local-write.d.ts +8 -2
- package/dist/migrate/local-write.js +55 -7
- package/dist/migrate/migration-report.d.ts +18 -0
- package/dist/migrate/migration-report.js +64 -1
- package/dist/migrate/push-to-cloud.d.ts +14 -3
- package/dist/migrate/push-to-cloud.js +77 -6
- package/dist/migrate/source.d.ts +50 -1
- package/dist/migrate/sources/launch/translate.d.ts +27 -1
- package/dist/migrate/sources/launch/translate.js +123 -6
- package/dist/migrate/sources/launch/types.d.ts +3 -0
- package/dist/migrate/sources/launch/zod-to-json-schema.d.ts +9 -0
- package/dist/migrate/sources/launch/zod-to-json-schema.js +521 -0
- package/dist/migrate/sources/launch.js +52 -7
- package/dist/push/bare-path-diff.d.ts +40 -0
- package/dist/push/bare-path-diff.js +126 -0
- package/dist/push/confirm.d.ts +10 -0
- package/dist/push/confirm.js +108 -0
- package/dist/push/diff-summary.d.ts +56 -0
- package/dist/push/diff-summary.js +115 -0
- package/dist/push/identity-check.d.ts +53 -0
- package/dist/push/identity-check.js +135 -0
- package/dist/push/run-push.d.ts +107 -0
- package/dist/push/run-push.js +318 -0
- package/dist/util/get-client.js +5 -4
- package/dist/util/get-valid-token.d.ts +3 -1
- package/dist/util/get-valid-token.js +28 -3
- package/dist/util/gitea-api.d.ts +15 -1
- package/dist/util/gitea-api.js +1 -0
- package/dist/util/gitea-token-storage.d.ts +7 -0
- package/dist/util/quonfig-json.d.ts +44 -0
- package/dist/util/quonfig-json.js +65 -0
- package/dist/verify/validate.js +6 -2
- package/oclif.manifest.json +104 -1
- package/package.json +3 -3
- /package/dist/{migrate/push-strategy.d.ts → util/clone-and-stack-push.d.ts} +0 -0
- /package/dist/{migrate/push-strategy.js → util/clone-and-stack-push.js} +0 -0
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ $ npm install -g @quonfig/cli
|
|
|
14
14
|
$ qfg COMMAND
|
|
15
15
|
running command...
|
|
16
16
|
$ qfg (--version)
|
|
17
|
-
@quonfig/cli/0.0.
|
|
17
|
+
@quonfig/cli/0.0.17 linux-x64 node-v24.4.1
|
|
18
18
|
$ qfg --help [COMMAND]
|
|
19
19
|
USAGE
|
|
20
20
|
$ qfg COMMAND
|
|
@@ -46,6 +46,7 @@ USAGE
|
|
|
46
46
|
* [`qfg migrate status`](#qfg-migrate-status)
|
|
47
47
|
* [`qfg override [NAME]`](#qfg-override-name)
|
|
48
48
|
* [`qfg pull`](#qfg-pull)
|
|
49
|
+
* [`qfg push`](#qfg-push)
|
|
49
50
|
* [`qfg schema NAME`](#qfg-schema-name)
|
|
50
51
|
* [`qfg sdk-key`](#qfg-sdk-key)
|
|
51
52
|
* [`qfg sdk-key create`](#qfg-sdk-key-create)
|
|
@@ -88,7 +89,7 @@ EXAMPLES
|
|
|
88
89
|
$ qfg config-schema --json-schema # full JSON Schema document (copy into your editor)
|
|
89
90
|
```
|
|
90
91
|
|
|
91
|
-
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
92
|
+
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/config-schema.ts)_
|
|
92
93
|
|
|
93
94
|
## `qfg create NAME`
|
|
94
95
|
|
|
@@ -160,7 +161,7 @@ EXAMPLES
|
|
|
160
161
|
$ qfg set-rollout my.new.flag --environment production --true-percent 20
|
|
161
162
|
```
|
|
162
163
|
|
|
163
|
-
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
164
|
+
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/create.ts)_
|
|
164
165
|
|
|
165
166
|
## `qfg flag info [NAME]`
|
|
166
167
|
|
|
@@ -360,7 +361,7 @@ EXAMPLES
|
|
|
360
361
|
$ qfg generate --targets node-ts -o ./dist # combine with targets
|
|
361
362
|
```
|
|
362
363
|
|
|
363
|
-
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
364
|
+
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/generate.ts)_
|
|
364
365
|
|
|
365
366
|
## `qfg generate-new-hex-key`
|
|
366
367
|
|
|
@@ -383,7 +384,7 @@ EXAMPLES
|
|
|
383
384
|
$ qfg generate-new-hex-key
|
|
384
385
|
```
|
|
385
386
|
|
|
386
|
-
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
387
|
+
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/generate-new-hex-key.ts)_
|
|
387
388
|
|
|
388
389
|
## `qfg get [NAME]`
|
|
389
390
|
|
|
@@ -415,7 +416,7 @@ EXAMPLES
|
|
|
415
416
|
$ qfg get my.config.name --environment=production
|
|
416
417
|
```
|
|
417
418
|
|
|
418
|
-
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
419
|
+
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/get.ts)_
|
|
419
420
|
|
|
420
421
|
## `qfg info [NAME]`
|
|
421
422
|
|
|
@@ -463,7 +464,7 @@ EXAMPLES
|
|
|
463
464
|
$ qfg info my.config.name --exclude-evaluations # skip 24h stats
|
|
464
465
|
```
|
|
465
466
|
|
|
466
|
-
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
467
|
+
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/info.ts)_
|
|
467
468
|
|
|
468
469
|
## `qfg init [DIRECTORY]`
|
|
469
470
|
|
|
@@ -472,13 +473,15 @@ Initialize or update a Quonfig workspace
|
|
|
472
473
|
```
|
|
473
474
|
USAGE
|
|
474
475
|
$ qfg init [DIRECTORY] [--json] [--interactive] [--no-color] [--verbose] [--dry-run] [--samples]
|
|
476
|
+
[--workspace <value>]
|
|
475
477
|
|
|
476
478
|
ARGUMENTS
|
|
477
479
|
DIRECTORY [default: .] Target directory (default: current directory)
|
|
478
480
|
|
|
479
481
|
FLAGS
|
|
480
|
-
--dry-run
|
|
481
|
-
--[no-]samples
|
|
482
|
+
--dry-run Show what would be done without writing anything
|
|
483
|
+
--[no-]samples Include sample configs (default: yes on first init, no on update)
|
|
484
|
+
--workspace=<value> Workspace slug to pin in quonfig.json (Guard 1). If omitted, no pin is written.
|
|
482
485
|
|
|
483
486
|
GLOBAL FLAGS
|
|
484
487
|
--[no-]interactive Force interactive mode
|
|
@@ -501,7 +504,7 @@ EXAMPLES
|
|
|
501
504
|
$ qfg init --dry-run
|
|
502
505
|
```
|
|
503
506
|
|
|
504
|
-
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
507
|
+
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/init.ts)_
|
|
505
508
|
|
|
506
509
|
## `qfg interactive`
|
|
507
510
|
|
|
@@ -531,7 +534,7 @@ EXAMPLES
|
|
|
531
534
|
$ qfg
|
|
532
535
|
```
|
|
533
536
|
|
|
534
|
-
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
537
|
+
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/interactive.ts)_
|
|
535
538
|
|
|
536
539
|
## `qfg list`
|
|
537
540
|
|
|
@@ -571,7 +574,7 @@ EXAMPLES
|
|
|
571
574
|
$ qfg list --feature-flags
|
|
572
575
|
```
|
|
573
576
|
|
|
574
|
-
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
577
|
+
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/list.ts)_
|
|
575
578
|
|
|
576
579
|
## `qfg login`
|
|
577
580
|
|
|
@@ -596,7 +599,7 @@ EXAMPLES
|
|
|
596
599
|
$ qfg login --profile myprofile
|
|
597
600
|
```
|
|
598
601
|
|
|
599
|
-
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
602
|
+
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/login.ts)_
|
|
600
603
|
|
|
601
604
|
## `qfg logout`
|
|
602
605
|
|
|
@@ -619,7 +622,7 @@ EXAMPLES
|
|
|
619
622
|
$ qfg logout
|
|
620
623
|
```
|
|
621
624
|
|
|
622
|
-
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
625
|
+
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/logout.ts)_
|
|
623
626
|
|
|
624
627
|
## `qfg mcp`
|
|
625
628
|
|
|
@@ -652,7 +655,7 @@ EXAMPLES
|
|
|
652
655
|
$ qfg mcp --url http://local-app.quonfig-staging.com:3003/api/v1/mcp
|
|
653
656
|
```
|
|
654
657
|
|
|
655
|
-
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
658
|
+
_See code: [src/commands/mcp.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/mcp.ts)_
|
|
656
659
|
|
|
657
660
|
## `qfg migrate`
|
|
658
661
|
|
|
@@ -706,7 +709,7 @@ EXAMPLES
|
|
|
706
709
|
$ qfg migrate --from launch --api-key $LAUNCH_API_KEY --dir ./quonfig-config --staging
|
|
707
710
|
```
|
|
708
711
|
|
|
709
|
-
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
712
|
+
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/migrate.ts)_
|
|
710
713
|
|
|
711
714
|
## `qfg migrate doctor`
|
|
712
715
|
|
|
@@ -744,7 +747,7 @@ EXAMPLES
|
|
|
744
747
|
$ qfg migrate doctor --json
|
|
745
748
|
```
|
|
746
749
|
|
|
747
|
-
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
750
|
+
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/migrate/doctor.ts)_
|
|
748
751
|
|
|
749
752
|
## `qfg migrate my-code`
|
|
750
753
|
|
|
@@ -776,7 +779,7 @@ EXAMPLES
|
|
|
776
779
|
$ qfg migrate my-code --dry-run
|
|
777
780
|
```
|
|
778
781
|
|
|
779
|
-
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
782
|
+
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/migrate/my-code.ts)_
|
|
780
783
|
|
|
781
784
|
## `qfg migrate status`
|
|
782
785
|
|
|
@@ -807,7 +810,7 @@ EXAMPLES
|
|
|
807
810
|
$ qfg migrate status --json
|
|
808
811
|
```
|
|
809
812
|
|
|
810
|
-
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
813
|
+
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/migrate/status.ts)_
|
|
811
814
|
|
|
812
815
|
## `qfg override [NAME]`
|
|
813
816
|
|
|
@@ -851,7 +854,7 @@ EXAMPLES
|
|
|
851
854
|
$ qfg override my.double.config --value=3.14159
|
|
852
855
|
```
|
|
853
856
|
|
|
854
|
-
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
857
|
+
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/override.ts)_
|
|
855
858
|
|
|
856
859
|
## `qfg pull`
|
|
857
860
|
|
|
@@ -895,7 +898,55 @@ EXAMPLES
|
|
|
895
898
|
$ qfg pull # uses QUONFIG_DIR env var
|
|
896
899
|
```
|
|
897
900
|
|
|
898
|
-
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
901
|
+
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/pull.ts)_
|
|
902
|
+
|
|
903
|
+
## `qfg push`
|
|
904
|
+
|
|
905
|
+
Push local config changes up to your workspace on Quonfig cloud.
|
|
906
|
+
|
|
907
|
+
```
|
|
908
|
+
USAGE
|
|
909
|
+
$ qfg push [--json] [--interactive] [--no-color] [--verbose] [--dir <value>] [--workspace <value>]
|
|
910
|
+
[--yes] [--skip-validate] [--no-pin-write] [-m <value>]
|
|
911
|
+
|
|
912
|
+
FLAGS
|
|
913
|
+
-m, --message=<value> Commit message for bare-path pushes (ignored on clone-path; push what is already committed)
|
|
914
|
+
--dir=<value> Local directory to push (defaults to QUONFIG_DIR env var)
|
|
915
|
+
--no-pin-write Do not offer to write workspace slug into quonfig.json on success
|
|
916
|
+
--skip-validate Skip `qfg validate` preflight
|
|
917
|
+
--workspace=<value> Workspace slug or UUID (defaults to active profile)
|
|
918
|
+
--yes Skip the standard Y/N confirm. Never skips typed-slug prompts.
|
|
919
|
+
|
|
920
|
+
GLOBAL FLAGS
|
|
921
|
+
--[no-]interactive Force interactive mode
|
|
922
|
+
--json Format output as json.
|
|
923
|
+
--no-color Do not colorize output
|
|
924
|
+
--verbose Verbose output
|
|
925
|
+
|
|
926
|
+
DESCRIPTION
|
|
927
|
+
Push local config changes up to your workspace on Quonfig cloud.
|
|
928
|
+
|
|
929
|
+
Enforces three guards before touching the remote:
|
|
930
|
+
1. The dir's quonfig.json "workspace" pin must match the backend (if set).
|
|
931
|
+
2. The dir's git origin must match the backend repo URL (if set).
|
|
932
|
+
3. A diff summary is shown; destructive changes (10+ deletes, >=25% of
|
|
933
|
+
files, or an unpinned dir) require typing the workspace slug to confirm.
|
|
934
|
+
|
|
935
|
+
qfg push # uses QUONFIG_DIR + profile workspace
|
|
936
|
+
qfg push --dir ./our-config
|
|
937
|
+
qfg push --dir ./our-config --workspace acme-prod
|
|
938
|
+
qfg push --dir ./our-config --yes # skip normal Y/N (never skips typed-slug)
|
|
939
|
+
qfg push --dir ./our-config --skip-validate
|
|
940
|
+
|
|
941
|
+
EXAMPLES
|
|
942
|
+
$ qfg push --dir ./our-config
|
|
943
|
+
|
|
944
|
+
$ qfg push --workspace acme-prod --dir ./our-config
|
|
945
|
+
|
|
946
|
+
$ qfg push --dir ./our-config --yes
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/push.ts)_
|
|
899
950
|
|
|
900
951
|
## `qfg schema NAME`
|
|
901
952
|
|
|
@@ -933,7 +984,7 @@ EXAMPLES
|
|
|
933
984
|
$ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
|
|
934
985
|
```
|
|
935
986
|
|
|
936
|
-
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
987
|
+
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/schema.ts)_
|
|
937
988
|
|
|
938
989
|
## `qfg sdk-key`
|
|
939
990
|
|
|
@@ -960,7 +1011,7 @@ EXAMPLES
|
|
|
960
1011
|
$ qfg sdk-key revoke <key-id>
|
|
961
1012
|
```
|
|
962
1013
|
|
|
963
|
-
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1014
|
+
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/sdk-key.ts)_
|
|
964
1015
|
|
|
965
1016
|
## `qfg sdk-key create`
|
|
966
1017
|
|
|
@@ -992,7 +1043,7 @@ EXAMPLES
|
|
|
992
1043
|
$ qfg sdk-key create --environment staging --type browser
|
|
993
1044
|
```
|
|
994
1045
|
|
|
995
|
-
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1046
|
+
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/sdk-key/create.ts)_
|
|
996
1047
|
|
|
997
1048
|
## `qfg sdk-key list`
|
|
998
1049
|
|
|
@@ -1021,7 +1072,7 @@ EXAMPLES
|
|
|
1021
1072
|
$ qfg sdk-key list --environment production
|
|
1022
1073
|
```
|
|
1023
1074
|
|
|
1024
|
-
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1075
|
+
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/sdk-key/list.ts)_
|
|
1025
1076
|
|
|
1026
1077
|
## `qfg sdk-key revoke KEYID`
|
|
1027
1078
|
|
|
@@ -1048,7 +1099,7 @@ EXAMPLES
|
|
|
1048
1099
|
$ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
1049
1100
|
```
|
|
1050
1101
|
|
|
1051
|
-
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1102
|
+
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/sdk-key/revoke.ts)_
|
|
1052
1103
|
|
|
1053
1104
|
## `qfg set-default [NAME]`
|
|
1054
1105
|
|
|
@@ -1119,7 +1170,7 @@ EXAMPLES
|
|
|
1119
1170
|
$ qfg pull && qfg config-schema
|
|
1120
1171
|
```
|
|
1121
1172
|
|
|
1122
|
-
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1173
|
+
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/set-default.ts)_
|
|
1123
1174
|
|
|
1124
1175
|
## `qfg set-rollout [NAME]`
|
|
1125
1176
|
|
|
@@ -1196,7 +1247,7 @@ EXAMPLES
|
|
|
1196
1247
|
$ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
|
|
1197
1248
|
```
|
|
1198
1249
|
|
|
1199
|
-
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1250
|
+
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/set-rollout.ts)_
|
|
1200
1251
|
|
|
1201
1252
|
## `qfg sync`
|
|
1202
1253
|
|
|
@@ -1228,7 +1279,7 @@ EXAMPLES
|
|
|
1228
1279
|
$ qfg sync --watch --dir ./our-config --interval 30
|
|
1229
1280
|
```
|
|
1230
1281
|
|
|
1231
|
-
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1282
|
+
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/sync.ts)_
|
|
1232
1283
|
|
|
1233
1284
|
## `qfg toggle [NAME]`
|
|
1234
1285
|
|
|
@@ -1330,7 +1381,7 @@ EXAMPLES
|
|
|
1330
1381
|
$ qfg verify --json
|
|
1331
1382
|
```
|
|
1332
1383
|
|
|
1333
|
-
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1384
|
+
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/verify.ts)_
|
|
1334
1385
|
|
|
1335
1386
|
## `qfg whoami`
|
|
1336
1387
|
|
|
@@ -1353,7 +1404,7 @@ EXAMPLES
|
|
|
1353
1404
|
$ qfg whoami
|
|
1354
1405
|
```
|
|
1355
1406
|
|
|
1356
|
-
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1407
|
+
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/whoami.ts)_
|
|
1357
1408
|
|
|
1358
1409
|
## `qfg workspace`
|
|
1359
1410
|
|
|
@@ -1376,7 +1427,7 @@ EXAMPLES
|
|
|
1376
1427
|
$ qfg workspace
|
|
1377
1428
|
```
|
|
1378
1429
|
|
|
1379
|
-
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1430
|
+
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/workspace.ts)_
|
|
1380
1431
|
|
|
1381
1432
|
## `qfg workspace bootstrap`
|
|
1382
1433
|
|
|
@@ -1409,7 +1460,7 @@ EXAMPLES
|
|
|
1409
1460
|
$ qfg workspace bootstrap --dir ./our-config --skip-validate
|
|
1410
1461
|
```
|
|
1411
1462
|
|
|
1412
|
-
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1463
|
+
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/workspace/bootstrap.ts)_
|
|
1413
1464
|
|
|
1414
1465
|
## `qfg workspace switch [SLUG]`
|
|
1415
1466
|
|
|
@@ -1437,7 +1488,7 @@ EXAMPLES
|
|
|
1437
1488
|
$ qfg workspace switch my-workspace-slug
|
|
1438
1489
|
```
|
|
1439
1490
|
|
|
1440
|
-
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1491
|
+
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.17/src/commands/workspace/switch.ts)_
|
|
1441
1492
|
<!-- commandsstop -->
|
|
1442
1493
|
|
|
1443
1494
|
## Local Development
|
package/dist/commands/init.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export default class Init extends BaseCommand {
|
|
|
8
8
|
static flags: {
|
|
9
9
|
'dry-run': import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
10
10
|
samples: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
11
|
+
workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
11
12
|
};
|
|
12
13
|
run(): Promise<object>;
|
|
13
14
|
}
|
package/dist/commands/init.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Args, Flags } from '@oclif/core';
|
|
2
2
|
import { BaseCommand } from '../index.js';
|
|
3
3
|
import { executeInit, planInit } from '../init/init-workspace.js';
|
|
4
|
+
import { writeWorkspaceSlug } from '../util/quonfig-json.js';
|
|
4
5
|
export default class Init extends BaseCommand {
|
|
5
6
|
static args = {
|
|
6
7
|
directory: Args.string({
|
|
@@ -25,6 +26,10 @@ export default class Init extends BaseCommand {
|
|
|
25
26
|
allowNo: true,
|
|
26
27
|
description: 'Include sample configs (default: yes on first init, no on update)',
|
|
27
28
|
}),
|
|
29
|
+
workspace: Flags.string({
|
|
30
|
+
description: 'Workspace slug to pin in quonfig.json (Guard 1). If omitted, no pin is written.',
|
|
31
|
+
required: false,
|
|
32
|
+
}),
|
|
28
33
|
};
|
|
29
34
|
async run() {
|
|
30
35
|
const { args, flags } = await this.parse(Init);
|
|
@@ -45,6 +50,9 @@ export default class Init extends BaseCommand {
|
|
|
45
50
|
this.log(` + ${action.description}`);
|
|
46
51
|
}
|
|
47
52
|
}
|
|
53
|
+
if (flags.workspace) {
|
|
54
|
+
this.log(` + Pin quonfig.json to workspace "${flags.workspace}"`);
|
|
55
|
+
}
|
|
48
56
|
if (skipActions.length > 0) {
|
|
49
57
|
this.log('\nSkipped:');
|
|
50
58
|
for (const action of skipActions) {
|
|
@@ -66,10 +74,20 @@ export default class Init extends BaseCommand {
|
|
|
66
74
|
}
|
|
67
75
|
// Execute for real
|
|
68
76
|
executeInit(plan, dir);
|
|
77
|
+
// If the user passed --workspace, pin the slug in quonfig.json
|
|
78
|
+
// (Guard 1 in project/plans/cli-git-sync.md). We deliberately keep this
|
|
79
|
+
// non-interactive: flag → write, no flag → no write. Users who need the
|
|
80
|
+
// pin later can run `qfg pull` (backfills) or edit the file directly.
|
|
81
|
+
if (flags.workspace) {
|
|
82
|
+
await writeWorkspaceSlug(dir, flags.workspace);
|
|
83
|
+
}
|
|
69
84
|
this.log(`${mode} workspace: ${dir === '.' ? process.cwd() : dir}\n`);
|
|
70
85
|
for (const action of writeActions) {
|
|
71
86
|
this.log(` + ${action.description}`);
|
|
72
87
|
}
|
|
88
|
+
if (flags.workspace) {
|
|
89
|
+
this.log(` + Pin quonfig.json to workspace "${flags.workspace}"`);
|
|
90
|
+
}
|
|
73
91
|
for (const action of skipActions) {
|
|
74
92
|
this.log(` - ${action.description}`);
|
|
75
93
|
}
|
package/dist/commands/migrate.js
CHANGED
|
@@ -4,10 +4,10 @@ import path from 'node:path';
|
|
|
4
4
|
import { BaseCommand } from '../index.js';
|
|
5
5
|
import { CrossSourceError, assertSourceMatches, readImportState } from '../migrate/import-state.js';
|
|
6
6
|
import { applyLocalMigration } from '../migrate/local-write.js';
|
|
7
|
-
import { PushConflictError } from '../
|
|
7
|
+
import { PushConflictError } from '../util/clone-and-stack-push.js';
|
|
8
8
|
import { pushMigrationToCloud } from '../migrate/push-to-cloud.js';
|
|
9
9
|
import { UnknownSourceError, getSource } from '../migrate/registry.js';
|
|
10
|
-
import { NotYetImplementedError } from '../migrate/source.js';
|
|
10
|
+
import { NotYetImplementedError, } from '../migrate/source.js';
|
|
11
11
|
import { applyLaunchBaseUrl } from '../migrate/sources/launch/api.js';
|
|
12
12
|
import { mintGiteaToken } from '../util/gitea-api.js';
|
|
13
13
|
import { displayUrl } from '../util/git-ops.js';
|
|
@@ -104,6 +104,7 @@ export default class Migrate extends BaseCommand {
|
|
|
104
104
|
return this.err(error.message);
|
|
105
105
|
throw error;
|
|
106
106
|
}
|
|
107
|
+
let duplicateResolutionsForWarn = null;
|
|
107
108
|
try {
|
|
108
109
|
await source.validateAuth(flags['api-key']);
|
|
109
110
|
const environments = await source.listEnvironments();
|
|
@@ -148,12 +149,14 @@ export default class Migrate extends BaseCommand {
|
|
|
148
149
|
const result = await pushMigrationToCloud({
|
|
149
150
|
changes: toProcess,
|
|
150
151
|
commitMessage: buildCommitMessage(flags.from, toProcess.length),
|
|
152
|
+
environments,
|
|
151
153
|
importState,
|
|
152
154
|
localDir: dir,
|
|
153
155
|
remoteUrl: repoUrl,
|
|
154
156
|
reportData: buildReportData(flags.from, toProcess.length),
|
|
155
157
|
source,
|
|
156
158
|
});
|
|
159
|
+
duplicateResolutionsForWarn = result.duplicateResolutions;
|
|
157
160
|
this.log(result.committed
|
|
158
161
|
? `Pushed ${toProcess.length} change(s). commit=${result.commitSha?.slice(0, 8) ?? ''} action=${result.action}`
|
|
159
162
|
: `No net changes produced by this run. Nothing to commit or push.`);
|
|
@@ -188,6 +191,7 @@ export default class Migrate extends BaseCommand {
|
|
|
188
191
|
reportData: buildReportData(flags.from, toProcess.length),
|
|
189
192
|
source,
|
|
190
193
|
});
|
|
194
|
+
duplicateResolutionsForWarn = localResult.duplicateResolutions;
|
|
191
195
|
this.log(localResult.committed
|
|
192
196
|
? `Committed ${toProcess.length} change(s). commit=${localResult.commitSha?.slice(0, 8) ?? ''} action=${localResult.action}`
|
|
193
197
|
: `No net changes produced by this run. Nothing to commit.`);
|
|
@@ -203,6 +207,13 @@ export default class Migrate extends BaseCommand {
|
|
|
203
207
|
return this.err(error.message);
|
|
204
208
|
throw error;
|
|
205
209
|
}
|
|
210
|
+
finally {
|
|
211
|
+
// Surface source-level warnings even if apply/push threw (e.g.
|
|
212
|
+
// detectDuplicateKeys). The source accumulator holds the data regardless.
|
|
213
|
+
warnAboutDroppedOverrides(this, source.getDroppedOverrides?.() ?? null);
|
|
214
|
+
warnAboutSkippedConfigs(this, source.getSkippedConfigs?.() ?? null);
|
|
215
|
+
warnAboutDuplicateResolutions(this, duplicateResolutionsForWarn);
|
|
216
|
+
}
|
|
206
217
|
}
|
|
207
218
|
async resolvePushWorkspace(slugOrId) {
|
|
208
219
|
const authConfig = await loadAuthConfig();
|
|
@@ -250,6 +261,41 @@ function buildReportData(source, count) {
|
|
|
250
261
|
unsupportedFeatures: [],
|
|
251
262
|
};
|
|
252
263
|
}
|
|
264
|
+
function warnAboutDroppedOverrides(cmd, dropped) {
|
|
265
|
+
if (!dropped || dropped.total === 0)
|
|
266
|
+
return;
|
|
267
|
+
const envIds = Object.keys(dropped.byEnv).sort();
|
|
268
|
+
cmd.warn(`Dropped ${dropped.total} override section(s) referencing ${envIds.length} env ID(s) not present in the source's env list (likely archived/deleted):`);
|
|
269
|
+
for (const envId of envIds) {
|
|
270
|
+
const perFlag = dropped.byEnv[envId];
|
|
271
|
+
const totalForEnv = Object.values(perFlag).reduce((s, n) => s + n, 0);
|
|
272
|
+
cmd.warn(` env-${envId}: ${totalForEnv} dropped from ${Object.keys(perFlag).length} flag(s)`);
|
|
273
|
+
for (const flagPath of Object.keys(perFlag).sort()) {
|
|
274
|
+
cmd.warn(` - ${flagPath} (${perFlag[flagPath]})`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
cmd.warn(`If any of these envs are still in use, restore them in the source system and re-run the migration. Full detail is also written to MIGRATION_REPORT.md.`);
|
|
278
|
+
}
|
|
279
|
+
function warnAboutDuplicateResolutions(cmd, resolved) {
|
|
280
|
+
if (!resolved || resolved.total === 0)
|
|
281
|
+
return;
|
|
282
|
+
cmd.warn(`Resolved ${resolved.total} cross-type key collision(s) in the source data by keeping the config side and deleting the other type(s). Review each and clean up the source system so the collision stops recurring:`);
|
|
283
|
+
const sorted = [...resolved.entries].sort((a, b) => a.key.localeCompare(b.key));
|
|
284
|
+
for (const entry of sorted) {
|
|
285
|
+
cmd.warn(` ${entry.key} (${entry.collisionTypes.join(', ')}): kept ${entry.kept}, deleted ${entry.deleted.join(', ')}`);
|
|
286
|
+
}
|
|
287
|
+
cmd.warn('Full detail is also written to MIGRATION_REPORT.md.');
|
|
288
|
+
}
|
|
289
|
+
function warnAboutSkippedConfigs(cmd, skipped) {
|
|
290
|
+
if (!skipped || skipped.total === 0)
|
|
291
|
+
return;
|
|
292
|
+
cmd.warn(`Skipped ${skipped.total} invalid config(s) from the source system — these were not written to the workspace. Fix each in the source system and re-run:`);
|
|
293
|
+
const sorted = [...skipped.entries].sort((a, b) => a.key.localeCompare(b.key));
|
|
294
|
+
for (const entry of sorted) {
|
|
295
|
+
cmd.warn(` ${entry.key}: ${entry.reason}`);
|
|
296
|
+
}
|
|
297
|
+
cmd.warn('Full detail is also written to MIGRATION_REPORT.md.');
|
|
298
|
+
}
|
|
253
299
|
function resolveTargetDir(dirFlag, cwd) {
|
|
254
300
|
if (dirFlag)
|
|
255
301
|
return path.resolve(cwd, dirFlag);
|
package/dist/commands/pull.d.ts
CHANGED
|
@@ -8,8 +8,21 @@ export default class Pull extends BaseCommand {
|
|
|
8
8
|
workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
9
|
};
|
|
10
10
|
run(): Promise<JsonObj | void>;
|
|
11
|
+
/**
|
|
12
|
+
* Local-only backfill of the `workspace` pin in `quonfig.json`.
|
|
13
|
+
*
|
|
14
|
+
* - Missing pin → write it from the token response's `workspaceSlug`.
|
|
15
|
+
* - Pin already set and matches → no-op.
|
|
16
|
+
* - Pin already set and disagrees → log a warning showing both values
|
|
17
|
+
* and LEAVE IT ALONE. Overwriting someone's explicit pin is a Guard 2
|
|
18
|
+
* concern, not a pull concern — the next `qfg push` is where the
|
|
19
|
+
* identity-check dispatches a hard abort.
|
|
20
|
+
*
|
|
21
|
+
* This never throws — a broken `quonfig.json` shouldn't fail `qfg pull`.
|
|
22
|
+
*/
|
|
23
|
+
private backfillWorkspacePin;
|
|
11
24
|
private looksLike401;
|
|
12
25
|
private maybeWriteQuonfigDir;
|
|
13
26
|
private refreshAndGetUrl;
|
|
14
|
-
private
|
|
27
|
+
private resolveTokenEntry;
|
|
15
28
|
}
|
package/dist/commands/pull.js
CHANGED
|
@@ -5,6 +5,7 @@ import { BaseCommand } from '../index.js';
|
|
|
5
5
|
import { getActiveProfile, loadAuthConfig } from '../util/token-storage.js';
|
|
6
6
|
import { loadGiteaToken, isGiteaTokenExpired, saveGiteaToken } from '../util/gitea-token-storage.js';
|
|
7
7
|
import { mintAndStoreGiteaReadToken, mintGiteaToken } from '../util/gitea-api.js';
|
|
8
|
+
import { readWorkspaceSlug, writeWorkspaceSlug } from '../util/quonfig-json.js';
|
|
8
9
|
import { isGitRepo, getRemoteUrl, gitFetch, gitSetRemote, canFastForward, hasDivergedFromRemote, gitMergeFfOnly, gitClone, displayUrl, } from '../util/git-ops.js';
|
|
9
10
|
export default class Pull extends BaseCommand {
|
|
10
11
|
static description = `Clone or update a local copy of your workspace config files.
|
|
@@ -56,7 +57,8 @@ CLI shortcuts (no JSON editing needed for simple cases):
|
|
|
56
57
|
}
|
|
57
58
|
this.verboseLog('Pull', { workspaceId, dir: resolvedDir });
|
|
58
59
|
// Get or mint Gitea read token
|
|
59
|
-
const
|
|
60
|
+
const tokenEntry = await this.resolveTokenEntry(workspaceId);
|
|
61
|
+
const repoUrl = tokenEntry.repoUrl;
|
|
60
62
|
// Perform git operation
|
|
61
63
|
const isRepo = await isGitRepo(resolvedDir);
|
|
62
64
|
if (isRepo) {
|
|
@@ -132,8 +134,61 @@ CLI shortcuts (no JSON editing needed for simple cases):
|
|
|
132
134
|
}
|
|
133
135
|
// Write QUONFIG_DIR to ~/.quonfig/config if not set
|
|
134
136
|
await this.maybeWriteQuonfigDir(resolvedDir);
|
|
137
|
+
// Backfill the `workspace` pin in `quonfig.json` if missing
|
|
138
|
+
// (Guard 1 in project/plans/cli-git-sync.md). Local-only write — we
|
|
139
|
+
// intentionally do NOT commit or push here; the user's next `qfg push`
|
|
140
|
+
// will sweep it up naturally.
|
|
141
|
+
await this.backfillWorkspacePin(resolvedDir, tokenEntry, workspaceId);
|
|
135
142
|
return { dir: resolvedDir, workspaceId };
|
|
136
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* Local-only backfill of the `workspace` pin in `quonfig.json`.
|
|
146
|
+
*
|
|
147
|
+
* - Missing pin → write it from the token response's `workspaceSlug`.
|
|
148
|
+
* - Pin already set and matches → no-op.
|
|
149
|
+
* - Pin already set and disagrees → log a warning showing both values
|
|
150
|
+
* and LEAVE IT ALONE. Overwriting someone's explicit pin is a Guard 2
|
|
151
|
+
* concern, not a pull concern — the next `qfg push` is where the
|
|
152
|
+
* identity-check dispatches a hard abort.
|
|
153
|
+
*
|
|
154
|
+
* This never throws — a broken `quonfig.json` shouldn't fail `qfg pull`.
|
|
155
|
+
*/
|
|
156
|
+
async backfillWorkspacePin(dir, tokenEntry, workspaceId) {
|
|
157
|
+
let backendSlug = tokenEntry.workspaceSlug;
|
|
158
|
+
if (!backendSlug) {
|
|
159
|
+
// Older cached entries lack the slug. Mint a fresh one solely to learn the
|
|
160
|
+
// slug. Cheap (one API call), and the refresh updates the cache for next time.
|
|
161
|
+
try {
|
|
162
|
+
const fresh = await mintAndStoreGiteaReadToken(workspaceId);
|
|
163
|
+
backendSlug = fresh.workspaceSlug;
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
this.verboseLog('Pull', `Could not mint token to learn workspace slug: ${String(error)}`);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (!backendSlug) {
|
|
171
|
+
this.verboseLog('Pull', 'Backend did not return a workspaceSlug; skipping pin backfill.');
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const existingPin = await readWorkspaceSlug(dir);
|
|
176
|
+
if (!existingPin) {
|
|
177
|
+
await writeWorkspaceSlug(dir, backendSlug);
|
|
178
|
+
this.verboseLog('Pull', `Backfilled workspace pin "${backendSlug}" into quonfig.json.`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (existingPin !== backendSlug) {
|
|
182
|
+
this.log('');
|
|
183
|
+
this.log(`Warning: quonfig.json pins workspace "${existingPin}", but the backend says this workspace is "${backendSlug}".`);
|
|
184
|
+
this.log('Leaving the existing pin in place. Run `qfg push` to see the full identity check.');
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
// Non-fatal — pull itself already succeeded.
|
|
189
|
+
this.verboseLog('Pull', `Could not backfill workspace pin: ${String(error)}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
137
192
|
looksLike401(err) {
|
|
138
193
|
const msg = err instanceof Error ? err.message : String(err);
|
|
139
194
|
return msg.includes('401') || msg.toLowerCase().includes('authentication failed');
|
|
@@ -158,17 +213,22 @@ CLI shortcuts (no JSON editing needed for simple cases):
|
|
|
158
213
|
}
|
|
159
214
|
async refreshAndGetUrl(workspaceId) {
|
|
160
215
|
const data = await mintGiteaToken(workspaceId, 'read', 'pull');
|
|
161
|
-
const entry = {
|
|
216
|
+
const entry = {
|
|
217
|
+
token: data.token,
|
|
218
|
+
repoUrl: data.repoUrl,
|
|
219
|
+
expiresAt: data.expiresAt,
|
|
220
|
+
workspaceSlug: data.workspaceSlug,
|
|
221
|
+
};
|
|
162
222
|
await saveGiteaToken(workspaceId, entry);
|
|
163
223
|
return entry.repoUrl;
|
|
164
224
|
}
|
|
165
|
-
async
|
|
225
|
+
async resolveTokenEntry(workspaceId) {
|
|
166
226
|
let entry = await loadGiteaToken(workspaceId);
|
|
167
227
|
if (!entry || isGiteaTokenExpired(entry)) {
|
|
168
228
|
this.verboseLog('Pull', 'Minting new Gitea read token...');
|
|
169
229
|
entry = await mintAndStoreGiteaReadToken(workspaceId);
|
|
170
230
|
}
|
|
171
|
-
return entry
|
|
231
|
+
return entry;
|
|
172
232
|
}
|
|
173
233
|
}
|
|
174
234
|
/**
|