{download.platform}
{download.detail}
-{download.note}
+ {download.trust} · {releaseSize(download.size)}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f7cd1c..eb8ee8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -93,7 +93,9 @@ jobs: MDBASE_TS_DIR: .sources/mdbase - name: Type and content checks - run: pnpm test:deploy:dev && pnpm check + run: pnpm test:deploy:dev && pnpm check:release && pnpm check + env: + GH_TOKEN: ${{ github.token }} - name: Build website run: pnpm build diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 66cf7b7..bb486bb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -100,7 +100,9 @@ jobs: MDBASE_TS_DIR: .sources/mdbase - name: Check and build website - run: pnpm check && pnpm build + run: pnpm check:release && pnpm check && pnpm build + env: + GH_TOKEN: ${{ github.token }} - name: Import specification run: pnpm import:spec diff --git a/package.json b/package.json index 71d3082..108e4f8 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,9 @@ "sync:contracts": "node scripts/sync-contracts.mjs", "import:spec": "node scripts/import-spec.mjs", "check:links": "node scripts/check-links.mjs", + "check:release": "node scripts/check-connect-release.mjs", "test:deploy:dev": "node --test scripts/deploy-pages-dev.test.mjs", - "test": "pnpm test:deploy:dev && pnpm check && pnpm build && pnpm import:spec && pnpm check:links" + "test": "pnpm test:deploy:dev && pnpm check:release && pnpm check && pnpm build && pnpm import:spec && pnpm check:links" }, "dependencies": { "@astrojs/sitemap": "^3.7.3", diff --git a/scripts/check-connect-release.mjs b/scripts/check-connect-release.mjs new file mode 100644 index 0000000..511a872 --- /dev/null +++ b/scripts/check-connect-release.mjs @@ -0,0 +1,75 @@ +import assert from "node:assert/strict"; +import { execFileSync } from "node:child_process"; +import { readFile } from "node:fs/promises"; + +const release = JSON.parse(await readFile(new URL("../src/data/connect-release.json", import.meta.url))); +const sources = JSON.parse(await readFile(new URL("../site-sources.json", import.meta.url))); + +assert.equal(release.tag, `v${release.version}`, "release tag must match version"); +assert.equal(release.sdkVersion, release.version, "public SDK and Connect release versions must match"); +assert.equal(sources.connect.ref, release.sourceRevision, "published docs must use the released Connect source"); +assert.match(release.releaseUrl, new RegExp(`/releases/tag/${release.tag}$`)); +assert.match(release.channelManifestUrl, new RegExp(`/releases/download/${release.tag}/mdbase-connect-channel-v1\\.json$`)); + +const artifacts = [...release.desktop, ...release.cli]; +assert.equal(new Set(artifacts.map(({ file }) => file)).size, artifacts.length, "artifact names must be unique"); +for (const artifact of artifacts) { + assert.ok(artifact.file.includes(release.version), `${artifact.file} must contain the selected version`); + assert.ok(Number.isSafeInteger(artifact.size) && artifact.size > 0, `${artifact.file} must have a bounded size`); +} +assert.deepEqual( + release.desktop.map(({ platform, detail }) => `${platform}:${detail}`), + [ + "macOS:Apple Silicon", + "macOS:Intel", + "Windows:64-bit", + "Linux:64-bit Debian or Ubuntu", + "Linux:64-bit Fedora, RHEL, or compatible" + ], + "desktop platform matrix changed without updating the release record" +); + +const github = JSON.parse(execFileSync("gh", [ + "api", + `repos/mdbase-dev/mdbase-connect/releases?per_page=100`, + "--jq", + `.[] | select(.tag_name == "${release.tag}")` +], { encoding: "utf8" })); +assert.ok(github, "selected GitHub release must exist"); +assert.equal(github.draft, false, "selected GitHub release must be published"); +const channelAsset = github.assets.find((asset) => asset.name === "mdbase-connect-channel-v1.json"); +assert.ok(channelAsset, "selected signed channel manifest must exist"); +const channel = JSON.parse(execFileSync("gh", [ + "api", + "-H", + "Accept: application/octet-stream", + `repos/mdbase-dev/mdbase-connect/releases/assets/${channelAsset.id}` +], { encoding: "utf8" })); +const sdkResponse = await fetch(`https://registry.npmjs.org/@mdbase-dev%2fconnect/${release.sdkVersion}`, { + headers: { accept: "application/json" } +}); +assert.equal(sdkResponse.status, 200, "selected SDK package must exist"); +assert.equal(channel.version, release.version, "channel manifest version differs from website"); +assert.equal(channel.tag, release.tag, "channel manifest tag differs from website"); +assert.equal(channel.release_url, release.releaseUrl, "channel manifest release link differs from website"); + +const publishedAssets = new Map(github.assets.map((asset) => [asset.name, asset.size])); +for (const artifact of artifacts) { + assert.equal(publishedAssets.get(artifact.file), artifact.size, `${artifact.file} differs from the published release`); +} + +const sdk = await sdkResponse.json(); +assert.equal(sdk.version, release.sdkVersion, "registry returned a different SDK version"); +const tagRef = JSON.parse(execFileSync("gh", [ + "api", + `repos/mdbase-dev/mdbase-connect/git/ref/tags/${release.tag}` +], { encoding: "utf8" })); +const tagTarget = tagRef.object.type === "tag" + ? JSON.parse(execFileSync("gh", [ + "api", + `repos/mdbase-dev/mdbase-connect/git/tags/${tagRef.object.sha}` + ], { encoding: "utf8" })).object + : tagRef.object; +assert.equal(tagTarget.type, "commit", "release tag must resolve to a commit"); +assert.equal(tagTarget.sha, release.sourceRevision, "released documentation source differs from the tag"); +console.log(`Connect release record verified: ${release.version}, ${artifacts.length} artifacts, SDK ${release.sdkVersion}.`); diff --git a/site-sources.json b/site-sources.json index 9ea8832..05e2ec8 100644 --- a/site-sources.json +++ b/site-sources.json @@ -5,7 +5,7 @@ }, "connect": { "repository": "mdbase-dev/mdbase-connect", - "ref": "673e9e1ddab2a2a50a7cdb1506fd2a15ac4b61ef" + "ref": "23a52aeeaa4ddb199c28d2cd1cf3d4fe51720188" }, "contracts": { "repository": "mdbase-dev/mdbase-contracts", diff --git a/src/components/DocsLayout.astro b/src/components/DocsLayout.astro index b334452..feb99a4 100644 --- a/src/components/DocsLayout.astro +++ b/src/components/DocsLayout.astro @@ -1,5 +1,6 @@ --- import BaseLayout from "../layouts/BaseLayout.astro"; +import release from "../data/connect-release.json"; interface Props { title: string; @@ -66,7 +67,7 @@ const docGroups = [
{download.detail}
-{download.note}
+ {download.trust} · {releaseSize(download.size)}