From 40ac6d1fc6527b221e897e5912b9fe628972e5e5 Mon Sep 17 00:00:00 2001 From: Nardo86 <64206225+Nardo86@users.noreply.github.com> Date: Fri, 17 Jul 2026 11:08:02 +0200 Subject: [PATCH] feat: tag images by NUT version + document tag scheme MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tags were date-based (YYYY.MM), which didn't reveal the NUT server version inside — the point raised in issue #3. Now the build resolves the packaged nut-server version from Debian bookworm and tags/labels the image by it, while keeping date tags as immutable build stamps. - Add version tags `X.Y.Z-R` (e.g. 2.8.0-7) and `X.Y.Z` (e.g. 2.8.0). - Add OCI labels org.networkupstools.version / .upstream and set org.opencontainers.image.version to the NUT version. - Record the built version in NUT_VERSION; the monthly commit doubles as a keepalive so GitHub doesn't auto-disable the cron after 60 days idle (which already caused the 2025.11–2026.04 gap in published tags). - Document the two-axis tag scheme (version vs build) in the README. - Drop unused semver git-tag triggers/patterns (versioning is NUT-based). Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/docker-publish.yml | 62 +++++++++++++++++++++++++--- NUT_VERSION | 6 +++ README.md | 35 +++++++++++++++- 3 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 NUT_VERSION diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 301b892..1131f1e 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -3,11 +3,13 @@ name: Build and Publish NUT Server Docker Images on: push: branches: [ "main", "master" ] - tags: [ 'v*.*.*' ] pull_request: branches: [ "main", "master" ] schedule: - # Run automatically on the 1st of every month at 03:00 UTC + # Monthly rebuild (1st, 03:00 UTC): refreshes Debian base security patches + # and re-detects the packaged NUT version. The build also commits a small + # NUT_VERSION stamp, which doubles as the keepalive that stops GitHub from + # auto-disabling this schedule after 60 days of repo inactivity. - cron: '0 3 1 * *' workflow_dispatch: @@ -19,7 +21,7 @@ jobs: build-and-push: runs-on: ubuntu-latest permissions: - contents: read + contents: write packages: write id-token: write @@ -41,6 +43,20 @@ jobs: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + # The image installs nut-server from Debian bookworm, so its version is + # whatever bookworm currently ships. Resolve the candidate here (package + # versions are identical across arches) and use it to tag and label the + # image, so users can pin/inspect by NUT version instead of build date. + - name: Resolve packaged NUT version + id: nut + run: | + full=$(docker run --rm debian:bookworm-slim sh -c \ + 'apt-get update -qq && apt-cache policy nut-server 2>/dev/null | awk "/Candidate:/{print \$2}"') + if [ -z "$full" ]; then echo "Could not resolve nut-server version" >&2; exit 1; fi + echo "full=$full" >> "$GITHUB_OUTPUT" + echo "upstream=${full%%-*}" >> "$GITHUB_OUTPUT" + echo "Resolved NUT: full=$full upstream=${full%%-*}" + - name: Extract Docker metadata id: meta uses: docker/metadata-action@v5 @@ -48,15 +64,23 @@ jobs: images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} tags: | type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} type=raw,value=latest,enable={{is_default_branch}} + # NUT version tags: full Debian version (e.g. 2.8.0-7) plus the + # upstream pointer (e.g. 2.8.0) that moves to the latest Debian + # revision. This is the primary way to pin a server version. + type=raw,value=${{ steps.nut.outputs.full }},enable={{is_default_branch}} + type=raw,value=${{ steps.nut.outputs.upstream }},enable={{is_default_branch}} + # Date tag: immutable stamp of a specific monthly build. type=raw,value={{date 'YYYY.MM'}},enable={{is_default_branch}} # Legacy aliases kept in sync with `latest` so users who pinned # these 2021-era tags keep receiving the current multi-arch image. type=raw,value=amd64-latest,enable={{is_default_branch}} type=raw,value=arm64v8-latest,enable={{is_default_branch}} type=raw,value=arm32v7-latest,enable={{is_default_branch}} + labels: | + org.opencontainers.image.version=${{ steps.nut.outputs.full }} + org.networkupstools.version=${{ steps.nut.outputs.full }} + org.networkupstools.upstream=${{ steps.nut.outputs.upstream }} - name: Build and push Docker image id: build-and-push @@ -77,4 +101,30 @@ jobs: username: ${{ secrets.DOCKER_HUB_USERNAME }} password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} repository: ${{ env.IMAGE_NAME }} - readme-filepath: ./README.md \ No newline at end of file + readme-filepath: ./README.md + + # Record the built version in-repo and commit it. This gives a + # human-readable version log and, because it runs on the monthly + # schedule, keeps the repo active so GitHub does not auto-disable the + # cron after 60 days of inactivity. [skip ci] avoids a redundant run + # (a GITHUB_TOKEN push does not trigger workflows anyway). + - name: Record built NUT version (keepalive) + if: github.ref == 'refs/heads/master' && (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') + run: | + cat > NUT_VERSION <