Skip to content

Commit

Permalink
🧪 Add a GHA workflow for updating lockfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
webknjaz committed Jul 8, 2024
1 parent 0b4af32 commit 0e222e5
Show file tree
Hide file tree
Showing 2 changed files with 319 additions and 0 deletions.
292 changes: 292 additions & 0 deletions .github/workflows/pip-tools.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,292 @@
---

name: 🔒 pip-tools

on:
workflow_dispatch:
inputs:
package-distribuition:
# github.event_name == 'workflow_dispatch'
# && github.event.inputs.package-distribuition
description: >-
A target Python package distribution to upgrade
required: false
pull_request:
paths:
- .github/workflows/pip-tools.yml
schedule:
- cron: 1 0 * * * # Run daily at 0:01 UTC

env:
GIT_BRANCH: >-
maintenance/pip-tools-constraint-lockfiles${{
(
github.event_name == 'workflow_dispatch'
&& github.event.inputs.package-distribuition
)
&& format('-updating-{0}', github.event.inputs.package-distribuition)
|| ''
}}
PIP_DISABLE_PIP_VERSION_CHECK: 1
PIP_NO_PYTHON_VERSION_WARNING: 1
PIP_NO_WARN_SCRIPT_LOCATION: 1
PY_COLORS: 1

concurrency:
group: >-
${{
github.workflow
}}-${{
github.event.inputs.package-distribuition
|| github.event.pull_request.number
|| github.sha
}}
cancel-in-progress: true

jobs:

deps:
name: >-
⛓🔒 🐍${{
matrix.python-version
}} @ ${{
matrix.os
}}
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version:
# NOTE: The latest and the lowest supported Pythons are prioritized
# NOTE: to improve the responsiveness. It's nice to see the most
# NOTE: important results first.
- 3.12
- 3.8
- pypy-3.10
- 3.11
- >-
3.10
- 3.9
os:
- ubuntu-20.04
- macOS-13.0
- windows-2019

env:
TOXENV: pip-compile-tox-env-lock

