Skip to content

ComPWA/update-pip-constraints

Repository files navigation

Update pip constraint files

BSD 3-Clause license GitPod pre-commit.ci status Spelling checked code style: prettier Ruff

This Python package is a wrapper around pip-tools. It helps updating a collection of PyPI constraints files for your repository. One constraint file is created for each Python version that your package supports (see here why this is important). The file structure is as follows:

.constraints/
├── py3.6.txt
├── py3.7.txt
└── ...

Constraint files allow you to pin your package requirements as follows:

python3 -m pip install -c .constraints/py3.7.txt -e .[dev]

This is just an example where you install your package in editable mode (-e flag) on Python 3.7, where we also installed optional dependencies defined under an extras_require section called [dev].

Why ship your repository with constraint files?

  • Constraint files reduce the resolution time of the PyPI dependency resolver.
  • Constraint files make the developer environment reproducible for each commit and for each supported version of Python.
  • Constraint files provide a way out of dependency hell.

Usage

Python package

This Package can be installed with pip from GitHub:

python3 -m pip install git+https://github.com/ComPWA/update-pip-constraints@main

Now, if you run the command:

update-pip-constraints

an updated constraint file will be created for your version of Python under .constraints/py3.x.txt.

GitHub Action

Here are two examples of how to use update-pip-constraints as a GitHub Action:

Update constraints files during a PR by pushing if there are dependency changes
name: Requirements (PR)

on:
  pull_request:
    branches: [main]

jobs:
  pip-constraints:
    name: Update pip constraints files
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        python-version:
          - "3.7"
          - "3.8"
          - "3.9"
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Check if there are dependency changes
        run: git diff origin/main --exit-code -- .constraints setup.cfg
        continue-on-error: true
      - name: Update pip constraints files
        if: success()
        uses: ComPWA/update-pip-constraints@main
        with:
          python-version: ${{ matrix.python-version }}

  push:
    name: Push changes
    if: github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-22.04
    needs:
      - pip-constraints
    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ secrets.PAT }}
      - uses: actions/download-artifact@v2
      - run: rm -rf .constraints/
      - run: mv artifact .constraints
      - name: Commit and push changes
        run: |
          git remote set-url origin https://x-access-token:${{ secrets.PAT }}@github.com/${{ github.repository }}
          git config --global user.name "GitHub"
          git config --global user.email "noreply@github.com"
          git checkout -b ${GITHUB_HEAD_REF}
          if [[ $(git status -s) ]]; then
            git add -A
            git commit -m "ci: upgrade pinned requirements (automatic)"
            git config pull.rebase true
            git pull origin ${GITHUB_HEAD_REF}
            git push origin HEAD:${GITHUB_HEAD_REF}
          fi
Create a PR with updated constraints files
name: Requirements (scheduled)

on:
  schedule:
    - cron: "0 2 * * 1"
  workflow_dispatch:

jobs:
  pip-constraints:
    name: Update pip constraint files
    runs-on: ubuntu-22.04
    strategy:
      fail-fast: false
      matrix:
        python-version:
          - "3.7"
          - "3.8"
          - "3.9"
    steps:
      - uses: actions/checkout@v4
      - uses: ComPWA/update-pip-constraints@v1
        with:
          python-version: ${{ matrix.python-version }}

  push:
    name: Create PR
    runs-on: ubuntu-22.04
    needs:
      - pip-constraints
    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ secrets.PAT }}
      - uses: actions/download-artifact@v4
      - run: rm -rf .constraints/
      - run: mv artifact .constraints
      - uses: peter-evans/create-pull-request@v6
        with:
          commit-message: "ci: update pip constraints files"
          committer: GitHub <noreply@github.com>
          author: GitHub <noreply@github.com>
          title: "ci: update pip constraints files"
          branch-suffix: timestamp
          delete-branch: true
          token: ${{ secrets.PAT }}
      - name: Print PR info
        run: |
          echo "Pull Request Number - ${{ steps.cpr.outputs.pull-request-number }}"
          echo "Pull Request URL - ${{ steps.cpr.outputs.pull-request-url }}"

Note that you will have to set a create a Personal Access Token (named PAT in the examples) in order to push the changes to the PR branch. The automatic GITHUB_TOKEN can be used as well, but that will not start the workflows.