Skip to content

Commit 6bc12fc

Browse files
ci: add build, vet and test workflow (#1)
* ci: build, vet and test on every push and pull request This repository had no CI at all. It ships a Go SDK that downstream users build against, and nothing verified it compiled. The steps are the ones already passing locally when this was added — build, vet, race-enabled tests, and gofmt. The intent is a gate that stays green and therefore stays worth reading, rather than a broad net that goes red and gets ignored. Go version comes from go.mod via go-version-file, so the toolchain and the module cannot drift apart. * style: apply gofmt Mechanical output of `gofmt -w .` across 8 files — struct field and comment alignment only. No semantic change is possible from gofmt, which is why this is a separate commit from the workflow that checks it. Without this the formatting job added in the previous commit would fail on arrival, and a CI check that is red from day one teaches people to ignore the whole workflow.
1 parent 1e19f6a commit 6bc12fc

9 files changed

Lines changed: 138 additions & 74 deletions

File tree

.github/workflows/ci.yml

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Build, vet and test the SDK on every push and pull request.
2+
#
3+
# This repository had no CI at all: it ships a Go SDK that downstream users
4+
# build against, and nothing verified it compiled. The steps below are the ones
5+
# that were already passing locally when this was added — the intent is a gate
6+
# that stays green and therefore stays meaningful, not a broad net that goes
7+
# red and gets ignored.
8+
name: CI
9+
10+
on:
11+
push:
12+
branches: [main]
13+
pull_request:
14+
15+
permissions:
16+
contents: read
17+
18+
jobs:
19+
build:
20+
name: Build and test
21+
runs-on: ubuntu-latest
22+
steps:
23+
- name: Checkout
24+
uses: actions/checkout@v4
25+
26+
- name: Install Go
27+
uses: actions/setup-go@v5
28+
with:
29+
# Tracks the version in go.mod rather than pinning separately, so
30+
# the two cannot drift.
31+
go-version-file: go.mod
32+
cache: true
33+
34+
- name: Build
35+
run: go build ./...
36+
37+
- name: Vet
38+
run: go vet ./...
39+
40+
- name: Test
41+
run: go test -race ./...
42+
43+
formatting:
44+
name: Formatting
45+
runs-on: ubuntu-latest
46+
steps:
47+
- name: Checkout
48+
uses: actions/checkout@v4
49+
50+
- name: Install Go
51+
uses: actions/setup-go@v5
52+
with:
53+
go-version-file: go.mod
54+
55+
- name: Check gofmt
56+
run: |
57+
unformatted="$(gofmt -l .)"
58+
if [ -n "$unformatted" ]; then
59+
echo "These files are not gofmt-formatted:"
60+
echo "$unformatted"
61+
echo
62+
echo "Run: gofmt -w ."
63+
exit 1
64+
fi

decision.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -276,17 +276,17 @@ func (d *Decision) Build() AgentResponse {
276276

277277
// Decisions provides shorthand functions for common decisions.
278278
var Decisions = struct {
279-
Allow func() *Decision
280-
Deny func() *Decision
279+
Allow func() *Decision
280+
Deny func() *Decision
281281
Unauthorized func() *Decision
282-
RateLimited func() *Decision
283-
Block func(status int, body string) *Decision
284-
Redirect func(url string, permanent bool) *Decision
282+
RateLimited func() *Decision
283+
Block func(status int, body string) *Decision
284+
Redirect func(url string, permanent bool) *Decision
285285
}{
286-
Allow: Allow,
287-
Deny: Deny,
286+
Allow: Allow,
287+
Deny: Deny,
288288
Unauthorized: Unauthorized,
289-
RateLimited: RateLimited,
289+
RateLimited: RateLimited,
290290
Block: func(status int, body string) *Decision {
291291
d := Block(status)
292292
if body != "" {

protocol.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,14 @@ const MaxMessageSize = 10 * 1024 * 1024
1919
type EventType string
2020

2121
const (
22-
EventTypeRequestHeaders EventType = "request_headers"
23-
EventTypeRequestBodyChunk EventType = "request_body_chunk"
24-
EventTypeResponseHeaders EventType = "response_headers"
25-
EventTypeResponseBodyChunk EventType = "response_body_chunk"
26-
EventTypeRequestComplete EventType = "request_complete"
27-
EventTypeWebSocketFrame EventType = "websocket_frame"
28-
EventTypeConfigure EventType = "configure"
29-
EventTypeGuardrailInspect EventType = "guardrail_inspect"
22+
EventTypeRequestHeaders EventType = "request_headers"
23+
EventTypeRequestBodyChunk EventType = "request_body_chunk"
24+
EventTypeResponseHeaders EventType = "response_headers"
25+
EventTypeResponseBodyChunk EventType = "response_body_chunk"
26+
EventTypeRequestComplete EventType = "request_complete"
27+
EventTypeWebSocketFrame EventType = "websocket_frame"
28+
EventTypeConfigure EventType = "configure"
29+
EventTypeGuardrailInspect EventType = "guardrail_inspect"
3030
)
3131

3232
// GuardrailInspectionType represents the type of guardrail inspection.

v2/agent_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ import (
1010
// TestAgentV2Impl is a test agent implementing AgentV2.
1111
type TestAgentV2Impl struct {
1212
BaseAgentV2
13-
onRequestCalled bool
14-
onShutdownCalled bool
15-
onDrainCalled bool
16-
onStreamClosedID string
17-
onCancelRequestID uint64
13+
onRequestCalled bool
14+
onShutdownCalled bool
15+
onDrainCalled bool
16+
onStreamClosedID string
17+
onCancelRequestID uint64
1818
}
1919

2020
func (a *TestAgentV2Impl) Name() string {

v2/grpc_convert.go

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@ import (
1010
// grpcProxyToAgentMessage is the JSON representation of a ProxyToAgent gRPC message.
1111
// This mirrors the proto oneof structure using JSON fields.
1212
type grpcProxyToAgentMessage struct {
13-
Handshake *grpcHandshakeRequest `json:"handshake,omitempty"`
14-
RequestHeaders *grpcRequestHeadersEvent `json:"request_headers,omitempty"`
15-
RequestBody *grpcBodyChunkEvent `json:"request_body_chunk,omitempty"`
13+
Handshake *grpcHandshakeRequest `json:"handshake,omitempty"`
14+
RequestHeaders *grpcRequestHeadersEvent `json:"request_headers,omitempty"`
15+
RequestBody *grpcBodyChunkEvent `json:"request_body_chunk,omitempty"`
1616
ResponseHeaders *grpcResponseHeadersEvent `json:"response_headers,omitempty"`
17-
ResponseBody *grpcBodyChunkEvent `json:"response_body_chunk,omitempty"`
18-
Cancel *grpcCancelRequest `json:"cancel,omitempty"`
19-
Configure *grpcConfigureEvent `json:"configure,omitempty"`
20-
Ping *grpcPing `json:"ping,omitempty"`
17+
ResponseBody *grpcBodyChunkEvent `json:"response_body_chunk,omitempty"`
18+
Cancel *grpcCancelRequest `json:"cancel,omitempty"`
19+
Configure *grpcConfigureEvent `json:"configure,omitempty"`
20+
Ping *grpcPing `json:"ping,omitempty"`
2121
RequestComplete *grpcRequestCompleteEvent `json:"request_complete,omitempty"`
2222
}
2323

@@ -40,20 +40,20 @@ type grpcHandshakeRequest struct {
4040

4141
// grpcHandshakeResponse maps to the proto HandshakeResponse.
4242
type grpcHandshakeResponse struct {
43-
ProtocolVersion uint32 `json:"protocol_version"`
44-
Capabilities *grpcAgentCapabilities `json:"capabilities,omitempty"`
45-
Success bool `json:"success"`
46-
Error *string `json:"error,omitempty"`
43+
ProtocolVersion uint32 `json:"protocol_version"`
44+
Capabilities *grpcAgentCapabilities `json:"capabilities,omitempty"`
45+
Success bool `json:"success"`
46+
Error *string `json:"error,omitempty"`
4747
}
4848

4949
// grpcAgentCapabilities maps to the proto AgentCapabilities.
5050
type grpcAgentCapabilities struct {
51-
ProtocolVersion uint32 `json:"protocol_version"`
52-
AgentID string `json:"agent_id"`
53-
Name string `json:"name"`
54-
Version string `json:"version"`
55-
SupportedEvents []int32 `json:"supported_events,omitempty"`
56-
Features *grpcFeatures `json:"features,omitempty"`
51+
ProtocolVersion uint32 `json:"protocol_version"`
52+
AgentID string `json:"agent_id"`
53+
Name string `json:"name"`
54+
Version string `json:"version"`
55+
SupportedEvents []int32 `json:"supported_events,omitempty"`
56+
Features *grpcFeatures `json:"features,omitempty"`
5757
}
5858

5959
// grpcFeatures maps to the proto AgentFeatures.
@@ -160,13 +160,13 @@ type grpcRequestCompleteEvent struct {
160160

161161
// grpcAgentResponse maps to the proto AgentResponse.
162162
type grpcAgentResponse struct {
163-
CorrelationID string `json:"correlation_id"`
164-
Decision interface{} `json:"decision"`
165-
RequestHeaders []grpcHeaderOp `json:"request_headers,omitempty"`
166-
ResponseHeaders []grpcHeaderOp `json:"response_headers,omitempty"`
167-
Audit map[string]interface{} `json:"audit,omitempty"`
168-
ProcessingTimeMs *uint64 `json:"processing_time_ms,omitempty"`
169-
NeedsMore bool `json:"needs_more"`
163+
CorrelationID string `json:"correlation_id"`
164+
Decision interface{} `json:"decision"`
165+
RequestHeaders []grpcHeaderOp `json:"request_headers,omitempty"`
166+
ResponseHeaders []grpcHeaderOp `json:"response_headers,omitempty"`
167+
Audit map[string]interface{} `json:"audit,omitempty"`
168+
ProcessingTimeMs *uint64 `json:"processing_time_ms,omitempty"`
169+
NeedsMore bool `json:"needs_more"`
170170
}
171171

172172
// grpcHeaderOp maps to the proto HeaderOp.

v2/grpc_service.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,14 @@ var agentServiceDesc = grpc.ServiceDesc{
8484
{
8585
StreamName: "ProcessStream",
8686
Handler: processStreamHandler,
87-
ServerStreams: true,
88-
ClientStreams: true,
87+
ServerStreams: true,
88+
ClientStreams: true,
8989
},
9090
{
9191
StreamName: "ControlStream",
9292
Handler: controlStreamHandler,
93-
ServerStreams: true,
94-
ClientStreams: true,
93+
ServerStreams: true,
94+
ClientStreams: true,
9595
},
9696
},
9797
Metadata: "agent_v2.proto",
@@ -328,10 +328,10 @@ func (s *agentGRPCService) controlStream(stream grpc.ServerStream) error {
328328

329329
// Parse the control message
330330
var controlMsg struct {
331-
Health json.RawMessage `json:"health,omitempty"`
332-
Metrics json.RawMessage `json:"metrics,omitempty"`
331+
Health json.RawMessage `json:"health,omitempty"`
332+
Metrics json.RawMessage `json:"metrics,omitempty"`
333333
ConfigUpdate json.RawMessage `json:"config_update,omitempty"`
334-
Log json.RawMessage `json:"log,omitempty"`
334+
Log json.RawMessage `json:"log,omitempty"`
335335
}
336336
if err := json.Unmarshal(in.Data, &controlMsg); err != nil {
337337
log.Error().Err(err).Str("stream_id", streamID).Msg("Failed to parse control message")

v2/handler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import (
77
"sync"
88
"time"
99

10-
zentinel "github.com/zentinelproxy/zentinel-agent-go-sdk"
1110
"github.com/rs/zerolog/log"
11+
zentinel "github.com/zentinelproxy/zentinel-agent-go-sdk"
1212
)
1313

1414
// AgentHandlerV2 handles v2 protocol events and routes them to the agent.

v2/health_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ func TestMetricsCollector_RecordRequest(t *testing.T) {
104104
collector := NewMetricsCollector()
105105

106106
// Record some requests
107-
collector.RecordRequest(true, 10.0) // allowed
108-
collector.RecordRequest(true, 20.0) // allowed
109-
collector.RecordRequest(false, 5.0) // blocked
107+
collector.RecordRequest(true, 10.0) // allowed
108+
collector.RecordRequest(true, 20.0) // allowed
109+
collector.RecordRequest(false, 5.0) // blocked
110110
collector.RecordError()
111111

112112
report := collector.Report()

v2/protocol.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,24 @@ const MaxMessageSizeV2 = 16 * 1024 * 1024
1212

1313
// Message type IDs for v2 binary protocol.
1414
const (
15-
MsgTypeHandshakeRequest byte = 0x01
16-
MsgTypeHandshakeResponse byte = 0x02
17-
MsgTypeRequestHeaders byte = 0x10
18-
MsgTypeRequestBodyChunk byte = 0x11
19-
MsgTypeResponseHeaders byte = 0x12
20-
MsgTypeResponseBodyChunk byte = 0x13
21-
MsgTypeDecision byte = 0x20
22-
MsgTypeBodyMutation byte = 0x21
23-
MsgTypeCancelRequest byte = 0x30
24-
MsgTypeCancelAll byte = 0x31
25-
MsgTypePing byte = 0xF0
26-
MsgTypePong byte = 0xF1
27-
MsgTypeHealthRequest byte = 0xE0
28-
MsgTypeHealthResponse byte = 0xE1
29-
MsgTypeMetricsRequest byte = 0xE2
30-
MsgTypeMetricsResponse byte = 0xE3
31-
MsgTypeRegistration byte = 0x03
32-
MsgTypeRegistrationAck byte = 0x04
15+
MsgTypeHandshakeRequest byte = 0x01
16+
MsgTypeHandshakeResponse byte = 0x02
17+
MsgTypeRequestHeaders byte = 0x10
18+
MsgTypeRequestBodyChunk byte = 0x11
19+
MsgTypeResponseHeaders byte = 0x12
20+
MsgTypeResponseBodyChunk byte = 0x13
21+
MsgTypeDecision byte = 0x20
22+
MsgTypeBodyMutation byte = 0x21
23+
MsgTypeCancelRequest byte = 0x30
24+
MsgTypeCancelAll byte = 0x31
25+
MsgTypePing byte = 0xF0
26+
MsgTypePong byte = 0xF1
27+
MsgTypeHealthRequest byte = 0xE0
28+
MsgTypeHealthResponse byte = 0xE1
29+
MsgTypeMetricsRequest byte = 0xE2
30+
MsgTypeMetricsResponse byte = 0xE3
31+
MsgTypeRegistration byte = 0x03
32+
MsgTypeRegistrationAck byte = 0x04
3333
)
3434

3535
// V2Message represents a v2 protocol message.

0 commit comments

Comments
 (0)