Prepare the Gitea move: measured CI deviations, self-hosted Renovate, issue migration script

Groundwork for #225, built on a throwaway pull request against the Gitea
instance rather than on assumptions. The move itself is not part of this
change - no Actions are switched off, no mirror is enabled, no branch
protection is touched.

What the probe run measured, and what follows from it:

- Which jobs need a Docker socket: only the install-test. Measured per
  job - detect-changed-charts, build-chart, the quantity render and the
  Trivy config scan all pass on `ubuntu-latest`, which on that instance
  has no socket. The install-test dies there at "Create k3d cluster"
  with "Cannot connect to the Docker daemon", an error naming k3d and
  never the runner label. Its `runs-on` is now an expression: the label
  `ubuntu-docker` on Gitea, `ubuntu-latest` on GitHub, because the
  former does not exist there and a chart PR would otherwise queue
  forever during the handover. Gitea evaluates it - the probe job was
  taken by the docker runner.

- The install-test still cannot pass on Gitea. That daemon is rootless
  with "Cgroup Driver: none"; the k3s node restarts immediately with an
  empty log because k3s needs cgroup delegation. The label is necessary
  and not sufficient, and the fix is the maintainer's.

- aquasecurity/setup-trivy cannot be used on Gitea: it checks out
  aquasecurity/trivy from github.com with `github.token`, which there is
  a Gitea token, and ends on "could not read Username for
  https://github.com". Replaced by the release tarball - one request,
  same behaviour on both forges, green on the probe run.

Also in this change:

- .github/workflows/renovate.yml, Renovate in self-operation: a daily
  scheduled run in the official container, inert on github.com via a
  `github.server_url` guard. Every rule proved by dry run instead of
  assumed - the pinned Renovate image is tracked by the built-in
  github-actions manager (44.80.0 -> 44.87.1, minor), TRIVY_VERSION is
  still tracked after setup-trivy is gone (v0.72.0 -> v0.74.0, minor),
  and `ubuntu-docker` is not extracted by the github-runners manager at
  all, so nothing will quietly "correct" it.

- ci/migrate-issues-to-gitea.sh, runnable, with --dry-run, and NOT run.
  Its header names what it cannot do: comment authorship, the #<n>
  references between issues, and attachments.

- CLAUDE.md and README.md on the new flow: which forge and which tool,
  auto-merge on green checks in place of the merge queue Gitea does not
  have, and the measured differences of the two CIs.

Refs #225 - deliberately no "Fixes": the move is not finished with this.
This commit is contained in:
jklein committed 2026-09-14 17:07:31 +02:00
1 parent 055d9f6a31
commit a6c27ebef3
6 files changed
+469 -17

No files matched your search

