@voxgig/create-sdkgen 0.23.0 → 0.24.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxgig/create-sdkgen",
3
- "version": "0.23.0",
3
+ "version": "0.24.0",
4
4
  "main": "dist/create-sdkgen.js",
5
5
  "type": "commonjs",
6
6
  "types": "dist/create-sdkgen.d.ts",
@@ -14,6 +14,40 @@ builds or tests, contact package registries, fetch Git refs, or change files.
14
14
  `--github` also reads recent workflow runs and Pages status through `gh`.
15
15
  Install the `.sdk` dependencies before using it.
16
16
 
17
+ ## Is the committed tree what the generator produces?
18
+
19
+ ```sh
20
+ cd .sdk && npm run check-drift
21
+ ```
22
+
23
+ `check-drift.sh` answers a question nothing else asks. Generation has two
24
+ failure modes that are **completely silent** — neither breaks a build or a
25
+ test, and the only symptom is a tree that disagrees with the model:
26
+
27
+ - **Regeneration never DELETES.** A file the generator has stopped emitting
28
+ stays in the tree. Narrow the secrets plugin selection, drop a target,
29
+ rename an entity, and the superseded output is still committed. One SDK
30
+ carried seven orphaned provider clients in `scala/` and seven in `zig/`;
31
+ the scala ones surfaced only because a test cross-checks the tree against
32
+ the model, and zig's were caught by nothing.
33
+ - **`voxgig-model --no-config` writes a REDUCED model.** It skips the
34
+ `.model-config` build that registers the apidef and sdkgen actions, so
35
+ neither runs — and `model/sdk.json` is still written, now missing the name
36
+ case variants and `main.api` / `main.custom`. Use `npm run dry-generate`
37
+ to inspect a model without side effects, and never `--no-config` in
38
+ anything whose output might be committed.
39
+
40
+ The script deletes every target tree, regenerates, and reports what differs —
41
+ which is the only way to see output the generator no longer emits. It refuses
42
+ to run on a dirty tree, restores the target's own build artifacts and
43
+ lockfiles (which the generator never writes), and leaves the regenerated tree
44
+ in place so the diff can be read.
45
+
46
+ It **detects**; it does not prevent. Run it before a release, or after
47
+ changing the model or the toolchain. It is deliberately not in CI: it
48
+ regenerates every target, which is minutes of work and needs the `.sdk`
49
+ dependencies installed.
50
+
17
51
  When a GitHub Pages edition and its CI workflow are enabled, docgen generates
18
52
  `setup-github-pages.sh` here. Preview its changes, then configure Pages:
19
53
 
@@ -26,6 +60,7 @@ The setup script configures GitHub Actions as the publishing source. It does
26
60
  not commit, push, merge, or deploy. Push the documentation workflow and project
27
61
  changes to its configured deployment branch to publish the website.
28
62
 
