const state = require('./state') const logger = require('../logger') const fs = require('../fs') const util = require('../util') const { join } = require('path') const Table = require('cli-table3') const dayjs = require('dayjs') const relativeTime = require('dayjs/plugin/relativeTime') const chalk = require('chalk') const _ = require('lodash') const getFolderSize = require('./get-folder-size') const Bluebird = require('bluebird') dayjs.extend(relativeTime) // output colors for the table const colors = { titles: chalk.white, dates: chalk.cyan, values: chalk.green, size: chalk.gray, } const logCachePath = () => { logger.always(state.getCacheDir()) return undefined } const clear = () => { return fs.removeAsync(state.getCacheDir()) } const prune = () => { const cacheDir = state.getCacheDir() const currentVersion = util.pkgVersion() let deletedBinary = false return fs.readdirAsync(cacheDir) .then((versions) => { return Bluebird.all(versions.map((version) => { if (version !== currentVersion) { deletedBinary = true const versionDir = join(cacheDir, version) return fs.removeAsync(versionDir) } })) }) .then(() => { if (deletedBinary) { logger.always(`Deleted all binary caches except for the ${currentVersion} binary cache.`) } else { logger.always(`No binary caches found to prune.`) } }) .catch({ code: 'ENOENT' }, () => { logger.always(`No Cypress cache was found at ${cacheDir}. Nothing to prune.`) }) } const fileSizeInMB = (size) => { return `${(size / 1024 / 1024).toFixed(1)}MB` } /** * Collects all cached versions, finds when each was used * and prints a table with results to the terminal */ const list = (showSize) => { return getCachedVersions(showSize) .then((binaries) => { const head = [colors.titles('version'), colors.titles('last used')] if (showSize) { head.push(colors.titles('size')) } const table = new Table({ head, }) binaries.forEach((binary) => { const versionString = colors.values(binary.version) const lastUsed = binary.accessed ? colors.dates(binary.accessed) : 'unknown' const row = [versionString, lastUsed] if (showSize) { const size = colors.size(fileSizeInMB(binary.size)) row.push(size) } return table.push(row) }) logger.always(table.toString()) }) } const getCachedVersions = (showSize) => { const cacheDir = state.getCacheDir() return fs .readdirAsync(cacheDir) .filter(util.isSemver) .map((version) => { return { version, folderPath: join(cacheDir, version), } }) .mapSeries((binary) => { // last access time on the folder is different from last access time // on the Cypress binary const binaryDir = state.getBinaryDir(binary.version) const executable = state.getPathToExecutable(binaryDir) return fs.statAsync(executable).then((stat) => { const lastAccessedTime = _.get(stat, 'atime') if (!lastAccessedTime) { // the test runner has never been opened // or could be a test simulating missing timestamp return binary } const accessed = dayjs(lastAccessedTime).fromNow() binary.accessed = accessed return binary }, (e) => { // could not find the binary or gets its stats return binary }) }) .mapSeries((binary) => { if (showSize) { const binaryDir = state.getBinaryDir(binary.version) return getFolderSize(binaryDir).then((size) => { return { ...binary, size, } }) } return binary }) } module.exports = { path: logCachePath, clear, prune, list, getCachedVersions, }