@quonfig/cli 0.0.53 → 0.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -45
- package/dist/commands/migrate.d.ts +6 -0
- package/dist/commands/migrate.js +69 -7
- package/dist/commands/push.js +9 -3
- package/dist/commands/workspace/create.js +9 -1
- package/dist/migrate/local-write.d.ts +1 -1
- package/dist/migrate/local-write.js +1 -1
- package/dist/migrate/migration-report.d.ts +11 -0
- package/dist/migrate/migration-report.js +13 -2
- package/dist/migrate/push-to-cloud.d.ts +2 -2
- package/dist/migrate/source.d.ts +7 -1
- package/dist/migrate/sources/launch/api.d.ts +1 -1
- package/dist/migrate/sources/launch/api.js +4 -1
- package/dist/migrate/sources/launch.js +4 -4
- package/dist/push/bare-path-diff.d.ts +7 -0
- package/dist/push/bare-path-diff.js +17 -1
- package/dist/push/run-push.d.ts +5 -7
- package/dist/push/run-push.js +4 -4
- package/dist/serve/handlers/evalContext.js +1 -30
- package/dist/util/friction-log.d.ts +7 -1
- package/dist/util/friction-log.js +9 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/oclif.manifest.json +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ $ npm install -g @quonfig/cli
|
|
|
17
17
|
$ qfg COMMAND
|
|
18
18
|
running command...
|
|
19
19
|
$ qfg (--version)
|
|
20
|
-
@quonfig/cli/0.0.
|
|
20
|
+
@quonfig/cli/0.0.55 linux-x64 node-v24.4.1
|
|
21
21
|
$ qfg --help [COMMAND]
|
|
22
22
|
USAGE
|
|
23
23
|
$ qfg COMMAND
|
|
@@ -196,7 +196,7 @@ EXAMPLES
|
|
|
196
196
|
$ qfg activity restore my.flag --yes
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
-
_See code: [src/commands/activity.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
199
|
+
_See code: [src/commands/activity.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity.ts)_
|
|
200
200
|
|
|
201
201
|
## `qfg activity deleted`
|
|
202
202
|
|
|
@@ -225,7 +225,7 @@ EXAMPLES
|
|
|
225
225
|
$ qfg activity deleted --json
|
|
226
226
|
```
|
|
227
227
|
|
|
228
|
-
_See code: [src/commands/activity/deleted.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
228
|
+
_See code: [src/commands/activity/deleted.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity/deleted.ts)_
|
|
229
229
|
|
|
230
230
|
## `qfg activity feed`
|
|
231
231
|
|
|
@@ -260,7 +260,7 @@ EXAMPLES
|
|
|
260
260
|
$ qfg activity feed --json
|
|
261
261
|
```
|
|
262
262
|
|
|
263
|
-
_See code: [src/commands/activity/feed.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
263
|
+
_See code: [src/commands/activity/feed.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity/feed.ts)_
|
|
264
264
|
|
|
265
265
|
## `qfg activity history [NAME]`
|
|
266
266
|
|
|
@@ -294,7 +294,7 @@ EXAMPLES
|
|
|
294
294
|
$ qfg activity history request.timeout --json
|
|
295
295
|
```
|
|
296
296
|
|
|
297
|
-
_See code: [src/commands/activity/history.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
297
|
+
_See code: [src/commands/activity/history.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity/history.ts)_
|
|
298
298
|
|
|
299
299
|
## `qfg activity restore [NAME]`
|
|
300
300
|
|
|
@@ -333,7 +333,7 @@ EXAMPLES
|
|
|
333
333
|
$ qfg activity restore request.timeout --type config --yes
|
|
334
334
|
```
|
|
335
335
|
|
|
336
|
-
_See code: [src/commands/activity/restore.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
336
|
+
_See code: [src/commands/activity/restore.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/activity/restore.ts)_
|
|
337
337
|
|
|
338
338
|
## `qfg audit-log [NAME]`
|
|
339
339
|
|
|
@@ -361,7 +361,7 @@ EXAMPLES
|
|
|
361
361
|
$ qfg audit-log my.flag
|
|
362
362
|
```
|
|
363
363
|
|
|
364
|
-
_See code: [src/commands/audit-log.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
364
|
+
_See code: [src/commands/audit-log.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/audit-log.ts)_
|
|
365
365
|
|
|
366
366
|
## `qfg config-schema`
|
|
367
367
|
|
|
@@ -390,7 +390,7 @@ EXAMPLES
|
|
|
390
390
|
$ qfg config-schema --json-schema # full JSON Schema document (copy into your editor)
|
|
391
391
|
```
|
|
392
392
|
|
|
393
|
-
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
393
|
+
_See code: [src/commands/config-schema.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/config-schema.ts)_
|
|
394
394
|
|
|
395
395
|
## `qfg create NAME`
|
|
396
396
|
|
|
@@ -475,7 +475,7 @@ EXAMPLES
|
|
|
475
475
|
$ qfg set-rollout my.new.flag --environment production --true-percent 20
|
|
476
476
|
```
|
|
477
477
|
|
|
478
|
-
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
478
|
+
_See code: [src/commands/create.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/create.ts)_
|
|
479
479
|
|
|
480
480
|
## `qfg delete NAME`
|
|
481
481
|
|
|
@@ -521,7 +521,7 @@ EXAMPLES
|
|
|
521
521
|
$ qfg delete my.config --yes
|
|
522
522
|
```
|
|
523
523
|
|
|
524
|
-
_See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
524
|
+
_See code: [src/commands/delete.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/delete.ts)_
|
|
525
525
|
|
|
526
526
|
## `qfg flag info [NAME]`
|
|
527
527
|
|
|
@@ -725,7 +725,7 @@ EXAMPLES
|
|
|
725
725
|
$ qfg generate --targets node-ts -o ./dist # combine with targets
|
|
726
726
|
```
|
|
727
727
|
|
|
728
|
-
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
728
|
+
_See code: [src/commands/generate.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/generate.ts)_
|
|
729
729
|
|
|
730
730
|
## `qfg generate-new-hex-key`
|
|
731
731
|
|
|
@@ -748,7 +748,7 @@ EXAMPLES
|
|
|
748
748
|
$ qfg generate-new-hex-key
|
|
749
749
|
```
|
|
750
750
|
|
|
751
|
-
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
751
|
+
_See code: [src/commands/generate-new-hex-key.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/generate-new-hex-key.ts)_
|
|
752
752
|
|
|
753
753
|
## `qfg get [NAME]`
|
|
754
754
|
|
|
@@ -780,7 +780,7 @@ EXAMPLES
|
|
|
780
780
|
$ qfg get my.config.name --environment=production
|
|
781
781
|
```
|
|
782
782
|
|
|
783
|
-
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
783
|
+
_See code: [src/commands/get.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/get.ts)_
|
|
784
784
|
|
|
785
785
|
## `qfg history NAME`
|
|
786
786
|
|
|
@@ -806,7 +806,7 @@ EXAMPLES
|
|
|
806
806
|
$ qfg history my.flag
|
|
807
807
|
```
|
|
808
808
|
|
|
809
|
-
_See code: [src/commands/history.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
809
|
+
_See code: [src/commands/history.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/history.ts)_
|
|
810
810
|
|
|
811
811
|
## `qfg info [NAME]`
|
|
812
812
|
|
|
@@ -854,7 +854,7 @@ EXAMPLES
|
|
|
854
854
|
$ qfg info my.config.name --exclude-evaluations # skip 24h stats
|
|
855
855
|
```
|
|
856
856
|
|
|
857
|
-
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
857
|
+
_See code: [src/commands/info.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/info.ts)_
|
|
858
858
|
|
|
859
859
|
## `qfg init [DIRECTORY]`
|
|
860
860
|
|
|
@@ -894,7 +894,7 @@ EXAMPLES
|
|
|
894
894
|
$ qfg init --dry-run
|
|
895
895
|
```
|
|
896
896
|
|
|
897
|
-
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
897
|
+
_See code: [src/commands/init.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/init.ts)_
|
|
898
898
|
|
|
899
899
|
## `qfg interactive`
|
|
900
900
|
|
|
@@ -924,7 +924,7 @@ EXAMPLES
|
|
|
924
924
|
$ qfg
|
|
925
925
|
```
|
|
926
926
|
|
|
927
|
-
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
927
|
+
_See code: [src/commands/interactive.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/interactive.ts)_
|
|
928
928
|
|
|
929
929
|
## `qfg list`
|
|
930
930
|
|
|
@@ -964,7 +964,7 @@ EXAMPLES
|
|
|
964
964
|
$ qfg list --feature-flags
|
|
965
965
|
```
|
|
966
966
|
|
|
967
|
-
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
967
|
+
_See code: [src/commands/list.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/list.ts)_
|
|
968
968
|
|
|
969
969
|
## `qfg log`
|
|
970
970
|
|
|
@@ -989,7 +989,7 @@ EXAMPLES
|
|
|
989
989
|
$ qfg log --limit 5
|
|
990
990
|
```
|
|
991
991
|
|
|
992
|
-
_See code: [src/commands/log.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
992
|
+
_See code: [src/commands/log.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/log.ts)_
|
|
993
993
|
|
|
994
994
|
## `qfg log-level NAME`
|
|
995
995
|
|
|
@@ -1054,7 +1054,7 @@ EXAMPLES
|
|
|
1054
1054
|
$ qfg log-level log-level.my-app --target=Chatty --value=INFO --environment=production
|
|
1055
1055
|
```
|
|
1056
1056
|
|
|
1057
|
-
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1057
|
+
_See code: [src/commands/log-level.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/log-level.ts)_
|
|
1058
1058
|
|
|
1059
1059
|
## `qfg login`
|
|
1060
1060
|
|
|
@@ -1079,7 +1079,7 @@ EXAMPLES
|
|
|
1079
1079
|
$ qfg login --profile myprofile
|
|
1080
1080
|
```
|
|
1081
1081
|
|
|
1082
|
-
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1082
|
+
_See code: [src/commands/login.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/login.ts)_
|
|
1083
1083
|
|
|
1084
1084
|
## `qfg logout`
|
|
1085
1085
|
|
|
@@ -1102,7 +1102,7 @@ EXAMPLES
|
|
|
1102
1102
|
$ qfg logout
|
|
1103
1103
|
```
|
|
1104
1104
|
|
|
1105
|
-
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1105
|
+
_See code: [src/commands/logout.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/logout.ts)_
|
|
1106
1106
|
|
|
1107
1107
|
## `qfg migrate`
|
|
1108
1108
|
|
|
@@ -1165,7 +1165,7 @@ EXAMPLES
|
|
|
1165
1165
|
$ qfg migrate --from launch --source-api-key $LAUNCH_API_KEY --dir ./quonfig-repo --staging
|
|
1166
1166
|
```
|
|
1167
1167
|
|
|
1168
|
-
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1168
|
+
_See code: [src/commands/migrate.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/migrate.ts)_
|
|
1169
1169
|
|
|
1170
1170
|
## `qfg migrate doctor`
|
|
1171
1171
|
|
|
@@ -1203,7 +1203,7 @@ EXAMPLES
|
|
|
1203
1203
|
$ qfg migrate doctor --json
|
|
1204
1204
|
```
|
|
1205
1205
|
|
|
1206
|
-
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1206
|
+
_See code: [src/commands/migrate/doctor.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/migrate/doctor.ts)_
|
|
1207
1207
|
|
|
1208
1208
|
## `qfg migrate my-code`
|
|
1209
1209
|
|
|
@@ -1235,7 +1235,7 @@ EXAMPLES
|
|
|
1235
1235
|
$ qfg migrate my-code --dry-run
|
|
1236
1236
|
```
|
|
1237
1237
|
|
|
1238
|
-
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1238
|
+
_See code: [src/commands/migrate/my-code.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/migrate/my-code.ts)_
|
|
1239
1239
|
|
|
1240
1240
|
## `qfg migrate status`
|
|
1241
1241
|
|
|
@@ -1266,7 +1266,7 @@ EXAMPLES
|
|
|
1266
1266
|
$ qfg migrate status --json
|
|
1267
1267
|
```
|
|
1268
1268
|
|
|
1269
|
-
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1269
|
+
_See code: [src/commands/migrate/status.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/migrate/status.ts)_
|
|
1270
1270
|
|
|
1271
1271
|
## `qfg override [NAME] [VALUE]`
|
|
1272
1272
|
|
|
@@ -1321,7 +1321,7 @@ EXAMPLES
|
|
|
1321
1321
|
$ qfg override my.flag true --env=staging
|
|
1322
1322
|
```
|
|
1323
1323
|
|
|
1324
|
-
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1324
|
+
_See code: [src/commands/override.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/override.ts)_
|
|
1325
1325
|
|
|
1326
1326
|
## `qfg pull`
|
|
1327
1327
|
|
|
@@ -1369,7 +1369,7 @@ EXAMPLES
|
|
|
1369
1369
|
$ qfg pull # uses QUONFIG_DIR env var
|
|
1370
1370
|
```
|
|
1371
1371
|
|
|
1372
|
-
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1372
|
+
_See code: [src/commands/pull.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/pull.ts)_
|
|
1373
1373
|
|
|
1374
1374
|
## `qfg push`
|
|
1375
1375
|
|
|
@@ -1418,7 +1418,7 @@ EXAMPLES
|
|
|
1418
1418
|
$ qfg push --dir ./our-config --yes
|
|
1419
1419
|
```
|
|
1420
1420
|
|
|
1421
|
-
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1421
|
+
_See code: [src/commands/push.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/push.ts)_
|
|
1422
1422
|
|
|
1423
1423
|
## `qfg run [COMMAND]`
|
|
1424
1424
|
|
|
@@ -1469,7 +1469,7 @@ EXAMPLES
|
|
|
1469
1469
|
$ qfg run --env DATABASE_URL=db.url --preserve-env --environment=staging -- npm test
|
|
1470
1470
|
```
|
|
1471
1471
|
|
|
1472
|
-
_See code: [src/commands/run.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1472
|
+
_See code: [src/commands/run.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/run.ts)_
|
|
1473
1473
|
|
|
1474
1474
|
## `qfg schema NAME`
|
|
1475
1475
|
|
|
@@ -1507,7 +1507,7 @@ EXAMPLES
|
|
|
1507
1507
|
$ qfg schema my-schema --set-json-schema=@schemas/my-schema.json --protected
|
|
1508
1508
|
```
|
|
1509
1509
|
|
|
1510
|
-
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1510
|
+
_See code: [src/commands/schema.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/schema.ts)_
|
|
1511
1511
|
|
|
1512
1512
|
## `qfg sdk-key`
|
|
1513
1513
|
|
|
@@ -1534,7 +1534,7 @@ EXAMPLES
|
|
|
1534
1534
|
$ qfg sdk-key revoke <key-id>
|
|
1535
1535
|
```
|
|
1536
1536
|
|
|
1537
|
-
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1537
|
+
_See code: [src/commands/sdk-key.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sdk-key.ts)_
|
|
1538
1538
|
|
|
1539
1539
|
## `qfg sdk-key create`
|
|
1540
1540
|
|
|
@@ -1566,7 +1566,7 @@ EXAMPLES
|
|
|
1566
1566
|
$ qfg sdk-key create --environment staging --type browser
|
|
1567
1567
|
```
|
|
1568
1568
|
|
|
1569
|
-
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1569
|
+
_See code: [src/commands/sdk-key/create.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sdk-key/create.ts)_
|
|
1570
1570
|
|
|
1571
1571
|
## `qfg sdk-key list`
|
|
1572
1572
|
|
|
@@ -1595,7 +1595,7 @@ EXAMPLES
|
|
|
1595
1595
|
$ qfg sdk-key list --environment production
|
|
1596
1596
|
```
|
|
1597
1597
|
|
|
1598
|
-
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1598
|
+
_See code: [src/commands/sdk-key/list.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sdk-key/list.ts)_
|
|
1599
1599
|
|
|
1600
1600
|
## `qfg sdk-key revoke KEYID`
|
|
1601
1601
|
|
|
@@ -1622,7 +1622,7 @@ EXAMPLES
|
|
|
1622
1622
|
$ qfg sdk-key revoke a1b2c3d4-e5f6-7890-abcd-ef1234567890
|
|
1623
1623
|
```
|
|
1624
1624
|
|
|
1625
|
-
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1625
|
+
_See code: [src/commands/sdk-key/revoke.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sdk-key/revoke.ts)_
|
|
1626
1626
|
|
|
1627
1627
|
## `qfg serve`
|
|
1628
1628
|
|
|
@@ -1679,7 +1679,7 @@ EXAMPLES
|
|
|
1679
1679
|
$ qfg serve --port 6581 --frontend-sdk-key PUBLIC_KEY
|
|
1680
1680
|
```
|
|
1681
1681
|
|
|
1682
|
-
_See code: [src/commands/serve.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1682
|
+
_See code: [src/commands/serve.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/serve.ts)_
|
|
1683
1683
|
|
|
1684
1684
|
## `qfg set-default [NAME]`
|
|
1685
1685
|
|
|
@@ -1750,7 +1750,7 @@ EXAMPLES
|
|
|
1750
1750
|
$ qfg pull && qfg config-schema
|
|
1751
1751
|
```
|
|
1752
1752
|
|
|
1753
|
-
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1753
|
+
_See code: [src/commands/set-default.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/set-default.ts)_
|
|
1754
1754
|
|
|
1755
1755
|
## `qfg set-rollout [NAME]`
|
|
1756
1756
|
|
|
@@ -1827,7 +1827,7 @@ EXAMPLES
|
|
|
1827
1827
|
$ qfg set-rollout my.variant.flag --environment production --weights "a:33,b:33,c:34" --hash-by user.id
|
|
1828
1828
|
```
|
|
1829
1829
|
|
|
1830
|
-
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1830
|
+
_See code: [src/commands/set-rollout.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/set-rollout.ts)_
|
|
1831
1831
|
|
|
1832
1832
|
## `qfg sync`
|
|
1833
1833
|
|
|
@@ -1859,7 +1859,7 @@ EXAMPLES
|
|
|
1859
1859
|
$ qfg sync --watch --dir ./our-config --interval 30
|
|
1860
1860
|
```
|
|
1861
1861
|
|
|
1862
|
-
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1862
|
+
_See code: [src/commands/sync.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/sync.ts)_
|
|
1863
1863
|
|
|
1864
1864
|
## `qfg toggle [NAME]`
|
|
1865
1865
|
|
|
@@ -1961,7 +1961,7 @@ EXAMPLES
|
|
|
1961
1961
|
$ qfg verify --json
|
|
1962
1962
|
```
|
|
1963
1963
|
|
|
1964
|
-
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1964
|
+
_See code: [src/commands/verify.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/verify.ts)_
|
|
1965
1965
|
|
|
1966
1966
|
## `qfg whoami`
|
|
1967
1967
|
|
|
@@ -1984,7 +1984,7 @@ EXAMPLES
|
|
|
1984
1984
|
$ qfg whoami
|
|
1985
1985
|
```
|
|
1986
1986
|
|
|
1987
|
-
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
1987
|
+
_See code: [src/commands/whoami.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/whoami.ts)_
|
|
1988
1988
|
|
|
1989
1989
|
## `qfg workspace`
|
|
1990
1990
|
|
|
@@ -2007,7 +2007,7 @@ EXAMPLES
|
|
|
2007
2007
|
$ qfg workspace
|
|
2008
2008
|
```
|
|
2009
2009
|
|
|
2010
|
-
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
2010
|
+
_See code: [src/commands/workspace.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/workspace.ts)_
|
|
2011
2011
|
|
|
2012
2012
|
## `qfg workspace bootstrap`
|
|
2013
2013
|
|
|
@@ -2040,7 +2040,7 @@ EXAMPLES
|
|
|
2040
2040
|
$ qfg workspace bootstrap --dir ./our-config --skip-validate
|
|
2041
2041
|
```
|
|
2042
2042
|
|
|
2043
|
-
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
2043
|
+
_See code: [src/commands/workspace/bootstrap.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/workspace/bootstrap.ts)_
|
|
2044
2044
|
|
|
2045
2045
|
## `qfg workspace create SLUG`
|
|
2046
2046
|
|
|
@@ -2074,7 +2074,7 @@ EXAMPLES
|
|
|
2074
2074
|
$ qfg workspace create my-team --org 11111111-1111-1111-1111-111111111111
|
|
2075
2075
|
```
|
|
2076
2076
|
|
|
2077
|
-
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
2077
|
+
_See code: [src/commands/workspace/create.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/workspace/create.ts)_
|
|
2078
2078
|
|
|
2079
2079
|
## `qfg workspace switch [SLUG]`
|
|
2080
2080
|
|
|
@@ -2102,7 +2102,7 @@ EXAMPLES
|
|
|
2102
2102
|
$ qfg workspace switch acme/production
|
|
2103
2103
|
```
|
|
2104
2104
|
|
|
2105
|
-
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.
|
|
2105
|
+
_See code: [src/commands/workspace/switch.ts](https://github.com/quonfig/cli/blob/v0.0.55/src/commands/workspace/switch.ts)_
|
|
2106
2106
|
<!-- commandsstop -->
|
|
2107
2107
|
|
|
2108
2108
|
## Local Development
|
|
@@ -19,5 +19,11 @@ export default class Migrate extends BaseCommand {
|
|
|
19
19
|
workspace: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
|
|
20
20
|
};
|
|
21
21
|
run(): Promise<JsonObj | void>;
|
|
22
|
+
/**
|
|
23
|
+
* After a local (non-push) migration commits, the user is left at a prompt
|
|
24
|
+
* with a fresh directory and no obvious next move. Spell out the review →
|
|
25
|
+
* push flow explicitly.
|
|
26
|
+
*/
|
|
27
|
+
private printLocalNextSteps;
|
|
22
28
|
private resolvePushWorkspace;
|
|
23
29
|
}
|
package/dist/commands/migrate.js
CHANGED
|
@@ -5,6 +5,7 @@ import { BaseCommand } from '../index.js';
|
|
|
5
5
|
import { CrossSourceError, assertSourceMatches, readImportState } from '../migrate/import-state.js';
|
|
6
6
|
import { applyLocalMigration, MigratorKeyCollisionError } from '../migrate/local-write.js';
|
|
7
7
|
import { buildPushConflictSuggestion } from '../migrate/migrate-suggestion.js';
|
|
8
|
+
import { migrationReportPath } from '../migrate/migration-report.js';
|
|
8
9
|
import { PushConflictError, PushHookRejectedError } from '../util/clone-and-stack-push.js';
|
|
9
10
|
import { MigratorVerifyError, pushMigrationToCloud } from '../migrate/push-to-cloud.js';
|
|
10
11
|
import { UnknownSourceError, getSource } from '../migrate/registry.js';
|
|
@@ -20,6 +21,10 @@ import { displayUrl } from '../util/git-ops.js';
|
|
|
20
21
|
import { resolveWorkspaceUuid } from '../util/resolve-workspace.js';
|
|
21
22
|
import { loadAuthConfig } from '../util/token-storage.js';
|
|
22
23
|
const DEFAULT_DIR = 'quonfig-repo';
|
|
24
|
+
// How many fetched changes between progress lines during the (silent,
|
|
25
|
+
// paginated) change-history fetch. Large accounts page through thousands of
|
|
26
|
+
// changes 50 at a time; without periodic output the CLI looks frozen.
|
|
27
|
+
const FETCH_PROGRESS_INTERVAL = 1000;
|
|
23
28
|
export default class Migrate extends BaseCommand {
|
|
24
29
|
static description = 'Migrate flags and configs from a legacy feature-flag system into a Quonfig workspace.\n\n' +
|
|
25
30
|
'Currently supported sources:\n' +
|
|
@@ -172,9 +177,28 @@ export default class Migrate extends BaseCommand {
|
|
|
172
177
|
return this.err(error.message);
|
|
173
178
|
throw error;
|
|
174
179
|
}
|
|
180
|
+
// The fetch and write phases below are silent for a long time on a large
|
|
181
|
+
// account. Print a plan up front and a step line before each slow phase so
|
|
182
|
+
// the CLI never looks frozen, even without --verbose.
|
|
183
|
+
this.log(`Migrating from ${flags.from} into ${dir}.`);
|
|
184
|
+
if (flags['dry-run']) {
|
|
185
|
+
this.log('Mode: dry run — changes are summarized but nothing is written.');
|
|
186
|
+
}
|
|
187
|
+
else if (flags.push) {
|
|
188
|
+
this.log('Mode: migrate to a local dir, then push to the Quonfig cloud workspace.');
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
this.log('Mode: local migration only — re-run with --push to also publish to a cloud workspace.');
|
|
192
|
+
}
|
|
193
|
+
this.log('');
|
|
175
194
|
let duplicateResolutionsForWarn = null;
|
|
195
|
+
// Set to the target dir once a local (non-push) migration commits, so the
|
|
196
|
+
// `finally` block can print "what now?" guidance after the warnings.
|
|
197
|
+
let localNextStepsDir = null;
|
|
176
198
|
try {
|
|
199
|
+
this.log(`Authenticating with ${flags.from}...`);
|
|
177
200
|
await source.validateAuth(sourceApiKey);
|
|
201
|
+
this.log(`Reading the environment list from ${flags.from}...`);
|
|
178
202
|
const environments = await source.listEnvironments();
|
|
179
203
|
// Plan §4.1.1: tell the user the real history horizon BEFORE the slow
|
|
180
204
|
// Phase-2 walk starts. validateAuth populated this for LD under
|
|
@@ -187,8 +211,15 @@ export default class Migrate extends BaseCommand {
|
|
|
187
211
|
}
|
|
188
212
|
const existing = readImportState(dir);
|
|
189
213
|
const sinceEpochMs = computeSince(flags, existing);
|
|
214
|
+
this.log(`Fetching change history from ${flags.from} — this can take a few minutes for a large account...`);
|
|
190
215
|
const changes = [];
|
|
191
|
-
|
|
216
|
+
let lastReportedFetch = 0;
|
|
217
|
+
for await (const change of source.fetchChanges(sinceEpochMs, (fetched) => {
|
|
218
|
+
if (fetched - lastReportedFetch >= FETCH_PROGRESS_INTERVAL) {
|
|
219
|
+
lastReportedFetch = fetched;
|
|
220
|
+
this.log(` ...fetched ${fetched} change(s) so far`);
|
|
221
|
+
}
|
|
222
|
+
})) {
|
|
192
223
|
changes.push(change);
|
|
193
224
|
}
|
|
194
225
|
const toProcess = flags.recent === undefined ? changes : changes.slice(-flags.recent);
|
|
@@ -239,6 +270,8 @@ export default class Migrate extends BaseCommand {
|
|
|
239
270
|
this.log(result.committed
|
|
240
271
|
? `Pushed ${toProcess.length} change(s). commit=${result.commitSha?.slice(0, 8) ?? ''} action=${result.action}`
|
|
241
272
|
: `No net changes produced by this run. Nothing to commit or push.`);
|
|
273
|
+
this.log(`Migration report written to ${migrationReportPath(dir)} — review it before cutover ` +
|
|
274
|
+
'(it lives in the hidden .qf/ directory).');
|
|
242
275
|
return {
|
|
243
276
|
...payload,
|
|
244
277
|
action: result.action,
|
|
@@ -285,6 +318,10 @@ export default class Migrate extends BaseCommand {
|
|
|
285
318
|
this.log(localResult.committed
|
|
286
319
|
? `Committed ${toProcess.length} change(s). commit=${localResult.commitSha?.slice(0, 8) ?? ''} action=${localResult.action}`
|
|
287
320
|
: `No net changes produced by this run. Nothing to commit.`);
|
|
321
|
+
// Print "what now?" guidance from the `finally` block (after the
|
|
322
|
+
// warnings) so it is the last thing the user sees.
|
|
323
|
+
if (localResult.committed)
|
|
324
|
+
localNextStepsDir = dir;
|
|
288
325
|
return {
|
|
289
326
|
...payload,
|
|
290
327
|
action: localResult.action,
|
|
@@ -307,8 +344,33 @@ export default class Migrate extends BaseCommand {
|
|
|
307
344
|
warnAboutCoercedSentinels(this, source.getCoercedSentinels?.() ?? null);
|
|
308
345
|
warnAboutDuplicateResolutions(this, duplicateResolutionsForWarn);
|
|
309
346
|
warnAboutConversionNotes(this, source.getConversionNotes?.() ?? null);
|
|
347
|
+
if (localNextStepsDir)
|
|
348
|
+
this.printLocalNextSteps(localNextStepsDir);
|
|
310
349
|
}
|
|
311
350
|
}
|
|
351
|
+
/**
|
|
352
|
+
* After a local (non-push) migration commits, the user is left at a prompt
|
|
353
|
+
* with a fresh directory and no obvious next move. Spell out the review →
|
|
354
|
+
* push flow explicitly.
|
|
355
|
+
*/
|
|
356
|
+
printLocalNextSteps(dir) {
|
|
357
|
+
this.log('');
|
|
358
|
+
this.log('Migration written to a local directory — nothing has been pushed to Quonfig cloud yet.');
|
|
359
|
+
this.log('');
|
|
360
|
+
this.log('Next steps:');
|
|
361
|
+
this.log(` 1. Review the migrated config files in ${dir}`);
|
|
362
|
+
this.log(' 2. Read the migration report — it lists every config that was skipped,');
|
|
363
|
+
this.log(' coerced, or needs a manual follow-up:');
|
|
364
|
+
this.log(` ${migrationReportPath(dir)}`);
|
|
365
|
+
this.log(' (it lives in the hidden .qf/ directory — use `ls -a` to see it)');
|
|
366
|
+
this.log(' 3. Publish to a Quonfig workspace once the migration looks right:');
|
|
367
|
+
this.log(` qfg push --dir ${dir} --workspace <org-slug>/<workspace-slug>`);
|
|
368
|
+
this.log(' (run `qfg login` first if you have not). You can also re-run this command');
|
|
369
|
+
this.log(' with `--push --workspace <org-slug>/<workspace-slug>` to migrate and push');
|
|
370
|
+
this.log(' in one step.');
|
|
371
|
+
this.log('');
|
|
372
|
+
this.log('Re-running `qfg migrate` later picks up only the changes made since this run.');
|
|
373
|
+
}
|
|
312
374
|
async resolvePushWorkspace(slugOrId) {
|
|
313
375
|
const authConfig = await loadAuthConfig();
|
|
314
376
|
if (!authConfig)
|
|
@@ -368,7 +430,7 @@ function warnAboutDroppedOverrides(cmd, dropped) {
|
|
|
368
430
|
cmd.warn(` - ${flagPath} (${perFlag[flagPath]})`);
|
|
369
431
|
}
|
|
370
432
|
}
|
|
371
|
-
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.`);
|
|
433
|
+
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 .qf/MIGRATION_REPORT.md.`);
|
|
372
434
|
}
|
|
373
435
|
function warnAboutDuplicateResolutions(cmd, resolved) {
|
|
374
436
|
if (!resolved || resolved.total === 0)
|
|
@@ -378,7 +440,7 @@ function warnAboutDuplicateResolutions(cmd, resolved) {
|
|
|
378
440
|
for (const entry of sorted) {
|
|
379
441
|
cmd.warn(` ${entry.key} (${entry.collisionTypes.join(', ')}): kept ${entry.kept}, deleted ${entry.deleted.join(', ')}`);
|
|
380
442
|
}
|
|
381
|
-
cmd.warn('Full detail is also written to MIGRATION_REPORT.md.');
|
|
443
|
+
cmd.warn('Full detail is also written to .qf/MIGRATION_REPORT.md.');
|
|
382
444
|
}
|
|
383
445
|
function warnAboutCoercedSentinels(cmd, coerced) {
|
|
384
446
|
if (!coerced || coerced.total === 0)
|
|
@@ -393,7 +455,7 @@ function warnAboutCoercedSentinels(cmd, coerced) {
|
|
|
393
455
|
cmd.warn(` - ${flagPath} (${perFlag[flagPath]})`);
|
|
394
456
|
}
|
|
395
457
|
}
|
|
396
|
-
cmd.warn(`If you want a real default for these rules, set one in the source system and re-run the migration. Full detail is also written to MIGRATION_REPORT.md.`);
|
|
458
|
+
cmd.warn(`If you want a real default for these rules, set one in the source system and re-run the migration. Full detail is also written to .qf/MIGRATION_REPORT.md.`);
|
|
397
459
|
}
|
|
398
460
|
function warnAboutConversionNotes(cmd, notes) {
|
|
399
461
|
if (!notes || notes.length === 0)
|
|
@@ -402,12 +464,12 @@ function warnAboutConversionNotes(cmd, notes) {
|
|
|
402
464
|
if (rebucketed.length > 0) {
|
|
403
465
|
cmd.warn(`${rebucketed.length} flag(s)/segment(s) use a percentage rollout — LaunchDarkly and Quonfig bucket users ` +
|
|
404
466
|
`differently, so individual user assignments WILL change after migration (the rollout percentage is preserved). ` +
|
|
405
|
-
`See the "Users will be re-bucketed" section of MIGRATION_REPORT.md.`);
|
|
467
|
+
`See the "Users will be re-bucketed" section of .qf/MIGRATION_REPORT.md.`);
|
|
406
468
|
}
|
|
407
469
|
const other = notes.filter((n) => n.category !== 'rebucketed-rollout' && n.category !== 'skipped-config');
|
|
408
470
|
if (other.length > 0) {
|
|
409
471
|
cmd.warn(`${other.length} source concept(s) could not be converted exactly (dropped prerequisites, lossy individual ` +
|
|
410
|
-
`targets, etc.). Nothing was silently dropped — see the "Conversion notes" section of MIGRATION_REPORT.md.`);
|
|
472
|
+
`targets, etc.). Nothing was silently dropped — see the "Conversion notes" section of .qf/MIGRATION_REPORT.md.`);
|
|
411
473
|
}
|
|
412
474
|
}
|
|
413
475
|
function warnAboutSkippedConfigs(cmd, skipped) {
|
|
@@ -418,7 +480,7 @@ function warnAboutSkippedConfigs(cmd, skipped) {
|
|
|
418
480
|
for (const entry of sorted) {
|
|
419
481
|
cmd.warn(` ${entry.key}: ${entry.reason}`);
|
|
420
482
|
}
|
|
421
|
-
cmd.warn('Full detail is also written to MIGRATION_REPORT.md.');
|
|
483
|
+
cmd.warn('Full detail is also written to .qf/MIGRATION_REPORT.md.');
|
|
422
484
|
}
|
|
423
485
|
function resolveTargetDir(dirFlag, cwd) {
|
|
424
486
|
if (dirFlag)
|
package/dist/commands/push.js
CHANGED
|
@@ -238,9 +238,15 @@ export function buildRealDeps(cmd, orgSlug) {
|
|
|
238
238
|
}
|
|
239
239
|
},
|
|
240
240
|
async getOriginMainSha(dir) {
|
|
241
|
-
//
|
|
242
|
-
//
|
|
243
|
-
// and
|
|
241
|
+
// Bare path: `diffHeadVsOrigin` (always called first by runPush)
|
|
242
|
+
// populated the probe clone. Its HEAD is our view of the workspace
|
|
243
|
+
// tip — and is exactly what the diff was computed against — so it is
|
|
244
|
+
// the correct `expectedSha` for the server-side optimistic lock
|
|
245
|
+
// (qfg-gj3i bare-path follow-up, qfg-nhcb). Without it the bare-path
|
|
246
|
+
// push omits expectedSha and the server rejects it with a 426.
|
|
247
|
+
if (barePathProbe)
|
|
248
|
+
return barePathProbe.remoteHeadSha;
|
|
249
|
+
// Clone path: the local origin/main SHA after fetch.
|
|
244
250
|
if (!(await isGitRepo(dir)))
|
|
245
251
|
return;
|
|
246
252
|
return getOriginMainSha(dir);
|
|
@@ -136,7 +136,15 @@ export default class WorkspaceCreate extends BaseCommand {
|
|
|
136
136
|
this.log(` Envs: ${created.environments.map((e) => e.name).join(', ')}`);
|
|
137
137
|
this.log('');
|
|
138
138
|
this.log(`To use this workspace locally:`);
|
|
139
|
-
|
|
139
|
+
// `qfg workspace switch` requires the `<org-slug>/<workspace-slug>` pin
|
|
140
|
+
// form — bare slugs are rejected. Fall back to the interactive picker if
|
|
141
|
+
// the server did not return an org slug.
|
|
142
|
+
if (created.organizationSlug) {
|
|
143
|
+
this.log(` qfg workspace switch ${created.organizationSlug}/${created.workspaceSlug}`);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
this.log(` qfg workspace switch`);
|
|
147
|
+
}
|
|
140
148
|
return {
|
|
141
149
|
workspaceId: created.workspaceId,
|
|
142
150
|
workspaceSlug: created.workspaceSlug,
|
|
@@ -94,7 +94,7 @@ export interface BuildAuditFinalCommitOptions {
|
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
96
|
* qfg-wbkj: build the final state-file commit for audit-log mode. Writes
|
|
97
|
-
* `.qf/import-state.json` +
|
|
97
|
+
* `.qf/import-state.json` + `.qf/MIGRATION_REPORT.md` with cumulative counts read
|
|
98
98
|
* from disk, and surfaces the source's accumulated dropped/skipped/coerced
|
|
99
99
|
* summaries via `onAccumulatorUpdate`. The `preWrite` / `postWrite` hooks let
|
|
100
100
|
* push-to-cloud merge environments and run server-side-equivalent validation
|
|
@@ -301,7 +301,7 @@ export const buildAuditPerChangeCommits = (changes, source) => changes.map((chan
|
|
|
301
301
|
});
|
|
302
302
|
/**
|
|
303
303
|
* qfg-wbkj: build the final state-file commit for audit-log mode. Writes
|
|
304
|
-
* `.qf/import-state.json` +
|
|
304
|
+
* `.qf/import-state.json` + `.qf/MIGRATION_REPORT.md` with cumulative counts read
|
|
305
305
|
* from disk, and surfaces the source's accumulated dropped/skipped/coerced
|
|
306
306
|
* summaries via `onAccumulatorUpdate`. The `preWrite` / `postWrite` hooks let
|
|
307
307
|
* push-to-cloud merge environments and run server-side-equivalent validation
|
|
@@ -85,5 +85,16 @@ export interface MigrationReportData {
|
|
|
85
85
|
*/
|
|
86
86
|
export declare const deriveFollowUpFromConversionNotes: (base: FollowUpChecklist, notes: ConversionNote[] | undefined) => FollowUpChecklist;
|
|
87
87
|
export declare const buildMigrationReport: (data: MigrationReportData) => string;
|
|
88
|
+
/**
|
|
89
|
+
* Path of the migration report. It lives inside the `.qf/` bookkeeping
|
|
90
|
+
* directory (next to `import-state.json`), NOT the workspace root (qfg-a631).
|
|
91
|
+
*
|
|
92
|
+
* `qfg push` mirrors every non-dotfile on disk and the server's
|
|
93
|
+
* config-path-allowlist only permits `configs/`, `feature-flags/`, etc. plus
|
|
94
|
+
* `quonfig.json`/`README.md`. A root-level `MIGRATION_REPORT.md` got swept
|
|
95
|
+
* into the push and rejected ("Path not allowed by push allow-list"). Keeping
|
|
96
|
+
* it under the `.qf/` dotdir means the existing dotfile/dotdir skip in
|
|
97
|
+
* `bare-path-diff.ts` excludes it — no server change needed.
|
|
98
|
+
*/
|
|
88
99
|
export declare const migrationReportPath: (outputDir: string) => string;
|
|
89
100
|
export declare const writeMigrationReport: (outputDir: string, data: MigrationReportData) => string;
|
|
@@ -565,10 +565,21 @@ export const buildMigrationReport = (data) => {
|
|
|
565
565
|
}
|
|
566
566
|
return sections.join('\n\n') + '\n';
|
|
567
567
|
};
|
|
568
|
-
|
|
568
|
+
/**
|
|
569
|
+
* Path of the migration report. It lives inside the `.qf/` bookkeeping
|
|
570
|
+
* directory (next to `import-state.json`), NOT the workspace root (qfg-a631).
|
|
571
|
+
*
|
|
572
|
+
* `qfg push` mirrors every non-dotfile on disk and the server's
|
|
573
|
+
* config-path-allowlist only permits `configs/`, `feature-flags/`, etc. plus
|
|
574
|
+
* `quonfig.json`/`README.md`. A root-level `MIGRATION_REPORT.md` got swept
|
|
575
|
+
* into the push and rejected ("Path not allowed by push allow-list"). Keeping
|
|
576
|
+
* it under the `.qf/` dotdir means the existing dotfile/dotdir skip in
|
|
577
|
+
* `bare-path-diff.ts` excludes it — no server change needed.
|
|
578
|
+
*/
|
|
579
|
+
export const migrationReportPath = (outputDir) => path.join(outputDir, '.qf', 'MIGRATION_REPORT.md');
|
|
569
580
|
export const writeMigrationReport = (outputDir, data) => {
|
|
570
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
571
581
|
const filePath = migrationReportPath(outputDir);
|
|
582
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
572
583
|
fs.writeFileSync(filePath, buildMigrationReport(data), 'utf8');
|
|
573
584
|
return filePath;
|
|
574
585
|
};
|
|
@@ -28,8 +28,8 @@ export interface PushMigrationToCloudOptions {
|
|
|
28
28
|
* each commit authored as the original Launch user with their `changedAt` as
|
|
29
29
|
* GIT_AUTHOR_DATE and `summary` as the message. One final state-file commit
|
|
30
30
|
* (migrator identity, now) lands on top carrying .qf/import-state.json +
|
|
31
|
-
* MIGRATION_REPORT.md so the audit-log commits aren't polluted by
|
|
32
|
-
* churn. Requires that `source.getCommitMeta` be defined.
|
|
31
|
+
* .qf/MIGRATION_REPORT.md so the audit-log commits aren't polluted by
|
|
32
|
+
* bookkeeping churn. Requires that `source.getCommitMeta` be defined.
|
|
33
33
|
*/
|
|
34
34
|
fullHistory?: boolean;
|
|
35
35
|
importState: ImportState;
|
package/dist/migrate/source.d.ts
CHANGED
|
@@ -87,7 +87,13 @@ export interface CommitMeta {
|
|
|
87
87
|
message: string;
|
|
88
88
|
}
|
|
89
89
|
export interface MigrationSource {
|
|
90
|
-
|
|
90
|
+
/**
|
|
91
|
+
* Yields the source's change history oldest→newest. `onProgress`, when
|
|
92
|
+
* provided, is called with the running fetched-change count as pagination
|
|
93
|
+
* proceeds, so the CLI can show progress during an otherwise-silent fetch.
|
|
94
|
+
* Sources that fetch in a single request may ignore it.
|
|
95
|
+
*/
|
|
96
|
+
fetchChanges(sinceEpochMs: null | number, onProgress?: (fetched: number) => void): AsyncIterable<LegacyChange>;
|
|
91
97
|
/**
|
|
92
98
|
* Optional post-translate accumulator. Returns any rule values that translate()
|
|
93
99
|
* coerced from a sentinel like Launch's empty-string "no value set yet" to the
|
|
@@ -4,4 +4,4 @@ export declare function selectLaunchBaseUrl(staging: boolean): string;
|
|
|
4
4
|
export declare function applyLaunchBaseUrl(staging: boolean): void;
|
|
5
5
|
export declare function fetchEnvironments(apiKey: string): Promise<Record<string, string>>;
|
|
6
6
|
export declare function fetchChangeHistoryPage(apiKey: string, cursor?: string): Promise<LaunchChangeHistoryResponse>;
|
|
7
|
-
export declare function fetchAllChangeHistory(apiKey: string, sinceEpochMs?: number): Promise<LaunchChangeEntry[]>;
|
|
7
|
+
export declare function fetchAllChangeHistory(apiKey: string, sinceEpochMs?: number, onProgress?: (fetched: number) => void): Promise<LaunchChangeEntry[]>;
|
|
@@ -49,7 +49,7 @@ export async function fetchChangeHistoryPage(apiKey, cursor) {
|
|
|
49
49
|
}
|
|
50
50
|
return (await apiFetch(`/api/v1/change-history?${params}`, apiKey));
|
|
51
51
|
}
|
|
52
|
-
export async function fetchAllChangeHistory(apiKey, sinceEpochMs) {
|
|
52
|
+
export async function fetchAllChangeHistory(apiKey, sinceEpochMs, onProgress) {
|
|
53
53
|
const collected = [];
|
|
54
54
|
const seenCursors = new Set();
|
|
55
55
|
let cursor;
|
|
@@ -66,6 +66,9 @@ export async function fetchAllChangeHistory(apiKey, sinceEpochMs) {
|
|
|
66
66
|
}
|
|
67
67
|
collected.push(change);
|
|
68
68
|
}
|
|
69
|
+
// Report cumulative progress after each page — pagination is silent and
|
|
70
|
+
// can run for many pages (50 changes/page) on a large account.
|
|
71
|
+
onProgress?.(collected.length);
|
|
69
72
|
if (done || !page.cursor || seenCursors.has(page.cursor))
|
|
70
73
|
break;
|
|
71
74
|
seenCursors.add(page.cursor);
|
|
@@ -19,10 +19,10 @@ function requireApiKey(operation) {
|
|
|
19
19
|
throw new MissingAuthError(operation);
|
|
20
20
|
return state.apiKey;
|
|
21
21
|
}
|
|
22
|
-
async function* fetchChangesImpl(sinceEpochMs) {
|
|
22
|
+
async function* fetchChangesImpl(sinceEpochMs, onProgress) {
|
|
23
23
|
const apiKey = requireApiKey('fetchChanges');
|
|
24
24
|
const since = sinceEpochMs === null ? undefined : sinceEpochMs;
|
|
25
|
-
const changes = await fetchAllChangeHistory(apiKey, since);
|
|
25
|
+
const changes = await fetchAllChangeHistory(apiKey, since, onProgress);
|
|
26
26
|
for (const change of changes) {
|
|
27
27
|
yield {
|
|
28
28
|
changedAt: change.changedAt,
|
|
@@ -147,8 +147,8 @@ async function validateAuthImpl(apiKey) {
|
|
|
147
147
|
state.coercedSentinels = new Map();
|
|
148
148
|
}
|
|
149
149
|
export const launchSource = {
|
|
150
|
-
fetchChanges(sinceEpochMs) {
|
|
151
|
-
return fetchChangesImpl(sinceEpochMs);
|
|
150
|
+
fetchChanges(sinceEpochMs, onProgress) {
|
|
151
|
+
return fetchChangesImpl(sinceEpochMs, onProgress);
|
|
152
152
|
},
|
|
153
153
|
getCoercedSentinels: getCoercedSentinelsImpl,
|
|
154
154
|
getCommitMeta: getCommitMetaImpl,
|
|
@@ -27,6 +27,13 @@
|
|
|
27
27
|
import { FileDelta } from './diff-summary.js';
|
|
28
28
|
export interface BarePathDiffResult {
|
|
29
29
|
deltas: FileDelta[];
|
|
30
|
+
/**
|
|
31
|
+
* The probe clone's HEAD sha — the workspace's current tip on the cloud
|
|
32
|
+
* repo. Threaded into `configs.push` as `expectedSha` so the server-side
|
|
33
|
+
* optimistic lock (qfg-gj3i) accepts bare-path pushes instead of rejecting
|
|
34
|
+
* them with a forced-upgrade 426.
|
|
35
|
+
*/
|
|
36
|
+
remoteHeadSha: string;
|
|
30
37
|
/** Tmp dir holding the probe clone. Caller must rm -rf in a finally block. */
|
|
31
38
|
scratchDir: string;
|
|
32
39
|
/** Total file count in the scratch clone (under the same prefix rules as `deltas`). */
|
|
@@ -52,6 +52,22 @@ export async function computeBarePathDiff(localDir, remoteUrl) {
|
|
|
52
52
|
}
|
|
53
53
|
throw error;
|
|
54
54
|
}
|
|
55
|
+
// The probe clone's HEAD is the workspace tip — the optimistic-lock value
|
|
56
|
+
// `configs.push` needs as `expectedSha` (qfg-gj3i bare-path follow-up).
|
|
57
|
+
let remoteHeadSha;
|
|
58
|
+
try {
|
|
59
|
+
const { stdout } = await runGit(['-C', scratchDir, 'rev-parse', 'HEAD']);
|
|
60
|
+
remoteHeadSha = stdout.trim();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
try {
|
|
64
|
+
fs.rmSync(scratchDir, { force: true, recursive: true });
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
/* ignore */
|
|
68
|
+
}
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
55
71
|
const localFiles = collectFiles(localDir);
|
|
56
72
|
const remoteFiles = collectFiles(scratchDir);
|
|
57
73
|
const deltas = [];
|
|
@@ -69,7 +85,7 @@ export async function computeBarePathDiff(localDir, remoteUrl) {
|
|
|
69
85
|
deltas.push({ kind: 'deleted', path: rel, beforeJson: remote.content });
|
|
70
86
|
}
|
|
71
87
|
}
|
|
72
|
-
return { deltas, scratchDir, totalRemoteFiles: remoteFiles.size };
|
|
88
|
+
return { deltas, remoteHeadSha, scratchDir, totalRemoteFiles: remoteFiles.size };
|
|
73
89
|
}
|
|
74
90
|
/**
|
|
75
91
|
* Walk `root` and return a map of relative-path -> {content, hash} for every
|
package/dist/push/run-push.d.ts
CHANGED
|
@@ -87,13 +87,11 @@ export interface GitOps {
|
|
|
87
87
|
/** Pack-push — local HEAD SHA. Forwarded as `newSha` on the wire. */
|
|
88
88
|
getHeadSha(dir: string): Promise<string>;
|
|
89
89
|
/**
|
|
90
|
-
* Returns the
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
* server-side optimistic lock (qfg-gj3i) can reject pushes whose
|
|
96
|
-
* origin moved between fetch and push.
|
|
90
|
+
* Returns the workspace-HEAD sha that the diff was computed against —
|
|
91
|
+
* threaded into `configs.push` as `expectedSha` for the server-side
|
|
92
|
+
* optimistic lock (qfg-gj3i). Clone path: the local `origin/main` SHA
|
|
93
|
+
* after fetch. Bare path: the probe clone's HEAD. Undefined only when
|
|
94
|
+
* neither is available; the server rejects a missing value with a 426.
|
|
97
95
|
*/
|
|
98
96
|
getOriginMainSha(dir: string): Promise<string | undefined>;
|
|
99
97
|
/**
|
package/dist/push/run-push.js
CHANGED
|
@@ -354,10 +354,10 @@ export async function runPush(input, deps) {
|
|
|
354
354
|
...(d.beforeJson === undefined ? {} : { beforeJson: d.beforeJson }),
|
|
355
355
|
...(d.afterJson === undefined ? {} : { afterJson: d.afterJson }),
|
|
356
356
|
}));
|
|
357
|
-
// qfg-gj3i:
|
|
358
|
-
// optimistic lock
|
|
359
|
-
//
|
|
360
|
-
//
|
|
357
|
+
// qfg-gj3i: expectedSha is the workspace HEAD the server checks its
|
|
358
|
+
// optimistic lock against. For the bare path it is the probe clone's
|
|
359
|
+
// HEAD (resolved inside getOriginMainSha, qfg-nhcb). It must be present
|
|
360
|
+
// — the server rejects a missing expectedSha with a forced-upgrade 426.
|
|
361
361
|
const expectedSha = await deps.gitOps.getOriginMainSha(input.dir);
|
|
362
362
|
log('Sending push to Quonfig cloud...');
|
|
363
363
|
const result = await deps.pushToServer({
|
|
@@ -87,7 +87,7 @@ export async function handleEvalContext(req, res, ctxToken, deps) {
|
|
|
87
87
|
const result = {
|
|
88
88
|
value: {
|
|
89
89
|
type: match.value.type,
|
|
90
|
-
value:
|
|
90
|
+
value: match.value.value,
|
|
91
91
|
},
|
|
92
92
|
configId: cfg.id,
|
|
93
93
|
configType: cfg.type,
|
|
@@ -113,35 +113,6 @@ export async function handleEvalContext(req, res, ctxToken, deps) {
|
|
|
113
113
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
114
114
|
res.end(JSON.stringify(envelope));
|
|
115
115
|
}
|
|
116
|
-
/**
|
|
117
|
-
* qfg-38sf.7: coerce `int`/`double` values to JS numbers so the envelope emits
|
|
118
|
-
* JSON numbers, not JSON strings.
|
|
119
|
-
*
|
|
120
|
-
* Config files (and integration-test-data) store numeric values as JSON
|
|
121
|
-
* strings — e.g. `{"type": "int", "value": "5"}`. api-delivery's
|
|
122
|
-
* `Value.UnmarshalJSON` (api-delivery/internal/config/types.go:182-215) coerces
|
|
123
|
-
* `int` via `strconv.ParseInt` and `double` via `strconv.ParseFloat` at config
|
|
124
|
-
* *load* time, so its EvalEnvelope emits JSON numbers. sdk-node's datadir
|
|
125
|
-
* loader does no such coercion and passes the value through verbatim, so
|
|
126
|
-
* `match.value.value` arrives here as a string. We coerce on output to match
|
|
127
|
-
* api-delivery — a browser SDK consumer doing arithmetic on an integer flag
|
|
128
|
-
* would otherwise get string-concatenation behavior.
|
|
129
|
-
*
|
|
130
|
-
* Discriminate on `valueType` — the value's *own* type, the same field
|
|
131
|
-
* api-delivery switches on (`raw.Type`). This is correct even when the matched
|
|
132
|
-
* value's type differs from the config's declared `valueType` (e.g. a
|
|
133
|
-
* weighted-values config resolving to an `int` arm). ONLY `int` and `double`
|
|
134
|
-
* are coerced; `string`, `bool`, `string_list`, `json`, `provided`, etc. pass
|
|
135
|
-
* through untouched. Integration-test-data's largest numeric value is ~2.5e12,
|
|
136
|
-
* far below Number.MAX_SAFE_INTEGER, so `Number(...)` is lossless here.
|
|
137
|
-
*/
|
|
138
|
-
function coerceNumeric(valueType, value) {
|
|
139
|
-
if ((valueType === 'int' || valueType === 'double') && typeof value === 'string') {
|
|
140
|
-
const n = Number(value);
|
|
141
|
-
return Number.isNaN(n) ? value : n;
|
|
142
|
-
}
|
|
143
|
-
return value;
|
|
144
|
-
}
|
|
145
116
|
function resolutionReason(cfg, _match) {
|
|
146
117
|
if (cfg.valueType === 'weighted_values')
|
|
147
118
|
return 'SPLIT';
|
|
@@ -4,7 +4,13 @@ export interface FrictionEntry {
|
|
|
4
4
|
exitCode: number;
|
|
5
5
|
ts: string;
|
|
6
6
|
}
|
|
7
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Default friction-log location: a `friction.log` file inside the shared
|
|
9
|
+
* Quonfig config home (`~/.quonfig`, the same dir as tokens/profile config).
|
|
10
|
+
* Honors `QUONFIG_CONFIG_HOME` via `getQuonfigConfigHome()` so tests and
|
|
11
|
+
* isolated environments redirect it along with everything else.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getDefaultFrictionLogPath(configHome?: string): string;
|
|
8
14
|
export declare function getFrictionLogPath(envValue?: string | undefined, cwd?: string): null | string;
|
|
9
15
|
export declare function buildFrictionEntry(opts: {
|
|
10
16
|
argv?: string[];
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
-
import os from 'node:os';
|
|
3
2
|
import path from 'node:path';
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { getQuonfigConfigHome } from './quonfig-home.js';
|
|
4
|
+
/**
|
|
5
|
+
* Default friction-log location: a `friction.log` file inside the shared
|
|
6
|
+
* Quonfig config home (`~/.quonfig`, the same dir as tokens/profile config).
|
|
7
|
+
* Honors `QUONFIG_CONFIG_HOME` via `getQuonfigConfigHome()` so tests and
|
|
8
|
+
* isolated environments redirect it along with everything else.
|
|
9
|
+
*/
|
|
10
|
+
export function getDefaultFrictionLogPath(configHome = getQuonfigConfigHome()) {
|
|
11
|
+
return path.join(configHome, 'friction.log');
|
|
6
12
|
}
|
|
7
13
|
export function getFrictionLogPath(envValue, cwd = process.cwd()) {
|
|
8
14
|
if (envValue === undefined)
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const _default: "0.0.
|
|
1
|
+
declare const _default: "0.0.55";
|
|
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.55';
|
package/oclif.manifest.json
CHANGED
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.55",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/quonfig/cli/issues"
|
|
7
7
|
},
|
|
@@ -21,10 +21,10 @@
|
|
|
21
21
|
"@oclif/core": "^3.18.2",
|
|
22
22
|
"@oclif/plugin-help": "^6",
|
|
23
23
|
"@oclif/plugin-plugins": "^4",
|
|
24
|
-
"@quonfig/node": "^0.0.
|
|
24
|
+
"@quonfig/node": "^0.0.32",
|
|
25
25
|
"acorn": "^8.10.0",
|
|
26
26
|
"acorn-walk": "^8.3.5",
|
|
27
|
-
"chalk": "^5.
|
|
27
|
+
"chalk": "^5.6.2",
|
|
28
28
|
"common-tags": "^1.8.2",
|
|
29
29
|
"fuzzy": "^0.1.3",
|
|
30
30
|
"inquirer-autocomplete-standalone": "^0.8.1",
|
|
@@ -54,11 +54,11 @@
|
|
|
54
54
|
"eslint-plugin-only-error": "^1.0.2",
|
|
55
55
|
"mocha": "^11.7.5",
|
|
56
56
|
"msw": "^2.14.6",
|
|
57
|
-
"oclif": "^4.
|
|
57
|
+
"oclif": "^4.23.7",
|
|
58
58
|
"prettier": "^3.8.3",
|
|
59
59
|
"shx": "^0.3.4",
|
|
60
60
|
"ts-node": "^10.9.1",
|
|
61
|
-
"tsx": "^4.
|
|
61
|
+
"tsx": "^4.22.0"
|
|
62
62
|
},
|
|
63
63
|
"engines": {
|
|
64
64
|
"node": ">=20.9.0"
|