@pareto4/pp-core 1.10.0 → 1.11.0
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/.claude-plugin/plugin.json +1 -1
- package/gemini-extension.json +1 -1
- package/package.json +5 -3
- package/pareto-skills/pp-brand/SKILL.md +1 -1
- package/pp-core/bin/lib/capability-registry.cjs +49 -1
- package/pp-core/bin/lib/compose-bindings.cjs +736 -0
- package/pp-core/bin/lib/git-provider.cjs +279 -0
- package/pp-core/bin/lib/init-contract.cjs +989 -0
- package/pp-core/bin/lib/init-interactive.cjs +183 -0
- package/pp-core/bin/lib/io.cjs +2 -0
- package/pp-core/bin/lib/org-profile.cjs +696 -0
- package/pp-core/bin/lib/platform-bindings.cjs +268 -64
- package/pp-core/bin/lib/vault-command-router.cjs +133 -2
- package/pp-core/bin/lib/vault-coordinates.cjs +485 -0
- package/pp-core/bin/lib/vault-write-back.cjs +178 -92
- package/pp-core/bin/lib/vault.cjs +158 -81
- package/pp-core/bin/pp-tools.cjs +199 -2
- package/pp-core/workflows/discovery-sprint.md +1 -1
- package/pp-core/workflows/discovery-to-build/steps/architecture-deliverables.md +1 -1
- package/pp-core/workflows/init.md +137 -76
- package/scripts/gen-platform-bindings-schema.cjs +148 -0
|
@@ -7,7 +7,23 @@ Implements decisions: D-02 (no Obsidian/board scaffolding), D-03 (no discovery s
|
|
|
7
7
|
scaffolding here — /pp-discovery self-generates them), D-04 (ASK handoff with smart
|
|
8
8
|
default), D-06 (generic remote wiring: prompt URL → git remote add → confirmed push,
|
|
9
9
|
zero provider API), D-07 (minimal identity only — no CLAUDE.md client-context block),
|
|
10
|
-
D-13 (gate on .platform-bindings.json via the Phase 7 reader
|
|
10
|
+
D-13 (gate on .platform-bindings.json via the Phase 7 reader — Step 2 now composes and
|
|
11
|
+
writes this file too, not only reads it, Phase 34 INIT-01).
|
|
12
|
+
|
|
13
|
+
Provisioning is provider-aware (Phase 34 D-11/D-13): `resolveProvider` is the sole
|
|
14
|
+
authority for which provisioning path runs. The gated GitLab `glab` recipe (G-2…G-5) runs
|
|
15
|
+
only when it resolves to `gitlab`; under any other provider (`azure` today) it is
|
|
16
|
+
**skipped, not failed**, and the run falls through to this same generic remote-wiring
|
|
17
|
+
path so the user still leaves with a wired remote (Phase 34 INIT-03).
|
|
18
|
+
|
|
19
|
+
Step 2 is a single instruction to run `pp-tools init` in a real terminal, not a prose
|
|
20
|
+
questionnaire and not a relay this document drives (Phase 34-09 gap closure). Four prior
|
|
21
|
+
UAT walks — plain prose, hardened prose, a tested CLI arm, and a session state machine the
|
|
22
|
+
agent was still handed one step at a time — all failed at the identical point: the agent's
|
|
23
|
+
own model of which steps exist overrode this document and it substituted its own
|
|
24
|
+
`AskUserQuestion` wizard for the question set (`34-VALIDATION.md`). The interaction now
|
|
25
|
+
lives entirely inside `pp-tools init` itself — a human types answers directly into the
|
|
26
|
+
process that acts on them — so this document has nothing left to compress, reorder or drop.
|
|
11
27
|
|
|
12
28
|
Inert of all Pareto/GitLab behavior when no .platform-bindings.json is present.
|
|
13
29
|
</purpose>
|
|
@@ -119,61 +135,51 @@ Step 0). Continue honoring it for every `AskUserQuestion` call in this and later
|
|
|
119
135
|
|
|
120
136
|
---
|
|
121
137
|
|
|
122
|
-
## 2.
|
|
138
|
+
## 2. Identity, provider and binding — run `pp-tools init` (D-05, D-07, INIT-01, INIT-02, INIT-03)
|
|
123
139
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
config. These fields are used by the gated GitLab provisioning path (Plan 02) for
|
|
127
|
-
`group/repo-slug` and `visibility`; they do NOT produce a `CLAUDE.md` client-context
|
|
128
|
-
block — that is Phase 9 (CTX-01..03) territory. Do NOT write a coding-standards,
|
|
129
|
-
architecture, or DoD block here; executor and Phase 9 must not double-capture these.
|
|
130
|
-
|
|
131
|
-
Ask the user with `AskUserQuestion` (or plain-text prompts in TEXT_MODE):
|
|
132
|
-
|
|
133
|
-
```text
|
|
134
|
-
1. Project name (human-readable, e.g. "Acme Dashboard")
|
|
135
|
-
2. Project slug (kebab-case, e.g. "acme-dashboard")
|
|
136
|
-
3. Client name (company / team, e.g. "Acme Corp") — type "internal" if none
|
|
137
|
-
4. Client slug (kebab-case, e.g. "acme") — type "internal" if none
|
|
138
|
-
5. One-line description (what this project does)
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
Also ask — used only by the gated GitLab path (D-13, Plan 02):
|
|
140
|
+
Tell the user to run this in their own terminal, then **stop** and wait for them to confirm
|
|
141
|
+
they are done before continuing to Step 3:
|
|
142
142
|
|
|
143
143
|
```text
|
|
144
|
-
|
|
145
|
-
7. Visibility (private / public / internal)
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Write these to `.planning/config.json`:
|
|
149
|
-
|
|
150
|
-
```json
|
|
151
|
-
{
|
|
152
|
-
"project": {
|
|
153
|
-
"name": "<value>",
|
|
154
|
-
"slug": "<value>",
|
|
155
|
-
"client_name": "<value>",
|
|
156
|
-
"client_slug": "<value>",
|
|
157
|
-
"description": "<value>",
|
|
158
|
-
"provider": "<value>",
|
|
159
|
-
"visibility": "<value>"
|
|
160
|
-
}
|
|
161
|
-
}
|
|
144
|
+
pp-tools init
|
|
162
145
|
```
|
|
163
146
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
147
|
+
**Do not ask the identity questions yourself. Do not describe the question set, the
|
|
148
|
+
provider options, or the compose confirmation. Do not attempt to drive the `init-contract`
|
|
149
|
+
session on the user's behalf — not even one step of it, even via `--session-next` /
|
|
150
|
+
`--session-answer`.** Four dated UAT walks (`34-VALIDATION.md` § Manual-Only Verifications)
|
|
151
|
+
found the IDENTICAL failure at exactly this boundary, across four escalating strategies —
|
|
152
|
+
plain prose, hardened prose, a data-supplying CLI arm, and a session state machine the
|
|
153
|
+
agent was still handed one step at a time. Every pure-bash step ran reliably every time;
|
|
154
|
+
the one step whose job is to ask a structured question set was always overridden by the
|
|
155
|
+
agent's own `AskUserQuestion` wizard instead. The only strategy left is to remove the
|
|
156
|
+
interaction from this document entirely: `pp-tools init` prompts on a real terminal, reads
|
|
157
|
+
the answers itself, and performs every write (`.planning/config.json`, `vault.provider`,
|
|
158
|
+
`.platform-bindings.json`) — a human types answers directly into the process that acts on
|
|
159
|
+
them, with nothing in between. It refuses to run when stdin is not a TTY.
|
|
160
|
+
|
|
161
|
+
**If this step is skipped, `/pp-init` has not completed** — no identity config, no
|
|
162
|
+
`vault.provider`, and no binding are produced without it. `pp_run query init-contract
|
|
163
|
+
--verify` reports exactly what is still missing if the operator wants to confirm before
|
|
164
|
+
continuing.
|
|
165
|
+
|
|
166
|
+
**Scope boundary:** this step writes identity, provider and the platform binding only. All
|
|
167
|
+
coding-standards, architecture context, client-engagement CLAUDE.md blocks, and
|
|
168
|
+
discovery-session scaffolding are out of scope here. Phase 9 owns those; /pp-discovery
|
|
169
|
+
self-generates its own session files (D-03).
|
|
169
170
|
|
|
170
171
|
---
|
|
171
172
|
|
|
172
173
|
## 3. Remote wiring — provider-agnostic (non-Pareto / no bindings)
|
|
173
174
|
|
|
174
|
-
**Gate (D-06, D-13)
|
|
175
|
-
|
|
176
|
-
|
|
175
|
+
**Gate (D-06, D-13) — bindings presence ONLY, never the provider answer.** Step 3 always
|
|
176
|
+
runs exactly one of its two sub-paths on every `/pp-init` invocation; it is never skipped
|
|
177
|
+
outright. Take THIS generic sub-path when
|
|
178
|
+
`pp_run query platform-bindings --pick present 2>/dev/null` does NOT equal `"true"`. When
|
|
179
|
+
bindings ARE present, take the gated GitLab/Azure sub-path below instead — that section is
|
|
180
|
+
still part of Step 3, not a separate optional step. **The provider answer never decides
|
|
181
|
+
whether Step 3 runs** — inside the gated sub-path it only decides whether the
|
|
182
|
+
GitLab-specific `glab` recipe fires or falls through to this same generic code below.
|
|
177
183
|
|
|
178
184
|
On the generic path:
|
|
179
185
|
|
|
@@ -213,17 +219,80 @@ gets its own per-action confirmation immediately before it fires — nothing bat
|
|
|
213
219
|
step makes **ZERO `gh`/`glab`/provider-API calls**. The push target is the user-supplied
|
|
214
220
|
URL only.
|
|
215
221
|
|
|
216
|
-
### Gated GitLab provisioning (
|
|
222
|
+
### Gated GitLab/Azure provisioning (Step 3's other sub-path — bindings present)
|
|
223
|
+
|
|
224
|
+
This is Step 3's other sub-path, not a separate step. It runs **instead of** the generic
|
|
225
|
+
remote-wiring path above when
|
|
226
|
+
`pp_run query platform-bindings --pick present 2>/dev/null` equals `"true"`. Within it,
|
|
227
|
+
G-1 always runs first to source coordinates and apply the two guards below; the
|
|
228
|
+
GitLab-specific create recipe (G-2…G-5) runs **only when the resolved provider is
|
|
229
|
+
`gitlab`** — under any other provider (`azure` today) it is skipped, not failed, and the
|
|
230
|
+
run falls through to Step 3's generic remote-wiring path above instead (D-11, D-13).
|
|
231
|
+
Implements D-05 (per-action confirmation), D-09 (existence probe → adopt-if-exists), D-10
|
|
232
|
+
(private by default), D-11 (auth verify-only; gated on provider), D-12 (adopt-if-exists
|
|
233
|
+
reconciliation; coordinates sourced one-per-value, no phantom `gitlab.*` binding reads),
|
|
234
|
+
D-13 (bindings gate; `resolveProvider` is the sole authority for which path runs).
|
|
235
|
+
Covers INIT-02, INIT-03, and the cross-cutting SAFE-01 invariant.
|
|
236
|
+
|
|
237
|
+
#### G-1. Config reads — one source per value (D-12)
|
|
238
|
+
|
|
239
|
+
Per-project values (repo slug, visibility, description) come from the `project.*` block
|
|
240
|
+
Step 2 already writes to `.planning/config.json`; the org-wide group root comes from the
|
|
241
|
+
org profile's init-only seed section, read through the composer's own offline probe; the
|
|
242
|
+
host comes from the composed binding. **No `gitlab` namespace exists in the schema**
|
|
243
|
+
(`additionalProperties: false` at the top level, no such block) or will be declared —
|
|
244
|
+
declaring one would put the same value in both the binding and `.planning/config.json`, a
|
|
245
|
+
fresh instance of the two-sources-of-truth class this repo has already paid for twice.
|
|
246
|
+
One source per value:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
GROUP=$(pp_run query compose-bindings --offline --pick seed.provisioningGroup 2>/dev/null)
|
|
250
|
+
SLUG=$(pp_run query config-get project.slug --raw 2>/dev/null)
|
|
251
|
+
VIS_RAW=$(pp_run query config-get project.visibility --raw 2>/dev/null)
|
|
252
|
+
HOST=$(pp_run query platform-bindings --pick platform.host 2>/dev/null)
|
|
253
|
+
# D-10: hard-default to private when field is absent or empty
|
|
254
|
+
VIS="${VIS_RAW:-private}"
|
|
255
|
+
DESC=$(pp_run query config-get project.description --raw 2>/dev/null || echo "")
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
`HOST` still defaults to `gitlab.com` in remote URLs below when empty (the existing
|
|
259
|
+
`${HOST:-gitlab.com}` fallback below, unchanged). `DESC` now carries `--raw`: without it
|
|
260
|
+
`config-get` returns a JSON-quoted string, so the create step's `--description "$DESC"`
|
|
261
|
+
would pass the surrounding quotes through into the created project's description.
|
|
262
|
+
|
|
263
|
+
**Empty-group guard (closes T-34-41).** A bare or half-empty group/slug pair is the
|
|
264
|
+
personal-namespace landmine named in the security invariants below: creating with an
|
|
265
|
+
empty group lands under the user's personal namespace, not the client group. When
|
|
266
|
+
`GROUP` or `SLUG` is empty, print a clear line naming which one is missing and where it
|
|
267
|
+
comes from, skip G-2 through G-5 entirely, and fall through to Step 3's generic remote
|
|
268
|
+
wiring:
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
if [ -z "$GROUP" ] || [ -z "$SLUG" ]; then
|
|
272
|
+
echo "Skipping GitLab provisioning: $( [ -z "$GROUP" ] && echo "GROUP (org profile seed provisioning group)" || echo "SLUG (project.slug)" ) is empty — falling through to Step 3's generic remote wiring."
|
|
273
|
+
fi
|
|
274
|
+
```
|
|
217
275
|
|
|
218
|
-
|
|
219
|
-
`
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
(
|
|
276
|
+
**Provider gate (D-11, D-13).** Read the one authority for which provisioning path runs —
|
|
277
|
+
`--offline` so this never touches the network:
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
PROVIDER=$(pp_run query compose-bindings --offline --pick provider 2>/dev/null)
|
|
281
|
+
if [ "$PROVIDER" != "gitlab" ]; then
|
|
282
|
+
echo "Skipping GitLab-specific provisioning for provider \"$PROVIDER\" — falling through to Step 3's generic remote wiring instead."
|
|
283
|
+
fi
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
Continue to G-2 only when the empty-group guard above did not skip **and** `PROVIDER`
|
|
287
|
+
equals `gitlab`. Otherwise stop here and go to Step 3's generic remote wiring (`:283-312`)
|
|
288
|
+
instead — do not fail, do not error, do not prompt for a missing CLI. "Skipped, not
|
|
289
|
+
failed" is satisfied *and* the user still leaves with a wired remote, which a bare skip
|
|
290
|
+
would not give them; pp-core makes zero provider-API calls it has no client for.
|
|
223
291
|
|
|
224
292
|
**Security invariants — landmine guards:**
|
|
225
293
|
- Always use the group-qualified `$GROUP/$SLUG` path — never a bare slug (T-08-04, D-09).
|
|
226
|
-
A bare slug creates under the user's personal namespace, not the client group.
|
|
294
|
+
A bare slug creates under the user's personal namespace, not the client group. G-1's
|
|
295
|
+
empty-group guard above is what actually enforces this before any create attempt.
|
|
227
296
|
- Always pass `--private` explicitly — never inherit `glab`'s default `--internal`
|
|
228
297
|
visibility (T-08-05, D-10). The `--internal` flag is what makes this dangerous; the
|
|
229
298
|
locked recipe must carry `--private` and must NOT emit `--internal`.
|
|
@@ -233,25 +302,6 @@ by default), D-11 (auth verify-only), D-12 (adopt-if-exists reconciliation), D-1
|
|
|
233
302
|
- Always reconcile create with a `glab repo view` probe — a non-zero create exit does
|
|
234
303
|
NOT mean nothing happened (T-08-09, D-09).
|
|
235
304
|
|
|
236
|
-
#### G-1. Config reads from the bindings reader (Pattern C, D-09, D-13)
|
|
237
|
-
|
|
238
|
-
All values come from the Phase 7 bindings reader. **Never hardcoded.**
|
|
239
|
-
|
|
240
|
-
```bash
|
|
241
|
-
GROUP=$(pp_run query platform-bindings --pick gitlab.group 2>/dev/null)
|
|
242
|
-
SLUG=$(pp_run query platform-bindings --pick gitlab.repo_slug 2>/dev/null)
|
|
243
|
-
VIS_RAW=$(pp_run query platform-bindings --pick gitlab.visibility 2>/dev/null)
|
|
244
|
-
HOST=$(pp_run query platform-bindings --pick gitlab.host 2>/dev/null)
|
|
245
|
-
# D-10: hard-default to private when field is absent or empty
|
|
246
|
-
VIS="${VIS_RAW:-private}"
|
|
247
|
-
DESC=$(pp_run query config-get project.description 2>/dev/null || echo "")
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
The `gitlab.*` namespace is provisional — the bindings reader is lenient on unknown fields
|
|
251
|
-
(it only hard-validates `schemaVersion`, `platform.repo`, and `platform.tag`). Treat any
|
|
252
|
-
absent `gitlab.*` field gracefully: fall back to safe defaults, never fail hard on a
|
|
253
|
-
missing key. `HOST` defaults to `gitlab.com` in remote URLs when empty.
|
|
254
|
-
|
|
255
305
|
#### G-2. Auth verify-only (D-11)
|
|
256
306
|
|
|
257
307
|
`/pp-init` **never establishes auth and never reads or writes the user's token.**
|
|
@@ -518,13 +568,24 @@ chosen command or stop cleanly.
|
|
|
518
568
|
|
|
519
569
|
## 5. Commit
|
|
520
570
|
|
|
521
|
-
Commit the `.planning/config.json` identity stub created in Step 2
|
|
571
|
+
Commit the `.planning/config.json` identity stub created in Step 2. When the Step 2 session
|
|
572
|
+
composed and wrote a binding (the `compose_confirm` approval), commit `.platform-bindings.json`
|
|
573
|
+
in this same commit rather than a second one (D-09): being committed is what makes the
|
|
574
|
+
binding outrank the profile on every later run, and leaving it untracked would leave the
|
|
575
|
+
precedence story half-built while still tripping every presence gate. The file may
|
|
576
|
+
legitimately not exist — the session declines or composes nothing on the inert path
|
|
577
|
+
(INIT-02) — so branch explicitly rather than word-splitting an unquoted variable into
|
|
578
|
+
`--files`:
|
|
522
579
|
|
|
523
580
|
```bash
|
|
524
|
-
|
|
581
|
+
if [ -f .platform-bindings.json ]; then
|
|
582
|
+
pp_run query commit "docs: initialize project" --files .planning/config.json .platform-bindings.json
|
|
583
|
+
else
|
|
584
|
+
pp_run query commit "docs: initialize project" --files .planning/config.json
|
|
585
|
+
fi
|
|
525
586
|
```
|
|
526
587
|
|
|
527
|
-
If
|
|
528
|
-
|
|
588
|
+
If other planning artifacts were created (e.g. `.planning/` directory structure), include
|
|
589
|
+
them too.
|
|
529
590
|
|
|
530
591
|
</process>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generates schemas/platform-bindings.schema.json's three GENERATED values
|
|
6
|
+
* from src/platform-bindings.cts's exported constants (via the COMPILED
|
|
7
|
+
* pp-core/bin/lib/platform-bindings.cjs). Commit the result; CI re-runs the
|
|
8
|
+
* script's --check arm and fails on drift.
|
|
9
|
+
*
|
|
10
|
+
* This script owns EXACTLY three values in the schema — the JSON pointers
|
|
11
|
+
* `properties.platform.properties.repo.pattern`,
|
|
12
|
+
* `properties.client.properties.vaultRepo.pattern`, and (34-02)
|
|
13
|
+
* `properties.schemaVersion.default`. Every other field in
|
|
14
|
+
* schemas/platform-bindings.schema.json is hand-authored and must be edited
|
|
15
|
+
* by hand. `--write` additionally normalizes the whole file's JSON
|
|
16
|
+
* formatting (two-space indent, single trailing newline).
|
|
17
|
+
*
|
|
18
|
+
* BUILD ORDER: this script requires the COMPILED
|
|
19
|
+
* pp-core/bin/lib/platform-bindings.cjs, so it must run AFTER `build:lib` in
|
|
20
|
+
* the `build` npm script chain — running it before the TypeScript build
|
|
21
|
+
* would throw a missing-module error.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* node scripts/gen-platform-bindings-schema.cjs # print to stdout
|
|
25
|
+
* node scripts/gen-platform-bindings-schema.cjs --write # write the schema
|
|
26
|
+
* node scripts/gen-platform-bindings-schema.cjs --check # exit 1 if committed schema is stale
|
|
27
|
+
* node scripts/gen-platform-bindings-schema.cjs --schema <path> # override the target file
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
const fs = require('node:fs');
|
|
31
|
+
const path = require('node:path');
|
|
32
|
+
|
|
33
|
+
const { ExitError, runMain } = require('./lib/cli-exit.cjs');
|
|
34
|
+
|
|
35
|
+
const ROOT = path.resolve(__dirname, '..');
|
|
36
|
+
const DEFAULT_SCHEMA_PATH = path.join(ROOT, 'schemas', 'platform-bindings.schema.json');
|
|
37
|
+
|
|
38
|
+
const PLATFORM_REPO_POINTER = 'properties.platform.properties.repo.pattern';
|
|
39
|
+
const VAULT_REPO_POINTER = 'properties.client.properties.vaultRepo.pattern';
|
|
40
|
+
// 34-02: carries CONTRACT_SCHEMA_VERSION into the schema as `default`, never
|
|
41
|
+
// `const`/`enum` — JSON Schema treats `default` as annotation-only, so this
|
|
42
|
+
// documents the current contract without constraining validation. Using
|
|
43
|
+
// `const`/`enum` here would reject every existing `1.0.0` binding and break
|
|
44
|
+
// Phase 32 D-08.
|
|
45
|
+
const CONTRACT_VERSION_POINTER = 'properties.schemaVersion.default';
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Two-space-indented JSON with a single trailing newline — the format the
|
|
49
|
+
* committed schema file already uses.
|
|
50
|
+
*/
|
|
51
|
+
function serialize(obj) {
|
|
52
|
+
return JSON.stringify(obj, null, 2) + '\n';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Build the regenerated schema object: read and parse `schemaPath`, then
|
|
57
|
+
* assign the two owned pattern values from the compiled library. Throws an
|
|
58
|
+
* ExitError if either exported pattern source is missing or empty — a
|
|
59
|
+
* silently-empty pattern written into the schema would accept everything.
|
|
60
|
+
*/
|
|
61
|
+
function buildSchema(schemaPath) {
|
|
62
|
+
const lib = require(path.join(ROOT, 'pp-core', 'bin', 'lib', 'platform-bindings.cjs'));
|
|
63
|
+
const { PLATFORM_REPO_PATTERN_SOURCE, VAULT_REPO_PATTERN_SOURCE, CONTRACT_SCHEMA_VERSION } = lib;
|
|
64
|
+
|
|
65
|
+
if (typeof PLATFORM_REPO_PATTERN_SOURCE !== 'string' || PLATFORM_REPO_PATTERN_SOURCE.length === 0) {
|
|
66
|
+
throw new ExitError(
|
|
67
|
+
1,
|
|
68
|
+
'pp-core/bin/lib/platform-bindings.cjs does not export a non-empty PLATFORM_REPO_PATTERN_SOURCE — ' +
|
|
69
|
+
'run `npm run build:lib` first.',
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
if (typeof VAULT_REPO_PATTERN_SOURCE !== 'string' || VAULT_REPO_PATTERN_SOURCE.length === 0) {
|
|
73
|
+
throw new ExitError(
|
|
74
|
+
1,
|
|
75
|
+
'pp-core/bin/lib/platform-bindings.cjs does not export a non-empty VAULT_REPO_PATTERN_SOURCE — ' +
|
|
76
|
+
'run `npm run build:lib` first.',
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (typeof CONTRACT_SCHEMA_VERSION !== 'string' || !/^\d+\.\d+\.\d+$/.test(CONTRACT_SCHEMA_VERSION)) {
|
|
80
|
+
throw new ExitError(
|
|
81
|
+
1,
|
|
82
|
+
'pp-core/bin/lib/platform-bindings.cjs does not export a semver-triplet CONTRACT_SCHEMA_VERSION — ' +
|
|
83
|
+
'run `npm run build:lib` first.',
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const schema = JSON.parse(fs.readFileSync(schemaPath, 'utf8'));
|
|
88
|
+
schema.properties.platform.properties.repo.pattern = PLATFORM_REPO_PATTERN_SOURCE;
|
|
89
|
+
schema.properties.client.properties.vaultRepo.pattern = VAULT_REPO_PATTERN_SOURCE;
|
|
90
|
+
schema.properties.schemaVersion.default = CONTRACT_SCHEMA_VERSION;
|
|
91
|
+
return schema;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function parseSchemaPathArg(argv) {
|
|
95
|
+
const idx = argv.indexOf('--schema');
|
|
96
|
+
if (idx === -1) return DEFAULT_SCHEMA_PATH;
|
|
97
|
+
const value = argv[idx + 1];
|
|
98
|
+
if (!value) {
|
|
99
|
+
throw new ExitError(1, '--schema requires a path argument');
|
|
100
|
+
}
|
|
101
|
+
return path.resolve(value);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function main() {
|
|
105
|
+
const argv = process.argv.slice(2);
|
|
106
|
+
const schemaPath = parseSchemaPathArg(argv);
|
|
107
|
+
|
|
108
|
+
if (argv.includes('--check')) {
|
|
109
|
+
const committed = fs.readFileSync(schemaPath, 'utf8');
|
|
110
|
+
const live = serialize(buildSchema(schemaPath));
|
|
111
|
+
if (committed !== live) {
|
|
112
|
+
const committedObj = JSON.parse(committed);
|
|
113
|
+
const liveObj = JSON.parse(live);
|
|
114
|
+
process.stderr.write(
|
|
115
|
+
schemaPath + ' is stale. The generator owns exactly three values:\n' +
|
|
116
|
+
' ' + PLATFORM_REPO_POINTER + '\n' +
|
|
117
|
+
' ' + VAULT_REPO_POINTER + '\n' +
|
|
118
|
+
' ' + CONTRACT_VERSION_POINTER + '\n\n' +
|
|
119
|
+
' ' + PLATFORM_REPO_POINTER + '\n' +
|
|
120
|
+
' committed: ' + JSON.stringify(committedObj.properties.platform.properties.repo.pattern) + '\n' +
|
|
121
|
+
' live: ' + JSON.stringify(liveObj.properties.platform.properties.repo.pattern) + '\n' +
|
|
122
|
+
' ' + VAULT_REPO_POINTER + '\n' +
|
|
123
|
+
' committed: ' + JSON.stringify(committedObj.properties.client.properties.vaultRepo.pattern) + '\n' +
|
|
124
|
+
' live: ' + JSON.stringify(liveObj.properties.client.properties.vaultRepo.pattern) + '\n' +
|
|
125
|
+
' ' + CONTRACT_VERSION_POINTER + '\n' +
|
|
126
|
+
' committed: ' + JSON.stringify(committedObj.properties.schemaVersion.default) + '\n' +
|
|
127
|
+
' live: ' + JSON.stringify(liveObj.properties.schemaVersion.default) + '\n\n' +
|
|
128
|
+
'Run:\n' +
|
|
129
|
+
' node scripts/gen-platform-bindings-schema.cjs --write' +
|
|
130
|
+
(schemaPath === DEFAULT_SCHEMA_PATH ? '' : ' --schema ' + schemaPath) +
|
|
131
|
+
'\n',
|
|
132
|
+
);
|
|
133
|
+
throw new ExitError(1);
|
|
134
|
+
}
|
|
135
|
+
process.stdout.write(schemaPath + ' is up to date.\n');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (argv.includes('--write')) {
|
|
140
|
+
fs.writeFileSync(schemaPath, serialize(buildSchema(schemaPath)));
|
|
141
|
+
process.stdout.write('Wrote ' + schemaPath + '\n');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
process.stdout.write(serialize(buildSchema(schemaPath)));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
runMain(main);
|