@quonfig/cli 0.0.33 → 0.0.34
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 +39 -36
- package/dist/commands/pull.d.ts +1 -0
- package/dist/commands/pull.js +42 -2
- package/dist/commands/push.js +24 -1
- package/dist/push/run-push.d.ts +17 -0
- package/dist/push/run-push.js +30 -0
- package/dist/util/git-ops.d.ts +40 -0
- package/dist/util/git-ops.js +82 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/oclif.manifest.json +7 -1
- package/package.json +1 -1
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.34 linux-x64 node-v24.4.1
|
|
18
18
|
$ qfg --help [COMMAND]
|
|
19
19
|
USAGE
|
|
20
20
|
$ qfg COMMAND
|
|
@@ -91,7 +91,7 @@ EXAMPLES
|
|
|
91
91
|
$ qfg config-schema --json-schema # full JSON Schema document (copy into your editor)
|
|
92
92
|
```
|
|
93
93
|
|
|
94
|
-
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
94
|
+
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/config-schema.ts)_
|
|
95
95
|
|
|
96
96
|
## `qfg create NAME`
|
|
97
97
|
|
|
@@ -176,7 +176,7 @@ EXAMPLES
|
|
|
176
176
|
$ qfg set-rollout my.new.flag --environment production --true-percent 20
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
-
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
179
|
+
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/create.ts)_
|
|
180
180
|
|
|
181
181
|
## `qfg delete NAME`
|
|
182
182
|
|
|
@@ -222,7 +222,7 @@ EXAMPLES
|
|
|
222
222
|
$ qfg delete my.config --yes
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
-
_See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
225
|
+
_See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/delete.ts)_
|
|
226
226
|
|
|
227
227
|
## `qfg flag info [NAME]`
|
|
228
228
|
|
|
@@ -425,7 +425,7 @@ EXAMPLES
|
|
|
425
425
|
$ qfg generate --targets node-ts -o ./dist # combine with targets
|
|
426
426
|
```
|
|
427
427
|
|
|
428
|
-
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
428
|
+
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/generate.ts)_
|
|
429
429
|
|
|
430
430
|
## `qfg generate-new-hex-key`
|
|
431
431
|
|
|
@@ -448,7 +448,7 @@ EXAMPLES
|
|
|
448
448
|
$ qfg generate-new-hex-key
|
|
449
449
|
```
|
|
450
450
|
|
|
451
|
-
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
451
|
+
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/generate-new-hex-key.ts)_
|
|
452
452
|
|
|
453
453
|
## `qfg get [NAME]`
|
|
454
454
|
|
|
@@ -480,7 +480,7 @@ EXAMPLES
|
|
|
480
480
|
$ qfg get my.config.name --environment=production
|
|
481
481
|
```
|
|
482
482
|
|
|
483
|
-
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
483
|
+
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/get.ts)_
|
|
484
484
|
|
|
485
485
|
## `qfg info [NAME]`
|
|
486
486
|
|
|
@@ -528,7 +528,7 @@ EXAMPLES
|
|
|
528
528
|
$ qfg info my.config.name --exclude-evaluations # skip 24h stats
|
|
529
529
|
```
|
|
530
530
|
|
|
531
|
-
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
531
|
+
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/info.ts)_
|
|
532
532
|
|
|
533
533
|
## `qfg init [DIRECTORY]`
|
|
534
534
|
|
|
@@ -568,7 +568,7 @@ EXAMPLES
|
|
|
568
568
|
$ qfg init --dry-run
|
|
569
569
|
```
|
|
570
570
|
|
|
571
|
-
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
571
|
+
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/init.ts)_
|
|
572
572
|
|
|
573
573
|
## `qfg interactive`
|
|
574
574
|
|
|
@@ -598,7 +598,7 @@ EXAMPLES
|
|
|
598
598
|
$ qfg
|
|
599
599
|
```
|
|
600
600
|
|
|
601
|
-
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
601
|
+
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/interactive.ts)_
|
|
602
602
|
|
|
603
603
|
## `qfg list`
|
|
604
604
|
|
|
@@ -638,7 +638,7 @@ EXAMPLES
|
|
|
638
638
|
$ qfg list --feature-flags
|
|
639
639
|
```
|
|
640
640
|
|
|
641
|
-
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
641
|
+
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/list.ts)_
|
|
642
642
|
|
|
643
643
|
## `qfg log-level NAME`
|
|
644
644
|
|
|
@@ -703,7 +703,7 @@ EXAMPLES
|
|
|
703
703
|
$ qfg log-level log-level.my-app --target=Chatty --value=INFO --environment=production
|
|
704
704
|
```
|
|
705
705
|
|
|
706
|
-
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
706
|
+
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/log-level.ts)_
|
|
707
707
|
|
|
708
708
|
## `qfg login`
|
|
709
709
|
|
|
@@ -728,7 +728,7 @@ EXAMPLES
|
|
|
728
728
|
$ qfg login --profile myprofile
|
|
729
729
|
```
|
|
730
730
|
|
|
731
|
-
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
731
|
+
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/login.ts)_
|
|
732
732
|
|
|
733
733
|
## `qfg logout`
|
|
734
734
|
|
|
@@ -751,7 +751,7 @@ EXAMPLES
|
|
|
751
751
|
$ qfg logout
|
|
752
752
|
```
|
|
753
753
|
|
|
754
|
-
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
754
|
+
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/logout.ts)_
|
|
755
755
|
|
|
756
756
|
## `qfg migrate`
|
|
757
757
|
|
|
@@ -805,7 +805,7 @@ EXAMPLES
|
|
|
805
805
|
$ qfg migrate --from launch --api-key $LAUNCH_API_KEY --dir ./quonfig-repo --staging
|
|
806
806
|
```
|
|
807
807
|
|
|
808
|
-
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
808
|
+
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/migrate.ts)_
|
|
809
809
|
|
|
810
810
|
## `qfg migrate doctor`
|
|
811
811
|
|
|
@@ -843,7 +843,7 @@ EXAMPLES
|
|
|
843
843
|
$ qfg migrate doctor --json
|
|
844
844
|
```
|
|
845
845
|
|
|
846
|
-
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
846
|
+
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/migrate/doctor.ts)_
|
|
847
847
|
|
|
848
848
|
## `qfg migrate my-code`
|
|
849
849
|
|
|
@@ -875,7 +875,7 @@ EXAMPLES
|
|
|
875
875
|
$ qfg migrate my-code --dry-run
|
|
876
876
|
```
|
|
877
877
|
|
|
878
|
-
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
878
|
+
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/migrate/my-code.ts)_
|
|
879
879
|
|
|
880
880
|
## `qfg migrate status`
|
|
881
881
|
|
|
@@ -906,7 +906,7 @@ EXAMPLES
|
|
|
906
906
|
$ qfg migrate status --json
|
|
907
907
|
```
|
|
908
908
|
|
|
909
|
-
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
909
|
+
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/migrate/status.ts)_
|
|
910
910
|
|
|
911
911
|
## `qfg override [NAME] [VALUE]`
|
|
912
912
|
|
|
@@ -961,7 +961,7 @@ EXAMPLES
|
|
|
961
961
|
$ qfg override my.flag true --env=staging
|
|
962
962
|
```
|
|
963
963
|
|
|
964
|
-
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
964
|
+
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/override.ts)_
|
|
965
965
|
|
|
966
966
|
## `qfg pull`
|
|
967
967
|
|
|
@@ -969,10 +969,13 @@ Clone or update a local copy of your workspace config files.
|
|
|
969
969
|
|
|
970
970
|
```
|
|
971
971
|
USAGE
|
|
972
|
-
$ qfg pull [--json] [--interactive] [--no-color] [--verbose] [--dir <value>] [--workspace
|
|
972
|
+
$ qfg pull [--json] [--interactive] [--no-color] [--verbose] [--dir <value>] [--rebase] [--workspace
|
|
973
|
+
<value>]
|
|
973
974
|
|
|
974
975
|
FLAGS
|
|
975
976
|
--dir=<value> Local directory to clone/update (defaults to QUONFIG_DIR env var)
|
|
977
|
+
--rebase Replay local commits on top of origin/main when the two have diverged. Conflicts are surfaced via
|
|
978
|
+
standard git markers; resolve with `git rebase --continue`.
|
|
976
979
|
--workspace=<value> Workspace ID (defaults to active profile)
|
|
977
980
|
|
|
978
981
|
GLOBAL FLAGS
|
|
@@ -1005,7 +1008,7 @@ EXAMPLES
|
|
|
1005
1008
|
$ qfg pull # uses QUONFIG_DIR env var
|
|
1006
1009
|
```
|
|
1007
1010
|
|
|
1008
|
-
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1011
|
+
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/pull.ts)_
|
|
1009
1012
|
|
|
1010
1013
|
## `qfg push`
|
|
1011
1014
|
|
|
@@ -1053,7 +1056,7 @@ EXAMPLES
|
|
|
1053
1056
|
$ qfg push --dir ./our-config --yes
|
|
1054
1057
|
```
|
|
1055
1058
|
|
|
1056
|
-
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1059
|
+
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/push.ts)_
|
|
1057
1060
|
|
|
1058
1061
|
## `qfg schema NAME`
|
|
1059
1062
|
|
|
@@ -1091,7 +1094,7 @@ EXAMPLES
|
|
|
1091
1094
|
$ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
|
|
1092
1095
|
```
|
|
1093
1096
|
|
|
1094
|
-
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1097
|
+
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/schema.ts)_
|
|
1095
1098
|
|
|
1096
1099
|
## `qfg sdk-key`
|
|
1097
1100
|
|
|
@@ -1118,7 +1121,7 @@ EXAMPLES
|
|
|
1118
1121
|
$ qfg sdk-key revoke <key-id>
|
|
1119
1122
|
```
|
|
1120
1123
|
|
|
1121
|
-
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1124
|
+
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/sdk-key.ts)_
|
|
1122
1125
|
|
|
1123
1126
|
## `qfg sdk-key create`
|
|
1124
1127
|
|
|
@@ -1150,7 +1153,7 @@ EXAMPLES
|
|
|
1150
1153
|
$ qfg sdk-key create --environment staging --type browser
|
|
1151
1154
|
```
|
|
1152
1155
|
|
|
1153
|
-
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1156
|
+
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/sdk-key/create.ts)_
|
|
1154
1157
|
|
|
1155
1158
|
## `qfg sdk-key list`
|
|
1156
1159
|
|
|
@@ -1179,7 +1182,7 @@ EXAMPLES
|
|
|
1179
1182
|
$ qfg sdk-key list --environment production
|
|
1180
1183
|
```
|
|
1181
1184
|
|
|
1182
|
-
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1185
|
+
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/sdk-key/list.ts)_
|
|
1183
1186
|
|
|
1184
1187
|
## `qfg sdk-key revoke KEYID`
|
|
1185
1188
|
|
|
@@ -1206,7 +1209,7 @@ EXAMPLES
|
|
|
1206
1209
|
$ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
1207
1210
|
```
|
|
1208
1211
|
|
|
1209
|
-
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1212
|
+
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/sdk-key/revoke.ts)_
|
|
1210
1213
|
|
|
1211
1214
|
## `qfg set-default [NAME]`
|
|
1212
1215
|
|
|
@@ -1277,7 +1280,7 @@ EXAMPLES
|
|
|
1277
1280
|
$ qfg pull && qfg config-schema
|
|
1278
1281
|
```
|
|
1279
1282
|
|
|
1280
|
-
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1283
|
+
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/set-default.ts)_
|
|
1281
1284
|
|
|
1282
1285
|
## `qfg set-rollout [NAME]`
|
|
1283
1286
|
|
|
@@ -1354,7 +1357,7 @@ EXAMPLES
|
|
|
1354
1357
|
$ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
|
|
1355
1358
|
```
|
|
1356
1359
|
|
|
1357
|
-
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1360
|
+
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/set-rollout.ts)_
|
|
1358
1361
|
|
|
1359
1362
|
## `qfg sync`
|
|
1360
1363
|
|
|
@@ -1386,7 +1389,7 @@ EXAMPLES
|
|
|
1386
1389
|
$ qfg sync --watch --dir ./our-config --interval 30
|
|
1387
1390
|
```
|
|
1388
1391
|
|
|
1389
|
-
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1392
|
+
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/sync.ts)_
|
|
1390
1393
|
|
|
1391
1394
|
## `qfg toggle [NAME]`
|
|
1392
1395
|
|
|
@@ -1488,7 +1491,7 @@ EXAMPLES
|
|
|
1488
1491
|
$ qfg verify --json
|
|
1489
1492
|
```
|
|
1490
1493
|
|
|
1491
|
-
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1494
|
+
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/verify.ts)_
|
|
1492
1495
|
|
|
1493
1496
|
## `qfg whoami`
|
|
1494
1497
|
|
|
@@ -1511,7 +1514,7 @@ EXAMPLES
|
|
|
1511
1514
|
$ qfg whoami
|
|
1512
1515
|
```
|
|
1513
1516
|
|
|
1514
|
-
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1517
|
+
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/whoami.ts)_
|
|
1515
1518
|
|
|
1516
1519
|
## `qfg workspace`
|
|
1517
1520
|
|
|
@@ -1534,7 +1537,7 @@ EXAMPLES
|
|
|
1534
1537
|
$ qfg workspace
|
|
1535
1538
|
```
|
|
1536
1539
|
|
|
1537
|
-
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1540
|
+
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/workspace.ts)_
|
|
1538
1541
|
|
|
1539
1542
|
## `qfg workspace bootstrap`
|
|
1540
1543
|
|
|
@@ -1567,7 +1570,7 @@ EXAMPLES
|
|
|
1567
1570
|
$ qfg workspace bootstrap --dir ./our-config --skip-validate
|
|
1568
1571
|
```
|
|
1569
1572
|
|
|
1570
|
-
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1573
|
+
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/workspace/bootstrap.ts)_
|
|
1571
1574
|
|
|
1572
1575
|
## `qfg workspace create SLUG`
|
|
1573
1576
|
|
|
@@ -1601,7 +1604,7 @@ EXAMPLES
|
|
|
1601
1604
|
$ qfg workspace create my-team --org 11111111-1111-1111-1111-111111111111
|
|
1602
1605
|
```
|
|
1603
1606
|
|
|
1604
|
-
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1607
|
+
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/workspace/create.ts)_
|
|
1605
1608
|
|
|
1606
1609
|
## `qfg workspace switch [SLUG]`
|
|
1607
1610
|
|
|
@@ -1629,7 +1632,7 @@ EXAMPLES
|
|
|
1629
1632
|
$ qfg workspace switch acme/production
|
|
1630
1633
|
```
|
|
1631
1634
|
|
|
1632
|
-
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1635
|
+
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.34/src/commands/workspace/switch.ts)_
|
|
1633
1636
|
<!-- commandsstop -->
|
|
1634
1637
|
|
|
1635
1638
|
## Local Development
|
package/dist/commands/pull.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export default class Pull extends BaseCommand {
|
|
|
5
5
|
static examples: string[];
|
|
6
6
|
static flags: {
|
|
7
7
|
dir: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
8
|
+
rebase: import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
|
|
8
9
|
workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
9
10
|
};
|
|
10
11
|
run(): Promise<JsonObj | void>;
|
package/dist/commands/pull.js
CHANGED
|
@@ -6,7 +6,7 @@ import { loadGiteaToken, isGiteaTokenExpired, saveGiteaToken } from '../util/git
|
|
|
6
6
|
import { mintAndStoreGiteaReadToken, mintGiteaToken } from '../util/gitea-api.js';
|
|
7
7
|
import { readWorkspaceSlug, tryParseWorkspacePin, writeWorkspaceSlug } from '../util/quonfig-json.js';
|
|
8
8
|
import { resolveWorkspaceUuid } from '../util/resolve-workspace.js';
|
|
9
|
-
import { isGitRepo, getRemoteUrl, gitFetch, gitSetRemote, canFastForward, hasDivergedFromRemote, gitMergeFfOnly, gitClone, displayUrl, addAndCommitFile, dirtyTrackedFiles, } from '../util/git-ops.js';
|
|
9
|
+
import { isGitRepo, getRemoteUrl, gitFetch, gitSetRemote, canFastForward, hasDivergedFromRemote, gitMergeFfOnly, gitPullRebase, gitClone, displayUrl, addAndCommitFile, dirtyTrackedFiles, } from '../util/git-ops.js';
|
|
10
10
|
export default class Pull extends BaseCommand {
|
|
11
11
|
static description = `Clone or update a local copy of your workspace config files.
|
|
12
12
|
|
|
@@ -29,6 +29,10 @@ CLI shortcuts (no JSON editing needed for simple cases):
|
|
|
29
29
|
dir: Flags.string({
|
|
30
30
|
description: 'Local directory to clone/update (defaults to QUONFIG_DIR env var)',
|
|
31
31
|
}),
|
|
32
|
+
rebase: Flags.boolean({
|
|
33
|
+
default: false,
|
|
34
|
+
description: 'Replay local commits on top of origin/main when the two have diverged. Conflicts are surfaced via standard git markers; resolve with `git rebase --continue`.',
|
|
35
|
+
}),
|
|
32
36
|
workspace: Flags.string({
|
|
33
37
|
description: 'Workspace ID (defaults to active profile)',
|
|
34
38
|
}),
|
|
@@ -85,7 +89,43 @@ CLI shortcuts (no JSON editing needed for simple cases):
|
|
|
85
89
|
const ff = await canFastForward(resolvedDir);
|
|
86
90
|
const diverged = !ff && (await hasDivergedFromRemote(resolvedDir));
|
|
87
91
|
if (diverged) {
|
|
88
|
-
|
|
92
|
+
if (flags.rebase) {
|
|
93
|
+
this.log('Local and origin/main have diverged. Rebasing local commits on top of origin/main...');
|
|
94
|
+
const result = await gitPullRebase(resolvedDir);
|
|
95
|
+
if (result.kind === 'clean') {
|
|
96
|
+
this.log(`Rebased ${result.commitsRebased} local commit(s) onto origin/main. Run \`qfg push\` to publish.`);
|
|
97
|
+
// Fall through to post-pull work (QUONFIG_DIR write, pin backfill).
|
|
98
|
+
}
|
|
99
|
+
else if (result.kind === 'conflicts') {
|
|
100
|
+
this.log('');
|
|
101
|
+
this.log('Rebase paused with conflicts in:');
|
|
102
|
+
for (const f of result.conflictedFiles)
|
|
103
|
+
this.log(` ${f}`);
|
|
104
|
+
this.log('');
|
|
105
|
+
this.log('To finish the rebase:');
|
|
106
|
+
this.log(` 1. Edit each file above and pick the values you want; remove the git conflict markers.`);
|
|
107
|
+
this.log(` 2. git -C ${resolvedDir} add <files>`);
|
|
108
|
+
this.log(` 3. git -C ${resolvedDir} rebase --continue`);
|
|
109
|
+
this.log(` 4. qfg push`);
|
|
110
|
+
this.log('');
|
|
111
|
+
this.log('To abort the rebase and try a different approach:');
|
|
112
|
+
this.log(` git -C ${resolvedDir} rebase --abort`);
|
|
113
|
+
return this.err('Rebase paused on conflicts — see instructions above.');
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
return this.err(`Rebase could not start: ${result.reason}\n\n` +
|
|
117
|
+
`Most likely the working tree has uncommitted edits. Commit or stash them, then re-run \`qfg pull --rebase\`.`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
return this.err(`Local commits diverge from origin/main.\n\n` +
|
|
122
|
+
`Your local branch has commits that are not on origin, and origin has commits that are not local. ` +
|
|
123
|
+
`qfg pull will not merge automatically. Pick one:\n\n` +
|
|
124
|
+
` Replay your local commits on top of origin/main:\n` +
|
|
125
|
+
` qfg pull --rebase --dir ${resolvedDir}\n\n` +
|
|
126
|
+
` Discard your local commits and take origin/main as-is (LOSES local work):\n` +
|
|
127
|
+
` git -C ${resolvedDir} reset --hard origin/main`);
|
|
128
|
+
}
|
|
89
129
|
}
|
|
90
130
|
if (ff) {
|
|
91
131
|
const newCommits = await gitMergeFfOnly(resolvedDir);
|
package/dist/commands/push.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as path from 'node:path';
|
|
|
3
3
|
import { Flags } from '@oclif/core';
|
|
4
4
|
import { BaseCommand } from '../index.js';
|
|
5
5
|
import { mintGiteaToken } from '../util/gitea-api.js';
|
|
6
|
-
import { getRemoteUrl, gitFetch, gitSetRemote, isGitRepo, runGit } from '../util/git-ops.js';
|
|
6
|
+
import { dirtyTrackedFiles, getRemoteUrl, gitFetch, gitSetRemote, isGitRepo, isLocalBehindOrDivergedFromRemote, runGit } from '../util/git-ops.js';
|
|
7
7
|
import { PushFatalError, runPush, } from '../push/run-push.js';
|
|
8
8
|
import { computeBarePathDiff } from '../push/bare-path-diff.js';
|
|
9
9
|
import { callConfigsPush } from '../push/config-push-client.js';
|
|
@@ -202,6 +202,29 @@ export function buildRealDeps(cmd, orgSlug) {
|
|
|
202
202
|
const probe = await ensureBarePathProbe(dir);
|
|
203
203
|
return probe.totalRemoteFiles;
|
|
204
204
|
},
|
|
205
|
+
async isLocalBehindRemote(dir) {
|
|
206
|
+
// qfg-fboj: stale-HEAD guard for the clone path. Implementation
|
|
207
|
+
// lives in git-ops.ts so it can be unit-tested against a real
|
|
208
|
+
// local git repo. We still gate on isGitRepo here because the
|
|
209
|
+
// helper does not (it returns false on any error, but we want to
|
|
210
|
+
// skip the rev-parse cost on the bare path entirely).
|
|
211
|
+
if (!(await isGitRepo(dir)))
|
|
212
|
+
return false;
|
|
213
|
+
return isLocalBehindOrDivergedFromRemote(dir);
|
|
214
|
+
},
|
|
215
|
+
async dirtyTrackedFiles(dir) {
|
|
216
|
+
// qfg-fboj: surface uncommitted edits so the user sees they were
|
|
217
|
+
// not included in this push. Only meaningful on the clone path —
|
|
218
|
+
// bare path doesn't have a .git/ to status.
|
|
219
|
+
if (!(await isGitRepo(dir)))
|
|
220
|
+
return [];
|
|
221
|
+
try {
|
|
222
|
+
return await dirtyTrackedFiles(dir);
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return [];
|
|
226
|
+
}
|
|
227
|
+
},
|
|
205
228
|
};
|
|
206
229
|
const deps = {
|
|
207
230
|
async mintWriteToken(requestedTarget) {
|
package/dist/push/run-push.d.ts
CHANGED
|
@@ -42,6 +42,23 @@ export interface GitOps {
|
|
|
42
42
|
getRemoteOriginUrl(dir: string): Promise<string | undefined>;
|
|
43
43
|
/** Returns true if the dir has a `.git/` (worktree or repo). */
|
|
44
44
|
isGitRepo(dir: string): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns true if origin/main has commits the local HEAD does not have
|
|
47
|
+
* (local strictly behind, or diverged). False when local is up-to-date
|
|
48
|
+
* or strictly ahead of origin/main, or when origin/main is unknown.
|
|
49
|
+
*
|
|
50
|
+
* The clone-path stale-HEAD guard (qfg-fboj) refuses to push in either
|
|
51
|
+
* "behind" or "diverged" state because both produce a diff that ships
|
|
52
|
+
* REVERSAL deltas to the server, silently undoing remote-newer commits.
|
|
53
|
+
*/
|
|
54
|
+
isLocalBehindRemote(dir: string): Promise<boolean>;
|
|
55
|
+
/**
|
|
56
|
+
* Tracked files (relative paths) with working-tree or staged changes.
|
|
57
|
+
* Used by the clone-path dirty-tree warning to surface uncommitted
|
|
58
|
+
* edits the user may believe are being pushed (qfg-fboj). Untracked
|
|
59
|
+
* files are excluded — same rule as `dirtyTrackedFiles` in git-ops.ts.
|
|
60
|
+
*/
|
|
61
|
+
dirtyTrackedFiles(dir: string): Promise<string[]>;
|
|
45
62
|
/** Set origin to `url` (add if missing, set-url if present). */
|
|
46
63
|
setRemoteOrigin(dir: string, url: string): Promise<void>;
|
|
47
64
|
}
|
package/dist/push/run-push.js
CHANGED
|
@@ -136,6 +136,15 @@ export async function runPush(input, deps) {
|
|
|
136
136
|
await deps.gitOps.setRemoteOrigin(input.dir, backend.repoUrl);
|
|
137
137
|
log('Fetching from remote...');
|
|
138
138
|
await deps.gitOps.fetch(input.dir);
|
|
139
|
+
// Guard 4 (qfg-fboj): refuse to push when local HEAD is behind or has
|
|
140
|
+
// diverged from origin/main. Without this guard the HEAD-vs-origin
|
|
141
|
+
// diff produces REVERSAL deltas that silently undo whatever was
|
|
142
|
+
// committed to the cloud since the user last pulled. Tell the user
|
|
143
|
+
// to run `qfg pull` first; the standalone `--ff-only` step keeps
|
|
144
|
+
// push from doing surprising merges on the user's behalf.
|
|
145
|
+
if (await deps.gitOps.isLocalBehindRemote(input.dir)) {
|
|
146
|
+
throw new PushFatalError('Local HEAD is behind origin/main. Run `qfg pull` first to merge remote changes, then re-run `qfg push`.', 'STALE_HEAD');
|
|
147
|
+
}
|
|
139
148
|
// Migration cleanup (qfg-0fn): older `qfg pull` runs wrote the
|
|
140
149
|
// workspace pin to the working tree without committing. The next
|
|
141
150
|
// diff would miss it. Sweep up that legacy state IF the only dirty
|
|
@@ -153,6 +162,27 @@ export async function runPush(input, deps) {
|
|
|
153
162
|
catch {
|
|
154
163
|
/* non-fatal */
|
|
155
164
|
}
|
|
165
|
+
// Guard 5 (qfg-fboj): warn loudly about any tracked file with
|
|
166
|
+
// uncommitted edits beyond the workspace pin. The clone path pushes
|
|
167
|
+
// HEAD content, not the working tree, so these edits are silently
|
|
168
|
+
// dropped from the push. Listing them in the log gives the user a
|
|
169
|
+
// chance to ctrl-C, commit, and retry instead of believing they
|
|
170
|
+
// shipped what's on disk. Untracked files are intentionally not
|
|
171
|
+
// included — same rule as dirtyTrackedFiles in git-ops.ts.
|
|
172
|
+
try {
|
|
173
|
+
const dirty = await deps.gitOps.dirtyTrackedFiles(input.dir);
|
|
174
|
+
const noisyDirty = dirty.filter((f) => f !== 'quonfig.json');
|
|
175
|
+
if (noisyDirty.length > 0) {
|
|
176
|
+
log('');
|
|
177
|
+
log('Warning: working tree has uncommitted changes that will NOT be included in this push:');
|
|
178
|
+
for (const f of noisyDirty)
|
|
179
|
+
log(` ${f}`);
|
|
180
|
+
log('Commit them with `git add` + `git commit` and re-run `qfg push` if you meant to ship them.');
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
/* non-fatal */
|
|
185
|
+
}
|
|
156
186
|
}
|
|
157
187
|
const deltas = await deps.gitOps.diffHeadVsOrigin(input.dir);
|
|
158
188
|
const totalFilesInRemote = await deps.gitOps.countFilesInRemote(input.dir);
|
package/dist/util/git-ops.d.ts
CHANGED
|
@@ -101,6 +101,20 @@ export declare const gitFetch: (dir: string) => Promise<void>;
|
|
|
101
101
|
* Returns true if origin/main has commits that can be fast-forwarded into the local branch.
|
|
102
102
|
*/
|
|
103
103
|
export declare const canFastForward: (dir: string) => Promise<boolean>;
|
|
104
|
+
/**
|
|
105
|
+
* Returns true iff origin/main is NOT an ancestor of the local HEAD —
|
|
106
|
+
* covering both "local strictly behind" and "diverged" in one boolean.
|
|
107
|
+
*
|
|
108
|
+
* Used by the clone-path stale-HEAD guard in `qfg push` (qfg-fboj):
|
|
109
|
+
* either of those two states would otherwise produce a `HEAD..origin/main`
|
|
110
|
+
* diff that ships REVERSAL deltas to the server, silently undoing
|
|
111
|
+
* remote-newer commits. Both must refuse.
|
|
112
|
+
*
|
|
113
|
+
* Returns false on any git failure (no `origin/main` ref yet, no `.git/`,
|
|
114
|
+
* etc.) so the caller falls through to its other guards rather than
|
|
115
|
+
* aborting on an opaque error.
|
|
116
|
+
*/
|
|
117
|
+
export declare const isLocalBehindOrDivergedFromRemote: (dir: string) => Promise<boolean>;
|
|
104
118
|
/**
|
|
105
119
|
* Returns true if local has commits not reachable from origin/main (diverged).
|
|
106
120
|
*/
|
|
@@ -120,3 +134,29 @@ export declare const hasAtLeastOneCommit: (dir: string) => Promise<boolean>;
|
|
|
120
134
|
* Get the URL stripped of credentials for display purposes.
|
|
121
135
|
*/
|
|
122
136
|
export declare const displayUrl: (url: string) => string;
|
|
137
|
+
/**
|
|
138
|
+
* Outcome of `gitPullRebase`. Three branches the caller MUST distinguish
|
|
139
|
+
* — silent failure here is what made qfg-4tey a P1 (qfg pull exit 0 with
|
|
140
|
+
* no recovery path).
|
|
141
|
+
*/
|
|
142
|
+
export type GitPullRebaseResult = {
|
|
143
|
+
kind: 'clean';
|
|
144
|
+
commitsRebased: number;
|
|
145
|
+
} | {
|
|
146
|
+
kind: 'conflicts';
|
|
147
|
+
conflictedFiles: string[];
|
|
148
|
+
} | {
|
|
149
|
+
kind: 'failed';
|
|
150
|
+
reason: string;
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* `git pull --rebase origin main`. Replays local commits on top of the
|
|
154
|
+
* remote tip. On conflicts, leaves the repo in rebase-in-progress state
|
|
155
|
+
* with `<<<<<<<` / `=======` / `>>>>>>>` markers planted by git so the
|
|
156
|
+
* user can resolve via standard git tools (`git rebase --continue` /
|
|
157
|
+
* `git rebase --abort`).
|
|
158
|
+
*
|
|
159
|
+
* Caller is responsible for surfacing recovery instructions; this function
|
|
160
|
+
* only reports the outcome.
|
|
161
|
+
*/
|
|
162
|
+
export declare const gitPullRebase: (dir: string) => Promise<GitPullRebaseResult>;
|
package/dist/util/git-ops.js
CHANGED
|
@@ -222,6 +222,41 @@ export const canFastForward = async (dir) => {
|
|
|
222
222
|
return false;
|
|
223
223
|
}
|
|
224
224
|
};
|
|
225
|
+
/**
|
|
226
|
+
* Returns true iff origin/main is NOT an ancestor of the local HEAD —
|
|
227
|
+
* covering both "local strictly behind" and "diverged" in one boolean.
|
|
228
|
+
*
|
|
229
|
+
* Used by the clone-path stale-HEAD guard in `qfg push` (qfg-fboj):
|
|
230
|
+
* either of those two states would otherwise produce a `HEAD..origin/main`
|
|
231
|
+
* diff that ships REVERSAL deltas to the server, silently undoing
|
|
232
|
+
* remote-newer commits. Both must refuse.
|
|
233
|
+
*
|
|
234
|
+
* Returns false on any git failure (no `origin/main` ref yet, no `.git/`,
|
|
235
|
+
* etc.) so the caller falls through to its other guards rather than
|
|
236
|
+
* aborting on an opaque error.
|
|
237
|
+
*/
|
|
238
|
+
export const isLocalBehindOrDivergedFromRemote = async (dir) => {
|
|
239
|
+
try {
|
|
240
|
+
const { stdout: localSha } = await runGit(['-C', dir, 'rev-parse', 'HEAD']);
|
|
241
|
+
const { stdout: remoteSha } = await runGit(['-C', dir, 'rev-parse', 'origin/main']);
|
|
242
|
+
const local = localSha.trim();
|
|
243
|
+
const remote = remoteSha.trim();
|
|
244
|
+
if (local === remote)
|
|
245
|
+
return false;
|
|
246
|
+
try {
|
|
247
|
+
// exit 0 → remote is an ancestor of local → local is strictly ahead, fine.
|
|
248
|
+
await runGit(['-C', dir, 'merge-base', '--is-ancestor', remote, local]);
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
// non-zero exit → remote is NOT an ancestor → behind or diverged.
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
};
|
|
225
260
|
/**
|
|
226
261
|
* Returns true if local has commits not reachable from origin/main (diverged).
|
|
227
262
|
*/
|
|
@@ -310,3 +345,50 @@ export const hasAtLeastOneCommit = async (dir) => {
|
|
|
310
345
|
* Get the URL stripped of credentials for display purposes.
|
|
311
346
|
*/
|
|
312
347
|
export const displayUrl = (url) => redactToken(url);
|
|
348
|
+
/**
|
|
349
|
+
* `git pull --rebase origin main`. Replays local commits on top of the
|
|
350
|
+
* remote tip. On conflicts, leaves the repo in rebase-in-progress state
|
|
351
|
+
* with `<<<<<<<` / `=======` / `>>>>>>>` markers planted by git so the
|
|
352
|
+
* user can resolve via standard git tools (`git rebase --continue` /
|
|
353
|
+
* `git rebase --abort`).
|
|
354
|
+
*
|
|
355
|
+
* Caller is responsible for surfacing recovery instructions; this function
|
|
356
|
+
* only reports the outcome.
|
|
357
|
+
*/
|
|
358
|
+
export const gitPullRebase = async (dir) => {
|
|
359
|
+
// Count local-only commits BEFORE the rebase so the success path can
|
|
360
|
+
// tell the user how many got moved. After a clean rebase, origin/main
|
|
361
|
+
// is the merge-base and ahead-count == commits-rebased.
|
|
362
|
+
let commitsRebased = 0;
|
|
363
|
+
try {
|
|
364
|
+
const { stdout } = await runGit(['-C', dir, 'rev-list', '--count', 'origin/main..HEAD']);
|
|
365
|
+
commitsRebased = Number.parseInt(stdout.trim(), 10) || 0;
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
// Non-fatal — counting is for UX, not correctness.
|
|
369
|
+
}
|
|
370
|
+
try {
|
|
371
|
+
await runGit(['-C', dir, 'pull', '--rebase', 'origin', 'main']);
|
|
372
|
+
return { kind: 'clean', commitsRebased };
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
// Distinguish "rebase paused on conflicts" from "rebase never started".
|
|
376
|
+
// Conflicts: `diff --diff-filter=U` lists the unmerged paths and
|
|
377
|
+
// `.git/rebase-merge/` is present.
|
|
378
|
+
try {
|
|
379
|
+
const { stdout } = await runGit(['-C', dir, 'diff', '--name-only', '--diff-filter=U']);
|
|
380
|
+
const conflictedFiles = stdout
|
|
381
|
+
.split('\n')
|
|
382
|
+
.map((s) => s.trim())
|
|
383
|
+
.filter(Boolean);
|
|
384
|
+
if (conflictedFiles.length > 0) {
|
|
385
|
+
return { kind: 'conflicts', conflictedFiles };
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
// Fall through to 'failed' — diff itself errored.
|
|
390
|
+
}
|
|
391
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
392
|
+
return { kind: 'failed', reason };
|
|
393
|
+
}
|
|
394
|
+
};
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "0.0.
|
|
1
|
+
declare const _default: "0.0.34";
|
|
2
2
|
export default _default;
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// AUTO-GENERATED from package.json by scripts/generate-version.mjs — do not edit.
|
|
2
|
-
export default '0.0.
|
|
2
|
+
export default '0.0.34';
|
package/oclif.manifest.json
CHANGED
|
@@ -1415,6 +1415,12 @@
|
|
|
1415
1415
|
"multiple": false,
|
|
1416
1416
|
"type": "option"
|
|
1417
1417
|
},
|
|
1418
|
+
"rebase": {
|
|
1419
|
+
"description": "Replay local commits on top of origin/main when the two have diverged. Conflicts are surfaced via standard git markers; resolve with `git rebase --continue`.",
|
|
1420
|
+
"name": "rebase",
|
|
1421
|
+
"allowNo": false,
|
|
1422
|
+
"type": "boolean"
|
|
1423
|
+
},
|
|
1418
1424
|
"workspace": {
|
|
1419
1425
|
"description": "Workspace ID (defaults to active profile)",
|
|
1420
1426
|
"name": "workspace",
|
|
@@ -2910,5 +2916,5 @@
|
|
|
2910
2916
|
]
|
|
2911
2917
|
}
|
|
2912
2918
|
},
|
|
2913
|
-
"version": "0.0.
|
|
2919
|
+
"version": "0.0.34"
|
|
2914
2920
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"packageManager": "yarn@4.11.0+sha512.4e54aeace9141df2f0177c266b05ec50dc044638157dae128c471ba65994ac802122d7ab35bcd9e81641228b7dcf24867d28e750e0bcae8a05277d600008ad54",
|
|
3
3
|
"name": "@quonfig/cli",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.34",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/quonfig/cli/issues"
|
|
7
7
|
},
|