From 38982454acac878877696f39fbe5c87e6e9882c0 Mon Sep 17 00:00:00 2001 From: Stephen Belanger Date: Tue, 24 Mar 2026 23:29:21 +0800 Subject: [PATCH] Port release CI from python-node - Add reusable build-and-test.yml workflow called by both CI and release - Add manual release.yml workflow with version/prerelease/skip-tests inputs - Simplify CI.yml to a thin wrapper - Add scripts/update-version.mjs for version bumping at release time CI fixes: - Use clang-16 (with LIBCLANG_PATH set to versioned .so.1) to avoid clang-14 emmintrin.h regression in node:22-slim - Cache key includes -llvm16 suffix to bust old clang-14 compiled cache - Run cargo test inside Docker to avoid root-owned target/ permissions - Use LD_PRELOAD to resolve PHP symbols in doctests (linker --as-needed drops libphp from DT_NEEDED) - Mark Embed::handle and lib.rs doctests as no_run to fix SIGSEGV from nested block_on in Drop impl Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/CI.yml | 468 +-------------------------- .github/workflows/build-and-test.yml | 444 +++++++++++++++++++++++++ .github/workflows/release.yml | 72 +++++ scripts/update-version.mjs | 6 + src/embed.rs | 2 +- src/lib.rs | 2 +- 6 files changed, 531 insertions(+), 463 deletions(-) create mode 100644 .github/workflows/build-and-test.yml create mode 100644 .github/workflows/release.yml create mode 100644 scripts/update-version.mjs diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 70e9178..4cde5f8 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,15 +1,9 @@ name: CI -env: - DEBUG: napi:* - APP_NAME: php - MACOSX_DEPLOYMENT_TARGET: '10.13' - permissions: - contents: write - id-token: write + contents: read -'on': +on: push: branches: - main @@ -21,462 +15,14 @@ permissions: - '**/*.gitignore' - .editorconfig - docs/** - pull_request: null + pull_request: 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: Test bindings - run: pnpm test - - 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 - - npm run test - - 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 }} + build-and-test: + uses: ./.github/workflows/build-and-test.yml + with: + run-tests: true diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..1609c95 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,444 @@ +name: Build and Test + +on: + workflow_call: + inputs: + run-tests: + description: 'Whether to run tests after build' + required: false + type: boolean + default: true + +env: + DEBUG: napi:* + APP_NAME: php + MACOSX_DEPLOYMENT_TARGET: '10.13' + +jobs: + build: + strategy: + fail-fast: false + matrix: + settings: + # + # macOS + # + + - host: macos-15 + 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: node:22-slim + 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 \ + clang-16 libclang-16-dev libreadline-dev libpng-dev libjpeg-dev libzip-dev \ + libsodium-dev libargon2-dev libpq-dev + ln -sf /usr/bin/clang-16 /usr/bin/clang + apt-get remove -y libclang1-14 2>/dev/null || true + export LIBCLANG_PATH=/usr/lib/x86_64-linux-gnu/libclang-16.so.1 + + name: stable - ${{ matrix.settings.target }} - node@22 + runs-on: ${{ matrix.settings.host }} + steps: + - uses: actions/checkout@v6 + - name: setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Setup node + uses: actions/setup-node@v6 + if: ${{ !matrix.settings.docker }} + 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 }}-llvm16 + - 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@v6 + 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 }} + if: ${{ !matrix.settings.docker }} + 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 + ldconfig + cd .. + + export CARGO_NET_GIT_FETCH_WITH_CLI=true + ${{ matrix.settings.build }} + LD_PRELOAD=/build/php-src/libs/libphp.so cargo test + - 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 + # Run cargo test outside Docker for non-Docker Linux builds. + # For Docker builds, cargo test runs inside the Docker step above. + - name: Test crates + if: ${{ contains(matrix.settings.target, 'linux') && !matrix.settings.docker }} + shell: bash + run: cargo test + - name: Upload npm package artifact + uses: actions/upload-artifact@v4 + with: + name: npm-${{ steps.fix-rpath.outputs.PLATFORM_ARCH }} + path: npm/${{ steps.fix-rpath.outputs.PLATFORM_ARCH }} + if-no-files-found: error + - name: Upload entrypoints artifact + uses: actions/upload-artifact@v4 + with: + name: entrypoints + path: | + index.d.ts + index.js + overwrite: true + if-no-files-found: error + + bundle: + name: Bundle release artifacts + needs: build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Download all npm package artifacts + uses: actions/download-artifact@v4 + with: + pattern: npm-* + path: npm-artifacts + + - name: Download entrypoints artifact + uses: actions/download-artifact@v4 + with: + name: entrypoints + path: . + + - name: Organize npm packages + run: | + for dir in npm-artifacts/npm-*/; do + platform=$(basename "$dir" | sed 's/^npm-//') + echo "Moving $dir to npm/$platform/" + mv "$dir"/* "npm/$platform/" + done + rm -rf npm-artifacts + shell: bash + + - name: List release bundle contents + run: | + echo "=== Root entrypoints ===" + ls -la index.js index.d.ts + echo "" + echo "=== npm package directories ===" + for dir in npm/*/; do + echo "--- $dir ---" + ls -la "$dir" + done + shell: bash + + - name: Upload release bundle + uses: actions/upload-artifact@v4 + with: + name: release-bundle + path: | + npm/ + index.js + index.d.ts + if-no-files-found: error + + test-macOS-windows-binding: + name: Test bindings on ${{ matrix.settings.target }} - node@${{ matrix.node }} + if: ${{ inputs.run-tests }} + 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@v6 + - name: setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Setup node + uses: actions/setup-node@v6 + 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 npm package artifact + uses: actions/download-artifact@v4 + with: + name: npm-${{ steps.node-target.outputs.TARGET }} + path: npm/${{ steps.node-target.outputs.TARGET }} + - name: List packages + run: ls -R ./npm + shell: bash + - name: Test bindings + run: pnpm test + + test-linux-binding: + name: Test bindings on ${{ matrix.target }} - node@${{ matrix.node }} + if: ${{ inputs.run-tests }} + 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@v6 + - name: setup pnpm + uses: pnpm/action-setup@v4 + with: + version: 10 + - name: Setup node + uses: actions/setup-node@v6 + 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 npm package artifact + uses: actions/download-artifact@v4 + with: + name: npm-${{ 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 + + npm run test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a214bc2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,72 @@ +name: Publish releases + +on: + workflow_dispatch: + inputs: + version: + description: 'The version number to tag and release' + required: true + type: string + prerelease: + description: 'Release as pre-release' + required: false + type: boolean + default: false + skip-tests: + description: 'Skip tests (use if tests already passed in CI)' + required: false + type: boolean + default: false + +permissions: + contents: write + id-token: write + +concurrency: + group: release + cancel-in-progress: false + +jobs: + build-and-test: + uses: ./.github/workflows/build-and-test.yml + with: + run-tests: ${{ inputs.skip-tests == false }} + + release-npm: + needs: build-and-test + runs-on: ubuntu-latest + environment: npm + steps: + - uses: actions/checkout@v6 + - uses: pnpm/action-setup@v4 + with: + version: 10 + - uses: actions/setup-node@v6 + with: + node-version: '24' + registry-url: 'https://registry.npmjs.org' + cache: 'pnpm' + - run: pnpm install + - uses: actions/download-artifact@v4 + with: + name: release-bundle + path: . + - name: Change version number + run: node scripts/update-version.mjs ${{ inputs.version }} + - name: Update lockfile + run: pnpm install + - name: GIT commit and push all changed files + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git commit -a -m "Bumped v${{ inputs.version }}" + git push origin HEAD:${{ github.ref }} + - name: Update npm + run: npm install -g npm@latest + - name: Publish + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npm publish --access public --tag ${{ inputs.prerelease == true && 'next' || 'latest' }} + - name: Create release notes + run: | + npx @matteo.collina/release-notes -a ${{ secrets.GITHUB_TOKEN }} -t v${{ inputs.version }} -r php-node -o platformatic ${{ inputs.prerelease == true && '-p' || '' }} -c ${{ github.ref }} diff --git a/scripts/update-version.mjs b/scripts/update-version.mjs new file mode 100644 index 0000000..707fcf2 --- /dev/null +++ b/scripts/update-version.mjs @@ -0,0 +1,6 @@ +import { readFile, writeFile } from 'fs/promises' + +const version = process.argv[2].replace(/^v/, '') +const packageJson = JSON.parse(await readFile('package.json', 'utf8')) +packageJson.version = version +await writeFile('package.json', JSON.stringify(packageJson, null, 2) + '\n') diff --git a/src/embed.rs b/src/embed.rs index 025ce99..03610a5 100644 --- a/src/embed.rs +++ b/src/embed.rs @@ -226,7 +226,7 @@ impl Handler for Embed { /// /// # Examples /// - /// ``` + /// ```no_run /// use std::{env::temp_dir, fs::File, io::Write}; /// use php::{Embed, Handler, Request, Response, MockRoot}; /// diff --git a/src/lib.rs b/src/lib.rs index da8d2c3..bfed450 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ //! //! ## Example //! -//! ```rust +//! ```rust,no_run //! use std::env::{args, current_dir}; //! # use std::path::PathBuf; //! # use php::MockRoot;