Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 115 additions & 24 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,63 +6,154 @@ on:
pull_request:

jobs:
docker-linux-build:
# Parallelized: run quick docker build tests per-Dockerfile in parallel
linux-test:
runs-on: ubuntu-latest
container:
image: hairyhenderson/dockerfiles-builder:latest
env:
BASHBREW_LIBRARY: ./library
BASHBREW_NAMESPACE: caddy
DOCKER_BUILDKIT: '1'
strategy:
matrix:
path:
- 2.10/alpine
- 2.10/builder
- 2.11/alpine
- 2.11/builder
steps:
- uses: actions/checkout@master
- name: non-master build test
run: |
docker build -f 2.10/alpine/Dockerfile 2.10/alpine
docker build -f 2.10/builder/Dockerfile 2.10/builder
- name: non-master build test (per-path)
if: github.repository != 'caddyserver/caddy-docker' || github.ref != 'refs/heads/master'
run: |
docker build -f ${{ matrix.path }}/Dockerfile ${{ matrix.path }}

# Main linux bashbrew build.
linux-build:
runs-on: ubuntu-latest
container:
image: hairyhenderson/dockerfiles-builder:latest
env:
BASHBREW_LIBRARY: ./library
BASHBREW_NAMESPACE: caddy
DOCKER_BUILDKIT: '1'
steps:
- uses: actions/checkout@master

- name: build
run: bashbrew build caddy

- name: push
if: github.repository == 'caddyserver/caddy-docker' && github.ref == 'refs/heads/master'
# NOTE: DOCKERHUB_TOKEN and DOCKERHUB_USERNAME must be present in https://github.com/caddyserver/caddy-docker/settings
# the user must have permission to push to https://hub.docker.com/r/caddy/caddy
run: |
echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login --username ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
bashbrew push caddy
if: github.repository == 'caddyserver/caddy-docker' && github.ref == 'refs/heads/master'

- name: push (non-master dry run)
if: github.repository != 'caddyserver/caddy-docker' || github.ref != 'refs/heads/master'
run: |
bashbrew push --dry-run caddy
if: github.repository != 'caddyserver/caddy-docker' || github.ref != 'refs/heads/master'

docker-windows-build:
runs-on: windows-2022
# env:
# BASHBREW_LIBRARY: ./library
# BASHBREW_NAMESPACE: caddy

# Parallelized: run quick windows docker build tests per-Dockerfile in parallel
windows-test:
strategy:
matrix:
include:
- version: '2.10'
os: ltsc2022
runner: windows-2022
- version: '2.10'
os: ltsc2025
runner: windows-2025
- version: '2.11'
os: ltsc2022
runner: windows-2022
- version: '2.11'
os: ltsc2025
runner: windows-2025
name: windows-test (${{ matrix.version }}/windows/${{ matrix.os }})
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@master
- name: non-master build test
run: |
docker build -f 2.10/windows/ltsc2022/Dockerfile 2.10/windows/ltsc2022
docker build -f 2.10/windows-nanoserver/ltsc2022/Dockerfile 2.10/windows-nanoserver/ltsc2022
- name: non-master build test (per-path)
if: github.repository != 'caddyserver/caddy-docker' || github.ref != 'refs/heads/master'
shell: pwsh
run: |
$path = "${{ matrix.version }}/windows/${{ matrix.os }}"
$nanopath = "${{ matrix.version }}/windows-nanoserver/${{ matrix.os }}"
Write-Host "Building test for $path"

# Early-exit: if there's no nanoserver Dockerfile for this version/os,
# just build the servercore Dockerfile and finish early.
if (-not (Test-Path "$nanopath\Dockerfile")) {
Write-Host "No nanoserver Dockerfile at $nanopath; building servercore only"
docker build -f "$path\Dockerfile" "$path"
exit 0
}

# nanoserver Dockerfile exists — parse it for the referenced servercore tag.
$nanoDockerfile = Get-Content "$nanopath\Dockerfile" -Raw
if (-not ($nanoDockerfile -match 'COPY\s+--from=caddy:([^\s]+)')) {
throw "Could not find 'COPY --from=caddy:<tag>' in $nanopath/Dockerfile; aborting nanoserver test."
}

$referencedTag = $Matches[1]
Write-Host "Found nanoserver copy-from tag: $referencedTag"

