2017-02-06 13:40:28 -05:00
|
|
|
// Imports
|
|
|
|
import * as fs from 'fs';
|
|
|
|
import * as path from 'path';
|
|
|
|
import * as shell from 'shelljs';
|
2017-06-24 18:40:04 -04:00
|
|
|
import {HIDDEN_DIR_PREFIX} from '../common/constants';
|
2018-05-10 08:56:07 -04:00
|
|
|
import {GithubApi} from '../common/github-api';
|
2017-02-06 13:40:28 -05:00
|
|
|
import {GithubPullRequests} from '../common/github-pull-requests';
|
2018-05-10 08:56:07 -04:00
|
|
|
import {assertNotMissingOrEmpty, getPrInfoFromDownloadPath} from '../common/utils';
|
2017-02-06 13:40:28 -05:00
|
|
|
|
|
|
|
// Classes
|
|
|
|
export class BuildCleaner {
|
2018-05-10 08:56:07 -04:00
|
|
|
|
2017-02-06 13:40:28 -05:00
|
|
|
// Constructor
|
2018-05-10 08:56:07 -04:00
|
|
|
constructor(protected buildsDir: string, protected githubOrg: string, protected githubRepo: string,
|
|
|
|
protected githubToken: string, protected downloadsDir: string, protected artifactPath: string) {
|
2017-02-27 14:04:43 -05:00
|
|
|
assertNotMissingOrEmpty('buildsDir', buildsDir);
|
2018-05-10 08:56:07 -04:00
|
|
|
assertNotMissingOrEmpty('githubOrg', githubOrg);
|
|
|
|
assertNotMissingOrEmpty('githubRepo', githubRepo);
|
2017-02-27 15:40:13 -05:00
|
|
|
assertNotMissingOrEmpty('githubToken', githubToken);
|
2018-05-10 08:56:07 -04:00
|
|
|
assertNotMissingOrEmpty('downloadsDir', downloadsDir);
|
|
|
|
assertNotMissingOrEmpty('artifactPath', artifactPath);
|
2017-02-06 13:40:28 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// Methods - Public
|
2018-05-10 08:56:07 -04:00
|
|
|
public async cleanUp() {
|
|
|
|
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[]) {
|
|
|
|
const existingBuilds = await this.getExistingBuildNumbers();
|
|
|
|
await this.removeUnnecessaryBuilds(existingBuilds, openPrs);
|
2017-02-06 13:40:28 -05:00
|
|
|
}
|
|
|
|
|
2018-05-10 08:56:07 -04:00
|
|
|
public async cleanDownloads(openPrs: number[]) {
|
|
|
|
const existingDownloads = await this.getExistingDownloads();
|
|
|
|
await this.removeUnnecessaryDownloads(existingDownloads, openPrs);
|
|
|
|
}
|
|
|
|
|
|
|
|
public getExistingBuildNumbers() {
|
|
|
|
return new Promise<number[]>((resolve, reject) => {
|
2017-02-06 13:40:28 -05:00
|
|
|
fs.readdir(this.buildsDir, (err, files) => {
|
|
|
|
if (err) {
|
|
|
|
return reject(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
const buildNumbers = files.
|
2017-06-24 18:40:04 -04:00
|
|
|
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
|
2017-02-06 13:40:28 -05:00
|
|
|
|
|
|
|
resolve(buildNumbers);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-05-10 08:56:07 -04:00
|
|
|
public async getOpenPrNumbers() {
|
|
|
|
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);
|
2017-02-06 13:40:28 -05:00
|
|
|
}
|
|
|
|
|
2018-05-10 08:56:07 -04:00
|
|
|
public removeDir(dir: string) {
|
2017-02-06 13:40:28 -05:00
|
|
|
try {
|
2017-06-24 18:40:04 -04:00
|
|
|
if (shell.test('-d', dir)) {
|
2018-06-08 05:55:15 -04:00
|
|
|
shell.chmod('-R', 'a+w', dir);
|
2017-06-24 18:40:04 -04:00
|
|
|
shell.rm('-rf', dir);
|
|
|
|
}
|
2017-02-06 13:40:28 -05:00
|
|
|
} catch (err) {
|
|
|
|
console.error(`ERROR: Unable to remove '${dir}' due to:`, err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-10 08:56:07 -04:00
|
|
|
public removeUnnecessaryBuilds(existingBuildNumbers: number[], openPrNumbers: number[]) {
|
2017-02-06 13:40:28 -05:00
|
|
|
const toRemove = existingBuildNumbers.filter(num => !openPrNumbers.includes(num));
|
|
|
|
|
|
|
|
console.log(`Existing builds: ${existingBuildNumbers.length}`);
|
|
|
|
console.log(`Removing ${toRemove.length} build(s): ${toRemove.join(', ')}`);
|
|
|
|
|
2017-06-24 18:40:04 -04:00
|
|
|
// Try removing public dirs.
|
2017-02-06 13:40:28 -05:00
|
|
|
toRemove.
|
|
|
|
map(num => path.join(this.buildsDir, String(num))).
|
|
|
|
forEach(dir => this.removeDir(dir));
|
2017-06-24 18:40:04 -04:00
|
|
|
|
|
|
|
// Try removing hidden dirs.
|
|
|
|
toRemove.
|
|
|
|
map(num => path.join(this.buildsDir, HIDDEN_DIR_PREFIX + String(num))).
|
|
|
|
forEach(dir => this.removeDir(dir));
|
2017-02-06 13:40:28 -05:00
|
|
|
}
|
2018-05-10 08:56:07 -04:00
|
|
|
|
|
|
|
public getExistingDownloads() {
|
|
|
|
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[]) {
|
|
|
|
const toRemove = existingDownloads.filter(filePath => {
|
|
|
|
const {pr} = getPrInfoFromDownloadPath(filePath);
|
|
|
|
return !openPrNumbers.includes(pr);
|
|
|
|
});
|
|
|
|
|
|
|
|
console.log(`Existing downloads: ${existingDownloads.length}`);
|
|
|
|
console.log(`Removing ${toRemove.length} download(s): ${toRemove.join(', ')}`);
|
|
|
|
|
|
|
|
toRemove.forEach(filePath => shell.rm(filePath));
|
|
|
|
}
|
2017-02-06 13:40:28 -05:00
|
|
|
}
|