Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
20 changes: 19 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ jobs:
java-version: '17'
cache: maven

- name: Set up Node for the showcase site scripts
uses: actions/setup-node@v7
with:
node-version: '20'

- name: Showcase site script tests
# web/ is published exactly as committed — its generated pages are built into
# it by scripts/site/build.mjs and committed from there — and web/** matches none
# of the path filters below, so the viewer's address and navigation logic, and the
# check that the committed pages are the ones web-src/ builds, are tested here, in
# the job every pull request runs. Every harness in the folder runs: naming one
# file is how a second suite comes to sit there never running.
run: |
for suite in scripts/site/*.test.mjs; do
echo "--- $suite"
node "$suite"
done

- name: Install graph-compose-fonts (resolved at test scope by core)
# The guards run scoped to core alone, and without `-am` core's test-scope
# assets come from Maven Central. Both carry their own version lines, so the
Expand All @@ -66,7 +84,7 @@ jobs:
# in qa) run in build-and-test below, which now also covers docs-only PRs.
run: |
./mvnw -B -ntp clean \
"-Dtest=EnginePdfBoundaryTest,DocumentationCoverageTest,CanonicalSurfaceGuardTest,PackageMapGuardTest,VersionConsistencyGuardTest,CiGuardListGuardTest,CiGateCoverageGuardTest,BinaryCompatibilityGateGuardTest,CodeQlScopeGuardTest,AgentsGuideGuardTest,BenchmarkDependencyInstallGuardTest" \
"-Dtest=EnginePdfBoundaryTest,DocumentationCoverageTest,CanonicalSurfaceGuardTest,PackageMapGuardTest,VersionConsistencyGuardTest,ShowcaseSiteGuardTest,CiGuardListGuardTest,CiGateCoverageGuardTest,BinaryCompatibilityGateGuardTest,CodeQlScopeGuardTest,AgentsGuideGuardTest,BenchmarkDependencyInstallGuardTest" \
test -pl :graph-compose-core

changes:
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/deploy-web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ name: Deploy web showcase to GitHub Pages

# Publishes the static showcase site under `web/` to GitHub Pages via
# GitHub Actions. The site is plain HTML / CSS / JS + generated showcase
# assets — no build step. It replaced the old "deploy from branch /docs"
# assets. This workflow runs no build: the pages — index.html, sitemap.xml and a
# page per document — are generated into web/ by scripts/site/build.mjs and
# committed from there, and everything
# here is uploaded as committed. It replaced the old "deploy from branch /docs"
# setup when the showcase moved out of docs/ into web/ so that docs/ holds
# documentation only.
#
Expand Down
158 changes: 158 additions & 0 deletions .github/workflows/release-script-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ on:
- '.github/workflows/release-script-check.yml'
# Carries the japicmp previous-release pin the script moves.
- 'templates/pom.xml'
# The published page is generated, so the cut bumps web-src/data/release.json and runs
# the site build. A change to either is a change to the release path.
- 'scripts/site/**'
- 'web-src/**'
pull_request:
paths:
- 'scripts/cut-release.ps1'
Expand All @@ -27,6 +31,8 @@ on:
- '.github/workflows/release.yml'
- '.github/workflows/release-script-check.yml'
- 'templates/pom.xml'
- 'scripts/site/**'
- 'web-src/**'

permissions:
contents: read
Expand Down Expand Up @@ -363,6 +369,158 @@ jobs:

Write-Host "README install snippets: every train coordinate moves, companions stay, idempotent; real README $checked coordinates checked."

- name: Unit-check the site release-data bump
shell: pwsh
run: |
# web/index.html is generated from web-src/, so the cut no longer rewrites the page —
# it moves the two values the build injects into the seven version spots, and the
# site is rebuilt from them. Three failures this covers: a value left behind, a
# non-version field dragged along by a sloppy pattern, and a spot that quietly stops
# matching — renamed or reformatted — after which the function used to report success
# while publishing the previous version. Lifted by AST like the checks around it, so
# the code under test is the code that ships.
$path = (Resolve-Path scripts/cut-release.ps1).Path
$ast = [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$null, [ref]$null)
$fn = $ast.FindAll({
param($n)
$n -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $n.Name -eq 'Update-SiteReleaseData'
}, $true) | Select-Object -First 1
if (-not $fn) { throw 'cut-release.ps1 no longer defines Update-SiteReleaseData' }
$calls = $ast.FindAll({
param($n)
$n -is [System.Management.Automation.Language.CommandAst] -and $n.GetCommandName() -eq 'Update-SiteReleaseData'
}, $true)
if ($calls.Count -eq 0) { throw 'nothing in cut-release.ps1 calls Update-SiteReleaseData' }
Invoke-Expression $fn.Extent.Text
function Note($m) { Write-Host " $m" }
$DryRun = $false

# One literal with placeholders, not a multi-line array of concatenations. The version
# has to land *inside* the JSON string, and a builder whose pieces can come apart
# writes "stableVersion": "<newline>1.2.3<newline>" — on which the pattern correctly
# finds nothing and this step fails for its own fixture instead of for the code.
function New-ReleaseData([string] $version) {
$file = Join-Path ([IO.Path]::GetTempPath()) ("release-" + [guid]::NewGuid().ToString() + ".json")
$json = '{~ "stableVersion": "VER",~ "releaseTag": "vVER",~ "javaMinimum": "17"~}'
Set-Content -Path $file -NoNewline -Value ($json.Replace('VER', $version).Replace('~', [string][char]10))
return $file
}

# 1. Both values move, and the bare/prefixed split is kept: the Maven Central
# coordinates the build writes take the bare semver, the badge and tag the v-form.
$data = New-ReleaseData '1.2.3'
Update-SiteReleaseData $data '9.9.9'
$after = Get-Content $data -Raw
if ($after -match '1\.2\.3') { throw "a 1.2.3 version survived the bump:`n$after" }
if ($after -notmatch '"stableVersion": "9\.9\.9"') { throw "stableVersion did not move:`n$after" }
if ($after -notmatch '"releaseTag": "v9\.9\.9"') { throw "the release tag kept no v-prefix:`n$after" }
# javaMinimum is not a release version and must not be dragged along — the failure a
# pattern loose enough to match any quoted value would produce.
if ($after -notmatch '"javaMinimum": "17"') { throw "the bump moved a field that is not the version:`n$after" }

# 2. Idempotent: a second bump is a no-op, not a throw.
Update-SiteReleaseData $data '9.9.9'
if ((Get-Content $data -Raw) -ne $after) { throw 'a second bump changed the file' }

# 3. A spot that no longer matches stops the cut, and names itself.
$moved = (Get-Content $data -Raw) -replace '"stableVersion"', '"stable_version"'
$broken = Join-Path ([IO.Path]::GetTempPath()) ("release-broken-" + [guid]::NewGuid().ToString() + ".json")
Set-Content -Path $broken -NoNewline -Value $moved
$refusal = try { Update-SiteReleaseData $broken '8.8.8'; '' } catch { "$_" }
if ($refusal -notmatch 'stableVersion') {
throw "a vanished spot did not stop the cut by name, got: '$refusal'"
}
if ((Get-Content $broken -Raw) -match '8\.8\.8') { throw 'the refusal still rewrote the file' }

Write-Host 'site release data: both values move, non-version fields stay, idempotent, and a vanished spot refuses.'

- name: The published page is rebuilt from the bumped data, not edited
shell: pwsh
run: |
# The half the unit check above cannot see: moving the data is only half a release,
# because what a visitor and a crawler read is the generated page. Run the real build
# against a bumped copy of the release data and assert the version reaches every spot
# VersionConsistencyGuardTest holds — then restore, so the job leaves no diff.
$data = 'web-src/data/release.json'
$backup = Get-Content $data -Raw
try {
Set-Content -Path $data -NoNewline -Value ($backup -replace '"stableVersion": "[^"]+"', '"stableVersion": "9.9.9"' -replace '"releaseTag": "[^"]+"', '"releaseTag": "v9.9.9"')
node scripts/site/build.mjs
if ($LASTEXITCODE -ne 0) { throw 'the site build failed on bumped release data' }
$page = Get-Content web/index.html -Raw
foreach ($shape in @(
'"stableVersion": "9\.9\.9"',
'"releaseTag": "v9\.9\.9"',
'"softwareVersion": "9\.9\.9"',
'graph-compose/9\.9\.9',
'v9\.9\.9 &middot; MIT',
'&lt;version&gt;9\.9\.9&lt;/version&gt;',
"io\.github\.demchaav:graph-compose:9\.9\.9")) {
if ($page -notmatch $shape) { throw "the rebuilt page does not carry /$shape/" }
}
Write-Host 'the bumped data reaches all seven version spots of the generated page.'
} finally {
# Restore the input AND rebuild from it. The try block wrote a 9.9.9 page, so checking
# here without rebuilding compares that page against a 2.4.0 build and fails every
# run — for this step's own fixture rather than for the code under test.
Set-Content -Path $data -NoNewline -Value $backup
node scripts/site/build.mjs
if ($LASTEXITCODE -ne 0) { throw 'the site build failed while restoring the tree' }
node scripts/site/build.mjs --check
if ($LASTEXITCODE -ne 0) { throw 'the tree was not restored after the rebuild check' }
}

- name: The cut stages every document page a rebuild changes, and nothing under showcase
shell: pwsh
run: |
# The release commit carries the document pages through one glob pathspec. A pathspec one
# level off would tag a release without the pages its rebuild wrote, and one reaching
# web/showcase/ would commit the /ID churn the cut restores; Step 5's guards read the
# working tree, so neither would stop the cut. Lifted by AST, then run against this
# checkout: a page rewritten, a page deleted, a page added and a catalogue file touched —
# `git add --dry-run` has to name the three pages and nothing else.
$path = (Resolve-Path scripts/cut-release.ps1).Path
$ast = [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$null, [ref]$null)
$fn = $ast.FindAll({
param($n)
$n -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $n.Name -eq 'Get-DocumentPagePathspecs'
}, $true) | Select-Object -First 1
if (-not $fn) { throw 'cut-release.ps1 no longer defines Get-DocumentPagePathspecs' }
$calls = $ast.FindAll({
param($n)
$n -is [System.Management.Automation.Language.CommandAst] -and $n.GetCommandName() -eq 'Get-DocumentPagePathspecs'
}, $true)
if ($calls.Count -lt 2) {
throw "the release commit and the post-release commit both stage the pages; found $($calls.Count) call(s)"
}
Invoke-Expression $fn.Extent.Text
$repoRoot = (Get-Location).Path

$pages = @(git ls-files -- ':(glob)web/*/*/*/index.html')
if ($pages.Count -lt 2) { throw "expected committed document pages under web/, found $($pages.Count)" }
$rewritten = $pages[0]
$deleted = $pages[1]
$added = 'web/probe-category/probe-family/probe-card/index.html'
$catalogueFile = @(git ls-files -- web/showcase)[0]
try {
Add-Content -Path $rewritten -Value '<!-- rewritten -->'
Remove-Item $deleted
New-Item -ItemType Directory -Force -Path (Split-Path $added) | Out-Null
Set-Content -Path $added -Value '<!-- added -->'
Add-Content -Path $catalogueFile -Value 'touched'
$staged = @(git add --dry-run -- @(Get-DocumentPagePathspecs)) | Sort-Object
if ($LASTEXITCODE -ne 0) { throw 'git add --dry-run refused the page pathspec' }
$expected = @("add '$added'", "add '$rewritten'", "remove '$deleted'") | Sort-Object
if (($staged -join "`n") -ne ($expected -join "`n")) {
throw "the page pathspec stages:`n$($staged -join "`n")`nwhere a rebuild changed:`n$($expected -join "`n")"
}
} finally {
git checkout -- $rewritten $deleted $catalogueFile
Remove-Item -Recurse -Force web/probe-category -ErrorAction SilentlyContinue
}
if (git status --porcelain -- web) { throw 'the probe left the web/ tree changed' }
Write-Host 'page staging: a rewritten, a deleted and an added page are staged, and nothing under web/showcase/.'

- name: Unit-check the roadmap promotion (refusals and the real rewrite)
shell: pwsh
run: |
Expand Down
Loading
Loading