Skip to content

feat: add F4 linter provisioning framework#125

Merged
SSobol77 merged 3 commits into
mainfrom
feature/f4-linter-provisioning-installer
Jul 9, 2026
Merged

feat: add F4 linter provisioning framework#125
SSobol77 merged 3 commits into
mainfrom
feature/f4-linter-provisioning-installer

Conversation

@SSobol77

@SSobol77 SSobol77 commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

Add the first provider-neutral F4 linter provisioning framework for ECLI.

This PR introduces the contract, planner, installer-selection model, dry-run evidence generation, and verification scripts needed for OS/artifact-aware F4 linter provisioning.

This is not yet the final artifact-specific installer/bundling implementation for every release package. It creates the framework and release evidence gates required before wiring concrete packaging actions.

Key changes

  • Add provider-neutral provisioning core under the F4 linter extension layer:

    • src/ecli/extensions/linters/core/provisioning_contract.py
    • src/ecli/extensions/linters/core/provisioning_registry.py
    • src/ecli/extensions/linters/core/provisioning.py
  • Add release/packaging CLI entrypoints:

    • scripts/provision_f4_linters.py
    • scripts/verify_f4_linter_provisioning.py
  • Extend linter package contract metadata with provisioning information:

    • allowed provisioning mechanisms
    • source/provenance metadata
    • required/optional profile metadata
    • Full-profile selection behavior
  • Add deterministic JSON evidence for provisioning plans.

  • Add installer-selection model:

    • Full profile
    • Custom profile
    • Minimal/constrained profile
    • per-tool selected/skipped/user-opt-out evidence
  • Preserve the existing ECLI artifact contract:

    • exactly 21 canonical ECLI-owned artifact entries
    • GitHub-generated source ZIP/TAR archives remain excluded
    • checksum sidecars remain CI/evidence, not uploaded release assets
  • Treat current docs and package_contract.py files as authoritative for Full-required linters.

Full-required F4 baseline

The Full-required baseline is derived from current package contracts and includes:

  • Ruff
  • Biome
  • markdownlint-cli2
  • yamllint
  • ShellCheck
  • Zig
  • Hadolint
  • Taplo
  • Actionlint
  • clang-tidy
  • cppcheck
  • clang-format
  • Checkstyle
  • PMD
  • SpotBugs
  • Cargo Clippy
  • golangci-lint
  • SQLFluff
  • TFLint

Optional / legacy / specialist tools remain not selected by default:

  • ESLint
  • Stylelint
  • Oxlint
  • Pylint

Artifact behavior

The provisioning evidence model covers exactly 21 artifact entries:

  • pypi-wheel
  • pypi-sdist
  • linux-pyinstaller
  • linux-tarball
  • deb
  • rpm
  • opensuse-rpm
  • arch-pkgbuild
  • slackware-txz
  • appimage
  • freebsd-pkg
  • freebsd-ports-chroot
  • macos-app
  • macos-dmg
  • windows-portable-exe
  • windows-nsis-installer
  • nix-flake
  • nixos-package
  • docker-deb-helper
  • docker-rpm-helper
  • gha-release-contract

pypi-wheel and pypi-sdist are explicitly represented as constrained/minimal artifacts because Python packaging metadata cannot reliably provision Node, Rust, Go, Zig, Java, or system binaries. The evidence records this through effective_profile, full_profile_complete, and custom_profile_reason.

Validation

Passed locally:

uv run ruff check src tests scripts
uv run ruff format --check src tests scripts
uv run python scripts/check_runtime_imports.py
uv run pytest -q tests/extensions/linters
uv run pytest -q tests/packaging
uv run pytest -q tests/docs
uv run pytest -q tests/extensions tests/packaging tests/docs

Results:

tests/extensions/linters: 221 passed
tests/packaging: 353 passed
tests/docs: 18 passed
combined: 1301 passed, 4 skipped

Manual smoke passed:

scripts/provision_f4_linters.py --artifact deb --mode dry-run --json
scripts/verify_f4_linter_provisioning.py --artifact deb
scripts/provision_f4_linters.py --all-artifacts --mode dry-run --json
scripts/verify_f4_linter_provisioning.py --all-artifacts

