Skip to content

chore(ci): add deadcode tooling to catch exported orphan funcs - #2127

Open
schnie wants to merge 4 commits into
mainfrom
deadcode-tooling
Open

chore(ci): add deadcode tooling to catch exported orphan funcs#2127
schnie wants to merge 4 commits into
mainfrom
deadcode-tooling

Conversation

@schnie

@schnie schnie commented May 6, 2026

Copy link
Copy Markdown
Member

Summary

Adds a whole-program dead-function check using golang.org/x/tools/cmd/deadcode so that exported orphan funcs are caught in CI, matching the pattern astro-desktop uses. Motivation: the recent #2126 incident showed that exported package-level identifiers can sit dead for years without golangci-lint's in-package unused analyzer noticing — airflow.Af2Gitignore was effectively a no-op because nothing imported it.

deadcode is whole-program: it loads the cli starting from cmd/astro/main and reports any function that is unreachable. With -test, it also includes test executables, which avoids false positives on helpers that are only reached from tests.

How it's wired

  • scripts/check-deadcode.sh — runs deadcode -test ./... and fails on any finding inside the cli's binary-style directories. The wrapper exists because deadcode itself exits 0 even when it finds dead code, so we need to gate on its output.
  • prek.toml — a new deadcode hook, in line with the existing gofumpt and golangci-lint hooks. Pins golang.org/x/tools/cmd/deadcode@v0.27.0 via additional_dependencies.
  • Makefilemake lint now runs make lint-go (golangci-lint) followed by make lint-deadcode. Each target wraps the corresponding prek hook so contributors can run them individually.
  • .circleci/config.yml — the lint job now runs golangci-lint and deadcode as two separate steps for clearer CI logs.
  • CONTRIBUTING.md — documents the layers and the scope decision.

Scope decision

deadcode enforcement is restricted to the cli's binary-style directories: cmd/, airflow/, cloud/, software/, config/, settings/, context/, houston/, internal/, version/, airflow_versions/, airflow-client/, astro-client-core/, astro-client-iam-core/, docker/.

