Every push rebuilt all six images regardless of what it touched. A git diff against github.event.before now gates each build step. Two things this needs to work at all: fetch-depth 0, because the default shallow clone has a single commit and nothing to diff against, and git installed in the dind container, which had node and npm but not git. The path mapping follows the build contexts rather than intuition — the Go images use a root context and COPY shared/, so shared/ fans out to all three, while the Next images use their own directory and cannot be affected from outside it. Anything that leaves no trustworthy base commit — manual run, new branch, force-push whose old head is gone — lists every tracked file instead, so the fallback is one code path rather than two. Known gap, documented: a repo variable change pushes no commit, so nothing rebuilds. workflow_dispatch is the escape hatch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,16 +4,88 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
# Manual runs rebuild everything: there is no "before" commit to diff
|
||||
# against, which the change detection below treats as "build it all". That
|
||||
# is also the escape hatch for a repo VARIABLE change — editing API_URL or
|
||||
# ADMIN_ENV pushes no commit, so nothing would rebuild on its own.
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-docker
|
||||
container: docker:dind
|
||||
steps:
|
||||
- name: Setup Node
|
||||
run: apk add --update nodejs npm
|
||||
# git is needed twice over: actions/checkout clones with it, and the
|
||||
# change detection below diffs with it.
|
||||
- name: Setup
|
||||
run: apk add --update nodejs npm git
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
# The default shallow clone has one commit, which cannot be
|
||||
# diffed against the previous push.
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Work out what changed
|
||||
id: changed
|
||||
run: |
|
||||
set -eu
|
||||
|
||||
BEFORE="${{ github.event.before }}"
|
||||
ZERO="0000000000000000000000000000000000000000"
|
||||
|
||||
# Build everything whenever the comparison cannot be trusted:
|
||||
# a manual run, a brand-new branch, or a force-push whose old
|
||||
# head is no longer in the repository. Listing every tracked
|
||||
# file makes every filter below match, so there is no second
|
||||
# code path to keep correct.
|
||||
if [ -z "$BEFORE" ] || [ "$BEFORE" = "$ZERO" ] || ! git cat-file -e "${BEFORE}^{commit}" 2>/dev/null; then
|
||||
echo "No usable base commit — building every image."
|
||||
git ls-files > /tmp/changed.txt
|
||||
else
|
||||
git diff --name-only "$BEFORE" HEAD > /tmp/changed.txt
|
||||
fi
|
||||
|
||||
echo "--- changed files ---"
|
||||
cat /tmp/changed.txt
|
||||
echo "---------------------"
|
||||
|
||||
# A change to the workflow itself can change a build arg, and
|
||||
# a build arg is baked into the image, so it rebuilds all.
|
||||
if grep -qE '^\.gitea/workflows/' /tmp/changed.txt; then
|
||||
ALL=1
|
||||
else
|
||||
ALL=0
|
||||
fi
|
||||
|
||||
flag() {
|
||||
name="$1"
|
||||
pattern="$2"
|
||||
if [ "$ALL" = "1" ] || grep -qE "$pattern" /tmp/changed.txt; then
|
||||
echo "$name=true" >> "$GITHUB_OUTPUT"
|
||||
echo "build $name"
|
||||
else
|
||||
echo "$name=false" >> "$GITHUB_OUTPUT"
|
||||
echo "skip $name"
|
||||
fi
|
||||
}
|
||||
|
||||
# The three Go images build from the repo root and COPY
|
||||
# shared/ plus their own directory, so shared/ rebuilds all
|
||||
# three. proto/ is in server's list as insurance: the
|
||||
# generated pb is committed under server/, but a proto change
|
||||
# that someone regenerates in the same push should not depend
|
||||
# on that ordering.
|
||||
flag server '^(server/|shared/|proto/|go\.work)'
|
||||
flag sitesvc '^(sitesvc/|shared/|go\.work)'
|
||||
flag admin '^(admin/|shared/|go\.work)'
|
||||
|
||||
# The three Next images use their own directory as the build
|
||||
# context, so nothing outside it can affect them.
|
||||
flag web '^web/'
|
||||
flag site '^site/'
|
||||
flag adminsite '^adminsite/'
|
||||
|
||||
- name: Log in to registry
|
||||
run: |
|
||||
@@ -22,6 +94,7 @@ jobs:
|
||||
-u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
||||
|
||||
- name: Build and push server image
|
||||
if: steps.changed.outputs.server == 'true'
|
||||
run: |
|
||||
IMAGE="${{ vars.DOCKER_HOST }}/${{ github.repository_owner }}/vantage/server:latest"
|
||||
# Root context: server depends on the shared module.
|
||||
@@ -29,6 +102,7 @@ jobs:
|
||||
docker push "$IMAGE"
|
||||
|
||||
- name: Build and push web image
|
||||
if: steps.changed.outputs.web == 'true'
|
||||
run: |
|
||||
IMAGE="${{ vars.DOCKER_HOST }}/${{ github.repository_owner }}/vantage/web:latest"
|
||||
docker build \
|
||||
@@ -39,6 +113,7 @@ jobs:
|
||||
docker push "$IMAGE"
|
||||
|
||||
- name: Build and push site image
|
||||
if: steps.changed.outputs.site == 'true'
|
||||
run: |
|
||||
IMAGE="${{ vars.DOCKER_HOST }}/${{ github.repository_owner }}/vantage/site:latest"
|
||||
docker build \
|
||||
@@ -50,6 +125,7 @@ jobs:
|
||||
docker push "$IMAGE"
|
||||
|
||||
- name: Build and push sitesvc image
|
||||
if: steps.changed.outputs.sitesvc == 'true'
|
||||
run: |
|
||||
IMAGE="${{ vars.DOCKER_HOST }}/${{ github.repository_owner }}/vantage/sitesvc:latest"
|
||||
# Root context: sitesvc depends on the shared module.
|
||||
@@ -57,6 +133,7 @@ jobs:
|
||||
docker push "$IMAGE"
|
||||
|
||||
- name: Build and push admin image
|
||||
if: steps.changed.outputs.admin == 'true'
|
||||
run: |
|
||||
IMAGE="${{ vars.DOCKER_HOST }}/${{ github.repository_owner }}/vantage/admin:latest"
|
||||
# Root context: admin depends on the shared module.
|
||||
@@ -64,6 +141,7 @@ jobs:
|
||||
docker push "$IMAGE"
|
||||
|
||||
- name: Build and push adminsite image
|
||||
if: steps.changed.outputs.adminsite == 'true'
|
||||
run: |
|
||||
IMAGE="${{ vars.DOCKER_HOST }}/${{ github.repository_owner }}/vantage/adminsite:latest"
|
||||
docker build \
|
||||
|
||||
@@ -542,13 +542,26 @@ GOOS=linux GOARCH=amd64 go build \
|
||||
|
||||
Builds and pushes six images to the Gitea container registry: `server`, `web`, `site`, `sitesvc`, `admin` and `adminsite`.
|
||||
|
||||
Note that despite the name, **this workflow does not deploy** — it only builds and pushes. There is no SSH step and no path filter; every push to `main` rebuilds all three images. Rolling them out is a separate manual step on the host:
|
||||
Note that despite the name, **this workflow does not deploy** — it only builds and pushes. There is no SSH step. Rolling images out is a separate manual step on the host:
|
||||
|
||||
```bash
|
||||
cd /opt/vantage && docker compose -f docker-compose.yml -f docker-compose.site.yml pull && \
|
||||
docker compose -f docker-compose.yml -f docker-compose.site.yml up -d --remove-orphans
|
||||
```
|
||||
|
||||
**Each image only rebuilds when its own inputs changed.** A `git diff` against `github.event.before` decides, which is why the checkout uses `fetch-depth: 0` — the default shallow clone has one commit and nothing to diff — and why `git` is installed in the `docker:dind` container. The mapping follows the build contexts exactly:
|
||||
|
||||
| Image | Rebuilds when |
|
||||
| --- | --- |
|
||||
| `server` | `server/`, `shared/`, `proto/`, `go.work` |
|
||||
| `admin` | `admin/`, `shared/`, `go.work` |
|
||||
| `sitesvc` | `sitesvc/`, `shared/`, `go.work` |
|
||||
| `web` · `site` · `adminsite` | their own directory only |
|
||||
|
||||
`shared/` fans out to all three Go images because each of their Dockerfiles copies `shared/` from a root context — **if a fourth service ever imports `shared/`, add it to that list or it will ship stale**. A change to the workflow file rebuilds everything, since a build arg is baked into the image. So does anything that leaves no trustworthy base commit: a manual `workflow_dispatch`, a new branch, or a force-push whose old head is gone.
|
||||
|
||||
The gap this leaves: **changing a repo variable pushes no commit, so nothing rebuilds.** After editing `API_URL`, `ADMIN_API_URL`, `HQ_URL` or `ADMIN_ENV`, run the workflow manually — that is what `workflow_dispatch` is there for. Base images also stop being refreshed on a service nobody touches; a periodic manual run covers that.
|
||||
|
||||
### Tagging
|
||||
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user