{"id":20202879,"url":"https://github.com/carderne/una","last_synced_at":"2026-04-06T00:02:07.685Z","repository":{"id":253117506,"uuid":"831735647","full_name":"carderne/una","owner":"carderne","description":"Easy monorepos with Python and uv","archived":false,"fork":false,"pushed_at":"2025-06-30T09:33:09.000Z","size":410,"stargazers_count":71,"open_issues_count":4,"forks_count":2,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-12-02T07:59:04.020Z","etag":null,"topics":["build","hatch","monorepo","python","uv"],"latest_commit_sha":null,"homepage":"http://una.rdrn.me","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/carderne.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"docs/contributing.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-07-21T13:29:50.000Z","updated_at":"2025-11-26T19:07:48.000Z","dependencies_parsed_at":"2025-04-02T19:01:53.312Z","dependency_job_id":"b5c5db0b-fd52-4726-8bcb-e466af73f255","html_url":"https://github.com/carderne/una","commit_stats":null,"previous_names":["carderne/una"],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/carderne/una","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carderne%2Funa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carderne%2Funa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carderne%2Funa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carderne%2Funa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/carderne","download_url":"https://codeload.github.com/carderne/una/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carderne%2Funa/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31454200,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"ssl_error","status_checked_at":"2026-04-05T21:22:51.943Z","response_time":75,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["build","hatch","monorepo","python","uv"],"created_at":"2024-11-14T04:58:37.439Z","updated_at":"2026-04-06T00:02:07.679Z","avatar_url":"https://github.com/carderne.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Una\n\nTLDR:\n- [uv](https://docs.astral.sh/uv/) is the best way to use Python, and it has some monorepo/workspace support.\n- If your build artifacts are Dockerfiles, uv is probably enough. See an example at [carderne/postmodern-mono](https://github.com/carderne/postmodern-mono).\n- But if you want to build Python wheels (or similar) in monorepos, read on!\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/carderne/una/main/docs/assets/logo.svg\" alt=\"Una logo\" width=\"100\" role=\"img\"\u003e\n  \u003cp\u003eEasy monorepos with Python and uv\u003c/p\u003e\n\u003c/div\u003e\n\n----\n\u003cdiv align=\"center\"\u003e\n\n\u003ca href=\"https://pypi.org/project/una/\"\u003e\n\u003cimg alt=\"pypi\" src=\"https://img.shields.io/pypi/v/una.svg?logo=pypi\u0026label=PyPI\u0026logoColor=gold\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://una.rdrn.me/\"\u003e\n\u003cimg alt=\"docs\" src=\"https://img.shields.io/badge/Docs-gray?logo=materialformkdocs\u0026logoColor=white\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/carderne/una\"\u003e\n\u003cimg alt=\"GitHub\" src=\"https://img.shields.io/badge/GitHub-una-blue?logo=github\"\u003e\n\u003c/a\u003e\n\n\u003c/div\u003e\n\nUna is a tool to build and productionise Python monorepos with [uv](https://docs.astral.sh/uv/).\n\nuv has [Workspaces](https://docs.astral.sh/uv/concepts/workspaces/), but no ability to _build_ them.\nThis means if you have dependencies between packages in your workspace, there's no good way to distribute or productionise the end result.\n\nUna solves this.\nNo additional configuration is needed: if you have a functional uv Workspace, just add Una.\nIt consists of the following two things:\n\n1. A CLI to ensure that all imports are correctly specified as dependencies (you don't have to use this).\n2. A build plugin that enables production builds of individual apps within a monorepo by injecting local dependencies and transitive third-party dependencies.\n\nUna doesn't try to replicate a full build system such as [Bazel](https://bazel.build/) or\n[Pants](https://www.pantsbuild.org/).\nIt just makes it possible to have a simple monorepo with interdependencies.\n\nUna works much like a Rust workspace, with each package having its own pyproject.toml.\nIn general, packages should either be libraries (imported but not run) or apps (run but never imported), but Una will not enforce this.\n\nIt only works with the [Hatch](https://hatch.pypa.io) build backend.\n\n## Examples\nYou can see an example repo here:\n\n- [una-example](https://github.com/carderne/una-example)\n\n## Quickstart\nThis will give you a quick view of how this all works.\n\nFirst install uv:\n```bash\ncurl -LsSf https://astral.sh/uv/install.sh | sh\n```\n\nAnd start your workspace:\n```bash\nuv init unarepo   # choose another name if you prefer\ncd unarepo\nuv add --dev una\n```\n\nThen setup the Una workspace. This will generate a structure and an example lib and app.\n```bash\nuv run una create workspace\nuv sync\n```\n\nHave a look at what's been generated:\n```bash\ntree\n```\n\nHave a look at the generated `__init__.py` files in the `apps/printer` and `libs/greeter` packages.\nAn external dependency ([cowsay-python](https://pypi.org/project/cowsay-python/)) has also been added to the latter's `pyproject.toml`.\n\nThe magic of Una then comes in to resolve the graph of direct and transitive dependencies, which looks like this:\n```elm\nprinter --\u003e greeter --\u003e cowsay-python\n```\n\nYou can do this by running the following.\nThis checks all imports and ensures they are added to `project.dependencies` and `tool.uv.sources` in each pyproject.\n```bash\nuv run una sync\n```\n\nHave a look at what happened:\n```bash\ntail apps/printer/pyproject.toml\n```\n\nIt added `greeter` as an internal dependency to `printer`.\nIt didn't add `cowsay-python`, as transitive external dependencies are only resolved at build-time.\n\nNow you can build your app. Note that you **must** specify the `--wheel` parameter. Una doesn't currently work for builds that do source -\u003e sdist -\u003e wheel, as these break some things with uv virtual envs.\n```bash\nuvx --from build pyproject-build --installer=uv \\\n    --outdir=dist --wheel apps/printer\n```\n\nAnd see the result:\n```bash\nls dist/\n```\n\nAnd you can do whatever you want with that wheel!\nWhat about stick it in a Dockerfile, have you ever seen such a simple one?\n```Dockerfile\nFROM python\nCOPY dist dist\nRUN pip install dist/*.whl\n```\n\nBuild it:\n\n```bash\ndocker build --tag unarepo-printer .\n```\n\nAnd run it:\n```bash\ndocker run --rm -it unarepo-printer \\\n  python -c 'from unarepo.printer import run; run()'\n```\n\n```\n┌────────────────┐\n│Hello from una! │\n└────────────────┘\n               \\\n                \\\n                  ^__^\n                  (oo)\\_______\n                  (__)\\       )\\/\\\n                      ||----w |\n                      ||     ||\n```\n\nYou can also get a handy table of your internal inter-dependencies:\n```bash\n┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━┓\n┃ Package \\ Imports ┃ printer ┃ greeter ┃\n┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━┩\n│           printer │ ✓       │ ✓       │\n│           greeter │         │ ✓       │\n└───────────────────┴─────────┴─────────┘\n```\n\n## Installation\nThe CLI tool isn't strictly necessary, as all the stuff that lets the monorepo builds work is in the separate (and tiny) [hatch-una](plugins/hatch) package.\nBut you will likely struggle to manage your monorepo without the tool!\n\nSo you may as well install it:\n```bash\nuv add --dev una\n```\n\nAs for the build-time `hatch-una`, it will automatically be installed by build tools when it spots this in your `pyproject.toml` (this will be configured automatically by the CLI):\n```toml\n[build-system]\nrequires = [\"hatchling\", \"hatch-una\"]\nbuild-backend = \"hatchling.build\"\n```\n\n## Usage\nThe CLI has a few commands and options, have a look:\n```bash\nuv run una --help\n\n Usage: una [OPTIONS] COMMAND [ARGS]...\n\n╭─ Options ─────────────────────────────────────────────╮\n│ --help          Show this message and exit.           │\n╰───────────────────────────────────────────────────────╯\n╭─ Commands ────────────────────────────────────────────╮\n│ create   Commands for creating workspace and packages.│\n│ sync     Update packages with missing dependencies.   │\n╰───────────────────────────────────────────────────────╯\n```\n\n## Documentation\n\nRead more at [the official documentation](https://una.rdrn.me/).\n\nIt covers additional things like:\n- [type-checking](https://una.rdrn.me/types-tests/), testing, editor integration\n- and more!\n\n## Contributing\nSee the instructions at the [official documentation](https://una.rdrn.me/contributing/).\n\nVery briefly, local development is with uv:\n```bash\nuv sync\nmake all  # will fmt, lint, typecheck and test\n```\n\nThen open a PR.\n\n## License\nUna is distributed under the terms of the MIT license.\nSome code is from the [python-polylith](https://github.com/DavidVujic/python-polylith) project (c) 2022 David Vujic\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarderne%2Funa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcarderne%2Funa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarderne%2Funa/lists"}