Skip to content

Commit db83798

Browse files
committed
general: update CI files
- set min version to python 3.9 - switch build backend to hatch - publish via uv
1 parent da56aae commit db83798

11 files changed

Lines changed: 276 additions & 76 deletions

File tree

.ci/release-uv

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env python3
2+
'''
3+
Deploys Python package onto [[https://pypi.org][PyPi]] or [[https://test.pypi.org][test PyPi]].
4+
5+
- running manually
6+
7+
You'll need =UV_PUBLISH_TOKEN= env variable
8+
9+
- running on Github Actions
10+
11+
Instead of env variable, relies on configuring github as Trusted publisher (https://docs.pypi.org/trusted-publishers/) -- both for test and regular pypi
12+
13+
It's running as =pypi= job in [[file:.github/workflows/main.yml][Github Actions config]].
14+
Packages are deployed on:
15+
- every master commit, onto test pypi
16+
- every new tag, onto production pypi
17+
'''
18+
19+
UV_PUBLISH_TOKEN = 'UV_PUBLISH_TOKEN'
20+
21+
import argparse
22+
import os
23+
import shutil
24+
from pathlib import Path
25+
from subprocess import check_call
26+
27+
is_ci = os.environ.get('CI') is not None
28+
29+
def main() -> None:
30+
p = argparse.ArgumentParser()
31+
p.add_argument('--use-test-pypi', action='store_true')
32+
args = p.parse_args()
33+
34+
publish_url = ['--publish-url', 'https://test.pypi.org/legacy/'] if args.use_test_pypi else []
35+
36+
root = Path(__file__).absolute().parent.parent
37+
os.chdir(root) # just in case
38+
39+
if is_ci:
40+
# see https://github.com/actions/checkout/issues/217
41+
check_call('git fetch --prune --unshallow'.split())
42+
43+
# TODO ok, for now uv won't remove dist dir if it already exists
44+
# https://github.com/astral-sh/uv/issues/10293
45+
dist = root / 'dist'
46+
if dist.exists():
47+
shutil.rmtree(dist)
48+
49+
# todo what is --force-pep517?
50+
check_call(['uv', 'build'])
51+
52+
if not is_ci:
53+
# CI relies on trusted publishers so doesn't need env variable
54+
assert UV_PUBLISH_TOKEN in os.environ, f'no {UV_PUBLISH_TOKEN} passed'
55+
56+
check_call(['uv', 'publish', *publish_url])
57+
58+
59+
if __name__ == '__main__':
60+
main()

.ci/run

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ if ! command -v sudo; then
1111
}
1212
fi
1313

14+
# --parallel-live to show outputs while it's running
15+
tox_cmd='run-parallel --parallel-live'
1416
if [ -n "${CI-}" ]; then
1517
# install OS specific stuff here
1618
case "$OSTYPE" in
@@ -20,7 +22,8 @@ if [ -n "${CI-}" ]; then
2022
;;
2123
cygwin* | msys* | win*)
2224
# windows
23-
:
25+
# ugh. parallel stuff seems super flaky under windows, some random failures, "file used by other process" and crap like that
26+
tox_cmd='run'
2427
;;
2528
*)
2629
# must be linux?
@@ -29,12 +32,5 @@ if [ -n "${CI-}" ]; then
2932
esac
3033
fi
3134

32-
33-
PY_BIN="python3"
34-
# some systems might have python pointing to python3
35-
if ! command -v python3 &> /dev/null; then
36-
PY_BIN="python"
37-
fi
38-
39-
"$PY_BIN" -m pip install --user tox
40-
"$PY_BIN" -m tox --parallel --parallel-live "$@"
35+
# NOTE: expects uv installed
36+
uv tool run --with tox-uv tox $tox_cmd "$@"

.github/workflows/main.yml

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ on:
66
branches: '*'
77
tags: 'v[0-9]+.*' # only trigger on 'release' tags for PyPi
88
# Ideally I would put this in the pypi job... but github syntax doesn't allow for regexes there :shrug:
9-
# P.S. fuck made up yaml DSLs.
109
pull_request: # needed to trigger on others' PRs
1110
# Note that people who fork it need to go to "Actions" tab on their fork and click "I understand my workflows, go ahead and enable them".
1211
workflow_dispatch: # needed to trigger workflows manually
@@ -25,23 +24,31 @@ jobs:
2524
fail-fast: false
2625
matrix:
2726
platform: [ubuntu-latest, macos-latest, windows-latest]
28-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
27+
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
2928
# vvv just an example of excluding stuff from matrix
3029
# exclude: [{platform: macos-latest, python-version: '3.6'}]
3130

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

