Files
rspade_system/bin/framework-pull-upstream.sh
2025-10-21 07:41:49 +00:00

553 lines
21 KiB
Bash
Executable File

#!/bin/bash
# RSpade Framework Update Script
# Pulls updates from rspade_upstream remote while preserving ./rsx in project mode
# This script runs BEFORE Laravel loads to update framework code independently
set -e
# Parse arguments
NO_REBUILD=false
SHOW_DIFF=false
STASH_CHANGES=false
for arg in "$@"; do
if [ "$arg" = "--no-rebuild" ]; then
NO_REBUILD=true
elif [ "$arg" = "--diff" ]; then
SHOW_DIFF=true
elif [ "$arg" = "--stash" ]; then
STASH_CHANGES=true
fi
done
# Configure git to ignore file mode changes
git config core.fileMode false
# Determine project mode: Check for .rspade-dev-environment or rsx/.git
IS_PROJECT_MODE=false
if [ -f ".rspade-dev-environment" ] || [ -d "rsx/.git" ]; then
IS_PROJECT_MODE=true
fi
# In project mode, rsx/.git MUST exist
if [ "$IS_PROJECT_MODE" = true ] && [ ! -d "rsx/.git" ]; then
echo "ERROR: ./rsx is not a git repository."
echo ""
echo "The development environment appears corrupted."
echo "Expected ./rsx to be an independent git repository."
exit 1
fi
# Check if framework is forked
if [ -f ".rspade-forked-framework" ]; then
echo "ERROR: Framework is in forked mode."
echo ""
echo "You have taken full ownership of the RSpade framework codebase."
echo "Automatic updates are disabled to prevent overwriting your changes."
echo ""
echo "To manually update from upstream:"
echo " 1. git fetch rspade_upstream"
echo " 2. git diff rspade_upstream/rspade_selfupdate"
echo " 3. Manually merge desired changes"
echo " 4. Test thoroughly"
echo ""
echo "For detailed procedures: php artisan rsx:man framework_fork"
exit 1
fi
# Check .env for IS_FRAMEWORK_DEVELOPER=true
if [ -f ".env" ]; then
if grep -q "^IS_FRAMEWORK_DEVELOPER=true" .env; then
echo "ERROR: This command is disabled for framework developers."
echo ""
echo "This command is for application developers to pull framework updates."
echo "Framework developers should use standard git commands to manage the monorepo."
echo ""
echo "To use this command, set IS_FRAMEWORK_DEVELOPER=false in .env"
exit 1
fi
fi
# Check APP_ENV for production
if [ -f ".env" ]; then
if grep -q "^APP_ENV=production" .env; then
echo "ERROR: Framework updates are disabled in production mode."
echo ""
echo "Framework updates should be incorporated into and tested in a"
echo "development environment before deploying to production."
echo ""
echo "Update in development, test thoroughly, then deploy to production."
exit 1
fi
fi
# Verify rspade_upstream remote exists, auto-configure for project mode
if ! git remote get-url rspade_upstream >/dev/null 2>&1; then
# Auto-configure remote for project mode (when system/ is a submodule)
if [ "$IS_PROJECT_MODE" = false ] && [ -f "../.gitmodules" ] && grep -q "path = system" ../.gitmodules 2>/dev/null; then
echo "→ Configuring rspade_upstream remote (first-time setup)..."
UPSTREAM_URL="ssh://git@privategit.hanson.xyz:3322/brianhansonxyz/rspade_system.git"
if git remote add rspade_upstream "$UPSTREAM_URL" 2>&1; then
echo " ✓ Remote configured: $UPSTREAM_URL"
echo ""
else
echo "ERROR: Failed to configure rspade_upstream remote."
exit 1
fi
else
# Framework dev mode or can't auto-detect - require manual configuration
echo "ERROR: Remote \"rspade_upstream\" not configured."
echo ""
echo "Configure the upstream remote:"
echo " git remote add rspade_upstream <upstream-url>"
exit 1
fi
fi
echo ""
echo "=== Pull RSpade Framework Updates ==="
echo ""
# Detect current branch and set upstream branch
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>&1)
# Handle detached HEAD state (common in git submodules)
if [ "$CURRENT_BRANCH" = "HEAD" ]; then
echo "⚠ Detected detached HEAD state (git submodule)"
echo " Checking out master branch..."
if ! git checkout master 2>&1; then
echo "ERROR: Failed to checkout master branch"
exit 1
fi
CURRENT_BRANCH="master"
echo " ✓ Now on master branch"
echo ""
fi
UPSTREAM_BRANCH="rspade_upstream/${CURRENT_BRANCH}"
if [ "$IS_PROJECT_MODE" = true ]; then
echo "Mode: Project (./rsx will be preserved)"
else
echo "Mode: Framework development (./rsx will be updated)"
fi
echo "Branch: $CURRENT_BRANCH$UPSTREAM_BRANCH"
echo ""
# Remove read-only attribute from CLAUDE.dist.md before update
if [ -f "docs/CLAUDE.dist.md" ]; then
echo "→ Removing read-only attribute from docs/CLAUDE.dist.md..."
chmod u+w docs/CLAUDE.dist.md 2>/dev/null || true
fi
# Fetch updates first (before verifying branch exists)
echo "Fetching updates from rspade_upstream..."
if ! git fetch rspade_upstream 2>&1; then
echo "ERROR: Failed to fetch updates"
exit 1
fi
echo " ✓ Updates fetched"
# Verify upstream branch exists (after fetch)
if ! git rev-parse "$UPSTREAM_BRANCH" >/dev/null 2>&1; then
echo "ERROR: Upstream branch $UPSTREAM_BRANCH not found"
echo ""
echo "Current branch: $CURRENT_BRANCH"
echo "Expected upstream: $UPSTREAM_BRANCH"
echo ""
echo "Available upstream branches:"
git branch -r | grep rspade_upstream/ | sed 's/^/ /'
exit 1
fi
# Check if already up-to-date
CURRENT_HEAD=$(git rev-parse HEAD 2>&1)
UPSTREAM_HEAD=$(git rev-parse "$UPSTREAM_BRANCH" 2>&1)
if [ "$CURRENT_HEAD" = "$UPSTREAM_HEAD" ]; then
echo ""
echo "✓ Already up to date"
echo ""
exit 0
fi
# Handle --diff option: show diff and exit
if [ "$SHOW_DIFF" = true ]; then
echo ""
echo "=== Git Diff: Local Changes vs Upstream ==="
echo ""
git diff HEAD "$UPSTREAM_BRANCH"
exit 0
fi
# In project mode, check for uncommitted changes outside ./rsx
if [ "$IS_PROJECT_MODE" = true ]; then
# Get uncommitted changes (modified, staged, deleted) excluding ./rsx
UNCOMMITTED_CHANGES=$(git diff --name-only -- . ":(exclude)rsx" 2>&1)
STAGED_CHANGES=$(git diff --cached --name-only -- . ":(exclude)rsx" 2>&1)
# Combine and deduplicate (|| true prevents grep exit code 1 from killing script)
ALL_CHANGES=$(echo -e "${UNCOMMITTED_CHANGES}\n${STAGED_CHANGES}" | sort -u | grep -v "^$" || true)
if [ -n "$ALL_CHANGES" ]; then
if [ "$STASH_CHANGES" = false ]; then
echo ""
echo "════════════════════════════════════════════════════════════════════════════════"
echo "❌ ERROR: Uncommitted framework changes detected"
echo "════════════════════════════════════════════════════════════════════════════════"
echo ""
echo "You are in PROJECT MODE where ./rsx is your application repository."
echo "Framework files outside ./rsx should NEVER be modified in project mode."
echo ""
echo "The following framework files have uncommitted changes:"
echo ""
echo "$ALL_CHANGES" | sed 's/^/ /'
echo ""
echo "These changes must be resolved before updating the framework."
echo ""
echo "OPTIONS:"
echo ""
echo " 1. VIEW DIFFERENCES"
echo " See what changed between your local files and upstream:"
echo ""
echo " php artisan rsx:framework:pull --diff"
echo ""
echo " 2. STASH CHANGES AND UPDATE (recommended)"
echo " Your changes will be stashed automatically, then the framework"
echo " will be updated. You can review or restore the stashed changes after:"
echo ""
echo " php artisan rsx:framework:pull --stash"
echo ""
echo " After update, review your stashed changes:"
echo " git stash list"
echo " git stash show"
echo " git stash pop (to restore)"
echo ""
echo " 3. DISCARD FRAMEWORK CHANGES"
echo " If these were accidental edits, reset them:"
echo ""
echo " git checkout HEAD -- [file paths]"
echo ""
echo " Or reset all framework changes:"
echo " git checkout HEAD -- . ':!rsx'"
echo ""
echo " 4. COMMIT THEN UPDATE (framework developers only)"
echo " If you're intentionally modifying the framework:"
echo ""
echo " git add -A"
echo " git commit -m \"Framework modifications\""
echo " php artisan rsx:framework:pull"
echo ""
echo " 5. FORK THE FRAMEWORK"
echo " If you need to preserve framework modifications permanently:"
echo ""
echo " php artisan rsx:man framework_fork"
echo ""
echo " This documents taking full control of the framework codebase."
echo ""
echo "════════════════════════════════════════════════════════════════════════════════"
echo "Note to LLM agents:"
echo "════════════════════════════════════════════════════════════════════════════════"
echo ""
echo "You have attempted to update the framework with uncommitted changes present."
echo ""
echo "REQUIRED ACTIONS:"
echo ""
echo " 1. Show the developer the full list of uncommitted files above"
echo " 2. Run 'git diff' to show what changed in those files"
echo " 3. Ask the developer explicitly: 'These framework files were modified."
echo " Do you want me to discard these changes and update the framework,"
echo " or stash them and proceed with the update?'"
echo ""
echo "You MUST NOT proceed until the developer has made a decision."
echo "You MUST NOT automatically use --stash without explicit permission."
echo ""
echo "════════════════════════════════════════════════════════════════════════════════"
exit 1
else
# Stash changes with descriptive message
echo ""
echo "⚠️ Stashing uncommitted framework changes..."
echo ""
STASH_MESSAGE="Auto-stashed by framework update with --stash on $(date '+%Y-%m-%d %H:%M:%S')"
if ! git stash push -u -m "$STASH_MESSAGE" -- . ":(exclude)rsx" 2>&1; then
echo "ERROR: Failed to stash changes"
exit 1
fi
echo " ✓ Changes stashed"
echo ""
echo "Your changes have been saved. After update completes, you can review them:"
echo " git stash list"
echo " git stash show"
echo " git stash pop (to restore changes)"
echo ""
fi
fi
fi
# In framework mode, check for uncommitted changes
if [ "$IS_PROJECT_MODE" = false ]; then
# Get uncommitted changes (modified, staged, deleted)
UNCOMMITTED_CHANGES=$(git diff --name-only 2>&1)
STAGED_CHANGES=$(git diff --cached --name-only 2>&1)
# Combine and deduplicate (|| true prevents grep exit code 1 from killing script)
ALL_CHANGES=$(echo -e "${UNCOMMITTED_CHANGES}\n${STAGED_CHANGES}" | sort -u | grep -v "^$" || true)
if [ -n "$ALL_CHANGES" ]; then
if [ "$STASH_CHANGES" = false ]; then
echo ""
echo "════════════════════════════════════════════════════════════════════════════════"
echo "❌ ERROR: Uncommitted changes detected"
echo "════════════════════════════════════════════════════════════════════════════════"
echo ""
echo "You are in FRAMEWORK MODE where this repository is the framework itself."
echo "The following files have uncommitted changes:"
echo ""
echo "$ALL_CHANGES" | sed 's/^/ /'
echo ""
echo "These changes must be resolved before updating the framework."
echo ""
echo "OPTIONS:"
echo ""
echo " 1. VIEW DIFFERENCES"
echo " See what changed between your local files and upstream:"
echo ""
echo " php artisan rsx:framework:pull --diff"
echo ""
echo " 2. STASH CHANGES AND UPDATE (recommended)"
echo " Your changes will be stashed automatically, then the framework"
echo " will be updated. You can review or restore the stashed changes after:"
echo ""
echo " php artisan rsx:framework:pull --stash"
echo ""
echo " After update, review your stashed changes:"
echo " git stash list"
echo " git stash show"
echo " git stash pop (to restore)"
echo ""
echo " 3. DISCARD CHANGES"
echo " If these were accidental edits, reset them:"
echo ""
echo " git reset --hard HEAD"
echo ""
echo " 4. COMMIT THEN UPDATE"
echo " If you're intentionally modifying the framework:"
echo ""
echo " git add -A"
echo " git commit -m \"Framework modifications\""
echo " php artisan rsx:framework:pull"
echo ""
echo "════════════════════════════════════════════════════════════════════════════════"
exit 1
else
# Stash changes with descriptive message
echo ""
echo "⚠️ Stashing uncommitted changes..."
echo ""
STASH_MESSAGE="Auto-stashed by framework update with --stash on $(date '+%Y-%m-%d %H:%M:%S')"
if ! git stash push -u -m "$STASH_MESSAGE" 2>&1; then
echo "ERROR: Failed to stash changes"
exit 1
fi
echo " ✓ Changes stashed"
echo ""
echo "Your changes have been saved. After update completes, you can review them:"
echo " git stash list"
echo " git stash show"
echo " git stash pop (to restore changes)"
echo ""
fi
fi
fi
# Extract and display changelog
echo ""
echo "⚠ RSpade framework has upstream changes"
echo ""
# Get commit subjects, filter out Claude Code attribution and blank lines
CHANGELOG=$(git log --pretty=format:"%s" HEAD.."$UPSTREAM_BRANCH" 2>&1 | \
grep -v "Claude Code" | \
grep -v "Co-Authored-By:" | \
grep -v "^$" | \
sed 's/^/ • /')
if [ -n "$CHANGELOG" ]; then
echo "Changelog:"
echo ""
echo "$CHANGELOG"
echo ""
fi
# Show file diff stats (exclude ./rsx if in project mode)
if [ "$IS_PROJECT_MODE" = true ]; then
DIFF_STATS=$(git diff --stat HEAD.."$UPSTREAM_BRANCH" -- . ":(exclude)rsx" 2>&1)
else
DIFF_STATS=$(git diff --stat HEAD.."$UPSTREAM_BRANCH" 2>&1)
fi
if [ -n "$DIFF_STATS" ]; then
echo "Files that will be updated:"
echo ""
echo "$DIFF_STATS" | sed 's/^/ /'
echo ""
fi
# Apply updates based on mode
echo "Applying framework updates..."
if [ "$IS_PROJECT_MODE" = true ]; then
# Project mode: merge with --no-ff (preserves ./rsx via .gitattributes)
if ! git merge --no-ff "$UPSTREAM_BRANCH" 2>&1; then
echo "ERROR: Framework update failed."
echo ""
# Show current git state
GIT_STATUS=$(git status --short 2>&1)
if [ -n "$GIT_STATUS" ]; then
echo "Current git state:"
echo ""
echo "$GIT_STATUS" | sed 's/^/ /'
echo ""
fi
echo "Check status: php artisan rsx:framework:status"
exit 1
fi
echo " ✓ Framework updated (./rsx preserved by .gitattributes)"
else
# Framework mode: fast-forward only (updates everything including ./rsx)
MERGE_OUTPUT=$(git merge --ff-only "$UPSTREAM_BRANCH" 2>&1) || {
if echo "$MERGE_OUTPUT" | grep -q "up to date\|up-to-date"; then
echo " ✓ Already up to date"
else
echo "ERROR: Framework update failed."
echo ""
echo "ERROR: Cannot fast-forward: you have local modifications"
echo ""
# Show modified files
GIT_STATUS=$(git status --short 2>&1)
if [ -n "$GIT_STATUS" ]; then
echo "Modified files blocking update:"
echo ""
echo "$GIT_STATUS" | sed 's/^/ /'
echo ""
fi
echo "To resolve:"
echo ""
echo " 1. VIEW DIFFERENCES"
echo " php artisan rsx:framework:pull --diff"
echo ""
echo " 2. STASH CHANGES AND UPDATE (recommended)"
echo " php artisan rsx:framework:pull --stash"
echo ""
echo " 3. DISCARD MODIFICATIONS"
echo " git reset --hard $UPSTREAM_BRANCH"
echo ""
echo " 4. COMMIT THEN UPDATE"
echo " git add -A"
echo " git commit -m \"Framework modifications\""
echo " php artisan rsx:framework:pull"
echo ""
echo " 5. CHECK STATUS"
echo " php artisan rsx:framework:status"
exit 1
fi
}
echo " ✓ Framework updated"
fi
echo ""
echo "✓ Framework updated successfully"
echo ""
if [ "$IS_PROJECT_MODE" = true ]; then
echo "Your application code in ./rsx was preserved."
echo ""
fi
# Rebuild caches after framework update
if [ "$NO_REBUILD" = true ]; then
echo "⚠️ Cleaning caches only (--no-rebuild specified)"
echo ""
if ! php artisan rsx:clean; then
echo "ERROR: Cache clean failed"
exit 1
fi
echo " ✓ Caches cleaned"
echo ""
echo "⚠ Manifest and bundles NOT rebuilt - you must run manually:"
echo " php artisan rsx:manifest:build"
echo " php artisan rsx:bundle:compile"
echo ""
else
echo "⚠️ Regenerating framework caches"
echo ""
echo " This rebuild is only required because the framework itself was updated."
echo " Never run these commands manually unless there are catastrophic errors."
echo ""
# Step 1: Clean caches
echo "Step 1/4: Cleaning caches..."
if ! php artisan rsx:clean; then
echo "ERROR: Cache clean failed"
exit 1
fi
echo " ✓ Caches cleaned"
echo ""
# Step 2: Rebuild manifest
echo "Step 2/4: Rebuilding manifest..."
if ! php artisan rsx:manifest:build; then
echo "ERROR: Manifest rebuild failed"
echo " Run manually: php artisan rsx:manifest:build"
exit 1
fi
echo " ✓ Manifest rebuilt"
echo ""
# Step 3: Run framework migrations (before bundle compile)
echo "Step 3/4: Running framework migrations..."
if ! php artisan migrate --framework-only --force; then
echo "ERROR: Framework migrations failed"
echo " Check errors above and fix migration issues"
echo ""
echo " Bundle compilation skipped due to migration failure"
exit 1
fi
echo " ✓ Framework migrations completed"
echo ""
# Step 4: Compile bundles (only if migrations succeeded)
echo "Step 4/4: Compiling all bundles..."
if ! php artisan rsx:bundle:compile; then
echo "ERROR: Bundle compilation failed"
echo " Check errors above and fix bundle issues"
exit 1
fi
echo " ✓ All bundles compiled"
echo ""
fi
# Set read-only attribute on CLAUDE.dist.md after successful update
if [ -f "docs/CLAUDE.dist.md" ]; then
echo "→ Setting read-only attribute on docs/CLAUDE.dist.md..."
chmod u-w docs/CLAUDE.dist.md 2>/dev/null || true
echo " ✓ Framework documentation marked read-only"
echo ""
fi
echo "✅ Framework update complete"
echo ""
exit 0