Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/codespell.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ name: Codespell

on:
pull_request:
branches: [master]
push:
branches: [master]

permissions:
contents: read
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ name: Lints

on:
pull_request:
branches: [master]
push:
branches: [master]
paths-ignore:
- '**.rst'

Expand Down
94 changes: 94 additions & 0 deletions .github/workflows/parse_release_notes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
"""Parse the latest release notes from CHANGELOG.md.

If running in GitHub Actions, set the `release_title` output
variable for use in subsequent step(s).

If running in CI, write the release notes to ReleaseNotes.md
for upload as an artifact.

Otherwise, print the release title and notes to stdout.
"""

import re
import subprocess
from os import environ
from pathlib import Path


class ChangesEntry:
def __init__(self, version: str, notes: str) -> None:
self.version = version
title = notes.splitlines()[0]
self.title = f'{version} {title}'
self.notes = notes[len(title) :].strip()


H1 = re.compile(r'^# (\d+\.\d+\.\d+)', re.MULTILINE)


def parse_changelog() -> list[ChangesEntry]:
changelog = Path('CHANGELOG.md').read_text(encoding='utf-8')
parsed = H1.split(changelog) # may result in a blank line at index 0
if not parsed[0]: # leading entry is a blank line due to re.split() implementation
parsed = parsed[1:]
assert len(parsed) % 2 == 0, (
'Malformed CHANGELOG.md; Entries expected to start with "# x.y.x"'
)

changes: list[ChangesEntry] = []
for i in range(0, len(parsed), 2):
version = parsed[i]
notes = parsed[i + 1].strip()
changes.append(ChangesEntry(version, notes))
return changes


def get_version_tag() -> str | None:
if 'GITHUB_REF' in environ: # for use in GitHub Actions
git_ref = environ['GITHUB_REF']
else: # for local use
git_out = subprocess.run(
['git', 'rev-parse', '--symbolic-full-name', 'HEAD'],
capture_output=True,
text=True,
check=True,
)
git_ref = git_out.stdout.strip()
version: str | None = None
if git_ref and git_ref.startswith('refs/tags/'):
version = git_ref[len('refs/tags/') :].lstrip('v')
else:
print(
f"Using latest CHANGELOG.md entry because the git ref '{git_ref}' is not a tag."
)
return version


def get_entry(changes: list[ChangesEntry], version: str | None) -> ChangesEntry:
latest = changes[0]
if version is not None:
for entry in changes:
if entry.version == version:
latest = entry
break
else:
raise ValueError(f'No changelog entry found for version {version}')
return latest


def main() -> None:
changes = parse_changelog()
version = get_version_tag()
latest = get_entry(changes=changes, version=version)
if 'GITHUB_OUTPUT' in environ:
with Path(environ['GITHUB_OUTPUT']).open('a') as gh_out:
print(f'release_title={latest.title}', file=gh_out)
if environ.get('CI', 'false') == 'true':
Path('ReleaseNotes.md').write_text(latest.notes, encoding='utf-8')
else:
print('Release notes:')
print(f'# {latest.title}\n{latest.notes}')


if __name__ == '__main__':
main()
48 changes: 3 additions & 45 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -1,40 +1,14 @@
name: Tests
name: Tests (s390x)

on:
pull_request:
branches: [master]
push:
branches: [master]
paths-ignore:
- '**.rst'

jobs:
linux:
runs-on: ${{ matrix.os }}
strategy:
matrix:
include:
- os: ubuntu-24.04
python-version: '3.10'
- os: ubuntu-24.04
python-version: '3.13'
- os: ubuntu-24.04
python-version: 'pypy3.10'
- os: ubuntu-24.04-arm
python-version: '3.13'

steps:
- name: Checkout pygit2
uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}

- name: Linux
run: |
sudo apt install tinyproxy
LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.1 /bin/sh build.sh test

linux-s390x:
runs-on: ubuntu-24.04
if: github.ref == 'refs/heads/master'
Expand All @@ -53,19 +27,3 @@ jobs:
run: |
LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.1 /bin/sh build.sh test
continue-on-error: true # Tests are expected to fail, see issue #812

macos-arm64:
runs-on: macos-latest
steps:
- name: Checkout pygit2
uses: actions/checkout@v5

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: '3.13'

- name: macOS
run: |
export OPENSSL_PREFIX=`brew --prefix openssl@3`
LIBSSH2_VERSION=1.11.1 LIBGIT2_VERSION=1.9.1 /bin/sh build.sh test
50 changes: 45 additions & 5 deletions .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
name: Wheels

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref_name != 'master' }}

on:
push:
branches:
- master
- wheels-*
branches: [master]
tags:
- 'v*'
- 'v*'
pull_request:
branches: [master]
paths-ignore:
- 'docs/**'

jobs:
build_wheels:
Expand Down Expand Up @@ -54,6 +60,7 @@ jobs:

build_wheels_ppc:
name: Wheels for linux-ppc
if: github.ref == 'refs/heads/master'
runs-on: ubuntu-24.04

steps:
Expand Down Expand Up @@ -86,6 +93,8 @@ jobs:

sdist:
runs-on: ubuntu-latest
outputs:
release_title: ${{ steps.parse_changelog.outputs.release_title }}
steps:
- uses: actions/checkout@v5
with:
Expand All @@ -104,6 +113,17 @@ jobs:
name: wheels-sdist
path: dist/*

- name: parse CHANGELOG for release notes
id: parse_changelog
run: python .github/workflows/parse_release_notes.py

- name: Upload Release Notes
uses: actions/upload-artifact@v4
with:
name: release-notes
path: ReleaseNotes.md


twine-check:
name: Twine check
# It is good to do this check on non-tagged commits.
Expand All @@ -123,7 +143,9 @@ jobs:

pypi:
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
needs: [build_wheels, build_wheels_ppc]
needs: [build_wheels, build_wheels_ppc, sdist]
permissions:
contents: write # to create GitHub Release
runs-on: ubuntu-24.04

steps:
Expand All @@ -140,3 +162,21 @@ jobs:
with:
user: __token__
password: ${{ secrets.PYPI_API_TOKEN }}
skip-existing: true

- uses: actions/download-artifact@v5
with:
name: release-notes
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ github.token }}
TAG: ${{ github.ref_name }}
REPO: ${{ github.repository }}
TITLE: ${{ needs.sdist.outputs.release_title }}
# https://cli.github.com/manual/gh_release_create
run: >-
gh release create ${TAG}
--verify-tag
--repo ${REPO}
--title ${TITLE}
--notes-file ReleaseNotes.md
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ environment = {LIBGIT2_VERSION="1.9.1", LIBSSH2_VERSION="1.11.1", OPENSSL_VERSIO

before-all = "sh build.sh"
test-command = "pytest"
test-sources = ["test"]
test-sources = ["test", "pytest.ini"]
before-test = "pip install -r {project}/requirements-test.txt"
# Will avoid testing on emulated architectures (specifically ppc64le)
test-skip = "*-*linux_ppc64le"
Expand Down
Loading