-
Notifications
You must be signed in to change notification settings - Fork 12k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@angular/cli): add
ng cache
command
With this change we create a new command `ng cache` that can be used control and check the disk cache settings. This command has 4 subcommands - `ng cache enable` which can be used to enable the cache. - `ng cache disable` which can be used to disable the cache. - `ng cache clean` which can be used to delete the cache from disk. - `ng cache info` which will print statistics and information about the cache.
- Loading branch information
1 parent
eef17b3
commit e5bf35e
Showing
10 changed files
with
448 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import { promises as fs } from 'fs'; | ||
import { Argv } from 'yargs'; | ||
import { | ||
CommandModule, | ||
CommandModuleImplementation, | ||
CommandScope, | ||
} from '../../../command-builder/command-module'; | ||
import { getCacheConfig } from '../utilities'; | ||
|
||
export class CacheCleanModule extends CommandModule implements CommandModuleImplementation { | ||
command = 'clean'; | ||
describe = 'Deletes persistent disk cache from disk.'; | ||
longDescriptionPath: string | undefined; | ||
static override scope: CommandScope.In; | ||
|
||
builder(localYargs: Argv): Argv { | ||
return localYargs.strict(); | ||
} | ||
|
||
run(): Promise<void> { | ||
const { path } = getCacheConfig(this.context.workspace); | ||
|
||
return fs.rm(path, { | ||
force: true, | ||
recursive: true, | ||
maxRetries: 3, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import { join } from 'path'; | ||
import { Argv } from 'yargs'; | ||
import { | ||
CommandModule, | ||
CommandModuleImplementation, | ||
CommandScope, | ||
Options, | ||
} from '../../command-builder/command-module'; | ||
import { | ||
addCommandModuleToYargs, | ||
demandCommandFailureMessage, | ||
} from '../../command-builder/utilities/command'; | ||
import { CacheCleanModule } from './clean/cli'; | ||
import { CacheInfoCommandModule } from './info/cli'; | ||
import { CacheDisableModule, CacheEnableModule } from './settings/cli'; | ||
|
||
export class CacheCommandModule extends CommandModule implements CommandModuleImplementation { | ||
command = 'cache'; | ||
describe = 'Configure persistent disk cache and retrieve cache statistics.'; | ||
longDescriptionPath = join(__dirname, 'long-description.md'); | ||
static override scope: CommandScope.In; | ||
|
||
builder(localYargs: Argv): Argv { | ||
const subcommands = [ | ||
CacheEnableModule, | ||
CacheDisableModule, | ||
CacheCleanModule, | ||
CacheInfoCommandModule, | ||
].sort(); | ||
|
||
for (const module of subcommands) { | ||
localYargs = addCommandModuleToYargs(localYargs, module, this.context); | ||
} | ||
|
||
return localYargs.demandCommand(1, demandCommandFailureMessage).strict(); | ||
} | ||
|
||
run(_options: Options<{}>): void {} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import { tags } from '@angular-devkit/core'; | ||
import { promises as fs } from 'fs'; | ||
import { join } from 'path'; | ||
import { Argv } from 'yargs'; | ||
import { | ||
CommandModule, | ||
CommandModuleImplementation, | ||
CommandScope, | ||
} from '../../../command-builder/command-module'; | ||
import { isCI } from '../../../utilities/environment-options'; | ||
import { getCacheConfig } from '../utilities'; | ||
|
||
export class CacheInfoCommandModule extends CommandModule implements CommandModuleImplementation { | ||
command = 'info'; | ||
describe = 'Prints persistent disk cache configuration and statistics in the console.'; | ||
longDescriptionPath?: string | undefined; | ||
static override scope: CommandScope.In; | ||
|
||
builder(localYargs: Argv): Argv { | ||
return localYargs.strict(); | ||
} | ||
|
||
async run(): Promise<void> { | ||
const { path, environment, enabled } = getCacheConfig(this.context.workspace); | ||
|
||
this.context.logger.info(tags.stripIndents` | ||
Enabled: ${enabled ? 'yes' : 'no'} | ||
Environment: ${environment} | ||
Path: ${path} | ||
Size on disk: ${await this.getSizeOfDirectory(path)} | ||
Effective status on current machine: ${this.effectiveEnabledStatus() ? 'enabled' : 'disabled'} | ||
`); | ||
} | ||
|
||
private async getSizeOfDirectory(path: string): Promise<string> { | ||
const directoriesStack = [path]; | ||
let size = 0; | ||
|
||
while (directoriesStack.length) { | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const dirPath = directoriesStack.pop()!; | ||
let entries: string[] = []; | ||
|
||
try { | ||
entries = await fs.readdir(dirPath); | ||
} catch {} | ||
|
||
for (const entry of entries) { | ||
const entryPath = join(dirPath, entry); | ||
const stats = await fs.stat(entryPath); | ||
|
||
if (stats.isDirectory()) { | ||
directoriesStack.push(entryPath); | ||
} | ||
|
||
size += stats.size; | ||
} | ||
} | ||
|
||
return this.formatSize(size); | ||
} | ||
|
||
private formatSize(size: number): string { | ||
if (size <= 0) { | ||
return '0 bytes'; | ||
} | ||
|
||
const abbreviations = ['bytes', 'kB', 'MB', 'GB']; | ||
const index = Math.floor(Math.log(size) / Math.log(1024)); | ||
const roundedSize = size / Math.pow(1024, index); | ||
// bytes don't have a fraction | ||
const fractionDigits = index === 0 ? 0 : 2; | ||
|
||
return `${roundedSize.toFixed(fractionDigits)} ${abbreviations[index]}`; | ||
} | ||
|
||
private effectiveEnabledStatus(): boolean { | ||
const { enabled, environment } = getCacheConfig(this.context.workspace); | ||
|
||
if (enabled) { | ||
switch (environment) { | ||
case 'ci': | ||
return isCI; | ||
case 'local': | ||
return !isCI; | ||
} | ||
} | ||
|
||
return enabled; | ||
} | ||
} |
53 changes: 53 additions & 0 deletions
53
packages/angular/cli/src/commands/cache/long-description.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
Angular CLI saves a number of cachable operations on disk by default. | ||
|
||
When you re-run the same build, the build system restores the state of the previous build and re-uses previously performed operations, which decreases the time taken to build and test your applications and libraries. | ||
|
||
To amend the default cache settings, add the `cli.cache` object to your [Workspace Configuration](guide/workspace-config). | ||
The object goes under `cli.cache` at the top level of the file, outside the `projects` sections. | ||
|
||
```jsonc | ||
{ | ||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", | ||
"version": 1, | ||
"cli": { | ||
"cache": { | ||
// ... | ||
} | ||
}, | ||
"projects": {} | ||
} | ||
``` | ||
|
||
For more information, see [cache options](guide/workspace-config#cache-options). | ||
|
||
### Cache environments | ||
|
||
By default, disk cache is only enabled for local environments. The value of environment can be one of the following: | ||
|
||
- `all` - allows disk cache on all machines. | ||
- `local` - allows disk cache only on development machines. | ||
- `ci` - allows disk cache only on continuous integration (Ci) systems. | ||
|
||
To change the environment setting to `all`, run the following command: | ||
|
||
```bash | ||
ng config cli.cache.environment all | ||
``` | ||
|
||
For more information, see `environment` in [cache options](guide/workspace-config#cache-options). | ||
|
||
<div class="alert is-helpful"> | ||
|
||
The Angular CLI checks for the presence and value of the `CI` environment variable to determine in which environment it is running. | ||
|
||
</div> | ||
|
||
### Cache path | ||
|
||
By default, `.angular/cache` is used as a base directory to store cache results. | ||
|
||
To change this path to `.cache/ng`, run the following command: | ||
|
||
```bash | ||
ng config cli.cache.path ".cache/ng" | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import { Argv } from 'yargs'; | ||
import { | ||
CommandModule, | ||
CommandModuleImplementation, | ||
CommandScope, | ||
} from '../../../command-builder/command-module'; | ||
import { updateCacheConfig } from '../utilities'; | ||
|
||
export class CacheDisableModule extends CommandModule implements CommandModuleImplementation { | ||
command = 'disable'; | ||
aliases = 'off'; | ||
describe = 'Disables persistent disk cache for all projects in the workspace.'; | ||
longDescriptionPath: string | undefined; | ||
static override scope: CommandScope.In; | ||
|
||
builder(localYargs: Argv): Argv { | ||
return localYargs; | ||
} | ||
|
||
run(): void { | ||
updateCacheConfig('enabled', false); | ||
} | ||
} | ||
|
||
export class CacheEnableModule extends CommandModule implements CommandModuleImplementation { | ||
command = 'enable'; | ||
aliases = 'on'; | ||
describe = 'Enables disk cache for all projects in the workspace.'; | ||
longDescriptionPath: string | undefined; | ||
static override scope: CommandScope.In; | ||
|
||
builder(localYargs: Argv): Argv { | ||
return localYargs; | ||
} | ||
|
||
run(): void { | ||
updateCacheConfig('enabled', true); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import { isJsonObject } from '@angular-devkit/core'; | ||
import { resolve } from 'path'; | ||
import { Cache, Environment } from '../../../lib/config/workspace-schema'; | ||
import { AngularWorkspace, getWorkspaceRaw } from '../../utilities/config'; | ||
|
||
export function updateCacheConfig<K extends keyof Cache>(key: K, value: Cache[K]): void { | ||
const [localWorkspace] = getWorkspaceRaw('local'); | ||
if (!localWorkspace) { | ||
throw new Error('Cannot find workspace configuration file.'); | ||
} | ||
|
||
localWorkspace.modify(['cli', 'cache', key], value); | ||
localWorkspace.save(); | ||
} | ||
|
||
export function getCacheConfig(workspace: AngularWorkspace | undefined): Required<Cache> { | ||
if (!workspace) { | ||
throw new Error(`Cannot retrieve cache configuration as workspace is not defined.`); | ||
} | ||
|
||
const defaultSettings: Required<Cache> = { | ||
path: resolve(workspace.basePath, '.angular/cache'), | ||
environment: Environment.Local, | ||
enabled: true, | ||
}; | ||
|
||
const cliSetting = workspace.extensions['cli']; | ||
if (!cliSetting || !isJsonObject(cliSetting)) { | ||
return defaultSettings; | ||
} | ||
|
||
const cacheSettings = cliSetting['cache']; | ||
if (!isJsonObject(cacheSettings)) { | ||
return defaultSettings; | ||
} | ||
|
||
const { | ||
path = defaultSettings.path, | ||
environment = defaultSettings.environment, | ||
enabled = defaultSettings.enabled, | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
} = cacheSettings as Record<string, any>; | ||
|
||
return { | ||
path: resolve(workspace.basePath, path), | ||
environment, | ||
enabled, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { createDir, expectFileNotToExist, expectFileToExist } from '../../../utils/fs'; | ||
import { ng } from '../../../utils/process'; | ||
|
||
export default async function () { | ||
const cachePath = '.angular/cache'; | ||
await createDir(cachePath); | ||
await expectFileToExist(cachePath); | ||
|
||
await ng('cache', 'clean'); | ||
await expectFileNotToExist(cachePath); | ||
} |
14 changes: 14 additions & 0 deletions
14
tests/legacy-cli/e2e/tests/commands/cache/cache-enable-disable.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { readFile } from '../../../utils/fs'; | ||
import { ng } from '../../../utils/process'; | ||
|
||
export default async function () { | ||
await ng('cache', 'enable'); | ||
if (JSON.parse(await readFile('angular.json')).cli.cache.enabled !== true) { | ||
throw new Error(`Expected 'cli.cache.enable' to be true.`); | ||
} | ||
|
||
await ng('cache', 'disable'); | ||
if (JSON.parse(await readFile('angular.json')).cli.cache.enabled !== false) { | ||
throw new Error(`Expected 'cli.cache.enable' to be false.`); | ||
} | ||
} |
Oops, something went wrong.