Skip to content

Commit

Permalink
update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
carderne committed Aug 27, 2024
1 parent 0be5a52 commit 38218f2
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 41 deletions.
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<div align="center">
<img src="https://raw.githubusercontent.com/carderne/una/main/docs/assets/logo.svg" alt="Una logo" width="100" role="img">
<p>Easy monorepos with Python</p>
<p>Easy monorepos with Python and uv</p>
</div>

----
Expand All @@ -20,11 +20,17 @@

</div>

Una is a tool to make Python monorepos with [uv](https://docs.astral.sh/uv/) easier.
It is a CLI tool and a build plugin that does the following things:
Una is a tool to build and productionise Python monorepos with [uv](https://docs.astral.sh/uv/).

1. Enable builds of individual apps or projects within a monorepo.
2. Ensure that internal and external dependencies are correctly specified.
uv has [Workspaces](https://docs.astral.sh/uv/concepts/workspaces/), but no ability to _build_ them.
This means if you have dependencies between packages in your workspace, there's no good way to distribute or productionise the end result.

Una solves this.
No additional configuration is needed: if you have a functional uv Workspace, just add Una.
It consists of the following two things:

1. A CLI to ensure that all imports are correctly specified as dependencies.
2. A build plugin that enables production builds of individual apps within a monorepo by injecting local dependencies and transitive third-party dependencies.

Una doesn't try to replicate a full build system such as [Bazel](https://bazel.build/) or
[Pants](https://www.pantsbuild.org/).
Expand All @@ -33,12 +39,12 @@ It just makes it possible to have a simple monorepo with interdependencies.
Una works much like a Rust workspace, with each package having its own pyproject.toml.
In general, packages should either be libraries (imported but not run) or apps (run but never imported), but Una will not enforce this.

It only works with [uv](https://docs.astral.sh/uv/) and with the [Hatch](https://hatch.pypa.io) build backend.
It only works with the [Hatch](https://hatch.pypa.io) build backend.

## Examples
You can see an example repo here:

- [una-example](https://github.com/carderne/una-example-packages)
- [una-example](https://github.com/carderne/una-example)

## Quickstart
This will give you a quick view of how this all works.
Expand Down Expand Up @@ -79,7 +85,7 @@ printer --> greeter --> cowsay-python
You can do this by running the following:
```bash
# this checks all imports and ensures they are added to
# [tool.una.deps] in the appropriate pyproject.toml
# project.dependencies and tool.uv.sources in the each pyproject.toml
uv run una sync
```

Expand All @@ -89,12 +95,12 @@ tail apps/printer/pyproject.toml
```

It added `greeter` as an internal dependency to `printer`.
It didn't add `cowsay-python`, as external dependencies are only resolved at build-time (keep reading).
It didn't add `cowsay-python`, as transitive external dependencies are only resolved at build-time.

Now you can build your app:
```bash
uvx --from build pyproject-build --installer=uv --outdir=dist apps/printer
# this will inject the cowsay-python externel dependency
# this will inject the cowsay-python external dependency
```

And see the result:
Expand Down
26 changes: 18 additions & 8 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<div align="center">
<img src="assets/logo.svg" width="100">
<p>Easy monorepos with Python</p>
<p>Easy monorepos with Python and uv</p>
</div>

----
Expand All @@ -20,18 +20,28 @@

</div>

Una is a tool to make Python monorepos easier. It is a CLI tool and a build plugin that does the following things:
Una is a tool to build and productionise Python monorepos with [uv](https://docs.astral.sh/uv/).

1. Enable builds of individual apps or projects within a monorepo.
2. Ensure that internal and external dependencies are correctly specified.
uv has [Workspaces](https://docs.astral.sh/uv/concepts/workspaces/), but no ability to _build_ them.
This means if you have dependencies between packages in your workspace, there's no good way to distribute or productionise the end result.

Una doesn't try to replicate a full build system such as [Bazel](https://bazel.build/) or [Pants](https://www.pantsbuild.org/). It just makes it possible to have a simple monorepo with interdependencies.
Una solves this.
No additional configuration is needed: if you have a functional uv Workspace, just add Una.
It consists of the following two things:

Una works much like a Rust workspace, with each package having its own pyproject.toml. In general, packages should either be libraries (imported but not run) or apps (run but never imported), but Una will not enforce this.
1. A CLI to ensure that all imports are correctly specified as dependencies.
2. A build plugin that enables production builds of individual apps within a monorepo by injecting local dependencies and transitive third-party dependencies.

It only works with [uv](https://docs.astral.sh/uv/) and with the [Hatch](https://hatch.pypa.io) build backend.
Una doesn't try to replicate a full build system such as [Bazel](https://bazel.build/) or
[Pants](https://www.pantsbuild.org/).
It just makes it possible to have a simple monorepo with interdependencies.

Una works much like a Rust workspace, with each package having its own pyproject.toml.
In general, packages should either be libraries (imported but not run) or apps (run but never imported), but Una will not enforce this.

It only works with the [Hatch](https://hatch.pypa.io) build backend.

## Examples
You can see an example repo here:

- [una-example](https://github.com/carderne/una-example-packages)
- [una-example](https://github.com/carderne/una-example)
39 changes: 20 additions & 19 deletions docs/packages.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
# Packages

You can see an example of this here:
- [una-example](https://github.com/carderne/una-example-packages)

In this setup, we use uv's built-in workspace support (but you could also just create this structure some other way, YMMV). The structure will look something like this:
- [una-example](https://github.com/carderne/una-example)

The structure will look something like the below. This is completely up to you though! Whatever glob patterns you specify in uv's [Workspace](https://docs.astral.sh/uv/concepts/workspaces/) `members` table will be supported.
```bash
.
├── pyproject.toml
├── requirements.lock
├── uv.lock
├── apps
│   └── server
│   ├── pyproject.toml
Expand All @@ -27,44 +28,44 @@ In this setup, we use uv's built-in workspace support (but you could also just c
   └── test_mylib.py
```

This means:

1. Each `app` or `lib` (collectively, internal dependencies) is it's own Python package with a `pyproject.toml`.
2. You must specify the workspace members in `tool.uv.workspace.members`.
3. Type-checking and testing should be done on a per-package level.
That is, you should run `pyright` and `pytest` from `apps/server` or `libs/mylib`, _not_ from the root.

In the example above, the only build artifact will be for `apps/server`. At build-time, Una will do the following:
At build-time, Una will do the following:

1. Read the list of internal dependencies (more on this shortly) and inject them into the build.
2. Read all externel requirements of those dependencies, and add them to the dependency table.
2. Read all external requirements of those dependencies, and add them to the dependency table.

You can then use the Una CLI tool to ensure that all internal dependencies are kept in sync. What are the key steps?
You can use the Una CLI tool to ensure that all internal dependencies are kept in sync.

1. Use a uv workspace:
```toml
# /pyproject.toml
[tool.uv]
dev-dependencies = []

[tool.uv.workspace]
members = ["apps/*", "libs/*"]
# this could also be something like below
# if you don't want to separate apps and libs
# members = ["packages/*"]
```

2. Create your apps and your libs as you would, ensuring that app code is never imported.
Ensure that you choose a good namespace and always use it in your package structures (check `your_ns` in the example structure above.)
3. Add external dependencies to your libs and apps as normal.
Then, to add an internal dependency to an app, we do the following in its pyproject.toml:
2. Create your packages as you like.
3. Add external dependencies to your packages as normal.
Then, to add an internal dependency to an app, we do the following in its pyproject.toml. This tells uv (and Una!) to find the `greeter` package locally in the workspace.

```toml
# /apps/server/pyproject.toml
[project]
dependencies = ["greeter"]

[tool.uv.sources]
greeter = { workspace = true }

[build-system]
requires = ["hatchling", "hatch-una"]
build-backend = "hatchling.build"

[tool.hatch.build.hooks.una-build]
[tool.hatch.build.hooks.una-meta]
[tool.una.deps]
"../../libs/mylib/example/mylib" = "example/mylib"
```

4. Then you can build from that package directory and Una will inject everything that is needed:
Expand Down
10 changes: 6 additions & 4 deletions docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ And start your workspace:
```bash
uv init unarepo # choose another name if you prefer
cd unarepo
git init
uv add --dev una
```

Then setup the Una workspace. This will generate a structure and an example lib and app.
```
uv run una create workspace
rm -rf src
uv sync
```

Expand All @@ -35,7 +37,7 @@ printer --> greeter --> cowsay-python
You can do this by running the following:
```bash
# this checks all imports and ensures they are added to
# [tool.una.deps] in the appropriate pyproject.toml
# project.dependencies and tool.uv.sources in the each pyproject.toml
uv run una sync
```

Expand All @@ -45,12 +47,12 @@ tail apps/printer/pyproject.toml
```

It added `greeter` as an internal dependency to `printer`.
It didn't add `cowsay-python`, as external dependencies are only resolved at build-time (keep reading).
It didn't add `cowsay-python`, as transitive external dependencies are only resolved at build-time.

Now you can build your app:
```bash
uvx --from build pyproject-build --installer uv apps/printer
# this will inject the cowsay-python externel dependency
uvx --from build pyproject-build --installer=uv --outdir=dist apps/printer
# this will inject the cowsay-python external dependency
```

And see the result:
Expand Down

0 comments on commit 38218f2

Please sign in to comment.