@inixiative/config 0.5.32 → 0.5.33

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 CHANGED
@@ -62,7 +62,7 @@ bunx @inixiative/config train [root]
62
62
 
63
63
  `scan` runs `check` across every ecosystem checkout under a root directory. Targets are derived from the BOM's `ecosystem` keys and matched by package name, so directory names and local layout never need declaring. Origin is the truth, not the checkout: scan fetches per repo and fails on checkouts behind origin (their findings describe a stale tree), fails when a BOM entry has no checkout at all, warns when a checkout sits on a non-default branch (its findings describe the branch, not what ships), and surfaces unmerged `claude/*` session branches.
64
64
 
65
- `train` is the release cascade, run from this repo's checkout. Origin is the truth: it refuses to run from a config checkout behind origin or with any BOM repo missing a checkout, and it fast-forwards clean checkouts to origin before working (dirty or diverged repos still skip). It only ships the default branch: a checkout on a feature branch is skipped whole — never re-locked, committed to, or published from. It walks the ecosystem in dependency order; per repo it: bumps ecosystem ranges to the BOM → re-locks → runs the repo's `check` → commits only its own changes (`package.json` + `bun.lock`) → publishes via `npm publish` when the local version is ahead of npm (OTP prompts pass through) → records the published version in the BOM. It never pushes; review its commits, push, then commit and publish this package so the BOM names the new state.
65
+ `train` is the release cascade, run from this repo's checkout. Origin is the truth: it refuses to run from a config checkout behind origin or with any BOM repo missing a checkout, and it fast-forwards clean checkouts to origin before working (dirty or diverged repos still skip). It only ships the default branch: a checkout on a feature branch is skipped whole — never re-locked, committed to, or published from. It walks the ecosystem in dependency order; per repo it: bumps ecosystem ranges to the BOM → re-locks → runs the repo's `check` → commits only its own changes (`package.json` + `bun.lock`) → publishes via `npm publish` when the local version is ahead of npm (OTP prompts pass through) and waits until the registry serves that version → records it in the BOM. When anything was published it then ships this package last, so the BOM names the new state: bumps its own patch version if npm already has the current one, blesses that version in the BOM, brings the `test/fixtures` consumers onto the blessed set, runs `check`, commits, publishes. It never pushes unless asked: pass `--push` to push every repo it committed to, otherwise it prints the `git push` for each.
66
66
 
67
67
  Division of labor: this CLI owns the toolchain set, ecosystem coherence, stubs, and lockfile format. Renovate owns everything else plus bumping `@inixiative/config` itself, and must be fenced off the toolchain packages.
68
68
 
@@ -85,4 +85,4 @@ jobs:
85
85
 
86
86
  ## Release flow
87
87
 
