Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"private": true,
"scripts": {
"test": "mocha -i --fgrep slow ./test/**/*.spec.js",
"test:slow": "mocha --fgrep slow ./test/**/*.spec.js"
"test:slow": "mocha --fgrep slow ./test/**/*.spec.js",
"lint:fix": "eslint . --fix"
},
"engines": {
"node": "^20.11.0",
Expand All @@ -24,9 +25,9 @@
},
"devDependencies": {
"chai": "^4.4.1",
"eslint": "^8.56.0",
"eslint": "^8.57.0",
"eslint-plugin-node": "^11.1.0",
"mocha": "10",
"mocha": "^10.6.0",
"mock-fs": "^5.2.0",
"nyc": "^17.0.0",
"proxyquire": "^2.1.3",
Expand Down
67 changes: 38 additions & 29 deletions src/clean/clean.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
* removes temporary folders matching known patterns
*/
const { join } = require('path');
const { realpathSync, statSync } = require('fs');
const { realpathSync } = require('fs');
const fsPromises = require('fs').promises;
const { readdir } = fsPromises;
const { tmpdir } = require('os');
const logger = console;

const defaultTempPath = realpathSync(tmpdir());

/**
* compatibility check for rm & rmdir
* @returns {Function}
Expand All @@ -32,15 +32,19 @@ const remover = rmCompatibility();
* @public
*/
const folderList = async (regex, rootFolder) => {
const folders = await readdir(rootFolder, { withFileTypes: true });
return folders
.filter(dirent => dirent.isDirectory() && regex.test(dirent.name))
.map(d => join(rootFolder, d.name));
const folders = await fsPromises.readdir(rootFolder, { withFileTypes: true });
return folders.reduce((acc, dirent) => {
if(dirent.isDirectory() && regex.test(dirent.name)) {
acc.push(join(rootFolder, dirent.name));
}
return acc;
}, []);
};

/**
* Deletes matching folders
*
* @param {string} name
* @param {RegExp} regex
* @param {string} root
* @returns {Promise}
Expand Down Expand Up @@ -72,39 +76,44 @@ const removeTarget = async (name, regex, root = defaultTempPath) => {
};



const DAY_MS = 86400000;

/**
*
* @param {object} options
* @param {string} options.root - over-ride sonarlint work folder path for testing
* @param {number} options.age
* @param {Function} logger
* @param {Promise<number>}
* @param {object=} logger
* @returns {Promise<number>} - number of folders removed
*/
const removeSonarTemp = async ({ root, age }, logger = console) => {
const _root = root || join(process.env.HOME, '.sonarlint', 'work');
const _root = root || realpathSync(join(process.env.HOME, '.sonarlint'));
const now = Date.now();
const folders = await readdir(_root, { withFileTypes: true })
.then(dirs =>
dirs
.filter(d => d.isDirectory())
.filter(d => {
const { ctimeMs } = statSync(join(_root, d.name));
const daysOld = Math.floor((now - ctimeMs) / DAY_MS);
return daysOld >= age;
})
const DAY_MS = 86400000;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid redeclaring DAY_MS.

DAY_MS is declared twice in the file. Consider moving it to a global constant to avoid redeclaration.

-    const DAY_MS = 86400000;

Committable suggestion was skipped due to low confidence.

let folders = [];
try {
await fsPromises.access(_root, fsPromises.constants.R_OK | fsPromises.constants.W_OK);

folders = await fsPromises.readdir(_root, { withFileTypes: true });
const targetFolders = folders.filter(d => d.isDirectory() && (d.name.startsWith('.sonarlinttmp_') || d.name.startsWith('xodus-local-only')));
const foldersToDelete = targetFolders.filter(async d => {
const { ctimeMs } = await fsPromises.stat(join(_root, d.name)).catch(() => ({ ctimeMs: 0 }));
const daysOld = Math.floor((now - ctimeMs) / DAY_MS);
return daysOld >= age;
});

const deleteFolders = await Promise.allSettled(
foldersToDelete.map(d => remover(join(_root, d.name), { recursive: true })
.then(() => `removed ${d.name}`)
)
);
logger.debug(`removing ${folders.length} folders`);

return Promise.allSettled(
folders.map(async d => {
await remover(join(_root, d.name), { recursive: true }).then(() => `removed ${d.name}`);
})
).then(r => {
return r.filter(f => f.status === 'fulfilled').length;
});
logger.debug(`removing ${folders.length} folders`);
return deleteFolders.filter(f => f.status === 'fulfilled').length;
}
catch {
return 0;
}


};

module.exports = {
Expand Down
92 changes: 51 additions & 41 deletions src/clean/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,63 @@ const { removeTarget, removeSonarTemp } = require('./clean');
const { join } = require('path');
const { existsSync } = require('fs');
const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');

const builds = /(^[a-z0-9]{32}$)|(^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$)/m;
const yarns = /^yarn--.+/m;

const sonar = {
command: 'sonar'
, desc: 'Remove excess sonar work folders'
, builder: yargs => yargs
.option('root', {
alias: 'r'
, describe: 'root path of work folders'
, type: 'string'
, default: join(process.env.HOME, '.sonarlint')
})
.option('logger', {
type: 'object'
, default: console
, hidden: true
})
.check(argv => {
if(existsSync(argv.root)) {
return true;
}
throw new Error('root path not found');
})
, handler: async (args) => {
const { root, age, logger } = args;
try {
const result = await removeSonarTemp({ root, age }, logger);
logger.info(`sonar cleanup completed, ${result} folders removed`);
}
catch (err) {
logger.error(err.message);
}
}
};

const yarnArtifacts = {
command: [ '$0', 'yarn', 'yn' ]
, desc: 'Remove yarn temp files'
, handler: () => removeTarget('Yarn', yarns)
};

yargs
.command({
command: [ 'sonar' ]
, desc: 'Remove excess sonar work folders'
, builder: _yargs => {
return _yargs
.option('root', {
alias: 'r'
, describe: 'root path of work folders'
, type: 'string'
, default: join(process.env.HOME, '.sonarlint', 'work')
})
.option('age', {
alias: 'd'
, describe: 'age in days to retain'
, type: 'number'
, default: 2
})
.check(argv => {
if(existsSync(argv.root)) {
return true;
}
throw new Error('root path not found');
});
}
, handler: removeSonarTemp
})
.command({
command: [ '$0', 'yarn', 'yn' ]
, desc: 'Remove yarn temp files'
, handler: () => removeTarget('Yarn', yarns)
})
.command({
command: [ 'builder', 'b' ]
, desc: 'Remove builder temp files'
, handler: () => removeTarget('Builder', builds)
});


// Main entry
yargs.help(true)
const buildArtifacts = {
command: [ 'builder', 'b' ]
, desc: 'Remove builder temp files'
, handler: () => removeTarget('Builder', builds)
};

yargs(hideBin(process.argv))
.command(sonar)
.command(yarnArtifacts)
.command(buildArtifacts)
.help(true)
.version(false)
.strict(true)
.parse();


Loading