Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How does semantic-release integrate with Trunk-based development? #1529

Open
ghost opened this issue Apr 16, 2020 · 20 comments
Open

How does semantic-release integrate with Trunk-based development? #1529

ghost opened this issue Apr 16, 2020 · 20 comments

Comments

@ghost
Copy link

ghost commented Apr 16, 2020

So I stumbled across this CI tool and wanted to integrate it in my new monorepo project ever since. Going through already closed "issues" asking about workflow-specific details, I've seen the recommendation to use GitHub Flow, but there also were mentions of Trunk-based development (which I prefer) being semantic-release compatible. However, I'm not sure where to start with it and how I should go about versioning, the docs are very scarce regarding information on this.

  • Should I run semantic-release on both my trunk/master and my (frozen) release branches, or only on the release branches to bump the version?
  • Should I use semantic-release at all for this? I've heard of people just keeping their version at 0 to escape versioning hell in a very fast-paced environment like trunk based development
  • Should I keep old release branches? How should I "version" their names then (release/X.X.X)?
  • Should I include feature/X branches in semantic-release?
  • Is it considered a good practice to automatically bump version numbers of dependencies within the same monorepo to their newest respective versions/keep them at @latest?
  • How do pre-releases and distribution channels play with this? I haven't worked with these concepts until now

I'm eager to hear from more experienced developers at this topic!

@oarcm
Copy link

oarcm commented May 12, 2020

Following. I'm in the same boat.

@BearKid
Copy link

BearKid commented Jul 24, 2020

my English is poor,after I read the document of semantic-release, I am confused whether semantic-release always trigger
a release on every commit ?
If the answer is yes, is it right that {major}.{minor}.{patch} version number will be bumped frequently ? the problem will aslo occur in Trunk-based development.

I am a fans of TBD, when implementing CD combined with TBD branch model, I have found that semver 2.0 specification makes me hard to acheive the goal :

I want that each commit can produce a releasable build with semantic version number ,
but only release a good looking ` {major}.{minor}.{patch}` version number after I collect enough commits.

I suppose there is something like that :

【commit-1】tagged as v1.0.0
【commit-2】no tag,but built as artifact v1.0.0-after.1
【commit-3】no tag,but built as artifact v1.0.0-after.2
【commit-4】tagged as v1.0.1 or v1.1.0 or v2.0.0
  • there is no pre-prelease version like v1.0.1-RC1.
  • we can not detemine which major/minor/patch field shoud be bumped until we plan to do a key release on commit-4.
  • commit-2 is treated as the 1st commit after v1.0.0
  • commit-3 is treated as the 2nd commit after v1.0.0
  • semver does not support this as it treats v1.0.0 > v1.0.0-after.1

alought semver does not fit what I need, I want to know whether the semantic-release project supports my idea and is willing to implement.

@Gyomu
Copy link

Gyomu commented Oct 27, 2020

Hello everyone,
Following a few answers on related questions like this one on commits, tagging and release naming for trunk based approach (https://stackoverflow.com/questions/54050444/release-version-commit-in-trunk-based-development), I'd say a first approach would be to focus semantic-release on triggering with release/N.N.x branches only (or release/*)

You might eventually go for a semantic-release trigger for master as well, but in a pre-release fashion. The idea behind this would be to always have your code in trunk "releasable" (as pre-release), but not released yet with semantic versioning.

This kind of approach would take into account two aspects of both guidelines:

  • with trunk-based development, your code is ready for release at all times, nothing should be committed that would break the code ideally speaking
  • with semantic-release and semantic versioning, you provide users (technical users for API consumption for example, or business users for higher end software) a meaning (hence semantic) between commits, version, changelog and features implemented.

Also on this road and building bits by bits. Hope these thoughts can nurture your own.

@Elashi
Copy link

Elashi commented Feb 23, 2021

Wonderful question that is not fully answered yet !

@maxkomarychev
Copy link

tl;dr version:

  1. keep master in pre-release version.
  2. when cutting a release - immediately bump master to next major/minor prerelease

disclaimer: I'm not using semantic-release directly but I am working with repository managed with lerna which seems to be pretty close, so here's my experience:

Should I run semantic-release on both my trunk/master and my (frozen) release branches, or only on the release branches to bump the version?

when we cut mater branch for release we immediately increase version in master. for next minor version (by default). so if current master is "1.0.0-alpha.0" when we cut "release/1.0" all code in there is considered as release candidate, while master becomes "1.1.0-alpha.0"

Should I use semantic-release at all for this? I've heard of people just keeping their version at 0 to escape versioning hell in a very fast-paced environment like trunk based development

we're releasing every week and haven't run into any issues

Should I keep old release branches? How should I "version" their names then (release/X.X.X)?

we name release branches after "major.minor": "release/1.0", "release/1.1" etc

Is it considered a good practice to automatically bump version numbers of dependencies within the same monorepo to their newest respective versions/keep them at @latest?

it really does depend on specific situation imo: we do not version packages independently and we're always pinning versions of between internal libraries within same monorepo

hope this helps :)

