From d3dfd56fb9189214e89e0c22faa8484b4889561b Mon Sep 17 00:00:00 2001 From: karishka1222 Date: Sun, 29 Jun 2025 11:57:40 +0300 Subject: [PATCH 1/3] fix: handle race condition in count function when files are deleted during counting (#375) --- src/mvnw.js | 42 +++++++++++++++++++++++++++++++----------- test/test_mvnw.js | 37 +++++++++++++++++++++++++++++++++++-- 2 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/mvnw.js b/src/mvnw.js index 15fbc010..f28964a8 100644 --- a/src/mvnw.js +++ b/src/mvnw.js @@ -7,7 +7,7 @@ const path = require('path'); const fs = require('fs'); const rel = require('relative'); const readline = require('readline'); -const {spawn} = require('child_process'); +const { spawn } = require('child_process'); const colors = require('colors'); /** @@ -30,7 +30,7 @@ let beginning, * @param {Object} opts - Opts provided to the "eoc" * @return {Array} of Maven options */ -module.exports.flags = function(opts) { +module.exports.flags = function (opts) { const sources = path.resolve(opts.sources); console.debug('Sources in %s', rel(sources)); const target = path.resolve(opts.target); @@ -58,7 +58,7 @@ module.exports.flags = function(opts) { * @param {Boolean} [batch] - Is it batch mode (TRUE) or interactive (FALSE)? * @return {Promise} of maven execution task */ -module.exports.mvnw = function(args, tgt, batch) { +module.exports.mvnw = function (args, tgt, batch) { return new Promise((resolve, reject) => { target = tgt; phase = args[0]; @@ -71,7 +71,7 @@ module.exports.mvnw = function(args, tgt, batch) { '--fail-fast', '--strict-checksums', ]), - cmd = `${bin } ${ params.join(' ')}`; + cmd = `${bin} ${params.join(' ')}`; console.debug('+ %s', cmd); const result = spawn( bin, @@ -114,7 +114,7 @@ module.exports.mvnw = function(args, tgt, batch) { function start() { running = true; beginning = Date.now(); - const check = function() { + const check = function () { if (running) { print(); setTimeout(check, 1000); @@ -144,13 +144,33 @@ function print() { */ function count(dir, curr) { if (fs.existsSync(dir)) { - for (const f of fs.readdirSync(dir)) { - const next = path.join(dir, f); - if (fs.statSync(next).isDirectory()) { - curr = count(next, curr); - } else { - curr++; + try { + for (const f of fs.readdirSync(dir)) { + const next = path.join(dir, f); + try { + if (fs.statSync(next).isDirectory()) { + curr = count(next, curr); + } else { + curr++; + } + } catch (err) { + // Handle race condition: file might be deleted during counting + if (err.code === 'ENOENT') { + // Skip this file/directory if it no longer exists + continue; + } + // Re-throw other errors + throw err; + } } + } catch (err) { + // Handle race condition: directory might be deleted during counting + if (err.code === 'ENOENT') { + // Return current count if directory no longer exists + return curr; + } + // Re-throw other errors + throw err; } } return curr; diff --git a/test/test_mvnw.js b/test/test_mvnw.js index 63b918c5..0fb1d601 100644 --- a/test/test_mvnw.js +++ b/test/test_mvnw.js @@ -3,12 +3,15 @@ * SPDX-License-Identifier: MIT */ -const {mvnw, flags} = require('../src/mvnw'); +const { mvnw, flags } = require('../src/mvnw'); const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); +const os = require('os'); describe('mvnw', () => { it('prints Maven own version', (done) => { - const opts = {batch: true}; + const opts = { batch: true }; mvnw(['--version', '--quiet'], null, opts.batch); done(); }); @@ -25,4 +28,34 @@ describe('mvnw', () => { done(); }); }); + it('handles race condition when files are deleted during counting', (done) => { + // Create a temporary directory for testing + const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'eoc-test-')); + const subDir = path.join(tmpDir, 'subdir'); + fs.mkdirSync(subDir); + + // Create some test files + fs.writeFileSync(path.join(tmpDir, 'file1.txt'), 'test'); + fs.writeFileSync(path.join(subDir, 'file2.txt'), 'test'); + + // Simulate the count function behavior by accessing the internal function + // We'll test this indirectly by calling mvnw with a target that has files + const opts = { + sources: 'sources', + target: tmpDir, + parser: 'parser', + homeTag: 'homeTag' + }; + + // This should not throw an error even if files are deleted during execution + mvnw(['--version', '--quiet', ...flags(opts)]).then(() => { + // Clean up + fs.rmSync(tmpDir, { recursive: true, force: true }); + done(); + }).catch((err) => { + // Clean up even if test fails + fs.rmSync(tmpDir, { recursive: true, force: true }); + done(err); + }); + }); }); From c0706816ce4a208c1ab3174bca0849fbc0d9ce1d Mon Sep 17 00:00:00 2001 From: karishka1222 Date: Sat, 12 Jul 2025 11:46:47 +0300 Subject: [PATCH 2/3] fixed ci cd --- src/mvnw.js | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/mvnw.js b/src/mvnw.js index f28964a8..b3994bb0 100644 --- a/src/mvnw.js +++ b/src/mvnw.js @@ -143,34 +143,34 @@ function print() { * @return {Integer} Total number files. */ function count(dir, curr) { - if (fs.existsSync(dir)) { + if (!fs.existsSync(dir)) { + return curr; + } + let files; + try { + files = fs.readdirSync(dir); + } catch (err) { + if (err.code === 'ENOENT') { + return curr; + } + throw err; + } + + for (const f of files) { + const next = path.join(dir, f); try { - for (const f of fs.readdirSync(dir)) { - const next = path.join(dir, f); - try { - if (fs.statSync(next).isDirectory()) { - curr = count(next, curr); - } else { - curr++; - } - } catch (err) { - // Handle race condition: file might be deleted during counting - if (err.code === 'ENOENT') { - // Skip this file/directory if it no longer exists - continue; - } - // Re-throw other errors - throw err; - } + const stats = fs.statSync(next); + if (stats.isDirectory()) { + curr = count(next, curr); + } else { + curr++; } } catch (err) { - // Handle race condition: directory might be deleted during counting if (err.code === 'ENOENT') { - // Return current count if directory no longer exists - return curr; + // ignore + } else { + throw err; } - // Re-throw other errors - throw err; } } return curr; From 03a4145a6069b8157b7c08ed6376ab7eeba842dc Mon Sep 17 00:00:00 2001 From: karishka1222 Date: Mon, 14 Jul 2025 22:57:14 +0300 Subject: [PATCH 3/3] deleted comments and spaces --- src/mvnw.js | 5 +---- test/test_mvnw.js | 30 ++++++++++-------------------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/src/mvnw.js b/src/mvnw.js index b3994bb0..936e66cf 100644 --- a/src/mvnw.js +++ b/src/mvnw.js @@ -155,7 +155,6 @@ function print() { } throw err; } - for (const f of files) { const next = path.join(dir, f); try { @@ -166,9 +165,7 @@ function print() { curr++; } } catch (err) { - if (err.code === 'ENOENT') { - // ignore - } else { + if (err.code !== 'ENOENT') { throw err; } } diff --git a/test/test_mvnw.js b/test/test_mvnw.js index 0fb1d601..74e53d92 100644 --- a/test/test_mvnw.js +++ b/test/test_mvnw.js @@ -29,32 +29,22 @@ describe('mvnw', () => { }); }); it('handles race condition when files are deleted during counting', (done) => { - // Create a temporary directory for testing - const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'eoc-test-')); - const subDir = path.join(tmpDir, 'subdir'); - fs.mkdirSync(subDir); - - // Create some test files - fs.writeFileSync(path.join(tmpDir, 'file1.txt'), 'test'); - fs.writeFileSync(path.join(subDir, 'file2.txt'), 'test'); - - // Simulate the count function behavior by accessing the internal function - // We'll test this indirectly by calling mvnw with a target that has files - const opts = { + const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'eoc-test-')); + const sub = path.join(dir, 'subdir'); + fs.mkdirSync(sub); + fs.writeFileSync(path.join(dir, 'file1.txt'), 'test'); + fs.writeFileSync(path.join(sub, 'file2.txt'), 'test'); + const opt = { sources: 'sources', - target: tmpDir, + target: dir, parser: 'parser', homeTag: 'homeTag' }; - - // This should not throw an error even if files are deleted during execution - mvnw(['--version', '--quiet', ...flags(opts)]).then(() => { - // Clean up - fs.rmSync(tmpDir, { recursive: true, force: true }); + mvnw(['--version', '--quiet', ...flags(opt)]).then(() => { + fs.rmSync(dir, { recursive: true, force: true }); done(); }).catch((err) => { - // Clean up even if test fails - fs.rmSync(tmpDir, { recursive: true, force: true }); + fs.rmSync(dir, { recursive: true, force: true }); done(err); }); });