Skip to content

Release dry-run workflow with per-binary Linux packaging#129

Merged
leynos merged 10 commits intomainfrom
terragon/update-linux-packaging-release-dry-run-uv2jzi
Jan 17, 2026
Merged

Release dry-run workflow with per-binary Linux packaging#129
leynos merged 10 commits intomainfrom
terragon/update-linux-packaging-release-dry-run-uv2jzi

Conversation

@leynos
Copy link
Copy Markdown
Owner

@leynos leynos commented Jan 7, 2026

Summary

  • Introduces a release dry-run workflow to validate packaging and artefact generation without publishing releases
  • Extends Linux packaging and release automation to support per-binary artefacts, versioning, and dry-run mode
  • Adds end-to-end release process for drafting/publishing GitHub releases with per-binary artefacts (comenq, comenqd)

Changes

Workflows

  • New: .github/workflows/release-dry-run.yml
    • Triggers on PR events (opened, synchronize, reopened, ready_for_review)
    • Delegates to the release workflow with dry-run: true
  • Updated: .github/workflows/release.yml
    • Introduces inputs: publish (bool) and dry-run (bool)
    • Adds outputs: version, should_publish, dry_run, should_upload_workflow_artifacts
    • Adds concurrency grouping per release tag
    • Adds metadata job to determine release mode and read package version
    • Builds and packages artefacts using updated actions, wiring in the published version and dry-run flags
    • Uploads artefacts conditionally based on dry-run and publish flags
    • Keeps drafting/publishing GitHub releases optional via should_publish
    • Uploads separate artefacts for comenq and comenqd with error checks

Packaging & Versioning

  • Version is read from the package metadata and propagated through the workflow for packaging and artefact naming
  • Packaging actions (build and linux-packages) updated to align with the new versioning flow and metadata
  • Deb/RPM artefacts produced but only uploaded in release steps when appropriate (dry-run or publish)
  • Adds binary targets for the comenq and comenqd crates to align packaging with crate layout

Code & Tests

  • Defines binary targets for crates/comenq and crates/comenqd to align packaging with crate layout
  • Minor test-support and helper updates to accommodate test workflow changes
  • Updates test-support workflow tests to reference the new shared upload-release-assets action commit

Why

  • Provides a safe, automated way to validate packaging and artefact generation without releasing artifacts
  • Improves release reliability by ensuring versioning is consistently applied and artefacts are generated correctly before publishing
  • Enables per-binary artefact handling and robust error reporting during release asset uploads

Testing plan

  • Open a PR to trigger the new release-dry-run workflow and verify it performs the full packaging steps in dry-run mode (no GitHub release or artefact uploads)
  • Verify that the metadata phase correctly determines:.version, should_publish, dry_run, and should_upload_workflow_artifacts
  • Check that when dry-run is true, artefacts are not uploaded and release is not created
  • When should_publish is true, confirm a draft GitHub release is created or updated and artefacts for both comenq and comenqd are uploaded
  • Validate error handling paths for release asset uploads print meaningful messages and fail gracefully
  • Update tests to reflect new upload-release-assets shared action usage

Notes

  • This change makes release validation safer by separating dry-run verification from actual release publishing
  • No changes to the public APIs or application logic other than CI workflow orchestration and packaging behavior

📎 Task: https://www.terragonlabs.com/task/30929c96-3b12-4ade-b223-2d92ca677aa1

Refactor the release workflow to use the updated shared-actions commit
(df81280dcc1d6e66134114dbc924313328b15f05) and add support for dry run
mode via a reusable workflow pattern.

Changes to release.yml:
- Add workflow_call trigger with dry-run and publish inputs
- Add metadata job using determine-release-modes and ensure-cargo-version
- Replace hardcoded version extraction with metadata job outputs
- Add conditional artifact upload based on release mode flags
- Replace softprops/action-gh-release with upload-release-assets action
- Add concurrency group to prevent parallel release runs
- Add permissions block for contents: write

New release-dry-run.yml:
- Triggers on PR events (opened, synchronize, reopened, ready_for_review)
- Calls release.yml with dry-run: true
- Enables CI verification of release packaging without publishing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 7, 2026

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

    • Added two new command‑line binaries for installation and distribution.
  • Chores

    • Introduced a release dry‑run workflow and improved release publishing to upload packaged artifacts reliably.
    • Enhanced packaging and distribution to include additional build outputs and deterministic release asset uploads.
  • Tests

    • Updated test infrastructure and test‑only utilities to validate release, packaging and publisher behaviours.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Introduce a reusable callable release workflow and a dry-run entrypoint; compute release metadata and conditionally upload artifacts; gate a publish job that prepares a draft release and uploads two assets; add binaries for two crates; extend test utilities to detect the new upload-release-assets publisher; expose test-only metadata helpers and simplify a test hook field.

Changes

Cohort / File(s) Summary
GitHub Actions Workflows
\.github/workflows/release-dry-run.yml, \.github/workflows/release.yml
Add release-dry-run.yml to invoke release.yml with dry-run: true. Make release.yml callable (workflow_call) with inputs publish and dry-run; add concurrency and a metadata job that computes version/flags and exposes outputs; update build-packages to use computed version and conditionally upload workflow artifacts; add gated publish job that prepares a draft release and uploads two assets.
Rust Crate Manifests
\crates/comenq/Cargo.toml`, `crates/comenqd/Cargo.toml``
Add a [[bin]] entry comenq in crates/comenq. Add a library target and a [[bin]] entry comenqd in crates/comenqd.
Test Support
\test-support/src/workflow.rs``
Add UPLOAD_RELEASE_ASSETS_PREFIX and EXPECTED_UPLOAD_RELEASE_ASSETS; change detection logic to strip the publisher prefix and compare the embedded commit to the shared actions commit; update and parameterise tests to assert presence and pinning behaviour of the new upload-release-assets publisher alongside the existing builder action.
Crate Test Utilities & Hooks
\crates/comenqd/src/util.rs`, `crates/comenqd/src/worker.rs``
Expose test-only metadata utilities (METADATA_FILE_NAMES, is_metadata_file) under cfg(any(test, feature = "test-support")) and add unit tests. Remove the conditional cfg_attr from WorkerHooks.drained, leaving the field unconditionally present.
Test-support Manifest
\test-support/Cargo.toml``
Add rstest as a dev-dependency.

Sequence Diagram(s)

sequenceDiagram
  participant PR as Pull Request
  participant GHA as GitHub Actions
  participant Metadata as Metadata Job
  participant Builder as Build-Packages Job
  participant Artifacts as Artifact Storage
  participant Release as Publish Job

  PR->>GHA: Trigger workflow (release-dry-run or release)
  GHA->>Metadata: Run metadata job to compute version and flags
  Metadata-->>GHA: Return version, should_publish, dry_run, should_upload_workflow_artifacts
  GHA->>Builder: Run build-packages with computed version
  Builder->>Artifacts: Upload artifacts if flagged
  GHA->>Release: If should_publish == true, prepare draft release
  Release->>Artifacts: Download dist artifacts
  Release->>Release: Upload assets (comenq, comenqd) to draft release
  Release-->>GHA: Finalise publish or end dry-run
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

✨ Pipelines hum and binaries wake,
Dry runs probe each careful stake,
Metadata reads the version true,
Two assets climb the release queue 🚀,
Tests now spot the publisher too.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: adding a release dry-run workflow alongside per-binary Linux packaging support.
Description check ✅ Passed The description clearly relates to the changeset, detailing the new dry-run workflow, versioning propagation, binary targets, and testing undertaken.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch terragon/update-linux-packaging-release-dry-run-uv2jzi

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

@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Jan 7, 2026

Reviewer's Guide

Makes the release workflow reusable with metadata-driven versioning, adds a PR-triggered dry‑run workflow, switches Linux packaging to use the package version, and replaces softprops GH releases with shared upload-release-assets actions plus stronger tests around shared-actions usage.

Sequence diagram for PR-triggered release dry-run workflow

sequenceDiagram
  actor Developer
  participant GitHubActions
  participant ReleaseDryRunWorkflow
  participant ReleaseWorkflow
  participant MetadataJob
  participant BuildPackagesJob
  participant ReleaseJob
  participant DetermineModes
  participant EnsureVersion
  participant RustBuild
  participant LinuxPackages

  Developer->>GitHubActions: Open or update pull request
  GitHubActions->>ReleaseDryRunWorkflow: Trigger on pull_request event
  ReleaseDryRunWorkflow->>ReleaseWorkflow: Call with dry-run true

  ReleaseWorkflow->>MetadataJob: Start metadata job
  MetadataJob->>DetermineModes: Run determine-release-modes
  DetermineModes-->>MetadataJob: should-publish, dry-run, should-upload-workflow-artifacts
  MetadataJob->>EnsureVersion: Run ensure-cargo-version
  EnsureVersion-->>MetadataJob: version
  MetadataJob-->>ReleaseWorkflow: Expose outputs version and mode flags

  ReleaseWorkflow->>BuildPackagesJob: Start build-packages matrix job
  BuildPackagesJob->>RustBuild: Build binaries for each target
  RustBuild-->>BuildPackagesJob: Build artifacts
  BuildPackagesJob->>LinuxPackages: Package deb and rpm using version
  LinuxPackages-->>BuildPackagesJob: Packaged artifacts

  alt should-upload-workflow-artifacts or should-publish
    BuildPackagesJob-->>GitHubActions: Upload workflow artifacts
  else no uploads in dry run
    BuildPackagesJob-->>GitHubActions: Skip artifact upload
  end

  alt should-publish is true
    ReleaseWorkflow->>ReleaseJob: Run release job
    ReleaseJob-->>GitHubActions: Ensure draft release and upload assets
  else dry-run or no publish
    ReleaseWorkflow-->>GitHubActions: Skip release job
  end
Loading

Flow diagram for release mode and conditional artifact handling

flowchart TD
  Start["Start release.yml workflow"] --> CallModes["Run determine-release-modes"]
  CallModes --> ModesOut["Outputs: should-publish, dry-run, should-upload-workflow-artifacts"]

  ModesOut --> CallVersion["Run ensure-cargo-version with check-tag from should-publish"]
  CallVersion --> VersionOut["Output: version"]

  VersionOut --> BuildPackages["Run build-packages job matrix"]

  BuildPackages --> CondUpload["Condition: should-upload-workflow-artifacts is true OR should-publish is true"]
  CondUpload -->|true| UploadArtifacts["Upload build artefacts as workflow artifacts"]
  CondUpload -->|false| SkipUpload["Skip workflow artifact upload"]

  ModesOut --> CondRelease["Condition: should-publish is true"]
  CondRelease -->|true| RunRelease["Run release job"]
  CondRelease -->|false| SkipRelease["Skip release job (dry run or no publish)"]

  RunRelease --> EnsureDraft["Ensure draft GitHub release exists"]
  EnsureDraft --> DownloadArtifacts["Download workflow artifacts to dist directory"]

  DownloadArtifacts --> UploadComenq["Upload comenq release assets"]
  DownloadArtifacts --> UploadComenqd["Upload comenqd release assets"]

  UploadComenq --> CheckComenq["If upload-error is true, print error-message and fail"]
  UploadComenqd --> CheckComenqd["If upload-error is true, print error-message and fail"]
Loading

File-Level Changes

Change Details Files
Expose release workflow as a reusable workflow with metadata-driven release modes and per-tag concurrency.
  • Add workflow_call trigger with publish and dry-run boolean inputs and corresponding outputs (version, should_publish, dry_run, should_upload_workflow_artifacts).
  • Introduce a metadata job that checks out the repo, determines release modes via shared determine-release-modes action, and reads the Cargo package version via ensure-cargo-version, wiring outputs to downstream jobs.
  • Add workflow-level concurrency grouping keyed by the release ref and cancel in-progress runs for the same tag.
.github/workflows/release.yml
Update build-packages job to consume metadata outputs, use the new shared-actions commit, and conditionally upload workflow artifacts.
  • Make build-packages depend on metadata and drop manual version extraction from tag in favor of using needs.metadata.outputs.version.
  • Update rust-build-release and linux-packages composite actions to the new pinned shared-actions commit hash.
  • Guard upload-artifact step on should_upload_workflow_artifacts or should_publish so artifacts are only uploaded when appropriate.
.github/workflows/release.yml
Rework release publishing to use a draft-or-create step plus shared upload-release-assets per binary with explicit error handling.
  • Gate the release job on should_publish, add contents: write permissions, and depend on both metadata and build-packages.
  • Ensure a draft GitHub release exists using gh release view/create before uploading assets.
  • Replace softprops/action-gh-release with leynos/upload-release-assets composite action, invoked separately for comenq and comenqd, and add follow-up steps that fail the job if either upload reports an error via outputs.
.github/workflows/release.yml
Introduce a PR-triggered dry-run workflow that reuses the release workflow in dry-run mode.
  • Add release-dry-run workflow triggered on pull_request events (opened, synchronize, reopened, ready_for_review).
  • Configure the dry-run workflow to call the release.yml reusable workflow with dry-run: true and inherit secrets.
.github/workflows/release-dry-run.yml
Tighten workflow tests around shared-actions usage and add support for the new upload-release-assets action.
  • Update the shared-actions commit literal and EXPECTED_SHARED_ACTIONS_COMMIT to the new hash in test-support helpers.
  • Add constants and expectations for the upload-release-assets composite action reference and incorporate them into tests.
  • Change uses_shared_release_actions to detect the shared upload-release-assets action instead of softprops/action-gh-release, and adjust tests to use the new constants in both happy-path and failure cases.
test-support/src/workflow.rs
Clarify binary targets in Cargo manifests to match the packaging expectations.
  • In comenqd Cargo.toml, add explicit [lib] and [[bin]] sections pointing lib to src/lib.rs and the binary to src/main.rs.
  • In comenq Cargo.toml, add an explicit [[bin]] section naming the comenq binary at src/main.rs while keeping the library target.
crates/comenqd/Cargo.toml
crates/comenq/Cargo.toml

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

Add explicit [[bin]] sections to both comenq and comenqd crates to
ensure Cargo correctly identifies and builds the binaries when building
the workspace. Without explicit declarations, Cargo's auto-detection
was failing because both crates have both lib.rs and main.rs files.

Changes:
- crates/comenq/Cargo.toml: Add [[bin]] section for comenq binary
- crates/comenqd/Cargo.toml: Add [lib] and [[bin]] sections for clarity
- test-support/src/workflow.rs: Update shared-actions SHA to
  df81280dcc1d6e66134114dbc924313328b15f05 and replace softprops/
  action-gh-release check with upload-release-assets action reference

This fixes the release dry-run workflow failure where cargo build
completed but the binary was not found at the expected path
target/<target>/release/<binary>.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@leynos leynos marked this pull request as ready for review January 11, 2026 00:16
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The workflow_call inputs (publish, dry-run) aren’t passed into determine-release-modes, so it’s not obvious how those flags influence should_publish/dry_run; consider wiring the inputs explicitly into that action (or documenting that it derives everything from context only) to avoid surprises when reusing the workflow.
  • The rust-build-release step no longer receives a version input whereas linux-packages does; if the build action still supports/needs explicit versioning, it may be clearer and safer to pass through needs.metadata.outputs.version there as well for consistency.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `workflow_call` inputs (`publish`, `dry-run`) aren’t passed into `determine-release-modes`, so it’s not obvious how those flags influence `should_publish`/`dry_run`; consider wiring the inputs explicitly into that action (or documenting that it derives everything from context only) to avoid surprises when reusing the workflow.
- The `rust-build-release` step no longer receives a `version` input whereas `linux-packages` does; if the build action still supports/needs explicit versioning, it may be clearer and safer to pass through `needs.metadata.outputs.version` there as well for consistency.

## Individual Comments

### Comment 1
<location> `.github/workflows/release.yml:7-16` </location>
<code_context>
   push:
     tags:
       - 'v*.*.*'
+  workflow_call:
+    inputs:
+      publish:
+        description: >-
+          Whether the workflow should publish artefacts to the GitHub release
+          associated with the tag. Defaults to `false` when invoked as a
+          reusable workflow.
+        required: false
+        type: boolean
+        default: false
+      dry-run:
+        description: >-
</code_context>

<issue_to_address>
**issue (bug_risk):** workflow_call inputs are defined but never referenced in jobs, so they currently don't influence behaviour

`publish` and `dry-run` are defined as `workflow_call` inputs but not used anywhere (e.g., via `inputs.publish` / `inputs['dry-run']` or when calling `determine-release-modes`). As a result, callers can set them but they won’t affect behaviour, which is currently driven only by `determine-release-modes`. Consider either wiring these inputs into that action/conditions or removing them if they’re not needed.
</issue_to_address>

