GoulvenClech
Sampo - automate changelogs, versioning, and publishing for Elixir (and multi-ecosystem monorepos)
Hey everyone ![]()
I’m excited to share Sampo, a tool suite to automate changelogs, versioning, and publishing—even for monorepos across multiple package registries. It currently Elixir (Hex) packages, alongside Rust (Crates), JavaScript/TypeScript (npm), Python (PyPI), PHP (Packagist)… And more coming soon!
In a nutshell
Sampo is a CLI, a GitHub App, and a GitHub Action, that automatically detects packages in your repository, and uses changesets (markdown files describing changes explicitly) to bump versions (in SemVer format), generate changelogs (human-readable files listing changes), and publish packages (to their respective registries). It’s designed to be easy to opt-in and opt-out, with minimal configuration required, sensible defaults, and no assumptions/constraints on your workflow (except using SemVer).
If you’ve ever struggled with keeping user-facing changelogs updated, coordinating version bumps across dependent packages, or automating your publishing process… Sampo might be the tool you were looking for!
In more details
Sampo is a CLI tool (for now only available on Cargo, but soon on your favorite OS package manager), a Github Action, and a GitHub App that work together to streamline your release process.
Sampo automatically discovers Elixir (and Rust/Javascript/Typescript) packages in your workspace (including umbrella projects), respects your mix.exs configuration, and calls mix hex.publish under the hood (you still need to have your HEX_API_KEY environment variable or Mix’s auth configured).
Like Hex.pm, Sampo enforces Semantic Versioning (SemVer) to indicate the nature of changes in each release. Versions follow the MAJOR.MINOR.PATCH format with three bump levels: patch — Bug fixes and backwards-compatible changes; minor — New features that are backwards-compatible: major — Breaking changes that are not backwards-compatible.
For example, a user can safely update from version 1.2.3 to 1.2.4 (patch) or 1.3.0 (minor), but should review changes before updating to 2.0.0 (major). Sampo also supports pre-release versions (e.g., 1.2.0-rc.1) as SemVer §9 conventions.
For each change made to your packages, you can use sampo add to create a new changeset file, the CLI will prompt you to select which packages were affected, the type of version bump (patch/minor/major), and a description of the change. You can use Sampo GitHub bot to get reminders on each PR without a changeset.
---
hex/back-end: minor
npm/web-app: patch
---
A helpful description of the change, to be read by your users.
Those changesets are markdown file, inspired by Changesets and Lerna, stored in the .sampo/changesets/ directory. Sampo consumes those changesets to determine version bumps, and update changelogs—a human-readable file listing all changes for each released version, at the root of every package.
# Example
## 0.2.0 — 2024-06-20
### Minor changes
- [abcdefg](link/to/commit) A helpful description of the changes. — Thanks @user!
## 0.1.1 — 2024-05-12
### Patch changes
- [hijklmn](link/to/commit) A brief description of the fix. — Thanks @first-time-contributor for their first contribution!
... previous entries ...
Run sampo release to process all pending changesets, bump package versions, and update changelogs. This can be automated in CI/CD pipelines using Sampo GitHub Action, with a release PR created whenever changesets are detected. As long as the release is not finalized, you can continue to add changesets and re-run the sampo release command. Sampo will update package versions and pending changelogs accordingly.
Finally, run sampo publish to publish updated packages to their respective registries and tag the current versions. This step can also be automated in CI/CD pipelines using Sampo GitHub Action, by merging the release PR, and can also create GitHub Releases and Discussions for each new tag.
Philosophy
Sampo is designed to be an helpful, reliable, and flexible tool that users can trust.
We want to make it easy to get started, with minimal configuration, sensible defaults, and automated workflows. At the same time, we want to provide rich configuration options, and flexible workflows to cover more advanced use cases. Finally, Sampo should be easy to opt in and opt out, with little to none assumptions, conventions to follow, or lock-ins.
It’s fully open-source, and we welcome contributions and feedback from the community! If you give it a try, please let us know what you think, and if we can do anything to improve the Elixir support ![]()
And leave a star on GitHub if you find it useful!
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #performance










