Files
pulse/.github/workflows/compile-release-payload.yml
pulse-triage[bot] ad1cfd33c3 fix(ci): qualify grouped release action pin consumers
The grouped action upgrade leaves signing, network and publication consumer assertions on superseded pins. Align those contracts and check every consumer against reviewed immutable upstream manifests, retaining exact dispatch and release trust boundaries without claiming hosted execution.

Change-source: pulse-maintainer
2026-09-10 00:35:47 +01:00

182 lines
7.0 KiB
YAML

name: Compile Release Payload on Hosted Runner
run-name: Compile v${{ inputs.version }} payload for request ${{ inputs.request_id }}
on:
workflow_dispatch:
inputs:
version:
description: 'Version number without the leading v'
required: true
type: string
source_sha:
description: 'Exact public Pulse source commit to compile'
required: true
type: string
request_id:
description: 'Parent release workflow run and attempt identifier'
required: true
type: string
permissions:
contents: read
jobs:
compile-release-payload:
name: Compile Exact-SHA Release Payload on Ephemeral VM
# Release binaries are a production trust boundary. Keep this job on a
# fresh GitHub-hosted VM rather than a persistent public-repository runner,
# whose state can survive earlier jobs and influence later compilation.
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- name: Validate isolated compiler request
env:
EXPECTED_SOURCE_SHA: ${{ inputs.source_sha }}
REQUEST_ID: ${{ inputs.request_id }}
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
[[ "${EXPECTED_SOURCE_SHA}" =~ ^[0-9a-f]{40}$ ]]
[[ "${REQUEST_ID}" =~ ^[0-9]+-[0-9]+$ ]]
[[ "${VERSION}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-((rc|alpha|beta)\.[0-9]+))?$ ]]
test "${GITHUB_SHA}" = "${EXPECTED_SOURCE_SHA}"
test "${GITHUB_WORKFLOW_SHA}" = "${EXPECTED_SOURCE_SHA}"
- name: Checkout exact release source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
ref: ${{ inputs.source_sha }}
- name: Validate candidate identity
env:
EXPECTED_SOURCE_SHA: ${{ inputs.source_sha }}
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
test "$(tr -d '\n' < VERSION)" = "${VERSION}"
test "$(git rev-parse HEAD)" = "${EXPECTED_SOURCE_SHA}"
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
cache: false
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '24'
package-manager-cache: false
- name: Verify compiler capacity
shell: bash
run: |
set -euo pipefail
available_memory_kib="$(awk '$1 == "MemAvailable:" {print $2}' /proc/meminfo)"
minimum_memory_kib="$((6 * 1024 * 1024))"
available_disk_bytes="$(df --output=avail -B1 "$RUNNER_TEMP" | tail -n 1 | tr -d ' ')"
minimum_disk_bytes="$((10 * 1024 * 1024 * 1024))"
if (( available_memory_kib < minimum_memory_kib )); then
echo "::error::Release compiler has less than 6 GiB available memory."
exit 1
fi
if (( available_disk_bytes < minimum_disk_bytes )); then
echo "::error::Release compiler has less than 10 GiB free under RUNNER_TEMP."
exit 1
fi
echo "Release compiler capacity: $((available_memory_kib / 1024)) MiB memory, $((available_disk_bytes / 1024 / 1024 / 1024)) GiB disk."
- name: Compile credential-free release payload
env:
PULSE_LICENSE_PUBLIC_KEY: ${{ secrets.PULSE_LICENSE_PUBLIC_KEY }}
PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}
PULSE_RELEASE_BUILD_JOBS: "2"
VERSION: ${{ inputs.version }}
run: ./scripts/build-release-binaries.sh "${VERSION}" "$RUNNER_TEMP/release-compiled"
- name: Upload exact-SHA compiled payload
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: release-compiled-${{ inputs.source_sha }}-${{ inputs.version }}-${{ inputs.request_id }}
path: ${{ runner.temp }}/release-compiled/
if-no-files-found: error
retention-days: 1
compression-level: 0
overwrite: true
compile-secure-runtime-qualification:
name: Compile Hosted Secure Runtime Qualification Packet
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
attestations: write
contents: read
id-token: write
steps:
- name: Validate isolated compiler request
env:
EXPECTED_SOURCE_SHA: ${{ inputs.source_sha }}
REQUEST_ID: ${{ inputs.request_id }}
run: |
set -euo pipefail
[[ "${EXPECTED_SOURCE_SHA}" =~ ^[0-9a-f]{40}$ ]]
[[ "${REQUEST_ID}" =~ ^[0-9]+-[0-9]+$ ]]
test "${GITHUB_SHA}" = "${EXPECTED_SOURCE_SHA}"
test "${GITHUB_WORKFLOW_SHA}" = "${EXPECTED_SOURCE_SHA}"
- name: Checkout exact release source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
fetch-depth: 0
ref: ${{ inputs.source_sha }}
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
cache: false
- name: Build hosted qualification subjects
env:
EXPECTED_SOURCE_SHA: ${{ inputs.source_sha }}
PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail
test "$(tr -d '\n' < VERSION)" = "${VERSION}"
test "$(git rev-parse HEAD)" = "${EXPECTED_SOURCE_SHA}"
./scripts/build-secure-runtime-qualification.sh \
"${VERSION}" \
"$RUNNER_TEMP/secure-runtime-qualification" \
amd64
- name: Attest hosted qualification subjects
id: attest_secure_runtime
uses: actions/attest@1e69f48acb82d1966a394da916b4c1698aa569d6 # v4.2.2
with:
subject-checksums: ${{ runner.temp }}/secure-runtime-qualification/secure-runtime-compiler-subjects.sha256
- name: Preserve portable compiler provenance
env:
PROVENANCE_BUNDLE: ${{ steps.attest_secure_runtime.outputs.bundle-path }}
run: |
set -euo pipefail
test -s "${PROVENANCE_BUNDLE}"
jq -e 'type == "object"' "${PROVENANCE_BUNDLE}" >/dev/null
install -m 0644 \
"${PROVENANCE_BUNDLE}" \
"$RUNNER_TEMP/secure-runtime-qualification/secure-runtime-compiler-provenance.sigstore.json"
- name: Upload hosted qualification packet
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: secure-runtime-qualification-${{ inputs.source_sha }}-${{ inputs.version }}-${{ inputs.request_id }}
path: ${{ runner.temp }}/secure-runtime-qualification/
if-no-files-found: error
retention-days: 1
compression-level: 0
overwrite: true