Skip to content

Release Conductor #16248

Release Conductor

Release Conductor #16248

name: Release Conductor
on:
workflow_dispatch:
inputs:
apply:
description: 'Apply mode (requires RELEASE_CONDUCTOR_ENABLED=1)'
required: false
default: false
type: boolean
repair_existing_tag:
description: 'Repair an existing authoritative tag or dispatch protected-tag-safe replay for immutable published tags'
required: false
default: false
type: boolean
channel:
description: 'Release channel'
required: false
default: stable
type: choice
options:
- stable
- rc
version:
description: 'Release version proposal (for example 0.8.0 or 0.8.0-rc.1)'
required: false
type: string
quarantine_stale_hours:
description: 'Quarantine stale threshold hours (default 24)'
required: false
type: string
schedule:
- cron: '*/15 * * * *'
workflow_run:
workflows:
- Queue Supervisor
types:
- completed
branches:
- develop
concurrency:
group: release-conductor-${{ github.ref }}
cancel-in-progress: true
jobs:
release-conductor:
runs-on: ubuntu-latest
permissions:
contents: write
actions: read
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- uses: actions/setup-node@v6
with:
node-version: '20'
- name: Prepare release conductor token
shell: pwsh
env:
INPUT_TOKEN_PRIMARY: ${{ secrets.GH_TOKEN }}
INPUT_TOKEN_SECONDARY: ${{ secrets.GITHUB_TOKEN }}
INPUT_TOKEN_TERTIARY: ${{ github.token }}
run: |
pwsh -NoLogo -NoProfile -File tools/priority/Resolve-PolicyToken.ps1 -TokenFileName release-conductor-gh-token.txt
- name: Configure release tag signing material
shell: bash
env:
RELEASE_TAG_SIGNING_PRIVATE_KEY: ${{ secrets.RELEASE_TAG_SIGNING_PRIVATE_KEY }}
RELEASE_TAG_SIGNING_PUBLIC_KEY: ${{ secrets.RELEASE_TAG_SIGNING_PUBLIC_KEY }}
RELEASE_TAG_SIGNING_IDENTITY_NAME: ${{ vars.RELEASE_TAG_SIGNING_IDENTITY_NAME || '' }}
RELEASE_TAG_SIGNING_IDENTITY_EMAIL: ${{ vars.RELEASE_TAG_SIGNING_IDENTITY_EMAIL || '' }}
run: |
set -euo pipefail
if [[ -z "${RELEASE_TAG_SIGNING_PRIVATE_KEY:-}" ]]; then
echo "No release tag signing key configured; skipping workflow-owned signing setup."
exit 0
fi
if [[ -z "${GH_TOKEN:-}" ]]; then
echo "::error::GH_TOKEN is unavailable after Resolve-PolicyToken; cannot derive workflow signing identity."
exit 1
fi
signing_dir="$RUNNER_TEMP/release-tag-signing"
mkdir -p "$signing_dir"
private_key_path="$signing_dir/id_release_tag_signing"
public_key_path="${private_key_path}.pub"
signing_login="$(gh api user --jq '.login')"
signing_id="$(gh api user --jq '.id')"
signing_name="${RELEASE_TAG_SIGNING_IDENTITY_NAME:-}"
signing_email="${RELEASE_TAG_SIGNING_IDENTITY_EMAIL:-}"
if [[ -z "$signing_name" ]]; then
signing_name="$(gh api user --jq '.name // .login')"
fi
if [[ -z "$signing_email" ]]; then
signing_email="$(gh api user --jq '.email // ""')"
fi
if [[ -z "$signing_email" ]]; then
signing_email="${signing_id}+${signing_login}@users.noreply.github.com"
fi
identity_source="policy-token-user"
if [[ -n "${RELEASE_TAG_SIGNING_IDENTITY_NAME:-}" || -n "${RELEASE_TAG_SIGNING_IDENTITY_EMAIL:-}" ]]; then
identity_source="repo-variable-override"
fi
printf '%s\n' "$RELEASE_TAG_SIGNING_PRIVATE_KEY" > "$private_key_path"
chmod 600 "$private_key_path"
if [[ -n "${RELEASE_TAG_SIGNING_PUBLIC_KEY:-}" ]]; then
printf '%s\n' "$RELEASE_TAG_SIGNING_PUBLIC_KEY" > "$public_key_path"
else
ssh-keygen -y -f "$private_key_path" > "$public_key_path"
fi
chmod 644 "$public_key_path"
git config gpg.format ssh
git config user.signingkey "$public_key_path"
git config user.name "$signing_name"
git config user.email "$signing_email"
git config tag.gpgSign true
{
echo "RELEASE_TAG_SIGNING_BACKEND=ssh"
echo "RELEASE_TAG_SIGNING_SOURCE=workflow-secret"
echo "RELEASE_TAG_SIGNING_IDENTITY_NAME=$signing_name"
echo "RELEASE_TAG_SIGNING_IDENTITY_EMAIL=$signing_email"
echo "RELEASE_TAG_SIGNING_IDENTITY_LOGIN=$signing_login"
echo "RELEASE_TAG_SIGNING_IDENTITY_ID=$signing_id"
echo "RELEASE_TAG_SIGNING_IDENTITY_SOURCE=$identity_source"
} >> "$GITHUB_ENV"
- name: Run release conductor
shell: pwsh
env:
RELEASE_CONDUCTOR_ENABLED: ${{ vars.RELEASE_CONDUCTOR_ENABLED || '0' }}
RELEASE_TAG_SIGNING_BACKEND: ${{ env.RELEASE_TAG_SIGNING_BACKEND || '' }}
RELEASE_TAG_SIGNING_SOURCE: ${{ env.RELEASE_TAG_SIGNING_SOURCE || '' }}
run: |
npm ci --ignore-scripts
node tools/npm/run-script.mjs priority:queue:supervisor -- --dry-run --report tests/results/_agent/queue/queue-supervisor-report.json
node tools/npm/run-script.mjs priority:policy:snapshot -- --output tests/results/_agent/policy/policy-state-snapshot.json
$reportPath = 'tests/results/_agent/release/release-conductor-report.json'
$args = @(
'tools/npm/run-script.mjs',
'priority:release:conductor',
'--',
'--report',
$reportPath,
'--queue-report',
'tests/results/_agent/queue/queue-supervisor-report.json',
'--policy-snapshot',
'tests/results/_agent/policy/policy-state-snapshot.json'
)
$eventName = '${{ github.event_name }}'
$apply = $false
$conductorEnabled = ($env:RELEASE_CONDUCTOR_ENABLED -eq '1')
if ($eventName -eq 'workflow_dispatch') {
$apply = ('${{ inputs.apply }}' -eq 'true')
} elseif ($eventName -eq 'workflow_run') {
$apply = $false
if (-not $conductorEnabled) {
Write-Host 'Release conductor apply mode disabled; workflow_run will remain proposal-only.'
} else {
Write-Host 'Release conductor workflow_run does not carry explicit release version inputs; running proposal-only.'
}
}
if ($apply) {
$args += '--apply'
} else {
$args += '--dry-run'
}
if ('${{ inputs.repair_existing_tag }}' -eq 'true') {
$args += '--repair-existing-tag'
}
$channelInput = '${{ inputs.channel }}'
if (-not [string]::IsNullOrWhiteSpace($channelInput)) {
$args += @('--channel', $channelInput.Trim().ToLowerInvariant())
}
$versionInput = '${{ inputs.version }}'
if (-not [string]::IsNullOrWhiteSpace($versionInput)) {
$args += @('--version', $versionInput.Trim())
}
$quarantineInput = '${{ inputs.quarantine_stale_hours }}'
if (-not [string]::IsNullOrWhiteSpace($quarantineInput)) {
$args += @('--quarantine-stale-hours', $quarantineInput.Trim())
}
node @args
- name: Upload release conductor artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: release-conductor-${{ github.run_id }}
path: |
tests/results/_agent/release/release-conductor-report.json
tests/results/_agent/queue/queue-supervisor-report.json
tests/results/_agent/policy/policy-state-snapshot.json
if-no-files-found: error