Path: blob/main/src/vs/code/electron-utility/sharedProcess/contrib/languagePackCachedDataCleaner.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { promises } from 'fs';6import { RunOnceScheduler } from '../../../../base/common/async.js';7import { IStringDictionary } from '../../../../base/common/collections.js';8import { onUnexpectedError } from '../../../../base/common/errors.js';9import { Disposable } from '../../../../base/common/lifecycle.js';10import { join } from '../../../../base/common/path.js';11import { Promises } from '../../../../base/node/pfs.js';12import { INativeEnvironmentService } from '../../../../platform/environment/common/environment.js';13import { ILogService } from '../../../../platform/log/common/log.js';14import { IProductService } from '../../../../platform/product/common/productService.js';1516interface IExtensionEntry {17version: string;18extensionIdentifier: {19id: string;20uuid: string;21};22}2324interface ILanguagePackEntry {25hash: string;26extensions: IExtensionEntry[];27}2829interface ILanguagePackFile {30[locale: string]: ILanguagePackEntry;31}3233export class LanguagePackCachedDataCleaner extends Disposable {3435private readonly dataMaxAge: number;3637constructor(38@INativeEnvironmentService private readonly environmentService: INativeEnvironmentService,39@ILogService private readonly logService: ILogService,40@IProductService productService: IProductService41) {42super();4344this.dataMaxAge = productService.quality !== 'stable'45? 1000 * 60 * 60 * 24 * 7 // roughly 1 week (insiders)46: 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months (stable)4748// We have no Language pack support for dev version (run from source)49// So only cleanup when we have a build version.50if (this.environmentService.isBuilt) {51const scheduler = this._register(new RunOnceScheduler(() => {52this.cleanUpLanguagePackCache();53}, 40 * 1000 /* after 40s */));54scheduler.schedule();55}56}5758private async cleanUpLanguagePackCache(): Promise<void> {59this.logService.trace('[language pack cache cleanup]: Starting to clean up unused language packs.');6061try {62const installed: IStringDictionary<boolean> = Object.create(null);63const metaData: ILanguagePackFile = JSON.parse(await promises.readFile(join(this.environmentService.userDataPath, 'languagepacks.json'), 'utf8'));64for (const locale of Object.keys(metaData)) {65const entry = metaData[locale];66installed[`${entry.hash}.${locale}`] = true;67}6869// Cleanup entries for language packs that aren't installed anymore70const cacheDir = join(this.environmentService.userDataPath, 'clp');71const cacheDirExists = await Promises.exists(cacheDir);72if (!cacheDirExists) {73return;74}7576const entries = await Promises.readdir(cacheDir);77for (const entry of entries) {78if (installed[entry]) {79this.logService.trace(`[language pack cache cleanup]: Skipping folder ${entry}. Language pack still in use.`);80continue;81}8283this.logService.trace(`[language pack cache cleanup]: Removing unused language pack: ${entry}`);8485await Promises.rm(join(cacheDir, entry));86}8788const now = Date.now();89for (const packEntry of Object.keys(installed)) {90const folder = join(cacheDir, packEntry);91const entries = await Promises.readdir(folder);92for (const entry of entries) {93if (entry === 'tcf.json') {94continue;95}9697const candidate = join(folder, entry);98const stat = await promises.stat(candidate);99if (stat.isDirectory() && (now - stat.mtime.getTime()) > this.dataMaxAge) {100this.logService.trace(`[language pack cache cleanup]: Removing language pack cache folder: ${join(packEntry, entry)}`);101102await Promises.rm(candidate);103}104}105}106} catch (error) {107onUnexpectedError(error);108}109}110}111112113