From 1beef44625de071c4f7a6bfd6e72486e32bc0572 Mon Sep 17 00:00:00 2001 From: Blizzard Date: Sat, 18 Jul 2026 13:44:38 +0800 Subject: [PATCH] =?UTF-8?q?ci:=20=E5=8A=A0=20-race=20+=20golangci-lint(new?= =?UTF-8?q?-issues)=20+=20govulncheck/gitleaks=20=E5=AE=89=E5=85=A8?= =?UTF-8?q?=E6=89=AB=E6=8F=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 补齐审计指出的「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 --- .github/workflows/ci.yml | 56 ++++++++++++++++++- .golangci.yml | 14 +++++ .../internal/store/payment_invariant_test.go | 4 +- sundynix-shared/blob/minio.go | 2 +- 4 files changed, 72 insertions(+), 4 deletions(-) create mode 100644 .golangci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 527815f..0fa99e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,15 +23,67 @@ jobs: with: go-version: "1.25" cache-dependency-path: "**/go.sum" - - name: build + vet + test(4 模块;bus 用内嵌 NATS,无需外部服务) + - name: build + vet + test -race(4 模块;bus 用内嵌 NATS,无需外部服务) run: | set -e for m in sundynix-shared sundynix-gateway sundynix-dispatcher sundynix-mcp-go; do echo "::group::$m" - (cd "$m" && go build ./... && go vet ./... && go test ./...) + # -race:数据竞争一票否决(4 模块本地已验证 race-clean)。ubuntu runner 自带 gcc,CGO 可用。 + (cd "$m" && go build ./... && go vet ./... && go test -race ./...) echo "::endgroup::" 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: govulncheck(advisory:报告不阻断) + 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: name: Frontend · tsc + vitest runs-on: ubuntu-latest diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..dfdcf1c --- /dev/null +++ b/.golangci.yml @@ -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 diff --git a/sundynix-gateway/internal/store/payment_invariant_test.go b/sundynix-gateway/internal/store/payment_invariant_test.go index 71441c5..89cd8d5 100644 --- a/sundynix-gateway/internal/store/payment_invariant_test.go +++ b/sundynix-gateway/internal/store/payment_invariant_test.go @@ -211,7 +211,9 @@ func TestRefundOrder_OnlyPaid(t *testing.T) { // pending 单不可退。 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", "") if err != nil { t.Fatalf("退 pending 单不应报错: %v", err) diff --git a/sundynix-shared/blob/minio.go b/sundynix-shared/blob/minio.go index faf2f84..aca3f0b 100644 --- a/sundynix-shared/blob/minio.go +++ b/sundynix-shared/blob/minio.go @@ -81,7 +81,7 @@ func (s *Store) GetBytes(ctx context.Context, key string) ([]byte, error) { if err != nil { return nil, err } - defer obj.Close() + defer func() { _ = obj.Close() }() return io.ReadAll(obj) }