Files
open-design/.github/workflows/convergence.atom.yml
PerishFireandGitHub b52fafd474 feat(ci): enforce reusable workload plans (#8393)
* feat(ci): enforce reusable workload plans

* fix(ci): skip incompatible convergence upgrades
2026-09-28 08:29:12 +00:00

221 lines
9.1 KiB
YAML

name: convergence.atom
# Atomic capability: publish immutable reusable workload results only after a
# successful workload execution, independently of the overall gate. All
# validation and R2 writes execute trusted default-branch
# Python; pull-request code is treated solely as data.
on:
workflow_call:
inputs:
config:
type: string
default: .github/config/convergence.json
handoff_id:
type: string
default: ci-results
pending_artifact:
description: Collect a completed result lane directly from its frozen Plan
type: string
default: ''
products:
description: Result lane to collect (manifest or none)
type: string
default: none
batches:
description: JSON array of declared product batches to collect (empty selects all)
type: string
default: ''
consumer_requests:
description: Project frozen requests after successful publication
type: boolean
default: false
workloads:
description: JSON array of independently completed workloads to collect
type: string
default: '[]'
outputs:
requests:
value: ${{ jobs.isolated.outputs.requests }}
sources:
value: ${{ jobs.isolated.outputs.sources }}
workflow_run:
workflows: [ci]
types: [completed]
permissions:
actions: read
contents: read
concurrency:
group: convergence-${{ github.event.workflow_run.id || github.run_id }}-${{ inputs.handoff_id || 'ci-results' }}
cancel-in-progress: false
jobs:
isolated:
name: '[cache] Default-branch manual workload results'
outputs:
requests: ${{ steps.publish.outputs.requests }}
sources: ${{ steps.publish.outputs.sources }}
if: >-
${{ !cancelled() && github.event_name == 'workflow_dispatch'
&& github.repository == 'nexu-io/open-design'
&& github.ref_name == github.event.repository.default_branch }}
runs-on: ubuntu-24.04
timeout-minutes: 5
steps:
- name: '[prepare] Authorized source snapshot'
uses: actions/checkout@v6.0.2
with:
ref: ${{ github.sha }}
fetch-depth: 1
- name: '[resolve] Gate handoff name'
if: ${{ inputs.pending_artifact == '' }}
id: artifact
env:
HANDOFF_ID: ${{ inputs.handoff_id }}
run: echo "name=$(python3 .github/scripts/handoff.py artifact-name convergence "$HANDOFF_ID")" >> "$GITHUB_OUTPUT"
- name: '[collect] Successful gate handoff'
if: ${{ inputs.pending_artifact == '' }}
uses: actions/download-artifact@v8
with:
name: ${{ steps.artifact.outputs.name }}
path: ${{ runner.temp }}/handoff-convergence
- name: '[collect] Frozen execution plan'
if: ${{ inputs.pending_artifact != '' }}
uses: actions/download-artifact@v8
with:
name: ${{ inputs.pending_artifact }}
path: ${{ runner.temp }}/plan
- name: '[validate] Completed workload evidence'
if: ${{ inputs.pending_artifact != '' }}
env:
GH_TOKEN: ${{ github.token }}
CONVERGENCE_CONFIG: ${{ inputs.config }}
HANDOFF_ID: ${{ inputs.handoff_id }}
PRODUCT_MODE: ${{ inputs.products }}
PRODUCT_BATCHES: ${{ inputs.batches || '[]' }}
WORKLOADS: ${{ inputs.workloads }}
run: >-
python3 .github/scripts/convergence.py --config "$CONVERGENCE_CONFIG" handoff
--pending "$RUNNER_TEMP/plan/pending-convergence.json" --products-root "$RUNNER_TEMP/references"
--products "$PRODUCT_MODE" --batches "$PRODUCT_BATCHES" --workloads "$WORKLOADS"
--handoff-root "$RUNNER_TEMP/handoff-convergence" --id "$HANDOFF_ID"
- name: '[validate] Branch, run and isolated storage identity'
id: trust
env:
GH_TOKEN: ${{ github.token }}
CONVERGENCE_CONFIG: ${{ inputs.config }}
run: >-
python3 .github/scripts/convergence.py --config "$CONVERGENCE_CONFIG" admit --isolated
--handoff-root "$RUNNER_TEMP/handoff-convergence"
- name: '[collect] Referenced build products'
env:
GH_TOKEN: ${{ github.token }}
run: >-
python3 .github/scripts/convergence.py stage-products
--candidate "${{ steps.trust.outputs.candidate }}"
--run-id "${{ github.run_id }}" --output-dir "$RUNNER_TEMP/products"
- name: '[cache] Publish isolated results'
id: publish
env:
CONVERGENCE_CONFIG: ${{ inputs.config }}
CONSUMER_REQUESTS: ${{ inputs.consumer_requests }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_URL: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_URL }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_BUCKET: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_BUCKET }}
OD_WORKLOAD_RESULTS_BASE_URL: ${{ secrets.OD_WORKLOAD_RESULTS_BASE_URL }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_AK: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_AK }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_SK: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_SK }}
run: |
args=()
if [ "$CONSUMER_REQUESTS" = true ]; then args+=(--pending "$RUNNER_TEMP/plan/pending-convergence.json"); fi
python3 .github/scripts/convergence.py --config "$CONVERGENCE_CONFIG" publish --isolated \
--candidate "${{ steps.trust.outputs.candidate }}" \
--products-root "$RUNNER_TEMP/products" --output-dir "$RUNNER_TEMP/publication" "${args[@]}"
publish:
name: '[cache] CI event workload results'
if: >-
${{
github.repository == 'nexu-io/open-design'
&& github.event.workflow_run.name == 'ci'
&& contains(fromJSON('["success", "failure"]'), github.event.workflow_run.conclusion)
&& github.event.workflow_run.head_repository.full_name == github.repository
&& (
github.event.workflow_run.event == 'pull_request'
|| github.event.workflow_run.event == 'merge_group'
)
}}
runs-on: ubuntu-24.04
timeout-minutes: 10
steps:
- name: Checkout trusted convergence code
uses: actions/checkout@v6.0.2
with:
repository: ${{ github.repository }}
ref: ${{ github.event.repository.default_branch }}
fetch-depth: 1
- name: Check trusted convergence contracts
run: |
python3 .github/scripts/handoff.py self-check
python3 .github/scripts/convergence.py validate
- name: Resolve convergence handoff
id: artifact
env:
GH_TOKEN: ${{ github.token }}
run: python3 .github/scripts/handoff.py resolve-run-artifact convergence ci-results
- name: Download convergence handoff
if: ${{ steps.artifact.outputs.found == 'true' }}
uses: actions/download-artifact@v8
with:
artifact-ids: ${{ steps.artifact.outputs.id }}
run-id: ${{ github.event.workflow_run.id }}
path: ${{ runner.temp }}/handoff-convergence
github-token: ${{ github.token }}
- name: Validate producing run and control plane
if: ${{ steps.artifact.outputs.found == 'true' }}
id: trust
env:
GH_TOKEN: ${{ github.token }}
run: >-
python3 .github/scripts/convergence.py admit
--handoff-root "$RUNNER_TEMP/handoff-convergence"
- name: Resolve publication configuration
if: ${{ steps.trust.outputs.publish == 'true' }}
id: storage
env:
CLOUDFLARE_R2_WORKLOAD_RESULTS_URL: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_URL }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_BUCKET: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_BUCKET }}
OD_WORKLOAD_RESULTS_BASE_URL: ${{ secrets.OD_WORKLOAD_RESULTS_BASE_URL }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_AK: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_AK }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_SK: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_SK }}
run: python3 .github/scripts/convergence.py storage-status
- name: Download current-run product artifacts
if: ${{ steps.storage.outputs.configured == 'true' }}
env:
GH_TOKEN: ${{ github.token }}
run: >-
python3 .github/scripts/convergence.py stage-products
--candidate "${{ steps.trust.outputs.candidate }}"
--output-dir "$RUNNER_TEMP/convergence-products"
- name: Publish immutable workload results
if: ${{ steps.storage.outputs.configured == 'true' }}
env:
CLOUDFLARE_R2_WORKLOAD_RESULTS_URL: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_URL }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_BUCKET: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_BUCKET }}
OD_WORKLOAD_RESULTS_BASE_URL: ${{ secrets.OD_WORKLOAD_RESULTS_BASE_URL }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_AK: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_AK }}
CLOUDFLARE_R2_WORKLOAD_RESULTS_SK: ${{ secrets.CLOUDFLARE_R2_WORKLOAD_RESULTS_SK }}
run: >-
python3 .github/scripts/convergence.py publish
--candidate "${{ steps.trust.outputs.candidate }}"
--output-dir "$RUNNER_TEMP/convergence-publication"
--products-root "$RUNNER_TEMP/convergence-products"