Skip to content

Commit 7f93735

Browse files
clydinangular-robot[bot]
authored andcommitted
build: use bazel to perform release builds
When performing a release via the dev-infra `ng-dev` tooling, the release builds for the packages that will be published are now performed using bazel. Prior to this, the release builds were performed using a custom build script that programmatically invoked TypeScript APIs. The Bazel build and discovery process for the releasable packages is performed by a script that is based on the scripts from components and framework repositories. Several small modifications were performed to match the behavior and structure of the cli repository: * Use of `packages` as the source root in the bazel query * Use of `pkg_npm` rule in the bazel query * Partial transition to native Node.js `fs` APIs instead of `shelljs` * Directory creation per package when copying output (supports multiple package scopes) * Copying of archives (tgz) for each package The snapshot and local build capabilities are not modified as part of this change but will be merged in a followup as part of a larger transition to use bazel throughout the package build process.
1 parent ff23d35 commit 7f93735

File tree

19 files changed

+201
-8
lines changed

19 files changed

+201
-8
lines changed

.circleci/dynamic_config.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ jobs:
332332

333333
publish_artifacts:
334334
executor: action-executor
335+
resource_class: medium
335336
environment:
336337
steps:
337338
- custom_attach_workspace
@@ -342,7 +343,7 @@ jobs:
342343
name: Copy tarballs to folder
343344
command: |
344345
mkdir -p dist/artifacts/
345-
cp dist/*.tgz dist/artifacts/
346+
cp dist/releases/*.tgz dist/artifacts/
346347
- store_artifacts:
347348
path: dist/artifacts/
348349
destination: angular

.ng-dev/release.mts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
import '../lib/bootstrap-local.js';
2-
31
import { ReleaseConfig } from '@angular/ng-dev';
42
import packages from '../lib/packages.js';
5-
import buildPackages from '../scripts/build.js';
63

74
const npmPackages = Object.entries(packages.releasePackages).map(([name, { experimental }]) => ({
85
name,
@@ -13,7 +10,12 @@ const npmPackages = Object.entries(packages.releasePackages).map(([name, { exper
1310
export const release: ReleaseConfig = {
1411
representativeNpmPackage: '@angular/cli',
1512
npmPackages,
16-
buildPackages: () => buildPackages.default(),
13+
buildPackages: async () => {
14+
// The `performNpmReleaseBuild` function is loaded at runtime to avoid loading additional
15+
// files and dependencies unless a build is required.
16+
const { performNpmReleaseBuild } = await import('../scripts/build-packages-dist.mjs');
17+
return performNpmReleaseBuild();
18+
},
1719
releaseNotes: {
1820
groupOrder: [
1921
'@angular/cli',

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
"@types/progress": "^2.0.3",
118118
"@types/resolve": "^1.17.1",
119119
"@types/semver": "^7.3.12",
120+
"@types/shelljs": "^0.8.11",
120121
"@types/tar": "^6.1.2",
121122
"@types/text-table": "^0.2.1",
122123
"@types/yargs": "^17.0.8",

packages/angular/cli/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ pkg_npm(
176176
"//packages/angular_devkit/schematics:package.json",
177177
"//packages/schematics/angular:package.json",
178178
],
179+
tags = ["release-package"],
179180
deps = [
180181
":README.md",
181182
":angular-cli",

packages/angular/create/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ genrule(
3232

3333
pkg_npm(
3434
name = "npm_package",
35+
tags = ["release-package"],
3536
visibility = ["//visibility:public"],
3637
deps = [
3738
":README.md",

packages/angular/pwa/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ pkg_npm(
8888
"//packages/angular_devkit/schematics:package.json",
8989
"//packages/schematics/angular:package.json",
9090
],
91+
tags = ["release-package"],
9192
deps = [
9293
":README.md",
9394
":license",

packages/angular_devkit/architect/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ pkg_npm(
112112
pkg_deps = [
113113
"//packages/angular_devkit/core:package.json",
114114
],
115+
tags = ["release-package"],
115116
deps = [
116117
":README.md",
117118
":architect",

packages/angular_devkit/architect/node/BUILD.bazel

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ ts_library(
2323
"//packages/angular_devkit/architect",
2424
"//packages/angular_devkit/core",
2525
"//packages/angular_devkit/core/node",
26-
"//tests/angular_devkit/architect/node/jobs:jobs_test_lib",
2726
"@npm//@types/node",
2827
"@npm//rxjs",
2928
],
@@ -40,6 +39,7 @@ ts_library(
4039
deps = [
4140
":node",
4241
"//packages/angular_devkit/architect",
42+
"//tests/angular_devkit/architect/node/jobs:jobs_test_lib",
4343
],
4444
)
4545

packages/angular_devkit/architect_cli/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pkg_npm(
4040
"//packages/angular_devkit/architect:package.json",
4141
"//packages/angular_devkit/core:package.json",
4242
],
43+
tags = ["release-package"],
4344
deps = [
4445
":README.md",
4546
":architect_cli",

packages/angular_devkit/build_angular/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ pkg_npm(
233233
"//packages/angular_devkit/core:package.json",
234234
"//packages/ngtools/webpack:package.json",
235235
],
236+
tags = ["release-package"],
236237
deps = [
237238
":README.md",
238239
":build_angular",

packages/angular_devkit/build_webpack/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ pkg_npm(
120120
pkg_deps = [
121121
"//packages/angular_devkit/architect:package.json",
122122
],
123+
tags = ["release-package"],
123124
deps = [
124125
":README.md",
125126
":build_webpack",

packages/angular_devkit/core/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ genrule(
8383

8484
pkg_npm(
8585
name = "npm_package",
86+
tags = ["release-package"],
8687
deps = [
8788
":README.md",
8889
":core",

packages/angular_devkit/schematics/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ pkg_npm(
8686
pkg_deps = [
8787
"//packages/angular_devkit/core:package.json",
8888
],
89+
tags = ["release-package"],
8990
deps = [
9091
":README.md",
9192
":collection-schema.json",

packages/angular_devkit/schematics_cli/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ pkg_npm(
110110
"//packages/angular_devkit/schematics:package.json",
111111
"//packages/angular_devkit/core:package.json",
112112
],
113+
tags = ["release-package"],
113114
deps = [
114115
":README.md",
115116
":license",

packages/ngtools/webpack/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ genrule(
8585

8686
pkg_npm(
8787
name = "npm_package",
88+
tags = ["release-package"],
8889
deps = [
8990
":README.md",
9091
":license",

packages/schematics/angular/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ pkg_npm(
166166
"//packages/angular_devkit/schematics:package.json",
167167
"//packages/angular_devkit/core:package.json",
168168
],
169+
tags = ["release-package"],
169170
deps = [
170171
":README.md",
171172
":angular",

scripts/build-packages-dist.mts

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/**
10+
* Script that builds the release output of all packages which have the "release-package
11+
* Bazel tag set. The script builds all those packages and copies the release output to the
12+
* distribution folder within the project.
13+
*/
14+
15+
import { BuiltPackage } from '@angular/ng-dev';
16+
import { execSync } from 'node:child_process';
17+
import { chmodSync, copyFileSync, mkdirSync, rmSync } from 'node:fs';
18+
import { dirname, join } from 'node:path';
19+
import { fileURLToPath } from 'node:url';
20+
import sh from 'shelljs';
21+
22+
/** Name of the Bazel tag that will be used to find release package targets. */
23+
const releaseTargetTag = 'release-package';
24+
25+
/** Path to the project directory. */
26+
const projectDir = join(dirname(fileURLToPath(import.meta.url)), '../');
27+
28+
/** Command that runs Bazel. */
29+
const bazelCmd = process.env.BAZEL || `yarn -s bazel`;
30+
31+
/** Command that queries Bazel for all release package targets. */
32+
const queryPackagesCmd =
33+
`${bazelCmd} query --output=label "attr('tags', '\\[.*${releaseTargetTag}.*\\]', //packages/...) ` +
34+
`intersect kind('pkg_npm', //packages/...)"`;
35+
36+
/** Path for the default distribution output directory. */
37+
const defaultDistPath = join(projectDir, 'dist/releases');
38+
39+
/** Builds the release packages for NPM. */
40+
export function performNpmReleaseBuild(): BuiltPackage[] {
41+
return buildReleasePackages(defaultDistPath, /* isSnapshotBuild */ false);
42+
}
43+
44+
/**
45+
* Builds the release packages as snapshot build. This means that the current
46+
* Git HEAD SHA is included in the version (for easier debugging and back tracing).
47+
*/
48+
export function performDefaultSnapshotBuild(): BuiltPackage[] {
49+
return buildReleasePackages(defaultDistPath, /* isSnapshotBuild */ true);
50+
}
51+
52+
/**
53+
* Builds the release packages with the given compile mode and copies
54+
* the package output into the given directory.
55+
*/
56+
function buildReleasePackages(distPath: string, isSnapshotBuild: boolean): BuiltPackage[] {
57+
console.log('######################################');
58+
console.log(' Building release packages...');
59+
console.log('######################################');
60+
61+
// List of targets to build. e.g. "packages/angular/cli:npm_package"
62+
const targets = exec(queryPackagesCmd, true).split(/\r?\n/);
63+
const packageNames = getPackageNamesOfTargets(targets);
64+
const bazelBinPath = exec(`${bazelCmd} info bazel-bin`, true);
65+
const getBazelOutputPath = (pkgName: string) =>
66+
join(bazelBinPath, 'packages', pkgName, 'npm_package');
67+
const getDistPath = (pkgName: string) => join(distPath, pkgName);
68+
69+
// Build with "--config=release" or `--config=snapshot` so that Bazel
70+
// runs the workspace stamping script. The stamping script ensures that the
71+
// version placeholder is populated in the release output.
72+
const stampConfigArg = `--config=${isSnapshotBuild ? 'snapshot' : 'release'}`;
73+
74+
// Walk through each release package and clear previous "npm_package" outputs. This is
75+
// a workaround for: https://github.com/bazelbuild/rules_nodejs/issues/1219. We need to
76+
// do this to ensure that the version placeholders are properly populated.
77+
packageNames.forEach((pkgName) => {
78+
// Directory output is created by the npm_package target
79+
const directoryOutputPath = getBazelOutputPath(pkgName);
80+
// Archive output is created by the npm_package_archive target
81+
const archiveOutputPath = directoryOutputPath + '_archive.tgz';
82+
83+
if (sh.test('-d', directoryOutputPath)) {
84+
sh.chmod('-R', 'u+w', directoryOutputPath);
85+
sh.rm('-rf', directoryOutputPath);
86+
}
87+
try {
88+
chmodSync(archiveOutputPath, '0755');
89+
rmSync(archiveOutputPath, { force: true });
90+
} catch {}
91+
});
92+
93+
// Build both the npm_package and npm_package_archive targets for each package
94+
// TODO: Consider switching to only using the archive for publishing
95+
const buildTargets = targets.flatMap((target) => [target, target + '_archive']);
96+
exec(`${bazelCmd} build ${stampConfigArg} ${buildTargets.join(' ')}`);
97+
98+
// Delete the distribution directory so that the output is guaranteed to be clean. Re-create
99+
// the empty directory so that we can copy the release packages into it later.
100+
rmSync(distPath, { force: true, recursive: true, maxRetries: 3 });
101+
mkdirSync(distPath, { recursive: true });
102+
103+
// Copy the package output into the specified distribution folder.
104+
packageNames.forEach((pkgName) => {
105+
// Directory output is created by the npm_package target
106+
const directoryOutputPath = getBazelOutputPath(pkgName);
107+
// Archive output is created by the npm_package_archive target
108+
const archiveOutputPath = directoryOutputPath + '_archive.tgz';
109+
110+
const targetFolder = getDistPath(pkgName);
111+
console.log(`> Copying package output to "${targetFolder}"`);
112+
113+
// Ensure package scope directory exists prior to copying
114+
mkdirSync(dirname(targetFolder), { recursive: true });
115+
116+
// Copy package contents to target directory
117+
sh.cp('-R', directoryOutputPath, targetFolder);
118+
sh.chmod('-R', 'u+w', targetFolder);
119+
120+
// Copy archive of package to target directory
121+
const archiveTargetPath = join(distPath, `${pkgName.replace('/', '_')}.tgz`);
122+
copyFileSync(archiveOutputPath, archiveTargetPath);
123+
chmodSync(archiveTargetPath, '0755');
124+
});
125+
126+
return packageNames.map((pkg) => {
127+
return {
128+
// Package names on disk do not have the @ scope prefix and use underscores instead of dashes
129+
name: `@${pkg.replace(/_/g, '-')}`,
130+
outputPath: getDistPath(pkg),
131+
};
132+
});
133+
}
134+
135+
/**
136+
* Gets the package names of the specified Bazel targets.
137+
* e.g. //packages/angular/cli:npm_package = angular/cli
138+
*/
139+
function getPackageNamesOfTargets(targets: string[]): string[] {
140+
return targets.map((targetName) => {
141+
const matches = targetName.match(/\/\/packages\/(.*?):/);
142+
if (matches === null) {
143+
throw Error(
144+
`Found Bazel target with "${releaseTargetTag}" tag, but could not ` +
145+
`determine release output name: ${targetName}`,
146+
);
147+
}
148+
149+
return matches[1];
150+
});
151+
}
152+
153+
/** Executes the given command in the project directory. */
154+
function exec(command: string): void;
155+
/** Executes the given command in the project directory and returns its stdout. */
156+
function exec(command: string, captureStdout: true): string;
157+
function exec(command: string, captureStdout?: true) {
158+
const stdout = execSync(command, {
159+
cwd: projectDir,
160+
stdio: ['inherit', captureStdout ? 'pipe' : 'inherit', 'inherit'],
161+
});
162+
163+
if (captureStdout) {
164+
process.stdout.write(stdout);
165+
166+
return stdout.toString().trim();
167+
}
168+
}

