Skip to content

feat: add devcontainer feature and template for Haskell toolchain - #216

Draft
develop7 wants to merge 1 commit into
haskell:masterfrom
develop7:feat/devcontainer
Draft

feat: add devcontainer feature and template for Haskell toolchain#216
develop7 wants to merge 1 commit into
haskell:masterfrom
develop7:feat/devcontainer

Conversation

@develop7

@develop7 develop7 commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Closes #123.

Summary

Adds an official devcontainer feature (haskell-toolchain) and template (haskell) to the repo, replacing the archived microsoft/vscode-dev-containers Haskell artifact.

Feature: haskell-toolchain v1.0.0

Installs the Haskell toolchain via ghcup on any Debian/Ubuntu base image:

Tool Version
GHC 9.14.1 (LTS)
cabal-install 3.16.1.0
Stack 3.11.1
HLS 2.14.0.0
ghcup 0.2.6.2
ormolu 0.8.2.0
fourmolu 0.20.0.0

8 options: version, installStack, installHLS, installStackGHCupHook, adjustBash, installLinters, globalPackages, globalLibraries.

Design choices

  • Version pinning via Nickel: versions.ncl imports from 9.14/bookworm.ncl and 9.14/_globals.ncl — the same data files that generate the Docker images. Single source of truth.
  • Non-root install: mirrors devcontainers-extra/features/src/haskellsudo -iu $_REMOTE_USER with GHCUP_USE_XDG_DIRS=1; root fallback when _REMOTE_USER is unset.
  • install.sh is generated from install.sh.tpl.ncl via Nickel (same pattern as the Dockerfiles).
  • Multistage Dockerfile.test for cached local testing with podman.

Why only ormolu and fourmolu?

hlint, stylish-haskell, and cabal-fmt constrain ghc < 9.13 or base < 4.22, making them incompatible with GHC 9.14.1 (base 4.22.0.0). They depend on ghc-lib-parser 9.12.x, which has not been updated for GHC 9.14. Users can install them via globalPackages on older GHC versions. See the feature README for details.

No Alpine in v1.0.0

Alpine support is tracked by #22 and will land in lockstep with the Alpine base images.

Template: haskell

Clone-and-go cabal helloworld project:

  • Base: mcr.microsoft.com/devcontainers/base:debian-12
  • VS Code extensions: haskell.haskell, haskell-lang-server
  • Format-on-save with fourmolu
  • postCreateCommand: cabal update && cabal build

CI

Workflow Trigger Purpose
feature-test.yml PR + push 4-job matrix (debian/ubuntu × amd64/arm64) + shellcheck
feature-publish.yml tag feature-v* OCI publish to ghcr.io/haskell/docker-haskell/devcontainer-features/haskell-toolchain
template-lint.yml PR Validate template metadata
feature-versions-sync.yml weekly cron Check for version drift, open issue on staleness

Build layers are cached via GitHub Actions cache (type=gha), so changes to test.sh or the workflow do not rebuild the toolchain.

How to verify (as a devcontainers user)

Option A: Use the template

# Clone this PR branch
git clone --branch feat/devcontainer https://github.com/develop7/docker-haskell.git
cd docker-haskell/devcontainer-template/src/haskell

# Open in VS Code with the Dev Containers extension
# (or use `devcontainer up --workspace-folder .`)

The container builds, installs the toolchain via ghcup, runs cabal update && cabal build, and you should have a working Haskell environment with GHC 9.14.1, HLS, ormolu, and fourmolu.

Option B: Use the feature in your own project

Create .devcontainer/devcontainer.json in any repo:

{
  "image": "mcr.microsoft.com/devcontainers/base:debian-12",
  "features": {
    "ghcr.io/haskell/docker-haskell/devcontainer-features/haskell-toolchain:1.0.0": {}
  }
}

Then devcontainer up --workspace-folder . or "Reopen in Container" in VS Code. Verify:

ghc --version          # → 9.14.1
cabal --version        # → 3.16.1.0
stack --version        # → 3.11.1
haskell-language-server-wrapper --version  # → 2.14.0.0
ormolu --version       # → 0.8.2.0
fourmolu --version     # → 0.20.0.0

