=========================
Releasing Ironic Projects
=========================

Since the responsibility for releases will move between people, we document
that process here.

A full list of projects that Ironic manages is available in the `governance
site`_.

Who is responsible for releases?
================================

The current PTL is ultimately responsible for making sure code gets released.
They may choose to delegate this responsibility to a liaison, which is
documented in the `cross-project liaison wiki`_.

Anyone may submit a release request per the process below, but the PTL or
liaison must +1 the request for it to be processed.

Release process
===============

Releases are managed by the OpenStack release team. The release process is
documented in the `Project Team Guide`_.

What do we have to release?
===========================

The Ironic project has a number of deliverables under its governance.  The
ultimate source of truth for this is `projects.yaml
<https://opendev.org/openstack/governance/src/branch/master/reference/projects.yaml>`__
in the governance repository. These deliverables have varying release models,
and these are defined in the `deliverables YAML files
<https://opendev.org/openstack/releases/src/branch/master/deliverables>`__ in
the releases repository.

In general, Ironic deliverables follow the `cycle-with-intermediary
<https://releases.openstack.org/reference/release_models.html#cycle-with-intermediary>`__
release model.

Non-client libraries
--------------------

The following deliverables are non-client libraries:

* `sushy`_

Client libraries
----------------

The following deliverables are client libraries:

* `python-ironicclient`_

Normal release
--------------

The following deliverables are Neutron plugins:

* `networking-baremetal`_
* `networking-generic-switch`_

The following deliverables are Horizon plugins:

* `ironic-ui`_

The following deliverables are Tempest plugins:

* `ironic-tempest-plugin`_

The following deliverables are tools:

* `ironic-python-agent-builder`_

  .. NOTE:: if ironic-python-agent-builder is branched after
            ironic-python-agent, we need to make sure all artifacts
            are built by the time of Ironic branching.

The following deliverables are services, or treated as such:

* `bifrost`_
* `ironic`_
* `ironic-prometheus-exporter`_
* `ironic-python-agent`_

Manual release
--------------

The ironic-staging-drivers follows a different procedure, see
`Releasing ironic-staging-drivers
<https://ironic-staging-drivers.readthedocs.io/en/latest/releasing.html>`__.

Independent
-----------

The following deliverables are released `independently
<https://releases.openstack.org/reference/release_models.html#independent>`__:

* `sushy-tools`_
* `virtualbmc`_

Not released
------------

The following deliverables do not need to be released:

* `ironic-specs`_

OpenStack Release Schedule
==========================

The Ironic project follows the OpenStack coordinated release schedule. Each
OpenStack release cycle spans approximately 6 months (26 weeks) and follows
a structured timeline with specific milestones and deadlines.

Based on the standard OpenStack release schedule, a typical release cycle
includes the following key phases:

Release Timeline Overview
-------------------------

**Development Phase (Weeks R-25 to R-12)**
  * **R-25 to R-21**: Early development, feature planning
  * **R-20 (Week 6)**: First milestone
  * **R-17**: Cycle-trailing release deadline for previous cycle
  * **R-12 (Week 14)**: Second milestone and membership freeze

**Feature Development Phase (Weeks R-11 to R-5)**
  * **R-11 to R-8**: Active feature development
  * **R-7**: Extra Active Contributor (AC) freeze
  * **R-6**: Final release for non-client libraries (`sushy`_)
  * **R-5 (Week 22)**: Third milestone, feature freeze,
    final release for client libraries (`python-ironicclient`_),
    plugins (`networking-baremetal`_, `networking-generic-switch`_, `ironic-ui`_, `ironic-tempest-plugin`_),
    and tools (`ironic-python-agent-builder`_), soft string freeze, requirements freeze