First 10 of 15 Posts
GoulvenClech
Hi everyone
Since I introduced Sampo here, I’ve written a longer article that goes into the motivations behind the project, the design philosophy, and some ideas for what’s next. I hope you find this interesting!
dimitarvp
Very timely, I’m recently doing one final pass out evaluating various such tools. Thanks for releasing yours!
GoulvenClech
Thanks for checking it out!
If you’re comparing options, there’s an « Alternatives » section in my article that outlines how Sampo compare to other tools: https://goulven-clech.dev/2025/introducing-sampo#alternatives.
I’m in active development, so if you find anything missing or have specific needs, please let me know and I’ll see what I can do
dimitarvp
Hey, I finally got around to this. I wanted to tell you that after reviewing
sampoI found it a bit heavy for my needs. I am mostly looking for manually bumping whichever part of the version I want and I want to be in charge of deciding that. Add also git tagging and GitHub release notes. That’s mostly what I need.While I am open to have a tool auto-generate more things for me in the future, I still am not sure I would want it to decide for me whether a major, minor, or patch version must be bumped. I am also not 100% sold on conventional commits which many other tools use to make that call f.ex. Ash’s creator @zachdaniel’s GitHub - zachdaniel/git_ops: A tool for version and changelog management in Elixir via conventional commits. · GitHub tool, though his allows us to only opt in to version management inside
mix.exswhen we want to.Thanks for your contribution, the tools looks amazing and I will always kind of shill for Rust tools because a multitude of them have been a net positive to my workflows. It’s just that the library that I am about to release is a much more manual and hands-on in nature when it comes to releases. I’ll reevaluate
sampoin the near future because I’ll start another library soon after the first one gets released. And I might need it for some customer projects, too.GoulvenClech
Hi @dimitarvp ,
Sorry for the slow reply, my weeks have been pretty packed lately and I’m only now catching up properly.
Thanks again for taking the time to check out Sampo. Just to clarify a couple of things :
Sampo never decides the bump level on its own: you explicitly declare the bump (
patch,minor, ormajor), the packages affected, and the user-facing description of the change, either via the CLI or by editing the changeset file by hand. Sampo then just does the deterministic SemVer math from there. There’s no hidden logic trying to guess what the next version “should” be.Sampo also doesn’t depend on Conventional Commits at all. One of the design goals was precisely to avoid mixing the technical history in git (written for and by contributors) with the API changelog (written for users, and sometimes reviewed/edited by product/docs owners). Changesets are the single source of truth, and the changelog and versions are generated from those.
It’s also meant to be modular. You can use it only to bump versions, generate changelogs, create git tags and GitHub releases, without letting it publish to registries. Similarly, you can stick to the CLI locally, and ignore both the GitHub App and the GitHub Action. The intent is that Sampo should be easy to opt in and out of, with as few assumptions, conventions, or lock-ins as possible. If there are still rough edges or sources of friction that made it feel “too heavy” in practice, I’m genuinely interested in hearing about them so I can keep smoothing things out
dimitarvp
Thanks for the follow-up, much appreciated.
Sadly can’t remember now but I think it was mostly the volume of the blog post + the changesets thing.
But seeing as I had to spend a few hours on a bash script together with an LLM to just be able to release my own package with GIT tagging and bumping+syncing a version between
mix.exsandCargo.tomland nothing much else – and the shell script still ballooned to nearly 200 lines due to having to catch error and double-check errors – then re-reviewingsampostarts making more and more sense.Not a remark towards you or the tool – but I find myself very puzzled at the current state of affairs. To be more concrete, these are the steps I need to release my package:
mix.exsCargo.toml(my library has a Rust NIF)mix hex.publishMy script is at nearly 200 lines and I still do the last 3 manually.
My main point of criticism, to your tool or any other, is that this is a mechanical logistical work that should have minimal involvement from the programmer.
This is my bucket list above. I use
cargo-editto directly set the version in theCargo.tomlfile andmix_versionto change the version inmix.exs– I don’t want to rely on UNIX text CLI tools for it. Hopefully that list is useful for you.For the record, I dig the idea of the changesets a lot but I’ve dragged my feet on releasing my library for years – bad health, general being super busy, family emergencies and previous burnout were involved – so I just wanted to get it over with. I am very likely to revisit
samposoon-ish.dimitarvp
Oh, just for the record for you or anyone else, here’s the bash abomination that me and Gemini worked on for about an hour in order to cover most failure modes and use proper native tools and not just use brittle CLI text manipulation: xqlite/scripts/release.sh at 5fd91f15c748f18bf5a0a91ebe39df252ef9f5ed · dimitarvp/xqlite · GitHub
Monstrosity. Just to bump a version in two files and do a little GIT magic.
It’s almost 2026. All this stuff should have been figured out and written in stone 10 years ago. Shameful. But maybe when I retire I’ll have the time.
GoulvenClech
Thanks for sharing all this context, I really get those periods where everything stacks up, and you’re just trying to ship something…
Tbh the blog post is really there as a deep dive into Sampo, but as I mention in the first paragraphs, it’s absolutely not required reading to use the tool. The actual documentation is in the repo’s README. Sadly writing great docs isn’t exactly my superpower, but I try to keep it concise, practical, and complete enough
In practice, getting started with Sampo is very lightweight: you run
sampo initonce, thensampo addeach time you want to add a changeset. The CLI guides you through selecting the bump level, the affected package(s), and the user-facing description, then you just commit the generated markdown file in the PR or commit where the change happens. The defaults should be fine for most projects, so you don’t need to touch the config file at all; but if you do, the config options are described here.For the release and publishing side, the easiest path is usually the GitHub Action. If you want to try Sampo quickly, you can basically hand your AI agent the main README (or even just the repo link) and/or the Action README, and let it wire up. Once it’s in place, Sampo will automatically open a release PR whenever there are unconsumed changesets, and merging that PR will take care of the changelog, GitHub releases, git tags, and publishing to Hex (with your Hex token set as an environment variable).
If you do end up revisiting Sampo and run into any friction, I’m genuinely interested in hearing about it. Your “bucket list” of steps is exactly the kind of workflow I want to make boring and reliable!
dimitarvp
I did not notice this. Thanks, this actually makes me want to try it again sooner.
GoulvenClech
You can actually see the GitHub Action in action (lol) on Bruits’ public repos, both Maudit and Sampo itself have currently an automatic release PR open, and there are quite a few closed ones in the history as well.