Skip to content

Commit 9f49cc7

Browse files
authored
ci: switch to uv + tox, add GitHub Actions workflows (#1)
* ci: switch to uv + tox, add GitHub Actions workflows - Replace setuptools with hatchling; declare runtime deps explicitly - Add uv dependency-groups (dev/test/lint) and tox envs via tox-uv - Configure ruff (format + lint) and mypy under tox - Add GitHub Actions: CI (3.12 + 3.14 matrix), PR title lint, publish, release-please - Slim environment-dev.yml to python + uv - Generate uv.lock - Reformat sources with ruff format; minor mypy type annotation - Stop tracking .devcontainer * style: preserve multi-line method chains with fmt: skip
1 parent 030d2ff commit 9f49cc7

21 files changed

Lines changed: 1966 additions & 718 deletions

.github/workflows/check-pr.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
name: PR Title Lint
2+
3+
on:
4+
pull_request:
5+
types:
6+
- opened
7+
- edited
8+
- synchronize
9+
- reopened
10+
11+
permissions:
12+
pull-requests: read
13+
statuses: write
14+
15+
jobs:
16+
conventional-title:
17+
name: Validate PR title (Conventional Commits)
18+
runs-on: ubuntu-latest
19+
steps:
20+
- uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1
21+
env:
22+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
23+
with:
24+
types: |
25+
feat
26+
fix
27+
chore
28+
docs
29+
refactor
30+
test
31+
ci
32+
build
33+
perf
34+
style
35+
revert
36+
requireScope: false
37+
subjectPattern: ^[a-z].+[^.]$
38+
subjectPatternError: |
39+
Subject must start with lowercase letter and not end with a period.
40+
Got: "{subject}"
41+
wip: true

.github/workflows/ci.yml

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
workflow_dispatch:
9+
10+
env:
11+
UV_VERSION: "0.11.6"
12+
UV_PYTHON_PREFERENCE: "managed"
13+
14+
jobs:
15+
quality-checks:
16+
name: Quality Checks
17+
runs-on: ubuntu-latest
18+
steps:
19+
- name: Checkout code
20+
uses: actions/checkout@v4
21+
22+
- name: Install uv
23+
uses: astral-sh/setup-uv@v6
24+
with:
25+
version: ${{ env.UV_VERSION }}
26+
enable-cache: true
27+
28+
- name: Set up Python
29+
run: uv python install 3.14
30+
31+
- name: Ensure uv lockfile is up-to-date
32+
run: uv lock --check
33+
34+
- name: Install project dependencies
35+
run: uv sync --all-groups
36+
37+
- name: Check formatting
38+
run: uv run tox -e format-check
39+
40+
- name: Linting
41+
run: uv run tox -e lints
42+
43+
- name: Run type checking
44+
run: uv run tox -e typecheck
45+
46+
test:
47+
name: Test (Python ${{ matrix.python-version }})
48+
needs: [quality-checks]
49+
runs-on: ubuntu-latest
50+
strategy:
51+
fail-fast: false
52+
matrix:
53+
python-version: ["3.12", "3.14"]
54+
steps:
55+
- name: Checkout code
56+
uses: actions/checkout@v4
57+
58+
- name: Install uv
59+
uses: astral-sh/setup-uv@v6
60+
with:
61+
version: ${{ env.UV_VERSION }}
62+
enable-cache: true
63+
64+
- name: Set up Python ${{ matrix.python-version }}
65+
run: uv python install ${{ matrix.python-version }}
66+
67+
- name: Install project dependencies
68+
run: uv sync --all-groups
69+
70+
- name: Run tests
71+
run: uv run tox -e py${{ matrix.python-version }}

.github/workflows/publish.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
tags:
7+
- "v[0-9]+.[0-9]+.[0-9]+*"
8+
9+
env:
10+
UV_VERSION: "0.11.6"
11+
12+
jobs:
13+
build-and-publish:
14+
name: Build & Publish to PyPI
15+
runs-on: ubuntu-latest
16+
environment:
17+
name: pypi
18+
url: https://pypi.org/p/nmd_scanner
19+
permissions:
20+
id-token: write
21+
contents: read
22+
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@v4
26+
with:
27+
ref: ${{ github.ref }}
28+
29+
- name: Install uv
30+
uses: astral-sh/setup-uv@v6
31+
with:
32+
version: ${{ env.UV_VERSION }}
33+
enable-cache: true
34+
35+
- name: Set up Python
36+
run: uv python install 3.14
37+
38+
- name: Build package
39+
run: uv build --sdist --wheel --out-dir dist
40+
41+
- name: Publish to PyPI
42+
uses: pypa/gh-action-pypi-publish@release/v1
43+
with:
44+
packages-dir: dist
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: Release Please
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
pull-requests: write
11+
12+
env:
13+
UV_VERSION: "0.11.6"
14+
UV_PYTHON_PREFERENCE: "managed"
15+
16+
jobs:
17+
release-please:
18+
name: Release Please
19+
runs-on: ubuntu-latest
20+
outputs:
21+
release_created: ${{ steps.release.outputs.release_created }}
22+
tag_name: ${{ steps.release.outputs.tag_name }}
23+
prs: ${{ steps.release.outputs.prs }}
24+
steps:
25+
- uses: googleapis/release-please-action@v4
26+
id: release
27+
with:
28+
config-file: release-please-config.json
29+
manifest-file: .release-please-manifest.json
30+
31+
refresh-lock:
32+
name: Refresh uv.lock on release PR
33+
needs: release-please
34+
if: ${{ needs.release-please.outputs.prs != '' && needs.release-please.outputs.prs != '[]' }}
35+
runs-on: ubuntu-latest
36+
permissions:
37+
contents: write
38+
steps:
39+
- name: Determine release PR branch
40+
id: branch
41+
run: |
42+
BRANCH=$(echo '${{ needs.release-please.outputs.prs }}' | jq -r '.[0].headBranchName')
43+
echo "name=$BRANCH" >> "$GITHUB_OUTPUT"
44+
45+
- name: Checkout release PR branch
46+
uses: actions/checkout@v4
47+
with:
48+
ref: ${{ steps.branch.outputs.name }}
49+
token: ${{ secrets.GITHUB_TOKEN }}
50+
51+
- name: Install uv
52+
uses: astral-sh/setup-uv@v6
53+
with:
54+
version: ${{ env.UV_VERSION }}
55+
enable-cache: true
56+
57+
- name: Set up Python
58+
run: uv python install 3.14
59+
60+
- name: Refresh uv.lock
61+
run: |
62+
uv lock
63+
if git diff --quiet uv.lock; then
64+
echo "uv.lock already in sync — nothing to commit."
65+
exit 0
66+
fi
67+
git config user.name "github-actions[bot]"
68+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
69+
git add uv.lock
70+
git commit -m "chore: refresh uv.lock for release"
71+
git push

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
.devcontainer/
12
.DS_Store
23
*.egg-info/
34
*.pyc

.release-please-manifest.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
".": "0.1.0"
3+
}

environment-dev.yaml

Lines changed: 0 additions & 22 deletions
This file was deleted.

environment-dev.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
name: nmd_scanner
2+
channels:
3+
- conda-forge
4+
- bioconda
5+
- nodefaults
6+
dependencies:
7+
- python >=3.13
8+
- uv =0.11

pyproject.toml

Lines changed: 89 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,104 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
15
[project]
26
name = "nmd_scanner"
37
version = "0.1.0"
48
description = "A simple scanner package for NMD"
59
authors = [
6-
{ name = "Your Name", email = "your.email@example.com" }
10+
{ name = "Carina Hui Schröder", email = "Carina2504@users.noreply.github.com" },
11+
{ name = "Florian R. Hölzlwimmer", email = "git.ich@frhoelzlwimmer.de" },
712
]
813
readme = "README.md"
9-
license = { text = "MIT" }
14+
license = "MIT"
15+
requires-python = ">=3.12"
1016
dependencies = [
11-
# Add your runtime dependencies here, e.g.
12-
# "requests >=2.25.1",
17+
"pandas",
18+
"numpy",
19+
"pyfaidx",
20+
"biopython",
21+
"pyranges",
22+
"pysam",
23+
"tqdm>=4.66.5,<5",
24+
"importlib-metadata",
1325
]
1426