@Gyomu
Copy link

Gyomu commented Feb 23, 2021

After trying it in a real work life context, I sadly must report that the hypothesis we had won't work. Semantic-release works on a few conventions that requires absolutely that you release on master.
There's no way with the way the code is currently architectured to bypass this.

So basically the only possibility is to strictly follow the current wiki recommendations: master/main is the default branch where you release stuff. Pre-release will happen on branches names next and the likes. The 1.0.x branch naming convention only work for maintenance, not for release actions.

So basically yes the different comments before are good reminders on how we'd do trunk based development. But it's not what semantic-release can do.

If you want to avoid yourself a few headaches, do not try to customize much the branch naming and so on. You might manage to release the first version fine, but the subsequent increments (the major ones being the worst) won't work because of the logic behind semantic-release.

The only way to achieve semantic release (the concept, not the tool) to trunk based development would be to write your own tool.
Basically: parsing the git log, extract meaningful information out of the commit messages, and act based on that.

@mowies
Copy link
Contributor

mowies commented Jul 15, 2021

Can somebody here point me to a semantic-release config file based on trunk based development?
I am trying to build a PoC with exactly that use case

@travi
Copy link
Member

travi commented Jul 19, 2021

apologies for neglecting this thread for so long. i have been meaning to respond but get lost each time in attempt to relate the questions in this thread to my understanding of trunk-based development.

my high-level answer is that semantic-release is absolutely compatible with trunk-based development. in fact, i would say that trunk-based development essentially defines the core approach of semantic-release's release flow expectations. semantic-release assumes that the trunk is always releasable. based on that assumption, if there are changes committed to trunk that impact a consumer of the package, a release happens immediately.

until this thread came up, this was the only flavor of trunk-based development that i was aware of. it sounds like the questions in this thread are asking about a separate variant of trunk-based development that i dont think i fully have my head around.

i think the key difference may come from this point from the list of caveats on the website:

Depending on the intended release cadence, there may be release branches that are cut from the trunk on a just-in-time basis, are ‘hardened’ before a release (without that being a team activity), and those branches are deleted some time after release. Alternatively, there may also be no release branches if the team is releasing from Trunk, and choosing a “fix forward” strategy for bug fixes. Releasing from trunk is also for high-throughput teams, too.

i use the latter approach and that is the one that i suggest is compatible with semantic-release. i dont understand what the value of creating the (temporary since it is mentioned that "those branches are deleted some time after release") release branches over releasing from trunk and tagging the commit where the release happened. i don't understand what hardening practices would be done from the branch that dont result in commits to the branch. if no commits are made to the branch, why branch at all?

@Gyomu
Copy link

Gyomu commented Jul 19, 2021

One use case I'm thinking of right now is when your customers actually aren't expecting 1 feature but a set of features. It's not a matter of "the best way to do it" because there is none (otherwise we would all do that by now) but how trunk based and its flavors can easily provide a way to answer a specific need.
Tldr: the trunk based flavors and potentially semantic release support for it is all about enforcing a few basic concepts while allowing enough versatility to adapt to custom organisational needs.

@travi
Copy link
Member

travi commented Jul 19, 2021

aren't expecting 1 feature but a set of features

if this is the goal you are after, semantic-release may not be the tool for you. semantic-release is opinionated and is not intended to be a general workflow tool. as mentioned earlier, a core goal of semantic-release is to encourage immediate release of features that are ready for consumption. keeping the trunk always deployable is a key aspect of this and we do not want to introduce delay in making those consumable features available. if a consumer wants to wait and batch what they bring in, they can choose to update later, but there is no reason to avoid making them available.

