Files
actions/docker-publish/action.yml
Ismo Vuorinen 6ebc5a21d5 fix: local references, release workflow (#301)
* fix: local references, release workflow

* chore: apply cr comments
2025-10-23 23:24:20 +03:00

322 lines
10 KiB
YAML

# yaml-language-server: $schema=https://json.schemastore.org/github-action.json
# permissions:
# - packages: write # Required for publishing to Docker registries
# - contents: read # Required for checking out repository
---
name: Docker Publish
description: Publish a Docker image to GitHub Packages and Docker Hub.
author: Ismo Vuorinen
branding:
icon: upload-cloud
color: blue
inputs:
registry:
description: 'Registry to publish to (dockerhub, github, or both).'
required: true
default: 'both'
nightly:
description: 'Is this a nightly build? (true or false)'
required: false
default: 'false'
platforms:
description: 'Platforms to build for (comma-separated)'
required: false
default: 'linux/amd64,linux/arm64,linux/arm/v7'
auto-detect-platforms:
description: 'Automatically detect and build for all available platforms'
required: false
default: 'false'
scan-image:
description: 'Scan images for vulnerabilities'
required: false
default: 'true'
sign-image:
description: 'Sign images with cosign'
required: false
default: 'false'
cache-mode:
description: 'Cache mode for build layers (min, max, or inline)'
required: false
default: 'max'
buildx-version:
description: 'Specific Docker Buildx version to use'
required: false
default: 'latest'
verbose:
description: 'Enable verbose logging'
required: false
default: 'false'
dockerhub-username:
description: 'Docker Hub username for authentication'
required: false
dockerhub-password:
description: 'Docker Hub password or access token for authentication'
required: false
token:
description: 'GitHub token for authentication'
required: false
default: ''
outputs:
registry:
description: 'Registry where image was published'
value: ${{ steps.dest.outputs.reg }}
tags:
description: 'Tags that were published'
value: ${{ steps.tags.outputs.all-tags }}
build-time:
description: 'Total build time in seconds'
value: ${{ steps.build.outputs.build-time }}
platform-matrix:
description: 'Build status per platform'
value: ${{ steps.build.outputs.platform-matrix }}
scan-results:
description: 'Vulnerability scan results if scanning enabled'
value: ${{ steps.build.outputs.scan-results }}
image-id:
description: 'Published image ID'
value: ${{ steps.publish-dockerhub.outputs.image-id || steps.publish-github.outputs.image-id }}
image-digest:
description: 'Published image digest'
value: ${{ steps.publish-dockerhub.outputs.digest || steps.publish-github.outputs.digest }}
repository:
description: 'Repository where image was published'
value: ${{ steps.publish-dockerhub.outputs.repository || steps.publish-github.outputs.repository }}
runs:
using: composite
steps:
- name: Mask Sensitive Inputs
shell: bash
env:
DOCKERHUB_PASSWORD: ${{ inputs.dockerhub-password }}
run: |
set -euo pipefail
# Mask Docker Hub credentials to prevent exposure in logs
if [[ -n "${DOCKERHUB_PASSWORD}" ]]; then
echo "::add-mask::${DOCKERHUB_PASSWORD}"
fi
- name: Validate Inputs
id: validate
shell: bash
env:
REGISTRY: ${{ inputs.registry }}
run: |
set -euo pipefail
# Validate registry input
if ! [[ "$REGISTRY" =~ ^(dockerhub|github|both)$ ]]; then
echo "::error::Invalid registry value. Must be 'dockerhub', 'github', or 'both'"
exit 1
fi
- name: Determine Tags
id: tags
shell: bash
env:
NIGHTLY: ${{ inputs.nightly }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
run: |
set -euo pipefail
# Initialize variables
declare -a tag_array
if [[ "$NIGHTLY" == "true" ]]; then
# Nightly build tags
current_date=$(date +'%Y%m%d-%H%M')
tag_array+=("nightly")
tag_array+=("nightly-${current_date}")
else
# Release tags
if [[ -n "$RELEASE_TAG" ]]; then
tag_array+=("$RELEASE_TAG")
tag_array+=("latest")
else
echo "::error::No release tag found and not a nightly build"
exit 1
fi
fi
# Join tags with comma
tags=$(IFS=,; echo "${tag_array[*]}")
echo "all-tags=${tags}" >> "$GITHUB_OUTPUT"
echo "Generated tags: ${tags}"
- name: Determine Publish Destination
id: dest
shell: bash
env:
REGISTRY: ${{ inputs.registry }}
run: |
set -euo pipefail
if [[ "$REGISTRY" == "both" ]]; then
echo "reg=github,dockerhub" >> "$GITHUB_OUTPUT"
else
echo "reg=$REGISTRY" >> "$GITHUB_OUTPUT"
fi
echo "Publishing to: $REGISTRY"
- name: Checkout Repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
token: ${{ inputs.token || github.token }}
- name: Build Multi-Arch Docker Image
id: build
uses: ivuorinen/actions/docker-build@7061aafd35a2f21b57653e34f2b634b2a19334a9
with:
tag: ${{ steps.tags.outputs.all-tags }}
architectures: ${{ inputs.platforms }}
auto-detect-platforms: ${{ inputs.auto-detect-platforms }}
scan-image: ${{ inputs.scan-image }}
sign-image: ${{ inputs.sign-image }}
cache-mode: ${{ inputs.cache-mode }}
buildx-version: ${{ inputs.buildx-version }}
verbose: ${{ inputs.verbose }}
push: 'false' # Don't push during build, let publish actions handle it
- name: Publish to Docker Hub
id: publish-dockerhub
if: contains(steps.dest.outputs.reg, 'dockerhub')
uses: ivuorinen/actions/docker-publish-hub@7061aafd35a2f21b57653e34f2b634b2a19334a9
with:
tags: ${{ steps.tags.outputs.all-tags }}
platforms: ${{ inputs.platforms }}
auto-detect-platforms: ${{ inputs.auto-detect-platforms }}
scan-image: ${{ inputs.scan-image }}
sign-image: ${{ inputs.sign-image }}
cache-mode: ${{ inputs.cache-mode }}
buildx-version: ${{ inputs.buildx-version }}
verbose: ${{ inputs.verbose }}
username: ${{ inputs.dockerhub-username }}
password: ${{ inputs.dockerhub-password }}
- name: Publish to GitHub Packages
id: publish-github
if: contains(steps.dest.outputs.reg, 'github')
uses: ivuorinen/actions/docker-publish-gh@7061aafd35a2f21b57653e34f2b634b2a19334a9
with:
tags: ${{ steps.tags.outputs.all-tags }}
platforms: ${{ inputs.platforms }}
auto-detect-platforms: ${{ inputs.auto-detect-platforms }}
scan-image: ${{ inputs.scan-image }}
sign-image: ${{ inputs.sign-image }}
cache-mode: ${{ inputs.cache-mode }}
buildx-version: ${{ inputs.buildx-version }}
verbose: ${{ inputs.verbose }}
- name: Verify Publications
id: verify
shell: bash
env:
DEST_REG: ${{ steps.dest.outputs.reg }}
DOCKERHUB_IMAGE_NAME: ${{ steps.publish-dockerhub.outputs.image-name }}
DOCKERHUB_TAGS: ${{ steps.publish-dockerhub.outputs.tags }}
GITHUB_IMAGE_NAME: ${{ steps.publish-github.outputs.image-name }}
GITHUB_TAGS: ${{ steps.publish-github.outputs.tags }}
ALL_TAGS: ${{ steps.tags.outputs.all-tags }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: |
set -euo pipefail
echo "Verifying publications..."
success=true
# Split registry string into array
IFS=',' read -ra REGISTRIES <<< "$DEST_REG"
for registry in "${REGISTRIES[@]}"; do
echo "Checking ${registry} publication..."
case "${registry}" in
"dockerhub")
# Get actual image name from publish step output or fallback to repo-based name
image_name="$DOCKERHUB_IMAGE_NAME"
if [[ -z "$image_name" ]]; then
image_name="docker.io/$GITHUB_REPOSITORY"
fi
# Get tags from publish step or fallback to metadata
tags="$DOCKERHUB_TAGS"
if [[ -z "$tags" ]]; then
tags="$ALL_TAGS"
fi
IFS=',' read -ra TAGS <<< "$tags"
for tag in "${TAGS[@]}"; do
tag=$(echo "$tag" | xargs) # trim whitespace
if ! docker manifest inspect "${image_name}:${tag}" > /dev/null 2>&1; then
echo "::error::Failed to verify Docker Hub publication for ${tag}"
success=false
break
fi
done
if [[ "${success}" != "true" ]]; then
break
fi
;;
"github")
# Get actual image name from publish step output or fallback to repo-based name
image_name="$GITHUB_IMAGE_NAME"
if [[ -z "$image_name" ]]; then
image_name="ghcr.io/$GITHUB_REPOSITORY"
fi
# Get tags from publish step or fallback to metadata
tags="$GITHUB_TAGS"
if [[ -z "$tags" ]]; then
tags="$ALL_TAGS"
fi
IFS=',' read -ra TAGS <<< "$tags"
for tag in "${TAGS[@]}"; do
tag=$(echo "$tag" | xargs) # trim whitespace
if ! docker manifest inspect "${image_name}:${tag}" > /dev/null 2>&1; then
echo "::error::Failed to verify GitHub Packages publication for ${tag}"
success=false
break
fi
done
if [[ "${success}" != "true" ]]; then
break
fi
;;
esac
done
if [[ "${success}" != "true" ]]; then
echo "::error::Publication verification failed"
exit 1
fi
echo "All publications verified successfully"
- name: Cleanup
if: always()
shell: bash
env:
DEST_REG: ${{ steps.dest.outputs.reg }}
run: |-
set -euo pipefail
echo "Cleaning up..."
# Remove any temporary files or caches
docker buildx prune -f --keep-storage=10GB
# Remove any temporary authentication
if [[ "$DEST_REG" =~ "dockerhub" ]]; then
docker logout docker.io || true
fi
if [[ "$DEST_REG" =~ "github" ]]; then
docker logout ghcr.io || true
fi
echo "Cleanup completed"