33+
# useful for 'optional' pipelines
34+
# continue-on-error: ${{ matrix.platform == 'windows-latest' }}
35+
3436
steps:
3537
# ugh https://github.com/actions/toolkit/blob/main/docs/commands.md#path-manipulation
3638
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH
3739

38-
- uses: actions/setup-python@v4
40+
- uses: actions/checkout@v4
3941
with:
40-
python-version: ${{ matrix.python-version }}
42+
submodules: recursive
43+
fetch-depth: 0 # nicer to have all git history when debugging/for tests
4144

42-
- uses: actions/checkout@v3
45+
- uses: actions/setup-python@v5
4346
with:
44-
submodules: recursive
47+
python-version: ${{ matrix.python-version }}
48+
49+
- uses: astral-sh/setup-uv@v5
50+
with:
51+
enable-cache: false # we don't have lock files, so can't use them as cache key
4552

4653
- uses: mxschmitt/action-tmate@v3
4754
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
@@ -50,39 +57,42 @@ jobs:
5057
- run: bash .ci/run
5158

5259
- if: matrix.platform == 'ubuntu-latest' # no need to compute coverage for other platforms
53-
uses: actions/upload-artifact@v3
60+
uses: actions/upload-artifact@v4
5461
with:
62+
include-hidden-files: true
5563
name: .coverage.mypy_${{ matrix.platform }}_${{ matrix.python-version }}
5664
path: .coverage.mypy/
5765

5866

5967
pypi:
6068
runs-on: ubuntu-latest
6169
needs: [build] # add all other jobs here
62-
70+
permissions:
71+
# necessary for Trusted Publishing
72+
id-token: write
6373
steps:
6474
# ugh https://github.com/actions/toolkit/blob/main/docs/commands.md#path-manipulation
6575
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH
6676

67-
- uses: actions/setup-python@v4
77+
- uses: actions/checkout@v4
6878
with:
69-
python-version: '3.8'
79+
submodules: recursive
7080

71-
- uses: actions/checkout@v3
81+
- uses: actions/setup-python@v5
7282
with:
73-
submodules: recursive
83+
python-version: '3.10'
84+
85+
- uses: astral-sh/setup-uv@v5
86+
with:
87+
enable-cache: false # we don't have lock files, so can't use them as cache key
7488

7589
- name: 'release to test pypi'
7690
# always deploy merged master to test pypi
7791
if: github.event_name != 'pull_request' && github.event.ref == 'refs/heads/master'
78-
env:
79-
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD_TEST }}
80-
run: pip3 install --user --upgrade build twine && .ci/release --test
92+
run: .ci/release-uv --use-test-pypi
8193

8294
- name: 'release to pypi'
8395
# always deploy tags to release pypi
8496
# NOTE: release tags are guarded by on: push: tags on the top
8597
if: github.event_name != 'pull_request' && startsWith(github.event.ref, 'refs/tags')
86-
env:
87-
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
88-
run: pip3 install --user --upgrade build twine && .ci/release
98+
run: .ci/release-uv

conftest.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# without it, pytest can't discover the package root for some reason
33
# also see https://github.com/karlicoss/pytest_namespace_pkgs for more
44

5+
import os
56
import pathlib
67
from typing import Optional
78

@@ -24,6 +25,10 @@ def resolve_package_path(path: pathlib.Path) -> Optional[pathlib.Path]:
2425
for parent in result.parents:
2526
if str(parent) in namespace_pkg_dirs:
2627
return parent
28+
if os.name == 'nt':
29+
# ??? for some reason on windows it is trying to call this against conftest? but not on linux/osx
30+
if path.name == 'conftest.py':
31+
return resolve_pkg_path_orig(path)
2732
raise RuntimeError("Couldn't determine path for ", path)
2833
_pytest.pathlib.resolve_package_path = resolve_package_path
2934

