feat: migrate to Echo v5 - #489
Merged
Merged
Conversation
Forced by #477. Dependabot's otelecho 0.69.0 -> 0.71.0 bump cannot merge: 0.71.0 marks the package deprecated, staticcheck's SA1019 fires and go-vet fails. The replacement instruments Echo v5, and its migration guide says applications must migrate Echo first. Almost all of the echo surface is generated. Flipping 25 cfg.yaml to echo5-server and regenerating fixed roughly 900 call sites, leaving a small hand-written remainder: - echo5-server generates StrictHandlerFunc in each of the 25 gen packages, so api.ScopeMiddleware cannot name any one of them. api.StrictHandlerFunc is declared once and the 23 call sites convert across the boundary, which Go permits since the signatures match. - v5 has no Shutdown. StartConfig.Start drains when its context is cancelled, so both servers hold a CancelFunc that Stop calls. - Response() returns a bare http.ResponseWriter: status comes from echo.UnwrapResponse, Flush from an http.Flusher assertion. - Routes() moved to Router().Routes(); HideBanner moved to StartConfig. The generator stays at the pinned v2.7.1. It already ships the echo5 templates, so no bump was needed, and bumping to v2.8.0 dragged in unrelated enum renaming and dropped auth scopes. slog-echo is removed rather than upgraded. Its only v5-compatible release tests for a routing error with err.(*echo.HTTPError), and v5 returns the unexported *echo.httpError, so every 404 became a 500. There is nothing newer to upgrade to. internal/telemetry/httplog uses Echo's own RequestLogger with HandleError set, which resolves the status through the error handler that knows it, and takes the same *slog.Logger from cmd/root.go so colouring, --json and trace handling are unchanged. CORS is registered only when origins are configured. v5 panics on an empty AllowOrigins where v4 defaulted to "*", and neither inheriting a permissive default nobody chose nor erroring on an optional feature is right. The middleware is left off, the browser applies same-origin policy, and both outcomes are logged so an operator is not left inferring it from a browser error. 72 packages pass, coverage 100.0%, golangci-lint reports 0 issues. Coverage was checked function by function against main rather than by the total: the only difference is httplog.New appearing at 100%. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FuKUsHFG1EqZXamffh9M2c
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #489 +/- ##
=======================================
Coverage 99.94% 99.94%
=======================================
Files 484 485 +1
Lines 22876 22940 +64
=======================================
+ Hits 22864 22928 +64
Misses 12 12
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migrates osapi from Echo v4 to Echo v5.
This was forced by #477. Dependabot's
otelecho0.69.0 → 0.71.0 bump cannotmerge: 0.71.0 marks the package deprecated, staticcheck's SA1019 fires, and
go-vetfails. The replacement,github.com/labstack/echo-opentelemetry,instruments Echo v5, so its migration guide says plainly
that applications must migrate Echo first.
Reviewing this
Skip the 26
.gen.gofiles. The split is lopsided:Most of the generated churn is not Echo. The committed files were produced by
oapi-codegen v2.5.1 while
go.modhas pinned v2.7.1 for some time, soregenerating drags in two versions of accumulated output: 69
Valid()methods,118
ContentTypehelpers, abearerAuthContextKeytype, andomitemptyaddedto four optional fields. Pre-existing drift that any regeneration surfaces.
Dependencies
labstack/echoecho-serverecho5-server+strict-echo5otelecholabstack/echo-opentelemetryv0.0.3samber/slog-echogo mod tidydroppedecho/v4,otelechoandslog-echoentirely. Thegenerator stays at the pinned v2.7.1: v2.7.1 already ships the echo5
templates, so no bump was needed, and bumping it dragged in unrelated enum
renaming and dropped auth scopes.
Hand-written changes
echo5-servergeneratesStrictHandlerFuncin eachof the 25
genpackages, soapi.ScopeMiddlewarecannot name any one of them.api.StrictHandlerFuncis declared once and the 23 call sites convert acrossthe boundary, which Go permits since the signatures match.
Shutdown;StartConfig.Start(ctx, e)drainswhen its context is cancelled. Both servers now hold a
CancelFuncthatStopcalls.
Response()returns a barehttp.ResponseWriter. Status comes fromecho.UnwrapResponse, andFlushfrom anhttp.Flusherassertion.Routes()moved toRouter().Routes().HideBannermoved ontoStartConfig.Two behaviour changes worth knowing
CORS is registered only when origins are configured. v5 panics on an empty
AllowOriginswhere v4 silently defaulted to*, so an unconfigured deploymentwould have crashed at startup rather than only in tests.
Inheriting the
*was the wrong fix: it sendsAccess-Control-Allow-Origin: *from an authenticated API without anyonechoosing it. Returning an error was also wrong, since CORS is optional and that
would break every deployment that never set it. So the middleware is left off,
the browser applies same-origin policy, and both outcomes are logged:
Nothing in
internal/orpkg/panics, which this preserves.slog-echois gone, replaced byinternal/telemetry/httplog. The onlyv5-compatible release, v2.1.0, turns every routing error into a 500:
Echo v5's
ErrNotFoundis*echo.httpError— unexported — so neither thatassertion nor
errors.Ascan ever match it. Every 404 became a 500. There is nonewer release to upgrade to, so
httploguses Echo's ownRequestLoggerWithConfigwithHandleError: true, which resolves the statusthrough the error handler that actually knows it.
It takes the same
*slog.Loggerbuilt incmd/root.go, sotintcolouring,--json,--debugand thetracinghandler all pass through unchanged, and itkeeps slog-echo's
request/responsefield groups so log consumers keepworking.
Verification
71/71 packages pass. Coverage is 100.0%, verified function-by-function
against
mainrather than by the headline number: the only difference ishttplog.Newappearing at 100%. Nothing regressed.Two functions had dropped and both are covered:
api.Server.Stophttplog.NewStop's gap was real: v5 shuts down by cancelling a context, soStopbeforeStarthas nothing to cancel and would have panicked on a nilCancelFuncduring a failed boot. There is now a row for that and for double-stop.
httplog's suite includes a regression test asserting an unmatched route logs404 — confirmed to fail against slog-echo rather than pass vacuously.
The CORS change needed a behavioural test, not a coverage number. The skipped
branch has no statements, so
go tool cover -funcreported 100% whether or notit ever ran, and the existing test only asserted
NotNil. There is now a probeissuing a real cross-origin request and checking the header in both directions,
verified by reintroducing the v4 behaviour:
Follow-up, not in here
#477can now be closed rather than merged:otelechois gone, so there isnothing to bump. The bug in
samber/slog-echo/v2is still upstream and unfixed;we are simply no longer affected.
🤖 Generated with Claude Code
https://claude.ai/code/session_01FuKUsHFG1EqZXamffh9M2c