ci: port workflows from .github/ to .forgejo/ (Forgejo Actions)
GitHub torrentclaw org is shadow-banned and the CI lives at git.torrentclaw.com
now. Forgejo Actions is enabled cluster-wide; this moves the workflows into the
runner's natively-watched .forgejo/workflows/ tree and adapts each step so the
existing Forgejo runner ('docker', 'ubuntu-latest' labels) can execute them
without leaning on GitHub-only tooling.
- ci.yml: drop actions/setup-go (use container: golang:1.25), replace
golangci-lint-action with the upstream install.sh, drop codecov-action
(third-party, can re-add later with a Forgejo-compatible variant).
- release.yml: drop goreleaser-action (install via curl), wire GITEA_TOKEN +
the new release.gitea_urls block in .goreleaser.yml so goreleaser publishes
to Forgejo. Sign step swaps 'gh release upload' for curl against the Forgejo
releases API (via the in-cluster forgejo:3000 hostname). VirusTotal job
dropped — depended heavily on 'gh release' wiring; can be reimplemented
against the Forgejo API later if we re-enable it.
- docker-rebuild.yml: drop docker/login-action + docker/build-push-action,
use raw 'docker' commands with manually-installed buildx + qemu. Same
weekly schedule (Mon 04:17 UTC) and same 'latest' refresh behaviour.
- pages.yml: deleted — install.sh / install.ps1 are already served from the
Hetzner releases volume at torrentclaw.com/install.sh, so the GitHub Pages
copy was redundant even before the shadow-ban.
.goreleaser.yml: add release.gitea_urls (api=forgejo:3000, download via the
public Forgejo URL) + prerelease:auto. ship.sh uses '--skip=publish' so local
runs aren't affected by the new release block.
This commit is contained in:
parent
2826ee712e
commit
4a00988ee1
7 changed files with 213 additions and 361 deletions
|
|
@ -12,35 +12,26 @@ permissions:
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
name: Test
|
name: Test
|
||||||
runs-on: ubuntu-latest
|
runs-on: docker
|
||||||
strategy:
|
container:
|
||||||
matrix:
|
image: docker.io/library/golang:1.25
|
||||||
go-version: ["1.25"]
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version: ${{ matrix.go-version }}
|
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: go test -v -race -count=1 ./...
|
run: go test -v -race -count=1 ./...
|
||||||
|
|
||||||
build:
|
build:
|
||||||
name: Build
|
name: Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: docker.io/library/golang:1.25
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
goos: [linux, darwin, windows]
|
goos: [linux, darwin, windows]
|
||||||
goarch: [amd64, arm64]
|
goarch: [amd64, arm64]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version: "1.25"
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
env:
|
env:
|
||||||
|
|
@ -50,30 +41,30 @@ jobs:
|
||||||
|
|
||||||
lint:
|
lint:
|
||||||
name: Lint
|
name: Lint
|
||||||
runs-on: ubuntu-latest
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: docker.io/library/golang:1.25
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Install golangci-lint
|
||||||
uses: actions/setup-go@v6
|
run: |
|
||||||
with:
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/v2.11.4/install.sh \
|
||||||
go-version: "1.25"
|
| sh -s -- -b /usr/local/bin v2.11.4
|
||||||
|
|
||||||
- name: Run golangci-lint
|
- name: Run golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v9
|
run: golangci-lint run ./...
|
||||||
with:
|
|
||||||
version: v2.11.4
|
|
||||||
|
|
||||||
coverage:
|
coverage:
|
||||||
name: Coverage
|
name: Coverage
|
||||||
runs-on: ubuntu-latest
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: docker.io/library/golang:1.25
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Go
|
- name: Install python3
|
||||||
uses: actions/setup-go@v6
|
run: apt-get update && apt-get install -y --no-install-recommends python3
|
||||||
with:
|
|
||||||
go-version: "1.25"
|
|
||||||
|
|
||||||
- name: Run tests with coverage (all packages)
|
- name: Run tests with coverage (all packages)
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -102,24 +93,13 @@ jobs:
|
||||||
print('OK: Coverage meets minimum threshold')
|
print('OK: Coverage meets minimum threshold')
|
||||||
"
|
"
|
||||||
|
|
||||||
- name: Upload coverage to Codecov
|
|
||||||
uses: codecov/codecov-action@v6
|
|
||||||
with:
|
|
||||||
files: ./coverage.out
|
|
||||||
fail_ci_if_error: false
|
|
||||||
env:
|
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
|
||||||
|
|
||||||
vet:
|
vet:
|
||||||
name: Vet
|
name: Vet
|
||||||
runs-on: ubuntu-latest
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: docker.io/library/golang:1.25
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v6
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Set up Go
|
|
||||||
uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version: "1.25"
|
|
||||||
|
|
||||||
- name: Run go vet
|
- name: Run go vet
|
||||||
run: go vet ./...
|
run: go vet ./...
|
||||||
61
.forgejo/workflows/docker-rebuild.yml
Normal file
61
.forgejo/workflows/docker-rebuild.yml
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
# Rebuilds and re-pushes the `latest` image without a version bump so newly
|
||||||
|
# *fixed* Alpine / ffmpeg / Go patches land between tagged releases. Versioned
|
||||||
|
# tags are immutable and never touched here. Runs weekly and on demand.
|
||||||
|
name: Docker rebuild
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
# Mondays 04:17 UTC (off the hour to avoid the scheduler rush)
|
||||||
|
- cron: "17 4 * * 1"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
rebuild:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: docker.io/library/docker:27-cli
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install build deps
|
||||||
|
run: apk add --no-cache curl git bash
|
||||||
|
|
||||||
|
- name: Install buildx
|
||||||
|
run: |
|
||||||
|
mkdir -p ~/.docker/cli-plugins
|
||||||
|
curl -sSL https://github.com/docker/buildx/releases/latest/download/buildx-linux-amd64 \
|
||||||
|
-o ~/.docker/cli-plugins/docker-buildx
|
||||||
|
chmod +x ~/.docker/cli-plugins/docker-buildx
|
||||||
|
|
||||||
|
- name: Set up qemu
|
||||||
|
run: docker run --rm --privileged tonistiigi/binfmt --install all
|
||||||
|
|
||||||
|
# Stamp the binary with the most recent release tag (not "dev").
|
||||||
|
- name: Resolve version
|
||||||
|
id: ver
|
||||||
|
run: |
|
||||||
|
v=$(git describe --tags --abbrev=0 2>/dev/null || echo dev)
|
||||||
|
echo "version=$v" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
env:
|
||||||
|
DH_USER: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
DH_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
run: echo "$DH_TOKEN" | docker login -u "$DH_USER" --password-stdin
|
||||||
|
|
||||||
|
- name: Build + push (refresh latest)
|
||||||
|
env:
|
||||||
|
VERSION: ${{ steps.ver.outputs.version }}
|
||||||
|
run: |
|
||||||
|
docker buildx create --name builder --use --driver docker-container
|
||||||
|
# Refresh the floating tag only — never overwrite a versioned release.
|
||||||
|
# Force a fresh base pull so apk upgrade picks up new patches.
|
||||||
|
docker buildx build \
|
||||||
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
--build-arg "VERSION=$VERSION" \
|
||||||
|
--tag "torrentclaw/unarr:latest" \
|
||||||
|
--no-cache \
|
||||||
|
--push \
|
||||||
|
.
|
||||||
113
.forgejo/workflows/release.yml
Normal file
113
.forgejo/workflows/release.yml
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
name: Release
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
image: docker.io/library/golang:1.25
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install build deps (bash, curl, jq, ffmpeg fetch deps)
|
||||||
|
run: |
|
||||||
|
apt-get update
|
||||||
|
apt-get install -y --no-install-recommends bash curl ca-certificates jq xz-utils unzip
|
||||||
|
|
||||||
|
- name: Install goreleaser
|
||||||
|
run: |
|
||||||
|
curl -sSfL https://github.com/goreleaser/goreleaser/releases/latest/download/goreleaser_Linux_x86_64.tar.gz \
|
||||||
|
| tar -xz -C /usr/local/bin goreleaser
|
||||||
|
|
||||||
|
- name: Run goreleaser
|
||||||
|
env:
|
||||||
|
# Forgejo runner injects GITHUB_TOKEN — but goreleaser uses it to talk to
|
||||||
|
# the *Forgejo* API thanks to the gitea_urls override in .goreleaser.yml.
|
||||||
|
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||||
|
# Empty when RELEASE_SIGNING_PUBKEY variable is unset — goreleaser
|
||||||
|
# accepts it and the resulting binary disables signature checks
|
||||||
|
# (back-compat: pre-signing releases continue to update). Set
|
||||||
|
# RELEASE_SIGNING_PUBKEY (variable) + RELEASE_SIGNING_KEY (secret)
|
||||||
|
# to turn verification on.
|
||||||
|
RELEASE_SIGNING_PUBKEY: ${{ vars.RELEASE_SIGNING_PUBKEY }}
|
||||||
|
run: goreleaser release --clean
|
||||||
|
|
||||||
|
- name: Sign checksums.txt with ed25519
|
||||||
|
if: ${{ vars.RELEASE_SIGNING_PUBKEY != '' && secrets.RELEASE_SIGNING_KEY != '' }}
|
||||||
|
env:
|
||||||
|
RELEASE_SIGNING_KEY: ${{ secrets.RELEASE_SIGNING_KEY }}
|
||||||
|
RELEASE_TAG: ${{ github.ref_name }}
|
||||||
|
FORGEJO_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# Tailscale IP — domain-agnostic; the runner shares the dokploy-network with
|
||||||
|
# forgejo (hostname `forgejo`), so the in-cluster hostname is fastest, but the
|
||||||
|
# Tailscale IP is the documented fallback.
|
||||||
|
FORGEJO_API: http://forgejo:3000/api/v1
|
||||||
|
REPO: deivid/unarr
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
go run ./scripts/sign-checksums \
|
||||||
|
-key "$RELEASE_SIGNING_KEY" \
|
||||||
|
-in dist/checksums.txt \
|
||||||
|
-out dist/checksums.txt.sig
|
||||||
|
|
||||||
|
# Find the release ID for this tag, then upload the sig as an asset.
|
||||||
|
rel_id=$(curl -sSf "$FORGEJO_API/repos/$REPO/releases/tags/$RELEASE_TAG" \
|
||||||
|
-H "Authorization: token $FORGEJO_TOKEN" | jq -r '.id')
|
||||||
|
curl -sSf -X POST \
|
||||||
|
"$FORGEJO_API/repos/$REPO/releases/$rel_id/assets?name=checksums.txt.sig" \
|
||||||
|
-H "Authorization: token $FORGEJO_TOKEN" \
|
||||||
|
-F "attachment=@dist/checksums.txt.sig"
|
||||||
|
|
||||||
|
docker:
|
||||||
|
needs: release
|
||||||
|
runs-on: docker
|
||||||
|
container:
|
||||||
|
# Docker-in-Docker capable image — buildx + qemu pre-installed.
|
||||||
|
image: docker.io/library/docker:27-cli
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install buildx
|
||||||
|
run: |
|
||||||
|
apk add --no-cache curl
|
||||||
|
mkdir -p ~/.docker/cli-plugins
|
||||||
|
curl -sSL https://github.com/docker/buildx/releases/latest/download/buildx-linux-amd64 \
|
||||||
|
-o ~/.docker/cli-plugins/docker-buildx
|
||||||
|
chmod +x ~/.docker/cli-plugins/docker-buildx
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
env:
|
||||||
|
DH_USER: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
|
DH_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
run: echo "$DH_TOKEN" | docker login -u "$DH_USER" --password-stdin
|
||||||
|
|
||||||
|
- name: Set up qemu
|
||||||
|
run: docker run --rm --privileged tonistiigi/binfmt --install all
|
||||||
|
|
||||||
|
- name: Build + push multi-arch image
|
||||||
|
env:
|
||||||
|
VERSION: ${{ github.ref_name }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
VERSION_SEMVER="${VERSION#v}"
|
||||||
|
MAJOR_MINOR="${VERSION_SEMVER%.*}"
|
||||||
|
docker buildx create --name builder --use --driver docker-container
|
||||||
|
docker buildx build \
|
||||||
|
--platform linux/amd64,linux/arm64 \
|
||||||
|
--build-arg "VERSION=$VERSION" \
|
||||||
|
--tag "torrentclaw/unarr:$VERSION_SEMVER" \
|
||||||
|
--tag "torrentclaw/unarr:$MAJOR_MINOR" \
|
||||||
|
--tag "torrentclaw/unarr:latest" \
|
||||||
|
--push \
|
||||||
|
.
|
||||||
52
.github/workflows/docker-rebuild.yml
vendored
52
.github/workflows/docker-rebuild.yml
vendored
|
|
@ -1,52 +0,0 @@
|
||||||
# Rebuilds and re-pushes the `latest` image without a version bump so newly
|
|
||||||
# *fixed* Alpine / ffmpeg / Go patches land between tagged releases. Versioned
|
|
||||||
# tags are immutable and never touched here. Runs weekly and on demand.
|
|
||||||
name: Docker rebuild
|
|
||||||
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
# Mondays 04:17 UTC (off the hour to avoid the scheduler rush)
|
|
||||||
- cron: "17 4 * * 1"
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
rebuild:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
# Stamp the binary with the most recent release tag (not "dev").
|
|
||||||
- name: Resolve version
|
|
||||||
id: ver
|
|
||||||
run: echo "version=$(git describe --tags --abbrev=0 2>/dev/null || echo dev)" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- uses: docker/setup-qemu-action@v4
|
|
||||||
- uses: docker/setup-buildx-action@v4
|
|
||||||
|
|
||||||
- uses: docker/login-action@v4
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- uses: docker/build-push-action@v7
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
# Refresh the floating tag only — never overwrite a versioned release.
|
|
||||||
tags: torrentclaw/unarr:latest
|
|
||||||
build-args: |
|
|
||||||
VERSION=${{ steps.ver.outputs.version }}
|
|
||||||
# Force a fresh base pull so apk upgrade picks up new patches.
|
|
||||||
no-cache: true
|
|
||||||
|
|
||||||
- name: Scan image for fixable CVEs (gate)
|
|
||||||
uses: docker/scout-action@v1
|
|
||||||
with:
|
|
||||||
command: cves
|
|
||||||
image: torrentclaw/unarr:latest
|
|
||||||
only-severities: critical,high
|
|
||||||
only-fixed: true
|
|
||||||
exit-code: true
|
|
||||||
52
.github/workflows/pages.yml
vendored
52
.github/workflows/pages.yml
vendored
|
|
@ -1,52 +0,0 @@
|
||||||
name: Deploy install scripts to Pages
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
paths:
|
|
||||||
- install.sh
|
|
||||||
- install.ps1
|
|
||||||
- CNAME
|
|
||||||
- .nojekyll
|
|
||||||
- .github/workflows/pages.yml
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
pages: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: pages
|
|
||||||
cancel-in-progress: false
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
environment:
|
|
||||||
name: github-pages
|
|
||||||
url: ${{ steps.deployment.outputs.page_url }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: actions/configure-pages@v5
|
|
||||||
- name: Stage install scripts
|
|
||||||
run: |
|
|
||||||
mkdir -p _site
|
|
||||||
cp install.sh install.ps1 _site/
|
|
||||||
[ -f CNAME ] && cp CNAME _site/
|
|
||||||
touch _site/.nojekyll
|
|
||||||
# Also index page (humans landing)
|
|
||||||
cat > _site/index.html <<'HTML'
|
|
||||||
<!doctype html>
|
|
||||||
<html><head><meta charset=utf-8><title>unarr installer</title></head>
|
|
||||||
<body><h1>unarr CLI installer</h1>
|
|
||||||
<pre>Linux/macOS: curl -fsSL https://unarr.torrentclaw.com/install.sh | sh
|
|
||||||
Windows: irm https://unarr.torrentclaw.com/install.ps1 | iex</pre>
|
|
||||||
<p>Source: <a href="https://github.com/torrentclaw/unarr">github.com/torrentclaw/unarr</a></p>
|
|
||||||
</body></html>
|
|
||||||
HTML
|
|
||||||
- uses: actions/upload-pages-artifact@v3
|
|
||||||
with:
|
|
||||||
path: _site
|
|
||||||
- id: deployment
|
|
||||||
uses: actions/deploy-pages@v4
|
|
||||||
210
.github/workflows/release.yml
vendored
210
.github/workflows/release.yml
vendored
|
|
@ -1,210 +0,0 @@
|
||||||
name: Release
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- "v*"
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
release:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- uses: actions/setup-go@v6
|
|
||||||
with:
|
|
||||||
go-version-file: go.mod
|
|
||||||
|
|
||||||
- uses: goreleaser/goreleaser-action@v6
|
|
||||||
with:
|
|
||||||
version: "~> v2"
|
|
||||||
args: release --clean
|
|
||||||
env:
|
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
|
||||||
# Empty when RELEASE_SIGNING_PUBKEY variable is unset — goreleaser
|
|
||||||
# accepts it and the resulting binary disables signature checks
|
|
||||||
# (back-compat: pre-signing releases continue to update). Set
|
|
||||||
# RELEASE_SIGNING_PUBKEY (variable) + RELEASE_SIGNING_KEY (secret)
|
|
||||||
# to turn verification on.
|
|
||||||
RELEASE_SIGNING_PUBKEY: ${{ vars.RELEASE_SIGNING_PUBKEY }}
|
|
||||||
|
|
||||||
- name: Sign checksums.txt with ed25519
|
|
||||||
# Reference secrets.X directly — step-level env defined in this same
|
|
||||||
# step is unreliable to read from this step's own if: expression.
|
|
||||||
if: ${{ vars.RELEASE_SIGNING_PUBKEY != '' && secrets.RELEASE_SIGNING_KEY != '' }}
|
|
||||||
env:
|
|
||||||
RELEASE_SIGNING_KEY: ${{ secrets.RELEASE_SIGNING_KEY }}
|
|
||||||
RELEASE_TAG: ${{ github.ref_name }}
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
go run ./scripts/sign-checksums \
|
|
||||||
-key "$RELEASE_SIGNING_KEY" \
|
|
||||||
-in dist/checksums.txt \
|
|
||||||
-out dist/checksums.txt.sig
|
|
||||||
gh release upload "$RELEASE_TAG" dist/checksums.txt.sig --clobber
|
|
||||||
|
|
||||||
docker:
|
|
||||||
needs: release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v6
|
|
||||||
|
|
||||||
- name: Docker meta
|
|
||||||
id: meta
|
|
||||||
uses: docker/metadata-action@v6
|
|
||||||
with:
|
|
||||||
images: torrentclaw/unarr
|
|
||||||
tags: |
|
|
||||||
type=semver,pattern={{version}}
|
|
||||||
type=semver,pattern={{major}}.{{minor}}
|
|
||||||
type=raw,value=latest
|
|
||||||
|
|
||||||
- uses: docker/setup-qemu-action@v4
|
|
||||||
- uses: docker/setup-buildx-action@v4
|
|
||||||
|
|
||||||
- uses: docker/login-action@v4
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
|
|
||||||
- uses: docker/build-push-action@v7
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
push: true
|
|
||||||
platforms: linux/amd64,linux/arm64
|
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
|
||||||
build-args: |
|
|
||||||
VERSION=${{ github.ref_name }}
|
|
||||||
|
|
||||||
# CVE gate. Fails the release on FIXABLE critical/high only — unfixed
|
|
||||||
# upstream ffmpeg codec CVEs are accepted (see SECURITY.md), so the
|
|
||||||
# codec noise does not block. Runs post-push (image already published);
|
|
||||||
# a failure here flags that a fixable CVE slipped through.
|
|
||||||
- name: Scan image for fixable CVEs (gate)
|
|
||||||
uses: docker/scout-action@v1
|
|
||||||
with:
|
|
||||||
command: cves
|
|
||||||
image: torrentclaw/unarr:latest
|
|
||||||
only-severities: critical,high
|
|
||||||
only-fixed: true
|
|
||||||
exit-code: true
|
|
||||||
|
|
||||||
# Sync the Docker Hub repo description from DOCKERHUB.md. Non-fatal: a
|
|
||||||
# description-API auth hiccup must not undo a successful image push.
|
|
||||||
- name: Update Docker Hub description
|
|
||||||
uses: peter-evans/dockerhub-description@v4
|
|
||||||
continue-on-error: true
|
|
||||||
with:
|
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
||||||
repository: torrentclaw/unarr
|
|
||||||
readme-filepath: ./DOCKERHUB.md
|
|
||||||
short-description: "unarr — the single binary that replaces your *arr stack"
|
|
||||||
|
|
||||||
|
|
||||||
virustotal:
|
|
||||||
needs: release
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: vars.VT_ENABLED == 'true'
|
|
||||||
steps:
|
|
||||||
- name: Get release tag
|
|
||||||
id: tag
|
|
||||||
run: echo "tag=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Download release assets
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
mkdir -p assets
|
|
||||||
gh release download "${{ steps.tag.outputs.tag }}" \
|
|
||||||
--repo "${{ github.repository }}" \
|
|
||||||
--dir assets \
|
|
||||||
--pattern '*.tar.gz' \
|
|
||||||
--pattern '*.zip' \
|
|
||||||
--pattern 'checksums.txt'
|
|
||||||
|
|
||||||
- name: Scan assets with VirusTotal
|
|
||||||
env:
|
|
||||||
VT_API_KEY: ${{ secrets.VT_API_KEY }}
|
|
||||||
run: |
|
|
||||||
mkdir -p results
|
|
||||||
for file in assets/*; do
|
|
||||||
filename=$(basename "$file")
|
|
||||||
echo "Uploading $filename to VirusTotal..."
|
|
||||||
|
|
||||||
response=$(curl -s --request POST \
|
|
||||||
--url https://www.virustotal.com/api/v3/files \
|
|
||||||
--header "x-apikey: $VT_API_KEY" \
|
|
||||||
--form "file=@$file")
|
|
||||||
|
|
||||||
analysis_id=$(echo "$response" | jq -r '.data.id // empty')
|
|
||||||
if [ -z "$analysis_id" ]; then
|
|
||||||
echo "::warning::Failed to upload $filename: $response"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "$filename=$analysis_id" >> results/scans.txt
|
|
||||||
echo " Analysis ID: $analysis_id"
|
|
||||||
|
|
||||||
# Rate limit: VT free tier allows 4 req/min
|
|
||||||
sleep 16
|
|
||||||
done
|
|
||||||
|
|
||||||
- name: Wait for analysis completion
|
|
||||||
env:
|
|
||||||
VT_API_KEY: ${{ secrets.VT_API_KEY }}
|
|
||||||
run: |
|
|
||||||
echo "Waiting 60s for VirusTotal analysis to complete..."
|
|
||||||
sleep 60
|
|
||||||
|
|
||||||
vt_report="## 🛡️ VirusTotal Scan Results\n\n"
|
|
||||||
vt_report+="| File | Result | Link |\n"
|
|
||||||
vt_report+="|------|--------|------|\n"
|
|
||||||
|
|
||||||
while IFS='=' read -r filename analysis_id; do
|
|
||||||
result=$(curl -s --request GET \
|
|
||||||
--url "https://www.virustotal.com/api/v3/analyses/$analysis_id" \
|
|
||||||
--header "x-apikey: $VT_API_KEY")
|
|
||||||
|
|
||||||
malicious=$(echo "$result" | jq -r '.data.attributes.stats.malicious // 0')
|
|
||||||
undetected=$(echo "$result" | jq -r '.data.attributes.stats.undetected // 0')
|
|
||||||
sha256=$(echo "$result" | jq -r '.meta.file_info.sha256 // empty')
|
|
||||||
|
|
||||||
if [ "$malicious" = "0" ]; then
|
|
||||||
status="✅ Clean ($undetected engines)"
|
|
||||||
else
|
|
||||||
status="⚠️ $malicious detections"
|
|
||||||
fi
|
|
||||||
|
|
||||||
link="https://www.virustotal.com/gui/file/$sha256"
|
|
||||||
vt_report+="| \`$filename\` | $status | [View]($link) |\n"
|
|
||||||
|
|
||||||
sleep 16
|
|
||||||
done < results/scans.txt
|
|
||||||
|
|
||||||
echo -e "$vt_report" > results/report.md
|
|
||||||
cat results/report.md
|
|
||||||
|
|
||||||
- name: Append scan results to release notes
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
run: |
|
|
||||||
current_body=$(gh release view "${{ steps.tag.outputs.tag }}" \
|
|
||||||
--repo "${{ github.repository }}" \
|
|
||||||
--json body --jq '.body')
|
|
||||||
|
|
||||||
new_body="${current_body}
|
|
||||||
|
|
||||||
$(cat results/report.md)"
|
|
||||||
|
|
||||||
gh release edit "${{ steps.tag.outputs.tag }}" \
|
|
||||||
--repo "${{ github.repository }}" \
|
|
||||||
--notes "$new_body"
|
|
||||||
|
|
@ -59,6 +59,18 @@ changelog:
|
||||||
- "^test:"
|
- "^test:"
|
||||||
- "^chore:"
|
- "^chore:"
|
||||||
|
|
||||||
|
# Self-hosted Forgejo at git.torrentclaw.com. goreleaser detects GITEA_TOKEN +
|
||||||
|
# these URLs and publishes the release there instead of GitHub. Reachable via
|
||||||
|
# `forgejo` hostname inside the dokploy-network (the runner shares it); for
|
||||||
|
# local goreleaser runs outside the network, override via env GITEA_API_URL.
|
||||||
|
release:
|
||||||
|
gitea_urls:
|
||||||
|
api: http://forgejo:3000/api/v1
|
||||||
|
download: https://git.torrentclaw.com
|
||||||
|
skip_tls_verify: false
|
||||||
|
draft: false
|
||||||
|
prerelease: auto
|
||||||
|
|
||||||
# Homebrew tap — requires PAT with repo scope (not GITHUB_TOKEN)
|
# Homebrew tap — requires PAT with repo scope (not GITHUB_TOKEN)
|
||||||
# Enable when torrentclaw/homebrew-tap PAT is configured as HOMEBREW_TAP_TOKEN
|
# Enable when torrentclaw/homebrew-tap PAT is configured as HOMEBREW_TAP_TOKEN
|
||||||
# brews:
|
# brews:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue