Skip to content
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

Fix #8305: failed to generate the changelog between HLC and Modular #8332

Merged
merged 14 commits into from
May 28, 2024
409 changes: 382 additions & 27 deletions tools/js-sdk-release-tools/package-lock.json

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion tools/js-sdk-release-tools/package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"name": "@azure-tools/js-sdk-release-tools",
"version": "2.7.9",
"version": "2.7.10",
"description": "",
"scripts": {
"dev": "nodemon src/changelogToolCli.ts",
"start": "node dist/changelogToolCli.js",
"debug": "node --inspect-brk dist/changelogToolCli.js",
"build": "rimraf dist && tsc -p .",
Expand Down Expand Up @@ -33,7 +34,11 @@
"yaml": "^1.10.2"
},
"devDependencies": {
"@types/node": "^20.12.12",
"@types/shelljs": "^0.8.15",
"nodemon": "^3.1.0",
"rimraf": "^3.0.2",
"ts-node": "^10.9.2",
"typescript": "^3.9.7"
}
}
5 changes: 5 additions & 0 deletions tools/js-sdk-release-tools/src/common/interfaces.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { ApiVersionType } from "./types";

export interface IApiVersionTypeExtractor {
(packageRoot: string): ApiVersionType;
}
11 changes: 11 additions & 0 deletions tools/js-sdk-release-tools/src/common/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export enum SDKType {
HLC = 'HLC',
RLC = 'RLC',
MLC = 'MLC',
qiaozha marked this conversation as resolved.
Show resolved Hide resolved
};

export enum ApiVersionType {
None = 'None',
Stable = 'Stable',
Preview = 'Preview',
}
15 changes: 15 additions & 0 deletions tools/js-sdk-release-tools/src/common/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import shell from 'shelljs';
import path from 'path';
import { SDKType } from './types'

export function getClassicClientParametersPath(packageRoot: string): string {
return path.join(packageRoot, 'src', 'models', 'parameters.ts');
}

export function getSDKType(packageRoot: string): SDKType {
const paraPath = getClassicClientParametersPath(packageRoot);
const exist = shell.test('-e', paraPath);
const type = exist ? SDKType.HLC : SDKType.MLC;
console.log(`SDK type: ${type} detected`);
return type;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Project, ScriptTarget } from "ts-morph";

import { ApiVersionType } from "../../common/types"
import { IApiVersionTypeExtractor } from "../../common/interfaces";
import { getClassicClientParametersPath } from "../../common/utils";

// TODO: add unit test
export const getApiVersionType: IApiVersionTypeExtractor = (packageRoot: string): ApiVersionType => {
const project = new Project({
compilerOptions: {
target: ScriptTarget.ES2015,
},
});
const paraPath = getClassicClientParametersPath(packageRoot);
project.addSourceFileAtPath(paraPath);
const source = project.getSourceFile(paraPath);
const variableDeclarations = source?.getVariableDeclarations();
if (!variableDeclarations) return ApiVersionType.Stable;
for (const variableDeclaration of variableDeclarations) {
const fullText = variableDeclaration.getFullText();
if (fullText.toLowerCase().includes('apiversion')) {
const match = fullText.match(/defaultValue: "([0-9a-z-]+)"/);
if (!match || match.length !== 2) {
continue;
}
if (match[1].includes('preview')) {
return ApiVersionType.Preview;
}
}
}
return ApiVersionType.Stable;
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import fs from 'fs';
import path from 'path';
import shell from 'shelljs';

import {extractExportAndGenerateChangelog, readSourceAndExtractMetaData} from "../../changelog/extractMetaData";
import {Changelog, changelogGenerator} from "../../changelog/changelogGenerator";
import {NPMScope, NPMViewResult} from "@ts-common/azure-js-dev-tools";
Expand All @@ -14,19 +18,19 @@ import {
getVersion,
isBetaVersion
} from "../../utils/version";
import {isGeneratedCodeStable} from "./isGeneratedCodeStable";
import {execSync} from "child_process";
import { getversionDate } from "../../utils/version";

const fs = require('fs');
const path = require('path');
import { ApiVersionType } from "../../common/types"
import { getApiVersionType } from '../../xlc/apiVersion/apiVersionTypeExtractor'

export async function generateChangelogAndBumpVersion(packageFolderPath: string) {
const shell = require('shelljs');
const jsSdkRepoPath = String(shell.pwd());
packageFolderPath = path.join(jsSdkRepoPath, packageFolderPath);
const isStableRelease = isGeneratedCodeStable(path.join(packageFolderPath, 'src', 'models', 'parameters.ts'));
const packageName = JSON.parse(fs.readFileSync(path.join(packageFolderPath, 'package.json'), {encoding: 'utf-8'})).name;
const packageJsonPath = path.join(packageFolderPath, 'package.json');
const ApiType = getApiVersionType(packageFolderPath);
const isStableRelease = ApiType != ApiVersionType.Preview;
const packageJson = fs.readFileSync(packageJsonPath, { encoding: 'utf-8' });
const packageName = JSON.parse(packageJson).name;
const npm = new NPMScope({ executionFolderPath: packageFolderPath });
const npmViewResult: NPMViewResult = await npm.view({ packageName });
const stableVersion = getVersion(npmViewResult,"latest");
Expand All @@ -45,11 +49,12 @@ export async function generateChangelogAndBumpVersion(packageFolderPath: string)
const usedVersions = npmViewResult['versions'];
// in our rule, we always compare to stableVersion. But here wo should pay attention to the some stableVersion which contains beta, which means the package has not been GA.
try {
await shell.mkdir(path.join(packageFolderPath, 'changelog-temp'));
await shell.cd(path.join(packageFolderPath, 'changelog-temp'));
await shell.exec(`npm pack ${packageName}@${stableVersion}`);
await shell.exec('tar -xzf *.tgz');
await shell.cd(packageFolderPath);
shell.mkdir(path.join(packageFolderPath, 'changelog-temp'));
shell.cd(path.join(packageFolderPath, 'changelog-temp'));
shell.exec(`npm pack ${packageName}@${stableVersion}`);
const files = shell.ls('*.tgz');
shell.exec(`tar -xzf ${files[0]}`);
shell.cd(packageFolderPath);

// only track2 sdk includes sdk-type with value mgmt
const sdkType = JSON.parse(fs.readFileSync(path.join(packageFolderPath, 'changelog-temp', 'package', 'package.json'), {encoding: 'utf-8'}))['sdk-type'];
Expand All @@ -62,12 +67,13 @@ export async function generateChangelogAndBumpVersion(packageFolderPath: string)
const changelog: Changelog = await extractExportAndGenerateChangelog(apiMdFileNPM, apiMdFileLocal);
let originalChangeLogContent = fs.readFileSync(path.join(packageFolderPath, 'changelog-temp', 'package', 'CHANGELOG.md'), {encoding: 'utf-8'});
if(nextVersion){
await shell.cd(path.join(packageFolderPath, 'changelog-temp'));
await shell.mkdir(path.join(packageFolderPath, 'changelog-temp', 'next'));
await shell.cd(path.join(packageFolderPath,'changelog-temp', 'next'));
await shell.exec(`npm pack ${packageName}@${nextVersion}`);
await shell.exec('tar -xzf *.tgz');
await shell.cd(packageFolderPath);
shell.cd(path.join(packageFolderPath, 'changelog-temp'));
shell.mkdir(path.join(packageFolderPath, 'changelog-temp', 'next'));
shell.cd(path.join(packageFolderPath,'changelog-temp', 'next'));
shell.exec(`npm pack ${packageName}@${nextVersion}`);
const files = shell.ls('*.tgz');
shell.exec(`tar -xzf ${files[0]}`);
shell.cd(packageFolderPath);
logger.log("Create next folder successfully")

const latestDate = getversionDate(npmViewResult, stableVersion);
Expand Down Expand Up @@ -106,8 +112,8 @@ export async function generateChangelogAndBumpVersion(packageFolderPath: string)
logger.log('Generate changelogs and setting version for migrating track1 to track2 successfully');
}
} finally {
await shell.exec(`rm -r ${path.join(packageFolderPath, 'changelog-temp')}`);
await shell.cd(jsSdkRepoPath);
shell.rm('-r', `${path.join(packageFolderPath, 'changelog-temp')}`);
shell.cd(jsSdkRepoPath);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Project, ScriptTarget, SyntaxKind } from "ts-morph";
import shell from 'shelljs';
import path from 'path';

import { ApiVersionType } from "../../common/types"
import { IApiVersionTypeExtractor } from "../../common/interfaces";

const findRestClientPath = (packageRoot: string): string => {
const restPath = path.join(packageRoot, 'src/rest/');
const fileNames = shell.ls(restPath);
const clientFiles = fileNames.filter(f => f.endsWith("Client.ts"));
if (clientFiles.length !== 1) throw new Error(`Single client is supported, but found ${clientFiles}`);

const clientPath = path.join(restPath, clientFiles[0]);
return clientPath;
};

const matchPattern = (text: string, pattern: RegExp): string | undefined => {
const match = text.match(pattern);
const found = match != null && match.length === 2;
return found ? match?.at(1) : undefined;
}

const findApiVersionInRestClient = (clientPath: string): string | undefined => {
const target = ScriptTarget.ES2015;
const compilerOptions = { target };
const project = new Project({ compilerOptions });
project.addSourceFileAtPath(clientPath);
const sourceFile = project.getSourceFile(clientPath);

const createClientFunction = sourceFile?.getFunction("createClient");
if (!createClientFunction) throw new Error("Function 'createClient' not found.");

const apiVersionStatements = createClientFunction.getStatements()
.filter(s =>
s.getKind() === SyntaxKind.ExpressionStatement &&
s.getText().indexOf("options.apiVersion") > -1);
if (apiVersionStatements.length === 0) return undefined;

const text = apiVersionStatements[apiVersionStatements.length - 1].getText();
const pattern = /(\d{4}-\d{2}-\d{2}(?:-preview)?)/;
const apiVersion = matchPattern(text, pattern);
return apiVersion;
};

const getApiVersionTypeFromRestClient: IApiVersionTypeExtractor = (packageRoot: string): ApiVersionType => {
const clientPath = findRestClientPath(packageRoot);
const apiVersion = findApiVersionInRestClient(clientPath);
if (apiVersion && apiVersion.indexOf("-preview") >= 0) return ApiVersionType.Preview;
if (apiVersion && apiVersion.indexOf("-preview") < 0) return ApiVersionType.Stable;
return ApiVersionType.None;
};

// TODO: not implemented: need a example
wanlwanl marked this conversation as resolved.
Show resolved Hide resolved
const getApiVersionTypeFromOperations: IApiVersionTypeExtractor = (packageRoot: string): ApiVersionType => {
console.log('findApiVersionFromOperations')
const paraPath = path.join(packageRoot, 'src/rest/parameters.ts');
return ApiVersionType.Stable;
};

// TODO: add unit test
export const getApiVersionType: IApiVersionTypeExtractor = (packageRoot: string): ApiVersionType => {
const typeFromClient = getApiVersionTypeFromRestClient(packageRoot);
if (typeFromClient !== ApiVersionType.None) return typeFromClient;
const typeFromOperations = getApiVersionTypeFromOperations(packageRoot);
if (typeFromOperations !== ApiVersionType.None) return typeFromOperations;
return ApiVersionType.Stable;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { getSDKType } from "../../common/utils";
import { ApiVersionType, SDKType } from "../../common/types";
import { IApiVersionTypeExtractor } from "../../common/interfaces";
import * as mlcApi from '../../mlc/apiVersion/apiVersionTypeExtractor'
import * as hlcApi from '../../hlc/apiVersion/apiVersionTypeExtractor'

// TODO: move to x-level-client folder
export const getApiVersionType: IApiVersionTypeExtractor = (packageRoot: string): ApiVersionType => {
const sdkType = getSDKType(packageRoot);
switch (sdkType) {
case SDKType.MLC:
return mlcApi.getApiVersionType(packageRoot);
case SDKType.HLC:
return hlcApi.getApiVersionType(packageRoot);
default:
console.warn(`Unsupported SDK type ${sdkType} to get detact api version`);
return ApiVersionType.None;
}
}