Additional evidence checks confirmed:

21 evidence files
21 artifact entries
constrained PyPI artifacts handled explicitly

Boundaries

This PR does not change:

  • F4 Diagnostics Panel UI
  • F4 keybindings
  • provider/parser runtime behavior
  • TextMate/theme rendering
  • actual release artifact generation
  • vendored binaries
  • downloaded tools
  • generated release outputs

Remaining gap before v0.2.4

Concrete artifact-specific installation/bundling actions still need to be wired into the packaging flows before v0.2.4 can claim real Full provisioning across release artifacts.

This PR provides the provisioning framework, component-selection model, dry-run planner, evidence format, and verifier needed for that next step.

Summary by CodeRabbit

  • New Features

    • Added provider-neutral F4 linter provisioning and evidence verification tooling with deterministic per-artifact JSON outputs for release checks.
    • Introduced contract tooling updates for selecting/install modes (full/custom/minimal) and evidence verification workflows.
    • Expanded linter package contracts with supported install mechanisms and source references.
  • Documentation

    • Updated release and packaging docs to reference the new provisioning evidence entrypoints and updated artifact readiness steps.
  • Bug Fixes

    • Strengthened contract/evidence validation to detect missing or inconsistent required entries earlier.
  • Tests

    • Added end-to-end coverage for provisioning/evidence generation and verification, including single-artifact, all-artifacts, and negative cases.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@SSobol77, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 51 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0236be4f-fcc0-44ab-abdb-a31f14754ae5

📥 Commits

Reviewing files that changed from the base of the PR and between c287e01 and 46c9545.

📒 Files selected for processing (2)
  • tests/extensions/linters/test_provisioning_contract.py
  • tests/packaging/test_f4_linter_provisioning_scripts.py
📝 Walkthrough

Walkthrough

This PR adds a provider-neutral F4 Full linter provisioning contract: new dataclasses and registry rules, planning and evidence generation/verification logic, two CLI scripts, per-linter contract metadata updates, documentation, and tests. Two unrelated scripts receive cosmetic reformatting.

Changes

F4 Linter Provisioning Feature

