diff --git a/cli/azd/README.md b/cli/azd/README.md index 544d89b52ea..8140e924b61 100644 --- a/cli/azd/README.md +++ b/cli/azd/README.md @@ -7,3 +7,5 @@ Install Go extension https://marketplace.visualstudio.com/items?itemName=golang. ## Contribute See [CONTRIBUTING.md](./CONTRIBUTING.md) for information on contributing. + +Test Changes diff --git a/eng/common/pipelines/templates/steps/login-to-github.yml b/eng/common/pipelines/templates/steps/login-to-github.yml index 3df66925da2..e81c9c797c5 100644 --- a/eng/common/pipelines/templates/steps/login-to-github.yml +++ b/eng/common/pipelines/templates/steps/login-to-github.yml @@ -8,18 +8,22 @@ parameters: - name: VariableNamePrefix type: string default: GH_TOKEN +- name: ExportAsOutputVariable + type: boolean + default: false - name: ScriptDirectory default: eng/common/scripts steps: - task: AzureCLI@2 displayName: "Login to GitHub" + name: LoginToGitHub inputs: azureSubscription: 'AzureSDKEngKeyVault Secrets' scriptType: pscore scriptLocation: scriptPath scriptPath: ${{ parameters.ScriptDirectory }}/login-to-github.ps1 arguments: > - -InstallationTokenOwners '${{ join(''',''', parameters.TokenOwners) }}' - -VariableNamePrefix '${{ parameters.VariableNamePrefix }}' - \ No newline at end of file + -InstallationTokenOwners '${{ join(''',''', parameters.TokenOwners) }}' + -VariableNamePrefix '${{ parameters.VariableNamePrefix }}' + -ExportAsOutputVariable:$${{ parameters.ExportAsOutputVariable }} \ No newline at end of file diff --git a/eng/common/scripts/login-to-github.ps1 b/eng/common/scripts/login-to-github.ps1 index 129946a409b..80c47c2dbb0 100644 --- a/eng/common/scripts/login-to-github.ps1 +++ b/eng/common/scripts/login-to-github.ps1 @@ -22,6 +22,10 @@ Prefix for the exported variable name (default: GH_TOKEN). With a single owner, exports as GH_TOKEN. With multiple owners, exports as GH_TOKEN_. +.PARAMETER ExportAsOutputVariable + When set in Azure DevOps, also exports the variable as an output variable + (##vso[task.setvariable ...;isOutput=true]) for downstream jobs/stages. + .OUTPUTS Sets environment variables in the current process and exports them to the CI system: - Azure DevOps: sets secret pipeline variables via ##vso logging commands @@ -34,7 +38,8 @@ param( [string] $KeyName = "azure-sdk-automation", [string] $GitHubAppId = '1086291', # Azure SDK Automation App ID [string[]] $InstallationTokenOwners = @("Azure"), - [string] $VariableNamePrefix = "GH_TOKEN" + [string] $VariableNamePrefix = "GH_TOKEN", + [switch] $ExportAsOutputVariable ) $ErrorActionPreference = 'Stop' @@ -115,6 +120,32 @@ function New-GitHubAppJwt { return "$UnsignedToken.$Signature" } +function Get-PropertyValue { + param( + [AllowNull()][object] $InputObject, + [Parameter(Mandatory)][string] $PropertyName + ) + + if ($null -eq $InputObject) { + return $null + } + + if ($InputObject -is [System.Collections.IDictionary]) { + if ($InputObject.Contains($PropertyName)) { + return $InputObject[$PropertyName] + } + + return $null + } + + $property = $InputObject | Get-Member -Name $PropertyName -MemberType NoteProperty,Property -ErrorAction SilentlyContinue + if ($null -ne $property) { + return $InputObject.$PropertyName + } + + return $null +} + function Get-GitHubInstallationId { param( [Parameter(Mandatory)][string] $Jwt, @@ -128,11 +159,46 @@ function Get-GitHubInstallationId { $uri = "$ApiBase/app/installations" $resp = Invoke-RestMethod -Method Get -Headers $headers -Uri $uri -TimeoutSec 30 -MaximumRetryCount 3 - $resp | Foreach-Object { Write-Host " $($_.id): $($_.account.login) [$($_.target_type)]" } + $resp = @($resp) + foreach ($installation in $resp) { + $installationId = Get-PropertyValue -InputObject $installation -PropertyName 'id' + $installationLogin = Get-PropertyValue -InputObject $installation -PropertyName 'login' + $installationType = Get-PropertyValue -InputObject $installation -PropertyName 'target_type' + + if ($null -eq $installationLogin) { + $installationLogin = Get-PropertyValue -InputObject (Get-PropertyValue -InputObject $installation -PropertyName 'account') -PropertyName 'login' + } + + Write-Host " ${installationId}: ${installationLogin} [${installationType}]" + } + + $loginMatches = @($resp | Where-Object { + $installationLogin = Get-PropertyValue -InputObject $_ -PropertyName 'login' + if ($null -eq $installationLogin) { + $installationLogin = Get-PropertyValue -InputObject (Get-PropertyValue -InputObject $_ -PropertyName 'account') -PropertyName 'login' + } + + $installationLogin -ieq $InstallationTokenOwner + }) - $resp = $resp | Where-Object { $_.account.login -ieq $InstallationTokenOwner } - if (!$resp.id) { throw "No installations found for this App." } - return $resp.id + $matchingInstallations = @($loginMatches | Where-Object { + $null -ne (Get-PropertyValue -InputObject $_ -PropertyName 'id') + }) + + if ($matchingInstallations.Count -eq 0) { + if ($loginMatches.Count -gt 0) { + throw "No installations with a valid id found for '$InstallationTokenOwner' on this App." + } + + throw "No installations found for '$InstallationTokenOwner' on this App." + } + + if ($matchingInstallations.Count -gt 1) { + Write-Warning "Multiple installations matched '$InstallationTokenOwner'; using the first one." + } + + $matchedInstallation = $matchingInstallations[0] + return (Get-PropertyValue -InputObject $matchedInstallation -PropertyName 'id') } function New-GitHubInstallationToken { @@ -149,50 +215,63 @@ function New-GitHubInstallationToken { return $resp.token } -Write-Host "Generating GitHub App JWT by signing via Azure Key Vault (no key export)..." -$jwt = New-GitHubAppJwt -VaultName $KeyVaultName -KeyName $KeyName -AppId $GitHubAppId +function Invoke-LoginToGitHub { + Write-Host "Generating GitHub App JWT by signing via Azure Key Vault (no key export)..." + $jwt = New-GitHubAppJwt -VaultName $KeyVaultName -KeyName $KeyName -AppId $GitHubAppId -foreach ($InstallationTokenOwner in $InstallationTokenOwners) -{ - Write-Host "Fetching installation ID for $InstallationTokenOwner ..." - $installationId = Get-GitHubInstallationId -Jwt $jwt -ApiBase $GitHubApiBaseUrl -ApiVersion $GitHubApiVersion -InstallationTokenOwner $InstallationTokenOwner + foreach ($InstallationTokenOwner in $InstallationTokenOwners) { + # Token owners can be provided as either "owner" or "owner/repo". Normalize to owner. + $normalizedOwner = ($InstallationTokenOwner -split '/')[0] + Write-Host "Fetching installation ID for $InstallationTokenOwner (normalized owner: $normalizedOwner) ..." + $installationId = Get-GitHubInstallationId -Jwt $jwt -ApiBase $GitHubApiBaseUrl -ApiVersion $GitHubApiVersion -InstallationTokenOwner $normalizedOwner - Write-Host "Installation ID resolved: $installationId" + Write-Host "Installation ID resolved: $installationId" - Write-Host "Exchanging JWT for installation access token..." - $installationToken = New-GitHubInstallationToken -Jwt $jwt -InstallationId $installationId -ApiBase $GitHubApiBaseUrl -ApiVersion $GitHubApiVersion + Write-Host "Exchanging JWT for installation access token..." + $installationToken = New-GitHubInstallationToken -Jwt $jwt -InstallationId $installationId -ApiBase $GitHubApiBaseUrl -ApiVersion $GitHubApiVersion - $variableName = $VariableNamePrefix - if ($InstallationTokenOwners.Count -gt 1) - { - $variableName = $VariableNamePrefix + "_" + $InstallationTokenOwner - } + $variableName = $VariableNamePrefix + if ($InstallationTokenOwners.Count -gt 1) { + $variableName = $VariableNamePrefix + "_" + $normalizedOwner + } - Set-Item -Path Env:$variableName -Value $installationToken + Set-Item -Path Env:$variableName -Value $installationToken - # Export for gh CLI & git - Write-Host "$variableName has been set in the current process." + # Export for gh CLI & git + Write-Host "$variableName has been set in the current process." - # Azure DevOps: set secret pipeline variable (so later tasks can reuse it) - if ($null -ne $env:SYSTEM_TEAMPROJECTID) { - Write-Host "##vso[task.setvariable variable=$variableName;issecret=true]$installationToken" - Write-Host "Azure DevOps variable '$variableName' has been set (secret)." - } + # Azure DevOps: set secret pipeline variable (so later tasks can reuse it) + if ($null -ne $env:SYSTEM_TEAMPROJECTID) { + Write-Host "##vso[task.setvariable variable=$variableName;issecret=true]$installationToken" + Write-Host "Azure DevOps variable '$variableName' has been set (secret)." - # GitHub Actions: mask the token and export to GITHUB_ENV - if ($env:GITHUB_ACTIONS -eq 'true') { - Write-Host "::add-mask::$installationToken" - Add-Content -Path $env:GITHUB_ENV -Value "$variableName=$installationToken" - Write-Host "GitHub Actions env variable '$variableName' has been exported." - } + if ($ExportAsOutputVariable) { + Write-Host "##vso[task.setvariable variable=$variableName;issecret=true;isOutput=true]$installationToken" + Write-Host "Azure DevOps output variable '$variableName' has been set (secret)." + } + } - try { - Write-Host "`n--- gh auth status ---" - $gh_token_value_before = $env:GH_TOKEN - $env:GH_TOKEN = $installationToken - & gh auth status - } - finally{ - $env:GH_TOKEN = $gh_token_value_before + # GitHub Actions: mask the token and export to GITHUB_ENV + if ($env:GITHUB_ACTIONS -eq 'true') { + Write-Host "::add-mask::$installationToken" + Add-Content -Path $env:GITHUB_ENV -Value "$variableName=$installationToken" + Write-Host "GitHub Actions env variable '$variableName' has been exported." + } + + try { + Write-Host "`n--- gh auth status ---" + $gh_token_value_before = $env:GH_TOKEN + $env:GH_TOKEN = $installationToken + & gh auth status + } + finally { + $env:GH_TOKEN = $gh_token_value_before + } } } + +if ($env:PESTER_TEST_RUN -eq 'true') { + return +} + +Invoke-LoginToGitHub diff --git a/eng/pipelines/templates/stages/publish-extension.yml b/eng/pipelines/templates/stages/publish-extension.yml index ada56ed662c..03a790ca2d1 100644 --- a/eng/pipelines/templates/stages/publish-extension.yml +++ b/eng/pipelines/templates/stages/publish-extension.yml @@ -52,19 +52,59 @@ stages: value: Registry update jobs: - - deployment: Publish_Release - condition: >- - and( - succeeded(), - ne('true', variables['Skip.Publish']) - ) - # Nightly publishes unattended, so it uses the no-approval 'none' - # environment and is not a governed production release. Manual releases - # go through the approval-gated package-publish environment. + - ${{ if ne(variables['Build.Reason'], 'Schedule') }}: + - job: Create_GitHub_Release + dependsOn: [] + condition: >- + and( + succeeded(), + ne('true', variables['Skip.Publish']) + ) + + pool: + name: $(LINUXPOOL) + image: $(LINUXVMIMAGE) + os: linux + + steps: + - checkout: self + + - template: /eng/common/pipelines/templates/steps/login-to-github.yml + + - template: /eng/pipelines/templates/steps/extension-set-metadata-variables.yml + + - task: DownloadPipelineArtifact@2 + displayName: Download release artifact + inputs: + artifactName: release + targetPath: release + + - task: DownloadPipelineArtifact@2 + displayName: Download changelog artifact + inputs: + artifactName: changelog + targetPath: changelog + + - template: /eng/pipelines/templates/steps/publish-extension-github-release.yml + parameters: + TagPrefix: azd-ext-${{ parameters.SanitizedExtensionId }} + TagVersion: $(EXT_VERSION) + + # Storage upload — runs for both nightly and manual. 1ES releaseJob keeps + # production governance (package-publish approval on manual runs). No + # GH_TOKEN needed here. azcopy --overwrite=true makes retry safe. + - deployment: Publish_Storage ${{ if eq(variables['Build.Reason'], 'Schedule') }}: + dependsOn: [] environment: none ${{ else }}: + dependsOn: Create_GitHub_Release environment: package-publish + condition: >- + and( + succeeded(), + ne('true', variables['Skip.Publish']) + ) pool: name: azsdk-pool @@ -82,10 +122,6 @@ stages: artifactName: release targetPath: release - - input: pipelineArtifact - artifactName: changelog - targetPath: changelog - strategy: runOnce: deploy: @@ -103,28 +139,22 @@ stages: displayName: Set StorageUploadLocations (nightly) - ${{ else }}: - pwsh: | - # Initial upload locations $publishUploadLocations = '${{ parameters.SanitizedExtensionId }}/$(EXT_VERSION)' - Write-Host "Setting StorageUploadLocations to $publishUploadLocations" Write-Host "###vso[task.setvariable variable=StorageUploadLocations]$publishUploadLocations" displayName: Set StorageUploadLocations - - template: /eng/pipelines/templates/steps/publish-extension.yml + - template: /eng/pipelines/templates/steps/publish-extension-storage.yml parameters: PublishUploadLocations: $(StorageUploadLocations) - TagPrefix: azd-ext-${{ parameters.SanitizedExtensionId }} - TagVersion: $(EXT_VERSION) - # Nightly builds publish to storage only; no GitHub release since that'd just be a ton of clutter. - # Nightly extension consumers just grab it from blob storage. - ${{ if eq(variables['Build.Reason'], 'Schedule') }}: - CreateGitHubRelease: false + # Updates the selected extension registry after the GitHub Release exists. # Production registry updates also refresh global command snapshots. # Mirrors Increment_Version in eng/pipelines/templates/stages/publish.yml. - job: Update_Registry - dependsOn: Publish_Release + dependsOn: + - Publish_Release condition: >- and( succeeded(), @@ -139,6 +169,8 @@ stages: steps: - checkout: self + - template: /eng/common/pipelines/templates/steps/login-to-github.yml + - template: /eng/pipelines/templates/steps/extension-set-metadata-variables.yml - template: /eng/pipelines/templates/steps/setup-go.yml @@ -243,7 +275,7 @@ stages: --version "$(EXT_VERSION)" displayName: Update $(ExtensionRegistryFile) (azd x publish) env: - GH_TOKEN: $(azuresdk-github-pat) + GH_TOKEN: $(GH_TOKEN) - ${{ if ne(parameters.PublishToDevRegistry, true) }}: - bash: | diff --git a/eng/pipelines/templates/stages/publish.yml b/eng/pipelines/templates/stages/publish.yml index 45ce4bc1a59..d2922c2d18f 100644 --- a/eng/pipelines/templates/stages/publish.yml +++ b/eng/pipelines/templates/stages/publish.yml @@ -459,96 +459,137 @@ stages: PublishUploadLocations: pr/$(PRNumber) UploadInstaller: true - - pwsh: | - $urlBase = "$(publish-storage-static-host)/azd/standalone/pr/$(PRNumber)" - Write-Host "##vso[task.setvariable variable=UrlBase;isOutput=true]$urlBase" - name: GenerateUrlBase - displayName: Set UrlBase + - job: Comment_On_PR + dependsOn: Publish_For_PR + condition: >- + and( + succeeded(), + ne(variables['Skip.Release'], 'true'), + or( + eq('PullRequest', variables['BuildReasonOverride']), + and( + eq('', variables['BuildReasonOverride']), + eq(variables['Build.Reason'], 'PullRequest') + ) + ) + ) - - pwsh: | - $urlBase = "$(GenerateUrlBase.UrlBase)" - $linuxReleaseUrl = "$urlBase/azd-linux-amd64.tar.gz" - $linuxReleaseUrlArm64 = "$urlBase/azd-linux-arm64.tar.gz" - $macosReleaseUrl = "$urlBase/azd-darwin-amd64.zip" - $macosReleaseUrlArm64 = "$urlBase/azd-darwin-arm64.zip" - $windowsReleaseUrl = "$urlBase/azd-windows-amd64.zip" - $windowsReleaseUrlArm64 = "$urlBase/azd-windows-arm64-alpha.zip" - $msiReleaseUrl = "$urlBase/azd-windows-amd64.msi" + pool: + name: $(LINUXPOOL) + image: $(LINUXVMIMAGE) + os: linux - $refDocContent = Get-Content docs/azd.md -Raw + steps: + - checkout: self - $content = @" - - ## Azure Dev CLI Install Instructions + - template: /eng/common/pipelines/templates/steps/login-to-github.yml - ### Install scripts + - task: DownloadPipelineArtifact@2 + displayName: Download docs artifact + inputs: + artifactName: docs + targetPath: docs - #### MacOS/Linux + - pwsh: | + $PRNumber = '$(System.PullRequest.PullRequestNumber)' + if ($env:PRNUMBEROVERRIDE) { + Write-Host "PR Number override: $($env:PRNUMBEROVERRIDE)" + $PRNumber = "$($env:PRNUMBEROVERRIDE)" + } + Write-Host "##vso[task.setvariable variable=PRNumber]$PRNumber" + displayName: Set PR Number Variable - > May elevate using ``sudo`` on some platforms and configurations + - pwsh: | + $urlBase = "$(publish-storage-static-host)/azd/standalone/pr/$(PRNumber)" + Write-Host "##vso[task.setvariable variable=UrlBase;isOutput=true]$urlBase" + name: GenerateUrlBase + displayName: Set UrlBase - bash: - `````` - curl -fsSL $urlBase/uninstall-azd.sh | bash; - curl -fsSL $urlBase/install-azd.sh | bash -s -- --base-url $urlBase --version '' --verbose --skip-verify - `````` + - pwsh: | + $urlBase = "$(GenerateUrlBase.UrlBase)" + $linuxReleaseUrl = "$urlBase/azd-linux-amd64.tar.gz" + $linuxReleaseUrlArm64 = "$urlBase/azd-linux-arm64.tar.gz" + $macosReleaseUrl = "$urlBase/azd-darwin-amd64.zip" + $macosReleaseUrlArm64 = "$urlBase/azd-darwin-arm64.zip" + $windowsReleaseUrl = "$urlBase/azd-windows-amd64.zip" + $windowsReleaseUrlArm64 = "$urlBase/azd-windows-arm64-alpha.zip" + $msiReleaseUrl = "$urlBase/azd-windows-amd64.msi" - pwsh: - `````` - Invoke-RestMethod '$urlBase/uninstall-azd.ps1' -OutFile uninstall-azd.ps1; ./uninstall-azd.ps1 - Invoke-RestMethod '$urlBase/install-azd.ps1' -OutFile install-azd.ps1; ./install-azd.ps1 -BaseUrl '$urlBase' -Version '' -SkipVerify -Verbose - `````` + $refDocContent = Get-Content docs/azd.md -Raw + $content = @" + + ## Azure Dev CLI Install Instructions - #### Windows + ### Install scripts - PowerShell install + #### MacOS/Linux - `````` - powershell -c "Set-ExecutionPolicy Bypass Process; irm '$urlBase/uninstall-azd.ps1' > uninstall-azd.ps1; ./uninstall-azd.ps1;" - powershell -c "Set-ExecutionPolicy Bypass Process; irm '$urlBase/install-azd.ps1' > install-azd.ps1; ./install-azd.ps1 -BaseUrl '$urlBase' -Version '' -SkipVerify -Verbose;" - `````` + > May elevate using ``sudo`` on some platforms and configurations - MSI install - `````` - powershell -c "irm '$msiReleaseUrl' -OutFile azd-windows-amd64.msi; msiexec /i azd-windows-amd64.msi /qn" - `````` + bash: + `````` + curl -fsSL $urlBase/uninstall-azd.sh | bash; + curl -fsSL $urlBase/install-azd.sh | bash -s -- --base-url $urlBase --version '' --verbose --skip-verify + `````` - ### Standalone Binary + pwsh: + `````` + Invoke-RestMethod '$urlBase/uninstall-azd.ps1' -OutFile uninstall-azd.ps1; ./uninstall-azd.ps1 + Invoke-RestMethod '$urlBase/install-azd.ps1' -OutFile install-azd.ps1; ./install-azd.ps1 -BaseUrl '$urlBase' -Version '' -SkipVerify -Verbose + `````` - * Linux - - * x86_64 - $linuxReleaseUrl - * ARM64 - $linuxReleaseUrlArm64 - * MacOS - - * x86_64 - $macosReleaseUrl - * ARM64 - $macosReleaseUrlArm64 - * Windows - - * x86_64 - $windowsReleaseUrl - * ARM64 - $windowsReleaseUrlArm64 - ### MSI + #### Windows - * x86_64 - $msiReleaseUrl + PowerShell install - ## Documentation + `````` + powershell -c "Set-ExecutionPolicy Bypass Process; irm '$urlBase/uninstall-azd.ps1' > uninstall-azd.ps1; ./uninstall-azd.ps1;" + powershell -c "Set-ExecutionPolicy Bypass Process; irm '$urlBase/install-azd.ps1' > install-azd.ps1; ./install-azd.ps1 -BaseUrl '$urlBase' -Version '' -SkipVerify -Verbose;" + `````` -
- learn.microsoft.com documentation + MSI install + `````` + powershell -c "irm '$msiReleaseUrl' -OutFile azd-windows-amd64.msi; msiexec /i azd-windows-amd64.msi /qn" + `````` - $refDocContent + ### Standalone Binary -
- "@ - $file = New-TemporaryFile - Set-Content -Path $file -Value $content - Write-Host "##vso[task.setvariable variable=CommentBodyFile]$file" - displayName: Write body content to temporary file + * Linux - + * x86_64 - $linuxReleaseUrl + * ARM64 - $linuxReleaseUrlArm64 + * MacOS - + * x86_64 - $macosReleaseUrl + * ARM64 - $macosReleaseUrlArm64 + * Windows - + * x86_64 - $windowsReleaseUrl + * ARM64 - $windowsReleaseUrlArm64 - - template: /eng/pipelines/templates/steps/update-prcomment.yml - parameters: - PrNumber: $(PRNumber) - BodyFile: $(CommentBodyFile) - Tag: '' + ### MSI + + * x86_64 - $msiReleaseUrl + + ## Documentation + +
+ learn.microsoft.com documentation + + $refDocContent + +
+ "@ + $file = New-TemporaryFile + Set-Content -Path $file -Value $content + Write-Host "##vso[task.setvariable variable=CommentBodyFile]$file" + displayName: Write body content to temporary file + + - template: /eng/pipelines/templates/steps/update-prcomment.yml + parameters: + PrNumber: $(PRNumber) + BodyFile: $(CommentBodyFile) + Tag: '' + GitHubToken: $(GH_TOKEN) - stage: PublishInstallers dependsOn: Sign diff --git a/eng/pipelines/templates/stages/vscode-publish-integration.yml b/eng/pipelines/templates/stages/vscode-publish-integration.yml index 4f73d9824c4..37b572cc5ff 100644 --- a/eng/pipelines/templates/stages/vscode-publish-integration.yml +++ b/eng/pipelines/templates/stages/vscode-publish-integration.yml @@ -100,28 +100,65 @@ stages: parameters: PublishLocations: azd/vscode/pr/$(PRNumber) - - pwsh: | - $downloadUrl = "$(publish-storage-static-host)/azd/vscode/pr/$(PRNumber)/azure-dev-$(VSIX_VERSION).vsix" - - $content = @" - - ## VSCode Extension Installation Instructions - - 1. Download the extension at $downloadUrl - 2. Extract the extension from the compressed file - 3. In vscode - a. Open "Extensions" (Ctrl+Shift+X) - b. Click the \`...\` menu at top of Extensions sidebar - c. Click "Install from VSIX" - d. Select location of downloaded file - "@ - $file = New-TemporaryFile - Set-Content -Path $file -Value $content - Write-Host "##vso[task.setvariable variable=CommentBodyFile]$file" - displayName: Write body content to temporary file - - - template: /eng/pipelines/templates/steps/update-prcomment.yml - parameters: - PrNumber: $(PRNumber) - Tag: '' - BodyFile: $(CommentBodyFile) + - job: Comment_On_PR + dependsOn: Publish_For_PR + condition: >- + and( + succeeded(), + ne(variables['Skip.Release'], 'true'), + or( + eq('PullRequest', variables['BuildReasonOverride']), + and( + eq('', variables['BuildReasonOverride']), + eq(variables['Build.Reason'], 'PullRequest') + ) + ) + ) + + pool: + name: $(LINUXPOOL) + image: $(LINUXVMIMAGE) + os: linux + + steps: + - checkout: self + + - template: /eng/common/pipelines/templates/steps/login-to-github.yml + + - template: /eng/pipelines/templates/steps/vscode-set-metadata-variables.yml + + - pwsh: | + $PRNumber = '$(System.PullRequest.PullRequestNumber)' + if ($env:PRNUMBEROVERRIDE) { + Write-Host "PR Number override: $($env:PRNUMBEROVERRIDE)" + $PRNumber = $env:PRNUMBEROVERRIDE + } + Write-Host "##vso[task.setvariable variable=PRNumber]$PRNumber" + displayName: Set PR Number Variable + + - pwsh: | + $downloadUrl = "$(publish-storage-static-host)/azd/vscode/pr/$(PRNumber)/azure-dev-$(VSIX_VERSION).vsix" + + $content = @" + + ## VSCode Extension Installation Instructions + + 1. Download the extension at $downloadUrl + 2. Extract the extension from the compressed file + 3. In vscode + a. Open "Extensions" (Ctrl+Shift+X) + b. Click the \`...\` menu at top of Extensions sidebar + c. Click "Install from VSIX" + d. Select location of downloaded file + "@ + $file = New-TemporaryFile + Set-Content -Path $file -Value $content + Write-Host "##vso[task.setvariable variable=CommentBodyFile]$file" + displayName: Write body content to temporary file + + - template: /eng/pipelines/templates/steps/update-prcomment.yml + parameters: + PrNumber: $(PRNumber) + Tag: '' + BodyFile: $(CommentBodyFile) + GitHubToken: $(GH_TOKEN) \ No newline at end of file diff --git a/eng/pipelines/templates/steps/publish-extension-github-release.yml b/eng/pipelines/templates/steps/publish-extension-github-release.yml new file mode 100644 index 00000000000..ab49fd3e67c --- /dev/null +++ b/eng/pipelines/templates/steps/publish-extension-github-release.yml @@ -0,0 +1,56 @@ +parameters: + - name: TagPrefix + type: string + - name: TagVersion + type: string + +steps: + # Fail fast if the tag/release already exists. With this split, retrying the + # downstream storage-upload deployment job won't trip this guard — only a + # retry of THIS regular job would. + - pwsh: | + $tag = "${{ parameters.TagPrefix }}_${{ parameters.TagVersion }}" + Write-Host "Release tag: $tag" + + $existingTag = gh api /repos/$(Build.Repository.Name)/tags | ConvertFrom-Json | Where-Object { $_.name -eq $tag } + if ($existingTag) { + Write-Host "Tag $tag already exists. Exiting." + exit 1 + } + + gh release view $tag --repo $(Build.Repository.Name) + if ($LASTEXITCODE -eq 0) { + Write-Host "Release ($tag) already exists. Exiting." + exit 1 + } + + Write-Host "##vso[task.setvariable variable=GH_RELEASE_TAG;]$tag" + exit 0 + displayName: Check for existing GitHub release + env: + GH_TOKEN: $(GH_TOKEN) + + - pwsh: | + if (Test-Path -Path release/_manifest) { + Remove-Item -Path release/_manifest -Recurse -Force + } + Write-Host "Release:" + Get-ChildItem -Recurse release/ | Select-Object -Property Length,FullName + displayName: Remove _manifest folder + + - pwsh: | + $version = "${{ parameters.TagVersion }}" + $createArgs = @( + "$(GH_RELEASE_TAG)", + "--title", "$(GH_RELEASE_TAG)", + "--notes-file", "changelog/CHANGELOG.md", + "--repo", "$(Build.Repository.Name)" + ) + if ($version -match "^0\." -or $version -match "-(alpha|beta|preview)") { + $createArgs += "--prerelease" + } + gh release create @createArgs + gh release upload $(GH_RELEASE_TAG) release/* --repo $(Build.Repository.Name) + displayName: Create GitHub Release and upload artifacts + env: + GH_TOKEN: $(GH_TOKEN) \ No newline at end of file diff --git a/eng/pipelines/templates/steps/publish-extension-storage.yml b/eng/pipelines/templates/steps/publish-extension-storage.yml new file mode 100644 index 00000000000..dd2a17bc60a --- /dev/null +++ b/eng/pipelines/templates/steps/publish-extension-storage.yml @@ -0,0 +1,37 @@ +parameters: + - name: PublishUploadLocations + type: string + - name: StorageContainerName + type: string + default: '`$web' + +steps: + - pwsh: | + if (Test-Path -Path release/_manifest) { + Remove-Item -Path release/_manifest -Recurse -Force + } + Write-Host "Release:" + Get-ChildItem -Recurse release/ | Select-Object -Property Length,FullName + displayName: Remove _manifest folder + + - task: AzurePowerShell@5 + displayName: Upload release to storage account + inputs: + azureSubscription: 'Azure SDK Artifacts' + azurePowerShellVersion: LatestVersion + pwsh: true + ScriptType: InlineScript + Inline: | + $uploadLocations = "${{ parameters.PublishUploadLocations }}" -split ';' + + Get-ChildItem release/ + foreach ($folder in $uploadLocations) { + Write-Host "Upload to ${{ parameters.StorageContainerName }}/azd/extensions/$folder" + azcopy copy "release/*" "$(publish-storage-location)/${{ parameters.StorageContainerName }}/azd/extensions/$folder" --overwrite=true + if ($LASTEXITCODE) { + Write-Error "Upload failed" + exit 1 + } + } + env: + AZCOPY_AUTO_LOGIN_TYPE: 'PSCRED' \ No newline at end of file diff --git a/eng/pipelines/templates/steps/publish-extension.yml b/eng/pipelines/templates/steps/publish-extension.yml deleted file mode 100644 index 8bf6cf942e2..00000000000 --- a/eng/pipelines/templates/steps/publish-extension.yml +++ /dev/null @@ -1,93 +0,0 @@ -parameters: - - name: PublishUploadLocations - type: string - - name: StorageContainerName - type: string - default: '`$web' - - name: TagPrefix - type: string - - name: TagVersion - type: string - # When false (nightly), skip the GitHub release and only upload to storage. - - name: CreateGitHubRelease - type: boolean - default: true - -steps: - - ${{ if eq(parameters.CreateGitHubRelease, true) }}: - # This step must run first because a duplicated tag means we don't need to - # continue with any of the subsequent steps. - - pwsh: | - $tag = "${{ parameters.TagPrefix }}_${{ parameters.TagVersion}}" - Write-Host "Release tag: $tag" - - # Check for tag using gh API - $existingTag = gh api /repos/$(Build.Repository.Name)/tags | ConvertFrom-Json | Where-Object { $_.name -eq $tag } - if ($existingTag) { - Write-Host "Tag $tag already exists. Exiting." - exit 1 - } - - gh release view $tag --repo $(Build.Repository.Name) - if ($LASTEXITCODE -eq 0) { - Write-Host "Release ($tag) already exists. Exiting." - exit 1 - } - - Write-Host "##vso[task.setvariable variable=GH_RELEASE_TAG;]$tag" - - # Exit with 0 (otherwise $LASTEXITCODE will not be 0 and the pipeline - # will fail) - exit 0 - displayName: Check for existing GitHub release - env: - GH_TOKEN: $(azuresdk-github-pat) - - - pwsh: | - Remove-Item -Path release/_manifest -Recurse -Force - Write-Host "Release:" - Get-ChildItem -Recurse release/ | Select-Object -Property Length,FullName - displayName: Remove _manifest folder - - - ${{ if eq(parameters.CreateGitHubRelease, true) }}: - - pwsh: | - $version = "${{ parameters.TagVersion }}" - $createArgs = @( - "$(GH_RELEASE_TAG)", - "--title", "$(GH_RELEASE_TAG)", - "--notes-file", "changelog/CHANGELOG.md", - "--repo", "$(Build.Repository.Name)" - ) - - if ($version -match "^0\." -or $version -match "-(alpha|beta|preview)") { - $createArgs += "--prerelease" - } - - gh release create @createArgs - - gh release upload $(GH_RELEASE_TAG) release/* --repo $(Build.Repository.Name) - displayName: Create GitHub Release and upload artifacts - env: - GH_TOKEN: $(azuresdk-github-pat) - - - task: AzurePowerShell@5 - displayName: Upload release to storage account - inputs: - azureSubscription: 'Azure SDK Artifacts' - azurePowerShellVersion: LatestVersion - pwsh: true - ScriptType: InlineScript - Inline: | - $uploadLocations = "${{ parameters.PublishUploadLocations }}" -split ';' - - Get-ChildItem release/ - foreach ($folder in $uploadLocations) { - Write-Host "Upload to ${{ parameters.StorageContainerName }}/azd/extensions/$folder" - azcopy copy "release/*" "$(publish-storage-location)/${{ parameters.StorageContainerName }}/azd/extensions/$folder" --overwrite=true - if ($LASTEXITCODE) { - Write-Error "Upload failed" - exit 1 - } - } - env: - AZCOPY_AUTO_LOGIN_TYPE: 'PSCRED' diff --git a/eng/pipelines/templates/steps/update-prcomment.yml b/eng/pipelines/templates/steps/update-prcomment.yml index bfc71810d38..f4c5e728424 100644 --- a/eng/pipelines/templates/steps/update-prcomment.yml +++ b/eng/pipelines/templates/steps/update-prcomment.yml @@ -13,7 +13,7 @@ parameters: default: '' - name: GitHubToken type: string - default: $(azuresdk-github-pat) + default: $(GH_TOKEN) steps: - pwsh: | diff --git a/ext/vscode/README.md b/ext/vscode/README.md index c37d2ce5e25..21b3ca486c1 100644 --- a/ext/vscode/README.md +++ b/ext/vscode/README.md @@ -111,3 +111,4 @@ See [the contribution guidelines](CONTRIBUTING.md) for ideas and guidance on how [MIT](https://github.com/Azure/azure-dev/LICENSE.md) +Test Changes \ No newline at end of file