Created
January 30, 2026 10:09
-
-
Save simoninglis/5f2e00d69c9042db84d35c6fbdc3d03b to your computer and use it in GitHub Desktop.
teax CI status for tmux - Real-time Gitea workflow visibility
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Description: Output Gitea CI workflow status for tmux status bar | |
| # Author: https://github.com/simoninglis/teax | |
| # License: MIT | |
| # | |
| # Dependencies: | |
| # - teax: Gitea CLI companion (pip install git+https://github.com/simoninglis/teax.git) | |
| # - tea: Official Gitea CLI (must be configured with `tea login add`) | |
| # - jq: JSON processor | |
| # | |
| # Installation: | |
| # 1. Place this script in your PATH (e.g., ~/.local/bin/) | |
| # 2. Make executable: chmod +x teax-ci-status | |
| # 3. Configure TEAX_CI_REPO below or set via environment | |
| # 4. Add to tmux status bar: | |
| # tmux set-option status-right '#(~/.local/bin/teax-ci-status) | %H:%M' | |
| # | |
| # Output format: B:✓ C:✓ D:✗ V:✓ +2 | |
| # - B = Build workflow | |
| # - C = CI workflow | |
| # - D = Deploy workflow | |
| # - V = Verify workflow | |
| # - +N = N additional workflows (all passing or count of failures) | |
| # - ✓ = passing (green) | |
| # - ✗ = failing (red) | |
| # - ? = unknown/error | |
| set -euo pipefail | |
| #============================================================================= | |
| # USER CONFIGURATION - Customize these settings | |
| #============================================================================= | |
| # Repository to monitor (required) | |
| # Format: owner/repo (e.g., "singlis/myproject") | |
| TEAX_CI_REPO="${TEAX_CI_REPO:-}" | |
| # Cache settings | |
| CACHE_TTL="${TEAX_CI_CACHE_TTL:-120}" # Seconds between API calls (default: 2 min) | |
| # Workflow name mappings (customize for your project) | |
| # Maps workflow filename to single-letter display code | |
| declare -A WORKFLOW_MAP=( | |
| ["build.yml"]="B" | |
| ["ci.yml"]="C" | |
| ["deploy-staging.yml"]="D" | |
| ["deploy-prod.yml"]="P" | |
| ["verify-staging.yml"]="V" | |
| ["verify-prod.yml"]="W" | |
| ) | |
| # Priority order for display (first N shown, rest summarized as +N) | |
| PRIORITY_WORKFLOWS=("build.yml" "ci.yml" "deploy-staging.yml" "verify-staging.yml") | |
| MAX_DISPLAY=4 | |
| # Tmux color styles | |
| STYLE_PASS="${TEAX_CI_STYLE_PASS:-fg=green}" | |
| STYLE_FAIL="${TEAX_CI_STYLE_FAIL:-fg=red,bold}" | |
| STYLE_UNKNOWN="${TEAX_CI_STYLE_UNKNOWN:-fg=yellow}" | |
| STYLE_DEFAULT="${TEAX_CI_STYLE_DEFAULT:-default}" | |
| # SSL verification (set to 1 for self-signed certs) | |
| TEAX_INSECURE="${TEAX_INSECURE:-0}" | |
| #============================================================================= | |
| # INTERNAL CONFIGURATION | |
| #============================================================================= | |
| readonly CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/teax-ci-status" | |
| readonly CACHE_FILE="${CACHE_DIR}/status" | |
| #============================================================================= | |
| # FUNCTIONS | |
| #============================================================================= | |
| check_dependencies() { | |
| local missing=() | |
| for cmd in teax jq; do | |
| if ! command -v "$cmd" &>/dev/null; then | |
| missing+=("$cmd") | |
| fi | |
| done | |
| if [[ ${#missing[@]} -gt 0 ]]; then | |
| echo "#[fg=red]ERR:${missing[*]}#[$STYLE_DEFAULT]" | |
| exit 1 | |
| fi | |
| } | |
| ensure_cache_dir() { | |
| if [[ ! -d "$CACHE_DIR" ]]; then | |
| mkdir -p "$CACHE_DIR" | |
| chmod 700 "$CACHE_DIR" | |
| fi | |
| } | |
| cache_valid() { | |
| if [[ ! -f "$CACHE_FILE" ]]; then | |
| return 1 | |
| fi | |
| local cache_age | |
| cache_age=$(( $(date +%s) - $(stat -c %Y "$CACHE_FILE" 2>/dev/null || echo 0) )) | |
| [[ "$cache_age" -lt "$CACHE_TTL" ]] | |
| } | |
| format_status() { | |
| local status="$1" | |
| case "$status" in | |
| success) | |
| echo "#[$STYLE_PASS]✓#[$STYLE_DEFAULT]" | |
| ;; | |
| failure|error) | |
| echo "#[$STYLE_FAIL]✗#[$STYLE_DEFAULT]" | |
| ;; | |
| *) | |
| echo "#[$STYLE_UNKNOWN]?#[$STYLE_DEFAULT]" | |
| ;; | |
| esac | |
| } | |
| fetch_status() { | |
| if [[ -z "$TEAX_CI_REPO" ]]; then | |
| echo "#[$STYLE_UNKNOWN]CI:no-repo#[$STYLE_DEFAULT]" | |
| return 1 | |
| fi | |
| local teax_cmd="teax" | |
| if [[ "$TEAX_INSECURE" == "1" ]]; then | |
| teax_cmd="TEAX_INSECURE=1 teax" | |
| fi | |
| # Fetch workflow status as JSON | |
| local response | |
| response=$(eval "$teax_cmd runs status -r '$TEAX_CI_REPO' -o json" 2>/dev/null) || { | |
| echo "#[$STYLE_UNKNOWN]CI:err#[$STYLE_DEFAULT]" | |
| return 1 | |
| } | |
| if [[ -z "$response" ]] || [[ "$response" == "null" ]]; then | |
| echo "#[$STYLE_UNKNOWN]CI:--#[$STYLE_DEFAULT]" | |
| return 1 | |
| fi | |
| # Parse and format output | |
| local output="" | |
| local shown=0 | |
| local extra_pass=0 | |
| local extra_fail=0 | |
| # Process priority workflows first | |
| for wf in "${PRIORITY_WORKFLOWS[@]}"; do | |
| local code="${WORKFLOW_MAP[$wf]:-}" | |
| if [[ -z "$code" ]]; then | |
| continue | |
| fi | |
| local status | |
| status=$(echo "$response" | jq -r --arg wf "$wf" '.[] | select(.workflow == $wf) | .status' 2>/dev/null) | |
| if [[ -n "$status" && "$status" != "null" ]]; then | |
| if [[ $shown -lt $MAX_DISPLAY ]]; then | |
| output+="${code}:$(format_status "$status") " | |
| ((shown++)) | |
| else | |
| if [[ "$status" == "success" ]]; then | |
| ((extra_pass++)) | |
| else | |
| ((extra_fail++)) | |
| fi | |
| fi | |
| fi | |
| done | |
| # Count remaining workflows not in priority list | |
| local all_workflows | |
| all_workflows=$(echo "$response" | jq -r '.[].workflow' 2>/dev/null) | |
| while IFS= read -r wf; do | |
| local in_priority=0 | |
| for pwf in "${PRIORITY_WORKFLOWS[@]}"; do | |
| if [[ "$wf" == "$pwf" ]]; then | |
| in_priority=1 | |
| break | |
| fi | |
| done | |
| if [[ $in_priority -eq 0 ]]; then | |
| local status | |
| status=$(echo "$response" | jq -r --arg wf "$wf" '.[] | select(.workflow == $wf) | .status' 2>/dev/null) | |
| if [[ "$status" == "success" ]]; then | |
| ((extra_pass++)) | |
| else | |
| ((extra_fail++)) | |
| fi | |
| fi | |
| done <<< "$all_workflows" | |
| # Add extra count if any | |
| local extra_total=$((extra_pass + extra_fail)) | |
| if [[ $extra_total -gt 0 ]]; then | |
| if [[ $extra_fail -gt 0 ]]; then | |
| output+="#[$STYLE_FAIL]+$extra_fail✗#[$STYLE_DEFAULT]" | |
| else | |
| output+="+$extra_total" | |
| fi | |
| fi | |
| # Trim trailing space | |
| output="${output% }" | |
| if [[ -z "$output" ]]; then | |
| echo "#[$STYLE_UNKNOWN]CI:--#[$STYLE_DEFAULT]" | |
| return 1 | |
| fi | |
| # Cache the result | |
| ensure_cache_dir | |
| local tmp_file | |
| tmp_file=$(mktemp "${CACHE_DIR}/status.XXXXXX") | |
| echo "$output" > "$tmp_file" | |
| mv -f "$tmp_file" "$CACHE_FILE" | |
| chmod 600 "$CACHE_FILE" | |
| echo "$output" | |
| } | |
| main() { | |
| check_dependencies | |
| # Return cached value if valid | |
| if cache_valid; then | |
| cat "$CACHE_FILE" | |
| return 0 | |
| fi | |
| # Fetch fresh status | |
| if ! fetch_status; then | |
| # On failure, use stale cache if available | |
| if [[ -f "$CACHE_FILE" ]]; then | |
| cat "$CACHE_FILE" | |
| echo "~" # Stale indicator | |
| fi | |
| fi | |
| } | |
| main "$@" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Example tmux session script with CI status integration | |
| # | |
| # This shows how to integrate teax-ci-status into your project's tmux session. | |
| # Customize TEAX_CI_REPO for your project. | |
| SESSION="myproject" | |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| # Set the repository for CI status monitoring | |
| export TEAX_CI_REPO="singlis/myproject" | |
| # Optional: For self-signed Gitea certificates | |
| # export TEAX_INSECURE=1 | |
| tmux has-session -t "=$SESSION" 2>/dev/null | |
| if [ $? != 0 ]; then | |
| # Create session with windows | |
| tmux new-session -d -s $SESSION -c "$SCRIPT_DIR" -n nvim | |
| tmux new-window -t $SESSION:1 -n claude -c "$SCRIPT_DIR" | |
| tmux new-window -t $SESSION:2 -n shell -c "$SCRIPT_DIR" | |
| # Launch editor in first window | |
| tmux send-keys -t $SESSION:0 'nvim .' C-m | |
| # Configure status bar with CI status | |
| # Format: B:✓ C:✓ D:✓ V:✓ +2 | 14:30 30-Jan | |
| tmux set-option -t $SESSION status-right-length 60 | |
| tmux set-option -t $SESSION status-right '#(~/.local/bin/teax-ci-status) │ %H:%M %d-%b' | |
| # Optional: Also show Claude usage if you have that script | |
| # tmux set-option -t $SESSION status-right '#(~/.local/bin/claude-usage) │ #(~/.local/bin/teax-ci-status) │ %H:%M %d-%b' | |
| fi | |
| # Attach to session | |
| tmux attach-session -t "=$SESSION:0" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment