Skip to content

Commit

Permalink
fix(@angular-devkit/build-angular): empty output directory instead of…
Browse files Browse the repository at this point in the history
… removing

When the `deleteOutputPath` option is enabled (default is enabled), the configured
`outputPath` for the project was previously removed directly. This was problematic
for cases such as when the path was mounted via a Docker setup. To alleviate these
issues, the contents of the output path will now be removed instead. This maintains
the existing output path directory itself but ensures an empty location to place the
new build output.

(cherry picked from commit 8e4802d)
  • Loading branch information
clydin authored and alan-agius4 committed Nov 9, 2023
1 parent faf3736 commit d3b5491
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 8 deletions.
Expand Up @@ -21,7 +21,7 @@ describe('Browser Builder output path', () => {
});
afterEach(async () => host.restore().toPromise());

it('deletes output path', async () => {
it('deletes output path content', async () => {
// Write a file to the output path to later verify it was deleted.
await host
.write(join(host.root(), 'dist/file.txt'), virtualFs.stringToFileBuffer('file'))
Expand All @@ -34,14 +34,14 @@ describe('Browser Builder output path', () => {
const run = await architect.scheduleTarget(target);
const output = await run.result;
expect(output.success).toBe(false);
expect(await host.exists(join(host.root(), 'dist')).toPromise()).toBe(false);
expect(await host.exists(join(host.root(), 'dist/file.txt')).toPromise()).toBe(false);
await run.stop();
});

it('deletes output path and unlink symbolic link', async () => {
it('deletes output path content and unlink symbolic link', async () => {
// Write a file to the output path to later verify it was deleted.
host.writeMultipleFiles({
'src-link/dummy.txt': '',
'src-link/a.txt': '',
'dist/file.txt': virtualFs.stringToFileBuffer('file'),
});

Expand All @@ -63,8 +63,8 @@ describe('Browser Builder output path', () => {
const output = await run.result;
expect(output.success).toBe(false);

expect(await host.exists(join(host.root(), 'dist')).toPromise()).toBe(false);
expect(await host.exists(join(host.root(), 'src-link')).toPromise()).toBe(true);
expect(await host.exists(join(host.root(), 'dist/file.txt')).toPromise()).toBe(false);
expect(await host.exists(join(host.root(), 'src-link/a.txt')).toPromise()).toBe(true);
await run.stop();
});

Expand Down
Expand Up @@ -7,7 +7,7 @@
*/

import * as fs from 'fs';
import { resolve } from 'path';
import { join, resolve } from 'path';

/**
* Delete an output directory, but error out if it's the root of the project.
Expand All @@ -18,5 +18,19 @@ export function deleteOutputDir(root: string, outputPath: string): void {
throw new Error('Output path MUST not be project root directory!');
}

fs.rmSync(resolvedOutputPath, { force: true, recursive: true, maxRetries: 3 });
// Avoid removing the actual directory to avoid errors in cases where the output
// directory is mounted or symlinked. Instead the contents are removed.
let entries;
try {
entries = fs.readdirSync(resolvedOutputPath);
} catch (error) {
if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
return;
}
throw error;
}

for (const entry of entries) {
fs.rmSync(join(resolvedOutputPath, entry), { force: true, recursive: true, maxRetries: 3 });
}
}

0 comments on commit d3b5491

Please sign in to comment.