This guide adds assay to a Polylith workspace: as a command, as a Git hook
that checks each commit, and as a GitHub Actions job that checks each pull
request and each push to main.
Assay needs:
workspace.edn with :top-namespace, and bricks
under components and bases.With Babashka, add assay and a task to the workspace's bb.edn:
{:deps {systems.thoughtfull/assay {:mvn/version "0.3.1"}}
:tasks
{assay {:doc "Check code metrics with assay"
:requires ([systems.thoughtfull.assay.cli.main :as assay])
:task (apply assay/-main *command-line-args*)}}}
With the Clojure CLI, add an alias to deps.edn:
{:aliases
{:assay {:replace-deps {systems.thoughtfull/assay {:mvn/version "0.3.1"}}
:main-opts ["-m" "systems.thoughtfull.assay.cli.main"]}}}
To use a commit that isn't on Clojars, use a Git dependency instead of
:mvn/version:
io.github.thoughtfull-clojure/assay {:git/sha "<commit sha>"
:deps/root "projects/assay"}
Then run it from the workspace root:
bb assay # or: clojure -M:assay
bb assay --format text # print violations
bb assay --help
By default, assay writes an HTML report to target/assay/index.html. See
the sample report for what it looks like, and the
sample GitHub summary for the job summary it
writes under GitHub Actions.
Assay works without configuration. To change thresholds, add assay.edn
at the workspace root, or pass another file with --config. Its keys are
the report's categories, each mapping metrics to their thresholds, merged
over the defaults. For example, to make deep functions an error past 8,
and let any brick require java-time:
{:complexity {:function-depth {:error 8}}
:io {:library-spread {:allow #{"java-time"}}}}
The README's configuration section lists every setting and its default.
The hook compares the working tree with HEAD and fails the commit only
on error-level violations that the commit introduces:
bb assay --format text --base HEAD
Assay can't compare with HEAD before the first commit, so each option
below skips the check until there is one.
Save this as .git/hooks/pre-commit and make it executable:
#!/bin/sh
# Skip the check before the first commit.
git rev-parse --verify --quiet HEAD >/dev/null || exit 0
exec bb assay --format text --base HEAD
Git doesn't share .git/hooks through the repository. To share the hook,
keep it in a directory such as .githooks and run
git config core.hooksPath .githooks in each clone.
With pre-commit or
prek, add a local hook to
.pre-commit-config.yaml:
repos:
- repo: local
hooks:
- id: assay
name: assay
entry: bb assay --format text --base HEAD
language: system
pass_filenames: false
files: ^(components|bases)/[^/]+/src/.*\.clj[cs]?$
The files pattern runs the hook only when a commit changes brick source.
With devenv, add the hook to devenv.nix:
git-hooks.hooks.assay = {
enable = true;
name = "assay";
description = "Check Clojure code metrics with assay.";
entry = "${pkgs.babashka}/bin/bb assay --format text --base HEAD";
pass_filenames = false;
files = "^(components|bases)/[^/]+/src/.*\\.clj[cs]?$";
};
This GitHub Actions workflow runs assay on each pull request and each push
to main. Save it as .github/workflows/assay.yml:
name: Assay
on:
push:
branches: [main]
pull_request:
permissions:
contents: read
jobs:
assay:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7.0.1
with:
# Assay compares with earlier commits and reads the history for
# co-change coupling, so it needs the history.
fetch-depth: 0
- uses: actions/setup-java@v6.0.1
with:
distribution: temurin
java-version: "21"
- uses: DeLaGuardo/setup-clojure@13.7.0
with:
bb: latest
# Fail only on new error-level violations. Pull requests compare with
# the target branch, and pushes to main with the commit before the
# push. Without that commit, assay has no base and fails on any
# error-level violation.
- name: Assay
env:
BASE_REF: ${{ github.base_ref }}
BEFORE: ${{ github.event.before }}
run: |
args=(--format github --format html)
if [ -n "$BASE_REF" ]; then
args+=(--base "origin/$BASE_REF")
else
# BEFORE is all zeros for a new branch, and a force push can
# leave it pointing at a commit that is gone.
if git cat-file -e "${BEFORE}^{commit}" 2>/dev/null; then
args+=(--base "$BEFORE")
fi
fi
bb assay "${args[@]}"
- name: Upload assay report
if: always()
uses: actions/upload-artifact@v7.0.1
with:
name: assay-report
path: target/assay/index.html
if-no-files-found: ignore
Each run then has:
deps.edn.assay-report artifact.Assay reports only error-level violations unless you pass --warnings.
Warnings point to code worth refactoring before it reaches an error, so
consider adding --warnings to the HTML report while leaving the hook
terse.
Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |