mirror of
https://github.com/temporalio/temporal.git
synced 2026-08-30 18:41:49 -07:00
New Docker Build GH Actions (#8825)
## What changed? * updates goreleaser to v2 * add GHA for build admin-tools and server images within this repo. `docker-builds` will only be used for building pre 1.30 images * added GHA to promote docker builds from temporaliotest to temproalio docker image repos * trivy security scanning gates for image promotion (pulled rom docker-builds repo). The gate can be overridden ## Why? * we decided to move away from building images in `docker-builds`. The complexity is not needed * updates goreleaser to v2 because the v1 definitions might be no longer supported at some point and this is a good time to do it * I used go scripts for the more complex flows instead of js or bash so we don't introduce another language contributors need to be familiar with. IMO the js or bash I did use it simple enough to understand. ## How did you test it? It passes in CI. ## Potential risks * these build pipelines are only compatible with the new docker images. * merging this PR may break our nightly tests. Will double check before merging * flows that are not triggered by opening a PR are untested and therefore not completely validated
This commit is contained in:
47
.github/actions/build-binaries/action.yml
vendored
Normal file
47
.github/actions/build-binaries/action.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
name: Build Binaries
|
||||||
|
description: Build Temporal binaries using GoReleaser
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
snapshot:
|
||||||
|
description: "Use snapshot mode (true) or release mode (false). Only applies to release command (build always uses snapshot)."
|
||||||
|
required: false
|
||||||
|
default: "true"
|
||||||
|
single-arch:
|
||||||
|
description: "Single architecture to build (amd64 or arm64, empty for all). Only used with build command, ignored if release is true."
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
release:
|
||||||
|
description: "Use release command (true) or build command (false). When true, single-arch is ignored and snapshot is respected."
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Setup Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version-file: "go.mod"
|
||||||
|
cache: true
|
||||||
|
|
||||||
|
- name: Run GoReleaser (release)
|
||||||
|
if: inputs.release == 'true'
|
||||||
|
uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser
|
||||||
|
version: v2.13.1
|
||||||
|
args: release ${{ inputs.snapshot == 'true' && '--snapshot --skip=publish' || '' }} --clean
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
|
||||||
|
- name: Run GoReleaser (build)
|
||||||
|
if: inputs.release != 'true'
|
||||||
|
uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
distribution: goreleaser
|
||||||
|
version: v2.13.1
|
||||||
|
args: build --snapshot ${{ inputs.single-arch != '' && '--single-target' || '' }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ github.token }}
|
||||||
|
GOOS: ${{ inputs.single-arch != '' && 'linux' || '' }}
|
||||||
|
GOARCH: ${{ inputs.single-arch }}
|
||||||
133
.github/actions/build-docker-images/action.yml
vendored
Normal file
133
.github/actions/build-docker-images/action.yml
vendored
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
name: Build Docker Images
|
||||||
|
description: |
|
||||||
|
Build Temporal Docker images from binaries.
|
||||||
|
|
||||||
|
Prerequisites:
|
||||||
|
- The build-binaries action must run before this action to produce the binaries.
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
push:
|
||||||
|
description: "Push images to Docker Hub"
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
tag-latest:
|
||||||
|
description: "Tag images as latest"
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
platform:
|
||||||
|
description: "Single platform to build (e.g., linux/amd64)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
load:
|
||||||
|
description: "Load image into local Docker daemon (only works with linux/amd64 - the runner architecture)"
|
||||||
|
required: false
|
||||||
|
default: "false"
|
||||||
|
cli-version:
|
||||||
|
description: "Temporal CLI version to download"
|
||||||
|
required: false
|
||||||
|
default: "1.5.1"
|
||||||
|
alpine-tag:
|
||||||
|
description: "Alpine base image tag with digest"
|
||||||
|
required: false
|
||||||
|
default: "3.23@sha256:c78ded0fee4493809c8ca71d4a6057a46237763d952fae15ea418f6d14137f2d"
|
||||||
|
dockerhub-username:
|
||||||
|
description: "Docker Hub username"
|
||||||
|
required: false
|
||||||
|
dockerhub-token:
|
||||||
|
description: "Docker Hub token"
|
||||||
|
required: false
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: composite
|
||||||
|
steps:
|
||||||
|
- name: Build docker-build-helper
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}/.github/actions/build-docker-images/scripts
|
||||||
|
run: |
|
||||||
|
go build -o docker-build-helper .
|
||||||
|
|
||||||
|
- name: Validate and sanitize branch name for Docker tag
|
||||||
|
id: sanitize-tag
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
run: |
|
||||||
|
.github/actions/build-docker-images/scripts/docker-build-helper sanitize-tag
|
||||||
|
|
||||||
|
- name: Organize binaries for Docker
|
||||||
|
id: organize-binaries
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
env:
|
||||||
|
PLATFORM: ${{ inputs.platform }}
|
||||||
|
run: |
|
||||||
|
.github/actions/build-docker-images/scripts/docker-build-helper organize-binaries
|
||||||
|
|
||||||
|
- name: Download Temporal CLI
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
env:
|
||||||
|
AVAILABLE_ARCHS: ${{ steps.organize-binaries.outputs.available-archs }}
|
||||||
|
CLI_VERSION: ${{ inputs.cli-version }}
|
||||||
|
run: |
|
||||||
|
.github/actions/build-docker-images/scripts/docker-build-helper download-cli
|
||||||
|
|
||||||
|
- 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
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
if: inputs.push == 'true'
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ inputs.dockerhub-username }}
|
||||||
|
password: ${{ inputs.dockerhub-token }}
|
||||||
|
|
||||||
|
- name: Build Docker images
|
||||||
|
if: inputs.push != 'true'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
env:
|
||||||
|
IMAGE_REPO: temporaliotest
|
||||||
|
IMAGE_SHA_TAG: ${{ github.sha }}
|
||||||
|
IMAGE_BRANCH_TAG: ${{ steps.sanitize-tag.outputs.tag }}
|
||||||
|
TEMPORAL_SHA: ${{ github.sha }}
|
||||||
|
TAG_LATEST: ${{ inputs.tag-latest }}
|
||||||
|
ALPINE_TAG: ${{ inputs.alpine-tag }}
|
||||||
|
SERVER_VERSION: ${{ steps.extract-version.outputs.server-version }}
|
||||||
|
run: |
|
||||||
|
if [ -n "${{ inputs.platform }}" ]; then
|
||||||
|
docker buildx bake \
|
||||||
|
--set "*.platform=${{ inputs.platform }}" \
|
||||||
|
${{ inputs.load == 'true' && '--load' || '' }} \
|
||||||
|
-f docker/docker-bake.hcl \
|
||||||
|
server admin-tools
|
||||||
|
else
|
||||||
|
docker buildx bake \
|
||||||
|
${{ inputs.load == 'true' && '--load' || '' }} \
|
||||||
|
-f docker/docker-bake.hcl \
|
||||||
|
server admin-tools
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and push Docker images
|
||||||
|
if: inputs.push == 'true'
|
||||||
|
shell: bash
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
env:
|
||||||
|
IMAGE_REPO: temporaliotest
|
||||||
|
IMAGE_SHA_TAG: ${{ github.sha }}
|
||||||
|
IMAGE_BRANCH_TAG: ${{ steps.sanitize-tag.outputs.tag }}
|
||||||
|
TEMPORAL_SHA: ${{ github.sha }}
|
||||||
|
TAG_LATEST: ${{ inputs.tag-latest }}
|
||||||
|
ALPINE_TAG: ${{ inputs.alpine-tag }}
|
||||||
|
SERVER_VERSION: ${{ steps.extract-version.outputs.server-version }}
|
||||||
|
run: |
|
||||||
|
docker buildx bake \
|
||||||
|
--push \
|
||||||
|
-f docker/docker-bake.hcl \
|
||||||
|
server admin-tools
|
||||||
1
.github/actions/build-docker-images/scripts/.gitignore
vendored
Normal file
1
.github/actions/build-docker-images/scripts/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
docker-build-helper
|
||||||
561
.github/actions/build-docker-images/scripts/main.go
vendored
Normal file
561
.github/actions/build-docker-images/scripts/main.go
vendored
Normal file
@@ -0,0 +1,561 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
var validArchs = []string{"amd64", "arm64"}
|
||||||
|
|
||||||
|
// defaultCliVersion should be updated to the latest cli version
|
||||||
|
const defaultCliVersion = "1.5.1"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if len(os.Args) < 2 {
|
||||||
|
fmt.Fprintf(os.Stderr, "Usage: %s <command>\n", os.Args[0])
|
||||||
|
fmt.Fprintf(os.Stderr, "Commands:\n")
|
||||||
|
fmt.Fprintf(os.Stderr, " sanitize-tag - Sanitize branch name for Docker tag\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")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
command := os.Args[1]
|
||||||
|
|
||||||
|
switch command {
|
||||||
|
case "sanitize-tag":
|
||||||
|
if err := sanitizeTag(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
case "organize-binaries":
|
||||||
|
if err := organizeBinaries(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
case "download-cli":
|
||||||
|
if err := downloadCLI(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
case "extract-version":
|
||||||
|
if err := extractVersion(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "Unknown command: %s\n", command)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// sanitizeTag sanitizes branch names for Docker tags
|
||||||
|
func sanitizeTag() error {
|
||||||
|
// Get GITHUB_REF from environment
|
||||||
|
ref := os.Getenv("GITHUB_REF")
|
||||||
|
if ref == "" {
|
||||||
|
return fmt.Errorf("GITHUB_REF environment variable not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove refs/heads/ or refs/tags/ prefix
|
||||||
|
ref = strings.TrimPrefix(ref, "refs/heads/")
|
||||||
|
ref = strings.TrimPrefix(ref, "refs/tags/")
|
||||||
|
|
||||||
|
// Sanitize ref name first
|
||||||
|
// Replace any non-alphanumeric (except .-_) with dash
|
||||||
|
reg := regexp.MustCompile(`[^a-zA-Z0-9._-]`)
|
||||||
|
sanitizedRef := reg.ReplaceAllString(ref, "-")
|
||||||
|
|
||||||
|
// Collapse multiple consecutive dashes
|
||||||
|
multiDashReg := regexp.MustCompile(`-+`)
|
||||||
|
sanitizedRef = multiDashReg.ReplaceAllString(sanitizedRef, "-")
|
||||||
|
|
||||||
|
// Remove leading and trailing dashes
|
||||||
|
sanitizedRef = strings.Trim(sanitizedRef, "-")
|
||||||
|
|
||||||
|
// Prefix with "branch-" for branch builds
|
||||||
|
safeTag := fmt.Sprintf("branch-%s", sanitizedRef)
|
||||||
|
|
||||||
|
// Docker tags must be lowercase
|
||||||
|
safeTag = strings.ToLower(safeTag)
|
||||||
|
|
||||||
|
// Truncate to 128 characters (Docker tag limit)
|
||||||
|
if len(safeTag) > 128 {
|
||||||
|
safeTag = safeTag[:128]
|
||||||
|
}
|
||||||
|
|
||||||
|
if safeTag == "" {
|
||||||
|
return fmt.Errorf("failed to generate valid Docker tag from branch name")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Original: %s\n", ref)
|
||||||
|
fmt.Printf("Sanitized: %s\n", safeTag)
|
||||||
|
|
||||||
|
// Set output for GitHub Actions
|
||||||
|
if err := setOutput("tag", safeTag); err != nil {
|
||||||
|
return fmt.Errorf("failed to set output: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// organizeBinaries organizes binaries for Docker builds
|
||||||
|
func organizeBinaries() error {
|
||||||
|
// Determine target architectures based on PLATFORM environment variable
|
||||||
|
platform := os.Getenv("PLATFORM")
|
||||||
|
var archs []string
|
||||||
|
|
||||||
|
if platform != "" {
|
||||||
|
// Parse platform (e.g., "linux/amd64" -> "amd64")
|
||||||
|
parts := strings.Split(platform, "/")
|
||||||
|
if len(parts) != 2 {
|
||||||
|
return fmt.Errorf("invalid platform format: %s (expected format: os/arch)", platform)
|
||||||
|
}
|
||||||
|
arch := parts[1]
|
||||||
|
|
||||||
|
// Check if arch is in valid list
|
||||||
|
found := false
|
||||||
|
for _, validArch := range validArchs {
|
||||||
|
if arch == validArch {
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("architecture %s not in supported list: %v", arch, validArchs)
|
||||||
|
}
|
||||||
|
|
||||||
|
archs = []string{arch}
|
||||||
|
fmt.Printf("Single architecture build: %s\n", arch)
|
||||||
|
} else {
|
||||||
|
// Default to all architectures
|
||||||
|
archs = []string{"amd64", "arm64"}
|
||||||
|
fmt.Println("Multi-architecture build: amd64, arm64")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Admin tool binaries (for admin-tools image)
|
||||||
|
adminToolBinaries := []string{
|
||||||
|
"temporal-cassandra-tool",
|
||||||
|
"temporal-sql-tool",
|
||||||
|
"temporal-elasticsearch-tool",
|
||||||
|
"tdbg",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Server binaries (for server image)
|
||||||
|
serverBinaries := []string{
|
||||||
|
"temporal-server",
|
||||||
|
}
|
||||||
|
|
||||||
|
// All binaries to copy
|
||||||
|
binaries := append(adminToolBinaries, serverBinaries...)
|
||||||
|
|
||||||
|
// Validate architecture and binary names
|
||||||
|
archReg := regexp.MustCompile(`^[a-z0-9]+$`)
|
||||||
|
for _, arch := range archs {
|
||||||
|
if !archReg.MatchString(arch) {
|
||||||
|
return fmt.Errorf("invalid architecture name: %s", arch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
binReg := regexp.MustCompile(`^[a-z0-9-]+$`)
|
||||||
|
for _, binary := range binaries {
|
||||||
|
if !binReg.MatchString(binary) {
|
||||||
|
return fmt.Errorf("invalid binary name: %s", binary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create architecture directories
|
||||||
|
for _, arch := range archs {
|
||||||
|
dir := filepath.Join("docker", "build", arch)
|
||||||
|
if err := validatePath(dir, "docker/build"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create directory %s: %w", dir, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map GoReleaser dist structure to build structure
|
||||||
|
archMap := map[string]string{
|
||||||
|
"amd64": "amd64_v1",
|
||||||
|
"arm64": "arm64",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy binaries
|
||||||
|
for _, binary := range binaries {
|
||||||
|
for _, arch := range archs {
|
||||||
|
distArch := archMap[arch]
|
||||||
|
distPath := filepath.Join("dist", fmt.Sprintf("%s_linux_%s", binary, distArch), binary)
|
||||||
|
buildPath := filepath.Join("docker", "build", arch, binary)
|
||||||
|
|
||||||
|
// Validate paths before file operations
|
||||||
|
if err := validatePath(distPath, "dist"); err != nil {
|
||||||
|
return fmt.Errorf("invalid dist path: %w", err)
|
||||||
|
}
|
||||||
|
if err := validatePath(buildPath, "docker/build"); err != nil {
|
||||||
|
return fmt.Errorf("invalid build path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(distPath); err == nil {
|
||||||
|
if err := copyFile(distPath, buildPath); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy %s to %s: %w", distPath, buildPath, err)
|
||||||
|
}
|
||||||
|
if err := os.Chmod(buildPath, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to chmod %s: %w", buildPath, err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Copied %s -> %s\n", distPath, buildPath)
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("binary not found: %s for architecture %s (expected at %s)", binary, arch, distPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy schema directory for admin-tools
|
||||||
|
schemaDir := filepath.Join("docker", "build", "temporal", "schema")
|
||||||
|
if err := validatePath(schemaDir, "docker/build"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(schemaDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create schema directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy all schema files recursively with path validation
|
||||||
|
if _, err := os.Stat("schema"); err == nil {
|
||||||
|
if err := copyRecursive("schema", schemaDir); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy schema directory: %w", err)
|
||||||
|
}
|
||||||
|
fmt.Println("Copied schema directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate required binaries for Docker images
|
||||||
|
fmt.Println("\nValidating required binaries for Docker images...")
|
||||||
|
|
||||||
|
// Check which architectures have binaries
|
||||||
|
var availableArchs []string
|
||||||
|
for _, arch := range archs {
|
||||||
|
testBinary := filepath.Join("docker", "build", arch, "temporal-server")
|
||||||
|
if _, err := os.Stat(testBinary); err == nil {
|
||||||
|
availableArchs = append(availableArchs, arch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(availableArchs) == 0 {
|
||||||
|
return fmt.Errorf("❌ No binaries found for any architecture")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Found binaries for architectures: %s\n", strings.Join(availableArchs, ", "))
|
||||||
|
|
||||||
|
// Validate that each available architecture has all required binaries
|
||||||
|
missingFiles := false
|
||||||
|
for _, arch := range availableArchs {
|
||||||
|
for _, binary := range binaries {
|
||||||
|
binaryPath := filepath.Join("docker", "build", arch, binary)
|
||||||
|
if _, err := os.Stat(binaryPath); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: Missing %s\n", binaryPath)
|
||||||
|
missingFiles = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate schema directory exists
|
||||||
|
if _, err := os.Stat(filepath.Join("docker", "build", "temporal", "schema")); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, "Error: Missing docker/build/temporal/schema directory")
|
||||||
|
missingFiles = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if missingFiles {
|
||||||
|
return fmt.Errorf("❌ Binary validation failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("✓ All required binaries present for available architectures")
|
||||||
|
|
||||||
|
// Export available architectures for Docker build
|
||||||
|
if err := setOutput("available-archs", strings.Join(availableArchs, ",")); err != nil {
|
||||||
|
return fmt.Errorf("failed to set output: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// downloadCLI downloads the Temporal CLI for available architectures
|
||||||
|
func downloadCLI() error {
|
||||||
|
// Get available architectures from environment or input
|
||||||
|
availableArchsStr := os.Getenv("AVAILABLE_ARCHS")
|
||||||
|
if availableArchsStr == "" {
|
||||||
|
return fmt.Errorf("AVAILABLE_ARCHS environment variable not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
availableArchs := strings.Split(availableArchsStr, ",")
|
||||||
|
|
||||||
|
// Filter to only valid architectures
|
||||||
|
var validAvailableArchs []string
|
||||||
|
for _, arch := range availableArchs {
|
||||||
|
arch = strings.TrimSpace(arch)
|
||||||
|
for _, validArch := range validArchs {
|
||||||
|
if arch == validArch {
|
||||||
|
validAvailableArchs = append(validAvailableArchs, arch)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(validAvailableArchs) == 0 {
|
||||||
|
return fmt.Errorf("no valid architectures found in: %s", availableArchsStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, arch := range validAvailableArchs {
|
||||||
|
if err := downloadCLIForArch(arch); err != nil {
|
||||||
|
return fmt.Errorf("failed to download CLI for %s: %w", arch, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadCLIForArch(arch string) error {
|
||||||
|
cliVersion := os.Getenv("CLI_VERSION")
|
||||||
|
if cliVersion == "" {
|
||||||
|
cliVersion = defaultCliVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
tarballName := fmt.Sprintf("temporal_cli_%s_linux_%s.tar.gz", cliVersion, arch)
|
||||||
|
downloadURL := fmt.Sprintf("https://github.com/temporalio/cli/releases/download/v%s/%s", cliVersion, tarballName)
|
||||||
|
|
||||||
|
fmt.Printf("Downloading Temporal CLI v%s for %s from %s\n", cliVersion, arch, downloadURL)
|
||||||
|
|
||||||
|
tempDir := filepath.Join(os.TempDir(), fmt.Sprintf("temporal-cli-%s", arch))
|
||||||
|
tarballPath := filepath.Join(os.TempDir(), tarballName)
|
||||||
|
|
||||||
|
// Download tarball
|
||||||
|
if err := downloadFile(downloadURL, tarballPath); err != nil {
|
||||||
|
return fmt.Errorf("failed to download: %w", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(tarballPath)
|
||||||
|
|
||||||
|
// Create temp directory
|
||||||
|
if err := os.MkdirAll(tempDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create temp directory: %w", err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(tempDir)
|
||||||
|
|
||||||
|
// Extract tarball
|
||||||
|
cmd := exec.Command("tar", "-xzf", tarballPath, "-C", tempDir)
|
||||||
|
if output, err := cmd.CombinedOutput(); err != nil {
|
||||||
|
return fmt.Errorf("failed to extract: %w\nOutput: %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to build directory
|
||||||
|
destDir := filepath.Join("docker", "build", arch)
|
||||||
|
if err := validatePath(destDir, "docker/build"); err != nil {
|
||||||
|
return fmt.Errorf("invalid build directory path: %w", err)
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(destDir, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to create build directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sourcePath := filepath.Join(tempDir, "temporal")
|
||||||
|
destPath := filepath.Join(destDir, "temporal")
|
||||||
|
|
||||||
|
if err := validatePath(destPath, "docker/build"); err != nil {
|
||||||
|
return fmt.Errorf("invalid destination path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Rename(sourcePath, destPath); err != nil {
|
||||||
|
// If rename fails (e.g., cross-device), try copy and delete
|
||||||
|
if err := copyFile(sourcePath, destPath); err != nil {
|
||||||
|
return fmt.Errorf("failed to copy binary: %w", err)
|
||||||
|
}
|
||||||
|
os.Remove(sourcePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Chmod(destPath, 0755); err != nil {
|
||||||
|
return fmt.Errorf("failed to chmod binary: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("Installed Temporal CLI to %s\n", destPath)
|
||||||
|
|
||||||
|
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
|
||||||
|
for _, arch := range validArchs {
|
||||||
|
candidatePath := filepath.Join("docker", "build", arch, "temporal-server")
|
||||||
|
if _, err := os.Stat(candidatePath); err == nil {
|
||||||
|
binaryPath = candidatePath
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if binaryPath == "" {
|
||||||
|
return fmt.Errorf("temporal-server binary not found in docker/build/{amd64,arm64}/")
|
||||||
|
}
|
||||||
|
|
||||||
|
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 := matches[1]
|
||||||
|
fmt.Printf("Extracted version: %s\n", version)
|
||||||
|
|
||||||
|
// Set output for GitHub Actions
|
||||||
|
if err := setOutput("server-version", version); err != nil {
|
||||||
|
return fmt.Errorf("failed to set output: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper functions
|
||||||
|
|
||||||
|
func setOutput(name, value string) error {
|
||||||
|
outputFile := os.Getenv("GITHUB_OUTPUT")
|
||||||
|
if outputFile == "" {
|
||||||
|
return fmt.Errorf("GITHUB_OUTPUT environment variable not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
f, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
_, err = fmt.Fprintf(f, "%s=%s\n", name, value)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatePath(path, allowedPrefix string) error {
|
||||||
|
// Clean and resolve paths
|
||||||
|
normalized := filepath.Clean(path)
|
||||||
|
resolved, err := filepath.Abs(normalized)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedResolved, err := filepath.Abs(allowedPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to resolve allowed prefix: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for path traversal
|
||||||
|
if strings.Contains(normalized, "..") {
|
||||||
|
return fmt.Errorf("path traversal detected in: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure path is within allowed directory
|
||||||
|
if !strings.HasPrefix(resolved, allowedResolved) {
|
||||||
|
return fmt.Errorf("path outside allowed directory: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFile(src, dst string) error {
|
||||||
|
sourceFile, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
destFile, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer destFile.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(destFile, sourceFile)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyRecursive(src, dst string) error {
|
||||||
|
// Validate paths
|
||||||
|
if err := validatePath(src, "schema"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := validatePath(dst, "docker/build"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
srcInfo, err := os.Stat(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if srcInfo.IsDir() {
|
||||||
|
// Create destination directory
|
||||||
|
if err := os.MkdirAll(dst, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read directory entries
|
||||||
|
entries, err := os.ReadDir(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy each entry
|
||||||
|
for _, entry := range entries {
|
||||||
|
// Validate item name to prevent directory traversal
|
||||||
|
if strings.Contains(entry.Name(), "..") || strings.Contains(entry.Name(), "/") || strings.Contains(entry.Name(), "\\") {
|
||||||
|
return fmt.Errorf("invalid file name: %s", entry.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
srcPath := filepath.Join(src, entry.Name())
|
||||||
|
dstPath := filepath.Join(dst, entry.Name())
|
||||||
|
|
||||||
|
if err := copyRecursive(srcPath, dstPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Copy file
|
||||||
|
if err := copyFile(src, dst); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func downloadFile(url, fpath string) error {
|
||||||
|
resp, err := http.Get(url)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return fmt.Errorf("bad status: %s", resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err := os.Create(fpath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer out.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(out, resp.Body)
|
||||||
|
return err
|
||||||
|
}
|
||||||
108
.github/actions/trivy-scan/action.yml
vendored
Normal file
108
.github/actions/trivy-scan/action.yml
vendored
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
name: "Trivy Security Scan"
|
||||||
|
description: "Run Trivy vulnerability scanner on Docker images"
|
||||||
|
|
||||||
|
inputs:
|
||||||
|
image-tags:
|
||||||
|
description: "Image tags (one per line or comma-separated). First tag will be used for scanning."
|
||||||
|
required: true
|
||||||
|
image-name:
|
||||||
|
description: "Image name without tag (e.g., temporaliotest/server)"
|
||||||
|
required: true
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
scan-result:
|
||||||
|
description: "Result of the scan (pass or fail)"
|
||||||
|
value: ${{ steps.evaluate.outputs.result }}
|
||||||
|
critical-count:
|
||||||
|
description: "Number of critical vulnerabilities found"
|
||||||
|
value: ${{ steps.evaluate.outputs.critical-count }}
|
||||||
|
high-count:
|
||||||
|
description: "Number of high vulnerabilities found"
|
||||||
|
value: ${{ steps.evaluate.outputs.high-count }}
|
||||||
|
|
||||||
|
runs:
|
||||||
|
using: "composite"
|
||||||
|
steps:
|
||||||
|
- name: Set variables
|
||||||
|
id: vars
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: ${{ inputs.image-name }}
|
||||||
|
IMAGE_TAGS: ${{ inputs.image-tags }}
|
||||||
|
run: |
|
||||||
|
# Get first tag
|
||||||
|
tag=$(echo "$IMAGE_TAGS" | head -1)
|
||||||
|
|
||||||
|
# Construct image reference
|
||||||
|
image_ref="${IMAGE_NAME}:${tag}"
|
||||||
|
echo "image-ref=${image_ref}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Create safe name for artifacts
|
||||||
|
name=$(echo "$IMAGE_NAME" | sed 's/.*\///; s/[^a-zA-Z0-9._-]/-/g')
|
||||||
|
echo "safe-name=${name}" >> $GITHUB_OUTPUT
|
||||||
|
echo "artifact-name=trivy-${name}-results" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
- name: Install Trivy
|
||||||
|
uses: aquasecurity/setup-trivy@v0.2.4
|
||||||
|
with:
|
||||||
|
version: v0.65.0
|
||||||
|
|
||||||
|
- name: Scan Container Image
|
||||||
|
id: scan
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
IMAGE_REF: ${{ steps.vars.outputs.image-ref }}
|
||||||
|
SAFE_NAME: ${{ steps.vars.outputs.safe-name }}
|
||||||
|
run: |
|
||||||
|
echo "Scanning $IMAGE_REF for CRITICAL,HIGH vulnerabilities..."
|
||||||
|
|
||||||
|
# Scan and output table format for logs
|
||||||
|
trivy image \
|
||||||
|
--severity CRITICAL,HIGH \
|
||||||
|
--format table \
|
||||||
|
--no-progress \
|
||||||
|
"$IMAGE_REF" | tee trivy-scan-${SAFE_NAME}.txt
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Generating detailed JSON report..."
|
||||||
|
|
||||||
|
# Scan and output JSON format for parsing
|
||||||
|
trivy image \
|
||||||
|
--severity CRITICAL,HIGH \
|
||||||
|
--format json \
|
||||||
|
--no-progress \
|
||||||
|
"$IMAGE_REF" > trivy-scan-${SAFE_NAME}.json
|
||||||
|
|
||||||
|
- name: Evaluate scan results
|
||||||
|
id: evaluate
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
SAFE_NAME: ${{ steps.vars.outputs.safe-name }}
|
||||||
|
FAIL_ON_VULNS: ${{ inputs.fail-on-vulnerabilities }}
|
||||||
|
run: |
|
||||||
|
# Count vulnerabilities by severity
|
||||||
|
CRITICAL_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="CRITICAL")] | length' trivy-scan-${SAFE_NAME}.json)
|
||||||
|
HIGH_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="HIGH")] | length' trivy-scan-${SAFE_NAME}.json)
|
||||||
|
MEDIUM_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="MEDIUM")] | length' trivy-scan-${SAFE_NAME}.json)
|
||||||
|
LOW_COUNT=$(jq '[.Results[]?.Vulnerabilities[]? | select(.Severity=="LOW")] | length' trivy-scan-${SAFE_NAME}.json)
|
||||||
|
|
||||||
|
echo "critical-count=${CRITICAL_COUNT}" >> $GITHUB_OUTPUT
|
||||||
|
echo "high-count=${HIGH_COUNT}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Vulnerability Summary ==="
|
||||||
|
echo "Critical: $CRITICAL_COUNT"
|
||||||
|
echo "High: $HIGH_COUNT"
|
||||||
|
echo "Medium: $MEDIUM_COUNT"
|
||||||
|
echo "Low: $LOW_COUNT"
|
||||||
|
echo "============================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Set result status without failing
|
||||||
|
if [ "$CRITICAL_COUNT" -gt 0 ] || [ "$HIGH_COUNT" -gt 0 ]; then
|
||||||
|
echo "result=fail" >> $GITHUB_OUTPUT
|
||||||
|
echo "⚠️ Security vulnerabilities found!"
|
||||||
|
else
|
||||||
|
echo "result=pass" >> $GITHUB_OUTPUT
|
||||||
|
echo "✓ No critical or high vulnerabilities found."
|
||||||
|
fi
|
||||||
23
.github/docker/targets/admin-tools.Dockerfile
vendored
23
.github/docker/targets/admin-tools.Dockerfile
vendored
@@ -1,23 +0,0 @@
|
|||||||
FROM alpine:3.22@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
|
|
||||||
|
|
||||||
ARG TARGETARCH
|
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
ca-certificates \
|
|
||||||
tzdata && addgroup -g 1000 temporal && \
|
|
||||||
adduser -u 1000 -G temporal -D temporal
|
|
||||||
|
|
||||||
# Copy all admin tool binaries:
|
|
||||||
# - temporal (CLI)
|
|
||||||
# - temporal-server
|
|
||||||
# - temporal-cassandra-tool
|
|
||||||
# - temporal-sql-tool
|
|
||||||
# - temporal-elasticsearch-tool
|
|
||||||
# - tdbg
|
|
||||||
COPY --chmod=755 ./build/${TARGETARCH}/* /usr/local/bin/
|
|
||||||
|
|
||||||
COPY ./build/temporal/schema /etc/temporal/schema
|
|
||||||
|
|
||||||
USER temporal
|
|
||||||
|
|
||||||
CMD ["sh", "-c", "trap exit INT HUP TERM; sleep infinity"]
|
|
||||||
62
.github/workflows/build-and-publish.yml
vendored
Normal file
62
.github/workflows/build-and-publish.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
name: Build and Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- cloud/*
|
||||||
|
- feature/*
|
||||||
|
- release/*
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push-docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Only push for main, cloud, release branches (not feature)
|
||||||
|
if: |
|
||||||
|
github.ref == 'refs/heads/main' ||
|
||||||
|
startsWith(github.ref, 'refs/heads/cloud/') ||
|
||||||
|
startsWith(github.ref, 'refs/heads/release/')
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
ref: ${{ github.ref }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Build binaries
|
||||||
|
uses: ./.github/actions/build-binaries
|
||||||
|
with:
|
||||||
|
snapshot: true
|
||||||
|
|
||||||
|
- name: Build and push Docker images
|
||||||
|
uses: ./.github/actions/build-docker-images
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tag-latest: ${{ github.ref == 'refs/heads/main' }}
|
||||||
|
dockerhub-username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
# For feature branches, just build (no push)
|
||||||
|
build-docker-feature:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: startsWith(github.ref, 'refs/heads/feature/')
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
ref: ${{ github.ref }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Build binaries
|
||||||
|
uses: ./.github/actions/build-binaries
|
||||||
|
with:
|
||||||
|
snapshot: true
|
||||||
|
|
||||||
|
- name: Build Docker images
|
||||||
|
uses: ./.github/actions/build-docker-images
|
||||||
|
with:
|
||||||
|
push: false
|
||||||
|
tag-latest: false
|
||||||
2
.github/workflows/check-pr-placeholders.yml
vendored
2
.github/workflows/check-pr-placeholders.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Validate PR description for placeholder lines or empty sections
|
- name: Validate PR description for placeholder lines or empty sections
|
||||||
uses: actions/github-script@v7
|
uses: actions/github-script@v8
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const pr = await github.rest.pulls.get({
|
const pr = await github.rest.pulls.get({
|
||||||
|
|||||||
4
.github/workflows/create-tag.yml
vendored
4
.github/workflows/create-tag.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
|||||||
owner: ${{ github.repository_owner }}
|
owner: ${{ github.repository_owner }}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
token: ${{ steps.generate_token.outputs.token }}
|
token: ${{ steps.generate_token.outputs.token }}
|
||||||
@@ -190,7 +190,7 @@ jobs:
|
|||||||
owner: ${{ github.repository_owner }}
|
owner: ${{ github.repository_owner }}
|
||||||
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
persist-credentials: true
|
persist-credentials: true
|
||||||
token: ${{ steps.generate_token.outputs.token }}
|
token: ${{ steps.generate_token.outputs.token }}
|
||||||
|
|||||||
105
.github/workflows/docker-build-manual.yml
vendored
Normal file
105
.github/workflows/docker-build-manual.yml
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
name: Manual Docker Build
|
||||||
|
|
||||||
|
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 with digest (e.g., 3.23@sha256:...)"
|
||||||
|
required: true
|
||||||
|
default: "3.23@sha256:c78ded0fee4493809c8ca71d4a6057a46237763d952fae15ea418f6d14137f2d"
|
||||||
|
push:
|
||||||
|
description: "Push images to Docker Hub"
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
tag-latest:
|
||||||
|
description: "Tag images as latest"
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
platform:
|
||||||
|
description: "Platform to build (leave empty for multi-arch, or specify linux/amd64 or linux/arm64)"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
|
snapshot:
|
||||||
|
description: "Build in snapshot mode (for non-release builds)"
|
||||||
|
required: true
|
||||||
|
type: boolean
|
||||||
|
default: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-docker:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.ref }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Determine single-arch parameter
|
||||||
|
id: arch-param
|
||||||
|
run: |
|
||||||
|
if [ -n "${{ inputs.platform }}" ]; then
|
||||||
|
# Extract arch from platform (e.g., linux/amd64 -> amd64)
|
||||||
|
ARCH=$(echo "${{ inputs.platform }}" | cut -d'/' -f2)
|
||||||
|
echo "single-arch=${ARCH}" >> "$GITHUB_OUTPUT"
|
||||||
|
else
|
||||||
|
echo "single-arch=" >> "$GITHUB_OUTPUT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build binaries
|
||||||
|
uses: ./.github/actions/build-binaries
|
||||||
|
with:
|
||||||
|
snapshot: ${{ inputs.snapshot }}
|
||||||
|
single-arch: ${{ steps.arch-param.outputs.single-arch }}
|
||||||
|
|
||||||
|
- name: Build Docker images
|
||||||
|
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
|
||||||
|
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
|
||||||
|
run: |
|
||||||
|
{
|
||||||
|
echo "### Docker Images Built"
|
||||||
|
echo ""
|
||||||
|
echo "**Git Ref:** ${{ inputs.ref }}"
|
||||||
|
echo "**CLI Version:** ${{ inputs.cli-version }}"
|
||||||
|
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}"
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
62
.github/workflows/features-integration.yml
vendored
62
.github/workflows/features-integration.yml
vendored
@@ -14,13 +14,69 @@ concurrency: # Auto-cancel existing runs in the PR when a new commit is pushed
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-docker-image:
|
build-docker-image:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
actions: write
|
actions: write
|
||||||
contents: read
|
contents: read
|
||||||
uses: temporalio/docker-builds/.github/workflows/docker-build-only.yml@main
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
temporal-server-repo-path: ${{github.event.pull_request.head.repo.full_name}}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
temporal-server-repo-ref: ${{github.event.pull_request.head.ref}}
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Build binaries
|
||||||
|
uses: ./.github/actions/build-binaries
|
||||||
|
with:
|
||||||
|
snapshot: true
|
||||||
|
single-arch: amd64
|
||||||
|
|
||||||
|
- name: Build Docker images
|
||||||
|
uses: ./.github/actions/build-docker-images
|
||||||
|
with:
|
||||||
|
push: false
|
||||||
|
tag-latest: false
|
||||||
|
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
|
||||||
|
|
||||||
|
- name: Prepare artifact
|
||||||
|
working-directory: ${{ github.workspace }}
|
||||||
|
run: |
|
||||||
|
# Upload-artifact has no good way to flatten paths, so we need to move the compose file
|
||||||
|
# to avoid some disgustingly long inner path inside the artifact zip.
|
||||||
|
cp ./develop/docker-compose/docker-compose.yml /tmp/docker-compose.yml
|
||||||
|
echo -n "${{env.IMAGE_SHA_TAG}}" > /tmp/image_sha_tag
|
||||||
|
|
||||||
|
- name: Upload Docker artifact
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: temporal-server-docker
|
||||||
|
path: |
|
||||||
|
/tmp/temporal-server.tar
|
||||||
|
/tmp/temporal-admin-tools.tar
|
||||||
|
/tmp/image_tag
|
||||||
|
/tmp/docker-compose.yml
|
||||||
|
|
||||||
|
retention-days: 7
|
||||||
|
|
||||||
feature-tests-ts:
|
feature-tests-ts:
|
||||||
needs: build-docker-image
|
needs: build-docker-image
|
||||||
|
|||||||
2
.github/workflows/flaky-tests-report.yml
vendored
2
.github/workflows/flaky-tests-report.yml
vendored
@@ -34,7 +34,7 @@ jobs:
|
|||||||
owner: ${{ github.repository_owner }}
|
owner: ${{ github.repository_owner }}
|
||||||
|
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
|||||||
32
.github/workflows/goreleaser.yml
vendored
32
.github/workflows/goreleaser.yml
vendored
@@ -1,32 +0,0 @@
|
|||||||
name: goreleaser
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types:
|
|
||||||
- released
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
goreleaser:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v5
|
|
||||||
with:
|
|
||||||
go-version-file: 'go.mod'
|
|
||||||
check-latest: true
|
|
||||||
|
|
||||||
- name: Run GoReleaser
|
|
||||||
uses: goreleaser/goreleaser-action@v5
|
|
||||||
with:
|
|
||||||
version: latest
|
|
||||||
args: release --clean
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
24
.github/workflows/linters.yml
vendored
24
.github/workflows/linters.yml
vendored
@@ -7,11 +7,11 @@ jobs:
|
|||||||
lint-actions:
|
lint-actions:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -26,11 +26,11 @@ jobs:
|
|||||||
lint-protos:
|
lint-protos:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -46,11 +46,11 @@ jobs:
|
|||||||
lint-api:
|
lint-api:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: true
|
submodules: true
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -67,11 +67,11 @@ jobs:
|
|||||||
lint-workflows:
|
lint-workflows:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -86,11 +86,11 @@ jobs:
|
|||||||
fmt-imports:
|
fmt-imports:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
@@ -111,11 +111,11 @@ jobs:
|
|||||||
golangci:
|
golangci:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: 'go.mod'
|
go-version-file: 'go.mod'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|||||||
31
.github/workflows/promote-admin-tools-image.yml
vendored
Normal file
31
.github/workflows/promote-admin-tools-image.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: Promote Admin Tools Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
source-tag:
|
||||||
|
description: "Source tag from temporaliotest registry (e.g. sha-abc123)"
|
||||||
|
required: true
|
||||||
|
target-tags:
|
||||||
|
description: "Target tags for temporalio registry (comma or newline separated, e.g., 1.29.1, latest)"
|
||||||
|
required: true
|
||||||
|
override-security-scan:
|
||||||
|
description: "Override security scan failures (use with caution)"
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
required: false
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
promote:
|
||||||
|
uses: ./.github/workflows/promote-docker-image.yml
|
||||||
|
with:
|
||||||
|
image-name: admin-tools
|
||||||
|
source-tag: ${{ inputs.source-tag }}
|
||||||
|
target-tags: ${{ inputs.target-tags }}
|
||||||
|
override-security-scan: ${{ inputs.override-security-scan }}
|
||||||
|
secrets:
|
||||||
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
211
.github/workflows/promote-docker-image.yml
vendored
Normal file
211
.github/workflows/promote-docker-image.yml
vendored
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
name: Promote Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
inputs:
|
||||||
|
image-name:
|
||||||
|
description: "Image name (e.g., server, admin-tools)"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
source-tag:
|
||||||
|
description: "Source tag from temporaliotest registry (e.g. sha-abc123)"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
target-tags:
|
||||||
|
description: "Target tags for temporalio registry (comma or newline separated, e.g., 1.29.1, latest)"
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
override-security-scan:
|
||||||
|
description: "Override security scan failures (use with caution)"
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
secrets:
|
||||||
|
DOCKERHUB_USERNAME:
|
||||||
|
required: true
|
||||||
|
DOCKERHUB_TOKEN:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
validate-inputs:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
source-tag-safe: ${{ steps.validate.outputs.source-tag }}
|
||||||
|
target-tags-safe: ${{ steps.validate.outputs.target-tags }}
|
||||||
|
steps:
|
||||||
|
- name: Validate input tags
|
||||||
|
id: validate
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
env:
|
||||||
|
SOURCE_TAG: ${{ inputs.source-tag }}
|
||||||
|
TARGET_TAGS: ${{ inputs.target-tags }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const sourceTag = process.env.SOURCE_TAG;
|
||||||
|
const targetTagsInput = process.env.TARGET_TAGS;
|
||||||
|
|
||||||
|
// Source tag: must be short SHA (sha-XXXXXXX) or full sha256 digest
|
||||||
|
// Examples: sha-b5b2dfe, sha256:082943409e71ae50d8dd8693593070eac8173f01fb5bfd4970ae59e52176753e
|
||||||
|
const sourceTagPattern = /^sha-[a-f0-9]{7,}$|^sha256:[a-f0-9]{64}$/;
|
||||||
|
|
||||||
|
// Target tag: semantic version pattern (major.minor.patch.build or shorter)
|
||||||
|
// Examples: 1.29.1, 1.29.1.1, 1.29, latest
|
||||||
|
const targetTagPattern = /^(\d+\.)*\d+$|^latest$/;
|
||||||
|
|
||||||
|
// Validate source tag format
|
||||||
|
if (!sourceTagPattern.test(sourceTag)) {
|
||||||
|
core.setFailed('Error: Invalid source tag format. Must be sha-XXXXXXX or sha256:...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse and validate target tags (split by newline or comma)
|
||||||
|
const targetTags = targetTagsInput
|
||||||
|
.split(/[\n,]/)
|
||||||
|
.map(tag => tag.trim())
|
||||||
|
.filter(tag => tag.length > 0);
|
||||||
|
|
||||||
|
if (targetTags.length === 0) {
|
||||||
|
core.setFailed('Error: At least one target tag must be provided');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const tag of targetTags) {
|
||||||
|
if (!targetTagPattern.test(tag)) {
|
||||||
|
core.setFailed(`Error: Invalid target tag format: "${tag}". Must be semantic version (e.g., 1.29.1, 1.29.1.1) or "latest"`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.setOutput('source-tag', sourceTag);
|
||||||
|
core.setOutput('target-tags', JSON.stringify(targetTags));
|
||||||
|
|
||||||
|
core.info('✓ Tag validation passed');
|
||||||
|
core.info(` Source: ${sourceTag}`);
|
||||||
|
core.info(` Target tags: ${targetTags.join(', ')}`);
|
||||||
|
|
||||||
|
scan-image:
|
||||||
|
needs: validate-inputs
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
scan-result: ${{ steps.scan.outputs.scan-result }}
|
||||||
|
critical-count: ${{ steps.scan.outputs.critical-count }}
|
||||||
|
high-count: ${{ steps.scan.outputs.high-count }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Pull source image
|
||||||
|
env:
|
||||||
|
SOURCE_TAG: ${{ needs.validate-inputs.outputs.source-tag-safe }}
|
||||||
|
IMAGE_NAME: ${{ inputs.image-name }}
|
||||||
|
run: |
|
||||||
|
echo "Pulling temporaliotest/${IMAGE_NAME}:${SOURCE_TAG}"
|
||||||
|
docker pull "temporaliotest/${IMAGE_NAME}:${SOURCE_TAG}"
|
||||||
|
|
||||||
|
- name: Scan image with Trivy
|
||||||
|
id: scan
|
||||||
|
uses: ./.github/actions/trivy-scan
|
||||||
|
with:
|
||||||
|
image-name: temporaliotest/${{ inputs.image-name }}
|
||||||
|
image-tags: ${{ needs.validate-inputs.outputs.source-tag-safe }}
|
||||||
|
|
||||||
|
- name: Upload Trivy scan results
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: trivy-${{ inputs.image-name }}-scan-results
|
||||||
|
path: trivy-scan-${{ inputs.image-name }}.json
|
||||||
|
retention-days: 30
|
||||||
|
|
||||||
|
check-security-gate:
|
||||||
|
needs: [scan-image]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
can-promote: ${{ steps.check.outputs.can-promote }}
|
||||||
|
steps:
|
||||||
|
- name: Evaluate security scan results
|
||||||
|
id: check
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
env:
|
||||||
|
OVERRIDE: ${{ inputs.override-security-scan }}
|
||||||
|
SCAN_RESULT: ${{ needs.scan-image.outputs.scan-result }}
|
||||||
|
CRITICAL_COUNT: ${{ needs.scan-image.outputs.critical-count }}
|
||||||
|
HIGH_COUNT: ${{ needs.scan-image.outputs.high-count }}
|
||||||
|
IMAGE_NAME: ${{ inputs.image-name }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const override = process.env.OVERRIDE === 'true';
|
||||||
|
const scanResult = process.env.SCAN_RESULT;
|
||||||
|
const criticalCount = process.env.CRITICAL_COUNT;
|
||||||
|
const highCount = process.env.HIGH_COUNT;
|
||||||
|
const imageName = process.env.IMAGE_NAME;
|
||||||
|
|
||||||
|
core.info('=== Security Scan Results ===');
|
||||||
|
core.info(`${imageName} image: ${scanResult}`);
|
||||||
|
core.info(` Critical: ${criticalCount}`);
|
||||||
|
core.info(` High: ${highCount}`);
|
||||||
|
core.info('');
|
||||||
|
core.info(`Override enabled: ${override}`);
|
||||||
|
core.info('==============================');
|
||||||
|
|
||||||
|
if (scanResult === 'fail') {
|
||||||
|
if (override) {
|
||||||
|
core.info('');
|
||||||
|
core.warning('Security vulnerabilities detected but OVERRIDE is enabled!');
|
||||||
|
core.info('Proceeding with image promotion despite security issues.');
|
||||||
|
core.setOutput('can-promote', 'true');
|
||||||
|
} else {
|
||||||
|
core.info('');
|
||||||
|
core.error('Security vulnerabilities detected. Promotion BLOCKED.');
|
||||||
|
core.info("To proceed anyway, re-run with 'override-security-scan' enabled.");
|
||||||
|
core.setOutput('can-promote', 'false');
|
||||||
|
core.setFailed('Security vulnerabilities detected');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
core.info('');
|
||||||
|
core.info('✓ Security scan passed. Proceeding with promotion.');
|
||||||
|
core.setOutput('can-promote', 'true');
|
||||||
|
}
|
||||||
|
|
||||||
|
promote:
|
||||||
|
needs: [validate-inputs, check-security-gate]
|
||||||
|
if: needs.check-security-gate.outputs.can-promote == 'true'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Pull, tag, and push image
|
||||||
|
uses: actions/github-script@v8
|
||||||
|
env:
|
||||||
|
SOURCE_TAG: ${{ needs.validate-inputs.outputs.source-tag-safe }}
|
||||||
|
TARGET_TAGS: ${{ needs.validate-inputs.outputs.target-tags-safe }}
|
||||||
|
IMAGE_NAME: ${{ inputs.image-name }}
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const sourceTag = process.env.SOURCE_TAG;
|
||||||
|
const targetTags = JSON.parse(process.env.TARGET_TAGS);
|
||||||
|
const imageName = process.env.IMAGE_NAME;
|
||||||
|
|
||||||
|
core.info(`Promoting ${imageName} image...`);
|
||||||
|
core.info(` From: temporaliotest/${imageName}:${sourceTag}`);
|
||||||
|
core.info(` To: ${targetTags.map(t => `temporalio/${imageName}:${t}`).join(', ')}`);
|
||||||
|
|
||||||
|
// Pull from test registry
|
||||||
|
core.info('Pulling source image...');
|
||||||
|
execSync(`docker pull temporaliotest/${imageName}:${sourceTag}`, { stdio: 'inherit' });
|
||||||
|
|
||||||
|
// Tag for each target tag
|
||||||
|
for (const targetTag of targetTags) {
|
||||||
|
core.info(`Tagging as temporalio/${imageName}:${targetTag}`);
|
||||||
|
execSync(`docker tag temporaliotest/${imageName}:${sourceTag} temporalio/${imageName}:${targetTag}`, { stdio: 'inherit' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push all tags at once
|
||||||
|
core.info('Pushing all tags to production registry...');
|
||||||
|
execSync(`docker push --all-tags temporalio/${imageName}`, { stdio: 'inherit' });
|
||||||
|
|
||||||
|
core.info(`✓ ${imageName} image promoted successfully to all tags`);
|
||||||
31
.github/workflows/promote-server-image.yml
vendored
Normal file
31
.github/workflows/promote-server-image.yml
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
name: Promote Server Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
source-tag:
|
||||||
|
description: "Source tag from temporaliotest registry (e.g. sha-abc123)"
|
||||||
|
required: true
|
||||||
|
target-tags:
|
||||||
|
description: "Target tags for temporalio registry (comma or newline separated, e.g., 1.29.1, latest)"
|
||||||
|
required: true
|
||||||
|
override-security-scan:
|
||||||
|
description: "Override security scan failures (use with caution)"
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
required: false
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
promote:
|
||||||
|
uses: ./.github/workflows/promote-docker-image.yml
|
||||||
|
with:
|
||||||
|
image-name: server
|
||||||
|
source-tag: ${{ inputs.source-tag }}
|
||||||
|
target-tags: ${{ inputs.target-tags }}
|
||||||
|
override-security-scan: ${{ inputs.override-security-scan }}
|
||||||
|
secrets:
|
||||||
|
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
25
.github/workflows/release.yml
vendored
Normal file
25
.github/workflows/release.yml
vendored
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- released
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
ref: ${{ github.ref }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Build binaries
|
||||||
|
uses: ./.github/actions/build-binaries
|
||||||
|
with:
|
||||||
|
snapshot: false
|
||||||
|
release: true
|
||||||
30
.github/workflows/run-tests.yml
vendored
30
.github/workflows/run-tests.yml
vendored
@@ -108,7 +108,7 @@ jobs:
|
|||||||
cat "$GITHUB_OUTPUT"
|
cat "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ref: ${{ env.COMMIT }}
|
ref: ${{ env.COMMIT }}
|
||||||
@@ -139,13 +139,13 @@ jobs:
|
|||||||
name: Pre-build for cache
|
name: Pre-build for cache
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
|
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ref: ${{ env.COMMIT }}
|
ref: ${{ env.COMMIT }}
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
|
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
@@ -181,7 +181,7 @@ jobs:
|
|||||||
needs: pre-build
|
needs: pre-build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
|
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -189,7 +189,7 @@ jobs:
|
|||||||
# buf-breaking tries to compare HEAD against merge base so we need to be able to find it
|
# buf-breaking tries to compare HEAD against merge base so we need to be able to find it
|
||||||
fetch-depth: 100
|
fetch-depth: 100
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
|
if: ${{ !inputs.run_single_functional_test && !inputs.run_single_unit_test }}
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
@@ -229,12 +229,12 @@ jobs:
|
|||||||
needs: [pre-build, set-up-single-test]
|
needs: [pre-build, set-up-single-test]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ref: ${{ env.COMMIT }}
|
ref: ${{ env.COMMIT }}
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
cache: false # do our own caching
|
cache: false # do our own caching
|
||||||
@@ -319,7 +319,7 @@ jobs:
|
|||||||
needs: [pre-build, set-up-single-test]
|
needs: [pre-build, set-up-single-test]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ref: ${{ env.COMMIT }}
|
ref: ${{ env.COMMIT }}
|
||||||
@@ -334,7 +334,7 @@ jobs:
|
|||||||
postgresql
|
postgresql
|
||||||
down-flags: -v
|
down-flags: -v
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
cache: false # do our own caching
|
cache: false # do our own caching
|
||||||
@@ -481,7 +481,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
key: docker-${{ runner.os }}${{ runner.arch }}-${{ hashFiles(env.DOCKER_COMPOSE_FILE) }}
|
key: docker-${{ runner.os }}${{ runner.arch }}-${{ hashFiles(env.DOCKER_COMPOSE_FILE) }}
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
if: ${{ inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER)) }}
|
if: ${{ inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER)) }}
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -495,7 +495,7 @@ jobs:
|
|||||||
services: "${{ join(matrix.containers, '\n') }}"
|
services: "${{ join(matrix.containers, '\n') }}"
|
||||||
down-flags: -v
|
down-flags: -v
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
if: ${{ inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER)) }}
|
if: ${{ inputs.run_single_functional_test != true || (inputs.run_single_functional_test == true && contains(fromJSON(needs.set-up-single-test.outputs.dbs), env.PERSISTENCE_DRIVER)) }}
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
@@ -645,7 +645,7 @@ jobs:
|
|||||||
PERSISTENCE_DRIVER: ${{ matrix.persistence_driver }}
|
PERSISTENCE_DRIVER: ${{ matrix.persistence_driver }}
|
||||||
TEST_PARALLEL_FLAGS: ${{ matrix.parallel_flags }}
|
TEST_PARALLEL_FLAGS: ${{ matrix.parallel_flags }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ref: ${{ env.COMMIT }}
|
ref: ${{ env.COMMIT }}
|
||||||
@@ -665,7 +665,7 @@ jobs:
|
|||||||
services: "${{ join(matrix.containers, '\n') }}"
|
services: "${{ join(matrix.containers, '\n') }}"
|
||||||
down-flags: -v
|
down-flags: -v
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
cache: false # do our own caching
|
cache: false # do our own caching
|
||||||
@@ -806,7 +806,7 @@ jobs:
|
|||||||
PERSISTENCE_DRIVER: ${{ matrix.persistence_driver }}
|
PERSISTENCE_DRIVER: ${{ matrix.persistence_driver }}
|
||||||
ES_VERSION: ${{ matrix.es_version }}
|
ES_VERSION: ${{ matrix.es_version }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
ref: ${{ env.COMMIT }}
|
ref: ${{ env.COMMIT }}
|
||||||
@@ -826,7 +826,7 @@ jobs:
|
|||||||
services: "${{ join(matrix.containers, '\n') }}"
|
services: "${{ join(matrix.containers, '\n') }}"
|
||||||
down-flags: -v
|
down-flags: -v
|
||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v6
|
||||||
with:
|
with:
|
||||||
go-version-file: "go.mod"
|
go-version-file: "go.mod"
|
||||||
cache: false # do our own caching
|
cache: false # do our own caching
|
||||||
|
|||||||
@@ -1,18 +1,25 @@
|
|||||||
|
# GoReleaser v2 configuration
|
||||||
|
version: 2
|
||||||
|
|
||||||
before:
|
before:
|
||||||
hooks:
|
hooks:
|
||||||
- go mod download
|
- go mod download
|
||||||
|
|
||||||
|
snapshot:
|
||||||
|
version_template: "{{ .Version }}-SNAPSHOT-{{ .ShortCommit }}"
|
||||||
|
|
||||||
archives:
|
archives:
|
||||||
- id: default
|
- id: default
|
||||||
builds:
|
ids:
|
||||||
- temporal-server
|
- temporal-server
|
||||||
- temporal-cassandra-tool
|
- temporal-cassandra-tool
|
||||||
- temporal-sql-tool
|
- temporal-sql-tool
|
||||||
- temporal-elasticsearch-tool
|
- temporal-elasticsearch-tool
|
||||||
|
- tdbg
|
||||||
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
|
||||||
format_overrides:
|
format_overrides:
|
||||||
- goos: windows
|
- goos: windows
|
||||||
format: zip
|
formats: [zip]
|
||||||
files:
|
files:
|
||||||
- ./config/*
|
- ./config/*
|
||||||
|
|
||||||
@@ -79,11 +86,11 @@ builds:
|
|||||||
- arm64
|
- arm64
|
||||||
|
|
||||||
checksum:
|
checksum:
|
||||||
name_template: 'checksums.txt'
|
name_template: "checksums.txt"
|
||||||
algorithm: sha256
|
algorithm: sha256
|
||||||
|
|
||||||
changelog:
|
changelog:
|
||||||
skip: true
|
disable: true
|
||||||
|
|
||||||
announce:
|
announce:
|
||||||
skip: "true"
|
skip: true
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
variable "SERVER_VERSION" {
|
variable "SERVER_VERSION" {
|
||||||
default = "1.29.1"
|
default = "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "CLI_VERSION" {
|
variable "CLI_VERSION" {
|
||||||
default = "1.5.0"
|
default = "unknown"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "IMAGE_REPO" {
|
variable "IMAGE_REPO" {
|
||||||
default = "temporaliotest"
|
default = "temporaliotest"
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "IMAGE_SHA_TAG" {}
|
variable "IMAGE_SHA_TAG" {
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
variable "IMAGE_BRANCH_TAG" {}
|
variable "IMAGE_BRANCH_TAG" {
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
variable "SAFE_IMAGE_BRANCH_TAG" {
|
variable "SAFE_IMAGE_BRANCH_TAG" {
|
||||||
default = join("-", [for c in regexall("[a-z0-9]+", lower(IMAGE_BRANCH_TAG)) : c])
|
default = join("-", [for c in regexall("[a-z0-9]+", lower(IMAGE_BRANCH_TAG)) : c])
|
||||||
@@ -26,12 +30,19 @@ variable "TAG_LATEST" {
|
|||||||
default = false
|
default = false
|
||||||
}
|
}
|
||||||
|
|
||||||
# Legacy targets (legacy-admin-tools, legacy-server) are for building images with server versions
|
# IMPORTANT: When updating ALPINE_TAG, also update the default value in:
|
||||||
# older than v1.27.0 (3 minor versions behind v1.30.0). Once support for pre-1.27.0 versions is
|
# - docker/targets/admin-tools.Dockerfile
|
||||||
# no longer needed, these legacy targets can be removed and only the standard targets should be used.
|
# - docker/targets/server.Dockerfile
|
||||||
|
variable "ALPINE_TAG" {
|
||||||
|
default = "3.23@sha256:c78ded0fee4493809c8ca71d4a6057a46237763d952fae15ea418f6d14137f2d"
|
||||||
|
}
|
||||||
|
|
||||||
target "admin-tools" {
|
target "admin-tools" {
|
||||||
|
context = "docker"
|
||||||
dockerfile = "targets/admin-tools.Dockerfile"
|
dockerfile = "targets/admin-tools.Dockerfile"
|
||||||
|
args = {
|
||||||
|
ALPINE_TAG = "${ALPINE_TAG}"
|
||||||
|
}
|
||||||
tags = compact([
|
tags = compact([
|
||||||
"${IMAGE_REPO}/admin-tools:${IMAGE_SHA_TAG}",
|
"${IMAGE_REPO}/admin-tools:${IMAGE_SHA_TAG}",
|
||||||
"${IMAGE_REPO}/admin-tools:${SAFE_IMAGE_BRANCH_TAG}",
|
"${IMAGE_REPO}/admin-tools:${SAFE_IMAGE_BRANCH_TAG}",
|
||||||
@@ -44,7 +55,6 @@ target "admin-tools" {
|
|||||||
"org.opencontainers.image.url" = "https://github.com/temporalio/temporal"
|
"org.opencontainers.image.url" = "https://github.com/temporalio/temporal"
|
||||||
"org.opencontainers.image.source" = "https://github.com/temporalio/temporal"
|
"org.opencontainers.image.source" = "https://github.com/temporalio/temporal"
|
||||||
"org.opencontainers.image.licenses" = "MIT"
|
"org.opencontainers.image.licenses" = "MIT"
|
||||||
"org.opencontainers.image.version" = "${SERVER_VERSION}"
|
|
||||||
"org.opencontainers.image.revision" = "${TEMPORAL_SHA}"
|
"org.opencontainers.image.revision" = "${TEMPORAL_SHA}"
|
||||||
"org.opencontainers.image.created" = timestamp()
|
"org.opencontainers.image.created" = timestamp()
|
||||||
"com.temporal.server.version" = "${SERVER_VERSION}"
|
"com.temporal.server.version" = "${SERVER_VERSION}"
|
||||||
@@ -53,7 +63,11 @@ target "admin-tools" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
target "server" {
|
target "server" {
|
||||||
|
context = "docker"
|
||||||
dockerfile = "targets/server.Dockerfile"
|
dockerfile = "targets/server.Dockerfile"
|
||||||
|
args = {
|
||||||
|
ALPINE_TAG = "${ALPINE_TAG}"
|
||||||
|
}
|
||||||
tags = compact([
|
tags = compact([
|
||||||
"${IMAGE_REPO}/server:${IMAGE_SHA_TAG}",
|
"${IMAGE_REPO}/server:${IMAGE_SHA_TAG}",
|
||||||
"${IMAGE_REPO}/server:${SAFE_IMAGE_BRANCH_TAG}",
|
"${IMAGE_REPO}/server:${SAFE_IMAGE_BRANCH_TAG}",
|
||||||
33
docker/targets/admin-tools.Dockerfile
Normal file
33
docker/targets/admin-tools.Dockerfile
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# IMPORTANT: When updating ALPINE_TAG, also update the default value in:
|
||||||
|
# - docker/docker-bake.hcl (variable "ALPINE_TAG")
|
||||||
|
# - docker/targets/server.Dockerfile (ARG ALPINE_TAG)
|
||||||
|
ARG ALPINE_TAG=3.23@sha256:c78ded0fee4493809c8ca71d4a6057a46237763d952fae15ea418f6d14137f2d
|
||||||
|
|
||||||
|
FROM alpine:${ALPINE_TAG}
|
||||||
|
|
||||||
|
ARG TARGETARCH
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
ca-certificates \
|
||||||
|
tzdata && addgroup -g 1000 temporal && \
|
||||||
|
adduser -u 1000 -G temporal -D temporal
|
||||||
|
|
||||||
|
# Copy all admin tool binaries:
|
||||||
|
# - temporal (CLI)
|
||||||
|
# - temporal-cassandra-tool
|
||||||
|
# - temporal-sql-tool
|
||||||
|
# - temporal-elasticsearch-tool
|
||||||
|
# - tdbg
|
||||||
|
COPY --chmod=755 \
|
||||||
|
./build/${TARGETARCH}/temporal \
|
||||||
|
./build/${TARGETARCH}/temporal-cassandra-tool \
|
||||||
|
./build/${TARGETARCH}/temporal-sql-tool \
|
||||||
|
./build/${TARGETARCH}/temporal-elasticsearch-tool \
|
||||||
|
./build/${TARGETARCH}/tdbg \
|
||||||
|
/usr/local/bin/
|
||||||
|
|
||||||
|
COPY ./build/temporal/schema /etc/temporal/schema
|
||||||
|
|
||||||
|
USER temporal
|
||||||
|
|
||||||
|
CMD ["sh", "-c", "trap exit INT HUP TERM; sleep infinity"]
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
FROM alpine:3.22@sha256:4b7ce07002c69e8f3d704a9c5d6fd3053be500b7f1c69fc0d80990c2ad8dd412
|
# IMPORTANT: When updating ALPINE_TAG, also update the default value in:
|
||||||
|
# - docker/docker-bake.hcl (variable "ALPINE_TAG")
|
||||||
|
# - docker/targets/admin-tools.Dockerfile (ARG ALPINE_TAG)
|
||||||
|
ARG ALPINE_TAG=3.23@sha256:c78ded0fee4493809c8ca71d4a6057a46237763d952fae15ea418f6d14137f2d
|
||||||
|
|
||||||
|
FROM alpine:${ALPINE_TAG}
|
||||||
|
|
||||||
ARG TARGETARCH
|
ARG TARGETARCH
|
||||||
|
|
||||||
Reference in New Issue
Block a user