Below is a rewritten draft that balances technical detail with a bit of personal reflection on the choices and lessons learned along the way. Feel free to tweak any sections to better match your own voice or specific insights!
Implementing Automatic Releases with Google’s release-please-action
Automating your release cycle isn’t just about speed—it’s about consistency, traceability, and freeing up mental bandwidth for higher-value work. Here’s how I integrated Google’s
release-please-action
into my CI/CD pipeline, and what I discovered along the way.
1. Branching for CI/CD Enhancements
Rather than bolting release logic onto my existing main
branch, I spun up a focused feature branch:
git switch -c ci/add-release-please
That simple act of isolation paid dividends: I could iterate on the workflow without destabilizing any active development or polluting merge history. Small discipline, big payoff.
2. Defining the Release Workflow
I kicked off by creating the workflow file under .github/workflows/release-please.yaml
:
name: Release Please
on:
push:
branches:
- main
jobs:
release-please:
runs-on: ubuntu-latest
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
token: "${{ secrets.DEVOPS_STUDY_APP }}"
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
Key technical points:
-
Trigger: Only fires on merges into
main
(no direct commits), ensuring release PRs are exclusively merge-driven. -
Runner: Ubuntu-latest to guarantee a standard Linux environment.
-
Action:
@v4
of Google’s action parses Conventional Commits, auto-bumps versions, and scaffolds changelogs.
Reflection: At first glance, that YAML felt a bit sparse—I worried I’d missed essential steps. But the beauty of
release-please
is its minimalism: it encapsulates changelog generation, tagging, and draft releases all in one.
3. GitHub Authentication Trade-Offs
By default, workflows use GITHUB_TOKEN
, but that comes with a gotcha: any events triggered by actions running under GITHUB_TOKEN
won’t spin up downstream workflows. In my case, I want a Docker-build job to pick up after a new release tag is created.
-
GITHUB_TOKEN
-
✔️ Easy, pre-built
-
❌ No recursive triggers
-
-
Personal Access Token (PAT)
-
✔️ Allows follow-on workflows
-
❌ Tied to a user account, less ideal for org-wide security
-
-
GitHub App / Deploy Key
-
✔️ Ideal for enterprise, fine-grained scopes
-
❌ More setup overhead
-
In the end I opted for a PAT stored as DEVOPS_STUDY_APP
. It felt like a pragmatic middle ground for my solo project, with a clear TODO to migrate to a GitHub App in a production setting.
4. Crafting the release-please
Config
To handle a monorepo structure, I defined a release-please-config.json
in the repo root:
{
"packages": {
"src/backend": {
"release-type": "python",
"package-name": "study-tracker-backend",
"component": "backend",
"include-component-in-tag": true,
"changelog-path": "CHANGELOG.md",
"extra-files": [
{
"type": "toml",
"path": "uv.lock",
"jsonpath": "$.package[?(@.name.value=='study-tracker-backend')].version"
}
]
}
}
}
-
release-type
signals a Python project (updatespyproject.toml
). -
Multiple components: even in a mono-repo, this lets me tag each sub-project distinctly.
-
extra-files
uses JSONPath to bump the version inuv.lock
alongside the TOML file.
Reflection: Figuring out that JSONPath expression took a few trial runs—I underestimated how picky it would be. In retrospect, writing a quick Python snippet to validate the path against a sample file would have saved time.
5. Initial Manifest: Laying the Groundwork
release-please
needs to know your starting versions. I committed a minimal .release-please-manifest.json
:
{
"src/backend": "0.0.0"
}
From here on out, each release PR will bump this automatically, so I never have to manually track patch/minor/major increments again.
6. Lessons Learned & Next Steps
-
Isolate CI work: Treat your CI as code in its own branch until you’re confident.
-
Beware default tokens: GitHub’s sensible default can quietly block subsequent workflows—plan your authentication up front.
-
Test JSONPath locally: Complex file updates deserve local validation scripts.
-
Iterate on component logic: Mono-repos shine with per-component tagging, but the config can get verbose.
Going forward, I’ll:
-
Chain a Docker build-and-push job off the
release.created
event. -
Explore GitHub App authentication for org-wide consistency.
-
Sweep the repo for any stray direct commits to
main
and tighten branch protections.
By automating releases this way, I’ve reclaimed hours of manual PR/merge/tag gymnastics—and gained a deeper appreciation for how a well-designed GitHub Action can become the backbone of a rock-solid CI/CD pipeline.