### Comment 2
<location> `test-support/src/workflow.rs:87` </location>
<code_context>
 mod tests {
     use super::{
-        EXPECTED_RUST_BUILDER, EXPECTED_SHARED_ACTIONS_COMMIT, uses_shared_release_actions,
+        EXPECTED_RUST_BUILDER, EXPECTED_SHARED_ACTIONS_COMMIT, EXPECTED_UPLOAD_RELEASE_ASSETS,
+        uses_shared_release_actions,
     };
</code_context>

<issue_to_address>
**suggestion (testing):** Add negative tests for the new upload-release-assets action detection (missing, wrong commit, unpinned).

Current tests only validate the happy path and builder-related failures; they don’t independently exercise the new `UPLOAD_RELEASE_ASSETS_PREFIX` / `EXPECTED_UPLOAD_RELEASE_ASSETS` logic.

Please add negative tests for the upload-release-assets action itself, e.g.:
- Workflow has the correct builder step but no upload-release-assets step → `uses_shared_release_actions` returns `false`.
- Workflow has the correct builder step and upload-release-assets with the right prefix but wrong commit → `false`.
- Workflow uses an unpinned upload-release-assets reference (e.g. `@v1` tag) instead of a commit → `false`.

These can mirror the existing builder negative tests so the pinning requirement is enforced consistently for both steps.

Suggested implementation:

```rust
#[cfg(test)]
mod tests {
    use super::{
        EXPECTED_RUST_BUILDER,
        EXPECTED_SHARED_ACTIONS_COMMIT,
        EXPECTED_UPLOAD_RELEASE_ASSETS,
        uses_shared_release_actions,
    };

    // Existing tests remain here...
}

```

Please add the following new tests inside the `mod tests` block (reusing whatever workflow-building helpers the existing builder tests use). They should live alongside the existing negative tests for the builder action.

```rust
    #[test]
    fn uses_shared_release_actions_false_when_upload_release_assets_step_missing() {
        // Workflow has the correct builder step but NO upload-release-assets step.
        let workflow = Workflow::from_yaml_str(
            r#"
            jobs:
              release:
                steps:
                  - name: Build release
                    uses: ${EXPECTED_RUST_BUILDER}
            "#,
        )
        .expect("valid workflow yaml");

        assert!(
            !uses_shared_release_actions(&workflow),
            "Expected uses_shared_release_actions to be false when upload-release-assets step is missing",
        );
    }

    #[test]
    fn uses_shared_release_actions_false_when_upload_release_assets_wrong_commit() {
        // Workflow has the correct builder step and upload-release-assets step,
        // but the upload-release-assets reference is pinned to the wrong commit.
        let workflow = Workflow::from_yaml_str(
            r#"
            jobs:
              release:
                steps:
                  - name: Build release
                    uses: ${EXPECTED_RUST_BUILDER}
                  - name: Upload release assets
                    uses: leynos/shared-actions/.github/actions/upload-release-assets@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            "#,
        )
        .expect("valid workflow yaml");

        assert!(
            !uses_shared_release_actions(&workflow),
            "Expected uses_shared_release_actions to be false when upload-release-assets is pinned to the wrong commit",
        );
    }

    #[test]
    fn uses_shared_release_actions_false_when_upload_release_assets_unpinned() {
        // Workflow has the correct builder step and upload-release-assets step,
        // but upload-release-assets is referenced via a tag (e.g. @v1) instead of a commit SHA.
        let workflow = Workflow::from_yaml_str(
            r#"
            jobs:
              release:
                steps:
                  - name: Build release
                    uses: ${EXPECTED_RUST_BUILDER}
                  - name: Upload release assets
                    uses: leynos/shared-actions/.github/actions/upload-release-assets@v1
            "#,
        )
        .expect("valid workflow yaml");

        assert!(
            !uses_shared_release_actions(&workflow),
            "Expected uses_shared_release_actions to be false when upload-release-assets is unpinned (tagged ref)",
        );
    }
```

You will need to adjust the workflow-construction to match the helpers already in this file. For example:

- If there is a helper like `parse_workflow(yaml: &str) -> Workflow`, use that instead of `Workflow::from_yaml_str`.
- If tests interpolate constants differently (e.g. via `format!()` instead of `${...}`), update the YAML strings accordingly:

```rust
let yaml = format!(
    r#"
    jobs:
      release:
        steps:
          - name: Build release
            uses: {builder}
          - name: Upload release assets
            uses: {upload_assets}
    "#,
    builder = EXPECTED_RUST_BUILDER,
    upload_assets = format!(
        "leynos/shared-actions/.github/actions/upload-release-assets@{}",
        EXPECTED_SHARED_ACTIONS_COMMIT,
    ),
);
let workflow = parse_workflow(&yaml);
```

Also ensure `EXPECTED_UPLOAD_RELEASE_ASSETS` is defined (likely mirroring `EXPECTED_RUST_BUILDER`), and consider using it in tests where you want the *correct* upload-release-assets reference so the negative tests clearly show only the mismatching parts (wrong commit or tag).
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread .github/workflows/release.yml
Copy link
Copy Markdown
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: 1

🤖 Fix all issues with AI agents
In @.github/workflows/release.yml:
- Line 109: The workflow conditional mixes boolean parsing and string
comparison: update the conditional that references
should_upload_workflow_artifacts and should_publish so both use the same boolean
parsing approach; specifically, wrap should_publish with fromJSON() (or
conversely remove fromJSON from should_upload_workflow_artifacts) so the if:
expression consistently evaluates booleans (e.g., use
fromJSON(needs.metadata.outputs.should_upload_workflow_artifacts) ||
fromJSON(needs.metadata.outputs.should_publish)).
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ac4612d and 1b9dcb0.

📒 Files selected for processing (5)
  • .github/workflows/release-dry-run.yml
  • .github/workflows/release.yml
  • crates/comenq/Cargo.toml
  • crates/comenqd/Cargo.toml
  • test-support/src/workflow.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Every module must begin with a module level (//! ) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Use rstest fixtures for shared setup.
Replace duplicated tests with #[rstest(...)] parameterised cases.
Prefer mockall for mocks/stubs.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.
Prefer semantic error enums. Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary. Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library. Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.

Files:

  • test-support/src/workflow.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • test-support/src/workflow.rs
⏰ 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). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: Sourcery review
🔇 Additional comments (12)
crates/comenqd/Cargo.toml (1)

6-11: LGTM!

The explicit [lib] and [[bin]] target declarations correctly separate the library and binary components, ensuring Cargo reliably builds both. This aligns with the PR's multi-binary release handling.

crates/comenq/Cargo.toml (1)

9-11: LGTM!

The [[bin]] target declaration for comenq follows the same pattern as comenqd and correctly enables the binary build for the release workflow's multi-binary handling.

test-support/src/workflow.rs (4)

21-37: LGTM!

The new constants for the upload-release-assets action follow the established pattern for the rust-build-release action. The compile-time concat! approach avoids runtime allocations, consistent with the existing EXPECTED_RUST_BUILDER constant.


71-76: LGTM!

The logic correctly detects the upload-release-assets action by stripping the prefix and comparing the commit hash against SHARED_ACTIONS_COMMIT, mirroring the existing builder detection pattern.


91-107: LGTM!

The test correctly validates that both EXPECTED_RUST_BUILDER and EXPECTED_UPLOAD_RELEASE_ASSETS end with the expected commit hash, and that the function detects both actions when present.


109-121: LGTM!

Good addition of a test case verifying that missing the builder action causes the check to fail, even when the publisher action is present.

.github/workflows/release-dry-run.yml (1)

1-12: LGTM!

The dry-run workflow correctly reuses the release workflow with dry-run: true, enabling PR-triggered validation of the packaging process without publishing artefacts. The secrets: inherit directive ensures required secrets are available to the called workflow.

.github/workflows/release.yml (5)

3-40: LGTM!

The reusable workflow structure with inputs, outputs, and concurrency grouping is well-designed. The cancel-in-progress: true setting appropriately prevents redundant release runs for the same tag.


43-61: LGTM!

The metadata job correctly centralizes version and release mode determination, making downstream jobs cleaner and ensuring consistency across the workflow.


130-141: LGTM!

The idempotent release creation pattern using gh release view ... || gh release create ... correctly handles both new and existing releases. Setting both GITHUB_TOKEN and GH_TOKEN ensures compatibility with both GitHub Actions and the gh CLI.


166-177: Error checks execute after both uploads complete.

The error-checking steps for comenq and comenqd run sequentially after both upload steps, meaning if upload_comenq fails, upload_comenqd still executes before the workflow reports the error. If you intend to fail fast on the first upload error, move each error check immediately after its corresponding upload step. If the intent is to attempt all uploads and report all errors, this ordering is correct—add a comment clarifying this design choice.


146-165: LGTM!

The per-binary upload steps correctly separate concerns and enable independent error tracking for each artefact. The action reference is pinned to the expected commit hash.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1b9dcb00e5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/release.yml Outdated
The rust-build-release shared action builds the manifest at
project-dir/Cargo.toml. Without project-dir, it defaults to the
workspace root which builds comenq-lib (a library with no binary).

By setting project-dir to crates/${{ matrix.bin }}, the action builds
each binary crate directly, ensuring the expected binary is produced
at target/<target>/release/<bin>.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Copy Markdown
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: 2

🤖 Fix all issues with AI agents
In @.github/workflows/release.yml:
- Line 110: The condition mixes JSON-parsed and string comparisons; make them
consistent by parsing both outputs as booleans. Replace the second operand so
the line reads: if:
fromJSON(needs.metadata.outputs.should_upload_workflow_artifacts) ||
fromJSON(needs.metadata.outputs.should_publish) so both
`needs.metadata.outputs.should_upload_workflow_artifacts` and
`needs.metadata.outputs.should_publish` are evaluated via fromJSON().
- Around line 167-178: Replace direct interpolation of `${{
steps.upload_comenq.outputs.error-message }}` and `${{
steps.upload_comenqd.outputs.error-message }}` in the run blocks with
environment variables to avoid shell injection: for each check step (the steps
named "Check comenq upload errors" and "Check comenqd upload errors") add an env
entry that maps ERROR_MESSAGE (or a distinct name like
COMENQ_ERROR_MESSAGE/COMENQD_ERROR_MESSAGE) to the corresponding output (`${{
steps.upload_comenq.outputs.error-message }}` / `${{
steps.upload_comenqd.outputs.error-message }}`), then reference that env var in
the run script and ensure you expand it quoted (e.g., "$ERROR_MESSAGE") when
passing to printf/echo so newlines and special chars are handled safely.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b9dcb0 and 59a1a84.

📒 Files selected for processing (1)
  • .github/workflows/release.yml
⏰ 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). (2)
  • GitHub Check: Sourcery review
  • GitHub Check: build-test
