|
12 | 12 |
|
13 | 13 | const {spawnSync} = require('child_process'); |
14 | 14 | const fs = require('fs'); |
| 15 | +const os = require('os'); |
| 16 | +const path = require('path'); |
15 | 17 | const yargs = require('yargs'); |
16 | 18 |
|
17 | 19 | const LAST_BUILD_FILENAME = 'React-Core-prebuilt/.last_build_configuration'; |
@@ -62,14 +64,66 @@ function replaceRNCoreConfiguration( |
62 | 64 | const tarballURLPath = `${podsRoot}/ReactNativeCore-artifacts/reactnative-core-${version.toLowerCase()}-${configuration.toLowerCase()}.tar.gz`; |
63 | 65 |
|
64 | 66 | const finalLocation = 'React-Core-prebuilt'; |
65 | | - console.log('Preparing the final location', finalLocation); |
66 | | - fs.rmSync(finalLocation, {force: true, recursive: true}); |
67 | | - fs.mkdirSync(finalLocation, {recursive: true}); |
68 | | - |
69 | | - console.log('Extracting the tarball', tarballURLPath); |
70 | | - spawnSync('tar', ['-xf', tarballURLPath, '-C', finalLocation], { |
71 | | - stdio: 'inherit', |
72 | | - }); |
| 67 | + |
| 68 | + // Extract to a temporary directory on a regular filesystem first, then move |
| 69 | + // into the final location. This avoids issues with partial tar extraction on |
| 70 | + // certain filesystems (e.g. EdenFS) where extracting directly can silently |
| 71 | + // produce incomplete results. |
| 72 | + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rncore-')); |
| 73 | + const tmpExtractDir = path.join(tmpDir, 'React-Core-prebuilt'); |
| 74 | + fs.mkdirSync(tmpExtractDir, {recursive: true}); |
| 75 | + |
| 76 | + try { |
| 77 | + console.log('Extracting the tarball to temp dir', tarballURLPath); |
| 78 | + const result = spawnSync( |
| 79 | + 'tar', |
| 80 | + ['-xf', tarballURLPath, '-C', tmpExtractDir], |
| 81 | + { |
| 82 | + stdio: 'inherit', |
| 83 | + }, |
| 84 | + ); |
| 85 | + |
| 86 | + if (result.status !== 0) { |
| 87 | + throw new Error(`tar extraction failed with exit code ${result.status}`); |
| 88 | + } |
| 89 | + |
| 90 | + // Verify extraction produced the expected xcframework structure |
| 91 | + const xcfwPath = path.join(tmpExtractDir, 'React.xcframework'); |
| 92 | + const modulemapPath = path.join(xcfwPath, 'Modules', 'module.modulemap'); |
| 93 | + if (!fs.existsSync(modulemapPath)) { |
| 94 | + throw new Error( |
| 95 | + `Extraction verification failed: ${modulemapPath} not found`, |
| 96 | + ); |
| 97 | + } |
| 98 | + |
| 99 | + // Move from temp to final location |
| 100 | + console.log('Preparing the final location', finalLocation); |
| 101 | + fs.rmSync(finalLocation, {force: true, recursive: true}); |
| 102 | + |
| 103 | + // Use mv for an atomic-ish replacement. If the final location is on the |
| 104 | + // same filesystem as tmpDir this is a rename; otherwise it falls back to |
| 105 | + // copy + delete via spawnSync. |
| 106 | + const mvResult = spawnSync('mv', [tmpExtractDir, finalLocation], { |
| 107 | + stdio: 'inherit', |
| 108 | + }); |
| 109 | + |
| 110 | + if (mvResult.status !== 0) { |
| 111 | + // Fallback: copy recursively then remove temp |
| 112 | + console.log('mv failed, falling back to cp -R'); |
| 113 | + fs.mkdirSync(finalLocation, {recursive: true}); |
| 114 | + const cpResult = spawnSync( |
| 115 | + 'cp', |
| 116 | + ['-R', tmpExtractDir + '/.', finalLocation], |
| 117 | + {stdio: 'inherit'}, |
| 118 | + ); |
| 119 | + if (cpResult.status !== 0) { |
| 120 | + throw new Error(`cp fallback failed with exit code ${cpResult.status}`); |
| 121 | + } |
| 122 | + } |
| 123 | + } finally { |
| 124 | + // Clean up temp directory |
| 125 | + fs.rmSync(tmpDir, {force: true, recursive: true}); |
| 126 | + } |
73 | 127 | } |
74 | 128 |
|
75 | 129 | function updateLastBuildConfiguration(configuration /*: string */) { |
|
0 commit comments