Continuous integration#
Continuous integration (CI) is the process of merging new changes into the main code base while ensuring that these changes are functional and do not break the existing code.
This process is automated as much as possible to alleviate the developer’s workload and ensure a quick development workflow.
Because PyAnsys projects are hosted in GitHub, the GitHub Actions framework is used.
Enable GitHub actions#
By default, Actions
are enabled in new repositories and can be accessed
using the associated GitHub repository sections.
If Actions
are not enabled, you can enable them. For more information, see
Managing GitHub Actions permissions for your repository
in the GitHub documentation.
Use GitHub Actions#
You must declare the GitHub Actions to be executed in the CI process in a
common ci.yml
file in the .github/workflows
directory. Although each
action is different, they all have a common structure:
A name identifying the action.
A collection of triggering events that run the action when required.
A collection of concurrent workflows conditions to, for example, avoid running several workflows for the same branch. (Multiple consecutive pushes could lead to multiple ongoing workflows when you want only the last push to run).
A collection of jobs with different steps to follow during the CI process.
name: <Name of the action>
on:
<Triggering events and conditions>
concurrency:
<Avoid concurrent workflows to be run>
jobs:
<All jobs must be defined below this line>
Disable concurrent workflows#
Handling hardware resources is a big deal, especially when running with self-hosted agents. If you are using public GitHub hardware for running your workflows, disabling concurrent CI workflows is a way to show that you care about the environment and sustainability.
For example, imagine the following situation:
You push some changes to your branch.
The CI workflow kicks in and starts executing the different stages.
You suddenly realize that there is a typo or a file missing.
You push the new commit to your PR.
A new CI workflow kicks in and starts running.
At this moment, you probably have two parallel workflows running at the same time, though you are only interested in the results from the last one.
One way to solve this is manually cancelling the oldest workflow. However, it is also possible to
automatically cancel pre-existing workflows for a PR. To do so, prior to the
jobs
section in the ci.yml
file, add the following lines to your workflow:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Required workflows#
PyAnsys projects require workflows for performing these types of checks:
You should collect all workflows in a common ci.yml
file. For more information,
see Workflow examples.
Parametrize workflows#
It is important to test a PyAnsys library on different operating systems using different Python versions:
The most common operating systems are Windows, macOS, and Linux/UNIX. For supported Python versions, see Python versions.
Because having a YML file for each workflow would be tedious, GitHub
Actions provides the matrix
parameter inside the strategy
. For more
information, see Using a matrix for your Jobs
in the GitHub documentation
Consider this example of a parametrized workflow:
jobs:
example_matrix:
strategy:
matrix:
python: ['3.9', '3.10', '3.11', '3.12']
os: [windows-latest, macos-latest, ubuntu-latest]
steps:
- echo "Running Python ${{ matrix.python }} in ${{ matrix.os }}"
Running Python 3.9 in windows-latest
Running Python 3.10 in windows-latest
Running Python 3.11 in windows-latest
Running Python 3.12 in windows-latest
Running Python 3.9 in macos-latest
Running Python 3.10 in macos-latest
Running Python 3.11 in macos-latest
Running Python 3.12 in macos-latest
Running Python 3.9 in ubuntu-latest
Running Python 3.10 in ubuntu-latest
Running Python 3.11 in ubuntu-latest
Running Python 3.12 in ubuntu-latest
Workflow examples#
Workflow examples are provided for various checks, such as code style, tests, documentation style, documentation building, and releasing.
code-style:
name: Code style
runs-on: ubuntu-latest
steps:
- name: "Run PyAnsys code style checks"
uses: ansys/actions/code-style@v4
doc-style:
name: Doc style
runs-on: ubuntu-latest
steps:
- name: "Run PyAnsys documentation style checks"
uses: ansys/actions/doc-style@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
tests:
name: "Test library"
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- name: "Run pytest"
uses: ansys/actions/tests-pytest@v4
with:
python-version: ${{ matrix.python-version }}
pytest-markers: "-k 'mocked'"
pytest-extra-args: "--cov=ansys.<library> --cov-report=term --cov-report=xml:.cov/coverage.xml --cov-report=html:.cov/html"
- name: "Upload coverage results"
uses: actions/upload-artifact@v4
if: matrix.python-version == ${{ env.MAIN_PYTHON_VERSION }}
with:
name: coverage-html
path: .cov/html
retention-days: 7
doc-build:
name: "Build project documentation"
runs-on: ubuntu-latest
steps:
- name: "Build project documentation"
uses: ansys/actions/doc-build@v4
with:
python-version: ${{ env.MAIN_PYTHON_VERSION }}
doc-deploy-dev:
name: "Deploy development documentation"
if: github.event_name == 'push'
runs-on: ubuntu-latest
needs: doc-build
steps:
- name: "Deploy development documentation"
uses: ansys/actions/doc-deploy-dev@v4
with:
cname: ${{ env.CNAME }}
token: ${{ secrets.GITHUB_TOKEN }}
doc-deploy-stable:
name: "Deploy stable documentation"
if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
runs-on: ubuntu-latest
needs: doc-deploy-dev
steps:
- name: "Deploy stable documentation"
uses: ansys/actions/doc-deploy-stable@v4
with:
cname: ${{ env.CNAME }}
token: ${{ secrets.GITHUB_TOKEN }}
doc-deploy-index:
name: "Index the documentation and scrap using PyMeilisearch"
runs-on: ubuntu-latest
needs: doc-deploy-dev
if: github.event_name == 'push'
steps:
- name: Scrape the stable documentation to PyMeilisearch
run: |
VERSION=$(python -c "from <your-package> import __version__; print('.'.join(__version__.split('.')[:2]))")
VERSION_MEILI=$(python -c "from <your-package> import __version__; print('-'.join(__version__.split('.')[:2]))")
echo "Calculated VERSION: $VERSION"
echo "Calculated VERSION_MEILI: $VERSION_MEILI"
- name: "Deploy the latest documentation index"
uses: ansys/actions/doc-deploy-index@v4.1
with:
cname: "<library>.docs.pyansys.com/version/$VERSION"
index-name: "<index-name>v$VERSION_MEILI"
host-url: "<meilisearch-host-url>"
api-key: ${{ secrets.MEILISEARCH_API_KEY }}
build-wheelhouse:
name: Build the wheelhouse of the Python library
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
python-version: ['3.9', '3.10', '3.11', '3.12']
steps:
- name: "Build a wheelhouse of the Python library"
uses: ansys/actions/build-wheelhouse@v4
with:
library-name: ${{ env.LIBRARY_NAME }}
library-namespace: ${{ env.LIBRARY_NAMESPACE }}
operating-system: ${{ matrix.os }}
python-version: ${{ matrix.python-version }}
build-library:
name: Build library
runs-on: ubuntu-latest
steps:
- name: "Build library source and wheel artifacts"
uses: ansys/actions/build-library@v4
with:
library-name: ${{ env.LIBRARY_NAME }}
release-pypi-private:
name: "Release to the private PyPI repository"
runs-on: ubuntu-latest
needs: [build-library]
steps:
- name: "Release to the private PyPI repository"
if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
uses: ansys/actions/release-pypi-private@v4
with:
library-name: ${{ env.LIBRARY_NAME }}
twine-username: "__token__"
twine-token: ${{ secrets.PYANSYS_PYPI_PRIVATE_PAT }}
release-pypi-public:
name: "Release to the public PyPI repository"
runs-on: ubuntu-latest
needs: [release-pypi-private]
steps:
- name: "Release to the public PyPI repository"
if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
uses: ansys/actions/release-pypi-public@v4
with:
library-name: ${{ env.LIBRARY_NAME }}
twine-username: "__token__"
twine-token: ${{ secrets.PYPI_TOKEN }}
release-github:
name: "Release to GitHub"
runs-on: ubuntu-latest
needs: [release-pypi-public]
steps:
- name: "Release to GitHub"
if: github.event_name == 'push' && contains(github.ref, 'refs/tags')
uses: ansys/actions/release-github@v4
with:
library-name: ${{ env.LIBRARY_NAME }}