Skip to content

nalchevanidze/hwm

HWM: Haskell Workspace Manager

Infrastructure-as-Code for your Haskell Workspaces.

HWM is a build-tool orchestration layer for Haskell workspaces. It connects tools you already rely on (cabal, stack, nix, hls) behind a single declarative config, with some current capability gaps documented below.

Think of HWM as Terraform for your local Haskell repository. Whether you are a Nix purist, a Stack loyalist, or rely purely on Cabal, HWM ensures the state of your project files matches your declared intent across all environments.

HWM is an active workspace maintainer that provides:

  • The Universal Translator: Write one hwm.yaml. HWM automatically derives and generates cabal.project, stack.yaml, hie.yaml, flake.nix, and .cabal files.
  • Zero Lock-in: HWM materializes standard configuration files directly at your project root. You can uninstall HWM at any time, and your repository will still build perfectly using standard native tools.
  • Smart Bounds Synchronization: Maintain a beautifully aligned, single-source-of-truth dependency registry. HWM automatically injects these bounds across your entire monorepo.
  • IDE Config Generation: HWM generates hie.yaml at the project root unless ignored by target policy, selecting stack or cabal cradle shape based on the active environment builder.
  • Flexible sync target policy: Control sync behavior per target via environments.targets defaults and per-profile targets overrides.
  • Cabal-first source inclusion sync (cabal-only): HWM discovers modules from source dirs and updates .cabal module inclusion only when out of sync.

HWM Status Output

🧩 The Agnostic Architecture

HWM sits one layer above your toolchain. It separates your workspace intent from your build implementation.

graph TD
    HWM[hwm.yaml] ===>|Single Source of Truth| Engine((HWM Engine))

    subgraph "Native Configurations (Managed & Idempotent)"
    Engine --> Cabal[cabal.project & *.cabal]
    Engine --> Nix[flake.nix]
    Engine --> Stack[stack.yaml]
    Engine --> HLS[hie.yaml]
    end

    subgraph "Your Build Tools"
    Cabal -.-> RunCabal[cabal build]
    Nix -.-> RunNix[nix develop]
    Stack -.-> RunStack[stack build]
    end

    style HWM fill:#f9f,stroke:#333,stroke-width:4px

Loading

🟢 Quick Start

Installation

cabal install hwm

Zero-Config Onboarding

Convert any existing repository into an HWM workspace in seconds.

# 1. Generate hwm.yaml. HWM automatically discovers packages and infers dependencies.
hwm init

# 2. Sync configuration (Generates cabal.project, stack.yaml, flake.nix, hie.yaml)
hwm sync

# 3. View the visual dashboard of your workspace
hwm status

HWM Init Auto-Discovery

⚠️ Current Behavior Notes (Transparency)

  • Unknown command handling: hwm <token> will execute a script only if <token> exists in scripts; otherwise it returns a command-oriented CLI error. hwm run <script> remains the explicit script path.
  • Script execution model: hwm run executes scripts via the platform shell ($SHELL -c on Unix-like systems, COMSPEC /c on Windows) and appends extra args to the script command using shell-safe quoting.
  • IDE generation behavior: generated hie.yaml follows the active builder (stack cradle for stack environments, cabal cradle for cabal/nix/nix-cabal environments).
  • Generated files are ephemeral: hwm sync keeps generated files aligned with the active environment and builder.
    • Builder-driven defaults:
      • cabal -> cabal.project
      • stack -> stack.yaml
      • nix -> flake.nix
      • nix/cabal -> cabal.project + flake.nix
    • hie.yaml is generated by default; you can override via target policy.
    • Optional environments.targets defaults (and per-profile targets overrides) can adjust sync modes per target (sync, check, ignore).
    • packages: sync updates dependency bounds and cabal source inclusion for cabal-only packages.
    • Cabal source inclusion sync discovers .hs modules from component source dirs (library, exe, test, bench, foreign-lib, sublibs), preserves generated Paths_*, and skips rewrites when already in sync.
    • Packages with package.yaml remain on hpack-driven behavior (no direct cabal source inclusion rewrite).
    • check mode is no-write: cabal/stack/nix targets check presence, hie validates builder/component compatibility and reports errors when invalid, and packages runs validation-only checks (including source inclusion drift reporting for cabal packages).
  • Nix install gap (current): hwm install with nix/nix/cabal is not supported and fails with explicit errors.
  • Publish model: hwm release publish is intentionally Cabal sdist based (builder-independent, Hackage-oriented).
  • Artifacts environment policy: release.artifacts[*].environments is an allowlist. Omitted means all environments are allowed, [] means none are allowed, non-empty list means only listed environments are allowed.
  • Environment removal safety: removing the last environment is blocked; removing the current default requires hwm environments remove <ENV> --set-default <NEW_DEFAULT>.

✅ Current Support Matrix (by builder)

Capability cabal stack nix nix/cabal
hwm build / hwm test
hwm install
hwm release artifacts --builder=...
hwm release publish (Hackage) ✅* ✅* ✅* ✅*

* hwm release publish is builder-independent by design (Cabal sdist + Hackage upload flow).

🛠️ Key Workflows

1. The Global Registry & Dependency Sync

HWM uses a gorgeous, tabular dictionary to manage your dependencies. You define the bounds once in hwm.yaml, and HWM automatically injects them into every package in your monorepo.

registry:
  Cabal: ">= 3.8      && <= 3.16.1.0"
  aeson: ">= 1.5.6.0  && <= 2.2.3.0"
  mtl: ">  2.0.0    && <  2.6.0"

Audit & Fix: Audit your bounds against actual snapshots to ensure you only claim support for versions validated by your build matrix.

hwm registry audit --fix

HWM Audit Command

2. Smart Workspace Routing

Managing monorepos with dozens of packages is finally clean. HWM uses prefix grouping to elegantly decouple your internal structure from your globally unique Hackage package names.

# 1) Create a workspace group
hwm workspace add libs

# 2) Scaffold a package inside the group
hwm workspace add libs/core

HWM Workspace Add Command

workspace:
  libs:
    prefix: morpheus-graphql
    members:
      - core
      - client
      - server

When HWM generates your configs, it automatically builds the exact relative paths (morpheus-graphql-core), saving you from writing out bloated package names over and over.

3. Environments, Builders & CI Profiles

Bring the power of CI matrices directly into your local workspace. HWM allows you to define logical environments that map to specific GHC versions, toolchain toggles, and specific builders (stack, cabal, or nix).

Instead of writing complex bash routing in GitHub Actions or juggling multiple config files locally, you define your targets exactly once. HWM treats these files (stack.yaml, cabal.project, flake.nix, hie.yaml) as ephemeral generators—artifacts of your current profile.

environments:
  # Global defaults for the workspace
  builder: stack 
  default: stable
  # Optional sync target policy (global fallback)
  targets:
    cabal: sync
    stack: sync
    nix: sync
    hie: sync
    packages: sync

  profiles:
    legacy:
      ghc: 8.10.7
      targets:
        hie: ignore
      stack:
        extra-deps:
          base-orphans: 0.8.1
          fastsum: 0.1.0.0

    stable:
      ghc: 9.6.3
      builder: nix/cabal # Uses Nix to provide the environment and Cabal to build

    # Purpose-built CI profiles
    ci-windows:
      ghc: 9.6.3
      builder: cabal
      targets:
        nix: ignore # Explicitly disable flake generation for this profile
        packages: check
    
    ci-nix:
      ghc: 9.6.3
      builder: nix

    ci-mixed:
      ghc: 9.6.3
      builder: nix/cabal # Uses Nix to provide the environment and Cabal to build

Seamless CI Integration: By defining profiles like ci-nix and ci-windows, your GitHub Actions workflow becomes incredibly simple. You just tell HWM to sync the environment, and it instantly pivots the workspace to use the correct underlying toolchain.

hwm sync ci-mixed
hwm build # Executes via 'nix develop --command cabal build'

# On Ubuntu/macOS runners:
hwm sync ci-nix
hwm build # Executes 'nix build --no-link .#env-ciNix-all' where 'env-ciNix-all' is a synthetic package that depends on all packages in the workspace, for environment 'ci-nix' with ghc 9.6.3.

# On Windows runners:
hwm sync ci-windows
hwm build # Executes via 'cabal build'

Run Your Matrix Locally: Avoid "CI Ping-Pong" by running tests across all defined environments locally. HWM handles the context switching between builders (Stack vs. Cabal vs. Nix) automatically.

# Runs the test suite across every defined profile
hwm test --env=all

HWM Matrix Build Output

Manual Environment & IDE Switching: When you run hwm sync, HWM updates build files and validates hie.yaml; it rewrites hie.yaml when invalid/missing unless targets.hie: ignore is set.

# Instantly aligns generated config files for GHC 8.10
hwm sync legacy

# Removing default env requires migration
hwm environments remove stable --set-default legacy

You can control sync behavior via targets defaults and per-profile overrides:

environments:
  targets:
    stack: sync
    nix: sync
    hie: ignore
    packages: check

  profiles:
    ci:
      ghc: 9.6.3
      targets:
        stack: ignore

4. Task Runner & Scripts

HWM includes a lightweight, pass-through task runner. Define simple aliases for your most common workflows directly in hwm.yaml.

scripts:
  format: sh scripts/format.sh
  lint: hlint .
  test: hwm test --fast

Argument forwarding note:

hwm run <script> [ARGS...] appends extra args directly to the script command. Prefer hwm run <script> -- [ARGS...] when forwarding flags.

5. Release & Distribution

HWM introduces Release Trains, a high-integrity system for decoupling workspace structure from distribution strategy while ensuring topological correctness.

📦 Artifact Pipeline

Transform raw binaries into hashed, compressed distribution units using your preferred engine. HWM ensures every artifact is strictly validated before the publication phase begins.

NOTE:

  • hwm install always uses the current/default environment (no --env option).
  • hwm install with nix/nix/cabal is currently unsupported and fails with an explicit error.
  • hwm release publish is intentionally Cabal sdist based for Hackage publishing (builder-independent).
  • hwm release artifacts --builder=nix is currently unsupported.
  • default output dir .hwm/dist is cleaned before artifact generation; custom --output-dir is created if missing and not cleaned.
  • release.artifacts[*].environments is an allowlist for artifact command execution: omitted = all envs, [] = no envs, non-empty list = only listed envs.
  • for Nix static release derivation generation, only explicitly listed environments are included.
environments:
  builder: stack # or nix or cabal
release:
  artifacts:
    hwm: libs/_root_:hwm

🚢 Publication Trains

Define groups of packages to be published to Hackage. HWM enforces a topological sort, ensuring "core" dependencies are published before the packages that rely on them.

release:
  publish:
    main:
      - libs

Usage:

# Bump version across the workspace
hwm version minor

# Build local binaries and hashes with builder of choice
hwm release artifacts --builder=cabal

# Push a train to Hackage (Requires HACKAGE_AUTH_TOKEN in environment with a valid API token)
hwm release publish main

HWM Publish Output

⚖️ The Haskell Tooling Landscape

Feature Standard Setup Nix / Bazel 🚀 HWM v0.2.0
Config Source Decentralized Centralized Centralized (hwm.yaml)
Build System Support Single Tool High Friction ✅ Agnostic (Nix, Cabal, Stack)
Idempotency Manual Edits Varies ✅ Silent Writes (mtime-safe)
IDE Setup Manual hie.yaml Complex ✅ Auto-Generated (Smart)
Lock-in High Extreme ✅ Zero Lock-in

🧬 Status

HWM is currently in v0.2.0 (Beta). It was built to solve the orchestration needs of the Morpheus GraphQL ecosystem, where it successfully synchronizes 15+ packages across legacy and modern GHC profiles.

Your feedback is highly valued! Please open an issue if you encounter bugs or want to share how you are using HWM.

About

Haskell Workspace Manager: a declarative CLI tool to manage Haskell projects at any scale.

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors