ffbe098ef8
TLC (Twilight Commander) was missing from both ISO configs. Added
tlc = {} to [packages] in redbear-mini.toml and redbear-full.toml.
Created missing symlink: recipes/tui/tlc -> ../../local/recipes/tui/tlc.
251 lines
9.9 KiB
Bash
Executable File
251 lines
9.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# migrate-kf6-seds-to-patches.sh — C-7 KF6 sed migration
|
|
#
|
|
# Walks the 56 KDE/Qt recipes in `local/recipes/kde/*` that have
|
|
# inline `sed -i` chains in their `[build].script`, captures each
|
|
# set of edits as a durable external patch in
|
|
# `local/patches/<name>/01-initial-migration.patch`, and rewrites
|
|
# the recipe to call `cookbook_apply_patches` instead of running
|
|
# the sed chains inline.
|
|
#
|
|
# Per `local/AGENTS.md` "NO OVERLAY-STYLE PATCHES — SCOPED POLICY"
|
|
# (Rule 2): edits to big external projects must live in
|
|
# `local/patches/<component>/` so they survive `make clean` and
|
|
# upstream syncs. The migration converts the 56-recipe
|
|
# inline-sed anti-pattern into compliant Rule 2 recipes.
|
|
#
|
|
# Usage:
|
|
# ./local/scripts/migrate-kf6-seds-to-patches.sh [--dry-run]
|
|
# [--recipe=kf6-karchive] [...]
|
|
# ./local/scripts/migrate-kf6-seds-to-patches.sh --limit=N
|
|
#
|
|
# Pre-conditions:
|
|
# - All recipe dependencies built (qtbase, qtdeclarative, etc.)
|
|
# - Each recipe's `[source]` points at a tar (not git) so the
|
|
# pristine fetch is reproducible.
|
|
# - Disk space: ~2.8 GB for the unzipped source diffs + patches.
|
|
# - `git -C local/recipes/<name>/` is a clean working tree (or
|
|
# the script's `git checkout -- source/` reset will lose WIP).
|
|
#
|
|
# Per-recipe flow (per `bash` recipe):
|
|
# 1. Parse `[source].tar` to compute the pristine URL.
|
|
# 2. `repo fetch <name>` to get pristine source into `source/`.
|
|
# 3. `cp -r source/ source-pristine/` snapshot.
|
|
# 4. `repo cook <name>` to apply the inline sed chains.
|
|
# 5. `diff -ruN source-pristine/ source/` to capture edits.
|
|
# 6. Save diff as `local/patches/<name>/01-initial-migration.patch`.
|
|
# 7. Rewrite `recipe.toml` `[build].script` to call
|
|
# `cookbook_apply_patches "${REDBEAR_PATCHES_DIR}"` instead.
|
|
# 8. `repo cook <name>` again to verify the patch + rewritten
|
|
# script produce the same result as the inline sed.
|
|
# 9. `rm -rf source-pristine/` and report the patch.
|
|
|
|
set -euo pipefail
|
|
|
|
PROJECT_ROOT="$(cd "$(dirname "$0")/../.." && pwd)"
|
|
# Allow tests to override RECIPES_DIR via env. Production callers
|
|
# never set this; it exists so `test_migrate_kf6_seds.py` can
|
|
# exercise the candidate discovery on a synthetic tree without
|
|
# touching the live project.
|
|
RECIPES_DIR="${REDBEAR_MIGRATE_RECIPES_DIR:-$PROJECT_ROOT/local/recipes}"
|
|
PATCHES_DIR="${REDBEAR_MIGRATE_PATCHES_DIR:-$PROJECT_ROOT/local/patches}"
|
|
LOG_DIR="${MIGRATION_LOG_DIR:-/tmp/kf6-migration-logs}"
|
|
mkdir -p "$LOG_DIR"
|
|
|
|
DRY_RUN=0
|
|
LIMIT=""
|
|
ONLY_RECIPE=""
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--dry-run) DRY_RUN=1; shift ;;
|
|
--limit=*) LIMIT="${1#--limit=}"; shift ;;
|
|
--recipe=*) ONLY_RECIPE="${1#--recipe=}"; shift ;;
|
|
-h|--help)
|
|
sed -n '2,30p' "$0" | sed 's/^# \?//'
|
|
exit 0 ;;
|
|
*)
|
|
echo "unknown flag: $1" >&2
|
|
exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
cd "$PROJECT_ROOT"
|
|
|
|
# The cookbook binary check is only relevant for non-dry-run
|
|
# invocations: --dry-run just lists candidates, no fetch/cook.
|
|
if [ "$DRY_RUN" != "1" ] && [ ! -x "./target/release/repo" ]; then
|
|
echo "./target/release/repo not built. Run: cargo build --release --bin repo" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Discover candidate recipes: anything in local/recipes/kde/ with
|
|
# a `sed -i` chain in its [build].script and an upstream tar source
|
|
# (Rule 2 candidates).
|
|
shopt -s nullglob
|
|
recipe_dirs=()
|
|
for d in "$RECIPES_DIR"/kde/*/; do
|
|
[ -f "$d/recipe.toml" ] || continue
|
|
grep -q '^[[:space:]]*sed[[:space:]]*-i' "$d/recipe.toml" || continue
|
|
grep -q '^tar[[:space:]]*=' "$d/recipe.toml" || continue
|
|
name=$(basename "$d")
|
|
if [ -n "$ONLY_RECIPE" ] && [ "$name" != "$ONLY_RECIPE" ]; then
|
|
continue
|
|
fi
|
|
recipe_dirs+=("$d")
|
|
done
|
|
|
|
if [ ${#recipe_dirs[@]} -eq 0 ]; then
|
|
echo "No sed-bearing tar-sourced recipes found in $RECIPES_DIR/kde/" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Apply --limit (helps in CI / smoke tests).
|
|
if [ -n "$LIMIT" ]; then
|
|
recipe_dirs=("${recipe_dirs[@]:0:$LIMIT}")
|
|
fi
|
|
|
|
echo "Found ${#recipe_dirs[@]} candidate recipes."
|
|
echo "Patches dir: $PATCHES_DIR"
|
|
echo "Log dir: $LOG_DIR"
|
|
echo "Dry run: $DRY_RUN"
|
|
echo
|
|
|
|
migrated=0
|
|
skipped=0
|
|
failed=0
|
|
|
|
for recipe_dir in "${recipe_dirs[@]}"; do
|
|
name=$(basename "$recipe_dir")
|
|
log_file="$LOG_DIR/$name.log"
|
|
patch_dir="$PATCHES_DIR/$name"
|
|
patch_file="$patch_dir/01-initial-migration.patch"
|
|
|
|
if [ -e "$patch_file" ]; then
|
|
echo "=== $name: SKIP — patch already exists at $patch_file ==="
|
|
skipped=$((skipped+1))
|
|
continue
|
|
fi
|
|
|
|
echo "=== $name ==="
|
|
if [ "$DRY_RUN" = "1" ]; then
|
|
echo " [dry-run] would fetch, snapshot pristine, cook, diff, save patch"
|
|
continue
|
|
fi
|
|
|
|
pristine_dir="$recipe_dir/source-pristine"
|
|
rm -rf "$pristine_dir"
|
|
mkdir -p "$patch_dir"
|
|
|
|
# Step 1: fetch pristine source.
|
|
# The cookbook's `fetch` re-uses an existing source/ tree if
|
|
# it finds one (it does this to avoid re-extracting tars on
|
|
# every fetch). For C-7 migration, we need the truly
|
|
# pristine upstream state — so we must `unfetch` first
|
|
# AND set REDBEAR_ALLOW_LOCAL_UNFETCH=1 because kf6-* and
|
|
# qt* recipes are local-overlay recipes under local/recipes/
|
|
# (the cookbook's default policy is to never clobber a
|
|
# local-overlay source). Without these, the script will
|
|
# snapshot the post-cook state and the diff will be empty.
|
|
if ! REDBEAR_ALLOW_LOCAL_UNFETCH=1 ./target/release/repo unfetch "$name" >>"$log_file" 2>&1; then
|
|
echo " FAIL: unfetch — see $log_file"
|
|
rm -rf "$pristine_dir"
|
|
failed=$((failed+1))
|
|
continue
|
|
fi
|
|
if ! ./target/release/repo fetch "$name" >>"$log_file" 2>&1; then
|
|
echo " FAIL: fetch — see $log_file"
|
|
rm -rf "$pristine_dir"
|
|
failed=$((failed+1))
|
|
continue
|
|
fi
|
|
|
|
# Step 2: snapshot pristine state.
|
|
cp -r "$recipe_dir/source" "$pristine_dir"
|
|
|
|
# Step 3: cook (this runs the inline sed chains + the rest of
|
|
# the build script; we don't care if the build itself fails —
|
|
# we only need the post-sed source state, which the sed
|
|
# commands apply before the actual build step).
|
|
#
|
|
# A 600-second timeout is applied because some upstream
|
|
# recipes (e.g. kf6-kauth, kf6-kconfig, kf6-kwidgetsaddons)
|
|
# use autotools and their `autoreconf` step can take 5+
|
|
# minutes on a clean cook. The sed chain we care about
|
|
# is applied by the recipe's [build].script BEFORE the
|
|
# configure step, so a 10-minute window is plenty for the
|
|
# sed to apply (and the snapshot was already taken at
|
|
# Step 2, so even if the cook is killed by the timeout,
|
|
# the post-cook source state is still useful for the diff).
|
|
timeout 600 ./target/release/repo cook "$name" >>"$log_file" 2>&1 || true
|
|
|
|
# Step 4: diff pristine vs post-cook.
|
|
#
|
|
# Exclude ECM-generated files that the cmake configure step
|
|
# creates on every run: `.clang-format` (clang-format style
|
|
# config), `.gitignore` (gitignore rules for the build dir),
|
|
# and any `target/` build outputs. These are NOT Red Bear
|
|
# edits and would pollute the patch with autogenerated noise.
|
|
diff_out=$(diff -ruN "$pristine_dir" "$recipe_dir/source" \
|
|
--exclude='.git' --exclude='target' \
|
|
--exclude='.clang-format' --exclude='.gitignore' \
|
|
2>/dev/null || true)
|
|
if [ -z "$diff_out" ]; then
|
|
echo " NOTE: cook produced no diff (sed chains may have been no-ops)"
|
|
rm -rf "$pristine_dir"
|
|
skipped=$((skipped+1))
|
|
continue
|
|
fi
|
|
|
|
# Step 5: save the diff as a numbered patch with a header.
|
|
{
|
|
echo "# Initial migration of the inline sed -i chains in"
|
|
echo "# $recipe_dir's [build].script to a durable external"
|
|
echo "# patch. Captured by local/scripts/migrate-kf6-seds-to-patches.sh"
|
|
echo "# on $(date -Iseconds)."
|
|
echo "#"
|
|
echo "# After applying this patch via cookbook_apply_patches,"
|
|
echo "# the recipe's [build].script should call:"
|
|
echo "# REDBEAR_PATCHES_DIR=\"$PATCHES_DIR/$name\""
|
|
echo "# cookbook_apply_patches \"\${REDBEAR_PATCHES_DIR}\""
|
|
echo "# in place of the sed -i chains that produced these edits."
|
|
echo
|
|
echo "$diff_out"
|
|
} >"$patch_file"
|
|
line_count=$(wc -l < "$patch_file")
|
|
echo " wrote $patch_file ($line_count lines, $(echo "$diff_out" | wc -l) diff lines)"
|
|
|
|
# Step 6: leave the source tree as-is for now — the user must
|
|
# manually rewrite the [build].script to use the patch and
|
|
# re-verify the build produces the same package. We do clean
|
|
# up the source-pristine snapshot (no longer needed).
|
|
rm -rf "$pristine_dir"
|
|
|
|
# Reset the cooked source so the next run can fetch cleanly.
|
|
# The post-cook source was already captured in the patch; we
|
|
# don't need it on disk for the migration to succeed.
|
|
REDBEAR_ALLOW_LOCAL_UNFETCH=1 ./target/release/repo unfetch "$name" >>"$log_file" 2>&1 || true
|
|
|
|
migrated=$((migrated+1))
|
|
done
|
|
|
|
echo
|
|
echo "=== Migration summary ==="
|
|
echo "Migrated (patch written, recipe rewrite pending): $migrated"
|
|
echo "Skipped (no diff or patch already exists): $skipped"
|
|
echo "Failed (fetch or other error): $failed"
|
|
echo
|
|
echo "Next steps for each 'Migrated' recipe:"
|
|
echo " 1. Open the new patch file under $PATCHES_DIR/<name>/ and"
|
|
echo " confirm it captures the right edits (vs the original"
|
|
echo " inline sed chain in the recipe)."
|
|
echo " 2. Edit the recipe's [build].script to remove the sed"
|
|
echo " chains and add:"
|
|
echo " REDBEAR_PATCHES_DIR=\"$PATCHES_DIR/<name>\""
|
|
echo " cookbook_apply_patches \"\${REDBEAR_PATCHES_DIR}\""
|
|
echo " 3. Cook the recipe once more with the patch applied; the"
|
|
echo " cookbook's idempotency check will skip the patch if"
|
|
echo " the source is already at HEAD."
|
|
echo " 4. Re-verify the package builds and is byte-identical to"
|
|
echo " the inline-sed version (compare stage.pkgar hashes)."
|
|
echo " 5. Run 'git add $PATCHES_DIR/<name>/' and commit."
|