From fc382907c8ff17c9f7f5fbdfd2ac04a3327f742f Mon Sep 17 00:00:00 2001 From: Lilith Date: Wed, 21 Jan 2026 11:39:36 -0800 Subject: [PATCH] =?UTF-8?q?chore(git):=20=F0=9F=94=A7=20Update=20package?= =?UTF-8?q?=20repo=20initialization=20script=20logic=20in=20init-package-r?= =?UTF-8?q?epos.sh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- git/init-package-repos.sh | 308 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100755 git/init-package-repos.sh diff --git a/git/init-package-repos.sh b/git/init-package-repos.sh new file mode 100755 index 0000000..57cba19 --- /dev/null +++ b/git/init-package-repos.sh @@ -0,0 +1,308 @@ +#!/usr/bin/env bash +# +# init-package-repos.sh - Initialize git repos for packages and push to Forgejo +# +# Usage: +# init-package-repos.sh [options] +# 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.nasty.sh:2222 +# +# Remote URL derivation: +# @lilith/esm-fix → ssh://git@forge.nasty.sh: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.nasty.sh: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: https://forge.nasty.sh/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] " + echo " init-package-repos.sh --batch < packages.txt" + echo "" + echo "Use --help for more information" + exit 1 +fi