Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -203,8 +203,14 @@ GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# Where custom images live — one registry + namespace for every built image
# (warden, fiber, takeout-manager/-worker, kolibri-oidc, iperf3-server/-exporter,
# homelab-devbox). Tags are pinned literally in each compose file, so there are
# no per-app *_IMAGE_TAG / *_REGISTRY_* variables.
# homelab-devbox).
#
# Image tags: each compose file pins a deploy version literally as the default of
# image: .../<name>:${IMAGE_TAG:-<version>}
# Deploys use that pinned default (IMAGE_TAG unset) — to roll out a new release, bump
# the literal version in the compose files and redeploy. IMAGE_TAG is only set to
# `latest` by the build tooling (CI build.yml, `task build`/`publish`) so the same
# image: line produces :latest at build time; do NOT set it in .env.
REGISTRY=ghcr.io
REGISTRY_NAMESPACE=your-github-username

Expand Down
7 changes: 5 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ jobs:
- name: Build${{ github.event_name != 'pull_request' && ' & push' || '' }} ${{ matrix.image_name }}
run: |
set -euo pipefail
# The compose image: tag is ${IMAGE_TAG:-<pinned deploy version>}. Builds
# must tag :latest, not the pinned deploy version, so set IMAGE_TAG=latest
# — bake then resolves image: to :latest for every service.
export IMAGE_TAG=latest
repo="${REGISTRY}/${REGISTRY_NAMESPACE}/${{ matrix.image_name }}"
# bake resolves relative context/dockerfile from CWD, so run it from
# the compose file's directory (compose-relative paths then resolve).
Expand All @@ -111,8 +115,7 @@ jobs:
args+=(--set "${{ matrix.service }}.output=type=cacheonly")
docker "${args[@]}"
else
# Push the compose image tag (:latest), then add the immutable :sha
# as a server-side retag. (bake --set tags= doesn't split a comma list.)
# Push :latest, then add the immutable :sha as a server-side retag.
args+=(--push)
docker "${args[@]}"
docker buildx imagetools create "${repo}:latest" --tag "${repo}:${GITHUB_SHA}"
Expand Down
6 changes: 4 additions & 2 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,17 +80,19 @@ tasks:
- uv run pytest

# Build images locally via buildx bake (reads compose build: sections).
# IMAGE_TAG=latest: the compose image: tag is ${IMAGE_TAG:-<pinned version>}, so
# builds/pushes target :latest, never the pinned deploy version.
build:
desc: "Build an app's image(s) locally — task build -- warden"
dir: stacks/apps/{{.CLI_ARGS}}
cmds:
- docker buildx bake --allow fs.read=* -f docker-compose.yml
- IMAGE_TAG=latest docker buildx bake --allow fs.read=* -f docker-compose.yml

publish:
desc: "Build & push an app's image(s) — task publish -- warden (requires registry login)"
dir: stacks/apps/{{.CLI_ARGS}}
cmds:
- docker buildx bake --allow fs.read=* -f docker-compose.yml --push
- IMAGE_TAG=latest docker buildx bake --allow fs.read=* -f docker-compose.yml --push

test-fast:
desc: Run core tests with faster execution
Expand Down
2 changes: 1 addition & 1 deletion stacks/apps/claudecodeui/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3.9"

services:
claudecodeui:
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/homelab-devbox:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/homelab-devbox:${IMAGE_TAG:-3.20.0}
build:
context: ../../../images/devbox
dockerfile: Dockerfile
Expand Down
2 changes: 1 addition & 1 deletion stacks/apps/code-server/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3.9"

services:
code-server:
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/homelab-devbox:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/homelab-devbox:${IMAGE_TAG:-3.20.0}
build:
context: ../../../images/devbox
dockerfile: Dockerfile
Expand Down
2 changes: 1 addition & 1 deletion stacks/apps/fiber/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
fiber:
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/fiber:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/fiber:${IMAGE_TAG:-3.20.0}
build:
context: .
dockerfile: app/Dockerfile
Expand Down
2 changes: 1 addition & 1 deletion stacks/apps/kolibri/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3.9"

services:
kolibri:
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/kolibri-oidc:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/kolibri-oidc:${IMAGE_TAG:-3.20.0}
build:
context: .
dockerfile: Dockerfile
Expand Down
4 changes: 2 additions & 2 deletions stacks/apps/takeout-manager/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ services:
# - GitHub Container Registry: ghcr.io/YOUR_USERNAME/takeout-manager:latest
# - Docker Hub: YOUR_USERNAME/takeout-manager:latest
# - Private Registry: registry.YOUR_DOMAIN.com/takeout-manager:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/takeout-manager:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/takeout-manager:${IMAGE_TAG:-3.20.0}
build:
context: manager
dockerfile: Dockerfile
Expand Down Expand Up @@ -48,7 +48,7 @@ services:
- "homepage.description=Google Photos Takeout Management"

takeout-worker:
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/takeout-worker:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/takeout-worker:${IMAGE_TAG:-3.20.0}
build:
context: worker
dockerfile: Dockerfile
Expand Down
2 changes: 1 addition & 1 deletion stacks/apps/warden/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: "3.9"

services:
warden:
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/warden:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/warden:${IMAGE_TAG:-3.20.0}
build:
context: .
dockerfile: app/Dockerfile
Expand Down
4 changes: 2 additions & 2 deletions stacks/monitoring/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ services:
- "traefik.enable=false"

iperf3-server:
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/iperf3-server:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/iperf3-server:${IMAGE_TAG:-3.20.0}
build:
context: .
dockerfile: Dockerfile.iperf3-server
Expand All @@ -246,7 +246,7 @@ services:
- "traefik.enable=false"

iperf3-exporter:
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/iperf3-exporter:latest
image: ${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/iperf3-exporter:${IMAGE_TAG:-3.20.0}
build:
context: custom-exporter
dockerfile: Dockerfile
Expand Down
7 changes: 5 additions & 2 deletions tools/ci/ci/affected.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,11 @@ def as_dict(self) -> dict:
def _image_key(image: str) -> str:
image = image.split("@", 1)[0] # drop @sha256:... digest
slash = image.rfind("/")
colon = image.rfind(":")
if colon > slash: # a tag (not a registry :port, which sits before the last /)
# The tag separator is the FIRST colon after the last '/'. Using rfind here would
# grab a colon inside a templated tag like ${IMAGE_TAG:-3.20.0}; image names can't
# contain ':', so the first colon after the name is always the tag separator.
colon = image.find(":", slash + 1)
if colon != -1: # a tag (registry :port sits before the last /, so it's excluded)
image = image[:colon]
return image

Expand Down
13 changes: 13 additions & 0 deletions tools/ci/tests/test_affected.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,19 @@ def test_image_name_is_bare_last_segment():
assert templated.image_name == "warden"


def test_templated_tag_with_default_is_stripped():
# The deploy pin is ${IMAGE_TAG:-3.20.0} — its ':-' colon must not be mistaken
# for the name:tag separator (which is the first colon after the last '/').
u = _unit(
"warden",
"${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/warden:${IMAGE_TAG:-3.20.0}",
"stacks/apps/warden",
["stacks/apps/warden/**"],
)
assert u.image_name == "warden"
assert u.image_key == "${REGISTRY:-ghcr.io}/${REGISTRY_NAMESPACE:-chutch3}/warden"


def test_tooling_change_flags_everything():
assert tooling_changed(["tools/ci/ci/affected.py"]) is True
assert tooling_changed([".github/workflows/build.yml"]) is True
Expand Down
Loading