From 8829988adaa82f7d8387e161bdb8717f5ae00c5e Mon Sep 17 00:00:00 2001 From: Will Jones Date: Mon, 24 Jul 2023 11:31:47 -0700 Subject: [PATCH] ci: build node in manylinux docker container (#350) Closes #359 TODO: * [x] test in a sample of Linux distro docker containers --- .github/workflows/npm-publish.yml | 71 ++------ ci/build_linux_artifacts.sh | 87 ++-------- ci/manylinux_node/Dockerfile | 31 ++++ ci/manylinux_node/build.sh | 19 +++ ci/manylinux_node/install_openssl.sh | 26 +++ ci/manylinux_node/install_protobuf.sh | 15 ++ ci/manylinux_node/prepare_manylinux_node.sh | 21 +++ node/package-lock.json | 174 +++++++++++++++++++- node/package.json | 2 +- 9 files changed, 304 insertions(+), 142 deletions(-) mode change 100644 => 100755 ci/build_linux_artifacts.sh create mode 100644 ci/manylinux_node/Dockerfile create mode 100755 ci/manylinux_node/build.sh create mode 100755 ci/manylinux_node/install_openssl.sh create mode 100755 ci/manylinux_node/install_protobuf.sh create mode 100755 ci/manylinux_node/prepare_manylinux_node.sh diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 309619dd..b3672ad0 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -67,48 +67,24 @@ jobs: node/dist/lancedb-vectordb-darwin*.tgz node-linux: - name: node-linux (${{ matrix.arch}}-unknown-linux-${{ matrix.libc }}) - runs-on: ubuntu-latest + name: node-linux (${{ matrix.config.arch}}-unknown-linux-gnu + runs-on: ${{ matrix.config.runner }} # Only runs on tags that matches the make-release action if: startsWith(github.ref, 'refs/tags/v') strategy: fail-fast: false matrix: - libc: - - gnu - # TODO: re-enable musl once we have refactored to pre-built containers - # Right now we have to build node from source which is too expensive. - # - musl - arch: - - x86_64 - # Building on aarch64 is too slow for now - # - aarch64 + config: + - arch: x86_64 + runner: ubuntu-latest + - arch: aarch64 + runner: buildjet-4vcpu-ubuntu-2204-arm steps: - name: Checkout uses: actions/checkout@v3 - - name: Change owner to root (for npm) - # The docker container is run as root, so we need the files to be owned by root - # Otherwise npm is a nightmare: https://github.com/npm/cli/issues/3773 - run: sudo chown -R root:root . - - name: Set up QEMU - if: ${{ matrix.arch == 'aarch64' }} - uses: docker/setup-qemu-action@v2 - with: - platforms: arm64 - - name: Build Linux GNU native node modules - if: ${{ matrix.libc == 'gnu' }} + - name: Build Linux Artifacts run: | - docker run \ - -v $(pwd):/io -w /io \ - rust:1.70-bookworm \ - bash ci/build_linux_artifacts.sh ${{ matrix.arch }}-unknown-linux-gnu - - name: Build musl Linux native node modules - if: ${{ matrix.libc == 'musl' }} - run: | - docker run --platform linux/arm64/v8 \ - -v $(pwd):/io -w /io \ - quay.io/pypa/musllinux_1_1_${{ matrix.arch }} \ - bash ci/build_linux_artifacts.sh ${{ matrix.arch }}-unknown-linux-musl + bash ci/build_linux_artifacts.sh ${{ matrix.config.arch }} - name: Upload Linux Artifacts uses: actions/upload-artifact@v3 with: @@ -116,33 +92,6 @@ jobs: path: | node/dist/lancedb-vectordb-linux*.tgz - node-linux-arm: - name: node-linux (aarch64-unknown-linux-gnu) - runs-on: buildjet-4vcpu-ubuntu-2204-arm - # Only runs on tags that matches the make-release action - if: startsWith(github.ref, 'refs/tags/v') - strategy: - fail-fast: false - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Change owner to root (for npm) - # The docker container is run as root, so we need the files to be owned by root - # Otherwise npm is a nightmare: https://github.com/npm/cli/issues/3773 - run: sudo chown -R root:root . - - name: Build Linux GNU native node modules - run: | - docker run \ - -v $(pwd):/io -w /io \ - rust:1.70-bookworm \ - bash ci/build_linux_artifacts.sh aarch64-unknown-linux-gnu - - name: Upload Linux Artifacts - uses: actions/upload-artifact@v3 - with: - name: native-linux-arm - path: | - node/dist/lancedb-vectordb-linux*.tgz - node-windows: runs-on: windows-2022 # Only runs on tags that matches the make-release action @@ -177,7 +126,7 @@ jobs: node/dist/lancedb-vectordb-win32*.tgz release: - needs: [node, node-macos, node-linux, node-linux-arm, node-windows] + needs: [node, node-macos, node-linux, node-windows] runs-on: ubuntu-latest # Only runs on tags that matches the make-release action if: startsWith(github.ref, 'refs/tags/v') diff --git a/ci/build_linux_artifacts.sh b/ci/build_linux_artifacts.sh old mode 100644 new mode 100755 index 3a91f3ff..70b06ad4 --- a/ci/build_linux_artifacts.sh +++ b/ci/build_linux_artifacts.sh @@ -1,76 +1,19 @@ #!/bin/bash -# Builds the Linux artifacts (node binaries). -# Usage: ./build_linux_artifacts.sh [target] -# Targets supported: -# - x86_64-unknown-linux-gnu:centos -# - aarch64-unknown-linux-gnu:centos -# - aarch64-unknown-linux-musl -# - x86_64-unknown-linux-musl - -# TODO: refactor this into a Docker container we can pull - set -e +ARCH=${1:-x86_64} -setup_dependencies() { - echo "Installing system dependencies..." - if [[ $1 == *musl ]]; then - # musllinux - apk add openssl-dev - else - # rust / debian - apt update - apt install -y libssl-dev protobuf-compiler - fi -} +# We pass down the current user so that when we later mount the local files +# into the container, the files are accessible by the current user. +pushd ci/manylinux_node +docker build \ + -t lancedb-node-manylinux \ + --build-arg="ARCH=$ARCH" \ + --build-arg="DOCKER_USER=$(id -u)" \ + --progress=plain \ + . +popd -install_node() { - echo "Installing node..." - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash - source "$HOME"/.bashrc - - if [[ $1 == *musl ]]; then - # This node version is 15, we need 16 or higher: - # apk add nodejs-current npm - # So instead we install from source (nvm doesn't provide binaries for musl): - nvm install -s --no-progress 17 - else - nvm install --no-progress 17 # latest that supports glibc 2.17 - fi -} - -build_node_binary() { - echo "Building node library for $1..." - pushd node - - npm ci - - if [[ $1 == *musl ]]; then - # This is needed for cargo to allow build cdylibs with musl - export RUSTFLAGS="-C target-feature=-crt-static" - fi - - # Cargo can run out of memory while pulling dependencies, especially when running - # in QEMU. This is a workaround for that. - export CARGO_NET_GIT_FETCH_WITH_CLI=true - - # We don't pass in target, since the native target here already matches - # We need to pass OPENSSL_LIB_DIR and OPENSSL_INCLUDE_DIR for static build to work https://github.com/sfackler/rust-openssl/issues/877 - if [[ $1 == aarch64-* ]]; then - OPENSSL_STATIC=1 OPENSSL_LIB_DIR=/usr/lib/aarch64-linux-gnu OPENSSL_INCLUDE_DIR=/usr/include/openssl/ npm run build-release - else - OPENSSL_STATIC=1 OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu OPENSSL_INCLUDE_DIR=/usr/include/openssl/ npm run build-release - fi - npm run pack-build - - popd -} - -TARGET=${1:-x86_64-unknown-linux-gnu} -# Others: -# aarch64-unknown-linux-gnu -# x86_64-unknown-linux-musl -# aarch64-unknown-linux-musl - -setup_dependencies $TARGET -install_node $TARGET -build_node_binary $TARGET +docker run \ + -v $(pwd):/io -w /io \ + lancedb-node-manylinux \ + bash ci/manylinux_node/build.sh $ARCH diff --git a/ci/manylinux_node/Dockerfile b/ci/manylinux_node/Dockerfile new file mode 100644 index 00000000..67e7d396 --- /dev/null +++ b/ci/manylinux_node/Dockerfile @@ -0,0 +1,31 @@ +# Many linux dockerfile with Rust, Node, and Lance dependencies installed. +# This container allows building the node modules native libraries in an +# environment with a very old glibc, so that we are compatible with a wide +# range of linux distributions. +ARG ARCH=x86_64 + +FROM quay.io/pypa/manylinux2014_${ARCH} + +ARG ARCH=x86_64 +ARG DOCKER_USER=default_user + +# Install static openssl +COPY install_openssl.sh install_openssl.sh +RUN ./install_openssl.sh ${ARCH} > /dev/null + +# Protobuf is also installed as root. +COPY install_protobuf.sh install_protobuf.sh +RUN ./install_protobuf.sh ${ARCH} + +ENV DOCKER_USER=${DOCKER_USER} +# Create a group and user +RUN echo ${ARCH} && adduser --user-group --create-home --uid ${DOCKER_USER} build_user + +# We switch to the user to install Rust and Node, since those like to be +# installed at the user level. +USER ${DOCKER_USER} + +COPY prepare_manylinux_node.sh prepare_manylinux_node.sh +RUN cp /prepare_manylinux_node.sh $HOME/ && \ + cd $HOME && \ + ./prepare_manylinux_node.sh ${ARCH} diff --git a/ci/manylinux_node/build.sh b/ci/manylinux_node/build.sh new file mode 100755 index 00000000..101719e6 --- /dev/null +++ b/ci/manylinux_node/build.sh @@ -0,0 +1,19 @@ +#!/bin/bash +# Builds the node module for manylinux. Invoked by ci/build_linux_artifacts.sh. +set -e +ARCH=${1:-x86_64} + +if [ "$ARCH" = "x86_64" ]; then + export OPENSSL_LIB_DIR=/usr/local/lib64/ +else + export OPENSSL_LIB_DIR=/usr/local/lib/ +fi +export OPENSSL_STATIC=1 +export OPENSSL_INCLUDE_DIR=/usr/local/include/openssl + +source $HOME/.bashrc + +cd node +npm ci +npm run build-release +npm run pack-build diff --git a/ci/manylinux_node/install_openssl.sh b/ci/manylinux_node/install_openssl.sh new file mode 100755 index 00000000..b509746a --- /dev/null +++ b/ci/manylinux_node/install_openssl.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Builds openssl from source so we can statically link to it + +# this is to avoid the error we get with the system installation: +# /usr/bin/ld: : version node not found for symbol SSLeay@@OPENSSL_1.0.1 +# /usr/bin/ld: failed to set dynamic section sizes: Bad value +set -e + +git clone -b OpenSSL_1_1_1u \ + --single-branch \ + https://github.com/openssl/openssl.git + +pushd openssl + +if [[ $1 == x86_64* ]]; then + ARCH=linux-x86_64 +else + # gnu target + ARCH=linux-aarch64 +fi + +./Configure no-shared $ARCH + +make + +make install \ No newline at end of file diff --git a/ci/manylinux_node/install_protobuf.sh b/ci/manylinux_node/install_protobuf.sh new file mode 100755 index 00000000..8ea18ddd --- /dev/null +++ b/ci/manylinux_node/install_protobuf.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Installs protobuf compiler. Should be run as root. +set -e + +if [[ $1 == x86_64* ]]; then + ARCH=x86_64 +else + # gnu target + ARCH=aarch_64 +fi + +PB_REL=https://github.com/protocolbuffers/protobuf/releases +PB_VERSION=23.1 +curl -LO $PB_REL/download/v$PB_VERSION/protoc-$PB_VERSION-linux-$ARCH.zip +unzip protoc-$PB_VERSION-linux-$ARCH.zip -d /usr/local \ No newline at end of file diff --git a/ci/manylinux_node/prepare_manylinux_node.sh b/ci/manylinux_node/prepare_manylinux_node.sh new file mode 100755 index 00000000..a5a3ddd8 --- /dev/null +++ b/ci/manylinux_node/prepare_manylinux_node.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +install_node() { + echo "Installing node..." + + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash + + source "$HOME"/.bashrc + + nvm install --no-progress 16 +} + +install_rust() { + echo "Installing rust..." + curl https://sh.rustup.rs -sSf | bash -s -- -y + export PATH="$PATH:/root/.cargo/bin" +} + +install_node +install_rust \ No newline at end of file diff --git a/node/package-lock.json b/node/package-lock.json index fa066566..f3b829bd 100644 --- a/node/package-lock.json +++ b/node/package-lock.json @@ -24,7 +24,7 @@ "axios": "^1.4.0" }, "devDependencies": { - "@neon-rs/cli": "^0.0.74", + "@neon-rs/cli": "^0.0.160", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/mocha": "^10.0.1", @@ -85,6 +85,97 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, + "node_modules/@cargo-messages/android-arm-eabi": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/android-arm-eabi/-/android-arm-eabi-0.0.160.tgz", + "integrity": "sha512-PTgCEmBHEPKJbxwlHVXB3aGES+NqpeBvn6hJNYWIkET3ZQCSJnScMlIDQXEkWndK7J+hW3Or3H32a93B/MbbfQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@cargo-messages/darwin-arm64": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/darwin-arm64/-/darwin-arm64-0.0.160.tgz", + "integrity": "sha512-YSVUuc8TUTi/XmZVg9KrH0bDywKLqC1zeTyZYAYDDmqVDZW9KeTnbBUECKRs56iyHeO+kuEkVW7MKf7j2zb/FA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cargo-messages/darwin-x64": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/darwin-x64/-/darwin-x64-0.0.160.tgz", + "integrity": "sha512-U+YlAR+9tKpBljnNPWMop5YhvtwfIPQSAaUYN2llteC7ZNU5/cv8CGT1vm7uFNxr2LeGuAtRbzIh2gUmTV8mng==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@cargo-messages/linux-arm-gnueabihf": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/linux-arm-gnueabihf/-/linux-arm-gnueabihf-0.0.160.tgz", + "integrity": "sha512-wqAelTzVv1E7Ls4aviqUbem5xjzCaJQxQtVnLhv6pf1k0UyEHCS2WdufFFmWcojGe7QglI4uve3KTe01MKYj0A==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cargo-messages/linux-x64-gnu": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/linux-x64-gnu/-/linux-x64-gnu-0.0.160.tgz", + "integrity": "sha512-LQ6e7O7YYkWfDNIi/53q2QG/+lZok72LOG+NKDVCrrY4TYUcrTqWAybOV6IlkVntKPnpx8YB95umSQGeVuvhpQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@cargo-messages/win32-arm64-msvc": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/win32-arm64-msvc/-/win32-arm64-msvc-0.0.160.tgz", + "integrity": "sha512-VDMBhyun02gIDwmEhkYP1W9Z0tYqn4drgY5Iua1qV2tYOU58RVkWhzUYxM9rzYbnwKZlltgM46J/j5QZ3VaFrA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@cargo-messages/win32-x64-msvc": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/win32-x64-msvc/-/win32-x64-msvc-0.0.160.tgz", + "integrity": "sha512-vnoglDxF6zj0W/Co9D0H/bgnrhUuO5EumIf9v3ujLtBH94rAX11JsXh/FgC/8wQnQSsLyWSq70YxNS2wdETxjA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -284,12 +375,21 @@ ] }, "node_modules/@neon-rs/cli": { - "version": "0.0.74", - "resolved": "https://registry.npmjs.org/@neon-rs/cli/-/cli-0.0.74.tgz", - "integrity": "sha512-9lPmNmjej5iKKOTMPryOMubwkgMRyTWRuaq1yokASvI5mPhr2kzPN7UVjdCOjQvpunNPngR9yAHoirpjiWhUHw==", + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@neon-rs/cli/-/cli-0.0.160.tgz", + "integrity": "sha512-GQjzHPJVTOARbX3nP/fAWqBq7JlQ8XgfYlCa+iwzIXf0LC1EyfJTX+vqGD/36b9lKoyY01Z/aDUB9o/qF6ztHA==", "dev": true, "bin": { "neon": "index.js" + }, + "optionalDependencies": { + "@cargo-messages/android-arm-eabi": "0.0.160", + "@cargo-messages/darwin-arm64": "0.0.160", + "@cargo-messages/darwin-x64": "0.0.160", + "@cargo-messages/linux-arm-gnueabihf": "0.0.160", + "@cargo-messages/linux-x64-gnu": "0.0.160", + "@cargo-messages/win32-arm64-msvc": "0.0.160", + "@cargo-messages/win32-x64-msvc": "0.0.160" } }, "node_modules/@neon-rs/load": { @@ -4602,6 +4702,55 @@ } } }, + "@cargo-messages/android-arm-eabi": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/android-arm-eabi/-/android-arm-eabi-0.0.160.tgz", + "integrity": "sha512-PTgCEmBHEPKJbxwlHVXB3aGES+NqpeBvn6hJNYWIkET3ZQCSJnScMlIDQXEkWndK7J+hW3Or3H32a93B/MbbfQ==", + "dev": true, + "optional": true + }, + "@cargo-messages/darwin-arm64": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/darwin-arm64/-/darwin-arm64-0.0.160.tgz", + "integrity": "sha512-YSVUuc8TUTi/XmZVg9KrH0bDywKLqC1zeTyZYAYDDmqVDZW9KeTnbBUECKRs56iyHeO+kuEkVW7MKf7j2zb/FA==", + "dev": true, + "optional": true + }, + "@cargo-messages/darwin-x64": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/darwin-x64/-/darwin-x64-0.0.160.tgz", + "integrity": "sha512-U+YlAR+9tKpBljnNPWMop5YhvtwfIPQSAaUYN2llteC7ZNU5/cv8CGT1vm7uFNxr2LeGuAtRbzIh2gUmTV8mng==", + "dev": true, + "optional": true + }, + "@cargo-messages/linux-arm-gnueabihf": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/linux-arm-gnueabihf/-/linux-arm-gnueabihf-0.0.160.tgz", + "integrity": "sha512-wqAelTzVv1E7Ls4aviqUbem5xjzCaJQxQtVnLhv6pf1k0UyEHCS2WdufFFmWcojGe7QglI4uve3KTe01MKYj0A==", + "dev": true, + "optional": true + }, + "@cargo-messages/linux-x64-gnu": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/linux-x64-gnu/-/linux-x64-gnu-0.0.160.tgz", + "integrity": "sha512-LQ6e7O7YYkWfDNIi/53q2QG/+lZok72LOG+NKDVCrrY4TYUcrTqWAybOV6IlkVntKPnpx8YB95umSQGeVuvhpQ==", + "dev": true, + "optional": true + }, + "@cargo-messages/win32-arm64-msvc": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/win32-arm64-msvc/-/win32-arm64-msvc-0.0.160.tgz", + "integrity": "sha512-VDMBhyun02gIDwmEhkYP1W9Z0tYqn4drgY5Iua1qV2tYOU58RVkWhzUYxM9rzYbnwKZlltgM46J/j5QZ3VaFrA==", + "dev": true, + "optional": true + }, + "@cargo-messages/win32-x64-msvc": { + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@cargo-messages/win32-x64-msvc/-/win32-x64-msvc-0.0.160.tgz", + "integrity": "sha512-vnoglDxF6zj0W/Co9D0H/bgnrhUuO5EumIf9v3ujLtBH94rAX11JsXh/FgC/8wQnQSsLyWSq70YxNS2wdETxjA==", + "dev": true, + "optional": true + }, "@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -4733,10 +4882,19 @@ "optional": true }, "@neon-rs/cli": { - "version": "0.0.74", - "resolved": "https://registry.npmjs.org/@neon-rs/cli/-/cli-0.0.74.tgz", - "integrity": "sha512-9lPmNmjej5iKKOTMPryOMubwkgMRyTWRuaq1yokASvI5mPhr2kzPN7UVjdCOjQvpunNPngR9yAHoirpjiWhUHw==", - "dev": true + "version": "0.0.160", + "resolved": "https://registry.npmjs.org/@neon-rs/cli/-/cli-0.0.160.tgz", + "integrity": "sha512-GQjzHPJVTOARbX3nP/fAWqBq7JlQ8XgfYlCa+iwzIXf0LC1EyfJTX+vqGD/36b9lKoyY01Z/aDUB9o/qF6ztHA==", + "dev": true, + "requires": { + "@cargo-messages/android-arm-eabi": "0.0.160", + "@cargo-messages/darwin-arm64": "0.0.160", + "@cargo-messages/darwin-x64": "0.0.160", + "@cargo-messages/linux-arm-gnueabihf": "0.0.160", + "@cargo-messages/linux-x64-gnu": "0.0.160", + "@cargo-messages/win32-arm64-msvc": "0.0.160", + "@cargo-messages/win32-x64-msvc": "0.0.160" + } }, "@neon-rs/load": { "version": "0.0.74", diff --git a/node/package.json b/node/package.json index c33cebd6..a11c3bae 100644 --- a/node/package.json +++ b/node/package.json @@ -27,7 +27,7 @@ "author": "Lance Devs", "license": "Apache-2.0", "devDependencies": { - "@neon-rs/cli": "^0.0.74", + "@neon-rs/cli": "^0.0.160", "@types/chai": "^4.3.4", "@types/chai-as-promised": "^7.1.5", "@types/mocha": "^10.0.1",