tools/azdo_pipelines/run-extractor.yaml (272 lines of code) (raw):

parameters: - name: APIM_INSTANCE_NAME displayName: APIM instance name type: string - name: RESOURCE_GROUP_NAME displayName: APIM instance resource group name type: string - name: APIM_REPOSITORY_NAME type: string displayName: APIM repository for pull request - name: API_MANAGEMENT_SERVICE_OUTPUT_FOLDER_PATH type: string displayName: Folder where you want to extract the artifacts - name: TARGET_BRANCH_NAME type: string displayName: Target branch for pull request default: main - name: CONFIGURATION_YAML_PATH type: string displayName: Optional configuration file values: - Extract All - configuration.extractor.yaml - name: API_SPECIFICATION_FORMAT type: string displayName: API Specification Format values: - OpenAPIV3Yaml - OpenAPIV3Json - OpenAPIV2Yaml - OpenAPIV2Json trigger: none variables: - group: apim-automation - name: System.Debug value: true stages: - stage: create_artifact_from_portal displayName: Create artifact from portal jobs: - job: create_artifact_from_portal displayName: Create artifact from portal pool: vmImage: ubuntu-latest steps: - task: AzureCLI@2 displayName: Set extraction variables inputs: azureSubscription: "$(SERVICE_CONNECTION_NAME)" scriptType: pscore scriptLocation: inlineScript inlineScript: | Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $VerbosePreference = "Continue" $InformationPreference = "Continue" Write-Host "##vso[task.setvariable issecret=true;variable=AZURE_BEARER_TOKEN]$(az account get-access-token --query "accessToken" --output tsv)" Write-Host "##vso[task.setvariable issecret=true;variable=AZURE_CLIENT_ID]$env:servicePrincipalId" Write-Host "##vso[task.setvariable issecret=true;variable=AZURE_CLIENT_SECRET]$env:servicePrincipalKey" Write-Host "##vso[task.setvariable issecret=true;variable=AZURE_TENANT_ID]$env:tenantId" if (-not $env:AZURE_SUBSCRIPTION_ID) { $subscriptionCount = az account list --query "length([])" --output tsv if ($subscriptionCount -eq 1) { $subscriptionId = az account list --query "[0].id" --output tsv Write-Host "Setting AZURE_SUBSCRIPTION_ID environment variable to: $subscriptionId" Write-Host "##vso[task.setvariable issecret=true;variable=AZURE_SUBSCRIPTION_ID]$($subscriptionId)" } elseif ($subscriptionCount -gt 1) { Write-Host "Multiple subscriptions are accessible. Please set the AZURE_SUBSCRIPTION_ID environment variable manually." exit 1 } } else { Write-Host "AZURE_SUBSCRIPTION_ID is already set to: $env:AZURE_SUBSCRIPTION_ID" } addSpnToEnvironment: true failOnStandardError: true - task: PowerShell@2 displayName: Fetch extractor inputs: targetType: "inline" script: | Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $VerbosePreference = "Continue" $InformationPreference = "Continue" Write-Information "Setting name variables..." $releaseFileName = "extractor-linux-x64.zip" $executableFileName = "extractor" if ("$(Agent.OS)" -like "*win*") { $releaseFileName = "extractor-win-x64.zip" $executableFileName = "extractor.exe" } elseif ("$(Agent.OS)" -like "*mac*" -and "$(Agent.OSArchitecture)" -like "*arm*") { $releaseFileName = "extractor-osx-arm64.zip" } elseif ("$(Agent.OS)" -like "*mac*" -and "$(Agent.OSArchitecture)" -like "*x86_64*") { $releaseFileName = "extractor-osx-x64.zip" } Write-Information "Downloading release..." $uri = "https://github.com/Azure/apiops/releases/download/$(apiops_release_version)/$releaseFileName" $downloadFilePath = Join-Path "$(Agent.TempDirectory)" $releaseFileName Invoke-WebRequest -Uri "$uri" -OutFile "$downloadFilePath" Write-Information "Extracting release..." $executableFolderPath = Join-Path "$(Agent.TempDirectory)" "extractor" Expand-Archive -Path "$downloadFilePath" -DestinationPath "$executableFolderPath" $executableFilePath = Join-Path "$executableFolderPath" $executableFileName Write-Information "Setting file permissions..." if ("$(Agent.OS)" -like "*linux*") { & chmod +x "$executableFilePath" if ($LASTEXITCODE -ne 0) { throw "Setting file permissions failed."} } Write-Host "##vso[task.setvariable variable=EXTRACTOR_FILE_PATH]$executableFilePath" Write-Information "Execution complete." failOnStderr: true pwsh: true - task: PowerShell@2 displayName: Run extractor inputs: targetType: "inline" script: | Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $VerbosePreference = "Continue" $InformationPreference = "Continue" & "$(EXTRACTOR_FILE_PATH)" if ($LASTEXITCODE -ne 0) { throw "Running extractor failed."} Write-Information "Execution complete." failOnStderr: true pwsh: true env: AZURE_BEARER_TOKEN: $(AZURE_BEARER_TOKEN) AZURE_CLIENT_ID: $(AZURE_CLIENT_ID) AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET) AZURE_TENANT_ID: $(AZURE_TENANT_ID) AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID) AZURE_RESOURCE_GROUP_NAME: ${{ parameters.RESOURCE_GROUP_NAME }} API_MANAGEMENT_SERVICE_NAME: ${{ parameters.APIM_INSTANCE_NAME }} API_MANAGEMENT_SERVICE_OUTPUT_FOLDER_PATH: $(Build.ArtifactStagingDirectory)/${{ parameters.API_MANAGEMENT_SERVICE_OUTPUT_FOLDER_PATH }} API_SPECIFICATION_FORMAT: ${{ parameters.API_SPECIFICATION_FORMAT }} ${{ if ne( parameters['CONFIGURATION_YAML_PATH'], 'Extract All' ) }}: CONFIGURATION_YAML_PATH: ${{ parameters.CONFIGURATION_YAML_PATH }} #Running Super Linting Tool on the API(s) - START - task: NodeTool@0 inputs: versionSpec: '20.x' displayName: 'Install Node.js' - script: | npm install -g @stoplight/spectral-cli displayName: 'Install Spectral' - script: | spectral lint --format stylish --format junit --output.junit $(Build.ArtifactStagingDirectory)/spectral-result.xml $(Build.ArtifactStagingDirectory)/${{ parameters.API_MANAGEMENT_SERVICE_OUTPUT_FOLDER_PATH }}/apis/**/specification.{json,yaml,yml} -r https://raw.githubusercontent.com/connectedcircuits/devops-api-linter/main/rules.yaml displayName: 'Run Spectral Linting' continueOnError: true failOnStderr: true - task: PublishTestResults@2 inputs: testResultsFormat: 'JUnit' testResultsFiles: '**/spectral-result.xml' searchFolder: $(Build.ArtifactStagingDirectory) testRunTitle: 'Linting results for API $(Build.SourceBranchName)' failTaskOnFailedTests: false #Running Super Linting Tool on the API(s) - END - task: PublishPipelineArtifact@1 displayName: Publish pipeline artifact inputs: targetPath: "$(Build.ArtifactStagingDirectory)" artifactType: pipeline artifactName: artifacts-from-portal - task: Bash@3 displayName: 'Pipeline Summary Tab Upload' inputs: targetType: 'inline' script: | outputFilePath="$(Build.ArtifactStagingDirectory)/run-extractor.md" apiDirectoryPath="$(Build.ArtifactStagingDirectory)/artifacts" cd "$apiDirectoryPath" # exclude top directory echo "# Azure APIM Modifications Summary" > "$outputFilePath" echo "" >> "outputFilePath" #newline echo '```' >> "$outputFilePath" tree -d --noreport -I "outputFilePath" | sed 's/^/ /' >> "$outputFilePath" echo '```' >> "$outputFilePath" # Display the path to the generated Markdown file echo "Generated Markdown summary at $outputFilePath" # Display the Markdown file in the pipeline summary echo "##vso[task.uploadsummary]$outputFilePath" - stage: create_template_branch displayName: Create template branch jobs: - job: create_artifacts_pull_request displayName: Create artifacts pull request pool: vmImage: ubuntu-latest steps: - task: DownloadPipelineArtifact@2 displayName: Download pipeline artifact inputs: source: current artifactName: artifacts-from-portal targetPath: $(Pipeline.Workspace)/artifacts-from-portal - task: PowerShell@2 displayName: Create pull request inputs: targetType: "inline" script: | Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $VerbosePreference = "Continue" $InformationPreference = "Continue" Write-Information "Installing Azure DevOps extension..." az extension add --name "azure-devops" az devops configure --defaults organization="$(System.TeamFoundationCollectionUri)" project="$(System.TeamProject)" Write-Information "Creating temporary folder..." $temporaryFolderPath = Join-Path "$(Agent.TempDirectory)" "artifacts-from-portal" New-Item -Path "$temporaryFolderPath" -ItemType "Directory" $branchName = "${{ parameters.TARGET_BRANCH_NAME }}" $temporaryBranchName = "artifacts-from-portal-build-$(Build.BuildId)" $repositoryName = "${{ parameters.APIM_REPOSITORY_NAME }}" Write-Information "Cloning branch $branchName in repository $repositoryName..." $cloneUrl = az repos show --repository "$repositoryName" --query "remoteUrl" --output tsv Write-Information "Clone URL is $cloneUrl" git -c http.extraheader="AUTHORIZATION: Bearer $(System.AccessToken)" clone --branch "$branchName" --depth 1 "$cloneUrl" "$temporaryFolderPath" if ($LASTEXITCODE -ne 0) { throw "Cloning branch $branchName in repository $repositoryName failed." } Write-Information "Creating temporary branch $temporaryBranchName from $branchName..." git -C "$temporaryFolderPath" checkout -b "$temporaryBranchName" "$branchName" if ($LASTEXITCODE -ne 0) { throw "Creating temporary branch $temporaryBranchName from $branchName failed." } Write-Information "Creating artifacts folder..." $artifactFolderPath = Join-Path "$temporaryFolderPath" "${{ parameters.API_MANAGEMENT_SERVICE_OUTPUT_FOLDER_PATH }}" if ((Test-Path -Path "$artifactFolderPath") -eq $false) { New-Item -Path "$artifactFolderPath" -ItemType "Directory" } Write-Information "Synchronizing artifacts..." $extractorArtifactsFolderPath = Join-Path "$(Pipeline.Workspace)" "artifacts-from-portal" ${{ parameters.API_MANAGEMENT_SERVICE_OUTPUT_FOLDER_PATH }} if ("$(Agent.OS)" -like "*win*") { & robocopy "$extractorArtifactsFolderPath" "$artifactFolderPath" /zb /mir /mt if ($LASTEXITCODE -gt 7) { throw "Setting $artifactFolderPath to contents of $extractorArtifactsFolderPath failed." } } else { & rsync --verbose --archive --delete --force --recursive "$extractorArtifactsFolderPath/" "$artifactFolderPath/" if ($LASTEXITCODE -ne 0) { throw "Setting $artifactFolderPath to contents of $extractorArtifactsFolderPath failed." } } Write-Information "Validating that changes exist to be published..." $gitStatus = git -C "$temporaryFolderPath" status --porcelain if ($LASTEXITCODE -ne 0) { throw "Getting git status failed." } if ([string]::IsNullOrWhiteSpace($gitStatus)) { Write-Information "No changes exist to be published." return } Write-Information "Setting git user information..." git config --global user.email "azuredevopsagent@azuredevops.com" git config --global user.name "Azure Devops agent" Write-Information "Adding changes..." git -C "$temporaryFolderPath" add --all if ($LASTEXITCODE -ne 0) { throw "Adding Git changes failed." } Write-Information "Committing changes" $commitOutput = git -C "$temporaryFolderPath" commit --message "Initial commit" if ($LASTEXITCODE -ne 0) { if ($commitOutput.Contains("nothing to commit, working tree clean")) { Write-Information "No changes exist to be published." return } throw "Committing Git changes failed." } Write-Information "Pushing changes" git -C "$temporaryFolderPath" -c http.extraheader="AUTHORIZATION: Bearer $(System.AccessToken)" push --set-upstream origin "$temporaryBranchName" if ($LASTEXITCODE -ne 0) { throw "Pushing Git changes failed." } Write-Information "Creating pull request..." az repos pr create --source-branch "$temporaryBranchName" --target-branch "$branchName" --title "Merging artifacts from portal (Build $(Build.BuildId))" --squash --delete-source-branch "true" --repository "$repositoryName" if ($LASTEXITCODE -ne 0) { throw "Creating pull request failed." } Write-Information "Deleting temporary folder contents..." Remove-Item -Path "$temporaryFolderPath" -Recurse -Force Write-Information "Execution complete." pwsh: true env: AZURE_DEVOPS_EXT_PAT: "$(System.AccessToken)"