Install pre-commit:
# preferred
pipx install pre-commit
# or on Homebrew-based systems
brew install pre-commit
Note: macOS can ship a broken Python 2
pre-commitstub at/usr/local/bin/pre-commit. A pipx- or Homebrew-installed version on your PATH should take precedence.
After cloning, install the pre-commit hooks once:
pre-commit install
cljfmt-fixRuns cljfmt fix on every staged Clojure file (.clj, .cljs, .cljc)
before each commit.
git commit to proceed.cljfmt must be on your PATH (e.g. installed via
bbin: bbin install io.github.weavejester/cljfmt).# Run against all files in the repo
pre-commit run --all-files
# Run only the cljfmt hook
pre-commit run cljfmt-fix
# Run against specific files
pre-commit run cljfmt-fix --files src/foo/bar.clj
clj-kondo-lintRuns clj-kondo --lint on every staged Clojure file before each commit.
The commit is blocked if there are any warnings or errors.
--cache false to avoid JVM file-lock contention when pre-commit
parallelises the hook across multiple files..clj-kondo/config.edn so individual-file linting
works without a full classpath scan.clj-kondo must be on your PATH (e.g. installed via
bbin: bbin install clj-kondo/clj-kondo).# Run only the clj-kondo hook
pre-commit run clj-kondo-lint
# Run against specific files
pre-commit run clj-kondo-lint --files src/foo/bar.clj
Check formatting without modifying files:
bb fmt:check
Fix formatting across the whole repo:
cljfmt fix bb.edn deps.edn .lsp/config.edn .psi/startup-prompts.edn \
components extensions spec test tests.edn extensions/tests.edn
bb lint
Project-local commit checks can be wired through .psi/commit-checks.edn and
run bb tasks that fail with a non-zero exit when a check should block follow-up.
This repo defines:
bb commit-check:rama-cc
bb commit-check:file-lengths
bb commit-check:dispatch-architecture
commit-check:rama-ccRuns:
rama-cc --threshold 21 --fail-above 20 components/ bases/
This task exits with the same exit code returned by the shell command, even
when rama-cc reports zero matched files.
commit-check:file-lengthsScans components/ and bases/ for files under src/ or test/ and fails if
any exceed 800 lines.
When it fails it prints the matching files to stderr and exits non-zero.
commit-check:dispatch-architectureRuns a psi-specific dispatch architecture check for agent-session.
Current behavior:
:effect/type missing from dispatch_schema.clj:effect/type missing from dispatch_effects.cljdispatch_handlers/(:state* ctx) writes outside a small allowlist of
infrastructure namespacesThis task is intentionally narrow and project-specific so we can prove its usefulness before broadening scope or upstreaming ideas.
https://github.com/hugoduncan/psi (push to master + tags).CLOJARS_USERNAME and CLOJARS_PASSWORD (deploy token) set as GitHub Actions
secrets on the repo (Settings → Secrets → Actions).master, up to date with origin.CHANGELOG.md has a non-empty ## [Unreleased] section
(bb changelog:check to verify).bb release
This single command:
master.PATCH = (git rev-list HEAD --count) + 1.CHANGELOG.md: [Unreleased] → [MAJOR.MINOR.PATCH] - YYYY-MM-DD,
prepends a fresh [Unreleased], and updates the comparison link footer.{:version "MAJOR.MINOR.PATCH"} to bases/main/resources/psi/version.edn."release: vMAJOR.MINOR.PATCH" and tags vMAJOR.MINOR.PATCH.version.edn to {:version "unreleased"} and commits
"release: post-vMAJOR.MINOR.PATCH reset version to unreleased".master + tags to origin.Pushing the tag triggers .github/workflows/release.yml, which:
org.hugoduncan/psi).:jar launcher policy against the deployed Clojars artifact
(retries up to 8×30s for propagation).The same workflow also supports manual dry-run testing via workflow_dispatch
without publishing anything publicly. Use GitHub Actions → Release → Run workflow
with:
publish = false to validate the release build path without external publicationref to test a branch/commit instead of the current refrelease_version to force a specific version label for the dry-runDry-run mode now stamps bases/main/resources/psi/version.edn inside the runner,
builds the library jar, installs that jar into the runner's local Maven repo,
smoke-tests both the :jar policy and the released bbin launcher entrypoint
against that locally installed artifact, builds the uberjar, and still skips
external publication steps such as Clojars deploy, changelog extraction, and
GitHub Release creation.
bb release and bb release:tag are re-entrant:
| Failure point | Recovery |
|---|---|
Died after stamp-changelog!, before git commit | Re-run detects stamped changelog, resumes from commit |
| Died after tag, before version reset commit | Re-run detects tag + un-reset version resource, completes reset |
| Died after version reset, before push | bb release detects local tag not on origin, goes straight to push |
| Push failed (network) | Re-run bb release — detects local tag not on origin, retries push |
If the GH Actions release job fails after Clojars deploy but before GH Release creation, re-pushing the tag is not safe (tag already exists). Instead:
workflow_dispatch on the tag, orbb build:jar + create the GH Release via gh release create.After the workflow completes:
# Verify Clojars artifact
clojure -Sdeps '{:deps {org.hugoduncan/psi {:mvn/version "X.Y.Z"}}}' \
-M -m psi.main --version
# Verify bbin install
bbin install org.hugoduncan/psi --as psi --mvn/version X.Y.Z
psi --version
bb build:lib and bb deploy can be run standalone against an already-stamped
version resource for debugging:
# 1. Temporarily stamp the version resource (do NOT commit)
echo '{:version "0.1.9999"}' > bases/main/resources/psi/version.edn
# 2. Build the library jar
bb build:lib # → target/psi-0.1.9999.jar
# 3. Deploy to Clojars (requires CLOJARS_USERNAME + CLOJARS_PASSWORD in env)
CLOJARS_USERNAME=you CLOJARS_PASSWORD=token bb deploy
# 4. Restore the version resource
echo '{:version "unreleased"}' > bases/main/resources/psi/version.edn
bb deploy auto-invokes bb build:lib if the jar is absent, so steps 2 and 3
can be combined as just bb deploy.
The GitHub Actions workflow (.github/workflows/ci.yml) runs on:
workflow_dispatch)mastermastercheck (fmt + lint)
├── clojure-test
└── emacs-test
check runs first. clojure-test and emacs-test run in parallel only
if check passes.
| Job | Tasks |
|---|---|
check | bb fmt:check, bb lint |
clojure-test | bb clojure:test (unit + extensions) |
emacs-test | bb emacs:check (byte-compile + ERT) |
Maven and Clojure deps (~/.m2, ~/.gitlibs, ~/.clojure) are cached
and keyed on deps.edn + bb.edn to speed up subsequent runs.
# All tests
bb test
# Clojure unit tests only
bb clojure:test:unit
# Clojure extension tests only
bb clojure:test:extensions
# Emacs frontend tests
bb emacs:check
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 |