Streams and ws #520
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 | |
| env: | |
| DEBUG: napi:* | |
| APP_NAME: php | |
| MACOSX_DEPLOYMENT_TARGET: '10.13' | |
| permissions: | |
| contents: write | |
| id-token: write | |
| 'on': | |
| push: | |
| branches: | |
| - main | |
| tags-ignore: | |
| - '**' | |
| paths-ignore: | |
| - '**/*.md' | |
| - LICENSE | |
| - '**/*.gitignore' | |
| - .editorconfig | |
| - docs/** | |
| pull_request: null | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }}-ci | |
| cancel-in-progress: true | |
| jobs: | |
| build: | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| settings: | |
| # | |
| # macOS | |
| # | |
| - host: macos-15 | |
| target: aarch64-apple-darwin | |
| # build: pnpm build --target aarch64-apple-darwin | |
| build: pnpm build | |
| setup: | | |
| brew install autoconf automake libtool re2c bison libiconv \ | |
| argon2 libzip postgresql@16 | |
| # | |
| # Linux | |
| # | |
| - host: ubuntu-22.04 | |
| target: x86_64-unknown-linux-gnu | |
| # docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-debian | |
| docker: node:22-slim | |
| # build: pnpm build --target x86_64-unknown-linux-gnu | |
| build: pnpm build | |
| setup: | | |
| apt-get update | |
| apt-get install -y curl libssl-dev pkg-config build-essential \ | |
| libcurl4-openssl-dev autoconf libxml2-dev libsqlite3-dev \ | |
| bison re2c libonig-dev patchelf zlib1g-dev openssh-client git \ | |
| libclang-dev libreadline-dev libpng-dev libjpeg-dev libzip-dev \ | |
| libsodium-dev libargon2-dev libpq-dev | |
| # TODO: Can't use the musl container, need to build with ssh-agent access. | |
| # TODO: Fails because missing x86_64-linux-musl-gcc | |
| # - host: ubuntu-latest | |
| # target: x86_64-unknown-linux-musl | |
| # # docker: ghcr.io/napi-rs/napi-rs/nodejs-rust:lts-alpine | |
| # build: pnpm build --target x86_64-unknown-linux-musl | |
| # setup: | | |
| # sudo apt-get update | |
| # sudo apt-get install -y libssl-dev pkg-config build-essential \ | |
| # libcurl4-openssl-dev autoconf libxml2-dev libsqlite3-dev \ | |
| # bison re2c libonig-dev | |
| # rustup target add x86_64-unknown-linux-musl | |
| # | |
| # Windows | |
| # | |
| # - host: windows-latest | |
| # target: x86_64-pc-windows-msvc | |
| # build: pnpm build --target x86_64-pc-windows-msvc | |
| # - host: windows-latest | |
| # target: aarch64-pc-windows-msvc | |
| # build: pnpm build --target aarch64-pc-windows-msvc | |
| # - host: windows-latest | |
| # target: i686-pc-windows-msvc | |
| # build: | | |
| # pnpm build --target i686-pc-windows-msvc | |
| # pnpm test | |
| name: stable - ${{ matrix.settings.target }} - node@22 | |
| runs-on: ${{ matrix.settings.host }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - name: Setup node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - name: Install Rust | |
| uses: dtolnay/rust-toolchain@stable | |
| if: ${{ !matrix.settings.docker }} | |
| with: | |
| toolchain: stable | |
| targets: ${{ matrix.settings.target }} | |
| - name: Cache cargo | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.cargo/registry/index/ | |
| ~/.cargo/registry/cache/ | |
| ~/.cargo/git/db/ | |
| ~/.napi-rs | |
| .cargo-cache | |
| target/ | |
| key: ${{ matrix.settings.target }}-cargo-${{ matrix.settings.host }} | |
| - uses: goto-bus-stop/setup-zig@v2 | |
| if: ${{ matrix.settings.target == 'armv7-unknown-linux-gnueabihf' || matrix.settings.target == 'armv7-unknown-linux-musleabihf' }} | |
| with: | |
| version: 0.13.0 | |
| - name: Checkout php-src | |
| uses: actions/checkout@v4 | |
| with: | |
| repository: php/php-src | |
| path: php-src | |
| ref: PHP-8.4 | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Fetch cargo dependencies | |
| run: cargo fetch --target ${{ matrix.settings.target }} | |
| shell: bash | |
| - name: Build & Install PHP | |
| if: ${{ !matrix.settings.docker }} | |
| shell: bash | |
| run: | | |
| set -x | |
| ${{ matrix.settings.setup }} | |
| # Use brew bison and libiconv manually. Force linking does not work on macOS 13 | |
| if [[ "$(uname)" == "Darwin" ]]; then | |
| # Expose bison to compiler | |
| export PATH="$(brew --prefix bison)/bin:$PATH" | |
| export LDFLAGS="$LDFLAGS -L$(brew --prefix bison)/lib" | |
| export CPPFLAGS="$CPPFLAGS -I$(brew --prefix bison)/include" | |
| export PKG_CONFIG_PATH="$(brew --prefix postgresql@16)/lib/pkgconfig" | |
| fi | |
| cd php-src | |
| ./buildconf | |
| # TODO: Figure out why macOS can't find libiconv and libpng (for gd) | |
| ./configure \ | |
| --enable-shared --enable-embed=shared \ | |
| --with-config-file-path=/usr/local/etc/php \ | |
| --with-config-file-scan-dir=/usr/local/etc/php/conf.d \ | |
| --enable-option-checking=fatal \ | |
| --with-pic \ | |
| --enable-zts \ | |
| --enable-mysqlnd --with-pdo-mysql=mysqlnd --with-mysqli=mysqlnd \ | |
| --with-pdo-sqlite=/usr --with-sqlite3=/usr \ | |
| --with-pdo-pgsql --with-pgsql \ | |
| --with-openssl --with-password-argon2 --with-sodium=shared \ | |
| --with-curl \ | |
| --enable-mbstring --with-mhash \ | |
| --enable-exif `#--enable-gd` \ | |
| --with-zip --with-zlib \ | |
| --without-iconv \ | |
| --without-readline \ | |
| --disable-phpdbg \ | |
| --with-pear \ | |
| --enable-fileinfo \ | |
| --disable-cgi | |
| make -j$([[ "$(uname)" == "Darwin" ]] && sysctl -n hw.physicalcpu || nproc) | |
| sudo make install | |
| cd .. | |
| ${{ matrix.settings.build }} | |
| - name: Build in docker | |
| uses: addnab/docker-run-action@v3 | |
| if: ${{ matrix.settings.docker }} | |
| with: | |
| image: ${{ matrix.settings.docker }} | |
| options: '--user 0:0 -v ${{ github.workspace }}/.cargo-cache/git/db:/usr/local/cargo/git/db -v ${{ github.workspace }}/.cargo/registry/cache:/usr/local/cargo/registry/cache -v ${{ github.workspace }}/.cargo/registry/index:/usr/local/cargo/registry/index -v ${{ github.workspace }}:/build -w /build' | |
| shell: bash | |
| run: | | |
| set -x | |
| ${{ matrix.settings.setup }} | |
| # Install rust toolchain | |
| curl https://sh.rustup.rs -sSf | bash -s -- -y -t ${{ matrix.settings.target }} | |
| source "$HOME/.cargo/env" | |
| # Install pnpm | |
| corepack disable | |
| npm i -g pnpm | |
| # Build PHP | |
| cd php-src | |
| ./buildconf --force | |
| ./configure \ | |
| --prefix=/usr \ | |
| --enable-shared --enable-embed=shared \ | |
| --with-config-file-path=/usr/local/etc/php \ | |
| --with-config-file-scan-dir=/usr/local/etc/php/conf.d \ | |
| --enable-option-checking=fatal \ | |
| --with-pic \ | |
| --enable-zts \ | |
| --enable-mysqlnd --with-pdo-mysql=mysqlnd --with-mysqli=mysqlnd \ | |
| --with-pdo-sqlite=/usr --with-sqlite3=/usr \ | |
| --with-pdo-pgsql=/usr --with-pgsql=/usr \ | |
| --with-openssl --with-password-argon2 --with-sodium=shared \ | |
| --with-curl \ | |
| --enable-mbstring --with-mhash \ | |
| --enable-exif --enable-gd \ | |
| --with-zip --with-zlib \ | |
| --without-iconv \ | |
| --with-readline \ | |
| --disable-phpdbg \ | |
| --with-pear \ | |
| --enable-fileinfo \ | |
| --disable-cgi | |
| make -j$([[ "$(uname)" == "Darwin" ]] && sysctl -n hw.physicalcpu || nproc) | |
| make install | |
| cd .. | |
| export CARGO_NET_GIT_FETCH_WITH_CLI=true | |
| ${{ matrix.settings.build }} | |
| - name: Fix rpath and copy libphp | |
| id: fix-rpath | |
| shell: bash | |
| run: | | |
| EXT=$([[ "$(uname)" == "Darwin" ]] && echo "dylib" || echo "so") | |
| # Find the .node file in the current directory | |
| NODE_FILE=$(ls *.node) | |
| echo "Found *.node files: $NODE_FILE" | |
| if [[ -z "$NODE_FILE" ]]; then | |
| echo "No .node file found!" | |
| exit 1 | |
| fi | |
| # Extract platform and architecture from the .node file name | |
| PLATFORM_ARCH=$(echo "$NODE_FILE" | sed -E 's/php\.(.+)\.node/\1/') | |
| echo "PLATFORM_ARCH=$PLATFORM_ARCH" | |
| # Modify rpath and copy libphp | |
| if [[ "$(uname)" == "Darwin" ]]; then | |
| install_name_tool -change @rpath/libphp.$EXT @loader_path/libphp.$EXT "$NODE_FILE" | |
| else | |
| sudo patchelf --set-rpath '$ORIGIN' "$NODE_FILE" | |
| fi | |
| # Create target directory and copy files | |
| mkdir -p npm/$PLATFORM_ARCH/ | |
| cp php-src/libs/libphp.$EXT npm/$PLATFORM_ARCH/libphp.$EXT | |
| cp "$NODE_FILE" npm/$PLATFORM_ARCH/binding.node | |
| echo "PLATFORM_ARCH=$PLATFORM_ARCH" >> $GITHUB_OUTPUT | |
| - name: List packages | |
| run: ls -R ./npm | |
| shell: bash | |
| # TODO: This should be moved to the test jobs, but needs build deps | |
| # for ext-php-rs to build correctly, including PHP cli | |
| - name: Test crates | |
| if: ${{ contains(matrix.target, 'linux') }} | |
| shell: bash | |
| run: cargo test | |
| - name: Upload target-specific package for ${{ matrix.settings.target }} | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ${{ steps.fix-rpath.outputs.PLATFORM_ARCH }} | |
| path: npm/${{ steps.fix-rpath.outputs.PLATFORM_ARCH }} | |
| if-no-files-found: error | |
| test-macOS-windows-binding: | |
| name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }} | |
| needs: | |
| - build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| settings: | |
| - host: macos-15 | |
| target: aarch64-apple-darwin | |
| architecture: arm64 | |
| setup: | | |
| brew install openssl@3 argon2 libzip postgresql@16 | |
| # - host: windows-latest | |
| # target: x86_64-pc-windows-msvc | |
| # architecture: x64 | |
| node: | |
| - '20' | |
| - '22' | |
| runs-on: ${{ matrix.settings.host }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - name: Setup node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| cache: pnpm | |
| architecture: ${{ matrix.settings.architecture }} | |
| - name: Install system packages | |
| if: ${{ matrix.settings.setup }} | |
| run: ${{ matrix.settings.setup }} | |
| shell: bash | |
| - name: Install dependencies | |
| run: pnpm install | |
| - name: Determine name of Node.js bindings target | |
| id: node-target | |
| shell: bash | |
| run: | | |
| OS="${{ contains(matrix.settings.target, 'darwin') && 'darwin' || 'win32' }}" | |
| ARCH="${{ matrix.settings.architecture }}" | |
| echo "TARGET=$OS-$ARCH" >> $GITHUB_OUTPUT | |
| - name: Download target-specific package for ${{ matrix.settings.target }} | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ${{ steps.node-target.outputs.TARGET }} | |
| path: npm/${{ steps.node-target.outputs.TARGET }} | |
| - name: List packages | |
| run: ls -R ./npm | |
| shell: bash | |
| - name: Show environment info | |
| run: | | |
| echo "=== macOS Info ===" | |
| uname -a | |
| sw_vers | |
| echo "" | |
| echo "=== Node.js ===" | |
| node -v | |
| echo "" | |
| echo "=== OpenSSL (brew) ===" | |
| brew info openssl@3 | head -5 | |
| echo "" | |
| echo "=== OpenSSL (node built-in) ===" | |
| node -e "console.log(process.versions)" | |
| echo "" | |
| echo "=== libphp dependencies ===" | |
| otool -L npm/darwin-arm64/libphp.dylib | head -20 | |
| echo "" | |
| echo "=== binding.node dependencies ===" | |
| otool -L npm/darwin-arm64/binding.node | head -20 | |
| - name: Test bindings | |
| run: | | |
| # Run tests with LLDB to catch segfaults on macOS | |
| LLDB_OUTPUT=$(mktemp) | |
| LLDB_SCRIPT=$(mktemp) | |
| # Create LLDB command script with stop-hook to automatically get backtraces | |
| cat > "$LLDB_SCRIPT" << 'LLDBSCRIPT' | |
| settings set auto-confirm true | |
| settings set target.process.stop-on-exec false | |
| # Add a stop hook that runs "bt all" whenever the process stops | |
| target stop-hook add -o "bt all" | |
| # Run the process | |
| run | |
| # After process exits or crashes, get final backtrace and quit | |
| bt all | |
| quit | |
| LLDBSCRIPT | |
| echo "=== Running tests under LLDB ===" | |
| # Run LLDB with the script | |
| lldb \ | |
| -s "$LLDB_SCRIPT" \ | |
| -- node node_modules/ava/entrypoints/cli.mjs __test__/handler.spec.mjs __test__/headers.spec.mjs __test__/request.spec.mjs __test__/response.spec.mjs __test__/rewriter.spec.mjs __test__/streaming.spec.mjs 2>&1 | tee "$LLDB_OUTPUT" | |
| LLDB_EXIT=$? | |
| echo "" | |
| echo "=== LLDB exit code: $LLDB_EXIT ===" | |
| rm -f "$LLDB_SCRIPT" | |
| # Check for crash indicators in LLDB output | |
| if grep -qE "(SIGSEGV|SIGABRT|SIGBUS|EXC_BAD_ACCESS|EXC_CRASH|zend_mm_heap corrupted|stop reason = EXC|stop reason = signal)" "$LLDB_OUTPUT"; then | |
| echo "" | |
| echo "========================================" | |
| echo "=== CRASH DETECTED ===" | |
| echo "========================================" | |
| echo "" | |
| echo "=== Backtrace output should be above ===" | |
| rm -f "$LLDB_OUTPUT" | |
| exit 1 | |
| fi | |
| # Check the output for test results | |
| if grep -q "tests passed" "$LLDB_OUTPUT" && ! grep -q "tests failed" "$LLDB_OUTPUT"; then | |
| echo "=== Tests passed ===" | |
| rm -f "$LLDB_OUTPUT" | |
| exit 0 | |
| fi | |
| # If we get here, something went wrong | |
| echo "=== Test run did not complete successfully ===" | |
| cat "$LLDB_OUTPUT" | |
| rm -f "$LLDB_OUTPUT" | |
| exit 1 | |
| test-linux-binding: | |
| name: Test bindings on ${{ matrix.target }} - node@${{ matrix.node }} | |
| needs: | |
| - build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - x86_64-unknown-linux-gnu | |
| # - x86_64-unknown-linux-musl | |
| # Not supported yet. Need ubuntu-24.04-arm runner | |
| # - aarch64-unknown-linux-gnu | |
| # - aarch64-unknown-linux-musl | |
| node: | |
| - '20' | |
| - '22' | |
| runs-on: ${{ contains(matrix.target, 'aarch64') && 'ubuntu-22.04-arm' || 'ubuntu-22.04' }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - name: Setup node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| cache: pnpm | |
| - name: Output docker params | |
| id: docker | |
| run: | | |
| node -e " | |
| if ('${{ matrix.target }}'.startsWith('aarch64')) { | |
| console.log('PLATFORM=linux/arm64') | |
| } else if ('${{ matrix.target }}'.startsWith('armv7')) { | |
| console.log('PLATFORM=linux/arm/v7') | |
| } else { | |
| console.log('PLATFORM=linux/amd64') | |
| } | |
| " >> $GITHUB_OUTPUT | |
| node -e " | |
| if ('${{ matrix.target }}'.endsWith('-musl')) { | |
| console.log('IMAGE=node:${{ matrix.node }}-alpine') | |
| } else { | |
| console.log('IMAGE=node:${{ matrix.node }}-slim') | |
| } | |
| " >> $GITHUB_OUTPUT | |
| echo "PNPM_STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT | |
| - name: Install dependencies | |
| run: pnpm install --force | |
| - name: Determine name of Node.js bindings target | |
| id: node-target | |
| shell: bash | |
| run: | | |
| ARCH="${{ contains(matrix.target, 'x86_64') && 'x64' || 'arm64' }}" | |
| LIBC="${{ contains(matrix.target, 'gnu') && 'gnu' || 'musl' }}" | |
| echo "TARGET=linux-$ARCH-$LIBC" >> $GITHUB_OUTPUT | |
| - name: Download target-specific package for ${{ matrix.target }} | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: ${{ steps.node-target.outputs.TARGET }} | |
| path: npm/${{ steps.node-target.outputs.TARGET }} | |
| - name: List packages | |
| run: ls -R ./npm | |
| shell: bash | |
| - name: Test crates and bindings | |
| uses: addnab/docker-run-action@v3 | |
| with: | |
| image: ${{ steps.docker.outputs.IMAGE }} | |
| options: -v ${{ steps.docker.outputs.PNPM_STORE_PATH }}:${{ steps.docker.outputs.PNPM_STORE_PATH }} -v ${{ github.workspace }}:${{ github.workspace }} -w ${{ github.workspace }} --platform ${{ steps.docker.outputs.PLATFORM }} | |
| run: | | |
| apt-get update | |
| apt-get install -y libssl-dev pkg-config build-essential \ | |
| libcurl4-openssl-dev autoconf libxml2-dev libsqlite3-dev \ | |
| bison re2c libonig-dev libargon2-dev libzip-dev zlib1g-dev \ | |
| openssh-client libclang-dev libreadline-dev libpng-dev \ | |
| libjpeg-dev libsodium-dev libpq5 gdb | |
| # Enable core dumps | |
| ulimit -c unlimited | |
| echo '/tmp/core.%e.%p' > /proc/sys/kernel/core_pattern || true | |
| # Run tests with GDB to catch segfaults | |
| # Use the ava entrypoint directly instead of the bin script | |
| gdb -batch \ | |
| -ex "set pagination off" \ | |
| -ex "set confirm off" \ | |
| -ex "handle SIGSEGV stop print" \ | |
| -ex "run" \ | |
| -ex "thread apply all bt full" \ | |
| --args node node_modules/ava/entrypoints/cli.mjs __test__/handler.spec.mjs __test__/headers.spec.mjs __test__/request.spec.mjs __test__/response.spec.mjs __test__/rewriter.spec.mjs __test__/streaming.spec.mjs | |
| EXIT_CODE=$? | |
| if [ $EXIT_CODE -ne 0 ]; then | |
| echo "=== Test failed with exit code $EXIT_CODE ===" | |
| # Try to find and analyze core dumps | |
| if ls /tmp/core.* 2>/dev/null; then | |
| for core in /tmp/core.*; do | |
| echo "=== Analyzing core dump: $core ===" | |
| gdb -batch -ex "bt full" -ex "thread apply all bt" node "$core" || true | |
| done | |
| fi | |
| exit $EXIT_CODE | |
| fi | |
| publish: | |
| name: Publish a release | |
| environment: release | |
| runs-on: ubuntu-latest | |
| if: ${{ (github.head_ref || github.ref_name) == 'main' }} | |
| needs: | |
| - test-linux-binding | |
| - test-macOS-windows-binding | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| - name: Setup node | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| registry-url: https://registry.npmjs.org | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install | |
| # - name: Clear npm directory | |
| # shell: bash | |
| # run: rm -rf npm/* | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| path: npm/ | |
| - name: List packages | |
| run: ls -R ./npm | |
| shell: bash | |
| # - name: Dev publish | |
| # shell: bash | |
| # run: | | |
| # npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN | |
| # npm config set scope "@platformatic" | |
| # npm config set provenance true | |
| # npm publish --tag dev | |
| # env: | |
| # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # NPM_TOKEN: ${{ secrets.NPM_TOKEN }} | |
| - name: Publish | |
| if: ${{ contains(github.ref, 'main') }} | |
| run: | | |
| npm config set //registry.npmjs.org/:_authToken=$NPM_TOKEN | |
| npm config set scope "@platformatic" | |
| npm config set provenance true | |
| if git log -1 --pretty=%B | grep "^v\?[0-9]\+\.[0-9]\+\.[0-9]\+"; | |
| then | |
| npm publish --access public | |
| elif git log -1 --pretty=%B | grep "^v\?[0-9]\+\.[0-9]\+\.[0-9]\+-\.+"; | |
| then | |
| npm publish --tag next --access public | |
| else | |
| echo "Not a release, skipping publish" | |
| fi | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| NPM_TOKEN: ${{ secrets.NPM_TOKEN }} |