The Gitea runner executes inside Docker, so volume mounts from docker run resolve against the host filesystem where the workspace path does not exist. Install apko directly on the runner to avoid the Docker-in-Docker volume mount issue. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
260 lines
10 KiB
YAML
260 lines
10 KiB
YAML
name: Build and Push .NET Images
|
|
|
|
on:
|
|
release:
|
|
types: [published, prereleased]
|
|
workflow_dispatch:
|
|
schedule:
|
|
# Weekly rebuild for CVE patches (Sunday 6am UTC)
|
|
- cron: "0 6 * * 0"
|
|
|
|
permissions:
|
|
id-token: write
|
|
contents: read
|
|
packages: write
|
|
|
|
env:
|
|
DOCKER_IMAGE: docker.io/svrnty/dotnet
|
|
RELEASES_INDEX_URL: https://dotnetcli.azureedge.net/dotnet/release-metadata/releases-index.json
|
|
|
|
jobs:
|
|
discover:
|
|
runs-on: ubuntu-latest
|
|
outputs:
|
|
matrix: ${{ steps.discover.outputs.matrix }}
|
|
lts_major: ${{ steps.discover.outputs.lts_major }}
|
|
sts_major: ${{ steps.discover.outputs.sts_major }}
|
|
tag_suffix: ${{ steps.suffix.outputs.tag_suffix }}
|
|
steps:
|
|
- name: Determine tag suffix
|
|
id: suffix
|
|
run: |
|
|
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
|
|
echo "tag_suffix=-test" >> $GITHUB_OUTPUT
|
|
echo "Prerelease detected — tags will use -test suffix"
|
|
else
|
|
echo "tag_suffix=" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Discover supported .NET versions
|
|
id: discover
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
RELEASES=$(curl -fsSL "$RELEASES_INDEX_URL")
|
|
|
|
# Filter active/go-live versions (excludes EOL automatically)
|
|
SUPPORTED=$(echo "$RELEASES" | jq -c '[.["releases-index"][] | select(.["support-phase"] == "active" or .["support-phase"] == "go-live")]')
|
|
|
|
# Build matrix JSON
|
|
MATRIX=$(echo "$SUPPORTED" | jq -c '{include: [.[] | {
|
|
major: (.["channel-version"] | split(".")[0]),
|
|
runtime: .["latest-runtime"],
|
|
sdk: .["latest-sdk"],
|
|
type: .["release-type"]
|
|
}]}')
|
|
|
|
# Find highest LTS and STS majors
|
|
LTS_MAJOR=$(echo "$SUPPORTED" | jq -r '[.[] | select(.["release-type"] == "lts")] | sort_by(.["channel-version"] | split(".") | map(tonumber)) | last | .["channel-version"] | split(".")[0]')
|
|
STS_MAJOR=$(echo "$SUPPORTED" | jq -r '[.[] | select(.["release-type"] == "sts")] | sort_by(.["channel-version"] | split(".") | map(tonumber)) | last | .["channel-version"] | split(".")[0] // ""')
|
|
|
|
echo "matrix=$MATRIX" >> $GITHUB_OUTPUT
|
|
echo "lts_major=$LTS_MAJOR" >> $GITHUB_OUTPUT
|
|
echo "sts_major=$STS_MAJOR" >> $GITHUB_OUTPUT
|
|
|
|
echo "Matrix: $MATRIX"
|
|
echo "LTS major: $LTS_MAJOR"
|
|
echo "STS major: $STS_MAJOR"
|
|
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
needs: discover
|
|
strategy:
|
|
fail-fast: false
|
|
matrix: ${{ fromJson(needs.discover.outputs.matrix) }}
|
|
env:
|
|
LTS_MAJOR: ${{ needs.discover.outputs.lts_major }}
|
|
STS_MAJOR: ${{ needs.discover.outputs.sts_major }}
|
|
TAG_SUFFIX: ${{ needs.discover.outputs.tag_suffix }}
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3
|
|
with:
|
|
platforms: arm64
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Log in to DockerHub
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
|
|
|
- name: Install apko
|
|
run: |
|
|
curl -fsSL https://github.com/chainguard-dev/apko/releases/latest/download/apko_$(curl -fsSL https://api.github.com/repos/chainguard-dev/apko/releases/latest | jq -r .tag_name | sed 's/^v//')_linux_amd64.tar.gz \
|
|
| tar xz -C /usr/local/bin apko
|
|
|
|
- name: Build apko base images
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
for VARIANT in runtime runtime-invariant sdk; do
|
|
for ARCH in x86_64 aarch64; do
|
|
echo "::group::Building apko base: $VARIANT ($ARCH)"
|
|
mkdir -p build-${ARCH}/${VARIANT}
|
|
apko build --arch $ARCH \
|
|
apko/${VARIANT}.yaml ${VARIANT}:latest build-${ARCH}/${VARIANT}/rootfs.tar.gz
|
|
echo "::endgroup::"
|
|
done
|
|
done
|
|
|
|
- name: Download .NET binaries
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
RUNTIME_VERSION="${{ matrix.runtime }}"
|
|
SDK_VERSION="${{ matrix.sdk }}"
|
|
|
|
for ARCH in x86_64 aarch64; do
|
|
if [ "$ARCH" = "x86_64" ]; then
|
|
DOTNET_ARCH="x64"
|
|
PLATFORM_ARCH="amd64"
|
|
else
|
|
DOTNET_ARCH="arm64"
|
|
PLATFORM_ARCH="arm64"
|
|
fi
|
|
|
|
# Download ASP.NET Core runtime (for runtime and runtime-invariant)
|
|
echo "::group::Downloading ASP.NET Core runtime $RUNTIME_VERSION ($DOTNET_ARCH)"
|
|
mkdir -p dotnet-${PLATFORM_ARCH}/runtime
|
|
curl -fsSL "https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/${RUNTIME_VERSION}/aspnetcore-runtime-${RUNTIME_VERSION}-linux-${DOTNET_ARCH}.tar.gz" \
|
|
-o /tmp/aspnet-runtime-${PLATFORM_ARCH}.tar.gz
|
|
tar xf /tmp/aspnet-runtime-${PLATFORM_ARCH}.tar.gz -C dotnet-${PLATFORM_ARCH}/runtime
|
|
echo "::endgroup::"
|
|
|
|
# Download .NET SDK
|
|
echo "::group::Downloading .NET SDK $SDK_VERSION ($DOTNET_ARCH)"
|
|
mkdir -p dotnet-${PLATFORM_ARCH}/sdk
|
|
curl -fsSL "https://dotnetcli.azureedge.net/dotnet/Sdk/${SDK_VERSION}/dotnet-sdk-${SDK_VERSION}-linux-${DOTNET_ARCH}.tar.gz" \
|
|
-o /tmp/dotnet-sdk-${PLATFORM_ARCH}.tar.gz
|
|
tar xf /tmp/dotnet-sdk-${PLATFORM_ARCH}.tar.gz -C dotnet-${PLATFORM_ARCH}/sdk
|
|
echo "::endgroup::"
|
|
done
|
|
|
|
- name: Build and push runtime image
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
MAJOR="${{ matrix.major }}"
|
|
VERSION="${{ matrix.runtime }}"
|
|
VARIANT="runtime"
|
|
|
|
# Prepare build context
|
|
mkdir -p context-runtime
|
|
for ARCH in amd64 arm64; do
|
|
APKO_ARCH=$([[ "$ARCH" == "amd64" ]] && echo "x86_64" || echo "aarch64")
|
|
mkdir -p context-runtime/build-${ARCH}/runtime
|
|
cp build-${APKO_ARCH}/${VARIANT}/rootfs.tar.gz context-runtime/build-${ARCH}/runtime/
|
|
mkdir -p context-runtime/dotnet-${ARCH}
|
|
cp -r dotnet-${ARCH}/runtime context-runtime/dotnet-${ARCH}/
|
|
done
|
|
cp dockerfiles/runtime.Dockerfile context-runtime/Dockerfile
|
|
|
|
# Determine tags
|
|
TAGS="-t $DOCKER_IMAGE:${VARIANT}-${MAJOR}${TAG_SUFFIX} -t $DOCKER_IMAGE:${VARIANT}-${VERSION}${TAG_SUFFIX}"
|
|
if [ "$MAJOR" = "$LTS_MAJOR" ]; then
|
|
TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-lts${TAG_SUFFIX}"
|
|
fi
|
|
if [ "$MAJOR" = "$STS_MAJOR" ]; then
|
|
TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-sts${TAG_SUFFIX}"
|
|
fi
|
|
|
|
echo "Building $VARIANT with tags: $TAGS"
|
|
docker buildx build --platform linux/amd64,linux/arm64 --push \
|
|
--sbom=true --provenance=mode=max \
|
|
$TAGS \
|
|
context-runtime/
|
|
|
|
- name: Build and push runtime-invariant image
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
MAJOR="${{ matrix.major }}"
|
|
VERSION="${{ matrix.runtime }}"
|
|
VARIANT="runtime-invariant"
|
|
|
|
mkdir -p context-runtime-invariant
|
|
for ARCH in amd64 arm64; do
|
|
APKO_ARCH=$([[ "$ARCH" == "amd64" ]] && echo "x86_64" || echo "aarch64")
|
|
mkdir -p context-runtime-invariant/build-${ARCH}/runtime-invariant
|
|
cp build-${APKO_ARCH}/${VARIANT}/rootfs.tar.gz context-runtime-invariant/build-${ARCH}/runtime-invariant/
|
|
mkdir -p context-runtime-invariant/dotnet-${ARCH}
|
|
cp -r dotnet-${ARCH}/runtime context-runtime-invariant/dotnet-${ARCH}/
|
|
done
|
|
cp dockerfiles/runtime-invariant.Dockerfile context-runtime-invariant/Dockerfile
|
|
|
|
TAGS="-t $DOCKER_IMAGE:${VARIANT}-${MAJOR}${TAG_SUFFIX} -t $DOCKER_IMAGE:${VARIANT}-${VERSION}${TAG_SUFFIX}"
|
|
if [ "$MAJOR" = "$LTS_MAJOR" ]; then
|
|
TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-lts${TAG_SUFFIX}"
|
|
fi
|
|
if [ "$MAJOR" = "$STS_MAJOR" ]; then
|
|
TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-sts${TAG_SUFFIX}"
|
|
fi
|
|
|
|
echo "Building $VARIANT with tags: $TAGS"
|
|
docker buildx build --platform linux/amd64,linux/arm64 --push \
|
|
--sbom=true --provenance=mode=max \
|
|
$TAGS \
|
|
context-runtime-invariant/
|
|
|
|
- name: Build and push SDK image
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
MAJOR="${{ matrix.major }}"
|
|
VERSION="${{ matrix.sdk }}"
|
|
VARIANT="sdk"
|
|
|
|
mkdir -p context-sdk
|
|
for ARCH in amd64 arm64; do
|
|
APKO_ARCH=$([[ "$ARCH" == "amd64" ]] && echo "x86_64" || echo "aarch64")
|
|
mkdir -p context-sdk/build-${ARCH}/sdk
|
|
cp build-${APKO_ARCH}/${VARIANT}/rootfs.tar.gz context-sdk/build-${ARCH}/sdk/
|
|
mkdir -p context-sdk/dotnet-${ARCH}
|
|
cp -r dotnet-${ARCH}/sdk context-sdk/dotnet-${ARCH}/
|
|
done
|
|
cp dockerfiles/sdk.Dockerfile context-sdk/Dockerfile
|
|
|
|
TAGS="-t $DOCKER_IMAGE:${VARIANT}-${MAJOR}${TAG_SUFFIX} -t $DOCKER_IMAGE:${VARIANT}-${VERSION}${TAG_SUFFIX}"
|
|
if [ "$MAJOR" = "$LTS_MAJOR" ]; then
|
|
TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-lts${TAG_SUFFIX}"
|
|
fi
|
|
if [ "$MAJOR" = "$STS_MAJOR" ]; then
|
|
TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-sts${TAG_SUFFIX}"
|
|
fi
|
|
|
|
echo "Building $VARIANT with tags: $TAGS"
|
|
docker buildx build --platform linux/amd64,linux/arm64 --push \
|
|
--sbom=true --provenance=mode=max \
|
|
$TAGS \
|
|
context-sdk/
|
|
|
|
- name: Install Docker Scout
|
|
run: |
|
|
curl -fsSL https://raw.githubusercontent.com/docker/scout-cli/main/install.sh -o install-scout.sh
|
|
sh install-scout.sh
|
|
|
|
- name: Docker Scout CVE Scan
|
|
run: |
|
|
MAJOR="${{ matrix.major }}"
|
|
for VARIANT in runtime runtime-invariant sdk; do
|
|
echo "::group::Scout scan: ${VARIANT}-${MAJOR}"
|
|
docker scout cves ${{ env.DOCKER_IMAGE }}:${VARIANT}-${MAJOR}${TAG_SUFFIX} --only-severity critical,high || true
|
|
echo "::endgroup::"
|
|
done
|