Skip to content
Closed
3 changes: 2 additions & 1 deletion .vscode/cspell-devops-ext.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ codepaths
LASTEXITCODE
tmrm
toolrunner
TOOLSDIRECTORY
tsbuildinfo
vsix
vsix
25 changes: 25 additions & 0 deletions eng/pipelines/release-azuredevops.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Continuous deployment trigger
trigger:
branches:
include:
- main
paths:
include:
- ext/azuredevops
- eng/pipelines/release-azuredevops.yml

pr:
paths:
include:
- ext/azuredevops
- eng/pipelines/release-azuredevops.yml

extends:
template: /eng/pipelines/templates/stages/1es-redirect.yml
parameters:
stages:
- template: /eng/pipelines/templates/stages/azuredevops-build-and-test.yml

- template: /eng/pipelines/templates/stages/azuredevops-sign.yml

- template: /eng/pipelines/templates/stages/azuredevops-publish-manual.yml
72 changes: 72 additions & 0 deletions eng/pipelines/templates/jobs/azuredevops-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
parameters:
- name: NameSuffix
type: string
- name: Pool
type: string
- name: ImageKey
type: string
default: image
- name: OSVmImage
type: string
- name: OS
type: string
- name: Variables
type: object

jobs:
- job: Build_${{ parameters.NameSuffix }}
displayName: Build ${{ parameters.NameSuffix }}

pool:
name: ${{ parameters.Pool }}
${{ parameters.ImageKey }}: ${{ parameters.OSVmImage }}
os: ${{ parameters.OS }}

variables:
# TODO: What version of Node to use for Azure DevOps extension build?
NodeVersion: 20.x
${{ insert }}: ${{ parameters.Variables }}

steps:
- checkout: self

- task: NodeTool@0
inputs:
versionSpec: $(NodeVersion)

- pwsh: |
npm i -g npm tfx-cli
npm ci
displayName: Install build tools and dependencies
workingDirectory: ext/azuredevops/setupAzd

# TODO: If private build, update vss-extension.json and other JSONs to
# set a dev version

- task: PowerShell@2
inputs:
targetType: 'filePath'
filePath: '$(Build.SourcesDirectory)/ext/azuredevops/ci-test.ps1'
workingDirectory: '$(Build.SourcesDirectory)/ext/azuredevops'
displayName: Test

- task: PowerShell@2
inputs:
targetType: 'filePath'
filePath: '$(Build.SourcesDirectory)/ext/azuredevops/ci-package.ps1'
workingDirectory: '$(Build.SourcesDirectory)/ext/azuredevops'
displayName: Package Extension

- pwsh: |
Copy-Item `
-Path "$(Build.SourcesDirectory)/ext/azuredevops/*.vsix" `
-Destination "$(Build.ArtifactStagingDirectory)/"
condition: and(succeeded(), eq('true', variables['UploadArtifact']))
displayName: Copy VSIX to staging directory

templateContext:
outputs:
- output: pipelineArtifact
path: $(Build.ArtifactStagingDirectory)
condition: and(succeeded(), eq('true', variables['UploadArtifact']))
artifact: vsix
2 changes: 1 addition & 1 deletion eng/pipelines/templates/stages/1es-redirect.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ resources:
- repository: azure-sdk-build-tools
type: git
name: internal/azure-sdk-build-tools
ref: refs/tags/azure-sdk-build-tools_20251112.1
ref: refs/tags/azure-sdk-build-tools_20260112.5

parameters:
- name: stages
Expand Down
45 changes: 45 additions & 0 deletions eng/pipelines/templates/stages/azuredevops-build-and-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
parameters:
- name: BuildMatrix
type: object
default:
Windows:
Pool: $(WINDOWSPOOL)
ImageKey: image
OSVmImage: $(WINDOWSVMIMAGE)
OS: windows
Variables: {}

Linux:
Pool: $(LINUXPOOL)
ImageKey: image
OSVmImage: $(LINUXVMIMAGE)
OS: linux
Variables:
UploadArtifact: 'true'
Codeql.Enabled: true
Codeql.SkipTaskAutoInjection: false
Codeql.BuildIdentifier: azuredevops_linux

Mac:
Pool: Azure Pipelines
ImageKey: vmImage
OSVmImage: $(MACVMIMAGE)
OS: macOS
Variables: {}

stages:
- stage: BuildAndTest
variables:
- template: /eng/pipelines/templates/variables/globals.yml
- template: /eng/pipelines/templates/variables/image.yml

jobs:
- ${{ each build in parameters.BuildMatrix }}:
- template: /eng/pipelines/templates/jobs/azuredevops-build.yml
parameters:
NameSuffix: ${{ build.key }}
Pool: ${{ build.value.Pool }}
ImageKey: ${{ build.value.ImageKey }}
OSVmImage: ${{ build.value.OSVmImage }}
OS: ${{ build.value.OS }}
Variables: ${{ build.value.Variables }}
64 changes: 64 additions & 0 deletions eng/pipelines/templates/stages/azuredevops-publish-manual.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
stages:
- stage: PublishManual
dependsOn: Sign
condition: >-
and(
succeeded(),
ne(variables['Skip.Release'], 'true'),
or(
eq('Manual', variables['BuildReasonOverride']),
and(
eq('', variables['BuildReasonOverride']),
eq(variables['Build.Reason'], 'Manual')
)
)
)

