Set up Forgejo CI/CD policy
ober
6f60d7bbf0ea7bb4bb138319a893dd9fab1fe946
new file mode 100755 --- /dev/null +++ b/.forgejo/ci-required.sh @@ -0,0 +1,68 @@ +#!/bin/sh +set -eu + +has_target() { + target=$1 + [ -f Makefile ] && grep -Eq "^${target}[[:space:]]*:" Makefile +} + +if has_target verify; then + make verify +else + ran=0 + for target in security test check build; do + if has_target "$target"; then + make "$target" + ran=1 + fi + done + [ "$ran" = 1 ] || { + echo "ERROR: no verify, test, check, or build target is available" >&2 + exit 1 + } +fi + +if ! has_target binary; then + echo "No standalone binary target; full repository verification passed." + exit 0 +fi + +make binary + +if has_target binary-smoke; then + make binary-smoke + exit 0 +fi +if has_target smoke; then + make smoke + exit 0 +fi + +binary_list=$(mktemp) +trap 'rm -f "$binary_list"' EXIT HUP INT TERM +find . -maxdepth 2 -type f -perm -111 \ + ! -path './.git/*' ! -path './.jerboa/*' ! -path './vendor/*' \ + ! -path './test/*' ! -path './tests/*' \ + -exec file {} \; | + awk -F: '/(ELF .*executable|Mach-O .*executable)/ { print $1 }' > "$binary_list" + +[ -s "$binary_list" ] || { + echo "ERROR: make binary succeeded but produced no runnable ELF executable" >&2 + exit 1 +} + +while IFS= read -r binary; do + echo "Smoke-checking $binary" + if timeout 30 env QT_QPA_PLATFORM=offscreen \ + QTWEBENGINE_CHROMIUM_FLAGS=--disable-gpu "$binary" --version >/dev/null 2>&1; then + continue + fi + if timeout 30 env QT_QPA_PLATFORM=offscreen \ + QTWEBENGINE_CHROMIUM_FLAGS=--disable-gpu "$binary" --help >/dev/null 2>&1; then + continue + fi + echo "ERROR: $binary failed both --version and --help runtime smoke checks" >&2 + exit 1 +done < "$binary_list" + +echo "Full verification, binary build, and runtime smoke checks passed." new file mode 100755 --- /dev/null +++ b/.forgejo/require-version-bump.sh @@ -0,0 +1,50 @@ +#!/bin/sh +set -eu + +version_file=${VERSION_FILE:-VERSION} +test -f "$version_file" || { + echo "ERROR: $version_file is required" >&2 + exit 1 +} + +new_version=$(tr -d '[:space:]' < "$version_file") +printf '%s\n' "$new_version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || { + echo "ERROR: VERSION must be semantic MAJOR.MINOR.PATCH, got: $new_version" >&2 + exit 1 +} + +if [ -f jpkg.sexp ]; then + manifest_version=$(awk -F'"' '/\(version "/ { print $2; exit }' jpkg.sexp) + [ "$manifest_version" = "$new_version" ] || { + echo "ERROR: jpkg.sexp version $manifest_version must match VERSION $new_version" >&2 + exit 1 + } +fi + +if [ "${FORGEJO_EVENT_NAME:-}" != pull_request ]; then + echo "VERSION $new_version is valid" + exit 0 +fi + +base_ref=${FORGEJO_BASE_REF:?FORGEJO_BASE_REF is required for pull requests} +old_version=$(git show "origin/$base_ref:$version_file" 2>/dev/null | tr -d '[:space:]' || true) +old_version=${old_version:-0.0.0} +printf '%s\n' "$old_version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$' || { + echo "ERROR: target branch VERSION is malformed: $old_version" >&2 + exit 1 +} + +if ! awk -F. -v old="$old_version" -v new="$new_version" 'BEGIN { + split(old, o, ".") + split(new, n, ".") + for (i = 1; i <= 3; i++) { + if ((n[i] + 0) > (o[i] + 0)) exit 0 + if ((n[i] + 0) < (o[i] + 0)) exit 1 + } + exit 1 +}'; then + echo "ERROR: VERSION must advance beyond $old_version; got $new_version" >&2 + exit 1 +fi + +echo "VERSION advances: $old_version -> $new_version" new file mode 100644 --- /dev/null +++ b/.forgejo/workflows/ci.yaml @@ -0,0 +1,25 @@ +name: required-ci + +on: + pull_request: + branches: [main] + push: + branches: [main] + workflow_dispatch: + +jobs: + required: + runs-on: docker + container: + image: debian:stable + steps: + - run: apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git jq + - uses: https://code.forgejo.org/actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + persist-credentials: false + - name: Validate registry metadata + run: | + git diff --check + for file in metadata/*.json publishers.json transparency.json; do + jq empty "$file" + done new file mode 100644 --- /dev/null +++ b/.forgejo/workflows/version-policy.yaml @@ -0,0 +1,23 @@ +name: version-policy + +on: + pull_request: + branches: [main] + +jobs: + required: + runs-on: docker + container: + image: debian:stable + steps: + - name: Install Git + run: | + apt-get update + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends git ca-certificates + - name: Check out full history + uses: https://code.forgejo.org/actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6 + with: + fetch-depth: 0 + persist-credentials: false + - name: Require semantic version advancement + run: sh .forgejo/require-version-bump.sh new file mode 100644 --- /dev/null +++ b/.gitsafeignore @@ -0,0 +1,2 @@ +.forgejo/workflows/ci.yaml:high-entropy-hex:17 +.forgejo/workflows/version-policy.yaml:high-entropy-hex:18 new file mode 100644 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,17 @@ +## STOP: Forgejo Pull Requests Are Mandatory + +Every change to this repository must use the Forgejo pull-request workflow. + +1. Start from the current remote default branch and create a dedicated feature, fix, or chore branch **before editing**. +2. Make only the scoped changes on that branch. +3. Run every repository-required test and build. If the repository produces binaries, build them and run a meaningful smoke check (such as the documented startup, `--help`, or `--version`). Do not commit while any required check fails. +4. Commit the verified changes on the feature branch and push that branch to `origin`. +5. Open a pull request on `git.jerboa.sh` targeting the default branch. A human must review, approve, and merge it. + +Absolute bans: never commit or push directly to `main` or `master`; never self-approve or self-merge; never bypass branch protection; and never leave completed changes only in a local branch. Release work and urgent fixes follow the same branch-and-PR process. + +### Every PR Must Advance the Version + +`VERSION` is the authoritative repository version. Every pull request must change it to a strictly greater semantic version (`MAJOR.MINOR.PATCH`). Use a patch increment for fixes and maintenance, a minor increment for backward-compatible features, and a major increment for breaking changes. Keep package manifests, generated version constants, release artifact names, and user-visible version output synchronized with `VERSION`. + +Forgejo CI compares the proposed `VERSION` with the target branch and rejects an unchanged, malformed, or lower version. new file mode 100644 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0