Skip to content

Consolidate linting infrastructure with reusable workflows - #47

Merged
juliolmuller merged 21 commits into
mainfrom
fix/scripts
Jun 4, 2026
Merged

Consolidate linting infrastructure with reusable workflows#47
juliolmuller merged 21 commits into
mainfrom
fix/scripts

Conversation

@juliolmuller

@juliolmuller juliolmuller commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Add reusable lint/test workflows, a manual publish-and-release workflow, and a CLI release generator.
  • Chores

    • Introduce shared monorepo helper tooling and new lint entrypoints; replace per-package config shims with centralized behavior and update CI action pins; update pre-push hook to run CI-oriented composer scripts.
  • Style

    • Ignore release output directory in repository ignores.

juliolmuller and others added 9 commits May 31, 2026 11:54
- Refactored `.php-cs-fixer.config.php` for improved directory handling.
- Updated `.php-stan.config.neon` comment for clarity.
- Modified `composer.json` to streamline linting commands.
- Added new scripts for lint checking and formatting.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
… linting

- Deleted `.php-cs-fixer.config.php` and `.php-stan.config.neon` files from multiple packages.
- Updated `composer.json` in each package to replace linting commands with new script references.
- Removed unused development dependencies related to PHP CS Fixer and PHPStan.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This is an optimization change due to changes on linting configuration, which migrated from per-package config files to root-level ones.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
- Updated PHPStan configuration to exclude vendor paths.
- Refactored linting commands in composer.json files across multiple packages to include package names.
- Improved linting scripts to handle package directories and resolve paths more effectively.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
- Standardized the `lint:ci` command in multiple package `composer.json` files to use `--dry-run` options.
- Enhanced the linting scripts to support a new `dryRun` parameter for better control during lint checks and formatting.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
- Updated `actions/checkout` from v6.0.2 to v6.0.3 in multiple workflows for improved performance and security.
- Upgraded `toshimaru/auto-author-assign` from v3.0.1 to v3.0.2 for better author assignment functionality.
- Updated `webfactory/ssh-agent` from v0.9.1 to v0.10.0 to leverage new features and fixes.
- Introduced `.lint.yml` and `.test.yml` workflows to standardize linting and testing processes across packages.
- Updated `ci.yml` to utilize these new workflows, improving maintainability and consistency in CI processes.
- Configured inputs for PHP versions and package names to enhance flexibility in running jobs.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
- Replaced `php run lint` with `composer run lint:ci` for consistency with the new CI workflows.
- Updated `php run test` to `composer run test` to align with the standardized testing process.
@juliolmuller
juliolmuller requested a review from a team as a code owner June 3, 2026 03:44
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds monorepo helpers and CLI lint scripts, centralizes lint/test into reusable GitHub workflows, removes per-package lint wrappers, refactors staged linting to use shared utilities, bumps workflow action pins, and adds a release workflow plus a release-note generator.

Changes

Monorepo Linting & CI

Layer / File(s) Summary
Reusable workflows & CI delegation
.github/workflows/.lint.yml, .github/workflows/.test.yml, .github/workflows/ci.yml
Add reusable lint/test workflows with package/PHP matrices; main CI now delegates lint/test jobs to these workflows and passes discovered package lists and PHP versions.
Workflow pins & small updates
.github/workflows/*
Update pinned action revisions across workflows (e.g., actions/checkout → v6.0.3, webfactory/ssh-agent → v0.10.0) and small uses: pin bumps.
Root configs & composer wiring
.php-cs-fixer.config.php, .php-stan.config.neon, composer.json, .captainhook.config.json, .gitignore
Root php-cs-fixer now returns a Config instance, phpstan excludes vendor, composer scripts delegate to new scripts/lint-* entrypoints and add dev deps, pre-push hook updated to run composer run lint:ci & composer run test, and .release/ is ignored.
Shared helpers and CLI entrypoints
scripts/helpers.php, scripts/lint-format.php, scripts/lint-check.php
Add monorepo path, process, and git helpers; new CLI scripts resolve package targets, group by package, and run php-cs-fixer/phpstan per-package.
Staged lint scripts refactor
scripts/lint-staged.php, scripts/phpstan-staged.php
Refactor staged linting to use shared helpers, run vendor binaries via helpers, perform package-aware php-cs-fixer invocations, and stage fixed files with helper-driven git operations.
Per-package wrappers removed & package composer updates
packages/*/.php-cs-fixer.config.php, packages/*/.php-stan.config.neon, packages/*/composer.json
Remove per-package php-cs-fixer/phpstan wrapper files and update package lint:ci scripts to call root entrypoints in dry-run mode; adjust package require-dev blocks accordingly.
Release workflow & script
.github/workflows/release.yml, scripts/release.php
Add release pipeline that runs lint/test, generates release notes from CHANGELOG.md into .release/<package>@<version>.md, validates tag/branch state, creates a GitHub Release, and syncs subtree tags.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • LacusSolutions/br-utils-php#36: Both PRs modify the CI workflow package discovery and matrix behavior; likely related to the workflow delegation and package inclusion changes.
  • LacusSolutions/br-utils-php#35: Overlaps in CI matrix/package inclusion changes and how newly discovered packages are exercised by lint/test workflows.

