mirror of
https://github.com/temporalio/temporal.git
synced 2026-08-31 02:51:51 -07:00
fix: resolve docker image tags from git and auto-detect CLI version (#9411)
## Summary - Resolve git ref and SHA from the checked-out tree (`git rev-parse`, `git symbolic-ref`) instead of `GITHUB_REF`/`GITHUB_SHA`, which point to the dispatch branch for `workflow_dispatch` events - Auto-detect CLI version from the downloaded binary via `temporal --version` fixing the admin-tools OCI label showing "unknown" - Remove `ref` input from the manual docker build workflow -- users should dispatch from the branch they want to build from
This commit is contained in:
27
.github/actions/build-docker-images/action.yml
vendored
27
.github/actions/build-docker-images/action.yml
vendored
@@ -23,9 +23,9 @@ inputs:
|
||||
required: false
|
||||
default: "false"
|
||||
cli-version:
|
||||
description: "Temporal CLI version to download"
|
||||
description: "Temporal CLI version to download (uses default from build helper if empty)"
|
||||
required: false
|
||||
default: "1.5.1"
|
||||
default: ""
|
||||
alpine-tag:
|
||||
description: "Alpine base image tag"
|
||||
required: false
|
||||
@@ -37,6 +37,14 @@ inputs:
|
||||
description: "Docker Hub token"
|
||||
required: false
|
||||
|
||||
outputs:
|
||||
branch-tag:
|
||||
description: "Docker-safe branch tag (e.g., branch-main, branch-release-v1.30.0)"
|
||||
value: ${{ steps.image-tags.outputs.tag }}
|
||||
sha-tag:
|
||||
description: "Docker SHA tag (e.g., sha-abc1234)"
|
||||
value: ${{ steps.image-tags.outputs.sha }}
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
@@ -70,12 +78,19 @@ runs:
|
||||
run: |
|
||||
.github/actions/build-docker-images/scripts/docker-build-helper download-cli
|
||||
|
||||
- name: Extract CLI version from binary
|
||||
id: extract-cli-version
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
.github/actions/build-docker-images/scripts/docker-build-helper extract-binary-version temporal cli-version
|
||||
|
||||
- name: Extract server version from binary
|
||||
id: extract-version
|
||||
shell: bash
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
.github/actions/build-docker-images/scripts/docker-build-helper extract-version
|
||||
.github/actions/build-docker-images/scripts/docker-build-helper extract-binary-version temporal-server server-version
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
@@ -98,10 +113,11 @@ runs:
|
||||
IMAGE_REPO: temporaliotest
|
||||
IMAGE_SHA_TAG: ${{ steps.image-tags.outputs.sha }}
|
||||
IMAGE_BRANCH_TAG: ${{ steps.image-tags.outputs.tag }}
|
||||
TEMPORAL_SHA: ${{ github.sha }}
|
||||
TEMPORAL_SHA: ${{ steps.image-tags.outputs.git-sha }}
|
||||
TAG_LATEST: ${{ inputs.tag-latest }}
|
||||
ALPINE_TAG: ${{ inputs.alpine-tag }}
|
||||
SERVER_VERSION: ${{ steps.extract-version.outputs.server-version }}
|
||||
CLI_VERSION: ${{ steps.extract-cli-version.outputs.cli-version }}
|
||||
run: |
|
||||
if [ -n "${{ inputs.platform }}" ]; then
|
||||
docker buildx bake \
|
||||
@@ -124,10 +140,11 @@ runs:
|
||||
IMAGE_REPO: temporaliotest
|
||||
IMAGE_SHA_TAG: ${{ steps.image-tags.outputs.sha }}
|
||||
IMAGE_BRANCH_TAG: ${{ steps.image-tags.outputs.tag }}
|
||||
TEMPORAL_SHA: ${{ github.sha }}
|
||||
TEMPORAL_SHA: ${{ steps.image-tags.outputs.git-sha }}
|
||||
TAG_LATEST: ${{ inputs.tag-latest }}
|
||||
ALPINE_TAG: ${{ inputs.alpine-tag }}
|
||||
SERVER_VERSION: ${{ steps.extract-version.outputs.server-version }}
|
||||
CLI_VERSION: ${{ steps.extract-cli-version.outputs.cli-version }}
|
||||
run: |
|
||||
docker buildx bake \
|
||||
--push \
|
||||
|
||||
@@ -23,7 +23,7 @@ func main() {
|
||||
fmt.Fprintf(os.Stderr, " set-image-tags - Generate Docker image tags from branch and SHA\n")
|
||||
fmt.Fprintf(os.Stderr, " organize-binaries - Organize binaries for Docker\n")
|
||||
fmt.Fprintf(os.Stderr, " download-cli - Download Temporal CLI\n")
|
||||
fmt.Fprintf(os.Stderr, " extract-version - Extract version from temporal-server binary\n")
|
||||
fmt.Fprintf(os.Stderr, " extract-binary-version <binary-name> <output-name> - Extract version from a binary\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -45,8 +45,12 @@ func main() {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
case "extract-version":
|
||||
if err := extractVersion(); err != nil {
|
||||
case "extract-binary-version":
|
||||
if len(os.Args) != 4 {
|
||||
fmt.Fprintf(os.Stderr, "Usage: %s extract-binary-version <binary-name> <output-name>\n", os.Args[0])
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := extractBinaryVersion(os.Args[2], os.Args[3]); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -56,18 +60,37 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// setImageTags generates Docker image tags from branch name and commit SHA
|
||||
func setImageTags() error {
|
||||
// Get GITHUB_REF from environment
|
||||
ref := os.Getenv("GITHUB_REF")
|
||||
if ref == "" {
|
||||
return fmt.Errorf("GITHUB_REF environment variable not set")
|
||||
// resolveGitInfo resolves the current git ref and SHA from the working tree.
|
||||
func resolveGitInfo() (ref string, sha string, err error) {
|
||||
shaCmd := exec.Command("git", "rev-parse", "HEAD")
|
||||
shaOut, err := shaCmd.Output()
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("failed to resolve git SHA: %w", err)
|
||||
}
|
||||
sha = strings.TrimSpace(string(shaOut))
|
||||
|
||||
// Use the symbolic ref (branch name) if available, otherwise fall back
|
||||
// to a tag name or the raw SHA.
|
||||
refCmd := exec.Command("git", "symbolic-ref", "HEAD")
|
||||
if refOut, err := refCmd.Output(); err == nil {
|
||||
ref = strings.TrimSpace(string(refOut))
|
||||
} else {
|
||||
tagCmd := exec.Command("git", "describe", "--tags", "--exact-match", "HEAD")
|
||||
if tagOut, tagErr := tagCmd.Output(); tagErr == nil {
|
||||
ref = strings.TrimSpace(string(tagOut))
|
||||
} else {
|
||||
ref = sha
|
||||
}
|
||||
}
|
||||
|
||||
// Get GITHUB_SHA from environment
|
||||
sha := os.Getenv("GITHUB_SHA")
|
||||
if sha == "" {
|
||||
return fmt.Errorf("GITHUB_SHA environment variable not set")
|
||||
return ref, sha, nil
|
||||
}
|
||||
|
||||
// setImageTags generates Docker image tags from branch name and commit SHA
|
||||
func setImageTags() error {
|
||||
ref, sha, err := resolveGitInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove refs/heads/ or refs/tags/ prefix
|
||||
@@ -119,6 +142,9 @@ func setImageTags() error {
|
||||
if err := setOutput("sha", shaTag); err != nil {
|
||||
return fmt.Errorf("failed to set sha output: %w", err)
|
||||
}
|
||||
if err := setOutput("git-sha", sha); err != nil {
|
||||
return fmt.Errorf("failed to set git-sha output: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -403,50 +429,57 @@ func downloadCLIForArch(arch string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractVersion extracts the version from the temporal-server binary
|
||||
func extractVersion() error {
|
||||
// Try to find the temporal-server binary in any available architecture directory
|
||||
var binaryPath string
|
||||
// findBuildBinary finds a binary by name in the docker/build/{arch}/ directories.
|
||||
func findBuildBinary(name string) (string, error) {
|
||||
for _, arch := range validArchs {
|
||||
candidatePath := filepath.Join("docker", "build", arch, "temporal-server")
|
||||
candidatePath := filepath.Join("docker", "build", arch, name)
|
||||
if _, err := os.Stat(candidatePath); err == nil {
|
||||
binaryPath = candidatePath
|
||||
break
|
||||
return candidatePath, nil
|
||||
}
|
||||
}
|
||||
return "", fmt.Errorf("%s binary not found in docker/build/{amd64,arm64}/", name)
|
||||
}
|
||||
|
||||
if binaryPath == "" {
|
||||
return fmt.Errorf("temporal-server binary not found in docker/build/{amd64,arm64}/")
|
||||
// extractBinaryVersion finds a binary, runs --version, parses the output, and sets a GitHub Actions output.
|
||||
func extractBinaryVersion(binaryName, outputName string) error {
|
||||
binaryPath, err := findBuildBinary(binaryName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Extracting version from %s\n", binaryPath)
|
||||
|
||||
// Run the binary with --version flag
|
||||
cmd := exec.Command(binaryPath, "--version")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to run %s --version: %w", binaryPath, err)
|
||||
}
|
||||
|
||||
// Parse the version from output like "temporal version 1.29.0"
|
||||
outputStr := strings.TrimSpace(string(output))
|
||||
versionRegex := regexp.MustCompile(`^temporal version (\d+\.\d+\.\d+)`)
|
||||
matches := versionRegex.FindStringSubmatch(outputStr)
|
||||
if len(matches) < 2 {
|
||||
return fmt.Errorf("failed to parse version from output: %s", outputStr)
|
||||
version, err := parseTemporalVersion(string(output))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
version := matches[1]
|
||||
fmt.Printf("Extracted version: %s\n", version)
|
||||
|
||||
// Set output for GitHub Actions
|
||||
if err := setOutput("server-version", version); err != nil {
|
||||
if err := setOutput(outputName, version); err != nil {
|
||||
return fmt.Errorf("failed to set output: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseTemporalVersion extracts the version from output like
|
||||
// "temporal version 1.29.0" or "temporal version 0.0.0-DEV (Server 1.30.1, UI 2.45.3)"
|
||||
func parseTemporalVersion(output string) (string, error) {
|
||||
s := strings.TrimSpace(output)
|
||||
re := regexp.MustCompile(`^temporal version\s+(\d+\.\d+\.\d+\S*)`)
|
||||
matches := re.FindStringSubmatch(s)
|
||||
if len(matches) < 2 {
|
||||
return "", fmt.Errorf("failed to parse version from output: %s", s)
|
||||
}
|
||||
return matches[1], nil
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
|
||||
func setOutput(name, value string) error {
|
||||
|
||||
78
.github/actions/build-docker-images/scripts/main_test.go
vendored
Normal file
78
.github/actions/build-docker-images/scripts/main_test.go
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseTemporalVersion(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
output string
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "server version",
|
||||
output: "temporal version 1.29.0",
|
||||
want: "1.29.0",
|
||||
},
|
||||
{
|
||||
name: "cli release version",
|
||||
output: "temporal version 1.6.0 (Server 1.30.0, UI 2.45.0)",
|
||||
want: "1.6.0",
|
||||
},
|
||||
{
|
||||
name: "cli dev version",
|
||||
output: "temporal version 0.0.0-DEV (Server 1.30.1, UI 2.45.3)",
|
||||
want: "0.0.0-DEV",
|
||||
},
|
||||
{
|
||||
name: "with trailing newline",
|
||||
output: "temporal version 1.6.0 (Server 1.30.0, UI 2.45.0)\n",
|
||||
want: "1.6.0",
|
||||
},
|
||||
{
|
||||
name: "with leading and trailing whitespace",
|
||||
output: " \n temporal version 1.6.0 (Server 1.30.0, UI 2.45.0) \n ",
|
||||
want: "1.6.0",
|
||||
},
|
||||
{
|
||||
name: "pre-release version",
|
||||
output: "temporal version 1.31.0-151.5",
|
||||
want: "1.31.0-151.5",
|
||||
},
|
||||
{
|
||||
name: "extra spaces between version and number",
|
||||
output: "temporal version 1.6.1",
|
||||
want: "1.6.1",
|
||||
},
|
||||
{
|
||||
name: "empty output",
|
||||
output: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "unexpected format",
|
||||
output: "something else entirely",
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := parseTemporalVersion(tt.output)
|
||||
if tt.wantErr {
|
||||
if err == nil {
|
||||
t.Fatalf("expected error, got version %q", got)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Fatalf("got %q, want %q", got, tt.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
28
.github/workflows/docker-build-manual.yml
vendored
28
.github/workflows/docker-build-manual.yml
vendored
@@ -1,16 +1,9 @@
|
||||
name: Manual Docker Build
|
||||
# Dispatch this workflow from the branch you want to build from.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: "Git ref (branch, tag, or SHA) to build from"
|
||||
required: true
|
||||
default: "main"
|
||||
cli-version:
|
||||
description: "Temporal CLI version to include in images"
|
||||
required: true
|
||||
default: "1.5.1"
|
||||
alpine-tag:
|
||||
description: "Alpine base image tag (e.g., 3.23.3)"
|
||||
required: true
|
||||
@@ -45,7 +38,6 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ inputs.ref }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Determine single-arch parameter
|
||||
@@ -66,40 +58,46 @@ jobs:
|
||||
single-arch: ${{ steps.arch-param.outputs.single-arch }}
|
||||
|
||||
- name: Build Docker images
|
||||
id: build-docker
|
||||
if: ${{ !inputs.push }}
|
||||
uses: ./.github/actions/build-docker-images
|
||||
with:
|
||||
push: false
|
||||
tag-latest: ${{ inputs.tag-latest }}
|
||||
platform: ${{ inputs.platform }}
|
||||
cli-version: ${{ inputs.cli-version }}
|
||||
alpine-tag: ${{ inputs.alpine-tag }}
|
||||
load: ${{ inputs.platform == 'linux/amd64' || inputs.platform == '' }}
|
||||
|
||||
- name: Build and push Docker images
|
||||
id: push-docker
|
||||
if: ${{ inputs.push }}
|
||||
uses: ./.github/actions/build-docker-images
|
||||
with:
|
||||
push: true
|
||||
tag-latest: ${{ inputs.tag-latest }}
|
||||
platform: ${{ inputs.platform }}
|
||||
cli-version: ${{ inputs.cli-version }}
|
||||
alpine-tag: ${{ inputs.alpine-tag }}
|
||||
dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Output image tags
|
||||
env:
|
||||
SHA_TAG: ${{ steps.build-docker.outputs.sha-tag || steps.push-docker.outputs.sha-tag }}
|
||||
BRANCH_TAG: ${{ steps.build-docker.outputs.branch-tag || steps.push-docker.outputs.branch-tag }}
|
||||
run: |
|
||||
{
|
||||
echo "### Docker Images Built"
|
||||
echo ""
|
||||
echo "**Git Ref:** ${{ inputs.ref }}"
|
||||
echo "**CLI Version:** ${{ inputs.cli-version }}"
|
||||
echo "**Branch:** ${{ github.ref_name }}"
|
||||
echo "**SHA Tag:** ${SHA_TAG}"
|
||||
echo "**Branch Tag:** ${BRANCH_TAG}"
|
||||
echo "**Platform:** ${{ inputs.platform || 'linux/amd64,linux/arm64' }}"
|
||||
echo "**Pushed to Docker Hub:** ${{ inputs.push }}"
|
||||
echo "**Tagged as latest:** ${{ inputs.tag-latest }}"
|
||||
echo ""
|
||||
echo "**Image Tags:**"
|
||||
echo "- temporaliotest/server:sha-${GITHUB_SHA:0:7}"
|
||||
echo "- temporaliotest/admin-tools:sha-${GITHUB_SHA:0:7}"
|
||||
echo "- temporaliotest/server:${SHA_TAG}"
|
||||
echo "- temporaliotest/admin-tools:${SHA_TAG}"
|
||||
echo "- temporaliotest/server:${BRANCH_TAG}"
|
||||
echo "- temporaliotest/admin-tools:${BRANCH_TAG}"
|
||||
} >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
20
.github/workflows/features-integration.yml
vendored
20
.github/workflows/features-integration.yml
vendored
@@ -32,6 +32,7 @@ jobs:
|
||||
single-arch: amd64
|
||||
|
||||
- name: Build Docker images
|
||||
id: build-docker
|
||||
uses: ./.github/actions/build-docker-images
|
||||
with:
|
||||
push: false
|
||||
@@ -39,24 +40,11 @@ jobs:
|
||||
platform: linux/amd64
|
||||
load: true
|
||||
|
||||
- name: Get Docker image tag
|
||||
id: image-tag
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const ref = context.ref.replace('refs/heads/', '').replace('refs/tags/', '');
|
||||
const branchName = `branch-${ref}`;
|
||||
let safeTag = branchName.replace(/[^a-zA-Z0-9._-]/g, '-');
|
||||
safeTag = safeTag.toLowerCase();
|
||||
safeTag = safeTag.replace(/^[^a-z0-9]+/, '');
|
||||
safeTag = safeTag.substring(0, 128);
|
||||
core.setOutput('tag', safeTag);
|
||||
|
||||
- name: Save Docker image as artifact
|
||||
run: |
|
||||
docker save temporaliotest/server:${{ steps.image-tag.outputs.tag }} -o /tmp/temporal-server.tar
|
||||
docker save temporaliotest/admin-tools:${{ steps.image-tag.outputs.tag }} -o /tmp/temporal-admin-tools.tar
|
||||
echo ${{ steps.image-tag.outputs.tag }} > /tmp/image_tag
|
||||
docker save temporaliotest/server:${{ steps.build-docker.outputs.branch-tag }} -o /tmp/temporal-server.tar
|
||||
docker save temporaliotest/admin-tools:${{ steps.build-docker.outputs.branch-tag }} -o /tmp/temporal-admin-tools.tar
|
||||
echo ${{ steps.build-docker.outputs.branch-tag }} > /tmp/image_tag
|
||||
|
||||
- name: Prepare artifact
|
||||
working-directory: ${{ github.workspace }}
|
||||
|
||||
Reference in New Issue
Block a user