Build and push custom Docker images #54
Workflow file for this run
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build and push custom Docker images | |
on: | |
workflow_dispatch: | |
inputs: | |
repository-owner: | |
type: string | |
required: true | |
description: The owner of the VMaNGOS code repository to use | |
default: vmangos | |
repository-name: | |
type: string | |
required: true | |
description: The name of the VMaNGOS code repository to use | |
default: core | |
revision: | |
type: string | |
required: true | |
description: The VMaNGOS code repository revision to use | |
default: development | |
client-version: | |
type: number | |
required: true | |
description: The client version for which to build VMaNGOS | |
default: 5875 | |
world-db-repository-owner: | |
type: string | |
required: true | |
description: The owner of the VMaNGOS world database repository to use | |
default: brotalnia | |
world-db-repository-name: | |
type: string | |
required: true | |
description: The name of the VMaNGOS world database repository to use | |
default: database | |
world-db-dump-name: | |
type: string | |
required: true | |
description: The name of the VMaNGOS world database dump to use | |
default: world_full_14_june_2021 | |
patches-repository-url: | |
type: string | |
required: false | |
description: (Optional) A URL to a repository that contains patches to apply to the VMaNGOS code (patches have to end in `.patch` and be located in the root of the repository) | |
custom-name: | |
type: string | |
required: false | |
description: (Optional) A custom name to use as part of the image tags (when provided, this overrides the default `{ owner }-{ repository }-{ revision }` naming scheme) | |
architectures: | |
type: choice | |
required: true | |
description: Which architectures should images be built for? | |
options: | |
- "Both x86_64 and aarch64" | |
- "x86_64 only" | |
- "aarch64 only" | |
default: "Both x86_64 and aarch64" | |
env: | |
REGISTRY: ghcr.io | |
IMAGE_NAME_SERVER: ${{ github.repository_owner }}/vmangos-server-custom | |
IMAGE_NAME_DATABASE: ${{ github.repository_owner }}/vmangos-database-custom | |
OCI_ANNOTATION_AUTHORS: ${{ vars.OCI_ANNOTATION_AUTHORS || github.repository_owner }} | |
OCI_ANNOTATION_URL: https://github.com/${{ github.repository }} | |
OCI_ANNOTATION_DOCUMENTATION: https://github.com/${{ github.repository }}/blob/master/README.md | |
OCI_ANNOTATION_SOURCE: https://github.com/${{ github.repository }} | |
OCI_ANNOTATION_VENDOR: ${{ vars.OCI_ANNOTATION_VENDOR || github.repository_owner }} | |
OCI_ANNOTATION_LICENSES: GPL-2.0 | |
OCI_ANNOTATION_SERVER_TITLE: vmangos-deploy - Custom VMaNGOS server image | |
OCI_ANNOTATION_SERVER_DESCRIPTION: VMaNGOS is a server emulator supporting versions 1.6.1 to 1.12.1. | |
OCI_ANNOTATION_SERVER_BASE_NAME: ubuntu:24.04 | |
OCI_ANNOTATION_DATABASE_TITLE: vmangos-deploy - Custom VMaNGOS database image | |
OCI_ANNOTATION_DATABASE_DESCRIPTION: Database for the VMaNGOS server emulator. | |
OCI_ANNOTATION_DATABASE_BASE_NAME: mariadb:11.4 | |
jobs: | |
setup: | |
name: Setup | |
runs-on: ubuntu-24.04 | |
steps: | |
# If the original MariaDB entrypoint script changes, we can't guarantee | |
# that our custom version (which relies on functions defined in the | |
# original) will still work, so we fail the workflow run at this point. | |
- name: Check if MariaDB entrypoint script has been updated | |
id: mariadb-entrypoint-check | |
run: | | |
known_entrypoint=https://raw.githubusercontent.com/MariaDB/mariadb-docker/64252135052f269ed2bb57134ad73537e93b7ab6/11.4/docker-entrypoint.sh | |
latest_entrypoint=https://raw.githubusercontent.com/MariaDB/mariadb-docker/master/11.4/docker-entrypoint.sh | |
curl -o known-entrypoint.sh $known_entrypoint | |
curl -o latest-entrypoint.sh $latest_entrypoint | |
diff known-entrypoint.sh latest-entrypoint.sh | |
build-and-push-server-images: | |
name: Build and push server images | |
needs: setup | |
runs-on: ubuntu-24.04 | |
permissions: | |
contents: read | |
packages: write | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Set up QEMU | |
if: inputs.architectures == 'Both x86_64 and aarch64' || inputs.architectures == 'aarch64 only' | |
uses: docker/setup-qemu-action@v3 | |
# Work around QEMU 7.x compilation issues by explicitly using 8.x, | |
# which is not yet the default for the action. | |
# See https://github.com/tonistiigi/binfmt/issues/215 | |
with: | |
image: tonistiigi/binfmt:qemu-v8.1.5 | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Log in to container registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ${{ env.REGISTRY }} | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Generate tags | |
uses: actions/github-script@v7 | |
id: generate-tags | |
env: | |
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_SERVER }} | |
REPOSITORY_OWNER: ${{ inputs.repository-owner }} | |
REPOSITORY_NAME: ${{ inputs.repository-name }} | |
REVISION: ${{ inputs.revision }} | |
CLIENT_VERSION: ${{ inputs.client-version }} | |
CUSTOM_NAME: ${{ inputs.custom-name }} | |
with: | |
result-encoding: string | |
script: | | |
const tags = [] | |
if (process.env.CUSTOM_NAME.trim() !== '') { | |
tags.push(`${process.env.IMAGE}:${process.env.CUSTOM_NAME}-${process.env.CLIENT_VERSION}`) | |
return tags.join(',') | |
} | |
tags.push(`${process.env.IMAGE}:${process.env.REPOSITORY_OWNER}-${process.env.REPOSITORY_NAME}-${process.env.REVISION}-${process.env.CLIENT_VERSION}`) | |
return tags.join(',') | |
- name: Generate timestamp | |
id: generate-timestamp | |
run: echo "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT | |
# See https://github.com/opencontainers/image-spec/blob/main/annotations.md | |
- name: Generate OCI annotations | |
uses: actions/github-script@v7 | |
id: generate-annotations | |
env: | |
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_SERVER }} | |
REPOSITORY_OWNER: ${{ inputs.repository-owner }} | |
REPOSITORY_NAME: ${{ inputs.repository-name }} | |
REVISION: ${{ inputs.revision }} | |
CLIENT_VERSION: ${{ inputs.client-version }} | |
CUSTOM_NAME: ${{ inputs.custom-name }} | |
MULTI_ARCH_BUILD: ${{ inputs.architectures == 'Both x86_64 and aarch64' }} | |
TIMESTAMP: ${{ steps.generate-timestamp.outputs.timestamp }} | |
with: | |
result-encoding: string | |
script: | | |
const refName = process.env.CUSTOM_NAME.trim() !== '' | |
? `${process.env.IMAGE}:${process.env.CUSTOM_NAME}-${process.env.CLIENT_VERSION}` | |
: `${process.env.IMAGE}:${process.env.REPOSITORY_OWNER}-${process.env.REPOSITORY_NAME}-${process.env.REVISION}-${process.env.CLIENT_VERSION}` | |
const annotations = [ | |
{ key: 'created', value: process.env.TIMESTAMP }, | |
{ key: 'authors', envKey: 'OCI_ANNOTATION_AUTHORS' }, | |
{ key: 'url', envKey: 'OCI_ANNOTATION_URL' }, | |
{ key: 'documentation', envKey: 'OCI_ANNOTATION_DOCUMENTATION' }, | |
{ key: 'source', envKey: 'OCI_ANNOTATION_SOURCE' }, | |
{ key: 'vendor', envKey: 'OCI_ANNOTATION_VENDOR' }, | |
{ key: 'licenses', envKey: 'OCI_ANNOTATION_LICENSES' }, | |
{ key: 'ref.name', value: refName }, | |
{ key: 'title', envKey: 'OCI_ANNOTATION_SERVER_TITLE' }, | |
{ key: 'description', envKey: 'OCI_ANNOTATION_SERVER_DESCRIPTION' }, | |
{ key: 'base.name', envKey: 'OCI_ANNOTATION_SERVER_BASE_NAME' }, | |
].map(({ key, value, envKey }) => { | |
const resolvedValue = value ?? process.env[envKey] ?? '' | |
const prefixes = ['manifest:'] | |
if (process.env.MULTI_ARCH_BUILD === 'true') prefixes.push('index:') | |
return prefixes.map(prefix => `${prefix}org.opencontainers.image.${key}=${resolvedValue}`) | |
}) | |
.flat() | |
.join('\n') | |
return annotations | |
# We also add the generated OCI metadata as labels because the GitHub | |
# Container registry does not correctly pick up the OCI annotations in | |
# some cases and prefers to pull displayed data from the image labels | |
# instead (which then results in data from the base images being shown if | |
# we do not provide values for the respective labels ourselves). | |
- name: Generate OCI labels | |
uses: actions/github-script@v7 | |
id: generate-labels | |
env: | |
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_SERVER }} | |
REPOSITORY_OWNER: ${{ inputs.repository-owner }} | |
REPOSITORY_NAME: ${{ inputs.repository-name }} | |
REVISION: ${{ inputs.revision }} | |
CLIENT_VERSION: ${{ inputs.client-version }} | |
CUSTOM_NAME: ${{ inputs.custom-name }} | |
TIMESTAMP: ${{ steps.generate-timestamp.outputs.timestamp }} | |
with: | |
result-encoding: string | |
script: | | |
const refName = process.env.CUSTOM_NAME.trim() !== '' | |
? `${process.env.IMAGE}:${process.env.CUSTOM_NAME}-${process.env.CLIENT_VERSION}` | |
: `${process.env.IMAGE}:${process.env.REPOSITORY_OWNER}-${process.env.REPOSITORY_NAME}-${process.env.REVISION}-${process.env.CLIENT_VERSION}` | |
const labels = [ | |
{ key: 'created', value: process.env.TIMESTAMP }, | |
{ key: 'authors', envKey: 'OCI_ANNOTATION_AUTHORS' }, | |
{ key: 'url', envKey: 'OCI_ANNOTATION_URL' }, | |
{ key: 'documentation', envKey: 'OCI_ANNOTATION_DOCUMENTATION' }, | |
{ key: 'source', envKey: 'OCI_ANNOTATION_SOURCE' }, | |
{ key: 'vendor', envKey: 'OCI_ANNOTATION_VENDOR' }, | |
{ key: 'licenses', envKey: 'OCI_ANNOTATION_LICENSES' }, | |
{ key: 'ref.name', value: refName }, | |
{ key: 'title', envKey: 'OCI_ANNOTATION_SERVER_TITLE' }, | |
{ key: 'description', envKey: 'OCI_ANNOTATION_SERVER_DESCRIPTION' }, | |
{ key: 'base.name', envKey: 'OCI_ANNOTATION_SERVER_BASE_NAME' }, | |
].map(({ key, value, envKey }) => { | |
const resolvedValue = value ?? process.env[envKey] ?? '' | |
return `org.opencontainers.image.${key}=${resolvedValue}` | |
}) | |
.join('\n') | |
return labels | |
- name: Build and push images | |
uses: docker/build-push-action@v6 | |
with: | |
context: . | |
platforms: ${{ inputs.architectures == 'Both x86_64 and aarch64' && 'linux/amd64, linux/arm64' || inputs.architectures == 'aarch64 only' && 'linux/arm64' || 'linux/amd64' }} | |
file: ./docker/server/Dockerfile | |
push: true | |
provenance: false | |
build-args: | | |
VMANGOS_REPOSITORY_OWNER=${{ inputs.repository-owner }} | |
VMANGOS_REPOSITORY_NAME=${{ inputs.repository-name }} | |
VMANGOS_REVISION=${{ inputs.revision }} | |
VMANGOS_CLIENT_VERSION=${{ inputs.client-version }} | |
VMANGOS_PATCHES_REPOSITORY_URL=${{ inputs.patches-repository-url }} | |
tags: ${{ steps.generate-tags.outputs.result }} | |
annotations: ${{ steps.generate-annotations.outputs.result }} | |
labels: ${{ steps.generate-labels.outputs.result }} | |
# Since the database image builds only take a few minutes (and therefore | |
# always complete before the server image builds), we need to build after the | |
# server images; otherwise, we would push new database images without knowing | |
# if the server image builds will succeed (and if there is, e.g., a | |
# compilation error due to a bug upstream we would end up with a version | |
# mismatch between the latest database images and the latest server images). | |
build-and-push-database-images: | |
name: Build and push database images | |
needs: [setup, build-and-push-server-images] | |
runs-on: ubuntu-24.04 | |
permissions: | |
contents: read | |
packages: write | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Set up QEMU | |
if: inputs.architectures == 'Both x86_64 and aarch64' || inputs.architectures == 'aarch64 only' | |
uses: docker/setup-qemu-action@v3 | |
- name: Set up Docker Buildx | |
uses: docker/setup-buildx-action@v3 | |
- name: Log in to container registry | |
uses: docker/login-action@v3 | |
with: | |
registry: ${{ env.REGISTRY }} | |
username: ${{ github.actor }} | |
password: ${{ secrets.GITHUB_TOKEN }} | |
- name: Generate tags | |
uses: actions/github-script@v7 | |
id: generate-tags | |
env: | |
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DATABASE }} | |
REPOSITORY_OWNER: ${{ inputs.repository-owner }} | |
REPOSITORY_NAME: ${{ inputs.repository-name }} | |
REVISION: ${{ inputs.revision }} | |
CUSTOM_NAME: ${{ inputs.custom-name }} | |
with: | |
result-encoding: string | |
script: | | |
const tags = [] | |
if (process.env.CUSTOM_NAME.trim() !== '') { | |
tags.push(`${process.env.IMAGE}:${process.env.CUSTOM_NAME}`) | |
return tags.join(',') | |
} | |
tags.push(`${process.env.IMAGE}:${process.env.REPOSITORY_OWNER}-${process.env.REPOSITORY_NAME}-${process.env.REVISION}`) | |
return tags.join(',') | |
- name: Generate timestamp | |
id: generate-timestamp | |
run: echo "timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> $GITHUB_OUTPUT | |
# See https://github.com/opencontainers/image-spec/blob/main/annotations.md | |
- name: Generate OCI annotations | |
uses: actions/github-script@v7 | |
id: generate-annotations | |
env: | |
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DATABASE }} | |
REPOSITORY_OWNER: ${{ inputs.repository-owner }} | |
REPOSITORY_NAME: ${{ inputs.repository-name }} | |
REVISION: ${{ inputs.revision }} | |
CUSTOM_NAME: ${{ inputs.custom-name }} | |
MULTI_ARCH_BUILD: ${{ inputs.architectures == 'Both x86_64 and aarch64' }} | |
TIMESTAMP: ${{ steps.generate-timestamp.outputs.timestamp }} | |
with: | |
result-encoding: string | |
script: | | |
const refName = process.env.CUSTOM_NAME.trim() !== '' | |
? `${process.env.IMAGE}:${process.env.CUSTOM_NAME}` | |
: `${process.env.IMAGE}:${process.env.REPOSITORY_OWNER}-${process.env.REPOSITORY_NAME}-${process.env.REVISION}` | |
const annotations = [ | |
{ key: 'created', value: process.env.TIMESTAMP }, | |
{ key: 'authors', envKey: 'OCI_ANNOTATION_AUTHORS' }, | |
{ key: 'url', envKey: 'OCI_ANNOTATION_URL' }, | |
{ key: 'documentation', envKey: 'OCI_ANNOTATION_DOCUMENTATION' }, | |
{ key: 'source', envKey: 'OCI_ANNOTATION_SOURCE' }, | |
{ key: 'vendor', envKey: 'OCI_ANNOTATION_VENDOR' }, | |
{ key: 'licenses', envKey: 'OCI_ANNOTATION_LICENSES' }, | |
{ key: 'ref.name', value: refName }, | |
{ key: 'title', envKey: 'OCI_ANNOTATION_DATABASE_TITLE' }, | |
{ key: 'description', envKey: 'OCI_ANNOTATION_DATABASE_DESCRIPTION' }, | |
{ key: 'base.name', envKey: 'OCI_ANNOTATION_DATABASE_BASE_NAME' }, | |
].map(({ key, value, envKey }) => { | |
const resolvedValue = value ?? process.env[envKey] ?? '' | |
const prefixes = ['manifest:'] | |
if (process.env.MULTI_ARCH_BUILD === 'true') prefixes.push('index:') | |
return prefixes.map(prefix => `${prefix}org.opencontainers.image.${key}=${resolvedValue}`) | |
}) | |
.flat() | |
.join('\n') | |
return annotations | |
# We also add the generated OCI metadata as labels because the GitHub | |
# Container registry does not correctly pick up the OCI annotations in | |
# some cases and prefers to pull displayed data from the image labels | |
# instead (which then results in data from the base images being shown if | |
# we do not provide values for the respective labels ourselves). | |
- name: Generate OCI labels | |
uses: actions/github-script@v7 | |
id: generate-labels | |
env: | |
IMAGE: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_DATABASE }} | |
REPOSITORY_OWNER: ${{ inputs.repository-owner }} | |
REPOSITORY_NAME: ${{ inputs.repository-name }} | |
REVISION: ${{ inputs.revision }} | |
CUSTOM_NAME: ${{ inputs.custom-name }} | |
TIMESTAMP: ${{ steps.generate-timestamp.outputs.timestamp }} | |
with: | |
result-encoding: string | |
script: | | |
const refName = process.env.CUSTOM_NAME.trim() !== '' | |
? `${process.env.IMAGE}:${process.env.CUSTOM_NAME}` | |
: `${process.env.IMAGE}:${process.env.REPOSITORY_OWNER}-${process.env.REPOSITORY_NAME}-${process.env.REVISION}` | |
const labels = [ | |
{ key: 'created', value: process.env.TIMESTAMP }, | |
{ key: 'authors', envKey: 'OCI_ANNOTATION_AUTHORS' }, | |
{ key: 'url', envKey: 'OCI_ANNOTATION_URL' }, | |
{ key: 'documentation', envKey: 'OCI_ANNOTATION_DOCUMENTATION' }, | |
{ key: 'source', envKey: 'OCI_ANNOTATION_SOURCE' }, | |
{ key: 'vendor', envKey: 'OCI_ANNOTATION_VENDOR' }, | |
{ key: 'licenses', envKey: 'OCI_ANNOTATION_LICENSES' }, | |
{ key: 'ref.name', value: refName }, | |
{ key: 'title', envKey: 'OCI_ANNOTATION_DATABASE_TITLE' }, | |
{ key: 'description', envKey: 'OCI_ANNOTATION_DATABASE_DESCRIPTION' }, | |
{ key: 'base.name', envKey: 'OCI_ANNOTATION_DATABASE_BASE_NAME' }, | |
].map(({ key, value, envKey }) => { | |
const resolvedValue = value ?? process.env[envKey] ?? '' | |
return `org.opencontainers.image.${key}=${resolvedValue}` | |
}) | |
.join('\n') | |
return labels | |
- name: Build and push images | |
uses: docker/build-push-action@v6 | |
with: | |
context: . | |
platforms: ${{ inputs.architectures == 'Both x86_64 and aarch64' && 'linux/amd64, linux/arm64' || inputs.architectures == 'aarch64 only' && 'linux/arm64' || 'linux/amd64' }} | |
file: ./docker/database/Dockerfile | |
push: true | |
provenance: false | |
build-args: | | |
VMANGOS_REPOSITORY_OWNER=${{ inputs.repository-owner }} | |
VMANGOS_REPOSITORY_NAME=${{ inputs.repository-name }} | |
VMANGOS_REVISION=${{ inputs.revision }} | |
VMANGOS_WORLD_DB_REPOSITORY_OWNER=${{ inputs.world-db-repository-owner }} | |
VMANGOS_WORLD_DB_REPOSITORY_NAME=${{ inputs.world-db-repository-name }} | |
VMANGOS_WORLD_DB_DUMP_NAME=${{ inputs.world-db-dump-name }} | |
tags: ${{ steps.generate-tags.outputs.result }} | |
annotations: ${{ steps.generate-annotations.outputs.result }} | |
labels: ${{ steps.generate-labels.outputs.result }} |