-
Notifications
You must be signed in to change notification settings - Fork 227
Fix #8305: failed to generate the changelog between HLC and Modular #8332
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
Changes from 12 commits
8925081
af6769f
2f37ada
2b44dbf
4ec8a30
3109bac
44a4abf
2816a82
6bfe0bf
cf87f03
cba9564
9e5c96b
32aa425
8d8a445
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| import { ApiVersionType } from "./types"; | ||
|
|
||
| export interface IApiVersionTypeExtractor { | ||
| (packageRoot: string): ApiVersionType; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| export enum SDKType { | ||
| HLC = 'HLC', | ||
| RLC = 'RLC', | ||
| MLC = 'MLC', | ||
| }; | ||
|
|
||
| export enum ApiVersionType { | ||
| None = 'None', | ||
| Stable = 'Stable', | ||
| Preview = 'Preview', | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| import shell from 'shelljs'; | ||
| import path from 'path'; | ||
| import fs from 'fs'; | ||
|
|
||
| import { SDKType } from './types' | ||
| import { logger } from "../utils/logger"; | ||
| import { Project, ScriptTarget, SourceFile } from 'ts-morph'; | ||
|
|
||
| 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; | ||
| logger.logInfo(`SDK type: ${type} detected in ${packageRoot}`); | ||
| return type; | ||
| } | ||
|
|
||
| export function getApiReviewPath(packageRoot: string): string { | ||
| const sdkType = getSDKType(packageRoot); | ||
| const reviewDir = path.join(packageRoot, 'review'); | ||
| switch (sdkType) { | ||
| case SDKType.MLC: | ||
| const fileNames = fs.readdirSync(reviewDir).sort(); | ||
| if (fileNames.length === 0) { | ||
| logger.logError(`Expects 1 API report, but find nothing.`); | ||
| process.exit(1); | ||
| } | ||
| // TODO: use a more concrete rule to find xxx.api.md | ||
| return path.join(packageRoot, 'review', fileNames[fileNames.length - 1]); | ||
|
||
| case SDKType.HLC: | ||
| case SDKType.RLC: | ||
| default: | ||
| // only one xxx.api.md | ||
| return path.join(packageRoot, 'review', fs.readdirSync(reviewDir)[0]); | ||
| } | ||
| } | ||
|
|
||
| export function getTsSourceFile(filePath: string): SourceFile | undefined { | ||
| const target = ScriptTarget.ES2015; | ||
| const compilerOptions = { target }; | ||
| const project = new Project({ compilerOptions }); | ||
| project.addSourceFileAtPath(filePath); | ||
| return project.getSourceFile(filePath); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { ApiVersionType } from "../../common/types" | ||
| import { IApiVersionTypeExtractor } from "../../common/interfaces"; | ||
| import { getClassicClientParametersPath, getTsSourceFile } from "../../common/utils"; | ||
|
|
||
| // TODO: add unit test | ||
| export const getApiVersionType: IApiVersionTypeExtractor = (packageRoot: string): ApiVersionType => { | ||
| const paraPath = getClassicClientParametersPath(packageRoot); | ||
| const source = getTsSourceFile(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; | ||
| } |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import {NPMScope} from "@ts-common/azure-js-dev-tools"; | |
| import {logger} from "../../utils/logger"; | ||
| import {getLatestStableVersion} from "../../utils/version"; | ||
| import {extractExportAndGenerateChangelog} from "../../changelog/extractMetaData"; | ||
| import { getApiReviewPath } from "../../common/utils"; | ||
|
|
||
| const shell = require('shelljs'); | ||
| const todayDate = new Date(); | ||
|
|
@@ -60,8 +61,9 @@ export async function generateChangelog(packagePath) { | |
| logger.logWarn("The latest package released in NPM doesn't contain review folder, so generate changelog same as first release"); | ||
| generateChangelogForFirstRelease(packagePath, version); | ||
| } else { | ||
| let apiMdFileNPM = path.join(tempReviewFolder, fs.readdirSync(tempReviewFolder)[0]); | ||
| let apiMdFileLocal = path.join(packagePath, 'review', fs.readdirSync(path.join(packagePath, 'review'))[0]); | ||
| const npmPackageRoot = path.join(packagePath, 'changelog-temp', 'package'); | ||
| const apiMdFileNPM = getApiReviewPath(npmPackageRoot); | ||
| const apiMdFileLocal = getApiReviewPath(packagePath); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. maybe not a high priority for now but should we error out if it's comparing between RLC and HLC? or Modular api layer and HLC ?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like all sdk type should error out in a general way. plan to start a new pr to do it
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. leave a TODO in code |
||
| const changelog = await extractExportAndGenerateChangelog(apiMdFileNPM, apiMdFileLocal); | ||
| if (!changelog.hasBreakingChange && !changelog.hasFeature) { | ||
| logger.logError('Cannot generate changelog because the codes of local and npm may be the same.'); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| import { SourceFile, SyntaxKind } from "ts-morph"; | ||
| import shell from 'shelljs'; | ||
| import path from 'path'; | ||
|
|
||
| import { ApiVersionType } from "../../common/types" | ||
| import { IApiVersionTypeExtractor } from "../../common/interfaces"; | ||
| import { getTsSourceFile } from "../../common/utils"; | ||
|
|
||
| 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 sourceFile = getTsSourceFile(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; | ||
| }; | ||
|
|
||
| const findApiVersionsInOperations = (sourceFile: SourceFile | undefined): Array<string> | undefined => { | ||
| const interfaces = sourceFile?.getInterfaces(); | ||
| const interfacesWithApiVersion = interfaces?.filter(itf => itf.getProperty('"api-version"')); | ||
| const apiVersions = interfacesWithApiVersion?.map(itf => { | ||
| const property = itf.getMembers() | ||
| .filter(m => { | ||
| const defaultValue = m.getChildrenOfKind(SyntaxKind.StringLiteral)[0]; | ||
| return defaultValue && defaultValue.getText() === '"api-version"'; | ||
| })[0]; | ||
| const apiVersion = property.getChildrenOfKind(SyntaxKind.LiteralType)[0].getText(); | ||
| return apiVersion; | ||
| }); | ||
| return apiVersions; | ||
| } | ||
|
|
||
| const getApiVersionTypeFromOperations: IApiVersionTypeExtractor = (packageRoot: string): ApiVersionType => { | ||
| const paraPath = path.join(packageRoot, 'src/rest/parameters.ts'); | ||
| const sourceFile = getTsSourceFile(paraPath); | ||
| const apiVersions = findApiVersionsInOperations(sourceFile); | ||
| if (!apiVersions) return ApiVersionType.None; | ||
| const previewVersions = apiVersions.filter(v => v.indexOf("-preview") >= 0); | ||
| return previewVersions.length > 0 ? ApiVersionType.Preview : 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; | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.