Skip to content

Commit 3ca988c

Browse files
committed
Improve project documentation
1 parent cfe2cd3 commit 3ca988c

2 files changed

Lines changed: 261 additions & 34 deletions

File tree

README.md

Lines changed: 248 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<h1>Kmono</h1>
1212

1313
<p>
14-
The missing workspace tool for clojure tools.deps projects
14+
A monorepo/workspace tool for clojure tools.deps projects
1515
</p>
1616

1717
[![Clojars Project](https://img.shields.io/clojars/v/com.kepler16/kmono-core.svg)](https://clojars.org/com.kepler16/kmono-core)
@@ -21,29 +21,165 @@
2121
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
2222
providing a better understanding of deps.edn projects.
2323

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.
24+
While Kmono was designed with a focus on improving the experience of working in Clojure monorepos, care has been taken
25+
to ensure it works great in standalone (non-workspace) projects too.
2626

2727
## Index
2828

2929
- **[Features](#features)**
30+
- **[About](#about)**
31+
- **[How It Works](#how-it-works)**
32+
- **[Workspace Package Graph](#workspace-package-graph)**
33+
- **[The Classpath and Package Aliases](#The-Classpath-and-Package-Aliases)**
34+
- **[Clojure APIs](#clojure-apis)**
35+
- **[Versioning / Change Detection](#Versioning--Change-Detection)**
3036
- **[Installation](#installation)**
31-
- **[Documentation](#Documentation)**
37+
- **[Documentation](#API-Documentation)**
3238
- **[Kmono CLI](#kmono-cli)**
3339
- **[Example Project](#example-project)**
40+
- **[Configuration](#configuration)**
41+
- **[Workspace Configuration](#workspace-configuration)**
42+
- **[Package Configuration](#package-configuration)**
43+
- **[Local-Only Configuration Overrides](#Local-Only-Configuration-Overrides)**
3444
- **[Clojure-lsp / Editor Integration](#clojure-lsp--editor-integration)**
3545

3646
## Features
3747

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

48184
## Installation
49185

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

67-
## Documentation
203+
## API Documentation
68204

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

89225
Version:
90-
4.6.0
226+
4.9.0
91227

92228
Commands:
93229
cp Produce a classpath string from a clojure project
@@ -108,16 +244,115 @@ Global Options
108244
#### Example project
109245
110246
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
247+
kmono is built to support. This should provide a good reference on how to correctly use the kmono API's and integrate it
248+
into your own project.
249+
250+
## Configuration
251+
252+
Kmono will work in any `deps.edn` based project, but needs explicit configuration in order to treat a project as a
253+
workspace (or monorepo).
254+
255+
### Workspace Configuration
256+
257+
To mark a project as a kmono workspace add a `:kmono/workspace {}` field to the root `deps.edn` config file. This
258+
configuration accepts the following properties:
259+
260+
| Field | Type | Default | Description |
261+
| ------------------ | ----------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
262+
| `:packages` | `string? \| #{string?}` | "./packages/\*\*" | A glob or set of file globs that describe the set of packages included in the workspace |
263+
| `:group` | `symbol?` | `nil` | The mvn group to apply to any packages in the workspace that have not specified a group |
264+
| `:repl-aliases` | `[keyword?]` | `nil` | A set of `deps` aliases to include when running `kmono repl` |
265+
| `:aliases` | `[keyword?]` | `nil` | A set of `deps` aliases to include for all kmono workspace commands by default |
266+
| `: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 |
267+
268+
Example:
269+
270+
```clojure
271+
;; deps.edn
272+
{:kmono/workspace {:group com.example
273+
:packages #{"./(packages|modules)/**"}
274+
;; Include any `:test` aliases from all (`*`) packages in the
275+
;; workspace
276+
:package-aliases [:*/test]}
277+
278+
:paths ["src" "resources"]
279+
280+
:deps {...}}
281+
```
282+
283+
### Package Configuration
284+
285+
Packages in the workspace can optionally provide their own configuration metadata. This is done by setting a
286+
`:kmono/package {}` config field in the packages `deps.edn` file. This config accepts the following properties:
287+
288+
| Field | Type | Default | Description |
289+
| ----------- | -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
290+
| `: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. |
291+
| `: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 |
292+
| `:excluded` | `boolean?` | `false` | Whether or not this package is excluded from the project workspace |
293+
294+
Example
295+
296+
```clojure
297+
;; packages/example/deps.edn
298+
{:kmono/package {;; Maven artifacts group
299+
:group com.example
300+
;; Override the default package name
301+
:name example-lib}
302+
303+
:paths ["src"]
304+
:deps {...}
305+
:aliases {:test {...}}}
306+
```
307+
308+
### Local-Only Configuration Overrides
309+
310+
Kmono will automatically read in a `deps.local.edn` file from the project root and deep-merge it with the root
311+
`deps.edn` file if present.
312+
313+
This is very useful for adding additional configuration such as default-enabled project aliases, new project-specific
314+
dev-only deps aliases or any other metadata in a way that is local to the developers environment and won't be committed
315+
to the project.
316+
317+
This `deps.local.edn` file should typically be added to `.gitignore`.
318+
319+
The below is a good example of how this `deps.local.edn` config might be used in someones environment:
320+
321+
```clojure
322+
{:kmono/workspace {;; Add the :nrepl alias from ~/.clojure/deps.edn when running
323+
;; `kmono repl`
324+
:repl-aliases [:nrepl]
325+
;; - Include the :dev alias from ~/.clojure/deps.edn
326+
;;
327+
;; - Include the :local alias defined below
328+
:aliases [:dev :local]}
329+
330+
:aliases {;; Define a custom local-only alias for this project
331+
:local {;; Override some dependency with a locally checked out copy
332+
;; for development
333+
:extra-deps {com.example/some-lib {:local/root "/some/local/lib/path"}}}
334+
:extra-paths ["local"]}}
335+
```
336+
337+
This config will be loaded and included for this project when running commands such as
338+
339+
```bash
340+
kmono cp
341+
kmono repl
342+
kmono clojure ...
343+
```
344+
345+
This is especially useful in an editor that has been configured to use
346+
[kmono as the project-specs source](#clojure-lsp--editor-integration), or when using kmono to start a repl for the
112347
project.
113348
114349
## Clojure-lsp / Editor Integration
115350
116351
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.
352+
`clojure -Spath` which is used by clojure-lsp by default.
118353
119354
If we instead use `kmono cp` to generate the classpath then your clojure-lsp server will be able to provide better
120-
analysis.
355+
analysis that includes information about subpackages of your workspace, as well as package aliases.
121356
122357
#### Project local Clojure-LSP config
123358
@@ -142,15 +377,3 @@ lspconfig.clojure_lsp.setup({
142377
},
143378
})
144379
```
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)