# Ensure the servercore Dockerfile exists and build/tag it so nanoserver can COPY from it.
$servercorePath = $path
if (-not (Test-Path "$servercorePath\Dockerfile")) {
throw "Expected servercore Dockerfile at $servercorePath not found; cannot prepare nanoserver build."
}

Write-Host "Building and tagging servercore image at $servercorePath as caddy:$referencedTag"
docker build -t "caddy:$referencedTag" -f "$servercorePath\Dockerfile" "$servercorePath"
Write-Host "Building nanoserver image at $nanopath"
docker build -f "$nanopath\Dockerfile" "$nanopath"

# Main windows build (matrix over constraints) - runs after tests
windows-build:
strategy:
matrix:
include:
- name: core-ltsc2022
constraint: windowsservercore-ltsc2022
runner: windows-2022
- name: nano-ltsc2022
constraint: nanoserver-ltsc2022,windowsservercore-ltsc2022
runner: windows-2022
- name: core-ltsc2025
constraint: windowsservercore-ltsc2025
runner: windows-2025
- name: nano-ltsc2025
constraint: nanoserver-ltsc2025,windowsservercore-ltsc2025
runner: windows-2025
name: windows-build (${{ matrix.name }})
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@master

- name: install bashbrew
run: curl -o /bashbrew.exe https://doi-janky.infosiftr.net/job/bashbrew/job/master/lastSuccessfulBuild/artifact/bashbrew-windows-amd64.exe

- name: build
run: |
/bashbrew --arch windows-amd64 --constraint windowsservercore-ltsc2022 --namespace caddy --library ./library build caddy;
/bashbrew --arch windows-amd64 --constraint windowsnanoserver-ltsc2022 --namespace caddy --library ./library build caddy
/bashbrew --arch windows-amd64 --constraint ${{ matrix.constraint }} --namespace caddy --library ./library build caddy

- name: push
if: github.repository == 'caddyserver/caddy-docker' && github.ref == 'refs/heads/master'
# NOTE: DOCKERHUB_TOKEN and DOCKERHUB_USERNAME must be present in https://github.com/caddyserver/caddy-docker/settings
# the user must have permission to push to https://hub.docker.com/r/caddy/caddy
run: |
echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login --username ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin;
/bashbrew --arch windows-amd64 --constraint windowsservercore-ltsc2022 --namespace caddy --library ./library push caddy;
/bashbrew --arch windows-amd64 --constraint windowsnanoserver-ltsc2022 --namespace caddy --library ./library push caddy
if: github.repository == 'caddyserver/caddy-docker' && github.ref == 'refs/heads/master'
/bashbrew --arch windows-amd64 --constraint ${{ matrix.constraint }} --namespace caddy --library ./library push caddy

- name: push (non-master dry run)
run: |
/bashbrew --arch windows-amd64 --constraint windowsservercore-ltsc2022 --namespace caddy --library ./library push --dry-run caddy;
/bashbrew --arch windows-amd64 --constraint windowsnanoserver-ltsc2022 --namespace caddy --library ./library push --dry-run caddy
if: github.repository != 'caddyserver/caddy-docker' || github.ref != 'refs/heads/master'
run: |
/bashbrew --arch windows-amd64 --constraint ${{ matrix.constraint }} --namespace caddy --library ./library push --dry-run caddy

200 changes: 200 additions & 0 deletions .github/workflows/ghcr.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
name: GHCR Build and Push

on:
push:
branches:
- master
paths:
- 'library/caddy'
pull_request:

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
parse:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.parse.outputs.result }}
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Parse library file
id: parse
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');

// Convert architecture names to Docker platform format
function archToPlatform(arch) {
const mapping = {
'amd64': 'linux/amd64',
'arm64v8': 'linux/arm64',
'arm32v6': 'linux/arm/v6',
'arm32v7': 'linux/arm/v7',
'ppc64le': 'linux/ppc64le',
'riscv64': 'linux/riscv64',
's390x': 'linux/s390x',
};
return mapping[arch] || null;
}

console.log('Parsing library/caddy...');

const content = fs.readFileSync('library/caddy', 'utf8');

// Split into sections (separated by blank lines)
const sections = content.split('\n\n');

const matrixItems = [];