15-
[build-system]
16-
requires = ["setuptools>=61.0", "wheel"]
17-
build-backend = "setuptools.build_meta"
27+
[project.urls]
28+
"Homepage" = "https://github.com/gagneurlab/NMD-Scanner"
1829

19-
[tool.setuptools.packages.find]
20-
where = ["src"]
30+
[dependency-groups]
31+
dev = [
32+
"tox>=4",
33+
"tox-uv",
34+
]
35+
test = [
36+
"pytest>=8,<9",
37+
"pytest-xdist",
38+
"pytest-mock",
39+
"pytest-cov",
40+
"pytest-sugar",
41+
]
42+
lint = [
43+
"ruff",
44+
"mypy",
45+
"pandas-stubs",
46+
]
2147

22-
[project.optional-dependencies]
23-
dev = ["pytest >=7.0"]
48+
[tool.hatch.build.targets.wheel]
49+
packages = ["src/nmd_scanner"]
2450

25-
[tool.pytest.ini_options]
26-
testpaths = [
27-
"tests"
28-
]
29-
addopts = [
30-
"--import-mode=importlib",
51+
[tool.ruff]
52+
target-version = "py312"
53+
line-length = 120
54+
extend-exclude = ["scripts", "resources", "testdir"]
55+
56+
[tool.ruff.lint]
57+
select = ["E", "F", "W", "I"]
58+
ignore = [
59+
"E501", # line-too-long
60+
"E712", # comparison-to-true (pandas idiom: df[col == True])
61+
"F403", # star imports used intentionally in package
62+
"F405", # may-be-undefined-from-star-import
63+
"F841", # local-variable-assigned-but-never-used
3164
]
3265

66+
[tool.ruff.lint.per-file-ignores]
67+
"tests/*" = ["S", "SIM", "PLR", "F811", "F841"]
68+
"__init__.py" = ["F401"]
69+
70+
[tool.tox]
71+
requires = ["tox>=4", "tox-uv"]
72+
env_list = ["format-check", "lints", "typecheck", "py3.12", "py3.14"]
73+
isolated_build = true
74+
75+
[tool.tox.env_run_base]
76+
runner = "uv-venv-lock-runner"
77+
description = "run unit tests"
78+
dependency_groups = ["test"]
79+
commands = [["pytest", { replace = "posargs", default = ["tests"], extend = true }]]
80+
81+
[tool.tox.env."format-check"]
82+
runner = "uv-venv-lock-runner"
83+
skip_install = true
84+
dependency_groups = ["lint"]
85+
commands = [["ruff", "format", "--check", "."]]
86+
87+
[tool.tox.env."lints"]
88+
runner = "uv-venv-lock-runner"
89+
skip_install = true
90+
dependency_groups = ["lint"]
91+
commands = [["ruff", "check", "."]]
92+
93+
[tool.tox.env."typecheck"]
94+
runner = "uv-venv-lock-runner"
95+
dependency_groups = ["lint"]
96+
commands = [["mypy", "src/nmd_scanner"]]
97+
98+
[tool.mypy]
99+
python_version = "3.12"
100+
ignore_missing_imports = true
101+
102+
[tool.pytest.ini_options]
103+
testpaths = ["tests"]
104+
addopts = ["--import-mode=importlib"]

0 commit comments

Comments
 (0)