From aac7e85c4374a4de46a88930ba195edf9afaddbd Mon Sep 17 00:00:00 2001 From: Doug Eubanks Date: Wed, 9 Sep 2026 22:20:52 -0400 Subject: [PATCH 1/3] ci: pin all action SHAs and add auto-tag release workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pin every GitHub Action reference in lint.yml and release.yml to a commit SHA instead of a mutable tag — prevents supply chain attacks via tag mutation. Versions pinned: actions/checkout v7.0.1 3d3c42e ludeeus/action-shellcheck 2.0.0 00cae50 (was @master) hadolint/hadolint-action v3.5.0 06be81b (was v3.3.0, matches PR #33) docker/setup-qemu-action v4.3.0 1f40c72 docker/setup-buildx-action v4.3.0 37fe631 docker/login-action v4.6.0 dbcb813 (was @v4, matches PR #31 + newer) docker/metadata-action v6.2.0 dc80280 docker/build-push-action v7.3.0 53b7df9 gitleaks/gitleaks-action v3 e0c47f4 softprops/action-gh-release v3.0.3 e598afb (was v3.0.0, matches PR #34) Also adds auto-tag.yml: on push to main, reads the semver from synology/INFO and creates a matching vMAJOR.MINOR.PATCH tag if absent. This makes the release workflow (release.yml) trigger automatically on version bumps rather than requiring a manual tag push. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01PhSFmsQDhrUoFR7F8EHkD5 --- .github/workflows/auto-tag.yml | 57 ++++++++++++++++++++++++++++++++++ .github/workflows/lint.yml | 30 +++++++++--------- .github/workflows/release.yml | 14 ++++----- 3 files changed, 79 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/auto-tag.yml diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml new file mode 100644 index 0000000..19091ce --- /dev/null +++ b/.github/workflows/auto-tag.yml @@ -0,0 +1,57 @@ +name: Auto-tag release + +# Runs on every push to main. Reads the version from synology/INFO and creates +# a semver git tag if one does not already exist for that version. The release +# workflow (release.yml) picks up the new tag and publishes the Docker image +# and GitHub Release automatically. +# +# The version in synology/INFO follows the format "MAJOR.MINOR.PATCH-N" (e.g. +# "2.1.1-1"). Only the semver portion (MAJOR.MINOR.PATCH) is used as the tag. +# +# Workflow: bump the version in synology/INFO and CHANGELOG.md → open PR → +# merge to main → this workflow fires → release.yml publishes. + +on: + push: + branches: [main] + +permissions: + contents: write # required to push the new tag + +jobs: + tag: + name: Create release tag + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + + - name: Read version and tag if new + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + + # Extract semver from synology/INFO: version="2.1.1-1" -> 2.1.1 + raw=$(grep '^version=' synology/INFO | sed 's/version="//;s/".*//') + semver="${raw%-*}" + + if ! [[ "$semver" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Unexpected version format: $raw — skipping tag creation." + exit 0 + fi + + tag="v${semver}" + + if git ls-remote --tags origin "refs/tags/${tag}" | grep -q "${tag}"; then + echo "Tag ${tag} already exists — nothing to do." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag "${tag}" -m "Release ${tag}" + git push origin "${tag}" + echo "Created and pushed tag ${tag}." diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 5835f74..6970b5d 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,9 +14,9 @@ jobs: name: ShellCheck runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Run ShellCheck - uses: ludeeus/action-shellcheck@master + uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # 2.0.0 with: scandir: "." severity: warning @@ -27,9 +27,9 @@ jobs: name: Hadolint (Dockerfile) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Run hadolint - uses: hadolint/hadolint-action@v3.3.0 + uses: hadolint/hadolint-action@06be81baf89a55ffd0e24b8f04a4185738dd3387 # v3.5.0 with: dockerfile: docker/Dockerfile config: .hadolint.yaml @@ -39,7 +39,7 @@ jobs: name: Bats unit tests runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Install bats run: sudo apt-get update && sudo apt-get install -y bats jq - name: Run tests @@ -49,7 +49,7 @@ jobs: name: Python unit tests (webserver) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Run tests run: python3 -m unittest discover -s tests -p 'test_*.py' -v @@ -57,13 +57,13 @@ jobs: name: Docker build smoke runs-on: ubuntu-latest steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0 - name: Set up Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 - name: Build multi-arch image (no push) - uses: docker/build-push-action@v7 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . file: ./docker/Dockerfile @@ -80,11 +80,11 @@ jobs: # cache populated by docker-build, so this is a near-instant rebuild. needs: docker-build steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 - name: Build single-arch image for scanning (load to local daemon) - uses: docker/build-push-action@v7 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . file: ./docker/Dockerfile @@ -123,10 +123,10 @@ jobs: # contents-only is not enough on pull_request events. pull-requests: write steps: - - uses: actions/checkout@v7 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 - name: Run gitleaks - uses: gitleaks/gitleaks-action@v3 + uses: gitleaks/gitleaks-action@e0c47f4f8be36e29cdc102c57e68cb5cbf0e8d1e # v3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e1ee809..8e0642c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,23 +14,23 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v7 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Set up QEMU - uses: docker/setup-qemu-action@v4 + uses: docker/setup-qemu-action@1f40c72289eff860ee54a304f1438e3cff362e0a # v4.3.0 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v4 + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 - name: Log in to Docker Hub - uses: docker/login-action@v4 + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Extract metadata id: meta - uses: docker/metadata-action@v6 + uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6.2.0 with: images: dougeubanks/gitpreserver # On a tag push {{is_default_branch}} is never true, so the old @@ -41,7 +41,7 @@ jobs: type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }} - name: Build and push - uses: docker/build-push-action@v7 + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 with: context: . file: ./docker/Dockerfile @@ -86,7 +86,7 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Create GitHub Release - uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0 + uses: softprops/action-gh-release@e598afbe1493e6b1bafb1f389cabb956eab91231 # v3.0.3 with: # Use CHANGELOG section when found; otherwise auto-generate notes. body_path: ${{ steps.notes.outputs.found == 'true' && steps.notes.outputs.file || '' }} From e85c86f6539295697d193ba190e002488d22d550 Mon Sep 17 00:00:00 2001 From: Doug Eubanks Date: Wed, 9 Sep 2026 22:25:48 -0400 Subject: [PATCH 2/3] fix: convert HEALTHCHECK CMD to JSON notation (DL3025) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hadolint v3.5.0 (via hadolint-action v3.5.0) flags shell-form HEALTHCHECK CMD with DL3025. Switch to JSON array notation, which is the preferred form and avoids an implicit /bin/sh wrapper. The redundant || exit 1 is dropped — sys.exit() already controls the exit code, and an unhandled exception in the urlopen call also produces a non-zero exit. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01PhSFmsQDhrUoFR7F8EHkD5 --- docker/Dockerfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index 2dfb90a..9ee984b 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -196,10 +196,7 @@ EXPOSE 6033 # stdlib http client (python3 is already a runtime dep) so we don't have to add # curl back into the slim runtime layer just for the probe. HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ - CMD python3 -c "import os,urllib.request,sys; \ -port=os.environ.get('GITPRESERVER_WEB_PORT','6033'); \ -sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:%s/healthz' % port, timeout=3).status==200 else 1)" \ - || exit 1 + CMD ["python3", "-c", "import os,urllib.request,sys; port=os.environ.get('GITPRESERVER_WEB_PORT','6033'); sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:%s/healthz' % port, timeout=3).status==200 else 1)"] WORKDIR /backups From 88d007fb1bcdb29b9e25523c98cd2fc76881afd5 Mon Sep 17 00:00:00 2001 From: Doug Eubanks Date: Wed, 9 Sep 2026 22:30:21 -0400 Subject: [PATCH 3/3] ci: scope auto-tag to code and version files only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documentation, README, workflow-only, and asset-only merges to main should not trigger a release check. Add path filters to auto-tag.yml so the job only runs when synology/INFO, CHANGELOG.md, backup scripts, docker files, or tests change — the files that actually accompany a version bump. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01PhSFmsQDhrUoFR7F8EHkD5 --- .github/workflows/auto-tag.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.github/workflows/auto-tag.yml b/.github/workflows/auto-tag.yml index 19091ce..22a1524 100644 --- a/.github/workflows/auto-tag.yml +++ b/.github/workflows/auto-tag.yml @@ -14,6 +14,16 @@ name: Auto-tag release on: push: branches: [main] + # Only consider version-carrying files. Documentation, CI workflow + # changes, and asset-only commits never bump the version and do not + # need to trigger a release check. + paths: + - "synology/INFO" + - "CHANGELOG.md" + - "backup/**" + - "docker/**" + - "tests/**" + - "webserver.py" permissions: contents: write # required to push the new tag