Skip to content

Make the dependency license list deterministic - #896

Merged
janhoy merged 1 commit into
apache:mainfrom
janhoy:fix-deterministic-license-urls
Nov 10, 2026
Merged

janhoy merged 1 commit into
apache:mainfrom
janhoy:fix-deterministic-license-urls

Conversation

@janhoy

@janhoy janhoy commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

🤖 AI assisted 🤖

Problem

make lint and the smoke test fail at random on check-licenses, on whichever PR runs while a shared runner is throttled. #889 passed at 00:30 and failed at 08:07 with no dependency change in between, and an unrelated branch failed in the same window on the same two rows.

< gomodules.xyz/jsonpatch/v2,https://github.com/gomodules/jsonpatch/blob/v2.5.0/v2/LICENSE,Apache-2.0
---
> gomodules.xyz/jsonpatch/v2,https://github.com/gomodules/jsonpatch/blob/v2.5.0/LICENSE,Apache-2.0

go-licenses report resolves the second column, the link to each license file, over the network: for a major version above v1 it probes the remote to tell the "major branch" and "major subdirectory" conventions apart. Running the current command four ways gives three different answers for that row, .../v2/LICENSE when the probe succeeds, .../LICENSE or Unknown when it fails. Only two dependencies can move, the two whose license file sits at the repository root rather than beside the go.mod.

The answer a successful probe gives is the wrong one, so the link in the file today is dead:

404  https://github.com/gomodules/jsonpatch/blob/v2.5.0/v2/LICENSE
200  https://github.com/gomodules/jsonpatch/blob/v2.5.0/LICENSE

And not only those two. Checking all 78 URLs in the file, 13 are dead: the other 11 are go-openapi/swag submodules, wrong the same way but consistently rather than intermittently, so they have never broken CI. The column has been 17% dead links.

Upstream #73 has the analysis and #131 the resulting 404s, both open: the Go module cache copies a repository-root LICENSE into a nested module that lacks one, so the tool finds it at the module root and guesses a path that was never there. Not new to us either, the bad URL has been in the file since the v1.5.0 bump in November 2022.

Solution

Render the report through --template, a documented go-licenses flag, and put the module version in that column instead of the link. The version comes from the module graph, so it needs no network and cannot drift.

gomodules.xyz/jsonpatch/v2,v2.5.0,Apache-2.0

The report now hashes identically across repeated runs, with the network blocked, and against the committed file. No license data changes: same 78 rows, and diff <(cut -d, -f1,3 old) <(cut -d, -f1,3 new) is empty. The GOOS=linux GOARCH=amd64 prefix is untouched, so the list still reflects the Linux build graph.

Rejected alternatives

  • Bump go-licenses. v2.0.1 is the latest release and the upstream fix is unmerged.
  • Just regenerate the CSV. The correct URL only appears when the probe fails, so the next regeneration on a healthy network restores the 404 form.
  • Compare only columns 1 and 3. Green CI, but leaves all 13 dead URLs in a compliance artifact and make prepare still rewrites them.
  • Build a pkg.go.dev link. Tried it; the template exposes the package path, not the module path, so three k8s.io/kube-openapi entries came out as 404s.
  • Drop the column. Also deterministic, but the version is worth keeping: it is information the file never recorded, and for license review it beats a link that may not resolve.

go-licenses' default report resolves the second column, a link to each
license file, over the network. The lookup decides whether a module's
major version directory is part of its repository, so the same inputs
produce different output run to run: the `vN/LICENSE` form when it
succeeds, the repository root form when it fails, and `Unknown` when the
module is not on a known host. CI compares the committed CSV byte for
byte, so `make lint` and the smoke test fail at random, on whichever PR
happens to run when a shared runner is throttled.

The `vN/LICENSE` form that a successful lookup produces does not exist:
both URLs the file currently carries return 404. That is
google/go-licenses#131, open and unfixed in v2.0.1, the latest release.

Report through a template instead, and put the module version in that
column. go-licenses takes the version from the module graph, so it needs
no network and cannot drift. The module and license columns are
byte-identical to before, and the version is more useful for license
review than a link that may not resolve.

Verified the report is identical across repeated runs and with the
network blocked.
@janhoy
janhoy requested a review from HoustonPutman November 9, 2026 08:46
@janhoy

janhoy commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

@HoustonPutman This has been an issue for a few years but we have not encountered it before due to not updating the file very often. And it's a flaky condition that most often would be fixed with a CI check re-run. But that's annoying so if we sacrifice the URLs as proposed, at least we'd get a stable check, but you'd need to find the license URL yourself based on module and version.

@janhoy
janhoy merged commit 404ba56 into apache:main Nov 10, 2026
3 checks passed
@janhoy
janhoy deleted the fix-deterministic-license-urls branch November 10, 2026 00:11
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.

2 participants