@@ -34,5 +39,9 @@ def resolve_package_path(path: pathlib.Path) -> Optional[pathlib.Path]:
3439
# not sure what are the consequences.. maybe it wouldn't be able to run against installed packages? not sure..
3540
search_pypath_orig = _pytest.main.search_pypath
3641
def search_pypath(module_name: str) -> str:
37-
return str(root_dir)
42+
mpath = root_dir / module_name.replace('.', os.sep)
43+
if not mpath.is_dir():
44+
mpath = mpath.with_suffix('.py')
45+
assert mpath.exists(), mpath # just in case
46+
return str(mpath)
3847
_pytest.main.search_pypath = search_pypath

mypy.ini

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
[mypy]
2-
namespace_packages = True
32
pretty = True
43
show_error_context = True
5-
show_error_codes = True
64
show_column_numbers = True
75
show_error_end = True
8-
warn_unused_ignores = True
6+
97
check_untyped_defs = True
10-
enable_error_code = possibly-undefined
8+
9+
# see https://mypy.readthedocs.io/en/stable/error_code_list2.html
10+
warn_redundant_casts = True
1111
strict_equality = True
12+
warn_unused_ignores = True
13+
enable_error_code = deprecated,redundant-expr,possibly-undefined,truthy-bool,truthy-iterable,ignore-without-code,unused-awaitable
14+
1215

1316
# an example of suppressing
1417
# [mypy-my.config.repos.pdfannots.pdfannots]

pyproject.toml

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
[project]
2-
dynamic = ["version"] # version is managed by setuptools_scm
2+
dynamic = ["version"] # version is managed by build backend
33
name = "orgparse"
4+
dependencies = [
5+
]
6+
requires-python = ">=3.9"
47
description = "orgparse - Emacs org-mode parser in Python"
58
license = {file = "LICENSE"}
69
authors = [
@@ -24,37 +27,29 @@ classifiers = [
2427
Homepage = "https://github.com/karlicoss/orgparse"
2528

2629
[project.optional-dependencies]
30+
[dependency-groups]
2731
testing = [
28-
"pytest",
29-
]
30-
linting = [
3132
"pytest",
3233
"ruff",
3334
"mypy",
3435
"lxml", # for mypy html coverage
3536
]
3637

3738

38-
[build-system]
39-
requires = ["setuptools", "setuptools-scm"]
40-
build-backend = "setuptools.build_meta"
4139

42-
[tool.setuptools_scm]
43-
version_scheme = "python-simplified-semver"
44-
local_scheme = "dirty-tag"
40+
# workaround for error during uv publishing
41+
# see https://github.com/astral-sh/uv/issues/9513#issuecomment-2519527822
42+
[tool.setuptools]
43+
license-files = []
4544

4645

47-
# nice things about pyproject.toml
48-
# - zip_safe=False isn't neccessary anymore
49-
# - correctly discovers namespace packages by defuilt?
50-
# - correctly handles py.typed by default?
51-
# - handles src layout automatically https://setuptools.pypa.io/en/latest/userguide/package_discovery.html#src-layout
46+
[build-system]
47+
requires = ["hatchling", "hatch-vcs"]
48+
build-backend = "hatchling.build"
5249

53-
# things I'm not sure about yet
54-
# - avoiding dupliation/variable reuse?
55-
# - file/git dependencies?
56-
# - unclear how to specify namespace package order https://github.com/seanbreckenridge/reorder_editable/issues/2
50+
# unfortunately have to duplicate project name here atm, see https://github.com/pypa/hatch/issues/1894
51+
[tool.hatch.build.targets.wheel]
52+
packages = ["src/orgparse"]
5753

58-
# TODO
59-
# - maybe it has a nicer pypi upload system? not sure
60-
# e.g. possibly use hatch/flit/pdb/poetry -- but not sure what's the benefit tbh
54+
[tool.hatch.version]
55+
source = "vcs"

pytest.ini

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
[pytest]
22
# discover files that don't follow test_ naming. Useful to keep tests along with the source code
33
python_files = *.py
4+
5+
# this setting only impacts package/module naming under pytest, not the discovery
6+
consider_namespace_packages = true
7+
48
addopts =
9+
# prevent pytest cache from being created... it craps into project dir and I never use it anyway
10+
-p no:cacheprovider
11+
512
# -rap to print tests summary even when they are successful
613
-rap
714
--verbose

0 commit comments

Comments
 (0)