diff --git a/.gitea/workflows/build.yaml b/.gitea/workflows/build.yaml index e4494ca..f70f7b4 100644 --- a/.gitea/workflows/build.yaml +++ b/.gitea/workflows/build.yaml @@ -2,7 +2,7 @@ name: Build and Push .NET Images on: release: - types: [published] + types: [published, prereleased] workflow_dispatch: schedule: # Weekly rebuild for CVE patches (Sunday 6am UTC) @@ -24,7 +24,18 @@ jobs: 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: | @@ -64,6 +75,7 @@ jobs: 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 @@ -152,12 +164,12 @@ jobs: cp dockerfiles/runtime.Dockerfile context-runtime/Dockerfile # Determine tags - TAGS="-t $DOCKER_IMAGE:${VARIANT}-${MAJOR} -t $DOCKER_IMAGE:${VARIANT}-${VERSION}" + 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" + TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-lts${TAG_SUFFIX}" fi if [ "$MAJOR" = "$STS_MAJOR" ]; then - TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-sts" + TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-sts${TAG_SUFFIX}" fi echo "Building $VARIANT with tags: $TAGS" @@ -184,12 +196,12 @@ jobs: done cp dockerfiles/runtime-invariant.Dockerfile context-runtime-invariant/Dockerfile - TAGS="-t $DOCKER_IMAGE:${VARIANT}-${MAJOR} -t $DOCKER_IMAGE:${VARIANT}-${VERSION}" + 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" + TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-lts${TAG_SUFFIX}" fi if [ "$MAJOR" = "$STS_MAJOR" ]; then - TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-sts" + TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-sts${TAG_SUFFIX}" fi echo "Building $VARIANT with tags: $TAGS" @@ -216,12 +228,12 @@ jobs: done cp dockerfiles/sdk.Dockerfile context-sdk/Dockerfile - TAGS="-t $DOCKER_IMAGE:${VARIANT}-${MAJOR} -t $DOCKER_IMAGE:${VARIANT}-${VERSION}" + 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" + TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-lts${TAG_SUFFIX}" fi if [ "$MAJOR" = "$STS_MAJOR" ]; then - TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-sts" + TAGS="$TAGS -t $DOCKER_IMAGE:${VARIANT}-sts${TAG_SUFFIX}" fi echo "Building $VARIANT with tags: $TAGS" @@ -240,6 +252,6 @@ jobs: 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} --only-severity critical,high || true + docker scout cves ${{ env.DOCKER_IMAGE }}:${VARIANT}-${MAJOR}${TAG_SUFFIX} --only-severity critical,high || true echo "::endgroup::" done diff --git a/DOCKERHUB.md b/DOCKERHUB.md new file mode 100644 index 0000000..c126997 --- /dev/null +++ b/DOCKERHUB.md @@ -0,0 +1,119 @@ +# .NET Docker Images + +Git Repository +Wolfi + +Minimal .NET Docker images for production and CI/CD. Built on [Wolfi](https://wolfi.dev), a security-focused Linux distribution designed for containers. All supported (non-EOL) .NET versions are rebuilt automatically. + +## Variants + +- `runtime` - ASP.NET Core runtime with ICU/globalization support +- `runtime-invariant` - ASP.NET Core runtime without ICU (smallest, invariant mode) +- `sdk` - .NET SDK with bash, git, curl for building apps + +All variants support `linux/amd64` and `linux/arm64`. + +## Why Wolfi? + +[Wolfi](https://wolfi.dev) is a lightweight Linux distribution built specifically for containers. It provides: + +- **Minimal footprint** - Only essential packages, nothing extra +- **Daily security updates** - Patches applied quickly +- **Designed for containers** - No legacy cruft from traditional distros + +## Features + +- **Lightweight** - Optimized for fast CI/CD pulls +- **Secure** - Built on Wolfi with continuous vulnerability scanning +- **Multi-arch** - Supports both `linux/amd64` and `linux/arm64` +- **Non-root** - Runtime images run as unprivileged user (UID 65532) +- **Supply chain security** - SBOM and SLSA provenance attestations included +- **EOL-aware** - Versions are automatically dropped when they reach end-of-life + +## Dockerfile Examples + +### Web API + +```dockerfile +ARG BUILDPLATFORM +FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-10 AS build + +WORKDIR /source +COPY . . +WORKDIR /source/MyApp.Api + +ARG TARGETARCH +RUN case "$TARGETARCH" in \ + amd64) ARCH=x64 ;; \ + arm64) ARCH=arm64 ;; \ + *) ARCH=$TARGETARCH ;; \ + esac && \ + dotnet publish -a $ARCH --self-contained false -o /app + +FROM svrnty/dotnet:runtime-invariant-10 AS final +WORKDIR /app +COPY --from=build /app . +USER 65532 +EXPOSE 8080 +ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyApp.Api.dll"] +``` + +### Worker service + +```dockerfile +ARG BUILDPLATFORM +FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-lts AS build + +WORKDIR /source +COPY . . + +ARG TARGETARCH +RUN case "$TARGETARCH" in \ + amd64) ARCH=x64 ;; \ + arm64) ARCH=arm64 ;; \ + *) ARCH=$TARGETARCH ;; \ + esac && \ + dotnet publish MyWorker -a $ARCH --self-contained false -o /app + +FROM svrnty/dotnet:runtime-invariant-lts AS final +WORKDIR /app +COPY --from=build /app . +USER 65532 +ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyWorker.dll"] +``` + +## CI/CD (Gitea/GitHub Actions) + +```yaml +jobs: + build: + runs-on: ubuntu-latest + container: + image: svrnty/dotnet:sdk-lts + steps: + - uses: actions/checkout@v4 + - run: dotnet restore + - run: dotnet test --no-restore + - run: dotnet publish -c Release -o /app +``` + +## Tags + +- `-` - Latest patch for a major version (e.g., `runtime-10`) +- `-` - Exact version pin (e.g., `runtime-10.0.2`, `sdk-10.0.102`) +- `-lts` - Floating tag for the highest active LTS +- `-sts` - Floating tag for the highest active STS + +## Automatic Updates + +Images are automatically rebuilt when: + +- New .NET patch versions are released (daily check) +- Base image security updates are available (weekly rebuild) +- A .NET version reaches EOL, it is automatically excluded + +Every build is scanned and includes supply chain attestations (SBOM, SLSA provenance). + +## License + +MIT diff --git a/README.md b/README.md new file mode 100644 index 0000000..31a55a1 --- /dev/null +++ b/README.md @@ -0,0 +1,161 @@ +# .NET Docker Images + +Git Repository +Docker Hub +Docker Pulls +.NET +Wolfi + +Minimal .NET Docker images for production and CI/CD. Built on [Wolfi](https://wolfi.dev), a security-focused Linux distribution designed for containers. All supported (non-EOL) .NET versions are rebuilt automatically. + +## Images + +| Variant | Use Case | .NET 10 (LTS) | .NET 9 (STS) | .NET 8 | Arch | +|---------|----------|----------------|---------------|--------|------| +| runtime | Production web apps | `runtime-10` `runtime-lts` | `runtime-9` `runtime-sts` | `runtime-8` | amd64 arm64 | +| runtime-invariant | Minimal production (UTC-only) | `runtime-invariant-10` `runtime-invariant-lts` | `runtime-invariant-9` `runtime-invariant-sts` | `runtime-invariant-8` | amd64 arm64 | +| sdk | Building .NET apps | `sdk-10` `sdk-lts` | `sdk-9` `sdk-sts` | `sdk-8` | amd64 arm64 | + +## Variants + +| Variant | Includes | Shell | ICU/Globalization | User | +|---------|----------|-------|-------------------|------| +| **runtime** | ASP.NET Core runtime | No | Yes | 65532 (nonroot) | +| **runtime-invariant** | ASP.NET Core runtime | No | No (invariant mode) | 65532 (nonroot) | +| **sdk** | .NET SDK + bash, git, curl | Yes (bash) | Yes | root | + +- **runtime** - Full globalization support (ICU + tzdata). Use this for apps that need locale-aware formatting, time zones, or culture-specific behavior. +- **runtime-invariant** - No ICU or tzdata. Smallest image size. Use this for APIs that only need UTC and ordinal string comparison. +- **sdk** - Everything needed to build .NET apps. Runs as root so `dotnet restore` can write to global caches. + +## Why Wolfi? + +[Wolfi](https://wolfi.dev) is a lightweight Linux distribution built specifically for containers. It provides: + +- **Minimal footprint** - Only essential packages, nothing extra +- **Daily security updates** - Patches applied quickly +- **Designed for containers** - No legacy cruft from traditional distros + +## Features + +- **Lightweight** - Optimized for fast CI/CD pulls +- **Secure** - Built on Wolfi with continuous vulnerability scanning +- **Multi-arch** - Supports both `linux/amd64` and `linux/arm64` +- **Non-root** - Runtime images run as unprivileged user (UID 65532) +- **Supply chain security** - SBOM and SLSA provenance attestations included +- **EOL-aware** - Versions are automatically dropped when they reach end-of-life + +## Dockerfile Examples + +### Web API (recommended) + +```dockerfile +ARG BUILDPLATFORM +FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-10 AS build + +WORKDIR /source +COPY . . +WORKDIR /source/MyApp.Api + +ARG TARGETARCH +RUN case "$TARGETARCH" in \ + amd64) ARCH=x64 ;; \ + arm64) ARCH=arm64 ;; \ + *) ARCH=$TARGETARCH ;; \ + esac && \ + dotnet publish -a $ARCH --self-contained false -o /app + +FROM svrnty/dotnet:runtime-invariant-10 AS final +WORKDIR /app +COPY --from=build /app . +USER 65532 +EXPOSE 8080 +ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyApp.Api.dll"] +``` + +### Web API with globalization (ICU) + +```dockerfile +ARG BUILDPLATFORM +FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-lts AS build + +WORKDIR /source +COPY . . + +ARG TARGETARCH +RUN case "$TARGETARCH" in \ + amd64) ARCH=x64 ;; \ + arm64) ARCH=arm64 ;; \ + *) ARCH=$TARGETARCH ;; \ + esac && \ + dotnet publish MyApp.sln -a $ARCH --self-contained false -o /app + +FROM svrnty/dotnet:runtime-lts AS final +WORKDIR /app +COPY --from=build /app . +USER 65532 +EXPOSE 8080 +ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyApp.dll"] +``` + +### Worker service + +```dockerfile +ARG BUILDPLATFORM +FROM --platform=$BUILDPLATFORM svrnty/dotnet:sdk-10 AS build + +WORKDIR /source +COPY . . + +ARG TARGETARCH +RUN case "$TARGETARCH" in \ + amd64) ARCH=x64 ;; \ + arm64) ARCH=arm64 ;; \ + *) ARCH=$TARGETARCH ;; \ + esac && \ + dotnet publish MyWorker -a $ARCH --self-contained false -o /app + +FROM svrnty/dotnet:runtime-invariant-10 AS final +WORKDIR /app +COPY --from=build /app . +USER 65532 +ENTRYPOINT ["/usr/share/dotnet/dotnet", "MyWorker.dll"] +``` + +## CI/CD (Gitea/GitHub Actions) + +```yaml +jobs: + build: + runs-on: ubuntu-latest + container: + image: svrnty/dotnet:sdk-lts + steps: + - uses: actions/checkout@v4 + - run: dotnet restore + - run: dotnet test --no-restore + - run: dotnet publish -c Release -o /app +``` + +## Tags + +Each variant is tagged with: + +- `-` - Latest patch for a major version (e.g., `runtime-10`) +- `-` - Exact version pin (e.g., `runtime-10.0.2`, `sdk-10.0.102`) +- `-lts` - Floating tag pointing to the highest active LTS (currently .NET 10) +- `-sts` - Floating tag pointing to the highest active STS (currently .NET 9) + +## Automatic Updates + +Images are automatically rebuilt when: + +- New .NET patch versions are released (daily check) +- Base image security updates are available (weekly rebuild) +- A .NET version reaches EOL, it is automatically excluded + +Every build is scanned with Docker Scout and includes supply chain attestations (SBOM, SLSA provenance). + +## License + +MIT