"🐰 I hopped through scripts and workflows with cheer,
Helpers and linters now gather near.
Workflows reused, configs made small,
Release notes ready — I danced down the hall.
Carrots and commits, tidy for all!"

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title Check ✅ Passed Title check skipped as CodeRabbit has written the PR title.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/scripts

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

❤️ Share

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

@juliolmuller juliolmuller linked an issue Jun 3, 2026 that may be closed by this pull request
4 tasks
@coderabbitai coderabbitai Bot changed the title @coderabbitai Consolidate linting infrastructure with reusable workflows Jun 3, 2026
@coderabbitai coderabbitai Bot added the refactoring Non-functional improvements or changes. label Jun 3, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 9

Caution

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

⚠️ Outside diff range comments (1)
scripts/lint-staged.php (1)

46-66: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

issue (blocking): Return a non-zero status when PhpStan fails.

This branch logs the PhpStan failure and then still exits 0 after running php-cs-fixer, so the hook passes even when static analysis found blocking errors.

Suggested fix
         $phpStanResult = $this->runPhpStan($phpFiles);

         if ($phpStanResult !== 0) {
-            echo "❌ PhpStan found issues. Fixing with php-cs-fixer...\n";
+            echo "❌ PhpStan found issues on staged files.\n";
+            return $phpStanResult;
         }

         echo "\n🔧 Running php-cs-fixer on staged files...\n";
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/lint-staged.php` around lines 46 - 66, The script currently logs
PhpStan failures but still returns 0; update the control flow so that when
runPhpStan($phpFiles) yields a non-zero $phpStanResult the hook ultimately
returns a non-zero exit code (e.g., return $phpStanResult or 1) instead of
returning 0. Concretely, after calling runPhpStan($phpFiles) and optionally
running runPhpCsFixer($phpFiles) and addFilesToStaging($phpFiles), ensure the
function checks $phpStanResult and returns a non-zero status when it failed;
keep runPhpCsFixer and adding fixed files but do not override the failing exit
code from runPhpStan.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/.lint.yml:
- Around line 36-40: The lint workflow unnecessarily enables Xdebug via the
shivammathur/setup-php action (see uses: shivammathur/setup-php@... and
coverage: xdebug), which adds overhead but no coverage is collected; update the
workflow by disabling coverage for the lint matrix—either remove the coverage:
xdebug key or set it to none—so the setup-php step for the matrix.php-version
runs without enabling Xdebug.

In @.github/workflows/.test.yml:
- Around line 44-48: The workflow currently enables Xdebug via the setup-php
step (coverage: xdebug) even though the job only runs composer test and does not
publish coverage; remove or conditionally disable the coverage setting in the
shivammathur/setup-php action (the block containing php-version, extensions,
coverage) so Xdebug is not installed by default — e.g., delete or change
coverage: xdebug to not set coverage (or set to none) unless a
coverage-publishing job is added, ensuring the setup-php invocation no longer
forces Xdebug across the test matrix.
- Around line 24-27: Update the reusable workflow secret contract so the
api-token secret is required by changing the secrets.api-token.required setting
to true (so dispatch fails early when API_TOKEN is missing) and remove the
coverage: xdebug option from the shivammathur/setup-php action block in
.github/workflows/.test.yml since this workflow runs composer test (phpunit)
without enabling coverage; search for the secrets.api-token key and the
shivammathur/setup-php step to apply these two edits.

In @.github/workflows/license-update.yml:
- Line 18: The actions/checkout step currently uses
actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 (v6.0.3) and relies on
the default persist-credentials: true; update both checkout occurrences to
explicitly set persist-credentials: false to avoid leaving write-capable
credentials in the job environment (i.e., add persist-credentials: false under
each actions/checkout step so later git commands won’t inherit the
GITHUB_TOKEN).

In @.github/workflows/subtrees-sync.yml:
- Line 18: Update the pinned commit for actions/checkout@v6.0.3 to the correct
SHA: replace the incorrect df4cb1c069e1874edd31b4311f1884172cec0e10 with
9f698171ed81b15d1823a05fc7211befd50c8ae0 where actions/checkout@v6.0.3 is used
(also verify and update the other occurrence of the same pin noted in the diff);
leave the webfactory/ssh-agent@v0.10.0 pin as-is since its SHA already matches.

In `@scripts/helpers.php`:
- Around line 146-175: split_script_arguments currently forwards every
dash-prefixed argument, allowing callers to override important flags; fix this
by restricting forwarded tool flags to an allowlist inside lint_tool_options:
update lint_tool_options to define an allowlist array of safe long/short flags
(e.g. exclude --config, --configuration, -c, --rules, --generate-baseline,
etc.), filter $splitArguments['options'] against that allowlist (only keep
allowed flags), then proceed with the existing dry-run merging/unique logic and
return the filtered list; reference the split_script_arguments and
lint_tool_options symbols to locate where to apply the change.
- Around line 178-257: resolve_lint_paths() currently defaults only to
packages/* and package_directory_for_absolute()/group_lint_paths_by_package()
reject anything outside packages/, which prevents linting repo-root files
(scripts/, config files). Update resolve_lint_paths to include monorepo_root()
(or a distinct "root" target like Path::join(monorepo_root()) or '') when no
path arguments are provided; modify package_directory_for_absolute to return
monorepo_root() (or a stable "root" directory value) for paths that are inside
the repo but not under packages/ instead of null; and change
group_lint_paths_by_package to accept that root directory as a valid group (do
not exit when package_directory is the monorepo root), mapping repo-root
relative paths into that group so scripts/* and root config files are included
in lint runs.

In `@scripts/lint-staged.php`:
- Around line 129-136: The addFilesToStaging method currently catches
RuntimeException from run_git(['add', $file]) and only echoes a warning,
allowing the script to continue and incorrectly report success; change it to
propagate failures by either rethrowing the caught RuntimeException or tracking
any failed files and exiting non-zero after the loop. Specifically update
addFilesToStaging to (a) call run_git(['add', $file]) inside the try block and
on catch either throw $exception; or mark the file as failed and after
iterating, if any failures exist call exit(1) or throw a RuntimeException so the
caller does not report "Fixed files added to the staging area." Ensure
references to addFilesToStaging and run_git are used so the change is applied in
the correct location.
- Around line 85-91: The current check skips any staged file when
package_directory_for($file) returns null, which unintentionally excludes
root-level PHP files (e.g., scripts/*.php and repo config files); update the
conditional in scripts/lint-staged.php so that files are only skipped when
package_directory_for($file) === null AND the file is not a PHP file (use
pathinfo($file, PATHINFO_EXTENSION) === 'php') and not in known root folders
(e.g., check strpos($file, 'scripts/') !== 0 or match config filenames). In
short, change the early continue so PHP files and root scripts/config files are
allowed through (using the existing package_directory_for($file) call and the
$file variable to locate the affected logic).

---

Outside diff comments:
In `@scripts/lint-staged.php`:
- Around line 46-66: The script currently logs PhpStan failures but still
returns 0; update the control flow so that when runPhpStan($phpFiles) yields a
non-zero $phpStanResult the hook ultimately returns a non-zero exit code (e.g.,
return $phpStanResult or 1) instead of returning 0. Concretely, after calling
runPhpStan($phpFiles) and optionally running runPhpCsFixer($phpFiles) and
addFilesToStaging($phpFiles), ensure the function checks $phpStanResult and
returns a non-zero status when it failed; keep runPhpCsFixer and adding fixed
files but do not override the failing exit code from runPhpStan.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2c58200b-1d75-4f5a-93aa-8d8f648128b5

📥 Commits

Reviewing files that changed from the base of the PR and between 540c995 and 84d1711.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (54)
  • .captainhook.config.json
  • .github/workflows/.lint.yml
  • .github/workflows/.test.yml
  • .github/workflows/ci.yml
  • .github/workflows/license-update.yml
  • .github/workflows/pr-author-assign.yml
  • .github/workflows/subtrees-sync.yml
  • .github/workflows/subtrees-tags-sync.yml
  • .php-cs-fixer.config.php
  • .php-stan.config.neon
  • composer.json
  • packages/br-utils/.php-cs-fixer.config.php
  • packages/br-utils/.php-stan.config.neon
  • packages/br-utils/composer.json
  • packages/cnpj-dv/.php-cs-fixer.config.php
  • packages/cnpj-dv/.php-stan.config.neon
  • packages/cnpj-dv/composer.json
  • packages/cnpj-fmt/.php-cs-fixer.config.php
  • packages/cnpj-fmt/.php-stan.config.neon
  • packages/cnpj-fmt/composer.json
  • packages/cnpj-gen/.php-cs-fixer.config.php
  • packages/cnpj-gen/.php-stan.config.neon
  • packages/cnpj-gen/composer.json
  • packages/cnpj-utils/.php-cs-fixer.config.php
  • packages/cnpj-utils/.php-stan.config.neon
  • packages/cnpj-utils/composer.json
  • packages/cnpj-val/.php-cs-fixer.config.php
  • packages/cnpj-val/.php-stan.config.neon
  • packages/cnpj-val/composer.json
  • packages/cpf-dv/.php-cs-fixer.config.php
  • packages/cpf-dv/.php-stan.config.neon
  • packages/cpf-dv/composer.json
  • packages/cpf-fmt/.php-cs-fixer.config.php
  • packages/cpf-fmt/.php-stan.config.neon
  • packages/cpf-fmt/composer.json
  • packages/cpf-gen/.php-cs-fixer.config.php
  • packages/cpf-gen/.php-stan.config.neon
  • packages/cpf-gen/composer.json
  • packages/cpf-utils/.php-cs-fixer.config.php
  • packages/cpf-utils/.php-stan.config.neon
  • packages/cpf-utils/composer.json
  • packages/cpf-val/.php-cs-fixer.config.php
  • packages/cpf-val/.php-stan.config.neon
  • packages/cpf-val/composer.json
  • packages/utils/.php-cs-fixer.config.php
  • packages/utils/.php-stan.config.neon
  • packages/utils/composer.json
  • scripts/helpers.php
  • scripts/lint-check.php
  • scripts/lint-format.php
  • scripts/lint-staged.php
  • scripts/phpstan-staged.php
  • scripts/phpunit-sync-configs.php
  • scripts/phpunit.common.xml
💤 Files with no reviewable changes (26)
  • packages/cnpj-fmt/.php-stan.config.neon
  • packages/utils/.php-stan.config.neon
  • packages/cnpj-gen/.php-cs-fixer.config.php
  • packages/cnpj-fmt/.php-cs-fixer.config.php
  • packages/br-utils/.php-cs-fixer.config.php
  • packages/cnpj-utils/.php-cs-fixer.config.php
  • packages/cnpj-val/.php-cs-fixer.config.php
  • packages/cnpj-dv/.php-cs-fixer.config.php
  • packages/cpf-gen/.php-cs-fixer.config.php
  • scripts/phpunit-sync-configs.php
  • packages/cpf-dv/.php-cs-fixer.config.php
  • packages/cnpj-utils/.php-stan.config.neon
  • packages/cpf-val/.php-cs-fixer.config.php
  • packages/cnpj-gen/.php-stan.config.neon
  • packages/cpf-utils/.php-stan.config.neon
  • packages/cpf-val/.php-stan.config.neon
  • packages/utils/.php-cs-fixer.config.php
  • packages/cpf-utils/.php-cs-fixer.config.php
  • packages/br-utils/.php-stan.config.neon
  • packages/cpf-fmt/.php-cs-fixer.config.php
  • packages/cpf-dv/.php-stan.config.neon
  • packages/cnpj-val/.php-stan.config.neon
  • packages/cnpj-dv/.php-stan.config.neon
  • packages/cpf-fmt/.php-stan.config.neon
  • packages/cpf-gen/.php-stan.config.neon
  • scripts/phpunit.common.xml

Comment thread .github/workflows/.lint.yml
Comment thread .github/workflows/.test.yml
Comment thread .github/workflows/.test.yml
Comment thread .github/workflows/license-update.yml
Comment thread .github/workflows/subtrees-sync.yml
Comment thread scripts/helpers.php
Comment thread scripts/helpers.php
Comment thread scripts/lint-staged.php
Comment thread scripts/lint-staged.php Outdated
FIx as per @coderabbitai review comment at #47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
juliolmuller added a commit to LacusSolutions/br-utils-js that referenced this pull request Jun 3, 2026
Adjustment as per @coderabbitai review comment at LacusSolutions/br-utils-php#47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
juliolmuller added a commit to LacusSolutions/br-utils-py that referenced this pull request Jun 3, 2026
Adjustment as per @coderabbitai review comment at LacusSolutions/br-utils-php#47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
juliolmuller and others added 3 commits June 3, 2026 11:18

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
composer.json (1)

13-73: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

issue (blocking): Missing root test script breaks pre-push and root-level test invocations.

.captainhook.config.json runs composer run test on pre-push, but this file only defines test:* and test:all. Without a test alias, pre-push will fail before running package tests.

Proposed fix
   "scripts": {
@@
+    "test": "`@test`:all",
     "test:all": [
       "`@test`:formatters",
       "`@test`:generators",
       "`@test`:validators",
       "`@test`:utils"
     ]
   },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@composer.json` around lines 13 - 73, The root composer scripts lack a "test"
