
go-format is an opinionated Go formatter for codebases that already use
gofmt, but still want more readable line wrapping and vertical spacing.
It runs:
gofmt
- pinned
golines for long-line wrapping
gofumpt
- a logical blank-line pass for dense functions
- optional safe source mutations
- optional non-mutating optimization advice
The blank-line pass separates guard clauses, validation and normalization,
setup/defer cleanup blocks, decision branches, lock groups, loops/switches, and
final returns. It keeps coupled value, err := call() / if err != nil pairs
together and keeps leading comments attached to the statement they describe.
It also separates top-level declaration groups, receiver groups, field groups,
large switch cases, and test flow blocks. It does not insert blank lines between
every statement.
Installation
go install github.com/stremovskyy/go-format@latest
For reproducible CI, install or run a tagged version:
go run github.com/stremovskyy/go-format@v0.1.0 --check ./...
Usage
Format the current directory:
go-format --write ./...
Check formatting in CI:
go-format --check ./...
Use a different target line length:
go-format --write --max-len 100 ./...
Limit parallel file formatting:
go-format --write --jobs 4 ./...
Skip golines when only gofmt, gofumpt, and logical blank lines are wanted:
go-format --write --skip-golines ./...
Format editor input from stdin:
go-format --stdin --stdin-path internal/cli/cli.go < internal/cli/cli.go
Print non-mutating optimization advice:
go-format --advice ./...
Preview safe code mutations without writing files:
go-format --check --mutate ./...
Apply safe code mutations:
go-format --write --mutate ./...
Fail CI when advice issues are found:
go-format --check --advice-fail ./...
Create a project config:
go-format --init
Print the effective config after discovery and CLI overrides:
go-format --print-config
List files that would change without printing diffs:
go-format --check --list --diff=false ./...
Disable progress output for quiet scripts:
go-format --check --progress=false ./...
Print the bundled formatter versions:
go-format --version
More examples and before/after comparisons are in docs/examples.md.
Behavior
go-format recursively discovers .go files under the provided paths and skips:
- generated files with
Code generated ... DO NOT EDIT
.git
vendor
third_party
node_modules
- hidden directories unless
--include-hidden is set
--check prints unified diffs and exits with status 1 when files need
formatting. Use --diff=false to suppress diffs and --list to print changed
file paths. --write rewrites files in place and can also use --list.
Path-based runs format files concurrently by default using GOMAXPROCS. Use
--jobs 1 for sequential processing or --jobs N to set a fixed worker count.
Path-based runs print file progress to standard error; use --progress=false to
keep script logs quiet.
--stdin formats source from standard input and writes the formatted source to
standard output. It accepts --stdin-path so parse errors and formatter
subprocesses can use a meaningful file name.
The first run may download and build the pinned golines module into the local
Go module and user cache. Later runs reuse the cached golines binary. Use
--skip-golines for environments that must avoid that subprocess. Use
--skip-readability to disable only the logical blank-line pass.
--mutate applies conservative source mutations after normal formatting and
before the readability pass. The first mutation set generates placeholder GoDoc
comments for undocumented exported top-level symbols, rewrites simple
fmt.Errorf("... %v", err) calls to use %w, and simplifies direct
if condition { return true } return false bool chains. In --check mode,
these mutations are shown as diffs and are not written.
--advice prints non-mutating optimization findings to standard error. Without
an explicit --write, advice runs in check mode and does not rewrite files.
Advice includes struct padding opportunities, inconsistent receiver names,
misplaced context.Context, missed %w error wrapping, defer inside loops,
function-local regexp.MustCompile, append-in-loop preallocation candidates,
strings.Builder literals without Grow, missing GoDoc on exported symbols,
and TODO format checks. Use --advice-fail to exit with status 1 when advice
issues are found.
Configuration
go-format discovers .go-format.yml from the current directory upward. CLI
flags override discovered config values, and --no-config disables discovery
for one run.
max_len: 120
skip_golines: false
skip_readability: false
advice: false
advice_fail: false
mutate: false
include_hidden: false
go_toolchain: local
exclude: []
Use --config path/to/.go-format.yml for an explicit config path. exclude
accepts simple filepath-style patterns relative to each formatted root, for
example ignored/** or *.pb.go.
CI and Editors
GitHub Actions can run the formatter directly from the module:
- name: Format check
run: go run github.com/stremovskyy/go-format@v0.1.0 --check ./...
For this repository, CI uses the checked-out command:
- name: Format check
run: go run . --check --progress=false ./...
A minimal pre-commit hook can format staged Go changes before commit:
#!/bin/sh
go-format --write ./...
git add $(git ls-files '*.go')
Editors can format buffers through stdin without touching files directly:
go-format --stdin --stdin-path "$FILE" < "$FILE"
Release
Releases use semver tags:
git tag v0.1.0
git push origin v0.1.0