variables:
- template: /eng/pipelines/templates/variables/globals.yml
- template: /eng/pipelines/templates/variables/image.yml

jobs:
- deployment: Publish_Release
environment: package-publish
pool:
name: azsdk-pool
image: ubuntu-24.04
os: linux

templateContext:
type: releaseJob
isProduction: true
inputs:
- input: pipelineArtifact
artifactName: signed
targetPath: signed

strategy:
runOnce:
deploy:
steps:
- pwsh: |
$files = Get-ChildItem -Path "$(Build.SourcesDirectory)/signed/*.vsix"
if ($files -is [Array]) {
if ($files.Count -gt 1) {
Write-Host "More than one VSIX file found in signed artifact."
exit 1
}
$vsixFile = $files[0].FullName
} else {
$vsixFile = $files.FullName
}

Write-Host "##vso[task.setvariable variable=VSIX_FILE]$vsixFile"
displayName: Get VSIX Filename

- task: 1ES.PublishAzureDevOpsExtension@1
displayName: Publish Extension
inputs:
targetPath: "$(Build.SourcesDirectory)/signed"
connectedServiceNameAzureRM: azure-sdk-vsmarketplace
fileType: vsix
vsixFile: "$(VSIX_FILE)"
useV5: true
validateExtension: false
54 changes: 54 additions & 0 deletions eng/pipelines/templates/stages/azuredevops-sign.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
stages:
- stage: Sign
dependsOn: BuildAndTest
variables:
- template: /eng/pipelines/templates/variables/globals.yml
- template: /eng/pipelines/templates/variables/image.yml

jobs:
- job: Sign
${{ if in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'Manual') }}:
displayName: Sign
${{ else }}:
displayName: SKIP Signing

pool:
name: $(WINDOWSPOOL)
image: $(WINDOWSVMIMAGE)
os: windows

steps:
- task: DownloadPipelineArtifact@2
inputs:
artifact: vsix
path: vsix

- ${{ if in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI', 'Manual') }}:
- template: pipelines/steps/azd-azdo-extension-signing.yml@azure-sdk-build-tools
parameters:
Path: $(Build.SourcesDirectory)\vsix
Pattern: '*.vsix'

- ${{ else }}:
- pwsh: Write-Host "Skipping signing. Build reason - $(Build.Reason)"
displayName: Signing process skipped for non-release build

- pwsh: |
New-Item -ItemType Directory -Path signed
Copy-Item vsix/*.vsix signed/
displayName: Copy signing outputs
condition: always()

templateContext:
outputs:
- output: pipelineArtifact
condition: succeeded()
displayName: Publish Signed Artifacts
artifact: signed
path: signed/

- output: pipelineArtifact
condition: failed()
displayName: Publish failed Signed Artifacts
artifact: signed-FailedAttempt$(System.JobAttempt)
path: signed/
23 changes: 23 additions & 0 deletions ext/azuredevops/ci-package.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env pwsh

$originalLocation = Get-Location

try {
Set-Location $PSScriptRoot

Write-Host "Running tsc build"
npm --prefix $PSScriptRoot/setupAzd/ run build
if ($LASTEXITCODE) {
Write-Host "Build failed"
exit $LASTEXITCODE
}
Write-Host "Building Azure DevOps extension package"
tfx extension create --manifest-globs vss-extension.json
if ($LASTEXITCODE) {
Write-Host "Packaging failed"
exit $LASTEXITCODE
}

} finally {
Set-Location $originalLocation
}
2 changes: 2 additions & 0 deletions ext/azuredevops/ci-test.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env pwsh
npm --prefix $PSScriptRoot/setupAzd/ test
15 changes: 11 additions & 4 deletions ext/azuredevops/setupAzd/tests/_suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,20 @@ import * as ttm from 'azure-pipelines-task-lib/mock-test';
describe('Setup azd task tests', function () {

before(function() {
// Setup before tests
// Disable tool download for tests to prevent actual network calls
process.env.AGENT_TOOLSDIRECTORY = '/tmp/test-tools';
process.env.RUNNER_TOOL_CACHE = '/tmp/test-tools';
});

after(() => {
// Cleanup after tests
delete process.env.AGENT_TOOLSDIRECTORY;
delete process.env.RUNNER_TOOL_CACHE;
});

it('should succeed with default version (latest)', function(done: Mocha.Done) {
this.timeout(5000);
// Increased timeout for macOS where tool cache operations can take longer
this.timeout(30000);

const tp: string = path.join(__dirname, 'success.js');
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
Expand All @@ -30,7 +35,8 @@ describe('Setup azd task tests', function () {
});

it('should succeed with specific version', function(done: Mocha.Done) {
this.timeout(5000);
// Increased timeout for macOS compatibility
this.timeout(30000);

const tp: string = path.join(__dirname, 'successVersion.js');
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
Expand All @@ -47,7 +53,8 @@ describe('Setup azd task tests', function () {
});

it('should fail with invalid version', function(done: Mocha.Done) {
this.timeout(5000);
// Increased timeout for macOS compatibility
this.timeout(30000);

const tp: string = path.join(__dirname, 'invalidVersion.js');
const tr: ttm.MockTestRunner = new ttm.MockTestRunner(tp);
Expand Down
Loading