build: fix ts-api-guardian does not work on windows w/ bazel (#26761)

* Fixes that the `ts-api-guardian` package does not work on Windows with Bazel. This is because `ts-api-guardian` does not resolve the runfiles through theNodeJS `require` function that properly handles runfiles within Bazel.

PR Close #26761
This commit is contained in:
Paul Gschwendtner 2018-10-29 21:25:00 +01:00 committed by Matias Niemelä
parent 0e1cceed50
commit 9ad54d74d2
6 changed files with 54 additions and 52 deletions

View File

@ -16,7 +16,7 @@ def generate_targets(golden_files):
[package_name, entry_point_tail] = entry_point.split("/", 1)
directory_name = entry_point_tail.split("/")[-1]
target_suffix = "/" + entry_point_tail if package_name != entry_point_tail else ""
actual_file = "packages/%s%s/%s.d.ts" % (package_name, target_suffix, directory_name)
actual_file = "angular/packages/%s%s/%s.d.ts" % (package_name, target_suffix, directory_name)
label_name = package_name + target_suffix.replace("/", "_")
ts_api_guardian_test(
@ -25,7 +25,7 @@ def generate_targets(golden_files):
data = [golden_file] + [
"//packages/%s:%s" % (package_name + target_suffix, directory_name),
],
golden = "tools/public_api_guard/%s" % golden_file,
golden = "angular/tools/public_api_guard/%s" % golden_file,
tags = [
"fixme-ivy-aot", # ivy no longer emits generated index file
"no-ivy-jit", # we will not ship JIT compiled packages to npm

View File

@ -78,6 +78,8 @@ jasmine_node_test(
]) + [
":ts-api-guardian",
"@ts-api-guardian_deps//chalk",
# TODO: remove this once the boostrap.js workaround has been removed.
":package.json",
],
node_modules = "@ts-api-guardian_deps//typescript:typescript__typings",
tags = ["local"],

View File

@ -53,7 +53,7 @@ export function startCli() {
if (mode === 'help') {
printUsageAndExit(!!errors.length);
} else {
const targets = generateFileNamePairs(argv, mode);
const targets = resolveFileNamePairs(argv, mode);
if (mode === 'out') {
for (const {entrypoint, goldenFile} of targets) {
@ -180,19 +180,38 @@ Options:
process.exit(error ? 1 : 0);
}
export function generateFileNamePairs(
/**
* Resolves a given path to the associated relative path if the current process runs within
* Bazel. We need to use the wrapped NodeJS resolve logic in order to properly handle the given
* runfiles files which are only part of the runfile manifest on Windows.
*/
function resolveBazelFilePath(fileName: string): string {
// If the CLI has been launched through the NodeJS Bazel rules, we need to resolve the
// actual file paths because otherwise this script won't work on Windows where runfiles
// are not available in the working directory. In order to resolve the real path for the
// runfile, we need to use `require.resolve` which handles runfiles properly on Windows.
if (process.env['BAZEL_TARGET']) {
return path.posix.relative(process.cwd(), require.resolve(fileName));
}
return fileName;
}
function resolveFileNamePairs(
argv: minimist.ParsedArgs, mode: string): {entrypoint: string, goldenFile: string}[] {
if (argv[mode]) {
return [{entrypoint: argv._[0], goldenFile: argv[mode]}];
return [{
entrypoint: resolveBazelFilePath(argv._[0]),
goldenFile: resolveBazelFilePath(argv[mode]),
}];
} else { // argv[mode + 'Dir']
let rootDir = argv['rootDir'] || '.';
const goldenDir = argv[mode + 'Dir'];
return argv._.map((fileName: string) => {
return {
entrypoint: fileName,
goldenFile: path.join(goldenDir, path.relative(rootDir, fileName))
entrypoint: resolveBazelFilePath(fileName),
goldenFile: resolveBazelFilePath(path.join(goldenDir, path.relative(rootDir, fileName))),
};
});
}

View File

@ -7,6 +7,12 @@
*/
import * as path from 'path';
// Before running tests, change directories to our directory under the runfiles
// From runfiles we want to go to the angular/tools/ts-api-guardian subfolder.
process.chdir(path.join(process.env['TEST_SRCDIR'], 'angular', 'tools', 'ts-api-guardian'));
// Resolve the path to the package.json of the ts-api-guardian. We need to resolve an actual
// path of a runfile because we want to determine the path to the directory that includes all
// test fixture runfiles. On Windows this is usually the original non-sandboxed disk location,
// otherwise this just refers to the runfile directory with all the proper symlinked files.
// TODO: remove the whole bootstrap file once the tests are Bazel and Windows compatible.
const runfilesDirectory = path.dirname(require.resolve('../package.json'));
process.chdir(runfilesDirectory);

View File

@ -12,7 +12,7 @@ import * as fs from 'fs';
import * as path from 'path';
import {assertFileEqual, unlinkRecursively} from './helpers';
const BINARY = 'ts-api-guardian/bin/ts-api-guardian';
const BINARY_PATH = require.resolve('../ts-api-guardian/bin/ts-api-guardian');
describe('cli: e2e test', () => {
const outDir = path.join(process.env['TEST_TMPDIR'], 'tmp');
@ -78,7 +78,7 @@ describe('cli: e2e test', () => {
});
it('should generate respecting --stripExportPattern', () => {
const {stdout, status} = execute([
const {status} = execute([
'--out', path.join(outDir, 'underscored.d.ts'), '--stripExportPattern', '^__.*',
'test/fixtures/underscored.d.ts'
]);
@ -88,7 +88,7 @@ describe('cli: e2e test', () => {
});
it('should not throw for aliased stripped exports', () => {
const {stdout, status} = execute([
const {status} = execute([
'--out', path.join(outDir, 'stripped_alias.d.ts'), '--stripExportPattern', '^__.*',
'test/fixtures/stripped_alias.d.ts'
]);
@ -121,9 +121,13 @@ function copyFile(sourceFile: string, targetFile: string) {
}
function execute(args: string[]): {stdout: string, stderr: string, status: number} {
const output = child_process.spawnSync(process.execPath, [path.resolve(BINARY), ...args], {
// We need to determine the directory that includes the `ts-api-guardian` npm_package that
// will be used to spawn the CLI binary. This is a workaround because technically we shouldn't
// spawn a child process that doesn't have the custom NodeJS module resolution for Bazel.
const nodePath = path.join(path.dirname(require.resolve('../lib/cli.js')), '../');
const output = child_process.spawnSync(process.execPath, [BINARY_PATH, ...args], {
env: {
'NODE_PATH': process.cwd(),
'NODE_PATH': nodePath,
}
});
chai.assert(!output.error, 'Child process failed or timed out: ' + output.error);

View File

@ -7,45 +7,42 @@
*/
import chai = require('chai');
import * as ts from 'typescript';
import {parseArguments, generateFileNamePairs} from '../lib/cli';
import {parseArguments} from '../lib/cli';
describe('cli: parseArguments', () => {
it('should show usage with error when supplied with no arguments', () => {
const {argv, mode, errors} = parseArguments([]);
const {mode, errors} = parseArguments([]);
chai.assert.equal(mode, 'help');
chai.assert.deepEqual(errors, ['No input file specified.']);
});
it('should show usage without error when supplied with --help', () => {
const {argv, mode, errors} = parseArguments(['--help']);
const {mode, errors} = parseArguments(['--help']);
chai.assert.equal(mode, 'help');
chai.assert.deepEqual(errors, []);
});
it('should show usage with error when supplied with none of --out/verify[Dir]', () => {
const {argv, mode, errors} = parseArguments(['input.d.ts']);
const {mode, errors} = parseArguments(['input.d.ts']);
chai.assert.equal(mode, 'help');
chai.assert.deepEqual(errors, ['Specify either --out[Dir] or --verify[Dir]']);
});
it('should show usage with error when supplied with both of --out/verify[Dir]', () => {
const {argv, mode, errors} =
const {mode, errors} =
parseArguments(['--out', 'out.d.ts', '--verifyDir', 'golden.d.ts', 'input.d.ts']);
chai.assert.equal(mode, 'help');
chai.assert.deepEqual(errors, ['Specify either --out[Dir] or --verify[Dir]']);
});
it('should show usage with error when supplied without input file', () => {
const {argv, mode, errors} = parseArguments(['--out', 'output.d.ts']);
const {mode, errors} = parseArguments(['--out', 'output.d.ts']);
chai.assert.equal(mode, 'help');
chai.assert.deepEqual(errors, ['No input file specified.']);
});
it('should show usage with error when supplied without input file', () => {
const {argv, mode, errors} =
parseArguments(['--out', 'output.d.ts', 'first.d.ts', 'second.d.ts']);
const {mode, errors} = parseArguments(['--out', 'output.d.ts', 'first.d.ts', 'second.d.ts']);
chai.assert.equal(mode, 'help');
chai.assert.deepEqual(errors, ['More than one input specified. Use --outDir instead.']);
});
@ -66,29 +63,3 @@ describe('cli: parseArguments', () => {
chai.assert.deepEqual(errors, []);
});
});
describe('cli: generateFileNamePairs', () => {
it('should generate one file pair in one-file mode', () => {
chai.assert.deepEqual(
generateFileNamePairs({_: ['input.d.ts'], out: 'output.d.ts'}, 'out'),
[{entrypoint: 'input.d.ts', goldenFile: 'output.d.ts'}]);
});
it('should generate file pairs in multi-file mode according to current directory', () => {
chai.assert.deepEqual(
generateFileNamePairs({_: ['src/first.d.ts', 'src/second.d.ts'], outDir: 'bank'}, 'out'), [
{entrypoint: 'src/first.d.ts', goldenFile: 'bank/src/first.d.ts'},
{entrypoint: 'src/second.d.ts', goldenFile: 'bank/src/second.d.ts'}
]);
});
it('should generate file pairs according to rootDir if provided', () => {
chai.assert.deepEqual(
generateFileNamePairs(
{_: ['src/first.d.ts', 'src/second.d.ts'], outDir: 'bank', rootDir: 'src'}, 'out'),
[
{entrypoint: 'src/first.d.ts', goldenFile: 'bank/first.d.ts'},
{entrypoint: 'src/second.d.ts', goldenFile: 'bank/second.d.ts'}
]);
});
});