**Stabilization Phase (Weeks R-4 to R-1)**
  * **R-4**: Cycle highlights deadline
  * **R-3**: RC1 target week for services (`bifrost`_, `ironic`_, `ironic-prometheus-exporter`_, `ironic-python-agent`_), hard string freeze
  * **R-2**: Bug fixing and stabilization
  * **R-1**: Final RCs and intermediary releases

**Release Week (R+0)**
  * **R+0**: Coordinated release for services (`bifrost`_, `ironic`_, `ironic-prometheus-exporter`_, `ironic-python-agent`_) (typically on a Wednesday)

Key Deadlines for Ironic Projects
----------------------------------

**Milestone Releases**
  * Projects following the cycle-with-intermediary model should aim to release
    at each milestone
  * Milestone participation is required for projects to be considered part of
    the coordinated release

**Library Releases**
  * Non-client libraries (`sushy`_): Final release at R-6
  * Client libraries (`python-ironicclient`_): Final release at R-5

**Plugin Releases**
  * Neutron plugins (`networking-baremetal`_, `networking-generic-switch`_): Final release at R-5
  * Horizon plugins (`ironic-ui`_): Final release at R-5
  * Tempest plugins (`ironic-tempest-plugin`_): Final release at R-5

**Service Releases**
  * Services (`bifrost`_, `ironic`_, `ironic-prometheus-exporter`_, `ironic-python-agent`_):
    Release candidates at R-3, final release at R+0
  * Tools (`ironic-python-agent-builder`_): Final release at R-5

**Feature Freeze (R-5)**
  * No new features accepted after the third milestone
  * Focus shifts to bug fixes and stabilization
  * Release notes should be finalized

**String Freeze**
  * Soft string freeze at R-5: No new user-facing strings
  * Hard string freeze at R-3: No string changes to allow translation work

**Requirements Freeze (R-5)**
  * Only critical requirements changes allowed after feature freeze
  * Helps downstream packagers prepare for the release

Planning Your Release
---------------------

When planning Ironic releases within an OpenStack cycle:

1. **Early Cycle (R-25 to R-12)**: Plan major features, coordinate with
   dependencies
2. **Mid Cycle (R-11 to R-6)**: Complete feature development, release
   libraries
3. **Late Cycle (R-5 to R+0)**: Focus on stabilization, bug fixes, and
   release preparation

For the most current release schedule, always refer to the official OpenStack
releases website at https://releases.openstack.org/.

Bugfix branches
===============

The following projects have ``bugfix/X.Y`` branches in addition to standard
openstack ``stable/NAME`` branches:

* ironic
* ironic-python-agent

These projects receive releases every six months as part of the coordinated
OpenStack release that happens semi-annually. These releases can be
found in a ``stable/NAME`` branch.

They are also evaluated for additional bugfix releases between scheduled
stable releases at the two and four month milestone between stable releases
(roughly every 2 months). These releases can be found in a ``bugfix/X.Y``
branch. A bugfix release is only created if there are significant
beneficial changes and a known downstream operator or distributor will consume
the release.

A bugfix branch is supported for 6 months after its creation date.

To leave some version space for releases from these branches, releases of these
projects from the master branch always increase either the major or the minor
version.

Currently retirement of bugfix branches cannot be automated and
must be done by the ironic team manually as explained below.

There are usually 3 bugfix branches present at all time, the latest 2 are
actively maintained, while the third one is considered unmaintained.
After creating a new bugfix branch, the oldest bugfix branch
should be tagged as EoL and deleted.

Only members of the ironic-core or ironic-release groups can delete branches,
please refer to this procedure to remove the oldest bugfix branch after
the creation of a new one:

* checkout locally the bugfix branch to move to EoL, if it does not exist
  locally it's possible to checkout it from remote and switch to it using
  ``git checkout -t``, for example for bugfix/24.0 use:

.. code-block:: bash

   git checkout -t origin/bugfix/24.0

* fast forward to latest change using:

.. code-block:: bash

   git pull --ff-only

