Skip to content

chore(operator): replace format_license.py with google/addlicense - #378

Merged
lockwobr merged 3 commits into
mainfrom
chore/377-addlicense
Jul 29, 2026
Merged

lockwobr merged 3 commits into
mainfrom
chore/377-addlicense

Conversation

@lockwobr

Copy link
Copy Markdown
Collaborator

Closes #377.

Two commits, reviewable separately.

6c949579 fix: make zz_generated.deepcopy.go gofmt-clean. One file, the pre-existing bug. The committed version had the header above //go:build !ignore_autogenerated; gofmt canonicalizes a build constraint to the top, so gofmt -l flagged it and every make fmt rewrote it. No behavior change: go list honors the constraint in both orderings.

3e286736 chore: replace format_license.py with google/addlicense. The migration.

Why the drift happened

Not a //go:build parsing problem in either tool. A build constraint may be preceded only by blank lines and line comments. Our header is a /* */ block comment, which is not legal there, so gofmt hoists the constraint above it and format_license.py re-inserts the header at position 0 on the next run. addlicense emits // for Go, which is legal above a constraint, and skips generated files outright via Code generated ... DO NOT EDIT..

Correction to the issue

#377 claims this is a zero-diff migration on the basis of a scan showing "570 files, 0 modified". That scan was wrong. It used xargs -a, which BSD xargs on macOS does not support, and the failure was swallowed by 2>/dev/null, so the empty output read as "nothing missing".

The real number is 4: four chainsaw fixtures under k8s-tests/chainsaw/nodewright/config-nodewright/ have no header today and gain one here. Verified they still parse to the same 3 documents each, before and after, with yq ea '[.] | length'; the header is a leading comment and semantically inert.

Every other tracked source file is untouched, so the substance of the claim holds: this is not a repo-wide rewrite. But 4 is not 0, and the issue text should not be trusted on that point.

What changed

  • deps.mk: pin ADDLICENSE_VERSION and add the install target next to go-licenses
  • Makefile: license-fmt runs addlicense against scripts/license-header.tmpl
  • Makefile: new license-header-check, wired into make lint so a missing header fails CI on every PR. There was no source-header gate before; license-check is go-licenses, covering dependency licenses, not headers.
  • scripts/license-header.tmpl: the existing wording, unchanged
  • scripts/format_license.py: deleted, 636 lines
  • .claude/CLAUDE.md: the tooling note, which described the script as adding and replacing headers

Paths come from git ls-files rather than a tree walk. That is deliberate and worth a look in review: addlicense takes explicit paths and will happily rewrite anything it is pointed at, and a naive walk picks up untracked local git worktrees under .claude/ and any stray venv. vendor/ and chart/ stay excluded as before.

Behavior changes worth knowing

  • Headers are added, never rewritten. The old script compared normalized text without stripping the year, so editing a file silently bumped its copyright year. That is why the tree currently reads 213 files at 2025 and 346 at 2026. Year of first publication with no bump matches Google, Kubernetes, and the Linux kernel.
  • A malformed header in an existing file is no longer auto-repaired. license-header-check does not catch this either, since it only detects absence. This is a real capability loss, not just a simplification.
  • Header style splits. New Go files get //; the existing ~570 keep /* */. Converting them is deliberately out of scope to avoid a PR touching every file, and it never converges on its own since addlicense will not rewrite an existing header. Happy to file a follow-up if you want consistency.

Verification

  • make fmt from a cold bin/ leaves the tree clean. The drift is gone; that was the point.
  • make license-fmt is idempotent: a second run produces no further changes.
  • make license-header-check exits 2 with headers missing, 0 once present, so it works as a gate.
  • go build ./... passes.
  • The 4 modified fixtures parse to the same document count as before.
  • make unit-tests not run: no Go source changed except the generated file's build-tag position, which is semantically identical.

The committed file had the Apache header above `//go:build
!ignore_autogenerated`. gofmt canonicalizes a build constraint to the
top of the file, so `gofmt -l` flagged it and every `make fmt` rewrote
it. The nodewright copy of the same generated file was already correct;
only the legacy one had drifted.

