diff --git a/.tmux.conf b/.tmux.conf index 9e50355..6e38a15 100644 --- a/.tmux.conf +++ b/.tmux.conf @@ -86,6 +86,10 @@ set-hook -g after-select-window 'run-shell -b "throb --next"' # Window configuration set -g @rose_pine_window_status_separator " | " +# Merge-train popup: latest train beside the whisper feed, auto-dismisses +# when a train completes on its watch +bind M display-popup -w 90% -h 60% -E "train watch --whispers" + # Send a command to all panes in the current window bind S command-prompt -p "Command:" \ "run \"tmux list-panes -F '##{session_name}:##{window_index}.##{pane_index}' \ diff --git a/bin/glab-merge b/bin/glab-merge new file mode 100755 index 0000000..8537cf2 --- /dev/null +++ b/bin/glab-merge @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# glab-merge: wait until a GitLab merge request is mergeable, then merge it. +# +# usage: glab-merge (e.g. glab-merge web/vsm-werkbonnen 685) +# Exit 0 merged (or already merged), 75 blocked on something a human must +# resolve (train marks the step needs-user), 1 on error or timeout. +# GLAB_MERGE_TIMEOUT overrides the wait budget in seconds (default 600). +set -uo pipefail + +project="${1:?usage: glab-merge }" +iid="${2:?usage: glab-merge }" +timeout="${GLAB_MERGE_TIMEOUT:-600}" +enc="${project//\//%2F}" + +while [ "$SECONDS" -lt "$timeout" ]; do + mr="$(glab api "projects/$enc/merge_requests/$iid")" || exit 1 + [ "$(jq -r .state <<<"$mr")" = merged ] && { + echo "!$iid already merged" + exit 0 + } + ms="$(jq -r .detailed_merge_status <<<"$mr")" + case "$ms" in + mergeable) + out="$(glab api --method PUT "projects/$enc/merge_requests/$iid/merge" \ + --raw-field should_remove_source_branch=true)" || { + echo "merge of !$iid rejected" >&2 + exit 1 + } + echo "!$iid merged ($(jq -r '.merge_commit_sha[:8]' <<<"$out"))" + exit 0 + ;; + checking | unchecked | preparing | ci_still_running | ci_must_pass | approvals_syncing) + sleep 5 + ;; + need_rebase | conflict | discussions_not_resolved | not_approved | blocked_status | \ + policies_denied | draft_status | requested_changes | jira_association_missing | \ + security_policy_violations) + echo "!$iid blocked: $ms" >&2 + exit 75 + ;; + *) + echo "!$iid not mergeable: $ms" >&2 + exit 1 + ;; + esac +done +echo "!$iid timed out after ${timeout}s waiting to become mergeable" >&2 +exit 1 diff --git a/bin/train b/bin/train new file mode 100755 index 0000000..2e13bea --- /dev/null +++ b/bin/train @@ -0,0 +1,563 @@ +#!/usr/bin/env python3 +"""train: run and watch multi-step merge/release trains. + +A train is a directory under ~/.local/state/train// (TRAIN_ROOT overrides): + + plan key|label|command one step per line, line order = run order + state key|status|detail status: pending|live|done|fail|needs-user|skipped + title one-line header for the watcher + summary one-liner (" 7/11 [live]