@biffo/cli 0.150.0 → 0.151.1

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.
Files changed (2) hide show
  1. package/dist/index.js +70 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1566,6 +1566,7 @@ function planMigrationCarry(options) {
1566
1566
  const recognised = [];
1567
1567
  const declinedIndex = new Map((options.declined ?? []).map((d) => [d.file, d]));
1568
1568
  const declined = [];
1569
+ const divergedBodies = [];
1569
1570
  let head = instanceHead;
1570
1571
  for (const m of chainOrder(template)) {
1571
1572
  const decline = declinedIndex.get(m.file);
@@ -1583,6 +1584,13 @@ function planMigrationCarry(options) {
1583
1584
  if (already.how !== "filename") {
1584
1585
  recognised.push({ file: m.file, instanceFile: already.instance.file, how: already.how });
1585
1586
  }
1587
+ if (migrationBodyHash(m.content) !== migrationBodyHash(already.instance.content)) {
1588
+ divergedBodies.push({
1589
+ file: m.file,
1590
+ instanceFile: already.instance.file,
1591
+ how: already.how
1592
+ });
1593
+ }
1586
1594
  continue;
1587
1595
  }
1588
1596
  let revision = m.revision;
@@ -1625,7 +1633,31 @@ function planMigrationCarry(options) {
1625
1633
  );
1626
1634
  const templateFiles = new Set(template.map((m) => m.file));
1627
1635
  const staleDeclines = [...declinedIndex.keys()].filter((f) => !templateFiles.has(f));
1628
- return { entries, instanceHead, skipped, recognised, declined, staleDeclines };
1636
+ return {
1637
+ entries,
1638
+ instanceHead,
1639
+ skipped,
1640
+ recognised,
1641
+ declined,
1642
+ staleDeclines,
1643
+ divergedBodies
1644
+ };
1645
+ }
1646
+ var TEST_PATH_RE = /(^|\/)tests?\/.*\btest_[^/]*\.py$/;
1647
+ function findMigrationTestPairings(changes, divergedBodies) {
1648
+ if (divergedBodies.length === 0) return [];
1649
+ const pairings = [];
1650
+ for (const change of changes) {
1651
+ if (!TEST_PATH_RE.test(change.path)) continue;
1652
+ const content = change.content;
1653
+ if (content === void 0) continue;
1654
+ for (const d of divergedBodies) {
1655
+ if (content.includes(d.file)) {
1656
+ pairings.push({ testPath: change.path, migration: d.file, instanceFile: d.instanceFile });
1657
+ }
1658
+ }
1659
+ }
1660
+ return pairings;
1629
1661
  }
1630
1662
  function indexInstanceMigrations(instance) {
1631
1663
  const index = {
@@ -2513,6 +2545,10 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
2513
2545
  printBreakingChanges(breaking, options.apply === true);
2514
2546
  printPlan(plan);
2515
2547
  printMigrationCarry(migrations);
2548
+ printMigrationBodyDrift(
2549
+ migrations,
2550
+ findMigrationTestPairings(plan.changes, migrations.divergedBodies)
2551
+ );
2516
2552
  printCoreVersionCleanup(coreVersionCleanup, options.apply === true);
2517
2553
  warnStaleFirstPartyCopies(options.cwd);
2518
2554
  console.log(
@@ -2757,6 +2793,39 @@ function printMigrationCarry(migrations) {
2757
2793
  console.log(` ${chalk4.green("migration".padEnd(15))} ${e.path}${suffix}`);
2758
2794
  }
2759
2795
  }
2796
+ function printMigrationBodyDrift(migrations, pairings) {
2797
+ if (migrations.divergedBodies.length === 0) return;
2798
+ console.log();
2799
+ for (const d of migrations.divergedBodies) {
2800
+ const alias = d.instanceFile === d.file ? "" : chalk4.dim(` (this instance calls it ${d.instanceFile})`);
2801
+ console.log(
2802
+ ` ${chalk4.yellow("body drift".padEnd(15))} ${d.file}${alias}
2803
+ ` + chalk4.dim(
2804
+ " The template has changed this migration since you carried it.\n An applied migration cannot be re-run, so the carry left yours\n alone and this change will NOT reach you."
2805
+ )
2806
+ );
2807
+ }
2808
+ if (pairings.length === 0) {
2809
+ console.log(
2810
+ chalk4.dim(
2811
+ "\n No arriving test asserts the new body, so this upgrade should still go green.\n It stays a convergence gap: fresh environments get the new body, you keep the old."
2812
+ )
2813
+ );
2814
+ return;
2815
+ }
2816
+ console.log(
2817
+ `
2818
+ ${chalk4.red.bold("This upgrade will not go green.")} ${pairings.length} arriving test(s) assert a migration body you will not receive:`
2819
+ );
2820
+ for (const p of pairings) {
2821
+ console.log(` ${chalk4.red(p.testPath)} ${chalk4.dim(`\u2192 asserts ${p.migration}`)}`);
2822
+ }
2823
+ console.log(
2824
+ chalk4.dim(
2825
+ "\n Resolve before merging, by one of:\n - port the migration body change into your copy by hand, keeping its\n `# biffo:carried-from:` marker \u2014 safe only if re-stating the DDL is a\n no-op against your already-migrated database;\n - drop the arriving test from this PR and raise it upstream, if the\n property it asserts cannot hold here;\n - ask upstream to ship the change as a follow-on migration instead, which\n is the only option that actually converges an applied chain.\n"
2826
+ )
2827
+ );
2828
+ }
2760
2829
  function printCoreVersionCleanup(cleanup, applying) {
2761
2830
  if (cleanup === null) return;
2762
2831
  if (cleanup.action === "delete") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.150.0",
3
+ "version": "0.151.1",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",