Skip to content

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

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

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

Conversation

@schnie
Copy link
Copy Markdown
Member

@schnie schnie commented May 6, 2026

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
Copy link
Copy Markdown

Coverage Report for CI Build 0

Coverage increased (+0.1%) to 39.845%

Details

  • Coverage increased (+0.1%) 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: 65891
Covered Lines: 26254
Line Coverage: 39.84%
Coverage Strength: 9.4 hits per line

💛 - Coveralls

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