entry so captainhook's pre-push which runs "composer run test" fails; add a
top-level "test" script to composer.json that forwards to the existing umbrella
script (e.g., "test": "`@test`:all") so that "composer run test" runs the defined
"`@test`:all" sequence (verify the change touches the "scripts" object where
"test:all" and other "test:*" entries are defined).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/release.yml:
- Around line 39-47: The prepare-release job currently runs without a needs
dependency so it can execute in parallel with lint and test; modify the workflow
to add needs: [lint, test] (referencing the prepare-release job name) so
prepare-release waits for both the lint and test jobs to succeed before running;
update any downstream expectations if needed but the primary change is adding
the needs dependency to the prepare-release job to prevent releases/tags from
being created when checks fail.
- Around line 55-67: Don't interpolate `${{ inputs.package }}` and `${{
inputs.version }}` directly into the `run:` script; instead set them as
environment variables and reference those safe vars inside the script. Add an
`env:` mapping (e.g., `PKG: ${{ inputs.package }}` and `VERSION: ${{
inputs.version }}`) for the step that runs `php scripts/release.php`, then in
the script use the existing `PKG` and `VERSION` shell variables (quoted as
`"$PKG"` and `"$VERSION"`) and test `if [ -n "$VERSION" ]` to build
`OUTPUT_PATH=$(php scripts/release.php "$PKG" --version "$VERSION")` or fallback
to `OUTPUT_PATH=$(php scripts/release.php "$PKG")`, keeping `set -euo pipefail`
and proper quoting to prevent command injection.
- Around line 24-37: Update the reusable-workflow inputs and job dependencies:
change the php-versions input for the lint and test jobs to use a literal, vars,
or workflow input (not env) so expressions evaluate correctly (modify the with:
php-versions used by the lint and test jobs); add prepare-release to the needs:
list for the create-release job (or stop referencing needs.prepare-release.*) so
outputs/env lookups use an actual dependency; add needs dependencies so
prepare-release runs after lint and test to gate the release; and in the
prepare-release job, stop interpolating inputs directly into run: shell
commands—pass inputs.package and inputs.version via env: or validate/sanitize
them before use to avoid shell injection.

---

Outside diff comments:
In `@composer.json`:
- Around line 13-73: The root composer scripts lack a "test" entry so
captainhook's pre-push which runs "composer run test" fails; add a top-level
"test" script to composer.json that forwards to the existing umbrella script
(e.g., "test": "`@test`:all") so that "composer run test" runs the defined
"`@test`:all" sequence (verify the change touches the "scripts" object where
"test:all" and other "test:*" entries are defined).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f7911bc7-bde3-4516-bb73-52c333d551a3

📥 Commits

Reviewing files that changed from the base of the PR and between 84d1711 and 1d81f93.

📒 Files selected for processing (8)
  • .github/workflows/.lint.yml
  • .github/workflows/.test.yml
  • .github/workflows/license-update.yml
  • .github/workflows/release.yml
  • .github/workflows/subtrees-tags-sync.yml
  • .gitignore
  • composer.json
  • scripts/release.php
💤 Files with no reviewable changes (1)
  • .github/workflows/subtrees-tags-sync.yml

Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml
Comment thread .github/workflows/release.yml
Adjustment as per @coderabbitai review comment at #47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
juliolmuller and others added 4 commits June 4, 2026 11:54
Fix as per @coderabbitai review comment at #47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
Fix as per @coderabbitai review comment at #47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
…ram is passed along

Fix as per @coderabbitai review comment at #47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Fix as per @coderabbitai review comment at #47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (3)
.github/workflows/release.yml (3)

28-28: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

issue (blocking): env context unavailable in reusable workflow with: block.

This will fail with "context 'env' is not allowed here" since env is not among the allowed contexts (github, inputs, matrix, needs, strategy, vars) at jobs.<job>.with.

Proposed fix
   lint:
     uses: ./.github/workflows/.lint.yml
     with:
       packages: ${{ format('["{0}"]', inputs.package) }}
-      php-versions: ${{ format('["{0}"]', env.PHP_VERSION) }}
+      php-versions: '["8.2"]'

   test:
     uses: ./.github/workflows/.test.yml
     with:
       packages: ${{ format('["{0}"]', inputs.package) }}
-      php-versions: ${{ format('["{0}"]', env.PHP_VERSION) }}
+      php-versions: '["8.2"]'

Also applies to: 34-34

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

In @.github/workflows/release.yml at line 28, The reusable-workflow `with:`
block is using an unsupported context `env` for `php-versions`; change it to an
allowed context (e.g. `inputs` or `vars`) and update callers accordingly:
replace uses like php-versions: ${{ format('["{0}"]', env.PHP_VERSION) }} with
php-versions: ${{ format('["{0}"]', inputs.PHP_VERSION) }} (or vars.PHP_VERSION)
in the `with` block of the reusable workflow and ensure the workflow caller
passes `PHP_VERSION` via `with`/inputs (repeat the same fix for the other
occurrence). Ensure the reusable workflow declares the corresponding input (name
`PHP_VERSION`) so callers can provide the value.

55-68: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

issue (blocking): Shell injection risk from direct input interpolation.

${{ inputs.package }} and ${{ inputs.version }} are interpolated directly into the shell script, allowing command injection via crafted input values.

Proposed fix
       - id: release
+        env:
+          INPUT_PACKAGE: ${{ inputs.package }}
+          INPUT_VERSION: ${{ inputs.version }}
         run: |
           set -euo pipefail

-          PKG="${{ inputs.package }}"
+          PKG="$INPUT_PACKAGE"

           echo "📦 Preparing release for package '$PKG'..."

-          if [ -n "${{ inputs.version }}" ]; then
-            echo "📌 Using requested version: ${{ inputs.version }}"
-            OUTPUT_PATH=$(php scripts/release.php "$PKG" --version "${{ inputs.version }}")
+          if [ -n "$INPUT_VERSION" ]; then
+            echo "📌 Using requested version: $INPUT_VERSION"
+            OUTPUT_PATH=$(php scripts/release.php "$PKG" --version "$INPUT_VERSION")
           else
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 55 - 68, The script directly
interpolates `${{ inputs.package }}` and `${{ inputs.version }}` into the shell,
enabling shell injection; fix by treating inputs as data: capture them into
safely-sanitized variables (e.g. PKG and VERSION) using printf/sed or a strict
whitelist regex to strip/validate any characters other than expected (letters,
digits, dot, underscore, hyphen), and always quote expansions and pass them as
separate arguments to php (e.g. PKG="$(printf '%s' '...sanitized...' )";
VERSION="$(printf '%s' '...sanitized...' )"; then call php scripts/release.php
"$PKG" --version "$VERSION" or php scripts/release.php "$PKG" when VERSION is
empty); reference the PKG, VERSION, OUTPUT_PATH variables and the php
scripts/release.php invocation when applying the changes.

161-163: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

issue (blocking): Release not gated on lint/test job success.

The needs fix correctly references prepare-release, but create-release (and downstream sync-tag) will proceed even if lint or test fail since neither is in any dependency chain. Running lint/test jobs that don't gate the release provides false confidence without enforcement.

Proposed fix
   create-release:
     name: Create GitHub Release
     needs:
+      - lint
+      - test
       - prepare-release
       - validate
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 161 - 163, The create-release
(and downstream sync-tag) job is not gated on lint/test; update the job
dependencies so create-release needs both lint and test (in addition to
prepare-release/validate) and ensure sync-tag inherits that chain (or explicitly
needs create-release); modify the needs list for the create-release job (and
sync-tag if present) to include the unique job names "lint" and "test" so
failures in those jobs will block the release flow.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.php-cs-fixer.config.php:
- Around line 8-27: The php-cs-fixer cache file is currently rooted at the
package working directory because $cacheFile is built from $vendorDir which uses
getcwd(); change it so the Finder still uses the package-scoped $dir/$vendorDir
for search/exclude but the cache is placed under the monorepo root vendor
directory instead. Introduce a new $rootVendorDir = __DIR__ .
DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR and set $cacheFile to
"{$rootVendorDir}.php-cs-fixer.cache" while leaving $vendorDir and the
Finder::create()->in($searchDirs) logic unchanged.

---

Duplicate comments:
In @.github/workflows/release.yml:
- Line 28: The reusable-workflow `with:` block is using an unsupported context
`env` for `php-versions`; change it to an allowed context (e.g. `inputs` or
`vars`) and update callers accordingly: replace uses like php-versions: ${{
format('["{0}"]', env.PHP_VERSION) }} with php-versions: ${{ format('["{0}"]',
inputs.PHP_VERSION) }} (or vars.PHP_VERSION) in the `with` block of the reusable
workflow and ensure the workflow caller passes `PHP_VERSION` via `with`/inputs
(repeat the same fix for the other occurrence). Ensure the reusable workflow
declares the corresponding input (name `PHP_VERSION`) so callers can provide the
value.
- Around line 55-68: The script directly interpolates `${{ inputs.package }}`
and `${{ inputs.version }}` into the shell, enabling shell injection; fix by
treating inputs as data: capture them into safely-sanitized variables (e.g. PKG
and VERSION) using printf/sed or a strict whitelist regex to strip/validate any
characters other than expected (letters, digits, dot, underscore, hyphen), and
always quote expansions and pass them as separate arguments to php (e.g.
PKG="$(printf '%s' '...sanitized...' )"; VERSION="$(printf '%s'
'...sanitized...' )"; then call php scripts/release.php "$PKG" --version
"$VERSION" or php scripts/release.php "$PKG" when VERSION is empty); reference
the PKG, VERSION, OUTPUT_PATH variables and the php scripts/release.php
invocation when applying the changes.
- Around line 161-163: The create-release (and downstream sync-tag) job is not
gated on lint/test; update the job dependencies so create-release needs both
lint and test (in addition to prepare-release/validate) and ensure sync-tag
inherits that chain (or explicitly needs create-release); modify the needs list
for the create-release job (and sync-tag if present) to include the unique job
names "lint" and "test" so failures in those jobs will block the release flow.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: effbcf50-0700-4133-b117-19c50d38868d

📥 Commits

Reviewing files that changed from the base of the PR and between 1d81f93 and 2993774.

📒 Files selected for processing (5)
  • .github/workflows/license-update.yml
  • .github/workflows/release.yml
  • .php-cs-fixer.config.php
  • scripts/helpers.php
  • scripts/lint-staged.php

Comment thread .php-cs-fixer.config.php
Fix as per @coderabbitai review comment at #47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
scripts/lint-staged.php (1)

48-72: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

issue (blocking): This hook can exit successfully even when PhpStan or php-cs-fixer fails.

run() never propagates a non-zero $phpStanResult, and runPhpCsFixer() downgrades fixer failures to continue. A staged file can therefore fail analysis or fail formatting altogether, yet the script still returns 0 and lets the commit proceed as if the staged set was validated.

Also applies to: 92-128

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

In `@scripts/lint-staged.php` around lines 48 - 72, The run() flow currently
swallows failures: ensure non-zero results from runPhpStan() and runPhpCsFixer()
propagate to a non-zero exit; specifically, after calling runPhpStan($phpFiles)
check if $phpStanResult !== 0 and immediately return that non-zero code (or
return 1) instead of just echoing and continuing, and modify
runPhpCsFixer($phpFiles) handling so that if it returns a failure status (not
just "hasChanges" boolean or an internal downgrade), run() returns a non-zero
exit code; update both places where php-cs-fixer is invoked (the block shown and
the similar block around 92-128) to treat fixer failures as fatal (return 1)
while preserving the existing addFilesToStaging($phpFiles) error handling.
scripts/helpers.php (1)

238-247: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

issue (blocking): Default lint runs still skip PHP files that live at the repo or package root.

resolve_lint_paths([]) seeds package roots, but normalize_package_lint_paths() immediately narrows empty scopes to src/tests (or scripts for the monorepo root). That means normal lint/check runs still miss root-level PHP config files like .php-cs-fixer.config.php and package-level config scripts such as packages/cpf-dv/.php-cs-fixer.config.php unless they are passed explicitly.

Also applies to: 336-359

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

In `@scripts/helpers.php` around lines 238 - 247, The current resolve_lint_paths()
+ normalize_package_lint_paths() flow narrows an empty scope to subdirs (like
src/tests or scripts) and thus skips root-level PHP files; fix by ensuring
package roots and the repo root are included when no explicit paths are passed.
Specifically, in resolve_lint_paths() keep seeding the package root paths
(Path::join('packages', $name)) and also add the package root itself (i.e., the
package directory without a suffix) and the monorepo root ('.' or 'scripts' as
appropriate) to the returned list so normalize_package_lint_paths() does not
drop root-level files; apply the same change to the duplicate logic at the other
occurrence referenced (lines ~336-359) so root-level config files are always
covered.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@scripts/helpers.php`:
- Around line 238-247: The current resolve_lint_paths() +
normalize_package_lint_paths() flow narrows an empty scope to subdirs (like
src/tests or scripts) and thus skips root-level PHP files; fix by ensuring
package roots and the repo root are included when no explicit paths are passed.
Specifically, in resolve_lint_paths() keep seeding the package root paths
(Path::join('packages', $name)) and also add the package root itself (i.e., the
package directory without a suffix) and the monorepo root ('.' or 'scripts' as
appropriate) to the returned list so normalize_package_lint_paths() does not
drop root-level files; apply the same change to the duplicate logic at the other
occurrence referenced (lines ~336-359) so root-level config files are always
covered.