Cause: a build constraint may be preceded only by blank lines and line
comments, and our header is a `/* */` block comment. gofmt therefore
hoists the constraint above it, while scripts/format_license.py
re-inserts the header at position 0 on the next run. `make fmt` runs
license-fmt then `go fmt` and ends correct; `make install` runs
license-fmt alone, which is how the un-gofmt'd version reached a commit.

No behavior change: `go list` honors the constraint in both orderings.

Refs #377

Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
@lockwobr
lockwobr requested a review from a team July 28, 2026 22:56
@github-actions github-actions Bot added doc Documentation change (PR path label; doc issues use the Documentation type) component/operator Skyhook operator (controller-manager) component/ci CI workflows, GitHub Actions, and repo tooling component/tests End-to-end / chainsaw test suites (k8s-tests) labels Jul 28, 2026
@coderabbitai

coderabbitai Bot commented Jul 28, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request replaces the Python license formatter with addlicense and a shared Apache-2.0 template. The operator Makefile adds formatting and header-check targets and runs the check during linting. A pinned addlicense installation target is added. License-header guidance and repository headers are updated, and a new ConfigMap update assertion fixture is introduced.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: rice-riley

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: replacing format_license.py with google/addlicense in the operator.
Description check ✅ Passed The description is detailed and directly describes the same migration and related fixes in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/377-addlicense

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@operator/deps.mk`:
- Around line 155-159: Update the install-deps aggregate target to depend on the
existing addlicense target, ensuring make install-deps installs the tool used by
license-fmt and license-header-check. Keep the addlicense target and its
installation behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: d747d91b-02ca-459d-b296-15d6c1314593

📥 Commits

Reviewing files that changed from the base of the PR and between bcef42d and 3e28673.

⛔ Files ignored due to path filters (1)
  • operator/api/v1alpha1/zz_generated.deepcopy.go is excluded by !**/zz_generated*.go
📒 Files selected for processing (9)
  • .claude/CLAUDE.md
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-deploy.yaml
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-update-no-interrupt.yaml
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-update-while-running.yaml
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-update.yaml
  • operator/Makefile
  • operator/deps.mk
  • scripts/format_license.py
  • scripts/license-header.tmpl
💤 Files with no reviewable changes (1)
  • scripts/format_license.py

Comment thread operator/deps.mk
@coveralls

coveralls commented Jul 28, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 30411362928

Coverage remained the same at 78.836%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 205 coverage regressions across 1 file.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

205 previously-covered lines in 1 file lost coverage.

File Lines Losing Coverage Coverage
operator/api/v1alpha1/zz_generated.deepcopy.go 205 27.99%

Coverage Stats

Coverage Status
Relevant Lines: 12701
Covered Lines: 10013
Line Coverage: 78.84%
Coverage Strength: 7.97 hits per line

💛 - Coveralls

@lockwobr
lockwobr force-pushed the chore/377-addlicense branch from 3e28673 to cdeea26 Compare July 29, 2026 00:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/CLAUDE.md:
- Line 295: Update the “License headers” guidance to accurately describe
generated-file handling: addlicense skips files with recognized generated-code
markers, while zz_generated.* files are not necessarily skipped solely by
filename. If the repository intends to exclude all zz_generated.* files,
document the explicit filename exclusion mechanism instead.

In `@operator/Makefile`:
- Around line 156-157: Update the license_files definition to account for
tracked JSON schemas, then ensure the license-fmt and license-header-check
targets apply a JSON-safe validation or explicitly exclude JSON through the
established policy mechanism. Preserve the existing exclusions for vendor and
chart files.
- Around line 164-165: Update the license-header-check target to validate
tracked source headers exactly against scripts/license-header.tmpl, including
the Apache-2.0/SPDX text, so MIT or malformed headers fail; keep license-fmt
add-only and unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: a5f9c7a2-8d28-4a30-a39d-75ae72eb052c

📥 Commits

Reviewing files that changed from the base of the PR and between 3e28673 and cdeea26.

📒 Files selected for processing (9)
  • .claude/CLAUDE.md
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-deploy.yaml
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-update-no-interrupt.yaml
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-update-while-running.yaml
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-update.yaml
  • operator/Makefile
  • operator/deps.mk
  • scripts/format_license.py
  • scripts/license-header.tmpl
💤 Files with no reviewable changes (1)
  • scripts/format_license.py

Comment thread .claude/CLAUDE.md Outdated
Comment thread operator/Makefile
Comment thread operator/Makefile Outdated
scripts/format_license.py inserted the Apache header at position 0 of
every file. For Go that meant a `/* */` block comment above any
`//go:build` constraint, which is not legal, so gofmt hoisted the
constraint back and the two tools fought on every `make fmt`.
addlicense emits `//` line comments for Go, which are legal above a
constraint, and skips generated files outright.

Changes:
- deps.mk: pin ADDLICENSE_VERSION and add the install target next to
  go-licenses
- Makefile: license-fmt now runs addlicense with the new template
- Makefile: add license-header-check and wire it into `make lint` so a
  missing header fails CI on every PR. There was no source-header gate
  before; `license-check` is go-licenses, which covers dependency
  licenses, not headers.
- scripts/license-header.tmpl: the existing wording, unchanged
- delete scripts/format_license.py (636 lines)

Four chainsaw fixtures had no header and now have one. Every other
tracked source file is untouched: addlicense skips anything whose first
1000 bytes already contain a copyright or SPDX identifier, so this is
not a repo-wide rewrite.

Behavior changes worth knowing:

- Headers are only added, never rewritten. The old script compared
  normalized text without stripping the year, so editing a file
  silently bumped its copyright year; the tree currently reads 213
  files at 2025 and 346 at 2026 as a result. Year of first publication
  and no bump matches Google, Kubernetes, and the Linux kernel.
- A malformed header in an existing file is no longer auto-repaired.
  license-header-check does not catch that either, since it only
  detects absence.
- New Go files get `//` headers while existing files keep `/* */`.
  Converting the existing ~570 is deliberately out of scope to avoid a
  PR touching every file; it can be a separate mechanical commit.
- Paths come from `git ls-files` rather than a tree walk, so an
  untracked local worktree or venv is never rewritten. vendor/ and
  chart/ stay excluded as before.

Closes #377

Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
@lockwobr
lockwobr force-pushed the chore/377-addlicense branch from cdeea26 to 325e373 Compare July 29, 2026 00:30

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
operator/Makefile (1)

156-157: 🔒 Security & Privacy | 🟠 Major

Include tracked JSON files in the enforced license set.

license_files omits *.json, so license-fmt and license-header-check never process tracked JSON files. That conflicts with the repository rule requiring Apache-2.0 SPDX headers for tracked JSON source files. Add a JSON-safe validation path or document an explicit exception.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@operator/Makefile` around lines 156 - 157, Update the Makefile’s
license_files definition to include tracked *.json files so license-fmt and
license-header-check enforce SPDX headers on them, using a JSON-safe validation
approach or explicitly documenting any intentional exception.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@operator/Makefile`:
- Around line 156-157: Update the Makefile’s license_files definition to include
tracked *.json files so license-fmt and license-header-check enforce SPDX
headers on them, using a JSON-safe validation approach or explicitly documenting
any intentional exception.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 9f3ec1fa-1c53-4b6a-84b7-36091cb64306

📥 Commits

Reviewing files that changed from the base of the PR and between cdeea26 and 325e373.

📒 Files selected for processing (10)
  • .claude/CLAUDE.md
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-deploy.yaml
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-update-no-interrupt.yaml
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-update-while-running.yaml
  • k8s-tests/chainsaw/nodewright/config-nodewright/assert-cm-update.yaml
  • operator/Makefile
  • operator/deps.mk
  • operator/versions.sh
  • scripts/format_license.py
  • scripts/license-header.tmpl
