Versioning and Releases#
Here we describe a suggested process for releasing code. Specifically with regard to consistent labelling which is required for matching issues tracked using the Sentry API to the code context in which they occurred.
[!NOTE]
This document is a work in progress.
It was written as a starting point for a more comprehensive guide, following a suggestion in a PR.
Background#
The Sentry API is a mechanism for reporting errors in deployed applications. We use an open source service supporting the API, called Glitchtip, to receive them.
When an error is detected, a call is made to the API with some tags and other metrics characterising the problem. One of these tags is the Sentry API release tag. This is matched to source maps uploaded previously to the API, allowing minified code, which is fairly unintelligible, to be shown in un-minified form. The match is done based on the filename and the release tag (and the dist tag if one is set).
In addition, the release tag and some other information is shown in the Glitchtip issue report. These need to be sufficient to allow developers inspecting these report to identify the version of the code-base in which the issue was created, so needs the commit ID in addition to the release.
So in order to get legible source code when viewing the report:
- We need consistent release tags created in the build.
- The source map files need to be deployed with the (minified) source. (In the front-end at least.)
- These need to be labelled with the appropriate release tag.
- The release tag also needs to be included in the application's initial Sentry API initialisation.
To facilitate that:
- All Mykomap modules use the same mechanism to deduce the build info at build time and insert it into the source code.
- This is reported by the back-end module's
version
endpoint, the on the console in the front-end. - The version is inferred from the last git tag with the format
v<semantic-version>
where the semantic version is a sequence of positive integers delimited with periods. e.g.v4.1.3
- The "build description" is generated using
git describe
and has the formv<semantic-version>-<commits>-g<commit-id>[-dirty]
, e.g.v4.1.3-23-b3dfba0
, where: v<semantic-version>
is the last matching version tag<commits>
is the number of commits on this branch since that tagged commit<commit-id>
is the current truncated commit ID, containing 7 or more characters.-dirty
is appended if the repository working directory has any modifications on build.- The semantic version is inserted into each modules'
package.json
as the"version"
attribute. - The Sentry API release tag is derived from it, and has the form
<module-name>@<semantic-version>
, where the former is the module name, modified to follow the rules defined here and the latter is the semantic version above. e.g.@mykomapfront-end@4.0.0
(the slash after@mykomap/
is illegal and is removed.) The former is included to distinguish the modules. - It is inserted into the front end module's
package.json
in the attribute"config.sentry.release"
.
Process#
This outlines a process for versioning and releases. The concepts are still
important - however, an npm
run-script has been added to help automate this,
see the section below, Automation of release tagging.
Tagging releases#
The remainder of this process assumes that commits the repository will
have periodic semantic versioning tags applied, of the form v<dotted
integers>
. "Semantic versioning" boils down to:
Given a version number
MAJOR.MINOR.PATCH
, increment the:
- MAJOR version when you make incompatible API changes
- MINOR version when you add functionality in a backward compatible manner
- PATCH version when you make backward compatible bug fixes
Additional labels for pre-release and build metadata are available as extensions to the
MAJOR.MINOR.PATCH
format. _[Presumably assupplemental dotted integers, in our scheme]_
The current version of Mykomap has a major version of 4, following on from previous versions, of which it is an entire rewrite, as well as incompatible to (insofar as it even had an API).
Our version of Mykomap does have an API, exposed by the back-end, and consumed by the front end. As these are intended to interoperate, we store both, plus libraries with shared data, in a "monorepo" - a single repository containing all of the related projects. Although we would like to maintain some independence between the components, and possibly allow them to be split apart again in the future, this does mean there is in practice a form of strong coupling between them - if not explicit, then possibly inadvertent couplings are likely to exist.
And as such all components naturally have the same semantic version,
stamped in their package.json
files' "version"
attribute.
However, the API is intended to be consumed - or to be able to be - by external systems. Which is clearly the main sense a semantic versioning system would apply here. Therefore we should be tagging our releases to reflect changes in that.
A second consideration is the idea that we are (at least in practice)
aiming to use a modified version of "trunk based
development". I say "modified" as we now seem
to have a distinct dev
branch as well as a main
branch, but the
former should always be a fast-forward-merge away from the latter, so
can be seen as a continuous branch.
Tags should be placed on this branch. The semantics of main
are such
that anything on that branch is (or could be) deployed in production,
and so should be given a release tag.
So to sum up, I think we should:
- Create a new version tag every time something is deployed to production
- Use the semantic versioning rules above to guide the selection of these versions
Automation of release tagging: npm run release
#
The TL:DR; here is that to create a release with a tagged commit, you can now run the following from the mykomap-monolith project root directory:
# To tag a new release with a sem-ver v4.1.3:
npm run release v4.1.3
...Which will tag the repository and rebuild it, updating the package.json
and
package-lock.json
files for you, then squash the result into one commit tagged
v4.1.3
. After which, you can manually push the result:
git push
git push --tags
...Or, inspect and delete the temporary branch created:
git branch -D prepare-release-v4.1.3
This automates the manual process, which is roughly as follows. Although note that the script intentionally follows a slightly different process involving a squash merge, to allow for inspection of failures.
The manual process is:
- Ensure the code builds cleanly:
npm run clean && npm ci && npm run build && npm run test
- Ensure any changes are committed, if this succeeds:
git add -u && git commit -m "...your commit message here..."
- Tag the commit with the version:
git tag v4.1.3
- Rebuild to apply the tags to the
package.json
files:npm run build
- Updateg the
package-lock.json
with the tags too:npm install --package-lock-only
- Commit that to the same commit:
git add -u && git commit --amend -c HEAD
The reason these all need to be squashed into the one commit with a tag applied is so that GitHub (and possibly other usages) will build and archive the actual deployable code for that version. If in practise a deployable release needs extra changes following the tagged release commit (as they do in practise) they'll be omitted from the release archive created by GitHub if they aren't squashed into it.
Reflections#
A consequence is that deployments using dev
will have the version of
the last production build, just with a different build specification.
Possibly this is unhelpful - in retrospect I wonder if we should
adjust our build labelling to special-case this region beyond main
and give them release tags which clearly identify them as different,
and "in development".
An alternative might be to extend the semantic versioning beyond
main
and onto dev
, with the idea that this is a continuous
process. That would however beg the question about why we need dev
,
perhaps we only need main
.
A third might be to adopt the even/odd versioning schemes used by some
software: minor version numbers which are even are considered
"development releases", and those which are odd are "production
releases". Then the dev
branch would be tagged with even minor
versions, and a transition to releasing for production would require
switching to odd minor versions. But I think this would imply we
should change our deployment and sprints processes accordingly, to
acknowledge this distinction and partitioned our work into attending
to development of new features, release of those features to
production, and post-release bug-fixes.
Disclaimer: all of these reflections are beyond the scope of current work, and need to be discussed by the team.
Preparing the code for a deploy#
When preparing to deploy, locally in the development working directory, we need to:
- Ensure the tests all run successfully.
npm run tests
- Apply any Git version tag you want to appear in the build, first (in
the format described above). e.g.
git tag v4.1.3
. - Run the build - the version attributes will be updated in all the
package.json
files, and the Sentry release tag in the front-end's. - Commit these changes to Git.
git push
the changes.
Deploying#
On the server where the code is being deployed:
- Ensure
apps/front-end/.env
includes the environment parameters (as described below). - Check out the correct commit.
- Ensure there are no other uncommitted changes (to avoid the
-dirty
suffix). - Run the build - the code will be tagged with the correct build info, including the commit ID.
- Invoke the front-end module's run-script
upload-sourcemaps
to upload the source-map files via the Sentry API.
The environment parameters needed by npm run upload-sourcemaps
are:
GLITCHTIP_KEY
- obtain this from our Glitchtip account, it's the alphanumeric code set in theglitchtip_key
parameter of the Security Endpoint URL, found under Settings -> Projects -> @mykomap/front-end.SENTRY_AUTH_TOKEN
- obtain this from our Glitchtip account, there is one generated already, called SentryCli, accessible under Profile -> Auth Tokens.SENTRY_URL
- typicallyhttps://app.glitchtip.com
SENTRY_ORG
- should bedigital-commons-coop
SENTRY_PROJECT
- should bemykomapfront-end
[!NOTE]
The environment parameters set in the
deploy.sh
script are slightly different, because of the wider context there. See the comments in the script itself for details.[!NOTE]
The upload is performed in a run-script by the Sentry CLI. It could in future use the Sentry Vite plugin, which may be simpler.