* add a signed tag to the latest commit of the bugfix branch named ``bugfix-X.Y-eol``
  and add "EOL bugfix/X.Y" as description, for example
  for bugfix/24.0 add the tag bugfix-24.0-eol; use the ``git tag``
  command for that, for example for bugfix/24.0 the syntax would be:

.. code-block:: bash

   git tag -s bugfix-24.0-eol -m "EOL bugfix/24.0"

* push the new tag to gerrit using ``git push gerrit TAG_NAME``, for example
  for bugfix/24.0 use:

.. code-block:: bash

   git push gerrit bugfix-24.0-eol

* delete the bugfix branch on gerrit using ``git push gerrit --delete BUGFIX_BRANCH_NAME``,
  again for bugfix/24.0 would be:

.. code-block:: bash

   git push gerrit --delete bugfix/24.0

After the creation of a bugfix branch it is highly recommended to update
the upper-constraints link for the tests in the tox.ini file, plus override
the branch for the requirements project to be sure to use the correct
upper-constraints from the branch creation time; for example see the
following change:

https://review.opendev.org/c/openstack/ironic/+/938660

It is also mandatory to comment out or remove the metal3 integration job as it
is not supposed to run in stable or bugfix branches, and also comment out or
remove the tox codespell job to avoid failures due to version changes, spelling
is fixed in master and most recent stable branches only and backported "as is".

The remaining metal3 jobs can be left on stable and bugfix branches but they
need to be updated once the metal3 community follows up with its releases. See
the comments in the job definitions for instructions.

Things to do before releasing
=============================

* Review the unreleased release notes, if the project uses them. Make sure
  they follow our :ref:`standards <faq_release_note>`, are coherent, and have
  proper grammar.
  Combine release notes if necessary (for example, a release note for a
  feature and another release note to add to that feature may be combined).

* For Ironic releases only: if any new API
  microversions have been added since the last release, update the REST API
  version history (``doc/source/contributor/webapi-version-history.rst``) to
  indicate that they were part of the new release.

* To support rolling upgrades, add this new release version (and release name
  if it is a named release) into ``ironic/common/release_mappings.py``:

  * in ``RELEASE_MAPPING`` make a copy of the ``master`` entry, and rename the
    first ``master`` entry to the new semver release version.

  * If this is a named release, add a ``RELEASE_MAPPING`` entry for the named
    release. Its value should be the same as that of the latest semver one
    (that you just added above).

    It is important to do this before a stable/<release> branch is made (or if
    `the grenade switch is made <http://lists.openstack.org/pipermail/openstack-dev/2017-February/111849.html>`_
    to use the latest release from stable as the 'old' release).
    Otherwise, once it is made, CI (the grenade job that tests new-release ->
    master) will fail.

* Check for any open patches that are close to be merged or release critical.

  This usually includes important bug fixes and/or features that we'd like to
  release, including the related documentation.

How to propose a release
========================

The steps that lead to a release proposal are mainly manual, while proposing
the release itself is almost a 100% automated process, accomplished by
following the next steps:

* Clone the `openstack/releases <https://opendev.org/openstack/releases>`_
  repository. This is where deliverables are tracked and all the automation
  resides.

  * Under the ``deliverables`` directory you can see yaml files for each
    deliverable (i.e. subproject) grouped by release cycles.

  * The ``_independent`` directory contains yaml files for deliverables that
    are not bound to (official) cycles (e.g. Ironic-python-agent-builder).

* To check the changes we're about to release we can use the tox environment
  ``list-unreleased-changes``, with this syntax:

  .. code-block:: bash

    tox -e venv -- list-unreleased-changes <series> <deliverable>

  The ``series`` argument is a release series (i.e. master or train,
  not stable/ussuri or stable/train).

  For example, assuming we're in the main directory of the releases repository,
  to check the changes in the ussuri series for Ironic-python-agent
  type:

  .. code-block:: bash

    tox -e venv -- list-unreleased-changes ussuri openstack/ironic-python-agent

