Releasing and publishing#

Releasing a new version is a critical procedure. It should be automated as much as possible to avoid human error.

This sections explains the Git workflow and steps that must be followed to create a successful release.

Attention

A project needs to be authorized to be released into public by following the process explained in the Project approval and public release section.

Semantic versioning#

PyAnsys library releases are managed through both automated and manual review processes.

PyAnsys follows Semantic Versioning, which produces release names in the form of X.Y.Z, where each letter corresponds to an integer value. This notation can also be understand as MAJOR.MINOR.PATCH:

  • Major version when you make incompatible API changes.

  • Minor version when you add a feature in a backwards-compatible manner.

  • Patch version when you make backwards compatible bug fixes.

To match the versioning methodology used by the ‘big three’ data science Python packages, numpy, scipy, and pandas, MAJOR versions of PyAnsys packages are not released when any incompatible API change is made but rather when major, globally breaking API changes are made.

Note that 0.MINOR.PATCH packages are expected to have fluid APIs and should be solidified at the 1.MINOR.PATCH release. At that point, APIs are expected to be much more stable.

PyAnsys libraries should not match product versions.

For example, the PyMAPDL library ansys-mapdl-core might have the version 0.59.0 whereas the product version is 22.2 (2022 R2). The reason behind this is PyAnsys libraries are expected to be developed outside the product release cycle in a rapid CI/CD manner.

Branch model#

The branching model for a PyAnsys project enables rapid development of features without sacrificing stability. The model closely follows the Trunk Based Development approach:

  • The main branch is the primary development branch. All features, patches, and other branches should be merged here. While all PRs should pass all applicable CI checks, this branch might be functionally unstable if changes have introduced unintended side effects or bugs that were not caught through unit testing. The version is always suffixed with .dev0 in the main branch.

    The main branch is the primary development branch.

    Fig. 2 The main branch is the primary development branch.#

  • When a minor release candidate is ready, a new release branch is created from main with the next incremented minor version (for example, release/0.2). This release branch is thoroughly tested. When deemed stable, it is tagged with the version (0.2.0 in this case). Older release branches should not be deleted so that they can be patched as needed.

  • There is one or more release/ branches based on minor releases (for example, release/0.2) that contain a stable version of the code base that is also reflected on PyPI. Hotfixes from fix/ branches should be integrated both to main and to these branches. When creating a new patch release is necessary, these release branches have their version updated and are tagged with a patched Semantic versioning (for example, 0.2.1). This triggers CI to push to PyPI so that hotfixes for past versions can be rapidly push without having to worry about untested features.

    Release branches are created based on minor releases.

    Fig. 3 Release branches are created based on minor releases.#

Releasing new versions#

Releasing is the process of creating a version of a software that developers consider useful for customers or other developers. Releases are usually labeled with tags. These tags are used to quickly identify a release in the version control system.

Release checklist
  • Your main or release branch is up to date.

  • All code and documentation style checks are passing successfully.

  • All tests are passing successfully.

  • All documentation builds successfully.

  • The project builds successfully.

Releasing major and minor versions

Before performing a release, you must verify that your origin main branch is up to date using the these commands:

git checkout main
git fetch origin main
git rebase origin/main

If you encounter any issues when running the preceding command, solve them before continuing with the release. Ensure that your style, tests, and documentation checks are passing too.

Create a new branch for the version you want to release with this command:

git checkout -b release/X.Y

Update X or Y version numbers in your project and replace the dev0 with a 0.

Check all locations, including The setup.py file, The pyproject.toml file, and any __init__.py or __version__.py your project may contain.

Stash and commit previous changes with the commands:

git add <files-edited-for-version-number-change>
git commit -m "Bump version X.Y.0"

Tag the previous commit using this command:

git tag vX.Y.0

Push the commit and the tag with these commands:

git push -u origin release/X.Y
git push origin vX.Y.0
Releasing patched versions

