Skip to content

entc/gen, ci: generated files import what they use, and lint runs per module - #14

Merged
lesomnus merged 1 commit into
masterfrom
ci-flakes
Oct 18, 2026
Merged

lesomnus merged 1 commit into
masterfrom
ci-flakes

Conversation

@lesomnus

Copy link
Copy Markdown
Member

Closes #10.

generate: a generated client imported the wrong ent

client.tmpl never imported the root package. It left ent.Hook and ent.Interceptor to imports.Process, which looks for any package named ent that exports both names. Every generated client exports both as aliases, and entc/integration has seventeen such packages, so the lookup had seventeen answers.

goimports also prefers what the neighbouring files import. format() wrote each file back while other files were still being formatted, so what goimports read from the neighbours depended on timing. That is the likeliest reason the failure was rare. It actually happened in this repository: #12's first CI run failed on migrate/entv1/client.go.

The client was not the only case. I regenerated all three modules while logging every import goimports added from outside the standard library. Before this change it added:

File Added by goimports
client.go the root package (40 files); Id types sid, uuidcompatible, schema (customid)
graph runtime.go, mutation.go dialect/sql, for a sql.NullString field
user templates in this repo node.tmpl's node packages, debug.tmpl's dialect

Changes

  • client.tmpl imports the root package and each node's types.
  • import/types handles a type whose package name matches a predefined import (e.g. sql). Before, it skipped such a type and assumed the file's own import block covered it. Now it imports the predefined package, which is what per-type files already resolve that name to. Graph-level files have their own import blocks, so they need this.
  • node.tmpl and debug.tmpl import what they use.
  • format() formats every file before writing any back. A lookup therefore reads the neighbouring files as they were generated, never half written.

With these changes, the same log over all three modules is empty. goimports still adds standard-library imports, but it resolves those from a fixed table, so they are deterministic.

The regenerated code differs only in where imports sit within their import blocks.

TestGraph_Gen_imports keeps it that way:

  • It generates a graph with an Id type from outside the standard library (entsql.ReferenceOption), a sql.NullString field, and a standard-library uuid Id.
  • The graph goes into a temporary module that points at this repository through replace, so goimports can find whatever a template leaves out.
  • It fails if goimports added any non-standard-library import.
  • Run against the templates from before this change, it reports exactly the four built-in lookups above.

lint: gopls could not see go-sqlmock

lint.sh handed gopls check the files of all four modules in one invocation, so gopls chose which go.mod to load each file under.

It now groups files by their nearest go.mod and runs once per module, from that module's directory, with only that module's files. The interface (a list of files) and the repository-relative output are unchanged. A finding planted in entc/integration is still reported.

I never reproduced the original failure. So this change removes gopls's choice, but it does not prove that choice was the cause.

Tests

  • go vet and go test pass in all three modules, plus dialect/sql/schema/integration.
  • entc/integration passes on SQLite.
  • The generate gate is clean.
  • lint.sh passes on every tracked file, plus the new test file.

🤖 Generated with Claude Code

… module

Two CI jobs failed now and then on files the change did not touch
(#10).

`generate` rewrote a committed client's import of the root package into
an import of some generated client package. client.tmpl never imported
the root package, and left `ent.Hook` and `ent.Interceptor` to
imports.Process, which looks for a package named `ent` that exports
both. Every generated client does, as aliases, and entc/integration has
seventeen of them, so the lookup had seventeen answers. goimports looks
at what the files beside one import first, and format() wrote each file
back while others were still being formatted, so what it saw there
depended on timing. That is the likeliest reason it was rare.

The client was not the only file leaving names to that lookup.
Regenerating every module and logging what goimports added found it
adding packages outside the standard library to client.go (the root
package; Id types such as sid, uuidcompatible and schema in customid),
to the graph's runtime.go and mutation.go (dialect/sql, for a field of
type sql.NullString), and through the repository's own user templates
(node.tmpl's node packages, debug.tmpl's dialect).

So:

- client.tmpl imports the root package and each node's types.
- import/types imports a type whose package name is a predefined
  import's (sql) as that predefined package, as the per-type files
  already resolve it, instead of skipping it on the assumption that the
  file's import block has it. A graph file's does not.
- node.tmpl and debug.tmpl import what they use.
- format() formats every file before writing any back, so a lookup
  reads the files beside it as they were generated, never half written.

With these, the same log over all three modules is empty. What goimports
still adds is from the standard library, which it resolves from a fixed
table.

TestGraph_Gen_imports keeps it that way. It generates a graph with an
Id type outside the standard library and a sql.NullString field into a
module of its own, where goimports can find what a template leaves out,
and fails on any non-standard import goimports added. On the templates
before this change it names the four lookups above.

`lint` reported that two root-module tests could not import go-sqlmock,
which the root module requires. lint.sh handed `gopls check` the files
of all four modules in one invocation, and which go.mod a file was
loaded under was gopls's choice. It now runs once per module, from that
module's directory, with only that module's files. Whether that was the
cause is not proven here; the failure was never reproduced, and this
takes the choice away either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@lesomnus
lesomnus merged commit eeca0ee into master Oct 18, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ci: generate and lint fail now and then, on files the change did not touch

1 participant