ci: speed up full release validation

This commit is contained in:
Peter Steinberger
2026-04-28 09:02:00 +01:00
parent c7af9c765c
commit a811e164e3
10 changed files with 456 additions and 59 deletions

View File

@@ -96,17 +96,23 @@ jobs:
outputs:
sha: ${{ steps.resolve.outputs.sha }}
steps:
- name: Checkout target ref
- name: Checkout trusted workflow helper
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}
fetch-depth: 0
ref: ${{ github.ref_name }}
path: workflow
fetch-depth: 1
persist-credentials: false
submodules: false
- name: Resolve target SHA
id: resolve
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
env:
TARGET_REF: ${{ inputs.ref }}
run: |
bash workflow/scripts/github/resolve-openclaw-ref.sh \
--ref "$TARGET_REF" \
--github-output "$GITHUB_OUTPUT"
- name: Summarize target
env:
@@ -324,7 +330,8 @@ jobs:
fi
dispatch_and_wait openclaw-release-checks.yml \
-f ref="$TARGET_SHA" \
-f ref="$TARGET_REF" \
-f expected_sha="$TARGET_SHA" \
-f provider="$PROVIDER" \
-f mode="$MODE" \
-f release_profile="$RELEASE_PROFILE" \

View File

@@ -51,6 +51,31 @@ on:
required: false
default: ""
type: string
candidate_artifact_name:
description: Optional current-run artifact name containing the candidate OpenClaw tarball
required: false
default: ""
type: string
candidate_artifact_run_id:
description: Optional workflow run id for candidate_artifact_name
required: false
default: ""
type: string
candidate_file_name:
description: Optional candidate tarball file name inside candidate_artifact_name
required: false
default: ""
type: string
candidate_version:
description: Optional candidate OpenClaw package version
required: false
default: ""
type: string
candidate_source_sha:
description: Optional source SHA used to build the candidate tarball
required: false
default: ""
type: string
workflow_call:
inputs:
ref:
@@ -90,6 +115,31 @@ on:
required: false
default: ""
type: string
candidate_artifact_name:
description: Optional current-run artifact name containing the candidate OpenClaw tarball
required: false
default: ""
type: string
candidate_artifact_run_id:
description: Optional workflow run id for candidate_artifact_name
required: false
default: ""
type: string
candidate_file_name:
description: Optional candidate tarball file name inside candidate_artifact_name
required: false
default: ""
type: string
candidate_version:
description: Optional candidate OpenClaw package version
required: false
default: ""
type: string
candidate_source_sha:
description: Optional source SHA used to build the candidate tarball
required: false
default: ""
type: string
secrets:
OPENAI_API_KEY:
required: false
@@ -119,7 +169,7 @@ env:
jobs:
prepare:
runs-on: ubuntu-latest
runs-on: blacksmith-8vcpu-ubuntu-2404
outputs:
baseline_file_name: ${{ steps.baseline_metadata.outputs.file_name }}
baseline_spec: ${{ steps.baseline.outputs.value }}
@@ -260,6 +310,7 @@ jobs:
persist-credentials: false
- name: Checkout public source ref
if: inputs.candidate_artifact_name == ''
uses: actions/checkout@v6
with:
repository: ${{ env.OPENCLAW_REPOSITORY }}
@@ -280,9 +331,10 @@ jobs:
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
cache-dependency-path: source/pnpm-lock.yaml
cache-dependency-path: ${{ inputs.candidate_artifact_name == '' && 'source/pnpm-lock.yaml' || 'workflow/pnpm-lock.yaml' }}
- name: Build candidate artifact once
if: inputs.candidate_artifact_name == ''
env:
OUTPUT_DIR: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare
run: |
@@ -291,6 +343,52 @@ jobs:
--source-dir source \
--output-dir "${OUTPUT_DIR}"
- name: Download provided candidate artifact
if: inputs.candidate_artifact_name != ''
uses: actions/download-artifact@v8
with:
name: ${{ inputs.candidate_artifact_name }}
run-id: ${{ inputs.candidate_artifact_run_id || github.run_id }}
github-token: ${{ github.token }}
path: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package
- name: Capture provided candidate artifact metadata
if: inputs.candidate_artifact_name != ''
env:
PACKAGE_DIR: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/package
INPUT_CANDIDATE_FILE_NAME: ${{ inputs.candidate_file_name }}
INPUT_CANDIDATE_VERSION: ${{ inputs.candidate_version }}
INPUT_CANDIDATE_SOURCE_SHA: ${{ inputs.candidate_source_sha }}
CANDIDATE_JSON: ${{ runner.temp }}/openclaw-cross-os-release-checks/prepare/candidate.json
run: |
node <<'NODE'
const fs = require("node:fs");
const path = require("node:path");
const packageDir = process.env.PACKAGE_DIR;
const requestedFileName = process.env.INPUT_CANDIDATE_FILE_NAME.trim();
const files = fs.readdirSync(packageDir).filter((file) => file.endsWith(".tgz"));
const candidateFileName = requestedFileName || (files.length === 1 ? files[0] : "");
if (!candidateFileName) {
throw new Error(`Expected exactly one candidate .tgz in ${packageDir}; found ${files.length}.`);
}
if (!fs.existsSync(path.join(packageDir, candidateFileName))) {
throw new Error(`Provided candidate artifact does not contain ${candidateFileName}.`);
}
const candidateVersion = process.env.INPUT_CANDIDATE_VERSION.trim();
if (!candidateVersion) {
throw new Error("candidate_version is required when candidate_artifact_name is provided.");
}
const sourceSha = process.env.INPUT_CANDIDATE_SOURCE_SHA.trim();
if (!/^[0-9a-f]{40}$/iu.test(sourceSha)) {
throw new Error("candidate_source_sha must be a full commit SHA when candidate_artifact_name is provided.");
}
fs.writeFileSync(
process.env.CANDIDATE_JSON,
`${JSON.stringify({ candidateFileName, candidateVersion, sourceSha }, null, 2)}\n`,
);
NODE
- name: Resolve baseline package spec
if: ${{ inputs.mode != 'fresh' }}
id: baseline

