Skip to content

ci: reuse SCA analysis caches - #444

Merged
XuPeng-SH merged 3 commits into
matrixorigin:mainfrom
XuPeng-SH:codex/cache-sca-analysis
Sep 4, 2026
Merged

XuPeng-SH merged 3 commits into
matrixorigin:mainfrom
XuPeng-SH:codex/cache-sca-analysis

Conversation

@XuPeng-SH

@XuPeng-SH XuPeng-SH commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

What

Optimize MatrixOne SCA on the unchanged 4-core/8-GiB runner without removing an analyzer or covered package:

  • restore a trusted main golangci-lint analysis cache on a best-effort basis;
  • keep molint and golangci-lint targeted at ./...;
  • check license headers only for PR-added, modified, copied, or renamed source files;
  • run dependency-license resolution only when go.mod, go.sum, license configuration, or the tool recipe changes;
  • eliminate the duplicate full go list -test ./... previously run by both Prepare ENV and static-check;
  • serialize golangci-lint only after MatrixOne provides the split analysis target.

The complete local/release make static-check entry point remains unchanged. Until matrixorigin/matrixone#28101 adds the split target, this workflow invokes the original make static-check command without a concurrency override.

Cache design

The outer v3 key is bounded to the analysis environment, weekly generation, go.mod, and .golangci.yml. Same-generation and previous-generation prefix restores are allowed; golangci-lint still validates cached facts against its own binary/config/module/package/dependency inputs. Unrelated Makefile, native, and go.sum edits no longer create new outer cache lineages.

PR jobs restore but never publish. Only the trusted main producer in matrixorigin/matrixone#28101 can save an analysis cache. A usable exact hit skips producer work; a miss or usable prefix hit can seed a new exact entry. Consumers discard failed, partial, or empty restores and perform a complete cold analysis. Because GitHub cache keys are immutable, the producer rejects an exact-but-unusable entry instead of spending a full cold run trying to overwrite it.

The shared restore action records exact/prefix/miss/unusable state, matched key, restored disk size, and restore-path time in every consumer/producer job summary.

Why

The latest ten successful ARM SCA jobs averaged 45m24s; Static Code Analysis averaged 40m34s. The full license-header scan alone repeatedly cost about 5 to 11 minutes. The previous workflow also ran the full package-graph verification twice in separate make invocations.

A previous version proposed archiving the Go build cache, but the measured uncompressed cache was about 4.7 GiB while this repository currently uses about 9.9 GiB of its 10 GiB Actions cache quota. This revision intentionally caches only bounded golangci-lint analysis data.

Correctness and unhappy paths

  • Cache restore failure or partial download is discarded and becomes a cold analyzer run.
  • Pull Files API failure, incomplete pagination, glob-like filenames, non-PR events, changed rules, or unknown license config/tool hashes become complete license checks.
  • Cheap license policy checks run before the long analyzers, so invalid PRs fail without consuming the long analyzer path.
  • NUL-delimited paths preserve legal whitespace/newlines; deleted files are excluded and rename sources/targets participate in rule-change detection.
  • The SCA job has a 90-minute hard bound.
  • License and cache-state selection are isolated behind committed deterministic tests.
  • ci: warm reusable SCA analysis caches matrixone#28101 bounds both cache file count and estimated compressed archive size, avoiding the false rejection caused by measuring filesystem blocks for many tiny files.

Evidence

  • Local actionlint, YAML parsing, git diff checks, and Make target contract checks pass at the current heads.
  • Eleven Node tests cover ordinary/non-source/deleted/renamed/config/dependency/glob/incomplete-list/API-ceiling, NUL-delimited publication, and failure-before-publication behavior.
  • Five cache-state tests cover exact, prefix, miss, exact-unusable, and prefix-unusable restores.
  • license-eye v0.4.0 path override was checked against selected files.
  • An earlier Linux arm64 4 CPU/8 GiB cold run showed automatic golangci concurrency can OOM while concurrency 1 fits; this supports the bounded producer setting, not a hot-duration claim.
  • A local representative accumulated golangci cache had 89,757 files and 353 MiB filesystem usage but compressed to 6,971,000 bytes in 10.43 seconds. This demonstrates why publication budget must use compressed archive size rather than du blocks.

No full local hot benchmark is claimed. Hot timing requires a trusted main cache, so acceptance is staged: merge this PR first, rerun matrixorigin/matrixone#28101 to validate the cold consumer/fallback path, then measure exact-hit state, restore time, analyzer time, cache size, and total SCA after the trusted producer publishes. The initial 20 to 30 minute common-PR range remains an estimate until those CI measurements exist.

Merge order: this PR first, then matrixorigin/matrixone#28101. This PR is safe alone because the legacy path remains unchanged. This PR supersedes #437.

Path-compatibility correction

golangci-lint package action IDs include absolute compiled-file paths. The v3 cache identity therefore includes the module root, GOMODCACHE, GOROOT, and GOCACHE, and the optimized consumer keeps its module cache at the same deterministic workspace path used by the trusted producer. Historical producer run 33789617303 and PR SCA run 33828032583 both used /home/runner/_work/matrixone/matrixone, /home/runner/_work/matrixone/matrixone/.sca-go-module-cache, /opt/caches/action-tool/go/1.26.4/arm64, and /home/runner/.cache/go-build.

MatrixOne branches that predate the companion path/license contract keep the original out-of-tree module-cache layout and skip analysis-cache restoration. This preserves the safe standalone merge of this PR: legacy complete license scans cannot traverse the repository-local module cache. The companion PR ignores .sca-go-module-cache/ in both Git and license scanning. A deterministic hash contract test proves every path/environment dimension creates an independent lineage, preventing an outer exact hit from hiding path-incompatible internal misses.

Network-failure hardening

MatrixOne SCA run 33837308158 failed in Prepare ENV before static analysis. After the large native cache restore was reset, the old fallback ran standalone make thirdparties and downloaded ONNX Runtime successfully. The following make cgo had to discard that unstamped generation under MatrixOne's complete native-provenance contract, then a second raw GitHub ONNX download exhausted four SSL-connect retries.

This revision now validates the restored native archive once and treats it as usable only when every required header, static library, ONNX library, and CGo output is present. When it is unusable, the workflow restores the dedicated small ONNX cache published by matrixorigin/matrixone#28101. The archive contains exactly one library plus a SHA-256 manifest; incomplete or corrupt contents are removed and become a normal verified-download fallback.

The cold path now calls only top-level make cgo, with ONNX_PREBUILT_DIR when the small cache is usable. This preserves MatrixOne's native generation/provenance owner, avoids duplicate compilation and guarantees the workflow itself requests the raw ONNX artifact at most once. A complete native hit skips the extra ONNX cache transfer entirely. PR jobs remain restore-only and cannot publish trusted cache data.

Additional exact-head checks: actionlint 1.7.12 and git diff --check pass; all eleven license-scope tests and both analysis-cache contract test suites pass; a MatrixOne Make dry-run proves the prebuilt directory reaches the platform-specific ONNX recipe.

@XuPeng-SH
XuPeng-SH force-pushed the codex/cache-sca-analysis branch from 6a2336f to 53ecccd Compare September 4, 2026 02:53
@XuPeng-SH
XuPeng-SH merged commit 13e9fa3 into matrixorigin:main Sep 4, 2026
1 check passed
XuPeng-SH added a commit to matrixorigin/matrixone that referenced this pull request Sep 4, 2026
## Which issue(s) this PR fixes:

Refs #28100

## What this PR does / why we need it:

Adds the trusted main producer for the golangci-lint analysis cache
consumed by matrixorigin/CI#444, and splits reusable analyzer commands
without changing the complete SCA contract.

- `make static-check` remains the full package-graph verification,
molint, header, dependency, and golangci entry point.
- `make static-check-analysis` remains full `./...` package-graph
verification plus molint and golangci for PR CI; the trusted CI workflow
owns conservative license scope selection.
- `make static-check-golangci` exists only so the producer can populate
the cache consumed by golangci. It does not repeat package-graph,
molint, or license validation because the producer is not a validation
gate and golangci itself owns cache population.
- The producer is scheduled daily to keep module/native/tool caches
inside the configured seven-day retention window.
- Expensive full golangci warming runs only when the exact analysis key
misses. Weekly generation rollover normally causes one cold warm per
week; exact daily hits skip it.
- A compatible prefix cache can seed a new exact entry, while
golangci-lint revalidates package source and dependency inputs.
- Only golangci-lint analysis data is published. The roughly 4.7-GiB Go
build cache is excluded to avoid thrashing the repository-wide 10-GiB
cache quota.
- Publication rejects an empty cache, more than 200,000 files, or an
estimated zstd archive above 160 MiB. It does not use filesystem-block
usage, which substantially overstates this many-small-file workload.
- An exact-but-unusable cache fails before the long warm because GitHub
cache keys are immutable and warming cannot repair the existing key.
Misses and usable prefix restores can still warm and publish a new exact
key.
- The shared restore action records exact/prefix/miss/unusable state,
matched key, restored disk size, and restore-path time.
- Golangci concurrency is limited to one so a cold generation fits the
unchanged 4-core/8-GiB runner.