Option C: Local Docker/podman test

git clone --branch feat/devcontainer https://github.com/develop7/docker-haskell.git
cd docker-haskell

# Build and test in one shot
podman build -t haskell-feature-test \
  -f devcontainer-feature/src/haskell-toolchain/Dockerfile.test \
  devcontainer-feature

# Or with a different base image
podman build --build-arg BASE_IMAGE=ubuntu:24.04 -t haskell-feature-ubuntu \
  -f devcontainer-feature/src/haskell-toolchain/Dockerfile.test \
  devcontainer-feature

Test plan (internal)

  • nickel export renders install.sh cleanly (no errors, correct substitutions)
  • shellcheck -x passes on all shell scripts (zero warnings)
  • devcontainer features test passes (_default scenario on mcr.microsoft.com/devcontainers/base:debian-12)
  • podman build with multistage Dockerfile.test — all tools verified:
    • ghc --version → 9.14.1
    • cabal --version → 3.16.1.0
    • stack --version → 3.11.1
    • haskell-language-server-wrapper --version → 2.14.0.0
    • ormolu --version → 0.8.2.0 (ghc-lib-parser 9.14.1)
    • fourmolu --version → 0.20.0.0 (ghc-lib-parser 9.14.1)
  • Root path (no _REMOTE_USER) — tools install under /root, test.sh detects and passes
  • Non-root path (_REMOTE_USER=vscode) — tools install under /home/vscode, test.sh detects and passes
  • CI: debian:bookworm × {amd64,arm64} + ubuntu:24.04 × {amd64,arm64} all pass
  • CI: shellcheck job passes
  • CI: lint (template metadata validation) passes
  • CI: generate (Dockerfile generator) passes
  • Reviewer: verify versions.ncl import paths resolve from devcontainer-feature/src/haskell-toolchain/ to 9.14/bookworm.ncl
  • Reviewer: verify devcontainer-template/src/haskell/helloworld.cabal base ^>=4.22 matches GHC 9.14.1
  • Reviewer: spot-check install.sh.tpl.ncl heredoc variable expansion (outer shell expands pinned versions, \$-escapes protect inner shell vars)

@develop7
develop7 marked this pull request as draft July 31, 2026 07:57
@develop7
develop7 force-pushed the feat/devcontainer branch 6 times, most recently from c0d6484 to 07d9519 Compare July 31, 2026 12:23
Add an official devcontainer feature (`haskell-toolchain`) and template
(`haskell`) to close haskell#123.

Feature:
- Installs GHC 9.14.1, cabal 3.16.1.0, Stack 3.11.1, HLS 2.14.0.0 via ghcup
- Curated formatters: ormolu 0.8.2.0, fourmolu 0.20.0.0 (the only linters
  compatible with GHC 9.14/base 4.22; hlint, stylish-haskell, cabal-fmt
  are documented as incompatible and available via globalPackages escape hatch)
- 8 options: version, installStack, installHLS, installStackGHCupHook,
  adjustBash, installLinters, globalPackages, globalLibraries
- Versions pinned in versions.ncl, which imports from 9.14/bookworm.ncl
  and 9.14/_globals.ncl — single source of truth shared with the Docker images
- Non-root install via sudo -iu _REMOTE_USER, mirroring devcontainers-extra
- install.sh rendered from install.sh.tpl.ncl via Nickel
- Multistage Dockerfile.test for cached local testing with podman
- 5 scenario tests + autogenerated test.sh

Template:
- Clone-and-go cabal helloworld project on debian-12 base
- VS Code extensions (haskell.haskell, haskell-lang-server)
- Format-on-save with fourmolu

CI:
- feature-test.yml: 5-job matrix (debian/ubuntu × amd64/arm64)
- feature-publish.yml: tag-triggered OCI publish to ghcr.io
- template-lint.yml: PR validation
- feature-versions-sync.yml: weekly drift check
@develop7
develop7 force-pushed the feat/devcontainer branch from 07d9519 to 29b858e Compare July 31, 2026 21:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Official Devcontainer feature and template

1 participant