Patched versions allow you to fix issues discovered in published releases by cherry-picking these fixes from the main branch.

Before performing a patch release, you must first identify which release/X.Y branch it belongs to.

git checkout release/X.Y
git fetch origin release/X.Y
git reset --hard origin/release/X.Y

Now, use the following code to cherry-pick the fix commit from main, which solves for the bug. Do not merge changes from main into the release branch. Always cherry-pick them.

git cherry-pick <commit hash>

Ensure that your style, tests, and documentation checks are also passing.

Increase by one unit the value of Z in your project version. Stash and amend these new changes using this command:

git add <files-edited-for-version-number-change>
git commit --amend -m "Bump version X.Y.Z"

Tag the previous commit with this command:

git tag vX.Y.Z

Push the commit and the tag using this command:

git push -u origin release/X.Y
git push origin vX.Y.Z

Publishing artifacts#

When a new version is released, some artifacts are provided with it. In Python, these Artifacts are typically the Wheel and Source files. Documentation in the form of PDF and HTML files are also considered artifacts.

Attention

Do not distribute artifacts without approval.

A project needs to be authorized to be released into public by following the process explained in the Project approval and public release section.

There are three possible places where artifacts can be published:

Private PyPI

This is a private index used to share artifacts across the company while making sure that projects remain private.

Public PyPI

This is the public PyPI used by the Python community to distribute libraries. A project requires Ansys authorization before being published in this index.

GitHub

This is a section created by GitHub within a project repository where artifacts can be published. A project requires Ansys authorization before being public in GitHub.

Private PyPI#

It is sometimes necessary to host and pull packages that are not ready to be hosted on the public PyPI. For example, if a PyAnsys library requires auto-generated gRPC interface files from a feature or service that is still private, this package should be hosted on a private PyPI repository.

ANSYS, Inc. has a private repository at PyAnsys PyPI. Access is controlled via a username and a password:

Credentials for publishing to private PyPI

Value

Username

__token__

Password

PYANSYS_PYPI_PRIVATE_PAT

The PYANSYS_PYPI_PRIVATE_PAT is a password in the form of a GitHub secret which is available only to repositories within PyAnsys. This secret is available during the execution of the CI/CD. Its value is never shown or shared in the log files.

Forked GitHub repositories do not have access to GitHub secrets. This is designed to protect against pull-requests that could potentially scrape tokens from PyAnsys CI/CD.

Using GitHub actions

The following code allows you to publish any Python Artifacts contained in the dist/ directory to the private PyPI. It is expected to be included when Use GitHub actions:

release-pypi-private:
  name: "Release to private PyPI"
  runs-on: ubuntu-latest
  if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
  steps:
    - uses: pyansys/actions/release-pypi-private@v3
      with:
        library-name: "ansys-<product>-<library>"
        twine-username: "__token__"
        twine-token: ${{ secrets.PYANSYS_PYPI_PRIVATE_PAT }}
Using the command line

Alternatively, instead of command-line tool arguments for Twine, you can use environment variables:

set TWINE_USERNAME=<PAT>
set TWINE_PASSWORD=<PYANSYS_PYPI_PRIVATE_PAT>
set TWINE_REPOSITORY_URL=https://pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/upload
$env:TWINE_USERNAME=<PAT>
$env:TWINE_PASSWORD=<PYANSYS_PYPI_PRIVATE_PAT>
$env:TWINE_REPOSITORY_URL=https://pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/upload
export TWINE_USERNAME=<PAT>
export TWINE_PASSWORD=<PYANSYS_PYPI_PRIVATE_PAT>
export TWINE_REPOSITORY_URL="https://pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/upload"
export TWINE_USERNAME=<PAT>
export TWINE_PASSWORD=<PYANSYS_PYPI_PRIVATE_PAT>
export TWINE_REPOSITORY_URL="https://pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/upload"

Finally, run this command:

