Skip to content

Instantly share code, notes, and snippets.

@tichopad
Last active February 19, 2026 10:49
Show Gist options
  • Select an option

  • Save tichopad/ea69eb9c5e59b09ff955f55c7463f8d5 to your computer and use it in GitHub Desktop.

Select an option

Save tichopad/ea69eb9c5e59b09ff955f55c7463f8d5 to your computer and use it in GitHub Desktop.
ghw - Run a GitHub Actions workflow and wait for completion with log streaming
#!/bin/zsh
# ghw - Run a GitHub Actions workflow and wait for it to complete
# Triggers a workflow via GitHub CLI, then watches it until completion
set -e
# Text formatting
BOLD='\033[1m'
RESET='\033[0m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
RED='\033[0;31m'
YELLOW='\033[0;33m'
usage() {
echo "${BOLD}Usage:${RESET}"
echo " ghw <workflow> [options]"
echo ""
echo "${BOLD}Description:${RESET}"
echo " Triggers a GitHub Actions workflow, waits for it to complete,"
echo " streams the logs, and reports the final status and duration."
echo ""
echo "${BOLD}Arguments:${RESET}"
echo " workflow The workflow file name, ID, or name (e.g., 'build.yml')"
echo ""
echo "${BOLD}Options:${RESET}"
echo " -r, --ref <branch> The branch or tag to run the workflow on (default: current branch)"
echo " -f, --field <key=val> Add a workflow input (can be repeated)"
echo " -R, --repo <owner/repo> Target repository (default: current repo)"
echo " -q, --quiet Don't stream logs, just wait and report status"
echo " -h, --help Show this help message"
echo ""
echo "${BOLD}Examples:${RESET}"
echo " ghw build.yml"
echo " ghw deploy.yml -r main"
echo " ghw test.yml -f environment=staging -f debug=true"
exit 0
}
error() {
echo "${RED}Error: $1${RESET}" >&2
exit 1
}
# Check for gh CLI
command -v gh >/dev/null 2>&1 || error "GitHub CLI (gh) is not installed. Install it with 'brew install gh'"
# Parse arguments
workflow=""
ref=""
repo_arg=()
fields=()
quiet=false
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
;;
-r|--ref)
ref="$2"
shift 2
;;
-f|--field)
fields+=("-f" "$2")
shift 2
;;
-R|--repo)
repo_arg=("-R" "$2")
shift 2
;;
-q|--quiet)
quiet=true
shift
;;
-*)
error "Unknown option: $1"
;;
*)
if [[ -z "$workflow" ]]; then
workflow="$1"
else
error "Unexpected argument: $1"
fi
shift
;;
esac
done
[[ -z "$workflow" ]] && error "Workflow name is required. Run 'ghw --help' for usage."
# Build the run command
ref_arg=()
[[ -n "$ref" ]] && ref_arg=("--ref" "$ref")
# Record start time
start_time=$(date +%s)
echo "${BLUE}Triggering workflow:${RESET} ${BOLD}$workflow${RESET}"
[[ -n "$ref" ]] && echo "${BLUE}On ref:${RESET} $ref"
# Trigger the workflow
if ! gh workflow run "$workflow" "${ref_arg[@]}" "${repo_arg[@]}" "${fields[@]}" 2>&1; then
error "Failed to trigger workflow"
fi
echo "${GREEN}✓ Workflow triggered${RESET}"
echo "${BLUE}Waiting for workflow run to appear...${RESET}"
# Wait a moment for the run to be registered
sleep 2
# Find the most recent run of this workflow
# We need to poll until we find a run that started after our trigger
max_attempts=30
attempt=0
run_id=""
while [[ -z "$run_id" && $attempt -lt $max_attempts ]]; do
# Get runs that are in progress or queued, started after we triggered
run_id=$(gh run list --workflow="$workflow" "${repo_arg[@]}" --limit 5 --json databaseId,status,createdAt \
-q '.[] | select(.status == "in_progress" or .status == "queued") | .databaseId' 2>/dev/null | head -1)
if [[ -z "$run_id" ]]; then
((attempt++))
sleep 1
fi
done
[[ -z "$run_id" ]] && error "Could not find the triggered workflow run. It may have completed very quickly or failed to start."
echo "${GREEN}✓ Found run:${RESET} $run_id"
echo ""
# Watch the run (streams logs by default)
watch_exit_code=0
if [[ "$quiet" == true ]]; then
echo "${BLUE}Waiting for completion (quiet mode)...${RESET}"
gh run watch "$run_id" "${repo_arg[@]}" --exit-status >/dev/null 2>&1 || watch_exit_code=$?
else
echo "${BLUE}Streaming logs...${RESET}"
echo "─────────────────────────────────────────────────────────────"
gh run watch "$run_id" "${repo_arg[@]}" --exit-status || watch_exit_code=$?
echo "─────────────────────────────────────────────────────────────"
fi
# Calculate duration
end_time=$(date +%s)
duration=$((end_time - start_time))
minutes=$((duration / 60))
seconds=$((duration % 60))
echo ""
if [[ $minutes -gt 0 ]]; then
echo "${BLUE}Duration:${RESET} ${minutes}m ${seconds}s"
else
echo "${BLUE}Duration:${RESET} ${seconds}s"
fi
# Report final status
if [[ $watch_exit_code -eq 0 ]]; then
echo "${GREEN}${BOLD}✓ Workflow completed successfully${RESET}"
exit 0
else
echo "${RED}${BOLD}✗ Workflow failed${RESET}"
echo "${BLUE}View details:${RESET} gh run view $run_id ${repo_arg[*]} --web"
exit 1
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment