release: v0.9.0-beta.6 (#1261) #53
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: Build Operator | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: 'Version tag (e.g. v0.7.0-beta.1 or v0.7.0)' | |
| required: true | |
| type: string | |
| default: 'v' | |
| dry_run: | |
| description: 'Dry run (build only, no push)' | |
| required: false | |
| type: boolean | |
| default: false | |
| no_cache: | |
| description: 'Build without Docker layer cache' | |
| required: false | |
| type: boolean | |
| default: false | |
| concurrency: | |
| group: build-operator-${{ github.ref_name }} | |
| cancel-in-progress: false | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository }} | |
| # All agent variants built from Dockerfile.unified targets. | |
| AGENTS: "kiro,claude,codex,copilot,cursor,devin,gemini,grok,hermes,mimocode,opencode,antigravity,pi,native,agentcore" | |
| jobs: | |
| resolve-tag: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| outputs: | |
| tag: ${{ steps.resolve.outputs.tag }} | |
| chart_version: ${{ steps.resolve.outputs.chart_version }} | |
| is_prerelease: ${{ steps.resolve.outputs.is_prerelease }} | |
| agents: ${{ steps.resolve.outputs.agents }} | |
| steps: | |
| - name: Resolve and validate tag | |
| id: resolve | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| TAG="${{ inputs.tag }}" | |
| else | |
| TAG="${GITHUB_REF_NAME}" | |
| fi | |
| # Validate tag format | |
| if [[ ! "$TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+ ]]; then | |
| echo "::error::Invalid tag format '${TAG}'. Expected v{major}.{minor}.{patch}[-prerelease]" | |
| exit 1 | |
| fi | |
| CHART_VERSION="${TAG#v}" | |
| # Pre-release if version contains '-' (e.g. 0.7.0-beta.1) | |
| if [[ "$CHART_VERSION" == *-* ]]; then | |
| IS_PRERELEASE="true" | |
| else | |
| IS_PRERELEASE="false" | |
| fi | |
| # Build agent matrix as JSON array + validate against allowlist | |
| ALLOWED="${{ env.AGENTS }}" | |
| AGENTS_JSON=$(echo "$ALLOWED" | tr ',' '\n' | sed 's/^ *//;s/ *$//' | jq -R . | jq -sc .) | |
| echo "tag=${TAG}" >> "$GITHUB_OUTPUT" | |
| echo "chart_version=${CHART_VERSION}" >> "$GITHUB_OUTPUT" | |
| echo "is_prerelease=${IS_PRERELEASE}" >> "$GITHUB_OUTPUT" | |
| echo "agents=${AGENTS_JSON}" >> "$GITHUB_OUTPUT" | |
| # ── Pre-release path: unified build ────────────────────────────── | |
| build-core: | |
| needs: resolve-tag | |
| if: ${{ needs.resolve-tag.outputs.is_prerelease == 'true' }} | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| platform: | |
| - { os: linux/amd64, runner: ubuntu-latest, arch: amd64 } | |
| - { os: linux/arm64, runner: ubuntu-24.04-arm, arch: arm64 } | |
| runs-on: ${{ matrix.platform.runner }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build shared builder stage | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile.unified | |
| target: local_builder | |
| platforms: ${{ matrix.platform.os }} | |
| # Always push builder — it's an internal image needed by build-agents. | |
| # dry_run only gates the final agent image push + manifest creation. | |
| push: true | |
| tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/builder:${{ needs.resolve-tag.outputs.chart_version }}-${{ matrix.platform.arch }} | |
| no-cache: ${{ inputs.no_cache == true }} | |
| cache-from: ${{ inputs.no_cache != true && format('type=gha,scope=unified-builder-{0}', matrix.platform.arch) || '' }} | |
| cache-to: ${{ inputs.no_cache != true && format('type=gha,scope=unified-builder-{0},mode=max', matrix.platform.arch) || '' }} | |
| build-agents: | |
| needs: [resolve-tag, build-core] | |
| if: ${{ needs.resolve-tag.outputs.is_prerelease == 'true' }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| agent: ${{ fromJson(needs.resolve-tag.outputs.agents) }} | |
| platform: | |
| - { os: linux/amd64, runner: ubuntu-latest, arch: amd64 } | |
| - { os: linux/arm64, runner: ubuntu-24.04-arm, arch: arm64 } | |
| runs-on: ${{ matrix.platform.runner }} | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build agent variant | |
| id: build | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile.unified | |
| target: ${{ matrix.agent }} | |
| platforms: ${{ matrix.platform.os }} | |
| build-args: | | |
| BUILDER_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/builder:${{ needs.resolve-tag.outputs.chart_version }}-${{ matrix.platform.arch }} | |
| outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }},push-by-digest=true,name-canonical=true,push=${{ inputs.dry_run != true }} | |
| no-cache: ${{ inputs.no_cache == true }} | |
| cache-from: ${{ inputs.no_cache != true && format('type=gha,scope=unified-agent-{0}-{1}', matrix.agent, matrix.platform.arch) || '' }} | |
| cache-to: ${{ inputs.no_cache != true && format('type=gha,scope=unified-agent-{0}-{1},mode=max', matrix.agent, matrix.platform.arch) || '' }} | |
| - name: Export digest | |
| if: inputs.dry_run != true | |
| run: | | |
| mkdir -p /tmp/digests | |
| digest="${{ steps.build.outputs.digest }}" | |
| touch "/tmp/digests/${digest#sha256:}" | |
| - name: Upload digest | |
| if: inputs.dry_run != true | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: digests-${{ matrix.agent }}-${{ matrix.platform.arch }} | |
| path: /tmp/digests/* | |
| retention-days: 1 | |
| merge-manifests: | |
| needs: [resolve-tag, build-agents] | |
| if: ${{ inputs.dry_run != true && needs.resolve-tag.outputs.is_prerelease == 'true' }} | |
| strategy: | |
| matrix: | |
| agent: ${{ fromJson(needs.resolve-tag.outputs.agents) }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Download digests | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: /tmp/digests | |
| pattern: digests-${{ matrix.agent }}-* | |
| merge-multiple: true | |
| - name: Validate digests | |
| run: | | |
| COUNT=$(ls -1 /tmp/digests/ | wc -l) | |
| if [ "$COUNT" -ne 2 ]; then | |
| echo "::error::Expected 2 digests (amd64 + arm64), got $COUNT" | |
| exit 1 | |
| fi | |
| for f in /tmp/digests/*; do | |
| name=$(basename "$f") | |
| if ! echo "$name" | grep -qE '^[a-f0-9]{64}$'; then | |
| echo "::error::Invalid digest format: $name" | |
| exit 1 | |
| fi | |
| done | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Create manifest list | |
| run: | | |
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| VERSION="${{ needs.resolve-tag.outputs.chart_version }}" | |
| AGENT="${{ matrix.agent }}" | |
| SHA="${{ github.sha }}" | |
| SHORT_SHA="${SHA:0:7}" | |
| DIGESTS=$(printf "${IMAGE}@sha256:%s " $(ls /tmp/digests/)) | |
| # All agents use the same tag format: <version>-<agent>, beta-<agent> | |
| # No bare tags, no "latest", no default agent. | |
| docker buildx imagetools create \ | |
| -t "${IMAGE}:${VERSION}-${AGENT}" \ | |
| -t "${IMAGE}:beta-${AGENT}" \ | |
| -t "${IMAGE}:${SHORT_SHA}-${AGENT}" \ | |
| ${DIGESTS} | |
| - name: Summary | |
| run: | | |
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| VERSION="${{ needs.resolve-tag.outputs.chart_version }}" | |
| AGENT="${{ matrix.agent }}" | |
| echo "### 📦 \`${IMAGE}:${VERSION}-${AGENT}\`" >> "$GITHUB_STEP_SUMMARY" | |
| echo "### 📦 \`${IMAGE}:beta-${AGENT}\`" >> "$GITHUB_STEP_SUMMARY" | |
| # ── Stable path: promote pre-release image (no rebuild) ────── | |
| promote-stable: | |
| needs: resolve-tag | |
| if: ${{ inputs.dry_run != true && needs.resolve-tag.outputs.is_prerelease == 'false' }} | |
| strategy: | |
| matrix: | |
| agent: ${{ fromJson(needs.resolve-tag.outputs.agents) }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - uses: docker/setup-buildx-action@v3 | |
| - uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Find pre-release image | |
| id: find-prerelease | |
| run: | | |
| CHART_VERSION="${{ needs.resolve-tag.outputs.chart_version }}" | |
| PRERELEASE_TAG=$(git tag -l "v${CHART_VERSION}-*" --sort=-v:refname | head -1) | |
| if [ -z "$PRERELEASE_TAG" ]; then | |
| echo "::error::No pre-release tag found for v${CHART_VERSION}-*. Run a pre-release build first." | |
| exit 1 | |
| fi | |
| PRERELEASE_VERSION="${PRERELEASE_TAG#v}" | |
| echo "Found pre-release: ${PRERELEASE_TAG} (${PRERELEASE_VERSION})" | |
| echo "prerelease_version=${PRERELEASE_VERSION}" >> "$GITHUB_OUTPUT" | |
| - name: Verify and promote to stable tags | |
| run: | | |
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| PRERELEASE_VERSION="${{ steps.find-prerelease.outputs.prerelease_version }}" | |
| CHART_VERSION="${{ needs.resolve-tag.outputs.chart_version }}" | |
| MAJOR_MINOR="${CHART_VERSION%.*}" | |
| AGENT="${{ matrix.agent }}" | |
| # All agents use the same format — no default, no latest | |
| echo "Checking ${IMAGE}:${PRERELEASE_VERSION}-${AGENT} ..." | |
| docker buildx imagetools inspect "${IMAGE}:${PRERELEASE_VERSION}-${AGENT}" || \ | |
| { echo "::error::Image ${IMAGE}:${PRERELEASE_VERSION}-${AGENT} not found"; exit 1; } | |
| # Promote: openab:<version>-<agent>, openab:<major.minor>-<agent>, openab:stable-<agent> | |
| docker buildx imagetools create \ | |
| -t "${IMAGE}:${CHART_VERSION}-${AGENT}" \ | |
| -t "${IMAGE}:${MAJOR_MINOR}-${AGENT}" \ | |
| -t "${IMAGE}:stable-${AGENT}" \ | |
| "${IMAGE}:${PRERELEASE_VERSION}-${AGENT}" | |
| - name: Summary | |
| run: | | |
| IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" | |
| CHART_VERSION="${{ needs.resolve-tag.outputs.chart_version }}" | |
| AGENT="${{ matrix.agent }}" | |
| echo "### 📦 Promoted \`${IMAGE}:${CHART_VERSION}-${AGENT}\`" >> "$GITHUB_STEP_SUMMARY" | |
| echo "### 📦 Promoted \`${IMAGE}:stable-${AGENT}\`" >> "$GITHUB_STEP_SUMMARY" | |
| # ── Chart release (runs after either path) ─────────────────── | |
| release-chart: | |
| needs: [resolve-tag, merge-manifests, promote-stable] | |
| if: >- | |
| ${{ always() && inputs.dry_run != true && | |
| needs.resolve-tag.result == 'success' && | |
| (needs.merge-manifests.result == 'success' || needs.promote-stable.result == 'success') }} | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Install Helm | |
| uses: azure/setup-helm@v4 | |
| - uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.repository_owner }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push chart to OCI | |
| run: | | |
| CHART_VERSION="${{ needs.resolve-tag.outputs.chart_version }}" | |
| helm package charts/openab | |
| helm push openab-${CHART_VERSION}.tgz oci://ghcr.io/${{ github.repository_owner }}/charts |