Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions .github/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,40 @@ The runbook (one-time settings, routines, the Dependabot procedure) is `docs/mai
refuses to go on unless its version is the one packed, writes `CHANGELOG.md`, commits it with the snapshot, tags,
pushes through `push-packages.sh`, and the site is redeployed. No build code runs beside the NuGet key, the App token
or the OIDC token.
- `CHANGELOG.md` and `CHANGELOG.satellites.md` open with their title, which ends with the line
`<!-- Releases are prepended below this line. -->`, every release below it. `.releaserc.json`'s `changelogTitle` is
`CHANGELOG.md`'s title, verbatim: `@semantic-release/changelog` keeps a title on top only when the file starts with
it, and otherwise writes the title, the notes, then the whole file. Change the title and the option together;
`release-train-plugin.test.mjs` holds them equal.
- `scripts/release-train-plugin.mjs`, with `scripts/release-train.releaserc.json`, releases one train through
semantic-release (ADR 0012), from the plan the pack job computes with `next-version.mjs --trains`: its `trains_json`,
in a file `RELEASE_TRAIN_PLAN` names, the train in `RELEASE_TRAIN`, this run's earlier release commits in
`RELEASE_RUN_COMMITS` and the version packed in `PACKED_VERSION` (its header lists them, each required, an empty one
included). The lists travel in the file, never in a variable: every process semantic-release starts inherits its
environment, and Linux refuses to start one with a variable above 128 KiB, about 3,200 hashes. It fails when
semantic-release counts from another tag than the plan's, sees a commit the plan did not walk, its earlier release
commits aside, or does not see one the plan walked, a `[skip release]` one aside. The type is the plan's
(`release-trains.mjs releaseType`, the line rule included), the version must be the one packed, a dry run included,
and the train's changelog is written below its marker, refused with a release above it. The notes list the train's
commits, `[skip release]` ones included (the row's `notes`), the core's with the commits no train owns, their breaking
marks made plain since they ship in no package, and any other train's end with the core it is packed against. The
configuration's analyzer rules, the slot aside, and notes options are `.releaserc.json`'s, which the test holds; its
release commit is `chore(release): <tag> [skip ci]`, and its GitHub Release names the train's bundle
(`AdCodicem.ValueObjects.satellites.<version>.sigstore.json` for the satellites). A caller's options take precedence
over the file's (`tagFormat`, `dryRun`). No workflow runs it yet: `release.yml` will, once it releases each train on
its own.
- `scripts/release-train-plugin.test.mjs` replays it in throwaway repositories (two trains at `0.2.1`, this repository's
scripts, configurations and changelogs, its `node_modules` linked, a bare remote), the plan computed there by
`next-version.mjs --trains`: a dry run per train of each history of its table (the analyzer's options, a train's own
breaking notes, `[skip release]` commits, and breaking commits no train owns, one of them reverted, among them), a
merge, a range of 3,300 commits, each refusal, and a real run of the core then the satellites. semantic-release runs
in a child process, since it hooks its process's standard streams, which carry `node:test`'s reports, with the
configuration minus its GitHub entry and an environment holding the plan alone, no CI variable. It also renders both
configurations' GitHub Release bodies.
- GitHub refuses a release body above 125,000 characters, and a train's notes grow with its range (about 160 characters
a commit, the core's carrying every commit no train owns): both configurations' `releaseBodyTemplate` cut the notes at
the last line below 100,000 characters and link the train's changelog at the tag, which holds them whole, as the
release commit's message does.
- `scripts/docs-snapshot.sh` takes the one version, or each train's (`core=X satellites=Y`: every train of
`src/release-trains.json` once, each stable). The line it freezes is the core's, and the entry it writes in
`website/released-versions.json` is that version or `{"core": X, "satellites": Y}`. `scripts/docs-snapshot.test.mjs`
Expand Down
22 changes: 13 additions & 9 deletions .github/scripts/ci-plan.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@
#
# A push to main runs everything. A pull request runs what the files its merge commit changes against the base branch
# (its first parent, hence a checkout depth of 2) select, each file by the first rule below that matches it:
# none CLAUDE.md files, docs/ but docs/docfx/, Markdown outside src/, website/ and skills/ but README.md,
# the repository's own settings and templates, and the workflows and scripts ci.yml never runs, each
# by name, so that a new one runs everything until it is given a rule; only lint.yml checks them.
# none CLAUDE.md files, docs/ but docs/docfx/, Markdown outside src/, website/ and skills/ but README.md
# and the changelogs (CHANGELOG.md, CHANGELOG.<train>.md), the repository's own settings and
# templates, and the workflows and scripts ci.yml never runs, each by name, so that a new one runs
# everything until it is given a rule; only lint.yml checks them.
# documentation README.md, the package READMEs (src/PackageReadme.md, src/AdCodicem.ValueObjects/README.md),
# website/docs/ and skills/value-objects/: the generator suite's tests that read them,
# [Trait("Reads", "Documentation")]. The package READMEs are also packed, website/docs/ also built
Expand All @@ -51,11 +52,12 @@
# and whose inputs release-trains.test.mjs holds to the trains each path releases, that test,
# docs-snapshot.sh, which docs-snapshot.test.mjs runs up to the entry it writes in
# website/released-versions.json, that test, release-pack.sh, whose checks of what nuget.org holds
# release-pack.test.mjs runs up to the pack, that test, and the weekly list of held-back majors
# (held-back-majors.mjs, its test, and dependabot.yml, whose shape its reader holds): the job
# "release tooling", which runs every .github/scripts/*.test.mjs. A project file under src/ runs it
# too, beside its graph lane: release-trains.mjs compile-items holds what MSBuild compiles to the
# trains.
# release-pack.test.mjs runs up to the pack, that test, the release-train plugin, its configuration,
# and the changelogs, whose shape and title release-train-plugin.test.mjs holds, that test, and the
# weekly list of held-back majors (held-back-majors.mjs, its test, and dependabot.yml, whose shape its
# reader holds): the job "release tooling", which runs every .github/scripts/*.test.mjs. A project
# file under src/ runs it too, beside its graph lane: release-trains.mjs compile-items holds what
# MSBuild compiles to the trains.
# pack icon.png, .github/shipped-dependencies and the scripts that check the packed set.
# graph src/, the unit suite's projects and tests/UnitTests.props, which they import, tests/Fixtures/ but
# the Functions fixture, and the .NET code of the rules below: the projects dotnet-affected
Expand Down Expand Up @@ -270,6 +272,7 @@ rule() {
case "$1" in
CLAUDE.md | */CLAUDE.md) echo none; return ;;
README.md) echo documentation; return ;;
CHANGELOG.md | CHANGELOG.*.md) echo release; return ;;
src/* | website/* | skills/*) ;;
*.md) echo none; return ;;
esac
Expand Down Expand Up @@ -297,7 +300,8 @@ rule() {
.github/scripts/semver.jq | .github/scripts/preview-gate.test.mjs | .github/scripts/held-back-majors.mjs | \
.github/scripts/held-back-majors.test.mjs | .github/scripts/docs-snapshot.sh | \
.github/scripts/docs-snapshot.test.mjs | .github/scripts/release-pack.sh | .github/scripts/release-pack.test.mjs | \
.github/dependabot.yml) echo release ;;
.github/scripts/release-train-plugin.mjs | .github/scripts/release-train.releaserc.json | \
.github/scripts/release-train-plugin.test.mjs | .github/dependabot.yml) echo release ;;
.github/shipped-dependencies | .github/scripts/verify-packages.sh | .github/scripts/package-ids.sh | \
.github/scripts/shipped-dependencies-check.sh) echo pack ;;
.github/scripts/dependabot-commit-type.sh | .github/scripts/package-assemblies.sh | .github/scripts/push-packages.sh) \
Expand Down
8 changes: 6 additions & 2 deletions .github/scripts/next-version.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@
// <train>_core_version, <train>_previous_core_version and <train>_line_moved;
// trains, preview_versions, release_types, next_versions and last_versions
// ("core=... satellites=..."); trains_json, all of it with the commits' hashes, on one
// line. One line per train goes to stderr, and a table to $GITHUB_STEP_SUMMARY when set.
// line, each train's notes beside its commits: the commits of its range attributed to it,
// [skip release] ones included, which the analyzer never reads but the release notes list,
// and the core's unowned, the commits of its range no train owns. One line per train goes
// to stderr, and a table to $GITHUB_STEP_SUMMARY when set.
import { createRequire } from "node:module";
import { execFileSync } from "node:child_process";
import { appendFileSync } from "node:fs";
Expand Down Expand Up @@ -171,6 +174,7 @@ async function trainOutput() {
const row = {
train, tag: from.tag, last: from.version, type, next, height: count, preview: `${next}-preview.${count}`,
commits: own.map(({ hash }) => hash), range: attributed.map(({ hash }) => hash),
notes: attributed.filter((commit) => commit.trains.has(train)).map(({ hash }) => hash),
};
if (train === "core") {
row.unowned = attributed.filter((commit) => commit.trains.size === 0).map(({ hash }) => hash);
Expand Down Expand Up @@ -211,7 +215,7 @@ async function trainOutput() {
keyed.next_versions = each((row) => row.next);
keyed.last_versions = each((row) => row.last);
keyed.trains_json = JSON.stringify(rows.map((row) => ({
...row, type: row.type ?? "", commits: row.commits.join(" "), range: row.range.join(" "),
...row, type: row.type ?? "", commits: row.commits.join(" "), range: row.range.join(" "), notes: row.notes.join(" "),
...(row.unowned ? { unowned: row.unowned.join(" ") } : {}),
})));

Expand Down
Loading
Loading