Recently there have been a few questions about how to share code between charms in a charm monorepo. As far as I’m aware, for now, there are three general approaches that are viable.
-
Publish the common code as a package and depend on the released version. It can still live in the monorepo. It could even be depended on as a
gitdependency (if you don’t want to publish to PyPI), though this may get confusing (may want to patch in testing to use latest local version, presumably need to depend on different versions in different channels). This approach doesn’t synergise with the benefits of monorepos (it’s a great fit for polyrepos – one repo per charm + one for the shared code) but the other approaches have their own pain points too, so there may be some appeal. The indirection of depending on a published package instead of on local files has the benefit of making it easy to split the charms into their own separate repos in future. I don’t know if any charm monorepos currently use this approach, but a good example of this approach for polyrepos is mongo-single-kernel-library. -
Vendor the common code into the charm directories. Treat the top-level common code as the source of truth, and ensure that the vendored copies are up-to-date in CI. You’ll want to have PRs fail if the code is out of sync, and probably automatically sync the vendored code when running tests, etc. The vault charm monorepo currently uses this approach. The big downside here is the noisy diffs when updating the common code. Also some friction with avoiding failures due to code being out of sync (easy if you use
pre-commitor similar though). -
If you have a charm directory in your repository, sibling folders aren’t visible to it during
charmcraft pack, becausecharmcraftmounts the project directory when packing. You can currently get around this by runningcharmcraft packin the repository root instead. Here’s a draft PR for the vault repo switching to this approach. Note particularly how charmcraft is wrapped in CI, and how the charmcraft.yaml files are changed so they can be interpreted at the repository root, and include the shared code.
You may have heard recommendations to avoid wrapping charmcraft pack, but the current thinking is that 3’s way of wrapping it may be compatible with build processes that invoke charmcraft pack directly by creating repositories for each charm with the appropriate top-level charmcraft.yaml symlink checked in if needed. Methods 1 and 2 don’t wrap charmcraft pack, but it’s not clear how/if they will be compatible with build processes directly invoking charmcraft pack either.
If you’re already using a different approach to code sharing in a charm monorepo, you probably don’t need to change anything at this time, but if you’re thinking of investing significant effort into a different approach it might be worth getting in touch with Charm Tech first (e.g. on matrix).