for (const section of sections) {
if (!section.trim() || section.startsWith('#') || section.startsWith('Maintainers:')) {
continue;
}

// Parse fields from this section
let tags = null;
let sharedTags = null;
let directory = null;
let architectures = null;

for (const line of section.split('\n')) {
const trimmedLine = line.trim();
if (trimmedLine.startsWith('#')) {
continue;
}

if (trimmedLine.startsWith('Tags: ')) {
tags = trimmedLine.substring(6).replace(/,/g, ' ');
} else if (trimmedLine.startsWith('SharedTags: ')) {
sharedTags = trimmedLine.substring(12).replace(/,/g, ' ');
} else if (trimmedLine.startsWith('Directory: ')) {
directory = trimmedLine.substring(11);
} else if (trimmedLine.startsWith('Architectures: ')) {
architectures = trimmedLine.substring(15).replace(/,/g, ' ');
}
}

// Skip if we don't have required fields
if (!tags || !directory || !architectures) {
continue;
}

// Skip Windows images
if (architectures.includes('windows')) {
console.log(`Skipping Windows image: ${directory}`);
continue;
}

// Convert architectures to platforms
const platforms = [];
for (const arch of architectures.split(/\s+/)) {
if (!arch.includes('windows')) {
const platform = archToPlatform(arch);
if (platform) {
platforms.push(platform);
}
}
}

if (platforms.length === 0) {
console.log(`No Linux platforms found for ${directory}, skipping`);
continue;
}

console.log(`Found Linux image: ${directory}`);

// Combine tags and shared tags
let allTags = tags;
if (sharedTags) {
allTags = `${tags} ${sharedTags}`.replace(/\s+/g, ' ').trim();
}

matrixItems.push({
directory: directory,
platforms: platforms.join(','),
tags: allTags
});
}

// Create matrix JSON
const matrix = { include: matrixItems };

console.log(`\nGenerated matrix with ${matrixItems.length} items:`);
console.log(JSON.stringify(matrix, null, 2));

// Set output for matrix
return matrix;

build:
needs: parse
name: Build (${{ matrix.directory }})
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.parse.outputs.matrix) }}

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
if: github.event_name != 'pull_request'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build and push image
shell: bash
run: |
set -e

DIRECTORY="${{ matrix.directory }}"
PLATFORMS="${{ matrix.platforms }}"
ALL_TAGS="${{ matrix.tags }}"
IS_PR="${{ github.event_name == 'pull_request' }}"

echo "=========================================="
echo "Building image from: $DIRECTORY"
echo "Platforms: $PLATFORMS"
echo "Tags: $ALL_TAGS"
echo "=========================================="
echo

# Build Docker tag arguments
TAG_ARGS=""
for tag in $ALL_TAGS; do
TAG_ARGS="$TAG_ARGS --tag ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:$tag"
done

# Construct buildx command string (print it for logs), then run it
if [[ "$IS_PR" == "true" ]]; then
CMD="docker buildx build --platform '$PLATFORMS' --file '$DIRECTORY/Dockerfile' $TAG_ARGS '$DIRECTORY'"
else
CMD="docker buildx build --push --platform '$PLATFORMS' --file '$DIRECTORY/Dockerfile' $TAG_ARGS '$DIRECTORY'"
fi

echo "=========================================="
echo "Running buildx command:"
echo "$CMD"
echo "=========================================="

# Execute the command
eval $CMD

echo "Successfully processed $DIRECTORY"
6 changes: 3 additions & 3 deletions 2.10/alpine/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ RUN set -eux; \
wget -O /usr/share/caddy/index.html "https://github.com/caddyserver/dist/raw/33ae08ff08d168572df2956ed14fbc4949880d94/welcome/index.html"

# https://github.com/caddyserver/caddy/releases
ENV CADDY_VERSION v2.10.2
ENV CADDY_VERSION=v2.10.2

RUN set -eux; \
apkArch="$(apk --print-arch)"; \
Expand All @@ -39,8 +39,8 @@ RUN set -eux; \
caddy version

# See https://caddyserver.com/docs/conventions#file-locations for details
ENV XDG_CONFIG_HOME /config
ENV XDG_DATA_HOME /data
ENV XDG_CONFIG_HOME=/config
ENV XDG_DATA_HOME=/data

LABEL org.opencontainers.image.version=v2.10.2
LABEL org.opencontainers.image.title=Caddy
Expand Down
Loading