* To update the deliverable file for the new release, we use a scripted process
  in the form of a tox environment called ``new-release``.

  To get familiar with it and see all the options, type:

  .. code-block:: bash

    tox -e venv -- new-release -h

  Now, based on the list of changes we found in the precedent step, and the
  release notes, we need to decide on whether the next version will be major,
  minor (feature) or patch (bugfix).

  Note that in this case ``series`` is a code name (train, ussuri), not a
  branch. That is also valid for the current development branch (master) that
  takes the code name of the future stable release, for example if the future
  stable release code name is wallaby, we need to use wallaby as ``series``.

  The ``--stable-branch argument`` is used only for branching in the end of a
  cycle, independent projects are not branched this way though.

  The ``--intermediate-branch`` option is used to create an intermediate
  bugfix branch following the
  `new release model for Ironic projects <https://specs.openstack.org/openstack/ironic-specs/specs/approved/new-release-model.html>`_.

  To propose the release, use the script to update the deliverable file, then
  commit the change, and propose it for review.

  For example, to propose a minor release for Ironic in the master branch
  (current development branch), considering that the code name of the future
  stable release is wallaby, use:

  .. code-block:: bash

    tox -e venv -- new-release -v wallaby ironic feature

  Remember to use a meaningful topic, usually using the name of the
  deliverable, the new version and the branch, if applicable.

  A good commit message title should also include the same, for example
  "Release Ironic 1.2.3 for ussuri"

* As an optional step, we can use ``tox -e list-changes`` to double-check the
  changes before submitting them for review.

  Also ``tox -e validate`` (it might take a while to run based on the number of
  changes) does some some sanity-checks, but since everything is scripted,
  there shouldn't be any issue.

  All the scripts are designed and maintained by the release team; in case of
  questions or doubts or if any errors should arise, you can reach to them in
  the IRC channel ``#openstack-release``; all release liaisons should be
  present there.

* After the change is up for review, the PTL or a release liaison will have to approve
  it before it can get approved by the release team. Then, it will be processed
  automatically by zuul.

Things to do after releasing
============================

When a release is done that results in a stable branch
------------------------------------------------------
When a release is done that results in a stable branch for the project,
several changes need to be made.

The release automation will push a number of changes that need to be approved.
This includes:

* In the new stable branch:

  .. NOTE:: OpenStack Release tooling does this automatically.

  * a change to point ``.gitreview`` at the branch
  * a change to update the upper constraints file used by ``tox``

* In the master branch:

  * updating the release notes RST to include the new branch.

    The generated RST does not include the version range in the title, so we
    typically submit a follow-up patch to do that. An example of this patch is
    `here <https://review.opendev.org/685070>`__.

  * update the ``templates`` in ``.zuul.yaml`` or ``zuul.d/project.yaml``.

    The update is necessary to use the job for the next release
    ``openstack-python3-<next_release>-jobs``. An example of this patch is
    `here <https://review.opendev.org/#/c/689705/>`__.

We need to submit patches for changes in the stable branch to:

* update the Ironic devstack plugin to point at the branched tarball for IPA.
  An example of this patch is
  `here <https://review.opendev.org/685069/>`_.
* set appropriate defaults for ``TEMPEST_BAREMETAL_MIN_MICROVERSION`` and
  ``TEMPEST_BAREMETAL_MAX_MICROVERSION`` in ``devstack/lib/ironic`` to make sure
  that unsupported API tempest tests are skipped on stable branches. E.g.
  `patch 495319 <https://review.opendev.org/495319>`_.
* remove any CI jobs which are *not* required. Mainly this revolves around the
  metal3-integration CI job, however other non-voting jobs can also be removed
  safely. This can be achieved by editing the ``.zuul.d/project.yaml`` file.

.. NOTE:: It is normal to reduce the number of CI jobs present on a stable
   branch the longer the branch exists. This is a mix of challenges related
   to distributions, dependencies, and CI resources. Maintainers should
   anticipate this as a normal activity and should avoid heroic efforts.

