Skip to content

Document staging helpers and restructure tests#193

Open
leynos wants to merge 6 commits intocodex/implement-network-and-command-functionsfrom
codex/action-unresolved-comments-on-pr-185
Open

Document staging helpers and restructure tests#193
leynos wants to merge 6 commits intocodex/implement-network-and-command-functionsfrom
codex/action-unresolved-comments-on-pr-185

Conversation

@leynos
Copy link
Owner

@leynos leynos commented Oct 10, 2025

Summary

  • expand staging helper docstrings, guard against reserved output collisions, and tidy CLI fallbacks
  • document the Windows path normalisation script and consume direct staging outputs in the release workflow
  • split the Python staging tests into dedicated modules with shared fixtures for config, module, and staging behaviours
  • add a regression test ensuring reserved staging outputs raise a StageError

Testing

  • make fmt
  • make lint
  • make test

https://chatgpt.com/codex/tasks/task_e_68e85138f48883229e8082f03b90e95e

Summary by Sourcery

Implement a unified staging workflow for release assets by introducing a configurable staging helper package, documenting its usage, refactoring existing scripts for optional dependencies and fallback parsing, and wiring it into GitHub Actions with comprehensive testing and design documentation.

New Features:

  • Add a generic, TOML-configurable staging helper package with CLI and GitHub Actions composite support for release artefacts

Bug Fixes:

  • Guard against collisions with reserved GitHub Actions output keys in the staging helper

Enhancements:

  • Refactor the release uploader script to decouple optional cyclopts and plumbum dependencies and add an argparse fallback

CI:

  • Replace ad-hoc staging steps in release.yml with the new staging composite action and introduce target_key matrix entries

Documentation:

  • Expand docstrings and update design docs (including an ER diagram) to describe the new staging config schema and Windows path normalisation

Tests:

  • Restructure Python staging tests into dedicated modules with shared fixtures and add regression tests for reserved outputs and Windows-specific path handling

Ensure the staging pipeline raises a StageError when a user defines a custom output that collides with the built-in artifact exports.
@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Oct 10, 2025

Reviewer's Guide

This PR transforms the staging helpers into a fully self-contained GitHub composite action, enriches documentation and docstrings, modularizes the CLI uploader’s dependency logic, refactors the release workflow to consume the new action and normalization script, and reorganizes and extends the Python test suite with focused modules and shared fixtures (including a regression for reserved output collisions).

Entity relationship diagram for the staging configuration schema (ER diagram)

erDiagram
  COMMON {
    string bin_name
    string dist_dir
    string checksum_algorithm
    string staging_dir_template
    ArtefactConfig[] artefacts
  }
  TARGETS {
    string platform
    string arch
    string target
    string bin_ext
    string staging_dir_template
    ArtefactConfig[] artefacts
  }
  ArtefactConfig {
    string source
    boolean required
    string output
    string destination
    string[] alternatives
  }
  COMMON ||--o{ TARGETS : "has targets"
  COMMON ||--o{ ArtefactConfig : "has artefacts"
  TARGETS ||--o{ ArtefactConfig : "has artefacts"
Loading

Class diagram for the new and refactored staging helper types

classDiagram
  class ArtefactConfig {
    +str source
    +bool required
    +str~ output
    +str~ destination
    +list~ alternatives
  }
  class StagingConfig {
    +Path workspace
    +str bin_name
    +str dist_dir
    +str checksum_algorithm
    +list~ artefacts
    +str platform
    +str arch
    +str target
    +str bin_ext
    +str staging_dir_template
    +str~ target_key
    +staging_dir() Path
    +staging_dir_name str
    +as_template_context() dict
  }
  class StageResult {
    +Path staging_dir
    +list~ staged_artefacts
    +dict outputs
    +dict checksums
  }
  class StageError {
    <<exception>>
  }
  ArtefactConfig <.. StagingConfig : contains
  StagingConfig <.. StageResult : used by
  StageError <|-- RuntimeError
Loading

File-Level Changes

Change Details Files
Introduce a standalone staging composite GitHub Action with dedicated helper library
  • Added action.yml for composite action inputs/outputs
  • Created CLI entrypoint script (stage.py) using cyclopts
  • Built a stage_common package (config, environment, staging logic, template_utils, glob_utils, fs_utils, checksum_utils, github_output, errors)
  • Updated design docs and examples to reference the generic action and its schema
.github/actions/stage/action.yml
.github/actions/stage/scripts/stage.py
.github/actions/stage/scripts/stage_common/config.py
.github/actions/stage/scripts/stage_common/environment.py
.github/actions/stage/scripts/stage_common/github_output.py
.github/actions/stage/scripts/stage_common/errors.py
.github/actions/stage/scripts/stage_common/staging.py
.github/actions/stage/scripts/stage_common/template_utils.py
.github/actions/stage/scripts/stage_common/glob_utils.py
.github/actions/stage/scripts/stage_common/checksum_utils.py
.github/actions/stage/scripts/stage_common/fs_utils.py
Refactor upload_release_assets CLI to use dynamic loaders and argparse fallback
  • Extracted Cyclopts/Plumbum imports into scripts/_release_upload_deps.py with availability flags
  • Replaced direct cyclopts/plumbum imports in upload_release_assets.py
  • Injected Parameter, App, and command error types via the loader
  • Added a fallback argparse‐based CLI path when Cyclopts is unavailable
  • Cleaned up type annotations and removed TYPE_CHECKING stubs
scripts/upload_release_assets.py
scripts/_release_upload_deps.py
Expand and tidy documentation and docstrings across staging helpers
  • Enriched docstrings in all staging helper modules
  • Documented Windows path normalization script usage
  • Revised netsuke-design.md to describe the generic composite action, TOML schema, and workflow changes
docs/netsuke-design.md
.github/actions/stage/scripts/stage_common/config.py
.github/actions/stage/scripts/stage_common/staging.py
.github/workflows/scripts/normalise_windows_paths.py
Refactor release workflow to consume the new composite action and normalization script
  • Swapped manual bash stage_windows and stage_macos steps for uses: ./.github/actions/stage
  • Passed config-file and target_key inputs to the composite action
  • Replaced manual PureWindowsPath logic with a dedicated Python normalization script
  • Updated ARTEFACT_MAP usage and environment variables
.github/workflows/release.yml
.github/workflows/scripts/normalise_windows_paths.py
Restructure and expand Python tests into focused modules with shared fixtures
  • Split tests into tests_python/{test_stage_common_config,test_stage_common_staging,test_stage_cli,test_stage_action}.py
  • Created conftest.py and stage_test_helpers.py for shared fixtures and helpers
  • Added comprehensive tests for config loader, staging logic, CLI entrypoint, composite action, and Windows glob/drive handling
tests_python/conftest.py
tests_python/stage_test_helpers.py
tests_python/test_stage_common_config.py
tests_python/test_stage_common_staging.py
tests_python/test_stage_cli.py
tests_python/test_stage_action.py
Guard against reserved output collisions in the staging pipeline
  • Added a reserved_keys check after staging to raise StageError on output key collisions
  • Wrote a regression test to ensure colliding custom output labels trigger StageError
.github/actions/stage/scripts/stage_common/staging.py
tests_python/test_stage_common_staging.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Summary by CodeRabbit

  • New Features

    • Unified, cross-platform release staging with consistent artefact layout and emitted checksums.
    • More reliable Windows/macOS packaging via a reusable workflow action.
  • Documentation

    • Updated release flow to a declarative, TOML-driven staging process with diagrams and clarified platform steps.
  • Refactor

    • Replaced bespoke staging scripts with a composite action and a Python CLI, standardising outputs and error handling.
    • Improved release upload CLI with optional dependency fallbacks and stricter input validation.
  • Tests

    • Added comprehensive tests for staging config, CLI, globbing, outputs, and action wiring.
  • Chores

    • Introduced automated linting, type checks, and Python tests in CI.

Walkthrough

Introduce a new composite GitHub Action to stage release artefacts via a TOML-driven Python CLI. Add a staging helper package with config, globbing, checksums, outputs, and error handling. Replace bespoke Windows/macOS staging scripts with the new action and config. Refactor release asset uploader to optionalise deps. Add tests, docs, and workflow updates.

Changes

Cohort / File(s) Summary
Composite Action: staging
.github/actions/stage/action.yml
Add composite action to install uv, verify availability, and run staging CLI with inputs (config-file, target); expose standardised outputs (paths, maps).
Staging CLI entry
.github/actions/stage/scripts/stage.py
Add cyclopts-based CLI that loads TOML config, requires GITHUB_OUTPUT, runs staging, handles errors, and logs results.
Staging helper package: public API
.github/actions/stage/scripts/stage_common/__init__.py
Re-export staging APIs: config, env, errors, staging core, and constants.
Staging helper package: utilities
.github/actions/stage/scripts/stage_common/checksum_utils.py, .github/actions/stage/scripts/stage_common/config.py, .github/actions/stage/scripts/stage_common/environment.py, .github/actions/stage/scripts/stage_common/errors.py, .github/actions/stage/scripts/stage_common/fs_utils.py, .github/actions/stage/scripts/stage_common/github_output.py, .github/actions/stage/scripts/stage_common/glob_utils.py, .github/actions/stage/scripts/stage_common/staging.py, .github/actions/stage/scripts/stage_common/template_utils.py
Add checksum writing, TOML config loader, env var path fetch, StageError, safe destination path, GitHub output writer, glob/matching utilities, core staging pipeline, and template rendering. Define public exports.
Release workflow migration
.github/workflows/release.yml
Switch platform jobs to the new staging action with target_key; consume standardised outputs; add Windows path normalisation step.
Windows path normalisation
.github/workflows/scripts/normalise_windows_paths.py
Add script to read ARTEFACT_MAP, validate keys, convert to Windows paths, and write GHA outputs.
Remove legacy staging scripts
.github/workflows/scripts/stage_common.py, .github/workflows/scripts/stage_macos.py, .github/workflows/scripts/stage_windows.py
Delete prior staging implementations and CLIs for Windows/macOS/common.
Staging configuration
.github/release-staging.toml
Add cross-platform release staging configuration with common and per-target sections, artefacts, and checksum algorithm.
Dev quality orchestrator
codex/refactor-python-staging-scripts-into-github-action
Add Bash script to run ruff format/check, cyclopts type checks, and pytest suite.
Cyclopts typing stubs
cyclopts/__init__.pyi
Add type stubs for App, Parameter, Env, and config to support typing without runtime deps.
Uploader deps facade
scripts/_release_upload_deps.py
Add optional loaders for cyclopts/plumbum with fallbacks; define RuntimeOptions and run_cli.
Upload script refactor
scripts/upload_release_assets.py
Refactor to use deps facade, normalise runtime options, adjust type hints and CLI signature; delegate to run_cli.
Tests: action and CLI
tests_python/test_stage_action.py, tests_python/test_stage_cli.py, tests_python/conftest.py, tests_python/stage_test_helpers.py
Add tests for action structure, CLI behaviour, env handling, output decoding, and workspace scaffolding.
Tests: staging core
tests_python/test_stage_common_config.py, tests_python/test_stage_common_module.py, tests_python/test_stage_common_staging.py
Add tests for config loader, public API, env helpers, globbing, staging pipeline, checksums, outputs, and error cases.
Remove legacy tests
tests_python/test_stage_common.py
Remove tests tied to deleted legacy staging module.
Docs
docs/netsuke-design.md
Update release flow docs to TOML-driven staging composite; add diagrams and narrative changes aligned to new abstraction.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Dev as Workflow Runner
  participant GA as Composite Action (stage)
  participant UV as setup-uv
  participant CLI as scripts/stage.py
  participant PKG as stage_common (config/glob/staging)
  participant GHO as $GITHUB_OUTPUT

  Dev->>GA: uses: ./.github/actions/stage<br/>with: config-file, target
  GA->>UV: Install uv (python 3.11)
  UV-->>GA: uv available
  GA->>CLI: uv run stage.py --config-file ... --target ...
  CLI->>PKG: load_config(config.toml, target)
  PKG-->>CLI: StagingConfig
  CLI->>PKG: stage_artefacts(config, GITHUB_OUTPUT)
  PKG->>PKG: Resolve sources (templates, glob)
  PKG->>PKG: Copy to staging dir, compute checksums
  PKG->>GHO: Append outputs (maps, paths)
  PKG-->>CLI: StageResult
  CLI-->>GA: Exit 0
  GA-->>Dev: outputs: artifact_dir, dist_dir, artefact_map, checksum_map, ...
Loading
sequenceDiagram
  autonumber
  participant CFG as load_config
  participant ENV as require_env_path
  participant TOML as release-staging.toml
  participant VAL as Validators

  CFG->>ENV: Require GITHUB_WORKSPACE
  ENV-->>CFG: Path(workspace)
  CFG->>TOML: Read and parse
  CFG->>VAL: Merge common + target, validate keys
  VAL-->>CFG: StagingConfig (bin_name, platform, arch, artefacts, checksum)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

New maps in TOML, the runners align,
uv spins up, staging on time.
Globs find the freshest, checksums inscribe,
Outputs sing JSON in a tidy vibe.
Old scripts retire; new action takes flight—
Release lanes converge, crisp and bright.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed Validate that the title succinctly captures the primary changes by documenting staging helpers and restructuring tests without extraneous details.
Description Check ✅ Passed Confirm that the description directly relates to the changeset by outlining the expanded staging helper docstrings, reserved output guards, CLI fallbacks, Windows path normalisation, and test restructuring.

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

sourcery-ai[bot]

This comment was marked as resolved.

@leynos
Copy link
Owner Author

leynos commented Oct 10, 2025

@sourcery-ai review

sourcery-ai[bot]

This comment was marked as resolved.

- centralise runtime option parsing so Cyclopts and argparse share validation
- replace loader stubs with named tuples, add run_cli helper, and raise with causes
- expose reserved staging outputs, align action coverage, and update docs/tests
@leynos
Copy link
Owner Author

leynos commented Oct 10, 2025

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 10, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 22

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
scripts/upload_release_assets.py (1)

103-107: Add a single‑line docstring for the private iterator.

Document intent per guidelines.

-def _iter_candidate_paths(dist_dir: Path, bin_name: str) -> typ.Iterator[Path]:
+def _iter_candidate_paths(dist_dir: Path, bin_name: str) -> typ.Iterator[Path]:
+    """Yield candidate artefact file paths under dist_dir for the given binary."""

As per coding guidelines

♻️ Duplicate comments (2)
.github/actions/stage/scripts/stage_common/config.py (2)

167-181: Checksum validation now robust and portable

Validate against algorithms_guaranteed and probe hashlib.new; this closes the platform‑specific pitfall. LGTM.

Based on learnings


226-251: Alternatives normalisation enforces types and strips empties

Enforce list[str] with clear StageError messages; this removes input ambiguity. LGTM.

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f69f0f and fa3fc6b.

📒 Files selected for processing (31)
  • .github/actions/stage/action.yml (1 hunks)
  • .github/actions/stage/scripts/stage.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/__init__.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/checksum_utils.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/config.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/environment.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/errors.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/fs_utils.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/github_output.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/glob_utils.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/staging.py (1 hunks)
  • .github/actions/stage/scripts/stage_common/template_utils.py (1 hunks)
  • .github/release-staging.toml (1 hunks)
  • .github/workflows/release.yml (4 hunks)
  • .github/workflows/scripts/normalise_windows_paths.py (1 hunks)
  • .github/workflows/scripts/stage_common.py (0 hunks)
  • .github/workflows/scripts/stage_macos.py (0 hunks)
  • .github/workflows/scripts/stage_windows.py (0 hunks)
  • codex/refactor-python-staging-scripts-into-github-action (1 hunks)
  • cyclopts/__init__.pyi (1 hunks)
  • docs/netsuke-design.md (2 hunks)
  • scripts/_release_upload_deps.py (1 hunks)
  • scripts/upload_release_assets.py (7 hunks)
  • tests_python/conftest.py (1 hunks)
  • tests_python/stage_test_helpers.py (1 hunks)
  • tests_python/test_stage_action.py (1 hunks)
  • tests_python/test_stage_cli.py (1 hunks)
  • tests_python/test_stage_common.py (0 hunks)
  • tests_python/test_stage_common_config.py (1 hunks)
  • tests_python/test_stage_common_module.py (1 hunks)
  • tests_python/test_stage_common_staging.py (1 hunks)
💤 Files with no reviewable changes (4)
  • .github/workflows/scripts/stage_macos.py
  • .github/workflows/scripts/stage_common.py
  • tests_python/test_stage_common.py
  • .github/workflows/scripts/stage_windows.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

⚙️ CodeRabbit configuration file

**/*.py: - Keep cyclomatic complexity ≤ 12

  • Follow single responsibility and CQRS (command/query segregation)
  • Docstrings must follow the numpy style guide. Use a single-line summary for private functions and methods, and full structured docs for all public interfaces.
  • Move conditionals with >2 branches to predicate/helper functions
  • Avoid eval, exec, pickle, monkey-patching, ctypes, unsafe shell
  • Every module must begin with a triple-quoted docstring explaining its purpose, utility, and usage, including example calls if appropriate.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lint suppressions:
    • Blanket # noqa, file-level skips, and categories are forbidden
    • Only narrow in-line disables (# noqa: XYZ) are permitted, and must be accompanied by FIXME: or a ticket link, and used only as a last resort.
  • Use pytest fixtures for shared setup (conftest.py or fixtures/)
  • Replace duplicate tests with @pytest.mark.parametrize
  • Prefer pytest-mock or unittest.mock for stubs/mocks
  • Use assert …, "message" over bare asserts
  • Reflect all API/behaviour changes in docs/ and update roadmap on completion
  • Files must not exceed 400 logical lines:
    • Decompose large modules into subpackages
    • Split large match/case or dispatch tables by domain and collocate with targets if appropriate
    • Move bulky data (fixtures, templates) to external files for parsing at runtime
  • Mutable defaults and shadowed built-ins are forbidden
  • All code must have clear type hints using modern style (A | B, list[str], class Foo[A]:, type Bar = int, etc.), with ABC imports drawn from the correct stdlib module.
  • All path manipulation must be performed using pathlib for cross platform safety. Do not use string manipulation for extraction of path components or filename elements.

Files:

  • tests_python/stage_test_helpers.py
  • tests_python/test_stage_common_module.py
  • scripts/_release_upload_deps.py
  • tests_python/test_stage_cli.py
  • tests_python/test_stage_action.py
  • tests_python/test_stage_common_config.py
  • tests_python/test_stage_common_staging.py
  • tests_python/conftest.py
  • scripts/upload_release_assets.py
**/*.md

⚙️ CodeRabbit configuration file

**/*.md: * Avoid 2nd person or 1st person pronouns ("I", "you", "we")

  • Use en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Headings must not be wrapped.
  • Documents must start with a level 1 heading
  • Headings must correctly increase or decrease by no more than one level at a time
  • Use GitHub-flavoured Markdown style for footnotes and endnotes.
  • Numbered footnotes must be numbered by order of appearance in the document.

Files:

  • docs/netsuke-design.md
🪛 Ruff (0.13.3)
.github/actions/stage/scripts/stage_common/template_utils.py

41-41: Remove quotes from type annotation

Remove quotes

(UP037)

scripts/_release_upload_deps.py

9-9: Import from collections.abc instead: Sequence

Import from collections.abc

(UP035)


89-89: Boolean positional value in function call

(FBT003)


92-92: Boolean positional value in function call

(FBT003)

.github/actions/stage/scripts/stage_common/__init__.py

8-17: __all__ is not sorted

Apply an isort-style sorting to __all__

(RUF022)

.github/actions/stage/scripts/stage_common/staging.py

68-68: Remove quotes from type annotation

Remove quotes

(UP037)


149-149: Remove quotes from type annotation

Remove quotes

(UP037)


150-150: Remove quotes from type annotation

Remove quotes

(UP037)

tests_python/test_stage_common_staging.py

173-173: Unused function argument: stage_common

(ARG001)


295-295: Unused function argument: stage_common

(ARG001)


307-307: Unused function argument: stage_common

(ARG001)


319-319: Unused function argument: stage_common

(ARG001)


325-325: Probable insecure usage of temporary file or directory: "/tmp/dist/*.zip"

(S108)


330-330: Unused function argument: stage_common

(ARG001)


336-336: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception

(PT011)

⏰ Context from checks skipped due to timeout of 120000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Sourcery review
🔇 Additional comments (30)
scripts/upload_release_assets.py (5)

54-66: Good separation of concerns and dependency gating.

Initialise support handles once and alias required symbols cleanly.


103-107: Type‑hint modernisation is sound.

Use typ.Iterator/typ.Iterable for API clarity without over‑materialising collections.

Also applies to: 219-226, 229-257


127-171: Runtime option normalisation reads cleanly.

Env fallbacks, strict boolean handling, and explicit missing‑args error are correct.


336-351: Route Cyclopts CLI through shared normaliser.

Bind CLI to _prepare_runtime_options, ensuring consistent behaviour across paths.


355-362: Entrypoint delegation to run_cli is correct and minimal.

Centralise CLI selection in the helper, keep the module lean.

.github/actions/stage/scripts/stage_common/checksum_utils.py (1)

11-21: LGTM!

The chunked reading pattern efficiently handles large files without excessive memory usage, and the two-space checksum format matches GNU coreutils conventions.

.github/actions/stage/scripts/stage_common/fs_utils.py (1)

12-21: LGTM!

The path traversal protection correctly resolves both paths before comparison, preventing symlink-based directory escape attacks. The validation-before-creation order ensures the filesystem is only modified for legitimate destinations.

.github/actions/stage/scripts/stage_common/github_output.py (1)

12-26: LGTM!

The UUID delimiter approach prevents content collision, and the type-check correctly distinguishes strings from other sequences. The multiline syntax conforms to GitHub Actions' heredoc format.

.github/workflows/scripts/normalise_windows_paths.py (1)

1-17: LGTM!

The module docstring clearly explains the script's purpose and provides a practical workflow integration example.

cyclopts/__init__.pyi (1)

1-23: LGTM!

The stub definitions provide adequate typing for IDE support whilst maintaining flexibility with the permissive signatures. The default method correctly preserves callable types using ParamSpec.

docs/netsuke-design.md (1)

1681-1748: LGTM!

The documentation updates accurately describe the new staging workflow and maintain consistency with the coding guidelines. The ER diagram clearly illustrates the configuration schema.

.github/actions/stage/scripts/stage_common/errors.py (1)

1-5: LGTM!

The StageError class provides a clear, purpose-specific exception type for staging failures. Inheriting from RuntimeError correctly signals non-recoverable errors.

.github/release-staging.toml (3)

1-10: LGTM!

The alternatives list for the man page elegantly handles Cargo's variable build output locations, and the output keys provide stable identifiers for workflow consumption.


12-22: LGTM!

The Windows target configurations correctly use platform-specific architecture naming (amd64, arm64) and the appropriate MSVC target triples.


24-42: LGTM!

The Unix target configurations appropriately adapt architecture naming to each platform's conventions (Darwin's x86_64 vs Linux's amd64 for Debian packages).

codex/refactor-python-staging-scripts-into-github-action (1)

1-8: LGTM: Strict error handling and sequential quality gates.

The script correctly uses set -euo pipefail for robust error propagation, and the sequential tooling steps (ruff format, ruff check, ty check, pytest) form a clear quality gate pipeline.

.github/actions/stage/scripts/stage_common/environment.py (1)

1-30: LGTM: Clear environment helper with proper error handling.

The require_env_path function is well-documented with a numpy-style docstring, has correct type hints, and provides clear error messages when the environment variable is unset or empty. The implementation is straightforward and aligns with the public API surface tested in test_stage_common_module.py.

.github/actions/stage/action.yml (2)

1-59: LGTM: Well-structured composite action with dependency verification.

The composite action correctly defines required inputs, exposes comprehensive outputs (including reserved keys for binary, man, and licence paths), and enforces strict shell error handling (set -euo pipefail). The explicit verification of uv availability (lines 45-53) provides a clear error message if the dependency is missing.


42-42: Pinned SHA ensures reproducibility.

Using a pinned commit SHA (8d55fbecc275b1c35dbe060458839f8d30439ccf) for astral-sh/setup-uv is excellent practice for supply-chain security and build reproducibility.

.github/actions/stage/scripts/stage.py (1)

1-61: LGTM: Clear CLI entry point with robust error handling.

The staging CLI is well-structured with:

  • PEP 723 inline script metadata declaring dependencies (lines 1-6)
  • Comprehensive module docstring with example usage (lines 8-19)
  • Proper numpy-style docstring for main (lines 35-43)
  • Type hints on all parameters and return type
  • Robust error handling that formats errors for GitHub Actions (line 50) and exits non-zero

The success path (lines 53-57) provides clear feedback about staged artefacts.

tests_python/conftest.py (1)

1-34: LGTM: Clean test fixtures with proper isolation.

Both fixtures are well-structured:

  • stage_common (lines 15-24) correctly manipulates sys.path with cleanup in a finally block
  • workspace (lines 27-34) creates an isolated temporary workspace and sets the environment variable appropriately

Path manipulation uses pathlib as required by the coding guidelines.

tests_python/test_stage_action.py (1)

1-42: LGTM: Focused tests validating composite action configuration.

The test suite effectively validates the composite action by:

  • Checking required outputs are declared (lines 11-17)
  • Verifying uv installation steps (lines 20-25)
  • Confirming the staging script is invoked correctly (lines 28-33)
  • Ensuring reserved output keys remain aligned with stage_common.RESERVED_OUTPUT_KEYS (lines 36-42)

Path manipulation uses pathlib as required.

tests_python/test_stage_common_module.py (1)

1-49: LGTM: Comprehensive validation of public API surface.

The test suite thoroughly validates the staging helper package:

  • Public interface matches documented API (lines 10-23)
  • StageError correctly subclasses RuntimeError with message preservation (lines 26-31)
  • require_env_path returns correct Path when environment variable is set (lines 34-38)
  • require_env_path raises StageError with clear message when variable is missing (lines 41-49)

Type hints are present and correct.

.github/actions/stage/scripts/stage_common/__init__.py (2)

1-17: LGTM: Clear public API re-exports.

The module correctly re-exports the public staging helper API from internal submodules, establishing a clean interface boundary. The __all__ list is comprehensive and matches the test expectations in test_stage_common_module.py.


8-17: Sort __all__ per static analysis recommendation.

Ruff flagged that __all__ is not sorted. Whilst the current ordering groups related symbols (config, environment, errors, staging), an isort-style alphabetical ordering improves maintainability and aligns with the RUF022 rule.

Apply this diff to sort __all__:

 __all__ = [
     "ArtefactConfig",
     "RESERVED_OUTPUT_KEYS",
     "StageError",
     "StageResult",
     "StagingConfig",
     "load_config",
     "require_env_path",
     "stage_artefacts",
 ]

(Note: The current ordering is already alphabetical, so this comment reflects the static analysis hint. If you prefer semantic grouping over strict alphabetical order, add a narrow in-line disable with a FIXME comment as required by the coding guidelines.)

Likely an incorrect or invalid review comment.

tests_python/test_stage_common_config.py (1)

36-70: Solid coverage of loader merge behaviour and outputs

Exercise core fields and artefact outputs; assertions are precise. LGTM.

.github/actions/stage/scripts/stage_common/glob_utils.py (1)

10-36: Glob and path resolution logic reads clean and covers edge cases

Absolute/relative and Windows/POSIX handling and newest‑by‑mtime selection are correct. LGTM.

.github/workflows/release.yml (1)

175-203: Confirm stage I/O and normalisation outputs align
Inputs config-file and target match action.yml; artefact_map, binary_path and license_path outputs are emitted as expected.

.github/actions/stage/scripts/stage_common/staging.py (2)

125-133: Reserved output collision guard is correct and message is actionable.

The check and guidance read well and block reserved key misuse as intended.


111-111: Avoid checksum key collisions in staging.py
Replace checksums[outcome.path.name] with a key relative to the staging directory to prevent name collisions in subfolders:

-        checksums[outcome.path.name] = outcome.digest
+        rel_key = outcome.path.relative_to(staging_dir).as_posix()
+        checksums[rel_key] = outcome.digest

Apply the same change at the other occurrence. Verify whether safe_destination_path permits subdirectories; if not, skip this.

Comment on lines 37 to 46
@dataclasses.dataclass(slots=True)
class ArtefactConfig:
"""Describe a single artefact to be staged."""

source: str
required: bool = True
output: str | None = None
destination: str | None = None
alternatives: list[str] = dataclasses.field(default_factory=list)

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Expand class docstrings to NumPy style for public interfaces

Document fields and usage for ArtefactConfig and StagingConfig using NumPy sections (Parameters, Attributes, Examples) to meet repo guidelines.

As per coding guidelines

Also applies to: 48-63

digest: str


def stage_artefacts(config: "StagingConfig", github_output_file: Path) -> StageResult:
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Drop quotes from type annotations (UP037).

Keep future annotations and remove unnecessary stringified types.

Apply this diff:

-def stage_artefacts(config: "StagingConfig", github_output_file: Path) -> StageResult:
+def stage_artefacts(config: StagingConfig, github_output_file: Path) -> StageResult:
@@
-    config: "StagingConfig",
-    artefact: "ArtefactConfig",
+    config: StagingConfig,
+    artefact: ArtefactConfig,

Also applies to: 149-150

🧰 Tools
🪛 Ruff (0.13.3)

68-68: Remove quotes from type annotation

Remove quotes

(UP037)

🤖 Prompt for AI Agents
.github/actions/stage/scripts/stage_common/staging.py lines 68 and 149-150:
remove unnecessary stringified type annotations introduced earlier—replace
quoted type names (e.g. "StagingConfig") with their bare identifiers
(StagingConfig) so annotations are actual types (keep the existing from
__future__ import annotations in place), and ensure imports cover those types if
not already imported; do this for the function signature at line 68 and the
other annotated occurrences at lines 149-150.

Comment on lines 97 to 100
staged_paths: list[Path] = []
outputs: dict[str, Path] = {}
checksums: dict[str, str] = {}

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Prevent destination collisions; reject reusing the same staged path.

Two artefacts can currently stage to the same destination, with the latter silently replacing the former. Guard against this.

Apply this diff:

     staged_paths: list[Path] = []
     outputs: dict[str, Path] = {}
     checksums: dict[str, str] = {}
+    seen_destinations: set[Path] = set()
@@
-        staged_paths.append(outcome.path)
+        if outcome.path in seen_destinations:
+            raise StageError(
+                f"Duplicate staged destination: {outcome.path.as_posix()}"
+            )
+        seen_destinations.add(outcome.path)
+        staged_paths.append(outcome.path)
         checksums[outcome.path.name] = outcome.digest
         if outcome.output_key:
             outputs[outcome.output_key] = outcome.path

Also applies to: 110-114

Comment on lines 112 to 114
if outcome.output_key:
outputs[outcome.output_key] = outcome.path

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Detect and fail on duplicate artefact output keys.

Current logic silently overwrites earlier entries when two artefacts declare the same output key. Reject duplicates to avoid nondeterministic outputs.

Apply this diff:

-        if outcome.output_key:
-            outputs[outcome.output_key] = outcome.path
+        if outcome.output_key:
+            if outcome.output_key in outputs:
+                prev = outputs[outcome.output_key]
+                raise StageError(
+                    f"Duplicate artefact output key {outcome.output_key!r}: "
+                    f"{prev.as_posix()} and {outcome.path.as_posix()}"
+                )
+            outputs[outcome.output_key] = outcome.path
📝 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
if outcome.output_key:
outputs[outcome.output_key] = outcome.path
if outcome.output_key:
if outcome.output_key in outputs:
prev = outputs[outcome.output_key]
raise StageError(
f"Duplicate artefact output key {outcome.output_key!r}: "
f"{prev.as_posix()} and {outcome.path.as_posix()}"
)
outputs[outcome.output_key] = outcome.path
🤖 Prompt for AI Agents
In .github/actions/stage/scripts/stage_common/staging.py around lines 112 to
114, the code unconditionally assigns outputs[outcome.output_key] = outcome.path
which silently overwrites earlier artefacts with the same key; change this to
detect duplicates and fail fast by first checking if outcome.output_key is
already present in outputs and, if so, raise an explicit exception or log an
error and exit (include the duplicate output key and both paths/artefact
identifiers in the message for debugging); otherwise proceed to assign the key
to the path.

Comment on lines +191 to +194
print(
f"Staged '{source_path.relative_to(config.workspace)}' ->"
f" '{destination_path.relative_to(config.workspace)}'",
)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid ValueError when destination is not under workspace.

relative_to(config.workspace) raises if the staging dir is outside the workspace. Fall back to an absolute/posix path.

Apply this diff:

-    print(
-        f"Staged '{source_path.relative_to(config.workspace)}' ->"
-        f" '{destination_path.relative_to(config.workspace)}'",
-    )
+    try:
+        dest_rel = destination_path.relative_to(config.workspace)
+    except ValueError:
+        dest_rel = destination_path
+    print(
+        f"Staged '{source_path.relative_to(config.workspace)}' ->"
+        f" '{dest_rel}'",
+    )
📝 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
print(
f"Staged '{source_path.relative_to(config.workspace)}' ->"
f" '{destination_path.relative_to(config.workspace)}'",
)
try:
dest_rel = destination_path.relative_to(config.workspace)
except ValueError:
dest_rel = destination_path
print(
f"Staged '{source_path.relative_to(config.workspace)}' ->"
f" '{dest_rel}'",
)
🤖 Prompt for AI Agents
In .github/actions/stage/scripts/stage_common/staging.py around lines 191 to
194, the call to relative_to(config.workspace) can raise ValueError when
destination_path (or source_path) is outside the workspace; catch that case and
fall back to an absolute/posix representation. Modify the print to compute
safe_source = try source_path.relative_to(config.workspace) except ValueError:
source_path.as_posix() and safe_dest = try
destination_path.relative_to(config.workspace) except ValueError:
destination_path.as_posix(), then use those safe_source/safe_dest in the
formatted message so it never raises.

Comment on lines +173 to +191
stage_common: object, workspace: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Absolute Windows-style globs should resolve relative to the drive root."""

monkeypatch.chdir(workspace)

drive_root = Path("C:\\")
windows_workspace = drive_root / "workspace"
man_dir = windows_workspace / "man"
man_dir.mkdir(parents=True, exist_ok=True)
candidate = man_dir / "netsuke.1"
candidate.write_text(".TH WINDOWS", encoding="utf-8")

staging = importlib.import_module("stage_common.staging")
matched = staging._match_candidate_path(
windows_workspace, "C:/workspace/man/*.1"
)

assert matched == candidate
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Suppress unused fixture warning with a narrow, annotated noqa

Add a targeted, annotated suppression to comply with lint policy.

As per coding guidelines

-def test_match_candidate_path_handles_windows_drive(
-    stage_common: object, workspace: Path, monkeypatch: pytest.MonkeyPatch
-) -> None:
+def test_match_candidate_path_handles_windows_drive(  # noqa: ARG001  # FIXME: fixture triggers plugin import
+    stage_common: object, workspace: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
📝 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
stage_common: object, workspace: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Absolute Windows-style globs should resolve relative to the drive root."""
monkeypatch.chdir(workspace)
drive_root = Path("C:\\")
windows_workspace = drive_root / "workspace"
man_dir = windows_workspace / "man"
man_dir.mkdir(parents=True, exist_ok=True)
candidate = man_dir / "netsuke.1"
candidate.write_text(".TH WINDOWS", encoding="utf-8")
staging = importlib.import_module("stage_common.staging")
matched = staging._match_candidate_path(
windows_workspace, "C:/workspace/man/*.1"
)
assert matched == candidate
def test_match_candidate_path_handles_windows_drive( # noqa: ARG001 # FIXME: fixture triggers plugin import
stage_common: object, workspace: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Absolute Windows-style globs should resolve relative to the drive root."""
🧰 Tools
🪛 Ruff (0.13.3)

173-173: Unused function argument: stage_common

(ARG001)

🤖 Prompt for AI Agents
In tests_python/test_stage_common_staging.py around lines 173–191, the unused
pytest fixture parameter stage_common raises a lint warning; fix it by renaming
the parameter to _stage_common (preserving its type annotation, e.g.
_stage_common: object) so the fixture remains provided to the test but the
unused-variable warning is suppressed narrowly without a broad noqa.

Comment on lines +295 to +304
def test_glob_root_and_pattern_handles_windows_drive(stage_common: object) -> None:
"""Absolute Windows globs should strip the drive before globbing."""

staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern

root, pattern = helper(PureWindowsPath("C:/dist/*.zip"))
assert root == "C:\\"
assert pattern == "dist/*.zip"

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Suppress unused fixture warning with a narrow, annotated noqa

Apply the same annotated suppression here.

As per coding guidelines

-def test_glob_root_and_pattern_handles_windows_drive(stage_common: object) -> None:
+def test_glob_root_and_pattern_handles_windows_drive(stage_common: object) -> None:  # noqa: ARG001  # FIXME: fixture triggers plugin import
📝 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_glob_root_and_pattern_handles_windows_drive(stage_common: object) -> None:
"""Absolute Windows globs should strip the drive before globbing."""
staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern
root, pattern = helper(PureWindowsPath("C:/dist/*.zip"))
assert root == "C:\\"
assert pattern == "dist/*.zip"
def test_glob_root_and_pattern_handles_windows_drive(stage_common: object) -> None: # noqa: ARG001 # FIXME: fixture triggers plugin import
"""Absolute Windows globs should strip the drive before globbing."""
staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern
root, pattern = helper(PureWindowsPath("C:/dist/*.zip"))
assert root == "C:\\"
assert pattern == "dist/*.zip"
🧰 Tools
🪛 Ruff (0.13.3)

295-295: Unused function argument: stage_common

(ARG001)

🤖 Prompt for AI Agents
In tests_python/test_stage_common_staging.py around lines 295-304, the test
function parameter stage_common is an unused pytest fixture and needs a narrow,
annotated noqa to suppress the unused-fixture warning; update the test signature
to add a per-parameter noqa comment on stage_common (e.g., annotate the
parameter with the specific flake/linters code) so only this unused-fixture
warning is silenced while leaving other linting intact.

Comment on lines 306 to 317
def test_glob_root_and_pattern_returns_wildcard_for_root_only(
stage_common: object,
) -> None:
"""Root-only absolute paths should glob all children."""

staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern

root, pattern = helper(PureWindowsPath("C:/"))
assert root == "C:\\"
assert pattern == "*"

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Suppress unused fixture warning with a narrow, annotated noqa

Apply the same annotated suppression here.

As per coding guidelines

-def test_glob_root_and_pattern_returns_wildcard_for_root_only(
-    stage_common: object,
-) -> None:
+def test_glob_root_and_pattern_returns_wildcard_for_root_only(  # noqa: ARG001  # FIXME: fixture triggers plugin import
+    stage_common: object,
+) -> None:
📝 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_glob_root_and_pattern_returns_wildcard_for_root_only(
stage_common: object,
) -> None:
"""Root-only absolute paths should glob all children."""
staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern
root, pattern = helper(PureWindowsPath("C:/"))
assert root == "C:\\"
assert pattern == "*"
def test_glob_root_and_pattern_returns_wildcard_for_root_only( # noqa: ARG001 # FIXME: fixture triggers plugin import
stage_common: object,
) -> None:
"""Root-only absolute paths should glob all children."""
staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern
root, pattern = helper(PureWindowsPath("C:/"))
assert root == "C:\\"
assert pattern == "*"
🧰 Tools
🪛 Ruff (0.13.3)

307-307: Unused function argument: stage_common

(ARG001)

🤖 Prompt for AI Agents
In tests_python/test_stage_common_staging.py around lines 306 to 317, the test
function accepts the unused fixture parameter stage_common and should suppress
the unused-fixture warning with a narrow annotated noqa; update the function
signature to attach a specific noqa comment to the parameter (for example:
stage_common: object  # noqa: ARG001) so the unused fixture warning is silenced
only for that parameter while leaving other lints intact.

Comment on lines 319 to 328
def test_glob_root_and_pattern_handles_posix_absolute(stage_common: object) -> None:
"""POSIX absolute paths should preserve relative segments for globbing."""

staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern

root, pattern = helper(PurePosixPath("/tmp/dist/*.zip"))
assert root == "/"
assert pattern.endswith("dist/*.zip"), pattern

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Suppress unused fixture warning with a narrow, annotated noqa

Apply the same annotated suppression here.

As per coding guidelines

-def test_glob_root_and_pattern_handles_posix_absolute(stage_common: object) -> None:
+def test_glob_root_and_pattern_handles_posix_absolute(stage_common: object) -> None:  # noqa: ARG001  # FIXME: fixture triggers plugin import
🧰 Tools
🪛 Ruff (0.13.3)

319-319: Unused function argument: stage_common

(ARG001)


325-325: Probable insecure usage of temporary file or directory: "/tmp/dist/*.zip"

(S108)

🤖 Prompt for AI Agents
In tests_python/test_stage_common_staging.py around lines 319 to 328, the pytest
fixture parameter stage_common is unused and triggers a linter warning; add a
narrow annotated noqa on the parameter in the test function signature (e.g.,
append "  # noqa: F841" to the stage_common parameter) so the suppression
targets only this unused fixture without silencing other checks.

Comment on lines 330 to 338
def test_glob_root_and_pattern_rejects_relative_paths(stage_common: object) -> None:
"""Relative globs should be rejected to avoid ambiguous anchors."""

staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern

with pytest.raises(ValueError):
helper(PurePosixPath("dist/*.zip"))

Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Tighten the exception assertion and suppress the unused fixture warning

Match the error text and add annotated suppression.

As per coding guidelines

-def test_glob_root_and_pattern_rejects_relative_paths(stage_common: object) -> None:
+def test_glob_root_and_pattern_rejects_relative_paths(stage_common: object) -> None:  # noqa: ARG001  # FIXME: fixture triggers plugin import
@@
-    with pytest.raises(ValueError):
+    with pytest.raises(ValueError, match="Expected absolute path"):
         helper(PurePosixPath("dist/*.zip"))
📝 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_glob_root_and_pattern_rejects_relative_paths(stage_common: object) -> None:
"""Relative globs should be rejected to avoid ambiguous anchors."""
staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern
with pytest.raises(ValueError):
helper(PurePosixPath("dist/*.zip"))
def test_glob_root_and_pattern_rejects_relative_paths(stage_common: object) -> None: # noqa: ARG001 # FIXME: fixture triggers plugin import
"""Relative globs should be rejected to avoid ambiguous anchors."""
staging = importlib.import_module("stage_common.staging")
helper = staging._glob_root_and_pattern
with pytest.raises(ValueError, match="Expected absolute path"):
helper(PurePosixPath("dist/*.zip"))
🧰 Tools
🪛 Ruff (0.13.3)

330-330: Unused function argument: stage_common

(ARG001)


336-336: pytest.raises(ValueError) is too broad, set the match parameter or use a more specific exception

(PT011)

🤖 Prompt for AI Agents
In tests_python/test_stage_common_staging.py around lines 330 to 338, tighten
the exception assertion by using pytest.raises(ValueError, match="^Relative
globs are not allowed") (or the exact error message emitted by
_glob_root_and_pattern) instead of a bare pytest.raises, and suppress the unused
fixture warning by renaming the parameter to _stage_common: object (or add an
inline noqa comment) so the fixture remains available but the linter no longer
flags it as unused.

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

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

Gates Failed
Enforce critical code health rules (2 files with Bumpy Road Ahead)
Enforce advisory code health rules (6 files with Complex Method, Excess Number of Function Arguments, Code Duplication)

Gates Passed
4 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
Enforce critical code health rules Violations Code Health Impact
staging.py 1 critical rule 9.34 Suppress
glob_utils.py 1 critical rule 9.54 Suppress
Enforce advisory code health rules Violations Code Health Impact
staging.py 1 advisory rule 9.34 Suppress
test_stage_common_config.py 1 advisory rule 9.39 Suppress
test_stage_common_staging.py 1 advisory rule 9.39 Suppress
glob_utils.py 1 advisory rule 9.54 Suppress
config.py 1 advisory rule 9.69 Suppress
upload_release_assets.py 1 advisory rule 10.00 → 9.69 Suppress

Quality Gate Profile: Pay Down Tech Debt
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant