From 6f70fb0344e76fcc159ee2c18f96b78e6fbf4fbd Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 3 Feb 2026 13:30:45 -0800 Subject: [PATCH 01/25] Initial patch --- .github/workflows/build-node-fibers.yml | 76 ++++++++++++++++++ .github/workflows/build-node-packages.yml | 77 +++++++++++++++++++ .github/workflows/build-node.yml | 94 +++++++++++++++++++++++ Dockerfile.Fibers | 35 +++++++++ Dockerfile.Node22 | 24 ++++++ Dockerfile.Node22fips | 29 +++++++ Dockerfile.Packages | 34 ++++++++ deps/v8/src/base/platform/platform.h | 6 ++ deps/v8/src/execution/isolate-inl.h | 24 +++--- deps/v8/src/execution/isolate.cc | 60 ++++++++++++--- deps/v8/src/execution/isolate.h | 38 +++++++-- deps/v8/src/execution/thread-id.cc | 8 +- lib/internal/bootstrap/realm.js | 12 ++- src/env.cc | 4 + 14 files changed, 489 insertions(+), 32 deletions(-) create mode 100644 .github/workflows/build-node-fibers.yml create mode 100644 .github/workflows/build-node-packages.yml create mode 100644 .github/workflows/build-node.yml create mode 100644 Dockerfile.Fibers create mode 100644 Dockerfile.Node22 create mode 100644 Dockerfile.Node22fips create mode 100644 Dockerfile.Packages diff --git a/.github/workflows/build-node-fibers.yml b/.github/workflows/build-node-fibers.yml new file mode 100644 index 000000000000..ee7fc5f13625 --- /dev/null +++ b/.github/workflows/build-node-fibers.yml @@ -0,0 +1,76 @@ +name: Build node-fibers with prebuilt Node + +on: + workflow_dispatch: + push: + branches: + - v22.21.1 + +jobs: + build-fibers: + strategy: + matrix: + include: + - platform: linux + arch: x64 + runs_on: ubuntu-22.04 + - platform: linux + arch: arm64 + runs_on: ubuntu-22.04-arm + runs-on: ${{ matrix.runs_on }} + + env: + NODE_VERSION: v22.21.1 + + steps: + - name: Debug Matrix Values + run: | + echo "Matrix platform: ${{ matrix.platform }}" + echo "Matrix arch: ${{ matrix.arch }}" + + - name: Download Node archive + run: | + gh release download node-${{ env.NODE_VERSION }}-release \ + --repo asana/node \ + --pattern "node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz" + mv node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz node.tar.xz + pwd + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Download Dockerfile from GitHub API + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + curl -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw" -o Dockerfile.Fibers https://api.github.com/repos/Asana/node/contents/Dockerfile.Fibers?ref=node22.21.1_dockerfile + cat Dockerfile.Fibers + pwd + + - name: Execute the Dockerfile + run: | + pwd + docker build -t node22_fibers_build -f Dockerfile.Fibers . + + - name: Extract resources + run: | + docker create --name temp_node_fibers_extract node22_fibers_build + docker cp temp_node_fibers_extract:/usr/src/node/node-fibers $GITHUB_WORKSPACE/node-fibers + docker rm temp_node_fibers_extract + + - name: Find and archive fibers.node + run: | + # Find the directory under bin/ that contains fibers.node + FIBERS_PATH=$(find ./node-fibers/bin -type f -name fibers.node | head -n1) + FIBERS_DIR=$(dirname "$FIBERS_PATH") + ARCHIVE_NAME=$(basename "$FIBERS_DIR").tar.gz + echo "ARCHIVE_NAME=$ARCHIVE_NAME" >> $GITHUB_ENV + tar -czf "$ARCHIVE_NAME" -C "$(dirname "$FIBERS_DIR")" "$(basename "$FIBERS_DIR")" + + - name: Upload archive to release + uses: softprops/action-gh-release@v1 + with: + name: node-${{ env.NODE_VERSION }}-LATEST + tag_name: node-${{ env.NODE_VERSION }}-release + files: ${{ env.ARCHIVE_NAME }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/build-node-packages.yml b/.github/workflows/build-node-packages.yml new file mode 100644 index 000000000000..87d799e1ce54 --- /dev/null +++ b/.github/workflows/build-node-packages.yml @@ -0,0 +1,77 @@ +name: Build node-packages with prebuilt Node + +on: + workflow_dispatch: + push: + branches: + - v22.21.1 + +jobs: + build-packages: + strategy: + matrix: + include: + - platform: linux + arch: x64 + runs_on: ubuntu-22.04 + - platform: linux + arch: arm64 + runs_on: ubuntu-22.04-arm + runs-on: ${{ matrix.runs_on }} + + env: + NODE_VERSION: v22.21.1 + + steps: + - name: Debug Matrix Values + run: | + echo "Matrix platform: ${{ matrix.platform }}" + echo "Matrix arch: ${{ matrix.arch }}" + + - name: Download Node archive + run: | + gh release download node-${{ env.NODE_VERSION }}-release \ + --repo asana/node \ + --pattern "node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz" + mv node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz node.tar.xz + pwd + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Download Dockerfile from GitHub API + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + curl -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw" -o Dockerfile.Packages https://api.github.com/repos/Asana/node/contents/Dockerfile.Packages?ref=node22.21.1_dockerfile + cat Dockerfile.Packages + pwd + + - name: Execute the Dockerfile + run: | + pwd + docker build -t node22_packages_build -f Dockerfile.Packages . + + - name: Extract resources + run: | + docker create --name temp_node_packages_extract node22_packages_build + docker cp temp_node_packages_extract:/usr/src/node/node_modules $GITHUB_WORKSPACE/node_modules + docker rm temp_node_packages_extract + + - name: Tar node-packages + run: | + mkdir -p ./bcrypt@5.1.0/node_modules + mkdir -p ./cld@2.9.1/node_modules + mkdir -p ./unix-dgram@2.0.6/node_modules + mv node_modules/bcrypt ./bcrypt@5.1.0/node_modules/ + mv node_modules/cld ./cld@2.9.1/node_modules/ + mv node_modules/unix-dgram ./unix-dgram@2.0.6/node_modules/ + tar --hard-dereference -cvzf packages_${{matrix.arch}}.tar.gz bcrypt@5.1.0 cld@2.9.1 unix-dgram@2.0.6 + + - name: Upload archive to release + uses: softprops/action-gh-release@v1 + with: + name: node-${{ env.NODE_VERSION }}-LATEST + tag_name: node-${{ env.NODE_VERSION }}-release + files: packages_${{matrix.arch}}.tar.gz + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/build-node.yml b/.github/workflows/build-node.yml new file mode 100644 index 000000000000..198752bda795 --- /dev/null +++ b/.github/workflows/build-node.yml @@ -0,0 +1,94 @@ +name: Build Node + +on: + workflow_dispatch: + push: + branches: + - v22.21.1 + +jobs: + build-node: + name: Build ${{ matrix.platform }}-${{ matrix.arch }} + strategy: + matrix: + include: + - platform: linux + arch: x64 + runs_on: ubuntu-22.04 + - platform: linux + arch: arm64 + runs_on: ubuntu-22.04-arm + runs-on: ${{ matrix.runs_on }} + + env: + S3_BUCKET: your-bucket-name + AWS_REGION: us-east-1 + + steps: + - name: Checkout Node fork + uses: actions/checkout@v3 + with: + repository: Asana/node + path: node + ref: ${{ github.event_name == 'pull_request' && format('refs/pull/{0}/merge', github.event.pull_request.number) || github.ref_name }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Node Version + id: extract-node-version + run: | + NODE_MAJOR_VERSION=$(grep '#define NODE_MAJOR_VERSION' node/src/node_version.h | awk '{print $3}') + NODE_MINOR_VERSION=$(grep '#define NODE_MINOR_VERSION' node/src/node_version.h | awk '{print $3}') + NODE_PATCH_VERSION=$(grep '#define NODE_PATCH_VERSION' node/src/node_version.h | awk '{print $3}') + NODE_VERSION="v${NODE_MAJOR_VERSION}.${NODE_MINOR_VERSION}.${NODE_PATCH_VERSION}" + echo "NODE_VERSION=${NODE_VERSION}" >> $GITHUB_ENV + + - name: Set build metadata + id: meta + working-directory: node + run: | + TIMESTAMP=$(date -u +%Y-%m-%dT%H-%M) + SHORT_SHA=$(git rev-parse --short HEAD) + echo "BUILD_ID=${TIMESTAMP}-${SHORT_SHA}" >> $GITHUB_ENV + echo "build_id=${TIMESTAMP}-${SHORT_SHA}" >> $GITHUB_OUTPUT + + - name: Execute the Dockerfile + working-directory: node + run: | + docker build -t node22_build -f ./Dockerfile.Node22 . + + - name: Extract resources + run: | + docker create --name temp_node_extract node22_build + docker cp temp_node_extract:/usr/src/node/node-install $GITHUB_WORKSPACE/node-install + docker rm temp_node_extract + + - name: Archive Node + run: | + mkdir -p artifacts + FILENAME=node-${NODE_VERSION}-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.meta.outputs.build_id }}.tar.xz + FILENAME_LATEST=node-${NODE_VERSION}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz + tar -C node-install -cJf artifacts/$FILENAME . + cp artifacts/$FILENAME artifacts/$FILENAME_LATEST + echo "NODE_ARCHIVE=$FILENAME" >> $GITHUB_ENV + echo "NODE_ARCHIVE_LATEST=$FILENAME_LATEST" >> $GITHUB_ENV + + - name: Upload Node archive + uses: actions/upload-artifact@v4 + with: + name: node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.meta.outputs.build_id }} + path: artifacts/${{ env.NODE_ARCHIVE }} + + - name: Upload Node archive latest + uses: actions/upload-artifact@v4 + with: + name: node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST + path: artifacts/${{ env.NODE_ARCHIVE_LATEST }} + + - name: Upload Node archive to release + uses: softprops/action-gh-release@v1 + with: + name: node-${{ env.NODE_VERSION }}-LATEST + tag_name: node-${{ env.NODE_VERSION }}-release + files: ./artifacts/${{ env.NODE_ARCHIVE_LATEST }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Dockerfile.Fibers b/Dockerfile.Fibers new file mode 100644 index 000000000000..7bc28e7b2062 --- /dev/null +++ b/Dockerfile.Fibers @@ -0,0 +1,35 @@ +# Stage 1 +FROM ubuntu:20.04 AS base + +# Set non-interactive mode to avoid prompts during installation +ENV DEBIAN_FRONTEND=noninteractive + +# Install necessary dependencies +RUN apt-get update +RUN apt-get install -y software-properties-common +RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test +RUN apt-get update +RUN apt-get install -y build-essential python3 python3-distutils g++-10 make curl git pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf automake cmake wget xz-utils unzip vim + +# Copy local Node.js source into the image +WORKDIR /usr/src/node +COPY node.tar.xz . + +RUN mkdir -p node-install && tar -C node-install -xJf node.tar.xz + +ENV PATH="/usr/src/node/node-install/usr/local/bin:$PATH" + +RUN which node +RUN node -v +RUN node -p "process.arch" +RUN echo ------------------- +RUN git clone --branch jackstrohm_node22_fibers --depth 1 https://github.com/asana/node-fibers.git node-fibers + +WORKDIR /usr/src/node/node-fibers +RUN npm install --nodedir="/usr/src/node/node-install/usr/local" +RUN npm test || true +RUN rm bin/repl +RUN find . + + +CMD ["bash"] diff --git a/Dockerfile.Node22 b/Dockerfile.Node22 new file mode 100644 index 000000000000..6142924f0cd1 --- /dev/null +++ b/Dockerfile.Node22 @@ -0,0 +1,24 @@ +# Stage 1 +FROM ubuntu:20.04 AS base + +# Set non-interactive mode to avoid prompts during installation +ENV DEBIAN_FRONTEND=noninteractive + +# Install necessary dependencies +RUN apt-get update +RUN apt-get install -y software-properties-common +RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test +RUN apt-get update && apt-get install -y build-essential python3 python3-distutils g++-10 make curl git pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf automake cmake wget xz-utils unzip vim +RUN rm -rf /var/lib/apt/lists/* + +# Set g++ 10 as the default +RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100 + +# Copy local Node.js source into the image +WORKDIR /usr/src/node +COPY . . + +RUN ./configure --experimental-enable-pointer-compression +RUN make -j4 install DESTDIR=./node-install + +CMD ["bash"] diff --git a/Dockerfile.Node22fips b/Dockerfile.Node22fips new file mode 100644 index 000000000000..4c47d8b3315a --- /dev/null +++ b/Dockerfile.Node22fips @@ -0,0 +1,29 @@ +# Stage 1 +FROM ubuntu:22.04 AS base +ARG ENABLE_FIPS +ARG DYNAMIC_LINK +ARG SHARED_OPENSSL_INCLUDES +ARG SHARED_OPENSSL_LIBNAME +ARG SHARED_OPENSSL_LIBPATH + +# Set non-interactive mode to avoid prompts during installation +ENV DEBIAN_FRONTEND=noninteractive + +# Install necessary dependencies +RUN apt-get update +RUN apt-get install -y software-properties-common +RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test +RUN apt-get update && apt-get install -y build-essential python3 python3-distutils g++-10 make curl git pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf automake cmake wget xz-utils unzip vim +RUN rm -rf /var/lib/apt/lists/* + +# Set g++ 10 as the default +RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100 + +# Copy local Node.js source into the image +WORKDIR /usr/src/node +COPY . . + +RUN ./configure --shared-openssl --experimental-enable-pointer-compression --openssl-conf-name 'openssl_conf' +RUN make -j4 install DESTDIR=./node-install + +CMD ["bash"] diff --git a/Dockerfile.Packages b/Dockerfile.Packages new file mode 100644 index 000000000000..beaf0d076abb --- /dev/null +++ b/Dockerfile.Packages @@ -0,0 +1,34 @@ +# Stage 1 +FROM ubuntu:20.04 AS base + +# Set non-interactive mode to avoid prompts during installation +ENV DEBIAN_FRONTEND=noninteractive + +# Install necessary dependencies +RUN apt-get update +RUN apt-get install -y software-properties-common +RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test +RUN apt-get update +RUN apt-get install -y build-essential python3 python3-distutils g++-10 make curl git pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf automake cmake wget xz-utils unzip vim + +# Copy local Node.js source into the image +WORKDIR /usr/src/node +COPY node.tar.xz . + +RUN mkdir -p node-install && tar -C node-install -xJf node.tar.xz + +ENV PATH="/usr/src/node/node-install/usr/local/bin:$PATH" + +RUN which node +RUN node -v +RUN node -p "process.arch" +RUN echo ------------------- + +RUN npm i --nodedir="/usr/src/node/node-install/usr/local" bcrypt@5.1.0 +RUN npm i --nodedir="/usr/src/node/node-install/usr/local" cld@2.9.1 +RUN npm i --nodedir="/usr/src/node/node-install/usr/local" unix-dgram@2.0.6 + +RUN ls +RUN ls node_modules + +CMD ["bash"] diff --git a/deps/v8/src/base/platform/platform.h b/deps/v8/src/base/platform/platform.h index 99be31da7019..9be154693165 100644 --- a/deps/v8/src/base/platform/platform.h +++ b/deps/v8/src/base/platform/platform.h @@ -582,7 +582,13 @@ class V8_BASE_EXPORT Thread { static LocalStorageKey CreateThreadLocalKey(); static void DeleteThreadLocalKey(LocalStorageKey key); static void* GetThreadLocal(LocalStorageKey key); + static int GetThreadLocalInt(LocalStorageKey key) { + return static_cast(reinterpret_cast(GetThreadLocal(key))); + } static void SetThreadLocal(LocalStorageKey key, void* value); + static void SetThreadLocalInt(LocalStorageKey key, int value) { + SetThreadLocal(key, reinterpret_cast(static_cast(value))); + } static bool HasThreadLocal(LocalStorageKey key) { return GetThreadLocal(key) != nullptr; } diff --git a/deps/v8/src/execution/isolate-inl.h b/deps/v8/src/execution/isolate-inl.h index 256db29d1407..e3d2d838118f 100644 --- a/deps/v8/src/execution/isolate-inl.h +++ b/deps/v8/src/execution/isolate-inl.h @@ -24,22 +24,24 @@ namespace v8 { namespace internal { // static -V8_INLINE Isolate::PerIsolateThreadData* -Isolate::CurrentPerIsolateThreadData() { - return g_current_per_isolate_thread_data_; -} +// Commented out for fibers support - these are now non-inline in isolate.h +// using LocalStorageKey instead of thread_local +//V8_INLINE Isolate::PerIsolateThreadData* +//Isolate::CurrentPerIsolateThreadData() { +// return g_current_per_isolate_thread_data_; +//} // static -V8_INLINE Isolate* Isolate::TryGetCurrent() { return g_current_isolate_; } +//V8_INLINE Isolate* Isolate::TryGetCurrent() { return g_current_isolate_; } // static -V8_INLINE Isolate* Isolate::Current() { - Isolate* isolate = TryGetCurrent(); - DCHECK_NOT_NULL(isolate); - return isolate; -} +//V8_INLINE Isolate* Isolate::Current() { +// Isolate* isolate = TryGetCurrent(); +// DCHECK_NOT_NULL(isolate); +// return isolate; +//} -bool Isolate::IsCurrent() const { return this == TryGetCurrent(); } +//bool Isolate::IsCurrent() const { return this == TryGetCurrent(); } void Isolate::set_context(Tagged context) { DCHECK(context.is_null() || IsContext(context)); diff --git a/deps/v8/src/execution/isolate.cc b/deps/v8/src/execution/isolate.cc index c2cc0458bb64..4fa3b0cd496a 100644 --- a/deps/v8/src/execution/isolate.cc +++ b/deps/v8/src/execution/isolate.cc @@ -494,9 +494,14 @@ size_t Isolate::HashIsolateForEmbeddedBlob() { Isolate* Isolate::process_wide_shared_space_isolate_{nullptr}; -thread_local Isolate::PerIsolateThreadData* g_current_per_isolate_thread_data_ - V8_CONSTINIT = nullptr; -thread_local Isolate* g_current_isolate_ V8_CONSTINIT = nullptr; +// LocalStorageKey-based thread locals for fibers support +base::Thread::LocalStorageKey Isolate::isolate_key_; +base::Thread::LocalStorageKey Isolate::per_isolate_thread_data_key_; +std::atomic Isolate::isolate_key_created_{false}; +// Commented out for fibers support - replaced with LocalStorageKey +//thread_local Isolate::PerIsolateThreadData* g_current_per_isolate_thread_data_ +// V8_CONSTINIT = nullptr; +//thread_local Isolate* g_current_isolate_ V8_CONSTINIT = nullptr; namespace { // A global counter for all generated Isolates, might overflow. @@ -551,7 +556,24 @@ Isolate::PerIsolateThreadData* Isolate::FindPerThreadDataForThread( return per_thread; } -void Isolate::InitializeOncePerProcess() { Heap::InitializeOncePerProcess(); } +void Isolate::InitializeOncePerProcess() { + // Initialize LocalStorageKeys for fibers support + isolate_key_ = base::Thread::CreateThreadLocalKey(); + bool expected = false; + CHECK(isolate_key_created_.compare_exchange_strong( + expected, true, std::memory_order_relaxed)); + per_isolate_thread_data_key_ = base::Thread::CreateThreadLocalKey(); + + Heap::InitializeOncePerProcess(); +} + +void Isolate::DisposeOncePerProcess() { + base::Thread::DeleteThreadLocalKey(isolate_key_); + bool expected = true; + CHECK(isolate_key_created_.compare_exchange_strong( + expected, false, std::memory_order_relaxed)); + base::Thread::DeleteThreadLocalKey(per_isolate_thread_data_key_); +} Address Isolate::get_address_from_id(IsolateAddressId id) { return isolate_addresses_[id]; @@ -3547,10 +3569,14 @@ void Isolate::Delete(Isolate* isolate) { // direct pointer. We don't use Enter/Exit here to avoid // initializing the thread data. PerIsolateThreadData* saved_data = isolate->CurrentPerIsolateThreadData(); - Isolate* saved_isolate = isolate->TryGetCurrent(); + // Use LocalStorageKey directly for fibers support + DCHECK_EQ(true, isolate_key_created_.load(std::memory_order_relaxed)); + Isolate* saved_isolate = reinterpret_cast( + base::Thread::GetThreadLocal(isolate->isolate_key_)); SetIsolateThreadLocals(isolate, nullptr); isolate->set_thread_id(ThreadId::Current()); - isolate->heap()->SetStackStart(base::Stack::GetStackStart()); + // Commented out for fibers support - can cause issues with fiber switching + //isolate->heap()->SetStackStart(base::Stack::GetStackStart()); isolate->Deinit(); @@ -4020,8 +4046,11 @@ void Isolate::Deinit() { void Isolate::SetIsolateThreadLocals(Isolate* isolate, PerIsolateThreadData* data) { - g_current_isolate_ = isolate; - g_current_per_isolate_thread_data_ = data; + // Use LocalStorageKey for fibers support + //g_current_isolate_ = isolate; + //g_current_per_isolate_thread_data_ = data; + base::Thread::SetThreadLocal(isolate_key_, isolate); + base::Thread::SetThreadLocal(per_isolate_thread_data_key_, data); #ifdef V8_COMPRESS_POINTERS_IN_ISOLATE_CAGE if (isolate) { @@ -4397,10 +4426,17 @@ void Isolate::AddCrashKeysForIsolateAndHeapPointers() { add_crash_key_callback_(v8::CrashKeyId::kReadonlySpaceFirstPageAddress, ToHexString(ro_space_firstpage_address)); - const uintptr_t old_space_firstpage_address = - heap()->old_space()->FirstPageAddress(); - add_crash_key_callback_(v8::CrashKeyId::kOldSpaceFirstPageAddress, - ToHexString(old_space_firstpage_address)); + // Commented out for pointer compression / fibers support + //const uintptr_t old_space_firstpage_address = + // heap()->old_space()->FirstPageAddress(); + //add_crash_key_callback_(v8::CrashKeyId::kOldSpaceFirstPageAddress, + // ToHexString(old_space_firstpage_address)); + if (heap()->new_space()) { + const uintptr_t map_space_firstpage_address = + heap()->new_space()->FirstPageAddress(); + add_crash_key_callback_(v8::CrashKeyId::kMapSpaceFirstPageAddress, + ToHexString(map_space_firstpage_address)); + } if (heap()->code_range_base()) { const uintptr_t code_range_base_address = heap()->code_range_base(); diff --git a/deps/v8/src/execution/isolate.h b/deps/v8/src/execution/isolate.h index 8f40fe7e1018..f641774d284e 100644 --- a/deps/v8/src/execution/isolate.h +++ b/deps/v8/src/execution/isolate.h @@ -625,6 +625,7 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { }; static void InitializeOncePerProcess(); + static void DisposeOncePerProcess(); // Creates Isolate object. Must be used instead of constructing Isolate with // new operator. @@ -645,15 +646,30 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { // Returns the PerIsolateThreadData for the current thread (or nullptr if one // is not currently set). - V8_INLINE static PerIsolateThreadData* CurrentPerIsolateThreadData(); + // Changed from V8_INLINE with thread_local to use LocalStorageKey for fibers support + static PerIsolateThreadData* CurrentPerIsolateThreadData() { + return reinterpret_cast( + base::Thread::GetThreadLocal(per_isolate_thread_data_key_)); + } // Returns the isolate inside which the current thread is running or nullptr. - V8_INLINE static Isolate* TryGetCurrent(); + // Changed from V8_INLINE with thread_local to use LocalStorageKey for fibers support + V8_INLINE static Isolate* TryGetCurrent() { + DCHECK_EQ(true, isolate_key_created_.load(std::memory_order_relaxed)); + return reinterpret_cast( + base::Thread::GetExistingThreadLocal(isolate_key_)); + } // Returns the isolate inside which the current thread is running. - V8_INLINE static Isolate* Current(); + // Changed from V8_INLINE with thread_local to use LocalStorageKey for fibers support + V8_INLINE static Isolate* Current() { + Isolate* isolate = TryGetCurrent(); + DCHECK_NOT_NULL(isolate); + return isolate; + } - inline bool IsCurrent() const; + // Changed from inline to use TryGetCurrent() with LocalStorageKey for fibers support + bool IsCurrent() const { return this == TryGetCurrent(); } // Usually called by Init(), but can be called early e.g. to allow // testing components that require logging but not the whole @@ -2285,6 +2301,12 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { static Isolate* process_wide_shared_space_isolate_; + // LocalStorageKey-based thread locals for fibers support + // (replaces thread_local variables) + static base::Thread::LocalStorageKey per_isolate_thread_data_key_; + static base::Thread::LocalStorageKey isolate_key_; + static std::atomic isolate_key_created_; + void Deinit(); static void SetIsolateThreadLocals(Isolate* isolate, @@ -2719,9 +2741,11 @@ class V8_EXPORT_PRIVATE Isolate final : private HiddenFactory { // These are outside the Isolate class with extern storage because in clang-cl, // thread_local is incompatible with dllexport linkage caused by // V8_EXPORT_PRIVATE being applied to Isolate. -extern thread_local Isolate::PerIsolateThreadData* - g_current_per_isolate_thread_data_ V8_CONSTINIT; -extern thread_local Isolate* g_current_isolate_ V8_CONSTINIT; +// +// Commented out for fibers support - replaced with LocalStorageKey in Isolate class +//extern thread_local Isolate::PerIsolateThreadData* +// g_current_per_isolate_thread_data_ V8_CONSTINIT; +//extern thread_local Isolate* g_current_isolate_ V8_CONSTINIT; #undef FIELD_ACCESSOR #undef THREAD_LOCAL_TOP_ACCESSOR diff --git a/deps/v8/src/execution/thread-id.cc b/deps/v8/src/execution/thread-id.cc index ec4e95ad5d83..d20308e7673b 100644 --- a/deps/v8/src/execution/thread-id.cc +++ b/deps/v8/src/execution/thread-id.cc @@ -11,7 +11,9 @@ namespace internal { namespace { -thread_local int thread_id = 0; +// Use LocalStorageKey instead of thread_local for fibers support +DEFINE_LAZY_LEAKY_OBJECT_GETTER(base::Thread::LocalStorageKey, GetThreadIdKey, + base::Thread::CreateThreadLocalKey()) std::atomic next_thread_id{1}; @@ -19,14 +21,18 @@ std::atomic next_thread_id{1}; // static ThreadId ThreadId::TryGetCurrent() { + int thread_id = base::Thread::GetThreadLocalInt(*GetThreadIdKey()); return thread_id == 0 ? Invalid() : ThreadId(thread_id); } // static int ThreadId::GetCurrentThreadId() { + auto key = *GetThreadIdKey(); + int thread_id = base::Thread::GetThreadLocalInt(key); if (thread_id == 0) { thread_id = next_thread_id.fetch_add(1); CHECK_LE(1, thread_id); + base::Thread::SetThreadLocalInt(key, thread_id); } return thread_id; } diff --git a/lib/internal/bootstrap/realm.js b/lib/internal/bootstrap/realm.js index af283265d37d..ea95107b5d0a 100644 --- a/lib/internal/bootstrap/realm.js +++ b/lib/internal/bootstrap/realm.js @@ -156,7 +156,17 @@ const experimentalModuleList = new SafeSet(['sqlite']); return internalBinding(module); } if (legacyWrapperList.has(module)) { - return requireBuiltin('internal/legacy/processbinding')[module](); + const legacy = requireBuiltin('internal/legacy/processbinding')[module](); + if (module === 'util') { + // We need getPromiseDetails and constants.kFulfilled from `internalBinding('util')` + // for fiber elimination utilities + return { + ...legacy, + ...internalBinding('util'), + }; + } else { + return legacy; + } } if (processBindingAllowList.has(module)) { return internalBinding(module); diff --git a/src/env.cc b/src/env.cc index 926645dc647f..a735654b8c22 100644 --- a/src/env.cc +++ b/src/env.cc @@ -586,6 +586,9 @@ IsolateData::IsolateData(Isolate* isolate, // safe guard against this. DCHECK_NE(descriptor.wrappable_type_index, BaseObject::kSlot); } else { + // Commented out for fibers support - CppHeap can cause issues + // with fiber context switching + /* cpp_heap_ = CppHeap::Create( platform, CppHeapCreateParams{ @@ -593,6 +596,7 @@ IsolateData::IsolateData(Isolate* isolate, WrapperDescriptor( BaseObject::kEmbedderType, BaseObject::kSlot, cppgc_id)}); isolate->AttachCppHeap(cpp_heap_.get()); + */ } // We do not care about overflow since we just want this to be different // from the cppgc id. From a8d4c9be7b1f6e26b377c20e22b88620a662eb24 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Wed, 4 Feb 2026 09:33:22 -0800 Subject: [PATCH 02/25] s3 staging script --- stage_for_s3.bash | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100755 stage_for_s3.bash diff --git a/stage_for_s3.bash b/stage_for_s3.bash new file mode 100755 index 000000000000..b18b0e311da9 --- /dev/null +++ b/stage_for_s3.bash @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +set -e + +mkdir stage +cd stage || exit + +TIMESTAMP=$(date '+%Y%m%d.%H%M') + +echo "Current timestamp is $TIMESTAMP" + +# Download node tarballs from the latest release +gh release download -R Asana/node -p "*.xz" + +# Generate unique identifier from timestamp and hash of downloaded files +SHORT_HASH=$(cat *.xz | sha1sum | cut -c1-4) +echo "HASH: $SHORT_HASH" +UNIQUE="pc-${TIMESTAMP}-${SHORT_HASH}" + +for file in *.tar.xz; do + if [[ "$file" == *-LATEST.tar.xz ]]; then + base="${file%-LATEST.tar.xz}" + new_name="${base}-${UNIQUE}.tar.xz" + + echo "Renaming: $file -> $new_name" + mv "$file" "$new_name" + + if [[ "$new_name" =~ node-v([0-9.]+)-(darwin|linux)-(arm64|x64).*\.tar\.xz$ ]]; then + version="${BASH_REMATCH[1]}" + os="${BASH_REMATCH[2]}" + arch="${BASH_REMATCH[3]}" + target_dir="node-v${version}-${os}-${arch}" + + echo "Target Dir: $target_dir" + mkdir "$target_dir" + tar -xJf "$new_name" -C "$target_dir" + + # Flatten directory structure if needed (handle both usr/local and direct layouts) + if [ -d "$target_dir/usr/local" ]; then + mv $target_dir/usr/local/* "$target_dir" + rm -rf "$target_dir/usr" + fi + + tar -cJf "$new_name" "$target_dir" + + rm -rf "$target_dir" + + echo "Done: Archive now contains:" + tar -tf "$new_name" | head + + else + echo "Warning: Skipped $new_name due to unexpected filename format." + fi + fi +done + +cd .. +mv stage "node-${UNIQUE}" + +echo "Files are in node-${UNIQUE}, please upload to s3" From 0c1288af1928a997b85bedc17cb3abb07f90f3ca Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Wed, 4 Feb 2026 09:33:40 -0800 Subject: [PATCH 03/25] script to build locally instead of using docker containers which don't run OSX --- build-local.sh | 132 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100755 build-local.sh diff --git a/build-local.sh b/build-local.sh new file mode 100755 index 000000000000..4d2f5f29fa01 --- /dev/null +++ b/build-local.sh @@ -0,0 +1,132 @@ +#!/bin/bash +set -e + +# Build Node.js with fibers and native packages locally +# Combines: Dockerfile.Node22, Dockerfile.Fibers, Dockerfile.Packages + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +OUTPUT_DIR="${SCRIPT_DIR}/local-build-output" +NODE_INSTALL_DIR="${OUTPUT_DIR}/node" +FIBERS_DIR="${OUTPUT_DIR}/node-fibers" +PACKAGES_DIR="${OUTPUT_DIR}/packages" + +# Number of parallel jobs for make +JOBS=${JOBS:-$(sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || echo 4)} + +echo "=== Local Node.js + Fibers Build Script ===" +echo "Output directory: ${OUTPUT_DIR}" +echo "Parallel jobs: ${JOBS}" +echo "" + +# Clean previous output +rm -rf "${OUTPUT_DIR}" +mkdir -p "${OUTPUT_DIR}" + +# ============================================ +# Stage 1: Build Node.js (Dockerfile.Node22) +# ============================================ +echo "=== Stage 1: Building Node.js ===" + +cd "${SCRIPT_DIR}" + +# Configure if needed +if [ ! -f config.gypi ] || [ ! -f config.mk ]; then + echo "Configuring Node.js with pointer compression..." + ./configure --experimental-enable-pointer-compression +fi + +# Build Node.js +echo "Building Node.js (this may take a while)..." +make -j${JOBS} + +# Install to local directory +echo "Installing Node.js to ${NODE_INSTALL_DIR}..." +make install DESTDIR="${NODE_INSTALL_DIR}" PREFIX="" + +# Verify installation +NODE_BIN="${NODE_INSTALL_DIR}/bin/node" +NPM_BIN="${NODE_INSTALL_DIR}/bin/npm" + +echo "Verifying Node.js installation..." +"${NODE_BIN}" --version +"${NODE_BIN}" -p "process.arch" +echo "Node module version: $("${NODE_BIN}" -p "process.versions.modules")" + +# IMPORTANT: Put our custom Node first in PATH so npm/node-gyp use it for native builds +export PATH="${NODE_INSTALL_DIR}/bin:${PATH}" +echo "Using node from: $(which node)" + +# ============================================ +# Stage 2: Build node-fibers (Dockerfile.Fibers) +# ============================================ +echo "" +echo "=== Stage 2: Building node-fibers ===" + +mkdir -p "${FIBERS_DIR}" +cd "${FIBERS_DIR}" + +# Clone node-fibers if not already present +if [ ! -d "node-fibers" ]; then + echo "Cloning node-fibers..." + git clone --branch jackstrohm_node20_fibers --depth 1 https://github.com/asana/node-fibers.git node-fibers +fi + +cd node-fibers + +echo "Building node-fibers..." +"${NPM_BIN}" install --nodedir="${NODE_INSTALL_DIR}" + +echo "Running node-fibers tests (failures allowed)..." +"${NPM_BIN}" test || true + +# Remove repl as in Dockerfile +rm -f bin/repl + +echo "node-fibers built successfully" + +# ============================================ +# Stage 3: Build native packages (Dockerfile.Packages) +# ============================================ +echo "" +echo "=== Stage 3: Building native packages ===" + +mkdir -p "${PACKAGES_DIR}" +cd "${PACKAGES_DIR}" + +# Initialize a package.json if needed +if [ ! -f package.json ]; then + echo '{"name": "native-packages", "private": true}' > package.json +fi + +echo "Installing bcrypt@5.1.0..." +"${NPM_BIN}" install --nodedir="${NODE_INSTALL_DIR}" bcrypt@5.1.0 + +echo "Installing cld@2.9.1..." +"${NPM_BIN}" install --nodedir="${NODE_INSTALL_DIR}" cld@2.9.1 + +echo "Installing unix-dgram@2.0.6..." +"${NPM_BIN}" install --nodedir="${NODE_INSTALL_DIR}" unix-dgram@2.0.6 + +# ============================================ +# Summary +# ============================================ +echo "" +echo "=== Build Complete ===" +echo "" +echo "Output directory structure:" +echo " ${OUTPUT_DIR}/" +echo " ├── node/ # Node.js installation" +echo " │ ├── bin/node" +echo " │ ├── bin/npm" +echo " │ └── ..." +echo " ├── node-fibers/ # node-fibers addon" +echo " │ └── node-fibers/" +echo " └── packages/ # Native packages (bcrypt, cld, unix-dgram)" +echo " └── node_modules/" +echo "" +echo "To use this Node.js:" +echo " export PATH=\"${NODE_INSTALL_DIR}/bin:\$PATH\"" +echo " node --version" +echo "" +echo "To test fibers:" +echo " cd ${FIBERS_DIR}/node-fibers && ${NODE_BIN} test.js" From 5e0424e7be6d7f44bf536e658dc311827f787767 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Wed, 4 Feb 2026 09:34:42 -0800 Subject: [PATCH 04/25] update github actions to build node fibers --- .github/workflows/build-node-fibers.yml | 12 +++--------- .github/workflows/build-node-packages.yml | 12 +++--------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build-node-fibers.yml b/.github/workflows/build-node-fibers.yml index ee7fc5f13625..d4fb84868338 100644 --- a/.github/workflows/build-node-fibers.yml +++ b/.github/workflows/build-node-fibers.yml @@ -23,6 +23,9 @@ jobs: NODE_VERSION: v22.21.1 steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Debug Matrix Values run: | echo "Matrix platform: ${{ matrix.platform }}" @@ -34,18 +37,9 @@ jobs: --repo asana/node \ --pattern "node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz" mv node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz node.tar.xz - pwd env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Download Dockerfile from GitHub API - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - curl -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw" -o Dockerfile.Fibers https://api.github.com/repos/Asana/node/contents/Dockerfile.Fibers?ref=node22.21.1_dockerfile - cat Dockerfile.Fibers - pwd - - name: Execute the Dockerfile run: | pwd diff --git a/.github/workflows/build-node-packages.yml b/.github/workflows/build-node-packages.yml index 87d799e1ce54..a75df0651b98 100644 --- a/.github/workflows/build-node-packages.yml +++ b/.github/workflows/build-node-packages.yml @@ -23,6 +23,9 @@ jobs: NODE_VERSION: v22.21.1 steps: + - name: Checkout repository + uses: actions/checkout@v3 + - name: Debug Matrix Values run: | echo "Matrix platform: ${{ matrix.platform }}" @@ -34,18 +37,9 @@ jobs: --repo asana/node \ --pattern "node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz" mv node-${{ env.NODE_VERSION }}-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz node.tar.xz - pwd env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Download Dockerfile from GitHub API - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - curl -H "Authorization: token $GITHUB_TOKEN" -H "Accept: application/vnd.github.v3.raw" -o Dockerfile.Packages https://api.github.com/repos/Asana/node/contents/Dockerfile.Packages?ref=node22.21.1_dockerfile - cat Dockerfile.Packages - pwd - - name: Execute the Dockerfile run: | pwd From 2d64ee8cd5d626a4c1092ea2fad098ee10e1ec3b Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Wed, 4 Feb 2026 09:44:18 -0800 Subject: [PATCH 05/25] fix node-fibers branchname --- Dockerfile.Fibers | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.Fibers b/Dockerfile.Fibers index 7bc28e7b2062..cc2c9b9b843c 100644 --- a/Dockerfile.Fibers +++ b/Dockerfile.Fibers @@ -23,7 +23,7 @@ RUN which node RUN node -v RUN node -p "process.arch" RUN echo ------------------- -RUN git clone --branch jackstrohm_node22_fibers --depth 1 https://github.com/asana/node-fibers.git node-fibers +RUN git clone --branch jackstrohm_node20_fibers --depth 1 https://github.com/asana/node-fibers.git node-fibers WORKDIR /usr/src/node/node-fibers RUN npm install --nodedir="/usr/src/node/node-install/usr/local" From b9b6b32cd801cfe54578f4e614178cb8d55f3219 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Wed, 4 Feb 2026 15:05:44 -0800 Subject: [PATCH 06/25] update staging directory --- stage_for_s3.bash | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/stage_for_s3.bash b/stage_for_s3.bash index b18b0e311da9..ae93b2d53d18 100755 --- a/stage_for_s3.bash +++ b/stage_for_s3.bash @@ -9,14 +9,38 @@ TIMESTAMP=$(date '+%Y%m%d.%H%M') echo "Current timestamp is $TIMESTAMP" -# Download node tarballs from the latest release +# Download packages and move to codez +gh release download -R Asana/node -p "packages_*.gz" +echo "Moving packages to $CODEZ/node/gyp" +mv packages_arm64.tar.gz $CODEZ/node/gyp/packages_arm64_node22.tar.gz +mv packages_x64.tar.gz $CODEZ/node/gyp/packages_amd64_node22.tar.gz + +# Download fibers binaries and node tarballs +gh release download -R Asana/node -p "linux-*.gz" gh release download -R Asana/node -p "*.xz" -# Generate unique identifier from timestamp and hash of downloaded files -SHORT_HASH=$(cat *.xz | sha1sum | cut -c1-4) +# Download fibers base package via CloudFront (public S3 access was disabled) +curl "https://asana-oss-cache.asana.biz/node-fibers/fibers-5.0.4.pc.tgz" --output fibers-5.0.4.tar.gz +tar -xzf fibers-5.0.4.tar.gz +rm fibers-5.0.4.tar.gz + +# Extract linux fibers binaries into the package +find . -name "linux-*.gz" | while read -r a +do + tar -xzf "$a" -C package/bin + rm "$a" +done + +# Repackage fibers with combined binaries +tar -czf temp.tgz package/ +rm -rf package +SHORT_HASH=$(cat temp.tgz | sha1sum | cut -c1-4) echo "HASH: $SHORT_HASH" UNIQUE="pc-${TIMESTAMP}-${SHORT_HASH}" +mv temp.tgz "fibers-5.0.4-${UNIQUE}.tgz" + +# Process node tarballs for file in *.tar.xz; do if [[ "$file" == *-LATEST.tar.xz ]]; then base="${file%-LATEST.tar.xz}" From b577cdadfc2a1408dfe9c6280565ef2dff898fac Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 12:30:01 -0800 Subject: [PATCH 07/25] Add fips build workflow --- .github/workflows/build-node-openssl-fips.yml | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 .github/workflows/build-node-openssl-fips.yml diff --git a/.github/workflows/build-node-openssl-fips.yml b/.github/workflows/build-node-openssl-fips.yml new file mode 100644 index 000000000000..347f4f22dde8 --- /dev/null +++ b/.github/workflows/build-node-openssl-fips.yml @@ -0,0 +1,98 @@ +name: Build Node with FIPS-enabled OpenSSL + +on: + workflow_dispatch: + inputs: + BUILD_REF: + description: 'Git ref to build Node.js from' + required: true + default: 'v22.21.1' + type: string + DOCKER_FILE: + description: 'Dockerfile to use for building Node.js' + required: true + default: 'Dockerfile.Node22fips' + type: string + +jobs: + build-node: + name: Build ${{ matrix.platform }}-${{ matrix.arch }} with FIPS OpenSSL + strategy: + matrix: + include: + - platform: linux + arch: x64 + runs_on: ubuntu-22.04 + - platform: linux + arch: arm64 + runs_on: ubuntu-22.04-arm + runs-on: ${{ matrix.runs_on }} + + steps: + - name: Checkout Node fork + uses: actions/checkout@v3 + with: + repository: Asana/node + path: node + ref: ${{ inputs.BUILD_REF }} + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Node Version + id: extract-node-version + run: | + NODE_MAJOR_VERSION=$(grep '#define NODE_MAJOR_VERSION' node/src/node_version.h | awk '{print $3}') + NODE_MINOR_VERSION=$(grep '#define NODE_MINOR_VERSION' node/src/node_version.h | awk '{print $3}') + NODE_PATCH_VERSION=$(grep '#define NODE_PATCH_VERSION' node/src/node_version.h | awk '{print $3}') + NODE_VERSION="v${NODE_MAJOR_VERSION}.${NODE_MINOR_VERSION}.${NODE_PATCH_VERSION}" + echo "NODE_VERSION=${NODE_VERSION}" >> $GITHUB_ENV + + - name: Set build metadata + id: meta + working-directory: node + run: | + TIMESTAMP=$(date -u +%Y-%m-%dT%H-%M) + SHORT_SHA=$(git rev-parse --short HEAD) + echo "BUILD_ID=${TIMESTAMP}-${SHORT_SHA}" >> $GITHUB_ENV + echo "build_id=${TIMESTAMP}-${SHORT_SHA}" >> $GITHUB_OUTPUT + + - name: Execute the Dockerfile + working-directory: node + run: | + docker build -t node22_fips_build -f ./${{ inputs.DOCKER_FILE }} . + + - name: Extract resources + run: | + docker create --name temp_node_extract node22_fips_build + docker cp temp_node_extract:/usr/src/node/node-install $GITHUB_WORKSPACE/node-install + docker rm temp_node_extract + + - name: Archive Node + run: | + mkdir -p artifacts + FILENAME=node-${NODE_VERSION}-fips-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.meta.outputs.build_id }}.tar.xz + FILENAME_LATEST=node-${NODE_VERSION}-fips-${{ matrix.platform }}-${{ matrix.arch }}-LATEST.tar.xz + tar -C node-install -cJf artifacts/$FILENAME . + cp artifacts/$FILENAME artifacts/$FILENAME_LATEST + echo "NODE_ARCHIVE=$FILENAME" >> $GITHUB_ENV + echo "NODE_ARCHIVE_LATEST=$FILENAME_LATEST" >> $GITHUB_ENV + + - name: Upload Node archive + uses: actions/upload-artifact@v4 + with: + name: node-${{ env.NODE_VERSION }}-fips-${{ matrix.platform }}-${{ matrix.arch }}-${{ steps.meta.outputs.build_id }} + path: artifacts/${{ env.NODE_ARCHIVE }} + + - name: Upload Node archive latest + uses: actions/upload-artifact@v4 + with: + name: node-${{ env.NODE_VERSION }}-fips-${{ matrix.platform }}-${{ matrix.arch }}-LATEST + path: artifacts/${{ env.NODE_ARCHIVE_LATEST }} + + - name: Upload Node archive to release + uses: softprops/action-gh-release@v1 + with: + name: node-${{ env.NODE_VERSION }}-fips-LATEST + tag_name: node-${{ env.NODE_VERSION }}-fips-release + files: ./artifacts/${{ env.NODE_ARCHIVE_LATEST }} + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From ba48e862551a1a562f3451f0ed677f53b818924d Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 12:35:16 -0800 Subject: [PATCH 08/25] change fiber and package build triggers --- .github/workflows/build-node-fibers.yml | 6 +++++- .github/workflows/build-node-packages.yml | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-node-fibers.yml b/.github/workflows/build-node-fibers.yml index d4fb84868338..1f9bc5427237 100644 --- a/.github/workflows/build-node-fibers.yml +++ b/.github/workflows/build-node-fibers.yml @@ -2,12 +2,16 @@ name: Build node-fibers with prebuilt Node on: workflow_dispatch: - push: + workflow_run: + workflows: ["Build Node"] + types: + - completed branches: - v22.21.1 jobs: build-fibers: + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} strategy: matrix: include: diff --git a/.github/workflows/build-node-packages.yml b/.github/workflows/build-node-packages.yml index a75df0651b98..8ec8d2fcc382 100644 --- a/.github/workflows/build-node-packages.yml +++ b/.github/workflows/build-node-packages.yml @@ -2,12 +2,16 @@ name: Build node-packages with prebuilt Node on: workflow_dispatch: - push: + workflow_run: + workflows: ["Build Node"] + types: + - completed branches: - v22.21.1 jobs: build-packages: + if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }} strategy: matrix: include: From 9977ae63bc2a06c103473544e8f61e71d3e7e3de Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 12:39:06 -0800 Subject: [PATCH 09/25] try to fix network errors on apt-get --- Dockerfile.Node22fips | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/Dockerfile.Node22fips b/Dockerfile.Node22fips index 4c47d8b3315a..381d7bc1e2b3 100644 --- a/Dockerfile.Node22fips +++ b/Dockerfile.Node22fips @@ -9,11 +9,21 @@ ARG SHARED_OPENSSL_LIBPATH # Set non-interactive mode to avoid prompts during installation ENV DEBIAN_FRONTEND=noninteractive +# Configure APT to retry on transient failures (common with ARM64 mirrors) +RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries + # Install necessary dependencies -RUN apt-get update -RUN apt-get install -y software-properties-common +RUN apt-get update --fix-missing && \ + apt-get install -y software-properties-common + RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test -RUN apt-get update && apt-get install -y build-essential python3 python3-distutils g++-10 make curl git pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf automake cmake wget xz-utils unzip vim + +RUN apt-get update --fix-missing && \ + apt-get install -y \ + build-essential python3 python3-distutils g++-10 make curl git \ + pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf \ + automake cmake wget xz-utils unzip vim + RUN rm -rf /var/lib/apt/lists/* # Set g++ 10 as the default From 2118248a0b116398e3bb61e66cf4da4018317698 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 12:43:30 -0800 Subject: [PATCH 10/25] working on apt-get failures --- Dockerfile.Node22fips | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile.Node22fips b/Dockerfile.Node22fips index 381d7bc1e2b3..7423e226fd1f 100644 --- a/Dockerfile.Node22fips +++ b/Dockerfile.Node22fips @@ -12,17 +12,17 @@ ENV DEBIAN_FRONTEND=noninteractive # Configure APT to retry on transient failures (common with ARM64 mirrors) RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries -# Install necessary dependencies +# Install necessary dependencies (--no-install-recommends reduces package count to avoid mirror rate limits) RUN apt-get update --fix-missing && \ - apt-get install -y software-properties-common + apt-get install -y --no-install-recommends software-properties-common gpg-agent RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test RUN apt-get update --fix-missing && \ - apt-get install -y \ + apt-get install -y --no-install-recommends \ build-essential python3 python3-distutils g++-10 make curl git \ pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf \ - automake cmake wget xz-utils unzip vim + automake cmake wget xz-utils unzip ca-certificates RUN rm -rf /var/lib/apt/lists/* From 5122fc2ae04ca6ff1b4a426461feefb4805b3ea2 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 12:45:51 -0800 Subject: [PATCH 11/25] removed push trigger --- .github/workflows/build-node.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build-node.yml b/.github/workflows/build-node.yml index 198752bda795..5a0abe9f33c0 100644 --- a/.github/workflows/build-node.yml +++ b/.github/workflows/build-node.yml @@ -2,9 +2,6 @@ name: Build Node on: workflow_dispatch: - push: - branches: - - v22.21.1 jobs: build-node: From 9260bf1f78e616f6ee47284e42431ae069066c15 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 12:47:19 -0800 Subject: [PATCH 12/25] another test --- Dockerfile.Node22fips | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Dockerfile.Node22fips b/Dockerfile.Node22fips index 7423e226fd1f..ef4f57ad91be 100644 --- a/Dockerfile.Node22fips +++ b/Dockerfile.Node22fips @@ -9,12 +9,13 @@ ARG SHARED_OPENSSL_LIBPATH # Set non-interactive mode to avoid prompts during installation ENV DEBIAN_FRONTEND=noninteractive -# Configure APT to retry on transient failures (common with ARM64 mirrors) -RUN echo 'Acquire::Retries "3";' > /etc/apt/apt.conf.d/80-retries +# Configure APT: retry on failures and use Azure mirror (ports.ubuntu.com rate-limits ARM64 builds) +RUN echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries && \ + sed -i 's|http://ports.ubuntu.com|http://azure.ports.ubuntu.com|g' /etc/apt/sources.list # Install necessary dependencies (--no-install-recommends reduces package count to avoid mirror rate limits) RUN apt-get update --fix-missing && \ - apt-get install -y --no-install-recommends software-properties-common gpg-agent + apt-get install -y --no-install-recommends ca-certificates software-properties-common gpg-agent RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test From cf4884db4edf8fb9db41701d0bf4b7e284deee29 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 12:58:29 -0800 Subject: [PATCH 13/25] switch FIPS Dockerfile to Ubuntu 20.04 Ubuntu 22.04 ARM64 mirrors (ports.ubuntu.com) are rate-limiting with 403 errors. Ubuntu 20.04 works reliably for both x64 and ARM64 builds. Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) --- Dockerfile.Node22fips | 26 +++++--------------------- 1 file changed, 5 insertions(+), 21 deletions(-) diff --git a/Dockerfile.Node22fips b/Dockerfile.Node22fips index ef4f57ad91be..061a9cbb1bea 100644 --- a/Dockerfile.Node22fips +++ b/Dockerfile.Node22fips @@ -1,30 +1,14 @@ # Stage 1 -FROM ubuntu:22.04 AS base -ARG ENABLE_FIPS -ARG DYNAMIC_LINK -ARG SHARED_OPENSSL_INCLUDES -ARG SHARED_OPENSSL_LIBNAME -ARG SHARED_OPENSSL_LIBPATH +FROM ubuntu:20.04 AS base # Set non-interactive mode to avoid prompts during installation ENV DEBIAN_FRONTEND=noninteractive -# Configure APT: retry on failures and use Azure mirror (ports.ubuntu.com rate-limits ARM64 builds) -RUN echo 'Acquire::Retries "5";' > /etc/apt/apt.conf.d/80-retries && \ - sed -i 's|http://ports.ubuntu.com|http://azure.ports.ubuntu.com|g' /etc/apt/sources.list - -# Install necessary dependencies (--no-install-recommends reduces package count to avoid mirror rate limits) -RUN apt-get update --fix-missing && \ - apt-get install -y --no-install-recommends ca-certificates software-properties-common gpg-agent - +# Install necessary dependencies +RUN apt-get update +RUN apt-get install -y software-properties-common RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test - -RUN apt-get update --fix-missing && \ - apt-get install -y --no-install-recommends \ - build-essential python3 python3-distutils g++-10 make curl git \ - pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf \ - automake cmake wget xz-utils unzip ca-certificates - +RUN apt-get update && apt-get install -y build-essential python3 python3-distutils g++-10 make curl git pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf automake cmake wget xz-utils unzip RUN rm -rf /var/lib/apt/lists/* # Set g++ 10 as the default From 381475749c79756aa3df32afd9c445a5b1781bd4 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 14:01:17 -0800 Subject: [PATCH 14/25] revert FIPS Dockerfile to Ubuntu 22.04 Ubuntu 22.04 is required for production and matches the v20 FIPS build. The earlier 403 errors were transient rate-limiting, not a fundamental issue. Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) --- Dockerfile.Node20fips | 29 +++++++++++++++++++++++++++++ Dockerfile.Node22fips | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 Dockerfile.Node20fips diff --git a/Dockerfile.Node20fips b/Dockerfile.Node20fips new file mode 100644 index 000000000000..4c47d8b3315a --- /dev/null +++ b/Dockerfile.Node20fips @@ -0,0 +1,29 @@ +# Stage 1 +FROM ubuntu:22.04 AS base +ARG ENABLE_FIPS +ARG DYNAMIC_LINK +ARG SHARED_OPENSSL_INCLUDES +ARG SHARED_OPENSSL_LIBNAME +ARG SHARED_OPENSSL_LIBPATH + +# Set non-interactive mode to avoid prompts during installation +ENV DEBIAN_FRONTEND=noninteractive + +# Install necessary dependencies +RUN apt-get update +RUN apt-get install -y software-properties-common +RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test +RUN apt-get update && apt-get install -y build-essential python3 python3-distutils g++-10 make curl git pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf automake cmake wget xz-utils unzip vim +RUN rm -rf /var/lib/apt/lists/* + +# Set g++ 10 as the default +RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-10 100 + +# Copy local Node.js source into the image +WORKDIR /usr/src/node +COPY . . + +RUN ./configure --shared-openssl --experimental-enable-pointer-compression --openssl-conf-name 'openssl_conf' +RUN make -j4 install DESTDIR=./node-install + +CMD ["bash"] diff --git a/Dockerfile.Node22fips b/Dockerfile.Node22fips index 061a9cbb1bea..88e00830cf85 100644 --- a/Dockerfile.Node22fips +++ b/Dockerfile.Node22fips @@ -1,5 +1,5 @@ # Stage 1 -FROM ubuntu:20.04 AS base +FROM ubuntu:22.04 AS base # Set non-interactive mode to avoid prompts during installation ENV DEBIAN_FRONTEND=noninteractive From 082412383fa7025daf0c5ffbb6c66388f91ed89a Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 14:26:03 -0800 Subject: [PATCH 15/25] add robust retry logic to FIPS Dockerfile apt-get Adds retry loop with 5 attempts and 15-second delays to handle transient mirror rate-limiting (403 errors) on ARM64 builds. Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) --- Dockerfile.Node22fips | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Dockerfile.Node22fips b/Dockerfile.Node22fips index 88e00830cf85..eb1a5601a9a9 100644 --- a/Dockerfile.Node22fips +++ b/Dockerfile.Node22fips @@ -8,7 +8,33 @@ ENV DEBIAN_FRONTEND=noninteractive RUN apt-get update RUN apt-get install -y software-properties-common RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test -RUN apt-get update && apt-get install -y build-essential python3 python3-distutils g++-10 make curl git pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf automake cmake wget xz-utils unzip +RUN set -e; \ + for i in 1 2 3 4 5; do \ + echo "Attempt $i: Updating and installing..."; \ + apt-get update --allow-releaseinfo-change && \ + apt-get install -y --no-install-recommends --fix-missing \ + build-essential \ + python3 \ + python3-distutils \ + g++-10 \ + make \ + curl \ + git \ + pkg-config \ + libssl-dev \ + libffi-dev \ + libgmp-dev \ + libtool \ + autoconf \ + automake \ + cmake \ + wget \ + xz-utils \ + unzip \ + vim \ + && break || \ + echo "Attempt $i failed! Waiting 15 seconds..." && sleep 15; \ + done RUN rm -rf /var/lib/apt/lists/* # Set g++ 10 as the default From 39bd639a0e205128b327163e9e6c8644d772836e Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 14:30:41 -0800 Subject: [PATCH 16/25] consolidate all apt-get commands into single retry block Combines apt-get update, software-properties-common install, PPA setup, and main package install into one retry loop. This protects all network operations from transient 403 errors on ARM64 mirrors. Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) --- Dockerfile.Node22fips | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Dockerfile.Node22fips b/Dockerfile.Node22fips index eb1a5601a9a9..926a914fee09 100644 --- a/Dockerfile.Node22fips +++ b/Dockerfile.Node22fips @@ -4,13 +4,13 @@ FROM ubuntu:22.04 AS base # Set non-interactive mode to avoid prompts during installation ENV DEBIAN_FRONTEND=noninteractive -# Install necessary dependencies -RUN apt-get update -RUN apt-get install -y software-properties-common -RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test +# Combine update, PPA setup, and install into one robust retry loop RUN set -e; \ for i in 1 2 3 4 5; do \ - echo "Attempt $i: Updating and installing..."; \ + echo "Attempt $i: Setting up build environment..." && \ + apt-get update --allow-releaseinfo-change && \ + apt-get install -y --no-install-recommends software-properties-common && \ + add-apt-repository -y ppa:ubuntu-toolchain-r/test && \ apt-get update --allow-releaseinfo-change && \ apt-get install -y --no-install-recommends --fix-missing \ build-essential \ @@ -33,7 +33,7 @@ RUN set -e; \ unzip \ vim \ && break || \ - echo "Attempt $i failed! Waiting 15 seconds..." && sleep 15; \ + (echo "Attempt $i failed! Sleeping 15s..." && sleep 15); \ done RUN rm -rf /var/lib/apt/lists/* From 18a6979c906b6c38a7a4165130af16684cab31e0 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 14:48:24 -0800 Subject: [PATCH 17/25] add robust retry logic to regular Node Dockerfile Same retry pattern as FIPS Dockerfile - consolidates all apt-get commands into single retry loop to handle transient mirror failures. Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) --- Dockerfile.Node22 | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/Dockerfile.Node22 b/Dockerfile.Node22 index 6142924f0cd1..bd59c3ffcc5e 100644 --- a/Dockerfile.Node22 +++ b/Dockerfile.Node22 @@ -4,11 +4,37 @@ FROM ubuntu:20.04 AS base # Set non-interactive mode to avoid prompts during installation ENV DEBIAN_FRONTEND=noninteractive -# Install necessary dependencies -RUN apt-get update -RUN apt-get install -y software-properties-common -RUN add-apt-repository -y ppa:ubuntu-toolchain-r/test -RUN apt-get update && apt-get install -y build-essential python3 python3-distutils g++-10 make curl git pkg-config libssl-dev libffi-dev libgmp-dev libtool autoconf automake cmake wget xz-utils unzip vim +# Combine update, PPA setup, and install into one robust retry loop +RUN set -e; \ + for i in 1 2 3 4 5; do \ + echo "Attempt $i: Setting up build environment..." && \ + apt-get update --allow-releaseinfo-change && \ + apt-get install -y --no-install-recommends software-properties-common && \ + add-apt-repository -y ppa:ubuntu-toolchain-r/test && \ + apt-get update --allow-releaseinfo-change && \ + apt-get install -y --no-install-recommends --fix-missing \ + build-essential \ + python3 \ + python3-distutils \ + g++-10 \ + make \ + curl \ + git \ + pkg-config \ + libssl-dev \ + libffi-dev \ + libgmp-dev \ + libtool \ + autoconf \ + automake \ + cmake \ + wget \ + xz-utils \ + unzip \ + vim \ + && break || \ + (echo "Attempt $i failed! Sleeping 15s..." && sleep 15); \ + done RUN rm -rf /var/lib/apt/lists/* # Set g++ 10 as the default From 99fede0cf98fa78f01713fec6416e09c5e6ae8cf Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 15:54:29 -0800 Subject: [PATCH 18/25] rename build workflows for clarity - Build Node -> Build Node (Standard) - Build Node with FIPS-enabled OpenSSL -> Build Node (FIPS) - Build node-fibers with prebuilt Node -> Build Custom Fibers - Build node-packages with prebuilt Node -> Build Node-Packages Also updated workflow_run triggers to reference new name. Co-Authored-By: Claude (global.anthropic.claude-opus-4-5-20251101-v1:0) --- .github/workflows/build-node-fibers.yml | 4 ++-- .github/workflows/build-node-openssl-fips.yml | 2 +- .github/workflows/build-node-packages.yml | 4 ++-- .github/workflows/build-node.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build-node-fibers.yml b/.github/workflows/build-node-fibers.yml index 1f9bc5427237..0d062421a568 100644 --- a/.github/workflows/build-node-fibers.yml +++ b/.github/workflows/build-node-fibers.yml @@ -1,9 +1,9 @@ -name: Build node-fibers with prebuilt Node +name: Build Custom Fibers on: workflow_dispatch: workflow_run: - workflows: ["Build Node"] + workflows: ["Build Node (Standard)"] types: - completed branches: diff --git a/.github/workflows/build-node-openssl-fips.yml b/.github/workflows/build-node-openssl-fips.yml index 347f4f22dde8..1837722ce720 100644 --- a/.github/workflows/build-node-openssl-fips.yml +++ b/.github/workflows/build-node-openssl-fips.yml @@ -1,4 +1,4 @@ -name: Build Node with FIPS-enabled OpenSSL +name: Build Node (FIPS) on: workflow_dispatch: diff --git a/.github/workflows/build-node-packages.yml b/.github/workflows/build-node-packages.yml index 8ec8d2fcc382..e09c78f287d9 100644 --- a/.github/workflows/build-node-packages.yml +++ b/.github/workflows/build-node-packages.yml @@ -1,9 +1,9 @@ -name: Build node-packages with prebuilt Node +name: Build Node-Packages on: workflow_dispatch: workflow_run: - workflows: ["Build Node"] + workflows: ["Build Node (Standard)"] types: - completed branches: diff --git a/.github/workflows/build-node.yml b/.github/workflows/build-node.yml index 5a0abe9f33c0..5d3e80fc1931 100644 --- a/.github/workflows/build-node.yml +++ b/.github/workflows/build-node.yml @@ -1,4 +1,4 @@ -name: Build Node +name: Build Node (Standard) on: workflow_dispatch: From 0f26935ba387c834e441f11f7bd1f9967c9bad27 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 16:43:02 -0800 Subject: [PATCH 19/25] added fips to the staging script to make sure we don't have cache issues --- stage_for_s3.bash | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/stage_for_s3.bash b/stage_for_s3.bash index ae93b2d53d18..6bc1a362c1ec 100755 --- a/stage_for_s3.bash +++ b/stage_for_s3.bash @@ -10,14 +10,17 @@ TIMESTAMP=$(date '+%Y%m%d.%H%M') echo "Current timestamp is $TIMESTAMP" # Download packages and move to codez -gh release download -R Asana/node -p "packages_*.gz" +gh release download -R Asana/node node-v22.21.1-release -p "packages_*.gz" echo "Moving packages to $CODEZ/node/gyp" mv packages_arm64.tar.gz $CODEZ/node/gyp/packages_arm64_node22.tar.gz mv packages_x64.tar.gz $CODEZ/node/gyp/packages_amd64_node22.tar.gz -# Download fibers binaries and node tarballs -gh release download -R Asana/node -p "linux-*.gz" -gh release download -R Asana/node -p "*.xz" +# Download fibers binaries and node tarballs (standard build) +gh release download -R Asana/node node-v22.21.1-release -p "linux-*.gz" +gh release download -R Asana/node node-v22.21.1-release -p "*.xz" + +# Download FIPS node tarballs +gh release download -R Asana/node node-v22.21.1-fips-release -p "*.xz" # Download fibers base package via CloudFront (public S3 access was disabled) curl "https://asana-oss-cache.asana.biz/node-fibers/fibers-5.0.4.pc.tgz" --output fibers-5.0.4.tar.gz @@ -49,11 +52,12 @@ for file in *.tar.xz; do echo "Renaming: $file -> $new_name" mv "$file" "$new_name" - if [[ "$new_name" =~ node-v([0-9.]+)-(darwin|linux)-(arm64|x64).*\.tar\.xz$ ]]; then + if [[ "$new_name" =~ node-v([0-9.]+)-(fips-)?(darwin|linux)-(arm64|x64).*\.tar\.xz$ ]]; then version="${BASH_REMATCH[1]}" - os="${BASH_REMATCH[2]}" - arch="${BASH_REMATCH[3]}" - target_dir="node-v${version}-${os}-${arch}" + fips="${BASH_REMATCH[2]}" + os="${BASH_REMATCH[3]}" + arch="${BASH_REMATCH[4]}" + target_dir="node-v${version}-${fips}${os}-${arch}" echo "Target Dir: $target_dir" mkdir "$target_dir" @@ -82,3 +86,4 @@ cd .. mv stage "node-${UNIQUE}" echo "Files are in node-${UNIQUE}, please upload to s3" +echo "Packages were moved to $CODEZ/node/gyp/packages_{arm64,amd64}_node22.tar.gz" From 829e1c9cd500d4941ff2ea7c88e69e88de504083 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 21:40:51 -0800 Subject: [PATCH 20/25] make staging do fips and update asana codez codebase --- stage_for_s3.bash | 85 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/stage_for_s3.bash b/stage_for_s3.bash index 6bc1a362c1ec..a50f10c0a511 100755 --- a/stage_for_s3.bash +++ b/stage_for_s3.bash @@ -87,3 +87,88 @@ mv stage "node-${UNIQUE}" echo "Files are in node-${UNIQUE}, please upload to s3" echo "Packages were moved to $CODEZ/node/gyp/packages_{arm64,amd64}_node22.tar.gz" + +# Compute SHA256 hashes for node archives +echo "" +echo "Computing SHA256 hashes..." +STAGE_DIR="node-${UNIQUE}" + +SHA_LINUX_ARM64=$(shasum -a 256 "${STAGE_DIR}/node-v22.21.1-linux-arm64-${UNIQUE}.tar.xz" | cut -d' ' -f1) +SHA_LINUX_X64=$(shasum -a 256 "${STAGE_DIR}/node-v22.21.1-linux-x64-${UNIQUE}.tar.xz" | cut -d' ' -f1) +SHA_FIPS_LINUX_ARM64=$(shasum -a 256 "${STAGE_DIR}/node-v22.21.1-fips-linux-arm64-${UNIQUE}.tar.xz" | cut -d' ' -f1) +SHA_FIPS_LINUX_X64=$(shasum -a 256 "${STAGE_DIR}/node-v22.21.1-fips-linux-x64-${UNIQUE}.tar.xz" | cut -d' ' -f1) + +echo " Standard linux-arm64: $SHA_LINUX_ARM64" +echo " Standard linux-x64: $SHA_LINUX_X64" +echo " FIPS linux-arm64: $SHA_FIPS_LINUX_ARM64" +echo " FIPS linux-x64: $SHA_FIPS_LINUX_X64" + +# Update package.json with new fibers URL +echo "" +echo "Updating $CODEZ/asana2/package.json..." +FIBERS_URL="https://asana-oss-cache.s3.us-east-1.amazonaws.com/node-fibers/fibers-5.0.4-${UNIQUE}.tgz" +sed -i '' "s|\"fibers\": \"https://asana-oss-cache.s3.us-east-1.amazonaws.com/node-fibers/fibers-5.0.4-[^\"]*\"|\"fibers\": \"${FIBERS_URL}\"|" "$CODEZ/asana2/package.json" +echo " Updated fibers URL to: $FIBERS_URL" + +# Update node_setup.bzl +echo "" +echo "Updating $CODEZ/asana2/third_party/node/node_setup.bzl..." +NODE_SETUP_FILE="$CODEZ/asana2/third_party/node/node_setup.bzl" + +# Remove existing nodejs22pc block if present +sed -i '' '/nodejs_register_toolchains(/,/^[[:space:]]*)/{ /name = "nodejs22pc"/,/^[[:space:]]*node_urls = NODE_FORK_URLS,/{ /^[[:space:]]*)/d; }; /name = "nodejs22pc"/,/node_urls = NODE_FORK_URLS,/d; }' "$NODE_SETUP_FILE" + +# Remove existing nodejsfips block (the old v20 one) +sed -i '' '/nodejs_register_toolchains(/,/^[[:space:]]*)/{ /name = "nodejsfips"/,/^[[:space:]]*node_urls = NODE_FIPS_URLS,/{ /^[[:space:]]*)/d; }; /name = "nodejsfips"/,/node_urls = NODE_FIPS_URLS,/d; }' "$NODE_SETUP_FILE" + +# Remove existing nodejs22fips block if present +sed -i '' '/nodejs_register_toolchains(/,/^[[:space:]]*)/{ /name = "nodejs22fips"/,/^[[:space:]]*node_urls = NODE_FIPS_URLS,/{ /^[[:space:]]*)/d; }; /name = "nodejs22fips"/,/node_urls = NODE_FIPS_URLS,/d; }' "$NODE_SETUP_FILE" + +# Write the new toolchain blocks to a temp file +TEMP_BLOCKS=$(mktemp) +cat > "$TEMP_BLOCKS" << EOF + + nodejs_register_toolchains( + name = "nodejs22pc", + node_version = "22.21.1", + node_repositories = { + "22.21.1-linux_arm64": ("node-v22.21.1-linux-arm64-${UNIQUE}.tar.xz", "node-v22.21.1-linux-arm64", "${SHA_LINUX_ARM64}"), + "22.21.1-linux_amd64": ("node-v22.21.1-linux-x64-${UNIQUE}.tar.xz", "node-v22.21.1-linux-x64", "${SHA_LINUX_X64}"), + }, + node_urls = NODE_FORK_URLS, + ) + + nodejs_register_toolchains( + name = "nodejs22fips", + node_version = "22.21.1", + node_repositories = { + "22.21.1-linux_arm64": ("node-v22.21.1-fips-linux-arm64-${UNIQUE}.tar.xz", "node-v22.21.1-fips-linux-arm64", "${SHA_FIPS_LINUX_ARM64}"), + "22.21.1-linux_amd64": ("node-v22.21.1-fips-linux-x64-${UNIQUE}.tar.xz", "node-v22.21.1-fips-linux-x64", "${SHA_FIPS_LINUX_X64}"), + }, + node_urls = NODE_FIPS_URLS, + ) +EOF + +# Find the line number of the closing paren after nodejs20pc and insert after it +LINE_NUM=$(awk '/name = "nodejs20pc"/{found=1} found && /^[[:space:]]*\)/{print NR; exit}' "$NODE_SETUP_FILE") +if [ -n "$LINE_NUM" ]; then + head -n "$LINE_NUM" "$NODE_SETUP_FILE" > "${NODE_SETUP_FILE}.tmp" + cat "$TEMP_BLOCKS" >> "${NODE_SETUP_FILE}.tmp" + tail -n +$((LINE_NUM + 1)) "$NODE_SETUP_FILE" >> "${NODE_SETUP_FILE}.tmp" + mv "${NODE_SETUP_FILE}.tmp" "$NODE_SETUP_FILE" +fi +rm "$TEMP_BLOCKS" + +echo " Added nodejs22pc and nodejs22fips toolchains" +echo " Removed old nodejsfips toolchain" + +echo "" +echo "==========================================" +echo "DONE! Please review the changes before committing:" +echo " - $CODEZ/asana2/package.json" +echo " - $CODEZ/asana2/third_party/node/node_setup.bzl" +echo " - Files in ${STAGE_DIR}/ ready for S3 upload" +echo "" +echo "After uploading files to S3, run:" +echo " cd $CODEZ/asana2 && pnpm install" +echo "==========================================" From 6dad6feb52778f77234f36c119b76941b4b32ea7 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Tue, 10 Feb 2026 21:42:59 -0800 Subject: [PATCH 21/25] update lockfile instructions --- stage_for_s3.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stage_for_s3.bash b/stage_for_s3.bash index a50f10c0a511..f28ab179b074 100755 --- a/stage_for_s3.bash +++ b/stage_for_s3.bash @@ -170,5 +170,5 @@ echo " - $CODEZ/asana2/third_party/node/node_setup.bzl" echo " - Files in ${STAGE_DIR}/ ready for S3 upload" echo "" echo "After uploading files to S3, run:" -echo " cd $CODEZ/asana2 && pnpm install" +echo " cd $CODEZ/asana2 && z npm update-lockfile" echo "==========================================" From ab57e94b5c639e1da834a2af1e5c3ce2ab7fb900 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Mon, 2 Mar 2026 13:34:57 -0800 Subject: [PATCH 22/25] fix staging script: validate fibers archive and fix node_setup.bzl updates - Use versioned fibers base package (20250515) which includes Node 20 binaries - Add validation that combined fibers archive contains Node 18, 20, and 22 binaries - Add diagnostic output when linux-*.gz injection step finds no files - Replace broken sed block-removal commands with Python to avoid leaving empty stubs - Preserve nodejsfips (Node 20 FIPS) toolchain instead of removing it Co-Authored-By: Claude Sonnet 4.6 --- stage_for_s3.bash | 89 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 15 deletions(-) diff --git a/stage_for_s3.bash b/stage_for_s3.bash index f28ab179b074..794de5173386 100755 --- a/stage_for_s3.bash +++ b/stage_for_s3.bash @@ -23,20 +23,52 @@ gh release download -R Asana/node node-v22.21.1-release -p "*.xz" gh release download -R Asana/node node-v22.21.1-fips-release -p "*.xz" # Download fibers base package via CloudFront (public S3 access was disabled) -curl "https://asana-oss-cache.asana.biz/node-fibers/fibers-5.0.4.pc.tgz" --output fibers-5.0.4.tar.gz +curl "https://asana-oss-cache.asana.biz/node-fibers/fibers-5.0.4-pc-20250515.1411-960a.tgz" --output fibers-5.0.4.tar.gz tar -xzf fibers-5.0.4.tar.gz rm fibers-5.0.4.tar.gz # Extract linux fibers binaries into the package -find . -name "linux-*.gz" | while read -r a -do - tar -xzf "$a" -C package/bin - rm "$a" -done +LINUX_GZ_FILES=$(find . -name "linux-*.gz") +if [ -z "$LINUX_GZ_FILES" ]; then + echo "WARNING: No linux-*.gz fibers archives were found to inject (check CI artifact names)" +else + echo "$LINUX_GZ_FILES" | while read -r a; do + echo "Injecting $(basename "$a") into package/bin..." + tar -xzf "$a" -C package/bin + rm "$a" + done +fi +echo "Contents of package/bin after injection:" +ls package/bin/ # Repackage fibers with combined binaries tar -czf temp.tgz package/ rm -rf package + +# Validate combined archive contains Node 18, 20, and 22 binaries +echo "Validating combined fibers archive..." +echo "fibers.node entries in archive:" +tar -tzf temp.tgz | grep 'fibers\.node' || echo " (none found)" +FOUND_VERSIONS="" +for bin_path in $(tar -tzf temp.tgz | grep 'fibers\.node$'); do + abi_ver=$(echo "$bin_path" | cut -d/ -f3 | cut -d- -f3) + case "$abi_ver" in + 109) FOUND_VERSIONS="$FOUND_VERSIONS Node18(abi=$abi_ver)" ;; + 115) FOUND_VERSIONS="$FOUND_VERSIONS Node20(abi=$abi_ver)" ;; + 127) FOUND_VERSIONS="$FOUND_VERSIONS Node22(abi=$abi_ver)" ;; + esac +done +echo "Found binaries:${FOUND_VERSIONS}" +MISSING="" +echo "$FOUND_VERSIONS" | grep -q "Node18" || MISSING="$MISSING Node18(abi=109)" +echo "$FOUND_VERSIONS" | grep -q "Node20" || MISSING="$MISSING Node20(abi=115)" +echo "$FOUND_VERSIONS" | grep -q "Node22" || MISSING="$MISSING Node22(abi=127)" +if [ -n "$MISSING" ]; then + echo "ERROR: Combined fibers archive is missing binaries for:$MISSING" >&2 + exit 1 +fi +echo "Validation passed: archive contains binaries for Node 18, 20, and 22" + SHORT_HASH=$(cat temp.tgz | sha1sum | cut -c1-4) echo "HASH: $SHORT_HASH" UNIQUE="pc-${TIMESTAMP}-${SHORT_HASH}" @@ -115,14 +147,42 @@ echo "" echo "Updating $CODEZ/asana2/third_party/node/node_setup.bzl..." NODE_SETUP_FILE="$CODEZ/asana2/third_party/node/node_setup.bzl" -# Remove existing nodejs22pc block if present -sed -i '' '/nodejs_register_toolchains(/,/^[[:space:]]*)/{ /name = "nodejs22pc"/,/^[[:space:]]*node_urls = NODE_FORK_URLS,/{ /^[[:space:]]*)/d; }; /name = "nodejs22pc"/,/node_urls = NODE_FORK_URLS,/d; }' "$NODE_SETUP_FILE" - -# Remove existing nodejsfips block (the old v20 one) -sed -i '' '/nodejs_register_toolchains(/,/^[[:space:]]*)/{ /name = "nodejsfips"/,/^[[:space:]]*node_urls = NODE_FIPS_URLS,/{ /^[[:space:]]*)/d; }; /name = "nodejsfips"/,/node_urls = NODE_FIPS_URLS,/d; }' "$NODE_SETUP_FILE" - -# Remove existing nodejs22fips block if present -sed -i '' '/nodejs_register_toolchains(/,/^[[:space:]]*)/{ /name = "nodejs22fips"/,/^[[:space:]]*node_urls = NODE_FIPS_URLS,/{ /^[[:space:]]*)/d; }; /name = "nodejs22fips"/,/node_urls = NODE_FIPS_URLS,/d; }' "$NODE_SETUP_FILE" +# Remove existing nodejs22pc and nodejs22fips blocks if present +python3 - "$NODE_SETUP_FILE" <<'PYEOF' +import sys + +def remove_toolchain_block(lines, name): + result = [] + i = 0 + while i < len(lines): + if 'nodejs_register_toolchains(' in lines[i]: + block = [lines[i]] + j = i + 1 + while j < len(lines): + block.append(lines[j]) + if lines[j].strip() == ')': + break + j += 1 + if any(f'name = "{name}"' in l for l in block): + i = j + 1 # skip the whole block + continue + else: + result.extend(block) + i = j + 1 + else: + result.append(lines[i]) + i += 1 + return result + +with open(sys.argv[1], 'r') as f: + lines = f.read().splitlines(keepends=True) + +for name in ['nodejs22pc', 'nodejs22fips']: + lines = remove_toolchain_block(lines, name) + +with open(sys.argv[1], 'w') as f: + f.writelines(lines) +PYEOF # Write the new toolchain blocks to a temp file TEMP_BLOCKS=$(mktemp) @@ -160,7 +220,6 @@ fi rm "$TEMP_BLOCKS" echo " Added nodejs22pc and nodejs22fips toolchains" -echo " Removed old nodejsfips toolchain" echo "" echo "==========================================" From bd560ee309954270cb6b7a187f8d069561ee80a7 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Wed, 4 Mar 2026 16:12:15 -0800 Subject: [PATCH 23/25] Untested support for profiler --- .github/workflows/build-node-packages.yml | 4 +++- Dockerfile.Packages | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-node-packages.yml b/.github/workflows/build-node-packages.yml index e09c78f287d9..e32ffb031000 100644 --- a/.github/workflows/build-node-packages.yml +++ b/.github/workflows/build-node-packages.yml @@ -60,10 +60,12 @@ jobs: mkdir -p ./bcrypt@5.1.0/node_modules mkdir -p ./cld@2.9.1/node_modules mkdir -p ./unix-dgram@2.0.6/node_modules + mkdir -p "./@datadog+pprof@5.8.0/node_modules/@datadog" mv node_modules/bcrypt ./bcrypt@5.1.0/node_modules/ mv node_modules/cld ./cld@2.9.1/node_modules/ mv node_modules/unix-dgram ./unix-dgram@2.0.6/node_modules/ - tar --hard-dereference -cvzf packages_${{matrix.arch}}.tar.gz bcrypt@5.1.0 cld@2.9.1 unix-dgram@2.0.6 + mv "node_modules/@datadog/pprof" "./@datadog+pprof@5.8.0/node_modules/@datadog/" + tar --hard-dereference -cvzf packages_${{matrix.arch}}.tar.gz bcrypt@5.1.0 cld@2.9.1 unix-dgram@2.0.6 "@datadog+pprof@5.8.0" - name: Upload archive to release uses: softprops/action-gh-release@v1 diff --git a/Dockerfile.Packages b/Dockerfile.Packages index beaf0d076abb..ef417ab33ead 100644 --- a/Dockerfile.Packages +++ b/Dockerfile.Packages @@ -28,6 +28,23 @@ RUN npm i --nodedir="/usr/src/node/node-install/usr/local" bcrypt@5.1.0 RUN npm i --nodedir="/usr/src/node/node-install/usr/local" cld@2.9.1 RUN npm i --nodedir="/usr/src/node/node-install/usr/local" unix-dgram@2.0.6 +# Build @datadog/pprof from source against Asana custom Node. +# The npm package ships with "install: exit 0" and no C++ source (only prebuilts), +# so we clone from GitHub to build the native binding against our pointer-compressed V8. +# The standard prebuilts are ABI-incompatible with --experimental-enable-pointer-compression. +RUN npm i @datadog/pprof@5.8.0 --ignore-scripts +RUN git clone --branch v5.8.0 --depth 1 https://github.com/DataDog/pprof-nodejs.git /usr/src/pprof-source +WORKDIR /usr/src/pprof-source +RUN npm install --ignore-scripts +RUN npm_config_nodedir="/usr/src/node/node-install/usr/local" node_modules/.bin/node-gyp rebuild --jobs=max +# Replace the linux prebuilt with our pointer-compression-compatible binary. +# NODE_MODULE_VERSION and arch are read dynamically from the custom Node binary. +WORKDIR /usr/src/node +RUN ARCH=$(node -e "process.stdout.write(process.arch)") && \ + MODULES_VER=$(node -e "process.stdout.write(process.versions.modules)") && \ + cp /usr/src/pprof-source/build/Release/profiler.node \ + node_modules/@datadog/pprof/prebuilds/linuxglibc-${ARCH}/node-${MODULES_VER}.node + RUN ls RUN ls node_modules From d2b5f7f5bf014aa5fc0e1c360e12c57c2f59f7a0 Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Thu, 5 Mar 2026 15:06:43 -0800 Subject: [PATCH 24/25] Install node-gyp before rebuilding pprof native binding node_modules/.bin/node-gyp was not found because node-gyp was not explicitly installed in the pprof-source workdir. Add an explicit npm install step to make it available. Co-Authored-By: Claude Sonnet 4.6 --- Dockerfile.Packages | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile.Packages b/Dockerfile.Packages index ef417ab33ead..b66c8ae63d7b 100644 --- a/Dockerfile.Packages +++ b/Dockerfile.Packages @@ -36,6 +36,7 @@ RUN npm i @datadog/pprof@5.8.0 --ignore-scripts RUN git clone --branch v5.8.0 --depth 1 https://github.com/DataDog/pprof-nodejs.git /usr/src/pprof-source WORKDIR /usr/src/pprof-source RUN npm install --ignore-scripts +RUN npm install --unsafe-perm node-gyp RUN npm_config_nodedir="/usr/src/node/node-install/usr/local" node_modules/.bin/node-gyp rebuild --jobs=max # Replace the linux prebuilt with our pointer-compression-compatible binary. # NODE_MODULE_VERSION and arch are read dynamically from the custom Node binary. From 17f3b44df69ac4675dd87557bb5544aa9664b98f Mon Sep 17 00:00:00 2001 From: Jack Strohm Date: Thu, 5 Mar 2026 15:18:10 -0800 Subject: [PATCH 25/25] Fix pprof native build: use --build-from-source and find .node output - Replace --ignore-scripts with --build-from-source so npm properly runs build steps for pprof dependencies - Switch node-gyp rebuild to use explicit --nodedir flag (more reliable than npm_config_nodedir env var) - Use find to locate the built .node binary instead of hardcoding profiler.node, which may differ across pprof-nodejs versions Co-Authored-By: Claude Sonnet 4.6 --- Dockerfile.Packages | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Dockerfile.Packages b/Dockerfile.Packages index b66c8ae63d7b..a18babc06af9 100644 --- a/Dockerfile.Packages +++ b/Dockerfile.Packages @@ -35,15 +35,19 @@ RUN npm i --nodedir="/usr/src/node/node-install/usr/local" unix-dgram@2.0.6 RUN npm i @datadog/pprof@5.8.0 --ignore-scripts RUN git clone --branch v5.8.0 --depth 1 https://github.com/DataDog/pprof-nodejs.git /usr/src/pprof-source WORKDIR /usr/src/pprof-source -RUN npm install --ignore-scripts +RUN npm install --unsafe-perm --build-from-source RUN npm install --unsafe-perm node-gyp -RUN npm_config_nodedir="/usr/src/node/node-install/usr/local" node_modules/.bin/node-gyp rebuild --jobs=max +RUN node_modules/.bin/node-gyp rebuild --jobs=max --nodedir=/usr/src/node/node-install/usr/local # Replace the linux prebuilt with our pointer-compression-compatible binary. # NODE_MODULE_VERSION and arch are read dynamically from the custom Node binary. +# Use find to locate the built .node file since the exact name may vary by version. WORKDIR /usr/src/node RUN ARCH=$(node -e "process.stdout.write(process.arch)") && \ MODULES_VER=$(node -e "process.stdout.write(process.versions.modules)") && \ - cp /usr/src/pprof-source/build/Release/profiler.node \ + PROFILER_NODE=$(find /usr/src/pprof-source/build/Release -name "*.node" | head -1) && \ + echo "Using profiler binary: $PROFILER_NODE" && \ + test -f "$PROFILER_NODE" && \ + cp "$PROFILER_NODE" \ node_modules/@datadog/pprof/prebuilds/linuxglibc-${ARCH}/node-${MODULES_VER}.node RUN ls