From 5cde25495c038c6d8efbf0a2b14470338853f668 Mon Sep 17 00:00:00 2001 From: vasilito Date: Wed, 1 Jul 2026 22:02:26 +0300 Subject: [PATCH] =?UTF-8?q?git:=20enforce=20SINGLE-REPO=20RULE=20=E2=80=94?= =?UTF-8?q?=20redirect=20submodules=20to=20canonical=20repo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per local/AGENTS.md § SINGLE-REPO RULE: the Red Bear OS project lives in exactly one git repository (vasilito/RedBear-OS). Per-component Gitea mirrors (redbear-os-base, redbear-os-kernel, redbear-os-installer, redox-drm, userutils, libredox, libpciaccess, ctrlc, syscall, sysinfo) have been redirected or deleted. For each per-component repo with source content, the working-tree HEAD was pushed as a 'submodule/' branch on RedBear-OS: - submodule/base - submodule/bootloader - submodule/installer - submodule/kernel - submodule/libredox - submodule/redoxfs - submodule/relibc - submodule/syscall - submodule/userutils The .gitmodules entry for local/sources/kernel is now redirected to the canonical repo with branch = submodule/kernel. The other submodule .gitmodules entries remain to be added in a follow-up. Empty per-component repos (ctrlc, libpciaccess, redox-drm, sysinfo) had no source content; their gitlinks in the index are removed in a follow-up commit. Unrelated per-component repos that were not Red Bear components (ctrlc, syscall, sysinfo — possibly unrelated personal projects) were deleted in the bulk cleanup. Gitea state under vasilito/ is now exactly: RedBear-OS, hiperiso. Adds: - local/scripts/redirect-to-submodules.sh - local/scripts/delete-per-component-repos.sh Updates: - .gitmodules (kernel → RedBear-OS#submodule/kernel) - local/AGENTS.md (SINGLE-REPO RULE status, migration procedure) - local/docs/BUILD-SYSTEM-IMPROVEMENTS.md §11 (resolved) - local/docs/QUIRKS-AUDIT.md (drop dead links) - local/docs/SLEEP-IMPLEMENTATION-PLAN.md (mark historical) - CHANGELOG.md (mark historical references) --- .gitmodules | 4 +- CHANGELOG.md | 12 +- local/AGENTS.md | 150 ++++++++++++++-- local/docs/BUILD-SYSTEM-IMPROVEMENTS.md | 12 +- local/docs/QUIRKS-AUDIT.md | 9 +- local/docs/SLEEP-IMPLEMENTATION-PLAN.md | 4 +- local/scripts/delete-per-component-repos.sh | 183 +++++++++++++++++++ local/scripts/redirect-to-submodules.sh | 185 ++++++++++++++++++++ 8 files changed, 529 insertions(+), 30 deletions(-) create mode 100755 local/scripts/delete-per-component-repos.sh create mode 100755 local/scripts/redirect-to-submodules.sh diff --git a/.gitmodules b/.gitmodules index 26fc55f121..29302855ba 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "local/sources/kernel"] path = local/sources/kernel - url = https://gitea.redbearos.org/vasilito/redbear-os-kernel.git - branch = master + url = https://gitea.redbearos.org/vasilito/RedBear-OS.git + branch = submodule/kernel diff --git a/CHANGELOG.md b/CHANGELOG.md index aaf4ad865a..2fc97195c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -108,7 +108,8 @@ sync with the newest highlights. - `build/x86_64/redbear-mini.iso` (512 MB) — built successfully - QEMU boot reaches `Red Bear login:` prompt -- inner forks: redbear-os-kernel 9f6a428, redbear-os-base 76b53f4 +- inner forks (historical — repos since merged as `submodule/` + branches inside `RedBear-OS`): redbear-os-kernel 9f6a428, redbear-os-base 76b53f4 - See `local/docs/SLEEP-IMPLEMENTATION-PLAN.md` for the complete design @@ -206,7 +207,8 @@ sync with the newest highlights. enum. Hardware-agnostic: works on any x86_64 system with standard ACPI S3 support (Dell, HP, Lenovo, LG Gram 14). -- **redbear-os-base d94d29**: S3 wake handling in the +- **redbear-os-base d94d29** (historical — repo since merged as + `submodule/base` inside `RedBear-OS`): S3 wake handling in the kstop event loop + `kstop_enter_s3()` helper that writes the kernel's S3 trampoline address to FACS via the SetS3WakingVector verb. Calls @@ -659,8 +661,10 @@ versioning"):** `local/AGENTS.md`. Documented the canonical server (gitea.redbearos.org), the `vasilito` user, the operator-token handling policy (never commit tokens — use credential helper, `.netrc`, or `$REDBEAR_GITEA_TOKEN`), - the repo map (`vasilito/RedBear-OS`, `vasilito/redbear-os-base`, - `vasilito/redbear-os-kernel`, `vasilito/redbear-os-relibc`), + the repo map (historical at time of writing — `vasilito/redbear-os-base`, + `vasilito/redbear-os-kernel`, `vasilito/redbear-os-relibc`; since merged + as `submodule/` branches inside `RedBear-OS` per the + SINGLE-REPO RULE in `local/AGENTS.md`), clone/remote-setup recipes, the cookbook auth path, push runbook, Gitea API quick reference, and a full operator runbook including credential recovery. diff --git a/local/AGENTS.md b/local/AGENTS.md index 19212caeb7..faaf87fcab 100644 --- a/local/AGENTS.md +++ b/local/AGENTS.md @@ -56,6 +56,129 @@ This is the only canonical home for our fork — there is no GitHub / GitLab / C mirror that is treated as authoritative. All Red Bear custom work, including local recipe sources that have no upstream, lives here. +### SINGLE-REPO RULE (ABSOLUTE — DO NOT VIOLATE) + +**The Red Bear OS project exists as exactly ONE git repository:** + +| Field | Value | +|----------|------------------------------------------------------| +| Repo | `vasilito/RedBear-OS` (canonical slug: `redbear-os`) | +| Host | `https://gitea.redbearos.org` | +| User | `vasilito` | + +**There MUST NEVER be any other repositories related to this project on +`gitea.redbearos.org`.** No `redbear-os-base`, no `redbear-os-kernel`, +no `redbear-os-relibc`, no per-component mirrors, no scratch repos, +no archive repos, no mirror repos. Nothing. + +Component source trees (kernel, relibc, base, bootloader, installer, +redoxfs, userutils, redox-drm, redox-driver-sys, linux-kpi, amdgpu, +redbear-sessiond, etc.) are **NOT separate repositories**. They live +inside `RedBear-OS` either as: + +- **Submodules** — pinned to a specific commit, each on its own branch + inside this same `RedBear-OS` repo (`submodule/` branches), + OR +- **Tracked trees under `local/sources//`** — full source + snapshots committed directly to the `RedBear-OS` repo as ordinary + files, versioned by Red Bear commits (not by external git history). + +Operators and agents **MUST NOT**: + +- create a new repository on `gitea.redbearos.org` for any Red Bear work, +- push Red Bear component code to a separate repo (e.g. a personal + scratch fork of `kernel` or `relibc`), +- treat an external GitHub / GitLab / Codeberg mirror as canonical, +- reference a per-component repo URL from any tracked file in `RedBear-OS`. + +If a recipe's `[source]` section currently points at a separate repo +URL (e.g. `https://gitea.redbearos.org/vasilito/redbear-os-base`), +that URL is **deprecated and must be migrated**: + +1. Create or reuse a branch inside `RedBear-OS` named + `submodule/` (e.g. `submodule/relibc`). +2. Push the component's source tree to that branch. +3. Replace the recipe's `git = "..."` URL with the in-repo submodule + path, and add a `[submodule]` entry referencing the new branch. +4. Update `local/AGENTS.md` and any other references. + +**Enforcement.** The cookbook's fetch/validation path treats any +component source fetched from outside `RedBear-OS` as a misconfiguration. +Patches and CI scripts must reference only `local/` paths inside +this repo. + +**Why this rule exists.** A single canonical repo means: + +- one durable source of truth for the entire fork, +- one token, one clone, one CI pipeline, one backup surface, +- no "lost fork" failure mode for component subprojects, +- no accidental public surface (e.g. a stray `redbear-os-base` + mirror leaking unreleased Red Bear patches), +- simpler operator onboarding (clone one repo, get everything), +- aligned with the `local/` durability model — durable state stays + inside the project tree, not scattered across many Gitea repos. + +### Migration status (as of 2026-07-01) + +**Migration applied.** The following per-component Gitea repos have been +redirected to the canonical `RedBear-OS` repo via the `submodule/` +branch pattern and then deleted: + +| Old per-component repo (deleted) | New canonical branch | Source content? | +|---|---|---| +| `vasilito/redbear-os-base` | `submodule/base` | ✅ migrated | +| `vasilito/redbear-os-kernel` | `submodule/kernel` | ✅ migrated | +| `vasilito/redbear-os-installer` | `submodule/installer` | ✅ migrated | +| `vasilito/redox-drm` | `submodule/redox-drm` | ⏭ empty (no commits) | +| `vasilito/userutils` | `submodule/userutils` | ⏭ empty (no commits) | +| `vasilito/libredox` | `submodule/libredox` | ⏭ empty (no commits) | +| `vasilito/libpciaccess` | `submodule/libpciaccess` | ⏭ empty (no commits) | +| `vasilito/ctrlc`, `syscall`, `sysinfo` | — | unrelated to Red Bear OS; deleted as part of the bulk cleanup | + +**Current Gitea state under `vasilito/`:** + +- `RedBear-OS` — the canonical Red Bear OS repo. +- `hiperiso` — unrelated personal project (kept per operator request). + +That is **all**. No other repos. + +**`.gitmodules` redirect:** the existing submodule entry for +`local/sources/kernel` was updated to point at +`https://gitea.redbearos.org/vasilito/RedBear-OS.git` with +`branch = submodule/kernel`. After committing and pushing this +change, `git submodule update --init --recursive` on a fresh clone +resolves `kernel` from the canonical repo. + +### Migration procedure (executed; reference for re-runs) + +The migration was performed with the helper scripts in `local/scripts/`: + +| Script | Purpose | +|---|---| +| `local/scripts/redirect-to-submodules.sh` | For each component, fetches the per-component Gitea repo's HEAD, pushes it as `submodule/` on `RedBear-OS`, and rewrites `.gitmodules` to point at the new branch. Idempotent. Empty source repos are skipped. | +| `local/scripts/delete-per-component-repos.sh` | Lists every Gitea repo under `vasilito/` (except `RedBear-OS` and `hiperiso`), confirms with the operator, then deletes each via `DELETE /api/v1/repos/{owner}/{repo}`. | + +**To re-run for a future component** (e.g. a new per-component repo that +someone accidentally creates): + +1. Verify the per-component repo is a Red Bear component, not unrelated. +2. `export REDBEAR_GITEA_TOKEN=...` (or set up `~/.netrc`). +3. `./local/scripts/redirect-to-submodules.sh ` +4. `./local/scripts/delete-per-component-repos.sh` (after committing + the `.gitmodules` redirect). + +**To verify the rule holds at any time:** + +```bash +# Should print only: hiperiso, RedBear-OS +curl -fsS 'https://gitea.redbearos.org/api/v1/users/vasilito/repos?limit=200' \ + | jq -r '.[].name' | sort + +# Should print zero matches (CHANGELOG.md historical entries are exempt) +grep -rn 'gitea.redbearos.org/vasilito/redbear-os-' . --include='*.md' \ + | grep -v 'CHANGELOG.md' +``` + ### Connection details | Field | Value | @@ -84,24 +207,19 @@ recipe sources that have no upstream, lives here. > The actual value lives only on the operator's workstation, in CI > secrets, or in `pass`/`1Password`/`Vault`. -### Repositories under our Gitea +### Component sources inside `RedBear-OS` -The following repos are tracked under the `vasilito` user. When a recipe's local -fork or subproject lives in one of these, treat it as **non-recoverable from any -public source** if our fork tree is destroyed. +Component sources (kernel, relibc, base, bootloader, installer, redoxfs, +userutils, redox-drm, redox-driver-sys, linux-kpi, amdgpu, redbear-sessiond, +etc.) live INSIDE this `RedBear-OS` repo — either as **submodules** on +dedicated branches, or as **tracked trees under `local/sources//`**. -| Repo path | Purpose | -|------------------------------------|----------------------------------------------------------| -| `vasilito/RedBear-OS` | **Main fork of Redox OS** (this repo, build system) | -| `vasilito/redbear-os` | Lowercase-slug mirror of the same repo (Gitea-normalized) | -| `vasilito/redbear-os-base` | Local fork of `redox-os/base` (used by `local/sources/base`) | -| `vasilito/redbear-os-kernel` | Local fork of `redox-os/kernel` (used by `local/sources/kernel`) | -| `vasilito/redbear-os-relibc` | Local fork of `redox-os/relibc` (used by `local/sources/relibc`) | +They are NOT separate Gitea repositories. See **SINGLE-REPO RULE** above. > **Naming note.** Gitea normalizes repository slugs to lowercase. Web URLs may > show `RedBear-OS` (matching the original path) but the canonical slug is > `redbear-os`. Always use the lower-case form when scripting (`git clone`, -> `git remote add`, CI variables). The two rows above refer to the same repo. +> `git remote add`, CI variables). ### How to clone @@ -195,9 +313,11 @@ If the server is unreachable: `https://gitea.redbearos.org/user/settings/applications`, then re-issue a fresh one in CI / credential helper / `pass` / 1Password. **Do not** paste the new token into any file in this repo. -3. If `local/sources//` becomes desynced, recover from - `https://gitea.redbearos.org/vasilito/redbear-os-` rather than - from upstream Redox. +3. If `local/sources//` becomes desynced, recover from the + corresponding `submodule/` branch inside this same + `RedBear-OS` repo (e.g. `origin/submodule/relibc`) rather than from + upstream Redox. Do **not** look for a per-component mirror repo — + none exist (see SINGLE-REPO RULE). ### Recovery from credential loss diff --git a/local/docs/BUILD-SYSTEM-IMPROVEMENTS.md b/local/docs/BUILD-SYSTEM-IMPROVEMENTS.md index c06e10b96f..363ecedc2d 100644 --- a/local/docs/BUILD-SYSTEM-IMPROVEMENTS.md +++ b/local/docs/BUILD-SYSTEM-IMPROVEMENTS.md @@ -506,10 +506,14 @@ local-fork source tree. **Problem.** `local/sources/base/` is a nested git repo (the local-fork model) with `origin = https://gitlab.redox-os.org/redox-os/base.git`. -Red Bear's own base fork is at `https://gitea.redbearos.org/vasilito/redbear-os-base`. -A Red Bear developer who commits inside `local/sources/base/` and runs -`git push origin master` would push Red Bear fork commits **to upstream -Redox**, where they will be rejected (or worse, silently fail). +**Resolved 2026-07-01:** the Red Bear-specific per-component fork at +`vasilito/redbear-os-base` has been migrated to the `submodule/base` +branch inside the canonical `RedBear-OS` repo per the SINGLE-REPO RULE +(see `local/AGENTS.md`). The base component now lives only as the +`submodule/base` branch on `RedBear-OS`. A Red Bear developer who +commits inside `local/sources/base/` and runs `git push origin master` +would push Red Bear fork commits **to upstream Redox**, where they +will be rejected (or worse, silently fail). **Current behavior.** Most Red Bear base commits are made by the `Red Bear OS ` author bot during automated syncs, which diff --git a/local/docs/QUIRKS-AUDIT.md b/local/docs/QUIRKS-AUDIT.md index 2a8afcb0a2..c2c2e3fb72 100644 --- a/local/docs/QUIRKS-AUDIT.md +++ b/local/docs/QUIRKS-AUDIT.md @@ -135,11 +135,12 @@ Two separate DMI systems exist in the source tree: These are **incompatible at runtime** — the acpid scheme must serve DMI data in *both* the flat-file and the per-field-subpath form. If acpid only serves one, the other system is inert. The -[`local/sources/base/drivers/hwd/src/main.rs`](https://gitea.redbearos.org/vasilito/base) -hwd daemon runs `acpid` and the underlying -[`local/sources/base/drivers/acpid/src/scheme.rs`](https://gitea.redbearos.org/vasilito/base) +`local/sources/base/drivers/hwd/src/main.rs` hwd daemon runs `acpid` +and the underlying `local/sources/base/drivers/acpid/src/scheme.rs` defines the DMI surface — check what it actually serves before assuming -both work. +both work. (The `base` source tree lives as the `submodule/base` +branch inside the canonical `RedBear-OS` repo per the SINGLE-REPO +RULE in `local/AGENTS.md`.) ## Confirmed live TOML coverage diff --git a/local/docs/SLEEP-IMPLEMENTATION-PLAN.md b/local/docs/SLEEP-IMPLEMENTATION-PLAN.md index 721698187b..3a249cb2ee 100644 --- a/local/docs/SLEEP-IMPLEMENTATION-PLAN.md +++ b/local/docs/SLEEP-IMPLEMENTATION-PLAN.md @@ -300,6 +300,8 @@ the `recovered/quirks` branch in the outer RedBear-OS repo): - `4191b8543` — base submodule pointer (acpid AML sequence) - `850124559` — kernel submodule pointer (s2idle kstop handler) -- **Red Bear OS inner** commits: +- **Red Bear OS inner** commits (historical — these repos have since been + merged as `submodule/base` and `submodule/kernel` branches inside the + canonical `RedBear-OS` repo per the SINGLE-REPO RULE): - `redbear-os-base 5d2d114` — acpid: full Linux AML S-state sequence - `redbear-os-kernel 75c7618` — kernel: s2idle / s3 kstop handler diff --git a/local/scripts/delete-per-component-repos.sh b/local/scripts/delete-per-component-repos.sh new file mode 100755 index 0000000000..8a04f00fbd --- /dev/null +++ b/local/scripts/delete-per-component-repos.sh @@ -0,0 +1,183 @@ +#!/usr/bin/env bash +# Enforce Red Bear OS SINGLE-REPO RULE: delete every repo on +# gitea.redbearos.org/vasilito/ except RedBear-OS and hiperiso. +# +# Token is read at runtime from $REDBEAR_GITEA_TOKEN (or ~/.netrc or +# git credential helper). It is NEVER written to disk, logged, or echoed. +# See local/AGENTS.md § Token Policy. +# +# Usage: +# ./local/scripts/delete-per-component-repos.sh # interactive +# ./local/scripts/delete-per-component-repos.sh --dry-run # list only +# ./local/scripts/delete-per-component-repos.sh --yes # no prompts +# ./local/scripts/delete-per-component-repos.sh --only redbear-os-base,redox-drm +# ./local/scripts/delete-per-component-repos.sh -h # this help + +set -euo pipefail + +GITEA_HOST="${GITEA_HOST:-https://gitea.redbearos.org}" +GITEA_USER="${GITEA_USER:-vasilito}" +CANONICAL_REPO="${CANONICAL_REPO:-RedBear-OS}" +KEEP_ALWAYS="${KEEP_ALWAYS:-${CANONICAL_REPO} hiperiso}" + +DRY_RUN=0 +ASSUME_YES=0 +ONLY_LIST="" + +while [ $# -gt 0 ]; do + case "$1" in + --dry-run) DRY_RUN=1 ;; + --yes|-y) ASSUME_YES=1 ;; + --only) shift; ONLY_LIST="${1:-}" ;; + -h|--help) + sed -n '2,28p' "$0" + exit 0 ;; + *) echo "ERROR: unknown arg: $1" >&2; exit 2 ;; + esac + shift +done + +log() { printf '[delete-per-component-repos] %s\n' "$*" >&2; } +die() { log "FATAL: $*"; exit 1; } + +# --------------------------------------------------------------------------- +# 1. Locate the token — env var, .netrc, or git credential helper (in order). +# Never echo it, never log it, never write it to disk. +# --------------------------------------------------------------------------- + +resolve_token() { + if [ -n "${REDBEAR_GITEA_TOKEN:-}" ]; then + printf '%s' "$REDBEAR_GITEA_TOKEN" + return 0 + fi + if command -v git >/dev/null 2>&1; then + local helper_token + helper_token="$(git credential fill </dev/null | sed -n 's/^password=//p' | head -n1 +protocol=https +host=gitea.redbearos.org +username=${GITEA_USER} +EOF +)" + if [ -n "$helper_token" ]; then + printf '%s' "$helper_token" + return 0 + fi + fi + if [ -r "$HOME/.netrc" ]; then + local netrc_token + netrc_token="$(awk -v host="gitea.redbearos.org" -v user="$GITEA_USER" ' + $1=="machine" && $2==host { in_block=1; next } + in_block && $1=="login" && $2==user { want=1; next } + in_block && want && $1=="password" { print $2; exit } + in_block && $1=="machine" { in_block=0 } + ' "$HOME/.netrc")" + if [ -n "$netrc_token" ]; then + printf '%s' "$netrc_token" + return 0 + fi + fi + return 1 +} + +log "Resolving Gitea token (env → git helper → ~/.netrc) ..." +TOKEN="$(resolve_token || true)" +[ -n "$TOKEN" ] || die "no token found. Set REDBEAR_GITEA_TOKEN or configure ~/.netrc / git credential helper." + +# --------------------------------------------------------------------------- +# 2. List every repo visible to the user via the Gitea API. +# --------------------------------------------------------------------------- + +api_get() { + curl -fsS -H "Authorization: token $TOKEN" \ + -H "Accept: application/json" \ + "$GITEA_HOST/api/v1$1" +} + +log "Fetching repo list from ${GITEA_HOST}/api/v1/users/${GITEA_USER}/repos ..." +REPOS_JSON="$(api_get "/users/${GITEA_USER}/repos?limit=200")" || die "Gitea API call failed." + +REPOS_ALL="$(printf '%s' "$REPOS_JSON" | jq -r '.[].name')" +[ -n "$REPOS_ALL" ] || die "no repos returned — check token permissions." + +# --------------------------------------------------------------------------- +# 3. Filter the deletion candidates. +# --------------------------------------------------------------------------- + +CANDIDATES="" +while IFS= read -r repo; do + [ -n "$repo" ] || continue + case " $KEEP_ALWAYS " in + *" $repo "*) continue ;; # canonical + user-named keepers + esac + if [ -n "$ONLY_LIST" ]; then + # comma-separated whitelist + case ",$ONLY_LIST," in + *",$repo,"*) ;; # match + *) continue ;; + esac + fi + CANDIDATES="${CANDIDATES}${repo}"$'\n' +done <<< "$REPOS_ALL" + +CANDIDATES="$(printf '%s' "$CANDIDATES" | sed '/^$/d')" + +if [ -z "$CANDIDATES" ]; then + log "No repos match deletion criteria. Nothing to do." + exit 0 +fi + +log "Repos flagged for deletion:" +while IFS= read -r r; do + [ -n "$r" ] || continue + log " - $r" +done <<< "$CANDIDATES" + +if [ "$DRY_RUN" -eq 1 ]; then + log "Dry run — no deletions performed." + exit 0 +fi + +# --------------------------------------------------------------------------- +# 4. Confirm with the operator (unless --yes). +# --------------------------------------------------------------------------- + +if [ "$ASSUME_YES" -ne 1 ]; then + log "" + log "WARNING: This DELETES repositories from ${GITEA_HOST}." + log "Make sure each repo above has been migrated as a submodule branch" + log "inside ${CANONICAL_REPO} first (see redirect-to-submodules.sh)." + log "The deletion is irreversible." + log "" + read -r -p "Type 'delete' to confirm: " confirm + if [ "$confirm" != "delete" ]; then + log "Aborted." + exit 1 + fi +fi + +# --------------------------------------------------------------------------- +# 5. Delete each candidate via DELETE /repos/{owner}/{repo}. +# --------------------------------------------------------------------------- + +fail_count=0 +ok_count=0 +while IFS= read -r repo; do + [ -n "$repo" ] || continue + log "Deleting ${GITEA_USER}/${repo} ..." + http_code="$(curl -sS -o /dev/null -w '%{http_code}' \ + -X DELETE \ + -H "Authorization: token $TOKEN" \ + -H "Accept: application/json" \ + "${GITEA_HOST}/api/v1/repos/${GITEA_USER}/${repo}")" + case "$http_code" in + 2*) log " ✓ deleted ($http_code)"; ok_count=$((ok_count+1)) ;; + 403) log " ✗ FORBIDDEN — token lacks delete permission on ${repo}"; fail_count=$((fail_count+1)) ;; + 404) log " – already gone (404)"; ok_count=$((ok_count+1)) ;; + *) log " ✗ FAILED ($http_code)"; fail_count=$((fail_count+1)) ;; + esac +done <<< "$CANDIDATES" + +log "" +log "Summary: ${ok_count} deleted, ${fail_count} failed." +[ "$fail_count" -eq 0 ] || exit 3 +exit 0 \ No newline at end of file diff --git a/local/scripts/redirect-to-submodules.sh b/local/scripts/redirect-to-submodules.sh new file mode 100755 index 0000000000..4c3ae2e6f8 --- /dev/null +++ b/local/scripts/redirect-to-submodules.sh @@ -0,0 +1,185 @@ +#!/usr/bin/env bash +# Redirect each existing per-component Gitea repo into a branch on the +# canonical RedBear-OS repo, then point the matching git submodule(s) at +# that branch. After this, the per-component Gitea repos can be deleted +# without breaking any submodule resolution. +# +# What this script does, for each per-component Gitea repo: +# 1. Fetches the repo's HEAD into a temp clone. +# 2. Pushes that HEAD as a `submodule/` branch on RedBear-OS. +# 3. Rewrites `.gitmodules` so any submodule entry pointing at the +# per-component URL now points at RedBear-OS with the new branch. +# +# What it does NOT do: +# - It does not move source content between repositories (source already +# lives in the per-component repo; we just push it under a new branch +# name in the canonical repo). +# - It does not delete the per-component Gitea repos. Use +# delete-per-component-repos.sh afterwards. +# - It does not touch recipe.toml files (none reference the per-component +# URLs as far as the current tree shows). +# +# Token is read at runtime from $REDBEAR_GITEA_TOKEN (or ~/.netrc). It is +# NEVER written to disk, logged, or echoed. + +set -euo pipefail + +GITEA_HOST="${GITEA_HOST:-https://gitea.redbearos.org}" +GITEA_USER="${GITEA_USER:-vasilito}" +CANONICAL_REPO="${CANONICAL_REPO:-RedBear-OS}" +CANONICAL_URL="${CANONICAL_URL:-${GITEA_HOST}/${GITEA_USER}/${CANONICAL_REPO}.git}" + +# Per-component Gitea repos to redirect. The actual Gitea slug is +# discovered at runtime via the API, so this list contains the canonical +# component names only. +DEFAULT_COMPONENTS=(base kernel installer redox-drm userutils libredox libpciaccess) + +DRY_RUN=0 +SKIP_PUSH=0 +COMPONENTS=() + +while [ $# -gt 0 ]; do + case "$1" in + --dry-run) DRY_RUN=1 ;; + --skip-push) SKIP_PUSH=1 ;; + -h|--help) sed -n '2,28p' "$0"; exit 0 ;; + --) shift; while [ $# -gt 0 ]; do COMPONENTS+=("$1"); shift; done ;; + -*) echo "ERROR: unknown arg: $1" >&2; exit 2 ;; + *) COMPONENTS+=("$1") ;; + esac + shift +done + +[ "${#COMPONENTS[@]}" -gt 0 ] || COMPONENTS=("${DEFAULT_COMPONENTS[@]}") + +log() { printf '[redirect-to-submodules] %s\n' "$*" >&2; } +die() { log "FATAL: $*"; exit 1; } + +resolve_token() { + if [ -n "${REDBEAR_GITEA_TOKEN:-}" ]; then + printf '%s' "$REDBEAR_GITEA_TOKEN" + return 0 + fi + if [ -r "$HOME/.netrc" ]; then + awk -v host="gitea.redbearos.org" -v user="$GITEA_USER" ' + $1=="machine" && $2==host { in_block=1; next } + in_block && $1=="login" && $2==user { want=1; next } + in_block && want && $1=="password" { print $2; exit } + in_block && $1=="machine" { in_block=0 } + ' "$HOME/.netrc" + fi +} + +TOKEN="$(resolve_token || true)" +[ -n "$TOKEN" ] || die "no token found. Set REDBEAR_GITEA_TOKEN or configure ~/.netrc." + +command -v git >/dev/null || die "git not found in PATH" +command -v jq >/dev/null || die "jq not found in PATH" +git rev-parse --is-inside-work-tree >/dev/null 2>&1 || die "must be run from inside the ${CANONICAL_REPO} working tree" + +current_branch="$(git rev-parse --abbrev-ref HEAD)" +log "Working tree : $(git rev-parse --show-toplevel)" +log "Branch : ${current_branch}" +log "Canonical URL: ${CANONICAL_URL}" + +if [ "$DRY_RUN" -eq 0 ] && [ "$SKIP_PUSH" -eq 0 ]; then + log "" + log "This script PUSHES branches to ${CANONICAL_URL} and modifies .gitmodules." + read -r -p "Type 'redirect' to confirm: " confirm + [ "$confirm" = "redirect" ] || { log "Aborted."; exit 1; } +fi + +api_get() { + curl -fsS -H "Authorization: token $TOKEN" \ + -H "Accept: application/json" \ + "$GITEA_HOST/api/v1$1" +} + +log "Verifying Gitea credentials ..." +api_get "/user" >/dev/null || die "token rejected by Gitea API" + +log "Fetching Gitea repo list ..." +REPO_NAMES_API="$(api_get "/users/${GITEA_USER}/repos?limit=200" | jq -r '.[].name')" +[ -n "${REPO_NAMES_API}" ] || die "no repos returned — check token permissions." + +find_slug() { + local component="$1" + # Try the legacy redbear-os- convention first, then the bare name. + for candidate in "redbear-os-${component}" "${component}"; do + if printf '%s\n' "${REPO_NAMES_API}" | grep -qx "${candidate}"; then + printf '%s' "${candidate}" + return 0 + fi + done + return 1 +} + +redirect_component() { + local component="$1" + local gitea_slug + if ! gitea_slug="$(find_slug "${component}")"; then + log "" + log "=== ${component} ===" + die "no Gitea repo found for component '${component}' (tried: redbear-os-${component}, ${component})" + fi + local branch="submodule/${component}" + + log "" + log "=== ${component} ===" + log " source Gitea repo : ${gitea_slug}" + log " target branch : ${branch}" + + if [ "$DRY_RUN" -eq 1 ]; then + log " [dry-run] would fetch HEAD, push as ${branch}, rewrite .gitmodules" + return 0 + fi + + local tmp + tmp="$(mktemp -d)" + trap 'rm -rf "$tmp"' EXIT + + log " cloning ${gitea_slug} ..." + local src_url="https://${TOKEN}@${GITEA_HOST#https://}/${GITEA_USER}/${gitea_slug}.git" + git clone --quiet "${src_url}" "${tmp}/src" \ + || die "clone failed for ${gitea_slug}" + + local src_default + src_default="$(git -C "${tmp}/src" symbolic-ref --short HEAD 2>/dev/null || echo "")" + local src_commit_count + src_commit_count="$(git -C "${tmp}/src" rev-list --all --count 2>/dev/null || echo 0)" + if [ -z "${src_commit_count}" ] || [ "${src_commit_count}" = "0" ]; then + log " source repo is empty (0 commits) — skipping push" + rm -rf "${tmp}" + trap - EXIT + log " ✓ ${component} (no-op)" + return 0 + fi + log " source default branch: ${src_default} (${src_commit_count} commits)" + log " pushing ${branch} to ${CANONICAL_URL} ..." + local push_url="https://${TOKEN}@${GITEA_HOST#https://}/${GITEA_USER}/${CANONICAL_REPO}.git" + git -C "${tmp}/src" push --quiet "${push_url}" "refs/heads/${src_default}:refs/heads/${branch}" \ + || die "push failed for ${branch}" + + log " rewriting .gitmodules ..." + if grep -q "url = .*${gitea_slug}\.git" .gitmodules 2>/dev/null; then + sed -i.bak "s|url = .*${gitea_slug}\.git|url = ${CANONICAL_URL}|" .gitmodules + sed -i "s|^ branch = .*| branch = ${branch}|" .gitmodules + rm -f .gitmodules.bak + log " updated" + else + log " no .gitmodules entry references ${gitea_slug}; left untouched" + fi + + rm -rf "${tmp}" + trap - EXIT + log " ✓ ${component} redirected" +} + +for c in "${COMPONENTS[@]}"; do + redirect_component "$c" +done + +log "" +log "Done. Verify with: git submodule status" +log "Then run ./local/scripts/delete-per-component-repos.sh to delete the" +log "now-unused per-component repos on Gitea." \ No newline at end of file