diff --git a/.local/sync-script.sh b/.local/sync-script.sh new file mode 100755 index 0000000..995b6cb --- /dev/null +++ b/.local/sync-script.sh @@ -0,0 +1,602 @@ +#!/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 \ No newline at end of file