29
- The status launcher comes from create-sdkgen; its reporting code comes from
30
- sdkgen. Docgen owns the generated Pages setup script. Put project-specific
31
- administration scripts beside them with different names.
63
+ The status launcher and the drift check come from create-sdkgen; the status
64
+ reporting code comes from sdkgen. Docgen owns the generated Pages setup
65
+ script. Put project-specific administration scripts beside them with
66
+ different names.
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env bash
2
+ # Is the committed tree what the generator actually produces?
3
+ #
4
+ # BOTH FAILURE MODES THIS CATCHES ARE SILENT. Nothing breaks when generated
5
+ # output drifts from the model, so nothing reports it:
6
+ #
7
+ # - STALE OUTPUT. Regeneration never DELETES a file it has stopped
8
+ # emitting. Narrow the model's secrets plugin selection, drop a target,
9
+ # rename an entity - the superseded files stay in the tree. Seen in one
10
+ # SDK as seven orphaned provider clients in scala and seven in zig: the
11
+ # scala ones surfaced only because one test cross-checks the tree
12
+ # against the model, and zig's were caught by nothing at all.
13
+ #
14
+ # - MODEL DRIFT. The committed .sdk/model/sdk.json can go round a loop,
15
+ # gaining and losing the name case variants and main.api / main.custom
16
+ # depending on which command last wrote it. `voxgig-model --no-config`
17
+ # writes a REDUCED model - it skips the .model-config build that
18
+ # registers the apidef and sdkgen actions, so neither runs, and sdk.json
19
+ # is still written without what they contribute. The only symptom is a
20
+ # dirty tree after regenerating, which is easy to commit past. To
21
+ # inspect a model with no side effects: `npm run dry-generate`.
22
+ #
23
+ # THE TARGET TREES ARE DELETED FIRST, and that is the whole point. A plain
24
+ # regeneration cannot find stale output: the file is committed, generation
25
+ # leaves it alone, and `git status` is silent. Only forcing the generator to
26
+ # reproduce the tree from nothing shows what it no longer emits.
27
+ #
28
+ # This DETECTS, it does not prevent. Run it before a release, or after
29
+ # changing the model or the toolchain.
30
+ #
31
+ # Not wired into CI on purpose: it regenerates every target, which is
32
+ # minutes of work and needs the .sdk dependencies installed - too heavy for
33
+ # a per-push job, and it would only tell CI what one command finds here.
34
+ set -euo pipefail
35
+
36
+ sdk_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
37
+ root="$(cd -- "$sdk_dir/.." && pwd)"
38
+ cd "$root"
39
+
40
+ # What each target's OWN toolchain produces - a TypeScript build, a
41
+ # resolved lockfile - which the generator does not emit and must not be
42
+ # reported as stale. Everything else under a target directory is generated.
43
+ keep='(^|/)(dist|dist-test|node_modules)/|(^|/)(package-lock\.json|composer\.lock|go\.sum|Cargo\.lock|yarn\.lock)$'
44
+
45
+ # A dirty tree makes the answer meaningless - every uncommitted edit would
46
+ # read as drift - and this deletes directories, so it must not run over
47
+ # work that is not in git.
48
+ if [[ -n "$(git status --porcelain)" ]]; then
49
+ echo 'check-drift: the working tree has uncommitted changes.' >&2
50
+ echo 'Commit or stash them first: this deletes and regenerates every' >&2
51
+ echo 'target, and uncommitted work would be lost and read as drift.' >&2
52
+ exit 2
53
+ fi
54
+
55
+ targets="$(node -e '
56
+ const m = require("./.sdk/model/sdk.json")
57
+ const t = (m.main && m.main.kit && m.main.kit.target) || {}
58
+ console.log(Object.keys(t).sort().join("\n"))
59
+ ')"
60
+
61
+ echo 'check-drift: deleting and regenerating every target ...'
62
+ while IFS= read -r t; do
63
+ [[ -n "$t" && -d "$t" ]] && rm -rf -- "$t"
64
+ done <<< "$targets"
65
+
66
+ log="$(mktemp)"
67
+ trap 'rm -f "$log"' EXIT
68
+ if ! ( cd "$sdk_dir" && npm run generate ) >"$log" 2>&1; then
69
+ echo 'check-drift: generation FAILED. Restoring the tree.' >&2
70
+ cat "$log" >&2
71
+ git checkout -- . || true
72
+ exit 2
73
+ fi
74
+
75
+ # Put back what the generator was never going to write.
76
+ git status --porcelain | awk '"D" == $1 { print $2 }' | grep -E "$keep" \
77
+ | while IFS= read -r f; do git checkout -- "$f"; done || true
78
+
79
+ drift="$(git status --porcelain)"
80
+
81
+ if [[ -z "$drift" ]]; then
82
+ echo 'check-drift: clean. The committed tree is what the generator produces.'
83
+ exit 0
84
+ fi
85
+
86
+ echo >&2
87
+ echo 'check-drift: DRIFT. These differ from what the generator produces:' >&2
88
+ echo >&2
89
+ echo "$drift" >&2
90
+ echo >&2
91
+ echo 'D output the generator no longer emits - delete it.' >&2
92
+ echo 'M output that changed - commit it.' >&2
93
+ echo '?? output not committed - commit it.' >&2
94
+ echo >&2
95
+ echo 'The regenerated tree is left in place so the diff can be read.' >&2
96
+ echo 'Restore with: git checkout -- .' >&2
97
+ exit 1
@@ -21,7 +21,8 @@
21
21
  "add-edition": "voxgig-sdkgen edition add",
22
22
  "text-qa": "cd .. && voxgig-docgen qa",
23
23
  "postinstall": "node build/docgen.js",
24
- "status": "bash admin/status.sh"
24
+ "status": "bash admin/status.sh",
25
+ "check-drift": "bash admin/check-drift.sh"
25
26
  },
26
27
  "author": "$$const.Name$$",
27
28
  "license": "$$const.License$$",