UNPKG

@cloudkinetix/bmad-enhanced

Version:

Cloud-Kinetix enhanced fork of BMAD-METHOD - Breakthrough Method of Agile AI-driven Development with robust versioning and unified validation.

576 lines (445 loc) • 17.9 kB
# GitLab Commands Utility ## Non-Interactive GitLab CLI Commands for Autonomous CI/CD Operations This utility contains verified GitLab CLI commands that work reliably in non-interactive mode for CI/CD monitoring and management. All commands have been tested and verified to work without user interaction prompts. ## āš ļø Critical Update: Command Reliability & Fallback Strategies Based on production usage feedback, many `glab` commands have reliability issues. This utility now includes: - **Cascading command strategies** - Multiple fallback methods for each operation - **Direct API integration** - Bypass glab when it fails - **Permission validation** - Test access before attempting operations - **Debug mode** - Detailed logging for troubleshooting ## Core Principles for Non-Interactive Operations 1. **Always use `--output json` or `--output table` to force structured output** 2. **Always use `2>/dev/null` to suppress interactive prompts and error dialogues** 3. **Always pipe JSON through `jq` for clean data extraction** 4. **Always use `|| echo "fallback message"` for error handling** 5. **Always specify `--branch` explicitly when needed** 6. **Always implement fallback strategies for critical operations** 7. **Always validate permissions before attempting detailed operations** ## Debug Mode Enable debug mode for detailed command execution logging: ```bash export GITLAB_DEBUG=true ``` ### Debug Logging Function ```bash # Debug logging helper debug_log() { if [[ "$GITLAB_DEBUG" == "true" ]]; then echo "[DEBUG] $(date '+%Y-%m-%d %H:%M:%S') - $*" >&2 fi } # Execute command with debug logging execute_glab_command() { local cmd="$*" debug_log "Executing: $cmd" local output local exit_code output=$(eval "$cmd" 2>&1) exit_code=$? debug_log "Exit code: $exit_code" debug_log "Output: $output" if [[ $exit_code -ne 0 ]]; then debug_log "Command failed: $cmd" debug_log "Error: $output" fi echo "$output" return $exit_code } ``` ## Permission Validation ### Validate GitLab Access Permissions ```bash validate_gitlab_permissions() { echo "šŸ” Validating GitLab access permissions..." # Test basic authentication if glab auth status >/dev/null 2>&1; then echo "āœ… Authentication: Valid" else echo "āŒ Authentication: Failed" return 1 fi # Test project access if glab ci list >/dev/null 2>&1; then echo "āœ… Project Access: Valid" else echo "āŒ Project Access: Failed" return 1 fi # Test pipeline detail access (often fails) local test_pipeline=$(glab ci list 2>/dev/null | head -2 | tail -1 | awk '{print $4}' | sed 's/[()]//g') if [[ -n "$test_pipeline" ]]; then if glab ci view "$test_pipeline" >/dev/null 2>&1; then echo "āœ… Pipeline Details: Accessible" else echo "āš ļø Pipeline Details: Limited (list only)" fi fi # Test job trace access echo "ā„¹ļø Job trace access will be tested when needed" } ``` ## Authentication & Project Info ### Check Authentication Status ```bash # Verify GitLab CLI authentication (non-interactive) glab auth status ``` ### Get Project Information ```bash # Get current project info glab repo view --output json | jq -r '.path_with_namespace // "No repo info"' # Get project statistics glab repo view --output json | jq -r '.statistics // "No stats"' # Check project permissions glab repo view --output json | jq -r '.permissions // "No permissions info"' ``` ## Pipeline Operations ### Latest Pipeline Information (With Fallback Strategies) ```bash # Robust pipeline information retrieval with cascading fallbacks get_pipeline_info_robust() { local branch="${1:-$(git branch --show-current)}" debug_log "Getting pipeline info for branch: $branch" # Method 1: Try glab ci get (often works) local pipeline_info=$(glab ci get --output json --branch "$branch" 2>/dev/null) if [[ -n "$pipeline_info" ]] && [[ "$pipeline_info" != "null" ]]; then debug_log "Method 1 successful: glab ci get" echo "$pipeline_info" return 0 fi # Method 2: Try glab ci status + list parsing debug_log "Method 1 failed, trying method 2: glab ci list parsing" local pipeline_id=$(glab ci list 2>/dev/null | grep "$branch" | head -1 | awk '{print $4}' | sed 's/[()]//g') if [[ -n "$pipeline_id" ]]; then # Try to get more info using the API if [[ -n "$GITLAB_TOKEN" ]] && [[ -n "$CI_PROJECT_ID" ]]; then local api_info=$(curl -s -H "Authorization: Bearer $GITLAB_TOKEN" \ "${GITLAB_HOST:-https://gitlab.com}/api/v4/projects/$CI_PROJECT_ID/pipelines/$pipeline_id" 2>/dev/null) if [[ -n "$api_info" ]]; then debug_log "Method 2 successful: API call with pipeline ID" echo "$api_info" return 0 fi fi # Fallback to basic info echo "{\"id\": \"$pipeline_id\", \"status\": \"$(glab ci status --branch $branch 2>/dev/null || echo unknown)\", \"ref\": \"$branch\"}" return 0 fi # Method 3: Basic fallback debug_log "All methods failed, returning basic status" echo "{\"error\": \"Limited GitLab access\", \"status\": \"unknown\", \"ref\": \"$branch\"}" return 1 } # Get latest pipeline on current branch get_pipeline_info_robust | jq -r '"\(.id // "N/A") \(.status // "unknown") \(.ref // "N/A") \(.duration // "N/A") \(.web_url // "N/A")"' # Get latest pipeline on specific branch get_pipeline_info_robust develop | jq -r '"\(.id // "N/A") \(.status // "unknown") \(.ref // "N/A") \(.duration // "N/A")"' # Get pipeline ID only (for chaining commands) get_pipeline_info_robust develop | jq -r '.id // "No pipeline"' ``` ### Pipeline Status Monitoring ```bash # Check pipeline status on specific branch glab ci status --branch develop 2>/dev/null || echo "No pipeline status available" # Get pipeline status in structured format glab ci get --output json 2>/dev/null | jq -r '.status // "unknown"' 2>/dev/null # Monitor running pipelines glab ci get --output json 2>/dev/null | jq -r 'select(.status == "running") | "\(.id) \(.status) \(.ref)"' 2>/dev/null || echo "No running pipelines" ``` ## Job Operations ### Job Information Extraction ```bash # Get all jobs from latest pipeline glab ci get --output json 2>/dev/null | jq -r '.jobs[] | "\(.id) \(.name) \(.status)"' 2>/dev/null || echo "No jobs found" # Get specific job status glab ci get --output json 2>/dev/null | jq -r '.jobs[] | select(.name == "test") | "\(.id) \(.status)"' 2>/dev/null || echo "Job not found" # Get failed jobs only glab ci get --output json 2>/dev/null | jq -r '.jobs[] | select(.status == "failed") | "\(.id) \(.name) \(.status)"' 2>/dev/null || echo "No failed jobs" # Get job IDs for log viewing glab ci get --output json 2>/dev/null | jq -r '.jobs[].id' 2>/dev/null | head -5 ``` ### Job Log Analysis (Critical Update) āš ļø **Important Discovery**: `glab ci trace` only works with numeric job IDs, NOT job names! ```bash # āŒ DOES NOT WORK - Common misconception # glab ci trace "job-name" # This will fail! # glab ci trace pipeline_id:job_name # This will fail! # āœ… CORRECT APPROACH - Get job ID first, then trace get_job_logs() { local pipeline_id="$1" local job_name="$2" debug_log "Getting logs for job '$job_name' in pipeline $pipeline_id" # Step 1: Get job ID using API (most reliable method) local job_id if [[ -n "$GITLAB_TOKEN" ]] && command -v glab >/dev/null 2>&1; then job_id=$(glab api "projects/:id/pipelines/$pipeline_id/jobs" 2>/dev/null | \ jq -r ".[] | select(.name == \"$job_name\") | .id" 2>/dev/null) fi if [[ -z "$job_id" ]]; then # Fallback: Try to get from glab ci get job_id=$(glab ci get --output json 2>/dev/null | \ jq -r ".jobs[] | select(.name == \"$job_name\") | .id" 2>/dev/null) fi if [[ -z "$job_id" ]]; then echo "āŒ Job '$job_name' not found in pipeline $pipeline_id" return 1 fi debug_log "Found job ID: $job_id for job name: $job_name" # Step 2: Get trace using numeric job ID local logs logs=$(glab ci trace "$job_id" 2>&1) local exit_code=$? if [[ $exit_code -eq 0 ]]; then echo "$logs" return 0 else # Fallback to API if glab fails if [[ -n "$GITLAB_TOKEN" ]] && [[ -n "$CI_PROJECT_ID" ]]; then debug_log "glab trace failed, trying API fallback" curl -s -H "Authorization: Bearer $GITLAB_TOKEN" \ "${GITLAB_HOST:-https://gitlab.com}/api/v4/projects/$CI_PROJECT_ID/jobs/$job_id/trace" 2>/dev/null else echo "āŒ Failed to retrieve logs for job $job_id. Error: $logs" return 1 fi fi } # Example usage: # get_job_logs "1909685353" "services_and_e2e_tests" # Get logs for all failed jobs get_failed_job_logs() { local pipeline_id="${1:-$(get_pipeline_info_robust | jq -r '.id // ""')}" if [[ -z "$pipeline_id" ]]; then echo "āŒ No pipeline ID available" return 1 fi # Get failed job names and IDs local failed_jobs if command -v glab >/dev/null 2>&1 && [[ -n "$GITLAB_TOKEN" ]]; then failed_jobs=$(glab api "projects/:id/pipelines/$pipeline_id/jobs" 2>/dev/null | \ jq -r '.[] | select(.status == "failed") | "\(.id):\(.name)"' 2>/dev/null) else failed_jobs=$(glab ci get --output json 2>/dev/null | \ jq -r '.jobs[] | select(.status == "failed") | "\(.id):\(.name)"' 2>/dev/null) fi if [[ -z "$failed_jobs" ]]; then echo "āœ… No failed jobs found" return 0 fi # Get logs for each failed job while IFS=: read -r job_id job_name; do echo "=== Logs for failed job: $job_name (ID: $job_id) ===" glab ci trace "$job_id" 2>&1 | tail -100 || echo "Failed to get logs" echo "" done <<< "$failed_jobs" } # Quick helper to get last N lines of job log get_job_tail() { local pipeline_id="$1" local job_name="$2" local lines="${3:-100}" get_job_logs "$pipeline_id" "$job_name" | tail -"$lines" } ``` ## Pipeline Management Operations ### Pipeline Control ```bash # Cancel running pipeline glab ci cancel --pipeline-id PIPELINE_ID 2>/dev/null || echo "Cannot cancel pipeline" # Retry failed pipeline glab ci retry --pipeline-id PIPELINE_ID 2>/dev/null || echo "Cannot retry pipeline" # Run new pipeline on branch glab ci run --branch BRANCH_NAME 2>/dev/null || echo "Cannot trigger pipeline" ``` ### Artifact Management ```bash # Download artifacts from latest pipeline (updated syntax) glab job artifact <refName> <jobName> 2>/dev/null || echo "No artifacts available" # Example: Download artifacts from specific branch and job glab job artifact develop test-job 2>/dev/null || echo "No artifacts for develop/test-job" ``` ## Configuration & Validation ### CI Configuration Analysis ```bash # Validate .gitlab-ci.yml glab ci lint 2>/dev/null || echo "CI configuration invalid" # View CI configuration glab ci config view 2>/dev/null || echo "No CI config found" ``` ## Composite Analysis Commands ### Complete Pipeline Status Check ```bash # Comprehensive pipeline analysis PIPELINE_INFO=$(glab ci get --output json --branch develop 2>/dev/null) if [ $? -eq 0 ] && [ "$PIPELINE_INFO" != "" ]; then echo "Pipeline Status:" echo "$PIPELINE_INFO" | jq -r '"ID: \(.id) | Status: \(.status) | Branch: \(.ref) | Duration: \(.duration)s | URL: \(.web_url)"' echo "Jobs:" echo "$PIPELINE_INFO" | jq -r '.jobs[] | " \(.name): \(.status)"' else echo "No pipeline information available" fi ``` ### Failed Job Analysis ```bash # Find and display logs for failed jobs PIPELINE_INFO=$(glab ci get --output json 2>/dev/null) if [ $? -eq 0 ] && [ "$PIPELINE_INFO" != "" ]; then FAILED_JOBS=$(echo "$PIPELINE_INFO" | jq -r '.jobs[] | select(.status == "failed") | .id' 2>/dev/null) if [ "$FAILED_JOBS" != "" ]; then for job_id in $FAILED_JOBS; do echo "=== Logs for failed job $job_id ===" glab ci trace $job_id 2>/dev/null | tail -50 || echo "No logs available" echo "" done else echo "No failed jobs found" fi else echo "No pipeline information available" fi ``` ### Continuous Pipeline Monitoring ```bash # Monitor pipeline progress (for automated watching) while true; do STATUS=$(glab ci get --output json 2>/dev/null | jq -r '.status // "unknown"' 2>/dev/null) if [ "$STATUS" = "running" ]; then echo "$(date): Pipeline still running..." glab ci get --output json 2>/dev/null | jq -r '.jobs[] | " \(.name): \(.status)"' 2>/dev/null sleep 30 else echo "$(date): Pipeline completed with status: $STATUS" break fi done ``` ## Error Handling Patterns ### Standard Error Handling Template ```bash RESULT=$(command 2>/dev/null) if [ $? -eq 0 ] && [ "$RESULT" != "" ]; then # Process successful result echo "$RESULT" | jq -r '.field' else # Handle failure case echo "Command failed or no data available" fi ``` ### Troubleshooting Commands ```bash # Check authentication glab auth status # Check project access glab repo view --output json | jq -r '.permissions // "No permissions info"' # Check if pipelines exist glab ci get --output json 2>/dev/null | jq -r 'keys[]' 2>/dev/null || echo "No pipeline data structure available" ``` ## Environment Variables Set these for consistent behavior: ```bash export GLAB_CONFIG_FILE="/path/to/config.yml" export GITLAB_TOKEN="your_token_here" export GITLAB_HOST="gitlab.com" ``` ## Quick Reference Commands ### Most Commonly Used ```bash # Current pipeline status glab ci get --output json 2>/dev/null | jq -r '"\(.status) | \(.ref) | \(.web_url)"' 2>/dev/null # Job count by status glab ci get --output json 2>/dev/null | jq -r '.jobs | group_by(.status) | map("\(.[0].status): \(length)") | .[]' 2>/dev/null # Latest pipeline duration glab ci get --output json 2>/dev/null | jq -r '.duration // "unknown"' 2>/dev/null # Failed job names glab ci get --output json 2>/dev/null | jq -r '.jobs[] | select(.status == "failed") | .name' 2>/dev/null ``` ## Direct API Fallback Methods When glab commands fail (which happens frequently), use these direct API methods: ```bash # Setup API variables setup_gitlab_api() { export GITLAB_HOST="${GITLAB_HOST:-https://gitlab.com}" export GITLAB_TOKEN="${GITLAB_TOKEN:-$(glab auth token 2>/dev/null)}" export CI_PROJECT_ID="${CI_PROJECT_ID:-$(glab repo view --output json 2>/dev/null | jq -r '.id // ""')}" if [[ -z "$GITLAB_TOKEN" ]]; then echo "āŒ No GitLab token available. Set GITLAB_TOKEN environment variable." return 1 fi } # Get pipeline details via API get_pipeline_via_api() { local pipeline_id="$1" setup_gitlab_api || return 1 curl -s -H "Authorization: Bearer $GITLAB_TOKEN" \ "$GITLAB_HOST/api/v4/projects/$CI_PROJECT_ID/pipelines/$pipeline_id" | jq '.' } # Get job logs via API get_job_logs_via_api() { local job_id="$1" setup_gitlab_api || return 1 curl -s -H "Authorization: Bearer $GITLAB_TOKEN" \ "$GITLAB_HOST/api/v4/projects/$CI_PROJECT_ID/jobs/$job_id/trace" } # List pipelines via API list_pipelines_via_api() { local branch="${1:-}" setup_gitlab_api || return 1 local url="$GITLAB_HOST/api/v4/projects/$CI_PROJECT_ID/pipelines" if [[ -n "$branch" ]]; then url="$url?ref=$branch" fi curl -s -H "Authorization: Bearer $GITLAB_TOKEN" "$url" | jq -r '.[] | "\(.id) \(.status) \(.ref) \(.created_at)"' } # Retry pipeline via API retry_pipeline_via_api() { local pipeline_id="$1" setup_gitlab_api || return 1 curl -X POST -H "Authorization: Bearer $GITLAB_TOKEN" \ "$GITLAB_HOST/api/v4/projects/$CI_PROJECT_ID/pipelines/$pipeline_id/retry" | jq '.' } # Cancel pipeline via API cancel_pipeline_via_api() { local pipeline_id="$1" setup_gitlab_api || return 1 curl -X POST -H "Authorization: Bearer $GITLAB_TOKEN" \ "$GITLAB_HOST/api/v4/projects/$CI_PROJECT_ID/pipelines/$pipeline_id/cancel" | jq '.' } ``` ## Commands to Avoid in Automation (Updated Based on Production Feedback) ### āŒ Commands That Consistently Fail: ```bash # These commands fail with 403/404 errors even with valid authentication: glab ci view <pipeline_id> # 404 Not Found - Use API fallback instead glab ci get <pipeline_id> --output json # "accepts 0 arg(s), received 1" glab ci trace <job_name> # Only works with numeric IDs, not names! glab ci list --limit <number> # Unknown flag - use 'head' instead glab ci trace --job <job_name> # Unknown flag glab job list # Often fails glab pipeline list # Often fails ``` ### āœ… Commands That Work Reliably: ```bash glab auth status # Authentication check glab ci list # Basic pipeline list (but no --limit flag) glab ci status # Current branch status glab ci status --branch <branch> # Specific branch status glab ci get --output json # Latest pipeline (no pipeline ID argument) glab ci trace <numeric_job_id> # Works ONLY with numeric IDs glab api <endpoint> # Direct API access glab ci lint # Validate CI config ``` ## Notes ### Critical Updates (Based on Production Feedback): - **Job Tracing**: `glab ci trace` ONLY works with numeric job IDs, NOT job names - **Pipeline Details**: `glab ci view` consistently fails with 404 errors - use API fallback - **Permission Issues**: Many detailed operations fail even with valid authentication - **Fallback Strategy**: Always implement cascading fallbacks for critical operations - **Debug Mode**: Use `GITLAB_DEBUG=true` to troubleshoot command failures ### Best Practices: - The `glab ci get` command is the most reliable for current pipeline information - Always use `--output json` with `jq` for parsing structured data - Always validate permissions before attempting detailed operations - Always include error handling with `|| echo "fallback message"` - All commands are designed for non-interactive automation environments - When glab fails, fall back to direct API calls using curl