In `@scripts/lint-staged.php`:
- Around line 48-72: The run() flow currently swallows failures: ensure non-zero
results from runPhpStan() and runPhpCsFixer() propagate to a non-zero exit;
specifically, after calling runPhpStan($phpFiles) check if $phpStanResult !== 0
and immediately return that non-zero code (or return 1) instead of just echoing
and continuing, and modify runPhpCsFixer($phpFiles) handling so that if it
returns a failure status (not just "hasChanges" boolean or an internal
downgrade), run() returns a non-zero exit code; update both places where
php-cs-fixer is invoked (the block shown and the similar block around 92-128) to
treat fixer failures as fatal (return 1) while preserving the existing
addFilesToStaging($phpFiles) error handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: cf560d65-34a1-4db8-8b18-e4093de038bb

📥 Commits

Reviewing files that changed from the base of the PR and between 2993774 and 8d7cf2c.

📒 Files selected for processing (7)
  • .php-cs-fixer.config.php
  • scripts/helpers.php
  • scripts/lint-check.php
  • scripts/lint-format.php
  • scripts/lint-staged.php
  • scripts/phpstan-staged.php
  • scripts/release.php

…bles

Fix as per @coderabbitai review comment at #47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
.github/workflows/release.yml (1)

261-267: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't force-push subtree version tags.

