From 57afcfa19ea7a3c913d0a89060371ae5fbb2259f Mon Sep 17 00:00:00 2001 From: Gustavo Freze Date: Thu, 15 Jan 2026 09:31:54 -0300 Subject: [PATCH] chore: Update PHP version to 8.5, improve CI/CD, and enhance development tooling. --- .gitattributes | 1 - .github/dependabot.yml | 31 ++++++++++ .github/workflows/auto-assign.yml | 2 +- .github/workflows/ci.yml | 57 +++++++++++++++--- .github/workflows/codeql.yml | 35 +++++++++++ .gitignore | 7 ++- Makefile | 60 ++++++++++++++----- composer.json | 37 ++++++------ phpmd.xml | 59 ------------------ src/Environment.php | 2 +- src/EnvironmentVariable.php | 2 +- .../Exceptions/EnvironmentVariableMissing.php | 4 +- .../Exceptions/InvalidEnvironmentValue.php | 9 ++- 13 files changed, 192 insertions(+), 114 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/codeql.yml delete mode 100644 phpmd.xml diff --git a/.gitattributes b/.gitattributes index 22aac70..8c85471 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,7 +4,6 @@ /LICENSE export-ignore /Makefile export-ignore /README.md export-ignore -/phpmd.xml export-ignore /phpunit.xml export-ignore /phpstan.neon.dist export-ignore /infection.json.dist export-ignore diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..f0ce8fc --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,31 @@ +version: 2 + +updates: + - package-ecosystem: "composer" + directory: "/" + schedule: + interval: "weekly" + open-pull-requests-limit: 0 + labels: + - "php" + - "security" + - "dependencies" + groups: + php-security: + applies-to: security-updates + patterns: + - "*" + + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + commit-message: + prefix: "build" + labels: + - "dependencies" + - "github-actions" + groups: + github-actions: + patterns: + - "*" diff --git a/.github/workflows/auto-assign.yml b/.github/workflows/auto-assign.yml index a17e92e..f37e91f 100644 --- a/.github/workflows/auto-assign.yml +++ b/.github/workflows/auto-assign.yml @@ -18,7 +18,7 @@ jobs: - name: Assign issues and pull requests uses: gustavofreze/auto-assign@2.0.0 with: - assignees: '${{ secrets.ASSIGNEES }}' + assignees: '${{ vars.ASSIGNEES }}' github_token: '${{ secrets.GITHUB_TOKEN }}' allow_self_assign: 'true' allow_no_assignees: 'true' diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 358ac92..e898dc1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,24 +7,59 @@ permissions: contents: read env: - PHP_VERSION: '8.3' + PHP_VERSION: '8.5' jobs: + build: + name: Build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Configure PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ env.PHP_VERSION }} + extensions: bcmath + tools: composer:2 + + - name: Validate composer.json + run: composer validate --no-interaction + + - name: Install dependencies + run: composer install --no-progress --optimize-autoloader --prefer-dist --no-interaction + + - name: Upload vendor and composer.lock as artifact + uses: actions/upload-artifact@v6 + with: + name: vendor-artifact + path: | + vendor + composer.lock + auto-review: name: Auto review runs-on: ubuntu-latest + needs: build steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Configure PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ env.PHP_VERSION }} + extensions: bcmath + tools: composer:2 - - name: Install dependencies - run: composer update --no-progress --optimize-autoloader + - name: Download vendor artifact from build + uses: actions/download-artifact@v7 + with: + name: vendor-artifact + path: . - name: Run review run: composer review @@ -32,18 +67,24 @@ jobs: tests: name: Tests runs-on: ubuntu-latest + needs: auto-review steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - - name: Use PHP ${{ env.PHP_VERSION }} + - name: Configure PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ env.PHP_VERSION }} + extensions: bcmath + tools: composer:2 - - name: Install dependencies - run: composer update --no-progress --optimize-autoloader + - name: Download vendor artifact from build + uses: actions/download-artifact@v7 + with: + name: vendor-artifact + path: . - name: Run tests run: composer tests diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..4c6d7f7 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,35 @@ +name: Security checks + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: "0 0 * * *" + +permissions: + actions: read + contents: read + security-events: write + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + language: [ "actions" ] + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + + - name: Perform CodeQL analysis + uses: github/codeql-action/analyze@v4 diff --git a/.gitignore b/.gitignore index 3333ef2..42b841a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .idea -/vendor/ -/report -*.lock +vendor +report .phpunit.* + +*.lock diff --git a/Makefile b/Makefile index 96ccd27..ef9a884 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,4 @@ -ifeq ($(OS),Windows_NT) - PWD := $(shell cd) -else - PWD := $(shell pwd -L) -endif - +PWD := $(CURDIR) ARCH := $(shell uname -m) PLATFORM := @@ -11,28 +6,63 @@ ifeq ($(ARCH),arm64) PLATFORM := --platform=linux/amd64 endif -DOCKER_RUN = docker run ${PLATFORM} --rm -it --net=host -v ${PWD}:/app -w /app gustavofreze/php:8.3 +DOCKER_RUN = docker run ${PLATFORM} --rm -it --net=host -v ${PWD}:/app -w /app gustavofreze/php:8.5-alpine -.PHONY: configure test test-file test-no-coverage review show-reports clean +RESET := \033[0m +GREEN := \033[0;32m +YELLOW := \033[0;33m -configure: +.DEFAULT_GOAL := help + +.PHONY: configure +configure: ## Configure development environment @${DOCKER_RUN} composer update --optimize-autoloader -test: +.PHONY: test +test: ## Run all tests with coverage @${DOCKER_RUN} composer tests -test-file: +.PHONY: test-file +test-file: ## Run tests for a specific file (usage: make test-file FILE=path/to/file) @${DOCKER_RUN} composer test-file ${FILE} -test-no-coverage: +.PHONY: test-no-coverage +test-no-coverage: ## Run all tests without coverage @${DOCKER_RUN} composer tests-no-coverage -review: +.PHONY: review +review: ## Run static code analysis @${DOCKER_RUN} composer review -show-reports: +.PHONY: show-reports +show-reports: ## Open static analysis reports (e.g., coverage, lints) in the browser @sensible-browser report/coverage/coverage-html/index.html report/coverage/mutation-report.html -clean: +.PHONY: clean +clean: ## Remove dependencies and generated artifacts @sudo chown -R ${USER}:${USER} ${PWD} @rm -rf report vendor .phpunit.cache *.lock + +.PHONY: help +help: ## Display this help message + @echo "Usage: make [target]" + @echo "" + @echo "$$(printf '$(GREEN)')Setup$$(printf '$(RESET)')" + @grep -E '^(configure):.*?## .*$$' $(MAKEFILE_LIST) \ + | awk 'BEGIN {FS = ":.*? ## "}; {printf "$(YELLOW)%-25s$(RESET) %s\n", $$1, $$2}' + @echo "" + @echo "$$(printf '$(GREEN)')Testing$$(printf '$(RESET)')" + @grep -E '^(test|test-file|test-no-coverage):.*?## .*$$' $(MAKEFILE_LIST) \ + | awk 'BEGIN {FS = ":.*?## "}; {printf "$(YELLOW)%-25s$(RESET) %s\n", $$1, $$2}' + @echo "" + @echo "$$(printf '$(GREEN)')Quality$$(printf '$(RESET)')" + @grep -E '^(review):.*?## .*$$' $(MAKEFILE_LIST) \ + | awk 'BEGIN {FS = ":.*?## "}; {printf "$(YELLOW)%-25s$(RESET) %s\n", $$1, $$2}' + @echo "" + @echo "$$(printf '$(GREEN)')Reports$$(printf '$(RESET)')" + @grep -E '^(show-reports):.*?## .*$$' $(MAKEFILE_LIST) \ + | awk 'BEGIN {FS = ":.*?## "}; {printf "$(YELLOW)%-25s$(RESET) %s\n", $$1, $$2}' + @echo "" + @echo "$$(printf '$(GREEN)')Cleanup$$(printf '$(RESET)')" + @grep -E '^(clean):.*?## .*$$' $(MAKEFILE_LIST) \ + | awk 'BEGIN {FS = ":.*?## "}; {printf "$(YELLOW)%-25s$(RESET) %s\n", $$1, $$2}' diff --git a/composer.json b/composer.json index c7923a9..3cb2970 100644 --- a/composer.json +++ b/composer.json @@ -22,6 +22,12 @@ "issues": "https://github.com/tiny-blocks/environment-variable/issues", "source": "https://github.com/tiny-blocks/environment-variable" }, + "config": { + "sort-packages": true, + "allow-plugins": { + "infection/extension-installer": true + } + }, "autoload": { "psr-4": { "TinyBlocks\\EnvironmentVariable\\": "src/" @@ -33,33 +39,24 @@ } }, "require": { - "php": "^8.3" + "php": "^8.5" }, "require-dev": { - "phpmd/phpmd": "^2.15", - "phpunit/phpunit": "^11", - "phpstan/phpstan": "^1", + "phpunit/phpunit": "^11.5", + "phpstan/phpstan": "^2.1", "dg/bypass-finals": "^1.8", - "infection/infection": "^0", - "squizlabs/php_codesniffer": "^3" - }, - "config": { - "sort-packages": true, - "allow-plugins": { - "infection/extension-installer": true - } + "infection/infection": "^0.32", + "squizlabs/php_codesniffer": "^4.0" }, "scripts": { - "test": "phpunit --configuration phpunit.xml tests", - "phpcs": "phpcs --standard=PSR12 --extensions=php ./src", - "phpmd": "phpmd ./src text phpmd.xml --suffixes php --ignore-violations-on-exit", - "phpstan": "phpstan analyse -c phpstan.neon.dist --quiet --no-progress", - "test-file": "phpunit --configuration phpunit.xml --no-coverage --filter", - "mutation-test": "infection --only-covered --threads=max --logger-html=report/coverage/mutation-report.html --coverage=report/coverage", - "test-no-coverage": "phpunit --configuration phpunit.xml --no-coverage tests", + "test": "php -d memory_limit=2G ./vendor/bin/phpunit --configuration phpunit.xml tests", + "phpcs": "php ./vendor/bin/phpcs --standard=PSR12 --extensions=php ./src", + "phpstan": "php ./vendor/bin/phpstan analyse -c phpstan.neon.dist --quiet --no-progress", + "test-file": "php ./vendor/bin/phpunit --configuration phpunit.xml --no-coverage --filter", + "mutation-test": "php ./vendor/bin/infection --threads=max --logger-html=report/coverage/mutation-report.html --coverage=report/coverage", + "test-no-coverage": "php ./vendor/bin/phpunit --configuration phpunit.xml --no-coverage tests", "review": [ "@phpcs", - "@phpmd", "@phpstan" ], "tests": [ diff --git a/phpmd.xml b/phpmd.xml deleted file mode 100644 index bb59312..0000000 --- a/phpmd.xml +++ /dev/null @@ -1,59 +0,0 @@ - - - PHPMD Custom rules - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Environment.php b/src/Environment.php index 27e224b..1b27ba8 100644 --- a/src/Environment.php +++ b/src/Environment.php @@ -28,7 +28,7 @@ public static function from(string $name): Environment; * @param string|null $defaultValueIfNotFound The default value to use if the environment variable is not found. * @return EnvironmentVariable The environment variable instance, either with the found value or the default. */ - public static function fromOrDefault(string $name, string $defaultValueIfNotFound = null): EnvironmentVariable; + public static function fromOrDefault(string $name, ?string $defaultValueIfNotFound = null): EnvironmentVariable; /** * Checks if the environment variable has a value. Values like `false`, `0`, and `-1` are valid and non-empty. diff --git a/src/EnvironmentVariable.php b/src/EnvironmentVariable.php index 6c7643a..858ba0b 100644 --- a/src/EnvironmentVariable.php +++ b/src/EnvironmentVariable.php @@ -22,7 +22,7 @@ public static function from(string $name): EnvironmentVariable : new EnvironmentVariable(value: $environmentVariable, variable: $name); } - public static function fromOrDefault(string $name, string $defaultValueIfNotFound = null): EnvironmentVariable + public static function fromOrDefault(string $name, ?string $defaultValueIfNotFound = null): EnvironmentVariable { $environmentVariable = getenv($name); diff --git a/src/Internal/Exceptions/EnvironmentVariableMissing.php b/src/Internal/Exceptions/EnvironmentVariableMissing.php index 7dd4afb..f7e76d3 100644 --- a/src/Internal/Exceptions/EnvironmentVariableMissing.php +++ b/src/Internal/Exceptions/EnvironmentVariableMissing.php @@ -8,10 +8,10 @@ final class EnvironmentVariableMissing extends InvalidArgumentException { - public function __construct(string $variable) + public function __construct(private readonly string $variable) { $template = 'Environment variable <%s> is missing.'; - parent::__construct(message: sprintf($template, $variable)); + parent::__construct(message: sprintf($template, $this->variable)); } } diff --git a/src/Internal/Exceptions/InvalidEnvironmentValue.php b/src/Internal/Exceptions/InvalidEnvironmentValue.php index 64d1d18..fbc3903 100644 --- a/src/Internal/Exceptions/InvalidEnvironmentValue.php +++ b/src/Internal/Exceptions/InvalidEnvironmentValue.php @@ -8,11 +8,14 @@ final class InvalidEnvironmentValue extends InvalidArgumentException { - public function __construct(string $value, string $variable, string $conversionType) - { + public function __construct( + private readonly string $value, + private readonly string $variable, + private readonly string $conversionType + ) { $template = 'The value <%s> for environment variable <%s> is invalid for conversion to <%s>.'; - parent::__construct(message: sprintf($template, $value, $variable, $conversionType)); + parent::__construct(message: sprintf($template, $this->value, $this->variable, $this->conversionType)); } public static function fromIntegerConversion(string $value, string $variable): InvalidEnvironmentValue