#!/bin/bash # NFOGuard Dual-Repo Development Sync Script # Manages Gitea (private) + GitHub (public) workflow with AI attribution prevention # CONFIDENTIAL: Stays in .local/ directory only set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration GITEA_REMOTE="origin" # gitea@192.168.253.221:sbcrumb/nfoguard.git GITHUB_REMOTE="github" # git@github.com:sbcrumb/nfoguard.git VERSION_FILE="VERSION" # Helper functions log_info() { echo -e "${BLUE}ℹ️ $1${NC}"; } log_success() { echo -e "${GREEN}✅ $1${NC}"; } log_warning() { echo -e "${YELLOW}⚠️ $1${NC}"; } log_error() { echo -e "${RED}❌ $1${NC}"; } # Help with common mistakes and suggestions suggest_command() { local input="$1" local suggestions=() # Common typos and alternatives case "$input" in "create"|"new"|"begin"|"init") suggestions+=("start") ;; "merge"|"pr"|"pullrequest"|"pull-request") suggestions+=("github-pr") ;; "clean"|"cleanup"|"remove"|"delete") suggestions+=("cleanup-github-pr") ;; "check"|"info"|"show") suggestions+=("status") ;; "push"|"deploy"|"publish") suggestions+=("release") ;; "dev"|"develop"|"development") suggestions+=("test") ;; "help"|"--help"|"-h") suggestions+=("(see help below)") ;; "ver"|"version") suggestions+=("version") ;; *) # Fuzzy matching for close commands local commands=("start" "test" "release" "github-pr" "cleanup-github-pr" "sync" "version" "status") for cmd in "${commands[@]}"; do # Simple character similarity check if [[ ${#input} -gt 2 && "$cmd" == *"$input"* ]]; then suggestions+=("$cmd") fi done ;; esac if [[ ${#suggestions[@]} -gt 0 ]]; then log_warning "Unknown command: '$input'" echo "" echo "Did you mean:" for suggestion in "${suggestions[@]}"; do echo " $0 $suggestion" done echo "" fi } # Check if feature branch exists and suggest next steps check_feature_status() { local feature_name="$1" local branch_name="feature/$feature_name" # Check if feature branch exists locally if git branch | grep -q " $branch_name$"; then log_info "Feature branch '$branch_name' exists locally" # Check if it's been tested (merged to dev) if git branch -r | grep -q "$GITEA_REMOTE/dev"; then git fetch "$GITEA_REMOTE" dev >/dev/null 2>&1 if git merge-base --is-ancestor "$branch_name" "$GITEA_REMOTE/dev" 2>/dev/null; then log_success "Feature has been tested on dev branch" log_info "Next step: $0 release $feature_name [patch|minor|major]" else log_warning "Feature not yet tested on dev branch" log_info "Next step: $0 test $feature_name" fi fi # Check if it's been released (merged to main) if git branch -r | grep -q "$GITEA_REMOTE/main"; then git fetch "$GITEA_REMOTE" main >/dev/null 2>&1 if git merge-base --is-ancestor "$branch_name" "$GITEA_REMOTE/main" 2>/dev/null; then log_success "Feature has been released to Gitea main" log_info "Next step: $0 github-pr $feature_name" fi fi else log_warning "Feature branch '$branch_name' does not exist" log_info "Start with: $0 start $feature_name" fi } # Show current workflow state and suggest next action show_workflow_guidance() { log_info "🔄 Current Workflow State Analysis" echo "" # Check for existing feature branches local feature_branches=($(git branch | grep "feature/" | sed 's/^[* ]*//' | sed 's/feature\///')) if [[ ${#feature_branches[@]} -eq 0 ]]; then log_info "No active feature branches found" log_info "Start a new feature: $0 start " echo "" echo "Example: $0 start user-dashboard" return fi log_info "Active feature branches:" for feature in "${feature_branches[@]}"; do echo " 📝 $feature" check_feature_status "$feature" echo "" done log_info "Quick reference:" echo " $0 test # Test feature on Gitea dev" echo " $0 release # Release to Gitea main" echo " $0 github-pr # Create GitHub PR workflow" } # Get current version get_version() { if [[ -f "$VERSION_FILE" ]]; then cat "$VERSION_FILE" | tr -d '\n' else echo "1.0.0" fi } # Bump version bump_version() { local current_version=$(get_version) local version_type=$1 IFS='.' read -ra VERSION_PARTS <<< "$current_version" local major=${VERSION_PARTS[0]} local minor=${VERSION_PARTS[1]} local patch=${VERSION_PARTS[2]} case $version_type in "patch"|"p") patch=$((patch + 1)) ;; "minor"|"m") minor=$((minor + 1)) patch=0 ;; "major"|"M") major=$((major + 1)) minor=0 patch=0 ;; *) log_error "Invalid version type. Use: patch|p, minor|m, major|M" exit 1 ;; esac local new_version="$major.$minor.$patch" echo "$new_version" > "$VERSION_FILE" log_success "Version bumped: $current_version → $new_version" echo "$new_version" } # Check if working directory is clean check_clean() { if [[ -n $(git status --porcelain) ]]; then log_error "Working directory not clean. Commit or stash changes first." exit 1 fi } # Ensure we're on the right branch ensure_branch() { local target_branch=$1 local current_branch=$(git branch --show-current) if [[ "$current_branch" != "$target_branch" ]]; then log_info "Switching to $target_branch" git checkout "$target_branch" fi } # Sync all branches to match a source sync_all_branches() { local source_remote=$1 local source_branch=$2 log_info "Syncing all branches to match $source_remote/$source_branch" # Fetch all remotes git fetch "$GITEA_REMOTE" git fetch "$GITHUB_REMOTE" # Reset local main to source ensure_branch main git reset --hard "$source_remote/$source_branch" # Push to Gitea (includes .local and .gitea) log_info "Pushing to Gitea main and dev..." git push "$GITEA_REMOTE" main --force-with-lease git push "$GITEA_REMOTE" main:dev --force-with-lease # For GitHub: create clean branch without .local and .gitea AND without AI attribution log_info "Creating GitHub-safe branch (excluding .local, .gitea, and AI attribution)..." local temp_branch="github-sync-temp" git checkout -b "$temp_branch" # Remove private directories if they exist if [[ -d ".local" ]]; then git rm -rf .local --cached 2>/dev/null || true fi if [[ -d ".gitea" ]]; then git rm -rf .gitea --cached 2>/dev/null || true fi # Commit the clean version for GitHub if [[ -n $(git status --porcelain) ]]; then git add . git commit -m "sync: Clean version for GitHub (excludes private dev files)" fi # Strip AI attribution AND fix author names from the entire branch history log_info "Removing AI attribution and fixing author names..." git filter-branch -f --env-filter ' # Fix author name/email for any commits by jskala or Claude if [ "$GIT_AUTHOR_NAME" = "jskala" ] || [ "$GIT_AUTHOR_EMAIL" = "jskala@gmail.com" ] || [ "$GIT_AUTHOR_NAME" = "Claude" ] || [ "$GIT_AUTHOR_EMAIL" = "noreply@anthropic.com" ]; then export GIT_AUTHOR_NAME="SBCrumb" export GIT_AUTHOR_EMAIL="sbcrumb@kickthetree.com" fi # Fix committer name/email for any commits by jskala or Claude if [ "$GIT_COMMITTER_NAME" = "jskala" ] || [ "$GIT_COMMITTER_EMAIL" = "jskala@gmail.com" ] || [ "$GIT_COMMITTER_NAME" = "Claude" ] || [ "$GIT_COMMITTER_EMAIL" = "noreply@anthropic.com" ]; then export GIT_COMMITTER_NAME="SBCrumb" export GIT_COMMITTER_EMAIL="sbcrumb@kickthetree.com" fi ' --msg-filter ' # Remove Claude co-authorship lines from commit messages sed "/Co-Authored-By: Claude/d" | sed "/Generated with.*Claude/d" | sed "/🤖 Generated with.*Claude/d" | # Remove any other AI references sed "/noreply@anthropic.com/d" ' -- "$temp_branch" 2>/dev/null || log_warning "Filter-branch may not be needed" log_info "Pushing to GitHub main and dev..." git push "$GITHUB_REMOTE" "$temp_branch:main" --force-with-lease git push "$GITHUB_REMOTE" "$temp_branch:dev" --force-with-lease # Clean up temp branch and return to main git checkout main git branch -D "$temp_branch" log_success "All branches synchronized with privacy respected!" log_warning "Note: .local and .gitea stay on Gitea only (as intended)" } # Start feature development start_feature() { local feature_name=$1 if [[ -z "$feature_name" ]]; then log_error "Feature name required" echo "" echo "Usage: $0 start " echo "" echo "Examples:" echo " $0 start user-dashboard # Creates feature/user-dashboard" echo " $0 start api-improvements # Creates feature/api-improvements" echo " $0 start bug-fix-login # Creates feature/bug-fix-login" echo "" log_info "💡 Use descriptive names with hyphens (no spaces)" exit 1 fi check_clean ensure_branch main # Create feature branch local branch_name="feature/$feature_name" log_info "Creating feature branch: $branch_name" git checkout -b "$branch_name" log_success "Feature branch '$branch_name' created. Start coding!" echo "" log_info "🔄 Next steps in workflow:" echo " 1. Make your changes and commit them" echo " 2. When ready for testing: $0 test $feature_name" echo " 3. After testing: $0 release $feature_name [patch|minor|major]" echo " 4. For GitHub PR: $0 github-pr $feature_name" } # Test feature (merge to dev) test_feature() { local feature_name=$1 if [[ -z "$feature_name" ]]; then log_error "Feature name required" echo "Usage: $0 test " exit 1 fi check_clean local branch_name="feature/$feature_name" # Merge to Gitea dev for testing ensure_branch dev git fetch "$GITEA_REMOTE" git reset --hard "$GITEA_REMOTE/dev" log_info "Merging $branch_name to dev for testing" git merge "$branch_name" --no-ff -m "test: Merge $feature_name for local testing" git push "$GITEA_REMOTE" dev log_success "Feature merged to Gitea dev for testing" echo "" log_info "🧪 Testing Phase:" echo " • Test your changes thoroughly on the dev environment" echo " • Check logs, functionality, and integration" echo " • When satisfied: $0 release $feature_name [patch|minor|major]" echo "" log_warning "💡 Remember: Once released to main, it goes to GitHub PR workflow" } # Release feature (merge to Gitea main, then create GitHub PR workflow) release_feature() { local feature_name=$1 local version_type=${2:-"patch"} if [[ -z "$feature_name" ]]; then log_error "Feature name required" echo "Usage: $0 release [patch|minor|major]" exit 1 fi check_clean # Bump version local new_version=$(bump_version "$version_type") # Merge to Gitea main ensure_branch main git fetch "$GITEA_REMOTE" git reset --hard "$GITEA_REMOTE/main" local branch_name="feature/$feature_name" log_info "Merging $branch_name to Gitea main" git merge "$branch_name" --no-ff -m "feat: $feature_name (v$new_version)" # Push to Gitea main git push "$GITEA_REMOTE" main log_success "Feature '$feature_name' merged to Gitea main as v$new_version" echo "" log_info "🚀 Ready for GitHub PR Workflow!" echo " Phase 1 (Gitea) Complete ✅" echo "" log_info "📋 Next steps:" echo " 1. $0 github-pr $feature_name" echo " 2. Go to GitHub → Create PR: github-feature/$feature_name → dev" echo " 3. Test on GitHub dev environment" echo " 4. Create PR: dev → main" echo " 5. $0 cleanup-github-pr $feature_name" echo "" log_warning "💡 The github-pr command will automatically exclude .local/.gitea files" } # Create GitHub PR workflow branch github_pr() { local feature_name=$1 if [[ -z "$feature_name" ]]; then log_error "Feature name required" echo "Usage: $0 github-pr " exit 1 fi check_clean # Create clean GitHub feature branch from current Gitea main log_info "Creating GitHub PR workflow for feature: $feature_name" ensure_branch main git fetch "$GITEA_REMOTE" git reset --hard "$GITEA_REMOTE/main" # Create GitHub-safe feature branch local github_branch="github-feature/$feature_name" git checkout -b "$github_branch" # Remove private directories for GitHub if [[ -d ".local" ]]; then git rm -rf .local --cached 2>/dev/null || true fi if [[ -d ".gitea" ]]; then git rm -rf .gitea --cached 2>/dev/null || true fi # Commit clean version if changes exist if [[ -n $(git status --porcelain) ]]; then git add . git commit -m "github: Clean feature branch for $feature_name PR workflow" fi # Apply privacy filters log_info "Applying privacy filters..." git filter-branch -f --env-filter ' if [ "$GIT_AUTHOR_NAME" = "jskala" ] || [ "$GIT_AUTHOR_EMAIL" = "jskala@gmail.com" ] || [ "$GIT_AUTHOR_NAME" = "Claude" ] || [ "$GIT_AUTHOR_EMAIL" = "noreply@anthropic.com" ]; then export GIT_AUTHOR_NAME="SBCrumb" export GIT_AUTHOR_EMAIL="sbcrumb@kickthetree.com" fi if [ "$GIT_COMMITTER_NAME" = "jskala" ] || [ "$GIT_COMMITTER_EMAIL" = "jskala@gmail.com" ] || [ "$GIT_COMMITTER_NAME" = "Claude" ] || [ "$GIT_COMMITTER_EMAIL" = "noreply@anthropic.com" ]; then export GIT_COMMITTER_NAME="SBCrumb" export GIT_COMMITTER_EMAIL="sbcrumb@kickthetree.com" fi ' --msg-filter ' sed "/Co-Authored-By: Claude/d" | sed "/Generated with.*Claude/d" | sed "/🤖 Generated with.*Claude/d" | sed "/noreply@anthropic.com/d" ' -- "$github_branch" 2>/dev/null || log_warning "Filter-branch may not be needed" # Push feature branch to GitHub git push "$GITHUB_REMOTE" "$github_branch" --force-with-lease # Also update GitHub dev branch git push "$GITHUB_REMOTE" "$github_branch:dev" --force-with-lease log_success "GitHub feature branch created: $github_branch" log_info "Next steps:" log_info " 1. Go to GitHub and create PR: $github_branch → dev" log_info " 2. Test the changes on github-dev" log_info " 3. Create PR: dev → main" log_info " 4. Run: $0 cleanup-github-pr $feature_name" # Return to main and cleanup git checkout main git branch -D "$github_branch" } # Cleanup after GitHub PR workflow cleanup_github_pr() { local feature_name=$1 if [[ -z "$feature_name" ]]; then log_error "Feature name required" echo "Usage: $0 cleanup-github-pr " exit 1 fi # Cleanup local feature branch local branch_name="feature/$feature_name" git branch -d "$branch_name" 2>/dev/null || true # Cleanup remote branches git push "$GITEA_REMOTE" --delete "$branch_name" 2>/dev/null || true git push "$GITHUB_REMOTE" --delete "github-feature/$feature_name" 2>/dev/null || true log_success "Cleaned up feature branches for: $feature_name" } # Show status show_status() { log_info "Current Repository Status" echo "" # Current branch and version local current_branch=$(git branch --show-current) local current_version=$(get_version) echo "📍 Current Branch: $current_branch" echo "🔢 Current Version: $current_version" echo "" # Remote status git fetch "$GITEA_REMOTE" >/dev/null 2>&1 git fetch "$GITHUB_REMOTE" >/dev/null 2>&1 echo "🔄 Branch Status:" echo " Local main: $(git rev-parse --short main)" echo " Gitea main: $(git rev-parse --short $GITEA_REMOTE/main)" echo " Gitea dev: $(git rev-parse --short $GITEA_REMOTE/dev)" echo " GitHub main: $(git rev-parse --short $GITHUB_REMOTE/main)" echo " GitHub dev: $(git rev-parse --short $GITHUB_REMOTE/dev)" echo "" # Check if synchronized local gitea_main=$(git rev-parse $GITEA_REMOTE/main) local gitea_dev=$(git rev-parse $GITEA_REMOTE/dev) local github_main=$(git rev-parse $GITHUB_REMOTE/main) local github_dev=$(git rev-parse $GITHUB_REMOTE/dev) if [[ "$gitea_main" == "$gitea_dev" && "$gitea_main" == "$github_main" && "$gitea_main" == "$github_dev" ]]; then log_success "All branches are synchronized! ✨" else log_warning "Branches are not synchronized" echo " Run: $0 sync to synchronize all branches" fi echo "" show_workflow_guidance } # Main script logic case "${1:-status}" in "start") start_feature "$2" ;; "test") test_feature "$2" ;; "release") release_feature "$2" "$3" ;; "github-pr") github_pr "$2" ;; "cleanup-github-pr") cleanup_github_pr "$2" ;; "sync") sync_all_branches "$GITEA_REMOTE" main ;; "version") if [[ -n "$2" ]]; then bump_version "$2" else echo "Current version: $(get_version)" fi ;; "status"|"") show_status ;; *) # Check if it's a typo or unknown command and suggest alternatives suggest_command "$1" echo "NFOGuard Development Sync Script" echo "" echo "Usage: $0 [options]" echo "" log_info "Available Commands:" echo " start Create new feature branch" echo " test Merge feature to dev for testing" echo " release [type] Merge to Gitea main (patch|minor|major)" echo " github-pr Create GitHub PR workflow branch" echo " cleanup-github-pr Cleanup after GitHub PR workflow" echo " sync Sync all branches to Gitea main" echo " version [type] Show or bump version" echo " status Show repository status (default)" echo "" log_info "Professional Git Workflow:" echo " # 🔧 Gitea Development (Private)" echo " $0 start user-auth # Create feature branch" echo " # ... develop feature ..." echo " $0 test user-auth # Test on Gitea dev" echo " $0 release user-auth minor # Merge to Gitea main" echo "" echo " # 🚀 GitHub PR Workflow (Public)" echo " $0 github-pr user-auth # Create clean GitHub branch" echo " # Create PR: github-feature/user-auth → github-dev" echo " # Test, then create PR: github-dev → github-main" echo " $0 cleanup-github-pr user-auth # Cleanup branches" echo "" log_info "Common Aliases:" echo " create, new, init → start" echo " merge, pr, pull-request → github-pr" echo " clean, cleanup, remove → cleanup-github-pr" echo " check, info, show → status" echo " push, deploy, publish → release" echo " dev, develop → test" echo "" log_warning "💡 Tip: Run '$0 status' to see your current workflow state and next steps" ;; esac