308 lines
7.6 KiB
Bash
Executable file
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
|