Skip to content

feat: add Linux F4 provisioning policy manifests#127

Merged
SSobol77 merged 2 commits into
mainfrom
feature/f4-linux-provisioning-policy
Jul 9, 2026
Merged

feat: add Linux F4 provisioning policy manifests#127
SSobol77 merged 2 commits into
mainfrom
feature/f4-linux-provisioning-policy

Conversation

@SSobol77

@SSobol77 SSobol77 commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

Add Linux F4 provisioning policy manifests and fail-closed Linux provisioning behavior.

This PR starts the concrete Linux provisioning layer after the provider-neutral provisioning framework and artifact evidence gates. It does not vendor binaries, run package managers, download upstream tools, or claim that all external Full-required linters are already fully bundled.

Instead, it adds deterministic Linux manifest generation, canonical policy validation, package-manager dependency metadata, Docker helper inheritance metadata, declarative Nix policy evidence, and explicit release-blocking blocked-missing-provenance entries where audited pins/checksums/provenance are still missing.

Linux artifact scope

Covered Linux release/artifact surfaces:

  • linux-pyinstaller
  • linux-tarball
  • appimage
  • deb
  • rpm
  • opensuse-rpm
  • arch-pkgbuild
  • slackware-txz
  • docker-deb-helper
  • docker-rpm-helper
  • nix-flake
  • nixos-package

Out of scope for this PR:

  • PyPI wheel/sdist
  • FreeBSD pkg / ports-chroot
  • macOS app / DMG
  • Windows portable EXE / NSIS
  • UI/keybindings/provider/parser runtime
  • TextMate/theme rendering

What changed

  • Added scripts/f4_linter_linux_provisioning.py

    • Linux artifact scope helpers
    • Linux Full-required tool policy matrix
    • deterministic manifest generation
    • manifest writer
    • manifest verifier
    • package-manager dependency-name helper
    • self-contained artifact tools layout helper
    • Docker helper inheritance metadata
    • Nix declarative policy metadata
  • Integrated Linux manifest generation into scripts/provision_f4_linters.py

    • Linux artifacts now write f4-linux-tools.json
    • --mode dry-run remains safe and non-installing
    • --mode verify-only remains non-installing
    • --mode provision fails closed when Linux policy contains release-blocking missing-provenance entries
  • Updated scripts/f4_linter_packaging.py

    • Clarifies that shared packaging evidence wiring remains separate from Linux-specific policy manifests
  • Added tests/packaging/test_f4_linter_linux_provisioning.py

    • exact Linux artifact scope coverage
    • Full-required tool coverage across all Linux artifacts
    • deterministic manifest generation
    • manifest path containment
    • manifest writer round-trip
    • invalid/non-Linux artifact rejection
    • package-manager policy checks
    • self-contained artifact layout checks
    • Docker helper policy inheritance checks
    • Nix declarative-only checks
    • provision/dry-run/verify-only behavior checks
    • negative verifier tests for tampered mechanisms, package names, release-blocking state, duplicates, wrong counts, and path escapes

Manifest verification hardening

The Linux manifest verifier validates content against the canonical Linux policy matrix.

It rejects:

  • wrong full_required_tool_count
  • top-level release_blocking mismatch
  • duplicate selected_tools
  • duplicate tools[].tool_id
  • unknown selected tools
  • unexpected tools for an artifact
  • mechanism drift from canonical policy
  • package-name drift from canonical policy
  • per-tool release_blocking drift
  • missing blocker reasons for blocked policies
  • per-tool artifact mismatch
  • manifest/tool path escapes

Policy behavior

Package-manager artifacts:

  • DEB/RPM/openSUSE/Arch/Slackware record explicit OS package-manager mappings where currently defined.
  • cargo-clippy is represented as a Rust toolchain component.
  • Tools without safe distro package mapping or pinned/checksummed artifact-managed provenance are marked blocked-missing-provenance.

Self-contained Linux artifacts:

  • linux-pyinstaller, linux-tarball, and appimage get deterministic artifact-managed tools layout and manifest paths.
  • External tools remain release-blocking until audited pins/checksums/provenance exist.

Docker helper artifacts:

Nix artifacts:

  • nix-flake and nixos-package use declarative nix-derivation policy.
  • No imperative package-manager or upstream download path is introduced.

Explicit non-scope

This PR does not:

  • vendor external linter binaries
  • download upstream tools
  • run package managers in tests
  • implement final audited bundle/download/install flows
  • weaken the Full-required baseline
  • reclassify Full-required tools as optional

Validation

Passed locally / by agent report:

git diff --check
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/packaging/test_f4_linter_linux_provisioning.py
uv run pytest -q tests/packaging
uv run pytest -q tests/extensions/linters
uv run pytest -q tests/docs

Observed results:

tests/packaging/test_f4_linter_linux_provisioning.py: 20 passed
tests/packaging: 393 passed
tests/extensions/linters: 226 passed
tests/docs: 18 passed

Safety checks:

No shell=True implementation use.
No network/package-manager execution in tests.
No hardcoded /tmp in new tests.
No vendored binaries/downloads/generated archives/logs/caches/pycache committed.
No UI/provider/parser/TextMate/theme changes.

Next work

A follow-up PR should add audited version pins, checksums, source provenance, and approved install/bundle implementations for currently blocked external Full-required Linux tools.

Summary by CodeRabbit

  • New Features

    • Added Linux provisioning support for F4 linters, including artifact-specific tool manifests and policy checks.
    • Linux runs now record per-tool evidence and can detect release-blocking conditions during provisioning.
  • Bug Fixes

    • Improved validation to catch invalid tool policies, duplicate entries, path escapes, and mismatched manifest data.
    • Linux provisioning now fails closed when required provenance is missing.
  • Tests

    • Added coverage for manifest generation, verification, Linux artifact behavior, and provisioning CLI modes.

@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: 43 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: 1088046d-748d-47a3-a695-a54d3c798c47

📥 Commits

Reviewing files that changed from the base of the PR and between 0c5670c and 7ccab74.

📒 Files selected for processing (2)
  • scripts/f4_linter_linux_provisioning.py
  • tests/packaging/test_f4_linter_linux_provisioning.py
📝 Walkthrough

Walkthrough

Adds a new Linux F4 linter provisioning module that models tool provisioning policies (mechanisms, package mappings, release-blocking rules) and builds/writes/verifies deterministic JSON manifests. Integrates this into the provisioning CLI, updates related documentation, and adds a comprehensive test suite.

Changes

Linux Provisioning Manifest

Layer / File(s) Summary
Policy constants and data model
scripts/f4_linter_linux_provisioning.py
Defines mechanism/artifact family types, LinuxToolProvisioningPolicy NamedTuple, and repo/module loading plus path-safety helpers.
Artifact ID utilities and policy construction
scripts/f4_linter_linux_provisioning.py
Lists/filters Linux artifact IDs, loads tool contracts, maps contracts to mechanisms (os-package-manager, toolchain-component, Nix, blocked-missing-provenance), and derives package dependency names.
Manifest assembly and writer
scripts/f4_linter_linux_provisioning.py
Builds manifest path/tool directory helpers, assembles manifests with selected-tool filtering and release_blocking computation, and writes validated sorted JSON.
Manifest loader and verification
scripts/f4_linter_linux_provisioning.py
Loads manifests from file/mapping and validates schema, summary invariants, tool selection consistency, per-tool fields, and directory containment.
CLI integration and docs
scripts/provision_f4_linters.py, scripts/f4_linter_packaging.py
Wires manifest build/write/verify into evidence writing per artifact, adds artifact-scoped directory helpers, and updates module docstrings.
Test suite for policy, manifest, and CLI
tests/packaging/test_f4_linter_linux_provisioning.py
Adds fixtures and tests for artifact scope, policy coverage, manifest determinism/round-trip, verifier rejections, artifact-family behavior, and CLI provision/dry-run/verify-only modes.

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

Sequence Diagram(s)

sequenceDiagram
  participant CLI as provision_f4_linters.py
  participant Helper as _write_linux_manifest_if_needed
  participant Builder as build_linux_provisioning_manifest
  participant Writer as write_linux_provisioning_manifest
  participant Verifier as verify_linux_provisioning_manifest
  CLI->>Helper: process artifact evidence
  Helper->>Builder: build manifest from selected tools
  Builder-->>Helper: manifest dict
  Helper->>Writer: write manifest JSON
  Helper->>Verifier: verify written manifest
  Verifier-->>Helper: list of errors
  Helper-->>CLI: failed flag (verification/release_blocking)
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding Linux F4 provisioning policy manifests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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-linux-provisioning-policy

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 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 `@scripts/f4_linter_linux_provisioning.py`:
- Around line 756-794: The _tool_item_errors function is too complex because it
contains mechanism-specific branching for os-package-manager and
blocked-missing-provenance. Extract that validation into a small helper such as
_tool_mechanism_errors and have _tool_item_errors call it with
errors.extend(...) so the main function stays flat and behavior remains
unchanged. Keep the existing checks and messages for tool_id, package_names,
release_blocking, and blocker_reason in the new helper, and use the existing
symbols _tool_item_errors and LINUX_ALLOWED_MECHANISMS to locate the code.
🪄 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: 4e82e553-ad6e-49f0-8fb8-5e3ccd561d05

