Skip to content

feat: wire F4 provisioning evidence gates into packaging#126

Merged
SSobol77 merged 2 commits into
mainfrom
feature/f4-linter-artifact-wiring
Jul 9, 2026
Merged

feat: wire F4 provisioning evidence gates into packaging#126
SSobol77 merged 2 commits into
mainfrom
feature/f4-linter-artifact-wiring

Conversation

@SSobol77

@SSobol77 SSobol77 commented Jul 9, 2026

Copy link
Copy Markdown
Owner

Summary

This PR wires F4 linter provisioning evidence gates into ECLI packaging and release flows.

It adds a shared packaging helper for artifact-specific F4 evidence generation and verification, then connects that helper to the existing packaging scripts and release verification path.

This PR is intentionally scoped as evidence-gate wiring. It does not claim concrete non-dry-run installer/download/bundling implementation for external linter binaries. Real non-dry-run provisioning remains explicit opt-in via ECLI_F4_LINTER_PROVISIONING_MODE=provision and is left for the next implementation PR.

What changed

  • Added scripts/f4_linter_packaging.py

    • canonical artifact policy map
    • safe target/evidence path construction
    • explicit argv construction, no shell=True
    • non-interactive env controls for mode/profile/include/exclude/selection JSON
    • release-level all-artifacts evidence helper
  • Wired F4 evidence hooks into packaging scripts:

    • DEB
    • RPM
    • openSUSE RPM wrapper
    • Arch PKGBUILD
    • Slackware TXZ
    • AppImage
    • Linux PyInstaller / tarball
    • FreeBSD pkg / ports-chroot
    • macOS app / DMG
    • Windows portable EXE / NSIS
    • Docker DEB helper
    • Docker RPM helper
    • PyPI constrained policy surface
  • Extended release verification:

    • scripts/verify_release_assets.py --f4-evidence-dir <dir>
    • release workflow now generates and verifies all 21 F4 evidence files
    • F4 verifier failure propagates as release verification failure
  • Added packaging tests:

    • exact 21 canonical artifact policy coverage
    • 17 full-provisioning-hook artifacts
    • 2 constrained-minimal PyPI artifacts
    • 2 Nix policy artifacts
    • no GitHub-generated Source ZIP/TAR counted
    • command construction and no-shell subprocess behavior
    • non-interactive custom selection env handling
    • Docker/openSUSE env wiring
    • release verifier legacy behavior without F4 evidence
    • release verifier F4 gate invocation and failure propagation

Artifact policy

Full evidence hook:

  • 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
  • docker-deb-helper
  • docker-rpm-helper
  • gha-release-contract

Constrained/minimal:

  • pypi-wheel
  • pypi-sdist

Nix policy:

  • nix-flake
  • nixos-package

Explicit non-scope

This PR does not implement concrete external linter binary installation, downloading, vendoring, or bundling.

No changes were made to:

  • F4 UI
  • keybindings
  • provider/parser runtime behavior
  • TextMate/theme code
  • src/ecli/core/Ecli.py
  • src/ecli/ui/
  • src/ecli/diagnostics/service.py

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

Observed results:

ruff check: passed
ruff format --check: 197 files already formatted
runtime import guard: passed
tests/packaging/test_f4_linter_packaging_hooks.py: 16 passed
tests/packaging: 371 passed
tests/extensions/linters: 226 passed
tests/docs: 18 passed

Safety checks:

No shell=True in new helper/tests.
No hardcoded /tmp/tempdir/mktemp in new helper/tests.
No F4 UI/provider/parser/core diagnostics scope changes.
Generated __pycache__/pyc files were removed before commit.

Next PR

Next implementation PR should handle concrete non-dry-run installer/download/bundling mechanisms per artifact family and package-manager policy.

Summary by CodeRabbit

  • New Features

    • Added broader release packaging checks across multiple build outputs to capture and verify additional evidence during release creation.
    • Release verification can now include an extra evidence check when available.
  • Bug Fixes

    • Improved release asset validation so missing or invalid evidence is caught earlier.
    • Strengthened packaging flows to stop on evidence-generation failures instead of reporting success.

