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
27 changes: 22 additions & 5 deletions .github/actions/build-docker-images/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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 \
Expand All @@ -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 \
Expand Down
99 changes: 66 additions & 33 deletions .github/actions/build-docker-images/scripts/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand All @@ -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)
}
Expand All @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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 {
Expand Down
78 changes: 78 additions & 0 deletions .github/actions/build-docker-images/scripts/main_test.go
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 15 deletions .github/workflows/docker-build-manual.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -45,7 +38,6 @@ jobs:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ inputs.ref }}
fetch-depth: 0

- name: Determine single-arch parameter
Expand All @@ -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"
Loading
Loading