Skip to content

Commit

Permalink
Switch breaking-changes and momentOfTruth scripts to TypeScript (#…
Browse files Browse the repository at this point in the history
…5643)

Switch `breaking-changes` and `momentOfTruth` scripts to TypeScript
  • Loading branch information
sergey-shandar authored Apr 15, 2019
1 parent dd797bb commit a2649ab
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 147 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,8 @@ warnings.txt
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

*.js
*.d.ts
*.js.map
*.d.ts.map
6 changes: 5 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,26 @@ script:
- >-
if [[ $MODE == 'semantic' ]]; then
npm install
npm run tsc
node scripts/semanticValidation.js
fi
- >-
if [[ $MODE == 'model' ]]; then
npm install
npm run tsc
node scripts/modelValidation.js
fi
- >-
if [[ $MODE == 'BreakingChange' ]]; then
scripts/install-dotnet.sh
npm install
node -- scripts/breaking-change.js
npm run tsc
node scripts/breaking-change.ts
fi
- >-
if [[ $MODE == 'lintdiff' ]]; then
scripts/install-dotnet.sh
npm install
npm run tsc
node scripts/momentOfTruth.js && node scripts/momentOfTruthPostProcessing.js
fi
8 changes: 4 additions & 4 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
displayName: 'npm install'
inputs:
verbose: false
- script: 'node scripts/semanticValidation.js'
- script: 'npm run tsc && node scripts/semanticValidation.js'
displayName: 'Semantic Validation'

- job: "ModelValidation"
Expand All @@ -52,7 +52,7 @@ jobs:
displayName: 'npm install'
inputs:
verbose: false
- script: 'node scripts/modelValidation.js'
- script: 'npm run tsc && node scripts/modelValidation.js'
displayName: 'Model Validation'

- job: "Avocado"
Expand All @@ -78,7 +78,7 @@ jobs:
displayName: 'npm install'
inputs:
verbose: false
- script: 'node scripts/breaking-change.js'
- script: 'npm run tsc && node scripts/breaking-change.js'
displayName: 'Breaking Changes'

- job: "LintDiff"
Expand All @@ -94,7 +94,7 @@ jobs:
verbose: false
- script: 'scripts/install-dotnet.sh'
displayName: 'install .Net'
- script: 'node scripts/momentOfTruth.js && node scripts/momentOfTruthPostProcessing.js'
- script: 'npm run tsc && node scripts/momentOfTruth.js && node scripts/momentOfTruthPostProcessing.js'
displayName: 'LintDiff'

