Explore guh.nvim's CI-log approach (raw job-logs endpoint + terminal render) against ours #784
Labels
No labels
bug
documentation
duplicate
enhancement
fugitive
good first issue
help wanted
invalid
question
v0.1.0
v0.2.0
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
barrettruth/forge.nvim#784
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Context
justinmk recently shipped CI-log viewing in guh.nvim (his fork of
ghlite.nvim, GitHub-only). It's a much smaller surface than ours but takes a few deliberate design decisions that are worth evaluating againstforge.nvim's implementation.The "recent solution" to dig into:
github.com/justinmk/guh.nvim@479a3d8949(feat: get CI logs for a PR)819d9ab(avoid gh's{workflow} / {job} {step}line prefix, #13),bd2c42c(strip UTF-8 BOM),ca8b3ac(2026-06-07,luanilfix for avim.NILsort crash)github.com/justinmk/guh.nvim@99cde3b545/lua/guh/gh.lua (L429-L536)github.com/justinmk/guh.nvim@99cde3b545/lua/guh/pr.lua (L536-L574)How guh.nvim does it
Entry point is
dlfrom the PR view (<Plug>(guh-logs)->pr.show_ci_logs). The whole feature is ~100 lines:get_pr_ci_jobs_logshitsgh api --paginate --slurp repos/{owner}/{repo}/commits/{head_sha}/check-runs?filter=latest&per_page=100, keepsapp.slug == 'github-actions', and extracts the Actions job id from the check-run'sdetails_url(/job/(%d+)).filter=latestcollapses re-runs server-side, so for the head commit this is usually a single page covering the whole matrix. Dedupes by job name keeping the lateststartedAt.vim.ui.selectpicker formatted as[<conclusion|status>] <name>, skippingskippedjobs.get_pr_ci_logsfetchesgh api repos/{owner}/{repo}/actions/jobs/{job_id}/logs— deliberately NOT `gh run view --log`, with the comment: "avoid gh's {workflow} / {job} {step} prefix". Then strips a leading UTF-8 BOM the REST API prepends.nvim_open_term, andnvim_chan_send(chan, logs)— letting Neovim's terminal emulator handle ANSI/termcodes natively. No custom ANSI parser, no folding, no step structure.vim.json.decodeturning JSONnullintovim.NIL(truthy!) broke the status sort comparator -> fixed withluanil(ca8b3ac); the BOM strip (bd2c42c).How forge.nvim does it today
We are far more capable (multi-forge, structured render) but also more machinery:
lua/forge/backends/github.luacheck_log_cmd(~L428-437). This is exactly the command that emits the{workflow} / {job} {step}\t<ts> <content>prefix.lua/forge/log/parser/github.luahas to parse tab-separatedJOB\tSTEP\tTIMESTAMP CONTENT, with per-backend parsers (log/parser/{github,gitlab,forgejo}.lua), an SGR/ANSI -> highlight map inlog/parser/common.lua, plus folding/durations/structure inlog/render.lua.lua/forge/pr/checks.lua(inspect_check), pullingrun_id/job_idout of the checklinkwith regexes, with live-tail + auto-refresh on top.What's worth evaluating / borrowing
check-runs?filter=latestfor the PR head commit. This is a notably cleaner path to "the CI for this PR's head commit" than ourgh run list+ summary normalization, and it gets the whole matrix with rerun-collapsing in one paginated call. Could simplify our GitHub PR-checks -> job-id resolution (we currently regex it out ofdetails_url/linkafter the fact anyway).actions/jobs/{id}/logsendpoint instead ofgh run view --log. Switching our GitHubcheck_log_cmd/run_log_cmdto the raw endpoint would remove the{workflow}/{job}/{step}prefix at the source — meaning a big chunk ofparser/github.luaexists only to undo noiseghadds. Worth measuring how much parser fragility we could delete. (Note the BOM gotcha if we do.)nvim_open_termgives perfect ANSI/termcode fidelity for free. We deliberately went the other way (structuredforgelogbuffer w/ folds, step durations, failure nav) and recently removed CI-log error-jump heuristics (#779/#780) — so this is a genuine tradeoff, not a clear win. But a terminal-render fallback might be more robust for formats our parsers don't model well.vim.ui.selectjob picker. Lightweight, integrates with whatever picker the user already has, vs. our checks-list buffer + cursor/name-filter. Possibly a nice alternate entry point.luanil/vim.NILsort crash is a good cautionary tale for our ownvim.json.decodecall sites.Where we're already ahead (keep in mind)
Multi-forge support (GitHub/GitLab/Forgejo), structured + folded rendering with step durations, live tail + auto-refresh for in-progress runs, and per-backend parsers. The goal here is to cherry-pick techniques, not to flatten our feature set to match a GitHub-only plugin.
Next steps
actions/jobs/{id}/logsfetch and measure how much ofparser/github.luait lets us dropcheck-runs?filter=latestshould back our GitHub PR-checks/job resolutionnvim_open_termrender path (option or fallback) vs. the structuredforgelogbuffervim.json.decodecall sites for thevim.NIL-is-truthy footgun (luanil)