Skip to content

fix(security): support SysvSemaphore in SharedMemoryPool withLock on PHP 8+ #50

fix(security): support SysvSemaphore in SharedMemoryPool withLock on PHP 8+

fix(security): support SysvSemaphore in SharedMemoryPool withLock on PHP 8+ #50

Workflow file for this run

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,
});
}