128 lines
4.5 KiB
TypeScript
128 lines
4.5 KiB
TypeScript
// Imports
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import * as shell from 'shelljs';
|
|
import {HIDDEN_DIR_PREFIX} from '../common/constants';
|
|
import {GithubApi} from '../common/github-api';
|
|
import {GithubPullRequests} from '../common/github-pull-requests';
|
|
import {assertNotMissingOrEmpty, getPrInfoFromDownloadPath, Logger} from '../common/utils';
|
|
|
|
// Classes
|
|
export class BuildCleaner {
|
|
|
|
private logger = new Logger('BuildCleaner');
|
|
|
|
// Constructor
|
|
constructor(protected buildsDir: string, protected githubOrg: string, protected githubRepo: string,
|
|
protected githubToken: string, protected downloadsDir: string, protected artifactPath: string) {
|
|
assertNotMissingOrEmpty('buildsDir', buildsDir);
|
|
assertNotMissingOrEmpty('githubOrg', githubOrg);
|
|
assertNotMissingOrEmpty('githubRepo', githubRepo);
|
|
assertNotMissingOrEmpty('githubToken', githubToken);
|
|
assertNotMissingOrEmpty('downloadsDir', downloadsDir);
|
|
assertNotMissingOrEmpty('artifactPath', artifactPath);
|
|
}
|
|
|
|
// Methods - Public
|
|
public async cleanUp(): Promise<void> {
|
|
try {
|
|
this.logger.log('Cleaning up builds and downloads');
|
|
const openPrs = await this.getOpenPrNumbers();
|
|
this.logger.log(`Open pull requests: ${openPrs.length}`);
|
|
await Promise.all([
|
|
this.cleanBuilds(openPrs),
|
|
this.cleanDownloads(openPrs),
|
|
]);
|
|
} catch (error) {
|
|
this.logger.error('ERROR:', error);
|
|
}
|
|
}
|
|
|
|
public async cleanBuilds(openPrs: number[]): Promise<void> {
|
|
const existingBuilds = await this.getExistingBuildNumbers();
|
|
await this.removeUnnecessaryBuilds(existingBuilds, openPrs);
|
|
}
|
|
|
|
public async cleanDownloads(openPrs: number[]): Promise<void> {
|
|
const existingDownloads = await this.getExistingDownloads();
|
|
await this.removeUnnecessaryDownloads(existingDownloads, openPrs);
|
|
}
|
|
|
|
public getExistingBuildNumbers(): Promise<number[]> {
|
|
return new Promise<number[]>((resolve, reject) => {
|
|
fs.readdir(this.buildsDir, (err, files) => {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
|
|
const buildNumbers = files.
|
|
map(name => name.replace(HIDDEN_DIR_PREFIX, '')). // Remove the "hidden dir" prefix
|
|
map(Number). // Convert string to number
|
|
filter(Boolean); // Ignore NaN (or 0), because they are not builds
|
|
|
|
resolve(buildNumbers);
|
|
});
|
|
});
|
|
}
|
|
|
|
public async getOpenPrNumbers(): Promise<number[]> {
|
|
const api = new GithubApi(this.githubToken);
|
|
const githubPullRequests = new GithubPullRequests(api, this.githubOrg, this.githubRepo);
|
|
const prs = await githubPullRequests.fetchAll('open');
|
|
return prs.map(pr => pr.number);
|
|
}
|
|
|
|
public removeDir(dir: string): void {
|
|
try {
|
|
if (shell.test('-d', dir)) {
|
|
shell.chmod('-R', 'a+w', dir);
|
|
shell.rm('-rf', dir);
|
|
}
|
|
} catch (err) {
|
|
this.logger.error(`ERROR: Unable to remove '${dir}' due to:`, err);
|
|
}
|
|
}
|
|
|
|
public removeUnnecessaryBuilds(existingBuildNumbers: number[], openPrNumbers: number[]): void {
|
|
const toRemove = existingBuildNumbers.filter(num => !openPrNumbers.includes(num));
|
|
|
|
this.logger.log(`Existing builds: ${existingBuildNumbers.length}`);
|
|
this.logger.log(`Removing ${toRemove.length} build(s): ${toRemove.join(', ')}`);
|
|
|
|
// Try removing public dirs.
|
|
toRemove.
|
|
map(num => path.join(this.buildsDir, String(num))).
|
|
forEach(dir => this.removeDir(dir));
|
|
|
|
// Try removing hidden dirs.
|
|
toRemove.
|
|
map(num => path.join(this.buildsDir, HIDDEN_DIR_PREFIX + String(num))).
|
|
forEach(dir => this.removeDir(dir));
|
|
}
|
|
|
|
public getExistingDownloads(): Promise<string[]> {
|
|
const artifactFile = path.basename(this.artifactPath);
|
|
return new Promise<string[]>((resolve, reject) => {
|
|
fs.readdir(this.downloadsDir, (err, files) => {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
files = files.filter(file => file.endsWith(artifactFile));
|
|
resolve(files);
|
|
});
|
|
});
|
|
}
|
|
|
|
public removeUnnecessaryDownloads(existingDownloads: string[], openPrNumbers: number[]): void {
|
|
const toRemove = existingDownloads.filter(filePath => {
|
|
const {pr} = getPrInfoFromDownloadPath(filePath);
|
|
return !openPrNumbers.includes(pr);
|
|
});
|
|
|
|
this.logger.log(`Existing downloads: ${existingDownloads.length}`);
|
|
this.logger.log(`Removing ${toRemove.length} download(s): ${toRemove.join(', ')}`);
|
|
|
|
toRemove.forEach(filePath => shell.rm(path.join(this.downloadsDir, filePath)));
|
|
}
|
|
}
|