diff --git a/scripts/end-to-end-tests.js b/scripts/end-to-end-tests.js index 2825ccd5..b0ba5505 100644 --- a/scripts/end-to-end-tests.js +++ b/scripts/end-to-end-tests.js @@ -13,8 +13,7 @@ } const childProcess = await import('child_process') - const { default: { buildBinary, dirname } } = await import('./esbuild.js') - const { default: rimraf } = await import('rimraf') + const { default: { buildBinary, dirname, removeRecursiveSync } } = await import('./esbuild.js') const assert = await import('assert') const path = await import('path') const util = await import('util') @@ -2147,7 +2146,7 @@ } // Clean up test output - rimraf.sync(thisTestDir, { disableGlob: true }); + removeRecursiveSync(thisTestDir) } catch (e) { @@ -2156,7 +2155,7 @@ assert.strictEqual(e.stderr, expectedStderr); // Clean up test output - rimraf.sync(thisTestDir, { disableGlob: true }); + removeRecursiveSync(thisTestDir) continue; } catch (e2) { e = e2; @@ -2193,7 +2192,7 @@ }) // Clean up test output - rimraf.sync(thisTestDir, { disableGlob: true }) + removeRecursiveSync(thisTestDir) } catch (e) { console.error(`❌ test failed: ${e && e.message || e} dir: ${path.relative(dirname, thisTestDir)}`) @@ -2205,7 +2204,7 @@ } // Create a fresh test directory - rimraf.sync(testDir, { disableGlob: true }) + removeRecursiveSync(testDir) await fs.mkdir(testDir, { recursive: true }) // Run all tests concurrently @@ -2216,8 +2215,6 @@ process.exit(1) } else { console.log(`✅ end-to-end tests passed`) - - // Clean up test output - rimraf.sync(testDir, { disableGlob: true }) + removeRecursiveSync(testDir) } })().catch(e => setTimeout(() => { throw e })) diff --git a/scripts/es6-fuzzer.js b/scripts/es6-fuzzer.js index 9156e3e7..bc18e6a9 100644 --- a/scripts/es6-fuzzer.js +++ b/scripts/es6-fuzzer.js @@ -16,8 +16,7 @@ return } - const { default: { buildBinary, dirname } } = await import('./esbuild.js'); - const { default: rimraf } = await import('rimraf'); + const { default: { buildBinary, dirname, removeRecursiveSync } } = await import('./esbuild.js'); const childProcess = await import('child_process'); const util = await import('util'); const path = await import('path'); @@ -120,11 +119,11 @@ } // Remove data for successful tests - rimraf.sync(testDir, { disableGlob: true }); + removeRecursiveSync(testDir); } const parentDir = path.join(dirname, '.es6-fuzzer'); - rimraf.sync(parentDir, { disableGlob: true }); + removeRecursiveSync(parentDir); fs.mkdirSync(parentDir); // Run a set number of tests in parallel @@ -140,6 +139,6 @@ // Remove everything if all tests passed if (failureCount === 0) { - rimraf.sync(parentDir, { disableGlob: true }); + removeRecursiveSync(parentDir); } })().catch(e => setTimeout(() => { throw e })); diff --git a/scripts/esbuild.js b/scripts/esbuild.js index 39dabb39..28b908f4 100644 --- a/scripts/esbuild.js +++ b/scripts/esbuild.js @@ -112,6 +112,26 @@ exports.buildBinary = () => { return path.join(repoDir, process.platform === 'win32' ? 'esbuild.exe' : 'esbuild') } +exports.removeRecursiveSync = path => { + try { + // Strangely node doesn't have a function to remove a directory tree. + // Using "rm -fr" will never work on Windows because the "rm" command + // doesn't exist. Using the "rimraf" should be cross-platform and even + // works on Windows some of the time. + rimraf.sync(testDir, { disableGlob: true }) + } catch (e) { + // Removing stuff on Windows is flaky and unreliable. Don't fail tests + // on CI if Windows is just being a pain. Common causes of flakes include + // random EPERM and ENOTEMPTY errors. + // + // The general "solution" to this is to try asking Windows to redo the + // failing operation repeatedly until eventually giving up after a + // timeout. But that doesn't guarantee that flakes will be fixed so we + // just give up instead. People that want reasonable file system + // behavior on Windows should use WSL instead. + } +} + exports.installForTests = () => { // Build the "esbuild" binary and library const esbuildPath = exports.buildBinary() diff --git a/scripts/js-api-tests.js b/scripts/js-api-tests.js index 06dfa759..3d3283a1 100644 --- a/scripts/js-api-tests.js +++ b/scripts/js-api-tests.js @@ -1,6 +1,5 @@ -const { installForTests } = require('./esbuild') +const { installForTests, removeRecursiveSync } = require('./esbuild') const { SourceMapConsumer } = require('source-map') -const rimraf = require('rimraf') const assert = require('assert') const path = require('path') const http = require('http') @@ -2299,7 +2298,7 @@ async function main() { const esbuild = installForTests() // Create a fresh test directory - rimraf.sync(rootTestDir, { disableGlob: true }) + removeRecursiveSync(rootTestDir) fs.mkdirSync(rootTestDir) // Time out these tests after 5 minutes. This exists to help debug test hangs in CI. @@ -2318,7 +2317,7 @@ async function main() { try { await mkdirAsync(testDir) await fn({ esbuild, service, testDir }) - rimraf.sync(testDir, { disableGlob: true }) + removeRecursiveSync(testDir) return true } catch (e) { console.error(`❌ ${name}: ${e && e.message || e}`) @@ -2346,24 +2345,7 @@ async function main() { process.exit(1) } else { console.log(`✅ js api tests passed`) - - // This randomly fails with EPERM on Windows in CI (GitHub Actions): - // - // Error: EPERM: operation not permitted: unlink 'esbuild\scripts\.js-api-tests\node_modules\esbuild\esbuild.exe' - // at Object.unlinkSync (fs.js) - // at fixWinEPERMSync (esbuild\scripts\node_modules\rimraf\rimraf.js) - // at rimrafSync (esbuild\scripts\node_modules\rimraf\rimraf.js) - // - // From searching related issues on GitHub it looks like apparently this is - // just how Windows works? It's kind of hard to believe something as - // fundamental as file operations is broken on Windows. It sounds like the - // file system implementation on Windows has race conditions or something. - // Anyway, deleting this is not important for the success of the test so - // just ignore errors here. - try { - rimraf.sync(rootTestDir, { disableGlob: true }) - } catch (e) { - } + removeRecursiveSync(rootTestDir) } clearTimeout(timeout); diff --git a/scripts/plugin-tests.js b/scripts/plugin-tests.js index 15625842..ef269b1a 100644 --- a/scripts/plugin-tests.js +++ b/scripts/plugin-tests.js @@ -1,5 +1,4 @@ -const { installForTests } = require('./esbuild') -const rimraf = require('rimraf') +const { installForTests, removeRecursiveSync } = require('./esbuild') const assert = require('assert') const path = require('path') const util = require('util') @@ -966,7 +965,7 @@ async function main() { const esbuild = installForTests() // Create a fresh test directory - rimraf.sync(rootTestDir, { disableGlob: true }) + removeRecursiveSync(rootTestDir) fs.mkdirSync(rootTestDir) // Time out these tests after 5 minutes. This exists to help debug test hangs in CI. @@ -985,7 +984,7 @@ async function main() { try { await mkdirAsync(testDir) await fn({ esbuild, service, testDir }) - rimraf.sync(testDir, { disableGlob: true }) + removeRecursiveSync(testDir) return true } catch (e) { console.error(`❌ ${name}: ${e && e.message || e}`) @@ -1003,13 +1002,7 @@ async function main() { process.exit(1) } else { console.log(`✅ plugin tests passed`) - - try { - rimraf.sync(rootTestDir, { disableGlob: true }) - } catch (e) { - // This doesn't work on Windows due to "EPERM: operation not permitted" - // but that's ok for CI because the VM will just be thrown away anyway. - } + removeRecursiveSync(rootTestDir) } clearTimeout(timeout); diff --git a/scripts/register-test.js b/scripts/register-test.js index 8c1cb0e4..3a5ef832 100644 --- a/scripts/register-test.js +++ b/scripts/register-test.js @@ -1,14 +1,13 @@ -const { installForTests } = require('./esbuild') +const { installForTests, removeRecursiveSync } = require('./esbuild') const child_process = require('child_process') const path = require('path') const fs = require('fs') -const rimraf = require('rimraf') const assert = require('assert') const esbuild = installForTests() // Create a fresh test directory const rootTestDir = path.join(__dirname, '.register-test') -rimraf.sync(rootTestDir, { disableGlob: true }) +removeRecursiveSync(rootTestDir) fs.mkdirSync(rootTestDir) const entry = path.join(rootTestDir, 'entry.ts') @@ -49,12 +48,7 @@ async function main() { main().then( () => { console.log(`✅ register test passed`) - try { - rimraf.sync(rootTestDir, { disableGlob: true }) - } catch (e) { - // This doesn't work on Windows due to "EPERM: operation not permitted" - // but that's ok for CI because the VM will just be thrown away anyway. - } + removeRecursiveSync(rootTestDir) }, e => { console.error(`❌ register test failed: ${e && e.message || e}`) diff --git a/scripts/ts-type-tests.js b/scripts/ts-type-tests.js index c4873763..201375f8 100644 --- a/scripts/ts-type-tests.js +++ b/scripts/ts-type-tests.js @@ -1,5 +1,5 @@ +const { removeRecursiveSync } = require('./esbuild') const child_process = require('child_process') -const rimraf = require('rimraf') const path = require('path') const fs = require('fs') @@ -318,7 +318,7 @@ const tests = { async function main() { let testDir = path.join(__dirname, '.ts-types-test') - rimraf.sync(testDir, { disableGlob: true }) + removeRecursiveSync(testDir) fs.mkdirSync(testDir, { recursive: true }) fs.writeFileSync(path.join(testDir, 'tsconfig.json'), JSON.stringify(tsconfigJson)) @@ -349,7 +349,7 @@ async function main() { process.exit(1) } else { console.log(`✅ typescript type tests passed`) - rimraf.sync(testDir, { disableGlob: true }) + removeRecursiveSync(testDir) } } diff --git a/scripts/verify-source-map.js b/scripts/verify-source-map.js index 642bb2e1..ac96bc66 100644 --- a/scripts/verify-source-map.js +++ b/scripts/verify-source-map.js @@ -1,7 +1,6 @@ const { SourceMapConsumer } = require('source-map') -const { buildBinary } = require('./esbuild') +const { buildBinary, removeRecursiveSync } = require('./esbuild') const childProcess = require('child_process') -const rimraf = require('rimraf') const path = require('path') const util = require('util') const fs = require('fs').promises @@ -341,7 +340,7 @@ async function check(kind, testCase, toSearch, { flags, entryPoints, crlf }) { checkMap(out2Js, out2Map, tempDir) } - if (!failed) rimraf.sync(tempDir, { disableGlob: true }) + if (!failed) removeRecursiveSync(tempDir) } catch (e) { @@ -424,7 +423,7 @@ async function main() { process.exit(1) } else { console.log(`✅ verify source map passed`) - rimraf.sync(testDir, { disableGlob: true }) + removeRecursiveSync(testDir) } }