Add Zenodo metadata file for release archiving #56
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build & Publish | |
| env: | |
| MAIN_TEST_PYTHON: '3.12' | |
| on: | |
| push: | |
| branches: [dev, main] | |
| pull_request: | |
| release: | |
| types: [published] | |
| jobs: | |
| build_wheels: | |
| name: Build wheels (${{ matrix.platform }}) | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: linux | |
| runner: ubuntu-latest | |
| - platform: macos | |
| runner: macos-latest | |
| - platform: windows | |
| runner: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Build wheels (Linux) | |
| if: matrix.platform == 'linux' | |
| uses: pypa/cibuildwheel@v2.21 | |
| env: | |
| CIBW_BUILD: cp310-manylinux_x86_64 cp311-manylinux_x86_64 cp312-manylinux_x86_64 | |
| CIBW_BEFORE_ALL_LINUX: yum install -y eigen3-devel | |
| with: | |
| platform: linux | |
| python-version: ${{ env.MAIN_TEST_PYTHON }} | |
| output-dir: ./wheelhouse | |
| - name: Build wheels (macOS) | |
| if: matrix.platform == 'macos' | |
| uses: pypa/cibuildwheel@v2.21 | |
| env: | |
| CIBW_BUILD: cp310-macosx* cp311-macosx* cp312-macosx* | |
| CIBW_ENVIRONMENT_MACOS: CMAKE_INCLUDE_PATH=/tmp/eigen3 | |
| CIBW_BEFORE_ALL_MACOS: python -c "import pathlib, shutil, tarfile, tempfile, urllib.request; url='https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.tar.gz'; tmp=tempfile.mkdtemp(); archive=pathlib.Path(tmp)/'eigen.tar.gz'; archive.write_bytes(urllib.request.urlopen(url).read()); src_root=pathlib.Path(tmp)/'src'; src_root.mkdir(); tarfile.open(archive).extractall(src_root); src=next(src_root.glob('eigen-*')); dst=pathlib.Path('/tmp/eigen3'); shutil.rmtree(dst, ignore_errors=True); shutil.copytree(src, dst)" | |
| with: | |
| platform: macos | |
| python-version: ${{ env.MAIN_TEST_PYTHON }} | |
| macos-deployment-target: '11.0' | |
| output-dir: ./wheelhouse | |
| - name: Build wheels (Windows) | |
| if: matrix.platform == 'windows' | |
| uses: pypa/cibuildwheel@v2.21 | |
| env: | |
| CIBW_BUILD: cp310-win* cp311-win* cp312-win* | |
| CIBW_ENVIRONMENT_WINDOWS: CMAKE_INCLUDE_PATH=C:/eigen3 | |
| CIBW_BEFORE_ALL_WINDOWS: python -c "import io, pathlib, shutil, tempfile, urllib.request, zipfile; url='https://gitlab.com/libeigen/eigen/-/archive/3.4.0/eigen-3.4.0.zip'; data=urllib.request.urlopen(url).read(); tmp=tempfile.mkdtemp(); zipfile.ZipFile(io.BytesIO(data)).extractall(tmp); src=next(pathlib.Path(tmp).glob('eigen-*')); dst=pathlib.Path('C:/eigen3'); shutil.rmtree(dst, ignore_errors=True); shutil.copytree(src, dst)" | |
| with: | |
| platform: windows | |
| python-version: ${{ env.MAIN_TEST_PYTHON }} | |
| output-dir: ./wheelhouse | |
| - name: Upload wheels | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: wheels-${{ matrix.platform }} | |
| path: ./wheelhouse/*.whl | |
| retention-days: 30 | |
| build_conda: | |
| name: Build conda (${{ matrix.platform }}) | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - platform: linux | |
| runner: ubuntu-latest | |
| - platform: macos | |
| runner: macos-latest | |
| - platform: windows | |
| runner: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Set up Miniconda (Linux) | |
| if: matrix.platform == 'linux' | |
| uses: conda-incubator/setup-miniconda@v3 | |
| with: | |
| python-version: ${{ env.MAIN_TEST_PYTHON }} | |
| miniforge-version: latest | |
| channels: conda-forge | |
| conda-remove-defaults: true | |
| - name: Set up Miniforge (macOS) | |
| if: matrix.platform == 'macos' | |
| uses: conda-incubator/setup-miniconda@v3 | |
| with: | |
| python-version: ${{ env.MAIN_TEST_PYTHON }} | |
| miniforge-version: latest | |
| channels: conda-forge | |
| conda-remove-defaults: true | |
| - name: Set up Miniforge (Windows) | |
| if: matrix.platform == 'windows' | |
| uses: conda-incubator/setup-miniconda@v3 | |
| with: | |
| python-version: ${{ env.MAIN_TEST_PYTHON }} | |
| miniforge-version: latest | |
| channels: conda-forge | |
| conda-remove-defaults: true | |
| - name: Download conda-forge pinning config | |
| shell: bash -l {0} | |
| run: | | |
| mkdir -p recipe | |
| curl -sL https://raw.githubusercontent.com/conda-forge/conda-forge-pinning-feedstock/main/recipe/conda_build_config.yaml -o recipe/conda_build_config.yaml | |
| - name: Build conda package (Linux) | |
| if: matrix.platform == 'linux' | |
| shell: bash -l {0} | |
| run: | | |
| conda install -y conda-build | |
| conda build recipe/ --output-folder dist/conda 2>&1 | tee conda_build.log || (cat conda_build.log && exit 1) | |
| - name: Build conda package (macOS) | |
| if: matrix.platform == 'macos' | |
| shell: bash -l {0} | |
| run: | | |
| conda install -y conda-build | |
| conda build recipe/ --output-folder dist/conda 2>&1 | tee conda_build.log || (cat conda_build.log && exit 1) | |
| - name: Build conda package (Windows) | |
| if: matrix.platform == 'windows' | |
| shell: bash -l {0} | |
| run: | | |
| conda install -y conda-build | |
| conda build recipe/ --output-folder dist/conda 2>&1 | tee conda_build.log || (type conda_build.log && exit 1) | |
| - name: Upload conda package | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: conda-${{ matrix.platform }} | |
| path: dist/conda/**/* | |
| retention-days: 30 | |
| - name: Upload conda build log | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: conda-build-log-${{ matrix.platform }} | |
| path: conda_build.log | |
| retention-days: 30 | |
| test: | |
| name: Test | |
| needs: [build_wheels, build_conda] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: ${{ env.MAIN_TEST_PYTHON }} | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| pip install pytest numpy packaging | |
| - name: Download wheels | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: wheelhouse | |
| - name: Install and test | |
| run: | | |
| python - <<'PY' | |
| import glob | |
| import os | |
| from packaging.tags import sys_tags | |
| from packaging.utils import parse_wheel_filename | |
| import subprocess | |
| import sys | |
| wheels = sorted(glob.glob("wheelhouse/**/*.whl", recursive=True)) | |
| print("Discovered wheels:") | |
| for wheel in wheels: | |
| print(f"- {wheel}") | |
| candidate_wheels = [ | |
| wheel | |
| for wheel in wheels | |
| if os.path.basename(wheel).startswith("labellib-") | |
| ] | |
| supported_tags = set(sys_tags()) | |
| compatible_wheels = [] | |
| for wheel in candidate_wheels: | |
| _, _, _, tags = parse_wheel_filename(os.path.basename(wheel)) | |
| if tags & supported_tags: | |
| compatible_wheels.append(wheel) | |
| if not compatible_wheels: | |
| raise SystemExit("No compatible labellib wheel found in downloaded artifacts") | |
| wheel = compatible_wheels[0] | |
| print(f"Installing wheel artifact: {wheel}") | |
| subprocess.check_call([sys.executable, "-m", "pip", "install", "--no-index", wheel]) | |
| PY | |
| pytest | |
| publish_conda: | |
| name: Publish conda to tpeulen | |
| needs: [build_conda, test] | |
| if: (github.event_name == 'push' && github.ref == 'refs/heads/dev') || (github.event_name == 'release' && github.event.release.target_commitish == 'main') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: conda-* | |
| path: dist/conda | |
| merge-multiple: true | |
| - name: Validate Anaconda token is configured | |
| env: | |
| ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }} | |
| run: | | |
| if [ -z "$ANACONDA_API_TOKEN" ]; then | |
| echo "Missing ANACONDA_TOKEN secret; cannot publish conda package." | |
| exit 1 | |
| fi | |
| - name: Publish conda package to tpeulen channel | |
| env: | |
| ANACONDA_API_TOKEN: ${{ secrets.ANACONDA_TOKEN }} | |
| run: | | |
| python -m pip install --upgrade anaconda-client | |
| shopt -s nullglob globstar | |
| files=(dist/conda/**/labellib-*.tar.bz2 dist/conda/**/labellib-*.conda) | |
| if [ ${#files[@]} -eq 0 ]; then | |
| echo "No labellib conda packages found to upload." | |
| exit 1 | |
| fi | |
| if [ "$GITHUB_EVENT_NAME" = "push" ] && [ "$GITHUB_REF" = "refs/heads/dev" ]; then | |
| echo "Uploading dev build with conda label: dev" | |
| anaconda --site anaconda.org upload -u tpeulen --skip-existing --label dev "${files[@]}" | |
| else | |
| echo "Uploading release build without extra conda label" | |
| anaconda --site anaconda.org upload -u tpeulen --skip-existing "${files[@]}" | |
| fi | |
| publish_testpypi: | |
| name: Publish to TestPyPI | |
| needs: [build_wheels, test] | |
| if: github.event_name == 'push' && github.ref == 'refs/heads/dev' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wheels-* | |
| path: dist | |
| merge-multiple: true | |
| - name: Validate TestPyPI token is configured | |
| env: | |
| TEST_PYPI_TOKEN: ${{ secrets.TEST_PYPI_TOKEN }} | |
| run: | | |
| if [ -z "$TEST_PYPI_TOKEN" ]; then | |
| echo "Missing TEST_PYPI_TOKEN secret; cannot publish to TestPyPI." | |
| exit 1 | |
| fi | |
| - name: Publish to TestPyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| repository-url: https://test.pypi.org/legacy/ | |
| packages-dir: dist | |
| skip-existing: true | |
| attestations: false | |
| user: __token__ | |
| password: ${{ secrets.TEST_PYPI_TOKEN }} | |
| publish_pypi: | |
| name: Publish to PyPI | |
| needs: [build_wheels, test] | |
| if: github.event_name == 'release' && github.event.release.target_commitish == 'main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wheels-* | |
| path: dist | |
| merge-multiple: true | |
| - name: Validate PyPI token is configured | |
| env: | |
| PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} | |
| run: | | |
| if [ -z "$PYPI_TOKEN" ]; then | |
| echo "Missing PYPI_TOKEN secret; cannot publish to PyPI." | |
| exit 1 | |
| fi | |
| - name: Publish to PyPI | |
| uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist | |
| skip-existing: true | |
| attestations: false | |
| user: __token__ | |
| password: ${{ secrets.PYPI_TOKEN }} | |
| attach_release_assets: | |
| name: Attach wheel and conda artifacts to release | |
| needs: [build_wheels, build_conda, test] | |
| if: github.event_name == 'release' && github.event.release.target_commitish == 'main' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - name: Download wheel artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: wheels-* | |
| path: dist/wheels | |
| merge-multiple: true | |
| - name: Download conda artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| pattern: conda-* | |
| path: dist/conda | |
| merge-multiple: true | |
| - name: Show collected release artifacts | |
| shell: bash | |
| run: | | |
| shopt -s nullglob globstar | |
| files=(dist/wheels/*.whl dist/conda/**/labellib-*.tar.bz2 dist/conda/**/labellib-*.conda) | |
| if [ ${#files[@]} -eq 0 ]; then | |
| echo "No wheel or conda artifacts found to attach to release." | |
| exit 1 | |
| fi | |
| printf '%s\n' "${files[@]}" | |
| - name: Upload artifacts to GitHub release | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ github.event.release.tag_name }} | |
| files: | | |
| dist/wheels/*.whl | |
| dist/conda/**/labellib-*.conda | |
| fail_on_unmatched_files: false |