diff --git a/.cursor/rules/engineering.mdc b/.cursor/rules/engineering.mdc index a365508..2290ae8 100644 --- a/.cursor/rules/engineering.mdc +++ b/.cursor/rules/engineering.mdc @@ -57,9 +57,9 @@ alwaysApply: true ## Code Style -**STYLE-01** | MUST | Go-код после `gofmt`; перед PR — `go vet ./...`. -*Rationale:* единый стиль. -*Проверка:* CI job `go`. +**STYLE-01** | MUST | Go-код после `gofmt`; перед PR — `go vet ./...`. Агент после правок Go: `gofmt -w` на изменённых файлах + `golangci-lint run` (или `scripts/lint-go.*`) до exit 0. +*Rationale:* CI job `go` включает golangci-lint (gofmt). +*Проверка:* CI job `go`; `.cursor/rules/engineering.mdc` STYLE-01. **STYLE-02** | MUST | Экспортируемые типы/функции публичных пакетов — godoc-комментарий. *Rationale:* навигация по API пакетов. @@ -230,7 +230,8 @@ alwaysApply: true go vet ./... go test ./... -race -count=1 npx @redocly/cli lint docs/openapi.yaml -# web: cd web; npm run check; npm run lint +# web: cd web; npm run check; npm run lint (или scripts/lint-web.ps1) +# go fmt/lint: gofmt -w ; scripts/lint-go.ps1 (gofmt + vet + golangci-lint) # birdfmt: go test ./internal/birdfmt/... -count=1 ``` diff --git a/AGENTS.md b/AGENTS.md index afee1dd..44dbf92 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -51,6 +51,7 @@ - Консоль пользователя: **PowerShell**; пути в стиле `deploy\compose`. - Быстрый старт и переменные: [docs/quickstart.md](docs/quickstart.md), [README.md](README.md). +- **Go:** после правок — `gofmt -w`, `go vet ./...`, `scripts/lint-go.ps1` (как CI golangci-lint). ## Язык документации проекта diff --git a/internal/agentserver/server.go b/internal/agentserver/server.go index 509ffdc..f6dfa24 100644 --- a/internal/agentserver/server.go +++ b/internal/agentserver/server.go @@ -15,20 +15,20 @@ import ( // Config holds evobgp-agent serve settings. type Config struct { - Listen string - Secret string - ControlPlaneURL string - NodeToken string - SpeakerID string - PubKeyB64 string - PubKeyHex string - ExtractDir string - BirdBin string - BirdcBin string - Socket string - SyncTimeout time.Duration - LastSync func() (revisionID string, at time.Time) - OnSyncSuccess func(revisionID string) + Listen string + Secret string + ControlPlaneURL string + NodeToken string + SpeakerID string + PubKeyB64 string + PubKeyHex string + ExtractDir string + BirdBin string + BirdcBin string + Socket string + SyncTimeout time.Duration + LastSync func() (revisionID string, at time.Time) + OnSyncSuccess func(revisionID string) } // Server serves Panel→Node internal API (Remnawave-style wake-up). @@ -107,9 +107,9 @@ func (s *Server) handleSync(w http.ResponseWriter, r *http.Request) { s.cfg.OnSyncSuccess(res.RevisionID) } writeJSON(w, http.StatusOK, map[string]any{ - "ok": true, - "applied_revision_id": res.RevisionID, - "main_config": res.MainConfig, + "ok": true, + "applied_revision_id": res.RevisionID, + "main_config": res.MainConfig, }) } diff --git a/internal/nodecli/sync.go b/internal/nodecli/sync.go index e8d010d..df8fdd8 100644 --- a/internal/nodecli/sync.go +++ b/internal/nodecli/sync.go @@ -17,18 +17,18 @@ import ( // SyncConfig drives pull → verify → apply on a replica node. type SyncConfig struct { - BaseURL string - Token string - SpeakerID string - RevisionID string // empty = latest published on CP - PubKeyB64 string - PubKeyHex string - ExtractDir string - BundlePath string // temp file; default os.TempDir()/evobgp-bundle.tar.gz - BirdBin string - BirdcBin string - Socket string - HTTPClient interface { + BaseURL string + Token string + SpeakerID string + RevisionID string // empty = latest published on CP + PubKeyB64 string + PubKeyHex string + ExtractDir string + BundlePath string // temp file; default os.TempDir()/evobgp-bundle.tar.gz + BirdBin string + BirdcBin string + Socket string + HTTPClient interface { Do(req interface{}) (interface{}, error) } Timeout time.Duration @@ -106,9 +106,9 @@ func SyncBundle(ctx context.Context, cfg SyncConfig) (SyncResult, error) { // SyncResultJSON encodes SyncResult for HTTP responses. func SyncResultJSON(r SyncResult) ([]byte, error) { return json.Marshal(map[string]any{ - "ok": true, + "ok": true, "applied_revision_id": r.RevisionID, - "main_config": r.MainConfig, + "main_config": r.MainConfig, }) } diff --git a/internal/nodedispatch/dispatch.go b/internal/nodedispatch/dispatch.go index cc08ab7..7eeacd5 100644 --- a/internal/nodedispatch/dispatch.go +++ b/internal/nodedispatch/dispatch.go @@ -26,11 +26,11 @@ type Result struct { // Options configures Panel→Node HTTP dispatch. type Options struct { - HTTPClient *http.Client - Timeout time.Duration - MaxRetries int - InsecureTLS bool - RevisionID string + HTTPClient *http.Client + Timeout time.Duration + MaxRetries int + InsecureTLS bool + RevisionID string } func (o Options) client() *http.Client { diff --git a/scripts/lint-go.ps1 b/scripts/lint-go.ps1 new file mode 100644 index 0000000..79f9b71 --- /dev/null +++ b/scripts/lint-go.ps1 @@ -0,0 +1,19 @@ +# Same gates as CI job go (subset): gofmt, vet, golangci-lint. +$ErrorActionPreference = 'Stop' +Set-Location (Join-Path $PSScriptRoot '..') + +$unfmt = gofmt -l . 2>$null +if ($unfmt) { + Write-Error "gofmt: unformatted files:`n$unfmt" +} +go vet ./... +$golangci = Get-Command golangci-lint -ErrorAction SilentlyContinue +if (-not $golangci) { + $golangciPath = Join-Path $env:USERPROFILE 'go\bin\golangci-lint.exe' + if (Test-Path $golangciPath) { $golangci = @{ Source = $golangciPath } } +} +if ($golangci) { + & $golangci.Source run +} else { + Write-Warning 'lint-go: golangci-lint not found, skipping (CI will run it)' +} diff --git a/scripts/lint-go.sh b/scripts/lint-go.sh new file mode 100644 index 0000000..1071c88 --- /dev/null +++ b/scripts/lint-go.sh @@ -0,0 +1,20 @@ +#!/bin/sh +# Same gates as CI job go (subset before full test): gofmt, vet, golangci-lint. +set -euxo pipefail +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +UNFMT="$(gofmt -l .)" +if [ -n "$UNFMT" ]; then + echo "gofmt: unformatted files:" >&2 + echo "$UNFMT" >&2 + exit 1 +fi + +go vet ./... + +if command -v golangci-lint >/dev/null 2>&1; then + golangci-lint run +else + echo "lint-go: golangci-lint not in PATH, skipping (CI will run it)" >&2 +fi