feat(workspace-team): team workspaces, shared resources, workspace-scoped billing, and the #5517 redesign #389
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: Docker image | |
| # Standalone Docker packaging line (not part of core merge validation in ci.yml). | |
| # | |
| # - pull_request / workflow_dispatch: smoke build only (push: false, amd64) | |
| # - tag v*.*.* / workflow_call from release: multi-arch build + push to ghcr.io | |
| on: | |
| workflow_call: | |
| inputs: | |
| ref: | |
| description: "Git ref to build when invoked as a reusable workflow." | |
| required: false | |
| type: string | |
| default: "" | |
| release_version: | |
| description: "Stable Docker tag to publish when invoked from release automation." | |
| required: false | |
| type: string | |
| default: "" | |
| publish_latest: | |
| description: "Whether to also publish :latest when invoked from release automation." | |
| required: false | |
| type: boolean | |
| default: false | |
| push: | |
| tags: ['v*.*.*'] | |
| pull_request: | |
| paths: | |
| - "deploy/**" | |
| - ".dockerignore" | |
| - "package.json" | |
| - "pnpm-lock.yaml" | |
| - "pnpm-workspace.yaml" | |
| - ".github/workflows/docker-image.yml" | |
| workflow_dispatch: | |
| jobs: | |
| build-and-push: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| id-token: write | |
| steps: | |
| - name: Check out repository | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ inputs.ref || github.sha }} | |
| - name: Resolve publish mode | |
| id: mode | |
| shell: bash | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| RELEASE_VERSION: ${{ inputs.release_version }} | |
| PUBLISH_LATEST: ${{ inputs.publish_latest }} | |
| run: | | |
| set -euo pipefail | |
| # Publish on: | |
| # - tag push (event_name=push with tags filter), or | |
| # - reusable release call that passes release_version / publish_latest. | |
| # Do NOT key on event_name == 'workflow_call': nested reusable workflows | |
| # keep the *caller's* event name (usually workflow_dispatch), so that | |
| # check never matches and stable GHCR publish would silently become a | |
| # smoke-only build. | |
| if [ "$EVENT_NAME" = "push" ] || [ -n "${RELEASE_VERSION:-}" ] || [ "${PUBLISH_LATEST:-}" = "true" ]; then | |
| echo "publish=true" >> "$GITHUB_OUTPUT" | |
| echo "platforms=linux/amd64,linux/arm64" >> "$GITHUB_OUTPUT" | |
| else | |
| # pull_request and standalone workflow_dispatch: smoke only | |
| echo "publish=false" >> "$GITHUB_OUTPUT" | |
| echo "platforms=linux/amd64" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Set up QEMU | |
| if: ${{ steps.mode.outputs.publish == 'true' }} | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: linux/amd64,linux/arm64 | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Log in to GitHub Container Registry | |
| if: ${{ steps.mode.outputs.publish == 'true' }} | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ghcr.io/${{ github.repository_owner }}/od | |
| # spec §15.1 tag scheme: | |
| # - vX.Y.Z push → :X.Y.Z + :latest | |
| # - workflow_call (stable) → :<release_version> + optional :latest | |
| # - PR / manual validate → local-only sha tags (only when not publishing) | |
| tags: | | |
| type=semver,pattern={{version}} | |
| type=raw,value=${{ inputs.release_version }},enable=${{ inputs.release_version != '' }} | |
| type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }} | |
| type=raw,value=latest,enable=${{ inputs.publish_latest == true }} | |
| type=sha,prefix=pr-${{ github.event.pull_request.number }}-sha-,format=short,enable=${{ steps.mode.outputs.publish != 'true' && github.event_name == 'pull_request' }} | |
| type=sha,prefix=manual-sha-,format=short,enable=${{ steps.mode.outputs.publish != 'true' && github.event_name == 'workflow_dispatch' }} | |
| labels: | | |
| org.opencontainers.image.source=https://github.com/${{ github.repository }} | |
| - name: Build and push | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: deploy/Dockerfile | |
| platforms: ${{ steps.mode.outputs.platforms }} | |
| push: ${{ steps.mode.outputs.publish == 'true' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| # The in-tree Dockerfile uses node:24-alpine + apk for build | |
| # tooling; we keep that default so the workflow doesn't drift | |
| # from local builds. Spec §15.1 nominates bookworm-slim as | |
| # the canonical base; switching is a follow-up that needs | |
| # the Dockerfile's apk lines re-cast for apt. | |
| build-args: | | |
| NODE_IMAGE=public.ecr.aws/docker/library/node:24-alpine | |
| RUNTIME_IMAGE=public.ecr.aws/docker/library/node:24-alpine | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| - name: Verify public GHCR pull access | |
| if: ${{ steps.mode.outputs.publish == 'true' }} | |
| shell: bash | |
| env: | |
| PUBLISHED_TAGS: ${{ steps.meta.outputs.tags }} | |
| run: | | |
| set -euo pipefail | |
| docker logout ghcr.io >/dev/null 2>&1 || true | |
| while IFS= read -r image; do | |
| [ -n "$image" ] || continue | |
| echo "Verifying anonymous access for $image" | |
| if ! docker buildx imagetools inspect "$image" >/dev/null; then | |
| printf '%s\n' \ | |
| "Published image is not anonymously pullable: $image" \ | |
| "" \ | |
| "Make the GHCR package public before cutting a public Docker release:" \ | |
| "GitHub organization -> Packages -> od -> Package settings -> Change visibility -> Public." >&2 | |
| exit 1 | |
| fi | |
| done <<< "$PUBLISHED_TAGS" |