Managing Gerber file releases for KiCad projects has always been a bit informal — manually exported, inconsistently packaged, and only loosely tied to any point in the git history. After getting to grips with GitHub Actions on a previous project, I wondered whether the same trigger-based approach could bring some proper structure to it.
My KiCad projects live in Git repositories, which in principle gives me the full benefit of commit history, branching, and tagging. In practice, I’ll be honest — the tagging side of things never really happened consistently. The intention was always there, but in the flow of iterating on a design, formally cutting a tagged release was the kind of step that was easy to skip. And without that discipline, there was no real structure around the manufacturing outputs either. Gerber files got exported from pcbnew into a Gerber directory, that directory got zipped up, and the archive went somewhere — but how it was packaged and where it ended up wasn’t consistent between projects, and the link between a specific set of files and a specific point in the design history was never more than approximate.
The inconsistency was the nagging part. It wasn’t so much the effort of the manual export process, more the faint uncertainty it left behind — not being entirely sure whether a set of files matched the current state of the board, or having to think about naming conventions each time rather than having something enforce them. I liked the idea of using Git tags to mark releases, I just wasn’t reliably doing it.
What changed was working on the Creating a Fritzing Part From a KiCad PCB project, which introduced me to GitHub Actions. Seeing how a workflow could be triggered automatically and do meaningful work without any manual intervention made me think about applying the same idea to the gerber release problem. If the act of pushing a tag could kick off the entire export and packaging process, the barrier to actually doing a proper release drops considerably — and the consistency problem solves itself.
The Approach
The solution I landed on uses GitHub Actions, which is well suited to this kind of triggered automation. The idea is that when a repository is tagged with a version in the form r#.#.# — say, r1.3.2 — a workflow kicks off that uses KiCad’s scripting interface to generate the gerber files, packages them into a zip archive, and attaches everything to a GitHub Release. The archive is named after the project and the tag, so something like atmega1608_dev-gerber-r1.3.2.zip — unambiguous and traceable.
There’s also a nice additional touch: the release tag gets passed through as a KiCad text variable called RELEASE, which means it can be referenced directly on the PCB itself. KiCad does have an internal variable REVISION which can also be used in silkscreens on the board. However I was not able to update this directly so I opted to pass the release tag into a KiCad text variable called RELEASE. Setting ${RELEASE} in the Revision of the title block will then reflect the release number automatically and existing references to REVISION will be updated respectively.


Making It Reusable
Rather than embed this workflow directly into each KiCad project, I wanted something I could install cleanly into any project without having to copy and re-edit files by hand each time. The result is a small module repository — kicad-release-automation-module — which holds the workflow template, a Python script that handles the actual gerber generation, and an example configuration file.
Installing it into a KiCad project is a single command:
./install.sh /path/to/your/kicad-project-repo
The script takes care of creating the necessary directory structure (.github/workflows/, scripts/) and copies across the workflow YAML and the Python generation script. It also drops in a release-artifacts.json configuration file if one doesn’t already exist — but importantly, it won’t overwrite one that does, so there’s no risk of clobbering a project-specific configuration on a subsequent install.
The release-artifacts.json is where you tell the module about your specific project: the path to the .kicad_pro file, and which layers you want included in the gerber export. Once that’s set up and committed, the project is ready to go.
The diagram below shows what gets installed into a target KiCad project repository and how the pieces relate to one another.
+-- examples| || +-- release-artifacts.json|+-- scripts| || +-- generate_release_artifacts.py|+-- templates| || +-- .github| || +-- workflows| || +-- release-gerbers.yml
Testing It Out
I used the atmega1608_dev repository as the test bed – a breakout and development board for the ATmega1608 that I’ve been working on. I added the RELEASE KiCad text variable and set the Revision in the title block to ${RELEASE}. Running through a few release tags gave me a good sense of where things needed adjusting. One iteration, for example, involved switching the CI workflow to use a KiCad nightly build, which was needed to correctly handle the board file format I was working with. Another was a straightforward fix to ensure the project name was correctly prefixed to the gerber zip archive — something that only becomes obvious when you look at the actual release assets and realise the naming isn’t quite right.
After working through those iterations, the releases page for the atmega1608_dev repository now shows cleanly versioned assets, generated automatically from the tag. It’s satisfying to see r1.3.1 and r1.3.2 sitting there with their associated zip files, knowing that each one corresponds to an exact, reproducible snapshot of the board at that point in time.
Conclusion
It’s a small piece of infrastructure, but it addresses something that had been quietly bothering me for a while. I can also see scope for further improvements such as running DRCs or exporting other artefacts for documentation purposes. Since I am in the hobby domain, I will stick with what I have here for a while to bed in the workflow and assess the further changes.
