The deploy was a job inside build.yml gated by `if: startsWith(github.ref, 'refs/tags/v')`. Gitea draws every job into the run graph before it evaluates that `if`, so an ordinary push to master showed a pending "Deploy to galactus" — indistinguishable from prod being about to be redeployed off an unreleased commit, and the only safe reaction is to cancel the run, which takes the images down with it. The gate itself was never wrong (no deploy-galactus run has ever been created from a branch ref), but a guarantee you cannot see is not much of a guarantee. `on: push: tags: ["v*"]` in a workflow of its own makes it structural: the deploy cannot appear on a master build because the workflow does not exist there. It replaces `needs: build` by polling the Actions API for the build.yml run at this tag and requiring it green, so both images are still known to be in the registry before anything is pulled. AUTO_DEPLOY_GALACTUS still cuts the chain. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
111 lines
4.1 KiB
YAML
111 lines
4.1 KiB
YAML
# Build + push the API and web container images to the git.mancinas.io registry.
|
|
#
|
|
# Two images from this one repo:
|
|
# git.mancinas.io/rmancinas/jorgecuadros-api
|
|
# git.mancinas.io/rmancinas/jorgecuadros-web
|
|
#
|
|
# Comprehensive versioning (docker/metadata-action). Every build pushes a set
|
|
# of tags so an image is addressable at several granularities:
|
|
# - vX.Y.Z / vX.Y when the trigger is a git tag vX.Y.Z (releases)
|
|
# - <branch> the branch that was pushed (e.g. master, feat-foo)
|
|
# - sha-<short> immutable per-commit id, always present
|
|
# - latest only on the default branch (master)
|
|
# The same version string + commit + build date are baked into the image as
|
|
# ARG/ENV (APP_VERSION / GIT_SHA / BUILD_DATE) and as OCI labels, so a running
|
|
# container can report exactly what is deployed.
|
|
#
|
|
# Release flow: git tag v1.2.0 && git push origin v1.2.0 -> versioned images
|
|
# -> deploy-on-tag.yml waits for this run to go green and then
|
|
# dispatches deploy-galactus.yml.
|
|
#
|
|
# This workflow BUILDS ONLY — it never deploys. The deploy chain used to be a
|
|
# job here, gated to tag refs, but Gitea draws every job of a workflow into the
|
|
# run graph before it evaluates the job's `if`: a routine master build showed a
|
|
# pending "Deploy to galactus" and looked like prod was about to be redeployed
|
|
# off an unreleased commit. Keeping the deploy in a `on: push: tags` workflow of
|
|
# its own makes that structurally impossible.
|
|
|
|
name: Build and Push Images
|
|
|
|
on:
|
|
push:
|
|
branches: [master]
|
|
tags: ["v*"]
|
|
paths:
|
|
- "apps/**"
|
|
- "packages/**"
|
|
- "docker/**"
|
|
- "package.json"
|
|
- "pnpm-lock.yaml"
|
|
- ".gitea/workflows/build.yml"
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
REGISTRY: git.mancinas.io
|
|
|
|
jobs:
|
|
build:
|
|
name: Build ${{ matrix.image }}
|
|
# release.yml pushes the release commit and its tag in a single `git push`,
|
|
# so Gitea creates two runs for the same commit: one for master, one for the
|
|
# tag. Only the tag run matters — it is the one that emits the X.Y.Z / X.Y
|
|
# image tags, and it publishes `latest` and `sha-<short>` too, since it is
|
|
# the same commit. Skip the branch run rather than racing or cancelling it.
|
|
# Ordinary pushes to master (any message but `chore(release):`) still build.
|
|
if: >-
|
|
github.event_name != 'push' ||
|
|
startsWith(github.ref, 'refs/tags/') ||
|
|
!startsWith(github.event.head_commit.message, 'chore(release):')
|
|
runs-on: docker
|
|
container:
|
|
image: docker:27-dind
|
|
options: --privileged
|
|
permissions:
|
|
contents: read
|
|
packages: write
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- image: jorgecuadros-api
|
|
dockerfile: docker/api.Dockerfile
|
|
- image: jorgecuadros-web
|
|
dockerfile: docker/web.Dockerfile
|
|
steps:
|
|
- name: Install Node.js for actions
|
|
run: apk add --no-cache nodejs npm
|
|
|
|
- uses: actions/checkout@v4
|
|
|
|
- uses: docker/setup-buildx-action@v3
|
|
|
|
- uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
|
|
|
- id: meta
|
|
uses: docker/metadata-action@v5
|
|
with:
|
|
images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ matrix.image }}
|
|
tags: |
|
|
type=semver,pattern={{version}}
|
|
type=semver,pattern={{major}}.{{minor}}
|
|
type=ref,event=branch
|
|
type=sha,format=short,prefix=sha-
|
|
type=raw,value=latest,enable={{is_default_branch}}
|
|
|
|
- uses: docker/build-push-action@v5
|
|
with:
|
|
context: .
|
|
file: ${{ matrix.dockerfile }}
|
|
push: true
|
|
tags: ${{ steps.meta.outputs.tags }}
|
|
labels: ${{ steps.meta.outputs.labels }}
|
|
platforms: linux/amd64
|
|
build-args: |
|
|
APP_VERSION=${{ steps.meta.outputs.version }}
|
|
GIT_SHA=${{ github.sha }}
|
|
BUILD_DATE=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.created'] }}
|