Skip to content

Commit 99e827c

Browse files
koki-developclaude
andcommitted
refactor: introduce cobra CLI and externalize sandbox configuration
Replace the hardcoded server entrypoint with a cobra-based CLI exposing a `serve` subcommand with `--addr`, `--timeout`, and `--output-limit` flags. Extract sandbox configuration into an explicit Config struct passed through the call chain instead of relying on package-level init() and constants. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent afc51b2 commit 99e827c

12 files changed

Lines changed: 123 additions & 58 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
with:
3737
persist-credentials: false
3838
- uses: jdx/mise-action@6d1e696aa24c1aa1bcc1adea0212707c71ab78a8 # v3.6.1
39-
- run: go build -o server ./cmd/server
39+
- run: go build -o sandbox .
4040

4141
unit-test:
4242
name: Unit Test

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
/server
1+
/sandbox

Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@ COPY . .
3636
RUN CGO_ENABLED=0 go build \
3737
-trimpath \
3838
-ldflags="-w -s" \
39-
-o /out/server \
40-
./cmd/server
39+
-o /out/sandbox \
40+
.
4141

4242
# ---
4343

4444
FROM base
4545

46-
COPY --from=builder /out/server /usr/local/bin/server
46+
COPY --from=builder /out/sandbox /usr/local/bin/sandbox
4747
EXPOSE 8080
48-
ENTRYPOINT ["/usr/local/bin/server"]
48+
ENTRYPOINT ["/usr/local/bin/sandbox", "serve"]

cmd/root.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package cmd
2+
3+
import (
4+
"os"
5+
6+
"github.com/spf13/cobra"
7+
)
8+
9+
var rootCmd = &cobra.Command{
10+
Use: "sandbox",
11+
}
12+
13+
func Execute() {
14+
if err := rootCmd.Execute(); err != nil {
15+
os.Exit(1)
16+
}
17+
}

cmd/serve.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package cmd
2+
3+
import (
4+
"time"
5+
6+
"github.com/codize-dev/sandbox/internal/handler"
7+
"github.com/codize-dev/sandbox/internal/sandbox"
8+
"github.com/labstack/echo/v5"
9+
"github.com/labstack/echo/v5/middleware"
10+
"github.com/spf13/cobra"
11+
)
12+
13+
var serveCmd = &cobra.Command{
14+
Use: "serve",
15+
RunE: runServe,
16+
}
17+
18+
func init() {
19+
rootCmd.AddCommand(serveCmd)
20+
21+
serveCmd.Flags().String("addr", ":8080", "TCP address to listen on")
22+
serveCmd.Flags().Int("timeout", 30, "sandbox run timeout in seconds")
23+
serveCmd.Flags().Int("output-limit", 1<<20, "maximum combined output bytes")
24+
}
25+
26+
func runServe(cmd *cobra.Command, _ []string) error {
27+
addr, err := cmd.Flags().GetString("addr")
28+
if err != nil {
29+
return err
30+
}
31+
32+
timeout, err := cmd.Flags().GetInt("timeout")
33+
if err != nil {
34+
return err
35+
}
36+
37+
outputLimit, err := cmd.Flags().GetInt("output-limit")
38+
if err != nil {
39+
return err
40+
}
41+
42+
cfg := sandbox.Config{
43+
RunTimeout: timeout,
44+
ExecTimeout: time.Duration(timeout+10) * time.Second,
45+
OutputLimit: outputLimit,
46+
}
47+
48+
h := &handler.Handler{Config: cfg}
49+
50+
e := echo.New()
51+
e.Use(middleware.RequestLogger())
52+
e.POST("/v1/run", h.RunHandler)
53+
54+
if err := e.Start(addr); err != nil {
55+
e.Logger.Error("failed to start server", "error", err)
56+
}
57+
return nil
58+
}

cmd/server/main.go

Lines changed: 0 additions & 18 deletions
This file was deleted.

compose.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ services:
44
context: .
55
dockerfile: Dockerfile
66
privileged: true
7-
environment:
8-
- SANDBOX_RUN_TIMEOUT=5
7+
command: ["serve", "--timeout", "5"]
98
ports:
109
- "8080:8080"

go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ require (
1111

1212
require (
1313
github.com/davecgh/go-spew v1.1.1 // indirect
14+
github.com/inconshreveable/mousetrap v1.1.0 // indirect
1415
github.com/pmezard/go-difflib v1.0.0 // indirect
16+
github.com/spf13/cobra v1.10.2 // indirect
17+
github.com/spf13/pflag v1.0.9 // indirect
1518
golang.org/x/time v0.14.0 // indirect
1619
gopkg.in/yaml.v3 v3.0.1 // indirect
1720
)

go.sum

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,20 @@
1+
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
12
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
23
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4+
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
5+
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
36
github.com/labstack/echo/v5 v5.0.4 h1:ll3I/O8BifjMztj9dD1vx/peZQv8cR2CTUdQK6QxGGc=
47
github.com/labstack/echo/v5 v5.0.4/go.mod h1:SyvlSdObGjRXeQfCCXW/sybkZdOOQZBmpKF0bvALaeo=
58
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
69
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
10+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
11+
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
12+
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
13+
github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY=
14+
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
715
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
816
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
17+
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
918
go.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=
1019
go.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
1120
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=

internal/handler/handler.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ type RunResponse struct {
2828
Run sandbox.Result `json:"run"`
2929
}
3030

31-
func RunHandler(c *echo.Context) error {
31+
// Handler holds dependencies for the HTTP handler.
32+
type Handler struct {
33+
Config sandbox.Config
34+
}
35+
36+
func (h *Handler) RunHandler(c *echo.Context) error {
3237
var req RunRequest
3338
if err := c.Bind(&req); err != nil {
3439
return c.JSON(http.StatusBadRequest, map[string]string{
@@ -73,7 +78,7 @@ func RunHandler(c *echo.Context) error {
7378
}
7479
}
7580

76-
result, err := sandbox.Run(c.Request().Context(), rt, tmpDir, req.Files[0].Name)
81+
result, err := sandbox.Run(c.Request().Context(), h.Config, rt, tmpDir, req.Files[0].Name)
7782
if err != nil {
7883
if errors.Is(err, context.DeadlineExceeded) {
7984
return c.JSON(http.StatusGatewayTimeout, map[string]string{

0 commit comments

Comments
 (0)