upload-artifact@v4 speaks the v2 artifacts API, which this Gitea answers
only at its v3-era shape, so the sixth release attempt cleared every gate
and then aborted at "Preserve verified candidate artifacts" with the GHES
compatibility error. Move the step to v3.2.1, the commit the upstream v3
tag resolves to, which keeps name, path, if-no-files-found and
retention-days unchanged. The step also becomes continue-on-error: it is a
pre-publish debugging backstop, and publish-release.sh attaches the same
directory as Gitea release assets, so losing it must never cost a release.
Replace cosign-installer with the direct fetch the scanner already uses.
The action issues no API call on this path, but it is a composite action
resting on envsubst and a resolved runner.arch, neither of which this
runner has exercised. Asked for the version it bootstraps, it downloads
exactly cosign-linux-amd64 from the v3.0.6 release, compares it against
c956e5df..., and exits; that digest matches the release checksums file, so
fetching the asset directly verifies identically with nothing unproven
left before the one-way publication gate. The binary joins the PATH that
publish-release.sh already resolves dotnet through.
trivy-action checks its own repository out of github.com using the runner
token; on this self-hosted Gitea that token is a Gitea token, GitHub answers
"Bad credentials", and both scan steps die before trivy is installed.
Download the v0.69.3 release archive directly, verify it against a sha256
digest pinned inline, and unpack only the binary into .release-work/bin,
which is gitignored and excluded from the Docker build context. The gate
keeps its exact semantics: --exit-code 1, --severity HIGH,CRITICAL, table
output, unfixed vulnerabilities still in scope. The SPDX step writes the
same filename release_artifacts.py normalize-container-sbom consumes, and
TRIVY_CACHE_DIR keeps the vulnerability DB inside the work directory.