From c84d5d221009716bbab333c3e33b88b3c6f9e1ab Mon Sep 17 00:00:00 2001 From: Admin <1+admin@noreply.git.dcntrlized.org> Date: Wed, 7 Oct 2026 02:25:16 +0100 Subject: [PATCH] ci: replace the license workflow with a read-only advisory check The license bot service (Dcntrlized/license-bot) is authoritative: it amends missing AGPL headers into the PR head commit, including for fork PRs, and reports the license-bot / addlicense commit status. This workflow now only reports an honest advisory signal, holds no secrets and never writes. --- .gitea/workflows/license.yml | 200 ++++++++++++----------------------- 1 file changed, 67 insertions(+), 133 deletions(-) diff --git a/.gitea/workflows/license.yml b/.gitea/workflows/license.yml index 77792f7..f71b485 100644 --- a/.gitea/workflows/license.yml +++ b/.gitea/workflows/license.yml @@ -1,25 +1,22 @@ name: license-headers -# Enforce AGPL-3.0 headers on every file ADDED by a pull request: amend the PR -# head commit with the headers and comment the new commit hash on the PR. +# Advisory license check. # -# `runs-on` must match a registered runner label. The org's only runner offers -# `alpine-latest` (container `node:alpine`), so jobs asking for anything else -# (e.g. `alpine-3.24`) queue forever — that is why this bot never ran before. -# -# No long-lived secret is needed or wanted here: Gitea mints a per-run token -# (`secrets.GITHUB_TOKEN`) scoped to this repository, which is enough to amend -# the PR branch and comment. Never add a PAT/SSH key — an action that runs on -# PRs (including forks) must stay keyless, and Gitea withholds repo/org secrets -# from fork PRs anyway. +# The Dcntrlized license bot (the webhook service in this repository) is the +# authority: it amends the missing AGPL headers into the pull request's head +# commit and reports the `license-bot / addlicense` commit status. This workflow +# duplicates the check read-only so a pull request still shows an honest signal +# when the bot is unavailable. It holds no secrets, never writes, and must not +# be made a required check: Gitea evaluates a pull request's workflows from the +# head branch, so any context this job reports can be reproduced by a +# contributor who edits the workflow. on: pull_request: types: [opened, synchronize, reopened, ready_for_review] permissions: - contents: write - issues: write + contents: read pull-requests: read concurrency: @@ -32,143 +29,80 @@ defaults: jobs: addlicense: + name: addlicense runs-on: alpine-latest steps: - - name: Install prerequisites + - name: Prerequisites run: apk add --no-cache git curl ca-certificates jq - - name: Checkout pull request head branch + - name: Install addlicense + run: | + set -e + curl -fsSL -o /tmp/addlicense.tgz \ + https://github.com/google/addlicense/releases/download/v1.2.0/addlicense_v1.2.0_Linux_x86_64.tar.gz + tar -xzf /tmp/addlicense.tgz -C /tmp + install -m 0755 /tmp/addlicense /usr/local/bin/addlicense + + - name: Check added files for an AGPL header env: TOKEN: ${{ secrets.GITHUB_TOKEN }} + SERVER: ${{ gitea.server_url }} + REPO: ${{ gitea.repository }} + NUM: ${{ gitea.event.pull_request.number }} + HEAD_REPO: ${{ gitea.event.pull_request.head.repo.full_name }} + HEAD_REF: ${{ gitea.event.pull_request.head.ref }} run: | set -e - REF="${{ gitea.event.pull_request.head.ref }}" - HEAD_REPO="${{ gitea.event.pull_request.head.repo.full_name }}" - BASE_REPO="${{ gitea.repository }}" - if [ "$HEAD_REPO" != "$BASE_REPO" ]; then - echo "Fork PR ($HEAD_REPO -> $BASE_REPO): the bot cannot push to a fork; skipping." - echo "fork_pr=true" >> "$GITHUB_ENV" - exit 0 - fi - # Keep the scheme: the dev Gitea is HTTP-only, so a hardcoded https:// - # here fails with "TLS connect error: wrong version number". - SERVER="${{ gitea.server_url }}" - SERVER="${SERVER%/}" SCHEME="${SERVER%%://*}" HOST="${SERVER#*://}" - git clone --depth 1 --branch "$REF" \ - "${SCHEME}://oauth2:${TOKEN}@${HOST}/${BASE_REPO}.git" . - # Lease value for the later force push: the exact remote head we based on. - git ls-remote origin "refs/heads/${REF}" | cut -f1 > /tmp/head_before.txt - echo "checked out ${REF} at $(git rev-parse HEAD)" + api() { curl -fsS -H "Authorization: token $TOKEN" "$@"; } - - name: Install addlicense - if: env.fork_pr != 'true' - run: | - set -e - curl -sSL -o /tmp/addlicense.tar.gz \ - https://github.com/google/addlicense/releases/download/v1.2.0/addlicense_v1.2.0_Linux_x86_64.tar.gz - tar -xzf /tmp/addlicense.tar.gz -C /tmp - - - name: Add AGPL-3.0 headers to newly added files - if: env.fork_pr != 'true' - env: - TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - SERVER="${{ gitea.server_url }}" - SERVER="${SERVER%/}" - REPO="${{ gitea.repository }}" - PR="${{ gitea.event.pull_request.number }}" - - # Files added by this PR, straight from the API (no base history needed). - : > /tmp/added.txt + # Every file this pull request added (paginated). page=1 - while : ; do - body="$(curl -sS -H "Authorization: token ${TOKEN}" \ - "${SERVER}/api/v1/repos/${REPO}/pulls/${PR}/files?limit=100&page=${page}")" - n="$(printf '%s' "$body" | jq 'length')" - [ "$n" = "0" ] && break - printf '%s' "$body" | jq -r '.[] | select(.status=="added") | .filename' >> /tmp/added.txt - [ "$n" -lt 100 ] && break + : > /tmp/added.txt + while :; do + batch="$(api "$SERVER/api/v1/repos/$REPO/pulls/$NUM/files?limit=50&page=$page")" + printf '%s' "$batch" | jq -r '.[] | select(.status=="added") | .filename' >> /tmp/added.txt + [ "$(printf '%s' "$batch" | jq 'length')" -lt 50 ] && break page=$((page + 1)) done - # addlicense validates -ignore with doublestar and silently skips file - # types it does not understand, so pass every added file and let - # .licenseignore be the only escape hatch. - set -- -v -f .license-header.tpl -c "Dcntrlized" -y "$(date +%Y)" - if [ -f .licenseignore ]; then - while IFS= read -r line; do - case "$line" in ""|\#*) continue ;; esac - set -- "$@" -ignore "$line" - done < .licenseignore + if [ ! -s /tmp/added.txt ]; then + echo "This pull request adds no files; nothing to check." + exit 0 fi - found=0 - while IFS= read -r f; do - [ -n "$f" ] || continue - [ -f "$f" ] || continue - found=1 - /tmp/addlicense "$@" "$f" + + # The head may be a fork: the per-run token is read-only and can + # still clone it. The scheme comes from gitea.server_url because the + # instance may be plain HTTP. + git clone --quiet --depth 1 --branch "$HEAD_REF" \ + "$SCHEME://oauth2:$TOKEN@$HOST/$HEAD_REPO.git" /tmp/pr + cd /tmp/pr + + # .licenseignore holds doublestar globs, not prefixes. + set -- + while IFS= read -r pattern; do + case "$pattern" in ''|\#*) continue ;; esac + set -- "$@" -ignore "$pattern" + done < .licenseignore + + year="$(date +%Y)" + : > /tmp/missing.txt + while IFS= read -r file; do + [ -z "$file" ] && continue + [ -e "$file" ] || continue + if ! addlicense -check -f .license-header.tpl -c Dcntrlized -y "$year" "$@" "$file" >/dev/null 2>&1; then + printf ' - %s\n' "$file" >> /tmp/missing.txt + fi done < /tmp/added.txt - [ "$found" = "1" ] || echo "No newly added files." - if git diff --quiet; then - echo "no_changes=true" >> "$GITHUB_ENV" - else - echo "no_changes=false" >> "$GITHUB_ENV" + if [ -s /tmp/missing.txt ]; then + echo "::error::This pull request adds files without an AGPL-3.0 header:" + cat /tmp/missing.txt + echo "" + echo "The license bot normally amends these into your commit automatically." + echo "If it did not, copy the header from .license-header.tpl and push again." + exit 1 fi - - name: Amend the commit and force-push - if: env.fork_pr != 'true' && env.no_changes == 'false' - env: - TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - REF="${{ gitea.event.pull_request.head.ref }}" - SERVER="${{ gitea.server_url }}" - SERVER="${SERVER%/}" - SCHEME="${SERVER%%://*}" - HOST="${SERVER#*://}" - git config user.name "Dcntrlized Auto CopyLeft Bot" - git config user.email "copyleft+bot@dcntrlized.org" - git add -A - git commit --amend --no-edit - SHA="$(git rev-parse HEAD)" - printf '%s\n' "$SHA" > /tmp/commit_sha.txt - echo "amended to $SHA" - BEFORE="$(cat /tmp/head_before.txt)" - git push --force-with-lease="refs/heads/${REF}:${BEFORE}" \ - "${SCHEME}://oauth2:${TOKEN}@${HOST}/${{ gitea.repository }}.git" \ - "HEAD:refs/heads/${REF}" - - - name: Comment on the pull request - if: env.fork_pr != 'true' && env.no_changes == 'false' - env: - TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - set -e - SERVER="${{ gitea.server_url }}" - SERVER="${SERVER%/}" - REPO="${{ gitea.repository }}" - PR="${{ gitea.event.pull_request.number }}" - SHA="$(cat /tmp/commit_sha.txt)" - cat > /tmp/pr-comment.md <