📥 Commits

Reviewing files that changed from the base of the PR and between 163e14b and 0c5670c.

📒 Files selected for processing (4)
  • scripts/f4_linter_linux_provisioning.py
  • scripts/f4_linter_packaging.py
  • scripts/provision_f4_linters.py
  • tests/packaging/test_f4_linter_linux_provisioning.py

Comment on lines +756 to +794
def _tool_item_errors(
item: Mapping[str, Any],
manifest: Mapping[str, Any],
expected_policy: LinuxToolProvisioningPolicy | None,
) -> list[str]:
prefix = str(item.get("tool_id", "<unknown>"))
errors: list[str] = []
tool_id = item.get("tool_id")
if not isinstance(tool_id, str) or not tool_id:
errors.append(f"{prefix}: missing tool_id")
item_artifact_id = item.get("artifact_entry_id")
if item_artifact_id is not None and item_artifact_id != manifest.get(
"artifact_entry_id"
):
errors.append(
f"{prefix}: artifact_entry_id differs from manifest artifact_entry_id"
)
mechanism = item.get("mechanism")
if mechanism not in LINUX_ALLOWED_MECHANISMS:
errors.append(f"{prefix}: invalid Linux mechanism {mechanism!r}")
if expected_policy is not None:
errors.extend(_tool_policy_mismatch_errors(prefix, item, expected_policy))
if (
not isinstance(item.get("executable_names"), list)
or not item["executable_names"]
):
errors.append(f"{prefix}: missing executable_names")
if not isinstance(item.get("version_probe"), list) or not item["version_probe"]:
errors.append(f"{prefix}: missing version_probe")
if mechanism == "os-package-manager" and not item.get("package_names"):
errors.append(f"{prefix}: os-package-manager policy requires package_names")
if mechanism == "blocked-missing-provenance":
if item.get("release_blocking") is not True:
errors.append(f"{prefix}: blocked policy must be release_blocking")
reason = item.get("blocker_reason")
if not isinstance(reason, str) or not reason.strip():
errors.append(f"{prefix}: blocked policy requires blocker_reason")
errors.extend(_tool_path_errors(prefix, item, manifest))
return errors

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

Reduce cognitive complexity of _tool_item_errors to clear the SonarCloud CI gate.

SonarCloud reports this function at cognitive complexity 18 (limit 15) as a failing check. Extracting the mechanism-specific validation (the os-package-manager / blocked-missing-provenance branches) into a small helper flattens the branching and clears the gate without behavior change.

♻️ Suggested extraction
+def _tool_mechanism_errors(prefix: str, item: Mapping[str, Any]) -> list[str]:
+    errors: list[str] = []
+    mechanism = item.get("mechanism")
+    if mechanism == "os-package-manager" and not item.get("package_names"):
+        errors.append(f"{prefix}: os-package-manager policy requires package_names")
+    if mechanism == "blocked-missing-provenance":
+        if item.get("release_blocking") is not True:
+            errors.append(f"{prefix}: blocked policy must be release_blocking")
+        reason = item.get("blocker_reason")
+        if not isinstance(reason, str) or not reason.strip():
+            errors.append(f"{prefix}: blocked policy requires blocker_reason")
+    return errors

Then replace the inline os-package-manager / blocked-missing-provenance blocks in _tool_item_errors with errors.extend(_tool_mechanism_errors(prefix, item)).

🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[failure] 756-756: Refactor this function to reduce its Cognitive Complexity from 18 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=SSobol77_ecli&issues=AZ9HRfmJ0Ru9QxJ5olBJ&open=AZ9HRfmJ0Ru9QxJ5olBJ&pullRequest=127

🤖 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 `@scripts/f4_linter_linux_provisioning.py` around lines 756 - 794, The
_tool_item_errors function is too complex because it contains mechanism-specific
branching for os-package-manager and blocked-missing-provenance. Extract that
validation into a small helper such as _tool_mechanism_errors and have
_tool_item_errors call it with errors.extend(...) so the main function stays
flat and behavior remains unchanged. Keep the existing checks and messages for
tool_id, package_names, release_blocking, and blocker_reason in the new helper,
and use the existing symbols _tool_item_errors and LINUX_ALLOWED_MECHANISMS to
locate the code.

Source: Linters/SAST tools

@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

@SSobol77 SSobol77 merged commit c083b19 into main Jul 9, 2026
4 checks passed
@SSobol77 SSobol77 deleted the feature/f4-linux-provisioning-policy branch July 9, 2026 14:48
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