We need to submit patches for changes on master to:

* to support rolling upgrades, since the release was a named release, we
  need to make these changes. Note that we need to wait until *after* the
  switch in grenade is made to test the latest release (N) with master
  (e.g. `for stable/queens <https://review.opendev.org/#/c/543615>`_).
  Doing these changes sooner -- after the Ironic release and before the switch
  when grenade is testing the prior release (N-1) with master, will cause
  the tests to fail. (You may want to ask/remind infra/qa team, as to
  when they will do this switch.)

  * In ``ironic/common/release_mappings.py``, delete any entries from
    ``RELEASE_MAPPING`` associated with the oldest named release. Since we
    support upgrades between adjacent named releases, the master branch will
    only support upgrades from the most recent named release to master.

  * remove any DB migration scripts from ``ironic.command.dbsync.ONLINE_MIGRATIONS``
    and remove the corresponding code from Ironic. (These migration scripts
    are used to migrate from an old release to this latest release; they
    shouldn't be needed after that.)

When a release is done that results in a bugfix branch
------------------------------------------------------

In this case the release management only creates a change to point
``.gitreview`` at the branch, ``tox.ini`` is not modified.

After the release:

* update the Tempest microversions as explained above.

* the CI needs additional configuration, so that Zuul knows which branch to
  take jobs definitions from. See the following examples:

  * `ironic 18.1 <https://review.opendev.org/c/openstack/ironic/+/801876>`_
  * `ironic-python-agent 8.1
    <https://review.opendev.org/c/openstack/ironic-python-agent/+/801898>`_

Ironic Tempest plugin
~~~~~~~~~~~~~~~~~~~~~

As **ironic-tempest-plugin** is branchless, we need to submit a patch adding
stable jobs to its master branch. `Example for Queens
<https://review.opendev.org/#/c/543555/>`_.

Bifrost
~~~~~~~

Bifrost needs to be updated to install dependencies using the stable branch.
`Example for Victoria <https://review.opendev.org/#/c/756289/>`_. The upper
constraints file referenced in ``scripts/install-deps.sh`` needs to be updated
to the new release.

For all releases
----------------

For all releases, whether or not it results in a stable branch:

* update the specs repo to mark any specs completed in the release as
  implemented.

* remove any -2s on patches that were blocked until after the release.

.. _`bifrost`: https://opendev.org/openstack/bifrost
.. _`cross-project liaison wiki`: https://wiki.openstack.org/wiki/CrossProjectLiaisons#Release_management
.. _`governance site`: https://governance.openstack.org/reference/projects/ironic.html
.. _`ironic`: https://opendev.org/openstack/ironic
.. _`ironic-prometheus-exporter`: https://opendev.org/openstack/ironic-prometheus-exporter
.. _`ironic-python-agent`: https://opendev.org/openstack/ironic-python-agent
.. _`ironic-python-agent-builder`: https://opendev.org/openstack/ironic-python-agent-builder
.. _`ironic-specs`: https://opendev.org/openstack/ironic-specs
.. _`ironic-tempest-plugin`: https://opendev.org/openstack/ironic-tempest-plugin
.. _`ironic-ui`: https://opendev.org/openstack/ironic-ui
.. _`networking-baremetal`: https://opendev.org/openstack/networking-baremetal
.. _`networking-generic-switch`: https://opendev.org/openstack/networking-generic-switch
.. _`Project Team Guide`: https://docs.openstack.org/project-team-guide/release-management.html#how-to-release
.. _`python-ironicclient`: https://opendev.org/openstack/python-ironicclient
.. _`sushy`: https://opendev.org/openstack/sushy
.. _`sushy-tools`: https://opendev.org/openstack/sushy-tools
.. _`virtualbmc`: https://opendev.org/openstack/virtualbmc