tsconfig-build.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"tests/**/*",
2222
"tools/**/*",
2323
".ng-dev/**/*",
24-
"**/*_spec.ts"
24+
"**/*_spec.ts",
25+
"scripts/**/*.mts"
2526
]
2627
}

yarn.lock

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3154,7 +3154,7 @@
31543154
"@types/qs" "*"
31553155
"@types/serve-static" "*"
31563156

3157-
"@types/glob@^8.0.0":
3157+
"@types/glob@*", "@types/glob@^8.0.0":
31583158
version "8.0.0"
31593159
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-8.0.0.tgz#321607e9cbaec54f687a0792b2d1d370739455d2"
31603160
integrity sha512-l6NQsDDyQUVeoTynNpC9uRvCUint/gSUXQA2euwmTuWGvPY5LSDUu6tkCtJB2SvGQlJQzLaKqcGZP4//7EDveA==
@@ -3422,6 +3422,14 @@
34223422
"@types/mime" "*"
34233423
"@types/node" "*"
34243424

3425+
"@types/shelljs@^0.8.11":
3426+
version "0.8.11"
3427+
resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.11.tgz#17a5696c825974e96828e96e89585d685646fcb8"
3428+
integrity sha512-x9yaMvEh5BEaZKeVQC4vp3l+QoFj3BXcd4aYfuKSzIIyihjdVARAadYy3SMNIz0WCCdS2vB9JL/U6GQk5PaxQw==
3429+
dependencies:
3430+
"@types/glob" "*"
3431+
"@types/node" "*"
3432+
34253433
"@types/sockjs@^0.3.33":
34263434
version "0.3.33"
34273435
resolved "https://registry.yarnpkg.com/@types/sockjs/-/sockjs-0.3.33.tgz#570d3a0b99ac995360e3136fd6045113b1bd236f"

0 commit comments

Comments
 (0)