the closest to making feature batches like this work that is supported by semantic-release is to batch the changes in a larger feature branch and only merge that branch into the trunk when you are ready for release. i don't recommend this approach since i would rather encourage the separate release of each individual feature, especially to avoid blocking some behind other things in a long-lived feature branch.

@Gyomu
Copy link

Gyomu commented Jul 19, 2021

Yes exactly. Hence to me the answer to this issue would be that semantic-release as a tool is not fit for trunk based as it is described in the original website but fit only one of its flavor.
But I urge people in industries (not necessarily where software is the main activity) to consider the concept of semantic release as we do with semantic versioning, independently of the tool to achieve automated release including automatically generated versions, based on meaningful commits.

@travi
Copy link
Member

travi commented Jul 19, 2021

semantic-release as a tool is not fit for trunk based as it is described in the original website but fit only one of its flavor.

i think it is worth calling out that semantic-release is suitable for trunk-based development, but not all flavors.

semantic-release is very well suited for, and directly targeted at supporting release from trunk. also worth noting, as mentioned on that docs page, branches can be made retroactively. this is where our support for maintenance releases comes in.

semantic-release does not support branch for release. this is partly because semantic-release determines the next version based on the previous version tag in the history of the commit that triggers the next release. tags in branches that are not merged to trunk prevent semantic-release from knowing about such previous versions. this is designed this way intentionally because of two things:

  • it enables version bumps to be automatic based on the described impact of each change (fix, feat, chore, etc)
  • it encourages continuous-deployment/continuous-publishing/continuous-releases (note that the docs page that it is mentioned that "CD teams do not do release branches", which aligns with the reason why we do not support this model)

@mowies
Copy link
Contributor

mowies commented Jul 20, 2021

Following a few answers on related questions like this one on commits, tagging and release naming for trunk based approach (stackoverflow.com/questions/54050444/release-version-commit-in-trunk-based-development), I'd say a first approach would be to focus semantic-release on triggering with release/N.N.x branches only (or release/*)

You might eventually go for a semantic-release trigger for master as well, but in a pre-release fashion. The idea behind this would be to always have your code in trunk "releasable" (as pre-release), but not released yet with semantic versioning.

@Gyomu This is the approach I would like to follow but I can't find a way to get the config right. e.g. release/* or release/N.x branches is always interpreted as maintenance branches by semantic-release which means that I can't release from those branches if there wasn't an initial release on my trunk branch first.

My goal would be to have main/master set to prerelease and have release branches in the form of release-N.N with the initial N.N.0 release and all subsequent N.N.x releases on one branch.

@Gyomu
Copy link

Gyomu commented Jul 20, 2021

@mowies it is basically the use case I needed too but in the end it did not work out because of the opinionated naming convention detected by the inner code of semantic-release. Any branch with a pattern N.x or N.N.x will be considered as maintenance, independently of an eventual explicit configuration by the user.
I found out it could work if you could explicitly define your branching scheme and behaviors in the .releaserc file.
But I haven't looked into this past this first analysis, like the eventual implication on the rest of the semantic-release behavior because of this.
In the end since time was of the essence, I went with releases from master only to still benefit from the automation capabilities for semantic versioning, changelog generation and so on. Hopefully it worked well with my team maturity on the release and CI/CD topic.

Note that you could implement such a behaviour without semantic-release, with a clever pipeline and the use of the inner components of semantic-release such as the commit-analyzer plugin for example.

@mowies
Copy link
Contributor

mowies commented Jul 20, 2021

Aha thanks for the insights :)
I really wanted to have a cleaner structure of where tags are pushed in my repo. I find it not very intuitive that you can have bugfix releases distributed over master/main and some eventual bugfix branch at the same time.

Currently, I am looking into the usefulness of using inner semantic-release components vs. other alternative packages like standard-version or vercel/release

@eladchen
Copy link

Hi,

Pretty sure this could help: https://github.com/abstracter-io/atomic-release

@eladchen
Copy link

eladchen commented Nov 3, 2021

Hi 👋🏻

Let me share my take on how to version each build artefact & branching strategies.

Library workflow

A build which produces a library artefact is the ideal candidate for semantic versioning, it conveys meaning to us programmers.

Programmers can choose which version of the library they want to use.
For example:
npm install package@version

The branching strategy i recommend, is to have a branch for each release channel which allows non
stable versions such "beta" / "alpha" / "whatever" to be developed and merged at a later point
in order to avoid changing the published stable version too often.

