microsoft/typespec

Public

mirrored from https://github.com/microsoft/typespecAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
e9cd6e6fe2d3a69fbaa46dd2d9e3813aded89c8f

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

eng/emitters/scripts/Create-APIReview.ps1

338lines · modecode

1[CmdletBinding()]
2Param (
3 [Parameter(Mandatory=$True)]
4 [array] $ArtifactList,
5 [Parameter(Mandatory=$True)]
6 [string] $ArtifactPath,
7 [Parameter(Mandatory=$True)]
8 [string] $APIKey,
9 [string] $SourceBranch,
10 [string] $DefaultBranch,
11 [string] $RepoName,
12 [string] $BuildId,
13 [string] $PackageName = "",
14 [string] $ConfigFileDir = "",
15 [string] $APIViewUri = "https://apiview.dev/AutoReview",
16 [string] $ArtifactName = "packages",
17 [bool] $MarkPackageAsShipped = $false,
18 [string] $LanguageShortName = "Unknown"
19)
20
21Set-StrictMode -Version 3
22. (Join-Path $PSScriptRoot ApiView-Helpers.ps1)
23. (Join-Path $PSScriptRoot SemVer.ps1)
24
25if ($LanguageShortName -eq "Unknown")
26{
27 Write-Host "Language short name is not provided. Please provide the language short name."
28 exit 1
29}
30else
31{
32 $functionScriptPath = Join-Path $PSScriptRoot "/../../../packages/http-client-$LanguageShortName/eng/scripts/Functions.ps1"
33 if (!(Test-Path $functionScriptPath))
34 {
35 Write-Host "Functions script path $($functionScriptPath) is invalid."
36 exit 1
37 }
38 . ($functionScriptPath)
39}
40
41# Submit API review request and return status whether current revision is approved or pending or failed to create review
42function Upload-SourceArtifact($filePath, $apiLabel, $releaseStatus, $packageVersion)
43{
44 Write-Host "File path: $filePath"
45 $fileName = Split-Path -Leaf $filePath
46 Write-Host "File name: $fileName"
47 $multipartContent = [System.Net.Http.MultipartFormDataContent]::new()
48 $FileStream = [System.IO.FileStream]::new($filePath, [System.IO.FileMode]::Open)
49 $fileHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
50 $fileHeader.Name = "file"
51 $fileHeader.FileName = $fileName
52 $fileContent = [System.Net.Http.StreamContent]::new($FileStream)
53 $fileContent.Headers.ContentDisposition = $fileHeader
54 $fileContent.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse("application/octet-stream")
55 $multipartContent.Add($fileContent)
56
57
58 $stringHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
59 $stringHeader.Name = "label"
60 $StringContent = [System.Net.Http.StringContent]::new($apiLabel)
61 $StringContent.Headers.ContentDisposition = $stringHeader
62 $multipartContent.Add($stringContent)
63 Write-Host "Request param, label: $apiLabel"
64
65 $versionParam = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
66 $versionParam.Name = "packageVersion"
67 $versionContent = [System.Net.Http.StringContent]::new($packageVersion)
68 $versionContent.Headers.ContentDisposition = $versionParam
69 $multipartContent.Add($versionContent)
70 Write-Host "Request param, packageVersion: $packageVersion"
71
72 $releaseTagParam = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
73 $releaseTagParam.Name = "setReleaseTag"
74 $releaseTagParamContent = [System.Net.Http.StringContent]::new($MarkPackageAsShipped)
75 $releaseTagParamContent.Headers.ContentDisposition = $releaseTagParam
76 $multipartContent.Add($releaseTagParamContent)
77 Write-Host "Request param, setReleaseTag: $MarkPackageAsShipped"
78
79 if ($releaseStatus -and ($releaseStatus -ne "Unreleased"))
80 {
81 $compareAllParam = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")
82 $compareAllParam.Name = "compareAllRevisions"
83 $compareAllParamContent = [System.Net.Http.StringContent]::new($true)
84 $compareAllParamContent.Headers.ContentDisposition = $compareAllParam
85 $multipartContent.Add($compareAllParamContent)
86 Write-Host "Request param, compareAllRevisions: true"
87 }
88
89 $uri = "${APIViewUri}/UploadAutoReview"
90 $headers = @{
91 "ApiKey" = $apiKey;
92 "content-type" = "multipart/form-data"
93 }
94
95 try
96 {
97 $Response = Invoke-WebRequest -Method 'POST' -Uri $uri -Body $multipartContent -Headers $headers
98 Write-Host "API review: $($Response.Content)"
99 $StatusCode = $Response.StatusCode
100 }
101 catch
102 {
103 Write-Host "Exception details: $($_.Exception.Response)"
104 $StatusCode = $_.Exception.Response.StatusCode
105 }
106
107 return $StatusCode
108}
109
110function Upload-ReviewTokenFile($packageName, $apiLabel, $releaseStatus, $reviewFileName, $packageVersion, $filePath)
111{
112 Write-Host "Original File path: $filePath"
113 $fileName = Split-Path -Leaf $filePath
114 Write-Host "OriginalFile name: $fileName"
115
116 $params = "buildId=${BuildId}&artifactName=${ArtifactName}&originalFilePath=${fileName}&reviewFilePath=${reviewFileName}"
117 $params += "&label=${apiLabel}&repoName=${RepoName}&packageName=${packageName}&project=internal&packageVersion=${packageVersion}"
118 if($MarkPackageAsShipped) {
119 $params += "&setReleaseTag=true"
120 }
121 $uri = "${APIViewUri}/CreateApiReview?${params}"
122 if ($releaseStatus -and ($releaseStatus -ne "Unreleased"))
123 {
124 $uri += "&compareAllRevisions=true"
125 }
126
127 Write-Host "Request to APIView: $uri"
128 $headers = @{
129 "ApiKey" = $APIKey;
130 }
131
132 try
133 {
134 $Response = Invoke-WebRequest -Method 'GET' -Uri $uri -Headers $headers
135 Write-Host "API review: $($Response.Content)"
136 $StatusCode = $Response.StatusCode
137 }
138 catch
139 {
140 Write-Host "Exception details: $($_.Exception)"
141 $StatusCode = $_.Exception.Response.StatusCode
142 }
143
144 return $StatusCode
145}
146
147function Get-APITokenFileName($packageName)
148{
149 $reviewTokenFileName = ${packageName}.StartsWith("typespec-") ? "${packageName}_js.json" : "${packageName}_${LanguageShortName}.json"
150 $tokenFilePath = Join-Path $ArtifactPath $packageName $reviewTokenFileName
151 if (Test-Path $tokenFilePath) {
152 Write-Host "Review token file is present at $tokenFilePath"
153 return $reviewTokenFileName
154 }
155 else {
156 Write-Host "Review token file is not present at $tokenFilePath"
157 return $null
158 }
159}
160
161function Submit-APIReview($packageInfo, $packagePath, $packageArtifactName)
162{
163 $packageName = $packageInfo.Name
164 $apiLabel = "Source Branch:${SourceBranch}"
165
166 # Get generated review token file if present
167 # APIView processes request using different API if token file is already generated
168 $reviewTokenFileName = Get-APITokenFileName $packageArtifactName
169 if ($reviewTokenFileName) {
170 Write-Host "Uploading review token file $reviewTokenFileName to APIView."
171 return Upload-ReviewTokenFile $packageArtifactName $apiLabel $packageInfo.ReleaseStatus $reviewTokenFileName $packageInfo.Version $packagePath
172 }
173 else {
174 Write-Host "Uploading $packagePath to APIView."
175 return Upload-SourceArtifact $packagePath $apiLabel $packageInfo.ReleaseStatus $packageInfo.Version
176 }
177}
178
179function IsApiviewStatusCheckRequired($packageInfo)
180{
181 if (($packageInfo.SdkType -eq "client" -or $packageInfo.SdkType -eq "spring") -and $packageInfo.IsNewSdk) {
182 return $true
183 }
184 return $false
185}
186
187function ProcessPackage($packageName)
188{
189 $packages = Find-Artifacts-For-Apireview $ArtifactPath $packageName
190 if ($packages)
191 {
192 foreach($pkgPath in $packages.Values)
193 {
194 $pkg = Split-Path -Leaf $pkgPath
195 $pkgPropPath = Join-Path -Path $ConfigFileDir "$packageName.json"
196 if (-Not (Test-Path $pkgPropPath))
197 {
198 Write-Host " Package property file path $($pkgPropPath) is invalid."
199 continue
200 }
201 # Get package info from json file created before updating version to daily dev
202 $pkgInfo = Get-Content $pkgPropPath | ConvertFrom-Json
203 Write-Host "Processing package: $($pkgInfo)"
204 $version = [AzureEngSemanticVersion]::ParseVersionString($pkgInfo.Version)
205 if ($null -eq $version)
206 {
207 Write-Host "Version info is not available for package $packageName, because version '$(pkgInfo.Version)' is invalid. Please check if the version follows Azure SDK package versioning guidelines."
208 return 1
209 }
210
211 Write-Host "Version: $($version)"
212 Write-Host "SDK Type: $($pkgInfo.SdkType)"
213 Write-Host "Release Status: $($pkgInfo.ReleaseStatus)"
214
215 # Run create review step only if build is triggered from main branch or if version is GA.
216 # This is to avoid invalidating review status by a build triggered from feature branch
217 if ( ($SourceBranch -eq $DefaultBranch) -or (-not $version.IsPrerelease) -or $MarkPackageAsShipped)
218 {
219 Write-Host "Submitting API Review request for package $($pkg), File path: $($pkgPath)"
220 $respCode = Submit-APIReview $pkgInfo $pkgPath $packageName
221 Write-Host "HTTP Response code: $($respCode)"
222
223 # no need to check API review status when marking a package as shipped
224 if ($MarkPackageAsShipped)
225 {
226 if ($respCode -eq '500')
227 {
228 Write-Host "Failed to mark package ${packageName} as released. Please reach out to Azure SDK engineering systems on teams channel."
229 return 1
230 }
231 Write-Host "Package ${packageName} is marked as released."
232 return 0
233 }
234
235 $apiStatus = [PSCustomObject]@{
236 IsApproved = $false
237 Details = ""
238 }
239 $pkgNameStatus = [PSCustomObject]@{
240 IsApproved = $false
241 Details = ""
242 }
243 Process-ReviewStatusCode $respCode $packageName $apiStatus $pkgNameStatus
244
245 if ($apiStatus.IsApproved) {
246 Write-Host "API status: $($apiStatus.Details)"
247 }
248 elseif (!$pkgInfo.ReleaseStatus -or $pkgInfo.ReleaseStatus -eq "Unreleased") {
249 Write-Host "Release date is not set for current version in change log file for package. Ignoring API review approval status since package is not yet ready for release."
250 }
251 elseif ($version.IsPrerelease)
252 {
253 # Check if package name is approved. Preview version cannot be released without package name approval
254 if (!$pkgNameStatus.IsApproved)
255 {
256 if (IsApiviewStatusCheckRequired $pkgInfo)
257 {
258 Write-Error $($pkgNameStatus.Details)
259 return 1
260 }
261 else{
262 Write-Host "Package name is not approved for package $($packageName), however it is not required for this package type so it can still be released without API review approval."
263 }
264 }
265 # Ignore API review status for prerelease version
266 Write-Host "Package version is not GA. Ignoring API view approval status"
267 }
268 else
269 {
270 # Return error code if status code is 201 for new data plane package
271 # Temporarily enable API review for spring SDK types. Ideally this should be done be using 'IsReviewRequired' method in language side
272 # to override default check of SDK type client
273 if (IsApiviewStatusCheckRequired $pkgInfo)
274 {
275 if (!$apiStatus.IsApproved)
276 {
277 Write-Host "Package version $($version) is GA and automatic API Review is not yet approved for package $($packageName)."
278 Write-Host "Build and release is not allowed for GA package without API review approval."
279 Write-Host "You will need to queue another build to proceed further after API review is approved"
280 Write-Host "You can check http://aka.ms/azsdk/engsys/apireview/faq for more details on API Approval."
281 }
282 return 1
283 }
284 else {
285 Write-Host "API review is not approved for package $($packageName), however it is not required for this package type so it can still be released without API review approval."
286 }
287 }
288 }
289 else {
290 Write-Host "Build is triggered from $($SourceBranch) with prerelease version. Skipping API review status check."
291 }
292 }
293 }
294 else {
295 Write-Host "No package is found in artifact path to submit review request"
296 }
297 return 0
298}
299
300$responses = @{}
301# Check if package config file is present. This file has package version, SDK type etc info.
302if (-not $ConfigFileDir)
303{
304 $ConfigFileDir = Join-Path -Path $ArtifactPath "PackageInfo"
305}
306
307Write-Host "Artifact path: $($ArtifactPath)"
308Write-Host "Source branch: $($SourceBranch)"
309Write-Host "Config File directory: $($ConfigFileDir)"
310
311# if package name param is not empty then process only that package
312if ($PackageName)
313{
314 Write-Host "Processing $($PackageName)"
315 $result = ProcessPackage -packageName $PackageName
316 $responses[$PackageName] = $result
317}
318else
319{
320 # process all packages in the artifact
321 foreach ($artifact in $ArtifactList)
322 {
323 Write-Host "Processing $($artifact.name)"
324 $result = ProcessPackage -packageName $artifact.name
325 $responses[$artifact.name] = $result
326 }
327}
328
329$exitCode = 0
330foreach($pkg in $responses.keys)
331{
332 if ($responses[$pkg] -eq 1)
333 {
334 Write-Host "API changes are not approved for $($pkg)"
335 $exitCode = 1
336 }
337}
338exit $exitCode