View File

@@ -457,15 +457,24 @@ jobs:
- chunk_id: package-update-core
label: package/update core
timeout_minutes: 120
- chunk_id: plugins-runtime-core
label: plugins/runtime core
timeout_minutes: 180
- chunk_id: plugins-runtime-plugins
label: plugins/runtime plugins
timeout_minutes: 120
- chunk_id: plugins-runtime-services
label: plugins/runtime services
timeout_minutes: 120
- chunk_id: plugins-runtime-install-a
label: plugins/runtime install A
timeout_minutes: 180
timeout_minutes: 120
- chunk_id: plugins-runtime-install-b
label: plugins/runtime install B
timeout_minutes: 180
timeout_minutes: 120
- chunk_id: plugins-runtime-install-c
label: plugins/runtime install C
timeout_minutes: 120
- chunk_id: plugins-runtime-install-d
label: plugins/runtime install D
timeout_minutes: 120
- chunk_id: bundled-channels-core
label: bundled channels core
timeout_minutes: 90

View File

@@ -7,6 +7,11 @@ on:
description: Branch, tag, or full commit SHA to validate
required: true
type: string
expected_sha:
description: Optional full SHA that ref must resolve to
required: false
default: ""
type: string
provider:
description: Provider lane for cross-OS onboarding and the end-to-end agent turn
required: false
@@ -86,24 +91,54 @@ jobs:
- name: Validate ref input
env:
RELEASE_REF: ${{ inputs.ref }}
EXPECTED_SHA: ${{ inputs.expected_sha }}
run: |
set -euo pipefail
if [[ -z "${RELEASE_REF// }" ]] || [[ "${RELEASE_REF}" == -* ]]; then
echo "Expected a branch, tag, or full commit SHA; got: ${RELEASE_REF}" >&2
exit 1
fi
if [[ -n "${EXPECTED_SHA// }" ]] && [[ ! "${EXPECTED_SHA}" =~ ^[0-9a-fA-F]{40}$ ]]; then
echo "Expected expected_sha to be a full commit SHA; got: ${EXPECTED_SHA}" >&2
exit 1
fi
- name: Checkout selected ref
- name: Checkout trusted workflow helper
uses: actions/checkout@v6
with:
ref: ${{ github.ref_name }}
path: workflow
fetch-depth: 1
- name: Fast-resolve selected ref
id: fast_ref
env:
RELEASE_REF: ${{ inputs.ref }}
EXPECTED_SHA: ${{ inputs.expected_sha }}
run: |
bash workflow/scripts/github/resolve-openclaw-ref.sh \
--ref "$RELEASE_REF" \
--expected-sha "$EXPECTED_SHA" \
--fallback-ok \
--github-output "$GITHUB_OUTPUT"
- name: Checkout selected ref for reachability fallback
if: steps.fast_ref.outputs.fallback == 'true'
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}
path: source
fetch-depth: 0
- name: Resolve checked-out SHA
id: ref
- name: Resolve checked-out fallback SHA
if: steps.fast_ref.outputs.fallback == 'true'
id: fallback_ref
working-directory: source
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Validate selected ref belongs to this repository
if: steps.fast_ref.outputs.fallback == 'true'
working-directory: source
env:
RELEASE_REF: ${{ inputs.ref }}
run: |
@@ -124,6 +159,29 @@ jobs:
echo "Secret-bearing release checks only run repository-owned branch/tag history, not arbitrary unreferenced commits." >&2
exit 1
- name: Finalize resolved SHA
id: ref
env:
FAST_SHA: ${{ steps.fast_ref.outputs.sha }}
FALLBACK_SHA: ${{ steps.fallback_ref.outputs.sha }}
EXPECTED_SHA: ${{ inputs.expected_sha }}
USED_FALLBACK: ${{ steps.fast_ref.outputs.fallback }}
run: |
set -euo pipefail
selected_sha="$FAST_SHA"
if [[ "$USED_FALLBACK" == "true" ]]; then
selected_sha="$FALLBACK_SHA"
fi
if [[ -z "$selected_sha" ]]; then
echo "Failed to resolve selected ref SHA." >&2
exit 1
fi
if [[ -n "${EXPECTED_SHA// }" ]] && [[ "${selected_sha,,}" != "${EXPECTED_SHA,,}" ]]; then
echo "Ref resolved to ${selected_sha}, expected ${EXPECTED_SHA}." >&2
exit 1
fi
echo "sha=${selected_sha,,}" >> "$GITHUB_OUTPUT"
- name: Capture selected inputs
id: inputs
env:
@@ -146,6 +204,7 @@ jobs:
env:
RELEASE_REF: ${{ inputs.ref }}
RELEASE_SHA: ${{ steps.ref.outputs.sha }}
RELEASE_REF_FAST_PATH: ${{ steps.fast_ref.outputs.fast }}
RELEASE_PROVIDER: ${{ inputs.provider }}
RELEASE_MODE: ${{ inputs.mode }}
RELEASE_PROFILE: ${{ inputs.release_profile }}
@@ -156,6 +215,7 @@ jobs:
echo
echo "- Requested ref: \`${RELEASE_REF}\`"
echo "- Validated SHA: \`${RELEASE_SHA}\`"
echo "- Ref resolution fast path: \`${RELEASE_REF_FAST_PATH}\`"
echo "- Cross-OS provider: \`${RELEASE_PROVIDER}\`"
echo "- Cross-OS mode: \`${RELEASE_MODE}\`"
echo "- Release profile: \`${RELEASE_PROFILE}\`"
@@ -166,7 +226,7 @@ jobs:
prepare_release_package:
name: Prepare release package artifact
needs: [resolve_target]
if: contains(fromJSON('["all","live-e2e","package"]'), needs.resolve_target.outputs.rerun_group)
if: contains(fromJSON('["all","cross-os","live-e2e","package"]'), needs.resolve_target.outputs.rerun_group)
runs-on: blacksmith-32vcpu-ubuntu-2404
timeout-minutes: 60
permissions:
@@ -175,6 +235,7 @@ jobs:
artifact_name: ${{ steps.artifact.outputs.name }}
package_sha256: ${{ steps.package.outputs.sha256 }}
package_version: ${{ steps.package.outputs.package_version }}
source_sha: ${{ steps.package.outputs.source_sha }}
steps:
- name: Checkout trusted workflow ref
uses: actions/checkout@v6
@@ -198,7 +259,7 @@ jobs:
id: package
shell: bash
env:
PACKAGE_REF: ${{ needs.resolve_target.outputs.ref }}
PACKAGE_REF: ${{ needs.resolve_target.outputs.sha }}
run: |
set -euo pipefail
node scripts/resolve-openclaw-package-candidate.mjs \
@@ -210,6 +271,8 @@ jobs:
--github-output "$GITHUB_OUTPUT"
digest="$(node -p "JSON.parse(require('fs').readFileSync('.artifacts/docker-e2e-package/package-candidate.json', 'utf8')).sha256")"
version="$(node -p "JSON.parse(require('fs').readFileSync('.artifacts/docker-e2e-package/package-candidate.json', 'utf8')).version")"
source_sha="$(node -p "JSON.parse(require('fs').readFileSync('.artifacts/docker-e2e-package/package-candidate.json', 'utf8')).packageSourceSha")"
echo "source_sha=$source_sha" >> "$GITHUB_OUTPUT"
{
echo "## Release package artifact"
echo
@@ -217,6 +280,7 @@ jobs:
echo "- Package ref: \`$PACKAGE_REF\`"
echo "- SHA-256: \`$digest\`"
echo "- Version: \`$version\`"
echo "- Source SHA: \`$source_sha\`"
} >> "$GITHUB_STEP_SUMMARY"
- name: Upload release package artifact
@@ -234,11 +298,11 @@ jobs:
contents: read
uses: ./.github/workflows/install-smoke.yml
with:
ref: ${{ needs.resolve_target.outputs.ref }}
ref: ${{ needs.resolve_target.outputs.sha }}
run_bun_global_install_smoke: true
cross_os_release_checks:
needs: [resolve_target]
needs: [resolve_target, prepare_release_package]
if: contains(fromJSON('["all","cross-os"]'), needs.resolve_target.outputs.rerun_group)
permissions: read-all
uses: ./.github/workflows/openclaw-cross-os-release-checks-reusable.yml
@@ -246,6 +310,11 @@ jobs:
ref: ${{ needs.resolve_target.outputs.ref }}
provider: ${{ needs.resolve_target.outputs.provider }}
mode: ${{ needs.resolve_target.outputs.mode }}
candidate_artifact_name: ${{ needs.prepare_release_package.outputs.artifact_name }}
candidate_artifact_run_id: ${{ github.run_id }}
candidate_file_name: openclaw-current.tgz
candidate_version: ${{ needs.prepare_release_package.outputs.package_version }}
candidate_source_sha: ${{ needs.prepare_release_package.outputs.source_sha }}
secrets:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -264,7 +333,7 @@ jobs:
pull-requests: read
uses: ./.github/workflows/openclaw-live-and-e2e-checks-reusable.yml
with:
ref: ${{ needs.resolve_target.outputs.ref }}
ref: ${{ needs.resolve_target.outputs.sha }}
include_repo_e2e: true
include_release_path_suites: true
include_openwebui: ${{ needs.resolve_target.outputs.release_profile != 'minimum' }}
@@ -419,7 +488,7 @@ jobs:
- name: Checkout selected ref
uses: actions/checkout@v6
with:
ref: ${{ needs.resolve_target.outputs.ref }}
ref: ${{ needs.resolve_target.outputs.sha }}
fetch-depth: 1
- name: Setup Node environment
@@ -487,7 +556,7 @@ jobs:
- name: Checkout selected ref
uses: actions/checkout@v6
with:
ref: ${{ needs.resolve_target.outputs.ref }}
ref: ${{ needs.resolve_target.outputs.sha }}
fetch-depth: 1
- name: Setup Node environment
@@ -543,7 +612,7 @@ jobs:
- name: Checkout selected ref
uses: actions/checkout@v6
with:
ref: ${{ needs.resolve_target.outputs.ref }}
ref: ${{ needs.resolve_target.outputs.sha }}
fetch-depth: 1
- name: Setup Node environment
@@ -622,7 +691,7 @@ jobs:
- name: Checkout selected ref
uses: actions/checkout@v6
with:
ref: ${{ needs.resolve_target.outputs.ref }}
ref: ${{ needs.resolve_target.outputs.sha }}
fetch-depth: 1
- name: Setup Node environment