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
4 changes: 2 additions & 2 deletions .github/workflows/live-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ jobs:
vars.LIVE_SMOKE_ENABLED == 'true' &&
github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
# Required repository configuration: protect this environment, its reviewers,
# COMETAPI_KEY secret, and the optional model variable.
# Required repository configuration: protect this environment without required
# reviewers, add COMETAPI_KEY, and optionally set the model variable.
environment: live-smoke
timeout-minutes: 5
steps:
Expand Down
46 changes: 6 additions & 40 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@ jobs:
contents: read
id-token: write
steps:
- name: Check out the verified release commit
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
ref: ${{ needs.verify.outputs.release-commit }}
- name: Set up Node.js 24
uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6.5.0
with:
Expand All @@ -213,46 +218,7 @@ jobs:
DIST_TAG: ${{ needs.verify.outputs.dist-tag }}
NODE_AUTH_TOKEN: ${{ vars.NPM_ALPHA1_BOOTSTRAP_ENABLED == 'true' && needs.verify.outputs.version == '0.1.0-alpha.1' && secrets.NPM_ALPHA1_BOOTSTRAP_TOKEN || '' }}
VERSION: ${{ needs.verify.outputs.version }}
shell: bash
run: |
set -euo pipefail
if [[ "$ALPHA1_BOOTSTRAP_ENABLED" == "true" && \
( "$VERSION" != "0.1.0-alpha.1" || "$DIST_TAG" != "next" ) ]]; then
echo "The token bootstrap is restricted to cometapi@0.1.0-alpha.1 on the next dist-tag." >&2
exit 1
fi

mapfile -t tarballs < <(find release-artifacts -maxdepth 1 -type f -name '*.tgz' -print)
if [[ "${#tarballs[@]}" -ne 1 ]]; then
echo "Expected exactly one downloaded artifact, found ${#tarballs[@]}." >&2
exit 1
fi
local_integrity="$(node -e 'const {createHash}=require("node:crypto");const {readFileSync}=require("node:fs");process.stdout.write("sha512-"+createHash("sha512").update(readFileSync(process.argv[1])).digest("base64"))' "${tarballs[0]}")"

view_error="$(mktemp)"
set +e
existing_dist="$(npm view "cometapi@${VERSION}" dist --json 2>"$view_error")"
view_status=$?
set -e
if [[ "$view_status" -eq 0 && -n "$existing_dist" ]]; then
EXISTING_DIST="$existing_dist" LOCAL_INTEGRITY="$local_integrity" node <<'EOF'
const dist = JSON.parse(process.env.EXISTING_DIST);
if (dist.integrity !== process.env.LOCAL_INTEGRITY) {
throw new Error("The existing registry version has different integrity.");
}
EOF
echo "cometapi@${VERSION} already matches the verified artifact; resuming checks."
elif grep -q "E404" "$view_error"; then
if [[ "$ALPHA1_BOOTSTRAP_ENABLED" == "true" && -z "$NODE_AUTH_TOKEN" ]]; then
echo "NPM_ALPHA1_BOOTSTRAP_TOKEN is required when the alpha.1 bootstrap is enabled." >&2
exit 1
fi
npm publish "${tarballs[0]}" --access public --provenance --tag "$DIST_TAG"
else
echo "Unable to determine whether cometapi@${VERSION} already exists." >&2
sed -n '1,20p' "$view_error" >&2
exit 1
fi
run: bash scripts/publish-artifact.sh
- name: Verify the public registry artifact
env:
DIST_TAG: ${{ needs.verify.outputs.dist-tag }}
Expand Down
11 changes: 9 additions & 2 deletions RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ artifacts. Authorized maintainers must supply or approve:

- Changes to the canonical identity and contact values listed above
- Repository creation and visibility, branch/tag protections, environments,
secrets, and required reviewers
- npm package ownership and Trusted Publisher configuration
secrets, and environment approval policies
- npm package ownership for the maintainer-confirmed `cometapi-team` account
and Trusted Publisher configuration
- A `COMETAPI_KEY`, request budget, and explicit authorization for live smoke
tests
- Immutable public tags, GitHub releases, environment approvals, and npm
Expand All @@ -76,6 +77,12 @@ artifacts. Authorized maintainers must supply or approve:
Missing identity, credentials, ownership, or authorization blocks the
corresponding release gate. Do not invent it or replace it with a mock.

The `cometapi` package does not exist in the public registry yet, so npm cannot
verify its owner before the first publication. Registry Alpha owner evidence is
complete only when `npm owner ls cometapi` lists the maintainer-confirmed
`cometapi-team` account after bootstrap; until then this remains a Registry
Alpha prerequisite, not a Public Preview blocker.

For the current milestone, authorized external actions stop at creating the
empty private repository, pushing its sanitized first history, and observing
credential-free CI. Visibility changes and every subsequent external action
Expand Down
118 changes: 67 additions & 51 deletions scripts/check-public-preview.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";

