Skip to content

Build and Push Argo Workflows (BoringCrypto) #280

Build and Push Argo Workflows (BoringCrypto)

Build and Push Argo Workflows (BoringCrypto) #280

name: Build and Push Argo Workflows (BoringCrypto)
on:
schedule:
# Check for new releases every day at 6 AM UTC
- cron: '0 6 * * *'
workflow_dispatch:
inputs:
version:
description: 'Argo Workflows version to build (e.g., v3.7.2)'
required: false
type: string
force_build:
description: 'Force build even if image exists'
required: false
default: false
type: boolean
registry:
description: 'Container registry to push to'
required: false
default: 'ghcr.io'
type: choice
options:
- 'ghcr.io'
- 'quay.io'
- 'docker.io'
push:
branches:
- main
paths:
- '.github/workflows/build-and-push.yml'
env: {}
jobs:
check-releases:
runs-on: ubuntu-latest
outputs:
versions: ${{ steps.get-versions.outputs.versions }}
latest-version: ${{ steps.get-versions.outputs.latest-version }}
steps:
- name: Get Argo Workflows releases
id: get-versions
run: |
# Get the latest releases from upstream
if [ "${{ github.event.inputs.version }}" != "" ]; then
# Manual trigger with specific version
versions='["${{ github.event.inputs.version }}"]'
latest_version="${{ github.event.inputs.version }}"
else
# Get latest 2 stable releases (non-RC versions)
releases=$(curl -s https://api.github.com/repos/argoproj/argo-workflows/releases | jq -r '.[] | select(.tag_name | test("^v[0-9]+\\.[0-9]+\\.[0-9]+$")) | .tag_name' | head -2)
versions=$(echo "$releases" | jq -R . | jq -s . -c)
latest_version=$(echo "$releases" | head -1)
fi
echo "versions=$versions" >> $GITHUB_OUTPUT
echo "latest-version=$latest_version" >> $GITHUB_OUTPUT
echo "Found versions: $versions"
echo "Latest version: $latest_version"
build-multiarch:
name: Build & push PowerPC/s390x
needs: check-releases
runs-on: ubuntu-latest
strategy:
matrix:
platform: [ linux/ppc64le, linux/s390x ]
target: [ workflow-controller, argocli, argoexec, argoexec-nonroot ]
version: ${{ fromJson(needs.check-releases.outputs.versions) }}
permissions:
contents: read
packages: write
steps:
- name: Set registry variables
run: |
if [ "${{ github.event.inputs.registry }}" != "" ]; then
echo "REGISTRY=${{ github.event.inputs.registry }}" >> $GITHUB_ENV
else
echo "REGISTRY=ghcr.io" >> $GITHUB_ENV
fi
# Set image namespace and login credentials based on registry
if [ "${{ env.REGISTRY || 'ghcr.io' }}" = "ghcr.io" ]; then
echo "IMAGE_NAMESPACE=ghcr.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.GHCR_USERNAME || github.actor }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
elif [ "${{ env.REGISTRY || 'ghcr.io' }}" = "quay.io" ]; then
echo "IMAGE_NAMESPACE=quay.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.QUAYIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.QUAYIO_PASSWORD }}" >> $GITHUB_ENV
else
echo "IMAGE_NAMESPACE=${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.DOCKERIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.DOCKERIO_PASSWORD }}" >> $GITHUB_ENV
fi
- name: Clone Argo Workflows source
run: |
git clone --depth 1 --branch ${{ matrix.version }} https://github.com/argoproj/argo-workflows.git
cd argo-workflows
- name: Modify Dockerfile for s390x argocli
if: matrix.platform == 'linux/s390x' && matrix.target == 'argocli'
run: |
cd argo-workflows
# Find and replace node:<version>-alpine with node:<version>-slim
sed -i 's/FROM node:\([0-9]\+\)-alpine/FROM node:\1-slim/' Dockerfile
# Find node line number and replace apk with apt in next 3 lines
LINE=$(grep -n "FROM node:[0-9]\+-slim" Dockerfile | cut -d: -f1)
sed -i "$((LINE+1)),$((LINE+3)){ s/RUN apk update && apk add --no-cache git/RUN apt-get update \&\& apt-get install -y git/; }" Dockerfile
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-${{ matrix.platform }}-${{ matrix.target }}-${{ matrix.version }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-${{ matrix.platform }}-${{ matrix.target }}-${{ matrix.version }}-buildx-
${{ runner.os }}-${{ matrix.platform }}-${{ matrix.target }}-buildx-
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_PASSWORD }}
- name: Check if image already exists
id: check-image
run: |
cd argo-workflows
tag="${{ matrix.version }}"
tag_suffix=$(echo "${{ matrix.platform }}" | sed -r "s/\//-/g")
# Special handling for argoexec-nonroot to create argoexec:tag-nonroot-platform instead
if [ "${{ matrix.target }}" = "argoexec-nonroot" ]; then
image_name="${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-${tag_suffix}"
else
image_name="${{ env.IMAGE_NAMESPACE }}/${{ matrix.target }}:${tag}-${tag_suffix}"
fi
if docker manifest inspect "$image_name" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Image $image_name already exists"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Image $image_name does not exist"
fi
echo "image_name=$image_name" >> $GITHUB_OUTPUT
- name: Build and push Docker image
if: steps.check-image.outputs.exists != 'true' || github.event.inputs.force_build == 'true'
run: |
cd argo-workflows
set -eux
tag="${{ matrix.version }}"
# Get git info (copied verbatim from upstream)
GIT_COMMIT=$(git rev-parse HEAD || echo unknown)
GIT_TAG=$(git describe --exact-match --tags --abbrev=0 2> /dev/null || echo untagged)
GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)
tag_suffix=$(echo "${{ matrix.platform }}" | sed -r "s/\//-/g")
# Special handling for argoexec-nonroot to create argoexec:tag-nonroot-platform instead
if [ "${{ matrix.target }}" = "argoexec-nonroot" ]; then
image_name="${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-${tag_suffix}"
else
image_name="${{ env.IMAGE_NAMESPACE }}/${{ matrix.target }}:${tag}-${tag_suffix}"
fi
docker buildx build \
--cache-from "type=local,src=/tmp/.buildx-cache" \
--cache-to "type=local,dest=/tmp/.buildx-cache-new,mode=max" \
--output "type=image,push=true" \
--build-arg GIT_COMMIT=$GIT_COMMIT \
--build-arg GIT_TAG=$GIT_TAG \
--build-arg GIT_TREE_STATE=$GIT_TREE_STATE \
--platform="${{ matrix.platform }}" \
--target ${{ matrix.target }} \
--provenance=false \
--tag $image_name .
- name: Move cache
if: steps.check-image.outputs.exists != 'true' || github.event.inputs.force_build == 'true'
run: |
rm -rf /tmp/.buildx-cache
mv /tmp/.buildx-cache-new /tmp/.buildx-cache
build-fips:
name: Build & push linux (FIPS)
needs: [check-releases, build-multiarch]
runs-on: ubuntu-latest
strategy:
matrix:
platform: [ linux/amd64, linux/arm64]
target: [ workflow-controller, argocli, argoexec, argoexec-nonroot ]
version: ${{ fromJson(needs.check-releases.outputs.versions) }}
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set registry variables
run: |
if [ "${{ github.event.inputs.registry }}" != "" ]; then
echo "REGISTRY=${{ github.event.inputs.registry }}" >> $GITHUB_ENV
else
echo "REGISTRY=ghcr.io" >> $GITHUB_ENV
fi
# Set image namespace and login credentials based on registry
if [ "${{ env.REGISTRY || 'ghcr.io' }}" = "ghcr.io" ]; then
echo "IMAGE_NAMESPACE=ghcr.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.GHCR_USERNAME || github.actor }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
elif [ "${{ env.REGISTRY || 'ghcr.io' }}" = "quay.io" ]; then
echo "IMAGE_NAMESPACE=quay.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.QUAYIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.QUAYIO_PASSWORD }}" >> $GITHUB_ENV
else
echo "IMAGE_NAMESPACE=${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.DOCKERIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.DOCKERIO_PASSWORD }}" >> $GITHUB_ENV
fi
- name: Clone Argo Workflows source
run: |
git clone --depth 1 --branch ${{ matrix.version }} https://github.com/argoproj/argo-workflows.git
cd argo-workflows
- name: Detect Go version from upstream Dockerfile
id: go-version
run: |
cd argo-workflows
# Extract Go version from upstream Dockerfile
GO_IMAGE=$(grep -E '^ARG GOLANG_IMAGE=' Dockerfile | head -1 | cut -d= -f2)
if [ -z "$GO_IMAGE" ]; then
# Fallback: try to extract from FROM golang line
GO_IMAGE=$(grep -E '^FROM golang:' Dockerfile | head -1 | awk '{print $2}' | sed 's/FROM //' | sed 's/ as.*//')
fi
if [ -z "$GO_IMAGE" ]; then
# Default fallback
GO_IMAGE="golang:1.25.6-alpine3.23"
echo "⚠️ Could not detect Go version from Dockerfile, using default: $GO_IMAGE"
else
echo "✅ Detected Go version from upstream: $GO_IMAGE"
fi
echo "golang_image=$GO_IMAGE" >> $GITHUB_OUTPUT
- name: Replace Dockerfile with FIPS version
run: |
cd argo-workflows
cp ../Dockerfile.fips Dockerfile
- name: Modify Dockerfile for s390x argocli
if: matrix.platform == 'linux/s390x' && matrix.target == 'argocli'
run: |
cd argo-workflows
# Find and replace node:<version>-alpine with node:<version>-slim
sed -i 's/FROM node:\([0-9]\+\)-alpine/FROM node:\1-slim/' Dockerfile
# Find node line number and replace apk with apt in next 3 lines
LINE=$(grep -n "FROM node:[0-9]\+-slim" Dockerfile | cut -d: -f1)
sed -i "$((LINE+1)),$((LINE+3)){ s/RUN apk update && apk add --no-cache git/RUN apt-get update \&\& apt-get install -y git/; }" Dockerfile
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache-fips
key: ${{ runner.os }}-${{ matrix.platform }}-${{ matrix.target }}-${{ matrix.version }}-buildx-fips-${{ github.sha }}
restore-keys: |
${{ runner.os }}-${{ matrix.platform }}-${{ matrix.target }}-${{ matrix.version }}-buildx-fips-
${{ runner.os }}-${{ matrix.platform }}-${{ matrix.target }}-buildx-fips-
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_PASSWORD }}
- name: Check if image already exists
id: check-image
run: |
cd argo-workflows
tag="${{ matrix.version }}-fips"
tag_suffix=$(echo "${{ matrix.platform }}" | sed -r "s/\//-/g")
# Special handling for argoexec-nonroot to create argoexec:tag-nonroot-platform instead
if [ "${{ matrix.target }}" = "argoexec-nonroot" ]; then
image_name="${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-${tag_suffix}"
else
image_name="${{ env.IMAGE_NAMESPACE }}/${{ matrix.target }}:${tag}-${tag_suffix}"
fi
if docker manifest inspect "$image_name" >/dev/null 2>&1; then
echo "exists=true" >> $GITHUB_OUTPUT
echo "Image $image_name already exists"
else
echo "exists=false" >> $GITHUB_OUTPUT
echo "Image $image_name does not exist"
fi
echo "image_name=$image_name" >> $GITHUB_OUTPUT
- name: Build and push Docker image (FIPS)
if: steps.check-image.outputs.exists != 'true' || github.event.inputs.force_build == 'true'
run: |
cd argo-workflows
set -eux
tag="${{ matrix.version }}-fips"
# Get git info (copied verbatim from upstream)
GIT_COMMIT=$(git rev-parse HEAD || echo unknown)
GIT_TAG=$(git describe --exact-match --tags --abbrev=0 2> /dev/null || echo untagged)
GIT_TREE_STATE=$(if [ -z "`git status --porcelain`" ]; then echo "clean" ; else echo "dirty"; fi)
tag_suffix=$(echo "${{ matrix.platform }}" | sed -r "s/\//-/g")
# Special handling for argoexec-nonroot to create argoexec:tag-nonroot-platform instead
if [ "${{ matrix.target }}" = "argoexec-nonroot" ]; then
image_name="${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-${tag_suffix}"
else
image_name="${{ env.IMAGE_NAMESPACE }}/${{ matrix.target }}:${tag}-${tag_suffix}"
fi
docker buildx build \
--cache-from "type=local,src=/tmp/.buildx-cache-fips" \
--cache-to "type=local,dest=/tmp/.buildx-cache-fips-new,mode=max" \
--output "type=image,push=true" \
--build-arg GOLANG_IMAGE=${{ steps.go-version.outputs.golang_image }} \
--build-arg GIT_COMMIT=$GIT_COMMIT \
--build-arg GIT_TAG=$GIT_TAG \
--build-arg GIT_TREE_STATE=$GIT_TREE_STATE \
--platform="${{ matrix.platform }}" \
--target ${{ matrix.target }} \
--provenance=false \
--tag $image_name .
- name: Move cache
if: steps.check-image.outputs.exists != 'true' || github.event.inputs.force_build == 'true'
run: |
rm -rf /tmp/.buildx-cache-fips
mv /tmp/.buildx-cache-fips-new /tmp/.buildx-cache-fips
push-multiarch-manifests:
name: Push multi-arch manifests (regular builds)
needs: [ check-releases, build-multiarch ]
runs-on: ubuntu-latest
strategy:
matrix:
version: ${{ fromJson(needs.check-releases.outputs.versions) }}
permissions:
contents: read
packages: write
steps:
- name: Set registry variables
run: |
if [ "${{ github.event.inputs.registry }}" != "" ]; then
echo "REGISTRY=${{ github.event.inputs.registry }}" >> $GITHUB_ENV
else
echo "REGISTRY=ghcr.io" >> $GITHUB_ENV
fi
# Set image namespace and login credentials based on registry
if [ "${{ env.REGISTRY || 'ghcr.io' }}" = "ghcr.io" ]; then
echo "IMAGE_NAMESPACE=ghcr.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.GHCR_USERNAME || github.actor }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
elif [ "${{ env.REGISTRY || 'ghcr.io' }}" = "quay.io" ]; then
echo "IMAGE_NAMESPACE=quay.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.QUAYIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.QUAYIO_PASSWORD }}" >> $GITHUB_ENV
else
echo "IMAGE_NAMESPACE=${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.DOCKERIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.DOCKERIO_PASSWORD }}" >> $GITHUB_ENV
fi
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_PASSWORD }}
- name: Push Multi-arch Manifests (PowerPC/s390x only)
run: |
# Enable experimental CLI features for manifest command
export DOCKER_CLI_EXPERIMENTAL=enabled
tag="${{ matrix.version }}"
targets="workflow-controller argoexec argoexec-nonroot argocli"
for target in $targets; do
if [ "$target" = "argoexec-nonroot" ]; then
# Special handling for argoexec-nonroot: create argoexec:tag-nonroot manifest
image_name="${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot"
docker manifest create $image_name \
${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-linux-ppc64le \
${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-linux-s390x
else
image_name="${{ env.IMAGE_NAMESPACE }}/${target}:${tag}"
if [ "$target" = "argocli" ]; then
docker manifest create $image_name \
${image_name}-linux-ppc64le \
${image_name}-linux-s390x
else
docker manifest create $image_name \
${image_name}-linux-ppc64le \
${image_name}-linux-s390x
fi
fi
docker manifest push $image_name
echo "✅ Pushed manifest: $image_name"
done
- name: Tag latest images (PowerPC/s390x only)
if: matrix.version == needs.check-releases.outputs.latest-version
run: |
export DOCKER_CLI_EXPERIMENTAL=enabled
tag="${{ matrix.version }}"
targets="workflow-controller argoexec argoexec-nonroot argocli"
for target in $targets; do
if [ "$target" = "argoexec-nonroot" ]; then
# Special handling for argoexec-nonroot
image_name="${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot"
latest_name="${{ env.IMAGE_NAMESPACE }}/argoexec:latest-nonroot"
docker manifest create $latest_name \
${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-linux-ppc64le \
${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-linux-s390x
else
image_name="${{ env.IMAGE_NAMESPACE }}/${target}:${tag}"
latest_name="${{ env.IMAGE_NAMESPACE }}/${target}:latest"
if [ "$target" = "argocli" ]; then
docker manifest create $latest_name \
${image_name}-linux-ppc64le \
${image_name}-linux-s390x
else
docker manifest create $latest_name \
${image_name}-linux-ppc64le \
${image_name}-linux-s390x
fi
fi
docker manifest push $latest_name
echo "✅ Pushed latest manifest: $latest_name"
done
push-fips-manifests:
name: Push FIPS manifests
needs: [ check-releases, build-fips ]
runs-on: ubuntu-latest
strategy:
matrix:
version: ${{ fromJson(needs.check-releases.outputs.versions) }}
permissions:
contents: read
packages: write
steps:
- name: Set registry variables
run: |
if [ "${{ github.event.inputs.registry }}" != "" ]; then
echo "REGISTRY=${{ github.event.inputs.registry }}" >> $GITHUB_ENV
else
echo "REGISTRY=ghcr.io" >> $GITHUB_ENV
fi
# Set image namespace and login credentials based on registry
if [ "${{ env.REGISTRY || 'ghcr.io' }}" = "ghcr.io" ]; then
echo "IMAGE_NAMESPACE=ghcr.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.GHCR_USERNAME || github.actor }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
elif [ "${{ env.REGISTRY || 'ghcr.io' }}" = "quay.io" ]; then
echo "IMAGE_NAMESPACE=quay.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.QUAYIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.QUAYIO_PASSWORD }}" >> $GITHUB_ENV
else
echo "IMAGE_NAMESPACE=${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.DOCKERIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.DOCKERIO_PASSWORD }}" >> $GITHUB_ENV
fi
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_PASSWORD }}
- name: Push FIPS Multi-arch Manifests
run: |
# Enable experimental CLI features for manifest command
export DOCKER_CLI_EXPERIMENTAL=enabled
tag="${{ matrix.version }}-fips"
targets="workflow-controller argoexec argoexec-nonroot argocli"
for target in $targets; do
if [ "$target" = "argoexec-nonroot" ]; then
# Special handling for argoexec-nonroot: create argoexec:tag-nonroot manifest
image_name="${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot"
docker manifest create $image_name \
${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-linux-arm64 \
${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-linux-amd64
else
image_name="${{ env.IMAGE_NAMESPACE }}/${target}:${tag}"
if [ "$target" = "argocli" ]; then
docker manifest create $image_name \
${image_name}-linux-arm64 \
${image_name}-linux-amd64
else
docker manifest create $image_name \
${image_name}-linux-arm64 \
${image_name}-linux-amd64
fi
fi
docker manifest push $image_name
echo "✅ Pushed FIPS manifest: $image_name"
done
- name: Tag latest FIPS images
if: matrix.version == needs.check-releases.outputs.latest-version
run: |
export DOCKER_CLI_EXPERIMENTAL=enabled
tag="${{ matrix.version }}-fips"
targets="workflow-controller argoexec argoexec-nonroot argocli"
for target in $targets; do
if [ "$target" = "argoexec-nonroot" ]; then
# Special handling for argoexec-nonroot
image_name="${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot"
latest_name="${{ env.IMAGE_NAMESPACE }}/argoexec:latest-fips-nonroot"
docker manifest create $latest_name \
${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-linux-arm64 \
${{ env.IMAGE_NAMESPACE }}/argoexec:${tag}-nonroot-linux-amd64
else
image_name="${{ env.IMAGE_NAMESPACE }}/${target}:${tag}"
latest_name="${{ env.IMAGE_NAMESPACE }}/${target}:latest-fips"
if [ "$target" = "argocli" ]; then
docker manifest create $latest_name \
${image_name}-linux-arm64 \
${image_name}-linux-amd64
else
docker manifest create $latest_name \
${image_name}-linux-arm64 \
${image_name}-linux-amd64
fi
fi
docker manifest push $latest_name
echo "✅ Pushed latest FIPS manifest: $latest_name"
done
test-docker-images:
name: Test Docker Images for FIPS
needs: [check-releases, push-fips-manifests]
runs-on: ubuntu-latest
strategy:
matrix:
version: ${{ fromJson(needs.check-releases.outputs.versions) }}
target: [workflow-controller, argocli, argoexec]
permissions:
contents: read
packages: read
steps:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24.4'
- name: Set registry variables
run: |
if [ "${{ github.event.inputs.registry }}" != "" ]; then
echo "REGISTRY=${{ github.event.inputs.registry }}" >> $GITHUB_ENV
else
echo "REGISTRY=ghcr.io" >> $GITHUB_ENV
fi
# Set image namespace and login credentials based on registry
if [ "${{ env.REGISTRY || 'ghcr.io' }}" = "ghcr.io" ]; then
echo "IMAGE_NAMESPACE=ghcr.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.GHCR_USERNAME || github.actor }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
elif [ "${{ env.REGISTRY || 'ghcr.io' }}" = "quay.io" ]; then
echo "IMAGE_NAMESPACE=quay.io/${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.QUAYIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.QUAYIO_PASSWORD }}" >> $GITHUB_ENV
else
echo "IMAGE_NAMESPACE=${{ github.repository }}" >> $GITHUB_ENV
echo "DOCKER_USERNAME=${{ secrets.DOCKERIO_USERNAME }}" >> $GITHUB_ENV
echo "DOCKER_PASSWORD=${{ secrets.DOCKERIO_PASSWORD }}" >> $GITHUB_ENV
fi
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_PASSWORD }}
- name: Test FIPS in Docker images
run: |
tag="${{ matrix.version }}-fips"
image_name="${{ env.IMAGE_NAMESPACE }}/${{ matrix.target }}:${tag}"
echo "🐳 Testing FIPS compliance in Docker image: $image_name"
# Pull the image
echo "Pulling image..."
docker pull "$image_name"
# Determine the binary path in the container based on target
case "${{ matrix.target }}" in
"workflow-controller")
binary_path="/bin/workflow-controller"
;;
"argocli")
binary_path="/bin/argo"
;;
"argoexec")
binary_path="/bin/argoexec"
;;
*)
echo "❌ Unknown target: ${{ matrix.target }}"
exit 1
;;
esac
echo "Testing binary: $binary_path"
# Extract binary from distroless container for testing
echo "🔍 Extracting binary from distroless container..."
container_id=$(docker create "$image_name")
docker cp "$container_id:$binary_path" "./binary-${{ matrix.target }}"
docker rm "$container_id"
# Test using strings command on host
echo "🔍 Testing FIPS strings in extracted binary..."
if strings "./binary-${{ matrix.target }}" | grep -iE '(boringssl|boringcrypto|fips)' | head -10; then
echo '✅ BoringSSL/FIPS strings found in ${{ matrix.target }}'
fips_test_result="✅ FIPS validation passed"
else
echo '⚠️ No BoringSSL/FIPS strings found in ${{ matrix.target }}'
fips_test_result="❌ FIPS validation failed"
fi
# Test with go tool nm if available on host
if command -v go >/dev/null 2>&1; then
echo "🔍 Testing FIPS symbols using go tool nm..."
if go tool nm "./binary-${{ matrix.target }}" | grep -iE '(boringcrypto|fips|boring)' | head -10; then
echo '✅ BoringCrypto/FIPS symbols found via go tool nm'
else
echo '⚠️ No BoringCrypto/FIPS symbols found via go tool nm'
fi
fi
# Clean up extracted binary
rm -f "./binary-${{ matrix.target }}"
echo "$fips_test_result for ${{ matrix.target }}"
# Test version command using subcommands
echo "🧪 Testing version command..."
case "${{ matrix.target }}" in
"workflow-controller")
docker run --rm "$image_name" version || echo "Version command test completed"
;;
"argocli")
docker run --rm "$image_name" version || echo "Version command test completed"
;;
"argoexec")
docker run --rm "$image_name" version || echo "Version command test completed"
;;
esac