🔇 Additional comments (3)
.github/workflows/release.yml (3)

3-40: Well-structured reusable workflow configuration.

The inputs/outputs and concurrency settings are sensible. Using release-${{ github.ref }} for the concurrency group ensures that concurrent runs for the same tag are properly managed.


43-61: Metadata job correctly extracts version and release modes.

Pinning the shared actions to a specific commit SHA (df81280dcc1d6e66134114dbc924313328b15f05) is good practice for reproducibility and security.


131-142: Verify intentional draft-only release behaviour.

The release is created with --draft but never undrafted. Confirm this is the intended workflow (i.e., manual publish after review) or add a step to publish the release after successful artifact uploads.

leynos and others added 2 commits January 11, 2026 20:25
Update shared-actions to cb06757 which supports the manifest-path
parameter for rust-build-release. This allows building workspace
member crates by specifying their Cargo.toml path directly.

The previous project-dir approach did not work because the action
still used --manifest-path Cargo.toml relative to the checkout root,
building only the root comenq-lib package (a library with no binary).

With manifest-path: crates/${{ matrix.bin }}/Cargo.toml, the action
builds each binary crate directly, producing the expected binaries at
target/<target>/release/<bin>.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
In release builds (without test targets), code only used by tests
appears as dead code. The -D warnings flag treats these as errors.

Changes:
- Gate METADATA_FILE_NAMES and is_metadata_file() with
  #[cfg(any(test, feature = "test-support"))] since they are only
  used in tests
- Remove incorrect #[expect(dead_code)] from WorkerHooks::drained
  field, which is actually used in production code via
  notify_drained_if_empty()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@leynos leynos changed the title Add Linux packaging updates and release dry-run workflow Update Linux packaging and add release dry-run workflow Jan 12, 2026
Copy link
Copy Markdown
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: 2

🤖 Fix all issues with AI agents
In @.github/workflows/release.yml:
- Line 110: The conditional mixes boolean parsing styles: it uses
fromJSON(needs.metadata.outputs.should_upload_workflow_artifacts) but compares
needs.metadata.outputs.should_publish == 'true'; change the second check to
fromJSON(needs.metadata.outputs.should_publish) so both operands use fromJSON()
for consistent boolean handling, updating the if condition that references
should_upload_workflow_artifacts and should_publish accordingly.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59a1a84 and 102a724.

📒 Files selected for processing (4)
  • .github/workflows/release.yml
  • crates/comenqd/src/util.rs
  • crates/comenqd/src/worker.rs
  • test-support/src/workflow.rs
💤 Files with no reviewable changes (1)
  • crates/comenqd/src/worker.rs
