ci: publish latest only from stable releases and scope workflow tokens

A manual run of the release image workflow tagged `latest` on whatever
ref it was dispatched from, and the demo deploy and every Watchtower
install follow `latest`. It now moves only for a published stable
release or a dispatch on a stable `v*` tag.

- `static-lite.yml` gave the whole workflow `contents: write`, including
  the build job that runs pull-request code. The build is read-only
  now; a separate release job with no checkout uploads the zips.
- `docker-publish.yml` passes the ref name through `env` instead of
  interpolating it into the shell script.
This commit is contained in:
Linus Rath
2026-09-26 16:30:23 +02:00
parent 3ac6c25c71
commit 582960ae06
3 changed files with 35 additions and 14 deletions
@@ -1,5 +1,8 @@
name: Publish Docker Image on Release
# `latest` moves only for a published stable release, or for a manual run
# dispatched on a stable release tag (to rebuild one). A dispatch on a
# branch builds nothing that the demo deploy or Watchtower users follow.
on:
release:
types: [published]
@@ -101,7 +104,7 @@ jobs:
with:
images: ${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable=${{ github.event_name != 'release' || !github.event.release.prerelease }}
type=raw,value=latest,enable=${{ (github.event_name == 'release' && !github.event.release.prerelease) || (github.event_name == 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-')) }}
type=semver,pattern=v{{version}}
type=semver,pattern={{version}}
type=semver,pattern=v{{major}}.{{minor}}
@@ -212,7 +215,7 @@ jobs:
flavor: |
suffix=-always,onlatest=true
tags: |
type=raw,value=latest,enable=${{ github.event_name != 'release' || !github.event.release.prerelease }}
type=raw,value=latest,enable=${{ (github.event_name == 'release' && !github.event.release.prerelease) || (github.event_name == 'workflow_dispatch' && startsWith(github.ref, 'refs/tags/v') && !contains(github.ref_name, '-')) }}
type=semver,pattern=v{{version}}
type=semver,pattern={{version}}
type=semver,pattern=v{{major}}.{{minor}}
+6 -3
View File
@@ -29,11 +29,14 @@ jobs:
steps:
- name: Set image name
id: set
env:
REF_NAME: ${{ github.ref_name }}
REPOSITORY: ${{ github.repository }}
run: |
if [ "${{ github.ref_name }}" = "main" ]; then
echo "image_name=ghcr.io/${{ github.repository }}-beta" >> $GITHUB_OUTPUT
if [ "$REF_NAME" = "main" ]; then
echo "image_name=ghcr.io/${REPOSITORY}-beta" >> "$GITHUB_OUTPUT"
else
echo "image_name=ghcr.io/${{ github.repository }}-${{ github.ref_name }}" >> $GITHUB_OUTPUT
echo "image_name=ghcr.io/${REPOSITORY}-${REF_NAME}" >> "$GITHUB_OUTPUT"
fi
build:
+24 -9
View File
@@ -42,7 +42,7 @@ on:
default: false
permissions:
contents: write
contents: read
jobs:
build:
@@ -98,21 +98,36 @@ jobs:
echo "ZIP=${ZIP}" >> "$GITHUB_ENV"
ls -la "${ZIP}"
- name: Upload release asset
if: github.event_name == 'release'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.event.release.tag_name }}
run: gh release upload "$TAG_NAME" "$ZIP" --clobber
- name: Upload artifact
if: github.event_name != 'release'
uses: actions/upload-artifact@v4
with:
name: bulwark-lite-${{ matrix.target }}
path: ${{ env.ZIP }}
retention-days: 7
release:
# The only job with a write token, and it runs no checked-out code.
needs: build
if: github.event_name == 'release'
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Download zips
uses: actions/download-artifact@v4
with:
pattern: bulwark-lite-*
path: zips
merge-multiple: true
- name: Upload release assets
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
TAG_NAME: ${{ github.event.release.tag_name }}
run: gh release upload "$TAG_NAME" zips/*.zip --clobber
smoke:
# Boots the demo export on a plain static server and drives it with
# Playwright: login, open a thread, switch surfaces, reload a deep link.