Skip to content

Commit

Permalink
feat: add @angular-builders/bazel package (#1048)
Browse files Browse the repository at this point in the history
* feat: add @angular-builders/bazel package

This provides a very small shim builder, which spawns the Bazel build tool under an ng build or ng test command.
It can be useful during a migration, where developers use Angular CLI but some parts of the workspace build and test with Bazel.

It is moving from https://github.com/bazelbuild/rules_nodejs/tree/stable/packages/angular
because that project is cutting scope.

Fixes #1047

Co-authored-by: JeB <jebbacca@gmail.com>
Co-authored-by: JeB <9823087+just-jeb@users.noreply.github.com>
  • Loading branch information
3 people authored Nov 8, 2021
1 parent b6d4f2a commit c651c3a
Show file tree
Hide file tree
Showing 17 changed files with 250 additions and 6 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Builders' and Angular **major** versions **must** match.

- [Custom webpack builders](./packages/custom-webpack) (an alternative to `ng eject`) [![npm version](https://img.shields.io/npm/v/@angular-builders/custom-webpack.svg) ![npm (tag)](https://img.shields.io/npm/v/@angular-builders/custom-webpack/next.svg) ![npm](https://img.shields.io/npm/dm/@angular-builders/custom-webpack.svg)](https://www.npmjs.com/package/@angular-builders/custom-webpack)
- [Jest builder](./packages/jest) (allows running `ng test` with Jest) [![npm version](https://img.shields.io/npm/v/@angular-builders/jest.svg) ![npm (tag)](https://img.shields.io/npm/v/@angular-builders/jest/next.svg) ![npm](https://img.shields.io/npm/dm/@angular-builders/jest.svg)](https://www.npmjs.com/package/@angular-builders/jest)
- [Bazel builder](./packages/bazel) (`ng` wrapper for `Bazel` build) [![npm version](https://img.shields.io/npm/v/@angular-builders/bazel.svg) ![npm (tag)](https://img.shields.io/npm/v/@angular-builders/bazel/next.svg) ![npm](https://img.shields.io/npm/dm/@angular-builders/bazel.svg)](https://www.npmjs.com/package/@angular-builders/bazek)
- [Timestamp builder](./packages/timestamp) (an example builder from [this](https://medium.com/@justjeb/angular-cli-6-under-the-hood-builders-demystified-f0690ebcf01) article) [![npm version](https://img.shields.io/npm/v/@angular-builders/timestamp.svg) ![npm (tag)](https://img.shields.io/npm/v/@angular-builders/timestamp/next.svg) ![npm](https://img.shields.io/npm/dm/@angular-builders/timestamp.svg)](https://www.npmjs.com/package/@angular-builders/timestamp)

# Get in touch
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"packages": [
"packages/*",
"packages/*/examples/*",
"packages/timestamp/example"
"packages/*/example"
],
"command": {
"publish": {
Expand Down
15 changes: 15 additions & 0 deletions packages/bazel/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
*.iml
.idea
node_modules

## this is generated by `npm pack`
*.tgz
package

dist
src/schema.ts
*.d.ts
**/*.js
!scripts/*.js
*.js.map
example/bazel-*
33 changes: 33 additions & 0 deletions packages/bazel/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Angular CLI support for Bazel

Provides an Angular CLI Builder, which can execute Bazel when triggered by `ng build`, `ng test`, etc.
See https://angular.io/guide/cli-builder for more info about Builders.

> This package is a replacement for parts of the deprecated @angular/bazel package previously maintained by the Angular team.
This builder assumes you have already created Bazel configurations (WORKSPACE and BUILD files).
There is presently no tooling to generate these automatically that's supported by either Angular team or rules_nodejs maintainers.
See the [`@bazel/create`](https://www.npmjs.com/package/@bazel/create)
package for a quickstart to creating a Bazel workspace, or look at examples in [rules_nodejs].

To use it, you would just install this package (it doesn't hook into `ng add` because it has no schematics):

```sh
$ npm install --save-dev @angular-builders/bazel
```

Then edit your `angular.json` to invoke Bazel. For example, to have `ng build` do `bazel build //:all` you would edit the `architect` block to have:

```json
"architect": {
"build": {
"builder": "@angular-builders/bazel:build",
"options": {
"targetLabel": "//:all",
"bazelCommand": "build"
}
}
}
```

[rules_nodejs]: https://github.com/bazelbuild/rules_nodejs
9 changes: 9 additions & 0 deletions packages/bazel/builders.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"builders": {
"build": {
"implementation": "./dist",
"schema": "./dist/schema.json",
"description": "Executes Bazel on a target."
}
}
}
6 changes: 6 additions & 0 deletions packages/bazel/example/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Writes "hello world" to ./bazel-bin/out
genrule(
name = "hello_world",
outs = ["out"],
cmd = "echo 'hello world' >$@",
)
Empty file.
18 changes: 18 additions & 0 deletions packages/bazel/example/angular.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"bazel-build": {
"architect": {
"build": {
"builder": "@angular-builders/bazel:build",
"options": {
"targetLabel": "//:all",
"bazelCommand": "build"
}
}
}
}
}
}
12 changes: 12 additions & 0 deletions packages/bazel/example/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "bazel-example",
"version": "13.0.0",
"scripts": {
"build": "ng build"
},
"private": true,
"devDependencies": {
"@angular/cli": "^13.0.1",
"@angular-builders/bazel": "latest"
}
}
41 changes: 41 additions & 0 deletions packages/bazel/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"name": "@angular-builders/bazel",
"description": "Run Bazel under the Angular CLI",
"license": "Apache-2.0",
"version": "13.0.0",
"repository": {
"type": "git",
"url": "https://github.com/just-jeb/angular-builders.git",
"directory": "packages/bazel"
},
"bugs": {
"url": "https://github.com/just-jeb/angular-builders/issues"
},
"keywords": [
"angular",
"bazel"
],
"builders": "./builders.json",
"main": "dist/index.js",
"files": [
"dist",
"builders.json"
],
"scripts": {
"prebuild": "yarn clean && yarn generate",
"build": "../../node_modules/.bin/tsc",
"postbuild": "yarn copy",
"clean": "../../node_modules/.bin/rimraf dist src/schema.ts",
"copy": "../../node_modules/.bin/cpy src/schema.json dist",
"generate": "../../node_modules/.bin/quicktype -s schema src/schema.json -o src/schema.ts",
"ci": "node ./scripts/ci.js"
},
"dependencies": {
"@angular-devkit/architect": ">=0.1300.0 < 0.1400.0",
"@bazel/bazelisk": "^1.4.0",
"@bazel/ibazel": "^0.13.1"
},
"devDependencies": {
"@angular-devkit/core": "^13.0.0"
}
}
24 changes: 24 additions & 0 deletions packages/bazel/scripts/ci.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const { execSync } = require('child_process');
const { readFileSync } = require('fs');
const { exit } = require('process');

runTests();

function runTests() {
process.chdir('./example');
runBuild();
compareResults();
}

function runBuild() {
execSync('yarn build');
}

function compareResults() {
const expectedOut = 'hello world\n';
const bazelOut = readFileSync('./bazel-bin/out', { encoding: 'utf-8' });
if (bazelOut !== expectedOut) {
console.log(`ERROR: Expected bazel output is ${expectedOut}, actual is ${bazelOut}`);
exit(1);
}
}
38 changes: 38 additions & 0 deletions packages/bazel/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { BuilderContext, BuilderOutput, createBuilder } from '@angular-devkit/architect';
import type { JsonObject } from '@angular-devkit/core';
import { getNativeBinary as bazeliskBin } from '@bazel/bazelisk/bazelisk';
import { getNativeBinary as ibazelBin } from '@bazel/ibazel';
import { spawn } from 'child_process';
import { Schema } from './schema';

async function _bazelBuilder(
options: JsonObject & Schema,
context: BuilderContext
): Promise<BuilderOutput> {
const { bazelCommand, targetLabel, watch } = options;
const binary = watch ? ibazelBin() : bazeliskBin();
if (typeof binary !== 'string') {
// this happens if no binary is located for the current platform
context.logger.error('No Bazel binary detected');
return { success: false };
} else {
try {
const ps = spawn(binary, [bazelCommand, targetLabel], { stdio: 'inherit' });

function shutdown() {
ps.kill('SIGTERM');
}

process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
return new Promise(resolve => {
ps.on('close', e => resolve({ success: e === 0 }));
});
} catch (err: any) {
context.logger.error(err.message);
return { success: false };
}
}
}

export default createBuilder(_bazelBuilder);
31 changes: 31 additions & 0 deletions packages/bazel/src/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"$schema": "http://json-schema.org/schema",
"title": "Bazel builder schema",
"description": "Options for Bazel Builder",
"type": "object",
"properties": {
"targetLabel": {
"type": "string",
"description": "Target to be executed under Bazel."
},
"bazelCommand": {
"type": "string",
"description": "Common commands supported by Bazel.",
"enum": [
"run",
"build",
"test"
]
},
"watch": {
"type": "boolean",
"description": "If true, watch the filesystem using ibazel.",
"default": false
}
},
"additionalProperties": false,
"required": [
"targetLabel",
"bazelCommand"
]
}
10 changes: 10 additions & 0 deletions packages/bazel/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"noImplicitAny": false
},
"files": [
"src/index.ts"
]
}
7 changes: 7 additions & 0 deletions packages/bazel/types.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module '@bazel/bazelisk/bazelisk' {
function getNativeBinary(): Promise<number> | string;
}

declare module '@bazel/ibazel' {
function getNativeBinary(): Promise<number> | string;
}
2 changes: 1 addition & 1 deletion packages/timestamp/example/package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "example",
"name": "timestamp-example",
"version": "13.0.0-beta.0",
"scripts": {
"ng": "ng",
Expand Down
7 changes: 3 additions & 4 deletions scripts/run-ci.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,9 @@ yarn bootstrap:examples

# Get travis's chrome version and download the appropriate webdriver-manager for protractor
CHROME_VERSION=`google-chrome --version | egrep -o '[0-9.]+' | head -1`
npx lerna exec --ignore '@angular-builders/*' -- ./node_modules/protractor/bin/webdriver-manager update --versions.chrome $CHROME_VERSION
WEBDRIVER_MANAGER_BIN=./node_modules/protractor/bin/webdriver-manager;
yarn lerna exec --ignore '@angular-builders/*' "[ -f $WEBDRIVER_MANAGER_BIN ] && $WEBDRIVER_MANAGER_BIN update --versions.chrome $CHROME_VERSION || echo \`pwd\`: No webdriver-manager found"

npx lerna run ci
yarn lerna run ci

cleanup


0 comments on commit c651c3a

Please sign in to comment.