All checks were successful
Build and Push Base Distro Images / build-and-push (apko/base.yaml, base) (push) Successful in 10m9s
Build and Push Base Distro Images / build-and-push (apko/build.yaml, build) (push) Successful in 10m20s
Build and Push Base Distro Images / build-and-push (apko/dotnet-runtime.yaml, dotnet-runtime) (push) Successful in 10m10s
Build and Push Base Distro Images / build-and-push (apko/dotnet-sdk.yaml, dotnet-sdk) (push) Successful in 10m18s
Build and Push Base Distro Images / build-and-push (apko/flutter-sdk.yaml, flutter-sdk) (push) Successful in 10m5s
Weekly Rebuild (CVE Updates) / rebuild (apko/base.yaml, base) (push) Successful in 9m56s
Weekly Rebuild (CVE Updates) / rebuild (apko/build.yaml, build) (push) Successful in 9m59s
Weekly Rebuild (CVE Updates) / rebuild (apko/dotnet-runtime.yaml, dotnet-runtime) (push) Successful in 9m59s
Weekly Rebuild (CVE Updates) / rebuild (apko/dotnet-sdk.yaml, dotnet-sdk) (push) Successful in 10m1s
Weekly Rebuild (CVE Updates) / rebuild (apko/flutter-sdk.yaml, flutter-sdk) (push) Successful in 10m0s
Extract rootfs layer and config from apko OCI archive separately, then generate Dockerfile with ENV, ENTRYPOINT, WORKDIR and USER from the OCI config. Fixes missing environment variables in the final multi-arch image. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
187 lines
7.7 KiB
YAML
187 lines
7.7 KiB
YAML
name: Build and Push Base Distro Images
|
|
|
|
on:
|
|
release:
|
|
types: [published, prereleased]
|
|
push:
|
|
paths:
|
|
- '.gitea/workflows/publish.yaml'
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
IMAGE_NAME: base-distro
|
|
APKO_VERSION: 1.1.2
|
|
|
|
jobs:
|
|
build-and-push:
|
|
runs-on: ubuntu-latest
|
|
strategy:
|
|
matrix:
|
|
include:
|
|
- config: apko/base.yaml
|
|
variant: base
|
|
- config: apko/build.yaml
|
|
variant: build
|
|
- config: apko/dotnet-runtime.yaml
|
|
variant: dotnet-runtime
|
|
- config: apko/dotnet-sdk.yaml
|
|
variant: dotnet-sdk
|
|
- config: apko/flutter-sdk.yaml
|
|
variant: flutter-sdk
|
|
steps:
|
|
- name: Checkout code
|
|
uses: actions/checkout@v3
|
|
|
|
- name: Determine tag type
|
|
id: tag
|
|
run: |
|
|
if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
|
|
echo "suffix=dev" >> $GITHUB_OUTPUT
|
|
else
|
|
echo "suffix=latest" >> $GITHUB_OUTPUT
|
|
fi
|
|
|
|
- name: Install apko
|
|
run: |
|
|
APKO_ARCH=$(uname -m | sed 's/x86_64/amd64/;s/aarch64/arm64/')
|
|
APKO_VER="${{ env.APKO_VERSION }}"
|
|
curl -fsSL "https://github.com/chainguard-dev/apko/releases/download/v${APKO_VER}/apko_${APKO_VER}_linux_${APKO_ARCH}.tar.gz" \
|
|
-o /tmp/apko.tar.gz
|
|
tar xzf /tmp/apko.tar.gz --strip-components=1 -C /usr/local/bin
|
|
rm /tmp/apko.tar.gz
|
|
|
|
- name: Set up QEMU
|
|
uses: docker/setup-qemu-action@v3
|
|
|
|
- name: Set up Docker Buildx
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Login to Docker Registry
|
|
uses: docker/login-action@v3
|
|
with:
|
|
username: ${{ secrets.REGISTRY_USERNAME }}
|
|
password: ${{ secrets.REGISTRY_PASSWORD }}
|
|
|
|
- name: Determine upstream version
|
|
id: version
|
|
run: |
|
|
VARIANT="${{ matrix.variant }}"
|
|
|
|
case "$VARIANT" in
|
|
base|build)
|
|
UPSTREAM=$(apko resolve ${{ matrix.config }} 2>&1 | grep -oP 'glibc-\K[0-9]+\.[0-9]+' | head -1 || echo "0.0")
|
|
if [ "$UPSTREAM" = "" ] || [ "$UPSTREAM" = "0.0" ]; then
|
|
UPSTREAM=$(apko resolve ${{ matrix.config }} 2>&1 | grep -oP 'glibc \(\K[0-9]+\.[0-9]+' | head -1 || echo "2.42")
|
|
fi
|
|
;;
|
|
dotnet-runtime)
|
|
UPSTREAM=$(curl -fsSL "https://dotnetcli.azureedge.net/dotnet/release-metadata/releases-index.json" \
|
|
| jq -r '[."releases-index"[] | select(."support-phase" == "active" or ."support-phase" == "go-live") | ."latest-runtime"] | sort_by(. | split(".") | map(tonumber)) | last')
|
|
;;
|
|
dotnet-sdk)
|
|
UPSTREAM=$(curl -fsSL "https://dotnetcli.azureedge.net/dotnet/release-metadata/releases-index.json" \
|
|
| jq -r '[."releases-index"[] | select(."support-phase" == "active" or ."support-phase" == "go-live") | ."latest-sdk"] | sort_by(. | split(".") | map(tonumber)) | last')
|
|
;;
|
|
flutter-sdk)
|
|
UPSTREAM=$(curl -fsSL "https://storage.googleapis.com/flutter_infra_release/releases/releases_linux.json" \
|
|
| jq -r '.current_release.stable as $hash | .releases[] | select(.hash == $hash and .channel == "stable") | .version')
|
|
;;
|
|
esac
|
|
|
|
echo "upstream=${UPSTREAM}" >> "$GITHUB_OUTPUT"
|
|
echo "Upstream version for ${VARIANT}: ${UPSTREAM}"
|
|
|
|
REPO_NAME="${{ env.IMAGE_NAME }}"
|
|
REGISTRY_URL="${{ secrets.REGISTRY_URL }}"
|
|
NAMESPACE=$(echo "$REGISTRY_URL" | sed 's|.*://||; s|.*\.io/||; s|/$||')
|
|
|
|
EXISTING_TAGS=$(curl -s "https://hub.docker.com/v2/repositories/${NAMESPACE}/${REPO_NAME}/tags?page_size=100&name=${VARIANT}-${UPSTREAM}." \
|
|
| jq -r '.results[]?.name // empty' 2>/dev/null || echo "")
|
|
|
|
MAX_BUILD=0
|
|
for tag in $EXISTING_TAGS; do
|
|
BUILD_NUM=$(echo "$tag" | grep -oP "\.\K[0-9]+$" || echo "0")
|
|
if [ "$BUILD_NUM" -gt "$MAX_BUILD" ] 2>/dev/null; then
|
|
MAX_BUILD=$BUILD_NUM
|
|
fi
|
|
done
|
|
|
|
NEXT_BUILD=$((MAX_BUILD + 1))
|
|
VERSION_TAG="${VARIANT}-${UPSTREAM}.${NEXT_BUILD}"
|
|
echo "version_tag=${VERSION_TAG}" >> "$GITHUB_OUTPUT"
|
|
echo "Next version tag: ${VERSION_TAG}"
|
|
|
|
- name: Build per-arch apko tarballs
|
|
run: |
|
|
mkdir -p /tmp/build-amd64 /tmp/build-arm64
|
|
apko build --arch x86_64 ${{ matrix.config }} \
|
|
${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} \
|
|
/tmp/build-amd64/image.tar
|
|
apko build --arch aarch64 ${{ matrix.config }} \
|
|
${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} \
|
|
/tmp/build-arm64/image.tar
|
|
|
|
# Extract the rootfs layer from the OCI archive for each arch.
|
|
# apko outputs an OCI image tarball; we need to extract just the
|
|
# filesystem layer (.tar.gz) and the config metadata.
|
|
for arch in amd64 arm64; do
|
|
cd /tmp/build-${arch}
|
|
tar xf image.tar
|
|
# The .tar.gz file is the filesystem layer
|
|
ROOTFS=$(ls *.tar.gz 2>/dev/null | head -1)
|
|
mv "$ROOTFS" rootfs.tar.gz
|
|
# Extract ENV and ENTRYPOINT from the OCI config
|
|
MANIFEST_DIGEST=$(jq -r '.manifests[0].digest' index.json | sed 's/sha256://')
|
|
CONFIG_DIGEST=$(jq -r '.config.digest' "sha256:${MANIFEST_DIGEST}" | sed 's/sha256://')
|
|
cp "sha256:${CONFIG_DIGEST}" config.json
|
|
# Clean up OCI artifacts
|
|
rm -f image.tar index.json manifest.json oci-layout sha256:*
|
|
cd /tmp
|
|
done
|
|
|
|
# Generate Dockerfile with metadata from the OCI config
|
|
# (use amd64 config as reference — env vars are the same for both arches)
|
|
ENV_LINES=$(jq -r '(.config.Env // [])[] | "ENV " + .' /tmp/build-amd64/config.json)
|
|
ENTRYPOINT=$(jq -r '(.config.Entrypoint // [])[]' /tmp/build-amd64/config.json | head -1)
|
|
USER_ID=$(jq -r '.config.User // "65532"' /tmp/build-amd64/config.json)
|
|
WORKDIR=$(jq -r '.config.WorkingDir // "/"' /tmp/build-amd64/config.json)
|
|
|
|
{
|
|
echo "FROM scratch"
|
|
echo "ARG TARGETARCH"
|
|
echo "ADD build-\${TARGETARCH}/rootfs.tar.gz /"
|
|
if [ -n "$ENV_LINES" ]; then
|
|
echo "$ENV_LINES"
|
|
fi
|
|
if [ -n "$ENTRYPOINT" ] && [ "$ENTRYPOINT" != "null" ]; then
|
|
echo "ENTRYPOINT [\"${ENTRYPOINT}\"]"
|
|
fi
|
|
echo "WORKDIR ${WORKDIR}"
|
|
echo "USER ${USER_ID}"
|
|
} > /tmp/Dockerfile
|
|
|
|
- name: Build and push with buildx (SBOM + provenance)
|
|
uses: docker/build-push-action@v5
|
|
with:
|
|
context: /tmp
|
|
file: /tmp/Dockerfile
|
|
platforms: linux/amd64,linux/arm64
|
|
push: true
|
|
sbom: true
|
|
provenance: mode=max
|
|
tags: |
|
|
${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }}
|
|
${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.version_tag }}
|
|
|
|
- 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: |
|
|
docker pull ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }}
|
|
docker scout cves ${{ secrets.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ matrix.variant }}-${{ steps.tag.outputs.suffix }} --only-severity critical,high
|