From b667d6eff8e9a72aee33f57969a9469770aea2d0 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <50652041+LeftTwixWand@users.noreply.github.com> Date: Tue, 5 Aug 2025 12:09:54 +0200 Subject: [PATCH 01/14] Add gulp build --test Added --test argument to gulp build command --- gulpfile.js | 78 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 42ddf7936..ca0db2df2 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -160,7 +160,7 @@ gulp.task("TaskModuleTest", gulp.series('copy:TaskModuleTest', function() { })); gulp.task('prepublish:TaskModulePublish', function (done) { - return del([TaskModulesTestRoot], done); + return del([TaskModulesTestRoot], done); }); gulp.task('TaskModulePublish', gulp.series('prepublish:TaskModulePublish', function (done) { @@ -651,7 +651,81 @@ function compileUIExtensions(extensionRoot) { }; } -gulp.task("build", gulp.series("compileNode")); +gulp.task("updateTestIds", function(cb) { + if (args.test) { + var buildExtensionsRoot = path.join(_buildRoot, 'Extensions'); + if (fs.existsSync(buildExtensionsRoot)) { + var extensionDirs = fs.readdirSync(buildExtensionsRoot).filter(function (file) { + return fs.statSync(path.join(buildExtensionsRoot, file)).isDirectory(); + }); + extensionDirs.forEach(function (extName) { + // Check multiple possible locations for vss-extension.json + var possiblePaths = [ + path.join(buildExtensionsRoot, extName, 'Src', 'vss-extension.json'), + path.join(buildExtensionsRoot, extName, 'src', 'vss-extension.json'), + path.join(buildExtensionsRoot, extName, 'vss-extension.json') + ]; + + var manifestPath = null; + for (var i = 0; i < possiblePaths.length; i++) { + if (fs.existsSync(possiblePaths[i])) { + manifestPath = possiblePaths[i]; + break; + } + } + + if (manifestPath) { + try { + // Read file and handle potential BOM + var fileContent = fs.readFileSync(manifestPath, 'utf8'); + // Remove BOM if present + if (fileContent.charCodeAt(0) === 0xFEFF) { + fileContent = fileContent.slice(1); + } + + var manifest = JSON.parse(fileContent); + var updated = false; + + // Check for both 'id' and 'extensionId' properties + var idProperty = manifest.id ? 'id' : (manifest.extensionId ? 'extensionId' : null); + + if (idProperty && manifest[idProperty] && !manifest[idProperty].endsWith('-test')) { + manifest[idProperty] = manifest[idProperty] + '-test'; + updated = true; + } + + // Always set public to false if it exists + if (manifest.hasOwnProperty('public') && manifest.public !== false) { + manifest.public = false; + updated = true; + } else if (!manifest.hasOwnProperty('public')) { + // Add public: false if it doesn't exist + manifest.public = false; + updated = true; + } + + if (updated) { + // Write back without BOM + fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); + console.log('Updated manifest for test build: ' + manifestPath); + console.log(' - ' + idProperty + ': ' + manifest[idProperty]); + console.log(' - Public: ' + manifest.public); + } else if (idProperty && manifest[idProperty].endsWith('-test')) { + console.log('Extension already has -test suffix: ' + manifestPath); + } + } catch (e) { + console.error('Failed to update manifest: ' + manifestPath + ' - ' + e.message); + } + } else { + console.log('No vss-extension.json found for extension: ' + extName); + } + }); + } + } + cb(); +}); + +gulp.task("build", gulp.series("compileNode", "updateTestIds")); gulp.task("handoff", function(cb) { handoff(cb); From 20e0f233b5dca7bb5de86962e0e4a36ef781e616 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Mon, 11 Aug 2025 11:59:48 +0200 Subject: [PATCH 02/14] Update name of the extension for test build --- gulpfile.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gulpfile.js b/gulpfile.js index 663038e88..c317e1e5c 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -694,6 +694,11 @@ gulp.task("updateTestIds", function(cb) { updated = true; } + // Update name property + if (manifest.hasOwnProperty('name')) { + manifest.name = `${manifest.name} (Test)`; + } + // Always set public to false if it exists if (manifest.hasOwnProperty('public') && manifest.public !== false) { manifest.public = false; From 398d07d81d457efc65cd3e8744d711dfa88c41e8 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Tue, 12 Aug 2025 12:51:24 +0200 Subject: [PATCH 03/14] Update extnsions categories to new standard --- Extensions/AzureExp/Src/vss-extension.json | 2 +- Extensions/BitBucket/Src/vss-extension.json | 2 +- Extensions/CircleCI/Src/vss-extension.json | 2 +- Extensions/ExternalTfs/Src/vss-extension.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Extensions/AzureExp/Src/vss-extension.json b/Extensions/AzureExp/Src/vss-extension.json index 311ca13ea..3345a254e 100644 --- a/Extensions/AzureExp/Src/vss-extension.json +++ b/Extensions/AzureExp/Src/vss-extension.json @@ -7,7 +7,7 @@ "description": "Azure Exp feature rollout", "public": false, "categories": [ - "Build and release" + "Azure Pipelines" ], "files": [ { diff --git a/Extensions/BitBucket/Src/vss-extension.json b/Extensions/BitBucket/Src/vss-extension.json index e3ab1294f..5608fe76b 100644 --- a/Extensions/BitBucket/Src/vss-extension.json +++ b/Extensions/BitBucket/Src/vss-extension.json @@ -7,7 +7,7 @@ "public": true, "description": "Tools related to connecting with Bitbucket", "_description.comment": "The below format to define artifact extensions is currently in preview and may change in future.", - "categories": [ "Integrate" ], + "categories": [ "Azure Pipelines" ], "Tags": [ "Bitbucket", "Release", "DevOps", "Artifacts" ], "targets": [ { diff --git a/Extensions/CircleCI/Src/vss-extension.json b/Extensions/CircleCI/Src/vss-extension.json index ad04f1ee8..d8c45a772 100644 --- a/Extensions/CircleCI/Src/vss-extension.json +++ b/Extensions/CircleCI/Src/vss-extension.json @@ -7,7 +7,7 @@ "public": true, "description": "Tool for connecting with CircleCI", "_description.comment": "The below format to define artifact extensions is currently in preview and may change in future.", - "categories": [ "Build and release", "Azure pipelines" ], + "categories": [ "Azure Pipelines" ], "Tags": [ "CircleCI", "Release", "DevOps", "Artifacts", "Pipelines" ], "targets": [ { diff --git a/Extensions/ExternalTfs/Src/vss-extension.json b/Extensions/ExternalTfs/Src/vss-extension.json index 21d15c0e6..90d24dd75 100644 --- a/Extensions/ExternalTfs/Src/vss-extension.json +++ b/Extensions/ExternalTfs/Src/vss-extension.json @@ -7,7 +7,7 @@ "description": "Deploy external TFS/ Azure DevOps artifacts using Release Management", "_description.comment": "The below format to define artifact extensions is currently in preview and may change in future.", "public": true, - "categories": [ "Build and release" ], + "categories": [ "Azure Pipelines" ], "tags": [ "Artifacts", "DevOps", "Release" ], "targets": [ { From c8130fc3d7100d1283972c932d392fe8b95c75ff Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Wed, 13 Aug 2025 11:50:07 +0200 Subject: [PATCH 04/14] Final update for azure pipeline with tests stages --- .pipelines/1es-migration/azure-pipelines.yml | 274 ++++++++++++++---- Extensions/Ansible/Src/vss-extension.json | 2 +- .../IISWebAppDeploy/Src/vss-extension.json | 2 +- 3 files changed, 214 insertions(+), 64 deletions(-) diff --git a/.pipelines/1es-migration/azure-pipelines.yml b/.pipelines/1es-migration/azure-pipelines.yml index ad80c9984..f4bb79205 100644 --- a/.pipelines/1es-migration/azure-pipelines.yml +++ b/.pipelines/1es-migration/azure-pipelines.yml @@ -1,14 +1,11 @@ # Azure Pipelines Extensions 1ES Build Pipeline -# This pipeline builds, signs, and publishes Azure DevOps extensions using 1ES standards -# Compliant with Secure Extension Onboarding initiative +# Builds, signs, and publishes Azure DevOps extensions +# Main and TEST variants are built in separate stages to avoid packaging overwrites name: Extension $(ExtensionName) - $(Date:yyyyMMdd)$(Rev:.r) appendCommitMessageToRunName: false trigger: none -# branches: -# include: -# - main parameters: - name: extensionName @@ -41,15 +38,11 @@ parameters: default: false variables: -# 1ES Security Scanning - name: CodeQL.Enabled value: true -# Extension registry settings - name: PublisherId value: 'ms-vscs-rm' -# Signing variables from secure group - group: EPS.ESRPSigningProdAME -# Dynamic variables - name: ExtensionName value: ${{ parameters.extensionName }} - name: IsMainBranchBuild @@ -57,7 +50,6 @@ variables: resources: repositories: - # 1ES Pipeline Templates - repository: 1ESPipelineTemplates type: git name: 1ESPipelineTemplates/1ESPipelineTemplates @@ -66,24 +58,19 @@ resources: extends: template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates parameters: - # SDL Configuration sdl: sourceAnalysisPool: name: 1ESPtTfsAgentBuildPoolSDL spotBugs: - enabled: false # Not applicable for TypeScript/JavaScript + enabled: false credscan: enabled: true binskim: enabled: true eslint: enabled: true - - # Build Pool pool: name: 1ESPtTfsAgentBuildPool1 - - # Custom Build Tags customBuildTags: - ES365AIMigrationTooling - 1ES-AzureExtensions @@ -92,20 +79,20 @@ extends: stages: - - stage: BuildAndTest - displayName: 'Build and Test ${{ parameters.extensionName }}' + # =============================== Build MAIN =============================== + - stage: BuildMain + displayName: 'Build MAIN ${{ parameters.extensionName }}' jobs: - - job: BuildAndTestJob - displayName: 'Build Extension' + - job: BuildMainJob + displayName: 'Build MAIN' templateContext: outputs: - output: pipelineArtifact - displayName: 'Publish Build Artifacts (Unsigned)' - targetPath: '$(System.ArtifactsDirectory)' + displayName: 'Publish MAIN Build Artifacts (Unsigned)' + targetPath: '$(System.ArtifactsDirectory)/unsigned' artifactName: 'vsix-unsigned' steps: - checkout: self - displayName: 'Checkout Extensions Repository' clean: true fetchTags: false @@ -124,7 +111,7 @@ extends: displayName: 'Install TFX CLI' inputs: command: 'custom' - customCommand: 'install -g tfx-cli' + customCommand: 'install -g tfx-cli' - task: Npm@1 displayName: 'Install Gulp CLI' @@ -133,57 +120,166 @@ extends: customCommand: 'install -g gulp-cli' - task: PowerShell@2 - displayName: 'Build Extensions with Gulp' + displayName: 'Clean (gulp clean)' inputs: - targetType: 'inline' + targetType: inline + script: 'gulp clean' + workingDirectory: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + displayName: 'Build MAIN (gulp build)' + inputs: + targetType: inline script: 'gulp build' workingDirectory: '$(Build.SourcesDirectory)' - task: PowerShell@2 - displayName: 'Package Extensions with Gulp' + displayName: 'Package MAIN (gulp package)' + inputs: + targetType: inline + script: 'gulp package' + workingDirectory: '$(Build.SourcesDirectory)' + + - task: CopyFiles@2 + displayName: 'Copy MAIN VSIX to Artifacts' + inputs: + SourceFolder: '$(Build.SourcesDirectory)/_package' + Contents: '${{ parameters.extensionName }}/*.vsix' + TargetFolder: '$(System.ArtifactsDirectory)/unsigned' + flattenFolders: true + + # =============================== Build TEST =============================== + - stage: BuildTest + displayName: 'Build TEST ${{ parameters.extensionName }}' + dependsOn: BuildMain + jobs: + - job: BuildTestJob + displayName: 'Build TEST' + templateContext: + outputs: + - output: pipelineArtifact + displayName: 'Publish TEST Build Artifacts (Unsigned)' + targetPath: '$(System.ArtifactsDirectory)/test-unsigned' + artifactName: 'vsix-test-unsigned' + steps: + - checkout: self + clean: true + fetchTags: false + + - task: NodeTool@0 + displayName: 'Install Node.js' + inputs: + versionSpec: '10.x' + + - task: Npm@1 + displayName: 'Install Dependencies' + inputs: + command: 'install' + verbose: false + + - task: Npm@1 + displayName: 'Install TFX CLI' + inputs: + command: 'custom' + customCommand: 'install -g tfx-cli' + + - task: Npm@1 + displayName: 'Install Gulp CLI' + inputs: + command: 'custom' + customCommand: 'install -g gulp-cli' + + - task: PowerShell@2 + displayName: 'Clean (gulp clean)' + inputs: + targetType: inline + script: 'gulp clean' + workingDirectory: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + displayName: 'Build TEST (gulp build --test)' + inputs: + targetType: inline + script: 'gulp build --test' + workingDirectory: '$(Build.SourcesDirectory)' + + - task: PowerShell@2 + displayName: 'Package TEST (gulp package)' inputs: - targetType: 'inline' + targetType: inline script: 'gulp package' workingDirectory: '$(Build.SourcesDirectory)' - # Copy VSIX files to artifacts directory - task: CopyFiles@2 - displayName: 'Copy VSIX Files to Artifacts' + displayName: 'Copy TEST VSIX to Artifacts' inputs: SourceFolder: '$(Build.SourcesDirectory)/_package' Contents: '${{ parameters.extensionName }}/*.vsix' - TargetFolder: '$(System.ArtifactsDirectory)' + TargetFolder: '$(System.ArtifactsDirectory)/test-unsigned' flattenFolders: true + # =============================== Code Signing ============================= - stage: CodeSigning + displayName: 'Code Signing' condition: or(eq(variables['IsMainBranchBuild'], 'true'), ${{ eq(parameters.forceCodeSign, 'true') }}, ${{ eq(parameters.simulateCodeSigningError, 'true') }}) - dependsOn: BuildAndTest + dependsOn: + - BuildMain + - BuildTest jobs: - job: CodeSigningJob - displayName: CodeSigning + displayName: 'ESRP CodeSigning' templateContext: outputs: - output: pipelineArtifact - displayName: 'Signed vsix artifact' - targetPath: '$(System.ArtifactsDirectory)' + displayName: 'Signed MAIN vsix artifact' + targetPath: '$(System.ArtifactsDirectory)/signed' artifactName: vsix-signed + - output: pipelineArtifact + displayName: 'Signed TEST vsix artifact' + targetPath: '$(System.ArtifactsDirectory)/test-signed' + artifactName: vsix-test-signed steps: - download: current artifact: vsix-unsigned - displayName: Download Artifact + displayName: 'Download unsigned (MAIN)' + - download: current + artifact: vsix-test-unsigned + displayName: 'Download unsigned (TEST)' + - script: | echo "Simulated error in CodeSigning step." exit 1 - displayName: Simulate CodeSigning Error + displayName: 'Simulate CodeSigning Error' condition: ${{ eq(parameters.simulateCodeSigningError, 'true') }} + + - task: EsrpCodeSigning@5 + displayName: 'ESRP CodeSigning (MAIN)' + inputs: + ConnectedServiceName: '$(Control.EsrpServiceConnectionName)' + AppRegistrationClientId: '$(Control.AppRegistrationClientId)' + AppRegistrationTenantId: '$(Control.AppRegistrationTenantId)' + AuthAKVName: '$(Control.AuthAKVName)' + AuthCertName: '$(Control.AuthCertName)' + AuthSignCertName: '$(Control.AuthSignCertName)' + FolderPath: '$(Pipeline.Workspace)\vsix-unsigned' + Pattern: '*.vsix' + signConfigType: inlineSignParams + inlineOperation: |- + [ + {"KeyCode":"CP-500813","OperationCode":"AdoExtensionSign","ToolName":"sign","ToolVersion":"1.0","Parameters":{}}, + {"KeyCode":"CP-500813","OperationCode":"AdoExtensionVerify","ToolName":"sign","ToolVersion":"1.0","Parameters":{}} + ] + SessionTimeout: 30 + - task: CopyFiles@2 - displayName: 'Copy Files to: $(System.DefaultWorkingDirectory)' + displayName: 'Collect signed (MAIN)' inputs: SourceFolder: '$(Pipeline.Workspace)\vsix-unsigned' Contents: '*.vsix' - TargetFolder: $(System.DefaultWorkingDirectory) + TargetFolder: '$(System.ArtifactsDirectory)/signed' + - task: EsrpCodeSigning@5 - displayName: ESRP CodeSigning + displayName: 'ESRP CodeSigning (TEST)' inputs: ConnectedServiceName: '$(Control.EsrpServiceConnectionName)' AppRegistrationClientId: '$(Control.AppRegistrationClientId)' @@ -191,43 +287,97 @@ extends: AuthAKVName: '$(Control.AuthAKVName)' AuthCertName: '$(Control.AuthCertName)' AuthSignCertName: '$(Control.AuthSignCertName)' - FolderPath: $(System.DefaultWorkingDirectory) + FolderPath: '$(Pipeline.Workspace)\vsix-test-unsigned' Pattern: '*.vsix' signConfigType: inlineSignParams inlineOperation: |- - [ - { - "KeyCode": "CP-500813", - "OperationCode": "AdoExtensionSign", - "ToolName": "sign", - "ToolVersion": "1.0", - "Parameters": {} - }, - { - "KeyCode": "CP-500813", - "OperationCode": "AdoExtensionVerify", - "ToolName": "sign", - "ToolVersion": "1.0", - "Parameters": {} - } + [ + {"KeyCode":"CP-500813","OperationCode":"AdoExtensionSign","ToolName":"sign","ToolVersion":"1.0","Parameters":{}}, + {"KeyCode":"CP-500813","OperationCode":"AdoExtensionVerify","ToolName":"sign","ToolVersion":"1.0","Parameters":{}} ] SessionTimeout: 30 + - task: CopyFiles@2 - name: CopyFiles_5 - displayName: 'Copy Files to: $(System.ArtifactsDirectory)' + displayName: 'Collect signed (TEST)' inputs: + SourceFolder: '$(Pipeline.Workspace)\vsix-test-unsigned' Contents: '*.vsix' - TargetFolder: $(System.ArtifactsDirectory) + TargetFolder: '$(System.ArtifactsDirectory)/test-signed' - - stage: PublishToMarketplace + # ===================== Manual Approval BEFORE TEST publish ================= + - stage: WaitForTestApproval + displayName: 'Approve TEST publish' dependsOn: CodeSigning jobs: + - job: ManualApprovalForTest + displayName: 'Manual Approval (TEST)' + pool: server + steps: + - task: ManualValidation@0 + timeoutInMinutes: 1440 + inputs: + notifyUsers: 'vhorbachov@microsoft.com' + instructions: 'Approve this step to publish the TEST extension (private).' + + # ============================ Publish TEST (private) ======================= + - stage: PublishTestToMarketplace + displayName: 'Publish TEST extension (private)' + dependsOn: WaitForTestApproval + jobs: + - job: PublishTestVSIX + displayName: 'Publish TEST vsix to marketplace (private)' + steps: + - download: current + artifact: vsix-test-signed + displayName: 'Download Signed TEST Artifact' + + - task: NodeTool@0 + displayName: 'Install Node.js' + inputs: + versionSpec: '10.x' + + - task: TfxInstaller@5 + inputs: + version: 'v0.21.1' + + - task: 1ES.PublishAzureDevOpsExtension@1 + displayName: 'Publish TEST extension (private)' + inputs: + connectTo: 'AzureRM' + connectedServiceNameAzureRM: '1es-extensions-publication-secure-service-connection' + fileType: 'vsix' + vsixFile: '$(Pipeline.Workspace)/vsix-test-signed/*.vsix' + targetPath: '$(Pipeline.Workspace)/vsix-test-signed' + validateExtension: false + useV5: true + extensionVisibility: 'private' + + # ===================== Manual Approval BEFORE PUBLIC publish =============== + - stage: WaitForValidation + displayName: 'Approve PUBLIC publish' + dependsOn: PublishTestToMarketplace + jobs: + - job: ManualApproval + displayName: 'Manual Approval (PUBLIC)' + pool: server + steps: + - task: ManualValidation@0 + timeoutInMinutes: 1440 + inputs: + notifyUsers: 'vhorbachov@microsoft.com' + instructions: 'Approve this step to publish the PUBLIC extension.' + + # ================================ Publish PUBLIC =========================== + - stage: PublishToMarketplace + displayName: 'Publish PUBLIC extension' + dependsOn: WaitForValidation + jobs: - job: PublishVSIX - displayName: Publish vsix to marketplace + displayName: 'Publish vsix to marketplace' steps: - download: current artifact: vsix-signed - displayName: Download Signed Artifact + displayName: 'Download Signed Artifact' - task: NodeTool@0 displayName: 'Install Node.js' diff --git a/Extensions/Ansible/Src/vss-extension.json b/Extensions/Ansible/Src/vss-extension.json index 1a413fc27..d91d29fbf 100644 --- a/Extensions/Ansible/Src/vss-extension.json +++ b/Extensions/Ansible/Src/vss-extension.json @@ -3,7 +3,7 @@ "id": "vss-services-ansible", "name": "Ansible", "publisher": "ms-vscs-rm", - "version": "0.258.1", + "version": "0.261.0", "public": true, "description": "This extension executes an Ansible playbook using a specified inventory via command line interface", "_description.comment": "The below format to define extensions is currently in preview and may change in future.", diff --git a/Extensions/IISWebAppDeploy/Src/vss-extension.json b/Extensions/IISWebAppDeploy/Src/vss-extension.json index 6b773d9c7..e3a02e451 100644 --- a/Extensions/IISWebAppDeploy/Src/vss-extension.json +++ b/Extensions/IISWebAppDeploy/Src/vss-extension.json @@ -11,7 +11,7 @@ "large": "images/IIS_Web_App_Large.png" }, "categories": [ - "Build and release" + "Azure Pipelines" ], "tags": [ ], "links": { From 2af09ed21e8ba566049602f025ac9f999dcc37c6 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Wed, 13 Aug 2025 12:15:21 +0200 Subject: [PATCH 05/14] Improve build time by merging stages --- .pipelines/1es-migration/azure-pipelines.yml | 171 ++++++------------- 1 file changed, 53 insertions(+), 118 deletions(-) diff --git a/.pipelines/1es-migration/azure-pipelines.yml b/.pipelines/1es-migration/azure-pipelines.yml index f4bb79205..92a7a29cb 100644 --- a/.pipelines/1es-migration/azure-pipelines.yml +++ b/.pipelines/1es-migration/azure-pipelines.yml @@ -1,6 +1,5 @@ # Azure Pipelines Extensions 1ES Build Pipeline -# Builds, signs, and publishes Azure DevOps extensions -# Main and TEST variants are built in separate stages to avoid packaging overwrites +# Single build stage: MAIN -> copy, TEST -> copy; then sign, approve, publish (TEST then PUBLIC) name: Extension $(ExtensionName) - $(Date:yyyyMMdd)$(Rev:.r) appendCommitMessageToRunName: false @@ -38,11 +37,15 @@ parameters: default: false variables: +# 1ES Security Scanning - name: CodeQL.Enabled value: true +# Publisher - name: PublisherId value: 'ms-vscs-rm' +# ESRP Signing secrets - group: EPS.ESRPSigningProdAME +# Dynamic - name: ExtensionName value: ${{ parameters.extensionName }} - name: IsMainBranchBuild @@ -61,16 +64,14 @@ extends: sdl: sourceAnalysisPool: name: 1ESPtTfsAgentBuildPoolSDL - spotBugs: - enabled: false - credscan: - enabled: true - binskim: - enabled: true - eslint: - enabled: true + spotBugs: { enabled: false } + credscan: { enabled: true } + binskim: { enabled: true } + eslint: { enabled: true } + pool: name: 1ESPtTfsAgentBuildPool1 + customBuildTags: - ES365AIMigrationTooling - 1ES-AzureExtensions @@ -79,155 +80,90 @@ extends: stages: - # =============================== Build MAIN =============================== - - stage: BuildMain - displayName: 'Build MAIN ${{ parameters.extensionName }}' + # ============================== Build (MAIN+TEST) ============================== + - stage: Build + displayName: 'Build ${{ parameters.extensionName }} (MAIN + TEST)' jobs: - - job: BuildMainJob - displayName: 'Build MAIN' + - job: BuildJob + displayName: 'Build and Package MAIN, then TEST' templateContext: outputs: - output: pipelineArtifact displayName: 'Publish MAIN Build Artifacts (Unsigned)' targetPath: '$(System.ArtifactsDirectory)/unsigned' artifactName: 'vsix-unsigned' + - output: pipelineArtifact + displayName: 'Publish TEST Build Artifacts (Unsigned)' + targetPath: '$(System.ArtifactsDirectory)/test-unsigned' + artifactName: 'vsix-test-unsigned' steps: - checkout: self + displayName: 'Checkout' clean: true fetchTags: false - task: NodeTool@0 displayName: 'Install Node.js' - inputs: - versionSpec: '10.x' + inputs: { versionSpec: '10.x' } - task: Npm@1 displayName: 'Install Dependencies' - inputs: - command: 'install' - verbose: false + inputs: { command: 'install', verbose: false } - task: Npm@1 displayName: 'Install TFX CLI' - inputs: - command: 'custom' - customCommand: 'install -g tfx-cli' + inputs: { command: 'custom', customCommand: 'install -g tfx-cli' } - task: Npm@1 displayName: 'Install Gulp CLI' - inputs: - command: 'custom' - customCommand: 'install -g gulp-cli' + inputs: { command: 'custom', customCommand: 'install -g gulp-cli' } + # ---- MAIN build/package -> copy to unsigned ---- - task: PowerShell@2 - displayName: 'Clean (gulp clean)' - inputs: - targetType: inline - script: 'gulp clean' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Clean (MAIN) - gulp clean' + inputs: { targetType: inline, script: 'gulp clean', workingDirectory: '$(Build.SourcesDirectory)' } - task: PowerShell@2 - displayName: 'Build MAIN (gulp build)' - inputs: - targetType: inline - script: 'gulp build' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Build MAIN - gulp build' + inputs: { targetType: inline, script: 'gulp build', workingDirectory: '$(Build.SourcesDirectory)' } - task: PowerShell@2 - displayName: 'Package MAIN (gulp package)' - inputs: - targetType: inline - script: 'gulp package' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Package MAIN - gulp package' + inputs: { targetType: inline, script: 'gulp package', workingDirectory: '$(Build.SourcesDirectory)' } - task: CopyFiles@2 - displayName: 'Copy MAIN VSIX to Artifacts' + displayName: 'Copy MAIN VSIX to $(System.ArtifactsDirectory)/unsigned' inputs: SourceFolder: '$(Build.SourcesDirectory)/_package' Contents: '${{ parameters.extensionName }}/*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/unsigned' flattenFolders: true - # =============================== Build TEST =============================== - - stage: BuildTest - displayName: 'Build TEST ${{ parameters.extensionName }}' - dependsOn: BuildMain - jobs: - - job: BuildTestJob - displayName: 'Build TEST' - templateContext: - outputs: - - output: pipelineArtifact - displayName: 'Publish TEST Build Artifacts (Unsigned)' - targetPath: '$(System.ArtifactsDirectory)/test-unsigned' - artifactName: 'vsix-test-unsigned' - steps: - - checkout: self - clean: true - fetchTags: false - - - task: NodeTool@0 - displayName: 'Install Node.js' - inputs: - versionSpec: '10.x' - - - task: Npm@1 - displayName: 'Install Dependencies' - inputs: - command: 'install' - verbose: false - - - task: Npm@1 - displayName: 'Install TFX CLI' - inputs: - command: 'custom' - customCommand: 'install -g tfx-cli' - - - task: Npm@1 - displayName: 'Install Gulp CLI' - inputs: - command: 'custom' - customCommand: 'install -g gulp-cli' - - - task: PowerShell@2 - displayName: 'Clean (gulp clean)' - inputs: - targetType: inline - script: 'gulp clean' - workingDirectory: '$(Build.SourcesDirectory)' - + # ---- TEST build/package -> copy to test-unsigned ---- - task: PowerShell@2 - displayName: 'Build TEST (gulp build --test)' - inputs: - targetType: inline - script: 'gulp build --test' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Build TEST - gulp build --test' + inputs: { targetType: inline, script: 'gulp build --test', workingDirectory: '$(Build.SourcesDirectory)' } - task: PowerShell@2 - displayName: 'Package TEST (gulp package)' - inputs: - targetType: inline - script: 'gulp package' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Package TEST - gulp package' + inputs: { targetType: inline, script: 'gulp package', workingDirectory: '$(Build.SourcesDirectory)' } - task: CopyFiles@2 - displayName: 'Copy TEST VSIX to Artifacts' + displayName: 'Copy TEST VSIX to $(System.ArtifactsDirectory)/test-unsigned' inputs: SourceFolder: '$(Build.SourcesDirectory)/_package' Contents: '${{ parameters.extensionName }}/*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/test-unsigned' flattenFolders: true - # =============================== Code Signing ============================= + # ================================ Code Signing ================================ - stage: CodeSigning displayName: 'Code Signing' condition: or(eq(variables['IsMainBranchBuild'], 'true'), ${{ eq(parameters.forceCodeSign, 'true') }}, ${{ eq(parameters.simulateCodeSigningError, 'true') }}) - dependsOn: - - BuildMain - - BuildTest + dependsOn: Build jobs: - job: CodeSigningJob - displayName: 'ESRP CodeSigning' + displayName: 'ESRP CodeSigning (MAIN + TEST)' templateContext: outputs: - output: pipelineArtifact @@ -242,6 +178,7 @@ extends: - download: current artifact: vsix-unsigned displayName: 'Download unsigned (MAIN)' + - download: current artifact: vsix-test-unsigned displayName: 'Download unsigned (TEST)' @@ -252,6 +189,7 @@ extends: displayName: 'Simulate CodeSigning Error' condition: ${{ eq(parameters.simulateCodeSigningError, 'true') }} + # Sign MAIN - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning (MAIN)' inputs: @@ -278,6 +216,7 @@ extends: Contents: '*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/signed' + # Sign TEST - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning (TEST)' inputs: @@ -304,7 +243,7 @@ extends: Contents: '*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/test-signed' - # ===================== Manual Approval BEFORE TEST publish ================= + # ======================== Manual Approval BEFORE TEST publish ======================== - stage: WaitForTestApproval displayName: 'Approve TEST publish' dependsOn: CodeSigning @@ -319,7 +258,7 @@ extends: notifyUsers: 'vhorbachov@microsoft.com' instructions: 'Approve this step to publish the TEST extension (private).' - # ============================ Publish TEST (private) ======================= + # ================================= Publish TEST (private) ============================== - stage: PublishTestToMarketplace displayName: 'Publish TEST extension (private)' dependsOn: WaitForTestApproval @@ -333,12 +272,10 @@ extends: - task: NodeTool@0 displayName: 'Install Node.js' - inputs: - versionSpec: '10.x' + inputs: { versionSpec: '10.x' } - task: TfxInstaller@5 - inputs: - version: 'v0.21.1' + inputs: { version: 'v0.21.1' } - task: 1ES.PublishAzureDevOpsExtension@1 displayName: 'Publish TEST extension (private)' @@ -352,7 +289,7 @@ extends: useV5: true extensionVisibility: 'private' - # ===================== Manual Approval BEFORE PUBLIC publish =============== + # ======================== Manual Approval BEFORE PUBLIC publish ======================== - stage: WaitForValidation displayName: 'Approve PUBLIC publish' dependsOn: PublishTestToMarketplace @@ -367,7 +304,7 @@ extends: notifyUsers: 'vhorbachov@microsoft.com' instructions: 'Approve this step to publish the PUBLIC extension.' - # ================================ Publish PUBLIC =========================== + # ===================================== Publish PUBLIC ================================== - stage: PublishToMarketplace displayName: 'Publish PUBLIC extension' dependsOn: WaitForValidation @@ -381,12 +318,10 @@ extends: - task: NodeTool@0 displayName: 'Install Node.js' - inputs: - versionSpec: '10.x' + inputs: { versionSpec: '10.x' } - task: TfxInstaller@5 - inputs: - version: 'v0.21.1' + inputs: { version: 'v0.21.1' } - task: 1ES.PublishAzureDevOpsExtension@1 displayName: 'Publish the public extension to ms-vscs-rm' From 6c02b561244fc37c0584b1a804d787bbfdfe3b02 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Wed, 13 Aug 2025 12:15:21 +0200 Subject: [PATCH 06/14] Bump iis version --- .pipelines/1es-migration/azure-pipelines.yml | 171 ++++++------------ .../IISWebAppDeploy/Src/vss-extension.json | 2 +- 2 files changed, 54 insertions(+), 119 deletions(-) diff --git a/.pipelines/1es-migration/azure-pipelines.yml b/.pipelines/1es-migration/azure-pipelines.yml index f4bb79205..92a7a29cb 100644 --- a/.pipelines/1es-migration/azure-pipelines.yml +++ b/.pipelines/1es-migration/azure-pipelines.yml @@ -1,6 +1,5 @@ # Azure Pipelines Extensions 1ES Build Pipeline -# Builds, signs, and publishes Azure DevOps extensions -# Main and TEST variants are built in separate stages to avoid packaging overwrites +# Single build stage: MAIN -> copy, TEST -> copy; then sign, approve, publish (TEST then PUBLIC) name: Extension $(ExtensionName) - $(Date:yyyyMMdd)$(Rev:.r) appendCommitMessageToRunName: false @@ -38,11 +37,15 @@ parameters: default: false variables: +# 1ES Security Scanning - name: CodeQL.Enabled value: true +# Publisher - name: PublisherId value: 'ms-vscs-rm' +# ESRP Signing secrets - group: EPS.ESRPSigningProdAME +# Dynamic - name: ExtensionName value: ${{ parameters.extensionName }} - name: IsMainBranchBuild @@ -61,16 +64,14 @@ extends: sdl: sourceAnalysisPool: name: 1ESPtTfsAgentBuildPoolSDL - spotBugs: - enabled: false - credscan: - enabled: true - binskim: - enabled: true - eslint: - enabled: true + spotBugs: { enabled: false } + credscan: { enabled: true } + binskim: { enabled: true } + eslint: { enabled: true } + pool: name: 1ESPtTfsAgentBuildPool1 + customBuildTags: - ES365AIMigrationTooling - 1ES-AzureExtensions @@ -79,155 +80,90 @@ extends: stages: - # =============================== Build MAIN =============================== - - stage: BuildMain - displayName: 'Build MAIN ${{ parameters.extensionName }}' + # ============================== Build (MAIN+TEST) ============================== + - stage: Build + displayName: 'Build ${{ parameters.extensionName }} (MAIN + TEST)' jobs: - - job: BuildMainJob - displayName: 'Build MAIN' + - job: BuildJob + displayName: 'Build and Package MAIN, then TEST' templateContext: outputs: - output: pipelineArtifact displayName: 'Publish MAIN Build Artifacts (Unsigned)' targetPath: '$(System.ArtifactsDirectory)/unsigned' artifactName: 'vsix-unsigned' + - output: pipelineArtifact + displayName: 'Publish TEST Build Artifacts (Unsigned)' + targetPath: '$(System.ArtifactsDirectory)/test-unsigned' + artifactName: 'vsix-test-unsigned' steps: - checkout: self + displayName: 'Checkout' clean: true fetchTags: false - task: NodeTool@0 displayName: 'Install Node.js' - inputs: - versionSpec: '10.x' + inputs: { versionSpec: '10.x' } - task: Npm@1 displayName: 'Install Dependencies' - inputs: - command: 'install' - verbose: false + inputs: { command: 'install', verbose: false } - task: Npm@1 displayName: 'Install TFX CLI' - inputs: - command: 'custom' - customCommand: 'install -g tfx-cli' + inputs: { command: 'custom', customCommand: 'install -g tfx-cli' } - task: Npm@1 displayName: 'Install Gulp CLI' - inputs: - command: 'custom' - customCommand: 'install -g gulp-cli' + inputs: { command: 'custom', customCommand: 'install -g gulp-cli' } + # ---- MAIN build/package -> copy to unsigned ---- - task: PowerShell@2 - displayName: 'Clean (gulp clean)' - inputs: - targetType: inline - script: 'gulp clean' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Clean (MAIN) - gulp clean' + inputs: { targetType: inline, script: 'gulp clean', workingDirectory: '$(Build.SourcesDirectory)' } - task: PowerShell@2 - displayName: 'Build MAIN (gulp build)' - inputs: - targetType: inline - script: 'gulp build' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Build MAIN - gulp build' + inputs: { targetType: inline, script: 'gulp build', workingDirectory: '$(Build.SourcesDirectory)' } - task: PowerShell@2 - displayName: 'Package MAIN (gulp package)' - inputs: - targetType: inline - script: 'gulp package' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Package MAIN - gulp package' + inputs: { targetType: inline, script: 'gulp package', workingDirectory: '$(Build.SourcesDirectory)' } - task: CopyFiles@2 - displayName: 'Copy MAIN VSIX to Artifacts' + displayName: 'Copy MAIN VSIX to $(System.ArtifactsDirectory)/unsigned' inputs: SourceFolder: '$(Build.SourcesDirectory)/_package' Contents: '${{ parameters.extensionName }}/*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/unsigned' flattenFolders: true - # =============================== Build TEST =============================== - - stage: BuildTest - displayName: 'Build TEST ${{ parameters.extensionName }}' - dependsOn: BuildMain - jobs: - - job: BuildTestJob - displayName: 'Build TEST' - templateContext: - outputs: - - output: pipelineArtifact - displayName: 'Publish TEST Build Artifacts (Unsigned)' - targetPath: '$(System.ArtifactsDirectory)/test-unsigned' - artifactName: 'vsix-test-unsigned' - steps: - - checkout: self - clean: true - fetchTags: false - - - task: NodeTool@0 - displayName: 'Install Node.js' - inputs: - versionSpec: '10.x' - - - task: Npm@1 - displayName: 'Install Dependencies' - inputs: - command: 'install' - verbose: false - - - task: Npm@1 - displayName: 'Install TFX CLI' - inputs: - command: 'custom' - customCommand: 'install -g tfx-cli' - - - task: Npm@1 - displayName: 'Install Gulp CLI' - inputs: - command: 'custom' - customCommand: 'install -g gulp-cli' - - - task: PowerShell@2 - displayName: 'Clean (gulp clean)' - inputs: - targetType: inline - script: 'gulp clean' - workingDirectory: '$(Build.SourcesDirectory)' - + # ---- TEST build/package -> copy to test-unsigned ---- - task: PowerShell@2 - displayName: 'Build TEST (gulp build --test)' - inputs: - targetType: inline - script: 'gulp build --test' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Build TEST - gulp build --test' + inputs: { targetType: inline, script: 'gulp build --test', workingDirectory: '$(Build.SourcesDirectory)' } - task: PowerShell@2 - displayName: 'Package TEST (gulp package)' - inputs: - targetType: inline - script: 'gulp package' - workingDirectory: '$(Build.SourcesDirectory)' + displayName: 'Package TEST - gulp package' + inputs: { targetType: inline, script: 'gulp package', workingDirectory: '$(Build.SourcesDirectory)' } - task: CopyFiles@2 - displayName: 'Copy TEST VSIX to Artifacts' + displayName: 'Copy TEST VSIX to $(System.ArtifactsDirectory)/test-unsigned' inputs: SourceFolder: '$(Build.SourcesDirectory)/_package' Contents: '${{ parameters.extensionName }}/*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/test-unsigned' flattenFolders: true - # =============================== Code Signing ============================= + # ================================ Code Signing ================================ - stage: CodeSigning displayName: 'Code Signing' condition: or(eq(variables['IsMainBranchBuild'], 'true'), ${{ eq(parameters.forceCodeSign, 'true') }}, ${{ eq(parameters.simulateCodeSigningError, 'true') }}) - dependsOn: - - BuildMain - - BuildTest + dependsOn: Build jobs: - job: CodeSigningJob - displayName: 'ESRP CodeSigning' + displayName: 'ESRP CodeSigning (MAIN + TEST)' templateContext: outputs: - output: pipelineArtifact @@ -242,6 +178,7 @@ extends: - download: current artifact: vsix-unsigned displayName: 'Download unsigned (MAIN)' + - download: current artifact: vsix-test-unsigned displayName: 'Download unsigned (TEST)' @@ -252,6 +189,7 @@ extends: displayName: 'Simulate CodeSigning Error' condition: ${{ eq(parameters.simulateCodeSigningError, 'true') }} + # Sign MAIN - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning (MAIN)' inputs: @@ -278,6 +216,7 @@ extends: Contents: '*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/signed' + # Sign TEST - task: EsrpCodeSigning@5 displayName: 'ESRP CodeSigning (TEST)' inputs: @@ -304,7 +243,7 @@ extends: Contents: '*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/test-signed' - # ===================== Manual Approval BEFORE TEST publish ================= + # ======================== Manual Approval BEFORE TEST publish ======================== - stage: WaitForTestApproval displayName: 'Approve TEST publish' dependsOn: CodeSigning @@ -319,7 +258,7 @@ extends: notifyUsers: 'vhorbachov@microsoft.com' instructions: 'Approve this step to publish the TEST extension (private).' - # ============================ Publish TEST (private) ======================= + # ================================= Publish TEST (private) ============================== - stage: PublishTestToMarketplace displayName: 'Publish TEST extension (private)' dependsOn: WaitForTestApproval @@ -333,12 +272,10 @@ extends: - task: NodeTool@0 displayName: 'Install Node.js' - inputs: - versionSpec: '10.x' + inputs: { versionSpec: '10.x' } - task: TfxInstaller@5 - inputs: - version: 'v0.21.1' + inputs: { version: 'v0.21.1' } - task: 1ES.PublishAzureDevOpsExtension@1 displayName: 'Publish TEST extension (private)' @@ -352,7 +289,7 @@ extends: useV5: true extensionVisibility: 'private' - # ===================== Manual Approval BEFORE PUBLIC publish =============== + # ======================== Manual Approval BEFORE PUBLIC publish ======================== - stage: WaitForValidation displayName: 'Approve PUBLIC publish' dependsOn: PublishTestToMarketplace @@ -367,7 +304,7 @@ extends: notifyUsers: 'vhorbachov@microsoft.com' instructions: 'Approve this step to publish the PUBLIC extension.' - # ================================ Publish PUBLIC =========================== + # ===================================== Publish PUBLIC ================================== - stage: PublishToMarketplace displayName: 'Publish PUBLIC extension' dependsOn: WaitForValidation @@ -381,12 +318,10 @@ extends: - task: NodeTool@0 displayName: 'Install Node.js' - inputs: - versionSpec: '10.x' + inputs: { versionSpec: '10.x' } - task: TfxInstaller@5 - inputs: - version: 'v0.21.1' + inputs: { version: 'v0.21.1' } - task: 1ES.PublishAzureDevOpsExtension@1 displayName: 'Publish the public extension to ms-vscs-rm' diff --git a/Extensions/IISWebAppDeploy/Src/vss-extension.json b/Extensions/IISWebAppDeploy/Src/vss-extension.json index e3a02e451..5f2938a65 100644 --- a/Extensions/IISWebAppDeploy/Src/vss-extension.json +++ b/Extensions/IISWebAppDeploy/Src/vss-extension.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "extensionId": "iiswebapp", "name": "IIS Web App Deployment Using WinRM", - "version": "1.258.0", + "version": "1.261.0", "publisher": "ms-vscs-rm", "description": "Using WinRM connect to the host Computer, to deploy a Web project using Web Deploy or a SQL DB using sqlpackage.exe.", "public": true, From 2aded9d07ac54610cb9675cb9fb3fbd3709b5dae Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Wed, 13 Aug 2025 13:29:36 +0200 Subject: [PATCH 07/14] Bump ansible version for tests --- Extensions/Ansible/Src/vss-extension.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/Ansible/Src/vss-extension.json b/Extensions/Ansible/Src/vss-extension.json index d91d29fbf..dae9f880c 100644 --- a/Extensions/Ansible/Src/vss-extension.json +++ b/Extensions/Ansible/Src/vss-extension.json @@ -3,7 +3,7 @@ "id": "vss-services-ansible", "name": "Ansible", "publisher": "ms-vscs-rm", - "version": "0.261.0", + "version": "0.261.1", "public": true, "description": "This extension executes an Ansible playbook using a specified inventory via command line interface", "_description.comment": "The below format to define extensions is currently in preview and may change in future.", From d23ccae4254525900adc4f1829d90b6cad6a1556 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Wed, 13 Aug 2025 17:48:07 +0200 Subject: [PATCH 08/14] Commented out test stage --- .pipelines/1es-migration/azure-pipelines.yml | 89 ++++++++++---------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/.pipelines/1es-migration/azure-pipelines.yml b/.pipelines/1es-migration/azure-pipelines.yml index 92a7a29cb..6708e7901 100644 --- a/.pipelines/1es-migration/azure-pipelines.yml +++ b/.pipelines/1es-migration/azure-pipelines.yml @@ -243,51 +243,50 @@ extends: Contents: '*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/test-signed' - # ======================== Manual Approval BEFORE TEST publish ======================== - - stage: WaitForTestApproval - displayName: 'Approve TEST publish' - dependsOn: CodeSigning - jobs: - - job: ManualApprovalForTest - displayName: 'Manual Approval (TEST)' - pool: server - steps: - - task: ManualValidation@0 - timeoutInMinutes: 1440 - inputs: - notifyUsers: 'vhorbachov@microsoft.com' - instructions: 'Approve this step to publish the TEST extension (private).' - - # ================================= Publish TEST (private) ============================== - - stage: PublishTestToMarketplace - displayName: 'Publish TEST extension (private)' - dependsOn: WaitForTestApproval - jobs: - - job: PublishTestVSIX - displayName: 'Publish TEST vsix to marketplace (private)' - steps: - - download: current - artifact: vsix-test-signed - displayName: 'Download Signed TEST Artifact' - - - task: NodeTool@0 - displayName: 'Install Node.js' - inputs: { versionSpec: '10.x' } - - - task: TfxInstaller@5 - inputs: { version: 'v0.21.1' } - - - task: 1ES.PublishAzureDevOpsExtension@1 - displayName: 'Publish TEST extension (private)' - inputs: - connectTo: 'AzureRM' - connectedServiceNameAzureRM: '1es-extensions-publication-secure-service-connection' - fileType: 'vsix' - vsixFile: '$(Pipeline.Workspace)/vsix-test-signed/*.vsix' - targetPath: '$(Pipeline.Workspace)/vsix-test-signed' - validateExtension: false - useV5: true - extensionVisibility: 'private' + # # ======================== Manual Approval BEFORE TEST publish ======================== + # - stage: WaitForTestApproval + # displayName: 'Approve TEST publish' + # dependsOn: CodeSigning + # jobs: + # - job: ManualApprovalForTest + # displayName: 'Manual Approval (TEST)' + # pool: server + # steps: + # - task: ManualValidation@0 + # timeoutInMinutes: 1440 + # inputs: + # notifyUsers: 'vhorbachov@microsoft.com' + # instructions: 'Approve this step to publish the TEST extension (private).' + + # # ================================= Publish TEST (private) ============================== + # - stage: PublishTestToMarketplace + # displayName: 'Publish TEST extension (private)' + # dependsOn: WaitForTestApproval + # jobs: + # - job: PublishTestVSIX + # displayName: 'Publish TEST vsix to marketplace (private)' + # steps: + # - download: current + # artifact: vsix-test-signed + # displayName: 'Download Signed TEST Artifact' + + # - task: NodeTool@0 + # displayName: 'Install Node.js' + # inputs: { versionSpec: '10.x' } + + # - task: TfxInstaller@5 + # inputs: { version: 'v0.21.1' } + + # - task: 1ES.PublishAzureDevOpsExtension@1 + # displayName: 'Publish TEST extension (private)' + # inputs: + # connectTo: 'AzureRM' + # connectedServiceNameAzureRM: '1es-extensions-publication-secure-service-connection' + # fileType: 'vsix' + # vsixFile: '$(Pipeline.Workspace)/vsix-test-signed/*.vsix' + # targetPath: '$(Pipeline.Workspace)/vsix-test-signed' + # validateExtension: false + # useV5: true # ======================== Manual Approval BEFORE PUBLIC publish ======================== - stage: WaitForValidation From b52a6ea6a3ec3c14b89ea9a83a54be3653767de2 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Thu, 14 Aug 2025 10:53:37 +0200 Subject: [PATCH 09/14] Bump circle ci --- .pipelines/1es-migration/azure-pipelines.yml | 88 ++++++++++---------- Extensions/CircleCI/Src/vss-extension.json | 2 +- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/.pipelines/1es-migration/azure-pipelines.yml b/.pipelines/1es-migration/azure-pipelines.yml index 6708e7901..286a409fb 100644 --- a/.pipelines/1es-migration/azure-pipelines.yml +++ b/.pipelines/1es-migration/azure-pipelines.yml @@ -243,50 +243,50 @@ extends: Contents: '*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/test-signed' - # # ======================== Manual Approval BEFORE TEST publish ======================== - # - stage: WaitForTestApproval - # displayName: 'Approve TEST publish' - # dependsOn: CodeSigning - # jobs: - # - job: ManualApprovalForTest - # displayName: 'Manual Approval (TEST)' - # pool: server - # steps: - # - task: ManualValidation@0 - # timeoutInMinutes: 1440 - # inputs: - # notifyUsers: 'vhorbachov@microsoft.com' - # instructions: 'Approve this step to publish the TEST extension (private).' - - # # ================================= Publish TEST (private) ============================== - # - stage: PublishTestToMarketplace - # displayName: 'Publish TEST extension (private)' - # dependsOn: WaitForTestApproval - # jobs: - # - job: PublishTestVSIX - # displayName: 'Publish TEST vsix to marketplace (private)' - # steps: - # - download: current - # artifact: vsix-test-signed - # displayName: 'Download Signed TEST Artifact' - - # - task: NodeTool@0 - # displayName: 'Install Node.js' - # inputs: { versionSpec: '10.x' } - - # - task: TfxInstaller@5 - # inputs: { version: 'v0.21.1' } - - # - task: 1ES.PublishAzureDevOpsExtension@1 - # displayName: 'Publish TEST extension (private)' - # inputs: - # connectTo: 'AzureRM' - # connectedServiceNameAzureRM: '1es-extensions-publication-secure-service-connection' - # fileType: 'vsix' - # vsixFile: '$(Pipeline.Workspace)/vsix-test-signed/*.vsix' - # targetPath: '$(Pipeline.Workspace)/vsix-test-signed' - # validateExtension: false - # useV5: true + # ======================== Manual Approval BEFORE TEST publish ======================== + - stage: WaitForTestApproval + displayName: 'Approve TEST publish' + dependsOn: CodeSigning + jobs: + - job: ManualApprovalForTest + displayName: 'Manual Approval (TEST)' + pool: server + steps: + - task: ManualValidation@0 + timeoutInMinutes: 1440 + inputs: + notifyUsers: 'vhorbachov@microsoft.com' + instructions: 'Approve this step to publish the TEST extension (private).' + + # ================================= Publish TEST (private) ============================== + - stage: PublishTestToMarketplace + displayName: 'Publish TEST extension (private)' + dependsOn: WaitForTestApproval + jobs: + - job: PublishTestVSIX + displayName: 'Publish TEST vsix to marketplace (private)' + steps: + - download: current + artifact: vsix-test-signed + displayName: 'Download Signed TEST Artifact' + + - task: NodeTool@0 + displayName: 'Install Node.js' + inputs: { versionSpec: '10.x' } + + - task: TfxInstaller@5 + inputs: { version: 'v0.21.1' } + + - task: 1ES.PublishAzureDevOpsExtension@1 + displayName: 'Publish TEST extension (private)' + inputs: + connectTo: 'AzureRM' + connectedServiceNameAzureRM: '1es-extensions-publication-secure-service-connection' + fileType: 'vsix' + vsixFile: '$(Pipeline.Workspace)/vsix-test-signed/*.vsix' + targetPath: '$(Pipeline.Workspace)/vsix-test-signed' + validateExtension: false + useV5: true # ======================== Manual Approval BEFORE PUBLIC publish ======================== - stage: WaitForValidation diff --git a/Extensions/CircleCI/Src/vss-extension.json b/Extensions/CircleCI/Src/vss-extension.json index d8c45a772..a6565054b 100644 --- a/Extensions/CircleCI/Src/vss-extension.json +++ b/Extensions/CircleCI/Src/vss-extension.json @@ -3,7 +3,7 @@ "id": "vss-services-circleci-extension", "name": "CircleCI artifacts for Release Pipeline", "publisher": "ms-vscs-rm", - "version": "0.258.0", + "version": "0.261.0", "public": true, "description": "Tool for connecting with CircleCI", "_description.comment": "The below format to define artifact extensions is currently in preview and may change in future.", From 716b6eadde011d34324a882de4b52b4549e742d5 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Thu, 14 Aug 2025 11:49:09 +0200 Subject: [PATCH 10/14] Bump iis version --- Extensions/IISWebAppDeploy/Src/vss-extension.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/IISWebAppDeploy/Src/vss-extension.json b/Extensions/IISWebAppDeploy/Src/vss-extension.json index 5f2938a65..7ab1a7b37 100644 --- a/Extensions/IISWebAppDeploy/Src/vss-extension.json +++ b/Extensions/IISWebAppDeploy/Src/vss-extension.json @@ -2,7 +2,7 @@ "manifestVersion": 1, "extensionId": "iiswebapp", "name": "IIS Web App Deployment Using WinRM", - "version": "1.261.0", + "version": "1.261.1", "publisher": "ms-vscs-rm", "description": "Using WinRM connect to the host Computer, to deploy a Web project using Web Deploy or a SQL DB using sqlpackage.exe.", "public": true, From 635a329c4519c09d04ecf09f82cad9e77a383c65 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Thu, 14 Aug 2025 13:54:17 +0200 Subject: [PATCH 11/14] Remove gulp clean from the build pipeline --- .pipelines/1es-migration/azure-pipelines.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.pipelines/1es-migration/azure-pipelines.yml b/.pipelines/1es-migration/azure-pipelines.yml index 286a409fb..f92377176 100644 --- a/.pipelines/1es-migration/azure-pipelines.yml +++ b/.pipelines/1es-migration/azure-pipelines.yml @@ -118,11 +118,6 @@ extends: displayName: 'Install Gulp CLI' inputs: { command: 'custom', customCommand: 'install -g gulp-cli' } - # ---- MAIN build/package -> copy to unsigned ---- - - task: PowerShell@2 - displayName: 'Clean (MAIN) - gulp clean' - inputs: { targetType: inline, script: 'gulp clean', workingDirectory: '$(Build.SourcesDirectory)' } - - task: PowerShell@2 displayName: 'Build MAIN - gulp build' inputs: { targetType: inline, script: 'gulp build', workingDirectory: '$(Build.SourcesDirectory)' } From 918478944c314301b654bd6279ff7c1cd2501cb5 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Thu, 14 Aug 2025 13:56:41 +0200 Subject: [PATCH 12/14] Replace notifyUsers --- .pipelines/1es-migration/azure-pipelines.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pipelines/1es-migration/azure-pipelines.yml b/.pipelines/1es-migration/azure-pipelines.yml index f92377176..36166bad9 100644 --- a/.pipelines/1es-migration/azure-pipelines.yml +++ b/.pipelines/1es-migration/azure-pipelines.yml @@ -250,7 +250,7 @@ extends: - task: ManualValidation@0 timeoutInMinutes: 1440 inputs: - notifyUsers: 'vhorbachov@microsoft.com' + notifyUsers: 'razvanmanole@microsoft.com' instructions: 'Approve this step to publish the TEST extension (private).' # ================================= Publish TEST (private) ============================== @@ -295,7 +295,7 @@ extends: - task: ManualValidation@0 timeoutInMinutes: 1440 inputs: - notifyUsers: 'vhorbachov@microsoft.com' + notifyUsers: 'razvanmanole@microsoft.com' instructions: 'Approve this step to publish the PUBLIC extension.' # ===================================== Publish PUBLIC ================================== From 49ae22944f476a49066638e27bb1d346c3012da6 Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Thu, 14 Aug 2025 14:03:59 +0200 Subject: [PATCH 13/14] Bump ansible version --- Extensions/Ansible/Src/vss-extension.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extensions/Ansible/Src/vss-extension.json b/Extensions/Ansible/Src/vss-extension.json index dae9f880c..eb2d8a3e1 100644 --- a/Extensions/Ansible/Src/vss-extension.json +++ b/Extensions/Ansible/Src/vss-extension.json @@ -3,7 +3,7 @@ "id": "vss-services-ansible", "name": "Ansible", "publisher": "ms-vscs-rm", - "version": "0.261.1", + "version": "0.261.2", "public": true, "description": "This extension executes an Ansible playbook using a specified inventory via command line interface", "_description.comment": "The below format to define extensions is currently in preview and may change in future.", From 7c8433f7b4dc4ce84da9e29e58b4c01e4184ae6e Mon Sep 17 00:00:00 2001 From: Vladyslav Horbachov <0965142838gvs@gmail.com> Date: Fri, 15 Aug 2025 17:04:26 +0200 Subject: [PATCH 14/14] Cleanup yaml --- .pipelines/1es-migration/azure-pipelines.yml | 190 +++++++++++-------- 1 file changed, 110 insertions(+), 80 deletions(-) diff --git a/.pipelines/1es-migration/azure-pipelines.yml b/.pipelines/1es-migration/azure-pipelines.yml index 36166bad9..0c1256cba 100644 --- a/.pipelines/1es-migration/azure-pipelines.yml +++ b/.pipelines/1es-migration/azure-pipelines.yml @@ -64,10 +64,14 @@ extends: sdl: sourceAnalysisPool: name: 1ESPtTfsAgentBuildPoolSDL - spotBugs: { enabled: false } - credscan: { enabled: true } - binskim: { enabled: true } - eslint: { enabled: true } + spotBugs: + enabled: false + credscan: + enabled: true + binskim: + enabled: true + eslint: + enabled: true pool: name: 1ESPtTfsAgentBuildPool1 @@ -80,12 +84,12 @@ extends: stages: - # ============================== Build (MAIN+TEST) ============================== - - stage: Build - displayName: 'Build ${{ parameters.extensionName }} (MAIN + TEST)' + # ============================== Build & Package ============================== + - stage: Build_And_Package_Main_And_Test + displayName: 'Build & Package Extensions (Main + Test)' jobs: - - job: BuildJob - displayName: 'Build and Package MAIN, then TEST' + - job: BuildAndPackage + displayName: 'Build and Package: MAIN then TEST' templateContext: outputs: - output: pipelineArtifact @@ -98,77 +102,95 @@ extends: artifactName: 'vsix-test-unsigned' steps: - checkout: self - displayName: 'Checkout' + displayName: 'Checkout repository' clean: true fetchTags: false - task: NodeTool@0 - displayName: 'Install Node.js' - inputs: { versionSpec: '10.x' } + displayName: 'Use Node.js 20.x' + inputs: + versionSpec: '20.x' - task: Npm@1 - displayName: 'Install Dependencies' - inputs: { command: 'install', verbose: false } + displayName: 'Install dependencies' + inputs: + command: install + verbose: false - task: Npm@1 displayName: 'Install TFX CLI' - inputs: { command: 'custom', customCommand: 'install -g tfx-cli' } + inputs: + command: custom + customCommand: 'install -g tfx-cli' - task: Npm@1 displayName: 'Install Gulp CLI' - inputs: { command: 'custom', customCommand: 'install -g gulp-cli' } + inputs: + command: custom + customCommand: 'install -g gulp-cli' - task: PowerShell@2 - displayName: 'Build MAIN - gulp build' - inputs: { targetType: inline, script: 'gulp build', workingDirectory: '$(Build.SourcesDirectory)' } + displayName: 'Build (MAIN)' + inputs: + targetType: inline + script: 'gulp build' + workingDirectory: '$(Build.SourcesDirectory)' - task: PowerShell@2 - displayName: 'Package MAIN - gulp package' - inputs: { targetType: inline, script: 'gulp package', workingDirectory: '$(Build.SourcesDirectory)' } + displayName: 'Package (MAIN)' + inputs: + targetType: inline + script: 'gulp package' + workingDirectory: '$(Build.SourcesDirectory)' - task: CopyFiles@2 - displayName: 'Copy MAIN VSIX to $(System.ArtifactsDirectory)/unsigned' + displayName: 'Copy MAIN VSIX to unsigned artifacts' inputs: SourceFolder: '$(Build.SourcesDirectory)/_package' Contents: '${{ parameters.extensionName }}/*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/unsigned' flattenFolders: true - # ---- TEST build/package -> copy to test-unsigned ---- - task: PowerShell@2 - displayName: 'Build TEST - gulp build --test' - inputs: { targetType: inline, script: 'gulp build --test', workingDirectory: '$(Build.SourcesDirectory)' } + displayName: 'Build (TEST)' + inputs: + targetType: inline + script: 'gulp build --test' + workingDirectory: '$(Build.SourcesDirectory)' - task: PowerShell@2 - displayName: 'Package TEST - gulp package' - inputs: { targetType: inline, script: 'gulp package', workingDirectory: '$(Build.SourcesDirectory)' } + displayName: 'Package (TEST)' + inputs: + targetType: inline + script: 'gulp package' + workingDirectory: '$(Build.SourcesDirectory)' - task: CopyFiles@2 - displayName: 'Copy TEST VSIX to $(System.ArtifactsDirectory)/test-unsigned' + displayName: 'Copy TEST VSIX to test-unsigned artifacts' inputs: SourceFolder: '$(Build.SourcesDirectory)/_package' Contents: '${{ parameters.extensionName }}/*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/test-unsigned' flattenFolders: true - # ================================ Code Signing ================================ - - stage: CodeSigning - displayName: 'Code Signing' + # ================================ ESRP Code Signing ================================ + - stage: ESRP_Code_Signing_Main_And_Test + displayName: 'ESRP Code Signing (Main + Test)' + dependsOn: Build_And_Package_Main_And_Test condition: or(eq(variables['IsMainBranchBuild'], 'true'), ${{ eq(parameters.forceCodeSign, 'true') }}, ${{ eq(parameters.simulateCodeSigningError, 'true') }}) - dependsOn: Build jobs: - - job: CodeSigningJob - displayName: 'ESRP CodeSigning (MAIN + TEST)' + - job: SignVsix + displayName: 'Sign VSIX: MAIN and TEST' templateContext: outputs: - output: pipelineArtifact - displayName: 'Signed MAIN vsix artifact' + displayName: 'Publish Signed MAIN Artifact' targetPath: '$(System.ArtifactsDirectory)/signed' - artifactName: vsix-signed + artifactName: 'vsix-signed' - output: pipelineArtifact - displayName: 'Signed TEST vsix artifact' + displayName: 'Publish Signed TEST Artifact' targetPath: '$(System.ArtifactsDirectory)/test-signed' - artifactName: vsix-test-signed + artifactName: 'vsix-test-signed' steps: - download: current artifact: vsix-unsigned @@ -181,12 +203,11 @@ extends: - script: | echo "Simulated error in CodeSigning step." exit 1 - displayName: 'Simulate CodeSigning Error' + displayName: 'Simulate CodeSigning error' condition: ${{ eq(parameters.simulateCodeSigningError, 'true') }} - # Sign MAIN - task: EsrpCodeSigning@5 - displayName: 'ESRP CodeSigning (MAIN)' + displayName: 'ESRP Sign (MAIN)' inputs: ConnectedServiceName: '$(Control.EsrpServiceConnectionName)' AppRegistrationClientId: '$(Control.AppRegistrationClientId)' @@ -211,9 +232,8 @@ extends: Contents: '*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/signed' - # Sign TEST - task: EsrpCodeSigning@5 - displayName: 'ESRP CodeSigning (TEST)' + displayName: 'ESRP Sign (TEST)' inputs: ConnectedServiceName: '$(Control.EsrpServiceConnectionName)' AppRegistrationClientId: '$(Control.AppRegistrationClientId)' @@ -238,13 +258,14 @@ extends: Contents: '*.vsix' TargetFolder: '$(System.ArtifactsDirectory)/test-signed' - # ======================== Manual Approval BEFORE TEST publish ======================== - - stage: WaitForTestApproval - displayName: 'Approve TEST publish' - dependsOn: CodeSigning + # ======================== Approval: Publish TEST (private) ======================== + - stage: Approval_For_Test_Publish + displayName: 'Approval: Publish TEST (Private)' + dependsOn: ESRP_Code_Signing_Main_And_Test + condition: and(succeeded(), ${{ eq(parameters.publishExtension, 'true') }}) jobs: - - job: ManualApprovalForTest - displayName: 'Manual Approval (TEST)' + - job: ApproveTest + displayName: 'Manual Approval for TEST publish' pool: server steps: - task: ManualValidation@0 @@ -253,43 +274,48 @@ extends: notifyUsers: 'razvanmanole@microsoft.com' instructions: 'Approve this step to publish the TEST extension (private).' - # ================================= Publish TEST (private) ============================== - - stage: PublishTestToMarketplace - displayName: 'Publish TEST extension (private)' - dependsOn: WaitForTestApproval + # ================================= Publish TEST (private) ============================ + - stage: Publish_Test_Extension_Private + displayName: 'Publish TEST Extension (Private Marketplace)' + dependsOn: Approval_For_Test_Publish + condition: and(succeeded(), ${{ eq(parameters.publishExtension, 'true') }}) jobs: - - job: PublishTestVSIX - displayName: 'Publish TEST vsix to marketplace (private)' + - job: PublishTest + displayName: 'Publish TEST VSIX to Marketplace (Private)' steps: - download: current artifact: vsix-test-signed - displayName: 'Download Signed TEST Artifact' + displayName: 'Download signed TEST artifact' - task: NodeTool@0 - displayName: 'Install Node.js' - inputs: { versionSpec: '10.x' } + displayName: 'Use Node.js 20.x' + inputs: + versionSpec: '20.x' - task: TfxInstaller@5 - inputs: { version: 'v0.21.1' } + displayName: 'Install TFX' + inputs: + version: 'v0.21.1' - task: 1ES.PublishAzureDevOpsExtension@1 displayName: 'Publish TEST extension (private)' inputs: - connectTo: 'AzureRM' + connectTo: AzureRM connectedServiceNameAzureRM: '1es-extensions-publication-secure-service-connection' - fileType: 'vsix' + fileType: vsix vsixFile: '$(Pipeline.Workspace)/vsix-test-signed/*.vsix' targetPath: '$(Pipeline.Workspace)/vsix-test-signed' validateExtension: false useV5: true - # ======================== Manual Approval BEFORE PUBLIC publish ======================== - - stage: WaitForValidation - displayName: 'Approve PUBLIC publish' - dependsOn: PublishTestToMarketplace + # ======================== Approval: Publish PUBLIC ============================== + - stage: Approval_For_Public_Publish + displayName: 'Approval: Publish PUBLIC' + dependsOn: Publish_Test_Extension_Private + condition: and(succeeded(), ${{ eq(parameters.publishExtension, 'true') }}) jobs: - - job: ManualApproval - displayName: 'Manual Approval (PUBLIC)' + - job: ApprovePublic + displayName: 'Manual Approval for PUBLIC publish' pool: server steps: - task: ManualValidation@0 @@ -298,32 +324,36 @@ extends: notifyUsers: 'razvanmanole@microsoft.com' instructions: 'Approve this step to publish the PUBLIC extension.' - # ===================================== Publish PUBLIC ================================== - - stage: PublishToMarketplace - displayName: 'Publish PUBLIC extension' - dependsOn: WaitForValidation + # ===================================== Publish PUBLIC ================================ + - stage: Publish_Public_Extension + displayName: 'Publish PUBLIC Extension (Marketplace)' + dependsOn: Approval_For_Public_Publish + condition: and(succeeded(), ${{ eq(parameters.publishExtension, 'true') }}) jobs: - - job: PublishVSIX - displayName: 'Publish vsix to marketplace' + - job: PublishPublic + displayName: 'Publish PUBLIC VSIX to Marketplace' steps: - download: current artifact: vsix-signed - displayName: 'Download Signed Artifact' + displayName: 'Download signed MAIN artifact' - task: NodeTool@0 - displayName: 'Install Node.js' - inputs: { versionSpec: '10.x' } + displayName: 'Use Node.js 20.x' + inputs: + versionSpec: '20.x' - task: TfxInstaller@5 - inputs: { version: 'v0.21.1' } + displayName: 'Install TFX' + inputs: + version: 'v0.21.1' - task: 1ES.PublishAzureDevOpsExtension@1 - displayName: 'Publish the public extension to ms-vscs-rm' + displayName: 'Publish PUBLIC extension (publisher: ms-vscs-rm)' inputs: - connectTo: 'AzureRM' + connectTo: AzureRM connectedServiceNameAzureRM: '1es-extensions-publication-secure-service-connection' - fileType: 'vsix' + fileType: vsix vsixFile: '$(Pipeline.Workspace)/vsix-signed/*.vsix' targetPath: '$(Pipeline.Workspace)/vsix-signed' validateExtension: false - useV5: true \ No newline at end of file + useV5: true