@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: 42 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: ef73d3c6-0852-471f-8736-ca5181d71b36

📥 Commits

Reviewing files that changed from the base of the PR and between bd91d77 and eaaa2ca.

📒 Files selected for processing (3)
  • scripts/build-and-package-windows.ps1
  • scripts/verify_release_assets.py
  • tests/packaging/test_f4_linter_packaging_hooks.py
📝 Walkthrough

Walkthrough

This PR introduces F4 linter provisioning evidence tracking across the packaging pipeline. A new scripts/f4_linter_packaging.py module centralizes provisioning/verification command building and artifact policy resolution. All platform build/package scripts (Arch, deb, RPM, FreeBSD, macOS, Windows, Docker, AppImage, PyInstaller) invoke this evidence hook and fail on non-success. The release workflow and verify_release_assets.py gain a new evidence-verification gate, and provisioning.py adds validation for constrained-profile evidence payloads. New tests cover the packaging module's behavior.

Changes

F4 Linter Provisioning Evidence

Layer / File(s) Summary
F4 linter packaging core module
scripts/f4_linter_packaging.py
New module defines provisioning mode/profile types, artifact policy registry, path helpers, environment-driven selection parsing, provisioning/verification command builders, and orchestration functions for single, multi-artifact, and release-wide evidence recording.
Constrained-profile evidence payload validation
src/ecli/extensions/linters/core/provisioning.py
Adds _payload_constrained_profile_errors requiring full_profile_complete=false and a non-empty custom_profile_reason for constrained artifacts, wired into verify_evidence_payload.
Python packaging scripts wired to F4 evidence hooks
scripts/build_and_package_arch.py, scripts/build_and_package_deb.py, scripts/build_and_package_freebsd.py, scripts/build_and_package_macos.py, scripts/build_and_package_opensuse_rpm.py, scripts/build_and_package_rpm.py, scripts/build_and_package_slackware.py, scripts/build_docker.py, scripts/build_freebsd_pkg.py, scripts/build_freebsd_port.py, scripts/build_pyinstaller_linux.py, scripts/package_appimage.py, scripts/publish_pypi.py
Each script imports the new evidence helpers and, after its build/verification step, records or reports F4 provisioning evidence, aborting with EXIT_ERROR on failure.
Windows PowerShell F4 evidence hook
scripts/build-and-package-windows.ps1
Adds Split-F4ToolList and Invoke-F4LinterEvidenceHook functions, invoked for windows-portable-exe and windows-nsis-installer artifacts after SHA256 sidecar generation.
Docker/OpenSUSE artifact ID environment wiring
docker/build-linux-deb.Dockerfile, docker/build-linux-rpm.Dockerfile, scripts/build_and_package_opensuse_rpm.py
Adds ECLI_F4_LINTER_EXTRA_ARTIFACT_IDS/ECLI_F4_LINTER_ARTIFACT_ID environment variables selecting the relevant artifact IDs.
Release workflow and asset verification gate
.github/workflows/release.yml, scripts/verify_release_assets.py
Release workflow provisions/copies F4 evidence for all assets and passes --f4-evidence-dir; verify_release_assets.py runs the F4 verification command after successful asset checks.
F4 packaging hook tests
tests/packaging/test_f4_linter_packaging_hooks.py
New test module covering policy map contents, command construction, environment parsing, subprocess sequencing, script wiring, release verifier behavior, and evidence payload validation.

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

Possibly related PRs

  • SSobol77/ecli#85: Both PRs modify scripts/build_and_package_freebsd.py around the verify_runtime.py call path, with this PR extending it to add F4 evidence recording.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 44.07% 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: wiring F4 provisioning evidence gates into packaging flows.
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-artifact-wiring

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: 3

