Skip to content
This repository was archived by the owner on Jan 3, 2024. It is now read-only.
Merged
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## 0.2.12

- Use Avocado git checkout and diff algorithms in breaking-changes and linter-diff.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@azure/rest-api-specs-scripts",
"version": "0.1.8",
"version": "0.2.12",
"description": "Scripts for the Azure RestAPI specification repository 'azure-rest-api-specs'.",
"types": "dist/index.d.ts",
"main": "dist/index.js",
Expand Down Expand Up @@ -32,6 +32,7 @@
"typescript": "^3.4.3"
},
"dependencies": {
"@azure/avocado": "^0.4.1",
"@azure/oad": "^0.5.1",
"@ts-common/string-map": "^0.3.0",
"fs-extra": "^7.0.1",
Expand Down
9 changes: 6 additions & 3 deletions src/breaking-change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import * as os from 'os'
import * as childProcess from 'child_process'
import * as oad from '@azure/oad'
import * as util from 'util'
import { devOps, cli } from '@azure/avocado'

const exec = util.promisify(childProcess.exec)

Expand Down Expand Up @@ -116,19 +117,21 @@ async function processViaAutoRest(swaggerPath: string) {

//main function
export async function runScript() {
const pr = await devOps.createPullRequestProperties(cli.defaultConfig())

// See whether script is in Travis CI context
console.log(`isRunningInTravisCI: ${isRunningInTravisCI}`);

let targetBranch = utils.getTargetBranch();
let swaggersToProcess = utils.getFilesChangedInPR();
let swaggersToProcess = await utils.getFilesChangedInPR(pr);

console.log('Processing swaggers:');
console.log(swaggersToProcess);

console.log('Finding new swaggers...')
let newSwaggers: unknown[] = [];
if (isRunningInTravisCI && swaggersToProcess.length > 0) {
newSwaggers = await utils.doOnBranch(utils.getTargetBranch(), async () => {
if (swaggersToProcess.length > 0 && pr !== undefined) {
newSwaggers = await utils.doOnTargetBranch(pr, async () => {
return swaggersToProcess.filter((s: string) => !fs.existsSync(s))
});
}
Expand Down
8 changes: 7 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,10 @@
// Licensed under the MIT License. See License in the project root for license information.

export { runScript as breakingChange } from './breaking-change'
export { runScript as momentOfTruth } from './momentOfTruth'
export { runScript as momentOfTruth } from './momentOfTruth'

import * as utils from './utils'
import * as momentOfTruthUtils from './momentOfTruthUtils'
import * as tsUtils from './ts-utils'

export { utils, momentOfTruthUtils, tsUtils }
33 changes: 16 additions & 17 deletions src/momentOfTruth.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

import * as stringMap from '@ts-common/string-map'
import * as momentOfTruthUtils from './momentOfTruthUtils'
import * as tsUtils from './ts-utils'
import { exec } from 'child_process'
import * as path from 'path'
import * as utils from './utils'
import * as fs from 'fs'
import { devOps, cli } from '@azure/avocado'


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);

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

var finalResult: FinalResult = {
var finalResult: momentOfTruthUtils.FinalResult = {
pullRequest: pullRequestNumber,
repositoryUrl: utils.getRepoUrl(),
files: {}
Expand Down Expand Up @@ -94,39 +89,43 @@ async function getLinterResult(swaggerPath: string|null|undefined) {
};

// Run linter tool
async function runTools(swagger: string, beforeOrAfter: string) {
async function runTools(swagger: string, beforeOrAfter: momentOfTruthUtils.BeforeOrAfter) {
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: string, errors: unknown, beforeOrAfter: string) {
async function updateResult(
spec: string,
errors: readonly momentOfTruthUtils.Issue[],
beforeOrAfter: momentOfTruthUtils.BeforeOrAfter
) {
const files = finalResult['files']
if (!files[spec]) {
files[spec] = {};
files[spec] = { before: [], after: [] };
}
const filesSpec = tsUtils.asNonUndefined(files[spec])
if (!filesSpec[beforeOrAfter]) {
filesSpec[beforeOrAfter] = {};
}
filesSpec[beforeOrAfter] = errors;
}

//main function
export async function runScript() {
const pr = await devOps.createPullRequestProperties(cli.defaultConfig())
const configsToProcess = await utils.getConfigFilesChangedInPR(pr);

console.log('Processing configs:');
console.log(configsToProcess);
createLogFile();
console.log(`The results will be logged here: "${logFilepath}".`)

if (configsToProcess.length > 0) {
if (configsToProcess.length > 0 && pr !== undefined) {
for (const configFile of configsToProcess) {
await runTools(configFile, 'after');
}

await utils.doOnBranch(utils.getTargetBranch(), async () => {
await utils.doOnTargetBranch(pr, async () => {
for (const configFile of configsToProcess) {
await runTools(configFile, 'before');
}
Expand Down
34 changes: 34 additions & 0 deletions src/momentOfTruthUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

import * as stringMap from '@ts-common/string-map'

export type Issue = {
readonly type?: string
readonly code: unknown
readonly message: unknown
readonly id: string
readonly validationCategory: string
readonly providerNamespace: unknown
readonly resourceType: unknown
readonly sources: readonly unknown[]
readonly jsonref: string
readonly filePath: string
readonly lineNumber: number
}

export type BeforeOrAfter = 'before' | 'after'

export type File = {
[key in BeforeOrAfter]: readonly Issue[]
}

/**
* Moment of truth is using this type for defining a format of file which is produced by
* `momentOfTruth.ts` script and `momentOfTruthPostProcessing`.
*/
export type FinalResult = {
readonly pullRequest: unknown,
readonly repositoryUrl: unknown,
readonly files: stringMap.MutableStringMap<File>
}
58 changes: 15 additions & 43 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

import * as tsUtils from './ts-utils'
import * as stringMap from '@ts-common/string-map'
import * as os from 'os'
import * as fs from 'fs-extra'
import * as glob from 'glob'
import * as path from 'path'
Expand All @@ -12,6 +11,7 @@ import * as YAML from 'js-yaml'
import request = require('request')
import * as util from 'util'
import { execSync } from 'child_process'
import { devOps } from '@azure/avocado'

const asyncJsonRequest = (url: string) => new Promise<unknown>((res, rej) => request(
{ url, json: true },
Expand Down Expand Up @@ -76,23 +76,21 @@ export const getTargetBranch = function() {
};

/**
* Check out a copy of a branch to a temporary location, execute a function, and then restore the previous state
* Check out a copy of a target branch to a temporary location, execute a function, and then restore the previous state.
*/
export const doOnBranch = async function<T>(branch: unknown, func: () => Promise<T>) {
fetchBranch(branch);
const branchSha = resolveRef(`origin/${branch}`);
const tmpDir = path.join(os.tmpdir(), branchSha);

export const doOnTargetBranch = async <T>(pr: devOps.PullRequestProperties, func: () => Promise<T>) => {
const currentDir = process.cwd();
checkoutBranch(branch, tmpDir);

pr.checkout(pr.targetBranch)

console.log(`Changing directory and executing the function...`);
process.chdir(tmpDir);
// pr.workingDir is a directory of a cloned Pull Request Git repository. We can't use
// the original Git repository to switch branches
process.chdir(pr.workingDir);
const result = await func();

console.log(`Restoring previous directory and deleting secondary working tree...`);
process.chdir(currentDir);
execSync(`rm -rf ${tmpDir}`);

return result;
}
Expand All @@ -106,24 +104,6 @@ export const resolveRef = function(ref: unknown) {
return execSync(cmd, { encoding: 'utf8' }).trim();
}

/**
* Fetch ref for a branch from the origin
*/
export const fetchBranch = function(branch: unknown) {
let cmds = [
`git remote -vv`,
`git branch --all`,
`git remote set-branches origin --add ${branch}`,
`git fetch origin ${branch}`
];

console.log(`Fetching branch ${branch} from origin...`);
for (let cmd of cmds) {
console.log(`> ${cmd}`);
execSync(cmd, { encoding: 'utf8', stdio: 'inherit' });
}
}

/**
* Checkout a copy of branch to location
*/
Expand Down Expand Up @@ -241,14 +221,10 @@ export const getTimeStamp = function() {
* Retrieves list of swagger files to be processed for linting
* @returns {Array} list of files to be processed for linting
*/
export const getConfigFilesChangedInPR = function() {
if (prOnly === 'true') {
let targetBranch, cmd, filesChanged;
export const getConfigFilesChangedInPR = async (pr: devOps.PullRequestProperties | undefined) => {
if (pr !== undefined) {
try {
targetBranch = getTargetBranch();
execSync(`git fetch origin ${targetBranch}`);
cmd = `git diff --name-only HEAD $(git merge-base HEAD FETCH_HEAD)`;
filesChanged = execSync(cmd, { encoding: 'utf8' }).split('\n');
let filesChanged = (await pr.diff()).map(file => file.path)
console.log('>>>>> Files changed in this PR are as follows:');
console.log(filesChanged);

Expand Down Expand Up @@ -286,18 +262,14 @@ export const getConfigFilesChangedInPR = function() {
* Retrieves list of swagger files to be processed for linting
* @returns {Array} list of files to be processed for linting
*/
export const getFilesChangedInPR = function() {
export const getFilesChangedInPR = async (pr: devOps.PullRequestProperties | undefined) => {
let result = swaggers;
if (prOnly === 'true') {
let targetBranch, cmd, filesChanged, swaggerFilesInPR;
if (pr !== undefined) {
try {
targetBranch = getTargetBranch();
execSync(`git fetch origin ${targetBranch}`);
cmd = `git diff --name-only HEAD $(git merge-base HEAD FETCH_HEAD)`;
filesChanged = execSync(cmd, { encoding: 'utf8' });
let filesChanged = (await pr.diff()).map(file => file.path)
console.log('>>>>> Files changed in this PR are as follows:')
console.log(filesChanged);
swaggerFilesInPR = filesChanged.split('\n').filter(function (item: string) {
let swaggerFilesInPR = filesChanged.filter(function (item: string) {
if (item.match(/.*(json|yaml)$/ig) == null || item.match(/.*specification.*/ig) == null) {
return false;
}
Expand Down