vantagectl is a released tool, not a running service. An operator restoring a database should be able to name the version they ran, and ":latest, rebuilt whenever main moved" cannot be named after the fact. The image now builds in vantagectl-release.yml on a vantagectl/v* tag, tagged with that version as well as latest, with VERSION passed through so the binary inside reports the tag rather than "dev". server-deploy.yml no longer builds it and is back to seven images. The cost is that a shared/ fix reaches the image only at the next release rather than the next push to main. That is the intended trade and is written down in CLAUDE.md next to the trigger table.
193 lines
8.9 KiB
YAML
193 lines
8.9 KiB
YAML
name: Server Deploy
|
|
|
|
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:
|
|
# 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 here build from the repo root and
|
|
# COPY shared/ plus their own directory, so shared/ rebuilds
|
|
# all three. vantagectl also depends on shared/ but is NOT
|
|
# built here: it is a released tool, so its image is built and
|
|
# version-tagged by vantagectl-release.yml on a vantagectl/v*
|
|
# tag. A shared/ change therefore reaches it at the next
|
|
# release rather than on the next push to main, which is the
|
|
# point — an operator restoring a database should be running a
|
|
# version they can name, not whatever main built last night.
|
|
# 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/|default_steps/|go\.work)'
|
|
flag sitesvc '^(sitesvc/|shared/|go\.work)'
|
|
flag admin '^(admin/|shared/|go\.work)'
|
|
|
|
# The three Next images and the docs site use their own
|
|
# directory as the build context, so nothing outside it can
|
|
# affect them.
|
|
flag web '^web/'
|
|
flag site '^site/'
|
|
flag adminsite '^adminsite/'
|
|
flag docsite '^docsite/'
|
|
|
|
- name: Log in to registry
|
|
run: |
|
|
echo "${{ secrets.RELEASE_TOKEN }}" | \
|
|
docker login ${{ vars.DOCKER_HOST }} \
|
|
-u "${{ secrets.REGISTRY_USER }}" --password-stdin
|
|
|
|
- name: Set up Go
|
|
if: steps.changed.outputs.server == 'true'
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: "1.26"
|
|
cache: true
|
|
cache-dependency-path: server/go.sum
|
|
|
|
- name: Verify the OpenAPI document is current
|
|
if: steps.changed.outputs.server == 'true'
|
|
run: |
|
|
go install github.com/swaggo/swag/v2/cmd/swag@v2.0.0-rc5
|
|
cd server
|
|
swag init --generalInfo cmd/main.go --dir ./,../shared \
|
|
--output internal/api/docs --outputTypes json --v3.1
|
|
mv -f internal/api/docs/swagger.json internal/api/docs/openapi.json
|
|
git diff --exit-code internal/api/docs/openapi.json
|
|
|
|
- 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.
|
|
docker build -t "$IMAGE" -f server/Dockerfile .
|
|
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 \
|
|
--build-arg NEXT_PUBLIC_HQ_URL="${{ vars.HQ_URL }}" \
|
|
-t "$IMAGE" \
|
|
-f web/Dockerfile web/
|
|
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 \
|
|
--build-arg NEXT_PUBLIC_SITE_API="${{ vars.SITE_API_URL }}" \
|
|
--build-arg NEXT_PUBLIC_CONTACT_EMAIL="support@hostxtra.co.uk" \
|
|
--build-arg NEXT_PUBLIC_ADMIN_API_URL="${{ vars.ADMIN_API_URL }}" \
|
|
-t "$IMAGE" \
|
|
-f site/Dockerfile site/
|
|
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.
|
|
docker build -t "$IMAGE" -f sitesvc/Dockerfile .
|
|
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.
|
|
docker build -t "$IMAGE" -f admin/Dockerfile .
|
|
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 \
|
|
--build-arg NEXT_PUBLIC_ADMIN_API_URL="${{ vars.ADMIN_API_URL }}" \
|
|
--build-arg NEXT_PUBLIC_ADMIN_ENV="${{ vars.ADMIN_ENV }}" \
|
|
--build-arg NEXT_PUBLIC_PADDLE_CLIENT_TOKEN="${{ vars.PADDLE_CLIENT_TOKEN }}" \
|
|
--build-arg NEXT_PUBLIC_PADDLE_ENV="${{ vars.PADDLE_ENV }}" \
|
|
--build-arg NEXT_PUBLIC_SITE_URL="${{ vars.SITE_URL }}" \
|
|
-t "$IMAGE" \
|
|
-f adminsite/Dockerfile adminsite/
|
|
docker push "$IMAGE"
|
|
|
|
- name: Build and push docsite image
|
|
if: steps.changed.outputs.docsite == 'true'
|
|
run: |
|
|
IMAGE="${{ vars.DOCKER_HOST }}/${{ github.repository_owner }}/vantage/docsite:latest"
|
|
# DOCS_BASE_URL must match the proxy location that routes to
|
|
# this container and the directory the image serves from.
|
|
docker build \
|
|
-t "$IMAGE" \
|
|
-f docsite/Dockerfile docsite/
|
|
docker push "$IMAGE"
|