This producer is a cache optimization, not a reduced validation gate. PR
SCA continues to run complete package-graph verification, both
full-package analyzers, and all applicable license checks.

## QA test plan

Fast checks completed at the current heads:

- actionlint, YAML parsing, and git diff checks pass;
- Make dry-runs prove `make static-check` still contains package-graph,
molint, both license, and full golangci checks;
- Make dry-runs prove `make static-check-analysis` still contains
package-graph, full `./...` molint, and full golangci;
- Make dry-runs prove the producer target runs exactly full `./...`
golangci without repeating package-graph, molint, or license work;
- companion matrixorigin/CI#444 has eleven license-scope tests and five
exact/prefix/miss/unusable cache-state tests;
- a local representative accumulated golangci cache had 89,757 files and
353 MiB filesystem usage but a 6,971,000-byte zstd archive produced in
10.43 seconds, validating the archive-based budget and file-count guard.

An earlier Linux arm64 4 CPU/8 GiB cold run showed concurrency 1 fits
where automatic concurrency can OOM. No full local hot-duration result
is claimed.

## Staged acceptance and rollback

matrixorigin/CI#444 must merge first. Then rerun this PR to validate the
new cold consumer/fallback path. A trusted main cache cannot be
published safely from an unmerged PR, so exact-hit timing is measured
after this producer first runs on main: record cache size,
exact/fallback hit state, restore time, analyzer time, and total SCA
against issue #28100 baseline of 45m24s average and 43m40s median.

The initial 20 to 30 minute common-PR range is an estimate, not a
measured result. If the cache is unavailable, corrupt, or rejected, PR
SCA falls back to the complete cold analyzer path; disabling the restore
action rolls back only the optimization, not test coverage.

Dependency and merge order: matrixorigin/CI#444 first, then this PR.

## Path-compatibility correction

The producer now keeps `GOMODCACHE` at the deterministic
`$GITHUB_WORKSPACE/.sca-go-module-cache` path also used by the optimized
PR consumer. `.gitignore` and `.licenserc.yml` explicitly exclude that
CI-only directory, so the stable path does not pollute the worktree or
expand complete license scans. The shared v3 restore action includes
module root, `GOMODCACHE`, `GOROOT`, and `GOCACHE` in its compatibility
identity because golangci-lint package hashes contain absolute
compiled-file paths.

Historical trusted-producer run 33789617303 and PR SCA run 33828032583
confirm that the relevant runner pool uses the same four absolute roots.
If a runner has a different layout, it receives a different outer
lineage and performs a complete cold analysis instead of reporting a
misleading exact hit. The companion action includes a deterministic test
that changing any compatibility input changes the cache identity.
## Network-failure hardening

SCA run 33837308158 exposed a separate Prepare ENV failure before
analysis began. The approximately 1.0 GB Go module archive restored only
about 10.7% before timing out, the full native-cache restore was reset,
and the fallback then downloaded ONNX Runtime twice: the first
standalone `make thirdparties` succeeded, but `make cgo` correctly
discarded that generation because it had no complete provenance stamp
and the second raw GitHub download exhausted all four SSL-connect
retries.

This revision now also:

- publishes a dedicated small ONNX Runtime fallback cache, keyed by OS,
architecture, and the exact thirdparty recipe/download implementation;
- records one library plus a SHA-256 manifest and rejects an
exact-but-incomplete/corrupt immutable cache in the trusted producer;
- supplies that verified fallback to the single top-level `make cgo`
owner when the full native cache misses;
- removes the temporary fallback before the later source/license scan.

The companion matrixorigin/CI#444 consumer restores this small archive
only when the full native cache is unusable. A missing or corrupt
fallback remains an optimization miss and uses MatrixOne's existing
checksum-verified download path. The consumer no longer invokes
standalone `make thirdparties`, so a cold fallback performs one complete
native generation and can download ONNX at most once.

Additional exact-head checks: actionlint 1.7.12 and `git diff --check`
pass; a Make dry-run proves `ONNX_PREBUILT_DIR` reaches the
platform-specific ONNX recipe; all eleven license-scope tests and both
analysis-cache contract test suites in CI#444 pass.
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.

1 participant