From 87fee24337e99352d0ae8f366024bc9deab88c8a Mon Sep 17 00:00:00 2001 From: D3strukt0r Date: Tue, 14 Apr 2026 14:21:15 +0200 Subject: [PATCH 1/7] chore: refactor export-ignore rules to use a whitelist Update .gitattributes to exclude all files by default and selectively include only necessary files for distribution. This allows for the removal of the redundant archive exclusion configuration in composer.json. --- .gitattributes | 26 +++++++++++--------------- composer.json | 16 ---------------- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/.gitattributes b/.gitattributes index 8a67309..4fac82d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,16 +1,12 @@ -# Created by https://www.richie-bendall.ml/gitattributes-generator/ -# End of https://www.richie-bendall.ml/gitattributes-generator/ - # This is what is relevant for composer installs -.devcontainer export-ignore -.github export-ignore -.vscode export-ignore -.editorconfig export-ignore -.gitignore export-ignore -.gitattributes export-ignore -CODEOWNERS export-ignore -*.md export-ignore -*.dist.php export-ignore -*release*.json export-ignore -README.md -export-ignore export-subst -CHANGELOG.md -export-ignore export-subst +# Whitelist: exclude everything, selectively include for distribution +* export-ignore + +/src/ -export-ignore +/src/** -export-ignore + +/composer.json -export-ignore + +/CHANGELOG.md -export-ignore +/LICENSE.txt -export-ignore +/README.md -export-ignore diff --git a/composer.json b/composer.json index 8aee0ab..a4dba72 100644 --- a/composer.json +++ b/composer.json @@ -41,21 +41,5 @@ "minimum-stability": "stable", "config": { "sort-packages": true - }, - "archive": { - "exclude": [ - ".devcontainer", - ".github", - ".vscode", - ".editorconfig", - ".gitignore", - ".gitattributes", - "CODEOWNERS", - "*.md", - "*.dist.php", - "*release*.json", - "!README.md", - "!CHANGELOG.md" - ] } } From 431c70d8db0f3fb78bb2dbaf0385b4bf9354c0be Mon Sep 17 00:00:00 2001 From: D3strukt0r Date: Tue, 14 Apr 2026 15:07:57 +0200 Subject: [PATCH 2/7] ci: run workflow on all pull requests Remove the branch filter for pull request triggers to ensure CI runs for all pull requests regardless of the target branch. --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fc5ad5..9d71aa1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,6 @@ on: push: branches: [main, develop] pull_request: - branches: [main, develop] permissions: contents: read From 74c6d2f37d90b73befb7d6e74d2739355d758881 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 15 Apr 2026 07:35:23 +0000 Subject: [PATCH 3/7] chore(deps): bump dependabot/fetch-metadata from 2 to 3 Bumps [dependabot/fetch-metadata](https://github.com/dependabot/fetch-metadata) from 2 to 3. - [Release notes](https://github.com/dependabot/fetch-metadata/releases) - [Commits](https://github.com/dependabot/fetch-metadata/compare/v2...v3) --- updated-dependencies: - dependency-name: dependabot/fetch-metadata dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/dependabot-automerge.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 8d17356..e547893 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -20,7 +20,7 @@ jobs: uses: actions/checkout@v6 - name: Fetch Dependabot metadata 🔍 - uses: dependabot/fetch-metadata@v2 + uses: dependabot/fetch-metadata@v3 id: metadata with: github-token: ${{ github.token }} From d952849f259e5760af1289e80ff1459ea4618735 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 07:35:50 +0000 Subject: [PATCH 4/7] chore(deps): bump googleapis/release-please-action from 4 to 5 Bumps [googleapis/release-please-action](https://github.com/googleapis/release-please-action) from 4 to 5. - [Release notes](https://github.com/googleapis/release-please-action/releases) - [Changelog](https://github.com/googleapis/release-please-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/googleapis/release-please-action/compare/v4...v5) --- updated-dependencies: - dependency-name: googleapis/release-please-action dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 387f35f..579fbe3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Create Release PR - uses: googleapis/release-please-action@v4 + uses: googleapis/release-please-action@v5 with: # Bot PAT with access to Workflows # The built-in GITHUB_TOKEN has cannot trigger other workflows From 6af4b5b6a5335d77eaea1d39e8d3d15159e23267 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 07:33:07 +0000 Subject: [PATCH 5/7] chore(deps): bump actions/cache from 5 to 6 Bumps [actions/cache](https://github.com/actions/cache) from 5 to 6. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d71aa1..cb281b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: id: composer-cache run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT - name: Cache Composer dependencies - uses: actions/cache@v5 + uses: actions/cache@v6 with: path: ${{ steps.composer-cache.outputs.dir }} key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} From 5f55ce744abb74c541409d48f8881848c77e4916 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 Jun 2026 07:33:11 +0000 Subject: [PATCH 6/7] chore(deps): bump actions/checkout from 6 to 7 Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 7. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v7) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '7' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- .github/workflows/dependabot-automerge.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d71aa1..2d8e1d6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,7 @@ jobs: name: Lint (PHP ${{ matrix.php-version }}) steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index e547893..38b2623 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -17,7 +17,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Fetch Dependabot metadata 🔍 uses: dependabot/fetch-metadata@v3 From 90a852cc66f6d0e252f2b593fa5bd79f215eb325 Mon Sep 17 00:00:00 2001 From: D3strukt0r Date: Thu, 25 Jun 2026 09:51:15 +0200 Subject: [PATCH 7/7] fix(ci): group dependabot updates and harden release workflow Group Dependabot version updates by ecosystem so related bumps land in one PR: github-actions and devcontainers each group all patterns, while composer groups non-breaking (minor and patch) updates. Pin the weekly schedule explicitly to monday across all three ecosystems. Move the release-please config and manifest into .github (dropping the leading dot from the manifest) and point the action at them via config-file and manifest-file. Add a reattribute job that rewrites the release-PR commit author to github-actions[bot] since the action commits as the PAT owner, and mark it continue-on-error because the PR branch may already be deleted when it runs. Switch the dependabot auto-merge approval to the GH_PAT so the bot's approval counts, and skip the greetings job for dependabot[bot]. Relocate CODEOWNERS into .github and add the Dependabot-managed paths (composer manifests, workflows, devcontainer) co-owned by @iwf-web/CI so the bot's auto-approval satisfies the code-owner requirement. Add a devcontainer-lock.json pinning feature digests. --- .devcontainer/devcontainer-lock.json | 29 ++++++++++++ .github/CODEOWNERS | 15 ++++++ .github/dependabot.yml | 18 +++++++- .../release-please-config.json | 0 .../release-please-manifest.json | 0 .github/workflows/dependabot-automerge.yml | 2 +- .github/workflows/greetings.yml | 1 + .github/workflows/release.yml | 46 +++++++++++++++++++ .gitignore | 1 + CLAUDE.md => AGENTS.md | 10 ++-- CHANGELOG.md | 5 ++ CODEOWNERS | 4 -- LICENSE.txt | 2 +- README.md | 2 +- composer.json | 6 ++- 15 files changed, 127 insertions(+), 14 deletions(-) create mode 100644 .devcontainer/devcontainer-lock.json create mode 100644 .github/CODEOWNERS rename release-please-config.json => .github/release-please-config.json (100%) rename .release-please-manifest.json => .github/release-please-manifest.json (100%) rename CLAUDE.md => AGENTS.md (68%) delete mode 100644 CODEOWNERS diff --git a/.devcontainer/devcontainer-lock.json b/.devcontainer/devcontainer-lock.json new file mode 100644 index 0000000..b920246 --- /dev/null +++ b/.devcontainer/devcontainer-lock.json @@ -0,0 +1,29 @@ +{ + "features": { + "ghcr.io/devcontainers-extra/features/act:1": { + "version": "1.0.15", + "resolved": "ghcr.io/devcontainers-extra/features/act@sha256:db4a2194930d1f7ec62822d4f600dd2fa4aff3c33b98cdb0b578b64ffb10924c", + "integrity": "sha256:db4a2194930d1f7ec62822d4f600dd2fa4aff3c33b98cdb0b578b64ffb10924c" + }, + "ghcr.io/devcontainers/features/common-utils:2": { + "version": "2.5.9", + "resolved": "ghcr.io/devcontainers/features/common-utils@sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a", + "integrity": "sha256:cb0c4d3c276f157eed17935747e364178d75fee17f55c4e129966f64633deb3a" + }, + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { + "version": "1.10.0", + "resolved": "ghcr.io/devcontainers/features/docker-outside-of-docker@sha256:c2c2cf829505ead8e4892c88c31b6594ae94a2bbb209e16e1fac456c1a3a624e", + "integrity": "sha256:c2c2cf829505ead8e4892c88c31b6594ae94a2bbb209e16e1fac456c1a3a624e" + }, + "ghcr.io/devcontainers/features/github-cli:1": { + "version": "1.1.0", + "resolved": "ghcr.io/devcontainers/features/github-cli@sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671", + "integrity": "sha256:d22f50b70ed75339b4eed1ba9ecde3a1791f90e88d37936517e3bace0bbad671" + }, + "ghcr.io/devcontainers/features/php:1": { + "version": "1.1.5", + "resolved": "ghcr.io/devcontainers/features/php@sha256:28f032b4b6867f09d09a15dffcf82f225057c35a4e819680a9f836f0684b041b", + "integrity": "sha256:28f032b4b6867f09d09a15dffcf82f225057c35a4e819680a9f836f0684b041b" + } + } +} diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..52f3ef1 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,15 @@ +# https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners +# Last matching pattern wins. Only ONE listed owner needs to approve, not all. + +# Default owner for everything. +* @D3strukt0r + +# Files Dependabot manages — @iwf-web/CI (incl. the bot user) can also review, +# so the bot's auto-approval satisfies the code-owner requirement on these paths. +# composer +/composer.json @D3strukt0r @iwf-web/CI +/composer.lock @D3strukt0r @iwf-web/CI +# github-actions +/.github/workflows/ @D3strukt0r @iwf-web/CI +# devcontainers +/.devcontainer/ @D3strukt0r @iwf-web/CI diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5d9d68a..db4f669 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -13,9 +13,14 @@ updates: labels: - "dependabot :robot:" schedule: - interval: weekly # on monday + interval: weekly + day: monday time: '09:30' timezone: Europe/Zurich + groups: + github-actions: + patterns: + - "*" - package-ecosystem: devcontainers directory: / @@ -24,8 +29,13 @@ updates: - "dependabot :robot:" schedule: interval: weekly + day: monday time: '09:30' timezone: Europe/Zurich + groups: + devcontainers: + patterns: + - "*" - package-ecosystem: composer directory: / @@ -34,5 +44,11 @@ updates: - "dependabot :robot:" schedule: interval: weekly + day: monday time: '09:30' timezone: Europe/Zurich + groups: + composer-non-breaking: + update-types: + - minor + - patch diff --git a/release-please-config.json b/.github/release-please-config.json similarity index 100% rename from release-please-config.json rename to .github/release-please-config.json diff --git a/.release-please-manifest.json b/.github/release-please-manifest.json similarity index 100% rename from .release-please-manifest.json rename to .github/release-please-manifest.json diff --git a/.github/workflows/dependabot-automerge.yml b/.github/workflows/dependabot-automerge.yml index 38b2623..ad55995 100644 --- a/.github/workflows/dependabot-automerge.yml +++ b/.github/workflows/dependabot-automerge.yml @@ -29,7 +29,7 @@ jobs: if: steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.update-type == 'version-update:semver-patch' env: PR_URL: ${{ github.event.pull_request.html_url }} - GITHUB_TOKEN: ${{ github.token }} + GITHUB_TOKEN: ${{ secrets.GH_PAT }} run: | gh pr checkout "$PR_URL" # sets the upstream metadata for `gh pr status` if [ "$(gh pr status --json reviewDecision -q .currentBranch.reviewDecision)" != "APPROVED" ]; then diff --git a/.github/workflows/greetings.yml b/.github/workflows/greetings.yml index 1ffdfa8..666b188 100644 --- a/.github/workflows/greetings.yml +++ b/.github/workflows/greetings.yml @@ -16,6 +16,7 @@ jobs: greeting: name: Greet First-Time Contributors runs-on: ubuntu-latest + if: github.actor != 'dependabot[bot]' steps: - name: Greet First-Time Contributors diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 579fbe3..070efe3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,10 +13,56 @@ permissions: jobs: release-please: runs-on: ubuntu-latest + outputs: + # Non-empty JSON when release-please created or updated a release PR + # this run. Used by `reattribute` below to decide whether to amend. + pr: ${{ steps.release.outputs.pr }} steps: - name: Create Release PR + id: release uses: googleapis/release-please-action@v5 with: + config-file: .github/release-please-config.json + manifest-file: .github/release-please-manifest.json # Bot PAT with access to Workflows # The built-in GITHUB_TOKEN has cannot trigger other workflows token: ${{ secrets.GH_PAT }} + + # release-please-action commits the release PR's contents as the PAT + # owner (the action has no input to override the commit author). Re-attribute + # to github-actions[bot] so the PR shows the bot as author instead of the + # person the PAT belongs to. + reattribute: + needs: release-please + if: ${{ needs.release-please.outputs.pr != '' }} + runs-on: ubuntu-latest + # Cosmetic best-effort: rewrites the release-PR commit author to the bot. If + # the release PR is merged before this runs, its branch is deleted and the + # checkout below can't fetch it — don't fail the release workflow over that. + continue-on-error: true + env: + # release-please's branch name varies by config (`release-please--branches--` + # for single-package, with extra `--components--` segments for component mode). + # The action emits the PR object as JSON — pull `headBranchName` from it instead of + # guessing the pattern. + PR_BRANCH: ${{ fromJSON(needs.release-please.outputs.pr).headBranchName }} + steps: + - name: Checkout release-please branch + uses: actions/checkout@v7 + with: + token: ${{ secrets.GH_PAT }} + ref: ${{ env.PR_BRANCH }} + fetch-depth: 2 + + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Amend release commit as github-actions[bot] + run: | + set -euo pipefail + # --reset-author rewrites both author and committer to the configured + # identity; --no-edit keeps the existing commit message verbatim. + git commit --amend --reset-author --no-edit + git push origin "$PR_BRANCH" --force-with-lease diff --git a/.gitignore b/.gitignore index 2adc2e7..3d8e533 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .claude/settings.local.json +CLAUDE.md .php-cs-fixer.cache /vendor/ composer.lock diff --git a/CLAUDE.md b/AGENTS.md similarity index 68% rename from CLAUDE.md rename to AGENTS.md index b284c34..f1fded0 100644 --- a/CLAUDE.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ -# CLAUDE.md +# AGENTS.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +This file provides guidance to coding agents (Claude Code, etc.) when working with code in this repository. `CLAUDE.md` simply imports this file via `@AGENTS.md`. ## Project Overview @@ -28,15 +28,15 @@ No test suite exists - this is a configuration library. ## Architecture -The package contains two source files in `src/`: +`src/` is autoloaded under two PSR-4 namespaces: `IWFWeb\CodingStandard\` (current) and `IWF\CodingStandard\` (deprecated, hosts only the legacy wrappers). The five source files: - **IWFWebStandardSet.php** - Extends `AbstractRuleSetDefinition`, returns `@IWFWeb/standard`. Builds on `@PhpCsFixer` with customizations: no Yoda style, strict types at file top (no blank line), trailing commas everywhere, preserved single-line DocBlocks. - **IWFWebStandardRiskySet.php** - Extends `AbstractRuleSetDefinition`, returns `@IWFWeb/standard:risky`. Builds on `@PhpCsFixer:risky` with customizations: PHPUnit uses `self::` instead of `$this->`, flexible data provider naming. -- **IWFSet.php** - Deprecated wrapper for `IWFWebStandardSet`. Will be removed in v2.0. +- **RuleSetNameResolver.php** - `@internal` helper. PHP-CS-Fixer's `AbstractRuleSetDefinition::getName()` derives the name from the class name (e.g. `@IWFWebStandard`), which can't contain a `/`. Both set definitions call `RuleSetNameResolver::resolve()` to rewrite that into the slash-separated public name (`@IWFWeb/standard`), preserving any `:risky` suffix. -- **IWFRiskySet.php** - Deprecated wrapper for `IWFWebStandardRiskySet`. Will be removed in v2.0. +- **IWFSet.php** / **IWFRiskySet.php** - Deprecated wrappers (in the `IWF\CodingStandard\` namespace) for the `IWFWeb*` sets. Will be removed in v2.0. ## Usage in Other Projects diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ebc52a..312b55c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + ## [1.1.0](https://github.com/iwf-web/php-coding-standard/compare/1.0.3...1.1.0) (2026-03-25) diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index 240fb51..0000000 --- a/CODEOWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners - -# These owners will be the default owners for everything in the repo. -* @D3strukt0r diff --git a/LICENSE.txt b/LICENSE.txt index 8eb2532..3901047 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2025 IWF Web Solutions +Copyright (c) 2025-2026 IWF Web Solutions Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 32f58f9..0460366 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Both rule sets build upon the excellent `@PhpCsFixer` rule set (which includes ` ### Prerequisites - PHP 8.2 or higher -- [PHP-CS-Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) ^3.0 +- [PHP-CS-Fixer](https://github.com/PHP-CS-Fixer/PHP-CS-Fixer) ^3.88 — the `Config::registerCustomRuleSets()` method used below was added in 3.88; earlier versions cannot load these rule sets ### Installation diff --git a/composer.json b/composer.json index a4dba72..b1592f5 100644 --- a/composer.json +++ b/composer.json @@ -30,7 +30,7 @@ "php": ">=7.4" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.0" + "friendsofphp/php-cs-fixer": "^3.88" }, "autoload": { "psr-4": { @@ -41,5 +41,9 @@ "minimum-stability": "stable", "config": { "sort-packages": true + }, + "scripts": { + "cs": "php-cs-fixer fix --dry-run --diff -v", + "cs:fix": "php-cs-fixer fix" } }