git: enforce SINGLE-REPO RULE — redirect submodules to canonical repo
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/<component>' 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)
This commit is contained in:
+2
-2
@@ -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
|
||||
|
||||
+8
-4
@@ -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/<component>`
|
||||
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/<component>` 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.
|
||||
|
||||
+135
-15
@@ -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/<component>` branches),
|
||||
OR
|
||||
- **Tracked trees under `local/sources/<component>/`** — 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/<component>` (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/<component>`
|
||||
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/<component>` 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 <component>`
|
||||
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/<component>/`**.
|
||||
|
||||
| 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/<component>/` becomes desynced, recover from
|
||||
`https://gitea.redbearos.org/vasilito/redbear-os-<component>` rather than
|
||||
from upstream Redox.
|
||||
3. If `local/sources/<component>/` becomes desynced, recover from the
|
||||
corresponding `submodule/<component>` 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
|
||||
|
||||
|
||||
@@ -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 <build@redbearos.org>` author bot during automated syncs, which
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Executable
+183
@@ -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 <<EOF 2>/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
|
||||
Executable
+185
@@ -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/<component>` 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-<component> 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."
|
||||
Reference in New Issue
Block a user