Skip to content

Commit df6be11

Browse files
NoriZCwyunchi-ms
andauthored
[Pipeline] Added a step in StaticAnalysis to verify the generated Sdk integrity (#22096)
* Gen code verify (#22080) --------- Co-authored-by: wyunchi-ms <[email protected]>
1 parent 0454249 commit df6be11

File tree

9 files changed

+245
-1
lines changed

9 files changed

+245
-1
lines changed

.ci-config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
{
22
"rules": [
3+
{
4+
"patterns": [
5+
"src/{ModuleName}/{ModuleName}.Management.Sdk/Generated/*",
6+
"src/{ModuleName}/{ModuleName}.LegacySdk/Generated/*",
7+
"src/{ModuleName}/{ModuleName}.Sdk/Generated/*"
8+
],
9+
"phases": [
10+
"verify-gensdk:module"
11+
]
12+
},
313
{
414
"patterns": [
515
"src/{ModuleName}/readme.md",

tools/BuildPackagesTask/Microsoft.Azure.Build.Tasks/CIFilterTask.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ public class CIFilterTask : Task
7070

7171
private const string BUILD_PHASE = "build";
7272
private const string TEST_PHASE = "test";
73-
private readonly List<string> ANALYSIS_PHASE_LIST = new List<string>() { "breaking-change", "help-example", "help", "dependency", "signature", "file-change", "ux" };
73+
private readonly List<string> ANALYSIS_PHASE_LIST = new List<string>() { "breaking-change", "help-example", "help", "dependency", "signature", "file-change", "ux", "verify-gensdk" };
7474
private readonly List<string> ONLY_AFFECT_MODULE_PHASE_LIST = new List<string>() { "cmdlet-diff" }; // These phases will be triggered only when the module is modified, not when its dependent module is updated.
7575
private const string ACCOUNT_MODULE_NAME = "Accounts";
7676

tools/ExecuteCIStep.ps1

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ Param(
6262
[Switch]
6363
$StaticAnalysisCmdletDiff,
6464

65+
[Switch]
66+
$StaticAnalysisVerifyGensdk,
67+
6568
[String]
6669
$RepoArtifacts='artifacts',
6770

@@ -397,6 +400,7 @@ If ($StaticAnalysis)
397400
.("$PSScriptRoot/ExecuteCIStep.ps1") -StaticAnalysisHelp @Parameters
398401
.("$PSScriptRoot/ExecuteCIStep.ps1") -StaticAnalysisUX @Parameters
399402
.("$PSScriptRoot/ExecuteCIStep.ps1") -StaticAnalysisCmdletDiff @Parameters
403+
.("$PSScriptRoot/ExecuteCIStep.ps1") -StaticAnalysisVerifyGensdk @Parameters
400404
Return
401405
}
402406

@@ -507,3 +511,22 @@ If ($StaticAnalysisCmdletDiff)
507511
}
508512
Return
509513
}
514+
515+
If ($StaticAnalysisVerifyGensdk)
516+
{
517+
If ($PSBoundParameters.ContainsKey("TargetModule"))
518+
{
519+
$VerifyGenSdkModuleList = $TargetModule
520+
}
521+
Else
522+
{
523+
$VerifyGenSdkModuleList = Join-String -Separator ';' -InputObject $CIPlan.'verify-gensdk'
524+
}
525+
If ("" -Ne $VerifyGenSdkModuleList)
526+
{
527+
Write-Host "Running static analysis to verify generated sdk..."
528+
$result = .($PSScriptRoot + "/StaticAnalysis/GeneratedSdkAnalyzer/SDKGeneratedCodeVerify.ps1")
529+
Write-Host "Static analysis to verify generated sdk result: $result"
530+
}
531+
Return
532+
}

tools/PipelineResultTemplate.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@
4747
"Details": [
4848
]
4949
},
50+
"verify-gensdk": {
51+
"PhaseName": "Verify Generated Sdk",
52+
"Order": 9,
53+
"Details": [
54+
]
55+
},
5056
"test": {
5157
"PhaseName": "Test",
5258
"Order": 100,

tools/StaticAnalysis/CollectStaticAnalysisPipelineResult.ps1

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ $Steps = @(
7171
@{
7272
PhaseName = "ux"
7373
IssuePath = "$StaticAnalysisOutputDirectory/UXMetadataIssues.csv"
74+
},
75+
@{
76+
PhaseName = "verify-gensdk"
77+
IssuePath = "$StaticAnalysisOutputDirectory/VerifyGensdkIssues.csv"
7478
}
7579
)
7680

@@ -147,6 +151,9 @@ ForEach ($Step In $Steps) {
147151
ElseIf ($PhaseName -Eq "ux") {
148152
$Content = "|Type|Module|ResourceType|SubResourceType|Command|Description|`n|---|---|---|---|---|---|`n"
149153
}
154+
ElseIf ($PhaseName -Eq "verify-gensdk") {
155+
$Content = "|Type|Module|Sdk|Description|Remediation|`n|---|---|---|---|---|`n"
156+
}
150157
#EndRegion
151158

152159
ForEach ($Issue In $MatchedIssues) {
@@ -166,6 +173,9 @@ ForEach ($Step In $Steps) {
166173
ElseIf ($PhaseName -Eq "ux") {
167174
$Content += "|$ErrorTypeEmoji|$($Issue.Module)|$($Issue.ResourceType)|$($Issue.SubResourceType)|$($Issue.Command)|$($Issue.Description)|`n"
168175
}
176+
ElseIf ($PhaseName -Eq "verify-gensdk") {
177+
$Content += "|$ErrorTypeEmoji|$($Issue.Module)|$($Issue.Sdk)|$($Issue.Description)|$($Issue.Remediation)|`n"
178+
}
169179
#EndRegion
170180
}
171181
$ModuleInfo.Content = $Content
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
# ----------------------------------------------------------------------------------
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
# Code generated by Microsoft (R) AutoRest Code Generator.Changes may cause incorrect behavior and will be lost If the code
13+
# is regenerated.
14+
# ----------------------------------------------------------------------------------
15+
16+
$ArtifactsFolder = "$PSScriptRoot/../../../artifacts"
17+
$FilesChangedPaths = "$ArtifactsFolder/FilesChanged.txt"
18+
$ExceptionFilePath = "$ArtifactsFolder/StaticAnalysisResults/VerifyGenSdkIssues.csv"
19+
20+
Class GeneratedSdkIssue {
21+
[String]$Module
22+
[String]$Sdk
23+
[Int]$Severity
24+
[Int]$ProblemId
25+
[String]$Description
26+
[String]$Remediation
27+
}
28+
29+
$ExceptionList = @()
30+
$SavePath = $PWD
31+
32+
$MissReadMe = 9000
33+
$GenSdkChanged = 9090
34+
try{
35+
if ((Test-Path $FilesChangedPaths -PathType Leaf) -and $FilesChangedPaths.EndsWith(".txt")) {
36+
# Read Changedfiles and check if generted sdk code is updated.
37+
$FilesChanged = Get-Content $FilesChangedPaths | Where-Object { ($_ -match "^src\/.*\.Sdk\/.*Generated.*")}
38+
# Collect Sdk paths whose files under Generated folder change.
39+
$ChangedSdks = New-Object System.Collections.Generic.List[System.Object]
40+
foreach ($_ in $FilesChanged) {
41+
$ChangedSdks.Add($_.Substring(0,$_.IndexOf('.Sdk'))+'.Sdk')
42+
}
43+
# Remove duplicated Sdks.
44+
$ChangedSdks = $ChangedSdks | select -unique
45+
}
46+
else {
47+
Write-Warning "Only accept .txt files as input."
48+
return
49+
}
50+
Write-Host "Preparing Autorest..."
51+
npm install -g autorest@latest
52+
autorest --reset
53+
foreach ($_ in $ChangedSdks) {
54+
# Extract Module Name
55+
$ModuleName = ($_ -split "\/|\\")[1]
56+
# Direct to the Sdk directory
57+
Write-Host "Directing to " "$PSScriptRoot/../../../$_"
58+
cd "$PSScriptRoot/../../../$_"
59+
60+
# Regenerate the Sdk under Generated folder
61+
if( Test-Path -Path "README.md" -PathType Leaf){
62+
Write-Host "Re-generating SDK under Generated folder for $ModuleName..."
63+
autorest --use:@microsoft.azure/autorest.csharp@2.3.90
64+
autorest README.md --version=v2
65+
}
66+
else {
67+
$ExceptionList += [GeneratedSdkIssue]@{
68+
Module = $ModuleName;
69+
Sdk = $_;
70+
Severity = 1;
71+
ProblemId = $MissReadMe;
72+
Description = "No README file detected under $_."
73+
Remediation = "Make sure that the ReadMe file of Sdk is loaded."
74+
}
75+
}
76+
# See if the code is completely the same as we generated
77+
$changes = git status ".\Generated" --porcelain
78+
if ($changes -ne $null){
79+
$changes = $changes.replace(" ", "`n")
80+
$ExceptionList += [GeneratedSdkIssue]@{
81+
Module = $ModuleName;
82+
Sdk = $_;
83+
Severity = 1;
84+
ProblemId = $GenSdkChanged
85+
Description = "Generated code for $ModuleName is not up to date or you have updated generated Sdk."
86+
Remediation = "You may need to rebase on the latest main, regenerate code accroding to README.md file under $_, and make sure no more updates based on generated files."
87+
}
88+
}
89+
Set-Location $SavePath
90+
}
91+
}
92+
catch{
93+
Write-Host "Caught an error."
94+
}
95+
finally {
96+
Write-Host ""
97+
Write-Host "Summary:"
98+
Write-Host ""
99+
Write-Host " $($ExceptionList.Length) error(s) detected while verifying generated sdk:"
100+
Write-Host ""
101+
102+
# foreach ($err in $ExceptionList) {
103+
# Write-Host "error : " $err.Description "`n " $err.Remediation
104+
# }
105+
106+
if ($ExceptionList.Length -ne 0) {
107+
$ExceptionList | Sort-Object -Unique -Property Module,Sdk,Description | Export-Csv $ExceptionFilePath -NoTypeInformation
108+
}
109+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// ----------------------------------------------------------------------------------
2+
//
3+
// Copyright Microsoft Corporation
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
// ----------------------------------------------------------------------------------
14+
15+
using System;
16+
using System.Text.RegularExpressions;
17+
using Tools.Common.Issues;
18+
19+
namespace StaticAnalysis.GeneratedSdkAnalyzer
20+
{
21+
public class VerifyGenSdkIssue : IReportRecord
22+
{
23+
/// <summary>
24+
/// The module containing the generated sdk issue
25+
/// </summary>
26+
public string Module { get; set; }
27+
/// <summary>
28+
/// The associated sdk of the module.
29+
/// </summary>
30+
public string Sdk { get; set; }
31+
public int ProblemId { get; set; }
32+
public string Description { get; set; }
33+
public string Remediation { get; set; }
34+
public int Severity { get; set; }
35+
public string PrintHeaders()
36+
{
37+
return "\"Module\",\"Sdk\",\"Severity\",\"ProblemId\",\"Description\",\"Remediation\"";
38+
}
39+
40+
public string FormatRecord()
41+
{
42+
return string.Format("\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\"",
43+
Module, Sdk, Severity, ProblemId, Description, Remediation);
44+
}
45+
46+
public bool Match(IReportRecord other)
47+
{
48+
var result = false;
49+
var record = other as VerifyGenSdkIssue;
50+
if (record != null)
51+
{
52+
result = string.Equals(record.Module, Module, StringComparison.OrdinalIgnoreCase) &&
53+
string.Equals(record.Sdk, Sdk, StringComparison.OrdinalIgnoreCase) &&
54+
string.Equals(record.Description, Description, StringComparison.OrdinalIgnoreCase)&&
55+
record.ProblemId == ProblemId;
56+
}
57+
58+
return result;
59+
}
60+
61+
public IReportRecord Parse(string line)
62+
{
63+
var matcher = "\"([^\"]*)\",\"([^\"]*)\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]*)\",\"([^\"]*)\"";
64+
var match = Regex.Match(line, matcher);
65+
if (!match.Success || match.Groups.Count < 7)
66+
{
67+
throw new InvalidOperationException(string.Format("Could not parse '{0}' as VerifyGenSdkIssue record", line));
68+
}
69+
70+
Module = match.Groups[1].Value;
71+
Sdk = match.Groups[2].Value;
72+
Severity = int.Parse(match.Groups[3].Value);
73+
ProblemId = int.Parse(match.Groups[4].Value);
74+
Description = match.Groups[5].Value;
75+
Remediation = match.Groups[6].Value;
76+
return this;
77+
}
78+
}
79+
}

tools/StaticAnalysis/IssueChecker/IssueChecker.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
using StaticAnalysis.SignatureVerifier;
2828
using StaticAnalysis.ExampleAnalyzer;
2929
using StaticAnalysis.UXMetadataAnalyzer;
30+
using StaticAnalysis.GeneratedSdkAnalyzer;
3031

3132
namespace StaticAnalysis.IssueChecker
3233
{
@@ -43,6 +44,7 @@ public class IssueChecker : IStaticAnalyzer
4344
("SignatureIssues.csv", typeof(SignatureIssue).FullName),
4445
("ExampleIssues.csv", typeof(ExampleIssue).FullName),
4546
("UXMetadataIssues.csv", typeof(UXMetadataIssue).FullName),
47+
("VerifyGenSdkIssues.csv", typeof(VerifyGenSdkIssue).FullName)
4648
};
4749
public AnalysisLogger Logger { get; set; }
4850

tools/StaticAnalysis/ReportRecordFactory.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
using StaticAnalysis.SignatureVerifier;
2020
using StaticAnalysis.ExampleAnalyzer;
2121
using StaticAnalysis.UXMetadataAnalyzer;
22+
using StaticAnalysis.GeneratedSdkAnalyzer;
2223

2324
using System;
2425
using System.Collections.Generic;
@@ -68,6 +69,10 @@ public static IReportRecord Create(string type)
6869
{
6970
return new UXMetadataIssue();
7071
}
72+
if (type.Equals(typeof(VerifyGenSdkIssue).FullName))
73+
{
74+
return new VerifyGenSdkIssue();
75+
}
7176

7277
return null;
7378
}

0 commit comments

Comments
 (0)