import { ROOT } from "./lib.mjs";
import {
Expand All @@ -8,64 +9,79 @@ import {
} from "./release-validation.mjs";
import { collectStandaloneContentViolations } from "./standalone-content.mjs";

const inputViolations = [];
const read = (name) => {
try {
return readFileSync(join(ROOT, name), "utf8");
} catch (error) {
const detail = error instanceof Error ? error.message : String(error);
inputViolations.push(`${name} could not be read: ${detail}`);
return undefined;
export function collectPublicPreviewGateViolations(root = ROOT) {
const inputViolations = [];
const read = (name) => {
try {
return readFileSync(join(root, name), "utf8");
} catch (error) {
const detail = error instanceof Error ? error.message : String(error);
inputViolations.push(`${name} could not be read: ${detail}`);
return undefined;
}
};

let sourceManifest;
const packageText = read("package.json");
if (packageText !== undefined) {
try {
sourceManifest = JSON.parse(packageText);
} catch (error) {
const detail = error instanceof Error ? error.message : String(error);
inputViolations.push(`package.json could not be parsed: ${detail}`);
}
}
};

let sourceManifest;
const packageText = read("package.json");
if (packageText !== undefined) {
const contentViolations = collectPublicPreviewViolations({
documents: {
agents: read("AGENTS.md"),
architecture: read("ARCHITECTURE.md"),
changelog: read("CHANGELOG.md"),
compatibility: read("COMPATIBILITY.md"),
conduct: read("CODE_OF_CONDUCT.md"),
contributing: read("CONTRIBUTING.md"),
license: read("LICENSE"),
readme: read("README.md"),
releasing: read("RELEASING.md"),
roadmap: read("ROADMAP.md"),
security: read("SECURITY.md"),
support: read("SUPPORT.md"),
},
sourceManifest,
});

const standaloneContentViolations = [];
try {
sourceManifest = JSON.parse(packageText);
standaloneContentViolations.push(
...collectStandaloneContentViolations(root),
);
} catch (error) {
const detail = error instanceof Error ? error.message : String(error);
inputViolations.push(`package.json could not be parsed: ${detail}`);
standaloneContentViolations.push(
`standalone content could not be checked: ${detail}`,
);
}
}

const violations = collectPublicPreviewViolations({
documents: {
agents: read("AGENTS.md"),
architecture: read("ARCHITECTURE.md"),
changelog: read("CHANGELOG.md"),
compatibility: read("COMPATIBILITY.md"),
conduct: read("CODE_OF_CONDUCT.md"),
contributing: read("CONTRIBUTING.md"),
license: read("LICENSE"),
readme: read("README.md"),
releasing: read("RELEASING.md"),
roadmap: read("ROADMAP.md"),
security: read("SECURITY.md"),
support: read("SUPPORT.md"),
},
sourceManifest,
});
return [
...inputViolations,
...contentViolations,
...standaloneContentViolations,
];
}

const standaloneContentViolations = [];
try {
standaloneContentViolations.push(...collectStandaloneContentViolations(ROOT));
} catch (error) {
const detail = error instanceof Error ? error.message : String(error);
standaloneContentViolations.push(
`standalone content could not be checked: ${detail}`,
);
function main() {
const violations = collectPublicPreviewGateViolations();
if (violations.length > 0) {
console.error(formatPublicPreviewViolations(violations));
process.exitCode = 1;
} else {
console.log("Public Preview content gate passed.");
}
}

const allViolations = [
...inputViolations,
...violations,
...standaloneContentViolations,
];
if (allViolations.length > 0) {
console.error(formatPublicPreviewViolations(allViolations));
process.exitCode = 1;
} else {
console.log("Public Preview content gate passed.");
if (
process.argv[1] !== undefined &&
resolve(process.argv[1]) === fileURLToPath(import.meta.url)
) {
main();
}
51 changes: 51 additions & 0 deletions scripts/publish-artifact.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash

set -euo pipefail

: "${DIST_TAG:?DIST_TAG is required}"
: "${VERSION:?VERSION is required}"

artifact_directory="${ARTIFACT_DIRECTORY:-release-artifacts}"
bootstrap_enabled="${ALPHA1_BOOTSTRAP_ENABLED:-}"

if [[ "$bootstrap_enabled" == "true" && \
( "$VERSION" != "0.1.0-alpha.1" || "$DIST_TAG" != "next" ) ]]; then
echo "The token bootstrap is restricted to cometapi@0.1.0-alpha.1 on the next dist-tag." >&2
exit 1
fi

shopt -s nullglob
tarballs=("$artifact_directory"/*.tgz)
if [[ "${#tarballs[@]}" -ne 1 ]]; then
echo "Expected exactly one downloaded artifact, found ${#tarballs[@]}." >&2
exit 1
fi

local_integrity="$(node -e 'const {createHash}=require("node:crypto");const {readFileSync}=require("node:fs");process.stdout.write("sha512-"+createHash("sha512").update(readFileSync(process.argv[1])).digest("base64"))' "${tarballs[0]}")"
view_error="$(mktemp)"
trap 'rm -f "$view_error"' EXIT

set +e
existing_dist="$(npm view "cometapi@${VERSION}" dist --json 2>"$view_error")"
view_status=$?
set -e

if [[ "$view_status" -eq 0 && -n "$existing_dist" ]]; then
EXISTING_DIST="$existing_dist" LOCAL_INTEGRITY="$local_integrity" node <<'EOF'
const dist = JSON.parse(process.env.EXISTING_DIST);
if (dist.integrity !== process.env.LOCAL_INTEGRITY) {
throw new Error("The existing registry version has different integrity.");
}
EOF
echo "cometapi@${VERSION} already matches the verified artifact; resuming checks."
elif grep -q "E404" "$view_error"; then
if [[ "$bootstrap_enabled" == "true" && -z "${NODE_AUTH_TOKEN:-}" ]]; then
echo "NPM_ALPHA1_BOOTSTRAP_TOKEN is required when the alpha.1 bootstrap is enabled." >&2
exit 1
fi
npm publish "${tarballs[0]}" --access public --provenance --tag "$DIST_TAG"
else
echo "Unable to determine whether cometapi@${VERSION} already exists." >&2
sed -n '1,20p' "$view_error" >&2
exit 1
fi
113 changes: 113 additions & 0 deletions tests/publish-artifact.test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import {
chmodSync,
existsSync,
mkdirSync,
mkdtempSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { delimiter, join } from "node:path";
import { spawnSync } from "node:child_process";
import { fileURLToPath, URL } from "node:url";

import { afterEach, describe, expect, it } from "vitest";

const script = fileURLToPath(
new URL("../scripts/publish-artifact.sh", import.meta.url),
);
const temporaryDirectories = [];

afterEach(() => {
for (const directory of temporaryDirectories.splice(0)) {
rmSync(directory, { force: true, recursive: true });
}
});

function fixture() {
const root = mkdtempSync(join(tmpdir(), "cometapi-publish-test-"));
temporaryDirectories.push(root);
const artifacts = join(root, "artifacts");
const bin = join(root, "bin");
const log = join(root, "npm-call.log");
mkdirSync(artifacts);
mkdirSync(bin);
writeFileSync(join(artifacts, "cometapi.tgz"), "artifact\n");
const npm = join(bin, "npm");
writeFileSync(
npm,
[
"#!/usr/bin/env bash",
'if [[ "$1" == "view" ]]; then',
' echo "npm error code E404" >&2',
" exit 1",
"fi",
'if [[ "$1" == "publish" ]]; then',
' printf "%s\\n" "${NODE_AUTH_TOKEN:+token-present}" > "$NPM_CALL_LOG"',
' printf "%s\\n" "$*" >> "$NPM_CALL_LOG"',
" exit 0",
"fi",
'echo "unexpected npm command: $*" >&2',
"exit 2",
"",
].join("\n"),
);
chmodSync(npm, 0o755);
return { artifacts, bin, log };
}

function runPublish({
bootstrapEnabled = "",
distTag = "next",
token = "",
version = "0.1.0-alpha.1",
} = {}) {
const { artifacts, bin, log } = fixture();
const result = spawnSync("bash", [script], {
encoding: "utf8",
env: {
...process.env,
ALPHA1_BOOTSTRAP_ENABLED: bootstrapEnabled,
ARTIFACT_DIRECTORY: artifacts,
DIST_TAG: distTag,
NODE_AUTH_TOKEN: token,
NPM_CALL_LOG: log,
PATH: `${bin}${delimiter}${process.env.PATH ?? ""}`,
VERSION: version,
},
});
return {
log: existsSync(log) ? readFileSync(log, "utf8") : "",
result,
};
}

describe("publish artifact authentication", () => {
it("uses Trusted Publishing without injecting a registry token by default", () => {
const { log, result } = runPublish({ version: "0.1.0-alpha.2" });
expect(result.status, result.stderr).toBe(0);
expect(log).toMatch(/^\npublish .* --provenance --tag next\n$/);
});

it("allows the protected token bootstrap for alpha.1 on next", () => {
const { log, result } = runPublish({
bootstrapEnabled: "true",
token: "opaque",
});
expect(result.status, result.stderr).toBe(0);
expect(log).toMatch(
/^token-present\npublish .* --provenance --tag next\n$/,
);
});

it.each([
["another version", { bootstrapEnabled: "true", version: "0.1.0-alpha.2" }],
["another dist-tag", { bootstrapEnabled: "true", distTag: "latest" }],
["a missing token", { bootstrapEnabled: "true" }],
])("rejects bootstrap mode for %s", (_name, options) => {
const { log, result } = runPublish(options);
expect(result.status).not.toBe(0);
expect(log).toBe("");
});
});
Loading