From 8140b68773e0fba7b478836348a4506d38525b83 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Tue, 21 Apr 2026 13:36:36 -0700 Subject: [PATCH 01/18] [release/v7.6.1] Correct Variable Template Reference in NonOfficial Pipeline Templates (#27317) Co-authored-by: Justin Chung <124807742+jshigetomi@users.noreply.github.com> Co-authored-by: Justin Chung --- .github/agents/SplitADOPipelines.agent.md | 52 ++++++++++++------- ...Shell-Coordinated_Packages-NonOfficial.yml | 5 +- .../PowerShell-Packages-NonOfficial.yml | 4 +- .../PowerShell-Release-Azure-NonOfficial.yml | 2 +- .../PowerShell-Release-NonOfficial.yml | 4 +- .../PowerShell-vPack-NonOfficial.yml | 4 +- .../stages/PowerShell-vPack-Stages.yml | 2 +- 7 files changed, 45 insertions(+), 28 deletions(-) diff --git a/.github/agents/SplitADOPipelines.agent.md b/.github/agents/SplitADOPipelines.agent.md index 8322f473e7b..9454670061f 100644 --- a/.github/agents/SplitADOPipelines.agent.md +++ b/.github/agents/SplitADOPipelines.agent.md @@ -6,7 +6,7 @@ tools: ['vscode', 'execute', 'read', 'agent', 'edit', 'search', 'todo'] This agent will implement and restructure the repository's existing ADO pipelines into Official and NonOfficial pipelines. -A repository will have under the ./pipelines directory a series of yaml files that define the ADO pipelines for the repository. +A repository will have under the .pipelines directory a series of yaml files that define the ADO pipelines for the repository. First confirm if the pipelines are using a toggle switch for Official and NonOfficial. This will look something like this @@ -25,15 +25,31 @@ extends: This is an indicator that this work needs to be done. This toggle switch is no longer allowed and the templates need to be hard coded. +## Template Reference Convention (MUST follow) + +All `- template:` references to files **inside this repo** must use the **absolute** form anchored at the repo root, with the `@self` suffix: + +```yaml +- template: /.pipelines/templates//.yml@self +``` + +Do **not** use relative paths such as `templates/...`, `../templates/...`, or bare filenames. Rationale: + +- Absolute paths resolve identically regardless of where the referring file lives, so moving a pipeline file between directories (for example, into `.pipelines/NonOfficial/`) does not silently break includes. +- Relative paths are resolved by Azure DevOps against the directory of the referring file, which has caused real outages in this repo when a relative include was composed into a nonexistent nested path like `.pipelines/templates/stages/.pipelines/templates/...`. +- The majority of existing includes already use the absolute form; keeping new work consistent reduces review burden. + +The only acceptable non-absolute references are to external repositories resolved via the `resources.repositories` block, for example `v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates`. + ## Refactoring Steps ### Step 1: Extract Shared Templates -For each pipeline file that uses the toggle switch pattern (e.g., `PowerShell-Packages.yml`): +For each pipeline file that uses the toggle switch pattern (e.g., `PowerShell-Packages-Official.yml`): -1. Create a `./pipelines/templates` directory if it doesn't exist -2. Extract the **variables section** into `./pipelines/templates/PowerShell-Packages-Variables.yml` -3. Extract the **stages section** into `./pipelines/templates/PowerShell-Packages-Stages.yml` +1. Create the `.pipelines/templates/variables` and `.pipelines/templates/stages` directories if they don't exist +2. Extract the **variables section** into `.pipelines/templates/variables/PowerShell-Packages-Variables.yml` +3. Extract the **stages section** into `.pipelines/templates/stages/PowerShell-Packages-Stages.yml` **IMPORTANT**: Only extract the `variables:` and `stages:` sections. All other sections (parameters, resources, extends, etc.) remain in the pipeline files. @@ -41,7 +57,7 @@ For each pipeline file that uses the toggle switch pattern (e.g., `PowerShell-Pa The original toggle-based file becomes the Official pipeline: -1. **Keep the file in its original location** (e.g., `./pipelines/PowerShell-Packages.yml` stays where it is) +1. **Keep the file in its original location** (e.g., `.pipelines/PowerShell-Packages-Official.yml` stays where it is) 2. Remove the toggle switch parameter (`templateFile` parameter) 3. Hard-code the Official template reference: ```yaml @@ -51,18 +67,18 @@ The original toggle-based file becomes the Official pipeline: 4. Replace the `variables:` section with a template reference: ```yaml variables: - - template: templates/PowerShell-Packages-Variables.yml + - template: /.pipelines/templates/variables/PowerShell-Packages-Variables.yml@self ``` 5. Replace the `stages:` section with a template reference: ```yaml stages: - - template: templates/PowerShell-Packages-Stages.yml + - template: /.pipelines/templates/stages/PowerShell-Packages-Stages.yml@self ``` ### Step 3: Create NonOfficial Pipeline -1. Create `./pipelines/NonOfficial` directory if it doesn't exist -2. Create the NonOfficial pipeline file (e.g., `./pipelines/NonOfficial/PowerShell-Packages-NonOfficial.yml`) +1. Create `.pipelines/NonOfficial` directory if it doesn't exist +2. Create the NonOfficial pipeline file (e.g., `.pipelines/NonOfficial/PowerShell-Packages-NonOfficial.yml`) 3. Copy the structure from the refactored Official pipeline 4. Hard-code the NonOfficial template reference: ```yaml @@ -72,13 +88,13 @@ The original toggle-based file becomes the Official pipeline: 5. Reference the same shared templates: ```yaml variables: - - template: ../templates/PowerShell-Packages-Variables.yml + - template: /.pipelines/templates/variables/PowerShell-Packages-Variables.yml@self stages: - - template: ../templates/PowerShell-Packages-Stages.yml + - template: /.pipelines/templates/stages/PowerShell-Packages-Stages.yml@self ``` -**Note**: The NonOfficial pipeline uses `../templates/` because it's one directory deeper than the Official pipeline. +**Note**: Always use **absolute** template paths of the form `/.pipelines/templates/...@self`. Do not use relative paths like `templates/...` or `../templates/...`. Absolute paths are anchored at the repo root and resolve consistently from any referring file, preventing breakage when files are moved between directories. ### Step 4: Link NonOfficial Pipelines to NonOfficial Dependencies @@ -124,29 +140,29 @@ Then you must configure the `ob_release_environment` parameter when referencing #### Official Pipeline Configuration -In the Official pipeline (e.g., `./pipelines/PowerShell-Packages.yml`): +In the Official pipeline (e.g., `.pipelines/PowerShell-Packages-Official.yml`): ```yaml stages: - - template: templates/PowerShell-Packages-Stages.yml + - template: /.pipelines/templates/stages/PowerShell-Packages-Stages.yml@self parameters: ob_release_environment: Production ``` #### NonOfficial Pipeline Configuration -In the NonOfficial pipeline (e.g., `./pipelines/NonOfficial/PowerShell-Packages-NonOfficial.yml`): +In the NonOfficial pipeline (e.g., `.pipelines/NonOfficial/PowerShell-Packages-NonOfficial.yml`): ```yaml stages: - - template: ../templates/PowerShell-Packages-Stages.yml + - template: /.pipelines/templates/stages/PowerShell-Packages-Stages.yml@self parameters: ob_release_environment: Test ``` #### Update Stages Template to Accept Parameter -The extracted stages template (e.g., `./pipelines/templates/PowerShell-Packages-Stages.yml`) must declare the parameter at the top: +The extracted stages template (e.g., `.pipelines/templates/stages/PowerShell-Packages-Stages.yml`) must declare the parameter at the top: ```yaml parameters: diff --git a/.pipelines/NonOfficial/PowerShell-Coordinated_Packages-NonOfficial.yml b/.pipelines/NonOfficial/PowerShell-Coordinated_Packages-NonOfficial.yml index 0b417df5c05..69506750c34 100644 --- a/.pipelines/NonOfficial/PowerShell-Coordinated_Packages-NonOfficial.yml +++ b/.pipelines/NonOfficial/PowerShell-Coordinated_Packages-NonOfficial.yml @@ -45,7 +45,7 @@ resources: ref: refs/heads/main variables: - - template: ./pipelines/templates/variables/PowerShell-Coordinated_Packages-Variables.yml@self + - template: /.pipelines/templates/variables/PowerShell-Coordinated_Packages-Variables.yml@self parameters: InternalSDKBlobURL: ${{ parameters.InternalSDKBlobURL }} ReleaseTagVar: ${{ parameters.ReleaseTagVar }} @@ -61,6 +61,7 @@ extends: LinuxHostVersion: Network: KS3 WindowsHostVersion: + Version: 2022 Network: KS3 incrementalSDLBinaryAnalysis: true globalSdl: @@ -90,7 +91,7 @@ extends: tsaOptionsFile: .config\tsaoptions.json stages: - - template: ./pipelines/templates/stages/PowerShell-Coordinated_Packages-Stages.yml@self + - template: /.pipelines/templates/stages/PowerShell-Coordinated_Packages-Stages.yml@self parameters: RUN_WINDOWS: ${{ parameters.RUN_WINDOWS }} RUN_TEST_AND_RELEASE: ${{ parameters.RUN_TEST_AND_RELEASE }} diff --git a/.pipelines/NonOfficial/PowerShell-Packages-NonOfficial.yml b/.pipelines/NonOfficial/PowerShell-Packages-NonOfficial.yml index 9419d3f29b5..0993cd69546 100644 --- a/.pipelines/NonOfficial/PowerShell-Packages-NonOfficial.yml +++ b/.pipelines/NonOfficial/PowerShell-Packages-NonOfficial.yml @@ -31,7 +31,7 @@ parameters: # parameters are shown up in ADO UI in a build queue time name: pkgs-$(BUILD.SOURCEBRANCHNAME)-nonofficial-$(Build.BuildId) variables: - - template: ./pipelines/templates/variables/PowerShell-Packages-Variables.yml@self + - template: /.pipelines/templates/variables/PowerShell-Packages-Variables.yml@self parameters: debug: ${{ parameters.debug }} ForceAzureBlobDelete: ${{ parameters.ForceAzureBlobDelete }} @@ -92,6 +92,6 @@ extends: enabled: false tsaOptionsFile: .config\tsaoptions.json stages: - - template: ./pipelines/templates/stages/PowerShell-Packages-Stages.yml@self + - template: /.pipelines/templates/stages/PowerShell-Packages-Stages.yml@self parameters: OfficialBuild: false diff --git a/.pipelines/NonOfficial/PowerShell-Release-Azure-NonOfficial.yml b/.pipelines/NonOfficial/PowerShell-Release-Azure-NonOfficial.yml index b524cb0ff81..4d406fbf9d5 100644 --- a/.pipelines/NonOfficial/PowerShell-Release-Azure-NonOfficial.yml +++ b/.pipelines/NonOfficial/PowerShell-Release-Azure-NonOfficial.yml @@ -17,7 +17,7 @@ parameters: # parameters are shown up in ADO UI in a build queue time name: ev2-$(BUILD.SOURCEBRANCHNAME)-nonofficial-$(Build.BuildId) variables: - - template: ./pipelines/templates/variables/PowerShell-Release-Azure-Variables.yml@self + - template: /.pipelines/templates/variables/PowerShell-Release-Azure-Variables.yml@self parameters: debug: ${{ parameters.debug }} diff --git a/.pipelines/NonOfficial/PowerShell-Release-NonOfficial.yml b/.pipelines/NonOfficial/PowerShell-Release-NonOfficial.yml index 7864513fc2c..ebfc599e42a 100644 --- a/.pipelines/NonOfficial/PowerShell-Release-NonOfficial.yml +++ b/.pipelines/NonOfficial/PowerShell-Release-NonOfficial.yml @@ -33,7 +33,7 @@ parameters: # parameters are shown up in ADO UI in a build queue time name: release-$(BUILD.SOURCEBRANCHNAME)-nonofficial-$(Build.BuildId) variables: - - template: ./pipelines/templates/variables/PowerShell-Release-Variables.yml@self + - template: /.pipelines/templates/variables/PowerShell-Release-Variables.yml@self parameters: debug: ${{ parameters.debug }} ReleaseTagVar: ${{ parameters.ReleaseTagVar }} @@ -98,7 +98,7 @@ extends: tsaOptionsFile: .config\tsaoptions.json stages: - - template: ./pipelines/templates/stages/PowerShell-Release-Stages.yml@self + - template: /.pipelines/templates/stages/PowerShell-Release-Stages.yml@self parameters: releaseEnvironment: Test SkipPublish: ${{ parameters.SkipPublish }} diff --git a/.pipelines/NonOfficial/PowerShell-vPack-NonOfficial.yml b/.pipelines/NonOfficial/PowerShell-vPack-NonOfficial.yml index f1f4211ca8f..071db02cff8 100644 --- a/.pipelines/NonOfficial/PowerShell-vPack-NonOfficial.yml +++ b/.pipelines/NonOfficial/PowerShell-vPack-NonOfficial.yml @@ -33,7 +33,7 @@ parameters: # parameters are shown up in ADO UI in a build queue time name: vPack_$(Build.SourceBranchName)_NonOfficial_Create.${{ parameters.createVPack }}_Name.${{ parameters.vPackName}}_$(date:yyyyMMdd).$(rev:rr) variables: - - template: ./pipelines/templates/variables/PowerShell-vPack-Variables.yml@self + - template: /.pipelines/templates/variables/PowerShell-vPack-Variables.yml@self parameters: debug: ${{ parameters.debug }} ReleaseTagVar: ${{ parameters.ReleaseTagVar }} @@ -82,7 +82,7 @@ extends: enabled: false tsaOptionsFile: .config/tsaoptions.json stages: - - template: ./pipelines/templates/stages/PowerShell-vPack-Stages.yml@self + - template: /.pipelines/templates/stages/PowerShell-vPack-Stages.yml@self parameters: createVPack: ${{ parameters.createVPack }} vPackName: ${{ parameters.vPackName }} diff --git a/.pipelines/templates/stages/PowerShell-vPack-Stages.yml b/.pipelines/templates/stages/PowerShell-vPack-Stages.yml index f0d49e8b489..01a83a5b161 100644 --- a/.pipelines/templates/stages/PowerShell-vPack-Stages.yml +++ b/.pipelines/templates/stages/PowerShell-vPack-Stages.yml @@ -50,7 +50,7 @@ stages: env: ob_restore_phase: true - - template: .pipelines/templates/SetVersionVariables.yml@self + - template: /.pipelines/templates/SetVersionVariables.yml@self parameters: ReleaseTagVar: $(ReleaseTagVar) CreateJson: yes From 6404717a8c5afcd03d2df4f03e143d97b5de11aa Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Tue, 21 Apr 2026 15:37:11 -0700 Subject: [PATCH 02/18] PMC release: Use slash instead of back-slash for Linux container (#27319) --- .pipelines/templates/release-prep-for-ev2.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pipelines/templates/release-prep-for-ev2.yml b/.pipelines/templates/release-prep-for-ev2.yml index e644bece68f..f73caa10450 100644 --- a/.pipelines/templates/release-prep-for-ev2.yml +++ b/.pipelines/templates/release-prep-for-ev2.yml @@ -23,7 +23,7 @@ stages: - group: 'mscodehub-code-read-akv' - group: 'packages.microsoft.com' - name: ob_sdl_credscan_suppressionsFile - value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + value: $(Build.SourcesDirectory)/PowerShell/.config/suppress.json steps: - checkout: self ## the global setting on lfs didn't work lfs: false From 57a6c424036bb4d3a6164fa03b7214b29abf0a71 Mon Sep 17 00:00:00 2001 From: Justin Chung <124807742+jshigetomi@users.noreply.github.com> Date: Wed, 22 Apr 2026 15:32:55 -0500 Subject: [PATCH 03/18] [release/v7.6.1] Download PMC Packages through `TemplateContext` (#27331) Co-authored-by: Justin Chung --- .../PowerShell-Release-Azure-NonOfficial.yml | 8 ++- .../PowerShell-Release-Official-Azure.yml | 2 +- .pipelines/templates/release-prep-for-ev2.yml | 46 ++++++------- .pipelines/templates/release-publish-pmc.yml | 65 ++++++++++++------- 4 files changed, 70 insertions(+), 51 deletions(-) diff --git a/.pipelines/NonOfficial/PowerShell-Release-Azure-NonOfficial.yml b/.pipelines/NonOfficial/PowerShell-Release-Azure-NonOfficial.yml index 4d406fbf9d5..b0bb4d79b39 100644 --- a/.pipelines/NonOfficial/PowerShell-Release-Azure-NonOfficial.yml +++ b/.pipelines/NonOfficial/PowerShell-Release-Azure-NonOfficial.yml @@ -67,10 +67,16 @@ extends: exactToolVersion: 4.4.2 policheck: break: true # always break the build on policheck issues. You can disable it by setting to 'false' - tsaOptionsFile: .config\tsaoptions.json + tsaOptionsFile: $(Build.SourcesDirectory)\.config\tsaoptions.json stages: - template: /.pipelines/templates/release-prep-for-ev2.yml@self parameters: skipPublish: ${{ parameters.skipPublish }} + # NonOfficial: run the publish stage to verify templateContext artifact download, + # but skip the actual Ev2 push to PMC. - template: /.pipelines/templates/release-publish-pmc.yml@self + parameters: + releaseEnvironment: Test + stagePrefix: Test + skipEv2Push: true diff --git a/.pipelines/PowerShell-Release-Official-Azure.yml b/.pipelines/PowerShell-Release-Official-Azure.yml index 24040a2463d..b5f57438925 100644 --- a/.pipelines/PowerShell-Release-Official-Azure.yml +++ b/.pipelines/PowerShell-Release-Official-Azure.yml @@ -67,7 +67,7 @@ extends: exactToolVersion: 4.4.2 policheck: break: true # always break the build on policheck issues. You can disable it by setting to 'false' - tsaOptionsFile: .config\tsaoptions.json + tsaOptionsFile: $(Build.SourcesDirectory)\.config\tsaoptions.json stages: - template: /.pipelines/templates/release-prep-for-ev2.yml@self parameters: diff --git a/.pipelines/templates/release-prep-for-ev2.yml b/.pipelines/templates/release-prep-for-ev2.yml index f73caa10450..3ad716a3af4 100644 --- a/.pipelines/templates/release-prep-for-ev2.yml +++ b/.pipelines/templates/release-prep-for-ev2.yml @@ -11,6 +11,20 @@ stages: displayName: 'Copy EV2 Files to Artifact' pool: type: linux + templateContext: + inputs: + - input: pipelineArtifact + pipeline: PSPackagesOfficial + artifactName: drop_linux_package_deb + - input: pipelineArtifact + pipeline: PSPackagesOfficial + artifactName: drop_linux_package_rpm + - input: pipelineArtifact + pipeline: PSPackagesOfficial + artifactName: drop_linux_package_mariner_x64 + - input: pipelineArtifact + pipeline: PSPackagesOfficial + artifactName: drop_linux_package_mariner_arm64 variables: - name: ob_outputDirectory value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' @@ -24,6 +38,8 @@ stages: - group: 'packages.microsoft.com' - name: ob_sdl_credscan_suppressionsFile value: $(Build.SourcesDirectory)/PowerShell/.config/suppress.json + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)/PowerShell/.config/tsaoptions.json steps: - checkout: self ## the global setting on lfs didn't work lfs: false @@ -99,39 +115,17 @@ stages: env: ob_restore_phase: true - - download: PSPackagesOfficial - artifact: 'drop_linux_package_deb' - displayName: 'Download artifact containing .deb_amd64.deb file from PSPackagesOfficial triggering pipeline' - env: - ob_restore_phase: true - - - download: PSPackagesOfficial - artifact: 'drop_linux_package_rpm' - displayName: 'Download artifact containing .rh.x64_86.rpm file from PSPackagesOfficial triggering pipeline' - env: - ob_restore_phase: true - - - download: PSPackagesOfficial - artifact: 'drop_linux_package_mariner_x64' - displayName: 'Download artifact containing .cm.x86_64.rpm file from PSPackagesOfficial triggering pipeline' - env: - ob_restore_phase: true - - - download: PSPackagesOfficial - artifact: 'drop_linux_package_mariner_arm64' - displayName: 'Download artifact containing .cm.aarch64.rpm file from PSPackagesOfficial triggering pipeline' - env: - ob_restore_phase: true - - pwsh: | Write-Verbose -Verbose "Copy ESRP signed .deb and .rpm packages" - $downloadedPipelineFolder = Join-Path '$(Pipeline.Workspace)' -ChildPath 'PSPackagesOfficial' + # templateContext.inputs places the PSPackagesOfficial pipelineArtifact files + # directly under $(Pipeline.Workspace), not in per-artifact subfolders. + $downloadedPipelineFolder = '$(Pipeline.Workspace)' $srcFilesFolder = Join-Path -Path '$(Pipeline.Workspace)' -ChildPath 'SourceFiles' New-Item -Path $srcFilesFolder -ItemType Directory $packagesFolder = Join-Path -Path $srcFilesFolder -ChildPath 'packages' New-Item -Path $packagesFolder -ItemType Directory - $packageFiles = Get-ChildItem -Path $downloadedPipelineFolder -Recurse -Directory -Filter "drop_*" | Get-ChildItem -File -Include *.deb, *.rpm + $packageFiles = Get-ChildItem -Path $downloadedPipelineFolder -File | Where-Object { $_.Extension -in '.deb', '.rpm' } foreach ($file in $packageFiles) { Write-Verbose -Verbose "copying file: $($file.FullName)" diff --git a/.pipelines/templates/release-publish-pmc.yml b/.pipelines/templates/release-publish-pmc.yml index d5454845211..dc7fc8534e3 100644 --- a/.pipelines/templates/release-publish-pmc.yml +++ b/.pipelines/templates/release-publish-pmc.yml @@ -1,37 +1,56 @@ +parameters: +- name: releaseEnvironment + type: string + default: Production + values: + - Production + - PPE + - Test +- name: approvalServiceEnvironment + type: string + default: Production + values: + - Production + - PPE + - Test +# OneBranch requires the stage name to be prefixed with the release environment. +# Official uses 'Prod' for Production; NonProd validators require '' (e.g. 'Test', 'PPE'). +- name: stagePrefix + type: string + default: Prod +# When true, the Ev2 push step is skipped. Useful for NonOfficial dry-runs that +# only want to validate artifact download via templateContext.inputs. +- name: skipEv2Push + type: boolean + default: false + stages: -- stage: 'Prod_Release' +- stage: ${{ parameters.stagePrefix }}_Release displayName: 'Deploy packages to PMC with EV2' dependsOn: - PrepForEV2 variables: - name: ob_release_environment - value: "Production" + value: ${{ parameters.releaseEnvironment }} - name: repoRoot value: $(Build.SourcesDirectory) jobs: - - job: Prod_ReleaseJob + - job: ${{ parameters.stagePrefix }}_ReleaseJob displayName: Publish to PMC pool: type: release - - steps: - - task: DownloadPipelineArtifact@2 + templateContext: inputs: - targetPath: '$(Pipeline.Workspace)' - artifact: drop_PrepForEV2_CopyEv2FilesToArtifact - displayName: 'Download drop_PrepForEV2_CopyEv2FilesToArtifact artifact that has all files needed' + - input: pipelineArtifact + artifactName: drop_PrepForEV2_CopyEv2FilesToArtifact - - task: DownloadPipelineArtifact@2 - inputs: - buildType: 'current' - targetPath: '$(Pipeline.Workspace)' - displayName: 'Download to get EV2 Files' - - - task: vsrm-ev2.vss-services-ev2.adm-release-task.ExpressV2Internal@1 - displayName: 'Ev2: Push to PMC' - inputs: - UseServerMonitorTask: true - EndpointProviderType: ApprovalService - ApprovalServiceEnvironment: Production - ServiceRootPath: '$(Pipeline.Workspace)/drop_PrepForEV2_CopyEV2FilesToArtifact/EV2Specs/ServiceGroupRoot' - RolloutSpecPath: '$(Pipeline.Workspace)/drop_PrepForEV2_CopyEV2FilesToArtifact/EV2Specs/ServiceGroupRoot/RolloutSpec.json' + steps: + - ${{ if not(parameters.skipEv2Push) }}: + - task: vsrm-ev2.vss-services-ev2.adm-release-task.ExpressV2Internal@1 + displayName: 'Ev2: Push to PMC' + inputs: + UseServerMonitorTask: true + EndpointProviderType: ApprovalService + ApprovalServiceEnvironment: ${{ parameters.approvalServiceEnvironment }} + ServiceRootPath: '$(Pipeline.Workspace)/EV2Specs/ServiceGroupRoot' + RolloutSpecPath: '$(Pipeline.Workspace)/EV2Specs/ServiceGroupRoot/RolloutSpec.json' From eafd0921ed34aec45bf0ec03122f52d6824e30ab Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 14:47:02 -0700 Subject: [PATCH 04/18] [release/v7.6.2] Add `appLicensing` capability to Appx manifest (#27437) --- assets/AppxManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/AppxManifest.xml b/assets/AppxManifest.xml index 50a8c7af45d..dfcd95935d9 100644 --- a/assets/AppxManifest.xml +++ b/assets/AppxManifest.xml @@ -43,6 +43,7 @@ + From cd8c725645621457f18532db8a7d1a6d9bb29b92 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 14:52:57 -0700 Subject: [PATCH 05/18] [release/v7.6.2] Externalize `findMissingNotices` target framework selection with ordered Windows fallback (#27424) --- tools/findMissingNotices.ps1 | 113 ++++++++++++++++++++++---- tools/findMissingNotices.targets.json | 5 ++ 2 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 tools/findMissingNotices.targets.json diff --git a/tools/findMissingNotices.ps1 b/tools/findMissingNotices.ps1 index 456b77df0ba..5ed931c5caa 100644 --- a/tools/findMissingNotices.ps1 +++ b/tools/findMissingNotices.ps1 @@ -16,6 +16,45 @@ Import-Module "$PSScriptRoot\..\.github\workflows\GHWorkflowHelper" -Force . "$PSScriptRoot\..\tools\buildCommon\startNativeExecution.ps1" . "$PSScriptRoot\clearlyDefined\Find-LastHarvestedVersion.ps1" +$targetsConfigPath = Join-Path -Path $PSScriptRoot -ChildPath 'findMissingNotices.targets.json' +if (-not (Test-Path -LiteralPath $targetsConfigPath)) { + throw "Missing target framework config file '$targetsConfigPath'. Add '/tools/findMissingNotices.targets.json' with 'dotnetTargetName' and 'windowsTargetNames' entries." +} + +try { + $targetsConfig = Get-Content -LiteralPath $targetsConfigPath -Raw -ErrorAction Stop | ConvertFrom-Json -AsHashtable -ErrorAction Stop +} catch { + throw "Failed to load target framework config from '$targetsConfigPath'. Ensure the file contains valid JSON. Error: $($_.Exception.Message)" +} + +if ($targetsConfig -isnot [hashtable]) { + throw "Invalid target framework config '$targetsConfigPath': expected a JSON object with 'dotnetTargetName' and 'windowsTargetNames'." +} + +if (-not $targetsConfig.ContainsKey('dotnetTargetName') -or [string]::IsNullOrWhiteSpace($targetsConfig['dotnetTargetName'])) { + throw "Invalid target framework config '$targetsConfigPath': 'dotnetTargetName' must be a non-empty string." +} + +if (-not $targetsConfig.ContainsKey('windowsTargetNames')) { + throw "Invalid target framework config '$targetsConfigPath': 'windowsTargetNames' must be present and must be an array." +} + +if ($null -eq $targetsConfig['windowsTargetNames'] -or $targetsConfig['windowsTargetNames'] -isnot [array]) { + throw "Invalid target framework config '$targetsConfigPath': 'windowsTargetNames' must be an array (empty array is allowed)." +} + +$script:dotnetTargetName = [string]$targetsConfig['dotnetTargetName'] +$script:windowsTargetNames = @() +foreach ($windowsTargetName in $targetsConfig['windowsTargetNames']) { + if ($windowsTargetName -isnot [string] -or [string]::IsNullOrWhiteSpace($windowsTargetName)) { + throw "Invalid target framework config '$targetsConfigPath': every entry in 'windowsTargetNames' must be a non-empty string." + } + + $script:windowsTargetNames += $windowsTargetName +} + +# Empty windowsTargetNames is valid and means "use base target fallback only". + $packageSourceName = 'findMissingNoticesNugetOrg' if (!(Get-PackageSource -Name $packageSourceName -ErrorAction SilentlyContinue)) { $null = Register-PackageSource -Name $packageSourceName -Location https://www.nuget.org/api/v2 -ProviderName NuGet @@ -195,8 +234,7 @@ function Get-CGRegistrations { $registrationChanged = $false - $dotnetTargetName = 'net10.0' - $dotnetTargetNameWin7 = 'net10.0-windows8.0' + $baseTargetName = $script:dotnetTargetName $unixProjectName = 'powershell-unix' $windowsProjectName = 'powershell-win-core' $actualRuntime = $Runtime @@ -204,35 +242,30 @@ function Get-CGRegistrations { switch -regex ($Runtime) { "alpine-.*" { $folder = $unixProjectName - $target = "$dotnetTargetName|$Runtime" - $neutralTarget = "$dotnetTargetName" + $target = "$baseTargetName|$Runtime" + $neutralTarget = "$baseTargetName" } "linux-.*" { $folder = $unixProjectName - $target = "$dotnetTargetName|$Runtime" - $neutralTarget = "$dotnetTargetName" + $target = "$baseTargetName|$Runtime" + $neutralTarget = "$baseTargetName" } "osx-.*" { $folder = $unixProjectName - $target = "$dotnetTargetName|$Runtime" - $neutralTarget = "$dotnetTargetName" - } - "win-x*" { - $sdkToUse = $winDesktopSdk - $folder = $windowsProjectName - $target = "$dotnetTargetNameWin7|$Runtime" - $neutralTarget = "$dotnetTargetNameWin7" + $target = "$baseTargetName|$Runtime" + $neutralTarget = "$baseTargetName" } "win-.*" { + $sdkToUse = $winDesktopSdk $folder = $windowsProjectName - $target = "$dotnetTargetNameWin7|$Runtime" - $neutralTarget = "$dotnetTargetNameWin7" + $target = "$baseTargetName|$actualRuntime" + $neutralTarget = "$baseTargetName" } "modules" { $folder = "modules" $actualRuntime = 'linux-x64' - $target = "$dotnetTargetName|$actualRuntime" - $neutralTarget = "$dotnetTargetName" + $target = "$baseTargetName|$actualRuntime" + $neutralTarget = "$baseTargetName" } Default { throw "Invalid runtime name: $Runtime" @@ -247,6 +280,50 @@ function Get-CGRegistrations { dotnet restore --runtime $actualRuntime "/property:SDKToUse=$sdkToUse" } $null = New-PADrive -Path $PSScriptRoot\..\src\$folder\obj\project.assets.json -Name $folder + + if ($Runtime -like "win-*") { + # Windows target selection is optional and ordered: + # 1. Try full Windows TFMs from config in order. + # 2. Fall back to the base non-Windows TFM if present. + try { + $availableTargets = @(Get-ChildItem -Path "${folder}:/targets" -ErrorAction Stop | Select-Object -ExpandProperty Name) + } catch { + throw "Unable to enumerate available targets for runtime '$Runtime' in '$folder'. Ensure dotnet restore succeeded and project.assets.json contains target data. Error: $($_.Exception.Message)" + } + + $selectedTargetName = $null + + foreach ($windowsTargetName in $script:windowsTargetNames) { + if ($windowsTargetName -in $availableTargets) { + $selectedTargetName = $windowsTargetName + break + } + } + + if (-not $selectedTargetName -and $baseTargetName -in $availableTargets) { + Write-Verbose "No configured windows target matched for '$Runtime'. Falling back to base target '$baseTargetName'." -Verbose + $selectedTargetName = $baseTargetName + } + + if (-not $selectedTargetName) { + Write-Verbose "Available targets for '$folder': $($availableTargets -join ', ')" -Verbose + if ($script:windowsTargetNames.Count -eq 0) { + throw "Unable to find a target for '$Runtime'. Tried fallback base target '$baseTargetName' (no windowsTargetNames configured). Ensure project.assets.json contains this target or update dotnetTargetName in '$targetsConfigPath'." + } + + throw "Unable to find a target for '$Runtime'. Tried configured windowsTargetNames '$($script:windowsTargetNames -join "', '")' and fallback base target '$baseTargetName'. Update '$targetsConfigPath' with a valid windows target from the available list." + } + + $target = "$selectedTargetName|$actualRuntime" + $neutralTarget = $selectedTargetName + } + + # Defensive check: non-Windows paths set targets in the switch block, + # Windows path may override them after inspecting available assets targets. + if (-not $target -or -not $neutralTarget) { + throw "Unable to determine restore targets for runtime '$Runtime'." + } + try { $targets = Get-ChildItem -Path "${folder}:/targets/$target" -ErrorAction Stop | Where-Object { $_.Type -eq 'package' } | select-object -ExpandProperty name $targets += Get-ChildItem -Path "${folder}:/targets/$neutralTarget" -ErrorAction Stop | Where-Object { $_.Type -eq 'project' } | select-object -ExpandProperty name diff --git a/tools/findMissingNotices.targets.json b/tools/findMissingNotices.targets.json new file mode 100644 index 00000000000..9e5ced685c2 --- /dev/null +++ b/tools/findMissingNotices.targets.json @@ -0,0 +1,5 @@ +{ + "dotnetTargetName": "net10.0", + "windowsTargetNames": [ + ] +} From 4aab6230a38af9c4173e03c85f1d3e34b0865e37 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 14:56:49 -0700 Subject: [PATCH 06/18] [release/v7.6.2] Add macOS binary code signing and package notarization (#27434) --- .pipelines/templates/mac.yml | 8 ++++++++ assets/macos-entitlements.plist | 14 ++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 assets/macos-entitlements.plist diff --git a/.pipelines/templates/mac.yml b/.pipelines/templates/mac.yml index 1699207c657..cb82a7bf4c0 100644 --- a/.pipelines/templates/mac.yml +++ b/.pipelines/templates/mac.yml @@ -69,6 +69,14 @@ jobs: $psOptPath = "$(OB_OUTPUTDIRECTORY)/psoptions.json" Save-PSOptions -PSOptionsPath $psOptPath + $entitlements = "$(PowerShellRoot)/assets/macos-entitlements.plist" + $pwshBin = "$(OB_OUTPUTDIRECTORY)/pwsh" + Write-Verbose -Verbose "Applying entitlements to $pwshBin" + codesign --sign - --force --options runtime --entitlements $entitlements $pwshBin + if ($LASTEXITCODE -ne 0) { + throw "codesign failed with exit code $LASTEXITCODE" + } + # Since we are using custom pool for macOS, we need to use artifact.upload to publish the artifacts Write-Host "##vso[artifact.upload containerfolder=$artifactName;artifactname=$artifactName]$(OB_OUTPUTDIRECTORY)" diff --git a/assets/macos-entitlements.plist b/assets/macos-entitlements.plist new file mode 100644 index 00000000000..9d534f4f4bf --- /dev/null +++ b/assets/macos-entitlements.plist @@ -0,0 +1,14 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.cs.disable-library-validation + + + From 901bf012d4f7b6ca4fe439bdaa646d76e8849d57 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 14:57:26 -0700 Subject: [PATCH 07/18] [release/v7.6.2] Create PowerShell package for arm debian distribution (#27433) --- .pipelines/templates/linux-package-build.yml | 1 + .../release-validate-packagenames.yml | 2 +- .../stages/PowerShell-Packages-Stages.yml | 7 ++++ .../linux/package-validation.tests.ps1 | 33 ++++++++++--------- tools/packaging/packaging.psm1 | 24 ++++++++++++-- 5 files changed, 47 insertions(+), 20 deletions(-) diff --git a/.pipelines/templates/linux-package-build.yml b/.pipelines/templates/linux-package-build.yml index bb7ed723f42..1487455107e 100644 --- a/.pipelines/templates/linux-package-build.yml +++ b/.pipelines/templates/linux-package-build.yml @@ -124,6 +124,7 @@ jobs: 'tar' { 'Signed-linux-x64', 'powershell*.tar.gz' } 'tar-alpine-fxdependent' { 'Signed-fxdependent-noopt-linux-musl-x64', 'powershell*.tar.gz' } 'deb' { 'Signed-linux-x64', 'powershell*.deb' } + 'deb-arm64' { 'Signed-linux-arm64', 'powershell*.deb' } 'rpm-fxdependent' { 'Signed-fxdependent-linux-x64', 'powershell*.rpm' } 'rpm-fxdependent-arm64' { 'Signed-fxdependent-linux-arm64', 'powershell*.rpm' } 'rpm' { 'Signed-linux-x64', 'powershell*.rpm' } diff --git a/.pipelines/templates/release-validate-packagenames.yml b/.pipelines/templates/release-validate-packagenames.yml index 6344418cd8f..5bce4361669 100644 --- a/.pipelines/templates/release-validate-packagenames.yml +++ b/.pipelines/templates/release-validate-packagenames.yml @@ -109,7 +109,7 @@ jobs: - pwsh: | $message = @() Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.deb | ForEach-Object { - if($_.Name -notmatch 'powershell(-preview|-lts)?_\d+\.\d+\.\d+([\-~][a-z]*.\d+)?-\d\.deb_amd64\.deb') + if($_.Name -notmatch 'powershell(-preview|-lts)?_\d+\.\d+\.\d+([\-~][a-z]*.\d+)?-\d\.deb_(amd64|arm64)\.deb') { $messageInstance = "$($_.Name) is not a valid package name" $message += $messageInstance diff --git a/.pipelines/templates/stages/PowerShell-Packages-Stages.yml b/.pipelines/templates/stages/PowerShell-Packages-Stages.yml index b1efb2a8097..285a9035d0a 100644 --- a/.pipelines/templates/stages/PowerShell-Packages-Stages.yml +++ b/.pipelines/templates/stages/PowerShell-Packages-Stages.yml @@ -87,6 +87,13 @@ stages: packageType: deb jobName: deb + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_arm64' + signedDrop: 'drop_linux_sign_linux_arm64' + packageType: deb-arm64 + jobName: deb_arm64 + - template: /.pipelines/templates/linux-package-build.yml@self parameters: unsignedDrop: 'drop_linux_build_linux_fxd_x64_mariner' diff --git a/test/packaging/linux/package-validation.tests.ps1 b/test/packaging/linux/package-validation.tests.ps1 index 594a729fa77..3b961120f2f 100644 --- a/test/packaging/linux/package-validation.tests.ps1 +++ b/test/packaging/linux/package-validation.tests.ps1 @@ -9,20 +9,20 @@ Describe "Linux Package Name Validation" { } else { $env:SYSTEM_ARTIFACTSDIRECTORY } - + if (-not $artifactsDir) { throw "Artifacts directory not found. GITHUB_WORKSPACE or SYSTEM_ARTIFACTSDIRECTORY must be set." } - + Write-Verbose "Artifacts directory: $artifactsDir" -Verbose } - + Context "RPM Package Names" { It "Should have valid RPM package names" { $rpmPackages = Get-ChildItem -Path $artifactsDir -Recurse -Filter *.rpm -ErrorAction SilentlyContinue - + $rpmPackages.Count | Should -BeGreaterThan 0 -Because "At least one RPM package should exist in the artifacts directory" - + $invalidPackages = @() # Regex pattern for valid RPM package names. # Breakdown: @@ -42,25 +42,26 @@ Describe "Linux Package Name Validation" { Write-Warning "$($package.Name) is not a valid RPM package name" } } - + if ($invalidPackages.Count -gt 0) { throw ($invalidPackages | Out-String) } } } - + Context "DEB Package Names" { It "Should have valid DEB package names" { $debPackages = Get-ChildItem -Path $artifactsDir -Recurse -Filter *.deb -ErrorAction SilentlyContinue - + $debPackages.Count | Should -BeGreaterThan 0 -Because "At least one DEB package should exist in the artifacts directory" - + $invalidPackages = @() # Regex pattern for valid DEB package names. # Valid examples: # - powershell-preview_7.6.0-preview.6-1.deb_amd64.deb # - powershell-lts_7.4.13-1.deb_amd64.deb # - powershell_7.4.13-1.deb_amd64.deb + # - powershell_7.6.0-1.deb_arm64.deb # Breakdown: # ^powershell : Starts with 'powershell' # (-preview|-lts)? : Optionally '-preview' or '-lts' @@ -78,19 +79,19 @@ Describe "Linux Package Name Validation" { Write-Warning "$($package.Name) is not a valid DEB package name" } } - + if ($invalidPackages.Count -gt 0) { throw ($invalidPackages | Out-String) } } } - + Context "Tar.Gz Package Names" { It "Should have valid tar.gz package names" { $tarPackages = Get-ChildItem -Path $artifactsDir -Recurse -Filter *.tar.gz -ErrorAction SilentlyContinue - + $tarPackages.Count | Should -BeGreaterThan 0 -Because "At least one tar.gz package should exist in the artifacts directory" - + $invalidPackages = @() foreach ($package in $tarPackages) { # Pattern matches: powershell-7.6.0-preview.6-linux-x64.tar.gz or powershell-7.6.0-linux-x64.tar.gz @@ -100,17 +101,17 @@ Describe "Linux Package Name Validation" { Write-Warning "$($package.Name) is not a valid tar.gz package name" } } - + if ($invalidPackages.Count -gt 0) { throw ($invalidPackages | Out-String) } } } - + Context "Package Existence" { It "Should find at least one package in artifacts directory" { $allPackages = Get-ChildItem -Path $artifactsDir -Recurse -Include *.rpm, *.tar.gz, *.deb -ErrorAction SilentlyContinue - + $allPackages.Count | Should -BeGreaterThan 0 -Because "At least one package should exist in the artifacts directory" } } diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index b0131d39ebf..641299df382 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -52,7 +52,7 @@ function Start-PSPackage { [string]$Name = "powershell", # Ubuntu, CentOS, Fedora, macOS, and Windows packages are supported - [ValidateSet("msix", "deb", "osxpkg", "rpm", "rpm-fxdependent", "rpm-fxdependent-arm64", "msi", "zip", "zip-pdb", "tar", "tar-arm", "tar-arm64", "tar-alpine", "fxdependent", "fxdependent-win-desktop", "min-size", "tar-alpine-fxdependent")] + [ValidateSet("msix", "deb", "deb-arm64", "osxpkg", "rpm", "rpm-fxdependent", "rpm-fxdependent-arm64", "msi", "zip", "zip-pdb", "tar", "tar-arm", "tar-arm64", "tar-alpine", "fxdependent", "fxdependent-win-desktop", "min-size", "tar-alpine-fxdependent")] [string[]]$Type, # Generate windows downlevel package @@ -641,6 +641,24 @@ function Start-PSPackage { } } } + 'deb-arm64' { + $Arguments = @{ + Type = 'deb' + PackageSourcePath = $Source + Name = $Name + Version = $Version + Force = $Force + NoSudo = $NoSudo + LTS = $LTS + HostArchitecture = "arm64" + } + foreach ($Distro in $Script:DebianDistributions) { + $Arguments["Distribution"] = $Distro + if ($PSCmdlet.ShouldProcess("Create DEB Package for $Distro")) { + New-UnixPackage @Arguments + } + } + } 'rpm' { $Arguments = @{ Type = 'rpm' @@ -1060,7 +1078,7 @@ function New-UnixPackage { # This is a string because strings are appended to it [string]$Iteration = "1", - # Host architecture values allowed for deb type packages: amd64 + # Host architecture values allowed for deb type packages: amd64, arm64 # Host architecture values allowed for rpm type packages include: x86_64, aarch64, native, all, noarch, any # Host architecture values allowed for osxpkg type packages include: x86_64, arm64 [string] @@ -5806,4 +5824,4 @@ function Test-IsProductFile { } return $false -} +} \ No newline at end of file From 783caca811dcca6c9e622ff8fbfb404f58de3d50 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 14:58:02 -0700 Subject: [PATCH 08/18] [release/v7.6.2] Fix checks for local user config file paths (#27432) --- .../host/msh/ConsoleHost.cs | 13 +- .../host/msh/UpdatesNotification.cs | 4 +- .../CoreCLR/CorePsPlatform.cs | 46 +- .../engine/CommandDiscovery.cs | 10 +- .../engine/Modules/AnalysisCache.cs | 7 +- .../engine/PSConfiguration.cs | 10 +- .../engine/hostifaces/HostUtilities.cs | 9 +- .../engine/hostifaces/MshHostUserInterface.cs | 5 + .../utils/Telemetry.cs | 927 +++++++++--------- 9 files changed, 552 insertions(+), 479 deletions(-) diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs index a84d18e6684..857c8f6345d 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs @@ -148,13 +148,16 @@ internal static int Start( try { string profileDir = Platform.CacheDirectory; -#if !UNIX - if (!Directory.Exists(profileDir)) + if (!string.IsNullOrEmpty(profileDir)) { - Directory.CreateDirectory(profileDir); - } +#if !UNIX + if (!Directory.Exists(profileDir)) + { + Directory.CreateDirectory(profileDir); + } #endif - ProfileOptimization.SetProfileRoot(profileDir); + ProfileOptimization.SetProfileRoot(profileDir); + } } catch { diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs index d0b1ed4572c..eb4557c04d2 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/UpdatesNotification.cs @@ -60,12 +60,12 @@ internal static class UpdatesNotification static UpdatesNotification() { s_notificationType = GetNotificationType(); - CanNotifyUpdates = s_notificationType != NotificationType.Off; + CanNotifyUpdates = s_notificationType != NotificationType.Off + && Platform.TryDeriveFromCache(PSVersionInfo.GitCommitId, out s_cacheDirectory); if (CanNotifyUpdates) { s_enumOptions = new EnumerationOptions(); - s_cacheDirectory = Path.Combine(Platform.CacheDirectory, PSVersionInfo.GitCommitId); // Build the template/pattern strings for the configured notification type. string typeNum = ((int)s_notificationType).ToString(); diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index dc5db5f2c48..530493f320a 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -167,8 +167,13 @@ public static bool IsStaSupported internal static readonly string ConfigDirectory = Platform.SelectProductNameForDirectory(Platform.XDG_Type.CONFIG); #else // Gets the location for cache and config folders. - internal static readonly string CacheDirectory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"\Microsoft\PowerShell"; - internal static readonly string ConfigDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + @"\PowerShell"; + internal static readonly string CacheDirectory = SafeDeriveFromSpecialFolder( + Environment.SpecialFolder.LocalApplicationData, + @"Microsoft\PowerShell"); + + internal static readonly string ConfigDirectory = SafeDeriveFromSpecialFolder( + Environment.SpecialFolder.Personal, + @"PowerShell"); private static readonly Lazy _isStaSupported = new Lazy(() => { @@ -189,6 +194,30 @@ public static bool IsStaSupported private static bool? _isWindowsDesktop = null; #endif + internal static bool TryDeriveFromCache(string path1, out string result) + { + if (CacheDirectory is null or []) + { + result = null; + return false; + } + + result = Path.Combine(CacheDirectory, path1); + return true; + } + + internal static bool TryDeriveFromCache(string path1, string path2, out string result) + { + if (CacheDirectory is null or []) + { + result = null; + return false; + } + + result = Path.Combine(CacheDirectory, path1, path2); + return true; + } + // format files internal static readonly string[] FormatFileNames = new string[] { @@ -218,6 +247,17 @@ internal static class CommonEnvVariableNames #endif } + private static string SafeDeriveFromSpecialFolder(Environment.SpecialFolder specialFolder, string subPath) + { + string basePath = Environment.GetFolderPath(specialFolder, Environment.SpecialFolderOption.DoNotVerify); + if (string.IsNullOrWhiteSpace(basePath)) + { + return string.Empty; + } + + return Path.Join(basePath, subPath); + } + #if UNIX private static string s_tempHome = null; @@ -360,7 +400,7 @@ internal static string GetFolderPath(Environment.SpecialFolder folder) _ => throw new NotSupportedException() }; #else - return Environment.GetFolderPath(folder); + return Environment.GetFolderPath(folder, Environment.SpecialFolderOption.DoNotVerify); #endif } diff --git a/src/System.Management.Automation/engine/CommandDiscovery.cs b/src/System.Management.Automation/engine/CommandDiscovery.cs index cf4f561c983..9ca87f4c834 100644 --- a/src/System.Management.Automation/engine/CommandDiscovery.cs +++ b/src/System.Management.Automation/engine/CommandDiscovery.cs @@ -1231,11 +1231,17 @@ internal LookupPathCollection GetLookupDirectoryPaths() string tempDir = directory.TrimStart(); if (tempDir.EqualsOrdinalIgnoreCase("~")) { - tempDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + tempDir = Environment.GetFolderPath( + Environment.SpecialFolder.UserProfile, + Environment.SpecialFolderOption.DoNotVerify); } else if (tempDir.StartsWith("~" + Path.DirectorySeparatorChar)) { - tempDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + Path.DirectorySeparatorChar + tempDir.Substring(2); + tempDir = Environment.GetFolderPath( + Environment.SpecialFolder.UserProfile, + Environment.SpecialFolderOption.DoNotVerify) + + Path.DirectorySeparatorChar + + tempDir.Substring(2); } _cachedPath.Add(tempDir); diff --git a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs index 44dc9f1b610..962022e1c54 100644 --- a/src/System.Management.Automation/engine/Modules/AnalysisCache.cs +++ b/src/System.Management.Automation/engine/Modules/AnalysisCache.cs @@ -664,6 +664,11 @@ private static byte[] GetHeader() public void QueueSerialization() { + if (string.IsNullOrEmpty(s_cacheStoreLocation)) + { + return; + } + // We expect many modules to rapidly call for serialization. // Instead of doing it right away, we'll queue a task that starts writing // after it seems like we've stopped adding stuff to write out. This is @@ -1121,7 +1126,7 @@ static AnalysisCacheData() cacheFileName = string.Create(CultureInfo.InvariantCulture, $"{cacheFileName}-{hashString}"); } - s_cacheStoreLocation = Path.Combine(Platform.CacheDirectory, cacheFileName); + Platform.TryDeriveFromCache(cacheFileName, out s_cacheStoreLocation); } } diff --git a/src/System.Management.Automation/engine/PSConfiguration.cs b/src/System.Management.Automation/engine/PSConfiguration.cs index e321423f768..419a4cae95f 100644 --- a/src/System.Management.Automation/engine/PSConfiguration.cs +++ b/src/System.Management.Automation/engine/PSConfiguration.cs @@ -89,7 +89,10 @@ private PowerShellConfig() // Note: This directory may or may not exist depending upon the execution scenario. // Writes will attempt to create the directory if it does not already exist. perUserConfigDirectory = Platform.ConfigDirectory; - perUserConfigFile = Path.Combine(perUserConfigDirectory, ConfigFileName); + if (!string.IsNullOrEmpty(perUserConfigDirectory)) + { + perUserConfigFile = Path.Combine(perUserConfigDirectory, ConfigFileName); + } emptyConfig = new JObject(); configRoots = new JObject[2]; @@ -387,6 +390,11 @@ internal PSKeyword GetLogKeywords() private T ReadValueFromFile(ConfigScope scope, string key, T defaultValue = default) { string fileName = GetConfigFilePath(scope); + if (string.IsNullOrEmpty(fileName)) + { + return defaultValue; + } + JObject configData = configRoots[(int)scope]; if (configData == null) diff --git a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs index f06399e09d2..8e5d30be71f 100644 --- a/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs +++ b/src/System.Management.Automation/engine/hostifaces/HostUtilities.cs @@ -148,10 +148,11 @@ internal static string GetFullProfileFileName(string shellId, bool forCurrentUse else { basePath = GetAllUsersFolderPath(shellId); - if (string.IsNullOrEmpty(basePath)) - { - return string.Empty; - } + } + + if (string.IsNullOrEmpty(basePath)) + { + return string.Empty; } string profileName = useTestProfile ? "profile_test.ps1" : "profile.ps1"; diff --git a/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs b/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs index 29fc5fa1f7f..5f51ba15751 100644 --- a/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs +++ b/src/System.Management.Automation/engine/hostifaces/MshHostUserInterface.cs @@ -1156,6 +1156,11 @@ internal static string GetTranscriptPath(string baseDirectory, bool includeDate) } } + if (string.IsNullOrEmpty(baseDirectory)) + { + return string.Empty; + } + if (includeDate) { baseDirectory = Path.Combine(baseDirectory, DateTime.Now.ToString("yyyyMMdd", CultureInfo.InvariantCulture)); diff --git a/src/System.Management.Automation/utils/Telemetry.cs b/src/System.Management.Automation/utils/Telemetry.cs index 8ab34950f60..9a67f4bb6ed 100644 --- a/src/System.Management.Automation/utils/Telemetry.cs +++ b/src/System.Management.Automation/utils/Telemetry.cs @@ -163,6 +163,8 @@ public static class ApplicationInsightsTelemetry private static readonly HashSet s_knownSubsystemNames; + private static readonly string s_uuidPath; + /// Gets a value indicating whether telemetry can be sent. public static bool CanSendTelemetry { get; private set; } @@ -176,470 +178,474 @@ public static class ApplicationInsightsTelemetry static ApplicationInsightsTelemetry() { // If we can't send telemetry, there's no reason to do any of this - CanSendTelemetry = !Utils.GetEnvironmentVariableAsBool(name: _telemetryOptoutEnvVar, defaultValue: false); - if (CanSendTelemetry) + CanSendTelemetry = !Utils.GetEnvironmentVariableAsBool(name: _telemetryOptoutEnvVar, defaultValue: false) + && Platform.TryDeriveFromCache("telemetry.uuid", out s_uuidPath); + + if (!CanSendTelemetry) { - s_sessionId = Guid.NewGuid().ToString(); - TelemetryConfiguration configuration = TelemetryConfiguration.CreateDefault(); - configuration.ConnectionString = "InstrumentationKey=" + _psCoreTelemetryKey; + return; + } - // Set this to true to reduce latency during development - configuration.TelemetryChannel.DeveloperMode = false; + s_sessionId = Guid.NewGuid().ToString(); + TelemetryConfiguration configuration = TelemetryConfiguration.CreateDefault(); + configuration.ConnectionString = "InstrumentationKey=" + _psCoreTelemetryKey; - // Be sure to obscure any information about the client node name. - configuration.TelemetryInitializers.Add(new NameObscurerTelemetryInitializer()); + // Set this to true to reduce latency during development + configuration.TelemetryChannel.DeveloperMode = false; - s_telemetryClient = new TelemetryClient(configuration); + // Be sure to obscure any information about the client node name. + configuration.TelemetryInitializers.Add(new NameObscurerTelemetryInitializer()); - // use a hashset when looking for module names, it should be quicker than a string comparison - s_knownModules = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "AADRM", - "activedirectory", - "adcsadministration", - "adcsdeployment", - "addsadministration", - "addsdeployment", - "adfs", - "adrms", - "adrmsadmin", - "agpm", - "AIShell", - "appbackgroundtask", - "applocker", - "appv", - "appvclient", - "appvsequencer", - "appvserver", - "appx", - "assignedaccess", - "Az", - "Az.Accounts", - "Az.Advisor", - "Az.Aks", - "Az.AlertsManagement", - "Az.AnalysisServices", - "Az.ApiManagement", - "Az.ApplicationInsights", - "Az.Attestation", - "Az.Automation", - "Az.Batch", - "Az.Billing", - "Az.Blueprint", - "Az.Cdn", - "Az.CognitiveServices", - "Az.Compute", - "Az.ContainerInstance", - "Az.ContainerRegistry", - "Az.DataBox", - "Az.DataFactory", - "Az.DataLakeAnalytics", - "Az.DataLakeStore", - "Az.DataMigration", - "Az.DataShare", - "Az.DeploymentManager", - "Az.DeviceProvisioningServices", - "Az.DevSpaces", - "Az.DevTestLabs", - "Az.Dns", - "Az.EventGrid", - "Az.EventHub", - "Az.FrontDoor", - "Az.GuestConfiguration", - "Az.HDInsight", - "Az.HealthcareApis", - "Az.IotCentral", - "Az.IotHub", - "Az.KeyVault", - "Az.Kusto", - "Az.LogicApp", - "Az.MachineLearning", - "Az.ManagedServiceIdentity", - "Az.ManagedServices", - "Az.ManagementPartner", - "Az.Maps", - "Az.MarketplaceOrdering", - "Az.Media", - "Az.MixedReality", - "Az.Monitor", - "Az.NetAppFiles", - "Az.Network", - "Az.NotificationHubs", - "Az.OperationalInsights", - "Az.Peering", - "Az.PolicyInsights", - "Az.PowerBIEmbedded", - "Az.PrivateDns", - "Az.RecoveryServices", - "Az.RedisCache", - "Az.Relay", - "Az.Reservations", - "Az.ResourceGraph", - "Az.Resources", - "Az.Search", - "Az.Security", - "Az.ServiceBus", - "Az.ServiceFabric", - "Az.SignalR", - "Az.Sql", - "Az.Storage", - "Az.StorageSync", - "Az.StorageTable", - "Az.StreamAnalytics", - "Az.Subscription", - "Az.Tools.Predictor", - "Az.TrafficManager", - "Az.Websites", - "Azs.Azurebridge.Admin", - "Azs.Backup.Admin", - "Azs.Commerce.Admin", - "Azs.Compute.Admin", - "Azs.Fabric.Admin", - "Azs.Gallery.Admin", - "Azs.Infrastructureinsights.Admin", - "Azs.Keyvault.Admin", - "Azs.Network.Admin", - "Azs.Storage.Admin", - "Azs.Subscriptions", - "Azs.Subscriptions.Admin", - "Azs.Update.Admin", - "AzStorageTable", - "Azure", - "Azure.AnalysisServices", - "Azure.Storage", - "AzureAD", - "AzureInformationProtection", - "AzureRM.Aks", - "AzureRM.AnalysisServices", - "AzureRM.ApiManagement", - "AzureRM.ApplicationInsights", - "AzureRM.Automation", - "AzureRM.Backup", - "AzureRM.Batch", - "AzureRM.Billing", - "AzureRM.Cdn", - "AzureRM.CognitiveServices", - "AzureRm.Compute", - "AzureRM.Compute.ManagedService", - "AzureRM.Consumption", - "AzureRM.ContainerInstance", - "AzureRM.ContainerRegistry", - "AzureRM.DataFactories", - "AzureRM.DataFactoryV2", - "AzureRM.DataLakeAnalytics", - "AzureRM.DataLakeStore", - "AzureRM.DataMigration", - "AzureRM.DeploymentManager", - "AzureRM.DeviceProvisioningServices", - "AzureRM.DevSpaces", - "AzureRM.DevTestLabs", - "AzureRm.Dns", - "AzureRM.EventGrid", - "AzureRM.EventHub", - "AzureRM.FrontDoor", - "AzureRM.HDInsight", - "AzureRm.Insights", - "AzureRM.IotCentral", - "AzureRM.IotHub", - "AzureRm.Keyvault", - "AzureRM.LocationBasedServices", - "AzureRM.LogicApp", - "AzureRM.MachineLearning", - "AzureRM.MachineLearningCompute", - "AzureRM.ManagedServiceIdentity", - "AzureRM.ManagementPartner", - "AzureRM.Maps", - "AzureRM.MarketplaceOrdering", - "AzureRM.Media", - "AzureRM.Network", - "AzureRM.NotificationHubs", - "AzureRM.OperationalInsights", - "AzureRM.PolicyInsights", - "AzureRM.PowerBIEmbedded", - "AzureRM.Profile", - "AzureRM.RecoveryServices", - "AzureRM.RecoveryServices.Backup", - "AzureRM.RecoveryServices.SiteRecovery", - "AzureRM.RedisCache", - "AzureRM.Relay", - "AzureRM.Reservations", - "AzureRM.ResourceGraph", - "AzureRM.Resources", - "AzureRM.Scheduler", - "AzureRM.Search", - "AzureRM.Security", - "AzureRM.ServerManagement", - "AzureRM.ServiceBus", - "AzureRM.ServiceFabric", - "AzureRM.SignalR", - "AzureRM.SiteRecovery", - "AzureRM.Sql", - "AzureRm.Storage", - "AzureRM.StorageSync", - "AzureRM.StreamAnalytics", - "AzureRM.Subscription", - "AzureRM.Subscription.Preview", - "AzureRM.Tags", - "AzureRM.TrafficManager", - "AzureRm.UsageAggregates", - "AzureRm.Websites", - "AzureRmStorageTable", - "bestpractices", - "bitlocker", - "bitstransfer", - "booteventcollector", - "branchcache", - "CimCmdlets", - "clusterawareupdating", - "CompatPowerShellGet", - "configci", - "ConfigurationManager", - "CompletionPredictor", - "DataProtectionManager", - "dcbqos", - "deduplication", - "defender", - "devicehealthattestation", - "dfsn", - "dfsr", - "dhcpserver", - "directaccessclient", - "directaccessclientcomponent", - "directaccessclientcomponents", - "dism", - "dnsclient", - "dnsserver", - "ElasticDatabaseJobs", - "EventTracingManagement", - "failoverclusters", - "fileserverresourcemanager", - "FIMAutomation", - "GPRegistryPolicy", - "grouppolicy", - "hardwarecertification", - "hcs", - "hgsattestation", - "hgsclient", - "hgsdiagnostics", - "hgskeyprotection", - "hgsserver", - "hnvdiagnostics", - "hostcomputeservice", - "hpc", - "HPC.ACM", - "HPC.ACM.API.PS", - "HPCPack2016", - "hyper-v", - "IISAdministration", - "international", - "ipamserver", - "iscsi", - "iscsitarget", - "ISE", - "kds", - "Microsoft.MBAM", - "Microsoft.MEDV", - "MgmtSvcAdmin", - "MgmtSvcConfig", - "MgmtSvcMySql", - "MgmtSvcSqlServer", - "Microsoft.AzureStack.ReadinessChecker", - "Microsoft.Crm.PowerShell", - "Microsoft.DiagnosticDataViewer", - "Microsoft.DirectoryServices.MetadirectoryServices.Config", - "Microsoft.Dynamics.Nav.Apps.Management", - "Microsoft.Dynamics.Nav.Apps.Tools", - "Microsoft.Dynamics.Nav.Ide", - "Microsoft.Dynamics.Nav.Management", - "Microsoft.Dynamics.Nav.Model.Tools", - "Microsoft.Dynamics.Nav.Model.Tools.Crm", - "Microsoft.EnterpriseManagement.Warehouse.Cmdlets", - "Microsoft.Medv.Administration.Commands.WorkspacePackager", - "Microsoft.PowerApps.Checker.PowerShell", - "Microsoft.PowerShell.Archive", - "Microsoft.PowerShell.ConsoleGuiTools", - "Microsoft.PowerShell.Core", - "Microsoft.PowerShell.Crescendo", - "Microsoft.PowerShell.Diagnostics", - "Microsoft.PowerShell.Host", - "Microsoft.PowerShell.LocalAccounts", - "Microsoft.PowerShell.Management", - "Microsoft.PowerShell.ODataUtils", - "Microsoft.PowerShell.Operation.Validation", - "Microsoft.PowerShell.PSAdapter", - "Microsoft.PowerShell.PSResourceGet", - "Microsoft.PowerShell.RemotingTools", - "Microsoft.PowerShell.SecretManagement", - "Microsoft.PowerShell.SecretStore", - "Microsoft.PowerShell.Security", - "Microsoft.PowerShell.TextUtility", - "Microsoft.PowerShell.Utility", - "Microsoft.SharePoint.Powershell", - "Microsoft.SystemCenter.ServiceManagementAutomation", - "Microsoft.Windows.ServerManager.Migration", - "Microsoft.WSMan.Management", - "Microsoft.Xrm.OnlineManagementAPI", - "Microsoft.Xrm.Tooling.CrmConnector.PowerShell", - "Microsoft.Xrm.Tooling.PackageDeployment", - "Microsoft.Xrm.Tooling.PackageDeployment.Powershell", - "Microsoft.Xrm.Tooling.Testing", - "MicrosoftPowerBIMgmt", - "MicrosoftPowerBIMgmt.Data", - "MicrosoftPowerBIMgmt.Profile", - "MicrosoftPowerBIMgmt.Reports", - "MicrosoftPowerBIMgmt.Workspaces", - "MicrosoftStaffHub", - "MicrosoftTeams", - "MIMPAM", - "mlSqlPs", - "MMAgent", - "MPIO", - "MsDtc", - "MSMQ", - "MSOnline", - "MSOnlineBackup", - "WmsCmdlets", - "WmsCmdlets3", - "NanoServerImageGenerator", - "NAVWebClientManagement", - "NetAdapter", - "NetConnection", - "NetEventPacketCapture", - "Netlbfo", - "Netldpagent", - "NetNat", - "Netqos", - "NetSecurity", - "NetSwitchtTeam", - "Nettcpip", - "Netwnv", - "NetworkConnectivity", - "NetworkConnectivityStatus", - "NetworkController", - "NetworkControllerDiagnostics", - "NetworkloadBalancingClusters", - "NetworkSwitchManager", - "NetworkTransition", - "NFS", - "NPS", - "OfficeWebapps", - "OperationsManager", - "PackageManagement", - "PartnerCenter", - "pcsvdevice", - "pef", - "Pester", - "pkiclient", - "platformidentifier", - "pnpdevice", - "PowerShellEditorServices", - "PowerShellGet", - "powershellwebaccess", - "printmanagement", - "ProcessMitigations", - "provisioning", - "PSDesiredStateConfiguration", - "PSDiagnostics", - "PSReadLine", - "PSScheduledJob", - "PSScriptAnalyzer", - "PSWorkflow", - "PSWorkflowUtility", - "RemoteAccess", - "RemoteDesktop", - "RemoteDesktopServices", - "ScheduledTasks", - "Secureboot", - "ServerCore", - "ServerManager", - "ServerManagerTasks", - "ServerMigrationcmdlets", - "ServiceFabric", - "Microsoft.Online.SharePoint.PowerShell", - "shieldedvmdatafile", - "shieldedvmprovisioning", - "shieldedvmtemplate", - "SkypeOnlineConnector", - "SkypeForBusinessHybridHealth", - "smbshare", - "smbwitness", - "smisconfig", - "softwareinventorylogging", - "SPFAdmin", - "Microsoft.SharePoint.MigrationTool.PowerShell", - "sqlps", - "SqlServer", - "StartLayout", - "StartScreen", - "Storage", - "StorageDsc", - "storageqos", - "Storagereplica", - "Storagespaces", - "Syncshare", - "System.Center.Service.Manager", - "TLS", - "TroubleshootingPack", - "TrustedPlatformModule", - "UEV", - "UpdateServices", - "UserAccessLogging", - "vamt", - "VirtualMachineManager", - "vpnclient", - "WasPSExt", - "WDAC", - "WDS", - "WebAdministration", - "WebAdministrationDsc", - "WebApplicationProxy", - "WebSites", - "Whea", - "WhiteboardAdmin", - "WindowsDefender", - "WindowsDefenderDsc", - "WindowsDeveloperLicense", - "WindowsDiagnosticData", - "WindowsErrorReporting", - "WindowServerRackup", - "WindowsSearch", - "WindowsServerBackup", - "WindowsUpdate", - "WinGetCommandNotFound", - "wsscmdlets", - "wsssetup", - "wsus", - "xActiveDirectory", - "xBitLocker", - "xDefender", - "xDhcpServer", - "xDismFeature", - "xDnsServer", - "xHyper-V", - "xHyper-VBackup", - "xPSDesiredStateConfiguration", - "xSmbShare", - "xSqlPs", - "xStorage", - "xWebAdministration", - "xWindowsUpdate", - }; - - // use a hashset when looking for module names, it should be quicker than a string comparison - s_knownModuleTags = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "CrescendoBuilt", - }; + s_telemetryClient = new TelemetryClient(configuration); - s_uniqueUserIdentifier = GetUniqueIdentifier().ToString(); - s_knownSubsystemNames = new HashSet(StringComparer.OrdinalIgnoreCase) - { - "Completion", - "General Feedback", - "Windows Package Manager - WinGet", - "Az Predictor" - }; - } + // use a hashset when looking for module names, it should be quicker than a string comparison + s_knownModules = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "AADRM", + "activedirectory", + "adcsadministration", + "adcsdeployment", + "addsadministration", + "addsdeployment", + "adfs", + "adrms", + "adrmsadmin", + "agpm", + "AIShell", + "appbackgroundtask", + "applocker", + "appv", + "appvclient", + "appvsequencer", + "appvserver", + "appx", + "assignedaccess", + "Az", + "Az.Accounts", + "Az.Advisor", + "Az.Aks", + "Az.AlertsManagement", + "Az.AnalysisServices", + "Az.ApiManagement", + "Az.ApplicationInsights", + "Az.Attestation", + "Az.Automation", + "Az.Batch", + "Az.Billing", + "Az.Blueprint", + "Az.Cdn", + "Az.CognitiveServices", + "Az.Compute", + "Az.ContainerInstance", + "Az.ContainerRegistry", + "Az.DataBox", + "Az.DataFactory", + "Az.DataLakeAnalytics", + "Az.DataLakeStore", + "Az.DataMigration", + "Az.DataShare", + "Az.DeploymentManager", + "Az.DeviceProvisioningServices", + "Az.DevSpaces", + "Az.DevTestLabs", + "Az.Dns", + "Az.EventGrid", + "Az.EventHub", + "Az.FrontDoor", + "Az.GuestConfiguration", + "Az.HDInsight", + "Az.HealthcareApis", + "Az.IotCentral", + "Az.IotHub", + "Az.KeyVault", + "Az.Kusto", + "Az.LogicApp", + "Az.MachineLearning", + "Az.ManagedServiceIdentity", + "Az.ManagedServices", + "Az.ManagementPartner", + "Az.Maps", + "Az.MarketplaceOrdering", + "Az.Media", + "Az.MixedReality", + "Az.Monitor", + "Az.NetAppFiles", + "Az.Network", + "Az.NotificationHubs", + "Az.OperationalInsights", + "Az.Peering", + "Az.PolicyInsights", + "Az.PowerBIEmbedded", + "Az.PrivateDns", + "Az.RecoveryServices", + "Az.RedisCache", + "Az.Relay", + "Az.Reservations", + "Az.ResourceGraph", + "Az.Resources", + "Az.Search", + "Az.Security", + "Az.ServiceBus", + "Az.ServiceFabric", + "Az.SignalR", + "Az.Sql", + "Az.Storage", + "Az.StorageSync", + "Az.StorageTable", + "Az.StreamAnalytics", + "Az.Subscription", + "Az.Tools.Predictor", + "Az.TrafficManager", + "Az.Websites", + "Azs.Azurebridge.Admin", + "Azs.Backup.Admin", + "Azs.Commerce.Admin", + "Azs.Compute.Admin", + "Azs.Fabric.Admin", + "Azs.Gallery.Admin", + "Azs.Infrastructureinsights.Admin", + "Azs.Keyvault.Admin", + "Azs.Network.Admin", + "Azs.Storage.Admin", + "Azs.Subscriptions", + "Azs.Subscriptions.Admin", + "Azs.Update.Admin", + "AzStorageTable", + "Azure", + "Azure.AnalysisServices", + "Azure.Storage", + "AzureAD", + "AzureInformationProtection", + "AzureRM.Aks", + "AzureRM.AnalysisServices", + "AzureRM.ApiManagement", + "AzureRM.ApplicationInsights", + "AzureRM.Automation", + "AzureRM.Backup", + "AzureRM.Batch", + "AzureRM.Billing", + "AzureRM.Cdn", + "AzureRM.CognitiveServices", + "AzureRm.Compute", + "AzureRM.Compute.ManagedService", + "AzureRM.Consumption", + "AzureRM.ContainerInstance", + "AzureRM.ContainerRegistry", + "AzureRM.DataFactories", + "AzureRM.DataFactoryV2", + "AzureRM.DataLakeAnalytics", + "AzureRM.DataLakeStore", + "AzureRM.DataMigration", + "AzureRM.DeploymentManager", + "AzureRM.DeviceProvisioningServices", + "AzureRM.DevSpaces", + "AzureRM.DevTestLabs", + "AzureRm.Dns", + "AzureRM.EventGrid", + "AzureRM.EventHub", + "AzureRM.FrontDoor", + "AzureRM.HDInsight", + "AzureRm.Insights", + "AzureRM.IotCentral", + "AzureRM.IotHub", + "AzureRm.Keyvault", + "AzureRM.LocationBasedServices", + "AzureRM.LogicApp", + "AzureRM.MachineLearning", + "AzureRM.MachineLearningCompute", + "AzureRM.ManagedServiceIdentity", + "AzureRM.ManagementPartner", + "AzureRM.Maps", + "AzureRM.MarketplaceOrdering", + "AzureRM.Media", + "AzureRM.Network", + "AzureRM.NotificationHubs", + "AzureRM.OperationalInsights", + "AzureRM.PolicyInsights", + "AzureRM.PowerBIEmbedded", + "AzureRM.Profile", + "AzureRM.RecoveryServices", + "AzureRM.RecoveryServices.Backup", + "AzureRM.RecoveryServices.SiteRecovery", + "AzureRM.RedisCache", + "AzureRM.Relay", + "AzureRM.Reservations", + "AzureRM.ResourceGraph", + "AzureRM.Resources", + "AzureRM.Scheduler", + "AzureRM.Search", + "AzureRM.Security", + "AzureRM.ServerManagement", + "AzureRM.ServiceBus", + "AzureRM.ServiceFabric", + "AzureRM.SignalR", + "AzureRM.SiteRecovery", + "AzureRM.Sql", + "AzureRm.Storage", + "AzureRM.StorageSync", + "AzureRM.StreamAnalytics", + "AzureRM.Subscription", + "AzureRM.Subscription.Preview", + "AzureRM.Tags", + "AzureRM.TrafficManager", + "AzureRm.UsageAggregates", + "AzureRm.Websites", + "AzureRmStorageTable", + "bestpractices", + "bitlocker", + "bitstransfer", + "booteventcollector", + "branchcache", + "CimCmdlets", + "clusterawareupdating", + "CompatPowerShellGet", + "configci", + "ConfigurationManager", + "CompletionPredictor", + "DataProtectionManager", + "dcbqos", + "deduplication", + "defender", + "devicehealthattestation", + "dfsn", + "dfsr", + "dhcpserver", + "directaccessclient", + "directaccessclientcomponent", + "directaccessclientcomponents", + "dism", + "dnsclient", + "dnsserver", + "ElasticDatabaseJobs", + "EventTracingManagement", + "failoverclusters", + "fileserverresourcemanager", + "FIMAutomation", + "GPRegistryPolicy", + "grouppolicy", + "hardwarecertification", + "hcs", + "hgsattestation", + "hgsclient", + "hgsdiagnostics", + "hgskeyprotection", + "hgsserver", + "hnvdiagnostics", + "hostcomputeservice", + "hpc", + "HPC.ACM", + "HPC.ACM.API.PS", + "HPCPack2016", + "hyper-v", + "IISAdministration", + "international", + "ipamserver", + "iscsi", + "iscsitarget", + "ISE", + "kds", + "Microsoft.MBAM", + "Microsoft.MEDV", + "MgmtSvcAdmin", + "MgmtSvcConfig", + "MgmtSvcMySql", + "MgmtSvcSqlServer", + "Microsoft.AzureStack.ReadinessChecker", + "Microsoft.Crm.PowerShell", + "Microsoft.DiagnosticDataViewer", + "Microsoft.DirectoryServices.MetadirectoryServices.Config", + "Microsoft.Dynamics.Nav.Apps.Management", + "Microsoft.Dynamics.Nav.Apps.Tools", + "Microsoft.Dynamics.Nav.Ide", + "Microsoft.Dynamics.Nav.Management", + "Microsoft.Dynamics.Nav.Model.Tools", + "Microsoft.Dynamics.Nav.Model.Tools.Crm", + "Microsoft.EnterpriseManagement.Warehouse.Cmdlets", + "Microsoft.Medv.Administration.Commands.WorkspacePackager", + "Microsoft.PowerApps.Checker.PowerShell", + "Microsoft.PowerShell.Archive", + "Microsoft.PowerShell.ConsoleGuiTools", + "Microsoft.PowerShell.Core", + "Microsoft.PowerShell.Crescendo", + "Microsoft.PowerShell.Diagnostics", + "Microsoft.PowerShell.Host", + "Microsoft.PowerShell.LocalAccounts", + "Microsoft.PowerShell.Management", + "Microsoft.PowerShell.ODataUtils", + "Microsoft.PowerShell.Operation.Validation", + "Microsoft.PowerShell.PSAdapter", + "Microsoft.PowerShell.PSResourceGet", + "Microsoft.PowerShell.RemotingTools", + "Microsoft.PowerShell.SecretManagement", + "Microsoft.PowerShell.SecretStore", + "Microsoft.PowerShell.Security", + "Microsoft.PowerShell.TextUtility", + "Microsoft.PowerShell.Utility", + "Microsoft.SharePoint.Powershell", + "Microsoft.SystemCenter.ServiceManagementAutomation", + "Microsoft.Windows.ServerManager.Migration", + "Microsoft.WSMan.Management", + "Microsoft.Xrm.OnlineManagementAPI", + "Microsoft.Xrm.Tooling.CrmConnector.PowerShell", + "Microsoft.Xrm.Tooling.PackageDeployment", + "Microsoft.Xrm.Tooling.PackageDeployment.Powershell", + "Microsoft.Xrm.Tooling.Testing", + "MicrosoftPowerBIMgmt", + "MicrosoftPowerBIMgmt.Data", + "MicrosoftPowerBIMgmt.Profile", + "MicrosoftPowerBIMgmt.Reports", + "MicrosoftPowerBIMgmt.Workspaces", + "MicrosoftStaffHub", + "MicrosoftTeams", + "MIMPAM", + "mlSqlPs", + "MMAgent", + "MPIO", + "MsDtc", + "MSMQ", + "MSOnline", + "MSOnlineBackup", + "WmsCmdlets", + "WmsCmdlets3", + "NanoServerImageGenerator", + "NAVWebClientManagement", + "NetAdapter", + "NetConnection", + "NetEventPacketCapture", + "Netlbfo", + "Netldpagent", + "NetNat", + "Netqos", + "NetSecurity", + "NetSwitchtTeam", + "Nettcpip", + "Netwnv", + "NetworkConnectivity", + "NetworkConnectivityStatus", + "NetworkController", + "NetworkControllerDiagnostics", + "NetworkloadBalancingClusters", + "NetworkSwitchManager", + "NetworkTransition", + "NFS", + "NPS", + "OfficeWebapps", + "OperationsManager", + "PackageManagement", + "PartnerCenter", + "pcsvdevice", + "pef", + "Pester", + "pkiclient", + "platformidentifier", + "pnpdevice", + "PowerShellEditorServices", + "PowerShellGet", + "powershellwebaccess", + "printmanagement", + "ProcessMitigations", + "provisioning", + "PSDesiredStateConfiguration", + "PSDiagnostics", + "PSReadLine", + "PSScheduledJob", + "PSScriptAnalyzer", + "PSWorkflow", + "PSWorkflowUtility", + "RemoteAccess", + "RemoteDesktop", + "RemoteDesktopServices", + "ScheduledTasks", + "Secureboot", + "ServerCore", + "ServerManager", + "ServerManagerTasks", + "ServerMigrationcmdlets", + "ServiceFabric", + "Microsoft.Online.SharePoint.PowerShell", + "shieldedvmdatafile", + "shieldedvmprovisioning", + "shieldedvmtemplate", + "SkypeOnlineConnector", + "SkypeForBusinessHybridHealth", + "smbshare", + "smbwitness", + "smisconfig", + "softwareinventorylogging", + "SPFAdmin", + "Microsoft.SharePoint.MigrationTool.PowerShell", + "sqlps", + "SqlServer", + "StartLayout", + "StartScreen", + "Storage", + "StorageDsc", + "storageqos", + "Storagereplica", + "Storagespaces", + "Syncshare", + "System.Center.Service.Manager", + "TLS", + "TroubleshootingPack", + "TrustedPlatformModule", + "UEV", + "UpdateServices", + "UserAccessLogging", + "vamt", + "VirtualMachineManager", + "vpnclient", + "WasPSExt", + "WDAC", + "WDS", + "WebAdministration", + "WebAdministrationDsc", + "WebApplicationProxy", + "WebSites", + "Whea", + "WhiteboardAdmin", + "WindowsDefender", + "WindowsDefenderDsc", + "WindowsDeveloperLicense", + "WindowsDiagnosticData", + "WindowsErrorReporting", + "WindowServerRackup", + "WindowsSearch", + "WindowsServerBackup", + "WindowsUpdate", + "WinGetCommandNotFound", + "wsscmdlets", + "wsssetup", + "wsus", + "xActiveDirectory", + "xBitLocker", + "xDefender", + "xDhcpServer", + "xDismFeature", + "xDnsServer", + "xHyper-V", + "xHyper-VBackup", + "xPSDesiredStateConfiguration", + "xSmbShare", + "xSqlPs", + "xStorage", + "xWebAdministration", + "xWindowsUpdate", + }; + + // use a hashset when looking for module names, it should be quicker than a string comparison + s_knownModuleTags = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "CrescendoBuilt", + }; + + s_uniqueUserIdentifier = GetUniqueIdentifier().ToString(); + s_knownSubsystemNames = new HashSet(StringComparer.OrdinalIgnoreCase) + { + "Completion", + "General Feedback", + "Windows Package Manager - WinGet", + "Az Predictor" + }; } /// @@ -984,8 +990,7 @@ private static Guid GetUniqueIdentifier() // Try to get the unique id. If this returns false, we'll // create/recreate the telemetry.uuid file to persist for next startup. Guid id = Guid.Empty; - string uuidPath = Path.Join(Platform.CacheDirectory, "telemetry.uuid"); - if (TryGetIdentifier(uuidPath, out id)) + if (TryGetIdentifier(s_uuidPath, out id)) { return id; } @@ -1000,7 +1005,7 @@ private static Guid GetUniqueIdentifier() m.WaitOne(); try { - return CreateUniqueIdentifierAndFile(uuidPath); + return CreateUniqueIdentifierAndFile(s_uuidPath); } finally { From e3133e0a09718604eaffdad023bbd011dd5d8d51 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 14:59:50 -0700 Subject: [PATCH 09/18] [release/v7.6.2] Update `Microsoft.PowerShell.Native` to the latest GA version (#27436) --- .../System.Management.Automation.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index 63dc69e5119..09e80684d43 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -43,7 +43,7 @@ - + From 32c5b651e19edfcaff99a450e61d692969c9f423 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 15:01:57 -0700 Subject: [PATCH 10/18] [release/v7.6.2] Update the MSIXBundle-VPack pipeline to create VPack for both LTS and Stable channel packages (#27435) --- .pipelines/MSIXBundle-vPack-Official.yml | 199 +++++------------- .../templates/create-msixbundle-vpack.yml | 178 ++++++++++++++++ 2 files changed, 230 insertions(+), 147 deletions(-) create mode 100644 .pipelines/templates/create-msixbundle-vpack.yml diff --git a/.pipelines/MSIXBundle-vPack-Official.yml b/.pipelines/MSIXBundle-vPack-Official.yml index 08edd0367bd..2461d3cd310 100644 --- a/.pipelines/MSIXBundle-vPack-Official.yml +++ b/.pipelines/MSIXBundle-vPack-Official.yml @@ -138,12 +138,6 @@ extends: if (-not $matched) { throw "Release tag must be in the format v#.#.#, such as 'v7.4.3'. Current version: $releaseTag" } - - # Extract minor version and verify it's even (LTS versions only) - $minorVersion = [int]$Matches[1] - if($minorVersion % 2 -ne 0) { - throw "Only release msixbundle vpack for LTS releases. Current version: $releaseTag" - } displayName: Stop any preview release env: ob_restore_phase: true @@ -270,7 +264,7 @@ extends: Write-Verbose -Message "checking pwsh exists in $signedFilesPath" -Verbose if (-not (Test-Path $signedFilesPath\pwsh.exe)) { - throw "pwsh.exe not found in $signedFilesPath" + throw "pwsh.exe not found in $signedFilesPath" } Write-Verbose -Message "Restoring PSOptions from $psoptionsFilePath" -Verbose @@ -278,166 +272,77 @@ extends: Restore-PSOptions -PSOptionsPath "$psoptionsFilePath" Get-PSOptions | Write-Verbose -Verbose - ## Generated packages are placed in the current directory by default. - Set-Location $repoRoot - Start-PSPackage -Type msix -SkipReleaseChecks -WindowsRuntime $runtime -ReleaseTag $(ReleaseTagVar) -PackageBinPath $signedFilesPath -LTS - - $msixPkgNameFilter = "PowerShell*.msix" - $msixPkgFile = Get-ChildItem -Path $repoRoot -Filter $msixPkgNameFilter -File - $msixPkgPath = $msixPkgFile.FullName - Write-Verbose -Verbose "Unsigned msix package: $msixPkgPath" - - $pkgDir = '$(ob_outputDirectory)\pkgs' - $null = New-Item -ItemType Directory -Path $pkgDir -Force - Copy-Item -Path $msixPkgPath -Destination $pkgDir -Force -Verbose - displayName: 'Build MSIX Package (Unsigned)' - - ### END OF Packaging ### - - - pwsh: | - Get-ChildItem -Path '$(ob_outputDirectory)\pkgs' -Recurse - displayName: 'List Unsigned Package' - - - stage: Pack_MSIXBundle_And_Sign - displayName: 'Pack and sign MSIXBundle' - dependsOn: [Build_MSIX_Package] - jobs: - - job: Bundle - pool: - type: windows - variables: - ArtifactPlatform: 'windows' - ob_outputDirectory: '$(BUILD.SOURCESDIRECTORY)\out' - ob_artifactBaseName: drop_pack_msixbundle - ob_createvpack_enabled: ${{ parameters.createVPack }} - ob_createvpack_packagename: 'PowerShell7.Store.app' - ob_createvpack_owneralias: 'dongbow' - ob_createvpack_description: 'VPack for the PowerShell 7 Store Application' - ob_createvpack_targetDestinationDirectory: '$(Destination)' ## The value is from the 'CreateVpack' task, used when pulling the generated VPack. - ob_createvpack_propsFile: false - ob_createvpack_provData: true - ob_createvpack_metadata: '$(Build.SourceVersion)' - ob_createvpack_versionAs: string - ob_createvpack_version: '$(Version)' - ob_createvpack_verbose: true - - steps: - - checkout: self - displayName: Checkout source code - during restore - clean: true - path: s ## $(Build.SourcesDirectory) is at '$(Pipeline.Workspace)\s', so we need to check out repo to the 's' folder. - env: - ob_restore_phase: true + $metadata = Get-Content "$repoRoot\tools\metadata.json" -Raw | ConvertFrom-Json + Write-Verbose -Verbose "metadata:" + $metadata | Out-String | Write-Verbose -Verbose - - template: /.pipelines/templates/SetVersionVariables.yml@self - parameters: - ReleaseTagVar: $(ReleaseTagVar) - CreateJson: no + $publishLTS = $metadata.LTSRelease.PublishToChannels + $publishStable = $metadata.StableRelease.PublishToChannels - - template: /.pipelines/templates/shouldSign.yml@self + Write-Verbose -Verbose "Publish LTS: $publishLTS" + Write-Verbose -Verbose "Publish Stable: $publishStable" - - task: DownloadPipelineArtifact@2 - inputs: - artifactName: drop_build_x64 - itemPattern: | - **/*.msix - targetPath: '$(Build.ArtifactStagingDirectory)\downloads' - displayName: Download msix for x64 + if (-not $publishLTS -and -not $publishStable) { + throw "metadata.json indicates no channels to publish to." + } - - task: DownloadPipelineArtifact@2 - inputs: - artifactName: drop_build_arm64 - itemPattern: | - **/*.msix - targetPath: '$(Build.ArtifactStagingDirectory)\downloads' - displayName: Download msix for arm64 + ## Generated packages are placed in the current directory by default. + Set-Location $repoRoot + Start-PSPackage -Type msix -SkipReleaseChecks -WindowsRuntime $runtime -ReleaseTag $(ReleaseTagVar) -PackageBinPath $signedFilesPath -LTS:$publishLTS - # Finds the makeappx tool on the machine. - - pwsh: | - Write-Verbose -Verbose 'PowerShell Version: $(Version)' - $cmd = Get-Command makeappx.exe -ErrorAction Ignore - if ($cmd) { - Write-Verbose -Verbose 'makeappx available in PATH' - $exePath = $cmd.Source - } else { - $makeappx = Get-ChildItem -Recurse 'C:\Program Files (x86)\Windows Kits\10\makeappx.exe' | - Where-Object { $_.DirectoryName -match 'x64' } | - Select-Object -Last 1 - $exePath = $makeappx.FullName - Write-Verbose -Verbose "makeappx was found: $exePath" + if ($publishLTS -and $publishStable) { + $enabledChannels = "LTS,Stable" + Write-Verbose -Verbose "Publish to both LTS and Stable channels. Building additional Stable MSIX." + Start-PSPackage -Type msix -SkipReleaseChecks -WindowsRuntime $runtime -ReleaseTag $(ReleaseTagVar) -PackageBinPath $signedFilesPath } - $vstsCommandString = "vso[task.setvariable variable=MakeAppxPath]$exePath" - Write-Host ("sending " + $vstsCommandString) - Write-Host "##$vstsCommandString" - displayName: Find makeappx tool - retryCountOnTaskFailure: 1 - - pwsh: | - $sourceDir = '$(Pipeline.Workspace)\releasePipeline\msix' - $null = New-Item -Path $sourceDir -ItemType Directory -Force + $msixPkgNameFilter = "PowerShell*.msix" + $msixPkgFile = Get-ChildItem -Path $repoRoot -Filter $msixPkgNameFilter -Recurse -File | ForEach-Object FullName + Write-Verbose -Verbose "Unsigned msix package(s): $msixPkgFile" - $msixFiles = Get-ChildItem -Path "$(Build.ArtifactStagingDirectory)\downloads\*.msix" -Recurse - foreach ($msixFile in $msixFiles) { - $null = Copy-Item -Path $msixFile.FullName -Destination $sourceDir -Force -Verbose - } + $pkgDir = '$(ob_outputDirectory)\pkgs' + $null = New-Item -ItemType Directory -Path $pkgDir -Force + Copy-Item -Path $msixPkgFile -Destination $pkgDir -Force -Verbose - $file = Get-ChildItem $sourceDir | Select-Object -First 1 - $prefix = ($file.BaseName -split "-win")[0] - $pkgName = "$prefix.msixbundle" - Write-Verbose -Verbose "Creating $pkgName" - - $makeappx = '$(MakeAppxPath)' - $outputDir = "$sourceDir\output" - New-Item $outputDir -Type Directory -Force > $null - & $makeappx bundle /d $sourceDir /p "$outputDir\$pkgName" - if ($LASTEXITCODE -ne 0) { - throw "makeappx bundle failed with exit code $LASTEXITCODE" + if (-not $enabledChannels) { + $enabledChannels = $publishLTS ? 'LTS' : ($publishStable ? 'Stable' : 'None') } - Get-ChildItem -Path $sourceDir -Recurse | Out-String -Width 200 - $vstsCommandString = "vso[task.setvariable variable=BundleDir]$outputDir" + ## Create an output variable for the enabled channels so that downstream stages can use it. + $vstsCommandString = "vso[task.setvariable variable=EnabledChannels;isOutput=true]$enabledChannels" Write-Host ("sending " + $vstsCommandString) Write-Host "##$vstsCommandString" - displayName: Create MsixBundle - retryCountOnTaskFailure: 1 + name: BuildMSIXPackage + displayName: 'Build MSIX Package (Unsigned)' - - task: onebranch.pipeline.signing@1 - displayName: Sign MsixBundle - inputs: - command: 'sign' - signing_profile: $(MSIXProfile) - files_to_sign: '**/*.msixbundle' - search_root: '$(BundleDir)' + ### END OF Packaging ### - pwsh: | - $signedBundle = Get-ChildItem -Path $(BundleDir) -Filter "*.msixbundle" -File - Write-Verbose -Verbose "Signed bundle: $signedBundle" - - $signature = Get-AuthenticodeSignature -FilePath $signedBundle.FullName - if ($signature.Status -ne 'Valid') { - throw "The bundle file doesn't have a valid signature. Signature status: $($signature.Status)" - } + Get-ChildItem -Path '$(ob_outputDirectory)\pkgs' -Recurse + displayName: 'List Unsigned Package' - if (-not (Test-Path '$(ob_outputDirectory)' -PathType Container)) { - $null = New-Item '$(ob_outputDirectory)' -ItemType Directory -ErrorAction Stop - } + - pwsh: | + $signedFilesPath = '$(ob_outputDirectory)\Signed-$(Runtime)' + Remove-Item -Path $signedFilesPath -Recurse -Force -Verbose + displayName: 'Remove Signed-$(Runtime) folder' - $targetPath = Join-Path '$(ob_outputDirectory)' 'Microsoft.PowerShell-LTS_8wekyb3d8bbwe.msixbundle' - Copy-Item -Verbose -Path $signedBundle.FullName -Destination $targetPath + - stage: Pack_MSIXBundle_And_Sign + displayName: 'Pack and sign MSIXBundle' + dependsOn: [Build_MSIX_Package] - Write-Verbose -Verbose "Uploaded Bundle:" - Get-ChildItem -Path $(ob_outputDirectory) | Out-String -Width 200 -Stream | Write-Verbose -Verbose - displayName: 'Stage msixbundle for VPack' + variables: + EnabledChannels: $[ stageDependencies.Build_MSIX_Package.Build.outputs['x64.BuildMSIXPackage.EnabledChannels'] ] - - pwsh: | - Write-Verbose "VPack Version: $(ob_createvpack_version)" -Verbose - $vpackFiles = Get-ChildItem -Path '$(ob_outputDirectory)\*' -Recurse - if($vpackFiles.Count -eq 0) { - throw "No files found in $(ob_outputDirectory)" - } - $vpackFiles | Out-String -Width 200 - displayName: Debug Output Directory and Version - condition: succeededOrFailed() + jobs: + - template: /.pipelines/templates/create-msixbundle-vpack.yml@self + parameters: + Channel: 'LTS' + createVPack: ${{ parameters.createVPack }} + + - template: /.pipelines/templates/create-msixbundle-vpack.yml@self + parameters: + Channel: 'Stable' + createVPack: ${{ parameters.createVPack }} - stage: Publish_Symbols displayName: 'Publish Symbols' diff --git a/.pipelines/templates/create-msixbundle-vpack.yml b/.pipelines/templates/create-msixbundle-vpack.yml new file mode 100644 index 00000000000..df46523675f --- /dev/null +++ b/.pipelines/templates/create-msixbundle-vpack.yml @@ -0,0 +1,178 @@ +parameters: + - name: Channel + type: string + - name: createVPack + type: boolean + +jobs: +- job: Bundle_${{ parameters.Channel }} + condition: contains(variables['EnabledChannels'], '${{ parameters.Channel }}') + pool: + type: windows + + variables: + ArtifactPlatform: 'windows' + Channel: ${{ parameters.Channel }} + ob_outputDirectory: '$(BUILD.SOURCESDIRECTORY)\out' + ob_artifactBaseName: 'drop_pack_$(Channel)' + ob_createvpack_enabled: ${{ parameters.createVPack }} + ob_createvpack_packagename: 'PowerShell7-$(Channel).Store.app' + ob_createvpack_owneralias: 'dongbow' + ob_createvpack_description: 'VPack for the PowerShell 7 Store Application ($(Channel))' + ob_createvpack_targetDestinationDirectory: '$(Destination)' ## The value is from the 'CreateVpack' task, used when pulling the generated VPack. + ob_createvpack_propsFile: false + ob_createvpack_provData: true + ob_createvpack_metadata: '$(Build.SourceVersion)' + ob_createvpack_versionAs: string + ob_createvpack_version: '$(Version)' + ob_createvpack_verbose: true + + steps: + - checkout: self + displayName: Checkout source code - during restore + clean: true + path: s ## $(Build.SourcesDirectory) is at '$(Pipeline.Workspace)\s', so we need to check out repo to the 's' folder. + env: + ob_restore_phase: true + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + + - template: /.pipelines/templates/shouldSign.yml@self + + - task: DownloadPipelineArtifact@2 + inputs: + artifactName: drop_build_x64 + itemPattern: | + **/*.msix + targetPath: '$(Build.ArtifactStagingDirectory)\downloads' + displayName: Download msix for x64 + + - task: DownloadPipelineArtifact@2 + inputs: + artifactName: drop_build_arm64 + itemPattern: | + **/*.msix + targetPath: '$(Build.ArtifactStagingDirectory)\downloads' + displayName: Download msix for arm64 + + # Finds the makeappx tool on the machine. + - pwsh: | + Write-Verbose -Verbose 'PowerShell Version: $(Version)' + $cmd = Get-Command makeappx.exe -ErrorAction Ignore + if ($cmd) { + Write-Verbose -Verbose 'makeappx available in PATH' + $exePath = $cmd.Source + } else { + $makeappx = Get-ChildItem -Recurse 'C:\Program Files (x86)\Windows Kits\10\makeappx.exe' | + Where-Object { $_.DirectoryName -match 'x64' } | + Select-Object -Last 1 + $exePath = $makeappx.FullName + Write-Verbose -Verbose "makeappx was found: $exePath" + } + $vstsCommandString = "vso[task.setvariable variable=MakeAppxPath]$exePath" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + displayName: Find makeappx tool + retryCountOnTaskFailure: 1 + + - pwsh: | + $sourceDir = '$(Pipeline.Workspace)\releasePipeline\msix' + $null = New-Item -Path $sourceDir -ItemType Directory -Force + + $channel = '$(Channel)' + if ($channel -eq 'LTS') { + Write-Verbose -Verbose "LTS channel. Remove Stable MSIX packages" + $stablePkgs = Get-ChildItem -Path "$(Build.ArtifactStagingDirectory)\downloads\*.msix" -Recurse | + Where-Object { $_.FullName -notlike '*-LTS-*.msix' } | ForEach-Object FullName + + if ($stablePkgs) { + Remove-Item -Path $stablePkgs -Force -Verbose -ErrorAction Stop + } else { + Write-Verbose -Verbose "No Stable MSIX package was found." + } + } + else { + Write-Verbose -Verbose "Stable channel. Remove LTS MSIX packages" + $ltsPkgs = Get-ChildItem -Path "$(Build.ArtifactStagingDirectory)\downloads\*.msix" -Recurse | + Where-Object { $_.FullName -like '*-LTS-*.msix' } | ForEach-Object FullName + + if ($ltsPkgs) { + Remove-Item -Path $ltsPkgs -Force -Verbose -ErrorAction Stop + } else { + Write-Verbose -Verbose "No LTS MSIX package was found." + } + } + + $msixFiles = Get-ChildItem -Path "$(Build.ArtifactStagingDirectory)\downloads\*.msix" -Recurse + foreach ($msixFile in $msixFiles) { + $null = Copy-Item -Path $msixFile.FullName -Destination $sourceDir -Force -Verbose + } + + $file = Get-ChildItem $sourceDir | Select-Object -First 1 + $prefix = ($file.BaseName -split "-win")[0] + $pkgName = "$prefix.msixbundle" + Write-Verbose -Verbose "Creating $pkgName" + + $makeappx = '$(MakeAppxPath)' + $outputDir = "$sourceDir\output" + New-Item $outputDir -Type Directory -Force > $null + & $makeappx bundle /d $sourceDir /p "$outputDir\$pkgName" + if ($LASTEXITCODE -ne 0) { + throw "makeappx bundle failed with exit code $LASTEXITCODE" + } + + Get-ChildItem -Path $sourceDir -Recurse | Out-String -Width 200 + $vstsCommandString = "vso[task.setvariable variable=BundleDir]$outputDir" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + displayName: Create MsixBundle + retryCountOnTaskFailure: 1 + + - task: onebranch.pipeline.signing@1 + displayName: Sign MsixBundle + inputs: + command: 'sign' + signing_profile: $(MSIXProfile) + files_to_sign: '**/*.msixbundle' + search_root: '$(BundleDir)' + + - pwsh: | + $signedBundle = Get-ChildItem -Path $(BundleDir) -Filter "*.msixbundle" -File + Write-Verbose -Verbose "Signed bundle: $signedBundle" + + $signature = Get-AuthenticodeSignature -FilePath $signedBundle.FullName + if ($signature.Status -ne 'Valid') { + throw "The bundle file doesn't have a valid signature. Signature status: $($signature.Status)" + } + + if (-not (Test-Path '$(ob_outputDirectory)' -PathType Container)) { + $null = New-Item '$(ob_outputDirectory)' -ItemType Directory -ErrorAction Stop + } + + $channel = '$(Channel)' + $targetFileName = if ($channel -eq 'LTS') { + 'Microsoft.PowerShell-LTS_8wekyb3d8bbwe.msixbundle' + } else { + 'Microsoft.PowerShell_8wekyb3d8bbwe.msixbundle' + } + $targetPath = Join-Path '$(ob_outputDirectory)' $targetFileName + Copy-Item -Verbose -Path $signedBundle.FullName -Destination $targetPath + + Write-Verbose -Verbose "Uploaded Bundle:" + Get-ChildItem -Path $(ob_outputDirectory) | Out-String -Width 200 -Stream | Write-Verbose -Verbose + displayName: 'Stage msixbundle for VPack' + + - pwsh: | + Write-Verbose "VPack enabled: $(ob_createvpack_enabled)" -Verbose + Write-Verbose "VPack Name: $(ob_createvpack_packagename)" -Verbose + Write-Verbose "VPack Version: $(ob_createvpack_version)" -Verbose + + $vpackFiles = Get-ChildItem -Path '$(ob_outputDirectory)\*' -Recurse + if($vpackFiles.Count -eq 0) { + throw "No files found in $(ob_outputDirectory)" + } + $vpackFiles | Out-String -Width 200 + displayName: Debug Output Directory and Version From 712231cad42e12f1642ec9cfbb9fce9da9914456 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 15:02:40 -0700 Subject: [PATCH 11/18] [release/v7.6.2] Remove package verification from the notice pipeline (#27425) --- .pipelines/apiscan-gen-notice.yml | 4 ---- .pipelines/templates/compliance/generateNotice.yml | 7 ------- 2 files changed, 11 deletions(-) diff --git a/.pipelines/apiscan-gen-notice.yml b/.pipelines/apiscan-gen-notice.yml index 9cc83e7136a..df5ebaac091 100644 --- a/.pipelines/apiscan-gen-notice.yml +++ b/.pipelines/apiscan-gen-notice.yml @@ -8,9 +8,6 @@ parameters: displayName: Debugging - Enable CodeQL and set cadence to 1 hour type: boolean default: false - - name: SkipVerifyPackages - type: boolean - default: false variables: # PAT permissions NOTE: Declare a SymbolServerPAT variable in this group with a 'microsoft' organizanization scoped PAT with 'Symbols' Read permission. @@ -112,4 +109,3 @@ extends: - template: /.pipelines/templates/compliance/generateNotice.yml@self parameters: parentJobs: [] - SkipVerifyPackages: ${{ parameters.SkipVerifyPackages }} diff --git a/.pipelines/templates/compliance/generateNotice.yml b/.pipelines/templates/compliance/generateNotice.yml index 90fd08dd8d9..aec44b9b8f6 100644 --- a/.pipelines/templates/compliance/generateNotice.yml +++ b/.pipelines/templates/compliance/generateNotice.yml @@ -4,8 +4,6 @@ parameters: - name: parentJobs type: jobList - - name: SkipVerifyPackages - type: boolean jobs: - job: generateNotice @@ -57,11 +55,6 @@ jobs: inputs: sourceScanPath: '$(repoRoot)\tools\cgmanifest\tpn' - - pwsh: | - $(repoRoot)/tools/clearlyDefined/ClearlyDefined.ps1 -TestAndHarvest - displayName: Verify that packages have license data - condition: eq(${{ parameters.SkipVerifyPackages }}, false) - - task: msospo.ospo-extension.8d7f9abb-6896-461d-9e25-4f74ed65ddb2.notice@0 displayName: 'NOTICE File Generator' inputs: From 8afbd5fc9b59ae566c71e8fc2066228979e6ed5c Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 15:03:23 -0700 Subject: [PATCH 12/18] [release/v7.6.2] Enable usage in AppContainers (#27423) --- .../engine/Modules/ModuleIntrinsics.cs | 4 ++- .../namespaces/FileSystemProvider.cs | 27 +++++++++++++++++-- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs index b687e502763..6d37de883d1 100644 --- a/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs +++ b/src/System.Management.Automation/engine/Modules/ModuleIntrinsics.cs @@ -967,7 +967,9 @@ internal static string GetPersonalModulePath() #if UNIX return Platform.SelectProductNameForDirectory(Platform.XDG_Type.USER_MODULES); #else - string myDocumentsPath = InternalTestHooks.SetMyDocumentsSpecialFolderToBlank ? string.Empty : Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + string myDocumentsPath = InternalTestHooks.SetMyDocumentsSpecialFolderToBlank + ? string.Empty + : Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments, Environment.SpecialFolderOption.DoNotVerify); return string.IsNullOrEmpty(myDocumentsPath) ? null : Path.Combine(myDocumentsPath, Utils.ModuleDirectory); #endif } diff --git a/src/System.Management.Automation/namespaces/FileSystemProvider.cs b/src/System.Management.Automation/namespaces/FileSystemProvider.cs index c5df7a9339a..bb66c0697e9 100644 --- a/src/System.Management.Automation/namespaces/FileSystemProvider.cs +++ b/src/System.Management.Automation/namespaces/FileSystemProvider.cs @@ -569,7 +569,7 @@ protected override PSDriveInfo NewDrive(PSDriveInfo drive) if (driveIsFixed) { // Since the drive is fixed, ensure the root is valid. - validDrive = Directory.Exists(drive.Root); + validDrive = SafeDoesPathExist(drive.Root); } if (validDrive) @@ -908,7 +908,7 @@ protected override Collection InitializeDefaultDrives() if (newDrive.DriveType == DriveType.Fixed) { - if (!newDrive.RootDirectory.Exists) + if (!SafeDoesPathExist(newDrive.RootDirectory.FullName)) { continue; } @@ -1227,6 +1227,29 @@ protected override void GetItem(string path) } } + private static bool SafeDoesPathExist(string rootDirectory) + { + if (Directory.Exists(rootDirectory)) + { + return true; + } + + try + { + return (File.GetAttributes(rootDirectory) & FileAttributes.Directory) is not 0; + } + // In some scenarios (like AppContainers) direct access to the root directory may + // be prevented, but more specific paths may be accessible. + catch (UnauthorizedAccessException) + { + return true; + } + catch + { + return false; + } + } + private FileSystemInfo GetFileSystemItem(string path, ref bool isContainer, bool showHidden) { path = NormalizePath(path); From c031a106f9b1e45dd45f8cda6cf454fdfec9aab7 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 15:04:20 -0700 Subject: [PATCH 13/18] [release/v7.6.2] Remove mariner2.0 from PMC mapping (#27422) --- tools/packages.microsoft.com/mapping.json | 32 ----------------------- 1 file changed, 32 deletions(-) diff --git a/tools/packages.microsoft.com/mapping.json b/tools/packages.microsoft.com/mapping.json index 154a9582872..7cb212ffee3 100644 --- a/tools/packages.microsoft.com/mapping.json +++ b/tools/packages.microsoft.com/mapping.json @@ -28,38 +28,6 @@ ], "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.rh.x86_64.rpm" }, - { - "url": "cbl-mariner-2.0-prod-Microsoft-aarch64", - "distribution": [ - "bionic" - ], - "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.cm.aarch64.rpm", - "channel": "stable" - }, - { - "url": "cbl-mariner-2.0-prod-Microsoft-x86_64", - "distribution": [ - "bionic" - ], - "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.cm.x86_64.rpm", - "channel": "stable" - }, - { - "url": "cbl-mariner-2.0-preview-Microsoft-aarch64", - "distribution": [ - "bionic" - ], - "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.cm.aarch64.rpm", - "channel": "preview" - }, - { - "url": "cbl-mariner-2.0-preview-Microsoft-x86_64", - "distribution": [ - "bionic" - ], - "PackageFormat": "PACKAGE_NAME-POWERSHELL_RELEASE-1.cm.x86_64.rpm", - "channel": "preview" - }, { "url": "azurelinux-3.0-prod-ms-oss-aarch64", "distribution": [ From 4c698487466a5b7f45d6005062f4f23de41a73be Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 16:11:54 -0700 Subject: [PATCH 14/18] [release/v7.6.2] Specify linux-arm64 runtime if package type is deb-arm64 in packaging.psm1 (#27440) --- tools/packaging/packaging.psm1 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index 641299df382..b5f88e8ba6c 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -121,6 +121,9 @@ function Start-PSPackage { elseif ($Type.Count -eq 1 -and $Type[0] -eq "tar-alpine-fxdependent") { New-PSOptions -Configuration "Release" -Runtime 'fxdependent-noopt-linux-musl-x64' -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } } + elseif ($Type.Count -eq 1 -and $Type[0] -eq "deb-arm64") { + New-PSOptions -Configuration "Release" -Runtime 'linux-arm64' -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } + } else { New-PSOptions -Configuration "Release" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } } From b1fbe52e37f942cd31d94411ffcbba8d80bd4169 Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 16:12:43 -0700 Subject: [PATCH 15/18] [release/v7.6.2] Fix *nix permissions and use `certificate_logical_to_actual` (#27439) --- .pipelines/templates/linux-package-build.yml | 23 ++++-- .pipelines/templates/mac-package-build.yml | 71 +++++++++++++++---- .pipelines/templates/mac.yml | 32 +++++++++ .pipelines/templates/nupkg.yml | 5 +- .pipelines/templates/shouldSign.yml | 6 +- .../stages/PowerShell-Packages-Stages.yml | 2 - .pipelines/templates/windows-hosted-build.yml | 2 +- tools/packaging/packaging.psd1 | 1 + tools/packaging/packaging.psm1 | 24 ++++++- 9 files changed, 141 insertions(+), 25 deletions(-) diff --git a/.pipelines/templates/linux-package-build.yml b/.pipelines/templates/linux-package-build.yml index 1487455107e..e37d8555533 100644 --- a/.pipelines/templates/linux-package-build.yml +++ b/.pipelines/templates/linux-package-build.yml @@ -1,9 +1,8 @@ parameters: unsignedDrop: 'drop_linux_build_linux_x64' - signedeDrop: 'drop_linux_sign_linux_x64' + signedDrop: 'drop_linux_sign_linux_x64' packageType: deb jobName: 'deb' - signingProfile: 'CP-450779-pgpdetached' jobs: - job: ${{ parameters.jobName }} @@ -20,6 +19,7 @@ jobs: - name: skipNugetSecurityAnalysis value: true - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual - name: ob_outputDirectory value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' - name: ob_sdl_binskim_enabled @@ -34,8 +34,16 @@ jobs: value: $(Build.SourcesDirectory)/PowerShell/.config/tsaoptions.json - name: ob_sdl_credscan_suppressionsFile value: $(Build.SourcesDirectory)/PowerShell/.config/suppress.json - - name: SigningProfile - value: ${{ parameters.signingProfile }} + # PGP signing profile selection: Mariner (Azure Linux) packages ship through + # a different distribution channel and must be signed with the Mariner release + # key; all other Linux packages use the standard PowerShell Linux key. Both + # key codes come from the `certificate_logical_to_actual` variable group. + - ${{ if startsWith(parameters.jobName, 'mariner') }}: + - name: SigningProfile + value: $(pgp_release_cert_id) + - ${{ else }}: + - name: SigningProfile + value: $(pgp_linux_cert_id) steps: - checkout: self @@ -193,6 +201,13 @@ jobs: $pkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $pkgFilter -Recurse -File | Select-Object -ExpandProperty FullName Write-Verbose -Verbose "pkgPath: $pkgPath" Copy-Item -Path $pkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose + + if ($pkgPath -like '*.tar.gz') { + $entry = & tar -tzvf $pkgPath | Where-Object { $_ -match '\spwsh$' } | Select-Object -First 1 + if ($entry -notmatch '^-..x') { + throw "pwsh is not executable in $pkgPath : $entry" + } + } displayName: 'Copy artifacts to output directory' env: __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) diff --git a/.pipelines/templates/mac-package-build.yml b/.pipelines/templates/mac-package-build.yml index 6585773c743..89dab90aa04 100644 --- a/.pipelines/templates/mac-package-build.yml +++ b/.pipelines/templates/mac-package-build.yml @@ -22,6 +22,7 @@ jobs: - name: skipNugetSecurityAnalysis value: true - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual - name: ob_outputDirectory value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' - name: ob_sdl_binskim_enabled @@ -76,6 +77,14 @@ jobs: # Diagnostics is not critical it passes every time it runs continueOnError: true + - pwsh: | + $signedDir = "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/drop_macos_sign_${{ parameters.buildArchitecture }}/Signed-${{ parameters.buildArchitecture }}" + Get-ChildItem $signedDir -Recurse -Include 'pwsh', '*.dylib' | ForEach-Object { + codesign --verify --deep --strict --verbose=4 $_.FullName + if ($LASTEXITCODE -ne 0) { throw "codesign verification failed for $($_.FullName)" } + } + displayName: 'Verify Apple codesign on signed binaries' + - pwsh: | # Add -SkipReleaseChecks as a mitigation to unblock release. # macos-10.15 does not allow creating a folder under root. Hence, moving the folder. @@ -154,11 +163,20 @@ jobs: foreach($t in $tarPkgPath) { $file = $t.FullName + $entry = & tar -tzvf $file | Where-Object { $_ -match '\spwsh$' } | Select-Object -First 1 + if ($entry -notmatch '^-..x') { + throw "pwsh is not executable in $file : $entry" + } Write-Verbose -verbose "Uploading $file to macos-pkgs" Write-Host "##vso[artifact.upload containerfolder=macos-pkgs;artifactname=macos-pkgs]$file" } + $packageInfo = Get-MacOSPackageIdentifierInfo -Version '$(Version)' -LTS:$LTS + Write-Verbose -Verbose "BundleId: $($packageInfo.PackageIdentifier)" + Write-Host "##vso[task.setvariable variable=BundleId;isOutput=true]$($packageInfo.PackageIdentifier)" + displayName: 'Package ${{ parameters.buildArchitecture}}' + name: packageStep env: __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) @@ -170,6 +188,7 @@ jobs: type: windows variables: + - group: certificate_logical_to_actual - name: ob_outputDirectory value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' - name: ob_sdl_binskim_enabled @@ -178,7 +197,8 @@ jobs: value: $(Build.SourcesDirectory)/PowerShell/.config/suppress.json - name: BuildArch value: ${{ parameters.buildArchitecture }} - - group: mscodehub-macos-package-signing + - name: BundleId + value: $[ dependencies.package_macOS_${{ parameters.buildArchitecture }}.outputs['packageStep.BundleId'] ] steps: - download: current @@ -216,32 +236,59 @@ jobs: inline_operation: | [ { - "KeyCode": "$(KeyCode)", + "KeyCode": "$(apple_cert_id)", "OperationCode": "MacAppDeveloperSign", "ToolName": "sign", "ToolVersion": "1.0", "Parameters": { - "Hardening": "Enable", - "OpusInfo": "http://microsoft.com" + "Hardening": "--options=runtime" + } + } + ] + + - task: onebranch.pipeline.signing@1 + displayName: 'OneBranch Notarize Package' + inputs: + command: 'sign' + files_to_sign: '**/*-osx-*.zip' + search_root: '$(Pipeline.Workspace)' + inline_operation: | + [ + { + "KeyCode": "$(apple_cert_id)", + "OperationCode": "MacAppNotarize", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": { + "BundleId": "$(BundleId)" } } ] + timeoutInMinutes: 120 - pwsh: | $signedPkg = Get-ChildItem -Path $(Pipeline.Workspace) -Filter "*osx*.zip" -File + if (-not (Test-Path $(ob_outputDirectory))) { + $null = New-Item -Path $(ob_outputDirectory) -ItemType Directory + } + + $expandDir = "$(Pipeline.Workspace)/pkgExpand" + $null = New-Item -Path $expandDir -ItemType Directory -Force + $signedPkg | ForEach-Object { Write-Verbose -Verbose "Signed package zip: $_" + Expand-Archive -Path $_ -DestinationPath $expandDir -Verbose + } - if (-not (Test-Path $_)) { - throw "Package not found: $_" - } - - if (-not (Test-Path $(ob_outputDirectory))) { - $null = New-Item -Path $(ob_outputDirectory) -ItemType Directory - } + # ESRP's signing pipeline nests the PKG inside a '.zip.unzipped' subfolder + $pkgFile = Get-ChildItem -Path $expandDir -Filter '*.pkg' -Recurse -File + if (-not $pkgFile) { + throw "Package not found in: $signedPkg" + } - Expand-Archive -Path $_ -DestinationPath $(ob_outputDirectory) -Verbose + $pkgFile | ForEach-Object { + Move-Item -Path $_ -Destination $(ob_outputDirectory) -Verbose } Write-Verbose -Verbose "Expanded pkg file:" diff --git a/.pipelines/templates/mac.yml b/.pipelines/templates/mac.yml index cb82a7bf4c0..cd492994617 100644 --- a/.pipelines/templates/mac.yml +++ b/.pipelines/templates/mac.yml @@ -152,4 +152,36 @@ jobs: binPath: $(DropRootPath) OfficialBuild: $(ps_official_build) + # Apple-sign the Mach-O binaries inside the signed output. + - pwsh: | + $signedDir = "$(ob_outputDirectory)/Signed-$(Runtime)" + $zipFile = "$(Pipeline.Workspace)/macho-$(BuildArchitecture).zip" + Compress-Archive -Path "$signedDir/*" -DestinationPath $zipFile -Force + displayName: Compress signed folder for Apple signing + + - task: onebranch.pipeline.signing@1 + displayName: Apple CodeSign Mach-O binaries + inputs: + command: 'sign' + files_to_sign: 'macho-$(BuildArchitecture).zip' + search_root: '$(Pipeline.Workspace)' + inline_operation: | + [ + { + "KeyCode": "$(apple_cert_id)", + "OperationCode": "MacAppDeveloperSign", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": { + "Hardening": "--options=runtime" + } + } + ] + + - pwsh: | + $signedDir = "$(ob_outputDirectory)/Signed-$(Runtime)" + $zipFile = "$(Pipeline.Workspace)/macho-$(BuildArchitecture).zip" + Expand-Archive -Path $zipFile -DestinationPath $signedDir -Force -Verbose + displayName: Expand Apple-signed Mach-O binaries into signed output + - template: /.pipelines/templates/step/finalize.yml@self diff --git a/.pipelines/templates/nupkg.yml b/.pipelines/templates/nupkg.yml index 3558c949402..c77b3a285c7 100644 --- a/.pipelines/templates/nupkg.yml +++ b/.pipelines/templates/nupkg.yml @@ -23,6 +23,7 @@ jobs: - group: mscodehub-feed-read-general - group: mscodehub-feed-read-akv - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual steps: - checkout: self @@ -208,7 +209,7 @@ jobs: displayName: Sign nupkg files inputs: command: 'sign' - cp_code: 'CP-401405' + cp_code: '$(nuget_cert_id)' files_to_sign: '**\*.nupkg' search_root: '$(Pipeline.Workspace)\nupkg' @@ -268,7 +269,7 @@ jobs: displayName: Sign nupkg files inputs: command: 'sign' - cp_code: 'CP-401405' + cp_code: '$(nuget_cert_id)' files_to_sign: '**\*.nupkg' search_root: '$(Pipeline.Workspace)\globaltools' diff --git a/.pipelines/templates/shouldSign.yml b/.pipelines/templates/shouldSign.yml index 551297f3aaa..f3701acbc97 100644 --- a/.pipelines/templates/shouldSign.yml +++ b/.pipelines/templates/shouldSign.yml @@ -6,11 +6,11 @@ parameters: steps: - powershell: | $shouldSign = $true - $authenticodeCert = 'CP-230012' - $msixCert = 'CP-230012' + $authenticodeCert = '$(authenticode_cert_id)' + $msixCert = '$(authenticode_cert_id)' if($env:IS_DAILY -eq 'true') { - $authenticodeCert = 'CP-460906' + $authenticodeCert = '$(authenticode_test_cert_id)' } if($env:SKIP_SIGNING -eq 'Yes') { diff --git a/.pipelines/templates/stages/PowerShell-Packages-Stages.yml b/.pipelines/templates/stages/PowerShell-Packages-Stages.yml index 285a9035d0a..399d242aa4b 100644 --- a/.pipelines/templates/stages/PowerShell-Packages-Stages.yml +++ b/.pipelines/templates/stages/PowerShell-Packages-Stages.yml @@ -100,7 +100,6 @@ stages: signedDrop: 'drop_linux_sign_linux_fxd_x64_mariner' packageType: rpm-fxdependent #mariner-x64 jobName: mariner_x64 - signingProfile: 'CP-459159-pgpdetached' - template: /.pipelines/templates/linux-package-build.yml@self parameters: @@ -108,7 +107,6 @@ stages: signedDrop: 'drop_linux_sign_linux_fxd_arm64_mariner' packageType: rpm-fxdependent-arm64 #mariner-arm64 jobName: mariner_arm64 - signingProfile: 'CP-459159-pgpdetached' - template: /.pipelines/templates/linux-package-build.yml@self parameters: diff --git a/.pipelines/templates/windows-hosted-build.yml b/.pipelines/templates/windows-hosted-build.yml index 83c6b32cdfd..99f1042b17e 100644 --- a/.pipelines/templates/windows-hosted-build.yml +++ b/.pipelines/templates/windows-hosted-build.yml @@ -315,7 +315,7 @@ jobs: displayName: Sign nupkg files inputs: command: 'sign' - cp_code: 'CP-401405' + cp_code: '$(nuget_cert_id)' files_to_sign: '**\*.nupkg' search_root: '$(ob_outputDirectory)\globaltool' condition: and(succeeded(), eq(variables['Architecture'], 'fxdependent')) diff --git a/tools/packaging/packaging.psd1 b/tools/packaging/packaging.psd1 index 0053428a481..e5b7fb84dfc 100644 --- a/tools/packaging/packaging.psd1 +++ b/tools/packaging/packaging.psd1 @@ -26,6 +26,7 @@ 'Test-PackageManifest' 'Update-PSSignedBuildFolder' 'Test-Bom' + 'Get-MacOSPackageIdentifierInfo' ) RootModule = "packaging.psm1" RequiredModules = @("build") diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index b5f88e8ba6c..2aecee36d27 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -825,6 +825,18 @@ function New-TarballPackage { $Staging = "$PSScriptRoot/staging" New-StagingFolder -StagingPath $Staging -PackageSourcePath $PackageSourcePath -R2RVerification $R2RVerification + # Ensure PowerShell executable has correct permissions in tarball + $pwshInStaging = Join-Path $Staging 'pwsh' + if (Test-Path -LiteralPath $pwshInStaging) { + Start-NativeExecution { chmod 755 $pwshInStaging } + } + + # Included .NET executable for producing crash dumps + $createdumpInStaging = Join-Path $Staging 'createdump' + if (Test-Path -LiteralPath $createdumpInStaging) { + Start-NativeExecution { chmod 755 $createdumpInStaging } + } + if (Get-Command -Name tar -CommandType Application -ErrorAction Ignore) { if ($Force -or $PSCmdlet.ShouldProcess("Create tarball package")) { $options = "-czf" @@ -1243,7 +1255,11 @@ function New-UnixPackage { find $Staging -type f | xargs chmod 644 chmod 644 $ManGzipInfo.GzipFile # refers to executable, does not vary by channel - chmod 755 "$Staging/pwsh" #only the executable file should be granted the execution permission + chmod 755 "$Staging/pwsh" # only the executable file should be granted the execution permission + # Included .NET executable for producing crash dumps + if (Test-Path "$Staging/createdump") { + chmod 755 "$Staging/createdump" + } } } @@ -1923,6 +1939,12 @@ $(if ($extendedDescription) { $extendedDescription + "`n" }) Start-NativeExecution { chmod 755 $pwshPath } } + # Included .NET executable for producing crash dumps + $createdumpPath = "$targetPath/createdump" + if (Test-Path $createdumpPath) { + Start-NativeExecution { chmod 755 $createdumpPath } + } + # Calculate md5sums for all files in data directory (excluding symlinks) $md5sumsFile = Join-Path $debianDir "md5sums" $md5Content = "" From 0ceb11ad68464a8202f4bf5e82e5ffded755249d Mon Sep 17 00:00:00 2001 From: Dongbo Wang Date: Wed, 13 May 2026 16:13:41 -0700 Subject: [PATCH 16/18] [release/v7.6.2] Update PowerShell telemetry to respect the diagnostics and feedback setting on Windows (#27438) --- .../PowerShell.Core.Instrumentation.man | 202 +++++++++++++++--- .../CoreCLR/CorePsPlatform.cs | 9 +- .../engine/remoting/common/PSETWTracer.cs | 4 + .../utils/Telemetry.cs | 10 +- .../utils/WindowsDataCollectionSetting.cs | 185 ++++++++++++++++ .../engine/Basic/Telemetry.Tests.ps1 | 50 +++++ 6 files changed, 427 insertions(+), 33 deletions(-) create mode 100644 src/System.Management.Automation/utils/WindowsDataCollectionSetting.cs diff --git a/src/PowerShell.Core.Instrumentation/PowerShell.Core.Instrumentation.man b/src/PowerShell.Core.Instrumentation/PowerShell.Core.Instrumentation.man index fb221cfe964..bb4e15351e5 100644 --- a/src/PowerShell.Core.Instrumentation/PowerShell.Core.Instrumentation.man +++ b/src/PowerShell.Core.Instrumentation/PowerShell.Core.Instrumentation.man @@ -121,6 +121,18 @@ value="0x3002" version="1" /> + + - + + @@ -2409,6 +2445,12 @@ symbol="T_EXPERIMENTALFEATURE" value="107" /> + - + + @@ -2593,11 +2647,23 @@ name="PSWorkflow" symbol="K_PSWORKFLOW" /> - + + @@ -4004,6 +4070,20 @@ name="StackTrace" /> + - + + + @@ -5535,6 +5647,14 @@ id="PS_PROVIDER.task.T_ExperimentalFeature.message" value="PowerShell Experimental Features" /> + + + + + + + + diff --git a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs index 530493f320a..36a6be4074b 100644 --- a/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs +++ b/src/System.Management.Automation/CoreCLR/CorePsPlatform.cs @@ -179,9 +179,12 @@ public static bool IsStaSupported { int result = Interop.Windows.CoInitializeEx(IntPtr.Zero, Interop.Windows.COINIT_APARTMENTTHREADED); - // If 0 is returned the thread has been initialized for the first time - // as an STA and thus supported and needs to be uninitialized. - if (result > 0) + // Per COM documentation: Each successful call to CoInitializeEx (including S_FALSE) + // must be balanced by a corresponding call to CoUninitialize. + // - S_OK (0) means we initialized for the first time. + // - S_FALSE (1) means already initialized, but still increments the reference count. + // Both require CoUninitialize to decrement the reference count. + if (result >= 0) { Interop.Windows.CoUninitialize(); } diff --git a/src/System.Management.Automation/engine/remoting/common/PSETWTracer.cs b/src/System.Management.Automation/engine/remoting/common/PSETWTracer.cs index 2fd2dc0a913..989ad33e987 100644 --- a/src/System.Management.Automation/engine/remoting/common/PSETWTracer.cs +++ b/src/System.Management.Automation/engine/remoting/common/PSETWTracer.cs @@ -166,6 +166,9 @@ internal enum PSEventId : int ExperimentalFeature_InvalidName = 0x3001, ExperimentalFeature_ReadConfig_Error = 0x3002, + // Windows Diagnostics And Usage Data Settings + Telemetry_Setting_Error = 0x3011, + // Scheduled Jobs ScheduledJob_Start = 0xD001, ScheduledJob_Complete = 0xD002, @@ -240,6 +243,7 @@ internal enum PSTask : int ProviderStop = 0x69, ExecutePipeline = 0x6A, ExperimentalFeature = 0x6B, + Telemetry = 0x6C, ScheduledJob = 0x6E, NamedPipe = 0x6F, ISEOperation = 0x78, diff --git a/src/System.Management.Automation/utils/Telemetry.cs b/src/System.Management.Automation/utils/Telemetry.cs index 9a67f4bb6ed..fbb7f588283 100644 --- a/src/System.Management.Automation/utils/Telemetry.cs +++ b/src/System.Management.Automation/utils/Telemetry.cs @@ -177,12 +177,20 @@ public static class ApplicationInsightsTelemetry /// static ApplicationInsightsTelemetry() { - // If we can't send telemetry, there's no reason to do any of this CanSendTelemetry = !Utils.GetEnvironmentVariableAsBool(name: _telemetryOptoutEnvVar, defaultValue: false) && Platform.TryDeriveFromCache("telemetry.uuid", out s_uuidPath); +#if !UNIX + if (CanSendTelemetry) + { + // Respect the diagnostics and feedback setting in Windows. + CanSendTelemetry = WindowsDataCollectionSetting.CanCollectDiagnostics(PlatformDataCollectionLevel.Enhanced); + } +#endif + if (!CanSendTelemetry) { + // Avoid the initialization work if we can't send telemetry. return; } diff --git a/src/System.Management.Automation/utils/WindowsDataCollectionSetting.cs b/src/System.Management.Automation/utils/WindowsDataCollectionSetting.cs new file mode 100644 index 00000000000..5f8b607550a --- /dev/null +++ b/src/System.Management.Automation/utils/WindowsDataCollectionSetting.cs @@ -0,0 +1,185 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if !UNIX + +using System; +using System.Management.Automation.Internal; +using System.Management.Automation.Tracing; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +namespace Microsoft.PowerShell.Telemetry; + +internal enum PlatformDataCollectionLevel : int +{ + /// + /// Minimum — only security-related data. Enterprise/education editions only. + /// + Security = 0, + + /// + /// Device info, capabilities, and basic reliability data. + /// + Basic = 1, + + /// + /// More detailed usage and reliability data, including app/feature usage patterns. + /// Removed as a user-facing option in Windows 11 (collapsed into Full). + /// + Enhanced = 2, + + /// + /// All of the above plus advanced diagnostics data that can help Microsoft fix problems. + /// + Full = 3, +} + +/// +/// Minimal projection of IInspectable, the base interface for all WinRT objects. +/// Slots 3–5 in every WinRT interface vtable (after IUnknown's QueryInterface/AddRef/Release). +/// +[GeneratedComInterface] +[Guid("AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90")] +internal partial interface IInspectable +{ + void GetIids(out uint iidCount, out nint iids); + + nint GetRuntimeClassName(); + + int GetTrustLevel(); +} + +/// +/// Projection of the WinRT interface Windows.System.Profile.IPlatformDiagnosticsAndUsageDataSettingsStatics +/// (IID B6E24C1B-7B1C-4B32-8C62-A66597CE723A). +/// Vtable slots 6–9, following the three IInspectable slots. +/// +[GeneratedComInterface] +[Guid("B6E24C1B-7B1C-4B32-8C62-A66597CE723A")] +internal partial interface IPlatformDiagnosticsAndUsageDataSettingsStatics : IInspectable +{ + PlatformDataCollectionLevel GetCollectionLevel(); + + long AddCollectionLevelChanged(nint handler); + + void RemoveCollectionLevelChanged(long token); + + // WinRT marshals bool as a byte; use byte to avoid any MarshalAs ambiguity with the source generator. + byte CanCollectDiagnostics(PlatformDataCollectionLevel level); +} + +/// +/// Wraps Windows.System.Profile.PlatformDiagnosticsAndUsageDataSettings using compile-time COM interop +/// and source-generated P/Invoke. No extra runtime DLLs are required. +/// +internal static partial class WindowsDataCollectionSetting +{ + /// + /// Returns if the device's diagnostic data collection policy permits collecting at or above . + /// + /// The minimum to test against. + internal static bool CanCollectDiagnostics(PlatformDataCollectionLevel level) + { + const string ClassName = "Windows.System.Profile.PlatformDiagnosticsAndUsageDataSettings"; + + // When initializing WinRT on the calling thread, use the multi-threaded apartment (MTA). + // This is to cover the case where PowerShell gets used in a thread-pool thread. + // See the doc at https://learn.microsoft.com/windows/win32/api/roapi/ne-roapi-ro_init_type + const int RO_INIT_MULTITHREADED = 1; + + // Return values for 'RoInitialize': + // - S_OK (0) - we successfully initialized; must call 'RoUninitialize'. + // - S_FALSE (1) - already initialized with the same apartment type; must still call 'RoUninitialize'. + // - RPC_E_CHANGED_MODE - already initialized with a different apartment type; WinRT still works, do NOT call 'RoUninitialize'. + const int RPC_E_CHANGED_MODE = unchecked((int)0x80010106); + + int initHr = -1; + nint hstring = default; + nint factoryPtr = default; + + try + { + // Initialize WinRT on the calling thread. 'RoGetActivationFactory' requires it. + initHr = RoInitialize(RO_INIT_MULTITHREADED); + if (initHr < 0 && initHr != RPC_E_CHANGED_MODE) + { + // The call to initialize the Windows Runtime failed. + // Throw an exception with the HRESULT error code to provide more context on the failure. + Marshal.ThrowExceptionForHR(initHr); + } + + Marshal.ThrowExceptionForHR( + WindowsCreateString(ClassName, (uint)ClassName.Length, out hstring)); + + Guid iid = new("B6E24C1B-7B1C-4B32-8C62-A66597CE723A"); + Marshal.ThrowExceptionForHR( + RoGetActivationFactory(hstring, ref iid, out factoryPtr)); + + var comWrappers = new StrategyBasedComWrappers(); + var comObject = comWrappers.GetOrCreateObjectForComInstance(factoryPtr, CreateObjectFlags.None); + var platformSetting = (IPlatformDiagnosticsAndUsageDataSettingsStatics)comObject; + + return platformSetting.CanCollectDiagnostics(level) != 0; + } + catch (Exception ex) + { + // Log any exceptions that occur during this process, but swallow them and return false to disable telemetry rather than crashing the product. + // This API is only used to gate telemetry collection, so failure should be non-fatal. + PSEtwLog.LogOperationalError( + PSEventId.Telemetry_Setting_Error, + PSOpcode.Exception, + PSTask.Telemetry, + PSKeyword.UseAlwaysOperational, + ex.GetType().FullName, + ex.Message, + ex.StackTrace); + + return false; + } + finally + { + if (factoryPtr != default) + { + Marshal.Release(factoryPtr); + } + + if (hstring != default) + { + _ = WindowsDeleteString(hstring); + } + + // Per COM documentation: Each successful call to 'RoInitialize' (including S_FALSE) + // must be balanced by a corresponding call to 'RoUninitialize'. + if (initHr >= 0) + { + RoUninitialize(); + } + } + } + + [LibraryImport("api-ms-win-core-winrt-string-l1-1-0.dll", StringMarshalling = StringMarshalling.Utf16)] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + private static partial int WindowsCreateString( + string sourceString, + uint length, + out nint hstring); + + [LibraryImport("api-ms-win-core-winrt-string-l1-1-0.dll")] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + private static partial int WindowsDeleteString(nint hstring); + + [LibraryImport("api-ms-win-core-winrt-l1-1-0.dll")] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + private static partial int RoGetActivationFactory(nint activatableClassId, ref Guid iid, out nint factory); + + [LibraryImport("api-ms-win-core-winrt-l1-1-0.dll")] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + private static partial int RoInitialize(int initType); + + [LibraryImport("api-ms-win-core-winrt-l1-1-0.dll")] + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + private static partial void RoUninitialize(); +} + +#endif diff --git a/test/powershell/engine/Basic/Telemetry.Tests.ps1 b/test/powershell/engine/Basic/Telemetry.Tests.ps1 index 2378b9e5a66..0da08316a56 100644 --- a/test/powershell/engine/Basic/Telemetry.Tests.ps1 +++ b/test/powershell/engine/Basic/Telemetry.Tests.ps1 @@ -5,8 +5,53 @@ # these tests aren't going to check that telemetry is being sent # only that we're not treating the telemetry.uuid file correctly +function Get-OSTelemetryLevel { + <# + .SYNOPSIS + Returns the effective Windows Telemetry level (0-3). + Logic: Checks GPO overrides, then System preferences, then defaults to 1. + #> + + $gpoPath = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\DataCollection" + $sysPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\DataCollection" + $valueName = "AllowTelemetry" + + # 1. Check the "Managed" Policy (Group Policy) + if (Test-Path $gpoPath) { + $gpoValue = Get-ItemProperty -Path $gpoPath -Name $valueName -ErrorAction SilentlyContinue + if ($gpoValue -and $gpoValue.$valueName) { + return [int]$gpoValue.$valueName + } + } + + # 2. Check the "User/System" Preference (Settings App) + if (Test-Path $sysPath) { + $sysValue = Get-ItemProperty -Path $sysPath -Name $valueName -ErrorAction SilentlyContinue + if ($sysValue -and $sysValue.$valueName) { + return [int]$sysValue.$valueName + } + } + + # 3. Fallback to OS Default (Basic/Required) + return 1 +} + Describe "Telemetry for shell startup" -Tag CI { BeforeAll { + $skipTelemetryTests = $false + + if ($IsWindows) { + ## Skip telemetry tests if the OS telemetry level is less than 2 (Enhanced) -- PS telemetry is disabled in this case. + $osTelemetryLevel = Get-OSTelemetryLevel + $skipTelemetryTests = $osTelemetryLevel -lt 2 + } + + if ($skipTelemetryTests) { + $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() + $PSDefaultParameterValues["it:skip"] = $true + return + } + # if the telemetry file exists, move it out of the way # the member is internal, but we can retrieve it via reflection $cacheDir = [System.Management.Automation.Platform].GetField("CacheDirectory","NonPublic,Static").GetValue($null) @@ -23,6 +68,11 @@ Describe "Telemetry for shell startup" -Tag CI { } AfterAll { + if ($skipTelemetryTests) { + $global:PSDefaultParameterValues = $originalDefaultParameterValues + return + } + # check and reset the telemetry.uuid file if ( $uuidFileExists ) { if ( Test-Path -Path "${uuidPath}.original" ) { From 43da485fafefaf6bfc2a622e1728a5ccf83a5efe Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Thu, 14 May 2026 15:30:03 -0400 Subject: [PATCH 17/18] [release/v7.6.2] Update branch for release (#27446) --- CHANGELOG/v7.6/dependencychanges.json | 13 +++++++++++++ DotnetRuntimeMetadata.json | 2 +- global.json | 2 +- tools/cgmanifest/main/cgmanifest.json | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGELOG/v7.6/dependencychanges.json b/CHANGELOG/v7.6/dependencychanges.json index 0189431db77..a0122c94ac6 100644 --- a/CHANGELOG/v7.6/dependencychanges.json +++ b/CHANGELOG/v7.6/dependencychanges.json @@ -49,5 +49,18 @@ "AdvisoryUrls": [], "Justification": "Required dependency of System.Security.Cryptography.Xml.", "TimestampUtc": "2026-04-16T23:13:22.4500098Z" + }, + { + "ChangeType": "NonSecurity", + "Branch": "release/v7.6.2", + "PackageId": ".NET SDK", + "FromVersion": "10.0.202", + "ToVersion": "10.0.300", + "VulnerabilityId": [], + "Severity": [], + "VulnerableRanges": [], + "AdvisoryUrls": [], + "Justification": "Updated .NET SDK. Building with the latest SDK is required.", + "TimestampUtc": "2026-05-14T17:53:01.1430124Z" } ] diff --git a/DotnetRuntimeMetadata.json b/DotnetRuntimeMetadata.json index 8b1ed423288..a09be0f382c 100644 --- a/DotnetRuntimeMetadata.json +++ b/DotnetRuntimeMetadata.json @@ -4,7 +4,7 @@ "quality": "daily", "qualityFallback": "preview", "packageVersionPattern": "9.0.0-preview.6", - "sdkImageVersion": "10.0.202", + "sdkImageVersion": "10.0.300", "nextChannel": "9.0.0-preview.7", "azureFeed": "", "sdkImageOverride": "" diff --git a/global.json b/global.json index 5ee7b7cb062..9769786b11b 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "sdk": { - "version": "10.0.202" + "version": "10.0.300" } } diff --git a/tools/cgmanifest/main/cgmanifest.json b/tools/cgmanifest/main/cgmanifest.json index fd645029d33..e06492f2e75 100644 --- a/tools/cgmanifest/main/cgmanifest.json +++ b/tools/cgmanifest/main/cgmanifest.json @@ -626,7 +626,7 @@ "Type": "nuget", "Nuget": { "Name": "System.Security.Cryptography.Pkcs", - "Version": "10.0.5" + "Version": "10.0.6" } }, "DevelopmentDependency": false From 0d3c290a8737d4252ee75c052d16190ffcdb7d19 Mon Sep 17 00:00:00 2001 From: PowerShell Team Bot <69177312+pwshBot@users.noreply.github.com> Date: Thu, 21 May 2026 13:59:17 -0400 Subject: [PATCH 18/18] Update 7.6 changelog for v7.6.2 (#27471) --- CHANGELOG/7.6.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/CHANGELOG/7.6.md b/CHANGELOG/7.6.md index a4d81683321..da1370779cc 100644 --- a/CHANGELOG/7.6.md +++ b/CHANGELOG/7.6.md @@ -1,5 +1,47 @@ # 7.6 Changelog +## [7.6.2] + +### Engine Updates and Fixes + +- Enable usage in AppContainers (#27423) +- Fix checks for local user config file paths (#27432) + +### General Cmdlet Updates and Fixes + +- Update PowerShell telemetry to respect the diagnostics and feedback setting on Windows (#27438) + +### Build and Packaging Improvements + +
+ + + +

Update to .NET SDK 10.0.300

+ +
+ +
    +
  • Update branch for release (#27446)
  • +
  • Fix *nix permissions and use certificate_logical_to_actual (#27439)
  • +
  • Specify linux-arm64 runtime if package type is deb-arm64 in packaging.psm1 (#27440)
  • +
  • Remove mariner2.0 from PMC mapping (#27422)
  • +
  • Remove package verification from the notice pipeline (#27425)
  • +
  • Update the MSIXBundle-VPack pipeline to create VPack for both LTS and Stable channel packages (#27435)
  • +
  • Update Microsoft.PowerShell.Native to the latest GA version (#27436)
  • +
  • Create PowerShell package for arm debian distribution (#27433)
  • +
  • Add macOS binary code signing and package notarization (#27434)
  • +
  • Externalize findMissingNotices target framework selection with ordered Windows fallback (#27424)
  • +
  • Add appLicensing capability to Appx manifest (#27437)
  • +
  • Download PMC Packages through TemplateContext (#27331)
  • +
  • PMC release: Use slash instead of back-slash for Linux container (#27319)
  • +
  • Correct Variable Template Reference in NonOfficial Pipeline Templates (#27317)
  • +
+ +
+ +[7.6.2]: https://github.com/PowerShell/PowerShell/compare/v7.6.1...v7.6.2 + ## [7.6.1] ### General Cmdlet Updates and Fixes