Skip to content

Commit 9bc7d38

Browse files
cipolleschimeta-codesync[bot]
authored andcommitted
Fix silent tar extraction failure on EdenFS in replace-rncore-version (facebook#55797)
Summary: On EdenFS, `tar -xf` extraction directly into an EdenFS-mounted directory silently produces incomplete results — only the first slice of the tarball (ios-arm64) gets extracted, while the remaining entries (catalyst, simulator, top-level Headers) have their directories created but files are missing. The `replace-rncore-version.js` script didn't check tar's exit code, so it would write the config file and subsequent builds would skip re-extraction. This fix extracts the tarball to a temporary directory on a regular filesystem first, verifies the extraction succeeded (exit code + expected file check), then moves the results into the final location. This is adapted from D94625297 (which targeted 0.83) to work with the current main branch, preserving the v0.84 behavior of only deleting subdirectories in `React-Core-prebuilt` (keeping files like `React-VFS.yaml`). ## Changelog: [IOS] [FIXED] - Fix silent tar extraction failure on EdenFS in replace-rncore-version.js by extracting to a temp directory first Pull Request resolved: facebook#55797 Test Plan: Validated by the original patch author on EdenFS in D94625297. The logic is equivalent, adapted for the v0.84 code which preserves files in the target directory. Reviewed By: cortinico Differential Revision: D94653392 Pulled By: cipolleschi fbshipit-source-id: f2f1956b434586f0a83e1ec648a03dbac25333f5
1 parent fdd1f6e commit 9bc7d38

File tree

1 file changed

+72
-15
lines changed

1 file changed

+72
-15
lines changed

packages/react-native/scripts/replace-rncore-version.js

Lines changed: 72 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
const {spawnSync} = require('child_process');
1414
const fs = require('fs');
15+
const os = require('os');
16+
const path = require('path');
1517
const yargs = require('yargs');
1618

1719
const LAST_BUILD_FILENAME = 'React-Core-prebuilt/.last_build_configuration';
@@ -63,22 +65,77 @@ function replaceRNCoreConfiguration(
6365

6466
const finalLocation = 'React-Core-prebuilt';
6567

66-
// Delete all directories - not files, since we want to keep the React-VFS.yaml file
67-
const dirs = fs
68-
.readdirSync(finalLocation, {withFileTypes: true})
69-
.filter(dirent => dirent.isDirectory());
70-
for (const dirent of dirs) {
71-
const direntName =
72-
typeof dirent.name === 'string' ? dirent.name : dirent.name.toString();
73-
const dirPath = `${finalLocation}/${direntName}`;
74-
console.log('Removing directory', dirPath);
75-
fs.rmSync(dirPath, {force: true, recursive: true});
76-
}
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+
}
7789

78-
console.log('Extracting the tarball', tarballURLPath);
79-
spawnSync('tar', ['-xf', tarballURLPath, '-C', finalLocation], {
80-
stdio: 'inherit',
81-
});
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+
// Delete all directories in finalLocation - not files, since we want to
100+
// keep the React-VFS.yaml file
101+
const dirs = fs
102+
.readdirSync(finalLocation, {withFileTypes: true})
103+
.filter(dirent => dirent.isDirectory());
104+
for (const dirent of dirs) {
105+
const direntName =
106+
typeof dirent.name === 'string' ? dirent.name : dirent.name.toString();
107+
const dirPath = `${finalLocation}/${direntName}`;
108+
console.log('Removing directory', dirPath);
109+
fs.rmSync(dirPath, {force: true, recursive: true});
110+
}
111+
112+
// Move extracted directories from temp to final location
113+
const extractedEntries = fs
114+
.readdirSync(tmpExtractDir, {withFileTypes: true})
115+
.filter(dirent => dirent.isDirectory());
116+
for (const dirent of extractedEntries) {
117+
const direntName =
118+
typeof dirent.name === 'string' ? dirent.name : dirent.name.toString();
119+
const src = path.join(tmpExtractDir, direntName);
120+
const dst = path.join(finalLocation, direntName);
121+
const mvResult = spawnSync('mv', [src, dst], {stdio: 'inherit'});
122+
if (mvResult.status !== 0) {
123+
// Fallback: copy recursively then remove source
124+
console.log(`mv failed for ${direntName}, falling back to cp -R`);
125+
const cpResult = spawnSync('cp', ['-R', src, dst], {
126+
stdio: 'inherit',
127+
});
128+
if (cpResult.status !== 0) {
129+
throw new Error(
130+
`cp fallback failed with exit code ${cpResult.status}`,
131+
);
132+
}
133+
}
134+
}
135+
} finally {
136+
// Clean up temp directory
137+
fs.rmSync(tmpDir, {force: true, recursive: true});
138+
}
82139
}
83140

84141
function updateLastBuildConfiguration(configuration /*: string */) {

0 commit comments

Comments
 (0)