feat(slack-bot): add escalation workflows, fix feedback/streaming bugs, refactor overthink #953
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: "[CI][A2A] Sub-Agents A2A Docker Build and Push" | |
| description: "Build and push A2A Docker images for sub-agents" | |
| on: | |
| # Trigger on push to main to detect changes and build/retag images | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - 'ai_platform_engineering/**' | |
| - 'build/**' | |
| - 'pyproject.toml' | |
| - 'uv.lock' | |
| # Build on all manual new tags as well | |
| tags: | |
| - '**' | |
| pull_request: | |
| branches: | |
| - main | |
| paths: | |
| - 'ai_platform_engineering/utils/**' | |
| - 'ai_platform_engineering/agents/**' | |
| - 'build/agents/Dockerfile.a2a' | |
| workflow_dispatch: | |
| inputs: | |
| build_all: | |
| description: 'Build all containers (skip change detection)' | |
| required: false | |
| default: false | |
| type: boolean | |
| tag_version: | |
| description: 'Version tag to apply (e.g., 0.2.8-rc.1). If provided, unchanged agents will be retagged from previous version.' | |
| required: false | |
| type: string | |
| jobs: | |
| load-config: | |
| runs-on: ubuntu-latest | |
| if: always() | |
| outputs: | |
| all_agents: ${{ steps.read-config.outputs.all_agents }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| sparse-checkout: .github/agents.json | |
| sparse-checkout-cone-mode: false | |
| - name: Read agents config | |
| id: read-config | |
| run: | | |
| ALL_AGENTS=$(jq -c '.a2a_agents' .github/agents.json) | |
| echo "all_agents=$ALL_AGENTS" >> $GITHUB_OUTPUT | |
| determine-agents: | |
| runs-on: ubuntu-latest | |
| needs: load-config | |
| if: | | |
| ((github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')))) || | |
| (github.event_name == 'pull_request' && !startsWith(github.head_ref, 'prebuild/')) || | |
| (github.event_name == 'workflow_dispatch') | |
| outputs: | |
| agents_to_build: ${{ steps.set-matrix.outputs.agents_to_build }} | |
| agents_to_retag: ${{ steps.set-matrix.outputs.agents_to_retag }} | |
| should_build: ${{ steps.set-matrix.outputs.should_build }} | |
| should_retag: ${{ steps.set-matrix.outputs.should_retag }} | |
| tag_version: ${{ steps.version.outputs.tag_version }} | |
| env: | |
| ALL_AGENTS: ${{ needs.load-config.outputs.all_agents }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| with: | |
| fetch-depth: 0 | |
| - name: Determine Tag | |
| id: version | |
| uses: ./.github/actions/determine-release-tag | |
| with: | |
| manual_tag: ${{ inputs.tag_version }} | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Generate path filters | |
| id: generate-filters | |
| uses: actions/github-script@v8 | |
| with: | |
| script: | | |
| const agents = JSON.parse(process.env.ALL_AGENTS); | |
| let filters = 'shared_utils:\n'; | |
| filters += ' - \'ai_platform_engineering/utils/**\'\n'; | |
| filters += 'shared_config:\n'; | |
| filters += ' - \'pyproject.toml\'\n'; | |
| filters += ' - \'uv.lock\'\n'; | |
| filters += 'shared_dockerfile:\n'; | |
| filters += ' - \'build/agents/Dockerfile.a2a\'\n'; | |
| agents.forEach(agent => { | |
| filters += `${agent}:\n`; | |
| filters += ` - 'ai_platform_engineering/agents/${agent}/**'\n`; | |
| filters += ` - '!ai_platform_engineering/agents/${agent}/mcp/**'\n`; | |
| filters += ` - '!ai_platform_engineering/agents/${agent}/build/Dockerfile.mcp'\n`; | |
| }); | |
| core.setOutput('filters', filters); | |
| - name: Detect changed paths | |
| id: filter | |
| uses: dorny/paths-filter@v4 | |
| with: | |
| predicate-quantifier: 'every' | |
| filters: ${{ steps.generate-filters.outputs.filters }} | |
| - name: Set matrix based on changes | |
| id: set-matrix | |
| uses: actions/github-script@v8 | |
| env: | |
| FILTER_OUTPUTS: ${{ toJson(steps.filter.outputs) }} | |
| TAG_VERSION: ${{ steps.version.outputs.tag_version }} | |
| MANUAL_BUILD_ALL: ${{ inputs.build_all || 'false' }} | |
| with: | |
| script: | | |
| const allAgents = JSON.parse(process.env.ALL_AGENTS); | |
| const filterOutputs = JSON.parse(process.env.FILTER_OUTPUTS); | |
| const tagVersion = process.env.TAG_VERSION; | |
| const manualBuildAll = process.env.MANUAL_BUILD_ALL === 'true'; | |
| const isRc = /-rc\.\d+$/.test(tagVersion); | |
| const isSharedChanged = filterOutputs.shared_utils === 'true' || | |
| filterOutputs.shared_config === 'true' || | |
| filterOutputs.shared_dockerfile === 'true'; | |
| // "Truly Build All" if: | |
| // 1. Manual build_all input is true | |
| // 2. Shared files (utils, config, dockerfile) changed | |
| // 3. We have a tag, but it's NOT an RC tag (e.g. final release) | |
| const shouldForceBuildAll = manualBuildAll || isSharedChanged || (tagVersion && !isRc); | |
| let agentsToBuild; | |
| let agentsToRetag = []; | |
| if (shouldForceBuildAll) { | |
| console.log("🚀 Forced 'Build All' mode activated"); | |
| agentsToBuild = allAgents; | |
| agentsToRetag = []; | |
| } else if (isRc) { | |
| console.log(`🏷️ RC Tag detected (${tagVersion}): Using Build vs. Retag optimization`); | |
| agentsToBuild = allAgents.filter(a => filterOutputs[a] === 'true'); | |
| agentsToRetag = allAgents.filter(a => filterOutputs[a] !== 'true'); | |
| } else { | |
| console.log("🔍 Standard PR/Push: Building only changed agents"); | |
| agentsToBuild = allAgents.filter(a => filterOutputs[a] === 'true'); | |
| agentsToRetag = []; | |
| } | |
| core.setOutput('agents_to_build', JSON.stringify(agentsToBuild)); | |
| core.setOutput('agents_to_retag', JSON.stringify(agentsToRetag)); | |
| core.setOutput('should_build', String(agentsToBuild.length > 0)); | |
| core.setOutput('should_retag', String(agentsToRetag.length > 0 && !!tagVersion)); | |
| console.log(`Agents to build: ${agentsToBuild.join(', ') || 'none'}`); | |
| console.log(`Agents to retag: ${agentsToRetag.join(', ') || 'none'}`); | |
| build-and-push: | |
| runs-on: ubuntu-latest | |
| needs: [determine-agents, load-config] | |
| if: (needs.determine-agents.outputs.should_build == 'true' && (github.event_name != 'pull_request' || !startsWith(github.head_ref, 'prebuild/'))) | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| matrix: | |
| agent: ${{ fromJson(needs.determine-agents.outputs.agents_to_build) }} | |
| fail-fast: false | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository_owner }}/agent-${{ matrix.agent }} | |
| AGENT_DIR: ai_platform_engineering/agents/${{ matrix.agent }} | |
| steps: | |
| - name: 🔒 harden runner | |
| uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 | |
| with: | |
| egress-policy: audit | |
| - name: Checkout repository | |
| uses: actions/checkout@v6 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to GitHub Container Registry | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata for Docker | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | |
| tags: | | |
| type=raw,value=latest,enable=${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' }} | |
| type=raw,value=${{ needs.determine-agents.outputs.tag_version }},enable=${{ needs.determine-agents.outputs.tag_version != '' }} | |
| type=ref,event=branch,prefix= | |
| type=ref,event=tag,prefix= | |
| type=sha,format=short,prefix= | |
| - name: Set up QEMU | |
| uses: docker/setup-qemu-action@v4 | |
| - name: Determine Dockerfile path | |
| id: dockerfile | |
| run: | | |
| if [ -f "${{ env.AGENT_DIR }}/build/Dockerfile.a2a" ]; then | |
| echo "path=${{ env.AGENT_DIR }}/build/Dockerfile.a2a" >> $GITHUB_OUTPUT | |
| else | |
| echo "path=build/agents/Dockerfile.a2a" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Determine Agent Package Name | |
| id: agent_package | |
| run: | | |
| # Special case: template agent uses agent_petstore package | |
| if [ "${{ matrix.agent }}" == "template" ]; then | |
| echo "name=petstore" >> $GITHUB_OUTPUT | |
| else | |
| echo "name=${{ matrix.agent }}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Build and Push A2A Docker image | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: ${{ steps.dockerfile.outputs.path }} | |
| push: ${{ github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| build-args: | | |
| AGENT_NAME=${{ matrix.agent }} | |
| AGENT_PACKAGE=${{ steps.agent_package.outputs.name }} | |
| platforms: linux/amd64,linux/arm64 | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Retag unchanged agents from previous version when tag_version is provided | |
| # If source image doesn't exist (still building), falls back to full build | |
| retag-unchanged: | |
| runs-on: ubuntu-latest | |
| needs: determine-agents | |
| if: needs.determine-agents.outputs.should_retag == 'true' | |
| permissions: | |
| contents: read | |
| packages: write | |
| strategy: | |
| matrix: | |
| agent: ${{ fromJson(needs.determine-agents.outputs.agents_to_retag) }} | |
| fail-fast: false | |
| env: | |
| REGISTRY: ghcr.io | |
| IMAGE_NAME: ${{ github.repository_owner }}/agent-${{ matrix.agent }} | |
| AGENT_DIR: ai_platform_engineering/agents/${{ matrix.agent }} | |
| steps: | |
| - name: 🔒 Harden runner | |
| uses: step-security/harden-runner@fa2e9d605c4eeb9fcad4c99c224cee0c6c7f3594 # v2.16.0 | |
| with: | |
| egress-policy: audit | |
| - name: 📦 Install crane | |
| run: | | |
| VERSION="v0.20.2" | |
| curl -sL "https://github.com/google/go-containerregistry/releases/download/${VERSION}/go-containerregistry_Linux_x86_64.tar.gz" | tar xz crane | |
| sudo mv crane /usr/local/bin/ | |
| - name: 🔐 Log in to registry | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| echo "$GH_TOKEN" | crane auth login ghcr.io -u ${{ github.actor }} --password-stdin | |
| - name: 🔍 Check source image and retag or build | |
| id: retag-or-build | |
| env: | |
| TAG_VERSION: ${{ needs.determine-agents.outputs.tag_version }} | |
| run: | | |
| FULL_IMAGE="${REGISTRY}/${IMAGE_NAME}" | |
| echo "🏷️ Processing agent-${{ matrix.agent }}..." | |
| # Determine source tag (previous version) | |
| if [[ "$TAG_VERSION" =~ ^(.+)-rc\.([0-9]+)$ ]]; then | |
| BASE_VERSION="${BASH_REMATCH[1]}" | |
| RC_NUM="${BASH_REMATCH[2]}" | |
| if [[ "$RC_NUM" -gt 1 ]]; then | |
| PREV_RC=$((RC_NUM - 1)) | |
| SOURCE_TAG="${BASE_VERSION}-rc.${PREV_RC}" | |
| else | |
| SOURCE_TAG="${BASE_VERSION}" | |
| fi | |
| else | |
| SOURCE_TAG="latest" | |
| fi | |
| echo " Source: ${SOURCE_TAG}" | |
| echo " Target: ${TAG_VERSION}" | |
| # Check if source image exists | |
| if crane manifest "${FULL_IMAGE}:${SOURCE_TAG}" >/dev/null 2>&1; then | |
| echo " ✅ Source image exists, retagging..." | |
| if crane tag "${FULL_IMAGE}:${SOURCE_TAG}" "${TAG_VERSION}"; then | |
| echo " ✅ Successfully retagged from ${SOURCE_TAG}" | |
| echo "needs_build=false" >> $GITHUB_OUTPUT | |
| else | |
| echo " ⚠️ Retag failed unexpectedly, will build instead" | |
| echo "needs_build=true" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| echo " ⚠️ Source image ${SOURCE_TAG} not found (may still be building)" | |
| echo " 📦 Will build fresh instead of using stale fallback" | |
| echo "needs_build=true" >> $GITHUB_OUTPUT | |
| fi | |
| # Fallback build if retag not possible | |
| - name: Checkout repository | |
| if: steps.retag-or-build.outputs.needs_build == 'true' | |
| uses: actions/checkout@v6 | |
| - name: Set up Docker Buildx | |
| if: steps.retag-or-build.outputs.needs_build == 'true' | |
| uses: docker/setup-buildx-action@v4 | |
| - name: Log in to GitHub Container Registry | |
| if: steps.retag-or-build.outputs.needs_build == 'true' | |
| uses: docker/login-action@v4 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Set up QEMU | |
| if: steps.retag-or-build.outputs.needs_build == 'true' | |
| uses: docker/setup-qemu-action@v4 | |
| - name: Determine Dockerfile path | |
| if: steps.retag-or-build.outputs.needs_build == 'true' | |
| id: dockerfile | |
| run: | | |
| if [ -f "${{ env.AGENT_DIR }}/build/Dockerfile.a2a" ]; then | |
| echo "path=${{ env.AGENT_DIR }}/build/Dockerfile.a2a" >> $GITHUB_OUTPUT | |
| else | |
| echo "path=build/agents/Dockerfile.a2a" >> $GITHUB_OUTPUT | |
| fi | |
| - name: Determine Agent Package Name | |
| if: steps.retag-or-build.outputs.needs_build == 'true' | |
| id: agent_package | |
| run: | | |
| if [ "${{ matrix.agent }}" == "template" ]; then | |
| echo "name=petstore" >> $GITHUB_OUTPUT | |
| else | |
| echo "name=${{ matrix.agent }}" >> $GITHUB_OUTPUT | |
| fi | |
| - name: 🔨 Build and Push (fallback) | |
| if: steps.retag-or-build.outputs.needs_build == 'true' | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: ${{ steps.dockerfile.outputs.path }} | |
| push: true | |
| tags: | | |
| ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.determine-agents.outputs.tag_version }} | |
| build-args: | | |
| AGENT_NAME=${{ matrix.agent }} | |
| AGENT_PACKAGE=${{ steps.agent_package.outputs.name }} | |
| platforms: linux/amd64,linux/arm64 | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| # Notify release-finalize workflow when this CI completes (for final release builds only) | |
| notify-release: | |
| runs-on: ubuntu-latest | |
| needs: [determine-agents, build-and-push, retag-unchanged] | |
| # Only notify for final releases (non-RC tags) - RC builds don't need release finalization | |
| if: | | |
| always() && | |
| needs.determine-agents.outputs.tag_version != '' && | |
| !contains(needs.determine-agents.outputs.tag_version, '-rc.') | |
| steps: | |
| - name: 🔔 Notify release finalize | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| TAG_VERSION: ${{ needs.determine-agents.outputs.tag_version }} | |
| run: | | |
| # Determine overall conclusion | |
| BUILD_RESULT="${{ needs.build-and-push.result }}" | |
| RETAG_RESULT="${{ needs.retag-unchanged.result }}" | |
| # Consider skipped as success (job conditions not met) | |
| if [[ "$BUILD_RESULT" == "skipped" ]]; then BUILD_RESULT="success"; fi | |
| if [[ "$RETAG_RESULT" == "skipped" ]]; then RETAG_RESULT="success"; fi | |
| if [[ "$BUILD_RESULT" == "success" && "$RETAG_RESULT" == "success" ]]; then | |
| CONCLUSION="success" | |
| else | |
| CONCLUSION="failure" | |
| fi | |
| echo "🔔 Notifying release-finalize: tag=$TAG_VERSION, conclusion=$CONCLUSION" | |
| gh api /repos/${{ github.repository }}/dispatches --input - <<EOF | |
| { | |
| "event_type": "ci-completed", | |
| "client_payload": { | |
| "tag": "$TAG_VERSION", | |
| "workflow": "${{ github.workflow }}", | |
| "conclusion": "$CONCLUSION" | |
| } | |
| } | |
| EOF |