-
Notifications
You must be signed in to change notification settings - Fork 1
ci: make go-ci test output visible in logs #860
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,16 +46,19 @@ jobs: | |
| - name: Run full tests with baseline | ||
| run: | | ||
| mkdir -p target | ||
| go test -json ./... > target/test-baseline.json | ||
| go test ./... > target/test-baseline.txt | ||
| set +e | ||
| go test -json -count=1 ./... 2>&1 | tee target/test-baseline.json | ||
| test_exit=${PIPESTATUS[0]} | ||
| set -e | ||
| # Fail the step if tests failed | ||
| exit "${test_exit}" | ||
| - name: Upload baseline artifact | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: go-test-baseline | ||
| path: | | ||
| target/test-baseline.json | ||
| target/test-baseline.txt | ||
| if-no-files-found: error | ||
| path: target/test-baseline.json | ||
|
Comment on lines
59
to
+60
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial CI and local baseline generation now diverge.
🤖 Prompt for AI Agents |
||
| if-no-files-found: warn | ||
|
|
||
| quality-ci: | ||
| name: quality-ci | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,35 +10,37 @@ import ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestGinLogrusRecoveryRepanicsErrAbortHandler(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Test the recovery logic directly: gin.CustomRecovery's internal recovery | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // handling varies across platforms (macOS vs Linux) and Go versions, so we | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // invoke the recovery callback that GinLogrusRecovery passes to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // gin.CustomRecovery and verify it re-panics ErrAbortHandler. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gin.SetMode(gin.TestMode) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| engine := gin.New() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| engine.Use(GinLogrusRecovery()) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| engine.GET("/abort", func(c *gin.Context) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| panic(http.ErrAbortHandler) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| req := httptest.NewRequest(http.MethodGet, "/abort", nil) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| recorder := httptest.NewRecorder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| recovered := recover() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if recovered == nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("expected panic, got nil") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err, ok := recovered.(error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !ok { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("expected error panic, got %T", recovered) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !errors.Is(err, http.ErrAbortHandler) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("expected ErrAbortHandler, got %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if err != http.ErrAbortHandler { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("expected exact ErrAbortHandler sentinel, got %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var repanicked bool | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var repanic interface{} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| defer func() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if r := recover(); r != nil { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| repanicked = true | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| repanic = r | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Simulate what gin.CustomRecovery does: call the recovery func | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // with the recovered value. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ginLogrusRecoveryFunc(nil, http.ErrAbortHandler) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| engine.ServeHTTP(recorder, req) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !repanicked { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("expected ginLogrusRecoveryFunc to re-panic http.ErrAbortHandler, but it did not") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| err, ok := repanic.(error) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !ok { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("expected error panic, got %T", repanic) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if !errors.Is(err, http.ErrAbortHandler) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| t.Fatalf("expected ErrAbortHandler, got %v", err) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
12
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Yes — the re-panic will propagate out of the In Gin v1.10.1, the recovery middleware does If nothing in your stack recovers it, the panic may still be recovered by Go’s Citations:
Extract the recovery callback into a testable helper function. The current test exceeds the 40-line function length limit (41 lines) and remains coupled to Gin's panic propagation semantics, which has introduced a failure in CI. While Extract the recovery logic into a standalone 🔧 Proposed refactordiff --git a/pkg/llmproxy/logging/gin_logger.go b/pkg/llmproxy/logging/gin_logger.go
@@
+func handleRecoveredPanic(c *gin.Context, recovered interface{}) {
+ if err, ok := recovered.(error); ok && errors.Is(err, http.ErrAbortHandler) {
+ panic(http.ErrAbortHandler)
+ }
+
+ log.WithFields(log.Fields{
+ "panic": recovered,
+ "stack": string(debug.Stack()),
+ "path": c.Request.URL.Path,
+ }).Error("recovered from panic")
+
+ c.AbortWithStatus(http.StatusInternalServerError)
+}
+
func GinLogrusRecovery() gin.HandlerFunc {
- return gin.CustomRecovery(func(c *gin.Context, recovered interface{}) {
- if err, ok := recovered.(error); ok && errors.Is(err, http.ErrAbortHandler) {
- panic(http.ErrAbortHandler)
- }
-
- log.WithFields(log.Fields{
- "panic": recovered,
- "stack": string(debug.Stack()),
- "path": c.Request.URL.Path,
- }).Error("recovered from panic")
-
- c.AbortWithStatus(http.StatusInternalServerError)
- })
+ return gin.CustomRecovery(handleRecoveredPanic)
}
diff --git a/pkg/llmproxy/logging/gin_logger_test.go b/pkg/llmproxy/logging/gin_logger_test.go
@@
func TestGinLogrusRecoveryRepanicsErrAbortHandler(t *testing.T) {
- // Test the recovery handler directly to avoid platform-dependent behavior
- // in gin's ServeHTTP panic propagation (macOS vs Linux differ).
- handler := GinLogrusRecovery()
-
gin.SetMode(gin.TestMode)
- engine := gin.New()
-
- var repanicked bool
- var repanic interface{}
-
- // Wrap the recovery middleware to intercept the re-panic before gin's
- // outer recovery can swallow it.
- engine.Use(func(c *gin.Context) {
- defer func() {
- if r := recover(); r != nil {
- repanicked = true
- repanic = r
- }
- }()
- handler(c)
- })
- engine.GET("/abort", func(c *gin.Context) {
- panic(http.ErrAbortHandler)
- })
-
- req := httptest.NewRequest(http.MethodGet, "/abort", nil)
- recorder := httptest.NewRecorder()
-
- engine.ServeHTTP(recorder, req)
-
- if !repanicked {
- t.Fatalf("expected GinLogrusRecovery to re-panic http.ErrAbortHandler, but it did not")
- }
- err, ok := repanic.(error)
- if !ok {
- t.Fatalf("expected error panic, got %T", repanic)
- }
- if !errors.Is(err, http.ErrAbortHandler) {
- t.Fatalf("expected ErrAbortHandler, got %v", err)
- }
+ c, _ := gin.CreateTestContext(httptest.NewRecorder())
+ c.Request = httptest.NewRequest(http.MethodGet, "/abort", nil)
+
+ defer func() {
+ r := recover()
+ err, ok := r.(error)
+ if !ok || !errors.Is(err, http.ErrAbortHandler) {
+ t.Fatalf("expected ErrAbortHandler, got %T (%v)", r, r)
+ }
+ }()
+
+ handleRecoveredPanic(c, http.ErrAbortHandler)
}📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Actions: pr-test-build[error] 44-51: GinLogrusRecovery: expected re-panic of http.ErrAbortHandler, but it did not 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| func TestGinLogrusRecoveryHandlesRegularPanic(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
Doesgo test -jsonguarantee JSON events on stdout while non-JSON diagnostics from thegotool may still be written to stderr?💡 Result:
Yes.
go test -jsonroutes the test run throughgo tool test2json, which “writes a corresponding stream of JSON events to standard output.” The JSON events include the test binary’s output (its stdout+stderr are merged intoTestEvent.Output). [1], [2]gocommand’s own diagnostics (for example, failures “building the tests”) are written to thegocommand’s standard error, which is “reserved for printing errors building the tests.” So non-JSON diagnostics from thegotool may still appear on stderr even when stdout is a JSON event stream. [3]Sources: [1]
cmd/test2jsondocs, [2] Go 1.10 release notes, [3]cmd/go/internal/testdocs/source.Keep stderr out of
test-baseline.json.2>&1 | tee target/test-baseline.jsonredirects thegocommand's own diagnostics (e.g., build errors) into the artifact, corrupting it with non-JSON content.go test -jsonsends JSON events to stdout only; remove2>&1so stderr stays in the job log where it should be.🔧 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents