Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@

*.php text diff=php

# Keep Claude tooling scripts out of GitHub's language statistics

# Dev-only, excluded from the Packagist tarball
/.github export-ignore
/tests export-ignore
Expand Down
4 changes: 2 additions & 2 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,5 @@ Closes #...

- [ ] Tests added or updated.
- [ ] Documentation updated when applicable.
- [ ] `composer review` passes.
- [ ] `composer tests` passes.
- [ ] `make review` passes.
- [ ] `make tests` passes.
9 changes: 5 additions & 4 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

## Context

PHP library (tiny-blocks). Immutable domain models, zero infrastructure dependencies in core.
PHP library in the tiny-blocks ecosystem.

## Mandatory pre-task step

Before starting any task, read and strictly follow all instruction files located in `.claude/CLAUDE.md` and
`.claude/rules/`. These files are the absolute source of truth for code generation. Apply every rule strictly. Do not
deviate from the patterns, folder structure, or naming conventions defined in them.
Before starting any task, read and strictly follow `.claude/CLAUDE.md` and every rule file in
`.claude/rules/`. These files are the absolute source of truth for code generation. Apply every
rule strictly. Do not deviate from the patterns, folder structure, or naming conventions defined
in them.
52 changes: 17 additions & 35 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,39 @@ permissions:
contents: read

jobs:
resolve-php-version:
name: Resolve PHP version
resolve-tooling-image:
name: Resolve tooling image
runs-on: ubuntu-latest
timeout-minutes: 5
outputs:
php-version: ${{ steps.config.outputs.php-version }}
php-image: ${{ steps.config.outputs.php-image }}
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Resolve PHP version from composer.json
- name: Resolve tooling image from the Makefile
id: config
run: |
version=$(jq -r '.require.php' composer.json | grep -oP '\d+\.\d+' | head -1)
echo "php-version=$version" >> "$GITHUB_OUTPUT"
run: echo "php-image=$(make show-image)" >> "$GITHUB_OUTPUT"

build:
name: Build
needs: resolve-php-version
needs: resolve-tooling-image
runs-on: ubuntu-latest
timeout-minutes: 15
env:
image: ${{ needs.resolve-tooling-image.outputs.php-image }}
workspace: /var/www/html
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
tools: composer:2
php-version: ${{ needs.resolve-php-version.outputs.php-version }}

- name: Validate composer.json
run: composer validate --no-interaction
run: docker run --rm -v "${PWD}":${{ env.workspace }} ${{ env.image }} composer validate --no-interaction

- name: Install dependencies
run: composer install --no-progress --optimize-autoloader --prefer-dist --no-interaction
run: >
docker run --rm -v "${PWD}":${{ env.workspace }} ${{ env.image }}
composer install --no-progress --optimize-autoloader --prefer-dist --no-interaction

- name: Upload vendor and composer.lock as artifact
uses: actions/upload-artifact@v7
Expand All @@ -58,31 +55,25 @@ jobs:

auto-review:
name: Auto review
needs: [resolve-php-version, build]
needs: [resolve-tooling-image, build]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout
uses: actions/checkout@v7

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
tools: composer:2
php-version: ${{ needs.resolve-php-version.outputs.php-version }}

- name: Download vendor artifact from build
uses: actions/download-artifact@v8
with:
name: vendor-artifact
path: .

- name: Run review
run: composer review
run: make review

tests:
name: Tests
needs: [resolve-php-version, auto-review]
needs: [resolve-tooling-image, auto-review]
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
Expand All @@ -98,17 +89,8 @@ jobs:
- name: Clean up Docker
run: docker system prune -f

- name: Create Docker network
run: docker network create tiny-blocks

- name: Create Docker volume for migrations
run: docker volume create test-adm-migrations

- name: Run tests
run: |
docker run --network=tiny-blocks \
-v ${PWD}:/app \
-v ${PWD}/tests/Integration/Database/Migrations:/test-adm-migrations \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /app \
gustavofreze/php:${{ needs.resolve-php-version.outputs.php-version }}-alpine bash -c "composer tests"
run: make tests
15 changes: 12 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,16 @@ endif

TTY := $(shell [ -t 0 ] && echo -it)

PHP_VERSION := $(shell sed -n 's/.*"php": *"^\([0-9]*\.[0-9]*\)".*/\1/p' composer.json)
IMAGE_VERSION := 1.0.0
PHP_IMAGE := gustavofreze/php:${PHP_VERSION}-cli-${IMAGE_VERSION}
WORKSPACE := /var/www/html

DOCKER_RUN = docker run ${PLATFORM} -u root --rm ${TTY} --network=tiny-blocks --name test-lib \
-v ${PWD}:/app \
-v ${PWD}:${WORKSPACE} \
-v ${PWD}/tests/Integration/Database/Migrations:/test-adm-migrations \
-v /var/run/docker.sock:/var/run/docker.sock \
-w /app gustavofreze/php:8.5-alpine
${PHP_IMAGE}

