gitea-pipelines

Reusable Gitea Actions workflows and the central Renovate configuration for the repos on scm.cabillot.eu.

This repo is public so the Actions runner can clone it for reusable workflow calls. No secret is stored here — they come from org-level secrets on the calling repo's org.

Contents

Path Role
config.json Global Renovate config (placeholders only, resolved at runtime)
.gitea/workflows/renovate.yaml Reusable Renovate runner (workflow_call)
.gitea/workflows/validate.yaml Reusable renovate-config-validator check for a consumer's renovate.json
.gitea/workflows/gitea-release.yaml Reusable "create the Gitea release for a tag" (fills the Release Notes of Renovate bump PRs)
.gitea/workflows/release-auto.yaml Reusable "tag the next version of the calling repo — only when the commits ship something" (conventional-commit gate)
.gitea/workflows/release.yaml Releases this repo: next vX.Y.Z tag + Gitea release
scripts/gitea-release.sh Implementation behind gitea-release.yaml (also usable locally)

Pinning: always @vX.Y.Z, never @main

A consumer references a workflow at a version tag:

uses: perso/gitea-pipelines/.gitea/workflows/renovate.yaml@vX.Y.Z

@main is a moving target: one push and every project using the pipeline changes at the same instant, with no review and no way to roll one project back on its own. Pinning inverts that: a pipeline change reaches a project only through a Renovate bump PR that its owner merges.

Renovate owns those refs. The github-actions manager parses .gitea/workflows/*.yaml and resolves owner/repo/.gitea/workflows/x.yaml@ref against Gitea's tag list (gitea-tags datasource, the instance endpoint from config.json). No custom manager is needed.

What a bump PR looks like:

  • title ci(pipelines): update perso/gitea-pipelines to vX.Y.Z — the auto-merge cron only matches explicit chore(deps) titles, so it never merges a pipelines bump on its own;
  • one PR per repo, both refs (renovate.yaml and tag.yaml) in it;
  • its "Release Notes" section is the body of that version's Gitea release, i.e. the actual commits — not a bare compare link;
  • a major bump always lands in its own PR (gitea-pipelines (major)), so the migration is reviewed separately from a routine bump;
  • no release-age soak on these refs: the global minimumReleaseAge is overridden to 0 days for perso/gitea-pipelines — the tag is published deliberately by the owner on this very repo, not by an upstream project, so waiting two days only hides a change that is already known. The review gate is the PR itself, which is still merged by hand.

What counts as major / minor / patch

Bump Changes
major an input is removed or renamed, a default changes meaning, a job disappears, a new secret becomes required, the runtime behaviour of an existing call changes
minor new optional input, new job, new workflow file
patch fixes, docs, comments, internal refactors with no interface change

The # pipelines-version marker

renovate.yaml and gitea-release.yaml read files from this repo at runtime (the Renovate config, the release script). Those reads are pinned to the same tag as the workflow itself:

        with:
          repository: perso/gitea-pipelines
          ref: vX.Y.Z # pipelines-version

Without that pin a consumer would run a workflow frozen at a tag while the config it executes kept sliding with main — half the pipeline would still be unversioned. The marker is written by the release job; never edit that line by hand (the release job refuses to publish if a marker is missing or if it finds fewer than two of them).

There is deliberately no moving vN alias tag: it would reproduce @main with extra steps.

Releasing a new version

Actions → Release → Run workflow, with bump = patch | minor | major (dry_run prints the computed version and the stamped refs without changing anything).

The job:

  1. computes the next version from the highest existing vX.Y.Z tag (the first release is v1.0.0, whatever the input says);
  2. stamps # pipelines-version refs;
  3. commits chore(release): vX.Y.Z, tags it, and pushes the tag first, then main — if the branch push fails, the published version still exists and main stays on the previous one, which is the safe side;
  4. creates the Gitea release on that tag (same script the image repos use), so the Renovate bump PR carries a readable changelog.

Adopting Renovate in a repo

Add .gitea/workflows/renovate.yaml to the repo:

name: Renovate

on:
  schedule:
    - cron: '0 0 */2 * *'
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  renovate:
    uses: perso/gitea-pipelines/.gitea/workflows/renovate.yaml@vX.Y.Z
    secrets: inherit

