Skip to content

Commit

Permalink
Merge branch 'main' into anksaha/movingPrivatespecs
Browse files Browse the repository at this point in the history
  • Loading branch information
anksaha09 authored Feb 26, 2025
2 parents 6a1aa9c + 5eebf11 commit 3136032
Show file tree
Hide file tree
Showing 10 changed files with 616 additions and 34 deletions.
10 changes: 6 additions & 4 deletions eng/pipelines/templates/stages/archetype-spec-gen-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ parameters:
extends:
template: /eng/pipelines/templates/stages/1es-redirect.yml
parameters:
Use1ESOfficial: false
stages:
- stage: Build
displayName: 'SDK Generation'
jobs:
- job:
timeoutInMinutes: 2400

variables:
- template: /eng/pipelines/templates/variables/image.yml
Expand All @@ -56,12 +58,12 @@ extends:
displayName: Publish SDK artifacts to Pipeline Artifacts
condition: and(ne(variables['ValidationResult'], ''), eq(variables['HasSDKArtifact'], 'true'))
artifactName: $(sdkArtifactName)
targetPath: "$(System.DefaultWorkingDirectory)/out/generatedSdkArtifacts"
targetPath: "$(System.DefaultWorkingDirectory)/out/stagedArtifacts"
- output: pipelineArtifact
displayName: Publish API View artifacts to Pipeline Artifacts
condition: and(ne(variables['ValidationResult'], ''), eq(variables['HasApiViewArtifact'], 'true'))
artifactName: $(ArtifactName)
targetPath: "$(System.DefaultWorkingDirectory)/out/sdkApiViewArtifacts"
targetPath: "$(System.DefaultWorkingDirectory)/out/stagedArtifacts"
- output: pipelineArtifact
displayName: Publish logs to Pipeline Artifacts
condition: ne(variables['ValidationResult'], '')
Expand Down Expand Up @@ -168,7 +170,7 @@ extends:
optional_params=""
sdk_gen_info="sdk generation from Config : "
if [ "${{ parameters.ConfigType }}" = "TypeSpec" ]; then
if [ "$(Build.Reason)" != "PullRequest" ] && [ "${{ parameters.ConfigType }}" = "TypeSpec" ]; then
optional_params="$optional_params --tsp-config-relative-path ${{ parameters.ConfigPath }}"
sdk_gen_info="$sdk_gen_info '${{ parameters.ConfigPath }}',"
elif [ "${{ parameters.ConfigType }}" = "OpenAPI" ]; then
Expand All @@ -177,7 +179,7 @@ extends:
fi
if [ "$(Build.Reason)" = "PullRequest" ]; then
optional_params="$optional_params --pr-number=$(System.PullRequest.PullRequestNumber)"
optional_params="$optional_params --pr-number $(System.PullRequest.PullRequestNumber)"
specPrUrl="${{ parameters.SpecRepoUrl }}/pull/$(System.PullRequest.PullRequestNumber)"
sdk_gen_info="$sdk_gen_info spec PR: $specPrUrl"
fi
Expand Down
Empty file modified eng/tools/spec-gen-sdk-runner/cmd/spec-gen-sdk-runner.js
100644 → 100755
Empty file.
18 changes: 7 additions & 11 deletions eng/tools/spec-gen-sdk-runner/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,27 +68,23 @@ const config = tseslint.config(
"@typescript-eslint/restrict-template-expressions": "off",
"@typescript-eslint/no-unsafe-assignment": "off",
"@typescript-eslint/no-unsafe-argument": "off",
"@typescript-eslint/consistent-indexed-object-style": "off",
"@typescript-eslint/no-unnecessary-condition": "off",
"@typescript-eslint/consistent-type-definitions": "off",
"@typescript-eslint/no-inferrable-types": "off",
"@typescript-eslint/no-dynamic-delete": "off",

// We want more flexibility with file names.
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/filename-case.md
"unicorn/filename-case": "off",
"unicorn/prefer-ternary": "off",
"unicorn/no-useless-undefined": "off",
"unicorn/prevent-abbreviations": "off",

// We prefer to have explicitly import at the top of the file, even if the same element is exported again,
// which we do in index.ts files.
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prefer-export-from.md
"unicorn/prefer-export-from": ["error", { ignoreUsedVariables: true }],

// We allow some abbreviations that we like.
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/docs/rules/prevent-abbreviations.md
"unicorn/prevent-abbreviations": [
"error",
{
allowList: {
args: true,
},
},
],
},
}
);
Expand Down
184 changes: 184 additions & 0 deletions eng/tools/spec-gen-sdk-runner/src/change-files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import path from "node:path";
import {
getChangedFiles,
searchRelatedParentFolders,
searchSharedLibrary,
searchRelatedTypeSpecProjectBySharedLibrary,
groupPathsByService,
createCombinedSpecs,
type SpecResults,
type ChangedSpecs,
getLastPathSegment,
} from "./utils.js";
import { logMessage } from "./log.js";
import { SpecGenSdkCmdInput } from "./types.js";

const readmeMdRegex = /^readme.md$/i;
const typespecProjectRegex = /^tspconfig.yaml$/i;
const typespecProjectSharedLibraryRegex = /[^/]+\.Shared/;

export function detectChangedSpecConfigFiles(commandInput: SpecGenSdkCmdInput): ChangedSpecs[] {
const prChangedFiles: string[] = getChangedFiles(commandInput.localSpecRepoPath) ?? [];
if (prChangedFiles.length === 0) {
logMessage("No files changed in the PR");
}
logMessage(`Changed files in the PR: ${prChangedFiles.length}`);
for (const file of prChangedFiles) {
logMessage(`\t${file}`);
}
const fileList = prChangedFiles
.filter((p) => p.startsWith("specification/"))
.filter((p) => !p.includes("/scenarios/"));

logMessage(`Related readme.md and typespec project list:`);
const changedSpecs: ChangedSpecs[] = [];

const readmeMDResult = searchRelatedParentFolders(fileList, {
searchFileRegex: readmeMdRegex,
specRepoFolder: commandInput.localSpecRepoPath,
stopAtFolder: "specification",
});

const typespecProjectResult = searchRelatedParentFolders(fileList, {
searchFileRegex: typespecProjectRegex,
specRepoFolder: commandInput.localSpecRepoPath,
stopAtFolder: "specification",
});

const typespecProjectSharedLibraries = searchSharedLibrary(fileList, {
searchFileRegex: typespecProjectSharedLibraryRegex,
specRepoFolder: commandInput.localSpecRepoPath,
});

const typespecProjectResultSearchedBySharedLibrary = searchRelatedTypeSpecProjectBySharedLibrary(
typespecProjectSharedLibraries,
{
searchFileRegex: typespecProjectRegex,
specRepoFolder: commandInput.localSpecRepoPath,
},
);

// Merge typespec project results
for (const folderPath of Object.keys(typespecProjectResultSearchedBySharedLibrary)) {
if (typespecProjectResult[folderPath]) {
typespecProjectResult[folderPath] = [
...typespecProjectResult[folderPath],
...typespecProjectResultSearchedBySharedLibrary[folderPath],
];
} else {
typespecProjectResult[folderPath] = typespecProjectResultSearchedBySharedLibrary[folderPath];
}
}

// Group paths by service
const serviceMap = groupPathsByService(readmeMDResult, typespecProjectResult);

const results: SpecResults = { readmeMDResult, typespecProjectResult };

// Process each service
for (const [, info] of serviceMap) {
// Case: Resource Manager with .Management
if (info.managementPaths.length > 0) {
if (info.resourceManagerPaths.length === 1) {
// Single resource-manager path - match with all Management paths
const newSpecs = createCombinedSpecs(
info.resourceManagerPaths[0].path,
info.managementPaths,
results,
);
changedSpecs.push(...newSpecs);
logMessage(
`\t readme folders: ${info.resourceManagerPaths[0].path}, tspconfig folders: ${info.managementPaths}`,
);
for (const p of info.managementPaths) {
delete typespecProjectResult[p];
}
delete readmeMDResult[info.resourceManagerPaths[0].path];
} else {
// Multiple resource-manager paths - match by subfolder name
for (const rmPath of info.resourceManagerPaths) {
const matchingManagements = info.managementPaths.filter((mPath) => {
const rmSubPath = rmPath.subPath;
const managementName = getLastPathSegment(mPath).replace(".Management", "");
return rmSubPath && rmSubPath === managementName;
});
if (matchingManagements.length > 0) {
const newSpecs = createCombinedSpecs(rmPath.path, matchingManagements, results);
changedSpecs.push(...newSpecs);
logMessage(
`\t readme folders: ${rmPath.path}, tspconfig folders: ${matchingManagements}`,
);
for (const p of matchingManagements) {
delete typespecProjectResult[p];
}
delete readmeMDResult[rmPath.path];
}
}
}
}

// Case: Data Plane matching
if (info.dataPlanePaths.length > 0 && info.otherTypeSpecPaths.length > 0) {
if (info.dataPlanePaths.length === 1) {
// Single data-plane path - match with all non-Management TypeSpec paths
const newSpecs = createCombinedSpecs(
info.dataPlanePaths[0].path,
info.otherTypeSpecPaths,
results,
);
changedSpecs.push(...newSpecs);
logMessage(
`\t readme folders: ${info.dataPlanePaths[0].path}, tspconfig folders: ${info.otherTypeSpecPaths}`,
);
for (const p of info.otherTypeSpecPaths) {
delete typespecProjectResult[p];
}
delete readmeMDResult[info.dataPlanePaths[0].path];
} else {
// Multiple data-plane paths - match by subfolder name
for (const dpPath of info.dataPlanePaths) {
const matchingTypeSpecs = info.otherTypeSpecPaths.filter((tsPath) => {
const dpSubFolder = dpPath.subFolder;
const tsLastSegment = getLastPathSegment(tsPath);
return dpSubFolder && dpSubFolder === tsLastSegment;
});
if (matchingTypeSpecs.length > 0) {
const newSpecs = createCombinedSpecs(dpPath.path, matchingTypeSpecs, results);
changedSpecs.push(...newSpecs);
logMessage(
`\t readme folders: ${dpPath.path}, tspconfig folders: ${matchingTypeSpecs}`,
);
for (const p of matchingTypeSpecs) {
delete typespecProjectResult[p];
}
delete readmeMDResult[dpPath.path];
}
}
}
}
}

// Process remaining unmatched paths
for (const folderPath of new Set([
...Object.keys(readmeMDResult),
...Object.keys(typespecProjectResult),
])) {
const cs: ChangedSpecs = {
specs: [],
};

if (typespecProjectResult[folderPath]) {
cs.specs = typespecProjectResult[folderPath];
cs.typespecProject = path.join(folderPath, "tspconfig.yaml");
logMessage(`\t tspconfig: ${cs.typespecProject}`);
} else {
cs.readmeMd = path.join(folderPath, "readme.md");
cs.specs = readmeMDResult[folderPath];
logMessage(`\t readme: ${cs.readmeMd}`);
}

changedSpecs.push(cs);
}

return changedSpecs;
}
Loading

0 comments on commit 3136032

Please sign in to comment.