Docker publish workflow (joeferner/redis-commander)
The Docker publish workflow from joeferner/redis-commander, explained and optimized by Latchkey.
CI health: C - fair
Point runs-on at Latchkey and get run de-duplication, job timeouts, SHA-pinned actions, self-healing for flaky steps, and up to 58% lower cost, applied automatically.
What it does
This is the Docker publish workflow from the joeferner/redis-commander repository, a real project running GitHub Actions. It is shown here with attribution under its MIT license.
Below, Latchkey shows a faster, safer version produced by its optimization engine.
The workflow
---
name: Docker publish
on:
push:
# Publish `master` as Docker `latest` image, version tags as respective version too
branches:
- master
- main
tags:
- v[0-9]*
paths-ignore:
- '.github/**'
- '.s2i/**'
- 'dist/**'
- 'docs/**'
- 'k8s/**'
- 'test/'
- '.*'
- 'docker-compose.yml'
- 'ecosystem.config.js'
- 'CHANGELOG.md'
- 'README.md'
- 'SECURITY.md'
env:
GITHUB_REG: ghcr.io
GITHUB_REPO: ${{ github.repository }}
DOCKERHUB_IMAGE: rediscommander/redis-commander
defaults:
run:
shell: bash
permissions:
actions: read
checks: read
contents: read
deployments: read
packages: write
pull-requests: read
repository-projects: read
security-events: read
statuses: read
jobs:
build_publish:
name: Build image and push
if: github.repository == 'joeferner/redis-commander'
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up QEMU for Buildx
id: qemu
uses: docker/setup-qemu-action@v3
with:
platforms: all
- name: Install Buildx
id: buildx
uses: docker/setup-buildx-action@v3
with:
version: latest
install: true
# log into github container registry as named registry
# and dockerhub without explicit name
# =============
#- name: Log in to Dockerhub
# uses: docker/login-action@v3
# with:
# username: "${{secrets.DOCKER_USERNAME}}"
# password: "${{secrets.DOCKER_PASSWORD}}"
- name: Log in to Github Container registry
uses: docker/login-action@v3
with:
registry: "${{ env.GITHUB_REG }}"
username: "${{ github.actor }}"
password: "${{ secrets.GITHUB_TOKEN }}"
- name: Prepare Tags
id: prep
env:
GITHUB_IMAGE: ${{ env.GITHUB_REG }}/${{ env.GITHUB_REPO }}
run: |
VERSION=latest
# first ghcr.io, second dockerhub
TAGS="${GITHUB_IMAGE}:${VERSION}"
#TAGS="$TAGS,${DOCKERHUB_IMAGE}:${VERSION}"
# If this is git tag, use the tag name as a docker tag too
if [[ $GITHUB_REF == refs/tags/v* ]]; then
VERSION=${GITHUB_REF#refs/tags/v}
VERSION_MINOR=${VERSION%.*}
VERSION_MAJOR=${VERSION%%.*}
# also tag with version numbers additionally to "latest"
TAGS="$TAGS,${GITHUB_IMAGE}:${VERSION},${GITHUB_IMAGE}:${VERSION_MINOR},${GITHUB_IMAGE}:${VERSION_MAJOR}"
#TAGS="$TAGS,${DOCKERHUB_IMAGE}:${VERSION},${DOCKERHUB_IMAGE}:${VERSION_MINOR},${DOCKERHUB_IMAGE}:${VERSION_MAJOR}"
fi
# Set output parameters.
echo "tags=${TAGS}" >> $GITHUB_OUTPUT
echo "docker_image=${DOCKERHUB_IMAGE}" >> $GITHUB_OUTPUT
- name: Show docker image tags to build
run: |
echo "Docker image: ${{ steps.prep.outputs.docker_image }}"
echo "Image tags: ${{ steps.prep.outputs.tags }}"
- name: Docker build and push to GHCR and Dockerhub for prepared tags
uses: docker/build-push-action@v6
with:
builder: ${{ steps.buildx.outputs.name }}
context: .
platforms: linux/amd64,linux/arm/v7,linux/arm64
push: true
tags: ${{ steps.prep.outputs.tags }}
# platform linux/riscv64 starts with alpine:edge, not available on 3.15 by now
# see "docker manifest inspect alpine:3.15 | alpine:edge
The same workflow, on Latchkey
Removes redundant runs and caps runaway jobs. Added and changed lines are highlighted.
--- name: Docker publish on: push: # Publish `master` as Docker `latest` image, version tags as respective version too branches: - master - main tags: - v[0-9]* paths-ignore: - '.github/**' - '.s2i/**' - 'dist/**' - 'docs/**' - 'k8s/**' - 'test/' - '.*' - 'docker-compose.yml' - 'ecosystem.config.js' - 'CHANGELOG.md' - 'README.md' - 'SECURITY.md' env: GITHUB_REG: ghcr.io GITHUB_REPO: ${{ github.repository }} DOCKERHUB_IMAGE: rediscommander/redis-commander defaults: run: shell: bash permissions: actions: read checks: read contents: read deployments: read packages: write pull-requests: read repository-projects: read security-events: read statuses: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build_publish: timeout-minutes: 30 name: Build image and push if: github.repository == 'joeferner/redis-commander' runs-on: latchkey-small steps: - name: Checkout uses: actions/checkout@v4 - name: Set up QEMU for Buildx id: qemu uses: docker/setup-qemu-action@v3 with: platforms: all - name: Install Buildx id: buildx uses: docker/setup-buildx-action@v3 with: version: latest install: true # log into github container registry as named registry # and dockerhub without explicit name # ============= #- name: Log in to Dockerhub # uses: docker/login-action@v3 # with: # username: "${{secrets.DOCKER_USERNAME}}" # password: "${{secrets.DOCKER_PASSWORD}}" - name: Log in to Github Container registry uses: docker/login-action@v3 with: registry: "${{ env.GITHUB_REG }}" username: "${{ github.actor }}" password: "${{ secrets.GITHUB_TOKEN }}" - name: Prepare Tags id: prep env: GITHUB_IMAGE: ${{ env.GITHUB_REG }}/${{ env.GITHUB_REPO }} run: | VERSION=latest # first ghcr.io, second dockerhub TAGS="${GITHUB_IMAGE}:${VERSION}" #TAGS="$TAGS,${DOCKERHUB_IMAGE}:${VERSION}" # If this is git tag, use the tag name as a docker tag too if [[ $GITHUB_REF == refs/tags/v* ]]; then VERSION=${GITHUB_REF#refs/tags/v} VERSION_MINOR=${VERSION%.*} VERSION_MAJOR=${VERSION%%.*} # also tag with version numbers additionally to "latest" TAGS="$TAGS,${GITHUB_IMAGE}:${VERSION},${GITHUB_IMAGE}:${VERSION_MINOR},${GITHUB_IMAGE}:${VERSION_MAJOR}" #TAGS="$TAGS,${DOCKERHUB_IMAGE}:${VERSION},${DOCKERHUB_IMAGE}:${VERSION_MINOR},${DOCKERHUB_IMAGE}:${VERSION_MAJOR}" fi # Set output parameters. echo "tags=${TAGS}" >> $GITHUB_OUTPUT echo "docker_image=${DOCKERHUB_IMAGE}" >> $GITHUB_OUTPUT - name: Show docker image tags to build run: | echo "Docker image: ${{ steps.prep.outputs.docker_image }}" echo "Image tags: ${{ steps.prep.outputs.tags }}" - name: Docker build and push to GHCR and Dockerhub for prepared tags uses: docker/build-push-action@v6 with: builder: ${{ steps.buildx.outputs.name }} context: . platforms: linux/amd64,linux/arm/v7,linux/arm64 push: true tags: ${{ steps.prep.outputs.tags }} # platform linux/riscv64 starts with alpine:edge, not available on 3.15 by now # see "docker manifest inspect alpine:3.15 | alpine:edge
What changed
- Run on Latchkey managed runners with one line (
runs-on), which apply the fixes below automatically and self-heal transient failures. This example useslatchkey-small; pick the runner size that fits the job. - Cancel superseded runs when a branch or PR gets a newer push.
- Add a job timeout so a hung step cannot burn hours of runner time.
4 third-party actions are referenced by a movable tag. Pin them to the commit SHA (Latchkey resolves and applies this automatically) so a repointed tag cannot change what runs.
What Latchkey heals here
This workflow has steps that commonly fail on transient issues (network, registries, flaky browsers). On Latchkey managed runners they are detected, retried, and self-healed instead of failing your build:
- Container pulls and builds
This workflow runs 1 job per trigger. On Latchkey the same minutes cost up to 58% less than GitHub-hosted, with zero queue time.