ci: 加 -race + golangci-lint(new-issues) + govulncheck/gitleaks 安全扫描

补齐审计指出的「CI 缺 lint/安全扫描/-race」低成本质量门:

- go job:go test → go test -race(4 模块本地已验证 race-clean,ubuntu 自带 gcc)。
- lint job:golangci-lint(standard 集)仅 PR 跑 + only-new-issues —— 新代码必须干净,
  存量 ~42 处(多为未检 Close/死代码)单独消化,不拿存量红门。加 .golangci.yml(测试放过 errcheck)。
- security job:govulncheck advisory(stdlib/nats CVE 靠 toolchain/依赖升级,只做可见性不阻断)
  + gitleaks 扫提交防密钥泄漏。
- 顺手清掉本会话新代码的 lint:blob GetBytes 的 defer Close、退款测试未检 CreateOrder。

注:gitleaks-action 个人/公开仓库免费;组织仓库需 GITLEAKS_LICENSE。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-18 13:44:38 +08:00
parent 5e5f6e9610
commit 1beef44625
4 changed files with 72 additions and 4 deletions
+54 -2
View File
@@ -23,15 +23,67 @@ jobs:
with: with:
go-version: "1.25" go-version: "1.25"
cache-dependency-path: "**/go.sum" cache-dependency-path: "**/go.sum"
- name: build + vet + test4 模块;bus 用内嵌 NATS,无需外部服务) - name: build + vet + test -race(4 模块;bus 用内嵌 NATS,无需外部服务)
run: | run: |
set -e set -e
for m in sundynix-shared sundynix-gateway sundynix-dispatcher sundynix-mcp-go; do for m in sundynix-shared sundynix-gateway sundynix-dispatcher sundynix-mcp-go; do
echo "::group::$m" echo "::group::$m"
(cd "$m" && go build ./... && go vet ./... && go test ./...) # -race:数据竞争一票否决(4 模块本地已验证 race-clean)。ubuntu runner 自带 gccCGO 可用。
(cd "$m" && go build ./... && go vet ./... && go test -race ./...)
echo "::endgroup::" echo "::endgroup::"
done done
# golangci-lint:只卡「新问题」——存量约 42 处(未检 Close/死代码)单独消化,不拿存量红门。
# 仅在 PR 跑:dev→main 的 PR 是唯一合入口,据 base 算 diff;push 到 main 不重复跑。
lint:
name: Go · golangci-lint (new issues)
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
module: [sundynix-shared, sundynix-gateway, sundynix-dispatcher, sundynix-mcp-go]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # only-new-issues 要据 base 分支算 diff,需完整历史
- uses: actions/setup-go@v5
with:
go-version: "1.25"
cache-dependency-path: "**/go.sum"
- uses: golangci/golangci-lint-action@v6
with:
version: latest
working-directory: ${{ matrix.module }}
only-new-issues: true
# 安全扫描:govulncheck(依赖 CVE) advisory —— stdlib/nats CVE 要靠 toolchain/依赖升级,
# 不拿它红门,只做可见性;gitleaks(密钥泄漏)在 PR 上扫 diff,硬拦提交密钥。
security:
name: Security · govulncheck + gitleaks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version: "1.25"
cache-dependency-path: "**/go.sum"
- name: govulncheckadvisory:报告不阻断)
continue-on-error: true
run: |
go install golang.org/x/vuln/cmd/govulncheck@latest
for m in sundynix-shared sundynix-gateway sundynix-dispatcher sundynix-mcp-go; do
echo "::group::govulncheck $m"
(cd "$m" && govulncheck ./...) || true
echo "::endgroup::"
done
- name: gitleaks(扫提交历史,命中即失败)
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
web: web:
name: Frontend · tsc + vitest name: Frontend · tsc + vitest
runs-on: ubuntu-latest runs-on: ubuntu-latest
+14
View File
@@ -0,0 +1,14 @@
# golangci-lint v2 配置。默认(standard)集:errcheck / govet / ineffassign / staticcheck / unused。
# 引入策略:CI 用 only-new-issues —— 新代码必须干净,存量约 42 处(多为未检 Close/死代码)
# 单独消化,不拿存量红整个门。本地全量跑:`golangci-lint run ./...`(在各模块目录)。
version: "2"
linters:
default: standard
exclusions:
rules:
# 测试里 defer x.Close() / 建数据的辅助调用不检返回值属常规,放过 errcheck。
- path: _test\.go
linters:
- errcheck
@@ -211,7 +211,9 @@ func TestRefundOrder_OnlyPaid(t *testing.T) {
// pending 单不可退。 // pending 单不可退。
o := &PaymentOrder{TenantID: "t1", UserID: "u1", AmountFen: 100, CreditsMicro: 1_000_000, Channel: ChannelWechat, Status: OrderPending} o := &PaymentOrder{TenantID: "t1", UserID: "u1", AmountFen: 100, CreditsMicro: 1_000_000, Channel: ChannelWechat, Status: OrderPending}
p.CreateOrder(ctx, o) if err := p.CreateOrder(ctx, o); err != nil {
t.Fatalf("建单失败: %v", err)
}
changed, err := p.RefundOrder(ctx, o.ID, "admin1", "") changed, err := p.RefundOrder(ctx, o.ID, "admin1", "")
if err != nil { if err != nil {
t.Fatalf("退 pending 单不应报错: %v", err) t.Fatalf("退 pending 单不应报错: %v", err)
+1 -1
View File
@@ -81,7 +81,7 @@ func (s *Store) GetBytes(ctx context.Context, key string) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer obj.Close() defer func() { _ = obj.Close() }()
return io.ReadAll(obj) return io.ReadAll(obj)
} }