feat: implement a command to clear the cache (#9868)
This commit is contained in:
parent
21a082fb9f
commit
b8d38cb05f
4
package-lock.json
generated
4
package-lock.json
generated
@ -9123,7 +9123,7 @@
|
||||
},
|
||||
"packages/browsers": {
|
||||
"name": "@puppeteer/browsers",
|
||||
"version": "0.0.5",
|
||||
"version": "0.1.1",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"debug": "4.3.4",
|
||||
@ -9137,7 +9137,7 @@
|
||||
"yargs": "17.7.1"
|
||||
},
|
||||
"bin": {
|
||||
"browsers": "lib/cjs/browsers.js"
|
||||
"browsers": "lib/cjs/main-cli.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^14.15.0",
|
||||
|
@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {stdin as input, stdout as output} from 'process';
|
||||
import * as readline from 'readline';
|
||||
|
||||
import ProgressBar from 'progress';
|
||||
import yargs from 'yargs';
|
||||
import {hideBin} from 'yargs/helpers';
|
||||
@ -24,6 +27,7 @@ import {
|
||||
BrowserPlatform,
|
||||
ChromeReleaseChannel,
|
||||
} from './browser-data/browser-data.js';
|
||||
import {Cache} from './Cache.js';
|
||||
import {detectBrowserPlatform} from './detectPlatform.js';
|
||||
import {fetch} from './fetch.js';
|
||||
import {
|
||||
@ -52,11 +56,17 @@ type LaunchArgs = {
|
||||
system: boolean;
|
||||
};
|
||||
|
||||
type ClearArgs = {
|
||||
path?: string;
|
||||
};
|
||||
|
||||
export class CLI {
|
||||
#cachePath;
|
||||
#rl?: readline.Interface;
|
||||
|
||||
constructor(cachePath = process.cwd()) {
|
||||
constructor(cachePath = process.cwd(), rl?: readline.Interface) {
|
||||
this.#cachePath = cachePath;
|
||||
this.#rl = rl;
|
||||
}
|
||||
|
||||
#defineBrowserParameter(yargs: yargs.Argv<unknown>): void {
|
||||
@ -82,13 +92,16 @@ export class CLI {
|
||||
});
|
||||
}
|
||||
|
||||
#definePathParameter(yargs: yargs.Argv<unknown>): void {
|
||||
#definePathParameter(yargs: yargs.Argv<unknown>, required = false): void {
|
||||
yargs.option('path', {
|
||||
type: 'string',
|
||||
desc: 'Path to the root folder for the browser downloads and installation. The installation folder structure is compatible with the cache structure used by Puppeteer.',
|
||||
default: process.cwd(),
|
||||
defaultDescription: 'Current working directory',
|
||||
...(required ? {} : {default: process.cwd()}),
|
||||
});
|
||||
if (required) {
|
||||
yargs.demandOption('path');
|
||||
}
|
||||
}
|
||||
|
||||
async run(argv: string[]): Promise<void> {
|
||||
@ -214,6 +227,31 @@ export class CLI {
|
||||
});
|
||||
}
|
||||
)
|
||||
.command(
|
||||
'clear',
|
||||
'Removes all installed browsers from the specified cache directory',
|
||||
yargs => {
|
||||
this.#definePathParameter(yargs, true);
|
||||
},
|
||||
async argv => {
|
||||
const args = argv as unknown as ClearArgs;
|
||||
const cacheDir = args.path ?? this.#cachePath;
|
||||
const rl = this.#rl ?? readline.createInterface({input, output});
|
||||
rl.question(
|
||||
`Do you want to permanently and recursively delete the content of ${cacheDir} (yes/No)? `,
|
||||
answer => {
|
||||
rl.close();
|
||||
if (!['y', 'yes'].includes(answer.toLowerCase().trim())) {
|
||||
console.log('Cancelled.');
|
||||
return;
|
||||
}
|
||||
const cache = new Cache(cacheDir);
|
||||
cache.clear();
|
||||
console.log(`${cacheDir} cleared.`);
|
||||
}
|
||||
);
|
||||
}
|
||||
)
|
||||
.demandCommand(1)
|
||||
.help()
|
||||
.wrap(Math.min(120, yargs.terminalWidth()))
|
||||
|
@ -16,6 +16,8 @@
|
||||
|
||||
import path from 'path';
|
||||
|
||||
import rimraf from 'rimraf';
|
||||
|
||||
import {Browser, BrowserPlatform} from './browser-data/browser-data.js';
|
||||
|
||||
/**
|
||||
@ -32,7 +34,7 @@ import {Browser, BrowserPlatform} from './browser-data/browser-data.js';
|
||||
* ------ specific structure.
|
||||
* @internal
|
||||
*/
|
||||
export class CacheStructure {
|
||||
export class Cache {
|
||||
#rootDir: string;
|
||||
|
||||
constructor(rootDir: string) {
|
||||
@ -50,4 +52,8 @@ export class CacheStructure {
|
||||
): string {
|
||||
return path.join(this.browserRoot(browser), `${platform}-${buildId}`);
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
rimraf.sync(this.#rootDir);
|
||||
}
|
||||
}
|
@ -25,7 +25,7 @@ import {
|
||||
BrowserPlatform,
|
||||
downloadUrls,
|
||||
} from './browser-data/browser-data.js';
|
||||
import {CacheStructure} from './CacheStructure.js';
|
||||
import {Cache} from './Cache.js';
|
||||
import {debug} from './debug.js';
|
||||
import {detectBrowserPlatform} from './detectPlatform.js';
|
||||
import {unpackArchive} from './fileUtil.js';
|
||||
@ -86,7 +86,7 @@ export async function fetch(options: Options): Promise<InstalledBrowser> {
|
||||
);
|
||||
const fileName = url.toString().split('/').pop();
|
||||
assert(fileName, `A malformed download URL was found: ${url}.`);
|
||||
const structure = new CacheStructure(options.cacheDir);
|
||||
const structure = new Cache(options.cacheDir);
|
||||
const browserRoot = structure.browserRoot(options.browser);
|
||||
const archivePath = path.join(browserRoot, fileName);
|
||||
if (!existsSync(browserRoot)) {
|
||||
|
@ -27,7 +27,7 @@ import {
|
||||
resolveSystemExecutablePath,
|
||||
ChromeReleaseChannel,
|
||||
} from './browser-data/browser-data.js';
|
||||
import {CacheStructure} from './CacheStructure.js';
|
||||
import {Cache} from './Cache.js';
|
||||
import {debug} from './debug.js';
|
||||
import {detectBrowserPlatform} from './detectPlatform.js';
|
||||
|
||||
@ -65,7 +65,7 @@ export function computeExecutablePath(options: Options): string {
|
||||
`Cannot download a binary for the provided platform: ${os.platform()} (${os.arch()})`
|
||||
);
|
||||
}
|
||||
const installationDir = new CacheStructure(options.cacheDir).installationDir(
|
||||
const installationDir = new Cache(options.cacheDir).installationDir(
|
||||
options.browser,
|
||||
options.platform,
|
||||
options.buildId
|
||||
|
@ -29,3 +29,4 @@ export {
|
||||
ChromeReleaseChannel,
|
||||
} from './browser-data/browser-data.js';
|
||||
export {CLI} from './CLI.js';
|
||||
export {Cache} from './Cache.js';
|
||||
|
@ -18,10 +18,11 @@ import assert from 'assert';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import rimraf from 'rimraf';
|
||||
import * as readline from 'readline';
|
||||
import {Writable, Readable} from 'stream';
|
||||
|
||||
import {CLI} from '../../lib/cjs/CLI.js';
|
||||
import {Cache} from '../../lib/cjs/main.js';
|
||||
|
||||
import {testChromeBuildId, testFirefoxBuildId} from './versions.js';
|
||||
|
||||
@ -30,12 +31,33 @@ describe('CLI', function () {
|
||||
|
||||
let tmpDir = '/tmp/puppeteer-browsers-test';
|
||||
|
||||
function createMockedInterface(input: string) {
|
||||
const readable = Readable.from([input]);
|
||||
const writable = new Writable({
|
||||
write(_chunk, _encoding, callback) {
|
||||
// Suppress the output to keep the test clean
|
||||
callback();
|
||||
},
|
||||
});
|
||||
|
||||
return readline.createInterface({
|
||||
input: readable,
|
||||
output: writable,
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'puppeteer-browsers-test'));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rimraf.sync(tmpDir);
|
||||
afterEach(async () => {
|
||||
new Cache(tmpDir).clear();
|
||||
await new CLI(tmpDir, createMockedInterface('yes')).run([
|
||||
'npx',
|
||||
'@puppeteer/browsers',
|
||||
'clear',
|
||||
`--path=${tmpDir}`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should download Chromium binaries', async () => {
|
||||
@ -57,6 +79,23 @@ describe('CLI', function () {
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
await new CLI(tmpDir, createMockedInterface('no')).run([
|
||||
'npx',
|
||||
'@puppeteer/browsers',
|
||||
'clear',
|
||||
`--path=${tmpDir}`,
|
||||
]);
|
||||
assert.ok(
|
||||
fs.existsSync(
|
||||
path.join(
|
||||
tmpDir,
|
||||
'chrome',
|
||||
`linux-${testChromeBuildId}`,
|
||||
'chrome-linux'
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('should download Firefox binaries', async () => {
|
||||
|
@ -21,9 +21,13 @@ import https from 'https';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import rimraf from 'rimraf';
|
||||
|
||||
import {fetch, canFetch, Browser, BrowserPlatform} from '../../lib/cjs/main.js';
|
||||
import {
|
||||
fetch,
|
||||
canFetch,
|
||||
Browser,
|
||||
BrowserPlatform,
|
||||
Cache,
|
||||
} from '../../lib/cjs/main.js';
|
||||
|
||||
import {testChromeBuildId, testFirefoxBuildId} from './versions.js';
|
||||
|
||||
@ -39,7 +43,7 @@ describe('fetch', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rimraf.sync(tmpDir);
|
||||
new Cache(tmpDir).clear();
|
||||
});
|
||||
|
||||
it('should check if a buildId can be downloaded', async () => {
|
||||
|
@ -19,8 +19,6 @@ import fs from 'fs';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
|
||||
import rimraf from 'rimraf';
|
||||
|
||||
import {
|
||||
CDP_WEBSOCKET_ENDPOINT_REGEX,
|
||||
computeExecutablePath,
|
||||
@ -28,6 +26,7 @@ import {
|
||||
fetch,
|
||||
Browser,
|
||||
BrowserPlatform,
|
||||
Cache,
|
||||
} from '../../lib/cjs/main.js';
|
||||
|
||||
import {testChromeBuildId, testFirefoxBuildId} from './versions.js';
|
||||
@ -86,7 +85,7 @@ describe('launcher', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rimraf.sync(tmpDir);
|
||||
new Cache(tmpDir).clear();
|
||||
});
|
||||
|
||||
it('should launch a Chrome browser', async () => {
|
||||
@ -146,7 +145,7 @@ describe('launcher', () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
rimraf.sync(tmpDir);
|
||||
new Cache(tmpDir).clear();
|
||||
});
|
||||
|
||||
it('should launch a Firefox browser', async () => {
|
||||
|
Loading…
Reference in New Issue
Block a user