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
4 changes: 3 additions & 1 deletion .github/workflows/build-master.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ on:

jobs:
meta:
runs-on: ubuntu-latest
runs-on:
- self-hosted-ghr
- size-m-x64
outputs:
short-sha: ${{ steps.sha.outputs.short }}
steps:
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/build-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ on:
jobs:
meta:
if: contains(github.event.pull_request.labels.*.name, 'build') && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
runs-on:
- self-hosted-ghr
- size-m-x64
outputs:
short-sha: ${{ steps.sha.outputs.short }}
steps:
Expand All @@ -31,7 +33,9 @@ jobs:
comment:
needs: [meta, build]
if: contains(github.event.pull_request.labels.*.name, 'build') && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
runs-on:
- self-hosted-ghr
- size-m-x64
steps:
- name: Comment PR with image tags
uses: actions/github-script@v7
Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ concurrency:

jobs:
test:
runs-on: ubuntu-latest
runs-on:
- self-hosted-ghr
- size-m-x64
steps:
- uses: actions/checkout@v4

Expand All @@ -45,7 +47,9 @@ jobs:
run: make test

lint:
runs-on: ubuntu-latest
runs-on:
- self-hosted-ghr
- size-m-x64
steps:
- uses: actions/checkout@v4

Expand Down
53 changes: 51 additions & 2 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ concurrency:

jobs:
goreleaser-check:
runs-on: ubuntu-latest
runs-on:
- self-hosted-ghr
- size-l-x64
steps:
- uses: actions/checkout@v4
with:
Expand All @@ -51,8 +53,55 @@ jobs:
env:
LATEST_TAG: latest

- name: Build server and sandbox images
run: |
mkdir -p linux/amd64
cp dist/panda-server_linux_amd64_v1/panda-server linux/amd64/panda-server
docker build -f goreleaser.server.Dockerfile -t panda-server-smoke .
docker build -f sandbox/Dockerfile -t panda-sandbox-smoke .

- name: Smoke test server sandbox initialization
run: |
# Write a minimal config that the server can parse.
# The server will initialize the sandbox (Docker access, image pull,
# network creation) before attempting to connect to the proxy.
cat > /tmp/smoke-config.yaml <<'CONFIG'
server:
host: "0.0.0.0"
port: 2480
transport: sse
sandbox:
image: "panda-sandbox-smoke:latest"
network: "panda-smoke-test"
proxy:
url: "http://localhost:19999"
CONFIG

# Run the server and capture logs. It will fail at proxy connection,
# but we check that sandbox initialization completed successfully.
LOGS=$(docker run --rm \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/smoke-config.yaml:/app/config.yaml:ro \
--group-add "$(stat -c '%g' /var/run/docker.sock)" \
panda-server-smoke \
serve --config /app/config.yaml 2>&1 || true)

echo "$LOGS"

if echo "$LOGS" | grep -q "Sandbox service started"; then
echo "Smoke test passed: sandbox initialized successfully"
else
echo "Smoke test failed: sandbox did not initialize"
exit 1
fi

# Clean up the network created by the server.
docker network rm panda-smoke-test 2>/dev/null || true

build-sandbox:
runs-on: ubuntu-latest
runs-on:
- self-hosted-ghr
- size-m-x64
steps:
- uses: actions/checkout@v4

Expand Down
8 changes: 6 additions & 2 deletions .github/workflows/eval.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@ on:

jobs:
evaluate:
runs-on: ubuntu-latest
runs-on:
- self-hosted-ghr
- size-m-x64
timeout-minutes: 60

steps:
Expand Down Expand Up @@ -224,7 +226,9 @@ jobs:
comment_mode: always

compare-models:
runs-on: ubuntu-latest
runs-on:
- self-hosted-ghr
- size-m-x64
needs: evaluate
if: github.event_name == 'workflow_dispatch' && inputs.model == 'claude-sonnet-4-5'

Expand Down
2 changes: 1 addition & 1 deletion goreleaser.server.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ RUN mkdir -p /model/all-MiniLM-L6-v2 && \
# =============================================================================
FROM alpine:3.21

RUN apk add --no-cache ca-certificates tzdata
RUN apk add --no-cache ca-certificates tzdata docker-cli

RUN addgroup -g 1000 panda && \
adduser -u 1000 -G panda -D panda
Expand Down
28 changes: 27 additions & 1 deletion pkg/cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"os"
"os/exec"
"path/filepath"
"strconv"
"syscall"
"time"

dockerimage "github.com/docker/docker/api/types/image"
Expand Down Expand Up @@ -197,6 +199,8 @@ proxy:
}

func buildComposeTemplate(serverImage, configDir string) string {
dockerGID := detectDockerSocketGID()

return fmt.Sprintf(`# panda server - Docker Compose configuration
# Generated by 'panda init'. Managed by 'panda server' commands.

Expand All @@ -205,6 +209,8 @@ services:
image: %s
container_name: panda-server
restart: unless-stopped
group_add:
- "%s"
ports:
- "127.0.0.1:2480:2480"
volumes:
Expand All @@ -223,7 +229,7 @@ networks:

volumes:
panda-storage:
`, serverImage, configDir)
`, serverImage, dockerGID, configDir)
}

func checkDockerAndPullImages() error {
Expand Down Expand Up @@ -298,3 +304,23 @@ func pullImage(cli *dockerclient.Client, image string) error {

return nil
}

// detectDockerSocketGID returns the group ID that owns /var/run/docker.sock.
// This is used to add the correct group to the server container so the
// non-root panda user can access the Docker socket. Falls back to "0" (root)
// if the socket cannot be stat'd.
func detectDockerSocketGID() string {
const dockerSocket = "/var/run/docker.sock"

info, err := os.Stat(dockerSocket)
if err != nil {
return "0"
}

stat, ok := info.Sys().(*syscall.Stat_t)
if !ok {
return "0"
}

return strconv.FormatUint(uint64(stat.Gid), 10)
}
Loading