Skip to content

Commit bade648

Browse files
committed
Improve project documentation
1 parent cfe2cd3 commit bade648

File tree

2 files changed

+262
-38
lines changed

2 files changed

+262
-38
lines changed

README.md

Lines changed: 249 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,14 @@
11
<div align="center">
22
<p>
33
<img
4-
src="https://github.com/user-attachments/assets/4f98b4d2-adcf-412f-bba5-1dbba43604a8"
4+
src="/Users/julienvincent/Downloads/kmono-white.png"
55
align="center"
66
alt="Logo"
7-
height="200px"
87
/>
98
</p>
109

11-
<h1>Kmono</h1>
12-
1310
<p>
14-
The missing workspace tool for clojure tools.deps projects
11+
A monorepo/workspace tool for clojure tools.deps projects
1512
</p>
1613

1714
[![Clojars Project](https://img.shields.io/clojars/v/com.kepler16/kmono-core.svg)](https://clojars.org/com.kepler16/kmono-core)
@@ -21,29 +18,165 @@
2118
Kmono is a suite of tools and API's for working in Clojure (mono)repos. It aims to meet Clojure where it's at by
2219
providing a better understanding of deps.edn projects.
2320

24-
This project was built with a focus on improving the experience of working in Clojure monorepos but works great in
25-
standalone projects too.
21+
While Kmono was designed with a focus on improving the experience of working in Clojure monorepos, care has been taken
22+
to ensure it works great in standalone (non-workspace) projects too.
2623

2724
## Index
2825

2926
- **[Features](#features)**
27+
- **[About](#about)**
28+
- **[How It Works](#how-it-works)**
29+
- **[Workspace Package Graph](#workspace-package-graph)**
30+
- **[The Classpath and Package Aliases](#The-Classpath-and-Package-Aliases)**
31+
- **[Clojure APIs](#clojure-apis)**
32+
- **[Versioning / Change Detection](#Versioning--Change-Detection)**
3033
- **[Installation](#installation)**
31-
- **[Documentation](#Documentation)**
34+
- **[Documentation](#API-Documentation)**
3235
- **[Kmono CLI](#kmono-cli)**
3336
- **[Example Project](#example-project)**
37+
- **[Configuration](#configuration)**
38+
- **[Workspace Configuration](#workspace-configuration)**
39+
- **[Package Configuration](#package-configuration)**
40+
- **[Local-Only Configuration Overrides](#Local-Only-Configuration-Overrides)**
3441
- **[Clojure-lsp / Editor Integration](#clojure-lsp--editor-integration)**
3542

3643
## Features
3744

3845
- **Workspace features**: Discovers packages and understands relationships between dependencies
3946
- **Aliases**: Allows working with packages aliases defined in `deps.edn` in a 'Clojure native' way without having to
40-
pull all alias definitions into root `deps.edn`
41-
- **Build Tools**: Exposes a suite of libs intended to be used from `tools.build` programs to build and release
42-
monorepos
47+
pull all alias definitions into a root `deps.edn`
48+
- **Build Tools**: Exposes a suite of tools and APIs intended to be used from `tools.build` programs to build and
49+
release monorepos
4350
- **Command Runner**: Allows executing Clojure and/or external commands in workspace packages
4451
- **Local Deps Overrides**: Allow overriding kmono config and `deps.edn` dependencies during local development. Useful
4552
for providing local paths to in-development libs without committing.
46-
- **Editor/Clojure-lsp**: Improves developer/editing experience by augmenting the classpath used by clojure-lsp
53+
- **Editor/Clojure-lsp**: Integrates with clojure-lsp to provide better classpath information and improve the
54+
developer/editing experience in monorepos
55+
56+
## About
57+
58+
It's generally a bit of a pain to work with `deps.edn` based Clojure monorepos. There is a general lack of good
59+
workspace features that one might find in other languages and that one needs in order to work effectively in a large
60+
monorepo.
61+
62+
Here is a broad list:
63+
64+
- Start a repl with some subset of package aliases (such as `:test`) active
65+
- Have your editor / clojure-lsp know about these subpackage aliases and include their `:extra-paths` on the classpath
66+
- Run common commands across packages in a monorepo
67+
- Track and increment package versions.
68+
- Release package jars with with their referenced workspace dependencies/dependent package versions correctly set.
69+
- Run CI build/test/release workflows against only the subset of packages that have changed since the previous version
70+
or revision.
71+
- Be able to define custom workspace configurations and pipelines that make use of the workspace graph / metadata in an
72+
unopinionated way which may differ from workspace to workspace.
73+
- Script against the workspace package graph (run queries against the package graph)
74+
75+
Kmono aims to solve all of these problems.
76+
77+
There are some existing projects out there that are trying to solve for this problem too - but I find that they are
78+
either too opinionated, too rigid, or stray too far from the 'official' tooling too much.
79+
80+
Kmono tries to build on top of `tools.deps` and the clojure cli in order to add additional functionality / capabilities
81+
in a way that feels native to `tools.deps` based projects. It tries to do this in a way that does not require
82+
configuring the project in a non-standard way, or configuring it in a manner which would render it incompatible with
83+
other tools.deps based tooling.
84+
85+
I see the functionality added here as a kind of proposal to the Clojure core team for how I might want workspace
86+
features to work in the core tools.deps and clojure tooling.
87+
88+
## How It Works
89+
90+
### Workspace Package Graph
91+
92+
At the core kmono is a tool which understands how packages in a Clojure workspace relate and depend on each other. This
93+
is done by analysing the `:deps` of packages to find `:local/root` coordinates to other packages in the same workspace.
94+
Using this information kmono can build up a graph of packages and their dependencies.
95+
96+
All of kmono's features and capabilities are built on top of this package graph.
97+
98+
### The Classpath and Package Aliases
99+
100+
Using the package graph kmono can augment the clojure cli with additional aliases constructed from the package graph.
101+
This allows 'telling' clojure about the aliases and paths of subpackages in the workspace.
102+
103+
When you run a command like `kmono cp -P ':*/test'` kmono will look for packages in the workspace containing a `:test`
104+
alias, and it will 'lift' these aliases into the root deps.edn config.
105+
106+
If we were to run this command in the [example workspace project](./examples/workspace/) it would result in a call to
107+
the clojure CLI that looks something like:
108+
109+
```bash
110+
clojure -Sdeps '{:aliases {:a/test {:extra-paths ["packages/a/test"]
111+
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
112+
:main-opts ["-m" "kaocha.runner" "-c" "../../tests.edn"]}
113+
:b/test {:extra-paths ["packages/b/test"]
114+
:extra-deps {lambdaisland/kaocha {:mvn/version "1.91.1392"}}
115+
:main-opts ["-m" "kaocha.runner" "-c" "../../tests.edn"]}
116+
:kmono/packages {:extra-deps {com.kepler16/a {:local/root "packages/a"}
117+
com.kepler16/b {:local/root "packages/b"}}}}}' \
118+
-A:kmono/packages:a/test:b/test -Spath
119+
```
120+
121+
From this you can see kmono has extended the `deps.edn` project configuration using the clojure cli `-Sdeps` flag,
122+
passing it a set of aliases dynamically constructed from the package graph.
123+
124+
You should notice that any relative paths defined by package aliases in the workspace have been adjusted to be relative
125+
to the project root instead of to the package.
126+
127+
Using `-A` kmono can selectively pick which aliases to apply!
128+
129+
Most kmono cli commands can be run with `-v` to print out debug information about what clojure command kmono is
130+
constructing.
131+
132+
### Clojure APIs
133+
134+
Kmono exposes a large set of clojure APIs which can be used to work with the analyzed workspace package graph. These
135+
APIs are primarily intended to be used within `tools.build` pipelines to define custom build + release workflows that
136+
fit the projects requirements, but could be used for building custom tools/clis/scripts.
137+
138+
Most API's are for querying, filtering, or versioning packages in the graph. Two key APIs exposed are
139+
`k16.kmono.build/for-each-package` and `k16.kmono.build/create-basis`.
140+
141+
#### `k16.kmono.build/for-each-package`
142+
143+
This allows iterating over the packages in a given package graph and executing a given function in the context of that
144+
package. It allows using the `tools.build` API's as if they were executed from the directory of the workspace package.
145+
146+
#### `k16.kmono.build/create-basis`
147+
148+
This allows constructing a `tools.build` basis wherein any references to workspace packages have been replaced with
149+
their maven coordinates. This is key to simplifying the process of incrementing package versions, building and
150+
releasing.
151+
152+
Please see the [example workspace](./examples/workspace/) or kmono itself for some examples on how to use the API's
153+
exposed by kmono.
154+
155+
### Versioning / Change Detection
156+
157+
Packages in the graph can have a set of `:commits` associated with them, as well as a `:version`. Various kmono API's
158+
work by populating these fields for packages in a graph or operating over this information.
159+
160+
For example there is `k16.kmono.version/inc-package-versions` which will increment the version of packages in a given
161+
package graph.
162+
163+
There is `k16.kmono.version/package-changed?` which can be used to filter out packages which have not changed since some
164+
previous revision / version.
165+
166+
This information can be populated into the graph however you want, but there are also some built-in mechanisms for
167+
associating package versions and commits.
168+
169+
For versions the built-in mechanism is to track package versions in git tags. The
170+
`k16.kmono.version/resolve-package-versions` will look for git tags following the pattern `<group>/<package-name>@<version>`
171+
and will use the `<version>` component to populate package version in the graph.
172+
173+
The `k16.kmono.version/resolve-package-changes` can be used to find commits that have changed the package since it's
174+
last tagged version.
175+
176+
The `k16.kmono.version/resolve-package-changes-since` can be used to find commits that have changed the package since a
177+
given git revision.
178+
179+
These tools and others can be used to build sophisticated build and release pipelines for kmono workspaces.
47180

48181
## Installation
49182

@@ -64,7 +197,7 @@ bash < <(curl -s https://raw.githubusercontent.com/kepler16/kmono/master/install
64197
Or alternatively binaries for various platforms can be pulled directly from the
65198
[Releases](https://github.com/kepler16/kmono/releases) page.
66199

67-
## Documentation
200+
## API Documentation
68201

69202
- **[kmono](https://cljdoc.org/d/com.kepler16/kmono)** - A BOM package containing all the submodules of kmono. This is
70203
the best one to look at for docs.
@@ -87,7 +220,7 @@ Usage:
87220
kmono [opts] <args>
88221

89222
Version:
90-
4.6.0
223+
4.9.0
91224

92225
Commands:
93226
cp Produce a classpath string from a clojure project
@@ -108,16 +241,115 @@ Global Options
108241
#### Example project
109242
110243
Take a look at **[the example project](./examples/workspace/)** to get a better idea of the type of project structures
111-
kmono is built to support and for references on how to correctly use the kmono API's and integrate it into your own
244+
kmono is built to support. This should provide a good reference on how to correctly use the kmono API's and integrate it
245+
into your own project.
246+
247+
## Configuration
248+
249+
Kmono will work in any `deps.edn` based project, but needs explicit configuration in order to treat a project as a
250+
workspace (or monorepo).
251+
252+
### Workspace Configuration
253+
254+
To mark a project as a kmono workspace add a `:kmono/workspace {}` field to the root `deps.edn` config file. This
255+
configuration accepts the following properties:
256+
257+
| Field | Type | Default | Description |
258+
| ------------------ | ----------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
259+
| `:packages` | `string? \| #{string?}` | "./packages/\*\*" | A glob or set of file globs that describe the set of packages included in the workspace |
260+
| `:group` | `symbol?` | `nil` | The mvn group to apply to any packages in the workspace that have not specified a group |
261+
| `:repl-aliases` | `[keyword?]` | `nil` | A set of `deps` aliases to include when running `kmono repl` |
262+
| `:aliases` | `[keyword?]` | `nil` | A set of `deps` aliases to include for all kmono workspace commands by default |
263+
| `:package-aliases` | `[keyword?]` | `nil` | A set of namespaced [alias globs](#alias-globs) that describe the aliases of packages within the workspace to include in the classpath |
264+
265+
Example:
266+
267+
```clojure
268+
;; deps.edn
269+
{:kmono/workspace {:group com.example
270+
:packages #{"./(packages|modules)/**"}
271+
;; Include any `:test` aliases from all (`*`) packages in the
272+
;; workspace
273+
:package-aliases [:*/test]}
274+
275+
:paths ["src" "resources"]
276+
277+
:deps {...}}
278+
```
279+
280+
### Package Configuration
281+
282+
Packages in the workspace can optionally provide their own configuration metadata. This is done by setting a
283+
`:kmono/package {}` config field in the packages `deps.edn` file. This config accepts the following properties:
284+
285+
| Field | Type | Default | Description |
286+
| ----------- | -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
287+
| `:group` | `symbol?` | `nil` | The mvn group to use for this package. If not specified, the `:group` specified in the root `:kmono/workspace` configuration will be used. |
288+
| `:name` | `string? \| symbol?` | `$dir` | The name of the package. If not set the name of the parent directory containing the packages' `deps.edn` file will be used as the package name |
289+
| `:excluded` | `boolean?` | `false` | Whether or not this package is excluded from the project workspace |
290+
291+
Example
292+
293+
```clojure
294+
;; packages/example/deps.edn
295+
{:kmono/package {;; Maven artifacts group
296+
:group com.example
297+
;; Override the default package name
298+
:name example-lib}
299+
300+
:paths ["src"]
301+
:deps {...}
302+
:aliases {:test {...}}}
303+
```
304+
305+
### Local-Only Configuration Overrides
306+
307+
Kmono will automatically read in a `deps.local.edn` file from the project root and deep-merge it with the root
308+
`deps.edn` file if present.
309+
310+
This is very useful for adding additional configuration such as default-enabled project aliases, new project-specific
311+
dev-only deps aliases or any other metadata in a way that is local to the developers environment and won't be committed
312+
to the project.
313+
314+
This `deps.local.edn` file should typically be added to `.gitignore`.
315+
316+
The below is a good example of how this `deps.local.edn` config might be used in someones environment:
317+
318+
```clojure
319+
{:kmono/workspace {;; Add the :nrepl alias from ~/.clojure/deps.edn when running
320+
;; `kmono repl`
321+
:repl-aliases [:nrepl]
322+
;; - Include the :dev alias from ~/.clojure/deps.edn
323+
;;
324+
;; - Include the :local alias defined below
325+
:aliases [:dev :local]}
326+
327+
:aliases {;; Define a custom local-only alias for this project
328+
:local {;; Override some dependency with a locally checked out copy
329+
;; for development
330+
:extra-deps {com.example/some-lib {:local/root "/some/local/lib/path"}}}
331+
:extra-paths ["local"]}}
332+
```
333+
334+
This config will be loaded and included for this project when running commands such as
335+
336+
```bash
337+
kmono cp
338+
kmono repl
339+
kmono clojure ...
340+
```
341+
342+
This is especially useful in an editor that has been configured to use
343+
[kmono as the project-specs source](#clojure-lsp--editor-integration), or when using kmono to start a repl for the
112344
project.
113345
114346
## Clojure-lsp / Editor Integration
115347
116348
One of the things that kmono enables is integration into your editor by acting as a drop-in replacement for
117-
`clojure -Spath` which is used by default by clojure-lsp.
349+
`clojure -Spath` which is used by clojure-lsp by default.
118350
119351
If we instead use `kmono cp` to generate the classpath then your clojure-lsp server will be able to provide better
120-
analysis.
352+
analysis that includes information about subpackages of your workspace, as well as package aliases.
121353
122354
#### Project local Clojure-LSP config
123355
@@ -142,15 +374,3 @@ lspconfig.clojure_lsp.setup({
142374
},
143375
})
144376
```
145-
146-
### Package configuration
147-
148-
Package-specific configurations are done within each `deps.edn` file under the `:kmono/package` key:
149-
150-
```clj
151-
:kmono/package {;; maven artifact's group
152-
:group com.example
153-
;; the package name which is also used as maven's artifactId
154-
;; this is optional and inferred from a package's dir name
155-
:name my-lib}
156-
```

examples/workspace/README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,23 @@ This is the overall structure of the project:
2323

2424
In this structure we have two packages - `a` and `b` where package `b` depends on package `a`.
2525

26-
This project demonstrates a workflow where:
26+
> [!NOTE]
27+
>
28+
> Run `kmono query` to query the workspace package graph and see how packages relate
2729
28-
1) Packages are built and released when PR's are merged to master.
29-
2) Only packages that have changed since their previous version are build and released.
30-
3) The project uses convensional-commits and package versions are derived from commits.
30+
This project demonstrates a workflow where:
31+
32+
1. Packages are built and released when PR's are merged to master.
33+
2. Only packages that have changed since their previous version are build and released.
34+
3. The project uses convensional-commits and package versions are derived from commits.
3135

3236
The above requirements aren't needed to make use of kmono - this just serves to demonstrate a particular workflow and
3337
how you might use kmono to achieve it.
3438

3539
## Building/Releasing
3640

3741
The build and release workflow described above is entirely encapsulated in the `build.clj` file using `tools.build` and
38-
kmono-* APIs.
42+
kmono-\* APIs.
3943

4044
Packages can be built by running:
4145

@@ -49,8 +53,8 @@ And the built packages can then be released by running
4953
clojure -T:build release
5054
```
5155

52-
For both building and releasing you can add the `:skip-unchanged true` argument to build and release only packages that
53-
have changed. The idea being that you would pass this by default during CI.
56+
For both building and releasing you can add the `:skip-unchanged true` argument to only build and release packages that
57+
have changed since their last release. The idea being that you would pass this by default during CI.
5458

5559
```bash
5660
clojure -T:build build :skip-unchanged true
@@ -69,8 +73,8 @@ pipeline for release.
6973
To run the tests for each package you can run the command
7074

7175
```bash
76+
# Run `clojure -M` in each package (indicated by the `*`) that has a `:test` alias.
7277
kmono run -M ':*/test'
7378
```
7479

75-
This means run `clojure -M` in each package (indicated by the `*`) that has a `:test` alias. Each respective packages'
76-
`:test` alias will then be appended to the command when it is run, like so: `clojure -M:test`.
80+
Each respective packages' `:test` alias will then be appended to the command when it is run, like so: `clojure -M:test`.

0 commit comments

Comments
 (0)