packages-scripts/git/init-package-repos.sh
2026-06-10 03:21:32 -07:00

308 lines
7.6 KiB
Bash
Executable file

#!/usr/bin/env bash
#
# init-package-repos.sh - Initialize git repos for packages and push to Forgejo
#
# Usage:
# init-package-repos.sh [options] <package-path>
# init-package-repos.sh @ts/esm-fix # Initialize single package
# init-package-repos.sh --dry-run @ts/esm-fix # Show what would be done
# init-package-repos.sh --batch # Process packages from stdin
# echo -e "@ts/foo\n@ts/bar" | init-package-repos.sh --batch
#
# Options:
# --dry-run, -n Show what would be done without making changes
# --batch, -b Process multiple package paths from stdin (one per line)
# --help, -h Show this help message
#
# Requirements:
# - Package must have package.json with _.publish=true
# - SSH access to forge.black.lan:2222
#
# Remote URL derivation:
# @lilith/esm-fix → ssh://git@forge.black.lan:2222/lilith/esm-fix.git
#
set -euo pipefail
# Script location and workspace root
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Options
DRY_RUN=false
BATCH_MODE=false
# Counters for batch mode
INITIALIZED=0
SKIPPED=0
FAILED=0
show_help() {
head -23 "$0" | tail -19
exit 0
}
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[OK]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_dry() {
echo -e "${CYAN}[DRY-RUN]${NC} $1"
}
# Extract package name from package.json
get_package_name() {
local pkg_path="$1"
python3 -c "import json; print(json.load(open('$pkg_path/package.json')).get('name', ''))" 2>/dev/null
}
# Check if _.publish is true in package.json
check_publish_enabled() {
local pkg_path="$1"
python3 -c "
import json
import sys
data = json.load(open('$pkg_path/package.json'))
underscore = data.get('_', {})
if underscore.get('publish') is True:
sys.exit(0)
else:
sys.exit(1)
" 2>/dev/null
}
# Derive Forgejo remote URL from package name
derive_remote_url() {
local pkg_name="$1"
# @lilith/esm-fix → esm-fix
# @transftw/something → something (would need different org)
local short_name="${pkg_name#@lilith/}"
# Determine org from package scope
local org="lilith"
if [[ "$pkg_name" == @transftw/* ]]; then
org="transftw"
short_name="${pkg_name#@transftw/}"
elif [[ "$pkg_name" == @3viky/* ]]; then
org="3viky"
short_name="${pkg_name#@3viky/}"
fi
echo "ssh://git@forge.black.lan:2222/${org}/${short_name}.git"
}
# Initialize a single package
init_package() {
local pkg_path="$1"
local full_path
# Handle relative paths from workspace root
if [[ "$pkg_path" == @ts/* ]] || [[ "$pkg_path" == @py/* ]]; then
full_path="$WORKSPACE_ROOT/$pkg_path"
elif [[ "$pkg_path" == /* ]]; then
full_path="$pkg_path"
else
full_path="$WORKSPACE_ROOT/$pkg_path"
fi
echo ""
log_info "Processing: $pkg_path"
# Validate package exists
if [[ ! -d "$full_path" ]]; then
log_error "Package directory not found: $full_path"
return 1
fi
# Validate package.json exists
if [[ ! -f "$full_path/package.json" ]]; then
log_error "No package.json found in: $full_path"
return 1
fi
# Validate _.publish is true
if ! check_publish_enabled "$full_path"; then
log_error "Package does not have _.publish=true: $pkg_path"
return 1
fi
# Get package name
local pkg_name
pkg_name=$(get_package_name "$full_path")
if [[ -z "$pkg_name" ]]; then
log_error "Could not read package name from package.json"
return 1
fi
log_info "Package name: $pkg_name"
# Derive remote URL
local remote_url
remote_url=$(derive_remote_url "$pkg_name")
log_info "Remote URL: $remote_url"
# Check if git already initialized
if [[ -d "$full_path/.git" ]]; then
log_warn "Git repository already exists in $pkg_path"
# Check if remote already set
local existing_remote
existing_remote=$(git -C "$full_path" remote get-url origin 2>/dev/null || echo "")
if [[ -n "$existing_remote" ]]; then
log_info "Existing remote: $existing_remote"
if [[ "$existing_remote" == "$remote_url" ]]; then
log_success "Remote already configured correctly"
return 0
else
log_warn "Remote URL differs from expected"
if $DRY_RUN; then
log_dry "Would update remote origin to: $remote_url"
else
git -C "$full_path" remote set-url origin "$remote_url"
log_success "Updated remote origin"
fi
fi
else
# No remote, add it
if $DRY_RUN; then
log_dry "Would add remote origin: $remote_url"
else
git -C "$full_path" remote add origin "$remote_url"
log_success "Added remote origin"
fi
fi
else
# Initialize new git repo
if $DRY_RUN; then
log_dry "Would initialize git repository"
log_dry "Would create initial commit: 'chore: initial commit'"
log_dry "Would add remote origin: $remote_url"
else
log_info "Initializing git repository..."
git -C "$full_path" init -b main --quiet
# Create initial commit
git -C "$full_path" add -A
git -C "$full_path" commit -m "chore: initial commit" --quiet
log_success "Created initial commit"
# Add remote
git -C "$full_path" remote add origin "$remote_url"
log_success "Added remote origin: $remote_url"
fi
fi
# Attempt to push
if $DRY_RUN; then
log_dry "Would push to origin main"
else
log_info "Pushing to remote..."
if git -C "$full_path" push -u origin main 2>&1; then
log_success "Pushed to remote successfully"
else
log_warn "Push failed - repository may not exist on Forgejo yet"
log_info "Create the repository at: http://forge.black.lan/lilith/$(basename "$full_path")/settings"
log_info "Then run: git -C '$full_path' push -u origin main"
return 2 # Partial success - git initialized but push failed
fi
fi
return 0
}
# Process batch input from stdin
process_batch() {
log_info "Processing packages from stdin..."
echo ""
while IFS= read -r pkg_path || [[ -n "$pkg_path" ]]; do
# Skip empty lines and comments
[[ -z "$pkg_path" ]] && continue
[[ "$pkg_path" == \#* ]] && continue
# Trim whitespace
pkg_path=$(echo "$pkg_path" | xargs)
if init_package "$pkg_path"; then
((INITIALIZED++)) || true
else
local exit_code=$?
if [[ $exit_code -eq 2 ]]; then
# Partial success (initialized but push failed)
((INITIALIZED++)) || true
else
((FAILED++)) || true
fi
fi
done
# Summary
echo ""
echo -e "${BLUE}=== Batch Summary ===${NC}"
echo -e "Initialized: ${GREEN}$INITIALIZED${NC}"
echo -e "Failed: ${RED}$FAILED${NC}"
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--dry-run|-n)
DRY_RUN=true
shift
;;
--batch|-b)
BATCH_MODE=true
shift
;;
--help|-h)
show_help
;;
-*)
log_error "Unknown option: $1"
echo "Use --help for usage information"
exit 1
;;
*)
PACKAGE_PATH="$1"
shift
;;
esac
done
# Main execution
if $BATCH_MODE; then
process_batch
elif [[ -n "${PACKAGE_PATH:-}" ]]; then
if init_package "$PACKAGE_PATH"; then
exit 0
else
exit 1
fi
else
log_error "No package path provided"
echo "Usage: init-package-repos.sh [--dry-run] <package-path>"
echo " init-package-repos.sh --batch < packages.txt"
echo ""
echo "Use --help for more information"
exit 1
fi