Excluded:

  • Library go.mod sub-modules: pkg/airflowrt, pkg/proxy, pkg/astroauth, pkg/telemetry, astro-client-platform-core — these are independently versioned and consumed by external Go modules (astro-desktop pulls all five), so reachability from cmd/astro/main is not a correctness signal.
  • Other pkg/* subdirectories (pkg/ansi, pkg/fileutil, pkg/util, etc.) — although they live in the cli main module, they are import-style helpers that may have external Go consumers, and treating them as binary code would risk false-positive deletions of public-API surface.

The exclusion list lives in scripts/check-deadcode.sh so it's easy to adjust.

Baseline

The first commit fixes the four pre-existing dead test functions the tool surfaced — none of these were intentional, all were latent bugs:

  • astro-client-core/client.test.go and astro-client-iam-core/client.test.go were named .test.go instead of _test.go, so Go silently never picked up TestNewCoreClient / TestNewIamCoreClient. Renaming surfaces the tests; both pass.
  • cmd/api/describe_test.go had an unused strPtr helper (deleted).
  • houston/suite_test.go defined SetupSuite as a free function rather than a method on *Suite, so testify's SetupAllSuite interface never invoked it. Converted to a method.

After the cleanup, the deadcode baseline is zero — no allowlist needed.

Test plan

  • make lint-deadcode passes locally on this branch
  • make lint-deadcode fails when a deliberately-unreachable function is added to a scoped directory (verified locally)
  • go test ./astro-client-core/... ./astro-client-iam-core/... ./cmd/api/... ./houston/... passes after the test renames + suite fix
  • CircleCI lint job runs make lint-go and make lint-deadcode as separate steps and both pass

🤖 Generated with Claude Code

schnie and others added 2 commits May 6, 2026 17:26
Pre-cleanup so the deadcode tool (added in the next commit) starts
with a zero baseline.

- `astro-client-core/client.test.go` and
  `astro-client-iam-core/client.test.go` were named `.test.go`
  instead of `_test.go`, so Go's test infrastructure silently never
  picked them up. Their `TestNewCoreClient` / `TestNewIamCoreClient`
  functions have been dead since they were introduced (PRs #873 and
  #1444). Renaming surfaces the tests; both pass.
- `cmd/api/describe_test.go` had an unused `strPtr` helper.
- `houston/suite_test.go` defined `SetupSuite` as a free function;
  testify's suite framework only invokes it via the `SetupAllSuite`
  interface, i.e. as a method on `*Suite`. Convert to a method so the
  test config actually initializes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a whole-program dead-function check using
`golang.org/x/tools/cmd/deadcode` to catch exported orphan funcs that
`golangci-lint`'s in-package `unused` analyzer cannot. This is the
analogue of astro-desktop's setup.

How it's wired:
- `scripts/check-deadcode.sh` runs `deadcode -test` and fails on any
  finding within the cli's binary-style directories (`cmd/`, `airflow/`,
  `cloud/`, `software/`, `config/`, `settings/`, `houston/`, etc.).
  Library sub-modules (`pkg/airflowrt`, `pkg/proxy`, `pkg/astroauth`,
  `pkg/telemetry`, `astro-client-platform-core`) and the rest of `pkg/`
  are excluded because they are consumed by external Go modules
  (e.g. astro-desktop) — reachability from `cmd/astro/main` is not a
  correctness signal for them.
- `prek.toml` gains a `deadcode` hook that pins
  `golang.org/x/tools/cmd/deadcode@v0.27.0` and runs the script.
- `make lint` now runs `make lint-go` (golangci-lint) +
  `make lint-deadcode` (the new hook). CircleCI's `lint` job runs both
  as separate steps for clearer logs.
- `CONTRIBUTING.md` documents the layers and scope.

Baseline is zero — the previous commit cleaned up the four pre-existing
dead test funcs the tool surfaced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@schnie
schnie requested review from a team as code owners May 6, 2026 21:30
@coveralls-official

coveralls-official Bot commented May 6, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 28722479648

Coverage increased (+0.06%) to 45.385%

Details

  • Coverage increased (+0.06%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 55531
Covered Lines: 25203
Line Coverage: 45.39%
Coverage Strength: 8.12 hits per line

💛 - Coveralls

jlaneve and others added 2 commits July 4, 2026 18:56
- port CI step split from deleted .circleci/config.yml to .github/workflows/ci.yaml
- update deadcode SCOPE for astro-client-v1/astro-client-v1alpha1 renames (#2093)
- cover the root main package in SCOPE (main.go lives at repo root, not cmd/astro/main)
- rename astro-client-v1alpha1/client.test.go to client_test.go so go test picks it up
- drop test files for deleted astro-client-core/astro-client-iam-core packages
- bump deadcode pin to x/tools v0.44.0 to match the go 1.26 toolchain
- remove unused DEADCODE env override and fix stale comments/docs

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BiAZQFjVHLJtoRV1PskPdm
Invert the 15-directory allowlist to a single pkg/ exclusion so new
top-level directories get deadcode coverage by default (fail loud
instead of silently unscanned). Behavior-identical on the current tree.
Trim the CONTRIBUTING section to point at the script instead of
duplicating its rationale, and fix the -test mode description.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BiAZQFjVHLJtoRV1PskPdm

@jlaneve jlaneve 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.

reviewed and brought the branch up to date with main. the tooling itself was solid but it was written against the May repo layout, so I adapted it while resolving conflicts:

  • ported the CI step split to github actions (.circleci/config.yml is gone from main)
  • updated the deadcode scope for the astro-client-v1/v1alpha1 renames from #2093, then simplified the allowlist to a single pkg/ exclusion so new top-level dirs get coverage by default instead of being silently skipped
  • the scope regex also missed the root main package (main.go lives at the repo root, not cmd/astro/main), fixed
  • applied the client.test.go -> client_test.go fix to astro-client-v1alpha1, which had the same never-runs bug on main
  • bumped the deadcode pin to x/tools v0.44.0 to match the go 1.26 toolchain

verified locally: check passes clean, fails on a planted dead func (including in the root package), pkg/ findings excluded, and the affected go tests pass

@jlaneve
jlaneve enabled auto-merge (squash) July 4, 2026 23:02
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.

3 participants