How putnami observe reads a Go repository
The Go detector finds the modules of a repository from its go.work and
go.mod files. It reads files only: it runs no go command and reaches no
module cache or network. The change analyses use what it finds to name the
changed and dependent modules, attribute tests and coverage, and diff the
dependency versions.
Components
Each Go module is a component:
- A directory that the root
go.worklists inuseis a component of sourceworkspace. - Any other
go.modis a component of sourcemanifest. The search skipsnode_modules,vendor,testdataand the directories whose name starts with.or_, such as.git.
A component's id is its directory relative to the repository root, . for
the root. Its name is the module path and its ecosystem is go. A file
belongs to the innermost module whose directory holds it, so a file outside
every module belongs to no component.
Dependency edges
A module depends on another module of the repository when:
- its
go.modrequires that module's path, after thereplacedirectives of itsgo.modand of the rootgo.work; or - a local
replacedirective of itsgo.mod, such asreplace example.com/pricing => ../pricing, points to that module's directory.
A require of a module outside the repository is no edge. An import that only
go.work resolves, without a require in the go.mod, is no edge either: the
detector reads manifests, not Go sources.
Reports
Go reports name packages and files by import path:
| Report | Field |
|---|---|
go test -json |
Package of each event |
gotestsum --junitfile, go-junit-report |
the name of each testsuite |
go test -coverprofile |
the file name at the start of each line |
The detector maps an import path to the repository path below the module
whose path is its longest prefix, and to that module. For example, in
Putnami/demo-observe-go,
github.com/Putnami/demo-observe-go/pricing/format.go maps to
pricing/format.go in the pricing module. The root module's own path
maps to the root module and no file.
Dependency versions
Each module's go.mod is its lockfile. Its locked dependencies are its
requires, each with the version that the replace directives of the
go.mod and of the root go.work at the same commit select, and whether it
is // indirect. go.work belongs to no module, so a change made only in its
replace directives shows in no lockfile. A require replaced by a local
directory, or of a module of the repository, is not a locked dependency.
go.sum holds checksums, not a selection, so the detector does not read it. Versions compare in Go's
semantic version order, pseudo-versions included.
Diagnostics
| Code | Meaning |
|---|---|
observe.go_manifest_unreadable |
A go.work, a go.mod or a directory cannot be read. A module without a readable go.mod is not a component. |
observe.go_manifest_invalid |
A go.work or go.mod cannot be parsed, or go.work uses a directory outside the repository. A go.mod that only the lenient grammar reads keeps its module and requires, without its replace directives; one that no grammar reads is a component without a name or edges. |
observe.go_duplicate_module |
Two go.mod files declare one module path. Both are components. The path names the go.work member, else the first directory in path order: a require of it is an edge to that directory, and a report's import path maps there. |
observe.go_walk_bounded |
The search for go.mod stopped after 200,000 entries. |