python -m twine upload dist/*

Public PyPI#

Publishing Artifacts to PyPI is the way of distributing Python libraries. Publishing to PyPI requires a username and a password:

Credentials for publishing to public PyPI

Value

Username

__token__

Password

PYPI_TOKEN

The PYPI_TOKEN is a password in the form of a GitHub secret. This secret is unique to each project. It can only be obtained after the first release to the public PyPI. Follow the process Project approval and public release process to obtain public release authorization.

Once authorized, contact pyansys.support@ansys.com to get support during the first release of the project. The team then enables the custom PYPI_TOKEN once your project has been successfully released for the first time. For future releases, everything is then automated.

Using GitHub actions

The following code allows you to publish any Python Artifacts contained in the dist/ directory to the public PyPI. It is expected to be included when Use GitHub actions:

release-pypi-public:
  name: "Release to public PyPI"
  runs-on: ubuntu-latest
  if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
  steps:
    - uses: pyansys/actions/release-pypi-public@v3
      with:
        library-name: "ansys-<product>-<library>"
        twine-username: "__token__"
        twine-token: ${{ secrets.PYPI_TOKEN }}

GitHub#

Publishing Artifacts to GitHub is also possible. These are available in the https://github.com/pyansys/project-name/releases section. The visibility of these artifacts follows the one in the repository. Visibility can be private, internal or public.

For enabling public visibility of a repository, follow the process explained in the Project approval and public release section.

Using GitHub actions

The following code allows you to publish any Python Artifacts contained in the dist/ directory to the GitHub release created. It is expected to be included when Use GitHub actions:

release-github:
  name: "Release to GitHub"
  runs-on: ubuntu-latest
  if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
  steps:
    - uses: pyansys/actions/release-github@v3
      with:
        library-name: "ansys-<product>-<library>"

Downloading artifacts#

Artifacts can be downloaded from all previous sources: Ansys private PyPI, public PyPI and GitHub.

Downloading artifacts from the Ansys private PyPI

Request the value of the PYANSYS_PYPI_PRIVATE_PAT token by sending an email to the pyansys.support@ansys.com email.

Create an environment variable named PYANSYS_PYPI_PRIVATE_PAT in your local machine an assign it the value of the token.

Warning

Take care to always use the --index-url switch rather than the --extra-index-url switch. As noted in pip Documentation, the --index-url switch changes the Python Package Index, which forces pip to use only packages from that package index.

The Ansys package index uses PyPI upstream. This prevents other users from being able to inject packages from PyPI that would supersede Ansys packages, even if they are of a higher version.

This is not the case if you use --extra-index-url, which adds to rather than replaces the default package index. For security, do not use --extra-index-url.

set PYANSYS_PYPI_PRIVATE_PAT=<REDACTED>
set INDEX_URL=https://%PYANSYS_PYPI_PRIVATE_PAT%@pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/simple/
python -m pip install ansys-<product/tool>-<library> --index-url %INDEX_URL% --no-dependencies
$env:INDEX_URL='https://$PYANSYS_PYPI_PRIVATE_PAT@pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/simple/'
python -m pip install ansys-<product/tool>-<library> --index-url $env:INDEX_URL --no-dependencies
export INDEX_URL='https://$PYANSYS_PYPI_PRIVATE_PAT@pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/simple/'

python -m pip install ansys-<product/tool>-<library> \
--index-url $INDEX_URL \
--no-dependencies
export INDEX_URL='https://$PYANSYS_PYPI_PRIVATE_PAT@pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/simple/'

python -m pip install ansys-<product/tool>-<library> \
--index-url $INDEX_URL \
--no-dependencies
Downloading artifacts from the public PyPI

Downloading artifacts from the public PyPI can be done by using the default settings by pip:

python -m pip install <package-name>
Downloading artifacts from GitHub

Downloading artifacts from GitHub can be done by checking the https://github.com/pyansys/project-name/releases section.

Note that if you download the Wheel of a Python package, you still need to manually install it by running:

python -m pip install path/to/package/wheel.whl