@@ -19,20 +19,19 @@ concurrency:
1919
2020env :
2121 REPO_SLUG : centralized-logging
22- DOCKERHUB_NAMESPACE : " " # set to mirror to Docker Hub or leave empty
22+ DOCKERHUB_NAMESPACE : " " # set to mirror to Docker Hub or leave empty
2323 PLATFORMS : linux/amd64,linux/arm64
2424
2525jobs :
2626 images :
2727 name : Build and Push Images (GHCR)
2828 runs-on : ubuntu-latest
29- # Push images only on master or version tags; still allows manual/PR runs for build validation
29+ # Push images only on master, tags, or manual dispatch (PRs still build if you press Run)
3030 if : >
3131 github.event_name == 'workflow_dispatch' ||
3232 startsWith(github.ref, 'refs/heads/master') ||
3333 startsWith(github.ref, 'refs/tags/v')
3434 env :
35- # expose secrets as env so conditions use env.* instead of secrets.*
3635 DOCKERHUB_USERNAME : ${{ secrets.DOCKERHUB_USERNAME }}
3736 DOCKERHUB_TOKEN : ${{ secrets.DOCKERHUB_TOKEN }}
3837 steps :
@@ -47,39 +46,42 @@ jobs:
4746 - name : Set up Docker Buildx
4847 uses : docker/setup-buildx-action@v3
4948
50- # Per-service metadata (tags)
51- - name : Meta (userapi)
52- id : meta_user
53- uses : docker/metadata-action@v5
54- with :
55- images : ghcr.io/${{ github.repository_owner }}/${{ env.REPO_SLUG }}/userapi
56- tags : |
57- type=raw,value=edge,enable=${{ startsWith(github.ref,'refs/heads/master') || startsWith(github.ref,'refs/tags/v') || github.event_name == 'workflow_dispatch' }}
58- type=raw,value=latest,enable=${{ startsWith(github.ref,'refs/heads/master') }}
59- type=semver,pattern={{version}},enable=${{ startsWith(github.ref,'refs/tags/v') }}
60- type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref,'refs/tags/v') }}
61-
62- - name : Meta (api)
63- id : meta_api
64- uses : docker/metadata-action@v5
65- with :
66- images : ghcr.io/${{ github.repository_owner }}/${{ env.REPO_SLUG }}/api
67- tags : |
68- type=raw,value=edge,enable=${{ startsWith(github.ref,'refs/heads/master') || startsWith(github.ref,'refs/tags/v') || github.event_name == 'workflow_dispatch' }}
69- type=raw,value=latest,enable=${{ startsWith(github.ref,'refs/heads/master') }}
70- type=semver,pattern={{version}},enable=${{ startsWith(github.ref,'refs/tags/v') }}
71- type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref,'refs/tags/v') }}
72-
73- - name : Meta (web)
74- id : meta_web
75- uses : docker/metadata-action@v5
76- with :
77- images : ghcr.io/${{ github.repository_owner }}/${{ env.REPO_SLUG }}/web
78- tags : |
79- type=raw,value=edge,enable=${{ startsWith(github.ref,'refs/heads/master') || startsWith(github.ref,'refs/tags/v') || github.event_name == 'workflow_dispatch' }}
80- type=raw,value=latest,enable=${{ startsWith(github.ref,'refs/heads/master') }}
81- type=semver,pattern={{version}},enable=${{ startsWith(github.ref,'refs/tags/v') }}
82- type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref,'refs/tags/v') }}
49+ # Build a single-line, comma-separated tag list per service
50+ - name : Compute tags
51+ id : tags
52+ shell : bash
53+ run : |
54+ set -euo pipefail
55+ OWNER_LC="${GITHUB_REPOSITORY_OWNER,,}" # force lowercase for GHCR
56+ REPO_SLUG="${REPO_SLUG}"
57+
58+ # Always push :edge
59+ user_tags="ghcr.io/${OWNER_LC}/${REPO_SLUG}/userapi:edge"
60+ api_tags="ghcr.io/${OWNER_LC}/${REPO_SLUG}/api:edge"
61+ web_tags="ghcr.io/${OWNER_LC}/${REPO_SLUG}/web:edge"
62+
63+ REF="${GITHUB_REF}"
64+
65+ # On master, also push :latest
66+ if [[ "$REF" == "refs/heads/master" ]]; then
67+ user_tags+=",ghcr.io/${OWNER_LC}/${REPO_SLUG}/userapi:latest"
68+ api_tags+=",ghcr.io/${OWNER_LC}/${REPO_SLUG}/api:latest"
69+ web_tags+=",ghcr.io/${OWNER_LC}/${REPO_SLUG}/web:latest"
70+ fi
71+
72+ # On tag vX.Y.Z, also push :vX.Y.Z and :X.Y
73+ if [[ "$REF" == refs/tags/v* ]]; then
74+ ver="${REF#refs/tags/}" # vX.Y.Z
75+ short="${ver#v}" # X.Y.Z
76+ minor="${short%.*}" # X.Y
77+ user_tags+=",ghcr.io/${OWNER_LC}/${REPO_SLUG}/userapi:${ver},ghcr.io/${OWNER_LC}/${REPO_SLUG}/userapi:${minor}"
78+ api_tags+=",ghcr.io/${OWNER_LC}/${REPO_SLUG}/api:${ver},ghcr.io/${OWNER_LC}/${REPO_SLUG}/api:${minor}"
79+ web_tags+=",ghcr.io/${OWNER_LC}/${REPO_SLUG}/web:${ver},ghcr.io/${OWNER_LC}/${REPO_SLUG}/web:${minor}"
80+ fi
81+
82+ echo "user_tags=$user_tags" >> "$GITHUB_OUTPUT"
83+ echo "api_tags=$api_tags" >> "$GITHUB_OUTPUT"
84+ echo "web_tags=$web_tags" >> "$GITHUB_OUTPUT"
8385
8486 - name : Login to GHCR
8587 uses : docker/login-action@v3
@@ -108,16 +110,17 @@ jobs:
108110 with :
109111 files : ./docker-bake.hcl
110112 push : true
113+ variables : |
114+ OWNER=${{ github.repository_owner }}
115+ REPO_SLUG=${{ env.REPO_SLUG }}
111116 set : |
112117 *.platform=${{ env.PLATFORMS }}
113118 *.cache-from=type=local,src=/tmp/.buildx-cache
114119 *.cache-to=type=local,dest=/tmp/.buildx-cache-new,mode=max
115120 *.labels.org.opencontainers.image.revision=${{ github.sha }}
116- OWNER=${{ github.repository_owner }}
117- REPO_SLUG=${{ env.REPO_SLUG }}
118- userapi.tags=${{ steps.meta_user.outputs.tags }}
119- api.tags=${{ steps.meta_api.outputs.tags }}
120- web.tags=${{ steps.meta_web.outputs.tags }}
121+ userapi.tags=${{ steps.tags.outputs.user_tags }}
122+ api.tags=${{ steps.tags.outputs.api_tags }}
123+ web.tags=${{ steps.tags.outputs.web_tags }}
121124
122125 - name : Save build cache
123126 if : always()
@@ -129,11 +132,16 @@ jobs:
129132 if : ${{ env.DOCKERHUB_NAMESPACE != '' && env.DOCKERHUB_USERNAME != '' && env.DOCKERHUB_TOKEN != '' }}
130133 run : |
131134 set -euo pipefail
132- mirror() { local svc="$1"; shift; for t in "$@"; do tg="$(basename "$t")"; \
133- ghcr="ghcr.io/${{ github.repository_owner }}/${{ env.REPO_SLUG }}/${svc}:${tg}"; \
134- hub="${{ env.DOCKERHUB_NAMESPACE }}/${{ env.REPO_SLUG }}-${svc}:${tg}"; \
135- echo "Mirroring $ghcr -> $hub"; docker pull "$ghcr"; docker tag "$ghcr" "$hub"; docker push "$hub"; done; }
136- mapfile -t USER_TAGS <<< "${{ steps.meta_user.outputs.tags }}"
137- mapfile -t API_TAGS <<< "${{ steps.meta_api.outputs.tags }}"
138- mapfile -t WEB_TAGS <<< "${{ steps.meta_web.outputs.tags }}"
139- mirror userapi "${USER_TAGS[@]}"; mirror api "${API_TAGS[@]}"; mirror web "${WEB_TAGS[@]}"
135+ mirror() { local svc="$1"; local list="$2"; IFS=',' read -ra arr <<< "$list"; \
136+ for img in "${arr[@]}"; do
137+ tag="${img##*:}"
138+ hub="${DOCKERHUB_NAMESPACE}/${REPO_SLUG}-${svc}:${tag}"
139+ echo "Mirroring $img -> $hub"
140+ docker pull "$img"
141+ docker tag "$img" "$hub"
142+ docker push "$hub"
143+ done
144+ }
145+ mirror userapi "${{ steps.tags.outputs.user_tags }}"
146+ mirror api "${{ steps.tags.outputs.api_tags }}"
147+ mirror web "${{ steps.tags.outputs.web_tags }}"
0 commit comments