+108
View File
@@ -0,0 +1,108 @@
# Renovate, self-hosted (issue #225).
#
# On github.com Renovate arrived as the `jlab-renovate` GitHub App and this
# repo only ever carried renovate.json5. Gitea has no app model and no hosted
# Renovate, so after the move the bot has to be *run* by this repo: a scheduled
# job with its own token. That is the only change to how dependencies are
# updated - renovate.json5 stays the single source of what gets updated, and
# everything in it works unchanged on the Gitea platform.
#
# What the maintainer has to create once on the instance (the token this
# workflow cannot create for itself):
#
# RENOVATE_TOKEN Gitea access token of the bot account, scopes
# read:user, write:issue, write:repository. Renovate opens
# the PRs and the dependency dashboard issue with it, so
# the PRs will be authored by whoever owns the token.
# GITHUB_COM_TOKEN Read-only github.com PAT (public_repo / no scopes at all
# is enough). NOT optional here: every `github-releases` and
# `github-tags` lookup in renovate.json5 - and the whole
# built-in github-actions manager, which resolves
# actions/checkout and friends on github.com - hits
# github.com's unauthenticated rate limit within one run
# otherwise. On the App this came for free; here it does not.
# README already names this for the `no-result` symptom.
#
# There is deliberately no `allowedCommands` to set up by hand any more: the
# postUpgradeTasks hook renovate.json5 needs (ci/renovate-bump-chart.sh, which
# recomputes `version:` after an appVersion change) is allow-listed by the
# RENOVATE_ALLOWED_COMMANDS env below, because this workflow *is* the instance
# config now. Without it Renovate skips the task silently and every chart PR
# stays red on the version-bump check - see README, "The version-bump hook".
name: Renovate
on:
schedule:
# 03:40 UTC, once a day. Renovate's own `timezone` in renovate.json5
# (Europe/Berlin) governs the PR schedules; this cron is plain UTC.
- cron: "40 3 * * *"
workflow_dispatch:
inputs:
dryRun:
description: "Dry run: 'full' looks up and renders everything but writes nothing"
type: choice
default: "full"
options: ["full", "lookup", "extract", "disabled"]
logLevel:
description: "Renovate log level"
type: choice
default: "info"
options: ["info", "debug"]
jobs:
renovate:
name: Renovate
# The guard that keeps this workflow inert on github.com. The file reaches
# GitHub as well (that is where the migration PR is merged), and a
# scheduled run there would authenticate a *Gitea* token against a Gitea
# endpoint from a runner that has neither - failing nightly for a reason
# nobody is watching for. GITHUB_SERVER_URL is the one context value that
# differs between the two platforms without naming our instance: Gitea
# sets it to its own base URL. Delete this line once github.com is a
# push mirror without Actions.
if: github.server_url != 'https://github.com'
# No Docker socket needed: the Gitea runner starts every job in a container
# itself, and `container:` is the runner's job to create, not the job's.
# Measured on the probe PR of #225 - the same distinction that puts
# install-test on ubuntu-docker leaves this job on ubuntu-latest.
runs-on: ubuntu-latest
# The official image rather than `npx renovate`: it ships the git, node and
# package-manager versions Renovate expects, and pulling ~200 MB from npm
# on every scheduled run is the alternative. Renovate's built-in
# github-actions manager keeps this tag current (proved by dry run, see the
# migration PR) - it reads `container:` images, not only `uses:`.
container: ghcr.io/renovatebot/renovate:44.87.1
steps:
- name: "Checkout Repository"
uses: actions/checkout@v7
- name: "Run Renovate"
env:
# Platform. The endpoint is derived from the run itself so the
# instance address stays out of this repository, the same rule the
# publish workflow follows for the registry URL.
RENOVATE_PLATFORM: gitea
RENOVATE_ENDPOINT: ${{ github.server_url }}/api/v1
RENOVATE_TOKEN: ${{ secrets.RENOVATE_TOKEN }}
GITHUB_COM_TOKEN: ${{ secrets.GITHUB_COM_TOKEN }}
# Exactly this repository. Autodiscovery would enrol every repo the
# token can see, and the token is an account token, not an app
# installation scoped to one repo.
RENOVATE_AUTODISCOVER: "false"
RENOVATE_REPOSITORIES: ${{ github.repository }}
# Who the commits are by. The e-mail must not be a real address of a
# person - Gitea attributes a commit to the account whose verified
# address matches, and these commits are the bot's.
RENOVATE_GIT_AUTHOR: "Renovate Bot <renovate-bot@noreply.invalid>"
# The instance-level half of renovate.json5's postUpgradeTasks. A
# command not listed here is skipped WITHOUT an error message.
RENOVATE_ALLOWED_COMMANDS: '["^ci/renovate-bump-chart\\.sh"]'
# Manual runs default to a dry run; the schedule is the only thing
# that writes on its own.
RENOVATE_DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dryRun || 'null' }}
LOG_LEVEL: ${{ github.event_name == 'workflow_dispatch' && inputs.logLevel || 'info' }}
run: renovate
+49 -5
View File
@@ -199,11 +199,27 @@ jobs:
uses: actions/checkout@v7
- name: "Install Helm"
uses: azure/setup-helm@v5
# A plain release download rather than aquasecurity/setup-trivy, and the
# reason is measured on the Gitea probe PR of #225, not assumed. That
# action runs actions/checkout against aquasecurity/trivy on github.com
# and hands it `github.token` - which on Gitea is a GITEA token.
# github.com answers 401, checkout retries twice and the job ends on
# "fatal: could not read Username for 'https://github.com': terminal
# prompts disabled", an error naming neither the action nor the token.
# Its actions/cache step is inert on the instance as well ("Cache action
# is only supported on GHES version >= 3.5"), and fetching the nested
# actions cost 4m30s before the job even started. One tarball behaves
# identically on both forges, and the `# renovate:` comment above still
# keeps TRIVY_VERSION current - verified by dry run: pinned to v0.72.0
# the lookup offers v0.74.0 as a minor update.
- name: "Install Trivy"
uses: aquasecurity/setup-trivy@v0.3.1
with:
version: ${{ env.TRIVY_VERSION }}
cache: true
run: |
curl -sSfL -o /tmp/trivy.tar.gz \
"https://github.com/aquasecurity/trivy/releases/download/${TRIVY_VERSION}/trivy_${TRIVY_VERSION#v}_Linux-64bit.tar.gz"
mkdir -p "$HOME/.local/bin"
tar -xzf /tmp/trivy.tar.gz -C "$HOME/.local/bin" trivy
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
"$HOME/.local/bin/trivy" --version
# Findings land in the job summary, not only in the log: exceptions with
# their reasons come from ci/trivy/trivyignore.yaml, the trusted-registry
# list from ci/trivy/data/ksv0125.yaml.
@@ -214,7 +230,35 @@ jobs:
name: "Install-test ${{ matrix.chart }}"
needs: [detect-changed-charts, build-chart]
if: needs.detect-changed-charts.outputs.any-install == 'true'
runs-on: ubuntu-latest
# The one job in this repo that does not simply say ubuntu-latest, and the
# expression is not decoration - the same label means different machines on
# the two forges this repo currently lives on (#225).
#
# This is the only job that needs a Docker socket: it creates a k3d
# cluster, and k3d is a Docker client. On GitHub's hosted runners every
# label carries Docker, so the question never came up. On the Gitea
# instance it was MEASURED on a probe pull request, per job:
# detect-changed-charts, build-chart, the quantity render and the Trivy
# scan all pass on `ubuntu-latest` - none of them needs Docker - while the
# install-test dies there at "Create k3d cluster" with "Cannot connect to
# the Docker daemon at unix:///var/run/docker.sock", an error that points
# at k3d and never at the runner label. `ubuntu-docker` is the label that
# carries a socket, and Gitea does evaluate this expression: the probe job
# was picked up by the docker runner with labels ["ubuntu-docker"].
#
# Why an expression and not a plain `ubuntu-docker`: that label does not
# exist on GitHub, so every chart PR would sit queued forever for as long
# as this repo still builds on github.com - blocking the merge with no
# error anywhere. github.server_url is the one context value that separates
# the two without naming our instance.
#
# KNOWN, and not fixed by this line: `ubuntu-docker`'s daemon is ROOTLESS
# and reports "Cgroup Driver: none" plus "Running in rootless-mode without
# cgroups". k3d then gets as far as starting the k3s node, which restarts
# immediately with an empty log, because k3s needs cgroup delegation. Until
# the instance's Docker-in-Docker provides it, this job cannot pass on
# Gitea - see #225. The label is necessary, it is just not sufficient.
runs-on: ${{ github.server_url == 'https://github.com' && 'ubuntu-latest' || 'ubuntu-docker' }}
# Blocking since Renovate automerges patch/minor updates: a chart that no
# longer starts must stop the PR before it reaches main and publishes. This
# ends the stabilization phase of issue #22 - the job reported failures