diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..69fada0 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,65 @@ +name: CI + +on: + pull_request: + types: [opened, synchronize, reopened, ready_for_review] + push: + branches: [main] + +concurrency: + group: ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + checks: + name: Lint / Format Check / Tests + runs-on: ubuntu-latest + if: ${{ github.event_name != 'push' || github.ref == 'refs/heads/main' }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Python + id: py + uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Install Poetry + uses: abatilo/actions-poetry@v3 + with: + poetry-version: "2.2.0" + + - name: Enable in-project venv + run: poetry config virtualenvs.in-project true + + - name: Cache Poetry venv + uses: actions/cache@v4 + with: + path: .venv + key: venv-${{ runner.os }}-${{ steps.py.outputs.python-version }}-${{ hashFiles('poetry.lock') }} + restore-keys: | + venv-${{ runner.os }}- + + - name: Install dependencies + run: poetry install --no-interaction + + # ---- Static checks (no file modifications in CI) ---- + - name: Ruff lint + run: poetry run ruff check . + + - name: Ruff format (check only) + run: poetry run ruff format --check . + + # ---- Run tests with coverage threshold ---- + - name: Run tests with coverage + run: | + poetry run pytest \ + -q --maxfail=1 --disable-warnings \ + --cov=src --cov-report=term-missing \ + --cov-report=xml:coverage.xml \ + --cov-fail-under=100 diff --git a/README.md b/README.md index af2eb1b..1fd278f 100644 --- a/README.md +++ b/README.md @@ -4,9 +4,12 @@ **Workflow**: 1. Fork this repository to your own GitHub account. -2. Create a new branch from `develop` and name it `feature/student1name_student2name`. -3. Clone the repo to AWS VM. -4. Implement the functions until all `pytest` tests pass. -5. Push your code to your Github repo. +2. Clone the repo to AWS VM. +3. Run poetry install to install dependencies +4. Run poetry run pre-commit install to install the pre-commit hook. +5. Create a new branch from `develop` and name it `feature/student1name_student2name`. +7. Implement the functions until all `pytest` tests pass. +8. Make the pre-commit checks all pass and commit the code. +9. Push your code to your Github repo. 5. Submit a Pull Request back to the `develop` branch of the main repo. CI must pass. 6. Wait for code review and grading. diff --git a/poetry.lock b/poetry.lock index 4177cc6..28cbed8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -940,5 +940,5 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [metadata] lock-version = "2.1" -python-versions = ">=3.12" -content-hash = "c6fe4b288327b501a15d79c555e296323d3da6a16b612a92ac74271600991431" +python-versions = "^3.12" +content-hash = "2208d21945ff72a894dfa1b053afd56efc83d7e50a8d39b29c814fdc7eb94a3e" diff --git a/pyproject.toml b/pyproject.toml index 4274d13..df128a4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,17 @@ dependencies = [ "pre-commit (>=4.3.0,<5.0.0)" ] +[tool.poetry] +name = "arithmetic" +version = "0.1.0" +description = "Toy arithmetic package" +authors = ["Your Name "] +readme = "README.md" +packages = [{ include = "src/arithmetic" }] + +[tool.poetry.dependencies] +python = "^3.12" + [build-system] requires = ["poetry-core>=2.0.0,<3.0.0"] diff --git a/src/arithmetic/arithmetic.py b/src/arithmetic/arithmetic.py index 86f6960..c104959 100644 --- a/src/arithmetic/arithmetic.py +++ b/src/arithmetic/arithmetic.py @@ -1,46 +1,37 @@ -"""arithmetic functions.""" - -from __future__ import annotations +# src/arithmetic/arithmetic.py +from typing import Optional def add_numbers(a: int, b: int) -> int: - """Return the sum of two integers. - - Args: - a: first integer - b: second integer - - Returns: - Sum of a and b. - """ - raise NotImplementedError + """Return the sum of two integers.""" + return a + b def factorial(n: int) -> int: - """Compute the factorial of n. - - Args: - n: non-negative integer - - Returns: - n! as int + """Compute n! for a non-negative integer n. Raises: - ValueError: if n is negative + ValueError: if n is negative. """ - raise NotImplementedError + if n < 0: + raise ValueError("factorial is undefined for negative integers") + result = 1 + for i in range(2, n + 1): + result *= i + return result def is_prime(n: int) -> bool: - """Check whether n is a prime number. - - A prime number is an integer greater than 1 that has no positive - divisors other than 1 and itself. - - Args: - n: integer to test - - Returns: - True if n is prime; otherwise False. - """ - raise NotImplementedError + """Return True if n is prime, else False.""" + if n <= 1: + return False + if n <= 3: + return True + if n % 2 == 0 or n % 3 == 0: + return False + i = 5 + while i * i <= n: + if n % i == 0 or n % (i + 2) == 0: + return False + i += 6 + return True