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
69 changes: 26 additions & 43 deletions .ci/release
Original file line number Diff line number Diff line change
@@ -1,65 +1,48 @@
#!/usr/bin/env python3
'''
Run [[file:scripts/release][scripts/release]] to deploy Python package onto [[https://pypi.org][PyPi]] and [[https://test.pypi.org][test PyPi]].
Deploys Python package onto [[https://pypi.org][PyPi]] or [[https://test.pypi.org][test PyPi]].

The script expects =TWINE_PASSWORD= environment variable to contain the [[https://pypi.org/help/#apitoken][PyPi token]] (not the password!).
- running manually

The script can be run manually.
It's also running as =pypi= job in [[file:.github/workflows/main.yml][Github Actions config]]. Packages are deployed on:
- every master commit, onto test pypi
- every new tag, onto production pypi
You'll need =UV_PUBLISH_TOKEN= env variable

You'll need to set =TWINE_PASSWORD= and =TWINE_PASSWORD_TEST= in [[https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets#creating-encrypted-secrets][secrets]]
for Github Actions deployment to work.
- running on Github Actions

Instead of env variable, relies on configuring github as Trusted publisher (https://docs.pypi.org/trusted-publishers/) -- both for test and regular pypi

It's running as =pypi= job in [[file:.github/workflows/main.yml][Github Actions config]].
Packages are deployed on:
- every master commit, onto test pypi
- every new tag, onto production pypi
'''

UV_PUBLISH_TOKEN = 'UV_PUBLISH_TOKEN'

import argparse
import os
import sys
from pathlib import Path
from subprocess import check_call
import shutil

is_ci = os.environ.get('CI') is not None


def main() -> None:
import argparse
p = argparse.ArgumentParser()
p.add_argument('--test', action='store_true', help='use test pypi')
p.add_argument('--use-test-pypi', action='store_true')
args = p.parse_args()

extra = []
if args.test:
extra.extend(['--repository', 'testpypi'])
publish_url = ['--publish-url', 'https://test.pypi.org/legacy/'] if args.use_test_pypi else []

root = Path(__file__).absolute().parent.parent
os.chdir(root) # just in case

if is_ci:
# see https://github.com/actions/checkout/issues/217
check_call('git fetch --prune --unshallow'.split())

dist = root / 'dist'
if dist.exists():
shutil.rmtree(dist)

check_call(['python3', '-m', 'build'])

TP = 'TWINE_PASSWORD'
password = os.environ.get(TP)
if password is None:
print(f"WARNING: no {TP} passed", file=sys.stderr)
import pip_secrets
password = pip_secrets.token_test if args.test else pip_secrets.token # meh

check_call([
'python3', '-m', 'twine',
'upload', *dist.iterdir(),
*extra,
], env={
'TWINE_USERNAME': '__token__',
TP: password,
**os.environ,
})
os.chdir(root) # just in case

check_call(['uv', 'build', '--clear'])

if not is_ci:
# CI relies on trusted publishers so doesn't need env variable
assert UV_PUBLISH_TOKEN in os.environ, f'no {UV_PUBLISH_TOKEN} passed'

check_call(['uv', 'publish', *publish_url])


if __name__ == '__main__':
Expand Down
56 changes: 0 additions & 56 deletions .ci/release-uv

This file was deleted.

4 changes: 3 additions & 1 deletion .ci/run
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ tox_cmd='run-parallel --parallel-live'
if [ -n "${CI-}" ]; then
# install OS specific stuff here
case "$OSTYPE" in
darwin*)
darwin*)
# macos
:
;;
Expand All @@ -34,3 +34,5 @@ fi

# NOTE: expects uv installed
uv tool run --with tox-uv tox $tox_cmd "$@"
# NOTE: experimenting for now... might switch later for good
# uv tool run nox "$@"
76 changes: 33 additions & 43 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,54 +27,43 @@ jobs:
fail-fast: false
matrix:
platform: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
# vvv just an example of excluding stuff from matrix
# exclude: [{platform: macos-latest, python-version: '3.6'}]
python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
exclude: [
# windows runners are pretty scarce, so let's only run lowest and highest python version
{platform: windows-latest, python-version: '3.11'},
{platform: windows-latest, python-version: '3.12'},
{platform: windows-latest, python-version: '3.13'},
]

