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:
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: ["1.25"]
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/library/golang:1.25
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Run tests
|
||||
run: go test -v -race -count=1 ./...
|
||||
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/library/golang:1.25
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [linux, darwin, windows]
|
||||
goarch: [amd64, arm64]
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25"
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
|
|
@ -50,30 +41,30 @@ jobs:
|
|||
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/library/golang:1.25
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25"
|
||||
- name: Install golangci-lint
|
||||
run: |
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/v2.11.4/install.sh \
|
||||
| sh -s -- -b /usr/local/bin v2.11.4
|
||||
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/golangci-lint-action@v9
|
||||
with:
|
||||
version: v2.11.4
|
||||
run: golangci-lint run ./...
|
||||
|
||||
coverage:
|
||||
name: Coverage
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/library/golang:1.25
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25"
|
||||
- name: Install python3
|
||||
run: apt-get update && apt-get install -y --no-install-recommends python3
|
||||
|
||||
- name: Run tests with coverage (all packages)
|
||||
run: |
|
||||
|
|
@ -102,24 +93,13 @@ jobs:
|
|||
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:
|
||||
name: Vet
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: docker
|
||||
container:
|
||||
image: docker.io/library/golang:1.25
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v6
|
||||
with:
|
||||
go-version: "1.25"
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 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:"
|
||||
- "^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)
|
||||
# Enable when torrentclaw/homebrew-tap PAT is configured as HOMEBREW_TAP_TOKEN
|
||||
# brews:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue