Subsequent job traces fail to load after the first call to RunTraceSha()
<!---
Please read this!
Before opening a new issue, make sure to search for keywords in the issues
filtered by the "bug" label:
- https://gitlab.com/gitlab-org/cli/-/issues/?label_name%5B%5D=type%3A%3Abug
and verify the issue you're about to submit isn't a duplicate.
--->
### Checklist
<!-- Please test the latest versions, that will remove the possibility that you see a bug that is fixed in a newer version. -->
- [x] I'm using the latest version of the extension (Run `glab --version`)
- Extension version:
- `glab 1.67.0 (1e957280)` (homebrew)
- `glab 1.67.0 (90f42d15)` (local build from `main`)
- [x] Operating system and version: MacOS Sequoia 15.6
- [x] Gitlab.com or self-managed instance? gitlab.com
- [x] GitLab version (if self-managed) N/A
- [x] I have performed `glab auth status` to check for authentication issues
- [x] Run the command in debug mode (like `DEBUG=true glab mr list`) and attach any useful output
### Summary
In `glab ci view`, global state variables in ciutils.RunTraceSha() cause subsequent job log views in the same session to display incorrect log output (sometimes empty), most likely due to a globally-scoped `offset` variable that should be scoped per-call
### Environment
<!--
on POSIX system (Linux, MacOS), run
bash -c 'printf -- "- OS: %s\n- SHELL: %s\n- TERM: %s\n- GLAB: %s" "$(uname -srm)" "$SHELL" "$TERM" "$(glab --version)"'
and replace the following section with the result.
If you use non-POSIX system, fill in the section manually:
- OS: Your operating system including version and architecture (Windows 11 - AMD64, MacOS Sonoma - ARM64)
- SHELL: Your shell (bash, fish, zsh, ...)
- TERM: Your terminal emulator (Kitty, Xterm2..)
- GLAB: result of running `glab --version` (glab version 1.32.0 (2023-08-18))
-->
- OS: Darwin 24.6.0 arm64
- SHELL: /bin/zsh
- TERM: xterm-256color
- GLAB: glab 1.67.0 (1e957280)
<!--
Please include any other information that you believe might be relevant
in debugging. For example, you may include a shell framework like oh-my-zsh
or other customizations like editing the prompt (PS1, PS2, and others).
-->
Other:
### Steps to reproduce
1. Open `glab ci view` for any pipeline with multiple jobs
1. Press Enter on the first job to view its logs (logs display correctly)
1. Press Escape to close the first job's logs
1. Navigate to a different job and press Enter to view its logs
1. Observe that only "Getting job trace..." appears instead of the full log content
1. this happens if the first job's log length is much greater than the second log's content
2. Also: the text should appear for each job, but is only shown on the first invocation
### What is the current _bug_ behavior?
- First job logs display correctly with the line `Showing logs for [Name] job #[ID].` and the full log content
- Second and subsequent job logs show only "Getting job trace..." (22 characters)
- The logs appear to "hang" showing just the placeholder text
- `RunTraceSha` completes successfully but TextView only contains the placeholder text
### What is the expected _correct_ behavior?
- All job logs should display their full content regardless of how many job logs have been viewed previously in the same session
- Each job's logs should be independent and show complete trace output
### Relevant logs and/or screenshots
{width=934 height=463}
I added debug logging to `runTraceSha()` and followed the steps above to reproduce:
```
[DEBUG] First job - runTraceSha completed, content length: 96048 ✅
[DEBUG] Second job - runTraceSha completed, content length: 22 ❌
```
Note that "Getting job trace..." is 22 characters, and is the only text emitted from `RunTraceSha` on subsequent executions.
### Possible fixes
I believe the root cause for both issues (log truncation and the omission of the "Showing logs for..." line) is using globally-scoped variables:
in `internal/commands/ci/ciutils/utils.go:27`
```go
var (
once sync.Once
offset int64
)
```
in `internal/commands/ci/ciutils/utils.go:92`
```go
// Note: runTraceSha wraps runTrace
func runTrace(ctx context.Context, apiClient *gitlab.Client, w io.Writer, pid any, jobId int) error {
fmt.Fprintln(w, "Getting job trace...")
for range time.NewTicker(time.Second * 3).C {
[...]
once.Do(func() { // ISSUE 1
fmt.Fprintf(w, "Showing logs for %s job #%d.\n", job.Name, job.ID)
})
trace, _, err := apiClient.Jobs.GetTraceFile(pid, jobId)
if err != nil || trace == nil {
return errors.Wrap(err, "failed to find job")
}
_, _ = io.CopyN(io.Discard, trace, offset)
lenT, err := io.Copy(w, trace)
[...]
offset += lenT // ISSUE 2
}
return nil
```
In the snippet above, there are two issues:
1. `once.Do()` is guaranteed to execute exactly once, no matter how many times it's called, which means the text "Showing logs for..." will only appear the first time job logs are retrieved.
2. Because `offset` is a global, in subsequent calls to `runTrace()`, the value of `offset` might be greater than the length of the log that is loaded. At best, this will truncate the beginning of the subsequent log text. At worst, it will skip the entire content of the log file
<!-- If you can, link to the line of code that might be responsible for the problem -->
issue