-
Notifications
You must be signed in to change notification settings - Fork 14
feat(sandbox): official Kit sandbox image + GHCR workflow #78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 1 commit
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| name: Sandbox Image | ||
|
|
||
| # Build the official Kit sandbox image and publish it to GHCR. The image is a | ||
| # ready-to-run Linux userland (Go + kit + gh/glab/tea + git/ssh) intended to be | ||
| # registered as a workdir.dev custom image: | ||
| # | ||
| # POST /v1/images | ||
| # { "source": { "type": "oci", | ||
| # "image_ref": "ghcr.io/mark3labs/kit-sandbox:latest" }, | ||
| # "name": "custom/mark3labs/kit-sandbox" } | ||
| # | ||
| # Then create sandboxes with { "image": "custom/mark3labs/kit-sandbox", ... }. | ||
|
|
||
| on: | ||
| push: | ||
| branches: [master] | ||
| paths: | ||
| - "deploy/sandbox/**" | ||
| - ".github/workflows/sandbox-image.yml" | ||
| # Rebuild on every release so the image tracks the latest kit binary. | ||
| release: | ||
| types: [published] | ||
| workflow_dispatch: | ||
| inputs: | ||
| kit_version: | ||
| description: "kit version to install (go install ...@VERSION)" | ||
| required: false | ||
| default: "latest" | ||
|
|
||
| concurrency: | ||
| group: sandbox-image-${{ github.ref }} | ||
| cancel-in-progress: true | ||
|
|
||
| env: | ||
| REGISTRY: ghcr.io | ||
| IMAGE_NAME: ${{ github.repository }}-sandbox # ghcr.io/mark3labs/kit-sandbox | ||
|
|
||
| jobs: | ||
| build: | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| packages: write | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Resolve kit version | ||
| id: kitver | ||
| run: | | ||
| if [ "${{ github.event_name }}" = "release" ]; then | ||
| echo "version=${{ github.event.release.tag_name }}" >> "$GITHUB_OUTPUT" | ||
| elif [ -n "${{ inputs.kit_version }}" ]; then | ||
| echo "version=${{ inputs.kit_version }}" >> "$GITHUB_OUTPUT" | ||
| else | ||
| echo "version=latest" >> "$GITHUB_OUTPUT" | ||
| fi | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| - uses: docker/setup-buildx-action@v3 | ||
|
|
||
| - name: Log in to GHCR | ||
| uses: docker/login-action@v3 | ||
| with: | ||
| registry: ${{ env.REGISTRY }} | ||
| username: ${{ github.actor }} | ||
| password: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Docker metadata (tags + labels) | ||
| id: meta | ||
| uses: docker/metadata-action@v5 | ||
| with: | ||
| images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} | ||
| tags: | | ||
| type=raw,value=latest,enable={{is_default_branch}} | ||
| type=ref,event=branch | ||
| type=sha,format=short | ||
| type=semver,pattern={{version}},event=release | ||
| type=semver,pattern={{major}}.{{minor}},event=release | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| - name: Build and push | ||
| uses: docker/build-push-action@v6 | ||
| with: | ||
| context: . | ||
| file: deploy/sandbox/Dockerfile | ||
| # workdir runs x86_64 Firecracker microVMs. | ||
| platforms: linux/amd64 | ||
| push: true | ||
| tags: ${{ steps.meta.outputs.tags }} | ||
| labels: ${{ steps.meta.outputs.labels }} | ||
| build-args: | | ||
| KIT_VERSION=${{ steps.kitver.outputs.version }} | ||
| cache-from: type=gha | ||
| cache-to: type=gha,mode=max | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,100 @@ | ||
| # syntax=docker/dockerfile:1 | ||
| # | ||
| # Official Kit sandbox image. | ||
| # | ||
| # A ready-to-run Linux userland for the Kit coding agent, designed to be | ||
| # registered as a workdir.dev custom image (`POST /v1/images` with | ||
| # `source.type = "oci"`). It pre-bakes the toolchain that the dev-sandbox | ||
| # integration otherwise installs at runtime on every boot: | ||
| # | ||
| # * Go (compiler + toolchain) and the Kit CLI (`kit`) | ||
| # * The three git-forge CLIs: gh (GitHub), glab (GitLab), tea (Gitea) | ||
| # * git + openssh-client for SSH-based clones | ||
| # * the `127.0.0.1 localhost` /etc/hosts entry Kit's OAuth listener needs | ||
| # | ||
| # The base apt layer mirrors workdir's curated base image | ||
| # (deploy/images/base/Dockerfile in mv37-org/workdir). We deliberately do NOT | ||
| # COPY in workdir's `sandbox-guest-agent` / `sandbox-init`: workdir's custom | ||
| # image builder injects the (static musl) guest agent + init itself when it | ||
| # converts this OCI image to an ext4 rootfs. | ||
| # | ||
| # Build (multi-arch capable; workdir runs x86_64 Firecracker): | ||
| # docker buildx build --platform linux/amd64 -f deploy/sandbox/Dockerfile -t kit-sandbox . | ||
| # | ||
| FROM ubuntu:24.04 | ||
|
|
||
| ENV DEBIAN_FRONTEND=noninteractive | ||
|
|
||
| # Pinned tool versions — override at build time with --build-arg. | ||
| ARG GO_VERSION=1.26.4 | ||
| ARG KIT_VERSION=latest | ||
| ARG GH_VERSION=2.95.0 | ||
| ARG GLAB_VERSION=1.105.0 | ||
| ARG TEA_VERSION=0.14.1 | ||
|
|
||
| # Resolved automatically by buildx (linux/amd64 -> amd64, linux/arm64 -> arm64). | ||
| ARG TARGETARCH=amd64 | ||
|
|
||
| # Base userland — mirrors workdir's curated base image apt layer, plus | ||
| # openssh-client (SSH clones) and unzip/jq/sudo conveniences. | ||
| RUN apt-get update && apt-get install -y --no-install-recommends \ | ||
| bash coreutils curl wget ca-certificates git openssh-client \ | ||
| socat iproute2 iputils-ping unzip jq sudo \ | ||
| python3 python3-pip python3-venv nodejs npm build-essential \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| # Go toolchain -> /usr/local/go, on PATH for all later layers and at runtime. | ||
| RUN curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${TARGETARCH}.tar.gz" \ | ||
| -o /tmp/go.tgz \ | ||
| && tar -C /usr/local -xzf /tmp/go.tgz \ | ||
| && rm /tmp/go.tgz | ||
| ENV PATH=/usr/local/go/bin:/usr/local/bin:/root/go/bin:$PATH | ||
| ENV GOPATH=/root/go | ||
| ENV GOTOOLCHAIN=auto | ||
|
|
||
| # Kit CLI — compiled from source and installed onto PATH as `kit`. Inject the | ||
| # version ldflag (matching .goreleaser.yaml) so `kit --version` reports the | ||
| # build instead of "dev"; on release builds KIT_VERSION is the real tag. | ||
| RUN GOBIN=/usr/local/bin go install \ | ||
| -ldflags "-s -w -X main.version=${KIT_VERSION}" \ | ||
| "github.com/mark3labs/kit/cmd/kit@${KIT_VERSION}" \ | ||
| && kit --version || true | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| # GitHub CLI (gh). | ||
| RUN curl -fsSL "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_${TARGETARCH}.tar.gz" \ | ||
| -o /tmp/gh.tgz \ | ||
| && tar -C /tmp -xzf /tmp/gh.tgz \ | ||
| && install -m755 "/tmp/gh_${GH_VERSION}_linux_${TARGETARCH}/bin/gh" /usr/local/bin/gh \ | ||
| && rm -rf /tmp/gh.tgz "/tmp/gh_${GH_VERSION}_linux_${TARGETARCH}" | ||
|
|
||
| # GitLab CLI (glab). | ||
| RUN curl -fsSL "https://gitlab.com/gitlab-org/cli/-/releases/v${GLAB_VERSION}/downloads/glab_${GLAB_VERSION}_linux_${TARGETARCH}.tar.gz" \ | ||
| -o /tmp/glab.tgz \ | ||
| && mkdir -p /tmp/glab \ | ||
| && tar -C /tmp/glab -xzf /tmp/glab.tgz \ | ||
| && install -m755 /tmp/glab/bin/glab /usr/local/bin/glab \ | ||
| && rm -rf /tmp/glab.tgz /tmp/glab | ||
|
|
||
| # Gitea CLI (tea) — single static binary. | ||
| RUN curl -fsSL "https://dl.gitea.com/tea/${TEA_VERSION}/tea-${TEA_VERSION}-linux-${TARGETARCH}" \ | ||
| -o /usr/local/bin/tea \ | ||
| && chmod +x /usr/local/bin/tea | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
|
|
||
| # Kit's OAuth callback listener binds localhost; without this entry it fails to | ||
| # create the listener and nil-panics on cleanup. Bake it in so the runtime | ||
| # doesn't have to patch /etc/hosts on every boot. | ||
| RUN grep -q localhost /etc/hosts || echo '127.0.0.1 localhost' >> /etc/hosts | ||
|
|
||
| # Default workspace the agent clones into. | ||
| RUN mkdir -p /workspace | ||
| WORKDIR /workspace | ||
|
|
||
| # Smoke-check that every CLI resolves on PATH at build time. | ||
| RUN set -e; \ | ||
| go version; \ | ||
| kit --version || true; \ | ||
| gh --version; \ | ||
| glab --version; \ | ||
| tea --version | ||
|
|
||
| CMD ["/bin/bash"] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| # Kit sandbox image | ||
|
|
||
| An official, pre-baked Linux image for running the [Kit](https://github.com/mark3labs/kit) | ||
| coding agent inside a [workdir.dev](https://workdir.dev) sandbox. | ||
|
|
||
| It exists so the dev-sandbox integration no longer has to `apt-get install` and | ||
| `go install` the toolchain on **every** sandbox boot — everything is baked in. | ||
|
|
||
| ## What's inside | ||
|
|
||
| Based on `ubuntu:24.04`, mirroring workdir's curated base apt layer, plus: | ||
|
|
||
| | Tool | Purpose | | ||
| | --- | --- | | ||
| | **Go** (`/usr/local/go`) | toolchain for building/running Go code | | ||
| | **kit** | the Kit coding agent CLI (`go install github.com/mark3labs/kit/cmd/kit`) | | ||
| | **gh** | GitHub CLI — open PRs, manage repos | | ||
| | **glab** | GitLab CLI — open MRs | | ||
| | **tea** | Gitea CLI | | ||
| | **git**, **openssh-client** | SSH-based clones | | ||
| | python3, node/npm, build-essential, jq, curl, … | general dev userland | | ||
|
|
||
| It also bakes the `127.0.0.1 localhost` `/etc/hosts` entry Kit's OAuth listener | ||
| requires, and creates `/workspace`. | ||
|
|
||
| > **Note:** this image intentionally does **not** ship workdir's | ||
| > `sandbox-guest-agent` / `sandbox-init`. workdir's custom-image builder injects | ||
| > the (static musl) guest agent and init when it converts this OCI image to an | ||
| > ext4 rootfs. | ||
|
|
||
| ## Build locally | ||
|
|
||
| ```bash | ||
| # from the repo root | ||
| docker buildx build --platform linux/amd64 \ | ||
| -f deploy/sandbox/Dockerfile \ | ||
| -t kit-sandbox . | ||
| ``` | ||
|
|
||
| Override pinned versions with `--build-arg` (`GO_VERSION`, `KIT_VERSION`, | ||
| `GH_VERSION`, `GLAB_VERSION`, `TEA_VERSION`). | ||
|
|
||
| ## CI / publishing | ||
|
|
||
| `.github/workflows/sandbox-image.yml` builds the image and pushes it to GHCR | ||
| (`ghcr.io/mark3labs/kit-sandbox`) on: | ||
|
|
||
| - pushes to `master` touching `deploy/sandbox/**`, | ||
| - published releases (the image is rebuilt against the released `kit` tag), | ||
| - manual `workflow_dispatch` (optionally pinning `kit_version`). | ||
|
|
||
| Tags published: `latest` (default branch), `sha-<short>`, branch name, and | ||
| `vX.Y.Z` / `vX.Y` on releases. | ||
|
|
||
| ## Register it as a workdir custom image | ||
|
|
||
| Once published, register the OCI image with workdir | ||
| ([API reference](https://github.com/mv37-org/workdir/blob/main/docs/API.md#images-spec-10-11)): | ||
|
|
||
| ```bash | ||
| curl -fsSL -X POST https://api.workdir.dev/v1/images \ | ||
| -H "Authorization: Bearer $WORKDIR_API_KEY" \ | ||
| -H "Content-Type: application/json" \ | ||
| -d '{ | ||
| "source": { "type": "oci", | ||
| "image_ref": "ghcr.io/mark3labs/kit-sandbox:latest" }, | ||
| "name": "custom/mark3labs/kit-sandbox", | ||
| "resources_hint": { "cpu": 2, "memory_mb": 4096, "disk_gb": 16 } | ||
| }' | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ``` | ||
|
|
||
| The build is asynchronous (`202 Accepted`); poll `GET /v1/images/:id` for status | ||
| and `build_log`. Then create sandboxes against it: | ||
|
|
||
| ```jsonc | ||
| POST /v1/sandboxes | ||
| { "image": "custom/mark3labs/kit-sandbox", | ||
| "image_version": "2026-06-10-ab12cd" } | ||
| ``` | ||
|
|
||
| > If the GHCR package is private, grant workdir's builder pull access (make the | ||
| > package public, or configure registry credentials on the workdir side). | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.