In other words:

  • A stable branch
    New commits result in a release to the "stable" channel ("latest" in the case of NPM)

    main branch -> new commit -> build & bump version "1.0.0" to "1.1.0" -> tag "v1.1.0" -> NPM publish
    main branch -> new commit -> build & bump version "1.1.0" to "1.2.0" -> tag "v1.2.0" -> NPM publish

  • A branch for each release channel (semantic-release lingo):
    New commis result in a release to a release channel (NPM dist-tag)

    beta branch -> new commit -> build & bump version "1.0.0" to "1.0.0-beta.0" -> tag "v1.0.0-beta.0" -> NPM publish

Applications workflow

A build which produces an application artefact can have any version you want, it can use the git hash, the date
or even a semantic version.

Users can not choose to use a specific version of an application.
For example:
https://aliexpress.com
OR
Visiting an app store and installing the latest AliExpress native application

The branching strategy I recommend, is to have a single branch (aka trunk-based development) and using
the git hash to version each released artefact.

The problem with using semantic-release and trunk-based development is that versions will
change way too often (and will create a tag each time a commit land on the master branch)


Not to step on anyones toes, This is exactly why atomic-release was created.
Using it, one can create a custom release strategy.

An application strategy can be created where each release does not create a tag, the version is the GIT hash (or the date, whatever floats your boat), and last but not least: A failure during the release rolls back any actions taken.

There is also a built in strategy to release NPM packages, similar to what the (much appreciated) semantic-release tool does.

Disclaimer: I'm the author of atomic-release

@edgetools
Copy link

I found this thread from Google and it's very insightful - I believe some clarification or discussion around terms may be useful

From my perspective, one of the primary goals of trunk-based development (or releasing from trunk) is that you are typically intending to decouple the actual 'release' (e.g. when PMs or Marketing get involved) from the merging of code. Branching, merges, and conflict resolution is the enemy of velocity, and so by continuously integrating into the trunk a lot of that pain is dealt with up front.

Bringing branching back into the matter afterwards seems like a step back from CI, especially as mentioned above, if you're ending up with additional commits into these release branches.

I believe one of the core tenets of trunk based development is feature flagging, dark shipping, or whatever you want to call it. Specifically, the ability to land code into the trunk that doesn't break the build, and, more importantly, doesn't run codepaths for new or breaking features by default.

So when mentioned above, "how do you hold back multiple features from the release before they're ready" and it being incompatible with a "release every commit" strategy. I would argue that you can have the best of both worlds, and that is by ensuring you've got a toggle system of some sort so that features aren't defaulted on (where they could break users) until it's explicit.

This effectively allows you to have a single release-from-trunk pipeline, while maintaining a feature strategy which allows you to dynamically choose how the code acts at any time. Power users or testers can be enabling the -FooWithBar upcoming feature off any build from main, while regular users continue to use (now internally named) FooVanilla inside the application.

Then some day it's decided that FooWithBar is ready to go, and depending on your application, either you do a follow-up release that changes the default from FooVanilla to FooWithBar (at which point it might actually be 'breaking'), or you have a mechanism post-release which allows you to toggle the default state of deployed applications in the wild, which doesn't even require a new build/release.

Either way, these strategies is one way to accomplish the goal of decoupling these product/feature release decisions from the actual code merge, compilation, and package process. Which is why I would argue perhaps this is a terminology issue:

Release from trunk doesn't always mean release a new feature, often it just means release a new build (with some new hidden feature work included). You see this in many modern packages today, the one off the top of my head is something like VSCode where the internal configuration file often allows users to opt into or select from upcoming feature changes which are "default dark" but still living inside the shipped end-user codebase.

So in many cases, "release" could instead be interpreted as "publish code", while the "feature release" is an independent action (or at least, loosely coupled to the underlying codebase)

The question then remains is, how does a dark-shipping trunk released app use semantic-release when it comes to commit messages? If you're queueing up feature or will-be-breaking work dark inside the codebase as you stream commits, you don't necessarily want the version number changing to reflect those changes yet. So do you use the breaking tag when you change the defaults? What do others think?

@daniellizik
Copy link

daniellizik commented Feb 5, 2023

Then every commit becomes a release, no? We don't have to deploy every release, either.

@AlexAtkinson
Copy link

This might help some folks. Trunk based automatic version versioning on every merge, powered by the branch naming scheme.

https://github.com/marketplace/actions/gitops-automatic-versioning

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests