Files
pulse/.github/workflows/build-release-candidate.yml
T
2026-07-22 15:37:48 +01:00

509 lines
24 KiB
YAML

name: Build Release Candidate
on:
workflow_call:
inputs:
version:
description: 'Version number without the leading v'
required: true
type: string
require_macos_signing:
description: 'Require Developer ID signed and notarized macOS agent binaries'
required: false
default: false
type: boolean
require_windows_signing:
description: 'Require Authenticode-signed Windows agent binaries'
required: false
default: false
type: boolean
windows_signing_backend:
description: 'Windows signing backend: signpath (canonical) or legacy-pfx (break-glass fallback)'
required: false
default: signpath
type: string
outputs:
artifact_name:
description: 'Immutable release candidate artifact name'
value: ${{ jobs.build.outputs.artifact_name }}
manifest_artifact_name:
description: 'Release candidate manifest artifact name'
value: ${{ jobs.build.outputs.manifest_artifact_name }}
windows_signing_backend:
description: 'Windows signing backend used for the candidate'
value: ${{ jobs.sign-windows-agent.outputs.signing_backend }}
permissions:
contents: read
jobs:
signing-configuration:
name: Verify Native Signing Configuration
if: ${{ inputs.require_macos_signing || inputs.require_windows_signing }}
runs-on: ubuntu-24.04
timeout-minutes: 2
steps:
- name: Report missing signing secrets
env:
APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64 }}
APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD }}
APPLE_DEVELOPER_ID_APPLICATION_IDENTITY: ${{ secrets.APPLE_DEVELOPER_ID_APPLICATION_IDENTITY }}
APPLE_NOTARY_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARY_KEY_P8_BASE64 }}
APPLE_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }}
APPLE_NOTARY_ISSUER_ID: ${{ secrets.APPLE_NOTARY_ISSUER_ID }}
WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64: ${{ secrets.WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64 }}
WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD }}
SIGNPATH_API_TOKEN: ${{ secrets.SIGNPATH_API_TOKEN }}
SIGNPATH_ORGANIZATION_ID: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
SIGNPATH_PROJECT_SLUG: ${{ vars.SIGNPATH_PROJECT_SLUG }}
SIGNPATH_SIGNING_POLICY_SLUG: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
SIGNPATH_ARTIFACT_CONFIGURATION_SLUG: ${{ vars.SIGNPATH_ARTIFACT_CONFIGURATION_SLUG }}
SIGNPATH_EXPECTED_CERTIFICATE_SUBJECT: ${{ vars.SIGNPATH_EXPECTED_CERTIFICATE_SUBJECT }}
REQUIRE_MACOS_SIGNING: ${{ inputs.require_macos_signing }}
REQUIRE_WINDOWS_SIGNING: ${{ inputs.require_windows_signing }}
WINDOWS_SIGNING_BACKEND: ${{ inputs.windows_signing_backend }}
run: |
set -euo pipefail
missing=0
required=()
if [[ "$REQUIRE_MACOS_SIGNING" == "true" ]]; then
required+=(
APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64
APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD
APPLE_DEVELOPER_ID_APPLICATION_IDENTITY
APPLE_NOTARY_KEY_P8_BASE64
APPLE_NOTARY_KEY_ID
APPLE_NOTARY_ISSUER_ID
)
fi
if [[ "$REQUIRE_WINDOWS_SIGNING" == "true" ]]; then
case "$WINDOWS_SIGNING_BACKEND" in
signpath)
required+=(
SIGNPATH_API_TOKEN
SIGNPATH_ORGANIZATION_ID
SIGNPATH_PROJECT_SLUG
SIGNPATH_SIGNING_POLICY_SLUG
SIGNPATH_ARTIFACT_CONFIGURATION_SLUG
SIGNPATH_EXPECTED_CERTIFICATE_SUBJECT
)
;;
legacy-pfx)
required+=(
WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64
WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD
)
echo "::warning::Using the break-glass legacy PFX Windows signing backend."
;;
*)
echo "::error::Unsupported Windows signing backend '${WINDOWS_SIGNING_BACKEND}'. Expected signpath or legacy-pfx."
missing=1
;;
esac
else
echo "::notice::Windows Authenticode is not required for this candidate."
fi
for name in "${required[@]}"; do
if [ -z "${!name:-}" ]; then
echo "::error::Missing required Actions secret ${name}."
missing=1
fi
done
exit "$missing"
sign-macos-agent:
name: Sign and Notarize macOS Agent
needs: signing-configuration
if: ${{ inputs.require_macos_signing && needs.signing-configuration.result == 'success' }}
runs-on: macos-15
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
cache: true
- name: Build, sign, and notarize agent binaries
shell: bash
env:
APPLE_CERTIFICATE_P12_BASE64: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_P12_BASE64 }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_DEVELOPER_ID_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_DEVELOPER_ID_APPLICATION_IDENTITY }}
APPLE_NOTARY_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARY_KEY_P8_BASE64 }}
APPLE_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }}
APPLE_NOTARY_ISSUER_ID: ${{ secrets.APPLE_NOTARY_ISSUER_ID }}
PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}
run: |
set -euo pipefail
for name in \
APPLE_CERTIFICATE_P12_BASE64 \
APPLE_CERTIFICATE_PASSWORD \
APPLE_SIGNING_IDENTITY \
APPLE_NOTARY_KEY_P8_BASE64 \
APPLE_NOTARY_KEY_ID \
APPLE_NOTARY_ISSUER_ID \
PULSE_UPDATE_SIGNING_PUBLIC_KEY; do
test -n "${!name:-}" || { echo "::error::Missing required ${name}."; exit 1; }
done
mkdir -p native-agent-binaries
python3 - <<'PY'
import base64, os
from pathlib import Path
Path('developer-id.p12').write_bytes(base64.b64decode(os.environ['APPLE_CERTIFICATE_P12_BASE64']))
Path('AuthKey.p8').write_bytes(base64.b64decode(os.environ['APPLE_NOTARY_KEY_P8_BASE64']))
PY
keychain="$RUNNER_TEMP/pulse-signing.keychain-db"
keychain_password="$(openssl rand -hex 24)"
security create-keychain -p "$keychain_password" "$keychain"
security set-keychain-settings -lut 21600 "$keychain"
security unlock-keychain -p "$keychain_password" "$keychain"
security import developer-id.p12 -k "$keychain" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "$keychain_password" "$keychain"
security list-keychains -d user -s "$keychain"
ldflags="$(./scripts/release_ldflags.sh agent \
--version "v${{ inputs.version }}" \
--update-public-keys "$PULSE_UPDATE_SIGNING_PUBLIC_KEY")"
for arch in amd64 arm64; do
output="native-agent-binaries/pulse-agent-darwin-${arch}"
GOOS=darwin GOARCH="$arch" CGO_ENABLED=0 \
go build -buildvcs=false -trimpath -ldflags="$ldflags" -o "$output" ./cmd/pulse-agent
codesign --force --timestamp --options runtime --sign "$APPLE_SIGNING_IDENTITY" "$output"
codesign --verify --deep --strict --verbose=2 "$output"
done
ditto -c -k --keepParent native-agent-binaries pulse-agent-macos-notarization.zip
xcrun notarytool submit pulse-agent-macos-notarization.zip \
--key AuthKey.p8 \
--key-id "$APPLE_NOTARY_KEY_ID" \
--issuer "$APPLE_NOTARY_ISSUER_ID" \
--wait \
--output-format json > notarization-result.json
python3 - <<'PY'
import json
from pathlib import Path
result = json.loads(Path('notarization-result.json').read_text())
if result.get('status') != 'Accepted':
raise SystemExit(f"Apple notarization was not accepted: {result.get('status', 'unknown')}")
PY
for binary in native-agent-binaries/pulse-agent-darwin-*; do
# Gatekeeper's spctl app assessment rejects bare command-line
# Mach-O binaries even when the notary service accepted them.
# Verify the signed bytes that will be packaged instead.
codesign --verify --deep --strict --verbose=2 "$binary"
done
- name: Upload signed macOS binaries
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: signed-macos-agent-${{ github.sha }}-${{ inputs.version }}
path: native-agent-binaries/
if-no-files-found: error
retention-days: 1
compression-level: 0
sign-windows-agent:
name: Authenticode Sign Windows Agent
needs: signing-configuration
if: ${{ inputs.require_windows_signing && needs.signing-configuration.result == 'success' }}
runs-on: windows-2025
timeout-minutes: 50
permissions:
actions: read
contents: read
outputs:
signing_backend: ${{ steps.evidence.outputs.signing_backend }}
steps:
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
cache: true
- name: Build unsigned agent binaries
shell: pwsh
env:
PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}
run: |
$ErrorActionPreference = 'Stop'
foreach ($name in @('PULSE_UPDATE_SIGNING_PUBLIC_KEY')) {
if ([string]::IsNullOrWhiteSpace((Get-Item "Env:$name").Value)) {
throw "Missing required $name."
}
}
New-Item -ItemType Directory -Path unsigned-native-agent-binaries -Force | Out-Null
$ldflags = & bash ./scripts/release_ldflags.sh agent --version "v${{ inputs.version }}" --update-public-keys $env:PULSE_UPDATE_SIGNING_PUBLIC_KEY
foreach ($arch in @('amd64', 'arm64', '386')) {
$env:GOOS = 'windows'
$env:GOARCH = $arch
$env:CGO_ENABLED = '0'
$output = "unsigned-native-agent-binaries/pulse-agent-windows-$arch.exe"
go build -buildvcs=false -trimpath -ldflags="$ldflags" -o $output ./cmd/pulse-agent
if ($LASTEXITCODE -ne 0) { throw "Go build failed for Windows $arch." }
}
- name: Sign with legacy PFX fallback
if: ${{ inputs.windows_signing_backend == 'legacy-pfx' }}
shell: pwsh
env:
WINDOWS_CERTIFICATE_PFX_BASE64: ${{ secrets.WINDOWS_CODE_SIGNING_CERTIFICATE_PFX_BASE64 }}
WINDOWS_CERTIFICATE_PASSWORD: ${{ secrets.WINDOWS_CODE_SIGNING_CERTIFICATE_PASSWORD }}
run: |
$ErrorActionPreference = 'Stop'
$pfxPath = Join-Path $env:RUNNER_TEMP 'pulse-code-signing.pfx'
[IO.File]::WriteAllBytes($pfxPath, [Convert]::FromBase64String($env:WINDOWS_CERTIFICATE_PFX_BASE64))
$password = ConvertTo-SecureString $env:WINDOWS_CERTIFICATE_PASSWORD -AsPlainText -Force
$certificate = Import-PfxCertificate -FilePath $pfxPath -CertStoreLocation Cert:\CurrentUser\My -Password $password -Exportable:$false
if ($null -eq $certificate) { throw 'Failed to import Windows code-signing certificate.' }
$signtool = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin" -Filter signtool.exe -Recurse | Sort-Object FullName -Descending | Select-Object -First 1 -ExpandProperty FullName
foreach ($output in (Get-ChildItem unsigned-native-agent-binaries\*.exe)) {
& $signtool sign /sha1 $certificate.Thumbprint /fd SHA256 /td SHA256 /tr http://timestamp.digicert.com $output.FullName
if ($LASTEXITCODE -ne 0) { throw "Authenticode signing failed for $($output.Name)." }
}
- name: Upload unsigned SignPath input
if: ${{ inputs.windows_signing_backend == 'signpath' }}
id: upload-unsigned-windows
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: unsigned-windows-agent-${{ github.sha }}-${{ inputs.version }}
path: unsigned-native-agent-binaries/
if-no-files-found: error
retention-days: 1
compression-level: 0
- name: Submit SignPath Authenticode request
if: ${{ inputs.windows_signing_backend == 'signpath' }}
id: signpath
uses: signpath/github-action-submit-signing-request@b9d91eadd323de506c0c81cf0c7fe7438f3360fd # v2
with:
api-token: ${{ secrets.SIGNPATH_API_TOKEN }}
organization-id: ${{ vars.SIGNPATH_ORGANIZATION_ID }}
project-slug: ${{ vars.SIGNPATH_PROJECT_SLUG }}
signing-policy-slug: ${{ vars.SIGNPATH_SIGNING_POLICY_SLUG }}
artifact-configuration-slug: ${{ vars.SIGNPATH_ARTIFACT_CONFIGURATION_SLUG }}
github-artifact-id: ${{ steps.upload-unsigned-windows.outputs.artifact-id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
wait-for-completion: true
wait-for-completion-timeout-in-seconds: 2400
output-artifact-directory: signed-native-agent-binaries
parameters: |
version: ${{ toJSON(inputs.version) }}
- name: Verify Authenticode signatures and write evidence
id: evidence
shell: pwsh
env:
WINDOWS_SIGNING_BACKEND: ${{ inputs.windows_signing_backend }}
SIGNPATH_SIGNING_REQUEST_ID: ${{ steps.signpath.outputs.signing-request-id }}
SIGNPATH_SIGNING_REQUEST_URL: ${{ steps.signpath.outputs.signing-request-web-url }}
SIGNPATH_INPUT_ARTIFACT_ID: ${{ steps.upload-unsigned-windows.outputs.artifact-id }}
SIGNPATH_EXPECTED_CERTIFICATE_SUBJECT: ${{ vars.SIGNPATH_EXPECTED_CERTIFICATE_SUBJECT }}
run: |
$ErrorActionPreference = 'Stop'
$sourceDir = if ($env:WINDOWS_SIGNING_BACKEND -eq 'signpath') { 'signed-native-agent-binaries' } else { 'unsigned-native-agent-binaries' }
New-Item -ItemType Directory -Path native-agent-binaries -Force | Out-Null
$signtool = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin" -Filter signtool.exe -Recurse | Sort-Object FullName -Descending | Select-Object -First 1 -ExpandProperty FullName
$files = @()
foreach ($arch in @('amd64', 'arm64', '386')) {
$name = "pulse-agent-windows-$arch.exe"
$source = Join-Path $sourceDir $name
if (-not (Test-Path $source -PathType Leaf)) { throw "Signed output is missing $name." }
& $signtool verify /pa /v $source
if ($LASTEXITCODE -ne 0) { throw "Authenticode verification failed for $name." }
$signature = Get-AuthenticodeSignature $source
if ($signature.Status -ne 'Valid' -or $null -eq $signature.SignerCertificate) { throw "Invalid Authenticode status for $name: $($signature.Status)." }
if ($env:WINDOWS_SIGNING_BACKEND -eq 'signpath' -and $signature.SignerCertificate.Subject -ne $env:SIGNPATH_EXPECTED_CERTIFICATE_SUBJECT) { throw "Unexpected Authenticode signer subject for ${name}: $($signature.SignerCertificate.Subject)." }
Copy-Item $source (Join-Path native-agent-binaries $name)
$files += [ordered]@{
name = $name
sha256 = (Get-FileHash $source -Algorithm SHA256).Hash.ToLowerInvariant()
signerSubject = $signature.SignerCertificate.Subject
signerThumbprint = $signature.SignerCertificate.Thumbprint
}
}
$evidence = [ordered]@{
schemaVersion = 1
backend = $env:WINDOWS_SIGNING_BACKEND
version = '${{ inputs.version }}'
sourceSha = $env:GITHUB_SHA
workflowRunUrl = "https://github.com/$env:GITHUB_REPOSITORY/actions/runs/$env:GITHUB_RUN_ID"
signPathSigningRequestId = $env:SIGNPATH_SIGNING_REQUEST_ID
signPathSigningRequestUrl = $env:SIGNPATH_SIGNING_REQUEST_URL
githubInputArtifactId = $env:SIGNPATH_INPUT_ARTIFACT_ID
files = $files
}
$evidence | ConvertTo-Json -Depth 6 | Set-Content windows-signing-evidence.json -Encoding utf8NoBOM
"signing_backend=$env:WINDOWS_SIGNING_BACKEND" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Upload signed Windows binaries
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: signed-windows-agent-${{ github.sha }}-${{ inputs.version }}
path: native-agent-binaries/
if-no-files-found: error
retention-days: 1
compression-level: 0
- name: Upload Windows signing evidence
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: windows-signing-evidence-${{ github.sha }}-${{ inputs.version }}
path: windows-signing-evidence.json
if-no-files-found: error
retention-days: 30
build:
name: Build and Validate Release Candidate
needs: [sign-macos-agent, sign-windows-agent]
if: ${{ always() && (!inputs.require_macos_signing || needs.sign-macos-agent.result == 'success') && (!inputs.require_windows_signing || needs.sign-windows-agent.result == 'success') }}
runs-on: ubuntu-24.04
timeout-minutes: 60
outputs:
artifact_name: ${{ steps.identity.outputs.artifact_name }}
manifest_artifact_name: ${{ steps.identity.outputs.manifest_artifact_name }}
steps:
- name: Resolve candidate identity
id: identity
run: |
set -euo pipefail
echo "artifact_name=release-candidate-${GITHUB_SHA}-${{ inputs.version }}" >> "$GITHUB_OUTPUT"
echo "manifest_artifact_name=release-candidate-manifest-${GITHUB_SHA}-${{ inputs.version }}" >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
fetch-depth: 0
- name: Validate candidate identity
run: |
set -euo pipefail
test "$(tr -d '\n' < VERSION)" = "${{ inputs.version }}"
test "$(git rev-parse HEAD)" = "${GITHUB_SHA}"
- name: Set up Go
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
cache: true
- name: Set up Node.js
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: 'frontend-modern/package-lock.json'
- name: Install release prerequisites
run: |
sudo apt-get update
sudo apt-get install -y zip
- name: Set up Helm
uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0
with:
version: 'v3.15.2'
- name: Install Syft
run: |
set -euo pipefail
SYFT_VERSION="1.42.4"
SYFT_ARCHIVE="syft_${SYFT_VERSION}_linux_amd64.tar.gz"
SYFT_SHA256="590650c2743b83f327d1bf9bec64f6f83b7fec504187bb84f500c862bf8f2a0f"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT
curl -fsSL "https://github.com/anchore/syft/releases/download/v${SYFT_VERSION}/${SYFT_ARCHIVE}" \
-o "${TMP_DIR}/${SYFT_ARCHIVE}"
printf '%s %s\n' "${SYFT_SHA256}" "${TMP_DIR}/${SYFT_ARCHIVE}" | sha256sum --check --
tar -xzf "${TMP_DIR}/${SYFT_ARCHIVE}" -C "${TMP_DIR}" syft
install -m 0755 "${TMP_DIR}/syft" /usr/local/bin/syft
- name: Download signed macOS binaries
if: ${{ inputs.require_macos_signing }}
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: signed-macos-agent-${{ github.sha }}-${{ inputs.version }}
path: native-agent-binaries
- name: Download signed Windows binaries
if: ${{ inputs.require_windows_signing }}
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: signed-windows-agent-${{ github.sha }}-${{ inputs.version }}
path: native-agent-binaries
- name: Download Windows signing evidence
if: ${{ inputs.require_windows_signing }}
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: windows-signing-evidence-${{ github.sha }}-${{ inputs.version }}
path: release-candidate-manifest
- name: Build release candidate
run: ./scripts/build-release.sh "${{ inputs.version }}"
env:
PULSE_LICENSE_PUBLIC_KEY: ${{ secrets.PULSE_LICENSE_PUBLIC_KEY }}
PULSE_UPDATE_SIGNING_KEY: ${{ secrets.PULSE_UPDATE_SIGNING_KEY }}
PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}
PULSE_REQUIRE_MACOS_SIGNING: ${{ inputs.require_macos_signing }}
PULSE_REQUIRE_WINDOWS_SIGNING: ${{ inputs.require_windows_signing }}
PULSE_AGENT_NATIVE_BINARIES_DIR: ${{ (inputs.require_macos_signing || inputs.require_windows_signing) && format('{0}/native-agent-binaries', github.workspace) || '' }}
- name: Validate installer signing key pins
env:
PULSE_UPDATE_SIGNING_PUBLIC_KEY: ${{ vars.PULSE_UPDATE_SIGNING_PUBLIC_KEY }}
run: |
set -euo pipefail
TRUSTED_SSH_PUBLIC_KEY="$(
go run ./scripts/release_update_key.go public-key-ssh \
--public-key "${PULSE_UPDATE_SIGNING_PUBLIC_KEY}" \
--comment pulse-installer
)"
for installer in install.sh scripts/pulse-auto-update.sh release/pulse-auto-update.sh; do
grep -F "PINNED_RELEASE_SSH_PUBLIC_KEY=\"${TRUSTED_SSH_PUBLIC_KEY}\"" "${installer}" >/dev/null || {
echo "::error::${installer} does not trust the configured release signing key."
exit 1
}
done
- name: Validate complete candidate locally
run: ./scripts/validate-release.sh "${{ inputs.version }}" --skip-docker
- name: Create immutable candidate manifest
run: |
python3 scripts/release_candidate_manifest.py create \
--release-dir release \
--version "${{ inputs.version }}" \
--source-sha "${GITHUB_SHA}" \
--output release-candidate-manifest/release-candidate.json
- name: Upload immutable release candidate
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ steps.identity.outputs.artifact_name }}
path: release/
if-no-files-found: error
retention-days: 1
compression-level: 0
overwrite: true
- name: Upload candidate manifest
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ steps.identity.outputs.manifest_artifact_name }}
path: release-candidate-manifest/
if-no-files-found: error
retention-days: 1
overwrite: true