🧰 Additional context used
📓 Path-based instructions (1)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Clippy warnings MUST be disallowed.
Fix any warnings emitted during tests in the code itself rather than silencing them.
Where a function is too long, extract meaningfully named helper functions adhering to separation of concerns and CQRS.
Where a function has too many parameters, group related parameters in meaningfully named structs.
Where a function is returning a large error consider using Arc to reduce the amount of data returned.
Write unit and behavioural tests for new functionality. Run both before and after making any change.
Every module must begin with a module level (//! ) comment explaining the module's purpose and utility.
Document public APIs using Rustdoc comments (///) so documentation can be generated with cargo doc.
Prefer immutable data and avoid unnecessary mut bindings.
Handle errors with the Result type instead of panicking where feasible.
Avoid unsafe code unless absolutely necessary and document any usage clearly.
Place function attributes after doc comments.
Do not use return in single-line functions.
Use predicate functions for conditional criteria with more than two branches.
Lints must not be silenced except as a last resort.
Lint rule suppressions must be tightly scoped and include a clear reason.
Prefer expect over allow.
Use rstest fixtures for shared setup.
Replace duplicated tests with #[rstest(...)] parameterised cases.
Prefer mockall for mocks/stubs.
Prefer .expect() over .unwrap().
Use concat!() to combine long string literals rather than escaping newlines with a backslash.
Prefer semantic error enums. Derive std::error::Error (via the thiserror crate) for any condition the caller might inspect, retry, or map to an HTTP status.
Use an opaque error only at the app boundary. Use eyre::Report for human-readable logs; these should not be exposed in public APIs.
Never export the opaque type from a library. Convert to domain enums at API boundaries, and to eyre only in the main main() entrypoint or top-level async task.

Files:

  • crates/comenqd/src/util.rs
  • test-support/src/workflow.rs

⚙️ CodeRabbit configuration file

**/*.rs: * Seek to keep the cognitive complexity of functions no more than 9.

  • Adhere to single responsibility and CQRS
  • Place function attributes after doc comments.
  • Do not use return in single-line functions.
  • Move conditionals with >2 branches into a predicate function.
  • Avoid unsafe unless absolutely necessary.
  • Every module must begin with a //! doc comment that explains the module's purpose and utility.
  • Comments and docs must follow en-GB-oxendict (-ize / -yse / -our) spelling and grammar
  • Lints must not be silenced except as a last resort.
    • #[allow] is forbidden.
    • Only narrowly scoped #[expect(lint, reason = "...")] is allowed.
    • No lint groups, no blanket or file-wide suppression.
    • Include FIXME: with link if a fix is expected.
  • Where code is only used by specific features, it must be conditionally compiled or a conditional expectation for unused_code applied.
  • Use rstest fixtures for shared setup and to avoid repetition between tests.
  • Replace duplicated tests with #[rstest(...)] parameterised cases.
  • Prefer mockall for mocks/stubs.
  • Prefer .expect() over .unwrap() in tests.
  • .expect() and .unwrap() are forbidden outside of tests. Errors must be propagated.
  • Ensure that any API or behavioural changes are reflected in the documentation in docs/
  • Ensure that any completed roadmap steps are recorded in the appropriate roadmap in docs/
  • Files must not exceed 400 lines in length
    • Large modules must be decomposed
    • Long match statements or dispatch tables should be decomposed by domain and collocated with targets
    • Large blocks of inline data (e.g., test fixtures, constants or templates) must be moved to external files and inlined at compile-time or loaded at run-time.
  • Environment access (env::set_var and env::remove_var) are always unsafe in Rust 2024 and MUST be marked as such
    • For testing of functionality depending upon environment variables, dependency injection and...

Files:

  • crates/comenqd/src/util.rs
  • test-support/src/workflow.rs
⏰ 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). (4)
  • GitHub Check: release / Build comenqd for aarch64-unknown-linux-gnu
  • GitHub Check: release / Build comenqd for x86_64-unknown-linux-gnu
  • GitHub Check: Sourcery review
  • GitHub Check: build-test
🔇 Additional comments (12)
crates/comenqd/src/util.rs (4)

1-3: LGTM!

Module-level documentation is present and clearly explains the module's purpose.


5-12: LGTM!

Conditional compilation correctly gates test-only utilities. The pub(crate) visibility for METADATA_FILE_NAMES is appropriate.


30-53: LGTM!

Tests are well-structured with parameterised rstest cases covering both positive and negative scenarios. Good inclusion of edge cases like the empty string.


14-28: No issues identified. The doc example import path is correct—is_metadata_file is re-exported from the daemon module at line 23 of daemon.rs. The example assertions are also accurate: "version" is in METADATA_FILE_NAMES, and "0001" is not. The doctest will pass.

test-support/src/workflow.rs (4)

21-24: LGTM: New constants follow the established pattern.

The new UPLOAD_RELEASE_ASSETS_PREFIX and EXPECTED_UPLOAD_RELEASE_ASSETS constants mirror the existing builder constants appropriately. The #[cfg(test)] gating on EXPECTED_UPLOAD_RELEASE_ASSETS correctly restricts it to test builds only.

Also applies to: 32-37


71-76: LGTM: Publisher detection logic correctly mirrors builder detection.

The pattern of strip_prefix followed by is_some_and for commit verification is consistent with the existing approach at lines 65-70.


86-106: LGTM: Tests updated to verify both builder and publisher actions.

The detects_shared_actions test now validates that both action references end with the expected commit hash and that the function correctly detects workflows containing both actions.


109-165: LGTM: Negative test cases appropriately updated.

The missing_builder_fails, mismatched_builder_commit_fails, and unpinned_builder_fails tests now include the publisher action where appropriate, ensuring they test the intended failure conditions in isolation.

.github/workflows/release.yml (4)

3-40: LGTM: Reusable workflow configuration is well-structured.

The workflow_call trigger with publish and dry-run inputs enables flexible reuse. The concurrency group keyed on github.ref with cancel-in-progress: true correctly prevents parallel runs for the same tag.


43-62: LGTM: Metadata job centralises version and mode computation.

Extracting version reading and release mode determination into a dedicated job ensures consistent values propagate to downstream jobs.


131-142: LGTM: Draft release creation with idempotent check.

The gh release view ... || gh release create pattern correctly ensures a draft release exists without failing if one already exists. The --verify-tag flag adds useful validation.


140-142: Remove redundant GITHUB_TOKEN from the gh release create step; verify token requirements for custom action.

Lines 140-142 set both tokens for the gh release create command, which uses only GH_TOKEN. Remove the redundant GITHUB_TOKEN here.

For lines 154-156 and 164-166, the leynos/shared-actions repository is not publicly accessible. Verify with the action's source code whether upload-release-assets requires both tokens or only GH_TOKEN. If only one is needed, remove the redundant variable from all three locations.

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml Outdated
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jan 12, 2026

@coderabbitai Have the following now been resolved?

Please address the comments from this code review:

## Overall Comments
- The `workflow_call` inputs (`publish`, `dry-run`) aren’t passed into `determine-release-modes`, so it’s not obvious how those flags influence `should_publish`/`dry_run`; consider wiring the inputs explicitly into that action (or documenting that it derives everything from context only) to avoid surprises when reusing the workflow.
- The `rust-build-release` step no longer receives a `version` input whereas `linux-packages` does; if the build action still supports/needs explicit versioning, it may be clearer and safer to pass through `needs.metadata.outputs.version` there as well for consistency.

## Individual Comments

### Comment 1
<location> `.github/workflows/release.yml:7-16` </location>
<code_context>
   push:
     tags:
       - 'v*.*.*'
+  workflow_call:
+    inputs:
+      publish:
+        description: >-
+          Whether the workflow should publish artefacts to the GitHub release
+          associated with the tag. Defaults to `false` when invoked as a
+          reusable workflow.
+        required: false
+        type: boolean
+        default: false
+      dry-run:
+        description: >-
</code_context>

<issue_to_address>
**issue (bug_risk):** workflow_call inputs are defined but never referenced in jobs, so they currently don't influence behaviour

`publish` and `dry-run` are defined as `workflow_call` inputs but not used anywhere (e.g., via `inputs.publish` / `inputs['dry-run']` or when calling `determine-release-modes`). As a result, callers can set them but they won’t affect behaviour, which is currently driven only by `determine-release-modes`. Consider either wiring these inputs into that action/conditions or removing them if they’re not needed.
</issue_to_address>

### Comment 2
<location> `test-support/src/workflow.rs:87` </location>
<code_context>
 mod tests {
     use super::{
-        EXPECTED_RUST_BUILDER, EXPECTED_SHARED_ACTIONS_COMMIT, uses_shared_release_actions,
+        EXPECTED_RUST_BUILDER, EXPECTED_SHARED_ACTIONS_COMMIT, EXPECTED_UPLOAD_RELEASE_ASSETS,
+        uses_shared_release_actions,
     };
</code_context>

<issue_to_address>
**suggestion (testing):** Add negative tests for the new upload-release-assets action detection (missing, wrong commit, unpinned).

Current tests only validate the happy path and builder-related failures; they don’t independently exercise the new `UPLOAD_RELEASE_ASSETS_PREFIX` / `EXPECTED_UPLOAD_RELEASE_ASSETS` logic.

Please add negative tests for the upload-release-assets action itself, e.g.:
- Workflow has the correct builder step but no upload-release-assets step → `uses_shared_release_actions` returns `false`.
- Workflow has the correct builder step and upload-release-assets with the right prefix but wrong commit → `false`.
- Workflow uses an unpinned upload-release-assets reference (e.g. `@v1` tag) instead of a commit → `false`.

These can mirror the existing builder negative tests so the pinning requirement is enforced consistently for both steps.

Suggested implementation:

```rust
#[cfg(test)]
mod tests {
    use super::{
        EXPECTED_RUST_BUILDER,
        EXPECTED_SHARED_ACTIONS_COMMIT,
        EXPECTED_UPLOAD_RELEASE_ASSETS,
        uses_shared_release_actions,
    };

    // Existing tests remain here...
}

```

Please add the following new tests inside the `mod tests` block (reusing whatever workflow-building helpers the existing builder tests use). They should live alongside the existing negative tests for the builder action.

```rust
    #[test]
    fn uses_shared_release_actions_false_when_upload_release_assets_step_missing() {
        // Workflow has the correct builder step but NO upload-release-assets step.
        let workflow = Workflow::from_yaml_str(
            r#"
            jobs:
              release:
                steps:
                  - name: Build release
                    uses: ${EXPECTED_RUST_BUILDER}
            "#,
        )
        .expect("valid workflow yaml");

        assert!(
            !uses_shared_release_actions(&workflow),
            "Expected uses_shared_release_actions to be false when upload-release-assets step is missing",
        );
    }

    #[test]
    fn uses_shared_release_actions_false_when_upload_release_assets_wrong_commit() {
        // Workflow has the correct builder step and upload-release-assets step,
        // but the upload-release-assets reference is pinned to the wrong commit.
        let workflow = Workflow::from_yaml_str(
            r#"
            jobs:
              release:
                steps:
                  - name: Build release
                    uses: ${EXPECTED_RUST_BUILDER}
                  - name: Upload release assets
                    uses: leynos/shared-actions/.github/actions/upload-release-assets@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
            "#,
        )
        .expect("valid workflow yaml");

        assert!(
            !uses_shared_release_actions(&workflow),
            "Expected uses_shared_release_actions to be false when upload-release-assets is pinned to the wrong commit",
        );
    }

    #[test]
    fn uses_shared_release_actions_false_when_upload_release_assets_unpinned() {
        // Workflow has the correct builder step and upload-release-assets step,
        // but upload-release-assets is referenced via a tag (e.g. @v1) instead of a commit SHA.
        let workflow = Workflow::from_yaml_str(
            r#"
            jobs:
              release:
                steps:
                  - name: Build release
                    uses: ${EXPECTED_RUST_BUILDER}
                  - name: Upload release assets
                    uses: leynos/shared-actions/.github/actions/upload-release-assets@v1
            "#,
        )
        .expect("valid workflow yaml");

        assert!(
            !uses_shared_release_actions(&workflow),
            "Expected uses_shared_release_actions to be false when upload-release-assets is unpinned (tagged ref)",
        );
    }
```

You will need to adjust the workflow-construction to match the helpers already in this file. For example:

- If there is a helper like `parse_workflow(yaml: &str) -> Workflow`, use that instead of `Workflow::from_yaml_str`.
- If tests interpolate constants differently (e.g. via `format!()` instead of `${...}`), update the YAML strings accordingly:

```rust
let yaml = format!(
    r#"
    jobs:
      release:
        steps:
          - name: Build release
            uses: {builder}
          - name: Upload release assets
            uses: {upload_assets}
    "#,
    builder = EXPECTED_RUST_BUILDER,
    upload_assets = format!(
        "leynos/shared-actions/.github/actions/upload-release-assets@{}",
        EXPECTED_SHARED_ACTIONS_COMMIT,
    ),
);
let workflow = parse_workflow(&yaml);
```

Also ensure `EXPECTED_UPLOAD_RELEASE_ASSETS` is defined (likely mirroring `EXPECTED_RUST_BUILDER`), and consider using it in tests where you want the *correct* upload-release-assets reference so the negative tests clearly show only the mismatching parts (wrong commit or tag).
</issue_to_address>

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 12, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the release workflow to check if inputs are being used
cat .github/workflows/release.yml

Length of output: 6466


🏁 Script executed:

#!/bin/bash
# Read the workflow test file to check if negative tests have been added
cat test-support/src/workflow.rs

Length of output: 6579


@leynos, unfortunately, neither issue has been fully resolved:

Issue 1: workflow_call inputs not being used ❌ Not resolved

The publish and dry-run inputs are still defined (lines 7-22 of .github/workflows/release.yml) but never referenced anywhere in the workflow. Specifically:

  • Line 50 calls determine-release-modes without any inputs
  • There are no references to inputs.publish or inputs['dry-run'] in any job or step
  • The workflow's behaviour is still driven entirely by the determine-release-modes action's internal logic

This means callers can set these inputs, but they won't affect behaviour.

Issue 2: Missing negative tests for upload-release-assets ⚠️ Partially resolved

You've added missing_publisher_fails (line 123-132), which checks when the upload-release-assets step is missing. However, two critical tests are still missing:

  1. Test for mismatched publisher commit (analogous to mismatched_builder_commit_fails at line 134)
  2. Test for unpinned publisher (analogous to unpinned_builder_fails at line 149)

These tests should verify that uses_shared_release_actions returns false when:

  • The upload-release-assets action is pinned to a different commit SHA
  • The upload-release-assets action uses a tag reference (e.g., @v1) instead of a commit SHA

Would you like me to generate the missing tests for issue 2?

leynos and others added 2 commits January 12, 2026 22:56
Pass inputs.dry-run and inputs.publish to the determine-release-modes
action so that when release-dry-run.yml invokes the workflow with
dry-run: true, the action respects that override instead of relying
solely on event context.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace string comparison `== 'true'` with `fromJSON()` for
  consistent boolean parsing across all workflow conditions
- Combine sequential upload error checks into a single step that
  reports all errors before failing, rather than terminating on the
  first failure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@leynos leynos changed the title Update Linux packaging and add release dry-run workflow Add release dry-run workflow and versioned Linux packaging Jan 12, 2026
Copy link
Copy Markdown
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: 2

🤖 Fix all issues with AI agents
In @.github/workflows/release.yml:
- Around line 38-40: The workflow sets concurrency with group: release-${{
github.ref }} and cancel-in-progress: true which can abort tag-triggered release
runs mid-upload; change cancel-in-progress to false or narrow the concurrency
group (e.g., include the tag name or a more specific identifier) so that release
workflows (the concurrency block using group and cancel-in-progress) are not
cancelled while performing multi-step uploads and can complete fully.
- Around line 170-186: The "Check upload errors" step interpolates outputs like
steps.upload_comenq.outputs.error-message directly into the run script which
risks shell injection; instead export those outputs as environment variables
(e.g., ERROR_MSG_COMENQ and ERROR_MSG_COMENQD and flags
UPLOAD_ERROR_COMENQ/UPLOAD_ERROR_COMENQD) via the step's env: block, then
reference them inside the script as "$ERROR_MSG_COMENQ" and "$ERROR_MSG_COMENQD"
(and test "$UPLOAD_ERROR_COMENQ" == "true") and use printf '%s\n'
"$ERROR_MSG_COMENQ" to safely print the messages. Ensure you update the step
named "Check upload errors" to use these env names and preserve the has_error
logic and exit 1 behavior.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 102a724 and 129d5c3.

📒 Files selected for processing (1)
  • .github/workflows/release.yml
🧰 Additional context used
🔍 Remote MCP Deepwiki

Based on my search of the comenq repository, here's the relevant context for reviewing this PR:

Summary of Relevant Repository Context

Project Overview

The comenq system is a fault-tolerant GitHub comment management service implemented in Rust that enables automated posting of GitHub Pull Request comments with mandatory rate limiting. The system consists of a comenq CLI client and a comenqd daemon that manages persistent queuing.

Binary Structure

The comenq binary acts as a command-line interface tool that parses user input and connects to the comenqd daemon via a Unix Domain Socket, while comenqd is a long-running background process responsible for managing a persistent job queue and interacting with the GitHub API.

Current Release Process

The release process is automated and triggered by pushing a semantic version tag to the repository, using GitHub Actions and GoReleaser to build, package, and distribute binaries for various platforms. The workflow uses goreleaser/goreleaser-action@v5 which handles building binaries, creating archives, generating native packages (RPM/DEB) for Linux, and creating Homebrew formulas for macOS.

Key Context for PR Review

Workflow Inputs vs. Determine-Release-Modes Action
The PR introduces workflow_call inputs (publish and dry-run) to the reusable release workflow, but according to the reviewer's concerns in the PR objectives, these inputs are defined but not being passed to the determine-release-modes action—this is a critical gap since callers of the workflow (like the new release-dry-run.yml) can set these inputs without affecting behavior.

Testing Coverage
The test suite includes comprehensive BDD tests with dedicated World objects like ReleaseWorld that validate aspects of the release workflow, but the PR reviewer notes that negative tests for the new upload-release-assets action detection are missing (mismatched commit and unpinned ref scenarios).

Packaging and Service Integration
The package includes systemd integration for Linux with security hardening, automated user/group creation, and service file installation, which is relevant for understanding the full release artifact scope.,,

⏰ 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). (4)
  • GitHub Check: release / Build comenqd for aarch64-unknown-linux-gnu
  • GitHub Check: release / Build comenqd for x86_64-unknown-linux-gnu
  • GitHub Check: build-test
  • GitHub Check: Sourcery review
🔇 Additional comments (4)
.github/workflows/release.yml (4)

54-64: Inputs correctly wired to determine-release-modes.

The dry-run and publish inputs are now properly forwarded to the determine-release-modes action, and the fromJSON() usage for boolean parsing in check-tag is appropriate. This addresses the reviewer's earlier concern about inputs being defined but not referenced.


134-145: Draft release creation logic is sound.

The gh release view || gh release create pattern handles the idempotent creation of draft releases appropriately. Setting both GITHUB_TOKEN and GH_TOKEN is slightly redundant (the gh CLI prefers GH_TOKEN), but it ensures compatibility and causes no harm.


150-169: Asset upload steps are well-structured.

Separating uploads for comenq and comenqd with distinct step IDs enables granular error tracking. The shared action is correctly pinned to a specific commit for reproducibility.


95-107: Remove this concern.

The rust-build-release action does not accept a version input parameter. Its inputs are limited to target, project-dir, manifest-path, and bin-name. Since the action reads build metadata directly from the Cargo.toml file specified via manifest-path, passing an explicit version parameter would be both unnecessary and impossible (the action would reject an unknown input).

Likely an incorrect or invalid review comment.

Comment thread .github/workflows/release.yml Outdated
Comment thread .github/workflows/release.yml
- Set cancel-in-progress to false so release workflows complete fully
  rather than being cancelled mid-upload when a new workflow starts
- Fix shell injection risk in upload error checking by exporting step
  outputs as environment variables and referencing them safely within
  the script

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jan 16, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 16, 2026

✅ 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.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jan 16, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 16, 2026

✅ 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.

@leynos
Copy link
Copy Markdown
Owner Author

leynos commented Jan 16, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 16, 2026

✅ 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.

Add tests to verify uses_shared_release_actions returns false when the
upload-release-assets action is pinned to a different commit SHA or uses
a tag reference instead of a commit SHA.

- mismatched_publisher_commit_fails: verifies detection of wrong SHA
- unpinned_publisher_fails: verifies detection of tag reference (@v1)

These tests are analogous to the existing mismatched_builder_commit_fails
and unpinned_builder_fails tests for the rust-build-release action.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@leynos leynos changed the title Add release dry-run workflow and versioned Linux packaging Release dry-run workflow and versioned Linux packaging Jan 16, 2026
Copy link
Copy Markdown
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: 1

🤖 Fix all issues with AI agents
In `@test-support/src/workflow.rs`:
- Around line 167-195: The four near-identical tests
mismatched_builder_commit_fails, unpinned_builder_fails,
mismatched_publisher_commit_fails, and unpinned_publisher_fails should be
consolidated using rstest parameterisation to remove duplication; replace the
four separate #[test] functions with a single #[rstest] function that takes
parameters for the step string (builder vs publisher variant) and an expected
boolean, call uses_shared_release_actions(&yaml).expect("parse") inside the
parametrised test, and reference the EXPECTED_RUST_BUILDER constant and the
uses_shared_release_actions function to build each yaml case; ensure the test
attributes map the four cases to the same assertion logic.

Comment thread test-support/src/workflow.rs Outdated
Replace four near-identical test functions (mismatched_builder_commit_fails,
unpinned_builder_fails, mismatched_publisher_commit_fails,
unpinned_publisher_fails) with a single parameterised rstest function
that covers all four cases.

- Add rstest as dev-dependency to test-support crate
- Use #[case] attributes to define each invalid pinning scenario
- Reduce test code duplication while maintaining full coverage

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@leynos leynos changed the title Release dry-run workflow and versioned Linux packaging Release dry-run workflow with per-binary Linux packaging Jan 16, 2026
Copy link
Copy Markdown
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: 1

🤖 Fix all issues with AI agents
In `@test-support/src/workflow.rs`:
- Around line 138-166: The test function invalid_action_pinning_fails uses
.expect("parse") but is missing the lint-expect attribute; add
#[expect(clippy::expect_used, reason = "simplify test output")] immediately
after the existing #[rstest] attribute above fn invalid_action_pinning_fails to
match other tests and silence the clippy expectation for the .expect call.

Comment thread test-support/src/workflow.rs
@leynos leynos merged commit 89157e0 into main Jan 17, 2026
9 checks passed
@leynos leynos deleted the terragon/update-linux-packaging-release-dry-run-uv2jzi branch January 17, 2026 02:16
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