@intentius/chant 0.105.1 → 0.106.3

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 (111) hide show
  1. package/dist/change-set.d.ts +30 -8
  2. package/dist/change-set.d.ts.map +1 -1
  3. package/dist/cli/commands/build.d.ts +11 -0
  4. package/dist/cli/commands/build.d.ts.map +1 -1
  5. package/dist/cli/commands/lint.d.ts.map +1 -1
  6. package/dist/cli/handlers/build.d.ts.map +1 -1
  7. package/dist/cli/handlers/fan-out.d.ts.map +1 -1
  8. package/dist/cli/handlers/operator.d.ts.map +1 -1
  9. package/dist/cli/handlers/pr.d.ts +16 -2
  10. package/dist/cli/handlers/pr.d.ts.map +1 -1
  11. package/dist/cli/handlers/promote.d.ts.map +1 -1
  12. package/dist/cli/handlers/run.d.ts.map +1 -1
  13. package/dist/cli/main.d.ts.map +1 -1
  14. package/dist/cli/mcp/op-tools.d.ts.map +1 -1
  15. package/dist/cli/registry.d.ts +7 -0
  16. package/dist/cli/registry.d.ts.map +1 -1
  17. package/dist/components/changed-files.d.ts +8 -2
  18. package/dist/components/changed-files.d.ts.map +1 -1
  19. package/dist/components/fan-out-record.d.ts +24 -0
  20. package/dist/components/fan-out-record.d.ts.map +1 -1
  21. package/dist/components/pr-pipeline.d.ts +67 -4
  22. package/dist/components/pr-pipeline.d.ts.map +1 -1
  23. package/dist/components/pr-run.d.ts +94 -3
  24. package/dist/components/pr-run.d.ts.map +1 -1
  25. package/dist/gated-waves.d.ts +4 -1
  26. package/dist/gated-waves.d.ts.map +1 -1
  27. package/dist/lexicon.d.ts +13 -2
  28. package/dist/lexicon.d.ts.map +1 -1
  29. package/dist/lifecycle/gate-ledger.d.ts.map +1 -1
  30. package/dist/op/gate-name.d.ts +7 -0
  31. package/dist/op/gate-name.d.ts.map +1 -1
  32. package/dist/op/gate-summary.d.ts +2 -0
  33. package/dist/op/gate-summary.d.ts.map +1 -1
  34. package/dist/op/gate.d.ts +10 -2
  35. package/dist/op/gate.d.ts.map +1 -1
  36. package/dist/op/index.d.ts +2 -2
  37. package/dist/op/index.d.ts.map +1 -1
  38. package/dist/op/local-executor.d.ts.map +1 -1
  39. package/dist/op/local-output.d.ts +2 -2
  40. package/dist/op/local-output.d.ts.map +1 -1
  41. package/dist/op/runtime.d.ts +29 -4
  42. package/dist/op/runtime.d.ts.map +1 -1
  43. package/dist/op/runtimes/local.d.ts.map +1 -1
  44. package/dist/pr-loop.d.ts +32 -6
  45. package/dist/pr-loop.d.ts.map +1 -1
  46. package/dist/workspace/lineage-upgrade-cli.d.ts.map +1 -1
  47. package/dist/workspace/member-pipeline.d.ts +15 -1
  48. package/dist/workspace/member-pipeline.d.ts.map +1 -1
  49. package/package.json +1 -1
  50. package/src/__fixtures__/change-set/lifecycle.change-set.golden.json +1 -1
  51. package/src/__fixtures__/change-set/warden.change-set.golden.json +2 -2
  52. package/src/__fixtures__/change-set/warden.plan.json +47 -40
  53. package/src/__fixtures__/plan-summary/normalization-vectors.json +275 -2
  54. package/src/__fixtures__/pr-loop/pr-apply-applied.note.golden.md +1 -1
  55. package/src/__fixtures__/pr-loop/pr-apply-refused.note.golden.md +2 -2
  56. package/src/__fixtures__/pr-loop/pr-plan.golden.json +2 -2
  57. package/src/__fixtures__/pr-loop/pr-plan.note.golden.md +2 -2
  58. package/src/build.test.ts +1 -1
  59. package/src/build.ts +1 -1
  60. package/src/change-set.test.ts +42 -12
  61. package/src/change-set.ts +50 -11
  62. package/src/cli/commands/build.ts +33 -14
  63. package/src/cli/commands/lint.ts +34 -1
  64. package/src/cli/handlers/build-lexicon-output.test.ts +102 -0
  65. package/src/cli/handlers/build.ts +68 -0
  66. package/src/cli/handlers/fan-out.test.ts +12 -1
  67. package/src/cli/handlers/fan-out.ts +7 -3
  68. package/src/cli/handlers/operator.ts +1 -2
  69. package/src/cli/handlers/pr.ts +223 -35
  70. package/src/cli/handlers/promote.test.ts +15 -1
  71. package/src/cli/handlers/promote.ts +5 -3
  72. package/src/cli/handlers/run.test.ts +215 -1
  73. package/src/cli/handlers/run.ts +36 -15
  74. package/src/cli/main.ts +13 -1
  75. package/src/cli/mcp/never-over-mcp.test.ts +120 -0
  76. package/src/cli/mcp/op-approve-origin.test.ts +1 -1
  77. package/src/cli/mcp/op-tools.ts +14 -2
  78. package/src/cli/mcp/server.test.ts +2 -1
  79. package/src/cli/registry.ts +7 -0
  80. package/src/components/changed-files.test.ts +58 -0
  81. package/src/components/changed-files.ts +19 -4
  82. package/src/components/fan-out-record.test.ts +47 -2
  83. package/src/components/fan-out-record.ts +53 -0
  84. package/src/components/pr-pipeline.ts +96 -6
  85. package/src/components/pr-run.test.ts +311 -4
  86. package/src/components/pr-run.ts +198 -5
  87. package/src/gated-waves.test.ts +11 -0
  88. package/src/gated-waves.ts +7 -2
  89. package/src/lexicon.ts +13 -1
  90. package/src/lifecycle/gate-ledger.ts +4 -3
  91. package/src/op/gate-name.ts +17 -0
  92. package/src/op/gate-summary.test.ts +12 -0
  93. package/src/op/gate-summary.ts +3 -1
  94. package/src/op/gate.test.ts +15 -1
  95. package/src/op/gate.ts +21 -4
  96. package/src/op/index.ts +2 -2
  97. package/src/op/local-executor.test.ts +35 -0
  98. package/src/op/local-executor.ts +2 -2
  99. package/src/op/local-output.test.ts +20 -0
  100. package/src/op/local-output.ts +4 -4
  101. package/src/op/runtime.ts +31 -4
  102. package/src/op/runtimes/local.test.ts +15 -0
  103. package/src/op/runtimes/local.ts +3 -1
  104. package/src/pr-loop.test.ts +41 -1
  105. package/src/pr-loop.ts +41 -10
  106. package/src/workspace/change-set.schema.json +1 -1
  107. package/src/workspace/lineage-upgrade-cli.ts +4 -3
  108. package/src/workspace/lineage-upgrade.test.ts +40 -0
  109. package/src/workspace/member-pipeline.test.ts +28 -0
  110. package/src/workspace/member-pipeline.ts +21 -3
  111. package/src/workspace/pr-report.schema.json +14 -1
