No description
  • Clojure 76.9%
  • Starlark 23.1%
Find a file
James Williams 718150fee1
All checks were successful
/ test (push) Successful in 56s
feat: logging via telemere, no more println
2026-08-18 23:56:42 +01:00
.forgejo/workflows feat: ci 2026-07-05 22:20:23 +01:00
bazel feat: moved graal build time dependency out of deps edn into bazel 2026-07-09 21:17:33 +01:00
design draft: exclusions 2026-08-16 19:12:35 +01:00
resources feat: update resources 2026-08-16 22:37:05 +01:00
src feat: logging via telemere, no more println 2026-08-18 23:56:42 +01:00
test feat: performance improvements through reduced re-sorts and allocations 2026-08-17 18:50:58 +01:00
.bazeliskrc init 2026-04-30 20:15:38 +01:00
.bazelrc Comment out rules_graalvm (#1) 2026-07-25 15:54:26 +02:00
.gitignore draft: find sorted things with subseq 2026-06-16 22:50:38 +01:00
BUILD.bazel feat: rename deps for use in other bazel repos 2026-07-29 15:31:07 +01:00
deps.edn feat: logging via telemere, no more println 2026-08-18 23:56:42 +01:00
LICENCE draft: exclusions 2026-08-16 19:12:35 +01:00
MODULE.bazel fix: bump rules_clojure to fix incorrect imports error 2026-08-03 14:05:36 +01:00
MODULE.bazel.lock Comment out rules_graalvm (#1) 2026-07-25 15:54:26 +02:00
README.md doc: update README for new config syntax 2026-08-16 22:15:20 +01:00
TODO.md doc: update TODO 2026-08-16 22:37:16 +01:00

Independent

Independent is a tool for developers using Bazel, designed to detect and prevent transitive dependencies forming where they are not wanted.

Introduction

You may have several beliefs about your codebase:

  • "the release binary does not contain the development tooling scripts"
  • "the backend tests do not depend on the fonts used in the frontend"
  • "the source code doesn't depend on any tests"

How are these beliefs enforced? To that end, what ensures that a transitive dependency never forms that violates these? This quickly becomes difficult to reason about after any change, especially in large and complex repositories.

Bazel is already a great tool for management and analysis of dependencies. Independent aims to leverage your use of Bazel to make it the best tool for enforcing these kinds of statements.

Alternatives

Every tool must justify itself, so we should first consider other methods of achieving this.

Package Grouping and Visibility

Bazel has a few built-in mechanisms for preventing immediate dependencies.

  • package_groups
  • package_visibility

These are great for preventing immediate dependencies where they are not wanted, but do not prevent transitive dependencies.

For example, we can use package_visibility to say A is visible to all except Z. This does not prevent Z depending on B, which depends on A, violating our desired behaviour.

Bazel Query Language

Bazel also has a query language for detecting immediate and transitive dependencies.

  • deps, rdeps, somepath, allpaths

These allow you to detect the existence of paths between two packages, however somepath will only display a single path, and allpaths will clobber together many different paths into one chain rather than listing each of the paths separately. This makes understanding the paths and removing them difficult, which is the immediate next step after detecting an error.

Usage

Independent takes two main inputs, a config file and a dependency graph file.

Config File Format

The config file is structured like the following.

{
  "version": "0",
  "bans": [
    {
      "depender": {
        "pattern": "//src/...",
        "exclusions": ["//src/debug/..."]
      },
      "dependees": [
        {
          "pattern": "//test/...",
        }
      ]
    }
  ]
}

Each ban is made up of a single depender and multiple dependees.

{
  "depender": {
    "pattern": "//src/...",
    "exclusions": ["//src/debug/..."]
  },
  "dependees": [
    {
      "pattern": "//test/...",
    }
  ]
}

means any target under '//src' may not depend on any target under '//test', with an 'exclusion' that targets under '//src/debug/' are ignored.

Note that the elipses syntax is the same as when invoking Bazel via the command line - '//src/...' expands to include all targets in all packages under '//src', not just '//src'.

We do not yet have support for the ':all' syntax e.g. '//src:all'.

Dependency Graph

A dependency graph file is produced by running a Bazel query command.

From the Bazel documentation:

Traditional Bazel query runs on the post-loading phase target graph and therefore has no concept of configurations and their related concepts. Notably, it doesn't correctly resolve select statements and instead returns all possible resolutions of selects. However, the configurable query environment, cquery, properly handles configurations but doesn't provide all of the functionality of this original query. https://bazel.build/query/language#cquery

For our uses, 'all possible resolution of selects' is quite suitable. Therefore, we recommend producing the graph file by running the following.

bazel query //... --output=streamed_jsonproto > dependency-graph.json

If you find yourself in need of cquery, you can produce a suitable dependency graph file by running the output through (jq)[https://jqlang.org/] first as follows.

bazel cquery //... --output=jsonproto | jq --compact-output .results.[].target > dependency-graph.json

The '--compact-output' is necessary as Independent expects each line in the dependency graph file to be a full JSON object.