steps:
- name: Grab the source from Git
uses: actions/checkout@v4 # Keep before `setup-python` for cache to work

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
cache: pip
cache-dependency-path: requirements/**
python-version: ${{ matrix.python-version }}

- name: Install tox
run: python -Im pip install tox

- name: Pre-populate the tox env
run: python -Im tox --skip-missing-interpreters false --notest

- name: Setup git user as [bot]
# Refs:
# * https://gh.neting.ccmunity/t/github-actions-bot-email-address/17204/6
# * https://github.com/actions/checkout/issues/13#issuecomment-724415212
uses: fregante/setup-git-user@v2.0.1

- name: Generate constraints files
run: >-
python -Im tox r -- py pyproject.toml
${{
(
github.event_name == 'workflow_dispatch'
&& github.event.inputs.package-distribuition
)
&& format(
'--upgrade-package="{0}"',
github.event.inputs.package-distribuition
)
|| '--upgrade'
}}
- name: Commit version bumps to Git
id: constraints
run: |
LOCK_BASE_NAME=$(python bin/print_lockfile_base_name.py py)
git add "requirements/${LOCK_BASE_NAME}.txt"
git commit "requirements/${LOCK_BASE_NAME}.txt" \
-m "Update ${LOCK_BASE_NAME} constraints${{
(
github.event_name == 'workflow_dispatch'
&& github.event.inputs.package-distribuition
)
&& format(' for {0}', github.event.inputs.package-distribuition)
|| ''
}}" \
&& {
echo "patch=${{
runner.temp
}}/patches/0001-Update-${LOCK_BASE_NAME}-constraints.patch" \
>> "${GITHUB_OUTPUT}"
} \
|| :
shell: bash # windows compat

- name: Log the patch
if: steps.constraints.outputs.patch
run: git show --color
- name: Create a temporary patch directory
if: steps.constraints.outputs.patch
run: mkdir -pv '${{ runner.temp }}/patches'
shell: bash # windows compat
- name: Create a patch from the last Git commit
if: steps.constraints.outputs.patch
run: >-
git format-patch
--output='${{ steps.constraints.outputs.patch }}'
-1
HEAD
- name: Make a GHA artifact suffix
if: steps.constraints.outputs.patch
id: random
run: echo uuid=$(uuidgen) >> "${GITHUB_OUTPUT}"
shell: bash # windows compat
- name: Save the package bump patch as a GHA artifact
if: steps.constraints.outputs.patch
uses: actions/upload-artifact@v4
with:
name: pip-constraints-git-patches--${{ steps.random.outputs.uuid }}
path: ${{ steps.constraints.outputs.patch }}

check: # This job does nothing and is only used for the branch protection
name: >-
check
${{
(
github.event_name == 'workflow_dispatch'
&& github.event.inputs.package-distribuition
)
&& format('`{0}`', github.event.inputs.package-distribuition)
|| 'everything'
}}
if: always()

needs:
- deps

runs-on: ubuntu-latest

steps:
- name: Decide whether the needed jobs succeeded or failed
uses: re-actors/alls-green@release/v1
with:
jobs: ${{ toJSON(needs) }}

publish-pr:
name: Open/refresh a PR
if: github.event_name != 'pull_request'
needs:
- check
runs-on: Ubuntu-latest

environment: pip-tools

permissions:
contents: write # Needed to enable auto-merge
pull-requests: write

steps:
- name: Download all the dists
id: artifacts-download
continue-on-error: true # and judge whether there's updates later
uses: actions/download-artifact@v4
with:
merge-multiple: true
path: ${{ runner.temp }}/patches/
pattern: pip-constraints-git-patches--*
- name: >-
Determine whether any change suggestions to lockfiles
have been produced
if: steps.artifacts-download.outcome == 'success'
id: artifacts
run: >-
echo "lockfile-updates-needed=true" >> "${GITHUB_OUTPUT}"
- name: Grab the source from Git
if: steps.artifacts.outputs.lockfile-updates-needed
uses: actions/checkout@v4
- name: Setup git user as [bot]
if: steps.artifacts.outputs.lockfile-updates-needed
# Refs:
# * https://gh.neting.ccmunity/t/github-actions-bot-email-address/17204/6
# * https://github.com/actions/checkout/issues/13#issuecomment-724415212
uses: fregante/setup-git-user@v2.0.1

- name: Figure out if the pre-existing remote branch exists
if: steps.artifacts.outputs.lockfile-updates-needed
id: pre-existing-remote-branch
run: >-
echo "info=$(
git ls-remote origin "${GIT_BRANCH}"
)" >> "${GITHUB_OUTPUT}"
- name: Fetch the existing remote PR branch
if: steps.pre-existing-remote-branch.outputs.info
run: git fetch origin "${GIT_BRANCH}"
- name: Switch to the PR branch
if: steps.artifacts.outputs.lockfile-updates-needed
run: git checkout -B "${GIT_BRANCH}"

- name: List Git patches
if: steps.artifacts.outputs.lockfile-updates-needed
run: ls -alh '${{ runner.temp }}/patches/'
- name: Apply patches to the Git repo
if: steps.artifacts.outputs.lockfile-updates-needed
run: git am '${{ runner.temp }}/patches'/*.patch
- name: Force-push the PR branch to remote
if: steps.artifacts.outputs.lockfile-updates-needed
run: git push origin "HEAD:${GIT_BRANCH}" --force-with-lease

- name: Create a PR
if: >-
!steps.pre-existing-remote-branch.outputs.info
&& steps.artifacts.outputs.lockfile-updates-needed
id: pr
uses: vsoch/pull-request-action@1.1.1
env:
BRANCH_PREFIX: ''
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PULL_REQUEST_BODY: >-
Automated pip-tools-managed pip constraint lockfiles update.
PULL_REQUEST_BRANCH: ${{ github.event.repository.default_branch }}
PULL_REQUEST_DRAFT: true
PULL_REQUEST_FROM_BRANCH: ${{ env.GIT_BRANCH }}
PULL_REQUEST_TITLE: >-
⛓🔒 Bump transitive deps in pip-tools-managed lockfiles${{
(
github.event_name == 'workflow_dispatch'
&& github.event.inputs.package-distribuition
)
&& format(' for {0}', github.event.inputs.package-distribuition)
|| ''
}}
- name: Log the pull request details
run: >-
echo
"PR number: ${{ steps.pr.outputs.pull_request_number }}"
"\nPR URL: ${{ steps.pr.outputs.pull_request_url }}"
- name: Instruct the maintainers to trigger CI by undrafting the PR
env:
GITHUB_TOKEN: ${{ github.token }}
run: >-
gh pr comment
--body 'Please mark the PR as ready for review to trigger PR checks.'
--repo '${{ github.repository }}'
'${{ steps.pr.outputs.pull_request_number }}'
...
27 changes: 27 additions & 0 deletions bin/print_lockfile_base_name.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#! /usr/bin/env python
"""A script that prints platform-specific constraints file name base."""

from __future__ import annotations

import sys

from pip_constraint_helpers import (
get_constraint_file_path,
get_runtime_python_tag,
)


def compute_constraint_base_name(toxenv: str) -> str:
"""Get the lock file name stem.
:param toxenv: Name of the tox env.
"""
return get_constraint_file_path(
req_dir='',
toxenv=toxenv,
python_tag=get_runtime_python_tag(),
).stem


if __name__ == '__main__':
print(compute_constraint_base_name(sys.argv[1]))

0 comments on commit 0e222e5

Please sign in to comment.