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
20 changes: 11 additions & 9 deletions .github/workflows/check-release-workflow-contract.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,16 @@ export function validateReleaseWorkflow(source) {
}
if (!prepare || !binaries || !publish || !release || !canary) return failures;

for (const [name, job] of Object.entries(jobs)) {
if (job.steps.some((step) =>
/(?:packages\/web\/dist|maestro-web-dist|release-web-dist)/u.test(
JSON.stringify([executableLines(step.run), step.with]),
),
)) {
failures.push(`${name} must not require or package obsolete browser assets`);
}
}

if (!hasExactPermissions(permissions, { contents: "read" })) {
failures.push("workflow default permissions must be exactly contents: read");
}
Expand Down Expand Up @@ -604,7 +614,6 @@ export function validateReleaseWorkflow(source) {
JSON.stringify([
"set -euo pipefail",
`test "$(node -p "require('./package.json').version")" = "$RELEASE_VERSION"`,
"test -f packages/web/dist/index.html",
"npm run check:rust-only-runtime",
])
) {
Expand Down Expand Up @@ -838,7 +847,6 @@ export function validateReleaseWorkflow(source) {
);
for (const artifactName of [
"npm-tarball-${{ needs.prepare.outputs.release_tag }}",
"release-web-dist-${{ needs.prepare.outputs.release_tag }}",
]) {
if (
!publishUploads.some(
Expand All @@ -853,7 +861,7 @@ export function validateReleaseWorkflow(source) {
step.uses.startsWith("actions/download-artifact@"),
);
if (
releaseDownloads.length !== 3 ||
releaseDownloads.length !== 2 ||
!releaseDownloads.some(
(step) =>
step.with.name ===
Expand All @@ -865,12 +873,6 @@ export function validateReleaseWorkflow(source) {
step.with.pattern === "maestro-*" &&
step.with.path === "release-assets" &&
step.with["merge-multiple"] === "true",
) ||
!releaseDownloads.some(
(step) =>
step.with.name ===
"release-web-dist-${{ needs.prepare.outputs.release_tag }}" &&
step.with.path === "release-assets",
)
) {
failures.push("github-release must restore the exact immutable release artifacts");
Expand Down
56 changes: 37 additions & 19 deletions .github/workflows/check-release-workflow-contract.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ jobs:
run: |
set -euo pipefail
test "$(node -p "require('./package.json').version")" = "$RELEASE_VERSION"
test -f packages/web/dist/index.html
npm run check:rust-only-runtime
- name: Publish to npm
env:
Expand Down Expand Up @@ -238,11 +237,6 @@ jobs:
name: npm-tarball-\${{ needs.prepare.outputs.release_tag }}
overwrite: true
path: package.tgz
- uses: actions/upload-artifact@sha
with:
name: release-web-dist-\${{ needs.prepare.outputs.release_tag }}
overwrite: true
path: maestro-web-dist.tar.gz
github-release:
needs: [prepare, binaries, publish, post-publish-canary]
runs-on: \${{ vars.PUBLIC_RELEASE_RUNNER || 'ubuntu-latest' }}
Expand All @@ -262,10 +256,6 @@ jobs:
pattern: maestro-*
path: release-assets
merge-multiple: true
- uses: actions/download-artifact@sha
with:
name: release-web-dist-\${{ needs.prepare.outputs.release_tag }}
path: release-assets
- name: Verify release tag has not moved
env:
EXPECTED_RELEASE_SHA: \${{ needs.prepare.outputs.release_sha }}
Expand Down Expand Up @@ -947,8 +937,8 @@ test("rejects a non-retryable or incomplete GitHub release job", () => {
);

const missingArtifact = completeWorkflow.replace(
" name: release-web-dist-${{ needs.prepare.outputs.release_tag }}",
" name: wrong-web-artifact",
" name: npm-tarball-${{ needs.prepare.outputs.release_tag }}",
" name: wrong-npm-artifact",
);
assert.ok(
validateReleaseWorkflow(missingArtifact).some((failure) =>
Expand Down Expand Up @@ -1093,13 +1083,41 @@ test("required CI tooling lane runs the release workflow contracts", async () =>
);
});

test("versioned browser asset is present in the release source tree", async () => {
const html = await readFile(
new URL("../../packages/web/dist/index.html", import.meta.url),
"utf8",
);
assert.match(html, /<!doctype html>/iu);
});
for (const [name, step] of [
["browser input requirement", " - run: test -f packages/web/dist/index.html"],
["browser archive packaging", " - run: tar -czf maestro-web-dist.tar.gz -C packages/web/dist ."],
["browser archive signing", " - run: sha256sum maestro-web-dist.tar.gz > SHA256SUMS"],
["browser artifact upload", ` - uses: actions/upload-artifact@sha
with:
name: release-web-dist-${releaseSha}
path: maestro-web-dist.tar.gz`],
["browser artifact download", ` - uses: actions/download-artifact@sha
with:
name: release-web-dist-${releaseSha}
path: release-assets`],
]) {
test(`rejects obsolete ${name}`, () => {
assert.deepEqual(validateReleaseWorkflow(completeWorkflow), []);
const workflow = completeWorkflow.replace(
" - name: Verify release tag has not moved",
`${step}\n - name: Verify release tag has not moved`,
);
assert.ok(validateReleaseWorkflow(workflow).some((failure) =>
failure.includes("obsolete browser assets"),
));
});
}

for (const line of [
` test "$(node -p "require('./package.json').version")" = "$RELEASE_VERSION"`,
" npm run check:rust-only-runtime",
]) {
test(`rejects missing native input check: ${line.trim()}`, () => {
assert.ok(validateReleaseWorkflow(completeWorkflow.replace(line, "")).some((failure) =>
failure.includes("exact native package input checks"),
));
});
}

for (const line of [' git merge-base --is-ancestor "$release_sha" FETCH_HEAD', ' timeout 60s git fetch --no-tags origin main']) {
test(`rejects missing ancestry guard: ${line.trim()}`, () => {
Expand Down
14 changes: 0 additions & 14 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,18 +254,9 @@ jobs:
run: |
set -euo pipefail
test "$(node -p "require('./package.json').version")" = "$RELEASE_VERSION"
test -f packages/web/dist/index.html
npm run check:rust-only-runtime
- name: Materialize native npm package
run: node scripts/materialize-native-package.mjs --input-dir release-binaries
- name: Package browser assets for native installs
run: tar -czf maestro-web-dist.tar.gz -C packages/web/dist .
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
retention-days: 14
name: release-web-dist-${{ needs.prepare.outputs.release_tag }}
overwrite: true
path: maestro-web-dist.tar.gz
- id: pack
run: |
PACK_JSON=$(npm pack --json)
Expand Down Expand Up @@ -465,10 +456,6 @@ jobs:
pattern: maestro-*
path: release-assets
merge-multiple: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0
with:
name: release-web-dist-${{ needs.prepare.outputs.release_tag }}
path: release-assets
- name: Install cosign
run: |
set -euo pipefail
Expand Down Expand Up @@ -514,7 +501,6 @@ jobs:
maestro-darwin-x64
maestro-linux-arm64
maestro-linux-x64
maestro-web-dist.tar.gz
)
files+=(release-metadata.json version.json channel-manifest.json manifest.json)
files+=(runtime-passport-maestro-*.json code-device-*.json)
Expand Down
Loading