ci: disable bazel in devkit-admin build

Replace the build step with a custom JSON schema output parallel to
bazel.
This commit is contained in:
Hans 2018-09-11 19:03:48 -07:00 committed by Keen Yee Liau
parent 30b0cbda0d
commit c63b4d8d2b
7 changed files with 116 additions and 62 deletions

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
bazel-* bazel-*
test-project-host-* test-project-host-*
dist/ dist/
dist-schema/
# IDEs # IDEs
.idea/ .idea/

View File

@ -34,7 +34,9 @@ function _mkdirp(p: string) {
if (!fs.existsSync(path.dirname(p))) { if (!fs.existsSync(path.dirname(p))) {
_mkdirp(path.dirname(p)); _mkdirp(path.dirname(p));
} }
fs.mkdirSync(p); if (!fs.existsSync(p)) {
fs.mkdirSync(p);
}
} }
function _recursiveFileList(p: string): string[] { function _recursiveFileList(p: string): string[] {
@ -186,21 +188,61 @@ function _exec(command: string, args: string[], opts: { cwd?: string }, logger:
function _build(logger: logging.Logger) { function _build(logger: logging.Logger) {
logger.info('Building...'); logger.info('Building...');
_exec('node_modules/.bin/tsc', ['-p', 'tsconfig.json'], {}, logger); _exec('node', [
require.resolve('typescript/bin/tsc'),
'-p',
'tsconfig.json',
], {}, logger);
} }
function _bazel(logger: logging.Logger) { async function _bazel(logger: logging.Logger) {
logger.info('Bazel build...'); // TODO: undo this when we fully support bazel on windows.
_exec('bazel', ['build', '//packages/...'], {}, logger); // logger.info('Bazel build...');
// _exec('bazel', ['build', '//packages/...'], {}, logger);
const allJsonFiles = glob.sync('packages/**/*.json', {
ignore: [
'**/node_modules/**',
'**/files/**',
'**/*-files/**',
'**/package.json',
],
});
const quicktypeRunner = require('../tools/quicktype_runner');
logger.info('Generating JSON Schema....');
for (const fileName of allJsonFiles) {
if (fs.existsSync(fileName.replace(/\.json$/, '.ts'))
|| fs.existsSync(fileName.replace(/\.json$/, '.d.ts'))) {
// Skip files that already exist.
continue;
}
const content = fs.readFileSync(fileName, 'utf-8');
const json = JSON.parse(content);
if (!json.$schema) {
// Skip non-schema files.
continue;
}
const tsContent = await quicktypeRunner.generate(fileName);
const tsPath = path.join(__dirname, '../dist-schema', fileName.replace(/\.json$/, '.ts'));
_mkdirp(path.dirname(tsPath));
fs.writeFileSync(tsPath, tsContent, 'utf-8');
}
} }
export default function(argv: { local?: boolean, snapshot?: boolean }, logger: logging.Logger) { export default async function(
argv: { local?: boolean, snapshot?: boolean },
logger: logging.Logger,
) {
_clean(logger); _clean(logger);
const sortedPackages = _sortPackages(); const sortedPackages = _sortPackages();
_bazel(logger); await _bazel(logger);
_build(logger); _build(logger);
logger.info('Moving packages to dist/'); logger.info('Moving packages to dist/');
@ -216,7 +258,7 @@ export default function(argv: { local?: boolean, snapshot?: boolean }, logger: l
for (const packageName of sortedPackages) { for (const packageName of sortedPackages) {
const pkg = packages[packageName]; const pkg = packages[packageName];
const bazelBinPath = pkg.build.replace(/([\\\/]dist[\\\/])(packages)/, (_, dist, packages) => { const bazelBinPath = pkg.build.replace(/([\\\/]dist[\\\/])(packages)/, (_, dist, packages) => {
return path.join(dist, 'bazel-bin', packages); return path.join(dist, 'dist-schema', packages);
}); });
if (fs.existsSync(bazelBinPath)) { if (fs.existsSync(bazelBinPath)) {
packageLogger.info(packageName); packageLogger.info(packageName);

View File

@ -29,9 +29,9 @@ function _exec(command: string, args: string[], opts: { cwd?: string }, logger:
} }
export default function (args: { tag?: string }, logger: logging.Logger) { export default async function (args: { tag?: string }, logger: logging.Logger) {
logger.info('Building...'); logger.info('Building...');
build({}, logger.createChild('build')); await build({}, logger.createChild('build'));
return Object.keys(packages).reduce((acc: Promise<void>, name: string) => { return Object.keys(packages).reduce((acc: Promise<void>, name: string) => {
const pkg = packages[name]; const pkg = packages[name];

View File

@ -53,7 +53,7 @@ export interface SnapshotsOptions {
githubToken?: string; githubToken?: string;
} }
export default function(opts: SnapshotsOptions, logger: logging.Logger) { export default async function(opts: SnapshotsOptions, logger: logging.Logger) {
// Get the SHA. // Get the SHA.
if (execSync(`git status --porcelain`).toString() && !opts.force) { if (execSync(`git status --porcelain`).toString() && !opts.force) {
logger.error('You cannot run snapshots with local changes.'); logger.error('You cannot run snapshots with local changes.');
@ -78,7 +78,7 @@ export default function(opts: SnapshotsOptions, logger: logging.Logger) {
// Run build. // Run build.
logger.info('Building...'); logger.info('Building...');
build({ snapshot: true }, logger.createChild('build')); await build({ snapshot: true }, logger.createChild('build'));
for (const packageName of Object.keys(packages)) { for (const packageName of Object.keys(packages)) {
const pkg = packages[packageName]; const pkg = packages[packageName];

View File

@ -5,39 +5,41 @@
* Use of this source code is governed by an MIT-style license that can be * 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 * found in the LICENSE file at https://angular.io/license
*/ */
/**
* This file is pure JavaScript because we want to avoid any dependency or build step
* to it. It's just simple (and zen-ier).
*
* This file wraps around quicktype and can do one of two things;
*
* `node quicktype_runner.js <in_path> <out_path>`
* Reads the in path and outputs the TS file at the out_path.
*
* Using `-` as the out_path will output on STDOUT instead of a file.
*/
// Imports.
const fs = require('fs'); const fs = require('fs');
const path = require('path'); const path = require('path');
const qtCore = require('quicktype-core'); const {
const tempRoot = process.env['BAZEL_TMPDIR'] || require('os').tmpdir(); InputData,
JSONSchema,
JSONSchemaInput,
JSONSchemaStore,
TypeScriptTargetLanguage,
parseJSON,
quicktype,
} = require('quicktype-core');
/**
* This file is pure JavaScript because Bazel only support compiling to ES5, while quicktype is
* ES2015. This results in an incompatible call to `super()` in the FetchingJSONSchemaStore
* class as it tries to call JSONSchemaStore's constructor in ES5.
* TODO: move this file to typescript when Bazel supports ES2015 output.
*
* This file wraps around quicktype and can do one of two things;
*
* `node quicktype_runner.js <in_path> <out_path>`
* Reads the in path and outputs the TS file at the out_path.
*
* Using `-` as the out_path will output on STDOUT instead of a file.
*/
// Header to add to all files. // Header to add to all files.
const header = ` const header = `
/** // THIS FILE IS AUTOMATICALLY GENERATED. TO UPDATE THIS FILE YOU NEED TO CHANGE THE
* @license // CORRESPONDING JSON SCHEMA FILE, THEN RUN devkit-admin build (or bazel build ...).
* Copyright Google Inc. 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
*/
// THIS FILE IS AUTOMATICALLY GENERATED IN BAZEL. TO UPDATE THIS FILE YOU NEED TO CHANGE THE // tslint:disable:no-global-tslint-disable
// CORRESPONDING JSON SCHEMA FILE, THEN RUN BAZEL. // tslint:disable
`.replace(/^\n/m, ''); // Remove the first \n, it's in the constant because formatting is 👍. `;
// Footer to add to all files. // Footer to add to all files.
const footer = ``; const footer = ``;
@ -46,7 +48,7 @@ const footer = ``;
* The simplest Node JSONSchemaStore implementation we can build which supports our custom protocol. * The simplest Node JSONSchemaStore implementation we can build which supports our custom protocol.
* Supports reading from ng-cli addresses, valid URLs and files (absolute). * Supports reading from ng-cli addresses, valid URLs and files (absolute).
*/ */
class FetchingJSONSchemaStore extends qtCore.JSONSchemaStore { class FetchingJSONSchemaStore extends JSONSchemaStore {
constructor(inPath) { constructor(inPath) {
super(); super();
this._inPath = inPath; this._inPath = inPath;
@ -83,10 +85,10 @@ class FetchingJSONSchemaStore extends qtCore.JSONSchemaStore {
} }
if (content == null) { if (content == null) {
throw new Error(`Address ${JSON.stringify(address)} cannot be resolved.`); return undefined;
} }
return qtCore.parseJSON(content, "JSON Schema", address); return parseJSON(content, "JSON Schema", address);
} }
} }
@ -113,40 +115,43 @@ async function main(inPath, outPath) {
async function generate(inPath) { async function generate(inPath) {
// Best description of how to use the API was found at // Best description of how to use the API was found at
// https://blog.quicktype.io/customizing-quicktype/ // https://blog.quicktype.io/customizing-quicktype/
const inputData = new qtCore.InputData(); const inputData = new InputData();
const source = { name: 'Schema', schema: fs.readFileSync(inPath, 'utf-8') }; const source = { name: 'Schema', schema: fs.readFileSync(inPath, 'utf-8') };
await inputData.addSource('schema', source, () => { await inputData.addSource('schema', source, () => {
return new qtCore.JSONSchemaInput(new FetchingJSONSchemaStore(inPath)); return new JSONSchemaInput(new FetchingJSONSchemaStore(inPath));
}); });
const lang = new qtCore.TypeScriptTargetLanguage(); const lang = new TypeScriptTargetLanguage();
const { lines } = await qtCore.quicktype({ const { lines } = await quicktype({
lang, lang,
inputData, inputData,
alphabetizeProperties: true, alphabetizeProperties: true,
src: [inPath],
rendererOptions: { rendererOptions: {
'just-types': true, 'just-types': 'true',
'explicit-unions': true, 'explicit-unions': 'true',
} },
}); });
return header + lines.join('\n') + footer; return header + lines.join('\n') + footer;
} }
// Parse arguments and run main(). if (require.main === module) {
const argv = process.argv.slice(2); // Parse arguments and run main().
if (argv.length < 2 || argv.length > 3) { const argv = process.argv.slice(2);
console.error('Must include 2 or 3 arguments.'); if (argv.length < 2 || argv.length > 3) {
process.exit(1); console.error('Must include 2 or 3 arguments.');
process.exit(1);
}
main(argv[0], argv[1])
.then(() => process.exit(0))
.catch(err => {
console.error('An error happened:');
console.error(err);
process.exit(127);
});
} }
main(...argv) exports.generate = generate;
.then(() => process.exit(0))
.catch(err => {
console.error('An error happened:');
console.error(err);
process.exit(127);
});

View File

@ -52,7 +52,12 @@ _ts_json_schema_interface = rule(
) )
def ts_json_schema(name, src, data = [], **kwargs): # Generates a library that contains the interface for a JSON Schema file. Takes a single `src`
# argument as input, an optional data field for reference files, and produces a ts_library()
# rule containing the typescript interface.
# The file produced will have the same name, with the extension replaced from `.json` to `.ts`.
# Any filename collision will be an error thrown by Bazel.
def ts_json_schema(name, src, data = []):
out = src.replace(".json", ".ts") out = src.replace(".json", ".ts")
_ts_json_schema_interface( _ts_json_schema_interface(

View File

@ -29,6 +29,7 @@
"baseUrl": "", "baseUrl": "",
"rootDirs": [ "rootDirs": [
".", ".",
"./dist-schema/",
"./bazel-bin/" "./bazel-bin/"
], ],
"typeRoots": [ "typeRoots": [