Ledger team has fully adopted the PVP versioning scheme for all packages in this repo.
Updating the version for every package in this repository is the responsibility of the
developer that is introducing a change to any package that is subject to release
process. Version must be updated in both the CHANGELOG.md
and in the .cabal
file for
the package in the same PR, as well as every other package that is affected by the change
in the repository. Every affected package that is subject to release process must receive
a lower and, potentially, an upper bound update on the dependency that has experienced the
change.
For example if I add a function to cardano-ledger-core
and use it in
cardano-ledger-conway
, then I must add a minor version bump to cardano-ledger-core
and
I also must add a lower version restriction in cardano-ledger-conway
for
cardano-ledger-core
, as per PVP.
In other words, when it comes to versioning, the fact that all of the packages in this repository are built together at the same time should be ignored. It should be assumed that every package and all of its required dependencies can be released to CHaPs at any point in time. This is necessary to guarantee that if such a package is compatible with other packages from this repository, which have been previously released, the version bounds should reflect that.
In certain instances, we don't strictly follow the PVP. One such case is when we are adding
orphan instances to testlib
s. As per the PVP, this should warrant a major version bump.
However, we treat testlib
orphans as regular instances since they are only defined for
types that are local to the package that contains the testlib
where the orphan must live.
Hence this won't cause breakage for dependent packages and thus it is enough to only bump
the minor version.
Another exception that we make is related to upper bounds mentioned below.
Normally, upper bounds are required by
PVP for all dependencies of a
package. This is one exception we make to the PVP, and we do not require strict upper
bounds for dependencies that come from Hackage. For example, there is no need to add an
upper bound on containers
package, unless there is a known incompatibility. Speculative
upper bounds over time lead to overly restrictive build plans that are forced to favor
older packages. Moreover, they lead to a lot of redundant maintenance burden. That being
said, most of the packages from within cardano-ledger
repository are a lot more volatile
and require tight interoperability, therefore speculative upper bounds are encouraged.
Placing lower bounds is a lot less controversial than upper bounds, because they don't suffer from speculation about the future. Older versions of dependencies are known at release time. Therefore lower bounds must be added for all dependencies that are known to be incompatible with the package
See the versioning section of the Cardano Engineering Handbook for more detailed information on this topic.
Every package, that is subject to the release
process, will always have the top most entry in
the CHANGELOG.md
set to an unreleased version. That top most section will have either
the patch, minor or the major versions bumped, but not all three, when compared to the
latest released version. Which one it will be depends on the recent changes that were
added after the latest release was made. More on why this should always be true is in the
Release Process section. For instance, if the latest version of
cardano-ledger-core
that was released to CHaPs is 1.2.3.0
, then there will be a
section like this which will have a version that is strictly higher than 1.2.3.0
, eg:
# Version history for `cardano-ledger-core`
## 1.2.3.1
* Add ...
...
In order to decide if the version needs to be bumped up it is necessary to know what was the latest released version of a package. Two simple ways are either look at the version on CHaP or look at the latest git tag for the version.
However the easiest, while also reliable way to figure out whether you need to bump up the version is to simply look at the current version in the changelog. It will be easier to explain why this is the case with an example.
Let's say you submit a PR which contains breaking changes to cardano-ledger-core
. You
then look into the changelog for current development version on master
:
- If the current version is set to
cardano-ledger-core-1.2.3.5
. We see that the patch version is bumped (not zero), that means that the latest version released iscardano-ledger-core-1.2.3.4
, so you update it tocardano-ledger-core-1.3.0.0
, because your changes break stuff - If the current version is set to
cardano-ledger-core-1.2.4.0
. That means there was at most a minor version bump and currently released version iscardano-ledger-core-1.2.3.x
. But your changes are breaking so you increase it even further tocardano-ledger-core-1.3.0.0
- If the current version is
cardano-ledger-core-1.3.0.0
. That means currently released version to CHaPs iscardano-ledger-core-1.2.x.x
. There is nothing to do, the version onmaster
already contains some other breaking changes since the last release to CHaPs - If the current version is
cardano-ledger-core-2.0.0.0
. There was a complete overhaul. Definitely nothing needs to be bumped. 😁
It is quite common to experience conflicts in the changelog, since that will be the most common section of the codebase being updated at the same time. When it comes to conflicts resolution, it is pretty easy:
- on the package version, the highest value should usually win.
- on the change log entries, all entries should usually survive.
New entries should always be prepended to the top of the section.
CHANGELOG.md
must be updated for the package that is experiencing a change and any other
package in the repository that re-exports that change in one form or another. In general
an entry is added according to these rules:
-
Every breaking change MUST appear in the
CHANGELOG.md
. -
Every non-breaking change SHOULD appear in the
CHANGELOG.md
-
Every change that does not affect the user facing semantics of the code (eg. changes to documentation, dependency bounds, test suites, addition of internal functions, performance improvements, etc.) COULD be added to the
CHANGELOG.md
, but they are discouraged. Exceptions should be made for changes that could really be valuable to the downstream user. -
Every change to a public sub-library of a package must be added to a separate section in the
CHANGELOG.md
and is versioned together with the main library. Eg.testlib
:# Version history for `cardano-ledger-core` ## 1.20.2.0 * Add `someFunction` ### `testlib` * Add `Arbitrary` instance for ... ...
Usually a package release will happen directly from the master
branch. Current release
process even allows for a release to happen from a commit that is slightly behind HEAD
of the master
branch. On a rare occasion when a bugfix needs to be backported to an
older version, a release can happen from an ephemeral release branch. Such a release
branch must follow the same procedure of a PR review and CI, that is why it should be
prefixed with release/some-branch-name
, because that will ensure that CI is triggered.
Here are the steps for a release engineer to follow.
Normally it should be OK to release at the same time all of the packages from the
repository that have experienced some sort of change, but that is not mandatory. It is
fine to release any package by itself at any point from the HEAD
of master
or even a
commit that is behind the HEAD
on the master
branch. A release from an ephemeral
branch is also possible, but it is paramount to ensure that all of the same changes will
be present in the next release from master
branch.
The most common case is to blindly release all of the packages that were changed since they were last released. This begs a question. How to decide which of the packages have changed, thus deserve a release?
The rule is very simple. Every package that falls under this release process and has a
version in its cabal file higher than the highest version released to CHaPs is allowed to
be released and CHaP will not allow to release a package version that has already been
released. Therefore, it safe to use this command to release all packages from the repo
from a sepecific <SHA>
that had a version bump when compared to CHaP:
$ ./scripts/add-from-github.sh https://github.com/IntersectMBO/cardano-ledger <SHA> \
eras/allegra/impl \
eras/alonzo/impl \
eras/alonzo/test-suite eras/babbage/impl \
eras/babbage/test-suite eras/conway/impl \
eras/conway/test-suite \
eras/mary/impl \
eras/shelley/impl \
eras/shelley/test-suite \
eras/shelley-ma/test-suite \
libs/cardano-ledger-api \
libs/cardano-ledger-core \
libs/cardano-ledger-binary \
libs/cardano-protocol-tpraos \
libs/non-integral \
libs/small-steps \
libs/cardano-data \
libs/set-algebra \
libs/vector-map \
eras/byron/chain/executable-spec \
eras/byron/ledger/executable-spec \
eras/byron/ledger/impl \
eras/byron/ledger/impl/test \
eras/byron/crypto \
eras/byron/crypto/test
More on that command in the section below.
-
Follow the CHaP release instructions
For example, to release commit with SHA
deadbeef
:$ ./scripts/add-from-github.sh https://github.com/intersectmbo/cardano-ledger deadbeef libs/cardano-ledger-core ...
It is important to supply a commit SHA instead of a branch name.
-
Create and merge a PR to https://github.com/intersectmbo/cardano-haskell-packages with the release(s). In case that a current release causes breakage on some downstream package due to that package lacking upper bounds, you will require to add a revision for that package that fixes the bounds in the same PR as the release. Also it is necessary to notify the maintainers of the package via a bug report or a PR with a fix.
-
Once the PR is merged then create a git tag with the same version for the same git SHA that was released, eg:
$ git tag cardano-ledger-core-1.20.1.1 deadbeef... $ git push tag cardano-ledger-core-1.20.1.1
-
Create a PR to
master
that updatesCHANGELOG.md
files for all of the packages that have just been released. The only addition to the file should be a markdown header section with the next patch version bumped, which must bring theCHANGELOG.md
to the state of the top level section containing a version higher than the highest one ever released. Due to concurrent nature of editing the repository it is possible thatCHANGELOG.md
have already received a version bump with a section that fits the higher version criteria, in which case nothing needs to be added. The body of the section, if added, must be empty with just one single asterisk*
.For example, if
cardano-ledger-core-1.20.1.1
was just released, then a new empty1.20.1.2
section in theCHANGELOG.md
must be added:# Version history for `cardano-ledger-core` ## 1.20.1.2 * ## 1.20.1.1 ...
It is important to note that the version in the cabal file should not be changed at this stage, because it will later be used for deciding which package have changed and can be released.
As mentioned above, sometimes a release could break downstream packages in which case you will need to add revisions.
The instructions here will walk you through a specific case that we had when we released some packages in cardano-base
and will also provide general instructions on how to add and test revisions.
It is occasionally necessary to release a minor version for a package that has its history
diverged from a version on master
significantly enough to make it impossible for a minor
version to be released from master
. In other words a patch backporting. In such a
scenario a few steps should be followed:
-
Two ephemeral branches with a prefix
release/
need to be created. Both should branch of from a tag of a package version that is being updated. For example if a current version onmaster
iscardano-ledger-core-1.22.10.0
then the latestcardano-ledger-core-1.21.x
should be used as base:$ git checkout -b release/cardano-ledger-core-1.21.2.1 cardano-ledger-core-1.21.2.1 $ git push -u origin release/cardano-ledger-core-1.21.2.1 $ git checkout -b release/cardano-ledger-core-1.21.3.0 cardano-ledger-core-1.21.2.1
We'll need the first branch in order to use it as base when creating a PR for code review.
-
Changes that need to be released should be
cherry-pick
ed from master. If a fix onmaster
was implemented in some incompatible fashion to the current release, then it is fine to reimplement it anew, as long as the change being introduced is also present on master in some form. That requirement also concerns the changelog entry, it should be present in both the patched version and in the next version released frommaster
. -
Regular release process should follow from here.
-
Once the package has been released and a git tag for that release was created, both of the
release/
branches can be removed.
This process does not accommodate backporting fixes to versions that are at least two major
versions behind the one on master
.
We release most of the packages in this repo to CHaP (Cardano Haskell Packages). However, there are a few packages that are either used for testing, debugging or benchmarking and do not deserve to be released into the World. They are neither released nor versioned. Bounds on the local dependencies do not need to be updated, because they will always use the versions for ledger packages from within the repository, rather than from CHaPs. Here is the full list of such packages as of today:
libs/cardano-ledger-test
libs/plutus-preprocessor
libs/ledger-state
libs/cardano-ledger-conformance
libs/cardano-ledger-repl-environment
Above list is likely to change in the future.
Here are test suite packages that are still subject to the versioning and release process, but the changelog only needs to indicate if breaking changes have been made:
cardano-ledger-shelley-test
cardano-ledger-shelley-ma-test
cardano-ledger-alonzo-test
cardano-ledger-babbage-test
cardano-ledger-conway-test
cardano-crypto-test
cardano-ledger-byron-test
A changelog which indicates that no changes have been made will have the form:
# Version history for `cardano-ledger-conway-test`
## 1.2.0.1
*
A changelog which indicates that changes have been made will have the form:
# Version history for `cardano-ledger-conway-test`
## 1.3.0.0
* breaking changes
These packages are mostly used internally and are planned to be deprecated and removed in the near
future in favor of testlib
s for each corresponding package.