CI/CD Setup
Bleep's CI story has two parts:
- A basic workflow that runs
bleep compile/bleep teston every push. This page covers it. - Two features that make CI fast: build only what changed
(
bleep build invalidated) and reuse compiled classes across runs and machines (remote build cache). The consolidated workflow at the end of this page combines both.
If your CI is your bottleneck, skip to Build only what changed and Reuse work across runs.
GitHub Actions
Basic CI
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: bleep-build/bleep-setup-action@v1
- run: bleep --no-color --no-bsp-progress compile
- run: bleep --no-color --no-bsp-progress test
bleep-setup-action reads $version out of bleep.yaml and
installs that exact bleep build. --no-color and --no-bsp-progress
make CI logs scriptable and small — details under
CI flags below.
Cache the Coursier and JVM downloads
The first CI run downloads every dependency, the JVM bleep manages,
and the Scala/Kotlin compilers via Coursier. That's slow, but it's
also identical between runs as long as your bleep.yaml doesn't
change. Cache it:
- uses: actions/cache@v4
with:
path: |
~/.cache/coursier
~/.bleep
key: ${{ runner.os }}-bleep-${{ hashFiles('**/bleep.yaml') }}
This is not the same thing as bleep's remote build cache (which caches compiled classes, not downloads). See Reuse work across runs below for that.
Build only what changed
bleep build invalidated --base origin/main
returns the projects whose source or config changed vs a base
commit, plus every project transitively depending on them. Pipe
that into bleep compile / bleep test and CI does the minimum
amount of work.
- name: Compile only what changed
run: bleep build invalidated --base origin/${{ github.event.pull_request.base.ref }} | xargs bleep compile
- name: Test only what changed
run: bleep build invalidated --base origin/${{ github.event.pull_request.base.ref }} | xargs bleep test
fetch-depth: 0 on the checkout step is required so git diff
against the base ref works.
For the full pattern (no-op when nothing matches, GitLab CI example, JSON output for matrix expansion), see CI project invalidation.
Reuse work across runs
The remote build cache stores compiled
classes (plus their Zinc analysis) in S3 / R2 / MinIO so a CI
runner can pull what's been built elsewhere instead of recompiling.
Pair it with invalidated and CI compiles only the projects that
genuinely changed and whose digest isn't in the cache.
- name: Pull cache
run: bleep remote-cache pull
env:
BLEEP_REMOTE_CACHE_S3_ACCESS_KEY_ID: ${{ secrets.BLEEP_REMOTE_CACHE_S3_ACCESS_KEY_ID }}
BLEEP_REMOTE_CACHE_S3_SECRET_ACCESS_KEY: ${{ secrets.BLEEP_REMOTE_CACHE_S3_SECRET_ACCESS_KEY }}
# ... compile, test ...
- name: Push cache
if: success()
run: bleep remote-cache push
env:
BLEEP_REMOTE_CACHE_S3_ACCESS_KEY_ID: ${{ secrets.BLEEP_REMOTE_CACHE_S3_ACCESS_KEY_ID }}
BLEEP_REMOTE_CACHE_S3_SECRET_ACCESS_KEY: ${{ secrets.BLEEP_REMOTE_CACHE_S3_SECRET_ACCESS_KEY }}
Configuration (the bucket URI plus credentials) lives in
bleep.yaml and ~/.config/bleep/config.yaml — see
Remote build cache for setup, S3-compatible
backends (MinIO / Cloudflare R2 / GCS), and bucket-lifecycle
expiration commands.
Consolidated workflow: invalidation + remote cache
The two features compose. Pull the cache first, compute the invalidated set, compile/test against it (cache hits skip work, genuine changes recompile), push the cache so the next run picks up the new artifacts.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # required for `bleep build invalidated`
- uses: bleep-build/bleep-setup-action@v1
- uses: actions/cache@v4
with:
path: |
~/.cache/coursier
~/.bleep
key: ${{ runner.os }}-bleep-${{ hashFiles('**/bleep.yaml') }}
- name: Pull remote cache
run: bleep remote-cache pull
env:
BLEEP_REMOTE_CACHE_S3_ACCESS_KEY_ID: ${{ secrets.BLEEP_REMOTE_CACHE_S3_ACCESS_KEY_ID }}
BLEEP_REMOTE_CACHE_S3_SECRET_ACCESS_KEY: ${{ secrets.BLEEP_REMOTE_CACHE_S3_SECRET_ACCESS_KEY }}
- name: Compile invalidated projects
run: |
BASE=origin/${{ github.event.pull_request.base.ref || 'main' }}
bleep build invalidated --base "$BASE" | xargs bleep --no-color --no-bsp-progress compile
- name: Test invalidated projects
run: |
BASE=origin/${{ github.event.pull_request.base.ref || 'main' }}
bleep build invalidated --base "$BASE" | xargs bleep --no-color --no-bsp-progress test
- name: Push remote cache
if: success()
run: bleep remote-cache push
env:
BLEEP_REMOTE_CACHE_S3_ACCESS_KEY_ID: ${{ secrets.BLEEP_REMOTE_CACHE_S3_ACCESS_KEY_ID }}
BLEEP_REMOTE_CACHE_S3_SECRET_ACCESS_KEY: ${{ secrets.BLEEP_REMOTE_CACHE_S3_SECRET_ACCESS_KEY }}
Release workflow
Tag-driven publish:
name: Release
on:
push:
tags: ['v*']
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # dynver reads tag history
- uses: bleep-build/bleep-setup-action@v1
- name: Publish to Sonatype
env:
SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }}
SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }}
PGP_SECRET: ${{ secrets.PGP_SECRET }}
PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }}
run: bleep publish --assert-release sonatype
--assert-release fails the job if dynver would emit a snapshot
version (uncommitted changes, commits past the tag, no tags at
all). See Publish to Maven Central
for the full release-pipeline story.
CI flags
bleep --no-color --no-bsp-progress compile
| Flag | Effect |
|---|---|
--no-color | Strip ANSI codes from log output. |
--no-bsp-progress | Drop the live BSP progress display so log files stay small. |
Both are appropriate for any non-interactive run.
Matrix builds
Test multiple Scala versions in parallel jobs:
jobs:
test:
strategy:
matrix:
scala: [2.13, 3]
steps:
- uses: actions/checkout@v4
- uses: bleep-build/bleep-setup-action@v1
- run: bleep test mylib@jvm${{ matrix.scala }}*
For invalidation-driven matrix expansion (build the matrix
dynamically from what's actually changed), pipe
bleep build invalidated --output json
through jq into the matrix. See
CI project invalidation for that
pattern.
Other CI systems
Bleep works with any CI that can run shell commands. Install via the curl installer or Coursier:
curl -fsSL https://bleep.build/install | sh
# or
cs install --channel https://raw.githubusercontent.com/oyvindberg/bleep/master/coursier-channel.json bleep
Remote build cache and CI project invalidation include GitLab CI examples alongside the GitHub Actions ones.