revert(analytics): hide disconnected inboxes on the dashboard cards a… #2630
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 and Push Docker Images | |
| on: | |
| push: | |
| branches: [main] | |
| tags: ["v*"] | |
| workflow_dispatch: | |
| inputs: | |
| environment: | |
| description: "Deployment environment" | |
| required: true | |
| default: "dev" | |
| type: choice | |
| options: | |
| - dev | |
| - prod | |
| env: | |
| BUILD_PLATFORMS: linux/amd64 #,linux/arm64 | |
| DOCKER_REGISTRY: ghcr.io | |
| concurrency: | |
| group: release-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # `next build` runs with `typescript.ignoreBuildErrors: true` and the Docker | |
| # build never runs `check-types`, so without this gate a `v*` tag (or a | |
| # workflow_dispatch on any branch) could push an image that never | |
| # type-checked. This workflow no longer triggers on `pull_request` at all — | |
| # ci.yml already runs lint/check-types/test on every PR and push-to-main, | |
| # and a Docker build was the only thing this workflow added on a PR, which | |
| # duplicated ci.yml's build-buildability signal for no benefit once ci.yml | |
| # covers it. So this job only needs to re-run for events ci.yml does not | |
| # cover: a version tag or a manual dispatch. | |
| check: | |
| name: Type-check, lint, test | |
| if: github.event_name == 'workflow_dispatch' || startsWith(github.ref, 'refs/tags/v') | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 30 | |
| permissions: | |
| contents: read | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| - name: Set up pnpm | |
| uses: pnpm/action-setup@v6.0.10 | |
| - name: Set up Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version: 24 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Set up Turbo cache | |
| uses: rharkor/caching-for-turbo@v2.5.1 | |
| # `pnpm lint` covers everything, cheapest-first: check:agent-instructions, | |
| # `turbo run lint` for apps/builder's i18n check, then repo-wide Biome | |
| # last (slowest). Type-check and test are separate turbo tasks with no | |
| # lint overlap, so they run standalone here instead of double-running | |
| # lint via a combined `turbo run check-types lint test`. | |
| - name: Lint | |
| run: pnpm lint | |
| - name: Type-check and test | |
| run: pnpm turbo run check-types test | |
| prepare-metadata: | |
| runs-on: ubuntu-latest | |
| # Only checks out and computes tag/label strings via docker/metadata- | |
| # action — never touches the registry, so packages: write is unneeded. | |
| permissions: | |
| contents: read | |
| environment: ${{ github.event.inputs.environment || 'dev' }} | |
| outputs: | |
| version: ${{ steps.meta.outputs.version }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| image_namespace: ${{ steps.image_namespace.outputs.owner_lc }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| - name: Set image namespace (GHCR requires lowercase) | |
| id: image_namespace | |
| run: echo "owner_lc=${GITHUB_REPOSITORY_OWNER,,}" >> "$GITHUB_OUTPUT" | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v6 | |
| with: | |
| images: | | |
| ${{ env.DOCKER_REGISTRY }}/${{ steps.image_namespace.outputs.owner_lc }}/chatbotx-builder | |
| ${{ env.DOCKER_REGISTRY }}/${{ steps.image_namespace.outputs.owner_lc }}/chatbotx-worker | |
| ${{ env.DOCKER_REGISTRY }}/${{ steps.image_namespace.outputs.owner_lc }}/chatbotx-realtime | |
| ${{ env.DOCKER_REGISTRY }}/${{ steps.image_namespace.outputs.owner_lc }}/chatbotx-mcp | |
| ${{ env.DOCKER_REGISTRY }}/${{ steps.image_namespace.outputs.owner_lc }}/chatbotx-javascript-executor | |
| tags: | | |
| type=ref,event=branch | |
| type=semver,pattern={{version}} | |
| type=semver,pattern={{major}}.{{minor}} | |
| type=sha,prefix={{branch}}- | |
| build-image: | |
| name: Build ${{ matrix.label }} | |
| runs-on: ${{ matrix.runner }} | |
| needs: | |
| - prepare-metadata | |
| - check | |
| # `check` deliberately does not run on push-to-main (see its `if:` above — | |
| # this workflow no longer triggers on `pull_request` at all; ci.yml covers | |
| # PRs). A skipped `needs` job skips its dependents by default, which | |
| # silently stopped every merge to main from publishing an image between | |
| # 2026-08-19 and this fix. This condition lets the build proceed when | |
| # `check` is skipped, while still blocking it when `check` actually fails | |
| # (tags / workflow_dispatch). NOT `always()` — that would ship images | |
| # whose quality gate failed. | |
| # | |
| # Note for any future job-level `if:` edit here: `jobs.<job_id>.if` only | |
| # has access to the `github`, `needs`, `vars`, and `inputs` contexts — | |
| # `matrix` isn't expanded yet at that point, so a job-level | |
| # `if: matrix.label == 'builder'` is invalid YAML that fails workflow | |
| # validation for every trigger, not just this job (this broke the whole | |
| # file for several hours — see git blame / incident notes before adding | |
| # a `matrix` reference here). `runs-on` above CAN see `matrix` because | |
| # it's evaluated after matrix expansion; step-level `if` can too. | |
| if: >- | |
| !cancelled() | |
| && needs.prepare-metadata.result == 'success' | |
| && needs.check.result != 'failure' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| # The builder compile is CPU-bound and `next build` only ever reports | |
| # 3 workers on a 4-vCPU runner, so a bigger runner is the cheapest | |
| # remaining win — but it needs one that actually exists. GitHub does | |
| # NOT publish an `ubuntu-latest-N-cores` label: larger runners are an | |
| # org-level feature that an admin must create and name, and | |
| # `ubuntu-latest-4-cores` in GitHub's docs is only an example name. | |
| # A public repo makes larger-runner minutes free, not the runners | |
| # themselves. An unmatched label does not fail the job — it queues | |
| # until it times out — so this stays on `ubuntu-latest` until a real | |
| # provisioned label is confirmed (`gh api /orgs/ChatbotXIO/actions/ | |
| # runner-groups` needs admin:org). Then it is a one-word change here. | |
| - label: builder | |
| image: chatbotx-builder | |
| dockerfile: ./apps/builder/docker/Dockerfile | |
| runner: ubuntu-latest | |
| - label: worker | |
| image: chatbotx-worker | |
| dockerfile: ./apps/worker/docker/Dockerfile | |
| runner: ubuntu-latest | |
| - label: realtime | |
| image: chatbotx-realtime | |
| dockerfile: ./apps/realtime/docker/Dockerfile | |
| runner: ubuntu-latest | |
| - label: mcp-server | |
| image: chatbotx-mcp | |
| dockerfile: ./apps/mcp-server/docker/Dockerfile | |
| runner: ubuntu-latest | |
| - label: javascript-executor | |
| image: chatbotx-javascript-executor | |
| dockerfile: ./apps/javascript-executor/docker/Dockerfile | |
| runner: ubuntu-latest | |
| # Baseline builder leg was ~16 min and the others ~1-2 min. Bounds a hung | |
| # build, and an unprovisioned `runner` label, against the 24-hour default. | |
| timeout-minutes: 45 | |
| permissions: | |
| contents: read | |
| packages: write | |
| environment: ${{ github.event.inputs.environment || 'dev' }} | |
| steps: | |
| - name: Checkout repository | |
| uses: actions/checkout@v7 | |
| - 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.DOCKER_REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Build and push main image | |
| if: github.ref_name == 'main' | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: ${{ matrix.dockerfile }} | |
| push: true | |
| tags: | | |
| ${{ env.DOCKER_REGISTRY }}/${{ needs.prepare-metadata.outputs.image_namespace }}/${{ matrix.image }}:main | |
| labels: ${{ needs.prepare-metadata.outputs.labels }} | |
| # Per-leg scope: the five matrix legs otherwise share one unscoped GHA | |
| # cache namespace and evict each other against the 10GB LRU budget. | |
| # mode=min (not max): `max` exports every intermediate layer of every | |
| # stage — "preparing build cache for export" alone cost 236.8s of a | |
| # 340.3s export — while a same-SHA rebuild still ran `pnpm install` | |
| # cold and `next build` in full, so the intermediate layers were never | |
| # usefully restored. See the phase table in the plan for measurements. | |
| cache-from: type=gha,scope=${{ matrix.label }} | |
| cache-to: type=gha,scope=${{ matrix.label }},mode=min | |
| platforms: ${{ env.BUILD_PLATFORMS }} | |
| # Turbo remote cache: deliberately NOT passed. buildx rejects an | |
| # empty-valued secret outright, so passing the partial set that exists | |
| # (TURBO_TOKEN/TURBO_TEAM, no TURBO_API) dropped TURBO_API and left | |
| # turbo stalling on its built-in Vercel default — | |
| # `Remote caching unavailable (Could not connect to vercel.com/api)`. | |
| # The Dockerfile's --mount=type=secret lines are optional-safe, so | |
| # restoring these three lines together is all it takes once a | |
| # turborepo-remote-cache is provisioned and TURBO_API is set. | |
| - name: Build and push version image | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| uses: docker/build-push-action@v7 | |
| with: | |
| context: . | |
| file: ${{ matrix.dockerfile }} | |
| push: true | |
| tags: | | |
| ${{ env.DOCKER_REGISTRY }}/${{ needs.prepare-metadata.outputs.image_namespace }}/${{ matrix.image }}:${{ needs.prepare-metadata.outputs.version }} | |
| ${{ env.DOCKER_REGISTRY }}/${{ needs.prepare-metadata.outputs.image_namespace }}/${{ matrix.image }}:latest | |
| labels: ${{ needs.prepare-metadata.outputs.labels }} | |
| # Per-leg scope: the five matrix legs otherwise share one unscoped GHA | |
| # cache namespace and evict each other against the 10GB LRU budget. | |
| # mode=min (not max): `max` exports every intermediate layer of every | |
| # stage — "preparing build cache for export" alone cost 236.8s of a | |
| # 340.3s export — while a same-SHA rebuild still ran `pnpm install` | |
| # cold and `next build` in full, so the intermediate layers were never | |
| # usefully restored. See the phase table in the plan for measurements. | |
| cache-from: type=gha,scope=${{ matrix.label }} | |
| cache-to: type=gha,scope=${{ matrix.label }},mode=min | |
| platforms: ${{ env.BUILD_PLATFORMS }} | |
| # Turbo remote cache secrets omitted — see "Build and push main image". | |
| output-image-urls: | |
| runs-on: ubuntu-latest | |
| needs: | |
| - prepare-metadata | |
| - build-image | |
| # Requiring build-image to have succeeded (not just skipped/cancelled) | |
| # keeps this job from announcing image URLs for a build that didn't run. | |
| if: >- | |
| !cancelled() | |
| && needs.build-image.result == 'success' | |
| # Only prints strings computed from `needs` outputs — no registry call. | |
| permissions: | |
| contents: read | |
| environment: ${{ github.event.inputs.environment || 'dev' }} | |
| steps: | |
| - name: Output image URLs | |
| run: | | |
| VERSION="${{ needs.prepare-metadata.outputs.version }}" | |
| REGISTRY="${{ env.DOCKER_REGISTRY }}/${{ needs.prepare-metadata.outputs.image_namespace }}" | |
| if [[ "${{ github.ref_name }}" == "main" ]]; then | |
| printf '%s\n' \ | |
| "Builder image (main): ${REGISTRY}/chatbotx-builder:main" \ | |
| "Worker image (main): ${REGISTRY}/chatbotx-worker:main" \ | |
| "Realtime image (main): ${REGISTRY}/chatbotx-realtime:main" \ | |
| "MCP Server image (main): ${REGISTRY}/chatbotx-mcp:main" \ | |
| "JavaScript Executor image (main): ${REGISTRY}/chatbotx-javascript-executor:main" | |
| elif [[ "${{ github.ref }}" == refs/tags/v* ]]; then | |
| printf '%s\n' \ | |
| "Builder image (version): ${REGISTRY}/chatbotx-builder:${VERSION}" \ | |
| "Builder image (latest): ${REGISTRY}/chatbotx-builder:latest" \ | |
| "Worker image (version): ${REGISTRY}/chatbotx-worker:${VERSION}" \ | |
| "Worker image (latest): ${REGISTRY}/chatbotx-worker:latest" \ | |
| "Realtime image (version): ${REGISTRY}/chatbotx-realtime:${VERSION}" \ | |
| "Realtime image (latest): ${REGISTRY}/chatbotx-realtime:latest" \ | |
| "MCP Server image (version): ${REGISTRY}/chatbotx-mcp:${VERSION}" \ | |
| "MCP Server image (latest): ${REGISTRY}/chatbotx-mcp:latest" \ | |
| "JavaScript Executor image (version): ${REGISTRY}/chatbotx-javascript-executor:${VERSION}" \ | |
| "JavaScript Executor image (latest): ${REGISTRY}/chatbotx-javascript-executor:latest" | |
| else | |
| echo "No publish tags for ref ${GITHUB_REF}." | |
| fi |