Repository: vercel/next.js
Pull request: [turbopack] Enable debug assertions in CI

## Enable debug assertions in CI

### What?
Build using `--profile release-with-assertions` for all tests in CI.  A previous PR (https://github.com/vercel/next.js/pull/80511) enabled them for unit tests, this completes the effort for e2e tests run in CI.

There is one exception. The production tests currently have a failing assertion for duplicate modules, so a new env var (`TURBOPACK_TEMP_DISABLE_DUPLICATE_MODULES_CHECK`) is introduced to disable that exact check to allow this to be landed.

### Why?
Debug assertions help catch bugs early by validating assumptions in the code. By enabling them in tests, we can improve quality

### How?
- Added a new `rustBuildProfile` parameter to the reusable build workflow to control the build profile
- Created a new `build-native-release-with-assertions` script in next-swc to set the cargo profile
- Updated the turbopack production tests to set the opt out variable

Closes PACK-4578

Closes PACK-4884

Changed files:
filename: .github/workflows/build_and_test.yml
patch: @@ -315,6 +315,8 @@ jobs:
         export TURBOPACK_BUILD=1
         export NEXT_TEST_MODE=start
         export NEXT_TEST_REACT_VERSION="${{ matrix.react }}"
+        # TODO(PACK-4578): Remove
+        export TURBOPACK_TEMP_DISABLE_DUPLICATE_MODULES_CHECK=1
 
         node run-tests.js --timings -g ${{ matrix.group }} --type production
       stepName: 'test-turbopack-production-react-${{ matrix.react }}-${{ matrix.group }}'

filename: .github/workflows/build_reusable.yml
patch: @@ -42,6 +42,11 @@ on:
         required: false
         description: 'if nextest rust dep is needed'
         type: string
+      rustBuildProfile:
+        required: false
+        description: 'The profile to use for the build, default is `release-with-assertions`, also supports `` for debug and `release` for normal release'
+        type: string
+        default: 'release-with-assertions'
       uploadSwcArtifact:
         required: false
         description: 'if swc artifact needs uploading'
@@ -179,7 +184,7 @@ jobs:
         with:
           cache-provider: 'turbo'
           save-if: ${{ github.ref_name == 'canary' }}
-          shared-key: ${{ inputs.rustCacheKey }}-${{ inputs.buildNativeTarget }}-build-${{ hashFiles('.cargo/config.toml') }}
+          shared-key: ${{ inputs.rustCacheKey }}-${{ inputs.buildNativeTarget }}-build-${{ inputs.rustBuildProfile }}-${{ hashFiles('.cargo/config.toml') }}
 
       # clean up any previous artifacts to avoid hitting disk space limits
       - run: git clean -xdf && rm -rf /tmp/next-repo-*; rm -rf /tmp/next-install-* /tmp/yarn-* /tmp/ncc-cache target
@@ -197,7 +202,7 @@ jobs:
       - run: node scripts/normalize-version-bump.js
         name: normalize versions
 
-      - run: pnpm dlx turbo@${TURBO_VERSION} run build-native-release -v --env-mode loose --remote-cache-timeout 90 --summarize -- --target ${{ inputs.buildNativeTarget }}
+      - run: pnpm dlx turbo@${TURBO_VERSION} run build-native-${{ inputs.rustBuildProfile }} -v --env-mode loose --remote-cache-timeout 90 --summarize -- --target ${{ inputs.buildNativeTarget }}
         if: ${{ inputs.skipNativeBuild != 'yes' }}
 
       - name: Upload next-swc artifact

filename: packages/next-swc/package.json
patch: @@ -9,6 +9,7 @@
     "clean": "node ../../scripts/rm.mjs native",
     "build-native": "napi build --platform -p next-swc-napi --cargo-cwd ../../ --cargo-name next_swc_napi --features plugin,image-extended --js false native",
     "build-native-release": "napi build --platform -p next-swc-napi --cargo-cwd ../../ --cargo-name next_swc_napi --release --features plugin,image-extended,tracing/release_max_level_info --js false native",
+    "build-native-release-with-assertions": "napi build --platform -p next-swc-napi --cargo-cwd ../../ --cargo-name next_swc_napi --profile release-with-assertions --features plugin,image-extended,tracing/release_max_level_info --js false native",
     "build-native-no-plugin": "napi build --platform -p next-swc-napi --cargo-cwd ../../ --cargo-name next_swc_napi --features image-webp --js false native",
     "build-native-no-plugin-release": "napi build --platform -p next-swc-napi --cargo-cwd ../../ --cargo-name next_swc_napi --release --features image-webp,tracing/release_max_level_info --js false native",
     "build-native-wasi": "npx --package=@napi-rs/cli@3.0.0-alpha.45 napi build --platform --target wasm32-wasip1-threads -p next-swc-napi --cwd ../../ --output-dir packages/next-swc/native --no-default-features",

filename: packages/next-swc/turbo.json
patch: @@ -28,6 +28,19 @@
       "env": ["CI"],
       "outputs": ["native/*.node"]
     },
+    "build-native-release-with-assertions": {
+      "inputs": [
+        "../../.cargo/**",
+        "../../crates/**",
+        "../../turbopack/crates/**",
+        "../../**/Cargo.toml",
+        "../../**/Cargo.lock",
+        "../../.github/workflows/build_and_deploy.yml",
+        "../../rust-toolchain"
+      ],
+      "env": ["CI"],
+      "outputs": ["native/*.node"]
+    },
     "build-native-no-plugin": {
       "inputs": [
         "../../.cargo/**",

filename: turbopack/crates/turbopack-core/src/module_graph/mod.rs
patch: @@ -344,16 +344,27 @@ impl SingleModuleGraph {
 
         #[cfg(debug_assertions)]
         {
-            let mut duplicates = Vec::new();
-            let mut set = FxHashSet::default();
-            for &module in modules.keys() {
-                let ident = module.ident().to_string().await?;
-                if !set.insert(ident.clone()) {
-                    duplicates.push(ident)
+            use once_cell::sync::Lazy;
+
+            // TODO(PACK-4578): This is temporary while the last issues are being addressed.
+            static CHECK_FOR_DUPLICATE_MODULES: Lazy<bool> = Lazy::new(|| {
+                match std::env::var_os("TURBOPACK_TEMP_DISABLE_DUPLICATE_MODULES_CHECK") {
+                    Some(v) => v != "1" && v != "true",
+                    None => true,
+                }
+            });
+            if *CHECK_FOR_DUPLICATE_MODULES {
+                let mut duplicates = Vec::new();
+                let mut set = FxHashSet::default();
+                for &module in modules.keys() {
+                    let ident = module.ident().to_string().await?;
+                    if !set.insert(ident.clone()) {
+                        duplicates.push(ident)
+                    }
+                }
+                if !duplicates.is_empty() {
+                    panic!("Duplicate module idents in graph: {duplicates:#?}");
                 }
-            }
-            if !duplicates.is_empty() {
-                panic!("Duplicate module idents in graph: {duplicates:#?}");
             }
         }