Permalink
Cannot retrieve contributors at this time
203 lines (196 sloc)
6.87 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
codeql-action/node_modules/check-disk-space/dist/check-disk-space.mjs
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { execFile } from 'node:child_process'; | |
import { access } from 'node:fs/promises'; | |
import { release } from 'node:os'; | |
import { normalize, sep } from 'node:path'; | |
import { platform } from 'node:process'; | |
import { promisify } from 'node:util'; | |
class InvalidPathError extends Error { | |
constructor(message) { | |
super(message); | |
this.name = 'InvalidPathError'; | |
// Set the prototype explicitly. | |
Object.setPrototypeOf(this, InvalidPathError.prototype); | |
} | |
} | |
class NoMatchError extends Error { | |
constructor(message) { | |
super(message); | |
this.name = 'NoMatchError'; | |
// Set the prototype explicitly. | |
Object.setPrototypeOf(this, NoMatchError.prototype); | |
} | |
} | |
/** | |
* Tells if directory exists | |
* | |
* @param directoryPath - The file/folder path | |
* @param dependencies - Dependencies container | |
*/ | |
async function isDirectoryExisting(directoryPath, dependencies) { | |
try { | |
await dependencies.fsAccess(directoryPath); | |
return Promise.resolve(true); | |
} | |
catch (error) { | |
return Promise.resolve(false); | |
} | |
} | |
/** | |
* Get the first existing parent path | |
* | |
* @param directoryPath - The file/folder path from where we want to know disk space | |
* @param dependencies - Dependencies container | |
*/ | |
async function getFirstExistingParentPath(directoryPath, dependencies) { | |
let parentDirectoryPath = directoryPath; | |
let parentDirectoryFound = await isDirectoryExisting(parentDirectoryPath, dependencies); | |
while (!parentDirectoryFound) { | |
parentDirectoryPath = dependencies.pathNormalize(parentDirectoryPath + '/..'); | |
parentDirectoryFound = await isDirectoryExisting(parentDirectoryPath, dependencies); | |
} | |
return parentDirectoryPath; | |
} | |
/** | |
* Tell if PowerShell 3 is available based on Windows version | |
* | |
* Note: 6.* is Windows 7 | |
* Note: PowerShell 3 is natively available since Windows 8 | |
* | |
* @param dependencies - Dependencies Injection Container | |
*/ | |
async function hasPowerShell3(dependencies) { | |
const major = parseInt(dependencies.release.split('.')[0], 10); | |
if (major <= 6) { | |
return false; | |
} | |
try { | |
await dependencies.cpExecFile('where', ['powershell'], { windowsHide: true }); | |
return true; | |
} | |
catch (error) { | |
return false; | |
} | |
} | |
/** | |
* Check disk space | |
* | |
* @param directoryPath - The file/folder path from where we want to know disk space | |
* @param dependencies - Dependencies container | |
*/ | |
function checkDiskSpace(directoryPath, dependencies = { | |
platform, | |
release: release(), | |
fsAccess: access, | |
pathNormalize: normalize, | |
pathSep: sep, | |
cpExecFile: promisify(execFile), | |
}) { | |
// Note: This function contains other functions in order | |
// to wrap them in a common context and make unit tests easier | |
/** | |
* Maps command output to a normalized object {diskPath, free, size} | |
* | |
* @param stdout - The command output | |
* @param filter - To filter drives (only used for win32) | |
* @param mapping - Map between column index and normalized column name | |
* @param coefficient - The size coefficient to get bytes instead of kB | |
*/ | |
function mapOutput(stdout, filter, mapping, coefficient) { | |
const parsed = stdout | |
.split('\n') // Split lines | |
.map(line => line.trim()) // Trim all lines | |
.filter(line => line.length !== 0) // Remove empty lines | |
.slice(1) // Remove header | |
.map(line => line.split(/\s+(?=[\d/])/)); // Split on spaces to get columns | |
const filtered = parsed.filter(filter); | |
if (filtered.length === 0) { | |
throw new NoMatchError(); | |
} | |
const diskData = filtered[0]; | |
return { | |
diskPath: diskData[mapping.diskPath], | |
free: parseInt(diskData[mapping.free], 10) * coefficient, | |
size: parseInt(diskData[mapping.size], 10) * coefficient, | |
}; | |
} | |
/** | |
* Run the command and do common things between win32 and unix | |
* | |
* @param cmd - The command to execute | |
* @param filter - To filter drives (only used for win32) | |
* @param mapping - Map between column index and normalized column name | |
* @param coefficient - The size coefficient to get bytes instead of kB | |
*/ | |
async function check(cmd, filter, mapping, coefficient = 1) { | |
const [file, ...args] = cmd; | |
/* istanbul ignore if */ | |
if (file === undefined) { | |
return Promise.reject(new Error('cmd must contain at least one item')); | |
} | |
try { | |
const { stdout } = await dependencies.cpExecFile(file, args, { windowsHide: true }); | |
return mapOutput(stdout, filter, mapping, coefficient); | |
} | |
catch (error) { | |
return Promise.reject(error); | |
} | |
} | |
/** | |
* Build the check call for win32 | |
* | |
* @param directoryPath - The file/folder path from where we want to know disk space | |
*/ | |
async function checkWin32(directoryPath) { | |
if (directoryPath.charAt(1) !== ':') { | |
return Promise.reject(new InvalidPathError(`The following path is invalid (should be X:\\...): ${directoryPath}`)); | |
} | |
const powershellCmd = [ | |
'powershell', | |
'Get-CimInstance -ClassName Win32_LogicalDisk | Select-Object Caption, FreeSpace, Size', | |
]; | |
const wmicCmd = [ | |
'wmic', | |
'logicaldisk', | |
'get', | |
'size,freespace,caption', | |
]; | |
const cmd = await hasPowerShell3(dependencies) ? powershellCmd : wmicCmd; | |
return check(cmd, driveData => { | |
// Only get the drive which match the path | |
const driveLetter = driveData[0]; | |
return directoryPath.toUpperCase().startsWith(driveLetter.toUpperCase()); | |
}, { | |
diskPath: 0, | |
free: 1, | |
size: 2, | |
}); | |
} | |
/** | |
* Build the check call for unix | |
* | |
* @param directoryPath - The file/folder path from where we want to know disk space | |
*/ | |
async function checkUnix(directoryPath) { | |
if (!dependencies.pathNormalize(directoryPath).startsWith(dependencies.pathSep)) { | |
return Promise.reject(new InvalidPathError(`The following path is invalid (should start by ${dependencies.pathSep}): ${directoryPath}`)); | |
} | |
const pathToCheck = await getFirstExistingParentPath(directoryPath, dependencies); | |
return check([ | |
'df', | |
'-Pk', | |
'--', | |
pathToCheck, | |
], () => true, // We should only get one line, so we did not need to filter | |
{ | |
diskPath: 5, | |
free: 3, | |
size: 1, | |
}, 1024); | |
} | |
// Call the right check depending on the OS | |
if (dependencies.platform === 'win32') { | |
return checkWin32(directoryPath); | |
} | |
return checkUnix(directoryPath); | |
} | |
export { InvalidPathError, NoMatchError, checkDiskSpace as default, getFirstExistingParentPath }; |