Skip to content

build: move breaking changes script away from gulp #17083

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
"e2e": "bazel test //src/... --test_tag_filters=e2e",
"deploy": "echo 'Not supported yet. Tracked with COMP-230'",
"webdriver-manager": "webdriver-manager",
"breaking-changes": "gulp breaking-changes",
"breaking-changes": "ts-node --project scripts scripts/breaking-changes.ts",
"gulp": "gulp",
"stage-release": "ts-node --project tools/release/ tools/release/stage-release.ts",
"publish-release": "ts-node --project tools/release/ tools/release/publish-release.ts",
Expand Down
104 changes: 104 additions & 0 deletions scripts/breaking-changes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {join, relative} from 'path';
import {readFileSync} from 'fs';
import {bold, red, green} from 'chalk';
import * as ts from 'typescript';
import * as tsutils from 'tsutils';

const projectRoot = process.cwd();

// Current version from the package.json. Splits it on the dash to ignore pre-release labels.
const packageVersion = require(join(projectRoot, 'package.json')).version.split('-')[0];

// Regex used to extract versions from a string.
const versionRegex = /\d+\.\d+\.\d+/;

// Goes through all of the TypeScript files in the project and puts
// together a summary of all of the pending and expired breaking changes.
const configFile = ts.readJsonConfigFile(join(projectRoot, 'tsconfig.json'), ts.sys.readFile);
const parsedConfig = ts.parseJsonSourceFileConfigFileContent(configFile, ts.sys, projectRoot);
const summary: {[version: string]: string[]} = {};

// Go through all the TS files in the project.
parsedConfig.fileNames.forEach(fileName => {
const sourceFile = ts.createSourceFile(fileName, readFileSync(fileName, 'utf8'),
configFile.languageVersion);
const lineRanges = tsutils.getLineRanges(sourceFile);

// Go through each of the comments of the file.
tsutils.forEachComment(sourceFile, (file, range) => {
const comment = file.substring(range.pos, range.end);
const versionMatch = comment.match(versionRegex);

// Don't do any extra work if the comment doesn't indicate a breaking change.
if (!versionMatch || comment.indexOf('@breaking-change') === -1) {
return;
}

// Use a path relative to the project root, in order to make the summary more tidy.
// Also replace escaped Windows slashes with regular forward slashes.
const pathInProject = relative(projectRoot, sourceFile.fileName).replace(/\\/g, '/');
const [version] = versionMatch;

summary[version] = summary[version] || [];
summary[version].push(` ${pathInProject}: ${formatMessage(comment, range, lineRanges)}`);
});
});

// Go through the summary and log out all of the breaking changes.
Object.keys(summary).forEach(version => {
const isExpired = hasExpired(packageVersion, version);
const status = isExpired ? red('(expired)') : green('(not expired)');
const header = bold(`Breaking changes for ${version} ${status}:`);
const messages = summary[version].join('\n');

console.log(isExpired ? red(header) : header);
console.log(isExpired ? red(messages) : messages, '\n');
});

/**
* Formats a message to be logged out in the breaking changes summary.
* @param comment Contents of the comment that contains the breaking change.
* @param commentRange Object containing info on the position of the comment in the file.
* @param lines Ranges of the lines of code in the file.
*/
function formatMessage(comment: string, commentRange: ts.CommentRange, lines: tsutils.LineRange[]) {
const lineNumber = lines.findIndex(line => line.pos > commentRange.pos);
const messageMatch = comment.match(/@deprecated(.*)|@breaking-change(.*)/);
const message = messageMatch ? messageMatch[0] : '';
const cleanMessage = message
.replace(/[\*\/\r\n]|@[\w-]+/g, '')
.replace(versionRegex, '')
.trim();

return `Line ${lineNumber}, ${cleanMessage || 'No message'}`;
}


/** Converts a version string into an object. */
function parseVersion(version: string) {
const [major = 0, minor = 0, patch = 0] = version.split('.').map(segment => parseInt(segment));
return {major, minor, patch};
}


/**
* Checks whether a version has expired, based on the current version.
* @param currentVersion Current version of the package.
* @param breakingChange Version that is being checked.
*/
function hasExpired(currentVersion: string, breakingChange: string) {
if (currentVersion === breakingChange) {
return true;
}

const current = parseVersion(currentVersion);
const target = parseVersion(breakingChange);

return target.major < current.major ||
(target.major === current.major && target.minor < current.minor) ||
(
target.major === current.major &&
target.minor === current.minor &&
target.patch < current.patch
);
}
7 changes: 7 additions & 0 deletions scripts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"compilerOptions": {
"lib": ["es2016"],
"types": ["node"],
"strictNullChecks": true
}
}
1 change: 0 additions & 1 deletion tools/gulp/gulpfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ createPackageBuildTasks(momentAdapterPackage);
createPackageBuildTasks(youTubePlayerPackage);
createPackageBuildTasks(googleMapsPackage);

import './tasks/breaking-changes';
import './tasks/ci';
import './tasks/clean';
import './tasks/default';
Expand Down
109 changes: 0 additions & 109 deletions tools/gulp/tasks/breaking-changes.ts

This file was deleted.