Repository navigation
ci: mirror the GitHub-native release workflow from mono#9079 - #1124
Conversation
The mirror's release.yml predated dx-corp/mono#9079 (release through GitHub on owned runners); scripts/update-lifecycle-contract.test.mjs expects the workflow to publish receipt metadata via create-release-metadata.mjs, which the stale copy lacked, so maestro-ci's linux-check failed on main. Source: dx-corp/mono#9079 (products/maestro). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014mGdZdLHUANa66S3RGuHMr
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e2e74f9c0d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| - id: private-cargo | ||
| uses: ./.github/actions/configure-private-cargo-git |
There was a problem hiding this comment.
Restore the missing local Cargo action
The binaries job now invokes ./.github/actions/configure-private-cargo-git, but a repo-wide git ls-tree -r d2ab24d -- .github/actions confirms that no action manifest exists at this path. Every tag or manual release will therefore stop during local-action resolution before building any binary; mirror the action into this repository or remove the private-only dependency.
Useful? React with 👍 / 👎.
| plan: | ||
| runs-on: ${{ vars.INTERNAL_CONFIRMATION_RUNNER || 'evalops-internal-arc' }} |
There was a problem hiding this comment.
Restrict release dispatches to admitted source
When workflow_dispatch selects a non-main ref, this unguarded job proceeds and the later checkouts execute that ref; the deleted prepare job was the only code that restricted the repository/ref and verified protected-main or protected-release ancestry. Because subsequent jobs expose release secrets, OIDC, and contents: write, an unprotected branch can now provide the scripts that are built and signed. Restore the protected-source admission before any release job runs.
AGENTS.md reference: AGENTS.md:L7-L9
Useful? React with 👍 / 👎.
| - id: pack | ||
| run: | | ||
| PACK_JSON=$(npm pack --json) | ||
| echo "$PACK_JSON" | ||
| node -e 'const fs=require("node:fs"); const p=JSON.parse(fs.readFileSync("package.json","utf8")); p.name="@evalops/maestro"; fs.writeFileSync("package.json", JSON.stringify(p, null, "\t")+"\n")' | ||
| ALIAS_PACK_JSON=$(npm pack --json) | ||
| echo "$ALIAS_PACK_JSON" | ||
| git checkout -- package.json | ||
| { | ||
| echo "tarball=$(echo "$PACK_JSON" | jq -r '.[0].filename')" | ||
| echo "integrity=$(echo "$PACK_JSON" | jq -r '.[0].integrity')" | ||
| echo "alias_tarball=$(echo "$ALIAS_PACK_JSON" | jq -r '.[0].filename')" | ||
| echo "alias_integrity=$(echo "$ALIAS_PACK_JSON" | jq -r '.[0].integrity')" | ||
| } >> "$GITHUB_OUTPUT" | ||
| echo "tarball=$(echo "$PACK_JSON" | jq -r '.[0].filename')" >> "$GITHUB_OUTPUT" |
There was a problem hiding this comment.
Publish the packed package to npm
The replacement workflow only runs npm pack; there is no npm publish anywhere afterward. This breaks the existing public release flow because .github/workflows/tag-release.yml explicitly dispatches release.yml when the version is absent from npm, so even a successful run would merely attach a tarball to a GitHub release while leaving the package unpublished and eligible for repeated dispatches. Restore the registry publisher and its post-publish verification.
AGENTS.md reference: AGENTS.md:L13-L14
Useful? React with 👍 / 👎.
| exit 1 | ||
| fi | ||
| sleep 2 | ||
| identity="^https://github.com/evalops/mono/\\.github/workflows/maestro-release.yml@" |
There was a problem hiding this comment.
Verify against this workflow's certificate identity
When this active workflow runs in evalops/maestro, keyless cosign signatures are issued for the public evalops/maestro/.github/workflows/release.yml workflow, but this regex accepts only the private evalops/mono/.github/workflows/maestro-release.yml identity. Consequently the immediately following verify-blob call rejects the signatures just created and the GitHub release step is unreachable; use the executing workflow's identity or disable this mirrored file as an entrypoint.
Useful? React with 👍 / 👎.
| identity="^https://github.com/evalops/mono/\\.github/workflows/maestro-release.yml@" | ||
| verify() { | ||
| cosign verify-blob \ | ||
| --bundle "$2" \ | ||
| --certificate-identity-regexp "${identity}" \ | ||
| --certificate-oidc-issuer https://token.actions.githubusercontent.com \ | ||
| "$1" |
There was a problem hiding this comment.
🔴 Wrong identity blocks every release
Every run signs with this workflow's OIDC identity, but identity accepts only the private mono workflow. Verification rejects every bundle, so no release completes.
Learn more
A keyless cosign bundle records the GitHub workflow that requested its OIDC certificate. This file runs as evalops/maestro/.github/workflows/release.yml, while the regular expression accepts only evalops/mono/.github/workflows/maestro-release.yml. The immediately preceding cosign sign-blob calls therefore produce certificates that the following verification cannot accept. The workflow exits before softprops/action-gh-release on every trigger.
Example: A v0.10.87 tag starts this workflow. Cosign signs with an identity beginning https://github.com/evalops/maestro/.github/workflows/release.yml@, but verification requires the evalops/mono prefix and fails.
Recommended fix: Match the identity of the workflow that actually executes this file. If both repositories legitimately execute equivalent workflows, explicitly allow both exact repository/workflow identities without broadening the expression beyond those workflows.
Was this helpful? React with 👍 or 👎 to provide feedback.
| - name: Conformance against the exact native release artifact | ||
| run: node scripts/run-runtime-conformance.mjs --binary release-binaries/maestro-linux-x64 |
There was a problem hiding this comment.
🔴 Downloaded binary cannot run
download-artifact removes maestro-linux-x64's executable mode, then conformance launches it directly. The publish job fails before packaging or release creation.
Learn more
GitHub artifact transfer does not preserve Unix file modes. The binary is executable in the matrix job, but the downloaded copy is a regular non-executable file. startRuntime passes that path directly to spawn, so the process cannot start.
Example: The build uploads maestro-linux-x64 as mode 0755. The publish job downloads it as mode 0644, and conformance receives EACCES instead of a readiness URL.
Recommended fix: Restore executable permission on downloaded binaries before any direct execution. Keeping the change before conformance also lets materialize-native-package.mjs package the same tested bytes later.
| - name: Conformance against the exact native release artifact | |
| run: node scripts/run-runtime-conformance.mjs --binary release-binaries/maestro-linux-x64 | |
| - name: Conformance against the exact native release artifact | |
| run: | | |
| chmod +x release-binaries/maestro-linux-x64 | |
| node scripts/run-runtime-conformance.mjs --binary release-binaries/maestro-linux-x64 |
Was this helpful? React with 👍 or 👎 to provide feedback.
| - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | ||
| with: | ||
| name: maestro-verified-native | ||
| path: release-binaries/* | ||
| name: maestro-${{ matrix.platform }} | ||
| path: | | ||
| dist/release/maestro-${{ matrix.platform }} | ||
| dist/release/smoked-${{ matrix.platform }}.txt | ||
| dist/release/rustc-${{ matrix.platform }}.txt |
There was a problem hiding this comment.
🔴 macOS artifacts always fail verification
A Darwin matrix leg uploads no signing or notarization markers. The coverage verifier requires both, so configured macOS releases never publish.
Learn more
The plan conditionally adds Darwin builds when a macOS runner variable is configured. Each matrix leg uploads only maestro-<platform>, smoked-<platform>.txt, and rustc-<platform>.txt. The publish job supplies the same platform list to the coverage verifier. For every Darwin platform, that verifier also requires signed-<platform>.json and notarized-<platform>.json, but no new step creates or uploads either file.
Example: Setting RELEASE_MACOS_ARM64_RUNNER=self-hosted-mac adds darwin-arm64. Its build succeeds, but publish reports darwin-arm64: no macOS Developer ID signature marker and exits.
Recommended fix: Add Developer ID signing and Apple notarization to each Darwin leg, generate the marker formats accepted by verify-release-smoke-coverage.mjs, and upload both markers. Keep Darwin platforms disabled until those steps and credentials are available.
Was this helpful? React with 👍 or 👎 to provide feedback.
| package-manager-cache: false | ||
| registry-url: https://registry.npmjs.org | ||
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.0 | ||
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd |
There was a problem hiding this comment.
🔴 Manual release uses unrelated source
A manual dispatch checks out its selected branch while REQUESTED names the release tag. Binaries and metadata can therefore disagree with the released tag.
Learn more
A manual workflow dispatch has two independent values: the selected Git ref becomes github.sha, while the version input becomes the release tag. The checkout uses the former, and all builds plus SOURCE_SHA use that checkout. The package-version check does not prove that the checked-out commit belongs to the requested tag. The release action can then attach those branch-built assets to v<version>.
Example: Main and v0.10.86 both still declare version 0.10.86, but main contains one extra commit. Dispatching version 0.10.86 from main builds the extra commit, records its SHA, and uploads those bytes under the existing v0.10.86 release.
Recommended fix: Normalize the requested version first, fetch and resolve the corresponding immutable tag, and checkout that resolved commit for manual runs. Use the resolved commit for all source metadata, and verify the tag still resolves to it before publishing.
Was this helpful? React with 👍 or 👎 to provide feedback.
| # Not a GitHub Actions entrypoint. The live private tarball release is | ||
| # evalops/mono .github/workflows/maestro-release.yml. | ||
|
|
||
| on: | ||
| repository_dispatch: | ||
| types: [maestro-signed-release] | ||
| push: | ||
| tags: ["v*.*.*"] | ||
| workflow_dispatch: |
| - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda | ||
| with: | ||
| tag_name: ${{ needs.prepare.outputs.release_tag }} | ||
| name: Maestro ${{ needs.prepare.outputs.release_version }} | ||
| tag_name: v${{ steps.version.outputs.value }} | ||
| name: Maestro ${{ steps.version.outputs.value }} | ||
| generate_release_notes: true |
| - name: Smoke packed package without JS runtime | ||
| env: | ||
| MAESTRO_REQUIRE_PACKAGED_TUI: "1" | ||
| NPM_CONFIG_FETCH_RETRIES: "1" | ||
| NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT: "2000" | ||
| NPM_CONFIG_FETCH_RETRY_MINTIMEOUT: "1000" | ||
| NPM_CONFIG_FETCH_TIMEOUT: "30000" | ||
| NPM_CONFIG_REGISTRY: https://registry.npmjs.org | ||
| PACKED_TARBALL: ${{ steps.pack.outputs.tarball }} | ||
| run: node scripts/smoke-packed-cli.js "$PACKED_TARBALL" | ||
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a | ||
| with: | ||
| retention-days: 14 | ||
| name: npm-tarball-${{ needs.prepare.outputs.release_tag }} | ||
| overwrite: true | ||
| path: | | ||
| ${{ steps.pack.outputs.tarball }} | ||
| ${{ steps.pack.outputs.alias_tarball }} | ||
| - name: Publish to npm | ||
| env: | ||
| NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} | ||
| PACKAGE_NAME: ${{ needs.prepare.outputs.package_name }} | ||
| ALIAS_PACKAGE_NAME: "@evalops/maestro" | ||
| PACKED_INTEGRITY: ${{ steps.pack.outputs.integrity }} | ||
| ALIAS_PACKED_INTEGRITY: ${{ steps.pack.outputs.alias_integrity }} | ||
| RELEASE_VERSION: ${{ needs.prepare.outputs.release_version }} | ||
| NPM_TAG: ${{ needs.prepare.outputs.npm_tag }} | ||
| TARBALL: ${{ steps.pack.outputs.tarball }} | ||
| ALIAS_TARBALL: ${{ steps.pack.outputs.alias_tarball }} | ||
| NPM_CONFIG_FETCH_RETRIES: "1" | ||
| NPM_CONFIG_FETCH_RETRY_MAXTIMEOUT: "2000" | ||
| NPM_CONFIG_FETCH_RETRY_MINTIMEOUT: "1000" | ||
| NPM_CONFIG_FETCH_TIMEOUT: "30000" | ||
| NPM_CONFIG_REGISTRY: https://registry.npmjs.org | ||
| run: node scripts/smoke-release-native-only.mjs bin/maestro |
Issue
Source PR: https://github.com/evalops/mono/pull/9079 (evalops/mono#9079) — products/maestro is the source of truth; this copies its release workflow to the mirror.
What changed
.github/workflows/release.ymlreplaced with the mono copy (GitHub-native release on owned runners, receipt metadata viascripts/create-release-metadata.mjs).Why
maestro-cion main failedlinux-check:scripts/update-lifecycle-contract.test.mjs:50expects the workflow to referencecreate-release-metadata.mjs; the mirror workflow predated mono#9079.Verification
node --test scripts/update-lifecycle-contract.test.mjs→ 3 pass, 0 fail🤖 Generated with Claude Code
https://claude.ai/code/session_014mGdZdLHUANa66S3RGuHMr