🤖 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/build_pyinstaller_linux.py`:
- Line 41: The unconditional F4 hook import in build_pyinstaller_linux.py is
affecting shared packaging flows like package_appimage and
build_and_package_slackware. Move the
run_or_record_f4_linter_provisioning_for_artifacts step out of the shared helper
and into the release-level entry point, or guard it with a top-level
flag/sentinel so only the intended PyInstaller path runs it. Update the
build_pyinstaller_linux helper so its behavior stays reusable without triggering
duplicate evidence work for linux-pyinstaller and linux-tarball.

In `@scripts/build-and-package-windows.ps1`:
- Around line 57-104: The Invoke-F4LinterEvidenceHook function assigns to
$profile, which collides with the PowerShell automatic variable and is flagged
by PSAvoidAssignmentToAutomaticVariable. Rename that local variable to a
non-reserved name such as $linterProfile, then update the value selection logic
and the $provisionArgs array entry that passes the profile option so the
behavior stays unchanged.

In `@tests/packaging/test_f4_linter_packaging_hooks.py`:
- Around line 178-187: Add a parallel path traversal rejection test for
build_provisioning_command, since it uses the same canonical_artifact_entry_id
validation as build_verification_command but lacks direct coverage. Reuse the
existing test pattern in
test_invalid_artifact_id_is_rejected_before_command_construction, and invoke
build_provisioning_command with a "../deb" artifact_entry_id plus a temporary
provisioning/evidence path, asserting it raises ValueError before any command
construction occurs.
🪄 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: 74a03503-6098-4fe3-8100-29a3ce9b36b9

📥 Commits

Reviewing files that changed from the base of the PR and between ffcad05 and bd91d77.

📒 Files selected for processing (21)
  • .github/workflows/release.yml
  • docker/build-linux-deb.Dockerfile
  • docker/build-linux-rpm.Dockerfile
  • scripts/build-and-package-windows.ps1
  • scripts/build_and_package_arch.py
  • scripts/build_and_package_deb.py
  • scripts/build_and_package_freebsd.py
  • scripts/build_and_package_macos.py
  • scripts/build_and_package_opensuse_rpm.py
  • scripts/build_and_package_rpm.py
  • scripts/build_and_package_slackware.py
  • scripts/build_docker.py
  • scripts/build_freebsd_pkg.py
  • scripts/build_freebsd_port.py
  • scripts/build_pyinstaller_linux.py
  • scripts/f4_linter_packaging.py
  • scripts/package_appimage.py
  • scripts/publish_pypi.py
  • scripts/verify_release_assets.py
  • src/ecli/extensions/linters/core/provisioning.py
  • tests/packaging/test_f4_linter_packaging_hooks.py

import sys
from pathlib import Path

from f4_linter_packaging import run_or_record_f4_linter_provisioning_for_artifacts

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -nP --type=py -B2 -A2 'build_pyinstaller_linux\.py' scripts/

Repository: SSobol77/ecli

Length of output: 1637


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== callers of build_pyinstaller_linux.py =='
rg -n "build_pyinstaller_linux\.py" scripts/ || true

echo
echo '== relevant section of scripts/build_pyinstaller_linux.py =='
sed -n '1,220p' scripts/build_pyinstaller_linux.py

echo
echo '== relevant sections of scripts/package_appimage.py and scripts/build_and_package_slackware.py =='
sed -n '90,130p' scripts/package_appimage.py
echo '---'
sed -n '145,170p' scripts/build_and_package_slackware.py

Repository: SSobol77/ecli

Length of output: 7663


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== definition of run_or_record_f4_linter_provisioning_for_artifacts =='
rg -n "def run_or_record_f4_linter_provisioning_for_artifacts|run_or_record_f4_linter_provisioning_for_artifacts\(" -S .

echo
echo '== build_pyinstaller_linux.py around the hook =='
sed -n '120,170p' scripts/build_pyinstaller_linux.py

echo
echo '== callers of build_pyinstaller_linux.py =='
rg -n "build_pyinstaller_linux\.py" scripts/

Repository: SSobol77/ecli

Length of output: 2167


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== f4 helper definition =='
sed -n '430,540p' scripts/f4_linter_packaging.py

echo
echo '== build_and_package_deb.py around F4 call =='
sed -n '320,360p' scripts/build_and_package_deb.py

echo
echo '== build_and_package_rpm.py around F4 call =='
sed -n '420,460p' scripts/build_and_package_rpm.py

echo
echo '== build_and_package_macos.py around F4 call =='
sed -n '560,600p' scripts/build_and_package_macos.py

Repository: SSobol77/ecli

Length of output: 5644


Move the F4 hook out of the shared PyInstaller build helper

build_pyinstaller_linux.py is reused by scripts/package_appimage.py and scripts/build_and_package_slackware.py, so the unconditional F4 step now runs for those packaging flows too and duplicates linux-pyinstaller/linux-tarball evidence work. Gate it behind a top-level flag/sentinel or move it to the release-level entry point.

🤖 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/build_pyinstaller_linux.py` at line 41, The unconditional F4 hook
import in build_pyinstaller_linux.py is affecting shared packaging flows like
package_appimage and build_and_package_slackware. Move the
run_or_record_f4_linter_provisioning_for_artifacts step out of the shared helper
and into the release-level entry point, or guard it with a top-level
flag/sentinel so only the intended PyInstaller path runs it. Update the
build_pyinstaller_linux helper so its behavior stays reusable without triggering
duplicate evidence work for linux-pyinstaller and linux-tarball.

