@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
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reader/writer for the workspace's `quonfig.json` file.
|
|
3
|
+
*
|
|
4
|
+
* Today the file is `{ environments: string[] }`. We are adding an optional
|
|
5
|
+
* `workspace: "<slug>"` pin (Guard 1 in `project/plans/cli-git-sync.md`)
|
|
6
|
+
* so commands like `qfg push` can confirm that the local dir belongs to the
|
|
7
|
+
* cloud workspace they think it does.
|
|
8
|
+
*
|
|
9
|
+
* The reader is permissive: missing file or missing field returns `undefined`
|
|
10
|
+
* (no throw). It only throws when the file exists but cannot be parsed.
|
|
11
|
+
*
|
|
12
|
+
* The writer preserves all unknown fields, sticks to 2-space indent, and
|
|
13
|
+
* always ends with a trailing newline — matching how `qfg init` and the
|
|
14
|
+
* migrate flow create the file today.
|
|
15
|
+
*/
|
|
16
|
+
import * as fs from 'node:fs';
|
|
17
|
+
import * as path from 'node:path';
|
|
18
|
+
const QUONFIG_JSON = 'quonfig.json';
|
|
19
|
+
async function readQuonfigJson(dir) {
|
|
20
|
+
const filePath = path.join(dir, QUONFIG_JSON);
|
|
21
|
+
let raw;
|
|
22
|
+
try {
|
|
23
|
+
raw = await fs.promises.readFile(filePath, 'utf8');
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
if (error.code === 'ENOENT')
|
|
27
|
+
return undefined;
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
// JSON.parse will throw on malformed JSON — that's the documented behavior.
|
|
31
|
+
const parsed = JSON.parse(raw);
|
|
32
|
+
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
33
|
+
throw new TypeError(`${QUONFIG_JSON} must be a JSON object`);
|
|
34
|
+
}
|
|
35
|
+
return parsed;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read the workspace slug pin from `<dir>/quonfig.json`.
|
|
39
|
+
*
|
|
40
|
+
* Returns `undefined` when the file is missing OR when the `workspace`
|
|
41
|
+
* field is absent OR when the field is present but not a string.
|
|
42
|
+
* Throws only when the file exists but contains malformed JSON, since
|
|
43
|
+
* that is a real bug a caller should surface.
|
|
44
|
+
*/
|
|
45
|
+
export async function readWorkspaceSlug(dir) {
|
|
46
|
+
const parsed = await readQuonfigJson(dir);
|
|
47
|
+
if (!parsed)
|
|
48
|
+
return undefined;
|
|
49
|
+
const slug = parsed.workspace;
|
|
50
|
+
return typeof slug === 'string' ? slug : undefined;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Write the workspace slug pin into `<dir>/quonfig.json`.
|
|
54
|
+
*
|
|
55
|
+
* If the file exists, all other fields are preserved and only `workspace`
|
|
56
|
+
* is set/overwritten. If the file is missing, a minimal `{workspace: slug}`
|
|
57
|
+
* file is created. Format is 2-space indent + trailing newline to match
|
|
58
|
+
* the file shape produced by `qfg init` and `qfg migrate`.
|
|
59
|
+
*/
|
|
60
|
+
export async function writeWorkspaceSlug(dir, slug) {
|
|
61
|
+
const existing = (await readQuonfigJson(dir)) ?? {};
|
|
62
|
+
const next = { ...existing, workspace: slug };
|
|
63
|
+
const filePath = path.join(dir, QUONFIG_JSON);
|
|
64
|
+
await fs.promises.writeFile(filePath, JSON.stringify(next, null, 2) + '\n', 'utf8');
|
|
65
|
+
}
|
package/dist/verify/validate.js
CHANGED
|
@@ -279,7 +279,9 @@ export function validateWorkspace(workspaceDir) {
|
|
|
279
279
|
quonfigParsed = null;
|
|
280
280
|
issues.push({ file: 'quonfig.json', message: `Invalid JSON: ${error.message}`, severity: 'error' });
|
|
281
281
|
}
|
|
282
|
-
const quonfigResult = z
|
|
282
|
+
const quonfigResult = z
|
|
283
|
+
.object({ environments: z.array(z.string()), workspace: z.string().optional() })
|
|
284
|
+
.safeParse(quonfigParsed);
|
|
283
285
|
if (quonfigResult.success) {
|
|
284
286
|
for (const envId of quonfigResult.data.environments) {
|
|
285
287
|
declaredEnvIds.add(envId);
|
|
@@ -606,7 +608,9 @@ export function validateFileMap(files) {
|
|
|
606
608
|
issues.push({ file: 'quonfig.json', message: `Invalid JSON: ${error.message}`, severity: 'error' });
|
|
607
609
|
}
|
|
608
610
|
if (quonfigParsed !== null) {
|
|
609
|
-
const quonfigResult = z
|
|
611
|
+
const quonfigResult = z
|
|
612
|
+
.object({ environments: z.array(z.string()), workspace: z.string().optional() })
|
|
613
|
+
.safeParse(quonfigParsed);
|
|
610
614
|
if (quonfigResult.success) {
|
|
611
615
|
for (const envId of quonfigResult.data.environments) {
|
|
612
616
|
declaredEnvIds.add(envId);
|
package/oclif.manifest.json
CHANGED
|
@@ -567,6 +567,14 @@
|
|
|
567
567
|
"name": "samples",
|
|
568
568
|
"allowNo": true,
|
|
569
569
|
"type": "boolean"
|
|
570
|
+
},
|
|
571
|
+
"workspace": {
|
|
572
|
+
"description": "Workspace slug to pin in quonfig.json (Guard 1). If omitted, no pin is written.",
|
|
573
|
+
"name": "workspace",
|
|
574
|
+
"required": false,
|
|
575
|
+
"hasDynamicHelp": false,
|
|
576
|
+
"multiple": false,
|
|
577
|
+
"type": "option"
|
|
570
578
|
}
|
|
571
579
|
},
|
|
572
580
|
"hasDynamicHelp": false,
|
|
@@ -1287,6 +1295,101 @@
|
|
|
1287
1295
|
"pull.js"
|
|
1288
1296
|
]
|
|
1289
1297
|
},
|
|
1298
|
+
"push": {
|
|
1299
|
+
"aliases": [],
|
|
1300
|
+
"args": {},
|
|
1301
|
+
"description": "Push local config changes up to your workspace on Quonfig cloud.\n\nEnforces three guards before touching the remote:\n 1. The dir's quonfig.json \"workspace\" pin must match the backend (if set).\n 2. The dir's git origin must match the backend repo URL (if set).\n 3. A diff summary is shown; destructive changes (10+ deletes, >=25% of\n files, or an unpinned dir) require typing the workspace slug to confirm.\n\n qfg push # uses QUONFIG_DIR + profile workspace\n qfg push --dir ./our-config\n qfg push --dir ./our-config --workspace acme-prod\n qfg push --dir ./our-config --yes # skip normal Y/N (never skips typed-slug)\n qfg push --dir ./our-config --skip-validate",
|
|
1302
|
+
"examples": [
|
|
1303
|
+
"<%= config.bin %> push --dir ./our-config",
|
|
1304
|
+
"<%= config.bin %> push --workspace acme-prod --dir ./our-config",
|
|
1305
|
+
"<%= config.bin %> push --dir ./our-config --yes"
|
|
1306
|
+
],
|
|
1307
|
+
"flags": {
|
|
1308
|
+
"json": {
|
|
1309
|
+
"description": "Format output as json.",
|
|
1310
|
+
"helpGroup": "GLOBAL",
|
|
1311
|
+
"name": "json",
|
|
1312
|
+
"allowNo": false,
|
|
1313
|
+
"type": "boolean"
|
|
1314
|
+
},
|
|
1315
|
+
"interactive": {
|
|
1316
|
+
"description": "Force interactive mode",
|
|
1317
|
+
"helpGroup": "GLOBAL",
|
|
1318
|
+
"name": "interactive",
|
|
1319
|
+
"allowNo": true,
|
|
1320
|
+
"type": "boolean"
|
|
1321
|
+
},
|
|
1322
|
+
"no-color": {
|
|
1323
|
+
"description": "Do not colorize output",
|
|
1324
|
+
"env": "NO_COLOR",
|
|
1325
|
+
"helpGroup": "GLOBAL",
|
|
1326
|
+
"name": "no-color",
|
|
1327
|
+
"allowNo": false,
|
|
1328
|
+
"type": "boolean"
|
|
1329
|
+
},
|
|
1330
|
+
"verbose": {
|
|
1331
|
+
"description": "Verbose output",
|
|
1332
|
+
"helpGroup": "GLOBAL",
|
|
1333
|
+
"name": "verbose",
|
|
1334
|
+
"allowNo": false,
|
|
1335
|
+
"type": "boolean"
|
|
1336
|
+
},
|
|
1337
|
+
"dir": {
|
|
1338
|
+
"description": "Local directory to push (defaults to QUONFIG_DIR env var)",
|
|
1339
|
+
"name": "dir",
|
|
1340
|
+
"hasDynamicHelp": false,
|
|
1341
|
+
"multiple": false,
|
|
1342
|
+
"type": "option"
|
|
1343
|
+
},
|
|
1344
|
+
"workspace": {
|
|
1345
|
+
"description": "Workspace slug or UUID (defaults to active profile)",
|
|
1346
|
+
"name": "workspace",
|
|
1347
|
+
"hasDynamicHelp": false,
|
|
1348
|
+
"multiple": false,
|
|
1349
|
+
"type": "option"
|
|
1350
|
+
},
|
|
1351
|
+
"yes": {
|
|
1352
|
+
"description": "Skip the standard Y/N confirm. Never skips typed-slug prompts.",
|
|
1353
|
+
"name": "yes",
|
|
1354
|
+
"allowNo": false,
|
|
1355
|
+
"type": "boolean"
|
|
1356
|
+
},
|
|
1357
|
+
"skip-validate": {
|
|
1358
|
+
"description": "Skip `qfg validate` preflight",
|
|
1359
|
+
"name": "skip-validate",
|
|
1360
|
+
"allowNo": false,
|
|
1361
|
+
"type": "boolean"
|
|
1362
|
+
},
|
|
1363
|
+
"no-pin-write": {
|
|
1364
|
+
"description": "Do not offer to write workspace slug into quonfig.json on success",
|
|
1365
|
+
"name": "no-pin-write",
|
|
1366
|
+
"allowNo": false,
|
|
1367
|
+
"type": "boolean"
|
|
1368
|
+
},
|
|
1369
|
+
"message": {
|
|
1370
|
+
"char": "m",
|
|
1371
|
+
"description": "Commit message for bare-path pushes (ignored on clone-path; push what is already committed)",
|
|
1372
|
+
"name": "message",
|
|
1373
|
+
"hasDynamicHelp": false,
|
|
1374
|
+
"multiple": false,
|
|
1375
|
+
"type": "option"
|
|
1376
|
+
}
|
|
1377
|
+
},
|
|
1378
|
+
"hasDynamicHelp": false,
|
|
1379
|
+
"hiddenAliases": [],
|
|
1380
|
+
"id": "push",
|
|
1381
|
+
"pluginAlias": "@quonfig/cli",
|
|
1382
|
+
"pluginName": "@quonfig/cli",
|
|
1383
|
+
"pluginType": "core",
|
|
1384
|
+
"strict": true,
|
|
1385
|
+
"enableJsonFlag": true,
|
|
1386
|
+
"isESM": true,
|
|
1387
|
+
"relativePath": [
|
|
1388
|
+
"dist",
|
|
1389
|
+
"commands",
|
|
1390
|
+
"push.js"
|
|
1391
|
+
]
|
|
1392
|
+
},
|
|
1290
1393
|
"schema": {
|
|
1291
1394
|
"aliases": [],
|
|
1292
1395
|
"args": {
|
|
@@ -2586,5 +2689,5 @@
|
|
|
2586
2689
|
]
|
|
2587
2690
|
}
|
|
2588
2691
|
},
|
|
2589
|
-
"version": "0.0.
|
|
2692
|
+
"version": "0.0.17"
|
|
2590
2693
|
}
|
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.17",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/quonfig/cli/issues"
|
|
7
7
|
},
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"lodash.camelcase": "^4.3.0",
|
|
33
33
|
"mustache": "^4.2.0",
|
|
34
34
|
"node-forge": "^1.4.0",
|
|
35
|
+
"typescript": "^5",
|
|
35
36
|
"zod": "^4.1.12"
|
|
36
37
|
},
|
|
37
38
|
"description": "Quonfig CLI. For arbitrary targeting rules (email, plan, segment, custom property), run `qfg config-schema` then `qfg pull`.",
|
|
@@ -58,8 +59,7 @@
|
|
|
58
59
|
"prettier": "^3.6.2",
|
|
59
60
|
"shx": "^0.3.4",
|
|
60
61
|
"ts-node": "^10.9.1",
|
|
61
|
-
"tsx": "^4.21.0"
|
|
62
|
-
"typescript": "^5"
|
|
62
|
+
"tsx": "^4.21.0"
|
|
63
63
|
},
|
|
64
64
|
"engines": {
|
|
65
65
|
"node": ">=18.0.0"
|
|
File without changes
|
|
File without changes
|