💤 Files with no reviewable changes (1)
  • scripts/format_license.py

@lockwobr
lockwobr enabled auto-merge (squash) July 29, 2026 17:04
@lockwobr

Copy link
Copy Markdown
Collaborator Author

@coderabbitai resolve

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Comments resolved. Approval is disabled; enable reviews.request_changes_workflow to allow explicit top-level @coderabbitai resolve or @coderabbitai approve commands.

@lockwobr
lockwobr merged commit 673a456 into main Jul 29, 2026
13 checks passed
@lockwobr
lockwobr deleted the chore/377-addlicense branch July 29, 2026 17:05
lockwobr added a commit that referenced this pull request Jul 29, 2026
* fix(operator): make zz_generated.deepcopy.go gofmt-clean

The committed file had the Apache header above `//go:build
!ignore_autogenerated`. gofmt canonicalizes a build constraint to the
top of the file, so `gofmt -l` flagged it and every `make fmt` rewrote
it. The nodewright copy of the same generated file was already correct;
only the legacy one had drifted.

Cause: a build constraint may be preceded only by blank lines and line
comments, and our header is a `/* */` block comment. gofmt therefore
hoists the constraint above it, while scripts/format_license.py
re-inserts the header at position 0 on the next run. `make fmt` runs
license-fmt then `go fmt` and ends correct; `make install` runs
license-fmt alone, which is how the un-gofmt'd version reached a commit.

No behavior change: `go list` honors the constraint in both orderings.

Refs #377

Signed-off-by: Brian Lockwood <lockwobr@gmail.com>

* chore(operator): replace format_license.py with google/addlicense

scripts/format_license.py inserted the Apache header at position 0 of
every file. For Go that meant a `/* */` block comment above any
`//go:build` constraint, which is not legal, so gofmt hoisted the
constraint back and the two tools fought on every `make fmt`.
addlicense emits `//` line comments for Go, which are legal above a
constraint, and skips generated files outright.

Changes:
- deps.mk: pin ADDLICENSE_VERSION and add the install target next to
  go-licenses
- Makefile: license-fmt now runs addlicense with the new template
- Makefile: add license-header-check and wire it into `make lint` so a
  missing header fails CI on every PR. There was no source-header gate
  before; `license-check` is go-licenses, which covers dependency
  licenses, not headers.
- scripts/license-header.tmpl: the existing wording, unchanged
- delete scripts/format_license.py (636 lines)

Four chainsaw fixtures had no header and now have one. Every other
tracked source file is untouched: addlicense skips anything whose first
1000 bytes already contain a copyright or SPDX identifier, so this is
not a repo-wide rewrite.

Behavior changes worth knowing:

- Headers are only added, never rewritten. The old script compared
  normalized text without stripping the year, so editing a file
  silently bumped its copyright year; the tree currently reads 213
  files at 2025 and 346 at 2026 as a result. Year of first publication
  and no bump matches Google, Kubernetes, and the Linux kernel.
- A malformed header in an existing file is no longer auto-repaired.
  license-header-check does not catch that either, since it only
  detects absence.
- New Go files get `//` headers while existing files keep `/* */`.
  Converting the existing ~570 is deliberately out of scope to avoid a
  PR touching every file; it can be a separate mechanical commit.
- Paths come from `git ls-files` rather than a tree walk, so an
  untracked local worktree or venv is never rewritten. vendor/ and
  chart/ stay excluded as before.

Closes #377

Signed-off-by: Brian Lockwood <lockwobr@gmail.com>

---------

Signed-off-by: Brian Lockwood <lockwobr@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/ci CI workflows, GitHub Actions, and repo tooling component/operator Skyhook operator (controller-manager) component/tests End-to-end / chainsaw test suites (k8s-tests) doc Documentation change (PR path label; doc issues use the Documentation type)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace scripts/format_license.py with google/addlicense

3 participants