Skip to content

Commit f6d0470

Browse files
committed
Modernize Docker build with parameterization and registry flexibility
Major improvements: - Add full parameterization (registry, namespace, image name) - Auto-detect registry: temporalio → docker.io, others → ghcr.io - Separate workflow for managing 'latest' tag on release events - Dynamic Docker labels using GITHUB_REPOSITORY variable - Add packages:write permission for GHCR - Remove artifact uploading (no longer needed) Benefits: - Works out-of-box for both upstream and forks - Flexible registry support (Docker Hub, GHCR, any registry) - Clean separation of release vs latest-tag concerns - Proper package association in GitHub Configuration requirements: - DOCKER_USERNAME and DOCKER_PASSWORD secrets needed for Docker Hub - GITHUB_TOKEN automatically provides GHCR access
1 parent 8918ee7 commit f6d0470

4 files changed

Lines changed: 218 additions & 42 deletions

File tree

.github/docker/docker-bake.hcl

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
variable "IMAGE_REPO" {
2-
default = "temporalio"
2+
default = "ghcr.io"
3+
}
4+
5+
variable "IMAGE_NAMESPACE" {
6+
default = ""
7+
}
8+
9+
variable "IMAGE_NAME" {
10+
default = "temporal"
11+
}
12+
13+
variable "GITHUB_REPOSITORY" {
14+
default = "temporalio/cli"
315
}
416