@@ -67,8 +67,11 @@ export declare function memberPipelineDir(provider: string): string | undefined;
67
67
  * The default path of a member's component pipeline for one environment,
68
68
  * relative to the repository root: `chant-<member>-<env>.yml` in the forge's
69
69
  * workflow directory, or `.gitlab/ci/chant-<member>-<env>.gitlab-ci.yml`.
70
+ * A pull-request pipeline (`kind` `"pr"`, #3465) is `chant-pr.<member>.<env>`, with the dots the apply group uses so no
71
+ * two member and environment pairs share one name, and it sits beside the
72
+ * member's deploy pipeline for the same environment.
70
73
  */
71
- export declare function memberPipelineFile(provider: string, member: string, env: string): string | undefined;
74
+ export declare function memberPipelineFile(provider: string, member: string, env: string, kind?: "components" | "pr"): string | undefined;
72
75
  /** A repository-relative path for an absolute one. */
73
76
  export declare function repoRelative(ctx: MemberContext, absolute: string): string;
74
77
  /** Read a member's generated-file record, or an empty one. */
@@ -107,6 +110,17 @@ export interface ComponentPlanInput {
107
110
  output?: string;
108
111
  params?: string[];
109
112
  paramsFile?: string;
113
+ /** `--pr-loop`, with its `--gate` and `--branch` (#3465). */
114
+ prLoop?: {
115
+ gate?: string;
116
+ branch?: string;
117
+ };
118
+ /** `--wave-gate` with its `--canary` and `--base` (#3049). */
119
+ waveGate?: {
120
+ gate: string;
121
+ canary?: string[];
122
+ base?: string;
123
+ };
110
124
  }
111
125
  /**
112
126
  * Plan a member's component pipeline: which member, which file and which
@@ -1 +1 @@
1
- {"version":3,"file":"member-pipeline.d.ts","sourceRoot":"","sources":["../../src/workspace/member-pipeline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"member-pipeline.d.ts","sourceRoot":"","sources":["../../src/workspace/member-pipeline.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intentius/chant",
3
- "version": "0.105.1",
3
+ "version": "0.106.3",
4
4
  "description": "Declarative infrastructure-as-code toolkit — TypeScript on Node.js",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://intentius.io/chant",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://intentius.io/chant/schemas/workspace/change-set/v1/change-set.schema.json",
3
3
  "contract": 1,
4
- "digest": "jcs1-sha256:5c2c5c9f6b07d3520f46f9b706bde78e47cd3424bb08adcd964c2ee8ebb6e1b4",
4
+ "digest": "jcs1-sha256:a66846faed3900cffc37cfa1c31d2735358875375b7218faa7c605502a17a529",
5
5
  "members": [
6
6
  {
7
7
  "member": "delivery",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://intentius.io/chant/schemas/workspace/change-set/v1/change-set.schema.json",
3
3
  "contract": 1,
4
- "digest": "jcs1-sha256:c3f30feb61c9337a7ddbeaa752cf1ff67ec8efefd7d705516e4bcca6840a8a50",
4
+ "digest": "jcs1-sha256:266dbcb79b7922940f2b18bae4bebda8418422d4a5539eeeed119759694bd3a3",
5
5
  "members": [
6
6
  {
7
7
  "member": "warden",
@@ -9,7 +9,7 @@
9
9
  "planner": "warden",
10
10
  "scope": "acme",
11
11
  "status": "planned",
12
- "planDigest": "jcs1-sha256:f86dc7f2ad51397bba9a6259ea7c1d98070ee1d1d2e61f11e1e753fa8c006d86",
12
+ "planDigest": "jcs1-sha256:0ae7141f1c5543916126f5162bd2ce3c9d3f240155b6b9965886280a9fc56e62",
13
13
  "holes": []
14
14
  }
15
15
  ],
@@ -1,44 +1,51 @@
1
- {
2
- "org": "acme",
3
- "entries": [
4
- {
5
- "kind": "update",
6
- "resourceType": "team",
7
- "key": "platform",
8
- "before": {
9
- "privacy": "secret",
10
- "description": "Platform team"
1
+ [
2
+ {
3
+ "org": "acme",
4
+ "entries": [
5
+ {
6
+ "kind": "delete",
7
+ "resourceType": "team",
8
+ "key": "contractors",
9
+ "before": {
10
+ "privacy": "closed",
11
+ "description": "Old contractors"
12
+ },
13
+ "verb": "membership"
11
14
  },
12
- "after": {
13
- "privacy": "closed",
14
- "description": "Platform team"
15
+ {
16
+ "kind": "update",
17
+ "resourceType": "team",
18
+ "key": "platform",
19
+ "before": {
20
+ "privacy": "secret",
21
+ "description": "Platform team"
22
+ },
23
+ "after": {
24
+ "privacy": "closed",
25
+ "description": "Platform team"
26
+ },
27
+ "fields": [
28
+ {
29
+ "field": "privacy",
30
+ "before": "secret",
31
+ "after": "closed"
32
+ }
33
+ ],
34
+ "verb": "membership"
15
35
  },
16
- "fields": [
17
- {
18
- "field": "privacy",
19
- "before": "secret",
20
- "after": "closed"
21
- }
22
- ]
23
- },
24
- {
25
- "kind": "create",
26
- "resourceType": "team",
27
- "key": "release",
28
- "after": {
29
- "privacy": "secret",
30
- "description": "Release managers"
31
- }
32
- },
33
- {
34
- "kind": "delete",
35
- "resourceType": "team",
36
- "key": "contractors",
37
- "before": {
38
- "privacy": "closed",
39
- "description": "Old contractors"
36
+ {
37
+ "kind": "create",
38
+ "resourceType": "team",
39
+ "key": "release",
40
+ "after": {
41
+ "privacy": "secret",
42
+ "description": "Release managers"
43
+ },
44
+ "verb": "membership"
40
45
  }
46
+ ],
47
+ "managedCounts": {
48
+ "team": 2
41
49
  }
42
- ],
43
- "managedCount": 2
44
- }
50
+ }
51
+ ]
@@ -5,7 +5,8 @@
5
5
  "Each repo keeps its own grouping code and runs every row in CI.",
6
6
  "rootPairs: a and b are two roots of a choudoufu live-plan-set -json document; summarize a set holding exactly the two, and they must land in one group iff group is true.",
7
7
  "instancePairs: a and b are two resource_changes of one terraform show -json plan; summarize that plan, and the two instances must land in one group iff group is true.",
8
- "Red first: a naive grouper (equal raw resource_changes) must fail every rootPairs row with group true, and a grouper that blanks every string value in before and after must falsely group exactly the rootPairs rows named in overEagerFalselyGroups."
8
+ "Red first: a naive grouper (equal raw resource_changes) must fail every rootPairs row with group true, and a grouper that blanks every string value in before and after must falsely group exactly the rootPairs rows named in overEagerFalselyGroups.",
9
+ "sensitive attribute: chant's change-set carries no value for an attribute marked in before_sensitive or after_sensitive, so two sensitive attributes group by path alone, and a sensitive attribute never groups with an unmarked one. read entry: a data source read carries no attributes, so reads of one address group whatever they returned."
9
10
  ],
10
11
  "rootPairs": [
11
12
  {
@@ -1379,6 +1380,277 @@
1379
1380
  ]
1380
1381
  }
1381
1382
  }
1383
+ },
1384
+ {
1385
+ "name": "sensitive attribute with different values",
1386
+ "group": true,
1387
+ "a": {
1388
+ "root": "roots/acme",
1389
+ "estate": "acme",
1390
+ "status": "planned",
1391
+ "plan": {
1392
+ "format_version": "1.2",
1393
+ "resource_changes": [
1394
+ {
1395
+ "address": "aws_db_instance.main",
1396
+ "change": {
1397
+ "actions": [
1398
+ "update"
1399
+ ],
1400
+ "before": {
1401
+ "password": "old-a",
1402
+ "instance_class": "db.t3.small"
1403
+ },
1404
+ "after": {
1405
+ "password": "new-a",
1406
+ "instance_class": "db.t3.small"
1407
+ },
1408
+ "before_sensitive": {
1409
+ "password": true
1410
+ },
1411
+ "after_sensitive": {
1412
+ "password": true
1413
+ }
1414
+ }
1415
+ }
1416
+ ]
1417
+ }
1418
+ },
1419
+ "b": {
1420
+ "root": "roots/globex",
1421
+ "estate": "globex",
1422
+ "status": "planned",
1423
+ "plan": {
1424
+ "format_version": "1.2",
1425
+ "resource_changes": [
1426
+ {
1427
+ "address": "aws_db_instance.main",
1428
+ "change": {
1429
+ "actions": [
1430
+ "update"
1431
+ ],
1432
+ "before": {
1433
+ "password": "old-b",
1434
+ "instance_class": "db.t3.small"
1435
+ },
1436
+ "after": {
1437
+ "password": "new-b",
1438
+ "instance_class": "db.t3.small"
1439
+ },
1440
+ "before_sensitive": {
1441
+ "password": true
1442
+ },
1443
+ "after_sensitive": {
1444
+ "password": true
1445
+ }
1446
+ }
1447
+ }
1448
+ ]
1449
+ }
1450
+ }
1451
+ },
1452
+ {
1453
+ "name": "sensitive against not sensitive on the same attribute",
1454
+ "group": false,
1455
+ "a": {
1456
+ "root": "roots/acme",
1457
+ "estate": "acme",
1458
+ "status": "planned",
1459
+ "plan": {
1460
+ "format_version": "1.2",
1461
+ "resource_changes": [
1462
+ {
1463
+ "address": "aws_db_instance.main",
1464
+ "change": {
1465
+ "actions": [
1466
+ "update"
1467
+ ],
1468
+ "before": {
1469
+ "password": "same-old",
1470
+ "instance_class": "db.t3.small"
1471
+ },
1472
+ "after": {
1473
+ "password": "same-new",
1474
+ "instance_class": "db.t3.small"
1475
+ },
1476
+ "before_sensitive": {
1477
+ "password": true
1478
+ },
1479
+ "after_sensitive": {
1480
+ "password": true
1481
+ }
1482
+ }
1483
+ }
1484
+ ]
1485
+ }
1486
+ },
1487
+ "b": {
1488
+ "root": "roots/globex",
1489
+ "estate": "globex",
1490
+ "status": "planned",
1491
+ "plan": {
1492
+ "format_version": "1.2",
1493
+ "resource_changes": [
1494
+ {
1495
+ "address": "aws_db_instance.main",
1496
+ "change": {
1497
+ "actions": [
1498
+ "update"
1499
+ ],
1500
+ "before": {
1501
+ "password": "same-old",
1502
+ "instance_class": "db.t3.small"
1503
+ },
1504
+ "after": {
1505
+ "password": "same-new",
1506
+ "instance_class": "db.t3.small"
1507
+ }
1508
+ }
1509
+ }
1510
+ ]
1511
+ }
1512
+ }
1513
+ },
1514
+ {
1515
+ "name": "read entries whose results differ",
1516
+ "group": true,
1517
+ "a": {
1518
+ "root": "roots/acme",
1519
+ "estate": "acme",
1520
+ "status": "planned",
1521
+ "plan": {
1522
+ "format_version": "1.2",
1523
+ "resource_changes": [
1524
+ {
1525
+ "address": "data.aws_ami.base",
1526
+ "change": {
1527
+ "actions": [
1528
+ "read"
1529
+ ],
1530
+ "after": {
1531
+ "id": "ami-0aaa111",
1532
+ "image_id": "ami-0aaa111",
1533
+ "owner": "amazon"
1534
+ },
1535
+ "after_unknown": {}
1536
+ }
1537
+ }
1538
+ ]
1539
+ }
1540
+ },
1541
+ "b": {
1542
+ "root": "roots/globex",
1543
+ "estate": "globex",
1544
+ "status": "planned",
1545
+ "plan": {
1546
+ "format_version": "1.2",
1547
+ "resource_changes": [
1548
+ {
1549
+ "address": "data.aws_ami.base",
1550
+ "change": {
1551
+ "actions": [
1552
+ "read"
1553
+ ],
1554
+ "after": {
1555
+ "id": "ami-0bbb222",
1556
+ "image_id": "ami-0bbb222",
1557
+ "owner": "amazon"
1558
+ },
1559
+ "after_unknown": {}
1560
+ }
1561
+ }
1562
+ ]
1563
+ }
1564
+ }
1565
+ },
1566
+ {
1567
+ "name": "a read of a different data source",
1568
+ "group": false,
1569
+ "a": {
1570
+ "root": "roots/acme",
1571
+ "estate": "acme",
1572
+ "status": "planned",
1573
+ "plan": {
1574
+ "format_version": "1.2",
1575
+ "resource_changes": [
1576
+ {
1577
+ "address": "data.aws_ami.base",
1578
+ "change": {
1579
+ "actions": [
1580
+ "read"
1581
+ ],
1582
+ "after": {
1583
+ "id": "ami-0aaa111",
1584
+ "image_id": "ami-0aaa111",
1585
+ "owner": "amazon"
1586
+ },
1587
+ "after_unknown": {}
1588
+ }
1589
+ }
1590
+ ]
1591
+ }
1592
+ },
1593
+ "b": {
1594
+ "root": "roots/globex",
1595
+ "estate": "globex",
1596
+ "status": "planned",
1597
+ "plan": {
1598
+ "format_version": "1.2",
1599
+ "resource_changes": [
1600
+ {
1601
+ "address": "data.aws_ami.other",
1602
+ "change": {
1603
+ "actions": [
1604
+ "read"
1605
+ ],
1606
+ "after": {
1607
+ "id": "ami-0aaa111",
1608
+ "image_id": "ami-0aaa111",
1609
+ "owner": "amazon"
1610
+ },
1611
+ "after_unknown": {}
1612
+ }
1613
+ }
1614
+ ]
1615
+ }
1616
+ }
1617
+ },
1618
+ {
1619
+ "name": "a read against a plan with no entry for it",
1620
+ "group": false,
1621
+ "a": {
1622
+ "root": "roots/acme",
1623
+ "estate": "acme",
1624
+ "status": "planned",
1625
+ "plan": {
1626
+ "format_version": "1.2",
1627
+ "resource_changes": [
1628
+ {
1629
+ "address": "data.aws_ami.base",
1630
+ "change": {
1631
+ "actions": [
1632
+ "read"
1633
+ ],
1634
+ "after": {
1635
+ "id": "ami-0aaa111",
1636
+ "image_id": "ami-0aaa111",
1637
+ "owner": "amazon"
1638
+ },
1639
+ "after_unknown": {}
1640
+ }
1641
+ }
1642
+ ]
1643
+ }
1644
+ },
1645
+ "b": {
1646
+ "root": "roots/globex",
1647
+ "estate": "globex",
1648
+ "status": "planned",
1649
+ "plan": {
1650
+ "format_version": "1.2",
1651
+ "resource_changes": []
1652
+ }
1653
+ }
1382
1654
  }
1383
1655
  ],
1384
1656
  "instancePairs": [
@@ -1588,6 +1860,7 @@
1588
1860
  "different policy document",
1589
1861
  "different tag value that does not carry the estate",
1590
1862
  "a tofu-estate marker naming some other estate",
1591
- "the estate name inside a longer word is not the estate"
1863
+ "the estate name inside a longer word is not the estate",
1864
+ "sensitive against not sensitive on the same attribute"
1592
1865
  ]
1593
1866
  }
@@ -10,7 +10,7 @@ Applied 2 members. Head `222222222222`, measured from `111111111111`.
10
10
 
11
11
  A member whose inputs moved applied the plan it was approved with. The next run plans it against the new values.
12
12
 
13
- Plan digest: `jcs1-sha256:46bff52773a158dffa5c895b98f8760d601a2f9dc6af7dc235dbf65496f7bf65`
13
+ Plan digest: `jcs1-sha256:7623a8677afb0bf98d77c602521a3abfa96f76cff25675d350a28c2e26a7bad0`
14
14
 
15
15
  Approved by `github:alice` for this digest.
16
16
 
@@ -3,14 +3,14 @@
3
3
 
4
4
  The apply refused. Head `222222222222`, measured from `111111111111`.
5
5
 
6
- > The plan changed after review, so nothing was applied. approved: jcs1-sha256:0000000000000000000000000000000000000000000000000000000000000063; planned now: jcs1-sha256:46bff52773a158dffa5c895b98f8760d601a2f9dc6af7dc235dbf65496f7bf65. Read the new plan, and if it is right, approve it: chant approve pr-12 pr-apply --plan jcs1-sha256:46bff52773a158dffa5c895b98f8760d601a2f9dc6af7dc235dbf65496f7bf65 --approver <you> --sign; then run the apply again.
6
+ > The plan changed after review, so nothing was applied. approved: jcs1-sha256:0000000000000000000000000000000000000000000000000000000000000063; planned now: jcs1-sha256:7623a8677afb0bf98d77c602521a3abfa96f76cff25675d350a28c2e26a7bad0. Read the new plan, and if it is right, approve it: chant approve pr-12 pr-apply --plan jcs1-sha256:7623a8677afb0bf98d77c602521a3abfa96f76cff25675d350a28c2e26a7bad0 --approver <you> --sign; then run the apply again.
7
7
 
8
8
  | Member | Component | Status | Create | Update | Replace | Delete |
9
9
  |---|---|---|---:|---:|---:|---:|
10
10
  | `a` | a | planned | 0 | 1 | 0 | 0 |
11
11
  | `app` | app | planned | 0 | 0 | 0 | 0 |
12
12
 
13
- Plan digest: `jcs1-sha256:46bff52773a158dffa5c895b98f8760d601a2f9dc6af7dc235dbf65496f7bf65`
13
+ Plan digest: `jcs1-sha256:7623a8677afb0bf98d77c602521a3abfa96f76cff25675d350a28c2e26a7bad0`
14
14
 
15
15
  An approval stands for `jcs1-sha256:0000000000000000000000000000000000000000000000000000000000000063`, not for this digest: the plan changed since it was approved.
16
16
 
@@ -8,7 +8,7 @@
8
8
  "head": "2222222222222222222222222222222222222222",
9
9
  "op": "pr-12",
10
10
  "gate": "pr-apply",
11
- "digest": "jcs1-sha256:46bff52773a158dffa5c895b98f8760d601a2f9dc6af7dc235dbf65496f7bf65",
11
+ "digest": "jcs1-sha256:7623a8677afb0bf98d77c602521a3abfa96f76cff25675d350a28c2e26a7bad0",
12
12
  "status": "planned",
13
13
  "approval": {
14
14
  "status": "pending"
@@ -60,7 +60,7 @@
60
60
  "changeSet": {
61
61
  "$schema": "https://intentius.io/chant/schemas/workspace/change-set/v1/change-set.schema.json",
62
62
  "contract": 1,
63
- "digest": "jcs1-sha256:46bff52773a158dffa5c895b98f8760d601a2f9dc6af7dc235dbf65496f7bf65",
63
+ "digest": "jcs1-sha256:7623a8677afb0bf98d77c602521a3abfa96f76cff25675d350a28c2e26a7bad0",
64
64
  "members": [
65
65
  {
66
66
  "member": "a",
@@ -8,14 +8,14 @@ Planned 2 members. Head `222222222222`, measured from `111111111111`.
8
8
  | `a` | a | planned | 0 | 1 | 0 | 0 |
9
9
  | `app` | app | planned | 0 | 0 | 0 | 0 |
10
10
 
11
- Plan digest: `jcs1-sha256:46bff52773a158dffa5c895b98f8760d601a2f9dc6af7dc235dbf65496f7bf65`
11
+ Plan digest: `jcs1-sha256:7623a8677afb0bf98d77c602521a3abfa96f76cff25675d350a28c2e26a7bad0`
12
12
 
13
13
  Not approved yet.
14
14
 
15
15
  A reviewer who approved this pull request approves the plan with:
16
16
 
17
17
  ```
18
- chant approve pr-12 pr-apply --plan jcs1-sha256:46bff52773a158dffa5c895b98f8760d601a2f9dc6af7dc235dbf65496f7bf65 --approver github:<your-login> --sign
18
+ chant approve pr-12 pr-apply --plan jcs1-sha256:7623a8677afb0bf98d77c602521a3abfa96f76cff25675d350a28c2e26a7bad0 --approver github:<your-login> --sign
19
19
  ```
20
20
 
21
21
  It applies on merge if the plan is still this one. If it moved, the apply refuses and names both digests.
package/src/build.test.ts CHANGED
@@ -253,7 +253,7 @@ export const testEntity = {
253
253
  const result = await build(testDir, []);
254
254
 
255
255
  expect(result.warnings.length).toBeGreaterThan(0);
256
- expect(result.warnings[0]).toContain('No serializer found for lexicon "unknown"');
256
+ expect(result.warnings[0]).toContain('No serializer found for lexicon "unknown". List the lexicon in chant.config.ts.');
257
257
  });
258
258
  });
259
259
 
package/src/build.ts CHANGED
@@ -897,7 +897,7 @@ async function buildFromDiscoveryResult(
897
897
  // designed shape until a lexicon materializes the receipt (#1835), not
898
898
  // a missing-serializer problem.
899
899
  } else {
900
- warnings.push(`No serializer found for lexicon "${lexiconName}"`);
900
+ warnings.push(`No serializer found for lexicon "${lexiconName}". List the lexicon in chant.config.ts.`);
901
901
  }
902
902
  }
903
903
 
@@ -10,10 +10,11 @@
10
10
  * update. The update, delete, effect and hole mappings are pinned on typed
11
11
  * entries below.
12
12
  *
13
- * `warden.plan.json` is the reconcile change set chant's own
14
- * `diffCollection` produces for a warden's teams cycle: one create, one
15
- * update and one owned delete. github-warden prints its plan as text today,
16
- * so this is the shape it would write.
13
+ * `warden.plan.json` is what github-warden's `reconcile --plan-json` wrote
14
+ * (github-warden#66) for a teams cycle in an org that declares `owned: true`:
15
+ * one create, one update and one owned delete, as a JSON array of reconcile
16
+ * change sets. It was produced by warden's own `runReconcile` and
17
+ * `teamsCycle` against a stubbed live state, not built here.
17
18
  *
18
19
  * Goldens regenerate with `UPDATE_GOLDEN=1`.
19
20
  */
@@ -24,6 +25,7 @@ import { describe, expect, test } from "vitest";
24
25
  import {
25
26
  CHANGE_SET_SCHEMA_ID,
26
27
  changeSetDigest,
28
+ changeSetDocumentDigest,
27
29
  composeChangeSet,
28
30
  lifecyclePlanPart,
29
31
  reconcilePlanPart,
@@ -140,10 +142,8 @@ describe("lifecyclePlanPart", () => {
140
142
  });
141
143
 
142
144
  describe("reconcilePlanPart", () => {
143
- test("a warden's teams cycle, as chant's reconcile primitives plan it", () => {
144
- const plan = wardenPlan();
145
- golden("warden.plan.json", plan);
146
- const part = reconcilePlanPart({ member: "warden", plan: read(DIR, "warden.plan.json") as ReconcileChangeSet });
145
+ test("a warden's teams cycle, as `reconcile --plan-json` wrote it", () => {
146
+ const part = reconcilePlanPart({ member: "warden", plan: read(DIR, "warden.plan.json") as ReconcileChangeSet[] });
147
147
  expect(part.entries.map((e) => [e.address, e.action, e.disruption ?? null])).toEqual([
148
148
  ["team.contractors", "delete", "destroy"],
149
149
  ["team.platform", "update", "unknown"],
@@ -167,11 +167,20 @@ describe("the digest", () => {
167
167
  const warden = () => reconcilePlanPart({ member: "warden", plan: wardenPlan() });
168
168
  const tofu = () => read(TF_DIR, "tofu.part.golden.json") as ChangeSetPart;
169
169
 
170
- test("is the set digest over { member, planDigest }, whatever order the parts come in", () => {
170
+ test("binds the set digest over { member, planDigest }, the members' status and holes, the entries and the side effects, whatever order the parts come in", () => {
171
171
  const doc = composeChangeSet([lifecycle(), warden(), tofu()]);
172
172
  expect(composeChangeSet([tofu(), warden(), lifecycle()])).toEqual(doc);
173
173
  const pairs = doc.members.map((m) => ({ member: m.member, planDigest: m.planDigest }));
174
- expect(doc.digest).toBe(computePlanDigest("change-set", pairs));
174
+ expect(changeSetDigest(doc.members)).toBe(computePlanDigest("change-set", pairs));
175
+ expect(doc.digest).toBe(
176
+ computePlanDigest("change-set-document", {
177
+ set: computePlanDigest("change-set", pairs),
178
+ members: doc.members.map((m) => ({ member: m.member, status: m.status, holes: m.holes })),
179
+ entries: doc.entries,
180
+ sideEffects: [],
181
+ }),
182
+ );
183
+ expect(doc.digest).toBe(changeSetDocumentDigest({ ...doc, entries: [...doc.entries].reverse() }));
175
184
  expect(doc.digest).toMatch(/^jcs1-sha256:[0-9a-f]{64}$/);
176
185
  expect(verifyChangeSetDigest(doc)).toBe(true);
177
186
  expect(verifyChangeSetDigest({ ...doc, digest: changeSetDigest(doc.members.slice(1)) })).toBe(false);
@@ -183,8 +192,29 @@ describe("the digest", () => {
183
192
  const moved = wardenPlan();
184
193
  moved.entries[0] = { ...moved.entries[0], after: { privacy: "closed", description: "Release engineers" } };
185
194
  expect(composeChangeSet([lifecycle(), reconcilePlanPart({ member: "warden", plan: moved })]).digest).not.toBe(base);
186
- // A wave is a subset of members: its digest is the same function over them.
187
- expect(changeSetDigest([lifecycle().member])).toBe(composeChangeSet([lifecycle()]).digest);
195
+ // A wave is a subset of members: its digest is the set digest over them, which the document's digest binds.
196
+ expect(changeSetDigest([lifecycle().member])).toBe(computePlanDigest("change-set", [{ member: "delivery", planDigest: lifecycle().member.planDigest }]));
197
+ expect(changeSetDigest([lifecycle().member])).not.toBe(composeChangeSet([lifecycle()]).digest);
198
+ });
199
+
200
+ test("moves when an entry, a hole, a member's status or a side effect does, so a document with edited entries does not verify (#3555)", () => {
201
+ const doc = composeChangeSet([lifecycle(), warden()]);
202
+ const [first, ...rest] = doc.entries;
203
+ const edited = [
204
+ { ...doc, entries: rest },
205
+ { ...doc, entries: [{ ...first, action: first.action === "delete" ? ("create" as const) : ("delete" as const) }, ...rest] },
206
+ { ...doc, entries: [{ ...first, attributes: [...first.attributes, { path: "planted", after: "x" }] }, ...rest] },
207
+ { ...doc, entries: [...doc.entries, { ...first, address: `${first.address}-planted` }] },
208
+ { ...doc, members: doc.members.map((m) => ({ ...m, holes: [...m.holes, { address: "planted", reason: "unobserved" }] })) },
209
+ { ...doc, members: doc.members.map((m, i) => (i === 0 ? { ...m, status: "failed" as const } : m)) },
210
+ { ...doc, sideEffects: [{ member: doc.members[0].member, address: "action.aws_lambda_invoke.planted", type: "aws_lambda_invoke" }] },
211
+ ];
212
+ expect(verifyChangeSetDigest(doc)).toBe(true);
213
+ for (const e of edited) {
214
+ expect(changeSetDocumentDigest(e)).not.toBe(doc.digest);
215
+ expect(verifyChangeSetDigest(e)).toBe(false);
216
+ }
217
+ expect(verifyChangeSetDigest({ ...doc, members: [...doc.members, doc.members[0]] })).toBe(false);
188
218
  });
189
219
 
190
220
  test("leaves provisional members out, so approving the document never covers them", () => {