- job: "SDK"
Expand Down
44 changes: 22 additions & 22 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,28 @@
"description": "Tests for Azure REST API Specifications",
"license": "MIT",
"devDependencies": {
"@azure/avocado": "^0.3.0",
"@azure/oad": "^0.4.3",
"@microsoft.azure/async-io": "^1.0.21",
"@microsoft.azure/literate": "^1.0.21",
"@microsoft.azure/polyfill": "^1.0.17",
"@ts-common/commonmark-to-markdown": "^1.1.10",
"@ts-common/fs": "0.1.1",
"@types/js-yaml": "^3.12.0",
"fs-extra": "^3.0.1",
"glob": "^5.0.14",
"js-yaml": "^3.13.0",
"json-schema-ref-parser": "^3.1.2",
"@azure/avocado": "^0.3.3",
"@azure/oad": "^0.5.1",
"@microsoft.azure/async-io": "^2.0.21",
"@microsoft.azure/literate": "^1.0.25",
"@microsoft.azure/polyfill": "^1.0.19",
"@octokit/rest": "^16.24.1",
"@ts-common/commonmark-to-markdown": "^1.2.0",
"@ts-common/fs": "0.2.0",
"@types/fs-extra": "^5.0.5",
"@types/js-yaml": "^3.12.1",
"@types/request": "^2.48.1",
"fs-extra": "^7.0.1",
"glob": "^7.1.3",
"js-yaml": "^3.13.1",
"json-schema-ref-parser": "^6.1.0",
"mocha": "*",
"oav": "^0.16.1",
"request": "^2.61.0",
"request-promise-native": "^1.0.5",
"ts-node": "^8.0.3",
"typescript": "^3.2.4",
"z-schema": "^3.25.1"
},
"dependencies": {
"@octokit/rest": "^15.2.6"
"oav": "^0.18.1",
"request": "^2.88.0",
"request-promise-native": "^1.0.7",
"ts-node": "^8.1.0",
"typescript": "^3.4.3",
"z-schema": "^4.0.2"
},
"homepage": "https://github.com/azure/azure-rest-api-specs",
"repository": {
Expand All @@ -42,7 +42,7 @@
"url": "http://github.com/azure/azure-rest-api-specs/issues"
},
"scripts": {
"test": "mocha -t 500000 --reporter min",
"test": "tsc && mocha -t 500000 --reporter min",
"oav": "oav",
"tsc": "tsc",
"multiapi": "ts-node ./scripts/multiapi.ts"
Expand Down
62 changes: 37 additions & 25 deletions scripts/breaking-change.js → scripts/breaking-change.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License in the project root for license information.

'use strict';
const utils = require('../test/util/utils'),
path = require('path'),
fs = require('fs-extra'),
os = require('os'),
exec = require('util').promisify(require('child_process').exec),
oad = require('@azure/oad');
import * as stringMap from '@ts-common/string-map'
import * as tsUtils from './ts-utils'
import * as utils from '../test/util/utils'
import * as path from 'path'
import * as fs from 'fs-extra'
import * as os from 'os'
import * as childProcess from 'child_process'
import * as oad from '@azure/oad'
import * as util from 'util'

const exec = util.promisify(childProcess.exec)

// This map is used to store the mapping between files resolved and stored location
var resolvedMapForNewSpecs = {};
var resolvedMapForNewSpecs: stringMap.MutableStringMap<string> = {};
let outputFolder = path.join(os.tmpdir(), "resolved");
// Used to enable running script outside TravisCI for debugging
let isRunningInTravisCI = process.env.TRAVIS === 'true';
Expand All @@ -20,7 +24,7 @@ const headerText = `
|-|------|----------|---------|
`;

function iconFor(type) {
function iconFor(type: unknown) {
if (type === 'Error') {
return ':x:';
} else if (type === 'Warning') {
Expand All @@ -32,26 +36,33 @@ function iconFor(type) {
}
}

function shortName(filePath) {
function shortName(filePath: string) {
return `${path.basename(path.dirname(filePath))}/&#8203;<strong>${path.basename(filePath)}</strong>`;
}

function tableLine(filePath, diff) {
type Diff = {
readonly type: unknown
readonly id: string
readonly code: unknown
readonly message: unknown
}

function tableLine(filePath: string, diff: Diff) {
return `|${iconFor(diff['type'])}|[${diff['type']} ${diff['id']} - ${diff['code']}](https://github.com/Azure/openapi-diff/blob/master/docs/rules/${diff['id']}.md)|[${shortName(filePath)}](${blobHref(filePath)} "${filePath}")|${diff['message']}|\n`;
}

function blobHref(file) {
function blobHref(file: unknown) {
return `https://github.com/${process.env.TRAVIS_PULL_REQUEST_SLUG}/blob/${process.env.TRAVIS_PULL_REQUEST_SHA}/${file}`;
}

/**
* Compares old and new specifications for breaking change detection.
*
* @param {string} oldSpec Path to the old swagger specification file.
* @param oldSpec Path to the old swagger specification file.
*
* @param {string} newSpec Path to the new swagger specification file.
* @param newSpec Path to the new swagger specification file.
*/
async function runOad(oldSpec, newSpec) {
async function runOad(oldSpec: string, newSpec: string) {
if (oldSpec === null || oldSpec === undefined || typeof oldSpec.valueOf() !== 'string' || !oldSpec.trim().length) {
throw new Error('oldSpec is a required parameter of type "string" and it cannot be an empty string.');
}
Expand All @@ -65,7 +76,7 @@ async function runOad(oldSpec, newSpec) {
console.log(`New Spec: "${newSpec}"`);
console.log(`>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`);

let result = await oad.compare(oldSpec, newSpec, { consoleLogLevel: 'warn', json: true });
let result = await oad.compare(oldSpec, newSpec, { consoleLogLevel: 'warn' });
console.log(result);

if (!result) {
Expand All @@ -81,9 +92,9 @@ async function runOad(oldSpec, newSpec) {
/**
* Processes the given swagger and stores the resolved swagger on to disk
*
* @param {string} swaggerPath Path to the swagger specification file.
* @param swaggerPath Path to the swagger specification file.
*/
async function processViaAutoRest(swaggerPath) {
async function processViaAutoRest(swaggerPath: string) {
if (swaggerPath === null || swaggerPath === undefined || typeof swaggerPath.valueOf() !== 'string' || !swaggerPath.trim().length) {
throw new Error('swaggerPath is a required parameter of type "string" and it cannot be an empty string.');
}
Expand Down Expand Up @@ -115,10 +126,10 @@ async function runScript() {
console.log(swaggersToProcess);

console.log('Finding new swaggers...')
let newSwaggers = [];
let newSwaggers: unknown[] = [];
if (isRunningInTravisCI && swaggersToProcess.length > 0) {
newSwaggers = await utils.doOnBranch(utils.getTargetBranch(), async () => {
return swaggersToProcess.filter(s => !fs.existsSync(s))
return swaggersToProcess.filter((s: string) => !fs.existsSync(s))
});
}

Expand All @@ -133,7 +144,7 @@ async function runScript() {
console.dir(resolvedMapForNewSpecs);

let errors = 0, warnings = 0;
const diffFiles = {};
const diffFiles: stringMap.MutableStringMap<Diff[]> = {};
const newFiles = [];

for (const swagger of swaggersToProcess) {
Expand All @@ -144,8 +155,9 @@ async function runScript() {
continue;
}

if (resolvedMapForNewSpecs[swagger]) {
const diffs = await runOad(swagger, resolvedMapForNewSpecs[swagger]);
const resolved = resolvedMapForNewSpecs[swagger]
if (resolved) {
const diffs = await runOad(swagger, resolved);
if (diffs) {
diffFiles[swagger] = diffs;
for (const diff of diffs) {
Expand Down Expand Up @@ -189,7 +201,7 @@ async function runScript() {

diffFileNames.sort();
for (const swagger of diffFileNames) {
const diffs = diffFiles[swagger];
const diffs = tsUtils.asNonUndefined(diffFiles[swagger]);
diffs.sort((a, b) => {
if (a.type === b.type) {
return a.id.localeCompare(b.id);
Expand Down Expand Up @@ -227,7 +239,7 @@ async function runScript() {
}

// magic starts here
runScript().then(success => {
runScript().then(() => {
console.log(`Thanks for using breaking change tool to review.`);
console.log(`If you encounter any issue(s), please open issue(s) at https://github.com/Azure/openapi-diff/issues .`);
}).catch(err => {
Expand Down
50 changes: 30 additions & 20 deletions scripts/momentOfTruth.js → scripts/momentOfTruth.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

'use strict';

const exec = require('child_process').exec,
path = require('path'),
utils = require('../test/util/utils'),
fs = require('fs');
import * as stringMap from '@ts-common/string-map'
import * as tsUtils from './ts-utils'
import { exec } from 'child_process'
import * as path from 'path'
import * as utils from '../test/util/utils'
import * as fs from 'fs'

let configsToProcess = utils.getConfigFilesChangedInPR();
let pullRequestNumber = utils.getPullRequestNumber();
let linterCmd = `npx autorest --validation --azure-validator --message-format=json `;
var filename = `${pullRequestNumber}.json`;
var logFilepath = path.join(getLogDir(), filename);
var finalResult = {};
finalResult["pullRequest"] = pullRequestNumber;
finalResult["repositoryUrl"] = utils.getRepoUrl();
finalResult["files"] = {};

type FinalResult = {
readonly pullRequest: unknown,
readonly repositoryUrl: unknown,
readonly files: stringMap.MutableStringMap<stringMap.MutableStringMap<unknown>>
}

var finalResult: FinalResult = {
pullRequest: pullRequestNumber,
repositoryUrl: utils.getRepoUrl(),
files: {}
}

// Creates and returns path to the logging directory
function getLogDir() {
Expand All @@ -39,12 +47,12 @@ function createLogFile() {
}

//appends the content to the log file
function writeContent(content) {
function writeContent(content: unknown) {
fs.writeFileSync(logFilepath, content);
}

// Executes linter on given swagger path and returns structured JSON of linter output
async function getLinterResult(swaggerPath) {
async function getLinterResult(swaggerPath: string|null|undefined) {
if (swaggerPath === null || swaggerPath === undefined || typeof swaggerPath.valueOf() !== 'string' || !swaggerPath.trim().length) {
throw new Error('swaggerPath is a required parameter of type "string" and it cannot be an empty string.');
}
Expand All @@ -56,7 +64,7 @@ async function getLinterResult(swaggerPath) {
let cmd = "npx autorest --reset && " + linterCmd + swaggerPath;
console.log(`Executing: ${cmd}`);
const { err, stdout, stderr } = await new Promise(res => exec(cmd, { encoding: 'utf8', maxBuffer: 1024 * 1024 * 64 },
(err, stdout, stderr) => res({ err: err, stdout: stdout, stderr: stderr })));
(err: unknown, stdout: unknown, stderr: unknown) => res({ err: err, stdout: stdout, stderr: stderr })));

if (err && stderr.indexOf("Process() cancelled due to exception") !== -1) {
console.error(`AutoRest exited with code ${err.code}`);
Expand Down Expand Up @@ -86,22 +94,24 @@ async function getLinterResult(swaggerPath) {
};

// Run linter tool
async function runTools(swagger, beforeOrAfter) {
async function runTools(swagger: string, beforeOrAfter: string) {
console.log(`Processing "${swagger}":`);
const linterErrors = await getLinterResult(swagger);
console.log(linterErrors);
await updateResult(swagger, linterErrors, beforeOrAfter);
};

// Updates final result json to be written to the output file
async function updateResult(spec, errors, beforeOrAfter) {
if (!finalResult['files'][spec]) {
finalResult['files'][spec] = {};
async function updateResult(spec: string, errors: unknown, beforeOrAfter: string) {
const files = finalResult['files']
if (!files[spec]) {
files[spec] = {};
}
if (!finalResult['files'][spec][beforeOrAfter]) {
finalResult['files'][spec][beforeOrAfter] = {};
const filesSpec = tsUtils.asNonUndefined(files[spec])
if (!filesSpec[beforeOrAfter]) {
filesSpec[beforeOrAfter] = {};
}
finalResult['files'][spec][beforeOrAfter] = errors;
filesSpec[beforeOrAfter] = errors;
}

//main function
Expand Down
4 changes: 4 additions & 0 deletions scripts/ts-utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License in the project root for license information.

export const asNonUndefined = <T>(v: T|undefined) => v as T
Loading

0 comments on commit a2649ab

Please sign in to comment.