Skip to content

Commit 36f5112

Browse files
authored
use uv and ruff (#195)
* use uv and ruff instead of poetry * address new linter and formatter config * add python 3.14 to the test matrix * bump pytest and fix tests
1 parent b3f0d74 commit 36f5112

33 files changed

+992
-1303
lines changed

.github/workflows/lint.yml

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,22 @@
11
---
2-
name: Linting
2+
name: Lint
33

44
on: [push, pull_request]
55

66
jobs:
7-
build:
7+
lint:
88
runs-on: ubuntu-latest
99

10-
strategy:
11-
matrix:
12-
python-version: ['3.11']
13-
14-
name: Linting
1510
steps:
16-
- uses: actions/checkout@v3
17-
- name: Setup python
18-
uses: actions/setup-python@v4
11+
- uses: actions/checkout@v5
12+
13+
- name: Install uv
14+
uses: astral-sh/setup-uv@v7
1915
with:
20-
python-version: ${{ matrix.python-version }}
21-
architecture: x64
16+
activate-environment: true
17+
2218
- name: Install dependencies
23-
run: |
24-
pip install poetry
25-
poetry install
19+
run: uv sync --all-groups
2620

2721
- name: Lint
28-
run: poetry run make lint
22+
run: make lint

.github/workflows/release.yml

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,34 @@
11
---
2-
name: Build and Publish
2+
name: Build
33

44
on:
55
release:
66
types: [published]
7-
pull_request:
8-
types: [opened, reopened, edited, synchronize]
97

108
jobs:
119
build:
12-
# The type of runner that the job will run on
1310
runs-on: ubuntu-latest
14-
strategy:
15-
matrix:
16-
python-version: [3.11]
1711

18-
# Steps represent a sequence of tasks that will be executed as part of the job
1912
steps:
20-
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
21-
- uses: actions/checkout@v2
13+
- uses: actions/checkout@v5
2214

23-
- name: Set up Python
24-
uses: actions/setup-python@v4
25-
with:
26-
python-version: 3.11
15+
- name: Install uv
16+
uses: astral-sh/setup-uv@v7
2717

2818
- name: Install dependencies
29-
run: |
30-
pip install poetry
31-
poetry install
19+
run: uv sync
3220

3321
- name: Build package
34-
run: poetry build
22+
run: uv build
3523

36-
- uses: actions/upload-artifact@v4
24+
- name: Upload artifacts
25+
uses: actions/upload-artifact@v6
3726
with:
38-
path: |
39-
./dist/*.tar.gz
27+
path: ./dist/
4028

41-
- name: Publish to PYPI
29+
- name: Publish
30+
if: ${{ github.event_name == 'release' }}
31+
run: uv publish
4232
env:
43-
PYPI_TOKEN: ${{ secrets.PYPI_PASSWORD }}
44-
if: ${{ github.event_name == 'release' && env.PYPI_TOKEN != null }}
45-
run: |
46-
poetry config pypi-token.pypi "$PYPI_TOKEN"
47-
poetry publish
33+
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_PASSWORD }}
34+

.github/workflows/test.yml

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,27 @@
11
---
2-
name: Testing
2+
name: Test
33

44
on: [push, pull_request]
55

66
jobs:
7-
build:
7+
test:
88
runs-on: ubuntu-latest
99

1010
strategy:
1111
matrix:
12-
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
12+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
1313

14-
name: Testing
1514
steps:
16-
- uses: actions/checkout@v3
17-
- name: Setup python
18-
uses: actions/setup-python@v4
15+
- uses: actions/checkout@v5
16+
17+
- name: Install uv
18+
uses: astral-sh/setup-uv@v7
1919
with:
20+
activate-environment: true
2021
python-version: ${{ matrix.python-version }}
21-
architecture: x64
22+
2223
- name: Install dependencies
23-
run: |
24-
pip install poetry
25-
poetry install
24+
run: uv sync --all-groups
2625

2726
- name: Test
28-
run: poetry run make test
27+
run: make test

Makefile

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1+
.PHONY: all
2+
.IGNORE: lint format
3+
14
lint:
2-
black --diff .
3-
isort --check .
5+
ruff format --check .
46
ruff check .
57

68
format:
7-
black .
8-
isort .
9-
ruff --fix .
9+
ruff check --select F401 --select TID252 --select I --fix .
10+
ruff format .
1011

1112
test:
1213
pytest --cov=ctfcli tests

ctfcli/__main__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import subprocess
55
import sys
66
from pathlib import Path
7-
from typing import Optional, Union
87

98
import click
109
import fire
@@ -33,7 +32,7 @@
3332
class CTFCLI:
3433
@staticmethod
3534
def init(
36-
directory: Optional[Union[str, os.PathLike]] = None,
35+
directory: str | os.PathLike | None = None,
3736
no_git: bool = False,
3837
no_commit: bool = False,
3938
):

ctfcli/cli/challenges.py

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import os
44
import subprocess
55
from pathlib import Path
6-
from typing import List, Optional, Tuple, Union
76
from urllib.parse import urlparse
87

98
import click
@@ -103,7 +102,7 @@ def view(self, challenge: str, color=True) -> int:
103102
if not challenge_instance:
104103
return 1
105104

106-
with open(challenge_instance.challenge_file_path, "r") as challenge_yml_file:
105+
with open(challenge_instance.challenge_file_path) as challenge_yml_file:
107106
challenge_yml = challenge_yml_file.read()
108107

109108
if color:
@@ -120,7 +119,12 @@ def templates(self) -> int:
120119
return TemplatesCommand.list()
121120

122121
def add(
123-
self, repo: str, directory: str = None, branch: str = None, force: bool = False, yaml_path: str = None
122+
self,
123+
repo: str,
124+
directory: str | None = None,
125+
branch: str | None = None,
126+
force: bool = False,
127+
yaml_path: str | None = None,
124128
) -> int:
125129
log.debug(f"add: {repo} (directory={directory}, branch={branch}, force={force}, yaml_path={yaml_path})")
126130
config = Config()
@@ -207,7 +211,7 @@ def add(
207211
click.secho(f"Could not process the challenge path: '{repo}'", fg="red")
208212
return 1
209213

210-
def push(self, challenge: str = None, no_auto_pull: bool = False, quiet=False) -> int:
214+
def push(self, challenge: str | None = None, no_auto_pull: bool = False, quiet=False) -> int:
211215
log.debug(f"push: (challenge={challenge}, no_auto_pull={no_auto_pull}, quiet={quiet})")
212216
config = Config()
213217

@@ -332,7 +336,7 @@ def push(self, challenge: str = None, no_auto_pull: bool = False, quiet=False) -
332336

333337
return 1
334338

335-
def pull(self, challenge: str = None, strategy: str = "fast-forward", quiet: bool = False) -> int:
339+
def pull(self, challenge: str | None = None, strategy: str = "fast-forward", quiet: bool = False) -> int:
336340
log.debug(f"pull: (challenge={challenge}, quiet={quiet})")
337341
config = Config()
338342

@@ -460,7 +464,7 @@ def pull(self, challenge: str = None, strategy: str = "fast-forward", quiet: boo
460464

461465
return 1
462466

463-
def restore(self, challenge: str = None) -> int:
467+
def restore(self, challenge: str | None = None) -> int:
464468
log.debug(f"restore: (challenge={challenge})")
465469
config = Config()
466470

@@ -562,7 +566,7 @@ def restore(self, challenge: str = None) -> int:
562566
return 1
563567

564568
def install(
565-
self, challenge: str = None, force: bool = False, hidden: bool = False, ignore: Union[str, Tuple[str]] = ()
569+
self, challenge: str | None = None, force: bool = False, hidden: bool = False, ignore: str | tuple[str] = ()
566570
) -> int:
567571
log.debug(f"install: (challenge={challenge}, force={force}, hidden={hidden}, ignore={ignore})")
568572

@@ -638,7 +642,7 @@ def install(
638642

639643
return 1
640644

641-
def sync(self, challenge: str = None, ignore: Union[str, Tuple[str]] = ()) -> int:
645+
def sync(self, challenge: str | None = None, ignore: str | tuple[str] = ()) -> int:
642646
log.debug(f"sync: (challenge={challenge}, ignore={ignore})")
643647

644648
if challenge:
@@ -672,7 +676,7 @@ def sync(self, challenge: str = None, ignore: Union[str, Tuple[str]] = ()) -> in
672676
continue
673677

674678
click.secho(
675-
f"Syncing '{challenge_name}' (" f"{challenge_instance.challenge_file_path}" ") ...",
679+
f"Syncing '{challenge_name}' ({challenge_instance.challenge_file_path}) ...",
676680
fg="blue",
677681
)
678682
try:
@@ -694,8 +698,8 @@ def sync(self, challenge: str = None, ignore: Union[str, Tuple[str]] = ()) -> in
694698

695699
def deploy(
696700
self,
697-
challenge: str = None,
698-
host: str = None,
701+
challenge: str | None = None,
702+
host: str | None = None,
699703
skip_login: bool = False,
700704
) -> int:
701705
log.debug(f"deploy: (challenge={challenge}, host={host}, skip_login={skip_login})")
@@ -836,7 +840,7 @@ def deploy(
836840

837841
def lint(
838842
self,
839-
challenge: str = None,
843+
challenge: str | None = None,
840844
skip_hadolint: bool = False,
841845
flag_format: str = "flag{",
842846
) -> int:
@@ -857,7 +861,7 @@ def lint(
857861
click.secho("Success! Lint didn't find any issues!", fg="green")
858862
return 0
859863

860-
def healthcheck(self, challenge: Optional[str] = None) -> int:
864+
def healthcheck(self, challenge: str | None = None) -> int:
861865
log.debug(f"healthcheck: (challenge={challenge})")
862866

863867
challenge_instance = self._resolve_single_challenge(challenge)
@@ -922,10 +926,10 @@ def healthcheck(self, challenge: Optional[str] = None) -> int:
922926

923927
def mirror(
924928
self,
925-
challenge: str = None,
929+
challenge: str | None = None,
926930
files_directory: str = "dist",
927931
skip_verify: bool = False,
928-
ignore: Union[str, Tuple[str]] = (),
932+
ignore: str | tuple[str] = (),
929933
create: bool = False,
930934
) -> int:
931935
log.debug(
@@ -991,7 +995,7 @@ def mirror(
991995

992996
return 1
993997

994-
def verify(self, challenge: str = None, ignore: Tuple[str] = ()) -> int:
998+
def verify(self, challenge: str | None = None, ignore: tuple[str] = ()) -> int:
995999
log.debug(f"verify: (challenge={challenge}, ignore={ignore})")
9961000

9971001
if challenge:
@@ -1056,7 +1060,7 @@ def verify(self, challenge: str = None, ignore: Tuple[str] = ()) -> int:
10561060

10571061
return 1
10581062

1059-
def format(self, challenge: Optional[str] = None) -> int:
1063+
def format(self, challenge: str | None = None) -> int:
10601064
log.debug(f"format: (challenge={challenge})")
10611065

10621066
if challenge:
@@ -1090,7 +1094,7 @@ def format(self, challenge: Optional[str] = None) -> int:
10901094
return 1
10911095

10921096
@staticmethod
1093-
def _resolve_single_challenge(challenge: Optional[str] = None) -> Optional[Challenge]:
1097+
def _resolve_single_challenge(challenge: str | None = None) -> Challenge | None:
10941098
# if a challenge is specified
10951099
if challenge:
10961100
# check if it's a path to challenge.yml, or the current directory
@@ -1113,10 +1117,10 @@ def _resolve_single_challenge(challenge: Optional[str] = None) -> Optional[Chall
11131117
return Challenge(challenge_path)
11141118
except ChallengeException as e:
11151119
click.secho(str(e), fg="red")
1116-
return
1120+
return None
11171121

11181122
@staticmethod
1119-
def _resolve_all_challenges() -> List[Challenge]:
1123+
def _resolve_all_challenges() -> list[Challenge]:
11201124
config = Config()
11211125
challenge_keys = config.challenges.keys()
11221126

ctfcli/cli/config.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def view(self, color=True, json=False) -> int:
4545
click.echo(config_json)
4646
return 0
4747

48-
with open(config.get_config_path(), "r") as config_file:
48+
with open(config.get_config_path()) as config_file:
4949
config_ini = config_file.read()
5050

5151
if color:

ctfcli/cli/instance.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ def push(self):
6060

6161
if not failed_configs:
6262
click.secho("Successfully pushed config", fg="green")
63-
else:
64-
return 1
63+
return 0
64+
65+
return 1
6566

6667

6768
class InstanceCommand:

ctfcli/cli/media.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def add(self, path):
1515

1616
api = API()
1717

18-
new_file = ("file", open(path, mode="rb"))
18+
new_file = ("file", open(path, mode="rb")) # noqa: SIM115
1919
filename = os.path.basename(path)
2020
location = f"media/{filename}"
2121
file_payload = {

0 commit comments

Comments
 (0)