Then Renovate scans every 2 days, re-runs on every push to the default branch (to rebase stale PRs), and can be dispatched manually.

Per-repo overrides

Add a renovate.json at the root of the repo; it is merged with the global config. To have it validated on every PR, call the reusable check:

jobs:
  renovate-config:
    uses: perso/gitea-pipelines/.gitea/workflows/validate.yaml@vX.Y.Z

Required secrets

Set as org-level secrets in the Gitea org of the consumer repo:

Secret Purpose
RENOVATE_TOKEN Gitea API token for the renovate bot
RENOVATE_GITHUB_TOKEN GitHub token for GitHub-hosted dependencies
RENOVATE_SSH_KEY SSH private key for git operations (optional)
SA_TOKEN_ACTION_PUSH_TAGS Token used by gitea-release.yaml to push tags and create releases (already org-wide for the image repos)

A called reusable workflow receives no secret unless the caller adds secrets: inherit — Gitea does not inherit them, not even same-repo.

Release notes for image repos (jcabillot/*)

Renovate copies a dependency's Gitea release body into the bump PR under "Release Notes". An image repo that only pushes git tags produces an empty section with a bare "Compare Source" link.

gitea-release.yaml fixes that at the source: called from the image repo's tag.yaml, after the image push and before the Renovate dispatch (Renovate reads the release while it builds the PR body):

  release:
    needs: [build-push]
    uses: perso/gitea-pipelines/.gitea/workflows/gitea-release.yaml@vX.Y.Z
    with:
      image: jcabillot/<repo>
    secrets: inherit

  trigger-renovate:
    needs: [build-push, release]

The body is the commit list since the previous tag, grouped by conventional type (features / fixes / refactor & perf / deps / CI & chores / docs), with links to each commit, the full diff, and the image reference. scripts/gitea-release.sh is idempotent: re-running for an existing tag updates that release instead of creating a duplicate.

Backfilling an existing tag needs no change in the image repo: dispatch the Gitea Release workflow with repository=<org>/<repo>, tag=<tag> and image=jcabillot/<repo> — or run the script locally with GITEA_URL, GITEA_TOKEN, GITEA_REPO, TAG and IMAGE.

Deciding whether to release at all (release-auto.yaml)

The tag is the single trigger of an image repo's chain: tag.yaml → build-push → Gitea release → Renovate dispatch. Tagging a ci:-only change therefore rebuilds an identical image, publishes a release and opens a bump PR for it.

release-auto.yaml moves the decision in front of the tag. It reads the commits since the highest reachable vX.Y.Z tag of the calling repo and, in auto mode:

Commits since the last tag Result
<type>! or a BREAKING CHANGE: footer major
feat minor
fix, perf, revert patch
chore(deps), build(deps) patch (the shipped image does change)
ci:, chore:, docs:, test:, build:, refactor:, style:, merge commits no release — green, no tag created

fix(ci): counts as CI-only, and chore(deps-dev) does not ship, so neither triggers a release. A repo with no vX.Y.Z tag yet gets v0.0.1 when the first dispatch finds a releasable commit (a ci-only history still skips it — force with bump). An explicit bump: patch|minor|major forces a release whatever the commits say — the escape hatch for a change whose commit message understates it.

Consumer workflow, one per image repo:

name: Release

on:
  workflow_dispatch:
    inputs:
      bump:
        description: "auto | patch | minor | major"
        type: choice
        options: [auto, patch, minor, major]
        default: auto
      dry_run:
        description: "Print the decision, create no tag"
        type: boolean
        default: false

jobs:
  release:
    uses: perso/gitea-pipelines/.gitea/workflows/release-auto.yaml@vX.Y.Z
    with:
      bump: ${{ inputs.bump }}
      dry_run: ${{ inputs.dry_run }}
    secrets: inherit

Then Actions → Release → Run workflow on the image repo. dry_run prints the computed version and the reason without touching a ref. The tag it creates fires the repo's own tag.yaml, which builds, releases and dispatches Renovate — release-auto.yaml creates the ref and nothing else. It is idempotent: a second dispatch on an already released version does not fail.

S
Description
Central Pipeline configuration
Readme
160 KiB
0 Stars 5 Watchers 0 Forks
v1.1.2
Latest
2026-10-21 14:13:41 -04:00
Languages
Shell 100%