Fix rustfmt formatting violations to unblock CI on release/v0.0.11 (#99) #14
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release Binaries | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| branches: | |
| - "version/**" | |
| workflow_dispatch: | |
| inputs: | |
| dry_run_note: | |
| description: Optional context for manual non-publishing release validation. | |
| required: false | |
| type: string | |
| permissions: | |
| contents: read | |
| jobs: | |
| web-ui-gates: | |
| name: Web UI Gates | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 25 | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup Node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: "22.x" | |
| cache: npm | |
| cache-dependency-path: crates/orchestrator-web-server/web-ui/package-lock.json | |
| - name: Setup Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| - name: Install web-ui dependencies | |
| working-directory: crates/orchestrator-web-server/web-ui | |
| run: npm ci | |
| - name: Run web-ui tests | |
| working-directory: crates/orchestrator-web-server/web-ui | |
| run: npm run test | |
| - name: Build web-ui bundle | |
| working-directory: crates/orchestrator-web-server/web-ui | |
| run: npm run build | |
| - name: Run web-ui smoke E2E | |
| id: smoke | |
| working-directory: crates/orchestrator-web-server/web-ui | |
| env: | |
| SMOKE_ARTIFACT_DIR: ${{ github.workspace }}/crates/orchestrator-web-server/web-ui/.smoke-artifacts | |
| run: npm run test:e2e:smoke | |
| - name: Upload smoke diagnostics | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: release-web-ui-smoke-failure-${{ github.run_id }}-${{ github.run_attempt }} | |
| path: ${{ github.workspace }}/crates/orchestrator-web-server/web-ui/.smoke-artifacts | |
| if-no-files-found: ignore | |
| retention-days: 7 | |
| build: | |
| name: Build (${{ matrix.target }}) | |
| runs-on: ${{ matrix.os }} | |
| needs: web-ui-gates | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - os: ubuntu-latest | |
| target: x86_64-unknown-linux-gnu | |
| archive_ext: tar.gz | |
| - os: macos-15-intel | |
| target: x86_64-apple-darwin | |
| archive_ext: tar.gz | |
| - os: macos-14 | |
| target: aarch64-apple-darwin | |
| archive_ext: tar.gz | |
| - os: windows-latest | |
| target: x86_64-pc-windows-msvc | |
| archive_ext: zip | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Install Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| targets: ${{ matrix.target }} | |
| - name: Build release binaries | |
| shell: bash | |
| run: | | |
| cargo ao-bin-build-release --locked --target ${{ matrix.target }} | |
| - name: Compute release version | |
| id: version | |
| shell: bash | |
| run: | | |
| if [[ "${GITHUB_REF}" == refs/tags/* ]]; then | |
| VERSION="${GITHUB_REF_NAME}" | |
| else | |
| BRANCH_SANITIZED="$(echo "${GITHUB_REF_NAME}" | sed -E 's/[^A-Za-z0-9._-]+/-/g; s/^-+//; s/-+$//')" | |
| if [[ -z "${BRANCH_SANITIZED}" ]]; then | |
| BRANCH_SANITIZED="ref" | |
| fi | |
| VERSION="${BRANCH_SANITIZED}-${GITHUB_SHA::7}" | |
| fi | |
| echo "value=${VERSION}" >> "${GITHUB_OUTPUT}" | |
| - name: Package artifact (unix) | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| env: | |
| DRY_RUN_NOTE: ${{ github.event.inputs.dry_run_note || '' }} | |
| run: | | |
| set -euo pipefail | |
| VERSION="${{ steps.version.outputs.value }}" | |
| TARGET="${{ matrix.target }}" | |
| STAGE_DIR="ao-${VERSION}-${TARGET}" | |
| mkdir -p "${STAGE_DIR}" | |
| BINARIES=(ao agent-runner llm-cli-wrapper) | |
| for binary in "${BINARIES[@]}"; do | |
| cp "target/${TARGET}/release/${binary}" "${STAGE_DIR}/" | |
| done | |
| export VERSION TARGET STAGE_DIR | |
| python3 - <<'PY' | |
| import json | |
| import os | |
| from pathlib import Path | |
| binaries = ["ao", "agent-runner", "llm-cli-wrapper"] | |
| metadata = { | |
| "schema": "ao.release.v1", | |
| "version": os.environ["VERSION"], | |
| "target": os.environ["TARGET"], | |
| "git_ref": os.environ["GITHUB_REF"], | |
| "git_sha": os.environ["GITHUB_SHA"], | |
| "event_name": os.environ["GITHUB_EVENT_NAME"], | |
| "dry_run_note": os.environ.get("DRY_RUN_NOTE", ""), | |
| "binaries": binaries, | |
| "files": binaries, | |
| } | |
| output = Path(os.environ["STAGE_DIR"]) / "release-metadata.json" | |
| output.write_text(json.dumps(metadata, indent=2, sort_keys=True) + "\n", encoding="utf-8") | |
| PY | |
| tar -czf "${STAGE_DIR}.tar.gz" "${STAGE_DIR}" | |
| - name: Validate staged artifact contract (unix) | |
| if: runner.os != 'Windows' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VERSION="${{ steps.version.outputs.value }}" | |
| TARGET="${{ matrix.target }}" | |
| STAGE_DIR="ao-${VERSION}-${TARGET}" | |
| ARCHIVE_PATH="${STAGE_DIR}.tar.gz" | |
| REQUIRED_FILES=(ao agent-runner llm-cli-wrapper release-metadata.json) | |
| for file in "${REQUIRED_FILES[@]}"; do | |
| if [[ ! -f "${STAGE_DIR}/${file}" ]]; then | |
| echo "missing required staged file: ${STAGE_DIR}/${file}" >&2 | |
| exit 1 | |
| fi | |
| done | |
| if [[ ! -f "${ARCHIVE_PATH}" ]]; then | |
| echo "missing expected archive: ${ARCHIVE_PATH}" >&2 | |
| exit 1 | |
| fi | |
| export VERSION TARGET STAGE_DIR | |
| python3 - <<'PY' | |
| import json | |
| import os | |
| from pathlib import Path | |
| stage_dir = Path(os.environ["STAGE_DIR"]) | |
| metadata_path = stage_dir / "release-metadata.json" | |
| metadata = json.loads(metadata_path.read_text(encoding="utf-8")) | |
| expected_binaries = ["ao", "agent-runner", "llm-cli-wrapper"] | |
| required_pairs = { | |
| "schema": "ao.release.v1", | |
| "version": os.environ["VERSION"], | |
| "target": os.environ["TARGET"], | |
| "binaries": expected_binaries, | |
| "files": expected_binaries, | |
| } | |
| for key, expected in required_pairs.items(): | |
| if metadata.get(key) != expected: | |
| raise SystemExit( | |
| f"release metadata mismatch for {key}: expected {expected!r}, got {metadata.get(key)!r}" | |
| ) | |
| for key in ("git_ref", "git_sha", "event_name", "dry_run_note"): | |
| if key not in metadata: | |
| raise SystemExit(f"release metadata missing key: {key}") | |
| PY | |
| ARCHIVE_LIST="$(tar -tzf "${ARCHIVE_PATH}")" | |
| for file in "${REQUIRED_FILES[@]}"; do | |
| if ! printf '%s\n' "${ARCHIVE_LIST}" | grep -Fqx "${STAGE_DIR}/${file}"; then | |
| echo "archive missing required entry: ${STAGE_DIR}/${file}" >&2 | |
| exit 1 | |
| fi | |
| done | |
| - name: Package artifact (windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| env: | |
| DRY_RUN_NOTE: ${{ github.event.inputs.dry_run_note || '' }} | |
| run: | | |
| $Version = "${{ steps.version.outputs.value }}" | |
| $Target = "${{ matrix.target }}" | |
| $StageDir = "ao-$Version-$Target" | |
| $Binaries = @("ao", "agent-runner", "llm-cli-wrapper") | |
| $BinaryFiles = $Binaries | ForEach-Object { "$_.exe" } | |
| New-Item -ItemType Directory -Path $StageDir -Force | Out-Null | |
| foreach ($Binary in $BinaryFiles) { | |
| Copy-Item "target/$Target/release/$Binary" "$StageDir/" | |
| } | |
| $Metadata = @{ | |
| schema = "ao.release.v1" | |
| version = $Version | |
| target = $Target | |
| git_ref = $env:GITHUB_REF | |
| git_sha = $env:GITHUB_SHA | |
| event_name = $env:GITHUB_EVENT_NAME | |
| dry_run_note = $env:DRY_RUN_NOTE | |
| binaries = $Binaries | |
| files = $BinaryFiles | |
| } | |
| $Metadata | ConvertTo-Json -Depth 4 | Set-Content -Path "$StageDir/release-metadata.json" -Encoding utf8NoBOM | |
| Compress-Archive -Path $StageDir -DestinationPath "$StageDir.zip" -Force | |
| - name: Validate staged artifact contract (windows) | |
| if: runner.os == 'Windows' | |
| shell: pwsh | |
| run: | | |
| $Version = "${{ steps.version.outputs.value }}" | |
| $Target = "${{ matrix.target }}" | |
| $StageDir = "ao-$Version-$Target" | |
| $ArchivePath = "$StageDir.zip" | |
| $Binaries = @("ao", "agent-runner", "llm-cli-wrapper") | |
| $BinaryFiles = $Binaries | ForEach-Object { "$_.exe" } | |
| $RequiredFiles = $BinaryFiles + "release-metadata.json" | |
| foreach ($File in $RequiredFiles) { | |
| if (-not (Test-Path "$StageDir/$File")) { | |
| throw "Missing required staged file: $StageDir/$File" | |
| } | |
| } | |
| if (-not (Test-Path $ArchivePath)) { | |
| throw "Missing expected archive: $ArchivePath" | |
| } | |
| $Metadata = Get-Content "$StageDir/release-metadata.json" -Raw | ConvertFrom-Json | |
| if ($Metadata.schema -ne "ao.release.v1") { | |
| throw "release metadata mismatch for schema" | |
| } | |
| if ($Metadata.version -ne $Version) { | |
| throw "release metadata mismatch for version" | |
| } | |
| if ($Metadata.target -ne $Target) { | |
| throw "release metadata mismatch for target" | |
| } | |
| if ((@($Metadata.binaries) -join ",") -ne ($Binaries -join ",")) { | |
| throw "release metadata mismatch for binaries" | |
| } | |
| if ((@($Metadata.files) -join ",") -ne ($BinaryFiles -join ",")) { | |
| throw "release metadata mismatch for files" | |
| } | |
| foreach ($Key in @("git_ref", "git_sha", "event_name", "dry_run_note")) { | |
| if ($null -eq $Metadata.$Key) { | |
| throw "release metadata missing key: $Key" | |
| } | |
| } | |
| Add-Type -AssemblyName System.IO.Compression.FileSystem | |
| $Zip = [System.IO.Compression.ZipFile]::OpenRead($ArchivePath) | |
| try { | |
| $ZipEntries = $Zip.Entries | ForEach-Object { $_.FullName.TrimEnd("/") } | |
| } | |
| finally { | |
| $Zip.Dispose() | |
| } | |
| foreach ($File in $RequiredFiles) { | |
| $EntryPath = "$StageDir/$File" | |
| if ($ZipEntries -notcontains $EntryPath) { | |
| throw "Archive missing required entry: $EntryPath" | |
| } | |
| } | |
| - name: Upload artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ao-${{ steps.version.outputs.value }}-${{ matrix.target }} | |
| path: ao-${{ steps.version.outputs.value }}-${{ matrix.target }}.${{ matrix.archive_ext }} | |
| if-no-files-found: error | |
| publish: | |
| name: Publish GitHub release (tags only) | |
| runs-on: ubuntu-latest | |
| needs: build | |
| if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: dist | |
| - name: Generate checksums | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| RELEASE_ASSETS_DIR="dist/release-assets" | |
| rm -rf "${RELEASE_ASSETS_DIR}" | |
| mapfile -d '' ARCHIVES < <(find dist -type f \( -name '*.tar.gz' -o -name '*.zip' \) ! -path "${RELEASE_ASSETS_DIR}/*" -print0 | LC_ALL=C sort -z) | |
| if [[ "${#ARCHIVES[@]}" -eq 0 ]]; then | |
| echo "no release archives found after download-artifact step" >&2 | |
| exit 1 | |
| fi | |
| mkdir -p "${RELEASE_ASSETS_DIR}" | |
| BASENAMES=() | |
| for archive in "${ARCHIVES[@]}"; do | |
| base_name="$(basename "${archive}")" | |
| if [[ -e "${RELEASE_ASSETS_DIR}/${base_name}" ]]; then | |
| echo "duplicate archive basename detected: ${base_name}" >&2 | |
| exit 1 | |
| fi | |
| cp "${archive}" "${RELEASE_ASSETS_DIR}/${base_name}" | |
| BASENAMES+=("${base_name}") | |
| done | |
| ( | |
| cd "${RELEASE_ASSETS_DIR}" | |
| mapfile -t BASENAMES_SORTED < <(printf '%s\n' "${BASENAMES[@]}" | LC_ALL=C sort) | |
| sha256sum "${BASENAMES_SORTED[@]}" > SHA256SUMS.txt | |
| ) | |
| - name: Publish release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| files: | | |
| dist/release-assets/*.tar.gz | |
| dist/release-assets/*.zip | |
| dist/release-assets/SHA256SUMS.txt | |
| generate_release_notes: true |