ci: run tests, lint and the UI build on every push
Add a golangci-lint config and fix its findings: the OIDC state cookie gets the Secure flag on https deployments, and pool sizes are clamped before the int32 conversion.
This commit is contained in:
@@ -1,14 +1,50 @@
|
||||
# Builds the LMR image and pushes it to the registry on push to master.
|
||||
# CI for LMR. Every push and pull request runs the checks. A push to master
|
||||
# then builds the image and pushes it to the author's registry; replace the
|
||||
# registry settings and secrets with yours when you fork.
|
||||
when:
|
||||
- event: push
|
||||
branch: master
|
||||
- event: manual
|
||||
branch: master
|
||||
- event: [push, pull_request, manual]
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:16-alpine
|
||||
environment:
|
||||
POSTGRES_USER: gateway
|
||||
POSTGRES_PASSWORD: gateway
|
||||
POSTGRES_DB: gateway
|
||||
|
||||
steps:
|
||||
test:
|
||||
image: golang:1.27-alpine
|
||||
environment:
|
||||
CGO_ENABLED: "0"
|
||||
GATEWAY_TEST_DB_URL: postgres://gateway:gateway@postgres:5432/gateway?sslmode=disable
|
||||
commands:
|
||||
- cd backend
|
||||
- go vet ./...
|
||||
- i=0; until nc -z postgres 5432 || [ $i -ge 60 ]; do i=$((i+1)); sleep 1; done
|
||||
- GATEWAY_DB_URL=$GATEWAY_TEST_DB_URL go run ./cmd/migrate up
|
||||
- go test -count=1 ./...
|
||||
|
||||
lint:
|
||||
image: golangci/golangci-lint:v2.14.0
|
||||
commands:
|
||||
- cd backend
|
||||
- golangci-lint run ./...
|
||||
|
||||
ui:
|
||||
image: node:22-alpine
|
||||
commands:
|
||||
- cd backend/dashboard
|
||||
- npm ci
|
||||
- npx tsc --noEmit
|
||||
- npm run build
|
||||
|
||||
build-and-push:
|
||||
image: woodpeckerci/plugin-docker-buildx:6
|
||||
privileged: true
|
||||
when:
|
||||
- event: [push, manual]
|
||||
branch: master
|
||||
settings:
|
||||
registry: registry.govekar.net
|
||||
repo: registry.govekar.net/lovrog05/localmodelroutex
|
||||
|
||||
41
backend/.golangci.yml
Normal file
41
backend/.golangci.yml
Normal file
@@ -0,0 +1,41 @@
|
||||
# golangci-lint v2 configuration. Run with `make lint`.
|
||||
version: "2"
|
||||
|
||||
run:
|
||||
timeout: 5m
|
||||
tests: true
|
||||
|
||||
linters:
|
||||
default: standard # errcheck, govet, ineffassign, staticcheck, unused
|
||||
enable:
|
||||
- misspell
|
||||
- unconvert
|
||||
- unparam
|
||||
- bodyclose
|
||||
- noctx
|
||||
- gosec
|
||||
settings:
|
||||
gosec:
|
||||
excludes:
|
||||
- G104 # unhandled errors are covered by errcheck
|
||||
- G404 # math/rand is fine for round-robin routing
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- std-error-handling
|
||||
rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gosec
|
||||
- noctx
|
||||
- bodyclose
|
||||
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
settings:
|
||||
goimports:
|
||||
local-prefixes:
|
||||
- git.govekar.net/LovroG05/localmodelroutex
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
@@ -27,12 +28,13 @@ func (a *API) oidcLogin(w http.ResponseWriter, r *http.Request) {
|
||||
_, _ = rand.Read(b)
|
||||
state := base64.RawURLEncoding.EncodeToString(b)
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
http.SetCookie(w, &http.Cookie{ //nolint:gosec // G124: Secure follows the public URL scheme, see secureCookies
|
||||
Name: oidcStateCookie,
|
||||
Value: state,
|
||||
Path: "/auth",
|
||||
MaxAge: int(oidcStateTTL.Seconds()),
|
||||
HttpOnly: true,
|
||||
Secure: a.secureCookies(),
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
|
||||
@@ -55,11 +57,14 @@ func (a *API) oidcCallback(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
// Consume the state cookie.
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
http.SetCookie(w, &http.Cookie{ //nolint:gosec // G124: Secure follows the public URL scheme, see secureCookies
|
||||
Name: oidcStateCookie,
|
||||
Value: "",
|
||||
Path: "/auth",
|
||||
MaxAge: -1,
|
||||
HttpOnly: true,
|
||||
Secure: a.secureCookies(),
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
|
||||
tok, err := a.oauth2Config().Exchange(r.Context(), r.URL.Query().Get("code"))
|
||||
@@ -105,3 +110,9 @@ func (a *API) oauth2Config() *oauth2.Config {
|
||||
Endpoint: oauth2.Endpoint{AuthURL: a.OIDCAuthURL, TokenURL: a.OIDCTokenURL},
|
||||
}
|
||||
}
|
||||
|
||||
// secureCookies reports whether cookies get the Secure flag. The public URL
|
||||
// decides, so a plain http://localhost dev setup keeps working.
|
||||
func (a *API) secureCookies() bool {
|
||||
return strings.HasPrefix(strings.ToLower(a.PublicBaseURL), "https://")
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -70,8 +71,8 @@ func Load() (*Config, error) {
|
||||
},
|
||||
DB: DBConfig{
|
||||
URL: v.GetString("db.url"),
|
||||
MaxConns: int32(v.GetInt("db.max_conns")),
|
||||
MinConns: int32(v.GetInt("db.min_conns")),
|
||||
MaxConns: clampInt32(v.GetInt("db.max_conns")),
|
||||
MinConns: clampInt32(v.GetInt("db.min_conns")),
|
||||
MaxConnLifetime: v.GetInt("db.max_conn_lifetime"),
|
||||
},
|
||||
OIDC: OIDCConfig{
|
||||
@@ -128,3 +129,14 @@ func (c *Config) validate() error {
|
||||
func (c *Config) IsDev() bool {
|
||||
return c.Server.Env == "development"
|
||||
}
|
||||
|
||||
// clampInt32 converts a pool size setting to int32 without overflow.
|
||||
func clampInt32(n int) int32 {
|
||||
if n < 0 {
|
||||
return 0
|
||||
}
|
||||
if n > math.MaxInt32 {
|
||||
return math.MaxInt32
|
||||
}
|
||||
return int32(n)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
tokenURL = "https://api.github.com/copilot_internal/v2/token"
|
||||
tokenURL = "https://api.github.com/copilot_internal/v2/token" //nolint:gosec // G101: a URL, not a credential
|
||||
chatBaseURL = "https://api.githubcopilot.com"
|
||||
// refreshSkew refreshes the token slightly before it actually expires.
|
||||
refreshSkew = time.Minute
|
||||
|
||||
@@ -53,7 +53,7 @@ func (p *provider) ImageGeneration(ctx context.Context, req *providers.ImageRequ
|
||||
cost float64
|
||||
)
|
||||
for i := 0; i < n; i++ {
|
||||
resp, err := p.Transport.ChatCompletion(ctx, chatReq)
|
||||
resp, err := p.ChatCompletion(ctx, chatReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user