eng/Generate.ps1 (397 lines of code) (raw):
#Requires -Version 7.0
param($filter, [switch]$continue, [switch]$reset, [switch]$noBuild, [switch]$fast, [switch]$debug, [String[]]$Exclude = "SmokeTests", $parallel = 5)
Import-Module "$PSScriptRoot\Generation.psm1" -DisableNameChecking -Force;
Import-Module "$PSScriptRoot\..\test\scripts\LocalTestNugetSource.psm1" -DisableNameChecking -Force;
$timer = [System.Diagnostics.Stopwatch]::new();
$timer.Start();
$ErrorActionPreference = 'Stop'
# General configuration
$repoRoot = Resolve-Path (Join-Path $PSScriptRoot '..')
$swaggerDefinitions = @{};
$tspDefinitions = @{};
# Test server test configuration
$testProjectDataFile = Join-Path $repoRoot 'eng' 'testProjects.json'
$autoRestPluginProject = (Get-AutoRestProject)
$testServerDirectory = Join-Path $repoRoot 'test' 'TestServerProjects'
$sharedSource = Join-Path $repoRoot 'src' 'assets'
$configurationPath = Join-Path $repoRoot 'readme.md'
$testServerSwaggerPath = Join-Path $repoRoot 'node_modules' '@microsoft.azure' 'autorest.testserver' 'swagger'
$azureSpecsDirectory = Join-Path $repoRoot 'node_modules' '@azure-tools' 'azure-http-specs' 'specs'
$specsDirectory = Join-Path $repoRoot 'node_modules' '@typespec' 'http-specs' 'specs'
$typespecEmitOptions = '--option @azure-tools/typespec-csharp.save-inputs=true --option @azure-tools/typespec-csharp.clear-output-folder=true'
function Add-Swagger ([string]$name, [string]$output, [string]$arguments, [string]$launchSettingsArgs = "") {
$swaggerDefinitions[$name] = @{
'projectName' = $name;
'output' = $output;
'arguments' = $arguments;
'launchSettingsArgs' = $launchSettingsArgs
}
}
function Add-TypeSpec([string]$name, [string]$output, [string]$mainFile = "", [string]$arguments = "", [string]$launchSettingsArgs = "") {
if ($output.EndsWith("tests")) { return }
if ($mainFile -eq "") {
$mainFile = Get-TypeSpec-Entry $output
}
$tspDefinitions[$name] = @{
'projectName' = $name;
'output' = $output;
'mainFile' = $mainFile;
'arguments' = "$typespecEmitOptions $arguments";
'launchSettingsArgs' = $launchSettingsArgs
}
}
function Add-TestServer-Swagger ([string]$testName, [string]$projectSuffix, [string]$testServerDirectory, [string]$additionalArgs = "") {
$projectDirectory = Join-Path $testServerDirectory $testName
if (Test-Path "$projectDirectory/*.sln" || $projectDirectory.Contains("NewProject-")) {
$projectDirectory = Join-path $projectDirectory "src"
}
$inputFile = Join-Path $testServerSwaggerPath "$testName.json"
$inputReadme = Join-Path $projectDirectory "readme.md"
# TODO -- remove the flag when the feature is generally available
Add-Swagger "$testName$projectSuffix" $projectDirectory "--require=$configurationPath --try-require=$inputReadme --input-file=$inputFile $additionalArgs --clear-output-folder=true --use-model-reader-writer=true"
}
function Add-CadlRanch-TypeSpec([string]$testName, [string]$projectPrefix, [string]$cadlRanchProjectsDirectory, [string]$outputProjectDir = "") {
$projectDirectory = Join-Path $cadlRanchProjectsDirectory $testName
if ($outputProjectDir -ne "") {
$projectDirectory = $outputProjectDir
}
$configFile = Join-Path $projectDirectory "tspconfig.yaml"
if (Test-Path $configFile) {
$configString = "--config=$configFile "
}
$projectDirectory = Join-Path $projectDirectory "src"
$azureMainTsp = Join-Path $azureSpecsDirectory $testName "main.tsp"
$azureClientTsp = Join-Path $azureSpecsDirectory $testName "client.tsp"
$mainTsp = Join-Path $specsDirectory $testName "main.tsp"
$clientTsp = Join-Path $specsDirectory $testName "client.tsp"
$entryFile = ""
if ($projectPrefix -eq "typespec-nonAzure-") {
$entryFile = $clientTsp
if (!(Test-Path $entryFile)) {
$entryFile = $mainTsp
}
if (!(Test-Path $entryFile)) {
$entryFile = $azureClientTsp
}
if (!(Test-Path $entryFile)) {
$entryFile = $azureMainTsp
}
}
else {
$entryFile = $azureClientTsp
if (!(Test-Path $entryFile)) {
$entryFile = $azureMainTsp
}
if (!(Test-Path $entryFile)) {
$entryFile = $clientTsp
}
if (!(Test-Path $entryFile)) {
$entryFile = $mainTsp
}
}
$entryFile = Resolve-Path $entryFile
if ($projectPrefix -eq "typespec-nonAzure-") {
Add-TypeSpec "$projectPrefix$testName" $projectDirectory $entryFile "$configString--option @azure-tools/typespec-csharp.new-project=true" "-n"
}
else {
Add-TypeSpec "$projectPrefix$testName" $projectDirectory $entryFile "$configString--option @azure-tools/typespec-csharp.new-project=true --option @azure-tools/typespec-csharp.flavor=azure" "-n"
}
}
function Get-TypeSpec-Entry([System.IO.DirectoryInfo]$directory) {
$tspDirectory = $directory
if ($tspDirectory.FullName.EndsWith("src")) {
$tspDirectory = $directory.Parent
}
$clientPath = Join-Path $tspDirectory "client.tsp"
if (Test-Path $clientPath) {
return $clientPath
}
$mainPath = Join-Path $tspDirectory "main.tsp"
if (Test-Path $mainPath) {
return $mainPath
}
$projectNamePath = Join-Path $tspDirectory "$($tspDirectory.Name).tsp"
if (Test-Path $projectNamePath) {
return $projectNamePath
}
throw "There is no client.tsp or main.tsp or other tsp file named after project name in project $($tspDirectory.Name)"
}
$testData = Get-Content $testProjectDataFile -Encoding utf8 -Raw | ConvertFrom-Json
$testNames = $testData.TestServerProjects
if (!($Exclude -contains "TestServer")) {
foreach ($testName in $testNames) {
Add-TestServer-Swagger $testName "" $testServerDirectory "--generation1-convenience-client"
}
}
$llcArgs = "--data-plane=true --security=AzureKey --security-header-name=Fake-Subscription-Key --new-project=true"
$testServerLowLevelDirectory = Join-Path $repoRoot 'test' 'TestServerProjectsLowLevel'
$testNamesLowLevel = $testData.TestServerProjectsLowLevel
$testNamesLowLevelWithoutArgs = $testData.TestServerProjectsLowLevelNoArgs
if (!($Exclude -contains "TestServerLowLevel")) {
foreach ($testName in $testNamesLowLevel) {
Add-TestServer-Swagger $testName "-LowLevel" $testServerLowLevelDirectory $llcArgs
}
foreach ($testName in $testNamesLowLevelWithoutArgs) {
Add-TestServer-Swagger $testName "-LowLevel" $testServerLowLevelDirectory
}
}
function Add-Directory ([string]$testName, [string]$directory) {
$readmeConfigurationPath = Join-Path $directory "readme.md"
$testArguments = $null
$launchSettings = ""
if (Test-Path $readmeConfigurationPath) {
$testArguments = "--require=$readmeConfigurationPath --clear-output-folder=true --generate-test-project=true"
if ($directory.EndsWith("\src")) {
$launchSettings = "-n"
}
}
if ($testName.EndsWith("TypeSpec")) {
Add-TypeSpec $testName $directory "" "--option @azure-tools/typespec-csharp.new-project=true" "-n"
}
else {
Add-Swagger $testName $directory $testArguments $launchSettings
}
}
function Add-TestProjects-Directory($directory) {
$testName = $directory.Name
if ($testName -eq "ConvenienceInitial-TypeSpec" -or $testName -eq "node_modules") {
return;
}
if ($testName -eq "sdk") {
foreach ($serviceDir in Get-ChildItem $directory -Directory) {
if ($serviceDir.Name -eq "core") {
continue;
}
foreach ($projectDir in Get-ChildItem $serviceDir -Directory) {
Add-TestProjects-Directory $projectDir
}
}
return;
}
$readmeConfigurationPath = Join-Path $directory "readme.md"
$tspConfigConfigurationPath = Join-Path $directory "tspconfig.yaml"
$possibleInputJsonFilePath = Join-Path $directory "$testName.json"
$testArguments = $null
$srcFolder = Join-Path $directory "src"
$testsFolder = Join-Path $directory "tests"
$srcReadmeConfigurationPath = Join-Path $srcFolder "readme.md"
# if we have both src and test directories, we treat it as a swagger directory project
if ((Test-Path -Path $srcFolder) -And (Test-Path -Path $testsFolder)) {
Add-Directory $testName $srcFolder
return
}
# if tspconfig.yaml exists, we treat it as a typespec project
if (Test-Path $tspConfigConfigurationPath) {
$directoryToUse = $directory
$launchSettingsArgs = ""
$options = ""
if ($directory.FullName.Contains("\sdk\")) {
$directoryToUse = $srcFolder
}
if ($directory.FullName.Contains(".NewProject.")) {
$launchSettingsArgs = "-n"
}
if ($directory.FullName.Contains("\UnbrandedProjects\")) {
if ($directoryToUse.Name -ne "src") {
$directoryToUse = Join-Path $directoryToUse "src"
}
$launchSettingsArgs = "-n"
$options = "--option @azure-tools/typespec-csharp.new-project=true"
}
Add-TypeSpec $testName $directoryToUse "" $options $launchSettingsArgs
}
elseif (Test-Path $readmeConfigurationPath) {
$testArguments = "--require=$readmeConfigurationPath --clear-output-folder=true"
Add-Swagger $testName $directory $testArguments
}
elseif (Test-Path $srcReadmeConfigurationPath) {
Add-Directory $testName $srcFolder
}
elseif (Test-Path $possibleInputJsonFilePath) {
$testArguments = "--require=$configurationPath --input-file=$possibleInputJsonFilePath --generation1-convenience-client --clear-output-folder=true"
Add-Swagger $testName $directory $testArguments
}
else {
throw "There is no tspconfig.yaml file or readme.md file or swagger json file $testName.json found in test project $testName"
}
}
if (!($Exclude -contains "TestProjects")) {
# Local test projects
$testProjectRoot = Join-Path $repoRoot 'test' 'TestProjects'
foreach ($directory in Get-ChildItem $testProjectRoot -Directory) {
Add-TestProjects-Directory $directory
}
}
if (!($Exclude -contains "UnbrandedProjects")) {
# Local test projects
$testProjectRoot = Join-Path $repoRoot 'test' 'UnbrandedProjects'
foreach ($directory in Get-ChildItem $testProjectRoot -Directory) {
Add-TestProjects-Directory $directory
}
}
if (!($Exclude -contains "Samples")) {
$sampleProjectsRoot = Join-Path $repoRoot 'samples'
foreach ($directory in Get-ChildItem $sampleProjectsRoot -Directory) {
$sampleName = $directory.Name
$projectDirectory = Join-Path $sampleProjectsRoot $sampleName
$srcProjectDirectory = Join-Path $projectDirectory "src"
$srcReadmeConfigurationPath = Join-Path $srcProjectDirectory "readme.md"
if (Test-Path $srcReadmeConfigurationPath) {
Add-Directory $sampleName $srcProjectDirectory
continue
}
if (Test-Path "$projectDirectory/*.sln") {
$projectDirectory = Join-Path $projectDirectory "src"
}
$sampleConfigurationPath = Join-Path $projectDirectory 'readme.md'
$tspConfigPath = Join-Path $directory "tspconfig.yaml"
if (Test-Path $sampleConfigurationPath) {
# for swagger samples
Add-Swagger $sampleName $projectDirectory "--require=$sampleConfigurationPath --clear-output-folder=true"
}
elseif (Test-Path $tspConfigPath) {
# for typespec projects
$tspMain = Join-Path $projectDirectory ".." "main.tsp"
$tspClient = Join-Path $projectDirectory ".." "client.tsp"
$mainTspFile = if (Test-Path $tspClient) { Resolve-Path $tspClient } else { Resolve-Path $tspMain }
Add-TypeSpec $sampleName $projectDirectory $mainTspFile
}
else {
throw "There is no tspconfig.yaml file or readme.md file found in sample project $sampleName"
}
}
}
# Azure cadl ranch projects
$cadlRanchProjectDirectory = Join-Path $repoRoot 'test' 'CadlRanchProjects'
$cadlRanchProjectPaths = $testData.CadlRanchProjects
if (!($Exclude -contains "CadlRanchProjects")) {
foreach ($testPath in $cadlRanchProjectPaths) {
Add-CadlRanch-TypeSpec $testPath "typespec-" $cadlRanchProjectDirectory
}
}
# Non azure cadl ranch projects
$cadlRanchProjectNonAzureDirectory = Join-Path $repoRoot 'test' 'CadlRanchProjectsNonAzure'
$cadlRanchProjectNonAzurePaths = $testData.CadlRanchProjectsNonAzure
if (!($Exclude -contains "CadlRanchProjectsNonAzure")) {
foreach ($testPath in $cadlRanchProjectNonAzurePaths) {
Add-CadlRanch-TypeSpec $testPath "typespec-nonAzure-" $cadlRanchProjectNonAzureDirectory
}
}
# add Head-As-Boolen project
Add-CadlRanch-TypeSpec "server/path/single" "headAsBoolean-typespec-" $cadlRanchProjectDirectory "$cadlRanchProjectDirectory/server/path/single-headAsBoolean"
# add Removed-OldVersion project
Add-CadlRanch-TypeSpec "versioning/removed" "removedOldVersion-typespec-" $cadlRanchProjectDirectory "$cadlRanchProjectDirectory/versioning/removed-oldversion"
# add Removed-BetaVersion project
Add-CadlRanch-TypeSpec "versioning/removed" "removedBetaVersion-typespec-" $cadlRanchProjectDirectory "$cadlRanchProjectDirectory/versioning/removed-betaversion"
# Smoke tests
if (!($Exclude -contains "SmokeTests")) {
foreach ($input in Get-Content (Join-Path $PSScriptRoot "SmokeTestInputs.txt")) {
if ($input -match "^(?<input>[^#].*?specification/(?<name>[\w-]+(/[\w-]+)+)/readme.md)(:(?<args>.*))?") {
$input = $Matches["input"]
$args = $Matches["args"]
$projectName = $Matches["name"].Replace("/", "-");
$projectDirectory = Join-Path $repoRoot 'samples' 'smoketests' $projectName
Add-Swagger $projectName $projectDirectory "--generation1-convenience-client --require=$configurationPath $args $input --clear-output-folder=true"
}
}
}
# Sorting file names that include '-' and '.' is broken in powershell - https://github.com/PowerShell/PowerShell/issues/3425
# So map each to characters invalid for file system use '?' and '|', sort, and then map back
function Sort-FileSafe ($names) {
return $names | % { $_.replace("-", "?") } | % { $_.replace(".", "|") } | Sort-Object | % { $_.replace("?", "-") } | % { $_.replace("|", ".") }
}
$launchSettings = Join-Path $autoRestPluginProject 'Properties' 'launchSettings.json'
$settings = @{
'profiles' = [ordered]@{}
};
# here we put the source code generation project (map $swaggerDefinitions) and the test code generation project (map $swaggerTestDefinitions) together
$testProjectEntries = @{};
$swaggerDefinitions.Keys | ForEach-Object {
$testProjectEntries[$_] = $swaggerDefinitions[$_];
};
$tspDefinitions.Keys | ForEach-Object {
$testProjectEntries[$_] = $tspDefinitions[$_];
}
foreach ($key in Sort-FileSafe ($testProjectEntries.Keys)) {
$definition = $testProjectEntries[$key];
if ($definition.output.Contains("\smoketests\")) {
#skip writing the smoketests since these aren't actually defined locally
#these get added when a filter is used so it can find the filter using
#all possible sources
continue;
}
$outputPath = Join-Path $definition.output "Generated"
if ($key -eq "TypeSchemaMapping") {
$outputPath = Join-Path $definition.output "SomeFolder" "Generated"
}
elseif ($key -eq "ConvenienceUpdate-TypeSpec" -or $key -eq "ConvenienceInitial-TypeSpec") {
$outputPath = "$outputPath --existing-project-folder $(Convert-Path $(Join-Path $definition.output ".." ".." "ConvenienceInitial-TypeSpec" "src" "Generated"))"
}
$outputPath = $outputPath.Replace($repoRoot, '$(SolutionDir)')
$launchSettingsArgs = $definition.launchSettingsArgs
if ($launchSettingsArgs -ne "") {
$launchSettingsArgs = " $launchSettingsArgs"
}
$settings.profiles[$key] = [ordered]@{
'commandName' = 'Project';
'commandLineArgs' = "--standalone $outputPath$launchSettingsArgs"
}
}
$settings | ConvertTo-Json | Out-File $launchSettings
$keys = $testProjectEntries.Keys | Sort-Object;
if (![string]::IsNullOrWhiteSpace($filter)) {
Write-Host "Using filter: $filter"
if ($continue) {
$keys = $keys.Where({ $_ -match $filter }, 'SkipUntil')
Write-Host "Continuing with $keys"
}
else {
$keys = $keys.Where({ $_ -match $filter })
}
}
$typespecCount = ([string]::IsNullOrWhiteSpace($filter) ? $tspDefinitions : $tspDefinitions.Keys.Where({ $_ -match $filter })).Count
if ($reset -or $env:TF_BUILD) {
$swaggerCount = $keys.Count - $typespecCount
if ($swaggerCount -gt 0) {
AutoRest-Reset;
}
if ($typespecCount -gt 0) {
Invoke-TypeSpecSetup
}
}
if (!$noBuild) {
Invoke "dotnet build $autoRestPluginProject"
#build the emitter
if ($typespecCount -gt 0) {
Invoke-TypeSpecSetup
}
}
[hashtable]$testPackagesToInstall = @{};
$localTestNugetSourceFolder = Join-Path $repoRoot 'test/NugetPackages'
foreach($key in $keys){
foreach($nugetPackageFilename in (Get-ChildItem -Path $localTestNugetSourceFolder | Select-Object -ExpandProperty Name)){
$nameForRegex = [Regex]::Escape($key);
if ($nugetPackageFilename -match "^($nameForRegex)\.([\.\d\w\-]+)\.nupkg$") {
$name = $matches[1]
$version = $matches[2]
$testPackagesToInstall[$name] = $version;
}
}
}
if($testPackagesToInstall.Count -gt 0){
Install-Test-Nuget-Packages $testPackagesToInstall
}
$keys | % { $swaggerDefinitions[$_] } | ForEach-Object -Parallel {
if ($_.output -ne $null) {
Import-Module "$using:PSScriptRoot\Generation.psm1" -DisableNameChecking;
Invoke-AutoRest $_.output $_.projectName $_.arguments $using:sharedSource $using:fast $using:debug;
}
} -ThrottleLimit $parallel
$keys | % { $tspDefinitions[$_] } | ForEach-Object -Parallel {
if ($_.output -ne $null) {
Import-Module "$using:PSScriptRoot\Generation.psm1" -DisableNameChecking;
Invoke-TypeSpec $_.output $_.projectName $_.mainFile $_.arguments $using:sharedSource $using:fast $using:debug;
}
} -ThrottleLimit $parallel
$timer.Stop();
Write-Host "Elapsed Time: $($timer.Elapsed.TotalSeconds) seconds";