88
- Publishing moves in dependency order (json-rules → permissions → transitions → rules-builder). `train` automates the walk; after it completes, commit and publish this package so the BOM names the new state. Downstream repos pick it up via `sync`.
88
+ Publishing moves in dependency order (json-rules → permissions → transitions → rules-builder → config). `train` automates the whole walk, this package included. Downstream repos pick it up via `sync`.
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { spawnSync as spawnSync2 } from "child_process";
5
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
5
+ import { existsSync as existsSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
6
6
  import { join as join2, resolve } from "path";
7
7
 
8
8
  // src/lib.ts
@@ -657,6 +657,7 @@ if (command === "train") {
657
657
  console.log(`train order: ${order.map((repo) => repo.name).join(" \u2192 ")}`);
658
658
  const published = [];
659
659
  const skipped = [];
660
+ const committed = [];
660
661
  let bomDirty = false;
661
662
  for (const repo of order) {
662
663
  console.log(`
@@ -756,6 +757,7 @@ if (command === "train") {
756
757
  console.error(`\u2717 commit failed in ${repo.name} \u2014 aborting train`);
757
758
  process.exit(1);
758
759
  }
760
+ committed.push(repo.dir);
759
761
  }
760
762
  if (ahead) {
761
763
  console.log(`publishing ${repo.name}@${localVersion} (npm has ${remoteVersion ?? "none"})`);
@@ -764,6 +766,13 @@ if (command === "train") {
764
766
  console.error(`\u2717 publish failed for ${repo.name} \u2014 aborting train`);
765
767
  process.exit(1);
766
768
  }
769
+ if (!servedByRegistry(repo.name, localVersion)) {
770
+ console.error(
771
+ `\u2717 npm accepted ${repo.name}@${localVersion} but the registry does not serve it \u2014 aborting train`
772
+ );
773
+ process.exit(1);
774
+ }
775
+ committed.push(repo.dir);
767
776
  manifest.ecosystem[repo.name] = localVersion;
768
777
  writeManifest(manifest);
769
778
  bomDirty = true;
@@ -775,11 +784,88 @@ if (command === "train") {
775
784
  console.log(`
776
785
  published: ${published.length === 0 ? "nothing" : published.join(", ")}`);
777
786
  if (skipped.length > 0) console.log(`skipped: ${skipped.join(", ")}`);
778
- if (bomDirty)
779
- console.log("BOM updated in versions.json \u2014 commit @inixiative/config and publish it");
780
- console.log("train does not push \u2014 review the commits it made, then push each repo");
787
+ if (bomDirty) {
788
+ const remote = spawnSync2("npm", ["view", "@inixiative/config@latest", "version"], {
789
+ encoding: "utf8"
790
+ });
791
+ const remoteVersion = remote.status === 0 ? remote.stdout.trim() : null;
792
+ let version = ownVersion();
793
+ if (remoteVersion !== null && compare(version, remoteVersion) <= 0) {
794
+ version = bumpPatch(remoteVersion);
795
+ const pkgPath = join2(packageRoot, "package.json");
796
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf8"));
797
+ pkg.version = version;
798
+ writeFileSync2(pkgPath, `${JSON.stringify(pkg, null, 2)}
799
+ `);
800
+ }
801
+ manifest.ecosystem["@inixiative/config"] = version;
802
+ writeManifest(manifest);
803
+ for (const fixture of fixtureDirs()) {
804
+ const inspection = inspect(fixture, manifest);
805
+ for (const finding of inspection.findings)
806
+ if (finding.kind === "ecosystem-range") finding.fix?.();
807
+ inspection.flush();
808
+ }
809
+ const check = spawnSync2("bun", ["run", "check"], { cwd: packageRoot, stdio: "inherit" });
810
+ if (check.status !== 0) {
811
+ console.error("\u2717 check failed in @inixiative/config \u2014 aborting before publish");
812
+ process.exit(1);
813
+ }
814
+ spawnSync2("git", ["add", "--", "package.json", "versions.json", "test/fixtures"], {
815
+ cwd: packageRoot
816
+ });
817
+ const commit = spawnSync2(
818
+ "git",
819
+ ["commit", "-m", `chore: bless ${published.join(", ")} \u2014 ${version}`],
820
+ {
821
+ cwd: packageRoot,
822
+ stdio: "inherit"
823
+ }
824
+ );
825
+ if (commit.status !== 0) {
826
+ console.error("\u2717 commit failed in @inixiative/config \u2014 aborting before publish");
827
+ process.exit(1);
828
+ }
829
+ const publish = spawnSync2("npm", ["publish"], { cwd: packageRoot, stdio: "inherit" });
830
+ if (publish.status !== 0 || !servedByRegistry("@inixiative/config", version)) {
831
+ console.error("\u2717 publish failed for @inixiative/config");
832
+ process.exit(1);
833
+ }
834
+ committed.push(packageRoot);
835
+ console.log(`published: @inixiative/config@${version} \u2014 the BOM names the new state`);
836
+ }
837
+ if (flags.has("--push")) {
838
+ for (const repoDir of committed) {
839
+ const push = spawnSync2("git", ["push"], { cwd: repoDir, stdio: "inherit" });
840
+ if (push.status !== 0) {
841
+ console.error(`\u2717 push failed in ${repoDir}`);
842
+ process.exit(1);
843
+ }
844
+ }
845
+ console.log(`pushed: ${committed.length} repo${committed.length === 1 ? "" : "s"}`);
846
+ } else if (committed.length > 0) {
847
+ console.log("train does not push without --push \u2014 review its commits, then push:");
848
+ for (const repoDir of committed) console.log(` git -C ${repoDir} push`);
849
+ }
781
850
  process.exit(0);
782
851
  }
852
+ function servedByRegistry(name, version) {
853
+ for (let attempt = 0; attempt < 10; attempt++) {
854
+ const view = spawnSync2("npm", ["view", `${name}@${version}`, "version"], { encoding: "utf8" });
855
+ if (view.status === 0 && view.stdout.trim() === version) return true;
856
+ Bun.sleepSync(3e3);
857
+ }
858
+ return false;
859
+ }
860
+ function bumpPatch(version) {
861
+ const [major, minor, patch] = version.split(".").map(Number);
862
+ return `${major}.${minor}.${(patch ?? 0) + 1}`;
863
+ }
864
+ function fixtureDirs() {
865
+ const root = join2(packageRoot, "test", "fixtures");
866
+ if (!existsSync2(root)) return [];
867
+ return readdirSync2(root).map((name) => join2(root, name)).filter((dir2) => existsSync2(join2(dir2, "package.json")));
868
+ }
783
869
  if (existsSync2(join2(dir, ".git")) && !flags.has("--force")) {
784
870
  const status = spawnSync2("git", ["status", "--porcelain"], { cwd: dir, encoding: "utf8" });
785
871
  if (status.status === 0 && status.stdout.trim().length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inixiative/config",
3
- "version": "0.5.32",
3
+ "version": "0.5.33",
4
4
  "description": "Shared toolchain for the inixiative ecosystem: tsconfig/biome/tsup presets, a version manifest (BOM), and a sync/check CLI",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/versions.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "@types/bun"
14
14
  ],
15
15
  "ecosystem": {
16
- "@inixiative/config": "0.5.32",
16
+ "@inixiative/config": "0.5.33",
17
17
  "@inixiative/json-rules": "2.25.0",
18
18
  "@inixiative/permissions": "0.3.2",
19
19
  "@inixiative/transitions": "0.1.0",