Skip to content

Commit

Permalink
ci: switch building & release to uv, update ruff and mypy configs
Browse files Browse the repository at this point in the history
  • Loading branch information
karlicoss committed Jan 22, 2025
1 parent 7906eaf commit d55b6b1
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 39 deletions.
60 changes: 60 additions & 0 deletions .ci/release-uv
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
'''
Deploys Python package onto [[https://pypi.org][PyPi]] or [[https://test.pypi.org][test PyPi]].
- running manually
You'll need =UV_PUBLISH_TOKEN= env variable
- 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 shutil
from pathlib import Path
from subprocess import check_call

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

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

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())

# TODO ok, for now uv won't remove dist dir if it already exists
# https://github.com/astral-sh/uv/issues/10293
dist = root / 'dist'
if dist.exists():
shutil.rmtree(dist)

# todo what is --force-pep517?
check_call(['uv', 'build'])

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__':
main()
15 changes: 2 additions & 13 deletions .ci/run
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,5 @@ if [ -n "${CI-}" ]; then
esac
fi


PY_BIN="python3"
# some systems might have python pointing to python3
if ! command -v python3 &> /dev/null; then
PY_BIN="python"
fi


# TODO hmm for some reason installing uv with pip and then running
# "$PY_BIN" -m uv tool fails with missing setuptools error??
# just uvx directly works, but it's not present in PATH...
"$PY_BIN" -m pip install --user pipx
"$PY_BIN" -m pipx run uv tool run --with=tox-uv tox $tox_cmd "$@"
# NOTE: expects uv installed
uv tool run --with tox-uv tox $tox_cmd "$@"
20 changes: 13 additions & 7 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

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

- uses: actions/checkout@v4
with:
Expand Down Expand Up @@ -70,7 +74,9 @@ jobs:
pypi:
runs-on: ubuntu-latest
needs: [build] # add all other jobs here

permissions:
# necessary for Trusted Publishing
id-token: write
steps:
# ugh https://github.com/actions/toolkit/blob/main/docs/commands.md#path-manipulation
- run: echo "$HOME/.local/bin" >> $GITHUB_PATH
Expand All @@ -79,21 +85,21 @@ jobs:
with:
python-version: '3.10'

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

- uses: actions/checkout@v4
with:
submodules: recursive

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

- name: 'release to pypi'
# always deploy tags to release pypi
# NOTE: release tags are guarded by on: push: tags on the top
if: github.event_name != 'pull_request' && startsWith(github.event.ref, 'refs/tags')
env:
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
run: pip3 install --user --upgrade build twine && .ci/release
run: .ci/release-uv
9 changes: 6 additions & 3 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ pretty = True
show_error_context = True
show_column_numbers = True
show_error_end = True
warn_redundant_casts = True
warn_unused_ignores = True

check_untyped_defs = True

# see https://mypy.readthedocs.io/en/stable/error_code_list2.html
warn_redundant_casts = True
strict_equality = True
enable_error_code = possibly-undefined
warn_unused_ignores = True
enable_error_code = deprecated,redundant-expr,possibly-undefined,truthy-bool,truthy-iterable,ignore-without-code,unused-awaitable

# an example of suppressing
# [mypy-my.config.repos.pdfannots.pdfannots]
Expand Down
7 changes: 7 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Homepage = "https://github.com/karlicoss/orger"
optional = [
"colorlog",
]

[dependency-groups]
testing = [
"pytest",
"ruff",
Expand All @@ -42,3 +44,8 @@ build-backend = "setuptools.build_meta"
[tool.setuptools_scm]
version_scheme = "python-simplified-semver"
local_scheme = "dirty-tag"

# workaround for error during uv publishing
# see https://github.com/astral-sh/uv/issues/9513#issuecomment-2519527822
[tool.setuptools]
license-files = []
1 change: 0 additions & 1 deletion ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ lint.ignore = [
"PLW0603", # global variable update.. we usually know why we are doing this
"PLW2901", # for loop variable overwritten, usually this is intentional

"PT004", # deprecated rule, will be removed later
"PT011", # pytest raises should is too broad
"PT012", # pytest raises should contain a single statement

Expand Down
4 changes: 2 additions & 2 deletions src/orger/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import traceback
import warnings
from datetime import datetime
from datetime import datetime, tzinfo
from pathlib import Path
from typing import TYPE_CHECKING

Expand All @@ -15,7 +15,7 @@ class settings:
USE_PANDOC: bool = True


_timezones = set() # type: ignore
_timezones: set[tzinfo | None] = set()


def dt_heading(dt: datetime | None, heading: str) -> str:
Expand Down
6 changes: 4 additions & 2 deletions src/orger/org_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ def main(cls, setup_parser=None) -> None:
def test_org_view_overwrite(tmp_path: Path):
class TestView(Mirror):
def __init__(self, items: list[OrgWithKey], *args, **kwargs) -> None:
super().__init__(*args, file_header='# autogenerated!\n#+TITLE: sometitle\nalso text\n', **kwargs) # type: ignore
kwargs['file_header'] = '# autogenerated!\n#+TITLE: sometitle\nalso text\n'
super().__init__(*args, **kwargs)
self.items = items

def get_items(self):
Expand Down Expand Up @@ -313,7 +314,8 @@ def get_items(self):
def test_org_view_append(tmp_path: Path) -> None:
class TestView(Queue):
def __init__(self, items: list[OrgWithKey], *args, **kwargs) -> None:
super().__init__(*args, file_header='# autogenerated!', **kwargs) # type: ignore
kwargs['file_header'] = '# autogenerated!'
super().__init__(*args, **kwargs)
self.items = items

def get_items(self):
Expand Down
4 changes: 2 additions & 2 deletions src/orger/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ def action() -> None:
assert mtime() == m2

# shouldn't trigger because item is already present
state.feed('a', 'err', lambda: None.whatever) # type: ignore
state.feed('a', 'err', lambda: None.whatever) # type: ignore[attr-defined]

with pytest.raises(AttributeError):
state.feed('hiii', 'error 2 ', lambda: None.whatever) # type: ignore
state.feed('hiii', 'error 2 ', lambda: None.whatever) # type: ignore[attr-defined]

assert mtime() == m2 # shouldn't corrupt or modify the file

Expand Down
20 changes: 11 additions & 9 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,21 @@ passenv =
MYPY_CACHE_DIR
RUFF_CACHE_DIR
usedevelop = true # for some reason tox seems to ignore "-e ." in deps section??
uv_seed = true # seems necessary so uv creates separate venvs per tox env?
setenv =
HPI_MODULE_INSTALL_USE_UV=true
uv_seed = true # seems necessary so uv creates separate venvs per tox env?


[testenv:ruff]
deps =
-e .[testing]
dependency_groups = testing
commands =
{envpython} -m ruff check src/

# todo not sure if there's much difference between deps and extras= like here?
# https://github.com/tox-dev/tox-uv?tab=readme-ov-file#uvlock-support

[testenv:tests]
deps =
-e .[testing]
dependency_groups = testing
commands =
{envpython} -m pytest \
--pyargs {[testenv]package_name} \
Expand All @@ -41,10 +41,11 @@ commands =


[testenv:mypy-core]
dependency_groups = testing
deps =
-e .[testing,optional]
-e .[optional]
commands =
{envpython} -m mypy --install-types --non-interactive \
{envpython} -m mypy --no-install-types \
# note: modules are tested separately, below
-p {[testenv]package_name} \
--exclude 'orger.modules' \
Expand All @@ -55,8 +56,9 @@ commands =


[testenv:mypy-misc]
dependency_groups = testing
deps =
-e .[testing,optional]
-e .[optional]
HPI
uv # for hpi module install
commands =
Expand All @@ -67,7 +69,7 @@ commands =
my.pinboard \
my.kobo

{envpython} -m mypy --install-types --non-interactive \
{envpython} -m mypy --no-install-types \
-p {[testenv]package_name}.modules \
# txt report is a bit more convenient to view on CI
--txt-report .coverage.mypy-misc \
Expand Down

0 comments on commit d55b6b1

Please sign in to comment.