Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
# buf breaking 需要完整历史以对比 origin/main 的 proto 契约。
fetch-depth: 0

- uses: actions/setup-go@v5
with:
Expand All @@ -31,14 +34,14 @@ jobs:
- name: Install pnpm
run: npm install -g pnpm@10.32.1

- name: Install protobuf toolchain
- name: Install buf
uses: bufbuild/buf-action@v1
with:
setup_only: true
version: 1.71.0

- name: Install protobuf Go plugins
run: |
sudo apt-get update
sudo apt-get install -y unzip
curl -fsSL -o "$RUNNER_TEMP/protoc.zip" \
https://github.com/protocolbuffers/protobuf/releases/download/v34.0/protoc-34.0-linux-x86_64.zip
unzip -q "$RUNNER_TEMP/protoc.zip" -d "$RUNNER_TEMP/protoc"
echo "$RUNNER_TEMP/protoc/bin" >> "$GITHUB_PATH"
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.36.11
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.6.2
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
Expand All @@ -49,10 +52,19 @@ jobs:
pnpm install --frozen-lockfile
pnpm build

- name: Proto lint and breaking-change check
run: make proto-check BUF_BREAKING_AGAINST='../../.git#branch=origin/main,subdir=crates/agent-gateway'

- name: Check generated protobuf
run: |
make proto
git diff --exit-code -- crates/agent-gateway/internal/proto/v1
git diff --exit-code -- crates/agent-gateway/internal/proto

- name: Lint gateway
uses: golangci/golangci-lint-action@v8
with:
version: v2.12.2
working-directory: crates/agent-gateway

- name: Test gateway
working-directory: crates/agent-gateway
Expand Down
17 changes: 16 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 11 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
AGENT_GUI_DIR := crates/agent-gui
AGENT_GATEWAY_DIR := crates/agent-gateway
AGENT_GATEWAY_WEB_DIR := $(AGENT_GATEWAY_DIR)/web
AGENT_GATEWAY_PROTO_FILE := proto/v1/gateway.proto

HOST_ARCH := $(shell uname -m)

Expand Down Expand Up @@ -33,7 +32,7 @@ RELEASE_TAG ?=

.PHONY: all dev build desktop-build-macos desktop-build-macos-release desktop-build-macos-intel desktop-build-macos-m desktop-build-windows desktop-build-linux github-release-main check-github-release-tag help
.PHONY: dev-gateway dev-webui
.PHONY: proto webui gateway-build gateway-docker-build gateway-docker-run gateway-docker-smoke build-linux build-linux-amd build-linux-arm
.PHONY: proto proto-check webui gateway-build gateway-docker-build gateway-docker-run gateway-docker-smoke build-linux build-linux-amd build-linux-arm
.PHONY: clean check-rust-target-% check-macos-signing-identity check-macos-notary-profile desktop-store-macos-notary-profile desktop-wait-macos-notary desktop-staple-macos desktop-verify-macos

all: build gateway-build
Expand Down Expand Up @@ -116,16 +115,16 @@ dev-webui:

## Gateway build and generated assets
proto:
@command -v protoc >/dev/null || (echo "protoc is required" && exit 1)
@command -v protoc-gen-go >/dev/null || (echo "protoc-gen-go is required. Run: go install google.golang.org/protobuf/cmd/protoc-gen-go@latest" && exit 1)
@command -v protoc-gen-go-grpc >/dev/null || (echo "protoc-gen-go-grpc is required. Run: go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest" && exit 1)
protoc \
--proto_path=$(AGENT_GATEWAY_DIR) \
--go_out=$(AGENT_GATEWAY_DIR) \
--go_opt=module=github.com/liveagent/agent-gateway \
--go-grpc_out=$(AGENT_GATEWAY_DIR) \
--go-grpc_opt=module=github.com/liveagent/agent-gateway \
$(AGENT_GATEWAY_PROTO_FILE)
@command -v buf >/dev/null || (echo "buf is required. Run: mise install" && exit 1)
cd $(AGENT_GATEWAY_DIR) && buf generate

# buf breaking 的对比基线(本地默认与当前 HEAD 对比;CI 覆写为 origin/main)。
BUF_BREAKING_AGAINST ?= ../../.git#subdir=$(AGENT_GATEWAY_DIR)

proto-check:
@command -v buf >/dev/null || (echo "buf is required. Run: mise install" && exit 1)
cd $(AGENT_GATEWAY_DIR) && buf lint
cd $(AGENT_GATEWAY_DIR) && buf breaking --against '$(BUF_BREAKING_AGAINST)'

webui:
pnpm --dir $(AGENT_GATEWAY_WEB_DIR) install --offline
Expand Down
35 changes: 35 additions & 0 deletions crates/agent-gateway/.golangci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# golangci-lint v2 —— 网关 Go 代码检查基线(此前无 linter,选务实检查集):govet/staticcheck/unused
# 管正确性与死代码(SA1019 兼作"调用已弃用 v1 符号"提示),errcheck/ineffassign/misspell 做基础卫生。
# 生成代码(internal/proto/)不参与检查。
version: "2"

linters:
default: none
enable:
- govet
- staticcheck
- unused
- errcheck
- ineffassign
- misspell
exclusions:
generated: lax
rules:
# v1 协议实现、其组装点与测试引用弃用 v1 符号是预期行为(整层同生命周期弃用):
# 对这些路径静默 SA1019,避免刷屏。
- path: internal/server/
linters: [staticcheck]
text: "SA1019"
- path: cmd/gateway/
linters: [staticcheck]
text: "SA1019"
- path: test/
linters: [staticcheck]
text: "SA1019"
# 测试代码允许忽略清理类调用的返回值。
- path: _test\.go
linters: [errcheck]

