Repository navigation
Conversation
… 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>
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.
Closes #10.
generate: a generated client imported the wrongentclient.tmplnever imported the root package. It leftent.Hookandent.Interceptortoimports.Process, which looks for any package namedentthat exports both names. Every generated client exports both as aliases, andentc/integrationhas 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 onmigrate/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:
client.gosid,uuidcompatible,schema(customid)runtime.go,mutation.godialect/sql, for asql.NullStringfieldnode.tmpl's node packages,debug.tmpl'sdialectChanges
client.tmplimports the root package and each node's types.import/typeshandles 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.tmplanddebug.tmplimport 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_importskeeps it that way:entsql.ReferenceOption), asql.NullStringfield, and a standard-libraryuuidId.replace, so goimports can find whatever a template leaves out.lint: gopls could not see go-sqlmocklint.shhandedgopls checkthe files of all four modules in one invocation, so gopls chose whichgo.modto load each file under.It now groups files by their nearest
go.modand 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 inentc/integrationis 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 vetandgo testpass in all three modules, plusdialect/sql/schema/integration.entc/integrationpasses on SQLite.lint.shpasses on every tracked file, plus the new test file.🤖 Generated with Claude Code