-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Expand file tree
/
Copy pathCompare-CurrentToCodegeneration.ps1
More file actions
237 lines (201 loc) · 9.54 KB
/
Compare-CurrentToCodegeneration.ps1
File metadata and controls
237 lines (201 loc) · 9.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
<#
.SYNOPSIS
Runs code generation for either Swagger or TypeSpec, based on configuration, and compares the generated code against
the state of the current codebase.
.DESCRIPTION
Runs code generation for either Swagger or TypeSpec, based on configuration, and compares the generated code against
the state of the current codebase.
If the regenerated code is different than the current codebase this will report the differences and exit with a failure
status.
.PARAMETER ServiceDirectories
The service directories that will be searched for either 'Update-Codegeneration.ps1' or 'tsp-location.yaml' files to
run code regeneration. If this parameter is not specified, the script will not check any directories and will exit
with a success status.
.PARAMETER RegenerationType
The type of regeneration to perform. This can be 'All', 'Swagger', or 'TypeSpec'. If not specified, the script will use
'All' as the default, which means it will run both Swagger and TypeSpec code generation.
.PARAMETER Parallelization
The number of parallel jobs to run. The default is the number of processors on the machine. If unspecified or
less than 1, it will default to 1.
#>
param(
[Parameter(Mandatory = $false)]
[string]$ServiceDirectories,
[Parameter(Mandatory = $false)]
[ValidateSet('All', 'Swagger', 'TypeSpec')]
[string]$RegenerationType = 'All',
[Parameter(Mandatory = $false)]
[int]$Parallelization = [Environment]::ProcessorCount
)
$sdkFolder = Join-Path -Path $PSScriptRoot ".." ".." "sdk"
$tspClientFolder = Join-Path -Path $PSScriptRoot ".." ".." "eng" "common" "tsp-client"
class GenerationInformation {
# The directory where the library is located. Used for logging and validation.
[string]$LibraryFolder
# The path to the script that will perform the code generation.
# This can be 'Update-Codegeneration.ps1' for Swagger or 'tsp-location.yaml' for TypeSpec.
[string]$ScriptPath
# The type of code generation this script performs, either 'Swagger' or 'TypeSpec'.
# This is used to determine actions to take based on the type of code generation.
[ValidateSet('Swagger', 'TypeSpec')]
[string]$Type
GenerationInformation([string]$libraryFolder, [string]$scriptPath, [string]$type) {
$this.LibraryFolder = $libraryFolder
$this.ScriptPath = $scriptPath
$this.Type = $type
}
}
function Find-GenerationInformation {
param (
[System.Collections.ArrayList]$GenerationInformations,
[string]$LibraryFolder
)
$path = Join-Path -Path $sdkFolder $LibraryFolder
if ($RegenerationType -eq 'Swagger' -or $RegenerationType -eq 'All') {
# Search for 'Update-Codegeneration.ps1' script in the specified service directory.
Get-ChildItem -Path $path -Filter "Update-Codegeneration.ps1" -Recurse | ForEach-Object {
$GenerationInformations.Add([GenerationInformation]::new($path, $_, 'Swagger')) | Out-Null
}
}
if ($RegenerationType -eq 'TypeSpec' -or $RegenerationType -eq 'All') {
if ($LibraryFolder.Contains("-v2")) {
# Skip v2 libraries for TypeSpec regeneration as they are not supported.
Write-Host "Skipping TypeSpec regeneration for v2 library: $LibraryFolder"
return
}
# Search for 'tsp-location.yaml' script in the specified service directory.
Get-ChildItem -Path $path -Filter "tsp-location.yaml" -Recurse | ForEach-Object {
$GenerationInformations.Add([GenerationInformation]::new($path, $_, 'TypeSpec')) | Out-Null
}
}
}
# No ServiceDirectories specified, no validation will be performed.
if (-not $ServiceDirectories) {
Write-Host "No ServiceDirectories specified, no validation will be performed."
exit 0
}
# If Parallelization is not specified or less than 1, set it to 1.
if ($Parallelization -lt 1) {
$Parallelization = 1
}
# Split the ServiceDirectories by comma and trim whitespace.
# Then search for 'Update-Codegeneration.ps1' or 'tsp-location.yaml' scripts in those directories,
# based on RegenerationType, and store the results in a list of GenerationInformation objects.
$generationInformations = New-Object 'Collections.ArrayList'
foreach ($serviceDirectory in $ServiceDirectories.Split(',')) {
$serviceDirectory = $serviceDirectory.Trim()
if ($serviceDirectory -match '\w+/\w+') {
# The service directory is a specific library, e.g., "communication/azure-communication-chat"
# Search the directory directly for an "Update-Codegeneration.ps1" script.
Find-GenerationInformation $generationInformations $serviceDirectory
} else {
# The service directory is a top-level service, e.g., "storage"
# Search for all libraries under the service directory.
$searchPath = Join-Path -Path $sdkFolder $serviceDirectory
Get-ChildItem -Path $searchPath -Directory | ForEach-Object {
Find-GenerationInformation $generationInformations "$serviceDirectory/$($_.Name)"
}
}
}
if ($generationInformations.Count -eq 0) {
$kind = $RegenerationType -eq 'All' ? 'Swagger or TypeSpec' : $RegenerationType
Write-Host "No $kind generation files to regenerate in directories: $ServiceDirectories."
exit 0
}
if ($RegenerationType -eq 'Swagger' -or $RegenerationType -eq 'All') {
# Ensure Autorest is installed.
$output = (& npm install -g autorest 2>&1)
if ($LASTEXITCODE -ne 0) {
Write-Error "Failed to install Autorest for Swagger regeneration.`n$output"
exit 1
}
}
if ($RegenerationType -eq 'TypeSpec' -or $RegenerationType -eq 'All') {
$output = (& npm --prefix "$tspClientFolder" ci 2>&1)
if ($LASTEXITCODE -ne 0) {
Write-Error "Error installing @azure-tools/typespec-client-generator-cli`n$output"
exit 1
}
}
$generateScript = {
$separatorBar = "======================================"
$directory = $_.LibraryFolder
$updateCodegenScript = $_.ScriptPath
if ($_.Type -eq 'Swagger') {
# 6>&1 redirects Write-Host calls in the script to the output stream, so we can capture it.
# 2>&1 redirects stderr to stdout to suppress autorest deprecation messages that would fail the pipeline.
$generateOutput = (& $updateCodegenScript 2>&1 6>&1)
if ($LastExitCode -ne 0) {
Write-Host "$separatorBar`nError running Swagger regeneration $updateCodegenScript`n$([String]::Join("`n", $generateOutput))`n$separatorBar"
throw
} else {
# prevent warning related to EOL differences which triggers an exception for some reason
(& git -c core.safecrlf=false diff --ignore-space-at-eol --exit-code -- "$directory/*.java") | Out-Null
if ($LastExitCode -ne 0) {
$status = (git status -s "$directory" | Out-String)
Write-Host "$separatorBar`nThe following Swagger generated files in directoy $directory are out of date`n$status`n$separatorBar"
throw
} else {
Write-Host "$separatorBar`nSuccessfully ran Swagger regneration with no diff $updateCodegenScript`n$separatorBar"
}
}
} elseif ($_.Type -eq 'TypeSpec') {
Push-Location $directory
try {
try {
$generateOutput = (& npx --no --prefix "$using:tspClientFolder" tsp-client update 2>&1)
if ($LastExitCode -ne 0) {
Write-Host "$separatorBar`nError running TypeSpec regeneration in directory $directory`n$([String]::Join("`n", $generateOutput))`n$separatorBar"
throw
}
} finally {
Get-ChildItem -Path $directory -Filter TempTypeSpecFiles -Recurse -Directory | ForEach-Object {
Remove-Item -Path $_.FullName -Recurse -Force | Out-Null
}
}
# Update code snippets before comparing the diff
$mvnOutput = (& mvn --no-transfer-progress codesnippet:update-codesnippet 2>&1)
if ($LastExitCode -ne 0) {
Write-Host "$separatorBar`nError updating TypeSpec codesnippets in directory $directory`n$([String]::Join("`n", $mvnOutput))`n$separatorBar"
throw
}
} finally {
Pop-Location
}
# prevent warning related to EOL differences which triggers an exception for some reason
(& git -c core.safecrlf=false diff --ignore-space-at-eol --exit-code -- "$directory/*.java" ":(exclude)**/src/test/**" ":
(exclude)**/src/samples/**" ":(exclude)**/src/main/**/implementation/**") | Out-Null
if ($LastExitCode -ne 0) {
$status = (git status -s "$directory" | Out-String)
Write-Host "$separatorBar`nThe following TypeSpec files in directoy $directory are out of date`n$status`n$separatorBar"
throw
} else {
Write-Host "$separatorBar`nSuccessfully ran TypeSpec update in directory with no diff $directory`n$separatorBar"
}
} else {
Write-Host "$separatorBar`nUnknown generation type: $($_.Type), directory: $directory`n$separatorBar"
throw
}
}
# Timeout is set to 60 seconds per script.
$scriptTimeoutInSeconds = 60
$timeout = $scriptTimeoutInSeconds * $generationInformations.Count
# Ensure a minimum timeout of 5 times the script timeout
# This is for scenarios where there are only a few scripts to run. Some script with large TypeSpec source can take a few minutes.
$minimumTimeout = 5 * $scriptTimeoutInSeconds
if ($timeout -lt $minimumTimeout) {
$timeout = $minimumTimeout
}
$job = $generationInformations | ForEach-Object -Parallel $generateScript -ThrottleLimit $Parallelization -AsJob
# Out-Null to suppress output information from the job and 2>$null to suppress any error messages from Receive-Job.
$job | Wait-Job -Timeout $timeout | Out-Null
$job | Receive-Job 2>$null | Out-Null
$jobTimeout = $job.State -eq 'Running'
$jobFailed = $job.State -eq 'Failed'
if ($jobTimeout) {
Write-Host "The aggregated generate job timed out after $timeout seconds."
}
# Clean up generated code, so that next step will not be affected.
git reset --hard | Out-Null
git clean -fd . | Out-Null
exit $jobFailed -or $jobTimeout