fix(security): support SysvSemaphore in SharedMemoryPool withLock on PHP 8+ #50
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| schedule: | |
| - cron: "17 6 * * 1" # weekly benchmark refresh | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| jobs: | |
| tests: | |
| name: PHP ${{ matrix.php-version }} (${{ matrix.judy == 'ext' && 'ext-judy' || 'polyfill' }}) | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| php-version: ["8.1", "8.2", "8.3", "8.4", "8.5"] | |
| judy: [ext, polyfill] | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: ${{ matrix.php-version }} | |
| # Since ext-judy 2.6.0 the bundled, patched libJudy is the default build: | |
| # ./configure needs no system library and downloads nothing. Forcing | |
| # --with-judy=/usr here would test a configuration that is not what | |
| # `pie install orieg/judy` gives a user of this package. | |
| - name: Install ext-judy via PIE (default = bundled libJudy) | |
| if: matrix.judy == 'ext' | |
| run: | | |
| curl -fsSL https://github.com/php/pie/releases/latest/download/pie.phar \ | |
| -o /usr/local/bin/pie && chmod +x /usr/local/bin/pie | |
| sudo pie install orieg/judy | |
| # This package documents ext-judy >= 2.6.0 as its supported floor: | |
| # earlier versions have a use-after-free in the teardown of the | |
| # STRING_TO_MIXED types it is built on (php-judy#162), and | |
| # JudySimpleCache warns at runtime when it sees one. Assert what PIE | |
| # actually resolved rather than only printing it — a silent drop to | |
| # an older release would otherwise test the configuration this | |
| # package tells users not to run. | |
| php -r ' | |
| $v = judy_version(); | |
| echo "ext-judy ", $v, PHP_EOL; | |
| if (version_compare($v, "2.6.0", "<")) { | |
| fwrite(STDERR, "::error::PIE resolved ext-judy $v; this package requires >= 2.6.0\n"); | |
| exit(1); | |
| }' | |
| - name: Composer validate + install | |
| run: | | |
| composer validate --strict | |
| composer install --no-interaction --no-progress | |
| - name: PSR-16 behavior + spec-clause tests | |
| run: php tests/simplecache.php | |
| - name: SlabArena allocator tests | |
| run: php tests/slab.php | |
| - name: SharedMemoryPool tests | |
| run: php tests/shmop.php | |
| - name: Model-based fuzz (3 backends x 5 seeds x 6 configs) | |
| run: php tests/fuzz.php 5000 | |
| - name: Symfony adapter tests | |
| run: php tests/symfony-adapter.php | |
| - name: Owner-process example smoke (IPC pattern) | |
| run: php examples/owner-process/demo.php 2 1000 | |
| - name: Large-values example smoke | |
| run: php examples/large-values/demo.php 1000 1024 10 2 | |
| - name: Multi-worker simulation smoke | |
| run: php examples/multi-worker/demo.php 2 500 | |
| bench: | |
| name: benchmark (PHP 8.4, ext-judy, apcu) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: "8.4" | |
| extensions: apcu | |
| ini-values: apc.enable_cli=1, apc.shm_size=512M | |
| - name: Install ext-judy via PIE (default = bundled libJudy) | |
| run: | | |
| curl -fsSL https://github.com/php/pie/releases/latest/download/pie.phar \ | |
| -o /usr/local/bin/pie && chmod +x /usr/local/bin/pie | |
| sudo pie install orieg/judy | |
| - name: Composer install | |
| run: composer install --no-interaction --no-progress | |
| - name: Run benchmark sweep (median of 5) | |
| run: | | |
| php bench/cache-bench.php 50000,200000,1000000 5 | tee bench-results.md | |
| echo "" >> bench-results.md | |
| echo "### Large-Value Storage & Multi-Worker Shootout" >> bench-results.md | |
| echo "\`\`\`" >> bench-results.md | |
| php examples/large-values/demo.php 10000 2048 50 8 >> bench-results.md | |
| echo "\`\`\`" >> bench-results.md | |
| { echo '## judy-cache benchmark'; cat bench-results.md; } >> "$GITHUB_STEP_SUMMARY" | |
| - uses: actions/upload-artifact@v7 | |
| with: | |
| name: bench-results | |
| path: bench-results.md | |
| ci: | |
| name: CI | |
| needs: [tests, bench] | |
| if: always() | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| issues: write | |
| steps: | |
| - name: Check all matrix jobs passed | |
| run: | | |
| if [ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" = "true" ]; then | |
| echo "One or more CI matrix jobs failed." | |
| exit 1 | |
| fi | |
| echo "All CI matrix jobs passed." | |
| - name: Download benchmark results | |
| if: github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: bench-results | |
| path: . | |
| - name: Post PR comment with benchmark results | |
| # Fork PRs run with a read-only GITHUB_TOKEN, so commenting fails | |
| # with "Resource not accessible by integration"; the report is | |
| # still available in the job summary for those. Non-blocking. | |
| if: github.event_name == 'pull_request' && !github.event.pull_request.head.repo.fork | |
| continue-on-error: true | |
| uses: actions/github-script@v9 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| if (!fs.existsSync('bench-results.md')) { | |
| console.log('No bench-results.md found, skipping PR comment'); | |
| return; | |
| } | |
| const report = fs.readFileSync('bench-results.md', 'utf8'); | |
| const marker = '<!-- judy-cache-benchmark-results -->'; | |
| const fullBody = marker + '\n## ⚡ judy-cache Benchmark Results\n\n' + report; | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const existing = comments.find(c => c.body && c.body.includes(marker)); | |
| if (existing) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: existing.id, | |
| body: fullBody, | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: fullBody, | |
| }); | |
| } |