517
variable "IMAGE_SHA_TAG" {}
@@ -27,9 +39,9 @@ target "cli" {
2739
dockerfile = ".github/docker/cli.Dockerfile"
2840
context = "."
2941
tags = compact([
30-
"${IMAGE_REPO}/temporal:${IMAGE_SHA_TAG}",
31-
"${IMAGE_REPO}/temporal:${VERSION}",
32-
TAG_LATEST ? "${IMAGE_REPO}/temporal:latest" : "",
42+
IMAGE_REPO == "" ? "${IMAGE_NAMESPACE}/${IMAGE_NAME}:${IMAGE_SHA_TAG}" : "${IMAGE_REPO}/${IMAGE_NAMESPACE}/${IMAGE_NAME}:${IMAGE_SHA_TAG}",
43+
IMAGE_REPO == "" ? "${IMAGE_NAMESPACE}/${IMAGE_NAME}:${VERSION}" : "${IMAGE_REPO}/${IMAGE_NAMESPACE}/${IMAGE_NAME}:${VERSION}",
44+
TAG_LATEST ? (IMAGE_REPO == "" ? "${IMAGE_NAMESPACE}/${IMAGE_NAME}:latest" : "${IMAGE_REPO}/${IMAGE_NAMESPACE}/${IMAGE_NAME}:latest") : "",
3345
])
3446
platforms = ["linux/amd64", "linux/arm64"]
3547
args = {
@@ -38,8 +50,8 @@ target "cli" {
3850
labels = {
3951
"org.opencontainers.image.title" = "temporal"
4052
"org.opencontainers.image.description" = "Temporal CLI"
41-
"org.opencontainers.image.url" = "https://github.com/temporalio/cli"
42-
"org.opencontainers.image.source" = "https://github.com/temporalio/cli"
53+
"org.opencontainers.image.url" = "https://github.com/${GITHUB_REPOSITORY}"
54+
"org.opencontainers.image.source" = "https://github.com/${GITHUB_REPOSITORY}"
4355
"org.opencontainers.image.licenses" = "MIT"
4456
"org.opencontainers.image.revision" = "${CLI_SHA}"
4557
"org.opencontainers.image.created" = timestamp()

.github/workflows/build-and-publish.yml

Lines changed: 79 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@ on:
1111
description: "Version tag for the release (required if publish is true)"
1212
required: false
1313
type: string
14+
registry:
15+
description: "Container registry (docker.io, ghcr.io, etc.)"
16+
required: false
17+
type: string
18+
default: ""
19+
registry_namespace:
20+
description: "Registry namespace/organization"
21+
required: false
22+
type: string
23+
default: ""
24+
image_name:
25+
description: "Image name"
26+
required: false
27+
type: string
28+
default: "temporal"
1429
secrets:
1530
DOCKER_USERNAME:
1631
required: false
@@ -84,18 +99,14 @@ jobs:
8499
- name: Set up Docker Buildx
85100
uses: docker/setup-buildx-action@v3
86101

87-
- name: Log in to Docker Hub
88-
if: inputs.publish && github.repository_owner == 'temporalio'
89-
uses: docker/login-action@v3
90-
with:
91-
username: ${{ secrets.DOCKER_USERNAME }}
92-
password: ${{ secrets.DOCKER_PASSWORD }}
93-
94102
- name: Get build metadata
95103
id: meta
96104
env:
97105
INPUT_VERSION: ${{ inputs.version }}
98106
INPUT_PUBLISH: ${{ inputs.publish }}
107+
INPUT_REGISTRY: ${{ inputs.registry }}
108+
INPUT_REGISTRY_NAMESPACE: ${{ inputs.registry_namespace }}
109+
INPUT_IMAGE_NAME: ${{ inputs.image_name }}
99110
REPO_OWNER: ${{ github.repository_owner }}
100111
run: |
101112
echo "cli_sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
@@ -111,30 +122,64 @@ jobs:
111122
echo "version=snapshot" >> $GITHUB_OUTPUT
112123
fi
113124
114-
# Determine image repo based on repository owner
115-
if [[ "$REPO_OWNER" == "temporalio" ]]; then
116-
echo "image_repo=temporalio" >> $GITHUB_OUTPUT
125+
# Determine registry (with auto-detection for temporalio vs forks)
126+
REGISTRY="$INPUT_REGISTRY"
127+
if [[ -z "$REGISTRY" ]]; then
128+
if [[ "$REPO_OWNER" == "temporalio" ]]; then
129+
REGISTRY="docker.io"
130+
else
131+
REGISTRY="ghcr.io"
132+
fi
133+
fi
134+
135+
# Determine registry type for authentication
136+
if [[ "$REGISTRY" == "ghcr.io" ]]; then
137+
echo "registry_type=ghcr" >> $GITHUB_OUTPUT
138+
elif [[ "$REGISTRY" == "docker.io" ]]; then
139+
echo "registry_type=dockerhub" >> $GITHUB_OUTPUT
117140
else
118-
echo "image_repo=$REPO_OWNER" >> $GITHUB_OUTPUT
141+
echo "registry_type=other" >> $GITHUB_OUTPUT
119142
fi
120143
121-
- name: Check if release is latest
122-
if: inputs.publish
123-
id: check_latest
124-
uses: actions/github-script@v7
144+
# Set namespace (defaults to repository owner)
145+
NAMESPACE="$INPUT_REGISTRY_NAMESPACE"
146+
if [[ -z "$NAMESPACE" ]]; then
147+
NAMESPACE="$REPO_OWNER"
148+
fi
149+
150+
# Set image name (defaults to 'temporal')
151+
IMAGE_NAME="$INPUT_IMAGE_NAME"
152+
if [[ -z "$IMAGE_NAME" ]]; then
153+
IMAGE_NAME="temporal"
154+
fi
155+
156+
# For Docker Hub, use empty string as registry (special case)
157+
if [[ "$REGISTRY" == "docker.io" ]]; then
158+
echo "image_repo=" >> $GITHUB_OUTPUT
159+
else
160+
echo "image_repo=${REGISTRY}" >> $GITHUB_OUTPUT
161+
fi
162+
163+
echo "image_namespace=${NAMESPACE}" >> $GITHUB_OUTPUT
164+
echo "image_name=${IMAGE_NAME}" >> $GITHUB_OUTPUT
165+
166+
- name: Log in to GitHub Container Registry
167+
if: inputs.publish && steps.meta.outputs.registry_type == 'ghcr'
168+
uses: docker/login-action@v3
125169
with:
126-
script: |
127-
const { data: release } = await github.rest.repos.getReleaseByTag({
128-
owner: context.repo.owner,
129-
repo: context.repo.repo,
130-
tag: context.ref.replace('refs/tags/', '')
131-
});
132-
// Tag as latest only if release is marked as latest (not pre-release)
133-
core.setOutput('tag_latest', release.prerelease ? 'false' : 'true');
134-
console.log(`Release prerelease: ${release.prerelease}, tag_latest: ${!release.prerelease}`)
170+
registry: ghcr.io
171+
username: ${{ github.actor }}
172+
password: ${{ secrets.GITHUB_TOKEN }}
173+
174+
- name: Log in to Docker Hub
175+
if: inputs.publish && steps.meta.outputs.registry_type == 'dockerhub'
176+
uses: docker/login-action@v3
177+
with:
178+
username: ${{ secrets.DOCKER_USERNAME }}
179+
password: ${{ secrets.DOCKER_PASSWORD }}
135180

136181
- name: Build and push Docker image
137-
if: inputs.publish && github.repository_owner == 'temporalio'
182+
if: inputs.publish
138183
run: |
139184
docker buildx bake \
140185
--file .github/docker/docker-bake.hcl \
@@ -145,8 +190,11 @@ jobs:
145190
IMAGE_SHA_TAG: ${{ steps.meta.outputs.image_sha_tag }}
146191
IMAGE_BRANCH_TAG: ${{ steps.meta.outputs.image_branch_tag }}
147192
VERSION: ${{ steps.meta.outputs.version }}
148-
TAG_LATEST: ${{ steps.check_latest.outputs.tag_latest }}
149-
IMAGE_REPO: temporalio
193+
TAG_LATEST: false
194+
IMAGE_REPO: ${{ steps.meta.outputs.image_repo }}
195+
IMAGE_NAMESPACE: ${{ steps.meta.outputs.image_namespace }}
196+
IMAGE_NAME: ${{ steps.meta.outputs.image_name }}
197+
GITHUB_REPOSITORY: ${{ github.repository }}
150198

151199
- name: Build Docker image
152200
if: ${{ !inputs.publish }}
@@ -160,12 +208,7 @@ jobs:
160208
IMAGE_BRANCH_TAG: ${{ steps.meta.outputs.image_branch_tag }}
161209
VERSION: ${{ steps.meta.outputs.version }}
162210
TAG_LATEST: false
163-
IMAGE_REPO: temporalio
164-
165-
- name: Upload build artifacts
166-
if: ${{ !inputs.publish }}
167-
uses: actions/upload-artifact@v4
168-
with:
169-
name: temporal-cli-dist
170-
path: dist/
171-
retention-days: 7
211+
IMAGE_REPO: ${{ steps.meta.outputs.image_repo }}
212+
IMAGE_NAMESPACE: ${{ steps.meta.outputs.image_namespace }}
213+
IMAGE_NAME: ${{ steps.meta.outputs.image_name }}
214+
GITHUB_REPOSITORY: ${{ github.repository }}

.github/workflows/goreleaser.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ on:
88

99
permissions:
1010
contents: write
11+
packages: write
1112

1213
jobs:
1314
release:
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
name: Update Latest Docker Tag
2+
3+
on:
4+
release:
5+
types:
6+
- edited
7+
- released
8+
9+
permissions:
10+
contents: read
11+
packages: write
12+
13+
jobs:
14+
update-latest:
15+
name: Update Latest Tag
16+
runs-on: ubuntu-latest
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3
20+
with:
21+
ref: ${{ github.event.release.tag_name }}
22+
23+
- name: Check if release is latest
24+
id: check_latest
25+
env:
26+
RELEASE_TAG: ${{ github.event.release.tag_name }}
27+
uses: actions/github-script@v7
28+
with:
29+
script: |
30+
const releaseTag = process.env.RELEASE_TAG;
31+
const { data: release } = await github.rest.repos.getReleaseByTag({
32+
owner: context.repo.owner,
33+
repo: context.repo.repo,
34+
tag: releaseTag
35+
});
36+
37+
const isLatest = !release.prerelease && !release.draft;
38+
core.setOutput('is_latest', isLatest);
39+
console.log(`Release: ${release.tag_name}`);
40+
console.log(`Prerelease: ${release.prerelease}, Draft: ${release.draft}`);
41+
console.log(`Should tag as latest: ${isLatest}`);
42+
43+
- name: Set up Docker Buildx
44+
if: steps.check_latest.outputs.is_latest == 'true'
45+
uses: docker/setup-buildx-action@v3
46+
47+
- name: Get registry configuration
48+
if: steps.check_latest.outputs.is_latest == 'true'
49+
id: registry
50+
run: |
51+
REPO_OWNER="${{ github.repository_owner }}"
52+
53+
# Auto-detect registry based on repository owner
54+
if [[ "$REPO_OWNER" == "temporalio" ]]; then
55+
REGISTRY="docker.io"
56+
echo "type=dockerhub" >> $GITHUB_OUTPUT
57+
echo "repo=" >> $GITHUB_OUTPUT
58+
else
59+
REGISTRY="ghcr.io"
60+
echo "type=ghcr" >> $GITHUB_OUTPUT
61+
echo "repo=${REGISTRY}" >> $GITHUB_OUTPUT
62+
fi
63+
64+
echo "namespace=${REPO_OWNER}" >> $GITHUB_OUTPUT
65+
echo "image=temporal" >> $GITHUB_OUTPUT
66+
67+
- name: Log in to GitHub Container Registry
68+
if: steps.check_latest.outputs.is_latest == 'true' && steps.registry.outputs.type == 'ghcr'
69+
uses: docker/login-action@v3
70+
with:
71+
registry: ghcr.io
72+
username: ${{ github.actor }}
73+
password: ${{ secrets.GITHUB_TOKEN }}
74+
75+
- name: Log in to Docker Hub
76+
if: steps.check_latest.outputs.is_latest == 'true' && steps.registry.outputs.type == 'dockerhub'
77+
uses: docker/login-action@v3
78+
with:
79+
username: ${{ secrets.DOCKER_USERNAME }}
80+
password: ${{ secrets.DOCKER_PASSWORD }}
81+
82+
- name: Get version tag
83+
if: steps.check_latest.outputs.is_latest == 'true'
84+
id: version
85+
env:
86+
RELEASE_TAG: ${{ github.event.release.tag_name }}
87+
run: |
88+
VERSION="$RELEASE_TAG"
89+
VERSION="${VERSION#v}"
90+
echo "version=${VERSION}" >> $GITHUB_OUTPUT
91+
92+
- name: Pull and retag image as latest
93+
if: steps.check_latest.outputs.is_latest == 'true'
94+
run: |
95+
# Construct image paths
96+
REPO="${{ steps.registry.outputs.repo }}"
97+
NAMESPACE="${{ steps.registry.outputs.namespace }}"
98+
IMAGE="${{ steps.registry.outputs.image }}"
99+
VERSION="${{ steps.version.outputs.version }}"
100+
101+
if [[ -z "$REPO" ]]; then
102+
# Docker Hub format
103+
SOURCE_IMAGE="${NAMESPACE}/${IMAGE}:${VERSION}"
104+
LATEST_IMAGE="${NAMESPACE}/${IMAGE}:latest"
105+
else
106+
# Other registries
107+
SOURCE_IMAGE="${REPO}/${NAMESPACE}/${IMAGE}:${VERSION}"
108+
LATEST_IMAGE="${REPO}/${NAMESPACE}/${IMAGE}:latest"
109+
fi
110+
111+
echo "Pulling ${SOURCE_IMAGE}..."
112+
docker pull ${SOURCE_IMAGE}
113+
114+
echo "Tagging as ${LATEST_IMAGE}..."
115+
docker tag ${SOURCE_IMAGE} ${LATEST_IMAGE}
116+
117+
echo "Pushing ${LATEST_IMAGE}..."
118+
docker push ${LATEST_IMAGE}
119+
120+
echo "✅ Successfully updated latest tag to point to version ${VERSION}"

0 commit comments

Comments
 (0)