From dadbe231698b71a5b1c8b6f13840d29e4c2d8122 Mon Sep 17 00:00:00 2001 From: j Date: Mon, 9 Mar 2026 07:07:44 +1300 Subject: [PATCH] Add --no-wait flag and CI workflow polling after push via Gitea Actions API --- gp | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/gp b/gp index cbed3ac..99b7e1b 100755 --- a/gp +++ b/gp @@ -34,6 +34,7 @@ OPTIONS: -b, --branch Specify branch to push to (default: current branch) -a, --add-all Add all files including untracked (default) --staged-only Only commit staged changes (don't add untracked files) + --no-wait Don't wait for CI workflow to complete after push ARGUMENTS: message Optional custom commit message (overrides auto-generation) @@ -476,6 +477,139 @@ show_status_and_confirm() { fi } +# Function to extract Gitea API URL and token from git remote +# Returns: sets GITEA_API_BASE and GITEA_API_TOKEN, or returns 1 +parse_gitea_remote() { + local remote_url + remote_url=$(git remote get-url origin 2>/dev/null) || return 1 + + # Match https://TOKEN@HOST/OWNER/REPO.git or https://HOST/OWNER/REPO.git + local token="" host="" owner="" repo="" + if [[ "$remote_url" =~ ^https://([^@]+)@([^/]+)/([^/]+)/([^/]+?)(\.git)?$ ]]; then + token="${BASH_REMATCH[1]}" + host="${BASH_REMATCH[2]}" + owner="${BASH_REMATCH[3]}" + repo="${BASH_REMATCH[4]}" + elif [[ "$remote_url" =~ ^https://([^/]+)/([^/]+)/([^/]+?)(\.git)?$ ]]; then + host="${BASH_REMATCH[1]}" + owner="${BASH_REMATCH[2]}" + repo="${BASH_REMATCH[3]}" + else + return 1 + fi + + # Only support Gitea instances (not github.com etc) + case "$host" in + github.com|gitlab.com|bitbucket.org) return 1 ;; + esac + + # Use GITEA_TOKEN env var if no token in URL + [ -z "$token" ] && token="${GITEA_TOKEN:-}" + [ -z "$token" ] && return 1 + + GITEA_API_BASE="https://${host}/api/v1/repos/${owner}/${repo}" + GITEA_API_TOKEN="$token" + GITEA_HTML_BASE="https://${host}/${owner}/${repo}" + return 0 +} + +# Function to wait for CI workflow to complete after push +# Polls Gitea Actions API, shows spinner, ESC cancels +wait_for_workflow() { + local head_sha="$1" + local branch="$2" + + if ! parse_gitea_remote; then + return 0 # silently skip if not a Gitea remote or no token + fi + + local spinner_chars='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' + local spin_idx=0 + local poll_interval=3 + local max_wait=600 # 10 minutes max + local elapsed=0 + + echo + print_info "Waiting for CI workflow... (press ESC to stop waiting)" + + # Save terminal settings and enable non-blocking input + local old_tty + old_tty=$(stty -g 2>/dev/null) || true + stty -echo -icanon min 0 time 0 2>/dev/null || true + + # Restore terminal on exit + trap 'stty "$old_tty" 2>/dev/null; trap - RETURN' RETURN + + local run_url="" status="" conclusion="" found=false + + while [ "$elapsed" -lt "$max_wait" ]; do + # Check for ESC key + local key="" + key=$(dd bs=1 count=1 2>/dev/null) || true + if [ "$key" = $'\x1b' ]; then + # Drain any remaining escape sequence bytes + dd bs=1 count=5 2>/dev/null >/dev/null || true + echo -e "\r\033[K" + print_info "Stopped waiting (CI still running in background)" + [ -n "$run_url" ] && print_info "View: $run_url" + return 0 + fi + + # Poll API + local response + response=$(curl -sf "${GITEA_API_BASE}/actions/runs?limit=5&token=${GITEA_API_TOKEN}" 2>/dev/null) || true + + if [ -n "$response" ]; then + # Find the run matching our commit SHA + local run_info + run_info=$(echo "$response" | python3 -c " +import json, sys +d = json.load(sys.stdin) +for r in d.get('workflow_runs', []): + if r.get('head_sha','').startswith('${head_sha}'): + print(r['status'], r.get('conclusion',''), r.get('html_url','')) + break +" 2>/dev/null) || true + + if [ -n "$run_info" ]; then + found=true + status=$(echo "$run_info" | awk '{print $1}') + conclusion=$(echo "$run_info" | awk '{print $2}') + run_url=$(echo "$run_info" | awk '{print $3}') + + if [ "$status" = "completed" ]; then + echo -e "\r\033[K" + if [ "$conclusion" = "success" ]; then + print_success "CI workflow passed" + else + print_error "CI workflow failed (conclusion: $conclusion)" + [ -n "$run_url" ] && print_info "View: $run_url" + return 1 + fi + return 0 + fi + fi + fi + + # Show spinner + local spin_char="${spinner_chars:$spin_idx:1}" + spin_idx=$(( (spin_idx + 1) % ${#spinner_chars} )) + local status_msg="waiting" + $found && status_msg="$status" + local mins=$((elapsed / 60)) + local secs=$((elapsed % 60)) + printf "\r %s CI %s (%d:%02d)" "$spin_char" "$status_msg" "$mins" "$secs" + + sleep "$poll_interval" + elapsed=$((elapsed + poll_interval)) + done + + echo -e "\r\033[K" + print_warning "Timed out waiting for CI (${max_wait}s)" + [ -n "$run_url" ] && print_info "View: $run_url" + return 0 +} + # Function to perform the actual commit and push do_commit_and_push() { local commit_msg="$1" @@ -493,6 +627,8 @@ do_commit_and_push() { # Push print_info "Pushing to $target_branch..." + local push_sha + push_sha=$(git rev-parse HEAD) # Check if branch exists on remote to determine if we need -u flag if git ls-remote --exit-code --heads origin "$target_branch" >/dev/null 2>&1; then # Branch exists on remote, normal push @@ -513,13 +649,18 @@ do_commit_and_push() { exit 1 fi fi + + # Wait for CI workflow if enabled + if [ "$WAIT_FOR_CI" = true ]; then + wait_for_workflow "$push_sha" "$target_branch" || true + fi } # Autocomplete function for bash autocomplete() { local args=("$@") if [ ${#args[@]} -eq 0 ]; then - printf "%s\n" "--help" "--dry-run" "--force" "--yes" "--add-all" "--staged-only" "--branch" "-h" "-n" "-f" "-y" "-a" "-b" + printf "%s\n" "--help" "--dry-run" "--force" "--yes" "--add-all" "--staged-only" "--no-wait" "--branch" "-h" "-n" "-f" "-y" "-a" "-b" fi } @@ -531,6 +672,7 @@ ADD_ALL=true # Default to adding all files CUSTOM_MESSAGE="" TARGET_BRANCH="" PUSH_ONLY=false +WAIT_FOR_CI=true # Handle special commands first case "${1:-}" in @@ -572,6 +714,10 @@ while [[ $# -gt 0 ]]; do ADD_ALL=false shift ;; + --no-wait) + WAIT_FOR_CI=false + shift + ;; -b|--branch) if [ -z "${2:-}" ]; then print_error "Option -b/--branch requires a branch name" @@ -628,6 +774,8 @@ main() { fi # Execute push only + local push_sha + push_sha=$(git rev-parse HEAD) print_info "Pushing existing commits to $TARGET_BRANCH..." # Check if branch exists on remote to determine if we need -u flag if git ls-remote --exit-code --heads origin "$TARGET_BRANCH" >/dev/null 2>&1; then @@ -649,6 +797,10 @@ main() { exit 1 fi fi + # Wait for CI workflow if enabled + if [ "$WAIT_FOR_CI" = true ]; then + wait_for_workflow "$push_sha" "$TARGET_BRANCH" || true + fi exit 0 fi