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.
2 changes: 1 addition & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ PHP library in the tiny-blocks ecosystem.

## Mandatory pre-task step

Before starting any task, read and strictly follow `CLAUDE.md` and every rule file in
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.
49 changes: 17 additions & 32 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,48 +55,36 @@ 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:
- 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 tests
run: composer tests
run: make tests
17 changes: 13 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
PWD := $(CURDIR)
ARCH := $(shell uname -m)
PLATFORM :=
TTY := $(shell [ -t 0 ] && echo -it)

ifeq ($(ARCH),arm64)
PLATFORM := --platform=linux/amd64
endif

PROJECT := $(notdir ${PWD})
DOCKER_RUN = docker run ${PLATFORM} --rm ${TTY} --net=host -v ${PWD}/..:/workspace -w /workspace/${PROJECT} gustavofreze/php:8.5-alpine
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} --rm ${TTY} --net=host -v ${PWD}:${WORKSPACE} ${PHP_IMAGE}

RESET := \033[0m
GREEN := \033[0;32m
Expand Down Expand Up @@ -44,6 +49,10 @@ show-reports: ## Open coverage and mutation reports in the browser
show-outdated: ## 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 @@ -66,7 +75,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
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"ergebnis/composer-normalize": true,
"infection/extension-installer": true
},
"process-timeout": 0,
"sort-packages": true
},
"scripts": {
Expand Down
26 changes: 2 additions & 24 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@ parameters:
paths:
- src
- tests
reportUnmatchedIgnoredErrors: true
tmpDir: reports/phpstan
ignoreErrors:
# Collection is an extension point designed for `new static()`. Subclasses inherit
# the private constructor via factory methods. PHPStan cannot verify constructor
# compatibility at the parent level, but the design enforces it by convention.
- identifier: new.static
path: src/Collection.php

# LazyPipeline defers element production, holding its source as `iterable|Closure` until
# iteration. The element type is only realized on materialization, so level max cannot bind
# the template through the constructor. `new LazyPipeline(...)` then reads as `<mixed>`, which
Expand All @@ -21,69 +20,54 @@ parameters:
path: src/Internal/LazyPipeline.php
- identifier: return.type
path: src/Internal/LazyPipeline.php

# src/Internal/ operations and resolvers carry no PHPDoc per php-library-code-style.md.
# Their iterable value types and mixed-origin argument types cannot be annotated in
# code (the generics carve-out covers only template type parameters, which these
# collaborators do not need). Suppressed by identifier per file.

- identifier: missingType.iterableValue
path: src/Internal/Operations/Resolving/Each.php
- identifier: callable.nonCallable
path: src/Internal/Operations/Resolving/Each.php

- identifier: missingType.iterableValue
path: src/Internal/Operations/Resolving/Find.php
- identifier: return.type
path: src/Internal/Operations/Resolving/Find.php
- identifier: argument.type
path: src/Internal/Operations/Resolving/Find.php

- identifier: missingType.iterableValue
path: src/Internal/Operations/Resolving/Join.php
- identifier: argument.type
path: src/Internal/Operations/Resolving/Join.php

- identifier: missingType.iterableValue
path: src/Internal/Operations/Resolving/Reduce.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/Add.php
- identifier: missingType.iterableValue
path: src/Internal/Operations/Transforming/Add.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/Filter.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/FlatMap.php
- identifier: missingType.iterableValue
path: src/Internal/Operations/Transforming/FlatMap.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/GroupInto.php
- identifier: offsetAccess.invalidOffset
path: src/Internal/Operations/Transforming/GroupInto.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/Map.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/Merge.php
- identifier: missingType.iterableValue
path: src/Internal/Operations/Transforming/Merge.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/Rearrange.php
- identifier: return.type
path: src/Internal/Operations/Transforming/Rearrange.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/Remove.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/RemoveAll.php

- identifier: missingType.generics
path: src/Internal/Operations/Transforming/Segment.php
- identifier: missingType.iterableValue
Expand All @@ -92,35 +76,29 @@ parameters:
path: src/Internal/Operations/Transforming/Segment.php
- identifier: generator.keyType
path: src/Internal/Operations/Transforming/Segment.php

# Test files: PHPDoc is prohibited on concrete classes. Mixed types originate
# from Collection's generic element access (first, last, getBy, reduce, groupBy)
# which returns mixed at the PHPStan level.

- identifier: property.nonObject
path: tests/CollectionTest.php
- identifier: argument.type
path: tests/CollectionTest.php

- identifier: property.nonObject
path: tests/EagerCollectionTest.php
- identifier: argument.type
path: tests/EagerCollectionTest.php

- identifier: property.nonObject
path: tests/LazyCollectionTest.php
- identifier: argument.type
path: tests/LazyCollectionTest.php

- identifier: missingType.generics
path: tests/Models/Products.php
- identifier: missingType.iterableValue
path: tests/Models/Products.php

- identifier: missingType.iterableValue
path: tests/Models/Shipment.php

- identifier: missingType.iterableValue
path: tests/Models/ShipmentRecord.php
- identifier: argument.type
path: tests/Models/ShipmentRecord.php
reportUnmatchedIgnoredErrors: true