From 61845426829ccbea072a417df45dd8b3d5d822d3 Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Wed, 21 Feb 2024 14:13:43 -0800 Subject: [PATCH] Add `BUN_DEBUG` flag to control where debug logs go (#9019) * Add `BUN_DEBUG` flag to control where debug logs go * Update all the actions * Configure temp * use spawn instead of rm * Use CLOSE_RANGE_CLOEXEC * Make some tests more reproducible * Update hot.test.ts * Detect file descriptor leaks and wait for stdout * Update runner.node.mjs * Update preload.ts --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- .github/workflows/bun-linux-aarch64.yml | 12 +- .github/workflows/bun-linux-build.yml | 19 +- .github/workflows/bun-mac-aarch64.yml | 31 +- .github/workflows/bun-mac-x64-baseline.yml | 33 +- .github/workflows/bun-mac-x64.yml | 33 +- .github/workflows/bun-release.yml | 18 +- .github/workflows/bun-types-tests.yml | 2 +- .github/workflows/bun-windows.yml | 37 +- bunfig.toml | 1 + docs/cli/install.md | 2 +- docs/guides/runtime/cicd.md | 2 +- .../bun-internal-test/src/runner.node.mjs | 116 ++++- packages/bun-uws/.github/workflows/codeql.yml | 2 +- packages/bun-uws/.github/workflows/cpp.yml | 2 +- src/bun.js/bindings/bun-spawn.cpp | 17 +- src/bun.js/bindings/c-bindings.cpp | 2 +- src/output.zig | 54 ++- test/cli/hot/hot.test.ts | 443 ++++++++++-------- test/js/node/watch/fs.watchFile.test.ts | 2 +- test/preload.ts | 10 + 20 files changed, 521 insertions(+), 317 deletions(-) create mode 100644 test/preload.ts diff --git a/.github/workflows/bun-linux-aarch64.yml b/.github/workflows/bun-linux-aarch64.yml index 292f58e02c..3ba8f9dab9 100644 --- a/.github/workflows/bun-linux-aarch64.yml +++ b/.github/workflows/bun-linux-aarch64.yml @@ -51,14 +51,14 @@ jobs: runner: linux-arm64 build_machine_arch: aarch64 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: false ref: ${{github.sha}} clean: true - run: | bash ./scripts/update-submodules.sh - - uses: docker/setup-buildx-action@v2 + - uses: docker/setup-buildx-action@v3 id: buildx with: install: true @@ -66,7 +66,7 @@ jobs: run: | rm -rf ${{runner.temp}}/release - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -74,7 +74,7 @@ jobs: - run: | mkdir -p /tmp/.buildx-cache-${{matrix.tag}} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: . push: false @@ -113,11 +113,11 @@ jobs: zip -r bun-${{matrix.tag}}-profile.zip bun-${{matrix.tag}}-profile zip -r bun-${{matrix.tag}}.zip bun-${{matrix.tag}} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: bun-${{matrix.tag}}-profile path: ${{runner.temp}}/release/bun-${{matrix.tag}}-profile.zip - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: bun-${{matrix.tag}} path: ${{runner.temp}}/release/bun-${{matrix.tag}}.zip diff --git a/.github/workflows/bun-linux-build.yml b/.github/workflows/bun-linux-build.yml index bee8cee6ac..6e8543902c 100644 --- a/.github/workflows/bun-linux-build.yml +++ b/.github/workflows/bun-linux-build.yml @@ -86,7 +86,7 @@ jobs: submodules: recursive ref: ${{github.sha}} clean: true - - uses: docker/setup-buildx-action@v2 + - uses: docker/setup-buildx-action@v3 id: buildx with: install: true @@ -94,7 +94,7 @@ jobs: run: | rm -rf ${{runner.temp}}/release - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -102,7 +102,7 @@ jobs: - run: | mkdir -p /tmp/.buildx-cache-${{matrix.tag}} - name: Build and push - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: . push: false @@ -154,19 +154,19 @@ jobs: zip -r bun-${{matrix.tag}}-profile.zip bun-${{matrix.tag}}-profile zip -r bun-${{matrix.tag}}.zip bun-${{matrix.tag}} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: bun-${{matrix.tag}}-profile path: ${{runner.temp}}/release/bun-${{matrix.tag}}-profile.zip - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: bun-${{matrix.tag}} path: ${{runner.temp}}/release/bun-${{matrix.tag}}.zip - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: bun-obj-${{matrix.tag}} path: ${{runner.temp}}/release/bun-obj - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{matrix.tag}}-dependencies path: ${{runner.temp}}/release/bun-dependencies @@ -234,7 +234,7 @@ jobs: clean: true - id: download name: Download - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: bun-${{matrix.tag}} path: ${{runner.temp}}/release @@ -275,6 +275,7 @@ jobs: name: Test (node runner) env: SMTP_SENDGRID_SENDER: ${{ secrets.SMTP_SENDGRID_SENDER }} + TMPDIR: ${{runner.temp}} TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} # if: ${{github.event.inputs.use_bun == 'false'}} @@ -283,7 +284,7 @@ jobs: ulimit -c node packages/bun-internal-test/src/runner.node.mjs || true - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: steps.test.outputs.failing_tests != '' with: name: cores diff --git a/.github/workflows/bun-mac-aarch64.yml b/.github/workflows/bun-mac-aarch64.yml index 8f312e4f49..bcb01918fa 100644 --- a/.github/workflows/bun-mac-aarch64.yml +++ b/.github/workflows/bun-mac-aarch64.yml @@ -51,20 +51,20 @@ jobs: # run: git submodule update --init --recursive --depth=1 --progress --force - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 id: buildx with: install: true - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Compile Zig Object - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 if: runner.arch == 'X64' with: context: . @@ -84,7 +84,7 @@ jobs: outputs: type=local,dest=${{runner.temp}}/release - name: Upload Zig Object - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.tag }} path: ${{runner.temp}}/release/bun-zig.o @@ -141,7 +141,7 @@ jobs: - name: Cache submodule dependencies id: cache-deps-restore - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: ${{runner.temp}}/bun-deps key: bun-deps-${{ matrix.tag }}-${{ steps.submodule-versions.outputs.sha }} @@ -159,13 +159,13 @@ jobs: - name: Cache submodule dependencies if: ${{ !steps.cache-deps-restore.outputs.cache-hit }} id: cache-deps-save - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 with: path: ${{runner.temp}}/bun-deps key: ${{ steps.cache-deps-restore.outputs.cache-primary-key }} - name: Upload submodule dependencies - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.tag }}-deps path: ${{runner.temp}}/bun-deps @@ -235,7 +235,7 @@ jobs: bash compile-cpp-only.sh -v - name: Upload C++ - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.tag }}-cpp path: ${{ runner.temp }}/bun-cpp-obj/bun-cpp-objects.a @@ -285,19 +285,19 @@ jobs: echo "${{ runner.temp }}/.bun/bin" >> $GITHUB_PATH - name: Download C++ - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ matrix.tag }}-cpp path: ${{ runner.temp }}/bun-cpp-obj - name: Download Zig Object - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ matrix.obj }} path: ${{ runner.temp }}/release - name: Downloaded submodule dependencies - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ matrix.tag }}-deps path: ${{runner.temp}}/bun-deps @@ -330,11 +330,11 @@ jobs: zip -r ${{matrix.tag}}-profile.zip ${{matrix.tag}}-profile zip -r ${{matrix.tag}}.zip ${{matrix.tag}} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{matrix.tag}}-profile path: ${{runner.temp}}/link-build/${{matrix.tag}}-profile.zip - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{matrix.tag}} path: ${{runner.temp}}/link-build/${{matrix.tag}}.zip @@ -394,12 +394,12 @@ jobs: steps: - id: checkout name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: false - id: download name: Download - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{matrix.tag}} path: ${{runner.temp}}/release @@ -426,6 +426,7 @@ jobs: name: Test (node runner) env: SMTP_SENDGRID_SENDER: ${{ secrets.SMTP_SENDGRID_SENDER }} + TMPDIR: ${{runner.temp}} TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} # if: ${{github.event.inputs.use_bun == 'false'}} diff --git a/.github/workflows/bun-mac-x64-baseline.yml b/.github/workflows/bun-mac-x64-baseline.yml index 3e1493f2b9..ce7518ebae 100644 --- a/.github/workflows/bun-mac-x64-baseline.yml +++ b/.github/workflows/bun-mac-x64-baseline.yml @@ -55,20 +55,20 @@ jobs: - uses: actions/checkout@v4 - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 id: buildx with: install: true - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Compile Zig Object - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: . push: false @@ -97,7 +97,7 @@ jobs: outputs: type=local,dest=${{runner.temp}}/release - name: Upload Zig Object - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.tag }} path: ${{runner.temp}}/release/bun-zig.o @@ -146,7 +146,7 @@ jobs: - name: Cache submodule dependencies id: cache-deps-restore - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: ${{runner.temp}}/bun-deps key: bun-deps-${{ matrix.tag }}-${{ steps.submodule-versions.outputs.sha }} @@ -164,13 +164,13 @@ jobs: - name: Cache submodule dependencies if: ${{ !steps.cache-deps-restore.outputs.cache-hit }} id: cache-deps-save - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 with: path: ${{runner.temp}}/bun-deps key: ${{ steps.cache-deps-restore.outputs.cache-primary-key }} - name: Upload submodule dependencies - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.tag }}-deps path: ${{runner.temp}}/bun-deps @@ -240,7 +240,7 @@ jobs: bash compile-cpp-only.sh -v - name: Upload C++ - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.tag }}-cpp path: ${{ runner.temp }}/bun-cpp-obj/bun-cpp-objects.a @@ -262,7 +262,7 @@ jobs: runner: macos-12-large artifact: bun-obj-darwin-x64-baseline steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Checkout submodules run: git submodule update --init --recursive --depth=1 --progress --force @@ -286,19 +286,19 @@ jobs: echo "${{ runner.temp }}/.bun/bin" >> $GITHUB_PATH - name: Download C++ - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ matrix.tag }}-cpp path: ${{ runner.temp }}/bun-cpp-obj - name: Download Zig Object - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ matrix.obj }} path: ${{ runner.temp }}/release - name: Downloaded submodule dependencies - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ matrix.tag }}-deps path: ${{runner.temp}}/bun-deps @@ -331,11 +331,11 @@ jobs: zip -r ${{matrix.tag}}-profile.zip ${{matrix.tag}}-profile zip -r ${{matrix.tag}}.zip ${{matrix.tag}} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{matrix.tag}}-profile path: ${{runner.temp}}/link-build/${{matrix.tag}}-profile.zip - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{matrix.tag}} path: ${{runner.temp}}/link-build/${{matrix.tag}}.zip @@ -396,12 +396,12 @@ jobs: steps: - id: checkout name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: false - id: download name: Download - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{matrix.tag}} path: ${{runner.temp}}/release @@ -428,6 +428,7 @@ jobs: name: Test (node runner) env: SMTP_SENDGRID_SENDER: ${{ secrets.SMTP_SENDGRID_SENDER }} + TMPDIR: ${{runner.temp}} TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} # if: ${{github.event.inputs.use_bun == 'false'}} diff --git a/.github/workflows/bun-mac-x64.yml b/.github/workflows/bun-mac-x64.yml index affdc7228c..753a391f22 100644 --- a/.github/workflows/bun-mac-x64.yml +++ b/.github/workflows/bun-mac-x64.yml @@ -52,20 +52,20 @@ jobs: - uses: actions/checkout@v4 - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 id: buildx with: install: true - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Compile Zig Object - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: . push: false @@ -94,7 +94,7 @@ jobs: outputs: type=local,dest=${{runner.temp}}/release - name: Upload Zig Object - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.tag }} path: ${{runner.temp}}/release/bun-zig.o @@ -144,7 +144,7 @@ jobs: - name: Cache submodule dependencies id: cache-deps-restore - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: ${{runner.temp}}/bun-deps key: bun-deps-${{ matrix.tag }}-${{ steps.submodule-versions.outputs.sha }} @@ -162,13 +162,13 @@ jobs: - name: Cache submodule dependencies if: ${{ !steps.cache-deps-restore.outputs.cache-hit }} id: cache-deps-save - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 with: path: ${{runner.temp}}/bun-deps key: ${{ steps.cache-deps-restore.outputs.cache-primary-key }} - name: Upload submodule dependencies - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.tag }}-deps path: ${{runner.temp}}/bun-deps @@ -238,7 +238,7 @@ jobs: bash compile-cpp-only.sh -v - name: Upload C++ - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ matrix.tag }}-cpp path: ${{ runner.temp }}/bun-cpp-obj/bun-cpp-objects.a @@ -260,7 +260,7 @@ jobs: runner: macos-12-large artifact: bun-obj-darwin-x64 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Checkout submodules run: git submodule update --init --recursive --depth=1 --progress --force @@ -284,19 +284,19 @@ jobs: echo "${{ runner.temp }}/.bun/bin" >> $GITHUB_PATH - name: Download C++ - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ matrix.tag }}-cpp path: ${{ runner.temp }}/bun-cpp-obj - name: Download Zig Object - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ matrix.obj }} path: ${{ runner.temp }}/release - name: Downloaded submodule dependencies - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ matrix.tag }}-deps path: ${{runner.temp}}/bun-deps @@ -329,11 +329,11 @@ jobs: zip -r ${{matrix.tag}}-profile.zip ${{matrix.tag}}-profile zip -r ${{matrix.tag}}.zip ${{matrix.tag}} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{matrix.tag}}-profile path: ${{runner.temp}}/link-build/${{matrix.tag}}-profile.zip - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{matrix.tag}} path: ${{runner.temp}}/link-build/${{matrix.tag}}.zip @@ -393,12 +393,12 @@ jobs: steps: - id: checkout name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: false - id: download name: Download - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{matrix.tag}} path: ${{runner.temp}}/release @@ -426,6 +426,7 @@ jobs: env: SMTP_SENDGRID_SENDER: ${{ secrets.SMTP_SENDGRID_SENDER }} TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} + TMPDIR: ${{runner.temp}} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} # if: ${{github.event.inputs.use_bun == 'false'}} run: | diff --git a/.github/workflows/bun-release.yml b/.github/workflows/bun-release.yml index f56a14b65b..89ca850737 100644 --- a/.github/workflows/bun-release.yml +++ b/.github/workflows/bun-release.yml @@ -51,7 +51,7 @@ jobs: working-directory: packages/bun-release steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup GPG uses: crazy-max/ghaction-import-gpg@v5 with: @@ -81,7 +81,7 @@ jobs: working-directory: packages/bun-release steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Bun uses: oven-sh/setup-bun@v1 with: @@ -105,7 +105,7 @@ jobs: working-directory: packages/bun-types steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v3 with: @@ -170,12 +170,12 @@ jobs: suffix: -distroless steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Docker emulator uses: docker/setup-qemu-action@v2 - id: buildx name: Setup Docker buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 with: platforms: linux/amd64,linux/arm64 - id: metadata @@ -192,12 +192,12 @@ jobs: type=match,pattern=(bun-v)?(canary|\d+.\d+),group=2,value=${{ env.BUN_VERSION }},suffix=${{ matrix.suffix }} type=match,pattern=(bun-v)?(canary|\d+),group=2,value=${{ env.BUN_VERSION }},suffix=${{ matrix.suffix }} - name: Login to Docker - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Push to Docker - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: context: ./dockerhub/${{ matrix.dir || matrix.variant }} platforms: linux/amd64,linux/arm64 @@ -216,7 +216,7 @@ jobs: if: ${{ github.event_name == 'release' || github.event.inputs.use-homebrew == 'true' }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: repository: oven-sh/homebrew-bun token: ${{ secrets.ROBOBUN_TOKEN }} @@ -252,7 +252,7 @@ jobs: working-directory: packages/bun-release steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup Bun uses: oven-sh/setup-bun@v1 with: diff --git a/.github/workflows/bun-types-tests.yml b/.github/workflows/bun-types-tests.yml index bf3f591aa6..6fe222e71c 100644 --- a/.github/workflows/bun-types-tests.yml +++ b/.github/workflows/bun-types-tests.yml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install bun uses: oven-sh/setup-bun@v1 diff --git a/.github/workflows/bun-windows.yml b/.github/workflows/bun-windows.yml index e74f2843c8..33c6a6e38b 100644 --- a/.github/workflows/bun-windows.yml +++ b/.github/workflows/bun-windows.yml @@ -60,13 +60,13 @@ jobs: - run: git config --global core.autocrlf false && git config --global core.eol lf - uses: actions/checkout@v4 - name: Setup Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 id: buildx with: install: true - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} @@ -79,7 +79,7 @@ jobs: echo "canary_revision=$(GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}" bash ./scripts/calculate-canary-revision.sh --raw)" >> $GITHUB_OUTPUT - name: Compile Zig Object - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 if: runner.arch == 'X64' with: context: . @@ -102,7 +102,7 @@ jobs: outputs: type=local,dest=${{runner.temp}}/release - name: Upload Zig Object - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-zig${{ matrix.cpu == 'nehalem' && '-baseline' || '' }} path: ${{runner.temp}}/release/bun-zig.o @@ -138,7 +138,7 @@ jobs: - name: Try fetch dependencies id: cache-deps-restore - uses: actions/cache/restore@v3 + uses: actions/cache/restore@v4 with: path: bun-deps key: bun-deps-${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-${{ steps.submodule-versions.outputs.sha }} @@ -165,7 +165,7 @@ jobs: .\scripts\all-dependencies.ps1 - name: Upload Dependencies - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-deps${{ matrix.cpu == 'nehalem' && '-baseline' || '' }} path: bun-deps/ @@ -173,7 +173,7 @@ jobs: - name: Cache Dependencies if: ${{ !steps.cache-deps-restore.outputs.cache-hit }} id: cache-deps-save - uses: actions/cache/save@v3 + uses: actions/cache/save@v4 with: path: bun-deps key: ${{ steps.cache-deps-restore.outputs.cache-primary-key }} @@ -204,7 +204,7 @@ jobs: if: ${{ env.canary == 'true' }} run: | echo "canary_revision=$(GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}" bash ./scripts/calculate-canary-revision.sh --raw)" > build-codegen-win32-x64/.canary_revision - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-codegen path: build-codegen-win32-x64/ @@ -228,7 +228,7 @@ jobs: version: ${{ env.LLVM_VERSION }} - run: choco install -y ninja - name: Download Codegen - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-codegen path: build @@ -263,7 +263,7 @@ jobs: if ($LASTEXITCODE -ne 0) { throw "CMake configuration failed" } .\compile-cpp-only.ps1 -v if ($LASTEXITCODE -ne 0) { throw "C++ compilation failed" } - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-cpp${{ matrix.cpu == 'nehalem' && '-baseline' || '' }} path: build/bun-cpp-objects.a @@ -288,22 +288,22 @@ jobs: version: ${{ env.LLVM_VERSION }} - run: choco install -y ninja - name: Download Codegen - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-codegen path: build - name: Download Dependencies - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-deps${{ matrix.cpu == 'nehalem' && '-baseline' || '' }} path: bun-deps - name: Download Zig Object - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-zig${{ matrix.cpu == 'nehalem' && '-baseline' || '' }} path: bun-zig - name: Download C++ Objects - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}-cpp${{ matrix.cpu == 'nehalem' && '-baseline' || '' }} path: bun-cpp @@ -336,11 +336,11 @@ jobs: cp -r build\bun.pdb "$Dist\bun.pdb" Compress-Archive "$Dist" "$Dist.zip" - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }} path: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}.zip - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile path: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile.zip @@ -398,12 +398,12 @@ jobs: - run: git config --global core.autocrlf false && git config --global core.eol lf - id: checkout name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: submodules: false - id: download name: Download Release - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: ${{ env.tag }}-${{ matrix.arch == 'x86_64' && 'x64' || 'aarch64' }}${{ matrix.cpu == 'nehalem' && '-baseline' || '' }}-profile path: ${{runner.temp}}/release @@ -431,6 +431,7 @@ jobs: name: Run tests env: SMTP_SENDGRID_SENDER: ${{ secrets.SMTP_SENDGRID_SENDER }} + TMPDIR: ${{runner.temp}} TLS_MONGODB_DATABASE_URL: ${{ secrets.TLS_MONGODB_DATABASE_URL }} TLS_POSTGRES_DATABASE_URL: ${{ secrets.TLS_POSTGRES_DATABASE_URL }} run: | diff --git a/bunfig.toml b/bunfig.toml index 99838d3ce6..fa1e4511ab 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -6,3 +6,4 @@ # # Instead, we can only scan the test directory for Bun's runtime tests root = "test" +preload = "./test/preload.ts" diff --git a/docs/cli/install.md b/docs/cli/install.md index 932f05a574..8c245dd5f7 100644 --- a/docs/cli/install.md +++ b/docs/cli/install.md @@ -195,7 +195,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Install bun uses: oven-sh/setup-bun@v1 - name: Install dependencies diff --git a/docs/guides/runtime/cicd.md b/docs/guides/runtime/cicd.md index 862dcff2c4..c6d6a36a3b 100644 --- a/docs/guides/runtime/cicd.md +++ b/docs/guides/runtime/cicd.md @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: # ... - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 # run any `bun` or `bunx` command diff --git a/packages/bun-internal-test/src/runner.node.mjs b/packages/bun-internal-test/src/runner.node.mjs index cec84753bf..14177e865d 100644 --- a/packages/bun-internal-test/src/runner.node.mjs +++ b/packages/bun-internal-test/src/runner.node.mjs @@ -1,10 +1,11 @@ import * as action from "@actions/core"; import { spawn, spawnSync } from "child_process"; -import { rmSync, writeFileSync, readFileSync } from "fs"; +import { rmSync, writeFileSync, readFileSync, mkdirSync, openSync, close, closeSync } from "fs"; import { readFile } from "fs/promises"; import { readdirSync } from "node:fs"; import { resolve, basename } from "node:path"; -import { cpus, hostname, totalmem, userInfo } from "os"; +import { cpus, hostname, tmpdir, totalmem, userInfo } from "os"; +import { join } from "path"; import { fileURLToPath } from "url"; const run_start = new Date(); @@ -24,6 +25,20 @@ process.chdir(cwd); const ci = !!process.env["GITHUB_ACTIONS"]; const enableProgressBar = !ci; +var prevTmpdir = ""; +function maketemp() { + if (prevTmpdir && !windows) { + spawn("rm", ["-rf", prevTmpdir], { stdio: "inherit", detached: true }).unref(); + } + + prevTmpdir = join( + tmpdir(), + "bun-test-tmp-" + (Date.now() | 0).toString() + "_" + ((Math.random() * 100_000_0) | 0).toString(36), + ); + mkdirSync(prevTmpdir, { recursive: true }); + return prevTmpdir; +} + function defaultConcurrency() { // Concurrency causes more flaky tests, only enable it by default on windows // See https://github.com/oven-sh/bun/issues/8071 @@ -40,10 +55,19 @@ const extensions = [".js", ".ts", ".jsx", ".tsx"]; const git_sha = process.env["GITHUB_SHA"] ?? spawnSync("git", ["rev-parse", "HEAD"], { encoding: "utf-8" }).stdout.trim(); +const TEST_FILTER = process.env.BUN_TEST_FILTER; + function isTest(path) { if (!basename(path).includes(".test.") || !extensions.some(ext => path.endsWith(ext))) { return false; } + + if (TEST_FILTER) { + if (!path.includes(TEST_FILTER)) { + return false; + } + } + return true; } @@ -100,6 +124,33 @@ const failing_tests = []; const passing_tests = []; const fixes = []; const regressions = []; +let maxFd = -1; +function getMaxFileDescriptor(path) { + if (process.platform === "win32") { + return -1; + } + + hasInitialMaxFD = true; + + if (process.platform === "linux") { + try { + readdirSync("/proc/self/fd").forEach(name => { + const fd = parseInt(name.trim(), 10); + if (Number.isSafeInteger(fd) && fd >= 0) { + maxFd = Math.max(maxFd, fd); + } + }); + + return maxFd; + } catch {} + } + + const devnullfd = openSync("/dev/null", "r"); + closeSync(devnullfd); + maxFd = devnullfd + 1; + return maxFd; +} +let hasInitialMaxFD = false; async function runTest(path) { const name = path.replace(cwd, "").slice(1); @@ -107,14 +158,16 @@ async function runTest(path) { const expected_crash_reason = windows ? await readFile(resolve(path), "utf-8").then(data => { - const match = data.match(/@known-failing-on-windows:(.*)\n/); - return match ? match[1].trim() : null; - }) + const match = data.match(/@known-failing-on-windows:(.*)\n/); + return match ? match[1].trim() : null; + }) : null; const start = Date.now(); - await new Promise((done, reject) => { + await new Promise((finish, reject) => { + const chunks = []; + const proc = spawn(bunExe, ["test", resolve(path)], { stdio: ["ignore", "pipe", "pipe"], timeout: 1000 * 60 * 3, @@ -127,10 +180,26 @@ async function runTest(path) { // reproduce CI results locally GITHUB_ACTIONS: process.env.GITHUB_ACTIONS ?? "true", BUN_DEBUG_QUIET_LOGS: "1", + TMPDIR: maketemp(), }, }); + proc.stdout.once("end", () => { + done(); + }); + + let doneCalls = 0; + let done = () => { + // TODO: wait for stderr as well + // spawn.test currently causes it to hang + if (doneCalls++ == 1) { + actuallyDone(); + } + }; + function actuallyDone() { + output = Buffer.concat(chunks).toString(); + finish(); + } - const chunks = []; proc.stdout.on("data", chunk => { chunks.push(chunk); if (run_concurrency === 1) process.stdout.write(chunk); @@ -140,18 +209,32 @@ async function runTest(path) { if (run_concurrency === 1) process.stderr.write(chunk); }); - proc.on("exit", (code_, signal_) => { + proc.once("exit", (code_, signal_) => { exitCode = code_; signal = signal_; - output = Buffer.concat(chunks).toString(); done(); }); - proc.on("error", err_ => { + proc.once("error", err_ => { err = err_; - done(); + done = () => {}; + actuallyDone(); }); }); + if (!hasInitialMaxFD) { + getMaxFileDescriptor(); + } else if (maxFd > 0) { + const prevMaxFd = maxFd; + maxFd = getMaxFileDescriptor(); + if (maxFd > prevMaxFd) { + process.stderr.write( + `\n\x1b[31mewarn\x1b[0;2m:\x1b[0m file descriptor leak in ${name}, delta: ${ + maxFd - prevMaxFd + }, current: ${maxFd}, previous: ${prevMaxFd}\n`, + ); + } + } + const passed = exitCode === 0 && !err && !signal; let reason = ""; @@ -195,7 +278,8 @@ async function runTest(path) { } console.log( - `\x1b[2m${formatTime(duration).padStart(6, " ")}\x1b[0m ${passed ? "\x1b[32m✔" : expected_crash_reason ? "\x1b[33m⚠" : "\x1b[31m✖" + `\x1b[2m${formatTime(duration).padStart(6, " ")}\x1b[0m ${ + passed ? "\x1b[32m✔" : expected_crash_reason ? "\x1b[33m⚠" : "\x1b[31m✖" } ${name}\x1b[0m${reason ? ` (${reason})` : ""}`, ); @@ -319,9 +403,10 @@ console.log("\n" + "-".repeat(Math.min(process.stdout.columns || 40, 80)) + "\n" console.log(header); console.log("\n" + "-".repeat(Math.min(process.stdout.columns || 40, 80)) + "\n"); -let report = `# bun test on ${process.env["GITHUB_REF"] ?? +let report = `# bun test on ${ + process.env["GITHUB_REF"] ?? spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { encoding: "utf-8" }).stdout.trim() - } +} \`\`\` ${header} @@ -345,7 +430,8 @@ if (regressions.length > 0) { report += regressions .map( ({ path, reason, expected_crash_reason }) => - `- [\`${path}\`](${sectionLink(path)}) ${reason}${expected_crash_reason ? ` (expected: ${expected_crash_reason})` : "" + `- [\`${path}\`](${sectionLink(path)}) ${reason}${ + expected_crash_reason ? ` (expected: ${expected_crash_reason})` : "" }`, ) .join("\n"); diff --git a/packages/bun-uws/.github/workflows/codeql.yml b/packages/bun-uws/.github/workflows/codeql.yml index abafedce26..8da1909db7 100644 --- a/packages/bun-uws/.github/workflows/codeql.yml +++ b/packages/bun-uws/.github/workflows/codeql.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/packages/bun-uws/.github/workflows/cpp.yml b/packages/bun-uws/.github/workflows/cpp.yml index f6c761de53..44c68bb2e7 100644 --- a/packages/bun-uws/.github/workflows/cpp.yml +++ b/packages/bun-uws/.github/workflows/cpp.yml @@ -20,7 +20,7 @@ jobs: language: c++ fuzz-seconds: 600 - name: Upload crash - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff --git a/src/bun.js/bindings/bun-spawn.cpp b/src/bun.js/bindings/bun-spawn.cpp index 407452ee38..ff83c5a57a 100644 --- a/src/bun.js/bindings/bun-spawn.cpp +++ b/src/bun.js/bindings/bun-spawn.cpp @@ -12,13 +12,14 @@ #include #include -static int close_range(unsigned int first) -{ - return syscall(__NR_close_range, first, ~0U, 0); -} - extern char** environ; +#ifndef CLOSE_RANGE_CLOEXEC +#define CLOSE_RANGE_CLOEXEC (1U << 2) +#endif + +extern "C" ssize_t bun_close_range(unsigned int start, unsigned int end, unsigned int flags); + enum FileActionType : uint8_t { None, Close, @@ -70,7 +71,7 @@ extern "C" ssize_t posix_spawn_bun( const auto childFailed = [&]() -> ssize_t { res = errno; status = res; - close_range(0); + bun_close_range(0, ~0U, 0); _exit(127); // should never be reached @@ -151,7 +152,9 @@ extern "C" ssize_t posix_spawn_bun( if (!envp) envp = environ; - close_range(current_max_fd + 1); + if (bun_close_range(current_max_fd + 1, ~0U, CLOSE_RANGE_CLOEXEC) != 0) { + bun_close_range(current_max_fd + 1, ~0U, 0); + } execve(path, argv, envp); _exit(127); diff --git a/src/bun.js/bindings/c-bindings.cpp b/src/bun.js/bindings/c-bindings.cpp index f2109b0da3..27eb3cb350 100644 --- a/src/bun.js/bindings/c-bindings.cpp +++ b/src/bun.js/bindings/c-bindings.cpp @@ -171,7 +171,7 @@ extern "C" int clock_gettime_monotonic(int64_t* tv_sec, int64_t* tv_nsec) #endif // close_range is glibc > 2.33, which is very new -static ssize_t bun_close_range(unsigned int start, unsigned int end, unsigned int flags) +extern "C" ssize_t bun_close_range(unsigned int start, unsigned int end, unsigned int flags) { return syscall(__NR_close_range, start, end, flags); } diff --git a/src/output.zig b/src/output.zig index a29cd80c9f..78df0b8c9c 100644 --- a/src/output.zig +++ b/src/output.zig @@ -486,7 +486,7 @@ pub fn scoped(comptime tag: @Type(.EnumLiteral), comptime disabled: bool) _log_f if (!out_set) { buffered_writer = .{ - .unbuffered_writer = writer(), + .unbuffered_writer = scopedWriter(), }; out = buffered_writer.writer(); out_set = true; @@ -495,7 +495,7 @@ pub fn scoped(comptime tag: @Type(.EnumLiteral), comptime disabled: bool) _log_f lock.lock(); defer lock.unlock(); - if (Output.enable_ansi_colors_stderr) { + if (Output.enable_ansi_colors_stdout and buffered_writer.unbuffered_writer.context.handle == writer().context.handle) { out.print(comptime prettyFmt("[" ++ @tagName(tag) ++ "] " ++ fmt, true), args) catch { really_disable = true; return; @@ -804,6 +804,56 @@ pub inline fn err(error_name: anytype, comptime fmt: []const u8, args: anytype) } } +fn scopedWriter() std.fs.File.Writer { + if (comptime !Environment.isDebug) { + @compileError("scopedWriter() should only be called in debug mode"); + } + + const Scoped = struct { + pub var loaded_env: ?bool = null; + pub var scoped_file_writer: std.fs.File.Writer = undefined; + pub var scoped_file_writer_lock: bun.Lock = bun.Lock.init(); + }; + std.debug.assert(source_set); + Scoped.scoped_file_writer_lock.lock(); + defer Scoped.scoped_file_writer_lock.unlock(); + const use_env = Scoped.loaded_env orelse brk: { + if (bun.getenvZ("BUN_DEBUG")) |path| { + if (path.len > 0 and !strings.eql(path, "0") and !strings.eql(path, "false")) { + if (std.fs.path.dirname(path)) |dir| { + std.fs.cwd().makePath(dir) catch {}; + } + + // do not use libuv through this code path, since it might not be initialized yet. + const fd = std.os.openat( + std.fs.cwd().fd, + path, + std.os.O.TRUNC | std.os.O.CREAT | std.os.O.WRONLY, + 0o644, + ) catch |err_| { + // Ensure we don't panic inside panic + Scoped.loaded_env = false; + Scoped.scoped_file_writer_lock.unlock(); + Output.panic("Failed to open file for debug output: {s} ({s})", .{ @errorName(err_), path }); + }; + Scoped.scoped_file_writer = bun.toFD(fd).asFile().writer(); + Scoped.loaded_env = true; + break :brk true; + } + } + + Scoped.loaded_env = false; + + break :brk false; + }; + + if (use_env) { + return Scoped.scoped_file_writer; + } + + return source.stream.writer(); +} + /// Print a red error message with "error: " as the prefix. For custom prefixes see `err()` pub inline fn errGeneric(comptime fmt: []const u8, args: anytype) void { prettyErrorln("error: " ++ fmt, args); diff --git a/test/cli/hot/hot.test.ts b/test/cli/hot/hot.test.ts index 422f87eb67..37674913eb 100644 --- a/test/cli/hot/hot.test.ts +++ b/test/cli/hot/hot.test.ts @@ -1,258 +1,307 @@ import { spawn } from "bun"; -import { expect, it } from "bun:test"; +import { beforeAll, beforeEach, expect, it } from "bun:test"; import { bunExe, bunEnv, tempDirWithFiles, bunRun, bunRunAsScript } from "harness"; -import { readFileSync, renameSync, rmSync, unlinkSync, writeFileSync, copyFileSync } from "fs"; +import { cpSync, readFileSync, renameSync, rmSync, unlinkSync, writeFileSync, copyFileSync } from "fs"; import { join } from "path"; +import { tmpdir } from "os"; -const hotRunnerRoot = join(import.meta.dir, "hot-runner-root.js"); +let hotRunnerRoot: string = "", + cwd = ""; +beforeEach(() => { + const hotPath = join(tmpdir(), "bun-hot-test-" + (Date.now() | 0) + "_" + Math.random().toString(36).slice(2)); + hotRunnerRoot = join(hotPath, "hot-runner-root.js"); + rmSync(hotPath, { recursive: true, force: true }); + cpSync(import.meta.dir, hotPath, { recursive: true, force: true }); + cwd = hotPath; +}); it("should hot reload when file is overwritten", async () => { const root = hotRunnerRoot; - const runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); + try { + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); - var reloadCounter = 0; + var reloadCounter = 0; - async function onReload() { - writeFileSync(root, readFileSync(root, "utf-8")); - } - - for await (const line of runner.stdout) { - var str = new TextDecoder().decode(line); - var any = false; - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; - reloadCounter++; - - if (reloadCounter === 3) { - runner.unref(); - runner.kill(); - break; - } - - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); - any = true; + async function onReload() { + writeFileSync(root, readFileSync(root, "utf-8")); } - if (any) await onReload(); - } + for await (const line of runner.stdout) { + var str = new TextDecoder().decode(line); + var any = false; + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + reloadCounter++; - expect(reloadCounter).toBe(3); + if (reloadCounter === 3) { + runner.unref(); + runner.kill(); + break; + } + + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + any = true; + } + + if (any) await onReload(); + } + + expect(reloadCounter).toBe(3); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); + } }); it("should recover from errors", async () => { const root = hotRunnerRoot; - const runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - stdout: "pipe", - stderr: "pipe", - stdin: "ignore", - }); + try { + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "pipe", + stdin: "ignore", + }); - let reloadCounter = 0; - const input = readFileSync(root, "utf-8"); - function onReloadGood() { - writeFileSync(root, input); - } - - function onReloadError() { - writeFileSync(root, "throw new Error('error');\n"); - } - - var queue = [onReloadError, onReloadGood, onReloadError, onReloadGood]; - var errors: string[] = []; - var onError: (...args: any[]) => void; - (async () => { - for await (let line of runner.stderr) { - var str = new TextDecoder().decode(line); - errors.push(str); - // @ts-ignore - onError && onError(str); + let reloadCounter = 0; + const input = readFileSync(root, "utf-8"); + function onReloadGood() { + writeFileSync(root, input); } - })(); - for await (const line of runner.stdout) { - var str = new TextDecoder().decode(line); - var any = false; - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; - reloadCounter++; + function onReloadError() { + writeFileSync(root, "throw new Error('error');\n"); + } - if (reloadCounter === 3) { - runner.unref(); - runner.kill(); - break; + var queue = [onReloadError, onReloadGood, onReloadError, onReloadGood]; + var errors: string[] = []; + var onError: (...args: any[]) => void; + (async () => { + for await (let line of runner.stderr) { + var str = new TextDecoder().decode(line); + errors.push(str); + // @ts-ignore + onError && onError(str); } + })(); - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); - any = true; - } + for await (const line of runner.stdout) { + var str = new TextDecoder().decode(line); + var any = false; + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + reloadCounter++; - if (any) { - queue.shift()!(); - await new Promise((resolve, reject) => { - if (errors.length > 0) { - errors.length = 0; - resolve(); - return; + if (reloadCounter === 3) { + runner.unref(); + runner.kill(); + break; } - onError = resolve; - }); + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + any = true; + } - queue.shift()!(); + if (any) { + queue.shift()!(); + await new Promise((resolve, reject) => { + if (errors.length > 0) { + errors.length = 0; + resolve(); + return; + } + + onError = resolve; + }); + + queue.shift()!(); + } } - } - expect(reloadCounter).toBe(3); + expect(reloadCounter).toBe(3); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); + } }); it("should not hot reload when a random file is written", async () => { const root = hotRunnerRoot; - const runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); + try { + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); - let reloadCounter = 0; - const code = readFileSync(root, "utf-8"); - async function onReload() { - writeFileSync(root + ".another.yet.js", code); - unlinkSync(root + ".another.yet.js"); - } - var waiter = new Promise((resolve, reject) => { - setTimeout(async () => { - resolve(); - }, 50); - }); - var finished = false; - await Promise.race([ - waiter, - (async () => { - if (finished) { - return; - } - for await (const line of runner.stdout) { + let reloadCounter = 0; + const code = readFileSync(root, "utf-8"); + async function onReload() { + writeFileSync(root + ".another.yet.js", code); + unlinkSync(root + ".another.yet.js"); + } + var waiter = new Promise((resolve, reject) => { + setTimeout(async () => { + resolve(); + }, 50); + }); + var finished = false; + await Promise.race([ + waiter, + (async () => { if (finished) { return; } - - var str = new TextDecoder().decode(line); - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; + for await (const line of runner.stdout) { if (finished) { return; } - await onReload(); - reloadCounter++; - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + var str = new TextDecoder().decode(line); + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + if (finished) { + return; + } + await onReload(); + + reloadCounter++; + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + } } - } - })(), - ]); - finished = true; - runner.kill(0); - runner.unref(); + })(), + ]); + finished = true; + runner.kill(0); + runner.unref(); - expect(reloadCounter).toBe(1); + expect(reloadCounter).toBe(1); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); + } }); it("should hot reload when a file is deleted and rewritten", async () => { - const root = hotRunnerRoot + ".tmp.js"; - copyFileSync(hotRunnerRoot, root); - const runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); + try { + const root = hotRunnerRoot + ".tmp.js"; + copyFileSync(hotRunnerRoot, root); + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); - var reloadCounter = 0; + var reloadCounter = 0; - async function onReload() { - const contents = readFileSync(root, "utf-8"); - rmSync(root); - writeFileSync(root, contents); - } - - for await (const line of runner.stdout) { - var str = new TextDecoder().decode(line); - var any = false; - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; - reloadCounter++; - - if (reloadCounter === 3) { - runner.unref(); - runner.kill(); - break; - } - - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); - any = true; + async function onReload() { + const contents = readFileSync(root, "utf-8"); + rmSync(root); + writeFileSync(root, contents); } - if (any) await onReload(); + for await (const line of runner.stdout) { + var str = new TextDecoder().decode(line); + var any = false; + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + reloadCounter++; + + if (reloadCounter === 3) { + runner.unref(); + runner.kill(); + break; + } + + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + any = true; + } + + if (any) await onReload(); + } + rmSync(root); + expect(reloadCounter).toBe(3); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); } - rmSync(root); - expect(reloadCounter).toBe(3); }); it("should hot reload when a file is renamed() into place", async () => { const root = hotRunnerRoot + ".tmp.js"; copyFileSync(hotRunnerRoot, root); - const runner = spawn({ - cmd: [bunExe(), "--hot", "run", root], - env: bunEnv, - stdout: "pipe", - stderr: "inherit", - stdin: "ignore", - }); + try { + var runner = spawn({ + cmd: [bunExe(), "--hot", "run", root], + env: bunEnv, + cwd, + stdout: "pipe", + stderr: "inherit", + stdin: "ignore", + }); - var reloadCounter = 0; + var reloadCounter = 0; - async function onReload() { - const contents = readFileSync(root, "utf-8"); - rmSync(root + ".tmpfile", { force: true }); - await 1; - writeFileSync(root + ".tmpfile", contents); - await 1; - rmSync(root); - await 1; - renameSync(root + ".tmpfile", root); - await 1; - } - - for await (const line of runner.stdout) { - var str = new TextDecoder().decode(line); - var any = false; - for (let line of str.split("\n")) { - if (!line.includes("[#!root]")) continue; - reloadCounter++; - - if (reloadCounter === 3) { - runner.unref(); - runner.kill(); - break; - } - - expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); - any = true; + async function onReload() { + const contents = readFileSync(root, "utf-8"); + rmSync(root + ".tmpfile", { force: true }); + await 1; + writeFileSync(root + ".tmpfile", contents); + await 1; + rmSync(root); + await 1; + renameSync(root + ".tmpfile", root); + await 1; } - if (any) await onReload(); + for await (const line of runner.stdout) { + var str = new TextDecoder().decode(line); + var any = false; + for (let line of str.split("\n")) { + if (!line.includes("[#!root]")) continue; + reloadCounter++; + + if (reloadCounter === 3) { + runner.unref(); + runner.kill(); + break; + } + + expect(line).toContain(`[#!root] Reloaded: ${reloadCounter}`); + any = true; + } + + if (any) await onReload(); + } + rmSync(root); + expect(reloadCounter).toBe(3); + } finally { + // @ts-ignore + runner?.unref?.(); + // @ts-ignore + runner?.kill?.(9); } - rmSync(root); - expect(reloadCounter).toBe(3); }); diff --git a/test/js/node/watch/fs.watchFile.test.ts b/test/js/node/watch/fs.watchFile.test.ts index ff84cd1841..6a3905b6a7 100644 --- a/test/js/node/watch/fs.watchFile.test.ts +++ b/test/js/node/watch/fs.watchFile.test.ts @@ -7,7 +7,7 @@ import { describe, expect, test } from "bun:test"; // before it is actually watching, we need to repeat the operation to avoid // a race condition. function repeat(fn: any) { - const interval = setInterval(fn, 20); + const interval = setInterval(fn, 20).unref(); return interval; } const encodingFileName = `新建文夹件.txt`; diff --git a/test/preload.ts b/test/preload.ts new file mode 100644 index 0000000000..9216ac179d --- /dev/null +++ b/test/preload.ts @@ -0,0 +1,10 @@ +import * as harness from "./harness"; + +// We make Bun.env read-only +// so process.env = {} causes them to be out of sync and we assume Bun.env is +for (let key in process.env) { + if (key === "TZ") continue; + delete process.env[key]; +} + +Bun.$.env(Object.assign(process.env, harness.bunEnv));