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 you must follow to create a successful release.

Attention

A project must be authorized to be publicly released. For an explanation of the process, see Project approval and public release.

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:

  • A MAJOR version is when you make incompatible API changes.

  • A MINOR version is when you add a feature in a backwards-compatible manner.

  • A PATCH version is 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 library versions should not match product versions.

PyAnsys libraries are expected to be developed outside the product release cycle in a rapid CI/CD manner. Thus, library versions should not match product versions. For example, PyMAPDL library (ansys-mapdl-core) might have the version 0.59.0 whereas the product (Ansys Parametric Design Language (APDL) might have the version is 22.2 (2022 R2).

Branching 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.#

New releases#

Releasing is the process of creating a version of the 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.
Release major and minor versions

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

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

If you encounter any issues when running the preceding commands, 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 that 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 files that your project may contain.

Stash and commit previous changes with these commands:

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

Tag the previous commit with this command:

git tag vX.Y.0

Push the commit and the tag it with these commands:

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

Patched versions allow you to fix issues discovered in published releases by cherry-picking these fixes from the main branch. For more information, see the get-cherry-pick description in the Git documentation.

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

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

Next, use the following code to cherry-pick the fix commit from the main branch, which solves for the bug. Do not merge changes from the main branch 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 with these commands:

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 it using this command:

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

Artifact publication#

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

Attention

Do not distribute artifacts without approval.

A project must be authorized to be publicly released. For an explanation of the process, see Project approval and public release.

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. You must have the proper credentials for publishing to this private repository:

Credentials

Value

Username

__token__

Password

PYANSYS_PYPI_PRIVATE_PAT

repository-url

https://pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/upload

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

When using Twine from the command line, you must add in --repository-url as an extra option. Otherwise, Twine attempts to upload the package to the public PyPI repository.

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

Here’s a cross-platform, one-line command for using Twine to upload a package:

python -m twine upload dist/* --repository-url https://pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/upload -u __token__ -p <TOKEN-REDACTED>

Replace <TOKEN-REDACTED> with the private PyPI token.

Use GitHub Actions

The following code lets you publish Python Artifacts in the dist directory to the private PyPI. This code is expected to be included when you 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: ansys/actions/release-pypi-private@v4
      with:
        library-name: "ansys-<product>-<library>"
        twine-username: "__token__"
        twine-token: ${{ secrets.PYANSYS_PYPI_PRIVATE_PAT }}
Use the command line

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

set TWINE_USERNAME=__token__
set TWINE_PASSWORD=<PYANSYS_PYPI_PRIVATE_PAT>
set TWINE_REPOSITORY_URL=https://pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/upload
$env:TWINE_USERNAME=__token__
$env:TWINE_PASSWORD=<PYANSYS_PYPI_PRIVATE_PAT>
$env:TWINE_REPOSITORY_URL=https://pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/upload
export TWINE_USERNAME=__token__
export TWINE_PASSWORD=<PYANSYS_PYPI_PRIVATE_PAT>
export TWINE_REPOSITORY_URL="https://pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/upload"
export TWINE_USERNAME=__token__
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 the PyAnsy core team to get support during the first release of the project. The team enables the custom PYPI_TOKEN once your project has been successfully released for the first time. For future releases, everything is automated.

Here’s a cross-platform, one-line command for using Twine to download a package:

python -m pip install <PACKAGE-NAME> --index-url <TOKEN-REDACTED>@pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/simple/

Replace <PACKAGE-NAME> and <TOKEN-REDACTED> with the package name and private PyPI token respectively.

Use GitHub Actions

The following code lets you publish any Python Artifacts contained in the dist directory to the public PyPI. It is expected to be included when you 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: ansys/actions/release-pypi-public@v4
      with:
        library-name: "ansys-<product>-<library>"
        twine-username: "__token__"
        twine-token: ${{ secrets.PYPI_TOKEN }}

GitHub#

You can publish Artifacts to GitHub, which makes them available in the https://github.com/ansys/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 Project approval and public release.

Use GitHub Actions

The following code lets you publish any Python Artifacts contained in the dist directory to the GitHub release created. It is expected to be included when you 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: ansys/actions/release-github@v4
      with:
        library-name: "ansys-<product>-<library>"

Artifact download#

You can download artifacts from the Ansys private PyPI, public PyPI, and GitHub.

Download artifacts from the Ansys private PyPI

Request the value of the PYANSYS_PYPI_PRIVATE_READ_PAT token by sending an email to the pyansys.core@ansys.com email.

Create an environment variable named PYANSYS_PYPI_PRIVATE_READ_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_READ_PAT=<REDACTED>
set INDEX_URL=https://%PYANSYS_PYPI_PRIVATE_READ_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://$env:PYANSYS_PYPI_PRIVATE_READ_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_READ_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_READ_PAT@pkgs.dev.azure.com/pyansys/_packaging/pyansys/pypi/simple/"

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

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

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

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

Note that if you download the wheel of a Python package, you must manually install it with a command like this:

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