workspace-cache 0.1.0

A CLI to generate cached Docker builds for Rust workspaces
workspace-cache-0.1.0 is not a library.

workspace-cache

Like cargo-chef but focused on Rust workspaces with multiple binaries (microservices). Generates optimized Dockerfiles with proper layer caching.

Installation

cargo install workspace-cache

Or install from git for the latest development version:

cargo install --git https://github.com/preiter93/workspace-cache workspace-cache

Quick Start

Generate a Dockerfile for your service:

workspace-cache dockerfile --bin user -o Dockerfile

This produces an optimized multi-stage Dockerfile:

# Stage 1: Install workspace-cache tool
FROM rust:1.94-bookworm AS base
WORKDIR /app
RUN cargo install workspace-cache@0.1.0

# Stage 2: Generate minimal workspace with stub sources
FROM base AS planner
COPY . .
RUN workspace-cache deps --bin user

# Stage 3: Build dependencies only (cached until Cargo.toml/Cargo.lock change)
FROM base AS deps
COPY --from=planner /app/.workspace-cache .
RUN cargo build --release

# Stage 4: Build the actual binary with real source code
FROM deps AS builder
RUN rm -rf crates/user/src crates/common/src
COPY crates/user crates/user
COPY crates/common crates/common
RUN cargo clean --release -p user -p common
RUN cargo build --release --bin user

# Stage 5: Minimal runtime image
FROM debian:bookworm-slim AS runtime
COPY --from=builder /app/target/release/user /usr/local/bin/user
ENTRYPOINT ["/usr/local/bin/user"]

Build & Run

docker build -f Dockerfile -t user .
docker run --rm user

How It Works

  1. planner - Creates a minimal workspace with stub sources for dependency resolution
  2. deps - Builds dependencies (cached until Cargo.toml/Cargo.lock changes)
  3. builder - Copies real source and builds binary
  4. runtime - Minimal image with just the binary

When source files change but dependencies don't, Docker skips the deps stage entirely.

Usage

The main command is dockerfile. It generates an optimized Dockerfile for your binary:

workspace-cache dockerfile --bin <binary> [OPTIONS]

Options:

  • --bin <binary> - Binary to build (required)
  • --profile <profile> - Build profile: release or debug (default: release)
  • -o, --output <path> - Output path (default: stdout)
  • --base-image <image> - Base image for build stages (default: rust:1.94-bookworm)
  • --runtime-image <image> - Runtime image (default: debian:bookworm-slim)
  • --tool-version <version> - Version of workspace-cache to install (default: current version)
  • --from-git - Install workspace-cache from git instead of crates.io
  • --fast - Fast mode: skip dependency resolution for faster generation

Examples:

# Generate release Dockerfile (default)
workspace-cache dockerfile --bin user -o Dockerfile

# Generate debug Dockerfile
workspace-cache dockerfile --bin user --profile debug -o Dockerfile.debug

# Use a specific version
workspace-cache dockerfile --bin user --tool-version 0.1.0 -o Dockerfile

# Install from git (latest dev version)
workspace-cache dockerfile --bin user --from-git -o Dockerfile

# Custom base image
workspace-cache dockerfile --bin user --base-image rust:1.80-alpine -o Dockerfile

Fast Mode

Use --fast to skip dependency resolution. This results in a less optimized cache, but speeds up Docker builds as long as no dependencies have changed.

workspace-cache dockerfile --bin user --fast -o Dockerfile

CI Usage (without Docker)

GitHub Action (Recommended)

The simplest way to use workspace-cache in CI is with the provided composite actions:

- name: Install workspace-cache
  uses: preiter93/workspace-cache/.github/actions/install-workspace-cache@main

- name: Build my binary
  uses: preiter93/workspace-cache/.github/actions/build-workspace@main
  with:
    binary: user

This handles all the caching and build steps automatically. The install-workspace-cache action caches the tool binary for faster subsequent runs. See the action READMEs for more options:

Manual Setup

You can also set up workspace-cache manually for more control:

- name: Install workspace-cache
  run: cargo install workspace-cache

- name: Generate minimal workspace
  run: workspace-cache deps --bin user

- name: Get cache key for dependencies
  id: cache-key
  run: |
    HASH="${{ hashFiles('.workspace-cache/Cargo.lock') }}"
    echo "key=${{ runner.os }}-workspace-cache-deps-${HASH}" >> $GITHUB_OUTPUT

- name: Cache dependencies
  uses: actions/cache@v4
  with:
    path: .workspace-cache/target
    key: ${{ steps.cache-key.outputs.key }}
    restore-keys: |
      ${{ runner.os }}-workspace-cache-deps-

- name: Build dependencies (cached when Cargo.lock unchanged)
  run: cargo build --release
  working-directory: .workspace-cache

- name: Copy real sources
  run: |
    workspace-cache members --bin user | while read path name; do
      rm -rf .workspace-cache/$path/src
      cp -r $path/src .workspace-cache/$path/src
    done

- name: Build binary
  run: |
    PACKAGES=$(workspace-cache members --bin user | awk '{print "-p " $2}' | tr '\n' ' ')
    cargo clean --release $PACKAGES
    cargo build --release --bin user
  working-directory: .workspace-cache

The cache key is based on the generated .workspace-cache/Cargo.lock, so dependencies are only rebuilt when they change. On cache hits, the dependency build step completes in seconds.

Other Commands

The following commands are mainly for debugging or understanding how the tool works internally.

Generate Minimal Workspace

workspace-cache deps [OPTIONS]

Options:

  • --bin <binary> - Only include dependencies for specific binary/binaries
  • -o, --output <dir> - Output directory (default: .workspace-cache)
  • --fast - Fast mode: skip dependency resolution

Examples:

# Generate for all workspace binaries
workspace-cache deps

# Generate for a specific binary
workspace-cache deps --bin user

# Generate for multiple binaries
workspace-cache deps --bin user --bin order

# Custom output directory
workspace-cache deps --bin user -o my-cache

Show Workspace Members

workspace-cache members --bin <binary>

Shows which workspace members a binary depends on, with their paths and names:

$ workspace-cache members --bin user
crates/pkg-a pkg_a
crates/pkg-b pkg_b
crates/user user

This output can be used in scripts to dynamically copy sources or generate package lists.

Build Workspace

workspace-cache build [OPTIONS]

Options:

  • --bin <binary> - Only build specific binary/binaries
  • --release - Build in release mode

Examples:

# Build all binaries
workspace-cache build

# Build specific binary in release mode
workspace-cache build --bin user --release

Testing

Run unit tests:

cargo test

Test locally in your workspace:

# Generate minimal workspace for a binary
workspace-cache deps --bin user

# Build dependencies
cd .workspace-cache
cargo build --release

# Copy real sources and build (deps are cached)
rm -rf crates/user/src crates/common/src
cp -r ../crates/user/src crates/user/src
cp -r ../crates/common/src crates/common/src
cargo build --release --bin user

Note: This mirrors how the generated Dockerfile works. The key is building the final binary from within .workspace-cache/ after copying real sources.

License

MIT