Skip to content

Repository files navigation

errx-migrate

Automatic github.com/pkg/errorsgithub.com/go-extras/errx migrator.

pkg/errors is no longer actively maintained — it has been in maintenance mode for years (its last release, v0.9.1, is from January 2020) and now accepts only bug fixes, not new functionality. errx-migrate is a small, type-aware AST rewriter that converts a pkg/errors-based codebase to errx safely and idempotently — leaving gofmt-clean output and explicit // TODO(errx-migrate) markers wherever a conversion cannot be proven safe.

It is type-aware: it uses golang.org/x/tools/go/packages to resolve which errors identifier is actually pkg/errors (handling aliases, dot-imports, and shadowing), so it never rewrites stdlib errors calls by mistake.

Install

go install github.com/go-extras/errx-migrate/cmd/errx-migrate@latest

Usage

# Preview a migration as a unified diff (no files written):
errx-migrate -diff ./...

# Apply the migration in place:
errx-migrate -w ./...

# Lean mode: map to the zero-dependency errx core + stdlib instead of the stacktrace subpackage:
errx-migrate -w -no-stack ./...

# Compat mode: route through the errx compat package (errxc), which accepts plain error classifications:
errx-migrate -w -compat ./...

Flags:

Flag Description
-w Write changes back to source files (default: print the result to stdout).
-diff Print a unified diff instead of writing or printing the full file.
-no-stack Lean mode — map to the dependency-free errx core + stdlib errors/fmt, dropping implicit stack capture. The default is stack-preserving mode.
-compat Compat mode — route the message/wrap family through the errx/compat package (imported as errxc), whose functions accept plain error classifications. Like lean it drops implicit stacks, but an explicit errors.WithStack is still preserved as errxtrace.Classify. Mutually exclusive with -no-stack.
-include-generated Also rewrite generated files (// Code generated ... DO NOT EDIT.). Skipped by default.

When they are imported, errx/stacktrace is aliased as errxtrace and errx/compat as errxc. (errx/stacktrace may not appear at all in a lean or compat run if no errors.WithStack rewrite needs it.)

The tool must be able to type-check the target packages, so run it from the module root with the relevant build tags/GOOS set if you have platform-specific files.

Modes

Stack-preserving (default). Stack-capturing pkg/errors calls are mapped to the errx/stacktrace subpackage (imported as errxtrace) so runtime behavior is unchanged.

Lean (-no-stack). Implicit stack capture is dropped and calls map to the zero-dependency errx core plus stdlib errors/fmt. Explicit errors.WithStack is still preserved as errxtrace.Classify (an explicit request for a stack is honored).

Compat (-compat). The message/wrap family is routed through the errx/compat package (imported as errxc), whose functions accept plain error classifications rather than errx.Classified — handy when you also classify with third-party error values. Like lean it carries no implicit stack; an explicit errors.WithStack is still preserved as errxtrace.Classify. The migrator always prefers errx/errxtrace where possible and only routes through compat in this mode.

API mapping

pkg/errors stack-preserving (default) lean (-no-stack) compat (-compat)
errors.New(msg) errxtrace.ClassifyNew(msg) errors.New(msg) (stdlib) errxc.ClassifyNew(msg)
errors.Errorf(f, a…) errxtrace.ClassifyNew(fmt.Sprintf(f, a…)) fmt.Errorf(f, a…) errxc.ClassifyNew(fmt.Sprintf(f, a…))
errors.Wrap(err, msg) errxtrace.Wrap(msg, err) errx.Wrap(msg, err) errxc.Wrap(msg, err)
errors.Wrapf(err, f, a…) errxtrace.Wrap(fmt.Sprintf(f, a…), err) errx.Wrap(fmt.Sprintf(f, a…), err) errxc.Wrap(fmt.Sprintf(f, a…), err)
errors.WithMessage(err, msg) errx.Wrap(msg, err) errx.Wrap(msg, err) errxc.Wrap(msg, err)
errors.WithMessagef(err, f, a…) errx.Wrap(fmt.Sprintf(f, a…), err) same errxc.Wrap(fmt.Sprintf(f, a…), err)
errors.WithStack(err) errxtrace.Classify(err) errxtrace.Classify(err) errxtrace.Classify(err)
errors.Is / As / Unwrap stdlib errors.* same same
errors.Cause(err) inline root-unwrap + // TODO(errx-migrate) same same

Notes:

  • Argument order swaps. pkg/errors.Wrap(err, msg) becomes errx.Wrap(msg, err) — the cause and message arguments are reordered.
  • Nil semantics match. pkg/errors.Wrap(nil, …) and errx.Wrap(…, nil) both return nil.
  • fmt.Sprintf injection is flagged. Where a dynamic message is injected (Wrapf, WithMessagef, and stack-mode Errorf), a // TODO(errx-migrate) notes that a dynamic error message is non-idiomatic and a constant message string is preferred.
  • errors.Cause is rewritten to an inline root-unwrap loop with a // TODO(errx-migrate) recommending you review it (often errors.Is/As is the better fit).
  • %+v stack formatting is left untouched; with stack-preserving mode and a recent errx, the captured trace still renders via %+v.

Limitations

The migrator is deliberately conservative: when it cannot prove a rewrite safe, it leaves the call in place with a // TODO(errx-migrate) comment for manual review rather than guess. In practice this happens for:

  • a pkg/errors call buried inside another (for example inside a closure passed to the call being wrapped);
  • a Wrapf/Errorf/WithMessagef whose format string contains %w (which fmt.Sprintf cannot render);
  • a comment sitting between the arguments of a call whose arguments are reordered.

Other notes:

  • Build tags / GOOS. Only files that type-check for the current GOOS/GOARCH are rewritten. Re-run with GOOS=windows, GOOS=darwin, etc. to cover platform-specific files.
  • Test files are migrated too.
  • The target packages must type-check (the tool relies on full type information).

As a vet/golangci-lint analyzer

The rewriter is also exposed as a go/analysis Analyzer that emits SuggestedFixes, so it can run under go vet, gopls, or golangci-lint.

Development

make build   # build bin/errx-migrate
make test    # go test -race ./...
make lint    # golangci-lint run ./...
make fmt     # golangci-lint run --fix ./...

License

MIT — see LICENSE.

About

Migrate to errx from other libraries (e.g. pkg/errors)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages