From 742aef89834ba4bf1d142a4e739b4855eced925d Mon Sep 17 00:00:00 2001 From: Mathias Beaulieu-Duncan Date: Tue, 3 Feb 2026 18:04:33 -0500 Subject: [PATCH] Merge discover and build into single job with shell loop Gitea Actions does not pass dynamic matrix values from fromJson cross-job outputs correctly, causing empty variables. Replace the two-job matrix strategy with a single job that discovers versions and iterates in bash. Co-Authored-By: Claude Opus 4.5 --- .gitea/workflows/build.yaml | 303 +++++++++++++----------------------- 1 file changed, 107 insertions(+), 196 deletions(-) diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index c014e5b..c50e5ba 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -18,64 +18,8 @@ env: 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 @@ -99,10 +43,36 @@ jobs: 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 --strip-components=1 -C /usr/local/bin - - name: Build apko base images + - 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: Determine tag suffix + id: suffix + run: | + if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then + echo "TAG_SUFFIX=-test" >> $GITHUB_ENV + echo "Prerelease detected — tags will use -test suffix" + else + echo "TAG_SUFFIX=" >> $GITHUB_ENV + fi + + - name: Discover versions and build all images run: | set -euo pipefail + # Discover supported .NET versions + RELEASES=$(curl -fsSL "$RELEASES_INDEX_URL") + SUPPORTED=$(echo "$RELEASES" | jq -c '[.["releases-index"][] | select(.["support-phase"] == "active" or .["support-phase"] == "go-live")]') + + 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 "LTS major: $LTS_MAJOR" + echo "STS major: $STS_MAJOR" + + # Build apko base images (shared across all .NET versions) for VARIANT in runtime runtime-invariant sdk; do for ARCH in x86_64 aarch64; do echo "::group::Building apko base: $VARIANT ($ARCH)" @@ -113,147 +83,88 @@ jobs: done done - - name: Download .NET binaries - run: | - set -euo pipefail + # Iterate over each supported .NET version + echo "$SUPPORTED" | jq -r '.[] | "\(.["channel-version"] | split(".")[0]) \(.["latest-runtime"]) \(.["latest-sdk"]) \(.["release-type"])"' | \ + while read -r MAJOR RUNTIME SDK TYPE; do + echo "=============================================" + echo "Building .NET $MAJOR: runtime=$RUNTIME sdk=$SDK type=$TYPE" + echo "=============================================" - RUNTIME_VERSION="${{ matrix.runtime }}" - SDK_VERSION="${{ matrix.sdk }}" + # Download .NET binaries + 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 - 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 + echo "::group::Downloading ASP.NET Core runtime $RUNTIME ($DOTNET_ARCH)" + mkdir -p dotnet-${PLATFORM_ARCH}/runtime + curl -fsSL "https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/${RUNTIME}/aspnetcore-runtime-${RUNTIME}-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 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::" + echo "::group::Downloading .NET SDK $SDK ($DOTNET_ARCH)" + mkdir -p dotnet-${PLATFORM_ARCH}/sdk + curl -fsSL "https://dotnetcli.azureedge.net/dotnet/Sdk/${SDK}/dotnet-sdk-${SDK}-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 - # 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::" + # Build and push each variant + for VARIANT in runtime runtime-invariant sdk; do + if [ "$VARIANT" = "sdk" ]; then + VERSION="$SDK" + DOTNET_DIR="sdk" + else + VERSION="$RUNTIME" + DOTNET_DIR="runtime" + fi + + echo "::group::Building $VARIANT-$MAJOR" + + # Prepare build context + CONTEXT="context-${VARIANT}-${MAJOR}" + rm -rf "$CONTEXT" + mkdir -p "$CONTEXT" + for ARCH in amd64 arm64; do + APKO_ARCH=$([[ "$ARCH" == "amd64" ]] && echo "x86_64" || echo "aarch64") + mkdir -p "$CONTEXT/build-${ARCH}/${VARIANT}" + cp "build-${APKO_ARCH}/${VARIANT}/rootfs.tar.gz" "$CONTEXT/build-${ARCH}/${VARIANT}/" + mkdir -p "$CONTEXT/dotnet-${ARCH}" + cp -r "dotnet-${ARCH}/${DOTNET_DIR}" "$CONTEXT/dotnet-${ARCH}/" + done + cp "dockerfiles/${VARIANT}.Dockerfile" "$CONTEXT/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/" + + echo "::endgroup::" + done + + # Docker Scout CVE scan + for VARIANT in runtime runtime-invariant sdk; do + echo "::group::Scout scan: ${VARIANT}-${MAJOR}" + docker scout cves "$DOCKER_IMAGE:${VARIANT}-${MAJOR}${TAG_SUFFIX}" --only-severity critical,high || true + echo "::endgroup::" + done + + # Clean up .NET binaries for this version before next iteration + rm -rf dotnet-amd64 dotnet-arm64 done