Comment thread scripts/build-and-package-windows.ps1
Comment on lines +178 to +187
def test_invalid_artifact_id_is_rejected_before_command_construction(
f4_packaging: ModuleType,
tmp_path: Path,
) -> None:
with pytest.raises(ValueError):
f4_packaging.build_verification_command(
tmp_path,
artifact_entry_id="../deb",
evidence_dir=tmp_path / "evidence",
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add path traversal test for build_provisioning_command too.

The existing test validates that build_verification_command rejects "../deb", but build_provisioning_command uses the same canonical_artifact_entry_id validation and is not independently tested for path traversal rejection. Adding a parallel test closes this security-relevant coverage gap.

♻️ Proposed additional test
 def test_invalid_artifact_id_is_rejected_before_command_construction(
     f4_packaging: ModuleType,
     tmp_path: Path,
 ) -> None:
     with pytest.raises(ValueError):
         f4_packaging.build_verification_command(
             tmp_path,
             artifact_entry_id="../deb",
             evidence_dir=tmp_path / "evidence",
         )
+
+    with pytest.raises(ValueError):
+        f4_packaging.build_provisioning_command(
+            tmp_path,
+            artifact_entry_id="../deb",
+            target_dir=tmp_path / "target",
+            evidence_dir=tmp_path / "evidence",
+        )
📝 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_invalid_artifact_id_is_rejected_before_command_construction(
f4_packaging: ModuleType,
tmp_path: Path,
) -> None:
with pytest.raises(ValueError):
f4_packaging.build_verification_command(
tmp_path,
artifact_entry_id="../deb",
evidence_dir=tmp_path / "evidence",
)
def test_invalid_artifact_id_is_rejected_before_command_construction(
f4_packaging: ModuleType,
tmp_path: Path,
) -> None:
with pytest.raises(ValueError):
f4_packaging.build_verification_command(
tmp_path,
artifact_entry_id="../deb",
evidence_dir=tmp_path / "evidence",
)
with pytest.raises(ValueError):
f4_packaging.build_provisioning_command(
tmp_path,
artifact_entry_id="../deb",
target_dir=tmp_path / "target",
evidence_dir=tmp_path / "evidence",
)
🤖 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/packaging/test_f4_linter_packaging_hooks.py` around lines 178 - 187,
Add a parallel path traversal rejection test for build_provisioning_command,
since it uses the same canonical_artifact_entry_id validation as
build_verification_command but lacks direct coverage. Reuse the existing test
pattern in test_invalid_artifact_id_is_rejected_before_command_construction, and
invoke build_provisioning_command with a "../deb" artifact_entry_id plus a
temporary provisioning/evidence path, asserting it raises ValueError before any
command construction occurs.

@sonarqubecloud

sonarqubecloud Bot commented Jul 9, 2026

Copy link
Copy Markdown

@SSobol77 SSobol77 merged commit 163e14b into main Jul 9, 2026
6 checks passed
@SSobol77 SSobol77 deleted the feature/f4-linter-artifact-wiring branch July 9, 2026 12:32
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