diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..0bc53ef --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,20 @@ +## Summary + + + +## Changes + + + +- + +## Testing + + + +- [ ] Tests pass locally (`python -m pytest tests/ -v`) +- [ ] Pre-commit hooks pass (`pre-commit run --all-files`) + +## Related Issues + + diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..056f1bb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,27 @@ +version: 2 + +updates: + # Python (pip) dependencies + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 10 + labels: + - "dependencies" + commit-message: + prefix: "chore(deps)" + + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + open-pull-requests-limit: 5 + labels: + - "dependencies" + - "ci" + commit-message: + prefix: "ci(deps)" diff --git a/.github/scripts/generate_pr_body.py b/.github/scripts/generate_pr_body.py new file mode 100755 index 0000000..4db8604 --- /dev/null +++ b/.github/scripts/generate_pr_body.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python3 +"""Generate PR body from git history.""" + +import os +import subprocess +import sys + + +def run_cmd(cmd: list[str]) -> str: + """ + Run command and return stdout. + + Note: Commands use validated inputs only. The BASE_REF is validated + in main() before being used in any git commands. + """ + result = subprocess.run( # nosec B603 # noqa: S603 + cmd, + capture_output=True, + text=True, + check=True, + shell=False, # Explicit: never use shell + ) + return result.stdout.strip() + + +def categorize_files(files: list[str]) -> dict[str, int]: + """Categorize changed files.""" + categories = { + "src": 0, + "test": 0, + "doc": 0, + "ci": 0, + } + + for f in files: + if f.startswith("src/"): + categories["src"] += 1 + elif f.startswith("tests/"): + categories["test"] += 1 + elif f.endswith("README.md") or (".github/" in f and f.endswith(".md")): + categories["doc"] += 1 + elif ".github/workflows/" in f or ".github/dependabot" in f: + categories["ci"] += 1 + + return categories + + +def build_summary_line(categories: dict[str, int], total: int) -> str: + """Build human-readable summary of changes.""" + parts = [] + if categories["src"] > 0: + parts.append(f"{categories['src']} source file(s)") + if categories["test"] > 0: + parts.append(f"{categories['test']} test file(s)") + if categories["doc"] > 0: + parts.append(f"{categories['doc']} doc file(s)") + if categories["ci"] > 0: + parts.append(f"{categories['ci']} CI file(s)") + + if parts: + return f"This PR touches {', '.join(parts)} across {total} file(s) total." + return f"This PR modifies {total} file(s)." + + +def main() -> int: + """Generate PR body from git history.""" + base_ref = os.environ["BASE_REF"] + + # Validate BASE_REF to prevent command injection + # Valid git refs: alphanumeric, hyphens, underscores, slashes, dots + # Reject anything suspicious + import re + + if not re.match(r"^[a-zA-Z0-9/_.-]+$", base_ref): + print(f"ERROR: Invalid BASE_REF format: {base_ref}", file=sys.stderr) + return 1 + + # Additional safety: reject refs that could be command injection attempts + if base_ref.startswith("-") or ".." in base_ref: + print(f"ERROR: Suspicious BASE_REF detected: {base_ref}", file=sys.stderr) + return 1 + + # Get merge base + merge_base = run_cmd(["git", "merge-base", f"origin/{base_ref}", "HEAD"]) + + # Get commit messages + commits = run_cmd(["git", "log", "--pretty=format:- %s", f"{merge_base}..HEAD"]) + + # Get changed files + diff_stat = run_cmd(["git", "diff", "--stat", f"{merge_base}..HEAD"]) + files = run_cmd(["git", "diff", "--name-only", f"{merge_base}..HEAD"]).split("\n") + files = [f for f in files if f] # Remove empty strings + + # Categorize + categories = categorize_files(files) + summary = build_summary_line(categories, len(files)) + + # Build body + body = f"""## Summary + +{summary} + +## Changes + +{commits} + +
+Diff stats +``` +{diff_stat} +``` + +
+ +## Testing + +- [ ] Tests pass locally (`python -m pytest tests/ -v`) +- [ ] Pre-commit hooks pass (`pre-commit run --all-files`) + +## Related Issues + + +""" + + print(body) + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/.github/workflows/auto-populate-pr.yml b/.github/workflows/auto-populate-pr.yml new file mode 100644 index 0000000..6548c50 --- /dev/null +++ b/.github/workflows/auto-populate-pr.yml @@ -0,0 +1,65 @@ +name: Auto-populate PR Body + +on: + pull_request: + types: [opened] + +permissions: + pull-requests: write + +jobs: + populate-body: + name: Generate PR Body + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Fetch base branch + run: git fetch origin ${{ github.event.pull_request.base.ref }} --depth=1 + + - name: Check if PR body needs population + id: check + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + run: | + set -euo pipefail + + CURRENT_BODY=$(gh pr view "$PR_NUMBER" --json body -q '.body') + + # Strip HTML comments, headers, whitespace, checklist items + STRIPPED=$(echo "$CURRENT_BODY" \ + | sed 's///g' \ + | sed '/^## /d' \ + | sed '/^- \[.\]/d' \ + | sed '/^-$/d' \ + | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' \ + | sed '/^$/d') + + if [[ -n "$STRIPPED" ]]; then + echo "PR body already has custom content. Skipping auto-populate." + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "PR body is empty or template-only. Will generate content." + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Generate and update PR body + if: steps.check.outputs.skip == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + BASE_REF: ${{ github.event.pull_request.base.ref }} + run: | + set -euo pipefail + + BODY=$(python3 .github/scripts/generate_pr_body.py) + gh pr edit "$PR_NUMBER" --body "$BODY" + echo "PR body has been auto-populated." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..8733ab2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,105 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + lint: + name: Lint & Format + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + + - name: Cache pip packages + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: pip-${{ runner.os }}-3.11-${{ hashFiles('requirements-dev.txt') }} + restore-keys: | + pip-${{ runner.os }}-3.11- + + - name: Install dependencies + run: | + pip install --upgrade pip + pip install -r requirements-dev.txt + + - name: Cache pre-commit hooks + uses: actions/cache@v4 + with: + path: ~/.cache/pre-commit + key: pre-commit-${{ hashFiles('.pre-commit-config.yaml') }} + restore-keys: pre-commit- + + - name: Run pre-commit + run: pre-commit run --all-files --show-diff-on-failure + + - name: Lint check with ruff + run: ruff check src/ tests/ apps/ + + - name: Type check with mypy + run: mypy src/ apps/ + + - name: Security check with bandit + run: bandit -c pyproject.toml -r src/ apps/ + + test: + name: Test (Python ${{ matrix.python-version }}, ${{ matrix.os }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + needs: lint + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + python-version: ["3.9", "3.10", "3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Cache pip packages + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: pip-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('requirements*.txt') }} + restore-keys: | + pip-${{ runner.os }}-${{ matrix.python-version }}- + pip-${{ runner.os }}- + + - name: Install dependencies + run: | + pip install --upgrade pip + pip install -r requirements.txt + pip install -r requirements-dev.txt + pip install -e . + + - name: Run tests with coverage + run: | + set -euo pipefail + python -m pytest tests/ \ + -v \ + --cov=template_project \ + --cov-fail-under=90 \ + --cov-report=term-missing \ + --cov-report=xml + + - name: Upload coverage report + if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11' + uses: actions/upload-artifact@v4 + with: + name: coverage-report + path: coverage.xml diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000..35062b8 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,25 @@ +name: Dependency Review + +on: + pull_request: + paths: + - "requirements*.txt" + - "setup.py" + - "pyproject.toml" + +permissions: + contents: read + +jobs: + dependency-review: + name: Review Dependencies + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - uses: actions/checkout@v4 + + - name: Dependency Review + uses: actions/dependency-review-action@v4 + with: + fail-on-severity: high + comment-summary-in-pr: always diff --git a/.github/workflows/pr-body.yml b/.github/workflows/pr-body.yml new file mode 100644 index 0000000..e712263 --- /dev/null +++ b/.github/workflows/pr-body.yml @@ -0,0 +1,59 @@ +name: PR Body Validation + +on: + pull_request: + # Runs on 'edited' and 'reopened' only — the 'opened' event is handled by + # the auto-populate workflow, which edits the body and triggers 'edited'. + types: [edited, reopened] + +jobs: + validate-body: + name: Validate PR Body + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Check PR body is not empty + run: | + set -euo pipefail + + BODY=$(cat <<'BODY_EOF' + ${{ github.event.pull_request.body }} + BODY_EOF + ) + + # Strip HTML comments, whitespace, and section headers + CLEANED=$(echo "$BODY" | sed 's///g; /^## /d; /^- \[ \]/d; s/^[[:space:]]*//; s/[[:space:]]*$//; /^$/d; /^-$/d') + + if [[ -z "$CLEANED" ]]; then + echo "::error::PR body is empty. Please provide a description of your changes." + echo "" + echo "A good PR description should include:" + echo " - What: A summary of the changes made" + echo " - Why: The motivation or issue being addressed" + echo " - How: Key implementation details (if non-obvious)" + echo " - Testing: How the changes were verified" + exit 1 + fi + + # Check minimum length (at least 20 characters of meaningful content) + CHAR_COUNT=${#CLEANED} + if [[ "$CHAR_COUNT" -lt 20 ]]; then + echo "::error::PR body is too short ($CHAR_COUNT chars). Please provide a meaningful description." + exit 1 + fi + + echo "PR body is present ($CHAR_COUNT chars)." + + - name: Check for placeholder text + run: | + set -euo pipefail + + BODY=$(cat <<'BODY_EOF' + ${{ github.event.pull_request.body }} + BODY_EOF + ) + + # Warn if common placeholder patterns are detected + if echo "$BODY" | grep -qiE '(TODO|FIXME|PLACEHOLDER|fill in|describe here|add description)'; then + echo "::warning::PR body may contain placeholder text. Please ensure all sections are filled in." + fi diff --git a/.github/workflows/pr-policy.yml b/.github/workflows/pr-policy.yml new file mode 100644 index 0000000..382437e --- /dev/null +++ b/.github/workflows/pr-policy.yml @@ -0,0 +1,69 @@ +name: PR Policy + +on: + pull_request: + types: [opened, edited, synchronize, labeled, unlabeled, reopened] + +jobs: + title-convention: + name: Validate PR Title + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Check conventional commit format + run: | + set -euo pipefail + TITLE="${{ github.event.pull_request.title }}" + PATTERN="^(feat|fix|docs|doc|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?!?: .+" + if [[ ! "$TITLE" =~ $PATTERN ]]; then + echo "::error::PR title does not follow Conventional Commits format." + echo "" + echo "Expected format: (): " + echo " Types: feat, fix, docs, doc, style, refactor, perf, test, build, ci, chore, revert" + echo "" + echo "Examples:" + echo " feat: add decrement CLI command" + echo " fix(core): handle empty config file" + echo " docs: update installation instructions" + echo "" + echo "Got: '$TITLE'" + exit 1 + fi + echo "PR title follows Conventional Commits format." + + label-check: + name: Require Label + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Check for at least one label + run: | + set -euo pipefail + LABEL_COUNT=$(echo '${{ toJson(github.event.pull_request.labels) }}' | jq 'length') + if [[ "$LABEL_COUNT" -eq 0 ]]; then + echo "::error::PR must have at least one label before merging." + echo "Consider adding a label such as: bug, enhancement, documentation, maintenance, etc." + exit 1 + fi + echo "PR has $LABEL_COUNT label(s)." + + branch-naming: + name: Validate Branch Name + runs-on: ubuntu-latest + timeout-minutes: 5 + steps: + - name: Check branch naming convention + run: | + set -euo pipefail + BRANCH="${{ github.head_ref }}" + PATTERN="^(feature|fix|bugfix|hotfix|docs|chore|refactor|test|ci|release|claude)/" + if [[ ! "$BRANCH" =~ $PATTERN ]]; then + echo "::warning::Branch name '$BRANCH' does not follow the recommended naming convention." + echo "" + echo "Recommended format: /" + echo " Types: feature, fix, bugfix, hotfix, docs, chore, refactor, test, ci, release" + echo "" + echo "Examples:" + echo " feature/add-decrement-cli" + echo " fix/empty-config-handling" + fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8b071b9..4a0792c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,6 @@ # - Python linting + formatting (Ruff) # - Typing checks (Mypy) # - Security checks (Bandit) -# - Docstring auto-formatting (Docformatter) # - Docs build smoke test # - Commit message enforcement # - Shell script linting (shfmt + shellcheck) @@ -44,14 +43,6 @@ repos: - id: ruff-format types_or: [python, pyi] - # ---------- DOCSTRING AUTO-FIX (Docformatter) ---------- - - repo: https://github.com/PyCQA/docformatter - rev: v1.7.7 - hooks: - - id: docformatter - args: [--in-place, --recursive, --wrap-summaries=88] - types_or: [python] - # ---------- TYPES (MYPY) ---------- - repo: https://github.com/pre-commit/mirrors-mypy rev: v1.19.1 @@ -125,11 +116,7 @@ repos: rev: v1.38.0 hooks: - id: yamllint - args: - [ - -d, - "{extends: default, rules: {line-length: {max: 120}, document-start: disable}}", - ] + args: [--config-file=.yamllint] # ---------- CI / PRE-COMMIT.CI CONFIG ---------- ci: diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..efa8b92 --- /dev/null +++ b/.yamllint @@ -0,0 +1,34 @@ +# .yamllint +extends: default + +rules: + # GitHub Actions REQUIRE unquoted true/false for certain keys + truthy: + allowed-values: ["true", "false", "on", "off", "yes", "no"] + check-keys: false # Critical: don't validate GitHub Actions boolean keys + + # Realistic line lengths for CI workflows (URLs, commands) + line-length: + max: 160 + allow-non-breakable-words: true + allow-non-breakable-inline-mappings: true + + # Enforce newline at EOF (pre-commit already does this) + new-lines: + type: unix + + # Ignore GitHub Actions schema-specific constructs + comments-indentation: disable + indentation: + indent-sequences: consistent + +# Apply stricter rules to project YAML (configs), relaxed for workflows +yaml-files: + - '*.yaml' + - '*.yml' + +ignore: | + .git/ + __pycache__/ + .venv/ + venv/ diff --git a/apps/decrement_hydra.py b/apps/decrement_hydra.py index 3178380..fe7130e 100644 --- a/apps/decrement_hydra.py +++ b/apps/decrement_hydra.py @@ -1,26 +1,36 @@ """Sample app decrementing a given number using Hydra to handle CLI.""" import hydra +from omegaconf import DictConfig -from PROJECT_NAME.add import add +from template_project.add import add def decrement(x: int) -> int: - """Return the decrement of a given value. + """ + Return the decrement of a given value. Args: x (int): value to be decremented Returns: int: The decremented value 'x-1' + """ return add(x, -1) @hydra.main(version_base=None, config_path="configs", config_name="decrement") -def main(config): +def main(config: DictConfig) -> None: + """ + Decrement the given value from config and print the result. + + Args: + config: Hydra config object with 'value' attribute + + """ result = decrement(config.value) - print(result) + print(result) # noqa: T201 if __name__ == "__main__": diff --git a/apps/increment_fire.py b/apps/increment_fire.py index 832e28d..9c52e16 100644 --- a/apps/increment_fire.py +++ b/apps/increment_fire.py @@ -2,17 +2,19 @@ import fire -from PROJECT_NAME.add import add +from template_project.add import add def increment(x: int) -> int: - """Return the increment of a given value. + """ + Return the increment of a given value. Args: x (int): value to be incremented Returns: int: The incremented value 'x+1' + """ return add(x, 1) diff --git a/docker/Dockerfile b/docker/Dockerfile index 89506ea..5af147d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,4 +15,4 @@ RUN apt update && \ echo "$USERNAME ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers USER $USERNAME -WORKDIR /workspace/PROJECT_NAME +WORKDIR /workspace/template_project diff --git a/docker/compose.yaml b/docker/compose.yaml index 7042e1c..0450e94 100644 --- a/docker/compose.yaml +++ b/docker/compose.yaml @@ -1,7 +1,7 @@ services: - project_name: - image: project_name - container_name: project_name_container + template_project: + image: template_project + container_name: template_project_container build: context: . args: @@ -17,6 +17,6 @@ services: capabilities: [gpu] command: sleep infinity volumes: - - ..:/workspace/PROJECT_NAME + - ..:/workspace/template_project - ${HOST_SSH_FOLDER}:/home/${HOST_USERNAME}/.ssh:ro - ${HOST_GITCONFIG_FILE}:/home/${HOST_USERNAME}/.gitconfig:ro diff --git a/docker/create.sh b/docker/create.sh index 0a623b6..d8f50b8 100755 --- a/docker/create.sh +++ b/docker/create.sh @@ -12,4 +12,4 @@ source set_env_variables.sh docker compose up -d # Get an interactive bash session in the container -docker compose exec project_name bash +docker compose exec template_project bash diff --git a/docker/exec.sh b/docker/exec.sh index 2026e39..bda1519 100755 --- a/docker/exec.sh +++ b/docker/exec.sh @@ -9,4 +9,4 @@ cd "$(dirname "$0")" || exit source set_env_variables.sh # Get an interactive bash session -docker compose exec project_name bash +docker compose exec template_project bash diff --git a/pyproject.toml b/pyproject.toml index 8f6e425..572d89c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,21 +1,30 @@ [tool.ruff] -# Core linting + formatting line-length = 88 -target-version = ["py39", "py310", "py311", "py312"] -fix = true -select = ["ALL"] +target-version = "py39" +src = ["src", "apps", "tests"] + +[tool.ruff.lint] +select = ["E", "F", "W", "B", "S", "D", "PLR"] extend-ignore = [ - "E203", # Conflicts with line break before ':' in slices (Black style) - "W503" # Line break before binary operator (Black style) + "E203", # Black-compatible slice formatting + "D203", # Google style: no blank line before class (keep D211) + "D212", # Google style: multi-line summary on second line (keep D213) ] -src = ["src", "apps", "tests"] -[tool.docformatter] -# Auto-format docstrings -wrap-summaries = 88 -pre-summary-newline = true -make-summary-multi-line = true -recursive = true +[tool.ruff.lint.per-file-ignores] +"tests/**/*.py" = [ + "D100", "D101", "D102", # No docstring requirements in tests + "S101", # allow assert + "S602", # shell=True in subprocess (tests only) + "PLR2004", # Magic values in assertions are normal +] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" + [tool.mypy] # Type checking diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 0000000..b964292 --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,7 @@ +bandit +mypy +# Development dependencies +pre-commit +pytest +pytest-cov +ruff diff --git a/requirements.txt b/requirements.txt index 4f9c3aa..2292cab 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,10 @@ +bandit fire hydra-core +mypy pre-commit<4 pytest +pytest-cov +pytest-mock +ruff +types-PyYAML diff --git a/setup.py b/setup.py index 6c701d8..49e5985 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,9 @@ +"""Docstring for setup.""" + from setuptools import find_packages, setup setup( - name="PROJECT_NAME", + name="template_project", version="0.0.0", description="", author="", diff --git a/src/PROJECT_NAME/__init__.py b/src/PROJECT_NAME/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/template_project/__init__.py b/src/template_project/__init__.py new file mode 100644 index 0000000..c10b7ed --- /dev/null +++ b/src/template_project/__init__.py @@ -0,0 +1 @@ +"""Docstring for template_project.""" diff --git a/src/PROJECT_NAME/add.py b/src/template_project/add.py similarity index 87% rename from src/PROJECT_NAME/add.py rename to src/template_project/add.py index aa39836..6124e82 100644 --- a/src/PROJECT_NAME/add.py +++ b/src/template_project/add.py @@ -2,7 +2,8 @@ def add(a: int, b: int) -> int: - """Return the sum of two numbers. + """ + Return the sum of two numbers. Adds two numbers using internally the '+' operator. @@ -12,6 +13,6 @@ def add(a: int, b: int) -> int: Returns: int: The value 'a+b' - """ + """ return a + b diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..40c103b 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1 @@ +"""Docstring for apps/__init__.py.""" diff --git a/tests/apps/__init__.py b/tests/apps/__init__.py new file mode 100644 index 0000000..56fced9 --- /dev/null +++ b/tests/apps/__init__.py @@ -0,0 +1 @@ +"""Docstring for tests.apps.""" diff --git a/tests/apps/test_decrement_hydra.py b/tests/apps/test_decrement_hydra.py index 93720bb..1ffac58 100644 --- a/tests/apps/test_decrement_hydra.py +++ b/tests/apps/test_decrement_hydra.py @@ -1,3 +1,5 @@ +"""Tests for the decrement function.""" + import subprocess import tempfile from pathlib import Path @@ -5,9 +7,8 @@ apps_directory_path = Path(__file__).parents[2] / "apps" -def test_increment_fire(): +def test_increment_fire() -> None: """Test the decrement_hydra.py script.""" - with tempfile.TemporaryDirectory() as tmp_dir: command = ( f"python {apps_directory_path / 'decrement_hydra.py'} " diff --git a/tests/apps/test_increment_fire.py b/tests/apps/test_increment_fire.py index 48081b1..6555aa2 100644 --- a/tests/apps/test_increment_fire.py +++ b/tests/apps/test_increment_fire.py @@ -1,12 +1,13 @@ +"""Tests for the increment function.""" + import subprocess from pathlib import Path apps_directory_path = Path(__file__).parents[2] / "apps" -def test_increment_fire(): +def test_increment_fire() -> None: """Test the increment_fire.py script.""" - command = f"python {apps_directory_path / 'increment_fire.py'} --x 2" res = int(subprocess.check_output(command, shell=True)) assert res == 3 diff --git a/tests/test_add.py b/tests/test_add.py index 6bb8101..93be505 100644 --- a/tests/test_add.py +++ b/tests/test_add.py @@ -1,7 +1,8 @@ -from PROJECT_NAME.add import add +"""Tests for the add function.""" +from template_project.add import add -def test_add(): - """Test of the add module.""" +def test_add() -> None: + """Test of the add module.""" assert add(0, 1) == 1