Format Markdown with prettier in make fmt (closes #43)
check / check (push) Failing after 3s

script/fmt and script/fmt-check now run prettier on every Markdown file
after their Go steps, as the standard scripts from sneak/prompts at
dd4027b do. script/bootstrap gains the standard node, nvm and yarn
install next to its goimports install, and the last Dockerfile stage
copies package.json and yarn.lock before running it. .prettierrc,
.prettierignore and yarn.lock are the standard copies. package.json is
the standard one without its licence field and marked private, because
this repo's licence is the WTFPL. .dockerignore keeps the log of a failed
yarn install out of the build context. README.md and TODO.md are
reformatted once with it; REPO_POLICIES.md already matched.

Model: opus-5-5
This commit was merged in pull request #44.
This commit is contained in:
2026-10-06 16:22:56 +02:00
parent 17796569b8
commit cb74aa593d
11 changed files with 303 additions and 165 deletions
+3 -1
View File
@@ -72,10 +72,12 @@
**/*.sublime-*
# This repository's host-built artifacts: the example program's binary,
# test binaries and coverage output.
# test binaries, coverage output, and the log yarn writes when an install
# fails.
/cmd/example/example
/*.test
/*.out
/yarn-error.log
# aider's files, among them a config file that can hold an API key.
**/.aider*
+2
View File
@@ -0,0 +1,2 @@
node_modules/
yarn.lock
+4
View File
@@ -0,0 +1,4 @@
{
"tabWidth": 4,
"proseWrap": "always"
}
+1 -1
View File
@@ -31,6 +31,6 @@ COPY --from=lint /src/go.sum /dev/null
COPY --from=test /src/go.sum /dev/null
WORKDIR /src
COPY script/ script/
COPY go.mod go.sum ./
COPY go.mod go.sum package.json yarn.lock ./
RUN script/bootstrap
COPY . .
+75 -80
View File
@@ -3,11 +3,10 @@
## Summary
simplelog is an opinionated logging package designed to facilitate easy and
structured logging in Go applications with an absolute minimum of
boilerplate.
structured logging in Go applications with an absolute minimum of boilerplate.
The idea is that you can add a single import line which replaces the
stdlib `log/slog` default handler, and solve the 90% case for logging.
The idea is that you can add a single import line which replaces the stdlib
`log/slog` default handler, and solve the 90% case for logging.
## Current Status
@@ -18,16 +17,15 @@ Released v1.0.0 2024-06-14. Works as intended. No known bugs.
- if output is a tty, outputs pretty color logs
- if output is not a tty, outputs json
- supports delivering each log message via a webhook
- emits every `slog` attribute: those passed to a log call, those
accumulated with `WithAttrs`, and those qualified by `WithGroup`.
`slog.Group` values nest, and `slog.LogValuer` values are resolved. In
json output attributes are object fields (groups become nested objects);
in console output they are appended as `key=value` pairs, with grouped
keys written as `group.key=value`. See
[Attribute output](#attribute-output) for the details worth knowing
- reports a record that could not be delivered as an error from the
handler's `Handle` method. See
[When delivery fails](#when-delivery-fails) for how to receive it
- emits every `slog` attribute: those passed to a log call, those accumulated
with `WithAttrs`, and those qualified by `WithGroup`. `slog.Group` values
nest, and `slog.LogValuer` values are resolved. In json output attributes are
object fields (groups become nested objects); in console output they are
appended as `key=value` pairs, with grouped keys written as `group.key=value`.
See [Attribute output](#attribute-output) for the details worth knowing
- reports a record that could not be delivered as an error from the handler's
`Handle` method. See [When delivery fails](#when-delivery-fails) for how to
receive it
## Planned Features
@@ -49,9 +47,9 @@ go get sneak.berlin/go/simplelog
## Usage
Below is an example of how to use SimpleLog in a Go application. This
example is provided in the form of a `main.go` file, which demonstrates
logging at various levels using structured logging syntax.
Below is an example of how to use SimpleLog in a Go application. This example is
provided in the form of a `main.go` file, which demonstrates logging at various
levels using structured logging syntax.
```go
package main
@@ -73,63 +71,58 @@ func main() {
## Attribute output
Attributes reach every handler: the ones passed to the log call, the ones
accumulated with `WithAttrs`, and the ones qualified by the groups open at
the time they were attached. `slog.Group` values nest, and
`slog.LogValuer` values are resolved to the value they stand for. A few
behaviours are worth knowing before you rely on them.
accumulated with `WithAttrs`, and the ones qualified by the groups open at the
time they were attached. `slog.Group` values nest, and `slog.LogValuer` values
are resolved to the value they stand for. A few behaviours are worth knowing
before you rely on them.
**Your values are never modified.** Whatever you log is read and rendered,
never written to. A map or a slice you pass to `slog.Any` comes back from
the logger exactly as you handed it over, even when a group later uses the
same key.
**Your values are never modified.** Whatever you log is read and rendered, never
written to. A map or a slice you pass to `slog.Any` comes back from the logger
exactly as you handed it over, even when a group later uses the same key.
**Durations are nanoseconds in json, and readable on the console.** The
json and webhook payloads emit a `slog.Duration` as a number of
nanoseconds, matching `slog.NewJSONHandler`, so a consumer can compare and
aggregate the field without parsing it. The console line emits the same
duration as `3s`, matching `slog.NewTextHandler`, because a person reads
it.
**Durations are nanoseconds in json, and readable on the console.** The json and
webhook payloads emit a `slog.Duration` as a number of nanoseconds, matching
`slog.NewJSONHandler`, so a consumer can compare and aggregate the field without
parsing it. The console line emits the same duration as `3s`, matching
`slog.NewTextHandler`, because a person reads it.
**The json payload is an object, with the consequences an object has.**
The record's own fields are named `Time`, `Level`, `Message` and `PC`, and
they own those names: an attribute keyed after one of them is dropped from
the json and webhook output. A key logged more than once keeps its last
value there for the same reason, with one exception: when both are
`slog.Group` values sharing a key, the two groups are merged into one
object holding the members of both, rather than the second replacing the
first. Neither the collision nor the merge applies to the console output,
which is a line of text: every pair appears, in order. If you need a field
called `message`, pick a key that does not collide - the collision is
silent.
**The json payload is an object, with the consequences an object has.** The
record's own fields are named `Time`, `Level`, `Message` and `PC`, and they own
those names: an attribute keyed after one of them is dropped from the json and
webhook output. A key logged more than once keeps its last value there for the
same reason, with one exception: when both are `slog.Group` values sharing a
key, the two groups are merged into one object holding the members of both,
rather than the second replacing the first. Neither the collision nor the merge
applies to the console output, which is a line of text: every pair appears, in
order. If you need a field called `message`, pick a key that does not collide -
the collision is silent.
**Empty things follow the `slog.Handler` contract.** An empty `Attr` is
ignored, an empty group is elided along with its key, a group with an
empty key is inlined into its parent, and `WithGroup("")` returns the
handler unchanged.
**Empty things follow the `slog.Handler` contract.** An empty `Attr` is ignored,
an empty group is elided along with its key, a group with an empty key is
inlined into its parent, and `WithGroup("")` returns the handler unchanged.
## When delivery fails
Every handler returns an error from `Handle` when it could not deliver
the record:
Every handler returns an error from `Handle` when it could not deliver the
record:
- `ConsoleHandler` and `JSONHandler` return the error from their write to
stdout, wrapped, so `errors.Is` still matches the original
- `WebhookHandler` returns an error when the request fails, and also when
the server answers with a status outside 2xx, a redirect included,
since it does not follow redirects. A request that has not finished
after 5 seconds fails with a timeout error, so a webhook server that
never answers holds up a log call for 5 seconds at most
- `MultiplexHandler`, which simplelog installs as the default, passes the
record to every handler it holds even after one of them fails, then
returns all their errors joined with `errors.Join` (nil if none failed)
- `WebhookHandler` returns an error when the request fails, and also when the
server answers with a status outside 2xx, a redirect included, since it does
not follow redirects. A request that has not finished after 5 seconds fails
with a timeout error, so a webhook server that never answers holds up a log
call for 5 seconds at most
- `MultiplexHandler`, which simplelog installs as the default, passes the record
to every handler it holds even after one of them fails, then returns all their
errors joined with `errors.Join` (nil if none failed)
`slog.Info`, `slog.Error` and the other `slog.Logger` methods throw that
error away. That is how `log/slog` works, and simplelog cannot change it.
To find out whether a record was delivered, build a `slog.Record` and pass
it to the handler yourself, for example
`slog.Default().Handler().Handle(ctx, record)`, then check the error it
returns. Console output takes the file and line from the record's `PC`,
which you set with `runtime.Callers` as the `log/slog` documentation
`slog.Info`, `slog.Error` and the other `slog.Logger` methods throw that error
away. That is how `log/slog` works, and simplelog cannot change it. To find out
whether a record was delivered, build a `slog.Record` and pass it to the handler
yourself, for example `slog.Default().Handler().Handle(ctx, record)`, then check
the error it returns. Console output takes the file and line from the record's
`PC`, which you set with `runtime.Callers` as the `log/slog` documentation
shows. A record whose `PC` is zero prints `???:0` instead.
## Entrypoints
@@ -143,8 +136,10 @@ alpine. We provide:
- `script/bootstrap` — install all dependencies (go if missing, goimports with
`go install` at a pinned commit unless the installed one already reports the
pinned version, then `go mod download`); golangci-lint is not installed,
since it runs only in Docker
pinned version, then `go mod download`; node through nvm at a pinned version
if node is missing, yarn at a pinned version if it is missing, then the
`prettier` that `package.json` and `yarn.lock` pin); golangci-lint is not
installed, since it runs only in Docker
- `script/setup` — set up the repo for development after a fresh clone: runs
`script/bootstrap`, then `script/install-precommit`
- `script/projectname` — output the project name (our own extension); used by
@@ -152,23 +147,23 @@ alpine. We provide:
- `script/test` — run the test suite under the race detector in Docker by
building only the `test` stage of the `Dockerfile`, without the build cache,
so every run tests; the image is tagged `simplelog-test`
- `script/lint` — run golangci-lint in Docker by building only the `lint`
stage of the `Dockerfile`, without the build cache, so every run lints; the
image is tagged `simplelog-lint`
- `script/fmt` — format all files (writes) with the goimports that
`script/bootstrap` installs
- `script/fmt-check` — check formatting (read-only); fails if `gofmt -l`
reports files
- `script/lint` — run golangci-lint in Docker by building only the `lint` stage
of the `Dockerfile`, without the build cache, so every run lints; the image is
tagged `simplelog-lint`
- `script/fmt` — format all files (writes): Go with the goimports that
`script/bootstrap` installs, then every `*.md` file with `prettier`
- `script/fmt-check` — check formatting (read-only); fails if `gofmt -l` reports
files, or if `prettier` would change any `*.md` file
- `script/check` — run all checks: `test`, `lint`, `fmt-check` (our own
extension)
- `script/docker` — build the Docker image without the build cache, tagged
via `script/projectname` (byte-identical across repos); it also passes the
output of `git describe` as the `VERSION` build argument, which is ignored
because this `Dockerfile` does not declare it
- `script/cibuild` — what CI runs: `script/bootstrap`, then `script/check`,
then the same image build as `script/docker` (byte-identical across repos)
- `script/precommit` — run by the git pre-commit hook (our own extension);
runs a `go mod tidy` guard, then `script/check`
- `script/docker` — build the Docker image without the build cache, tagged via
`script/projectname` (byte-identical across repos); it also passes the output
of `git describe` as the `VERSION` build argument, which is ignored because
this `Dockerfile` does not declare it
- `script/cibuild` — what CI runs: `script/bootstrap`, then `script/check`, then
the same image build as `script/docker` (byte-identical across repos)
- `script/precommit` — run by the git pre-commit hook (our own extension); runs
a `go mod tidy` guard, then `script/check`
- `script/install-precommit` — installs the git pre-commit hook (our own
extension); `make hooks` shims to it
+73 -70
View File
@@ -1,86 +1,89 @@
# Workflow
* branch (from `main`)
* do the work in Next Step
* move Next Step to the top of Completed Steps
* move the top item of Future Steps into Next Step
* commit (`TODO.md` changes in the same commit as the work)
* merge to `main` if the branch is not protected, otherwise open a PR
* push
- branch (from `main`)
- do the work in Next Step
- move Next Step to the top of Completed Steps
- move the top item of Future Steps into Next Step
- commit (`TODO.md` changes in the same commit as the work)
- merge to `main` if the branch is not protected, otherwise open a PR
- push
# Status
1.0+
Tagged v1.0.0 (2024-06-14) and 1.0.1 (2026-02-08). In post-1.0
maintenance; the library is referenced by the Go styleguide.
Tagged v1.0.0 (2024-06-14) and 1.0.1 (2026-02-08). In post-1.0 maintenance; the
library is referenced by the Go styleguide.
# Next Step
Restructure README.md into the standard sections: Description, Getting
Started, Rationale, Design, TODO, License, Author
Restructure README.md into the standard sections: Description, Getting Started,
Rationale, Design, TODO, License, Author
# Completed Steps
* 2026-10-06: re-vendored the standard files from `sneak/prompts` at
commit `dd4027b`: `REPO_POLICIES.md`, the CI workflow, `.gitignore`,
and `.golangci.yml` together with the lint stage's new image, plus
the `.editorconfig` and `.dockerignore` this repo lacked, which
finishes the Makefile and policy-files step. `script/cibuild` now
bootstraps and runs `script/check` before building the image, the
format check runs only on the host, the last `Dockerfile` stage runs
`script/bootstrap`, which installs goimports at a pinned commit
* 2026-10-06: a webhook request now times out after 5 seconds, so a
server that never answers no longer stops every log call; the webhook
handler also reads each answer to the end so its connection is reused
* 2026-10-06: the console handler takes the file and line it prints from
the record's `PC` instead of counting stack frames, so they name the
call site whether the record came through a `slog.Logger` or straight
to `Handle`
* 2026-10-06: the tests run under the race detector, in Docker:
`script/test` builds the `test` stage of the `Dockerfile`, which runs
`go test -race`, and a new final stage makes a plain `docker build`
run both the `lint` and `test` stages
* 2026-10-06: the linter runs only in Docker: `script/lint` builds the
`lint` stage of the `Dockerfile`, every `docker build` in `script/`
runs without the build cache, and `script/bootstrap` no longer
installs golangci-lint
* 2026-10-06: every handler now returns a failed delivery from `Handle`
instead of discarding it: console and JSON return the stdout write
error, the webhook also fails on a non-2xx answer, and the multiplex
delivers to every handler and returns their errors joined
* 2026-08-10: fixed every handler discarding slog attributes: console,
JSON and webhook handlers now emit record attributes, accumulate
WithAttrs without mutating the receiver, and honour WithGroup;
slog.Group values nest and LogValuer values are resolved; console
keys and values holding invalid UTF-8 are quoted
* 2026-08-07: added canonical `.golangci.yml` (v2 schema), pinned the
`Dockerfile` lint stage to golangci-lint v2.12.2 (tag+digest), and
fixed all findings the v1→v2 jump surfaced without changing any
exported signatures or behavior
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints,
Makefile shims, README Entrypoints section
* 2026-02-08: fixed JSONHandler deadlock from recursive log.Println,
with regression test; tagged 1.0.1
* 2024-06-14: 1.0 prep: lint and fmt enforced in Docker build, call
stack depth fix so log locations report correctly, example script,
removed non-building RELP code; tagged v1.0.0
* 2024-05-22: module path moved to sneak.berlin/go/simplelog
* 2024-05-14: initial library: slog-based console, JSON, webhook, and
RELP handlers, MultiplexHandler, caller file/line info, UTC ISO
timestamps, level-colored output
- 2026-10-06: `make fmt` and `make fmt-check` also format Markdown, with
`prettier` at the standard settings, pinned through `package.json` and
`yarn.lock`; `script/bootstrap` installs node and yarn for it, and the
existing Markdown was formatted once
- 2026-10-06: re-vendored the standard files from `sneak/prompts` at commit
`dd4027b`: `REPO_POLICIES.md`, the CI workflow, `.gitignore`, and
`.golangci.yml` together with the lint stage's new image, plus the
`.editorconfig` and `.dockerignore` this repo lacked, which finishes the
Makefile and policy-files step. `script/cibuild` now bootstraps and runs
`script/check` before building the image, the format check runs only on the
host, the last `Dockerfile` stage runs `script/bootstrap`, which installs
goimports at a pinned commit
- 2026-10-06: a webhook request now times out after 5 seconds, so a server that
never answers no longer stops every log call; the webhook handler also reads
each answer to the end so its connection is reused
- 2026-10-06: the console handler takes the file and line it prints from the
record's `PC` instead of counting stack frames, so they name the call site
whether the record came through a `slog.Logger` or straight to `Handle`
- 2026-10-06: the tests run under the race detector, in Docker: `script/test`
builds the `test` stage of the `Dockerfile`, which runs `go test -race`, and a
new final stage makes a plain `docker build` run both the `lint` and `test`
stages
- 2026-10-06: the linter runs only in Docker: `script/lint` builds the `lint`
stage of the `Dockerfile`, every `docker build` in `script/` runs without the
build cache, and `script/bootstrap` no longer installs golangci-lint
- 2026-10-06: every handler now returns a failed delivery from `Handle` instead
of discarding it: console and JSON return the stdout write error, the webhook
also fails on a non-2xx answer, and the multiplex delivers to every handler
and returns their errors joined
- 2026-08-10: fixed every handler discarding slog attributes: console, JSON and
webhook handlers now emit record attributes, accumulate WithAttrs without
mutating the receiver, and honour WithGroup; slog.Group values nest and
LogValuer values are resolved; console keys and values holding invalid UTF-8
are quoted
- 2026-08-07: added canonical `.golangci.yml` (v2 schema), pinned the
`Dockerfile` lint stage to golangci-lint v2.12.2 (tag+digest), and fixed all
findings the v1→v2 jump surfaced without changing any exported signatures or
behavior
* 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints, Makefile
shims, README Entrypoints section
- 2026-02-08: fixed JSONHandler deadlock from recursive log.Println, with
regression test; tagged 1.0.1
- 2024-06-14: 1.0 prep: lint and fmt enforced in Docker build, call stack depth
fix so log locations report correctly, example script, removed non-building
RELP code; tagged v1.0.0
- 2024-05-22: module path moved to sneak.berlin/go/simplelog
- 2024-05-14: initial library: slog-based console, JSON, webhook, and RELP
handlers, MultiplexHandler, caller file/line info, UTC ISO timestamps,
level-colored output
# Future Steps
* Delete the old plain TODO file once this TODO.md lands
* Add .aider.* to .gitignore and remove the stray aider artifacts from
the working tree
* Pick one tag scheme before the next release (v1.0.0 vs 1.0.1 are
inconsistent)
* Tag v1.0.2, with the leading v, once the attribute fix lands, so
consuming repos can move off pseudo-version pins in one step
* Fix RELP output to cache (from old TODO)
* Re-add RELP delivery over TCP to remote rsyslog imrelp; removed
2024-06-14 because it did not build (README planned feature)
* Add regex filtering for webhook logs (from old TODO)
* Better console output format (from old TODO)
- Delete the old plain TODO file once this TODO.md lands
- Add .aider.\* to .gitignore and remove the stray aider artifacts from the
working tree
- Pick one tag scheme before the next release (v1.0.0 vs 1.0.1 are inconsistent)
- Tag v1.0.2, with the leading v, once the attribute fix lands, so consuming
repos can move off pseudo-version pins in one step
- Fix RELP output to cache (from old TODO)
- Re-add RELP delivery over TCP to remote rsyslog imrelp; removed 2024-06-14
because it did not build (README planned feature)
- Add regex filtering for webhook logs (from old TODO)
- Better console output format (from old TODO)
+6
View File
@@ -0,0 +1,6 @@
{
"private": true,
"devDependencies": {
"prettier": "3.8.1"
}
}
+78 -1
View File
@@ -2,11 +2,21 @@
# script/bootstrap: install all dependencies needed to build and develop
# this repo. Idempotent: every install is guarded by a check so already
# installed tools are skipped. Base tooling comes from nix, apt, brew,
# or apk (detected in that order); assumes nothing is present.
# or apk (detected in that order); assumes nothing is present. Node is
# used directly if installed; otherwise it is installed at a pinned
# version via nvm (installing nvm itself first, from a hash-verified
# release archive, never curl | sh).
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Pinned versions, 2026-07-06
NODE_VERSION="22.17.0"
NVM_VERSION="0.40.3"
# sha256 of https://github.com/nvm-sh/nvm/archive/refs/tags/v0.40.3.tar.gz
NVM_SHA256="5f4d6aaa04a177dc93c985e31dbc411ab6b8c6e1e21d8015dbc1372625fcd1d0"
YARN_VERSION="1.22.22"
# goimports from golang.org/x/tools v0.30.0, 2025-02-10: the last release
# that Go 1.22, the Go of go.mod and the Dockerfile, can build.
GOIMPORTS_COMMIT="09747cdf594a7924dcecb506312be3bd6e437962"
@@ -81,6 +91,69 @@ ensure_goimports() {
echo "goimports $GOIMPORTS_VERSION at $(command -v goimports)"
}
# verify_sha256 <file> <expected-hash>
verify_sha256() {
if command -v sha256sum >/dev/null 2>&1; then
actual="$(sha256sum "$1" | cut -d' ' -f1)"
else
actual="$(shasum -a 256 "$1" | cut -d' ' -f1)"
fi
if [ "$actual" != "$2" ]; then
echo "bootstrap: sha256 mismatch for $1" >&2
echo " expected: $2" >&2
echo " actual: $actual" >&2
exit 1
fi
}
# nvm is a bash script; run a command in a bash with nvm loaded
nvm_sh() {
bash -c ". \"\$HOME/.nvm/nvm.sh\" && $*"
}
ensure_nvm() {
[ -s "$HOME/.nvm/nvm.sh" ] && return 0
# nvm prerequisites; nvm itself requires bash
if missing bash; then pkg_install bash bash bash bash; fi
if missing curl; then pkg_install curl curl curl curl; fi
if missing git; then pkg_install git git git git; fi
tmp="$(mktemp -d)"
curl -fsSL -o "$tmp/nvm.tar.gz" \
"https://github.com/nvm-sh/nvm/archive/refs/tags/v${NVM_VERSION}.tar.gz"
verify_sha256 "$tmp/nvm.tar.gz" "$NVM_SHA256"
mkdir -p "$HOME/.nvm"
tar -xzf "$tmp/nvm.tar.gz" -C "$HOME/.nvm" --strip-components=1
rm -rf "$tmp"
}
ensure_node() {
if ! missing node; then return 0; fi
ensure_nvm
nvm_sh "nvm install $NODE_VERSION"
}
ensure_yarn() {
if ! missing yarn; then return 0; fi
if ! missing corepack; then
corepack enable
corepack prepare "yarn@$YARN_VERSION" --activate
elif [ -s "$HOME/.nvm/nvm.sh" ]; then
nvm_sh "nvm use $NODE_VERSION >/dev/null && corepack enable && \
corepack prepare yarn@$YARN_VERSION --activate"
else
npm install -g "yarn@$YARN_VERSION"
fi
}
install_js_deps() {
if missing yarn && [ -s "$HOME/.nvm/nvm.sh" ]; then
nvm_sh "nvm use $NODE_VERSION >/dev/null && cd \"$ROOT\" && \
yarn install --frozen-lockfile"
else
yarn install --frozen-lockfile
fi
}
main() {
cd "$ROOT"
@@ -93,6 +166,10 @@ main() {
go mod download
ensure_node
ensure_yarn
install_js_deps
echo "bootstrap complete"
}
+24 -4
View File
@@ -1,17 +1,37 @@
#!/bin/sh
# script/fmt: format all files (writes) with the goimports that
# script/bootstrap installs. It puts Go's bin directory first on PATH, as
# script/bootstrap does, so it runs that goimports even when the directory
# is not on the caller's PATH.
# script/fmt: format all files (writes): Go with the goimports that
# script/bootstrap installs, then Markdown with prettier. It puts Go's bin
# directory first on PATH, as script/bootstrap does, so it runs that
# goimports even when the directory is not on the caller's PATH.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Must match the pin in script/bootstrap.
NODE_VERSION="22.17.0"
# script/bootstrap installs node and yarn under nvm and leaves neither
# on the PATH of the shell that called it, so resolve the pinned
# toolchain here the way bootstrap's own install step does. nvm is a
# bash script, hence the subshell.
run_yarn() {
if command -v yarn >/dev/null 2>&1; then
exec yarn "$@"
fi
if [ ! -s "$HOME/.nvm/nvm.sh" ]; then
echo "fmt: no yarn; run script/bootstrap first" >&2
exit 1
fi
exec bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
}
main() {
cd "$ROOT"
gobin="$(go env GOBIN)"
PATH="${gobin:-$(go env GOPATH)/bin}:$PATH"
goimports -l -w .
run_yarn run prettier --write '**/*.md' --tab-width 4 --prose-wrap always
}
main "$@"
+22 -1
View File
@@ -1,10 +1,30 @@
#!/bin/sh
# script/fmt-check: check formatting (read-only). Fails and lists the
# offending files if gofmt would reformat anything.
# offending files if gofmt would reformat any Go file; otherwise fails if
# prettier would reformat any Markdown file.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Must match the pin in script/bootstrap.
NODE_VERSION="22.17.0"
# script/bootstrap installs node and yarn under nvm and leaves neither
# on the PATH of the shell that called it, so resolve the pinned
# toolchain here the way bootstrap's own install step does. nvm is a
# bash script, hence the subshell.
run_yarn() {
if command -v yarn >/dev/null 2>&1; then
exec yarn "$@"
fi
if [ ! -s "$HOME/.nvm/nvm.sh" ]; then
echo "fmt-check: no yarn; run script/bootstrap first" >&2
exit 1
fi
exec bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
}
main() {
cd "$ROOT"
unformatted="$(gofmt -l .)"
@@ -13,6 +33,7 @@ main() {
echo "$unformatted"
exit 1
fi
run_yarn run prettier --check '**/*.md' --tab-width 4 --prose-wrap always
}
main "$@"
+8
View File
@@ -0,0 +1,8 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
prettier@3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173"
integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==