diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..e2f9790 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,14 @@ +# Coverage is measured on Windows and consumed by the SonarQube scanner running +# on Linux, so the report must not carry machine-specific paths. relative_files +# makes coverage emit repo-root-relative names (pybreeze/utils/... rather than an +# absolute plus a package-relative filename), which is what the scanner +# resolves against. +[run] +relative_files = True +source = pybreeze +branch = True + +[report] +exclude_also = + if TYPE_CHECKING: + if __name__ == .__main__.: diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index 20d4cb0..15f0f62 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -30,9 +30,17 @@ jobs: - name: Install dev dependencies run: python -m pip install -r dev_requirements.txt - name: Install test tooling - run: python -m pip install pytest hypothesis + run: python -m pip install pytest pytest-cov hypothesis - name: Run unit tests (pytest) - run: python -m pytest test/test_utils/ -v --tb=short + run: python -m pytest test/test_utils/ -v --tb=short --cov=pybreeze --cov-branch --cov-report=xml + - name: Upload coverage for analysis + # One leg is enough; the scanner only consumes a single report. + if: matrix.python-version == '3.12' + uses: actions/upload-artifact@v4 + with: + name: coverage-xml + path: coverage.xml + if-no-files-found: error - name: Run AutomationEditor With Debug Mode run: python ./test/unit_test/start_automation/start_automation_test.py env: @@ -43,19 +51,25 @@ jobs: PYTHONPATH: . sonarcloud: - # Automatic Analysis is off, so branch and pull-request analysis both come - # from here. Skipped on the nightly schedule, where re-scanning an unchanged - # commit adds nothing, and on fork pull requests, which cannot read the token. + # Pull requests only. SonarCloud's plan for this organization exposes results + # for the main branch and for pull requests; an analysis pushed for `dev` + # succeeds but its results are not readable, so scanning every push here + # would spend CI time on numbers nobody can see. Fork pull requests are + # skipped because they cannot read the token. if: >- - github.event_name != 'schedule' && - (github.event_name != 'pull_request' || - github.event.pull_request.head.repo.full_name == github.repository) + github.event_name == 'pull_request' && + github.event.pull_request.head.repo.full_name == github.repository + needs: unit-tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: # Sonar needs the full history to attribute new code to the right commits. fetch-depth: 0 + - name: Download coverage + uses: actions/download-artifact@v4 + with: + name: coverage-xml - name: SonarQube Cloud scan uses: SonarSource/sonarqube-scan-action@v8.2.1 env: diff --git a/.github/workflows/stable.yml b/.github/workflows/stable.yml index 3a64059..00c0997 100644 --- a/.github/workflows/stable.yml +++ b/.github/workflows/stable.yml @@ -30,9 +30,17 @@ jobs: - name: Install dependencies run: python -m pip install -r requirements.txt - name: Install test tooling - run: python -m pip install pytest hypothesis + run: python -m pip install pytest pytest-cov hypothesis - name: Run unit tests (pytest) - run: python -m pytest test/test_utils/ -v --tb=short + run: python -m pytest test/test_utils/ -v --tb=short --cov=pybreeze --cov-branch --cov-report=xml + - name: Upload coverage for analysis + # One leg is enough; the scanner only consumes a single report. + if: matrix.python-version == '3.12' + uses: actions/upload-artifact@v4 + with: + name: coverage-xml + path: coverage.xml + if-no-files-found: error - name: Run AutomationEditor With Debug Mode run: python ./test/unit_test/start_automation/start_automation_test.py env: @@ -43,19 +51,25 @@ jobs: PYTHONPATH: . sonarcloud: - # Automatic Analysis is off, so branch and pull-request analysis both come - # from here. Skipped on the nightly schedule, where re-scanning an unchanged - # commit adds nothing, and on fork pull requests, which cannot read the token. + # Automatic Analysis is off, so main-branch and pull-request analysis both + # come from here. Skipped on the nightly schedule, where re-scanning an + # unchanged commit adds nothing, and on fork pull requests, which cannot + # read the token. if: >- github.event_name != 'schedule' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) + needs: unit-tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: # Sonar needs the full history to attribute new code to the right commits. fetch-depth: 0 + - name: Download coverage + uses: actions/download-artifact@v4 + with: + name: coverage-xml - name: SonarQube Cloud scan uses: SonarSource/sonarqube-scan-action@v8.2.1 env: diff --git a/CLAUDE.md b/CLAUDE.md index 4c3817d..4823b03 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,6 +2,8 @@ Automation-first Python IDE built on PySide6 + JEditor, integrating Web/API/GUI/Load testing into a single environment. +**This file is the only home for project rules.** Anything that constrains how work is done here — conventions, security requirements, quality gates, commit policy — belongs in this file. Do not start a `progress.md`, a scratch notes file, or any other side document to hold rules: a rule kept somewhere else is a rule nobody reads. Reference material that is not a rule (the architecture map, the plugin API) lives in its own file and is linked from here. + ## Architecture ``` @@ -59,7 +61,9 @@ pybreeze/ - `main`: stable, publishes `pybreeze` · `dev`: development, publishes `pybreeze_dev` - Version config: `pyproject.toml` (stable), `dev.toml` (dev) — keep both in sync when bumping - `unit-tests` job: GitHub Actions on Windows, Python 3.10–3.14 — install deps → pytest `test/test_utils/` → `start_automation_test` → `extend_automation_test` -- `sonarcloud` job: CI-based SonarQube Cloud analysis (`sonar-project.properties`), skipped on the nightly schedule and on fork PRs. Automatic Analysis is off — it only covers main and PRs, so `dev` went unanalysed; the two modes are mutually exclusive, so do not re-enable it +- `sonarcloud` job: CI-based SonarQube Cloud analysis (`sonar-project.properties`), `needs: unit-tests` so it can consume the `coverage-xml` artifact that leg uploads. Automatic Analysis is off and must stay off — the two modes are mutually exclusive and the scanner refuses to run alongside it +- SonarCloud's plan for this organization exposes results for `main` and for pull requests only. An analysis pushed for another branch succeeds but its results read back 403, so `dev.yml` scans on pull requests only; `stable.yml` also scans pushes to `main`. Do not "fix" this by scanning every `dev` push — the numbers are not readable +- Coverage comes from the 3.12 matrix leg (`pytest --cov`), configured by `.coveragerc`. `relative_files = True` is required: the report is produced on Windows and consumed by a Linux scanner, so it must not carry machine-specific paths ## Development diff --git a/architecture_explore.md b/architecture_explore.md index 3e204bd..a5a2d8f 100644 --- a/architecture_explore.md +++ b/architecture_explore.md @@ -398,8 +398,10 @@ extend_ai_gui/ - **單元測試** `test/test_utils/` — 60 個 `test_*.py`。純邏輯 + headless Qt widget 測試(`QT_QPA_PLATFORM=offscreen`)。涵蓋 curl/HAR 解析、SSRF 驗證、SSH 安全、process reader EOF、queue pump、語言對齊、mermaid parser、diagram 序列化、prthinker 設定等。有 hypothesis fuzz 測試(`test_fuzz_pure_logic.py`)。 - **整合測試** `test/unit_test/start_automation/` — 以 `debug_mode=True` 啟動 IDE,10 秒後自動關閉,驗證啟動流程與 extend tab -- **CI** `.github/workflows/{dev,stable}.yml` — `unit-tests` job 跑 Windows runner、Python 3.10–3.14 矩陣;`sonarcloud` job 跑 ubuntu,排程與 fork PR 會跳過。每日 02:00 排程 + push/PR 觸發。`stable.yml` 另有 `publish` job 負責版號遞增與 PyPI 發布 -- **靜態分析** SonarCloud(`sonar-project.properties`,由兩個 workflow 的 `sonarcloud` job 執行 CI-based analysis;Automatic Analysis 已關閉,因為它只看 main 與 PR,`dev` 完全不會被分析)+ Codacy(`.codacy.yml`)+ Bandit(`pyproject.toml` 中排除 test、skip B101/B404) +- **CI** `.github/workflows/{dev,stable}.yml` — `unit-tests` job 跑 Windows runner、Python 3.10–3.14 矩陣,3.12 那一腳額外上傳 `coverage-xml` artifact;`sonarcloud` job 跑 ubuntu、`needs: unit-tests`。每日 02:00 排程 + push/PR 觸發。`stable.yml` 另有 `publish` job 負責版號遞增與 PyPI 發布 +- **覆蓋率** `.coveragerc` — `relative_files = True` 是必要的:報告在 Windows 產生、由 Linux 上的 scanner 讀取,路徑不能帶機器資訊。目前整體 57%(`utils/` 與 `tools_gui` 95–100%,UI 層 20–45% 拉低) +- **靜態分析** SonarCloud(`sonar-project.properties`,CI-based analysis;Automatic Analysis 已關閉且必須維持關閉,兩種模式互斥)+ Codacy(`.codacy.yml`)+ Bandit(`pyproject.toml` 中排除 test、skip B101/B404) +- **SonarCloud 方案限制** 該組織的方案只開放 `main` 與 PR 的分析結果。非 main 分支的分析送得出去、CE 任務也會成功,但結果讀回來是 403(組織內每個專案都只有 `main` 一條分支)。因此 `dev.yml` 只在 PR 時掃描,`stable.yml` 另外掃 push to `main` --- diff --git a/sonar-project.properties b/sonar-project.properties index 7040869..009b45e 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -9,3 +9,7 @@ sonar.organization=integration-automation sonar.sources=pybreeze sonar.tests=test sonar.python.version=3.10, 3.11, 3.12, 3.13, 3.14 + +# Produced by the test matrix and handed to the scanner as an artifact. Without +# it the quality gate scores new code at 0% coverage and fails on that alone. +sonar.python.coverage.reportPaths=coverage.xml