RESET := \033[0m
GREEN := \033[0;32m
Expand Down Expand Up @@ -54,6 +59,10 @@ show-reports: ## Open coverage and mutation reports in the browser
show-outdated: configure-test-environment ## Show outdated direct dependencies
@${DOCKER_RUN} composer outdated --direct

.PHONY: show-image
show-image: ## Show the pinned PHP tooling image
@echo ${PHP_IMAGE}

.PHONY: clean
clean: ## Remove dependencies and generated artifacts
@sudo chown -R ${USER}:${USER} ${PWD}
Expand All @@ -76,7 +85,7 @@ help: ## Display this help message
| awk 'BEGIN {FS = ":.*? ## "}; {printf "$(YELLOW)%-25s$(RESET) %s\n", $$1, $$2}'
@echo ""
@echo "$$(printf '$(GREEN)')Reports$$(printf '$(RESET)')"
@grep -E '^(show-reports|show-outdated):.*?## .*$$' $(MAKEFILE_LIST) \
@grep -E '^(show-reports|show-outdated|show-image):.*?## .*$$' $(MAKEFILE_LIST) \
| awk 'BEGIN {FS = ":.*? ## "}; {printf "$(YELLOW)%-25s$(RESET) %s\n", $$1, $$2}'
@echo ""
@echo "$$(printf '$(GREEN)')Cleanup$$(printf '$(RESET)')"
Expand Down
2 changes: 1 addition & 1 deletion infection.json.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"text": "reports/infection/logs/infection-text.log",
"summary": "reports/infection/logs/infection-summary.log"
},
"tmpDir": "reports/infection/cache/",
"tmpDir": "reports/infection/",
"minMsi": 100,
"timeout": 120,
"source": {
Expand Down
20 changes: 2 additions & 18 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@ parameters:
paths:
- src
- tests
reportUnmatchedIgnoredErrors: true
tmpDir: reports/phpstan
ignoreErrors:
# Late static binding in extension-point container factories that consumers subclass.
- identifier: new.static
paths:
- src/FlywayDockerContainer.php
- src/GenericDockerContainer.php
- src/MySQLDockerContainer.php

# tiny-blocks/collection generics carry a mixed TValue on Internal command and definition types.
- identifier: missingType.generics
paths:
Expand All @@ -21,7 +20,6 @@ parameters:
- src/Internal/Containers/Address/Ports.php
- src/Internal/Containers/Definitions/ContainerDefinition.php
- src/Internal/Containers/Environment/EnvironmentVariables.php

# Internal collaborators take raw config arrays at the boundary, where PHPDoc is prohibited.
- identifier: missingType.iterableValue
paths:
Expand All @@ -35,81 +33,67 @@ parameters:
- src/Internal/Containers/Definitions/VolumeMapping.php
- src/Internal/Containers/Drivers/MySQL/MySQLCommands.php
- src/Internal/Containers/Environment/EnvironmentVariables.php

# Internal command builders return argument lists assembled from collection mixed values.
- identifier: return.type
paths:
- src/Internal/Commands/DockerCopy.php
- src/Internal/Commands/DockerExecute.php
- src/Internal/Commands/DockerRun.php
- src/Internal/Containers/Address/Ports.php

# Collection each/reduce pass concrete closures against the mixed-typed variadic API.
- identifier: argument.type
paths:
- src/Internal/CommandHandler/ContainerCommandHandler.php
- src/Internal/Commands/DockerRun.php
- src/Internal/Containers/Address/Ports.php

# docker inspect json_decode and sprintf feed mixed values across the system boundary.
- identifier: argument.type
paths:
- src/Internal/Containers/ContainerInspection.php
- src/Internal/Containers/Drivers/MySQL/MySQLCommands.php

# gethostname() returns string|false at the system boundary when the host name is unavailable.
- identifier: argument.type
path: src/Internal/Containers/ContainerReaper.php

# docker inspect json_decode produces mixed offsets read in ContainerInspection.
- identifier: offsetAccess.nonOffsetAccessible
path: src/Internal/Containers/ContainerInspection.php

# docker inspect json_decode yields a mixed value cast to int at the boundary.
- identifier: cast.int
path: src/Internal/Containers/ContainerInspection.php

# docker inspect json_decode yields a mixed value iterated at the boundary.
- identifier: foreach.nonIterable
path: src/Internal/Containers/ContainerInspection.php

# Environment variable values arrive as mixed and are cast to string at the boundary.
- identifier: cast.string
path: src/Internal/Containers/Environment/EnvironmentVariables.php

# Fixtures and mocks accept raw arrays, where PHPDoc is prohibited inside tests.
- identifier: missingType.iterableValue
paths:
- tests/Integration/MySQLRepository.php
- tests/Models/InspectResponseFixture.php
- tests/Unit/ClientMock.php

# ClientMock and fixtures hand mixed values to typed sinks and assertions in tests.
- identifier: argument.type
paths:
- tests/Unit/ClientMock.php
- tests/Unit/FlywayDockerContainerTest.php
- tests/Unit/GenericDockerContainerTest.php
- tests/Unit/MySQLDockerContainerTest.php

# ClientMock's match arms assemble output tuples from untyped recorded responses.
- identifier: return.type
path: tests/Unit/ClientMock.php

# ClientMock casts a mixed recorded value to string when building command output.
- identifier: cast.string
path: tests/Unit/ClientMock.php

# ClientMock indexes into a mixed recorded payload when replaying responses.
- identifier: offsetAccess.nonArray
path: tests/Unit/ClientMock.php

# MySQLRepository reads a mixed PDO result as an object at the system boundary.
- identifier: method.nonObject
path: tests/Integration/MySQLRepository.php

# Mock-verified and no-exception tests assert a literal true to satisfy the assertion count.
- identifier: staticMethod.alreadyNarrowedType
paths:
- tests/Unit/GenericDockerContainerTest.php
- tests/Unit/Waits/ContainerWaitForDependencyTest.php
reportUnmatchedIgnoredErrors: true