diff --git a/.github/workflows/autoupdate.yml b/.github/workflows/autoupdate.yml new file mode 100644 index 0000000..a56671e --- /dev/null +++ b/.github/workflows/autoupdate.yml @@ -0,0 +1,21 @@ +name: Auto-Update PRs + +on: + push: + branches: [master] + +permissions: + contents: read + +jobs: + autoupdate: + name: Auto-update PRs + runs-on: ubuntu-latest + steps: + - name: Auto-update all PRs + uses: chinthakagodawita/autoupdate@0707656cd062a3b0cf8fa9b2cda1d1404d74437e # v1.7.0 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_FILTER: "all" + PR_READY_STATE: "ready_for_review" + MERGE_CONFLICT_ACTION: "fail" diff --git a/.github/workflows/bandit.yml b/.github/workflows/bandit.yml deleted file mode 100644 index dcb19e3..0000000 --- a/.github/workflows/bandit.yml +++ /dev/null @@ -1,54 +0,0 @@ -# Static Application Security Testing -name: Bandit - -on: - workflow_call: - push: - branches: [master] - paths: - - ".github/workflows/bandit.yml" - - "**/*.py" - pull_request: - branches: [master] - -permissions: - contents: read - -jobs: - sast-scan: - if: | - github.actor != 'dependabot[bot]' && - github.actor != 'github-actions[bot]' && - github.actor != 'protected-auto-commits[bot]' - name: Bandit Scan - concurrency: sast-scan - runs-on: ubuntu-latest - steps: - - name: Harden the runner - uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 - with: - egress-policy: audit - disable-telemetry: true - disable-sudo: true - - - name: Checkout Code - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - - - name: Set up Python - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 - with: - python-version: "3.13" - cache: "pip" - - - name: Install Bandit - run: pip install bandit==1.8.6 - - - name: Run Bandit Scan - run: bandit -r . -f json -o bandit-report.json - - - name: Upload Bandit Scan Results - if: always() - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 - with: - name: bandit-scan-results - path: bandit-report.json diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 2cec16b..a4a6597 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -12,9 +12,6 @@ on: pull_request: branches: [master] -permissions: - contents: read - jobs: codeql: if: | diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 7df4e3a..452436a 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -46,7 +46,6 @@ jobs: - name: Install Dependencies run: | pip install -r requirements.txt - playwright install --with-deps chromium - name: Run tests with coverage run: | diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 18c5aaf..80aeb01 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -8,9 +8,6 @@ on: - "docs/**" - "mkdocs.yml" -permissions: - contents: read - jobs: build: name: Build Docs @@ -22,6 +19,8 @@ jobs: github.actor != 'github-actions[bot]' && github.actor != 'protected-auto-commits[bot]' runs-on: ubuntu-latest + permissions: + contents: read steps: - name: Harden the runner uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..6063b8e --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,97 @@ +name: Playwright Tests + +on: + pull_request: + paths: + - "requirements.txt" + - "pyproject.toml" + - "Dockerfile" + - ".github/workflows/playwright.yml" + branches: [master] + workflow_dispatch: + +jobs: + pip-test: + name: Test Pip Package + runs-on: ubuntu-latest + concurrency: pip-test + permissions: + contents: read + steps: + - name: Harden the runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + disable-telemetry: true + disable-sudo: true + + - name: Checkout Code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Setup Python + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 + with: + python-version: "3.13" + cache: "pip" + + - name: Install dependencies + run: pip install -r requirements.txt + + - name: Verify Playwright installation + run: | + python -m playwright --version + python -c "from playwright.sync_api import sync_playwright; print('Playwright API: OK')" + + - name: Run smoke tests + run: pytest tests/smoke/ -v --tb=short + + docker-test: + name: Test Docker Build + needs: pip-test + runs-on: ubuntu-latest + concurrency: docker-test + permissions: + contents: read + env: + INPUT_STEAM_API_KEY: ${{ secrets.STEAM_API_KEY }} + INPUT_STEAM_ID: ${{ vars.STEAM_ID }} + INPUT_STEAM_CUSTOM_ID: ${{ vars.STEAM_CUSTOM_ID }} + steps: + - name: Harden the runner + uses: step-security/harden-runner@20cf305ff2072d973412fa9b1e3a4f227bda3c76 # v2.14.0 + with: + egress-policy: audit + disable-telemetry: true + disable-sudo: true + + - name: Checkout Code + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 + + - name: Build Docker image + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 + with: + context: . + file: ./Dockerfile + push: false + load: true + tags: steam-stats:test + platforms: linux/amd64 + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Verify Docker image + run: | + docker run --rm steam-stats:test python -m playwright --version + docker run --rm steam-stats:test playwright --version + + - name: Run smoke tests in container + run: | + docker run --rm \ + -e INPUT_STEAM_API_KEY \ + -e INPUT_STEAM_ID \ + -e INPUT_STEAM_CUSTOM_ID \ + steam-stats:test \ + pytest tests/smoke/ -v --tb=short diff --git a/README.md b/README.md index e275e19..5d4cf12 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,6 @@ [](https://github.com/Nicconike/Steam-Stats/actions/workflows/steam-stats.yml) [](https://github.com/Nicconike/Steam-Stats/actions/workflows/release.yml) [](https://github.com/Nicconike/Steam-Stats/actions/workflows/codeql.yml) -[](https://github.com/Nicconike/Steam-Stats/actions/workflows/bandit.yml) [](https://github.com/Nicconike/Steam-Stats/actions/workflows/coverage.yml) [](https://github.com/Nicconike/Steam-Stats/actions/workflows/docs.yml) [](https://github.com/Nicconike/Steam-Stats/actions/workflows/scorecard.yml) diff --git a/docs/developer-guide/contributing.md b/docs/developer-guide/contributing.md index d17726e..b5e5a92 100644 --- a/docs/developer-guide/contributing.md +++ b/docs/developer-guide/contributing.md @@ -51,7 +51,7 @@ Follow [Local Development](local-development.md) for environment setup. | Import sorting | isort | Compatible with black | | Linting | pylint | 10/10 score maintained | | Type checking | mypy | Strict mode on new code | -| Security scanning | bandit | Vulnerability detection | +| Security scanning | codeql | Security analysis | ### 5. Commit Messages diff --git a/docs/developer-guide/local-development.md b/docs/developer-guide/local-development.md index 9e1be72..24b4f76 100644 --- a/docs/developer-guide/local-development.md +++ b/docs/developer-guide/local-development.md @@ -213,8 +213,7 @@ pre-commit run --all-files All PRs run through these automated workflows: -- **Security Scanning** `(bandit.yml)`: Vulnerability detection in Python code -- **Code Quality** `(codeql.yml)`: Static analysis and security scanning +- **Security Scanning** `(codeql.yml)`: Static analysis and security scanning - **Test Coverage** `(coverage.yml)`: Code coverage reporting and enforcement - **Dependency Security** `(dependency-review.yml)`: Check for vulnerable dependencies - **Container Build**: Ensures Docker image builds successfully diff --git a/docs/developer-guide/project-layout.md b/docs/developer-guide/project-layout.md index 860af23..14f2583 100644 --- a/docs/developer-guide/project-layout.md +++ b/docs/developer-guide/project-layout.md @@ -45,7 +45,6 @@ Steam-Stats/ │ │ ├── help-wanted.yml │ │ └── question.yml │ └── workflows/ # GitHub Actions workflows -│ ├── bandit.yml # Security vulnerability scanning │ ├── codeql.yml # Code quality and security analysis │ ├── coverage.yml # Code coverage reporting │ ├── dependency-review.yml # Dependency security review @@ -251,7 +250,7 @@ graph TD |------------------|-------------------------------------------------------------------|------------------------------------| | **Testing** | `pytest-asyncio`, `pytest-cov`, `pytest-mock`, `requests-mock` | Unit testing, coverage, mocking | | **Documentation**| `mkdocs-material`, `mkdocs-mermaid2-plugin`, `mkdocstrings-python`| Documentation generation | -| **Code Quality** | `bandit`, `pylint` | Security scanning, code linting | +| **Code Quality** | `pylint` | Code linting | | **Release** | `python-semantic-release`, `twine` | Automated releases, PyPI publishing| ### System Dependencies :octicons-package-dependencies-24: diff --git a/mkdocs.yml b/mkdocs.yml index 7e636d6..7044eee 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -5,7 +5,7 @@ site_url: https://nicconike.github.io/Steam-Stats/ repo_name: Nicconike/Steam-Stats repo_url: https://github.com/Nicconike/Steam-Stats edit_uri: edit/master/docs/ -copyright: Copyright © 2024 - 2025 Nicconike +copyright: Copyright © 2024 - 2026 Nicconike theme: name: material diff --git a/pyproject.toml b/pyproject.toml index fd6f368..3d23cd5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,8 +24,8 @@ license = "GPL-3.0" license-files = ["LICENSE"] dependencies = [ "bs4==0.0.2", - "DateTime==5.5", - "playwright==1.57.0", + "DateTime==6.0", + "playwright==1.58.0", "PyGithub==2.8.1", "python-semantic-release==10.5.3", ] @@ -60,25 +60,24 @@ Issues = "https://github.com/Nicconike/Steam-Stats/issues" [project.optional-dependencies] test = [ - "pytest-asyncio==1.2.0", + "pytest-asyncio==1.3.0", "pytest-cov==7.0.0", "pytest-mock==3.15.1", "requests-mock==1.12.1", ] dev = [ - "build==1.3.0", - "bandit==1.8.6", - "pipdeptree==2.28.0", - "pylint==3.3.9", + "build==1.4.0", + "pipdeptree==2.30.0", + "pylint==4.0.4", "python-dotenv==1.2.1", "twine==6.2.0", ] docs = [ - "mkdocs-git-revision-date-localized-plugin==1.5.0", - "mkdocs-material==9.7.0", + "mkdocs-git-revision-date-localized-plugin==1.5.1", + "mkdocs-material==9.7.1", "mkdocs-mermaid2-plugin==1.2.3", "mkdocs-minify-plugin==0.8.0", - "mkdocstrings-python==1.18.2", + "mkdocstrings-python==2.0.2", ] [tool.setuptools.packages.find] diff --git a/pytest.ini b/pytest.ini index 0102b0a..087e74e 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,2 +1,5 @@ [pytest] asyncio_default_fixture_loop_scope = function +testpaths = tests +python_files = test_*.py +python_functions = test_* diff --git a/requirements.txt b/requirements.txt index 1d22d6d..6d7ff1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,17 +1,16 @@ -bandit==1.8.6 bs4==0.0.2 -build==1.3.0 -DateTime==5.5 -mkdocs-git-revision-date-localized-plugin==1.5.0 -mkdocs-material==9.7.0 +build==1.4.0 +DateTime==6.0 +mkdocs-git-revision-date-localized-plugin==1.5.1 +mkdocs-material==9.7.1 mkdocs-mermaid2-plugin==1.2.3 mkdocs-minify-plugin==0.8.0 -mkdocstrings-python==1.18.2 -pipdeptree==2.28.0 -playwright==1.57.0 +mkdocstrings-python==2.0.2 +pipdeptree==2.30.0 +playwright==1.58.0 PyGithub==2.8.1 -pylint==3.3.9 -pytest-asyncio==1.2.0 +pylint==4.0.4 +pytest-asyncio==1.3.0 pytest-cov==7.0.0 pytest-mock==3.15.1 python-dotenv==1.2.1 diff --git a/tests/smoke/__init__.py b/tests/smoke/__init__.py new file mode 100644 index 0000000..1f8366f --- /dev/null +++ b/tests/smoke/__init__.py @@ -0,0 +1 @@ +"""Smoke tests for Playwright functionality""" diff --git a/tests/smoke/test_browser_launch.py b/tests/smoke/test_browser_launch.py new file mode 100644 index 0000000..c7a1b29 --- /dev/null +++ b/tests/smoke/test_browser_launch.py @@ -0,0 +1,48 @@ +"""Verify Chromium browser launches successfully""" + +import os +import tempfile +import pytest +from playwright.sync_api import sync_playwright + + +def test_browser_launch(): + """Verify Chromium browser can launch and close successfully""" + with sync_playwright() as p: + browser = p.chromium.launch(headless=True) + if browser is None: + pytest.fail("Browser failed to launch") + browser.close() + + +def test_browser_new_page(): + """Verify browser can create a new page""" + with sync_playwright() as p: + browser = p.chromium.launch(headless=True) + page = browser.new_page() + if page is None: + pytest.fail("Failed to create new page") + page.close() + browser.close() + + +def test_browser_navigate_to_file(): + """Verify browser can navigate to a local file""" + with sync_playwright() as p: + browser = p.chromium.launch(headless=True) + page = browser.new_page() + + with tempfile.NamedTemporaryFile(mode="w", suffix=".html", delete=False) as f: + f.write("
Test card for smoke testing
+