🔄 Migration Required: Update Docker Publish Workflow Reference
This repository is currently using the legacy Docker publish workflow, which has been replaced with a new standardized workflow.
📦 What changed
A new workflow is now used for publishing Docker images:
The previous workflow is still functional for now but is considered legacy and will be removed once all dependent repositories have been migrated.
⚠️ Required change
Please update all references in this repository from the old workflow to the new workflow.
🧪 Backward compatibility
- The old workflow remains temporarily supported
- No immediate breaking changes
- Migration can be performed safely at any time
🚀 Action required
🗓️ Deprecation note
The old workflow will be removed once all repositories have been migrated.
Migration progress is tracked here:
frappe/frappe_docker#1884
Example for new Workflow
# This workflow uses frappe_docker only as shared build logic.
# The app repo itself owns publishing.
#
# Replace these placeholders before using it:
# - <app_name>
# - <ghcr_image_name>
# - <dockerhub_image>
#
# This reference is best suited for single-app repositories.
name: Build and Publish Docker Images
on:
release:
types: [released]
workflow_dispatch:
inputs:
app_ref:
description: Git tag or branch to build
required: false
type: string
image_tag:
description: Override the published image tag
required: false
type: string
frappe_ref:
description: Override the base Frappe image tag, for example <frappe_ref_example>
required: false
type: string
push:
description: Push the built images to registries
required: true
default: true
type: boolean
permissions:
contents: read
packages: write
concurrency:
group: docker-image-${{ github.event.release.tag_name || github.event.inputs.app_ref || github.ref_name }}
cancel-in-progress: false
jobs:
prepare:
name: Resolve Build Metadata
runs-on: ubuntu-latest
outputs:
app_ref: ${{ steps.meta.outputs.app_ref }}
app_repo: ${{ steps.meta.outputs.app_repo }}
frappe_ref: ${{ steps.meta.outputs.frappe_ref }}
ghcr_image: ${{ steps.meta.outputs.ghcr_image }}
dockerhub_image: ${{ steps.meta.outputs.dockerhub_image }}
image_tags: ${{ steps.meta.outputs.image_tags }}
push: ${{ steps.meta.outputs.push }}
steps:
- name: Resolve metadata
id: meta
shell: bash
env:
EVENT_NAME: ${{ github.event_name }}
RELEASE_TAG: ${{ github.event.release.tag_name }}
INPUT_APP_REF: ${{ github.event.inputs.app_ref }}
INPUT_IMAGE_TAG: ${{ github.event.inputs.image_tag }}
INPUT_FRAPPE_REF: ${{ github.event.inputs.frappe_ref }}
INPUT_PUSH: ${{ github.event.inputs.push }}
REPOSITORY: ${{ github.repository }}
REPOSITORY_OWNER: ${{ github.repository_owner }}
# This comes from the repository variable "DOCKERHUB_IMAGE".
# If it is not set, Docker Hub publishing is skipped.
DOCKERHUB_IMAGE: ${{ vars.DOCKERHUB_IMAGE }}
run: |
set -euo pipefail
# Resolve the app repo ref to build.
# Priority:
# 1. manual workflow input
# 2. release tag
# 3. current ref name
app_ref="${INPUT_APP_REF:-}"
if [ -z "$app_ref" ]; then
if [ "$EVENT_NAME" = "release" ] && [ -n "${RELEASE_TAG:-}" ]; then
app_ref="$RELEASE_TAG"
else
app_ref="${GITHUB_REF_NAME}"
fi
fi
# Release events always push.
# Manual runs may choose push=false.
push="${INPUT_PUSH:-true}"
if [ "$EVENT_NAME" = "release" ]; then
push="true"
fi
# Resolve the Frappe base image line.
#
# This logic assumes release tags can be mapped to the correct
# Frappe base image line.
#
# Example:
# <release_tag_example> -> <frappe_ref_example>
#
# If that assumption is not valid for a given app repo,
# this logic must be adjusted.
frappe_ref="${INPUT_FRAPPE_REF:-}"
major=""
if [ -z "$frappe_ref" ]; then
if [[ "$app_ref" =~ ^v([0-9]+)([.].*)?$ ]]; then
major="${BASH_REMATCH[1]}"
frappe_ref="version-${major}"
elif [[ "$app_ref" =~ ^version-([0-9]+)$ ]]; then
major="${BASH_REMATCH[1]}"
frappe_ref="$app_ref"
elif [ "$app_ref" = "develop" ]; then
frappe_ref="develop"
else
echo "::error::Unable to derive frappe_ref from app_ref '$app_ref'. Provide the workflow_dispatch input 'frappe_ref' or adjust the derivation logic."
exit 1
fi
elif [[ "$frappe_ref" =~ ^version-([0-9]+)$ ]]; then
major="${BASH_REMATCH[1]}"
fi
# Resolve published image tags.
#
# Example:
# v16.15.0 -> v16.15.0, v16, version-16
# develop -> develop, latest
#
# Adjust this logic if the app repository uses a different tagging scheme.
declare -a tags
if [ -n "${INPUT_IMAGE_TAG:-}" ]; then
tags=("$INPUT_IMAGE_TAG")
elif [[ "$app_ref" =~ ^v([0-9]+)([.].*)?$ ]]; then
major="${BASH_REMATCH[1]}"
tags=("$app_ref" "v${major}" "version-${major}")
elif [ "$app_ref" = "develop" ]; then
tags=("develop" "latest")
else
tags=("$app_ref")
fi
# Deduplicate tags and serialize them to JSON so the publish jobs
# can consume them via a matrix.
declare -A seen
image_tags="["
for tag in "${tags[@]}"; do
if [ -z "$tag" ] || [ -n "${seen[$tag]+x}" ]; then
continue
fi
seen[$tag]=1
image_tags="${image_tags}\"${tag}\","
done
image_tags="${image_tags%,}]"
if [ "$image_tags" = "]" ]; then
echo "::error::No image tags were resolved."
exit 1
fi
# Build the GHCR image name from the current repository owner.
# Example:
# github.repository_owner=frappe -> ghcr.io/frappe/<ghcr_image_name>
owner_lower="$(echo "$REPOSITORY_OWNER" | tr '[:upper:]' '[:lower:]')"
# Docker Hub image comes from the repository variable DOCKERHUB_IMAGE.
dockerhub_image="$(echo "${DOCKERHUB_IMAGE:-}" | tr '[:upper:]' '[:lower:]')"
{
echo "app_ref=$app_ref"
echo "app_repo=https://github.com/$REPOSITORY"
echo "frappe_ref=$frappe_ref"
echo "ghcr_image=ghcr.io/$owner_lower/<ghcr_image_name>"
echo "dockerhub_image=$dockerhub_image"
echo "image_tags=$image_tags"
echo "push=$push"
} >> "$GITHUB_OUTPUT"
validate-dockerhub:
name: Validate Docker Hub Configuration
needs: prepare
if: ${{ needs.prepare.outputs.push == 'true' && needs.prepare.outputs.dockerhub_image != '' }}
runs-on: ubuntu-latest
steps:
- name: Check required secrets
shell: bash
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
run: |
set -euo pipefail
test -n "${DOCKERHUB_USERNAME:-}" || { echo "::error::Missing secret DOCKERHUB_USERNAME."; exit 1; }
test -n "${DOCKERHUB_TOKEN:-}" || { echo "::error::Missing secret DOCKERHUB_TOKEN."; exit 1; }
publish-ghcr:
name: Publish GHCR
needs: prepare
strategy:
fail-fast: false
matrix:
image_tag: ${{ fromJSON(needs.prepare.outputs.image_tags) }}
# Key migration point:
# use the reusable app image workflow from frappe_docker
# instead of a custom build or a curl-based dispatch flow.
uses: frappe/frappe_docker/.github/workflows/app-build-image.yml@main
with:
# This must match the app name that Bench installs.
app_name: <app_name>
# Build source: the current app repository and ref.
app_repo: ${{ needs.prepare.outputs.app_repo }}
app_ref: ${{ needs.prepare.outputs.app_ref }}
# Base Frappe image line to build on.
frappe_ref: ${{ needs.prepare.outputs.frappe_ref }}
# Final image target in GHCR.
image_name: ${{ needs.prepare.outputs.ghcr_image }}
image_tag: ${{ matrix.image_tag }}
# Manual runs may choose not to push.
push: ${{ needs.prepare.outputs.push == 'true' }}
registry: ghcr.io
platforms: linux/amd64,linux/arm64
publish-dockerhub:
name: Publish Docker Hub
needs:
- prepare
- validate-dockerhub
if: ${{ needs.prepare.outputs.push == 'true' && needs.prepare.outputs.dockerhub_image != '' }}
strategy:
fail-fast: false
matrix:
image_tag: ${{ fromJSON(needs.prepare.outputs.image_tags) }}
uses: frappe/frappe_docker/.github/workflows/app-build-image.yml@main
with:
app_name: <app_name>
app_repo: ${{ needs.prepare.outputs.app_repo }}
app_ref: ${{ needs.prepare.outputs.app_ref }}
frappe_ref: ${{ needs.prepare.outputs.frappe_ref }}
image_name: ${{ needs.prepare.outputs.dockerhub_image }}
image_tag: ${{ matrix.image_tag }}
push: true
platforms: linux/amd64,linux/arm64
secrets:
REGISTRY_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.DOCKERHUB_TOKEN }}
🙌 Thanks
This migration helps unify and simplify our CI/CD pipeline across repositories.
🔄 Migration Required: Update Docker Publish Workflow Reference
This repository is currently using the legacy Docker publish workflow, which has been replaced with a new standardized workflow.
📦 What changed
A new workflow is now used for publishing Docker images:
https://github.com/frappe/frappe_docker/blob/main/.github/workflows/core-build-stable.yml
The previous workflow is still functional for now but is considered legacy and will be removed once all dependent repositories have been migrated.
Please update all references in this repository from the old workflow to the new workflow.
Old workflow:
https://github.com/frappe/frappe_docker/blob/main/.github/workflows/build_stable.yml
New workflow:
https://github.com/frappe/frappe_docker/blob/main/.github/workflows/core-build-stable.yml
🧪 Backward compatibility
🚀 Action required
🗓️ Deprecation note
The old workflow will be removed once all repositories have been migrated.
Migration progress is tracked here:
frappe/frappe_docker#1884
Example for new Workflow
🙌 Thanks
This migration helps unify and simplify our CI/CD pipeline across repositories.