runs-on: ${{ matrix.platform }}

# useful for 'optional' pipelines
# continue-on-error: ${{ matrix.platform == 'windows-latest' }}

steps:
# ugh https://github.com/actions/toolkit/blob/main/docs/commands.md#path-manipulation
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH

- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0 # nicer to have all git history when debugging/for tests

- uses: actions/setup-python@v6
- uses: astral-sh/setup-uv@v8.1.0
with:
python-version: ${{ matrix.python-version }}

- uses: astral-sh/setup-uv@v7
with:
enable-cache: false # we don't have lock files, so can't use them as cache key
enable-cache: false # we don't have lock files during initial CI checkout, so can't use them as cache key

- uses: mxschmitt/action-tmate@v3
with:
limit-access-to-actor: true # restrict to the user who kicked off pipeline
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}

# explicit bash command is necessary for Windows CI runner, otherwise it thinks it's cmd...
- run: bash .ci/run
env:
# only compute lxml coverage on ubuntu; it crashes on windows
CI_MYPY_COVERAGE: ${{ matrix.platform == 'ubuntu-latest' && '--cobertura-xml-report .coverage.mypy' || '' }}

- if: matrix.platform == 'ubuntu-latest' # no need to compute coverage for other platforms
uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true # default false
token: ${{ secrets.CODECOV_TOKEN }}
flags: mypy-${{ matrix.python-version }}
files: .coverage.mypy/cobertura.xml


pypi:
# Do not run it for PRs/cron schedule etc.
# NOTE: release tags are guarded by on: push: tags on the top.
if: github.event_name == 'push' && (startsWith(github.event.ref, 'refs/tags/') || (github.event.ref == format('refs/heads/{0}', github.event.repository.master_branch)))
if: github.event_name == 'push' && (github.ref_type == 'tag' || github.ref_name == github.event.repository.master_branch)
# Ugh, I tried using matrix or something to explicitly generate only test pypi or prod pypi pipelines.
# But github actions is so shit, it's impossible to do any logic at all, e.g. doesn't support conditional matrix, if/else statements for variables etc.

Expand All @@ -85,30 +74,31 @@ jobs:
permissions:
# necessary for Trusted Publishing
id-token: write

env:
# always deploy merged master to test pypi
# always deploy tags to release pypi
TARGET: ${{ github.ref_type == 'tag' && 'pypi' || 'testpypi' }}
environment:
# for "deployments" tab on github
# sadly can't reuse env.TARGET here...
name: ${{ github.ref_type == 'tag' && 'pypi' || 'testpypi' }}
url: https://${{ github.ref_type == 'tag' && 'pypi.org' || 'test.pypi.org' }}/project/${{ steps.meta.outputs.pypi_name }}/
steps:
# ugh https://github.com/actions/toolkit/blob/main/docs/commands.md#path-manipulation
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH

- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0 # pull all commits to correctly infer vcs version

- uses: actions/setup-python@v6
with:
python-version: '3.10'

- uses: astral-sh/setup-uv@v7
- uses: astral-sh/setup-uv@v8.1.0
with:
enable-cache: false # we don't have lock files, so can't use them as cache key
python-version: '3.12'
enable-cache: false # we don't have lock files during initial CI checkout, so can't use them as cache key

- name: 'release to test pypi'
# always deploy merged master to test pypi
if: github.event.ref == format('refs/heads/{0}', github.event.repository.master_branch)
run: .ci/release-uv --use-test-pypi
- name: 'release ${{ steps.meta.outputs.pypi_name }} to ${{ env.TARGET }}'
run: .ci/release ${{ env.TARGET == 'testpypi' && '--use-test-pypi' || '' }}

- name: 'release to prod pypi'
# always deploy tags to release pypi
if: startsWith(github.event.ref, 'refs/tags/')
run: .ci/release-uv
- id: meta
shell: bash
run: |
pypi_name=$(unzip -p dist/*.whl '*.dist-info/METADATA' | awk '/^Name:/ {print $2; exit}')
echo "pypi_name=$pypi_name" >> "$GITHUB_OUTPUT"
58 changes: 0 additions & 58 deletions conftest.py

This file was deleted.

18 changes: 0 additions & 18 deletions mypy.ini

This file was deleted.

Loading
Loading