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
26 changes: 10 additions & 16 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
---
name: Linting
name: Lint

on: [push, pull_request]

jobs:
build:
lint:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ['3.11']

name: Linting
steps:
- uses: actions/checkout@v3
- name: Setup python
uses: actions/setup-python@v4
- uses: actions/checkout@v5

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
python-version: ${{ matrix.python-version }}
architecture: x64
activate-environment: true

- name: Install dependencies
run: |
pip install poetry
poetry install
run: uv sync --all-groups

- name: Lint
run: poetry run make lint
run: make lint
41 changes: 14 additions & 27 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,47 +1,34 @@
---
name: Build and Publish
name: Build

on:
release:
types: [published]
pull_request:
types: [opened, reopened, edited, synchronize]

jobs:
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.11]

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

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Install uv
uses: astral-sh/setup-uv@v7

- name: Install dependencies
run: |
pip install poetry
poetry install
run: uv sync

- name: Build package
run: poetry build
run: uv build

- uses: actions/upload-artifact@v4
- name: Upload artifacts
uses: actions/upload-artifact@v6
with:
path: |
./dist/*.tar.gz
path: ./dist/

- name: Publish to PYPI
- name: Publish
if: ${{ github.event_name == 'release' }}
run: uv publish
env:
PYPI_TOKEN: ${{ secrets.PYPI_PASSWORD }}
if: ${{ github.event_name == 'release' && env.PYPI_TOKEN != null }}
run: |
poetry config pypi-token.pypi "$PYPI_TOKEN"
poetry publish
UV_PUBLISH_TOKEN: ${{ secrets.PYPI_PASSWORD }}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe needs to be PYPI_TOKEN?


23 changes: 11 additions & 12 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,27 @@
---
name: Testing
name: Test

on: [push, pull_request]

jobs:
build:
test:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13']
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]

name: Testing
steps:
- uses: actions/checkout@v3
- name: Setup python
uses: actions/setup-python@v4
- uses: actions/checkout@v5

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
activate-environment: true
python-version: ${{ matrix.python-version }}
architecture: x64

- name: Install dependencies
run: |
pip install poetry
poetry install
run: uv sync --all-groups

- name: Test
run: poetry run make test
run: make test
11 changes: 6 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
.PHONY: all
.IGNORE: lint format

lint:
black --diff .
isort --check .
ruff format --check .
ruff check .

format:
black .
isort .
ruff --fix .
ruff check --select F401 --select TID252 --select I --fix .
ruff format .

test:
pytest --cov=ctfcli tests
Expand Down
3 changes: 1 addition & 2 deletions ctfcli/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import subprocess
import sys
from pathlib import Path
from typing import Optional, Union

import click
import fire
Expand Down Expand Up @@ -33,7 +32,7 @@
class CTFCLI:
@staticmethod
def init(
directory: Optional[Union[str, os.PathLike]] = None,
directory: str | os.PathLike | None = None,
no_git: bool = False,
no_commit: bool = False,
):
Expand Down
44 changes: 24 additions & 20 deletions ctfcli/cli/challenges.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import os
import subprocess
from pathlib import Path
from typing import List, Optional, Tuple, Union
from urllib.parse import urlparse

import click
Expand Down Expand Up @@ -103,7 +102,7 @@ def view(self, challenge: str, color=True) -> int:
if not challenge_instance:
return 1

with open(challenge_instance.challenge_file_path, "r") as challenge_yml_file:
with open(challenge_instance.challenge_file_path) as challenge_yml_file:
challenge_yml = challenge_yml_file.read()

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

def add(
self, repo: str, directory: str = None, branch: str = None, force: bool = False, yaml_path: str = None
self,
repo: str,
directory: str | None = None,
branch: str | None = None,
force: bool = False,
yaml_path: str | None = None,
) -> int:
log.debug(f"add: {repo} (directory={directory}, branch={branch}, force={force}, yaml_path={yaml_path})")
config = Config()
Expand Down Expand Up @@ -207,7 +211,7 @@ def add(
click.secho(f"Could not process the challenge path: '{repo}'", fg="red")
return 1

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

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

return 1

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

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

return 1

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

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

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

Expand Down Expand Up @@ -638,7 +642,7 @@ def install(

return 1

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

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

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

def deploy(
self,
challenge: str = None,
host: str = None,
challenge: str | None = None,
host: str | None = None,
skip_login: bool = False,
) -> int:
log.debug(f"deploy: (challenge={challenge}, host={host}, skip_login={skip_login})")
Expand Down Expand Up @@ -839,7 +843,7 @@ def deploy(

def lint(
self,
challenge: str = None,
challenge: str | None = None,
skip_hadolint: bool = False,
flag_format: str = "flag{",
) -> int:
Expand All @@ -860,7 +864,7 @@ def lint(
click.secho("Success! Lint didn't find any issues!", fg="green")
return 0

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

challenge_instance = self._resolve_single_challenge(challenge)
Expand Down Expand Up @@ -925,10 +929,10 @@ def healthcheck(self, challenge: Optional[str] = None) -> int:

def mirror(
self,
challenge: str = None,
challenge: str | None = None,
files_directory: str = "dist",
skip_verify: bool = False,
ignore: Union[str, Tuple[str]] = (),
ignore: str | tuple[str] = (),
create: bool = False,
) -> int:
log.debug(
Expand Down Expand Up @@ -994,7 +998,7 @@ def mirror(

return 1

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

if challenge:
Expand Down Expand Up @@ -1059,7 +1063,7 @@ def verify(self, challenge: str = None, ignore: Tuple[str] = ()) -> int:

return 1

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

if challenge:
Expand Down Expand Up @@ -1093,7 +1097,7 @@ def format(self, challenge: Optional[str] = None) -> int:
return 1

@staticmethod
def _resolve_single_challenge(challenge: Optional[str] = None) -> Optional[Challenge]:
def _resolve_single_challenge(challenge: str | None = None) -> Challenge | None:
# if a challenge is specified
if challenge:
# check if it's a path to challenge.yml, or the current directory
Expand All @@ -1116,10 +1120,10 @@ def _resolve_single_challenge(challenge: Optional[str] = None) -> Optional[Chall
return Challenge(challenge_path)
except ChallengeException as e:
click.secho(str(e), fg="red")
return
return None

@staticmethod
def _resolve_all_challenges() -> List[Challenge]:
def _resolve_all_challenges() -> list[Challenge]:
config = Config()
challenge_keys = config.challenges.keys()

Expand Down
2 changes: 1 addition & 1 deletion ctfcli/cli/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def view(self, color=True, json=False) -> int:
click.echo(config_json)
return 0

with open(config.get_config_path(), "r") as config_file:
with open(config.get_config_path()) as config_file:
config_ini = config_file.read()

if color:
Expand Down
5 changes: 3 additions & 2 deletions ctfcli/cli/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ def push(self):

if not failed_configs:
click.secho("Successfully pushed config", fg="green")
else:
return 1
return 0

return 1


class InstanceCommand:
Expand Down
2 changes: 1 addition & 1 deletion ctfcli/cli/media.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def add(self, path):

api = API()

new_file = ("file", open(path, mode="rb"))
new_file = ("file", open(path, mode="rb")) # noqa: SIM115
Copy link

Copilot AI Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File is opened but is not closed.

Copilot uses AI. Check for mistakes.
filename = os.path.basename(path)
location = f"media/{filename}"
file_payload = {
Expand Down
Loading