Skip to content

crunchloop/devcontainer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

devcontainer

CI Status: alpha Go Reference License: Apache 2.0

A programmatic Go runtime for Dev Containers. Embed the full devcontainer lifecycle — resolve, build, up, exec, lifecycle phases, down — into your Go application without shelling out to the Node @devcontainers/cli.

Why

The reference @devcontainers/cli is a Node binary. Embedding it in a Go service means a Node runtime dependency, opaque failure modes (success exit codes with outcome:error JSON on stdout), and CLI-flag-shaped APIs for every interaction. This library is a clean Go implementation of the spec's embedding-relevant subset, designed to be a drop-in replacement for shelling out.

Status

Alpha. API is stable enough for early integration but may change between minor versions until v1.0.0. The events channel surface is explicitly experimental. See PRD.md for scope and roadmap.

Source kind Works? Notes
image Pull, run, exec, lifecycle
build (Dockerfile) User Dockerfile + features layered atop
dockerComposeFile compose-go for parse, shell-out to docker compose for orchestration
Features (OCI / HTTPS / local) DAG ordering, options validation, content-addressed cache
Pre-baked image (skip-already-installed) devcontainer.metadata label read on Up
Lifecycle phases (onCreatepostAttach) Per-phase idempotency markers in container
${...} substitution Host context at Resolve, container env post-create
customizations.<tool> pass-through map[string]json.RawMessage for callers to decode

Out of scope for v1: Templates spec, dotfiles repos, IDE/SSH injection hooks, Kubernetes / podman drivers, forwardPorts actuation. See design/ for the full non-goals list.

Install

go get github.com/crunchloop/devcontainer

Requires:

  • Go 1.25+
  • Docker daemon socket reachable
  • Docker Compose v2 plugin (only for dockerComposeFile source)

Quick start

package main

import (
	"context"
	"fmt"
	"log"

	devcontainer "github.com/crunchloop/devcontainer"
	"github.com/crunchloop/devcontainer/runtime/docker"
)

func main() {
	ctx := context.Background()

	rt, err := docker.New(ctx, docker.Options{})
	if err != nil {
		log.Fatalf("docker: %v", err)
	}
	defer rt.Close()

	eng, err := devcontainer.New(devcontainer.EngineOptions{Runtime: rt})
	if err != nil {
		log.Fatalf("engine: %v", err)
	}

	ws, err := eng.Up(ctx, devcontainer.UpOptions{
		LocalWorkspaceFolder: "/path/to/your/project",
	})
	if err != nil {
		log.Fatalf("up: %v", err)
	}
	defer eng.Down(ctx, ws, devcontainer.DownOptions{Remove: true})

	res, err := eng.Exec(ctx, ws, devcontainer.ExecOptions{
		Cmd: []string{"sh", "-c", "echo $USER"},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("user:", res.Stdout)
}

Runnable end-to-end examples in examples/:

API surface

The main entry points live in the root package:

type Engine struct { /* ... */ }

func New(opts EngineOptions) (*Engine, error)
func Resolve(ctx context.Context, opts ResolveOptions) (*ResolvedConfig, error)

func (*Engine) Up(ctx, UpOptions) (*Workspace, error)
func (*Engine) Attach(ctx, WorkspaceID) (*Workspace, error)
func (*Engine) Exec(ctx, *Workspace, ExecOptions) (ExecResult, error)
func (*Engine) ExecByID(ctx, WorkspaceID, ExecOptions) (ExecResult, error)
func (*Engine) RunLifecycle(ctx, *Workspace, LifecyclePhase) error
func (*Engine) Down(ctx, *Workspace, DownOptions) error

Sub-packages:

  • config — devcontainer.json parsing, merging, host-context substitution
  • runtime — container backend abstraction (Runtime, ComposeRuntime)
  • runtime/docker — Docker Engine API implementation (uses moby/moby/client)
  • feature — feature resolution (OCI / HTTPS / local), DAG ordering, dockerfile generation
  • composedockerComposeFile parsing via compose-spec/compose-go, override-file generation

Tests

make test              # unit tests (~140 cases, ~3s)
make test-integration  # integration tests against real Docker (~30s)
make lint              # golangci-lint

The integration suite (build tag integration) exercises real Docker: pulls public images from GHCR, builds Dockerfiles, runs feature install scripts, drives docker compose up/down. Skipped automatically if a Docker daemon isn't reachable.

Design docs

PRD and design docs are kept in design/ (private during incubation). They will move to docs/ and become public alongside the v0.1.0 tag.

Contributing

See CONTRIBUTING.md. Bug reports welcome via GitHub issues.

License

Apache License 2.0.

About

A programmatic Go runtime for Dev Containers

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors