Skip to content
This repository was archived by the owner on Oct 13, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 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
18 changes: 10 additions & 8 deletions .github/workflows/evaos-beta-rc-canary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,10 @@ jobs:
TAG_COMMIT: ${{ steps.provenance.outputs.tag_commit }}
run: |
set -euo pipefail
ZIP_NAME=$(node - release-assets/latest-arm64-mac.yml <<'NODE'
ZIP_NAME_PATH="$RUNNER_TEMP/evaos-updater-zip-name.txt"
node - release-assets/latest-arm64-mac.yml "$ZIP_NAME_PATH" <<'NODE'
const fs = require('fs');
const metadataPath = process.argv[2];
const [metadataPath, outputPath] = process.argv.slice(2);
const text = fs.readFileSync(metadataPath, 'utf8');
const refs = [];
for (const line of text.split(/\r?\n/)) {
Expand All @@ -321,25 +322,26 @@ jobs:
if (unique.length !== 1 || !/arm64/i.test(unique[0])) {
throw new Error('latest-arm64-mac.yml must identify exactly one arm64 updater ZIP.');
}
process.stdout.write(unique[0]);
fs.writeFileSync(outputPath, unique[0], 'utf8');
NODE
)
ZIP_NAME=$(cat "$ZIP_NAME_PATH")
UPDATER_ZIP="release-assets/$ZIP_NAME"
if [ ! -f "$UPDATER_ZIP" ]; then
echo "::error::Updater metadata references missing arm64 ZIP: $ZIP_NAME"
exit 1
fi
EXPECTED_SHA=$(node - release-assets/evaos-beta-release-manifest.json "$ZIP_NAME" <<'NODE'
EXPECTED_SHA_PATH="$RUNNER_TEMP/evaos-updater-zip-expected-sha.txt"
node - release-assets/evaos-beta-release-manifest.json "$ZIP_NAME" "$EXPECTED_SHA_PATH" <<'NODE'
const fs = require('fs');
const [manifestPath, assetName] = process.argv.slice(2);
const [manifestPath, assetName, outputPath] = process.argv.slice(2);
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
const asset = (manifest.assets || []).find((candidate) => candidate.name === assetName);
if (!asset || !/^[0-9a-f]{64}$/i.test(String(asset.sha256 || ''))) {
throw new Error(`Trusted manifest does not bind updater ZIP ${assetName}.`);
}
process.stdout.write(asset.sha256);
fs.writeFileSync(outputPath, asset.sha256, 'utf8');
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
NODE
)
EXPECTED_SHA=$(cat "$EXPECTED_SHA_PATH")
ACTUAL_SHA=$(shasum -a 256 "$UPDATER_ZIP" | awk '{print $1}')
if [ "$ACTUAL_SHA" != "$EXPECTED_SHA" ]; then
echo "::error::Updater ZIP checksum does not match the trusted release manifest."
Expand Down
11 changes: 11 additions & 0 deletions scripts/evaosBetaReleaseGate.js
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,17 @@ function collectReleaseDistributeWorkflowIssues(workflow) {

function collectRcCanaryWorkflowIssues(workflow) {
const issues = [];
const workflowText = String(workflow || '');
if (
workflowText.includes("ZIP_NAME=$(node - release-assets/latest-arm64-mac.yml <<'NODE'") ||
Comment thread
100yenadmin marked this conversation as resolved.
Outdated
workflowText.includes(
'EXPECTED_SHA=$(node - release-assets/evaos-beta-release-manifest.json "$ZIP_NAME" <<\'NODE\''
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
) {
issues.push(
'.github/workflows/evaos-beta-rc-canary.yml: macOS Bash 3.2 must not wrap updater ZIP Node heredocs in command substitution'
);
}
const installerBlocks = Array.from(
String(workflow || '').matchAll(/^ {10}install_app_from_dmg\(\) \{\n([\s\S]*?)^ {10}\}$/gm),
(match) => match[1]
Expand Down
12 changes: 12 additions & 0 deletions tests/unit/process/evaosBetaReleaseGate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1136,12 +1136,24 @@ describe('evaOS beta release gate', () => {
it('keeps RC DMG installation and installed local-control proof fail closed', () => {
if (process.platform === 'win32') return;
const workflow = fs.readFileSync(path.join(repoRoot, '.github/workflows/evaos-beta-rc-canary.yml'), 'utf8');
const commandSubstitutionHeredocIssue =
'.github/workflows/evaos-beta-rc-canary.yml: macOS Bash 3.2 must not wrap updater ZIP Node heredocs in command substitution';
const expectedIssue =
'.github/workflows/evaos-beta-rc-canary.yml: install_app_from_dmg must not reference the ZIP-only extract_dir variable under nounset';
const controlStartIssue =
'.github/workflows/evaos-beta-rc-canary.yml: installed candidate must run the operator-acknowledged local control_start suite';

expect(releaseGate.collectRcCanaryWorkflowIssues(workflow)).toEqual([]);
expect(
releaseGate.collectRcCanaryWorkflowIssues(
`${workflow}\n ZIP_NAME=$(node - release-assets/latest-arm64-mac.yml <<'NODE'\n NODE\n )\n`
)
).toContain(commandSubstitutionHeredocIssue);
expect(
releaseGate.collectRcCanaryWorkflowIssues(
`${workflow}\n EXPECTED_SHA=$(node - release-assets/evaos-beta-release-manifest.json "$ZIP_NAME" <<'NODE'\n NODE\n )\n`
)
).toContain(commandSubstitutionHeredocIssue);
const drifted = workflow.replace(
' hdiutil detach "$mount_dir" -quiet',
' rm -rf "$extract_dir"\n hdiutil detach "$mount_dir" -quiet'
Expand Down
Loading