fix: document and implement local fork version sync policy
Add comprehensive policy documentation in AGENTS.md covering: - local/ fork always takes precedence over recipes/ paths - build system must ensure local fork is at latest available version - all Red Bear patches must be applied cleanly on top of latest version - automatic version bump + patch reapplication via bump-fork.sh Create local/scripts/bump-fork.sh that implements automatic version bumping: - Detects current local version vs required version from Cargo.lock - Fetches upstream source at required version - Applies all Red Bear patches atomically - Updates version field and replaces local fork contents Fix driver-manager Cargo.toml lockfile collision: - Remove redundant syscall dependency (transitive via pcid_interface) - Update all driver recipes to use local/sources/syscall and libredox paths - This eliminates the redox_syscall lockfile collision between local/sources/syscall and recipes/core/base/syscall (same dir, different paths) relibc: fix unsafe call for Rust 2024 edition compatibility
This commit is contained in:
@@ -421,6 +421,62 @@ KDE recipes, and all recipes carrying Red Bear patches (Qt, DRM, Mesa, Wayland,
|
||||
glib, etc.). Any recipe with a `redox.patch` or local patches is a candidate for
|
||||
protection — add it when adding patches.
|
||||
|
||||
### Local Fork Priority and Version Sync (MANDATORY)
|
||||
|
||||
**Rule**: If a recipe exists in `local/sources/<component>/` (a local fork), it
|
||||
**always takes precedence** over the upstream version in `recipes/<component>/`.
|
||||
However, the build system **must guarantee** that the local fork is at the
|
||||
**latest available version** and that **all Red Bear patches are applied cleanly**
|
||||
on top of that version.
|
||||
|
||||
**Rationale**: Red Bear forks are designed to be drop-in compatible with
|
||||
upstream. Transitive dependencies from crates.io may pull in newer versions
|
||||
of shared crates (e.g. `redox_syscall`, `redox-scheme`). If the local fork is
|
||||
at a lower version than what the dependency graph requires, two consequences
|
||||
follow:
|
||||
1. **Lockfile collision**: Cargo sees `redox_syscall v0.8.1 (local/sources/syscall)`
|
||||
and `redox_syscall v0.8.1 (recipes/core/base/syscall)` as different sources
|
||||
even when they resolve to the same directory via symlink.
|
||||
2. **Type mismatch**: Transitive deps built against `redox_syscall 0.9.x` (from
|
||||
crates.io) produce types incompatible with our local `0.8.x` fork,
|
||||
causing `error[E0308]: mismatched types` at link/compile time.
|
||||
|
||||
**Detection**: The build system MUST detect these conditions automatically.
|
||||
On every build, the cookbook MUST:
|
||||
1. Compare the version in `local/sources/<component>/Cargo.toml` against the
|
||||
highest version required by any transitive dependency in the build graph.
|
||||
2. Compare the version in `local/sources/<component>/Cargo.toml` against the
|
||||
upstream `recipes/<component>/source/Cargo.toml` (if available).
|
||||
3. If the local version is lower than either:
|
||||
a. Record the required version in a manifest (`local/sources/<component>/.required-version`)
|
||||
b. Emit a clear actionable error: `LOCAL FORK OUTDATED: local/sources/<component> is at vX.Y.Z but vA.B.C is required by <dep>`
|
||||
c. Do NOT silently proceed with a broken build.
|
||||
|
||||
**Remediation (automatic, when invoked)**: The `local/scripts/bump-fork.sh`
|
||||
script (or equivalent `repo bump-fork <component>` command) MUST:
|
||||
1. Fetch the upstream source at the required version.
|
||||
2. Apply all Red Bear patches from `local/patches/<component>/` using the
|
||||
atomic patch application mechanism (see "Atomic Patch Application" below).
|
||||
3. Update the version field in the local fork's `Cargo.toml`.
|
||||
4. Commit the result to the `submodule/<component>` branch in the RedBear-OS repo.
|
||||
5. Rebuild the affected packages.
|
||||
|
||||
**Symlink aliasing**: `recipes/<component>/` MUST be a symlink to
|
||||
`../../../local/sources/<component>/` when a local fork exists. This ensures
|
||||
backward compatibility for recipes that reference the `recipes/` path while
|
||||
still preferring the local fork as the source of truth.
|
||||
|
||||
**Implementation status**: Detection is partially implemented via Cargo's
|
||||
own lockfile collision errors. Full automatic detection and remediation
|
||||
requires changes to `src/cook/fetch.rs` (version comparison logic) and the
|
||||
addition of `local/scripts/bump-fork.sh`. Until fully automated, the manual
|
||||
process is:
|
||||
1. `cd local/sources/<component>`
|
||||
2. Edit `Cargo.toml` version field to match upstream
|
||||
3. `git pull` or fetch upstream at the new tag
|
||||
4. Reapply all `local/patches/<component>/*.patch` (see AGENTS.md "Atomic Patch Application")
|
||||
5. Commit to `submodule/<component>` branch
|
||||
|
||||
### Offline-First By Default
|
||||
|
||||
Red Bear OS is a **fork with frozen sources**. The cookbook tool defaults to
|
||||
|
||||
@@ -10,11 +10,15 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
usb-core = { path = "../../usb-core/source" }
|
||||
libredox = { version = "0.1", features = ["call", "std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
redox-driver-sys = { path = "../../redox-driver-sys/source" }
|
||||
redox-scheme = "0.11"
|
||||
syscall = { package = "redox_syscall", version = "0.8", features = ["std"] }
|
||||
redox-scheme = "<0.11.2"
|
||||
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
|
||||
[target.'cfg(target_os = "redox")'.dependencies]
|
||||
redox-driver-sys = { path = "../../redox-driver-sys/source", features = ["redox"] }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -6,8 +6,8 @@ description = "Linux Kernel API compatibility layer for Redox OS (LinuxKPI-style
|
||||
license = "MIT"
|
||||
|
||||
[dependencies]
|
||||
libredox = "0.1"
|
||||
redox_syscall = { version = "0.8", features = ["std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
log = "0.4"
|
||||
thiserror = "2"
|
||||
lazy_static = "1.4"
|
||||
@@ -15,3 +15,7 @@ redox-driver-sys = { path = "../../redox-driver-sys/source" }
|
||||
|
||||
[lib]
|
||||
crate-type = ["rlib", "staticlib"]
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -10,5 +10,9 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
usb-core = { path = "../../usb-core/source" }
|
||||
redox_syscall = "0.8"
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
log = "0.4"
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -9,7 +9,11 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
libredox = { version = "0.1", features = ["call", "std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
redox-scheme = "0.11"
|
||||
syscall = { package = "redox_syscall", version = "0.8", features = ["std"] }
|
||||
redox-scheme = "<0.11.2"
|
||||
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -6,4 +6,8 @@ description = "PCI bus backend for redox-driver-core"
|
||||
|
||||
[dependencies]
|
||||
redox-driver-core = { path = "../../redox-driver-core/source" }
|
||||
redox_syscall = "0.8"
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -5,8 +5,8 @@ edition = "2021"
|
||||
description = "Safe Rust wrappers for Redox OS scheme-based hardware access"
|
||||
|
||||
[dependencies]
|
||||
libredox = "0.1.0"
|
||||
redox_syscall = { version = "0.8", features = ["std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
log = "0.4"
|
||||
thiserror = "2"
|
||||
bitflags = "2"
|
||||
@@ -28,3 +28,7 @@ linux-kpi = { path = "../../linux-kpi/source" }
|
||||
name = "smoke_test"
|
||||
harness = false
|
||||
required-features = ["redox"]
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -10,5 +10,9 @@ path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
usb-core = { path = "../../usb-core/source" }
|
||||
redox_syscall = "0.8"
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
log = "0.4"
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -11,9 +11,13 @@ path = "src/main.rs"
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
log = "0.4"
|
||||
libredox = { version = "=0.1.16", features = ["call", "std"] }
|
||||
redox_syscall = { version = "0.7", features = ["std"] }
|
||||
libredox = { path = "../../../../../local/sources/libredox", features = ["call", "std"] }
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
redox-driver-sys = { path = "../../redox-driver-sys/source" }
|
||||
syscall = { package = "redox_syscall", version = "0.7", features = ["std"] }
|
||||
syscall = { package = "redox_syscall", path = "../../../../../local/sources/syscall", features = ["std"] }
|
||||
inputd = { path = "../../../../sources/base/drivers/inputd" }
|
||||
common = { path = "../../../../sources/base/drivers/common" }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
|
||||
@@ -12,8 +12,11 @@ path = "src/main.rs"
|
||||
redox-driver-core = { path = "../../../drivers/redox-driver-core/source" }
|
||||
redox-driver-pci = { path = "../../../drivers/redox-driver-pci/source" }
|
||||
pcid_interface = { path = "../../../../../local/sources/base/drivers/pcid", package = "pcid" }
|
||||
redox-scheme = "0.11"
|
||||
syscall = { package = "redox_syscall", version = "0.8" }
|
||||
redox-scheme = "=0.11.1"
|
||||
log = "0.4"
|
||||
toml = "0.8"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[patch.crates-io]
|
||||
redox_syscall = { path = "../../../../../local/sources/syscall" }
|
||||
libredox = { path = "../../../../../local/sources/libredox" }
|
||||
Executable
+165
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env bash
|
||||
# bump-fork.sh — Auto-bump a local fork to match upstream version
|
||||
#
|
||||
# Usage: bump-fork.sh <component> [--version=X.Y.Z]
|
||||
#
|
||||
# If --version is provided, bumps to that version.
|
||||
# If --version is omitted, queries Cargo to determine the required version
|
||||
# from the build graph.
|
||||
#
|
||||
# Implements: AGENTS.md "Local Fork Priority and Version Sync (MANDATORY)"
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
COMPONENT="${1:-}"
|
||||
if [[ -z "$COMPONENT" ]]; then
|
||||
echo "Usage: $0 <component> [--version=X.Y.Z]"
|
||||
echo ""
|
||||
echo "Components with local forks:"
|
||||
for d in local/sources/*/; do
|
||||
if [[ -d "$d" ]] && [[ -f "${d}Cargo.toml" ]]; then
|
||||
echo " $(basename "$d")"
|
||||
fi
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
shift
|
||||
|
||||
VERSION=""
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--version=*)
|
||||
VERSION="${1#*=}"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
LOCAL_DIR="local/sources/${COMPONENT}"
|
||||
RECIPE_DIR="recipes/core/${COMPONENT}"
|
||||
|
||||
if [[ ! -d "$LOCAL_DIR" ]]; then
|
||||
echo "ERROR: $LOCAL_DIR does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Detect current local version
|
||||
if [[ -f "${LOCAL_DIR}/Cargo.toml" ]]; then
|
||||
CURRENT_VERSION=$(grep -E '^version\s*=' "${LOCAL_DIR}/Cargo.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/')
|
||||
echo "Current local version: ${CURRENT_VERSION:-unknown}"
|
||||
else
|
||||
echo "WARNING: ${LOCAL_DIR}/Cargo.toml not found — not a Cargo crate?"
|
||||
CURRENT_VERSION=""
|
||||
fi
|
||||
|
||||
# Detect required version from build graph if not specified
|
||||
if [[ -z "$VERSION" ]]; then
|
||||
echo "Querying build graph for required version of ${COMPONENT}..."
|
||||
|
||||
# Search Cargo.lock files for the required version
|
||||
REQUIRED_VERSION=""
|
||||
while IFS= read -r lockfile; do
|
||||
# Look for [[package]] entries that match this component
|
||||
entry=$(awk '
|
||||
/^name = "'"${COMPONENT}"'"$/ { found=1; next }
|
||||
found && /^version = / { gsub(/"/, ""); print; exit }
|
||||
' "$lockfile" 2>/dev/null)
|
||||
if [[ -n "$entry" ]]; then
|
||||
REQUIRED_VERSION="$entry"
|
||||
break
|
||||
fi
|
||||
done < <(find . -name 'Cargo.lock' -not -path '*/.git/*' 2>/dev/null)
|
||||
|
||||
if [[ -z "$REQUIRED_VERSION" ]]; then
|
||||
echo "ERROR: Could not determine required version for ${COMPONENT}" >&2
|
||||
echo " Pass --version=X.Y.Z explicitly" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION="$REQUIRED_VERSION"
|
||||
echo "Required version (from Cargo.lock): ${VERSION}"
|
||||
fi
|
||||
|
||||
# Check if bump is needed
|
||||
if [[ -n "$CURRENT_VERSION" ]] && [[ "$CURRENT_VERSION" == "$VERSION" ]]; then
|
||||
echo "Local fork already at v${VERSION} — no bump needed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Bumping ${COMPONENT}: v${CURRENT_VERSION:-unknown} → v${VERSION}"
|
||||
|
||||
# Fetch upstream source at the required version
|
||||
echo "Fetching upstream source at v${VERSION}..."
|
||||
|
||||
# Find the upstream repo URL from the recipe
|
||||
if [[ -f "${RECIPE_DIR}/recipe.toml" ]]; then
|
||||
UPSTREAM_GIT=$(grep -E '^git\s*=' "${RECIPE_DIR}/recipe.toml" | head -1 | sed 's/.*"\(.*\)".*/\1/')
|
||||
fi
|
||||
|
||||
if [[ -z "${UPSTREAM_GIT:-}" ]]; then
|
||||
echo "ERROR: Cannot determine upstream git URL from ${RECIPE_DIR}/recipe.toml" >&2
|
||||
echo " Set up the recipe or pass --upstream-git=<url>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Upstream git: ${UPSTREAM_GIT}"
|
||||
|
||||
# Fetch into a staging directory
|
||||
STAGING_DIR="/tmp/redbear-bump-${COMPONENT}-$$"
|
||||
trap "rm -rf ${STAGING_DIR}" EXIT
|
||||
|
||||
echo "Cloning ${UPSTREAM_GIT} at v${VERSION} into ${STAGING_DIR}..."
|
||||
git clone --depth 1 --branch "v${VERSION}" "${UPSTREAM_GIT}" "${STAGING_DIR}" 2>/dev/null \
|
||||
|| git clone --depth 1 "${UPSTREAM_GIT}" "${STAGING_DIR}"
|
||||
|
||||
cd "${STAGING_DIR}"
|
||||
git fetch --tags
|
||||
if git tag -e "v${VERSION}" 2>/dev/null; then
|
||||
git checkout "v${VERSION}"
|
||||
else
|
||||
echo "WARNING: tag v${VERSION} not found — using main branch HEAD"
|
||||
fi
|
||||
cd - >/dev/null
|
||||
|
||||
# Apply Red Bear patches
|
||||
PATCH_DIR="local/patches/${COMPONENT}"
|
||||
if [[ -d "$PATCH_DIR" ]]; then
|
||||
echo "Applying Red Bear patches from ${PATCH_DIR}..."
|
||||
for patch in "${PATCH_DIR}"/*.patch; do
|
||||
if [[ -f "$patch" ]]; then
|
||||
echo " Applying $(basename "$patch")..."
|
||||
if ! patch -p1 -d "${STAGING_DIR}" --dry-run < "$patch" >/dev/null 2>&1; then
|
||||
echo "ERROR: Patch $(basename "$patch") fails to apply to upstream v${VERSION}" >&2
|
||||
echo " Manual intervention required — patch may need rebasing" >&2
|
||||
exit 1
|
||||
fi
|
||||
patch -p1 -d "${STAGING_DIR}" < "$patch"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Update version in Cargo.toml
|
||||
if [[ -f "${STAGING_DIR}/Cargo.toml" ]]; then
|
||||
sed -i "s/^version = \"${CURRENT_VERSION}\"/version = \"${VERSION}\"/" "${STAGING_DIR}/Cargo.toml"
|
||||
fi
|
||||
|
||||
# Replace local fork contents with staged (atomic via temp dir rename)
|
||||
echo "Updating ${LOCAL_DIR}..."
|
||||
mv "${LOCAL_DIR}" "${LOCAL_DIR}.old-${$$}"
|
||||
mv "${STAGING_DIR}" "${LOCAL_DIR}"
|
||||
rm -rf "${LOCAL_DIR}.old-${$$}"
|
||||
|
||||
echo ""
|
||||
echo "SUCCESS: ${COMPONENT} bumped to v${VERSION}"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. cd ${LOCAL_DIR}"
|
||||
echo " 2. git diff (verify patches applied correctly)"
|
||||
echo " 3. git add -A && git commit -m 'bump: ${COMPONENT} v${CURRENT_VERSION} -> v${VERSION}'"
|
||||
echo " 4. git push origin submodule/${COMPONENT}"
|
||||
echo " 5. Rebuild affected packages: ./local/scripts/build-redbear.sh redbear-mini"
|
||||
+1
-1
Submodule local/sources/relibc updated: 26595f1624...89a4aa8a05
Reference in New Issue
Block a user