issues:
max-issues-per-linter: 0
max-same-issues: 0
15 changes: 15 additions & 0 deletions crates/agent-gateway/buf.gen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# buf 代码生成配置(v2 schema)。Go 插件用 mise 固定版本、`module=` 与历史 protoc 一致,
# 输出 internal/proto/;TS 插件 protoc-gen-es 由 web/node_modules 锁定,输出 web/src/lib/proto/gen/。
version: v2
plugins:
- local: protoc-gen-go
out: .
opt: module=github.com/liveagent/agent-gateway
- local: protoc-gen-go-grpc
out: .
opt: module=github.com/liveagent/agent-gateway
- local: web/node_modules/.bin/protoc-gen-es
out: web/src/lib/proto/gen
opt: target=ts
inputs:
- directory: .
33 changes: 33 additions & 0 deletions crates/agent-gateway/buf.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# buf 模块配置(v2 schema)。模块根设在 crates/agent-gateway/ 而非 proto/,保持 import 路径
# "proto/v1/gateway.proto" 与历史 protoc 及 Rust build.rs 一致,Go 生成物与既有提交字节级一致。
version: v2
modules:
- path: .
excludes:
# 排除 WebUI node_modules 里的第三方 .proto。
- web
lint:
use:
- STANDARD
except:
# 历史布局 proto/<版本>/ 早于 buf;迁目录会改 import 路径、破坏 Rust build.rs 与生成物,不翻新。
- PACKAGE_DIRECTORY_MATCH
# v1 契约早于 buf lint,历史命名不翻新(wire 兼容优先);豁免为全量 lint 确认后的最小集,
# 仅限 v1 文件——v2 及新增 proto 必须完整满足 STANDARD。
ignore_only:
SERVICE_SUFFIX:
- proto/v1/gateway.proto
RPC_REQUEST_STANDARD_NAME:
- proto/v1/gateway.proto
RPC_RESPONSE_STANDARD_NAME:
- proto/v1/gateway.proto
RPC_REQUEST_RESPONSE_UNIQUE:
- proto/v1/gateway.proto
ENUM_VALUE_PREFIX:
- proto/v1/gateway.proto
ENUM_ZERO_VALUE_SUFFIX:
- proto/v1/gateway.proto
breaking:
use:
# WIRE_JSON:兼守二进制与 JSON 线格式兼容(v1 JSON 协议仍在服务期)。
- WIRE_JSON
26 changes: 17 additions & 9 deletions crates/agent-gateway/cmd/gateway/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package main
import (
"context"
"errors"
"log"
"log/slog"
"net"
"net/http"
"os"
Expand All @@ -18,25 +18,33 @@ import (

"github.com/liveagent/agent-gateway/internal/auth"
"github.com/liveagent/agent-gateway/internal/config"
"github.com/liveagent/agent-gateway/internal/observability"
gatewayv1 "github.com/liveagent/agent-gateway/internal/proto/v1"
"github.com/liveagent/agent-gateway/internal/server"
"github.com/liveagent/agent-gateway/internal/session"
)

const grpcShutdownTimeout = 3 * time.Second

// fatal 记录错误并以非零码退出(slog 没有 Fatal 级别,集中在此处理)。
func fatal(msg string, args ...any) {
slog.Error(msg, args...)
os.Exit(1)
}

func main() {
observability.SetupLogging()
cfg := config.Load()
sm := session.NewManager()

grpcServer, err := newGRPCServer(cfg, sm)
if err != nil {
log.Fatalf("create gRPC server: %v", err)
fatal("create gRPC server failed", "err", err)
}

grpcListener, err := net.Listen("tcp", cfg.GRPCAddr)
if err != nil {
log.Fatalf("listen gRPC: %v", err)
fatal("listen gRPC failed", "addr", cfg.GRPCAddr, "err", err)
}

httpServer := &http.Server{
Expand All @@ -48,14 +56,14 @@ func main() {
errCh := make(chan error, 2)

go func() {
log.Printf("gRPC listening on %s", cfg.GRPCAddr)
slog.Info("gRPC listening", "addr", cfg.GRPCAddr)
if serveErr := grpcServer.Serve(grpcListener); serveErr != nil && !errors.Is(serveErr, grpc.ErrServerStopped) {
errCh <- serveErr
}
}()

go func() {
log.Printf("HTTP listening on %s", cfg.HTTPAddr)
slog.Info("HTTP listening", "addr", cfg.HTTPAddr)
var serveErr error
if cfg.TLSCert != "" || cfg.TLSKey != "" {
serveErr = httpServer.ListenAndServeTLS(cfg.TLSCert, cfg.TLSKey)
Expand All @@ -72,9 +80,9 @@ func main() {

select {
case sig := <-signalCh:
log.Printf("received signal %s, shutting down", sig)
slog.Info("received signal, shutting down", "signal", sig.String())
case err := <-errCh:
log.Fatalf("server error: %v", err)
fatal("server error", "err", err)
}

ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
Expand All @@ -86,11 +94,11 @@ func main() {
}()

if forced := shutdownGRPCServer(grpcServer, grpcShutdownTimeout); forced {
log.Printf("gRPC graceful shutdown timed out after %s, forcing stop", grpcShutdownTimeout)
slog.Warn("gRPC graceful shutdown timed out, forcing stop", "timeout", grpcShutdownTimeout)
}

if err := <-httpShutdownErrCh; err != nil {
log.Printf("http shutdown error: %v", err)
slog.Warn("http shutdown error", "err", err)
}
}

Expand Down
Loading
Loading