I recently did something similar. Using uv workspaces, I used the uv CLI's dependency graph to analyze the dependency tree then conditionally trigger CI workflows for affected projects. I wish there was a better way to access the uv dependency worktree other than parsing the `tree` like output
I use Github Actions triggers to pass flags to a monorepo dagger script to build and test the affected components. For example, if a commit touches the front- and back ends, rebuild both. If it only touches the front end, run integration tests using the latest backend without rebuilding it.
Yea this definitely makes sense for smaller monorepos. For us, we ended up writing our own dependency graph parser to figure out what tests to run (which is easy enough with a single language like python honestly)
We used pants initially (which I believe is similar to bazel). And indeed the dependency graphing it does was very helpful, but other parts of the tool motivated us to create something more bespoke and debuggable (we were only using like 20% or less of the features pants offers)
Yes! For example, previously with pants, users would hit a lot of weird errors since how tests run with pants is different than running tests locally with pytest
We did not expect users to learn pants, but this often meant a lot of back and forth with maintainers to get PR tests working.
I find it quite astonishing there is no go-to build system / task runner yet for handling small to medium-sized monorepos across ecosystems.
I want a tool that
- allows me to define tasks with inputs (+ secrets) and outputs. Inputs can be files & folders from the repo, Docker images, build parameters / env vars, outputs from other tasks, … Typical tasks I have in mind are setup/build/test/deploy, which of course will typically depend on one another, thereby forming a pipeline or dependency graph.
- sandboxes/containerizes tasks by default (in particular: no access to repo file system / working copy, env vars, … beyond what's specified as inputs) but does provide easy escape hatches (for deployment pipelines, sharing venv/node_modules between task and working copy / IDE, …),
- by default automatically caches a task's output & logs for a given input, unless I explicitly tell it not to (again, deployment tasks!). Then, when running a task upon the user's request, it automatically figures out the dependency graph and runs only those tasks that have not been cached before. This includes the case of the task definition itself having changed. (Many tools allowing you to define tasks in a full-blown programming language struggle with detecting this reliably.)
- comes with monorepo support, so supports collecting definitions of e.g. the "test" task across subfolders/projects and running them all in parallel (as far as the dependency graph allows),
- is language-/ecosystem-agnostic, so that I can invoke whatever tool or shell script inside a given task.
- provides a sane configuration language (not YAML) – ideally a lightweight functional language that makes side effects very explicit,
- can be run both in CI and locally, without much setup effort. In fact, since the tool should be used as task runner for everything else in the repo, it should be easily bootstrappable after cloning the repo.
- can be integrated somewhat nicely with Github/GitLab/Azure DevOps/… (actually not that easy).
Dagger comes pretty close in terms of general idea but I'm not sure I like it so far.
Interesting to see LlamaIndex's journey from Poetry+Pants to uv+LlamaDev for managing their extensive monorepo. The speed improvements and better developer experience with `uv` are compelling. It's a good reminder of how tooling choices evolve with scale.
I recently did something similar. Using uv workspaces, I used the uv CLI's dependency graph to analyze the dependency tree then conditionally trigger CI workflows for affected projects. I wish there was a better way to access the uv dependency worktree other than parsing the `tree` like output
I use Github Actions triggers to pass flags to a monorepo dagger script to build and test the affected components. For example, if a commit touches the front- and back ends, rebuild both. If it only touches the front end, run integration tests using the latest backend without rebuilding it.
edit: spell out GHA
Yea this definitely makes sense for smaller monorepos. For us, we ended up writing our own dependency graph parser to figure out what tests to run (which is easy enough with a single language like python honestly)
Was bazel an option?
We used pants initially (which I believe is similar to bazel). And indeed the dependency graphing it does was very helpful, but other parts of the tool motivated us to create something more bespoke and debuggable (we were only using like 20% or less of the features pants offers)
GHA - GitHub Actions, right?
I agree! I hope uv introduces more tools for monorepos or refines the workspaces concept.
I saw workspaces require all dependencies to agree with eachother, which isn't quite possible in our repo
So just to let me get this straight: Does this new setup aim to make it easier to contribute to llamaindex submodules specifically?
Yes! For example, previously with pants, users would hit a lot of weird errors since how tests run with pants is different than running tests locally with pytest
We did not expect users to learn pants, but this often meant a lot of back and forth with maintainers to get PR tests working.
Should be much easier now!
I find it quite astonishing there is no go-to build system / task runner yet for handling small to medium-sized monorepos across ecosystems.
I want a tool that
- allows me to define tasks with inputs (+ secrets) and outputs. Inputs can be files & folders from the repo, Docker images, build parameters / env vars, outputs from other tasks, … Typical tasks I have in mind are setup/build/test/deploy, which of course will typically depend on one another, thereby forming a pipeline or dependency graph.
- sandboxes/containerizes tasks by default (in particular: no access to repo file system / working copy, env vars, … beyond what's specified as inputs) but does provide easy escape hatches (for deployment pipelines, sharing venv/node_modules between task and working copy / IDE, …),
- by default automatically caches a task's output & logs for a given input, unless I explicitly tell it not to (again, deployment tasks!). Then, when running a task upon the user's request, it automatically figures out the dependency graph and runs only those tasks that have not been cached before. This includes the case of the task definition itself having changed. (Many tools allowing you to define tasks in a full-blown programming language struggle with detecting this reliably.)
- comes with monorepo support, so supports collecting definitions of e.g. the "test" task across subfolders/projects and running them all in parallel (as far as the dependency graph allows),
- is language-/ecosystem-agnostic, so that I can invoke whatever tool or shell script inside a given task.
- provides a sane configuration language (not YAML) – ideally a lightweight functional language that makes side effects very explicit,
- can be run both in CI and locally, without much setup effort. In fact, since the tool should be used as task runner for everything else in the repo, it should be easily bootstrappable after cloning the repo.
- can be integrated somewhat nicely with Github/GitLab/Azure DevOps/… (actually not that easy).
Dagger comes pretty close in terms of general idea but I'm not sure I like it so far.
Interesting to see LlamaIndex's journey from Poetry+Pants to uv+LlamaDev for managing their extensive monorepo. The speed improvements and better developer experience with `uv` are compelling. It's a good reminder of how tooling choices evolve with scale.