This job never checks whether refs/tags/$PACKAGE_VERSION already exists on the subtree remote, and git push ... --force will rewrite it if it does. That makes published package tags mutable and can silently repoint consumers to a different commit.

Suggested guard
-          git tag "$PACKAGE_VERSION" "refs/tags/$TAG_NAME" -f
+          if git ls-remote --exit-code --tags "$REMOTE_URL" "refs/tags/$PACKAGE_VERSION" >/dev/null 2>&1; then
+            echo "❌ Remote tag '$PACKAGE_VERSION' already exists in $REMOTE_URL"
+            exit 1
+          fi
+
+          git tag "$PACKAGE_VERSION" "refs/tags/$TAG_NAME"
@@
-          git push "remote-repo-${PACKAGE_NAME}" "refs/tags/$PACKAGE_VERSION" --force
+          git push "remote-repo-${PACKAGE_NAME}" "refs/tags/$PACKAGE_VERSION"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release.yml around lines 261 - 267, The workflow is
force-pushing subtree tags (git push ... --force) which can rewrite published
tags; change the logic around the tag push to first check the remote for an
existing ref for refs/tags/$PACKAGE_VERSION (use git ls-remote or similar
against remote-repo-${PACKAGE_NAME}), and if the tag already exists either skip
pushing or fail the job instead of using --force; remove the --force flag from
the git push of refs/tags/$PACKAGE_VERSION so published tags remain immutable
and only push when the remote tag is absent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In @.github/workflows/release.yml:
- Around line 261-267: The workflow is force-pushing subtree tags (git push ...
--force) which can rewrite published tags; change the logic around the tag push
to first check the remote for an existing ref for refs/tags/$PACKAGE_VERSION
(use git ls-remote or similar against remote-repo-${PACKAGE_NAME}), and if the
tag already exists either skip pushing or fail the job instead of using --force;
remove the --force flag from the git push of refs/tags/$PACKAGE_VERSION so
published tags remain immutable and only push when the remote tag is absent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: a908705c-5080-44f3-afa0-73091b02bae1

📥 Commits

Reviewing files that changed from the base of the PR and between 8d7cf2c and eb3bbdf.

📒 Files selected for processing (3)
  • .github/workflows/.lint.yml
  • .github/workflows/.test.yml
  • .github/workflows/release.yml

@juliolmuller
juliolmuller merged commit 4ba088d into main Jun 4, 2026
100 checks passed
@juliolmuller
juliolmuller deleted the fix/scripts branch June 4, 2026 20:31
juliolmuller added a commit to LacusSolutions/br-utils-js that referenced this pull request Jun 10, 2026
Adjustment as per @coderabbitai review comment at LacusSolutions/br-utils-php#47 (comment).

Co-authored-by: CodeRabbit AI <noreply@coderabbit.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactoring Non-functional improvements or changes.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix incomplete and repetitive Composer scripts across monorepo

1 participant