Layer / File(s) Summary
Provisioning data contracts and registry base fields
src/ecli/extensions/linters/core/provisioning_contract.py, src/ecli/extensions/linters/core/registry.py
New Literal types and frozen dataclasses for provisioning modes, profiles, actions, plans, and evidence; PackageContract gains install mechanism and provenance fields with validation.
Artifact and linter contract registry
src/ecli/extensions/linters/core/provisioning_registry.py
Canonical 21-entry artifact registry, deterministic linter load order, dynamic manifest/package-contract loading, per-tool contract validation, and loader/filter helpers.
Provisioning planning and evidence engine
src/ecli/extensions/linters/core/provisioning.py
Builds component models, resolves selection profiles, chooses per-tool strategies, plans actions with version probing, serializes and writes deterministic evidence JSON, and verifies evidence payloads and directories.
Provisioning and verification CLI scripts
scripts/provision_f4_linters.py, scripts/verify_f4_linter_provisioning.py
New CLIs to plan and write provisioning evidence per artifact and to verify evidence files against the contract, with exit codes for CI use.
Per-linter package contract metadata updates
src/ecli/extensions/linters/*/package_contract.py
Each linter's PACKAGE_CONTRACT now declares allowed_install_mechanisms and source_url; Ruff also adds provenance_requirements and checksum_required_for_downloads.
Provisioning contract tests
tests/extensions/linters/test_provisioning_contract.py
New pytest coverage for registry counts, profile selection, plan outcomes, evidence verification, and input-safety checks.
Provisioning script integration tests
tests/packaging/test_f4_linter_provisioning_scripts.py, tests/packaging/test_packaging_release_contract.py
End-to-end script tests validate CLI behavior, dry-run evidence output, all-artifact coverage, and updated packaging script glob coverage.
Release and diagnostics documentation updates
docs/extensions/diagnostics-linter-layer.md, docs/release/*.md
Documentation clarifies metadata-only provisioning scope and adds references to the new provisioning and verification scripts and evidence contract.

Unrelated Formatting Tweaks

Layer / File(s) Summary
Reformatting of arch packaging and release asset verification scripts
scripts/build_and_package_arch.py, scripts/verify_release_assets.py
Cosmetic single-line and multi-line reformatting of a re.sub call and print/return statements with no logic change.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • SSobol77/ecli#85: Modifies the same PACKAGING_SCRIPT_GLOBS packaging-migration contract surface that this PR extends with the new provisioning scripts.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a new F4 linter provisioning framework.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/f4-linter-provisioning-installer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

coderabbitai[bot]
coderabbitai Bot previously requested changes Jul 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/release/release-checklist.md`:
- Around line 81-84: The checklist examples for provision_f4_linters.py and
verify_f4_linter_provisioning.py are missing required path flags, so update the
release checklist entry to include the needed arguments for both commands. Add
--target-dir and --evidence-dir to the provision_f4_linters.py example, and add
--evidence-dir to the verify_f4_linter_provisioning.py example, keeping the
commands in docs/release/release-checklist.md runnable as written.

In `@scripts/provision_f4_linters.py`:
- Around line 153-213: main() is too complex because it mixes the list-selection
branch and the provisioning branch; split those paths into dedicated helpers.
Extract the --list-selection-options handling into a helper that builds and
prints the models, and extract the provisioning loop into another helper that
writes evidence, collects output, and computes failure state. Keep main()
focused on argument setup plus dispatching to these helpers while preserving the
existing behavior of build_provisioning_plan(), write_evidence(),
_print_human(), and plan_has_release_blocking_failure().

In `@src/ecli/extensions/linters/core/provisioning_contract.py`:
- Line 194: `ProvisioningEvidence` still exposes a mutable `os_context` even
though the dataclass is frozen, so callers can mutate evidence after
construction. Update the `ProvisioningEvidence` definition to make `os_context`
immutable by changing the type to `Mapping[str, str]` and/or normalizing it in
`__post_init__` to a read-only mapping, using the `os_context` field as the
location to fix. Ensure any construction or serialization paths in
`ProvisioningEvidence` continue to work with the immutable representation.

In `@src/ecli/extensions/linters/core/provisioning_registry.py`:
- Around line 40-59: The first two ArtifactContractEntry definitions in
provisioning_registry.py are passing the constrained-artifact fields
positionally, which makes the intent unclear and fragile. Update those entries
to use the named parameters full_provisioning_supported= and minimal_reason=,
matching the contract style used elsewhere and keeping the ArtifactContractEntry
calls in core/provisioning_registry.py explicit and stable if the constructor
changes.

In `@src/ecli/extensions/linters/core/provisioning.py`:
- Around line 683-750: `verify_evidence_payload` is over the
cognitive-complexity threshold, so split its validation logic into smaller
helpers and keep the main function as a simple orchestrator. Extract the
header/payload field checks and the per-required-tool validation loop into
dedicated functions near `verify_evidence_payload`, reusing symbols like
`artifact_entry_id`, `loaded`, `required`, and `_strategy_errors` so the logic
stays easy to locate. Apply the same refactor pattern to `_selection_json`,
`resolve_profile`, `_action_for_contract`, and `verify_evidence_dir` by moving
branching-heavy chunks into focused helper functions until each method is below
the SonarCloud limit.

In `@src/ecli/extensions/linters/core/registry.py`:
- Around line 167-180: Derive the runtime allowlists from the existing Literal
types instead of hardcoding them, so `ALLOWED_INSTALL_MECHANISMS` and the
corresponding `ALLOWED_*` frozensets stay in sync automatically. Update the
registry logic around `InstallMechanism` and `ProvenanceRequirement` to build
the frozensets with `typing.get_args()` from the Literal definitions, and keep
the surrounding validation code using those derived sets.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 45d646fe-3c61-43c6-9003-fdfd239eab37

📥 Commits

Reviewing files that changed from the base of the PR and between a4f129b and 906b92e.

📒 Files selected for processing (41)
  • docs/extensions/diagnostics-linter-layer.md
  • docs/release/README-release.md
  • docs/release/artifact-contract.md
  • docs/release/artifact-verification.md
  • docs/release/build-matrix.md
  • docs/release/packaging-flows.md
  • docs/release/release-checklist.md
  • scripts/build_and_package_arch.py
  • scripts/provision_f4_linters.py
  • scripts/verify_f4_linter_provisioning.py
  • scripts/verify_release_assets.py
  • src/ecli/extensions/linters/actionlint/package_contract.py
  • src/ecli/extensions/linters/biome/package_contract.py
  • src/ecli/extensions/linters/cargo_clippy/package_contract.py
  • src/ecli/extensions/linters/clang_format/package_contract.py
  • src/ecli/extensions/linters/clang_tidy/package_contract.py
  • src/ecli/extensions/linters/core/provisioning.py
  • src/ecli/extensions/linters/core/provisioning_contract.py
  • src/ecli/extensions/linters/core/provisioning_registry.py
  • src/ecli/extensions/linters/core/registry.py
  • src/ecli/extensions/linters/cppcheck/package_contract.py
  • src/ecli/extensions/linters/eslint/package_contract.py
  • src/ecli/extensions/linters/golangci_lint/package_contract.py
  • src/ecli/extensions/linters/hadolint/package_contract.py
  • src/ecli/extensions/linters/java_checkstyle/package_contract.py
  • src/ecli/extensions/linters/java_pmd/package_contract.py
  • src/ecli/extensions/linters/java_spotbugs/package_contract.py
  • src/ecli/extensions/linters/markdownlint/package_contract.py
  • src/ecli/extensions/linters/oxlint/package_contract.py
  • src/ecli/extensions/linters/pylint/package_contract.py
  • src/ecli/extensions/linters/ruff/package_contract.py
  • src/ecli/extensions/linters/shellcheck/package_contract.py
  • src/ecli/extensions/linters/sqlfluff/package_contract.py
  • src/ecli/extensions/linters/stylelint/package_contract.py
  • src/ecli/extensions/linters/taplo/package_contract.py
  • src/ecli/extensions/linters/tflint/package_contract.py
  • src/ecli/extensions/linters/yamllint/package_contract.py
  • src/ecli/extensions/linters/zig/package_contract.py
  • tests/extensions/linters/test_provisioning_contract.py
  • tests/packaging/test_f4_linter_provisioning_scripts.py
  • tests/packaging/test_packaging_release_contract.py

Comment thread docs/release/release-checklist.md
Comment thread scripts/provision_f4_linters.py Outdated

artifact_entry_id: str
run_mode: ProvisioningMode
os_context: dict[str, str]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

os_context is a mutable dict inside a frozen dataclass.

ProvisioningEvidence is frozen=True, but os_context: dict[str, str] remains mutable — callers can alter it after construction, potentially corrupting evidence before serialization. Consider using Mapping[str, str] for the type hint or copying into a types.MappingProxyType in __post_init__ to enforce immutability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ecli/extensions/linters/core/provisioning_contract.py` at line 194,
`ProvisioningEvidence` still exposes a mutable `os_context` even though the
dataclass is frozen, so callers can mutate evidence after construction. Update
the `ProvisioningEvidence` definition to make `os_context` immutable by changing
the type to `Mapping[str, str]` and/or normalizing it in `__post_init__` to a
read-only mapping, using the `os_context` field as the location to fix. Ensure
any construction or serialization paths in `ProvisioningEvidence` continue to
work with the immutable representation.

Comment on lines +40 to +59
ArtifactContractEntry(
1,
"pypi-wheel",
"PyPI wheel",
"PyPI / Python",
"ecli_editor-<version>-py3-none-any.whl",
False,
"Python wheel metadata cannot reliably provision Node, Rust, Go, Zig, "
"Java, or system binaries.",
),
ArtifactContractEntry(
2,
"pypi-sdist",
"PyPI source distribution",
"PyPI / Python",
"ecli_editor-<version>.tar.gz",
False,
"Source distribution installs are developer/source installs and may be "
"minimal for non-Python F4 toolchains.",
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -t f provisioning_contract.py src | xargs -I{} ast-grep run --pattern 'class ArtifactContractEntry:
  $$$' --lang python {}
rg -nP 'full_provisioning_supported|minimal_reason' src/ecli/extensions/linters/core/provisioning_contract.py

Repository: SSobol77/ecli

Length of output: 1137


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,240p' src/ecli/extensions/linters/core/provisioning_registry.py

Repository: SSobol77/ecli

Length of output: 6323


Prefer keyword arguments for the constrained-artifact flag.

Entries 1–2 pass False and the reason as bare positionals, while the rest of the contract omits both. Using full_provisioning_supported= and minimal_reason= here makes the intent explicit and avoids accidental argument-order mistakes if ArtifactContractEntry changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ecli/extensions/linters/core/provisioning_registry.py` around lines 40 -
59, The first two ArtifactContractEntry definitions in provisioning_registry.py
are passing the constrained-artifact fields positionally, which makes the intent
unclear and fragile. Update those entries to use the named parameters
full_provisioning_supported= and minimal_reason=, matching the contract style
used elsewhere and keeping the ArtifactContractEntry calls in
core/provisioning_registry.py explicit and stable if the constructor changes.

Comment thread src/ecli/extensions/linters/core/provisioning.py
Comment on lines +167 to +180
ALLOWED_INSTALL_MECHANISMS: frozenset[str] = frozenset(
{
"artifact-policy",
"bundled-binary",
"bundled-internal",
"ecli-managed-tools",
"jar-shim",
"language-package-manager",
"nix-derivation",
"os-package-manager",
"toolchain-component",
"verified-upstream-download",
}
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive ALLOWED_* frozensets from their Literal types to eliminate manual sync.

The InstallMechanism/ProvenanceRequirement Literal types and their corresponding ALLOWED_* frozensets are manually synchronized. Adding a new mechanism or provenance value requires updating both the Literal and the frozenset — if one is forgotten, either the type checker or the runtime validator will be silently inconsistent. Use typing.get_args() to derive the frozensets from the Literals automatically.

♻️ Proposed refactor
+from typing import get_args
+
 ALLOWED_INSTALL_MECHANISMS: frozenset[str] = frozenset(
-    {
-        "artifact-policy",
-        "bundled-binary",
-        "bundled-internal",
-        "ecli-managed-tools",
-        "jar-shim",
-        "language-package-manager",
-        "nix-derivation",
-        "os-package-manager",
-        "toolchain-component",
-        "verified-upstream-download",
-    }
+    get_args(InstallMechanism)
 )

 ALLOWED_PROVENANCE_REQUIREMENTS: frozenset[str] = frozenset(
-    {
-        "artifact-entry-id",
-        "checksum-or-ecli-provenance-when-downloaded",
-        "deterministic-install-log",
-        "ecli-version",
-        "executable-permission",
-        "pinned-version-when-downloaded",
-        "source-url-when-downloaded",
-        "version-probe",
-    }
+    get_args(ProvenanceRequirement)
 )

Also applies to: 193-204

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/ecli/extensions/linters/core/registry.py` around lines 167 - 180, Derive
the runtime allowlists from the existing Literal types instead of hardcoding
them, so `ALLOWED_INSTALL_MECHANISMS` and the corresponding `ALLOWED_*`
frozensets stay in sync automatically. Update the registry logic around
`InstallMechanism` and `ProvenanceRequirement` to build the frozensets with
`typing.get_args()` from the Literal definitions, and keep the surrounding
validation code using those derived sets.

coderabbitai[bot]
coderabbitai Bot previously requested changes Jul 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/extensions/linters/test_provisioning_contract.py`:
- Around line 191-194: The
`test_evidence_filename_rejects_pathlike_artifact_ids` loop should be replaced
with `@pytest.mark.parametrize` so each path-like `artifact_id` is tested
independently. Update the `evidence_filename` test to keep the same invalid
inputs but run them as separate cases, which ensures one failure does not skip
the rest and gives clearer per-input diagnostics.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ac3bce6e-0dac-4645-bf7d-5708a454bec5

📥 Commits

Reviewing files that changed from the base of the PR and between 906b92e and c287e01.

📒 Files selected for processing (4)
  • scripts/provision_f4_linters.py
  • src/ecli/extensions/linters/core/provisioning.py
  • tests/extensions/linters/test_provisioning_contract.py
  • tests/packaging/test_f4_linter_provisioning_scripts.py

Comment on lines +191 to +194
def test_evidence_filename_rejects_pathlike_artifact_ids() -> None:
for artifact_id in ("../deb", "/tmp/deb", "deb/../rpm", r"C:\tmp\deb"):
with pytest.raises(ValueError):
evidence_filename(artifact_id)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider using @pytest.mark.parametrize for individual input isolation.

The for-loop tests four path-like inputs, but if the first one fails to raise ValueError, the remaining three are never exercised. Parametrizing would test each independently and produce clearer failure diagnostics.

♻️ Optional refactor
+@pytest.mark.parametrize(
+    "artifact_id",
+    ["../deb", "/tmp/deb", "deb/../rpm", r"C:\tmp\deb"],
+)
-def test_evidence_filename_rejects_pathlike_artifact_ids() -> None:
-    for artifact_id in ("../deb", "/tmp/deb", "deb/../rpm", r"C:\tmp\deb"):
-        with pytest.raises(ValueError):
-            evidence_filename(artifact_id)
+def test_evidence_filename_rejects_pathlike_artifact_ids(artifact_id: str) -> None:
+    with pytest.raises(ValueError):
+        evidence_filename(artifact_id)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def test_evidence_filename_rejects_pathlike_artifact_ids() -> None:
for artifact_id in ("../deb", "/tmp/deb", "deb/../rpm", r"C:\tmp\deb"):
with pytest.raises(ValueError):
evidence_filename(artifact_id)
`@pytest.mark.parametrize`(
"artifact_id",
["../deb", "/tmp/deb", "deb/../rpm", r"C:\tmp\deb"],
)
def test_evidence_filename_rejects_pathlike_artifact_ids(artifact_id: str) -> None:
with pytest.raises(ValueError):
evidence_filename(artifact_id)
🧰 Tools
🪛 ast-grep (0.44.1)

[info] 191-191: Do not hardcode temporary file or directory names
Context: "/tmp/deb"
Note: [CWE-377] Insecure Temporary File.

(hardcoded-tmp-file)

🪛 GitHub Check: SonarCloud Code Analysis

[failure] 192-192: Make sure publicly writable directories are used safely here.

See more on https://sonarcloud.io/project/issues?id=SSobol77_ecli&issues=AZ9F8-yjoX6w6Qp9-b1O&open=AZ9F8-yjoX6w6Qp9-b1O&pullRequest=125


[failure] 192-192: Make sure publicly writable directories are used safely here.

See more on https://sonarcloud.io/project/issues?id=SSobol77_ecli&issues=AZ9F8-yjoX6w6Qp9-b1P&open=AZ9F8-yjoX6w6Qp9-b1P&pullRequest=125

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/extensions/linters/test_provisioning_contract.py` around lines 191 -
194, The `test_evidence_filename_rejects_pathlike_artifact_ids` loop should be
replaced with `@pytest.mark.parametrize` so each path-like `artifact_id` is
tested independently. Update the `evidence_filename` test to keep the same
invalid inputs but run them as separate cases, which ensures one failure does
not skip the rest and gives clearer per-input diagnostics.

@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

@SSobol77 SSobol77 dismissed stale reviews from coderabbitai[bot] and coderabbitai[bot] July 9, 2026 08:41

Dismiss stale CodeRabbit change request after follow-up fixes and all required checks passing.

@SSobol77 SSobol77 merged commit ffcad05 into main Jul 9, 2026
6 checks passed
@SSobol77 SSobol77 deleted the feature/f4-linter-provisioning-installer branch July 9, 2026 08:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant