diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json new file mode 100644 index 000000000..0f1ded2de --- /dev/null +++ b/.config/tsaoptions.json @@ -0,0 +1,9 @@ +{ + "instanceUrl": "https://msazure.visualstudio.com", + "projectName": "One", + "areaPath": "One\\MGMT\\Compute\\Powershell\\Powershell\\PowerShell Core", + "notificationAliases": [ + "dongbow@microsoft.com", + "slee@microsoft.com" + ] +} diff --git a/.pipelines/PSReadLine-Official.yml b/.pipelines/PSReadLine-Official.yml new file mode 100644 index 000000000..ef4897ed7 --- /dev/null +++ b/.pipelines/PSReadLine-Official.yml @@ -0,0 +1,237 @@ +################################################################################# +# OneBranch Pipelines # +# This pipeline was created by EasyStart from a sample located at: # +# https://aka.ms/obpipelines/easystart/samples # +# Documentation: https://aka.ms/obpipelines # +# Yaml Schema: https://aka.ms/obpipelines/yaml/schema # +# Retail Tasks: https://aka.ms/obpipelines/tasks # +# Support: https://aka.ms/onebranchsup # +################################################################################# + +name: PSReadLine-ModuleBuild-$(Build.BuildId) +trigger: none +pr: none + +variables: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + WindowsContainerImage: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest + +resources: + repositories: + - repository: onebranchTemplates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +extends: + template: v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates + parameters: + featureFlags: + WindowsHostVersion: '1ESWindows2022' + globalSdl: + disableLegacyManifest: true + sbom: + enabled: true + packageName: PSReadLine + codeql: + compiled: + enabled: true + asyncSdl: # https://aka.ms/obpipelines/asyncsdl + enabled: true + forStages: [Build] + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory)\PSReadLine\PSReadLine + binskim: + enabled: true + apiscan: + enabled: false + + stages: + - stage: buildstage + displayName: Build and Sign PSReadLine + jobs: + - job: buildjob + displayName: Build PSReadLine Files + variables: + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: repoRoot + value: $(Build.SourcesDirectory)\PSReadLine + - name: ob_sdl_tsa_configFile + value: $(repoRoot)\.config\tsaoptions.json + - name: signSrcPath + value: $(repoRoot)\bin\Release\PSReadLine + - name: ob_sdl_sbom_enabled + value: true + - name: ob_signing_setup_enabled + value: true + #CodeQL tasks added manually to workaround signing failures + - name: ob_sdl_codeql_compiled_enabled + value: false + + pool: + type: windows + steps: + - checkout: self + env: + # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + ob_restore_phase: true + + - pwsh: | + if (-not (Test-Path $(repoRoot)/.config/tsaoptions.json)) { + throw "tsaoptions.json does not exist under $(repoRoot)/.config" + } + displayName: Test if tsaoptions.json exists + env: + # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + ob_restore_phase: true + + - pwsh: | + Write-Host "PS Version: $($PSVersionTable.PSVersion)" + Set-Location -Path '$(repoRoot)' + .\build.ps1 -Bootstrap + displayName: Bootstrap + env: + # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + ob_restore_phase: true + + # Add CodeQL Init task right before your 'Build' step. + - task: CodeQL3000Init@0 + env: + # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + ob_restore_phase: true + inputs: + Enabled: true + AnalyzeInPipeline: true + Language: csharp + + - pwsh: | + Write-Host "PS Version: $($($PSVersionTable.PSVersion))" + Set-Location -Path '$(repoRoot)' + .\build.ps1 -Configuration Release -Framework net462 + displayName: Build + env: + # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + ob_restore_phase: true + + # Add CodeQL Finalize task right after your 'Build' step. + - task: CodeQL3000Finalize@0 + condition: always() + env: + # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + ob_restore_phase: true + + - task: onebranch.pipeline.signing@1 + displayName: Sign 1st party files + inputs: + command: 'sign' + signing_profile: external_distribution + files_to_sign: '*.psd1;*.psm1;*.ps1;*.ps1xml;**\Microsoft*.dll;!Microsoft.PowerShell.Pager.dll' + search_root: $(signSrcPath) + + # Verify the signatures + - pwsh: | + $HasInvalidFiles = $false + $WrongCert = @{} + Get-ChildItem -Path $(signSrcPath) -Recurse -Include "*.dll","*.ps*1*" | ` + Get-AuthenticodeSignature | ForEach-Object { + Write-Host "$($_.Path): $($_.Status)" + if ($_.Status -ne 'Valid') { $HasInvalidFiles = $true } + if ($_.SignerCertificate.Subject -notmatch 'CN=Microsoft Corporation.*') { + $WrongCert.Add($_.Path, $_.SignerCertificate.Subject) + } + } + + if ($HasInvalidFiles) { throw "Authenticode verification failed. There is one or more invalid files." } + if ($WrongCert.Count -gt 0) { + $WrongCert + throw "Certificate should have the subject starts with 'Microsoft Corporation'" + } + + Write-Host "Display files in the folder ..." -ForegroundColor Yellow + Get-ChildItem -Path $(signSrcPath) -Recurse | Out-String -Width 120 + displayName: 'Verify the signed files' + + - task: CopyFiles@2 + displayName: "Copy signed files to ob_outputDirectory - '$(ob_outputDirectory)'" + inputs: + SourceFolder: $(signSrcPath) + Contents: '**\*' + TargetFolder: $(ob_outputDirectory) + + - pwsh: | + $versionInfo = Get-Item "$(signSrcPath)\Microsoft.PowerShell.PSReadLine2.dll" | ForEach-Object VersionInfo + $moduleVersion = $versionInfo.ProductVersion.Split('+')[0] + $vstsCommandString = "vso[task.setvariable variable=ob_sdl_sbom_packageversion]${moduleVersion}" + + Write-Host "sending $vstsCommandString" + Write-Host "##$vstsCommandString" + displayName: Setup SBOM Package Version + + - job: nupkg + dependsOn: buildjob + displayName: Package PSReadLine module + variables: + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: repoRoot + value: $(Build.SourcesDirectory)\PSReadLine + - name: ob_sdl_tsa_configFile + value: $(repoRoot)\.config\tsaoptions.json + # Disable because SBOM was already built in the previous job + - name: ob_sdl_sbom_enabled + value: false + - name: signOutPath + value: $(repoRoot)\signed\PSReadLine + - name: nugetPath + value: $(repoRoot)\signed\NuGetPackage + - name: ob_signing_setup_enabled + value: true + # This job is not compiling code, so disable codeQL + - name: ob_sdl_codeql_compiled_enabled + value: false + + pool: + type: windows + steps: + - checkout: self + + - task: DownloadPipelineArtifact@2 + displayName: 'Download build files' + inputs: + targetPath: $(signOutPath) + artifact: drop_buildstage_buildjob + + - pwsh: | + Get-ChildItem $(signOutPath) -Recurse + New-Item -Path $(nugetPath) -ItemType Directory > $null + displayName: Capture artifacts structure + + - pwsh: | + try { + $RepoName = "PSRLLocal" + Register-PSRepository -Name $RepoName -SourceLocation $(nugetPath) -PublishLocation $(nugetPath) -InstallationPolicy Trusted + Publish-Module -Repository $RepoName -Path $(signOutPath) + } finally { + Unregister-PSRepository -Name $RepoName -ErrorAction SilentlyContinue + } + Get-ChildItem -Path $(nugetPath) + displayName: 'Create the NuGet package' + + - task: onebranch.pipeline.signing@1 + displayName: Sign nupkg + inputs: + command: 'sign' + signing_profile: external_default + files_to_sign: '*.nupkg' + search_root: $(nugetPath) + + - task: CopyFiles@2 + displayName: "Copy nupkg to ob_outputDirectory - '$(ob_outputDirectory)'" + inputs: + SourceFolder: $(nugetPath) + Contents: '**\*' + TargetFolder: $(ob_outputDirectory) diff --git a/.vsts-ci/releaseBuild.yml b/.vsts-ci/releaseBuild.yml deleted file mode 100644 index dd150aa9a..000000000 --- a/.vsts-ci/releaseBuild.yml +++ /dev/null @@ -1,192 +0,0 @@ -name: PSReadLine-ModuleBuild-$(Build.BuildId) -trigger: none -pr: none - -variables: - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - POWERSHELL_TELEMETRY_OPTOUT: 1 - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 - SBOMGenerator_Formats: 'spdx:2.2' - -resources: - repositories: - - repository: ComplianceRepo - type: github - endpoint: ComplianceGHRepo - name: PowerShell/compliance - -stages: -- stage: Build - displayName: Build and Sign - pool: - name: PowerShell1ES - demands: - - ImageOverride -equals PSMMS2019-Secure - jobs: - - job: build_windows - displayName: Build PSReadLine - variables: - - group: ESRP - - steps: - - - checkout: self - clean: true - persistCredentials: true - - - pwsh: | - function Send-VstsCommand ($vstsCommandString) { - Write-Host ("sending: " + $vstsCommandString) - Write-Host "##$vstsCommandString" - } - Write-Host "PS Version: $($($PSVersionTable.PSVersion))" - Set-Location -Path '$(Build.SourcesDirectory)\PSReadLine' - .\build.ps1 -Bootstrap - .\build.ps1 -Configuration Release -Framework net462 - - # Set target folder paths - New-Item -Path .\bin\Release\NuGetPackage -ItemType Directory > $null - Send-VstsCommand "vso[task.setvariable variable=NuGetPackage]$(Build.SourcesDirectory)\PSReadLine\bin\Release\NuGetPackage" - Send-VstsCommand "vso[task.setvariable variable=PSReadLine]$(Build.SourcesDirectory)\PSReadLine\bin\Release\PSReadLine" - Send-VstsCommand "vso[task.setvariable variable=Signed]$(Build.SourcesDirectory)\PSReadLine\bin\Release\Signed" - displayName: Bootstrap & Build - - - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 - displayName: 'Component Governance Detection' - inputs: - sourceScanPath: '$(Build.SourcesDirectory)\PSReadLine' - snapshotForceEnabled: true - scanType: 'Register' - failOnAlert: true - - - checkout: ComplianceRepo - - # Sign the module files - - template: EsrpSign.yml@ComplianceRepo - parameters: - # the folder which contains the binaries to sign - buildOutputPath: $(PSReadLine) - # the location to put the signed output - signOutputPath: $(Signed) - # the certificate ID to use - certificateId: "CP-230012" - pattern: | - *.psd1 - *.psm1 - *.ps1 - *.ps1xml - **\*.dll - !System.Runtime.InteropServices.RuntimeInformation.dll - !Microsoft.PowerShell.Pager.dll - useMinimatch: true - - # Replace the *.psm1, *.ps1, *.psd1, *.dll files with the signed ones - - pwsh: | - # Show the signed files - Get-ChildItem -Path $(Signed) - Copy-Item -Path $(Signed)\* -Destination $(PSReadLine) -Recurse -Force - displayName: 'Replace unsigned files with signed ones' - - # Verify the signatures - - pwsh: | - $HasInvalidFiles = $false - $WrongCert = @{} - Get-ChildItem -Path $(PSReadLine) -Recurse -Include "*.dll","*.ps*1*" | ` - Get-AuthenticodeSignature | ForEach-Object { - $_ | Select-Object Path, Status - if ($_.Status -ne 'Valid') { $HasInvalidFiles = $true } - if ($_.SignerCertificate.Subject -notmatch 'CN=Microsoft Corporation.*') { - $WrongCert.Add($_.Path, $_.SignerCertificate.Subject) - } - } - - if ($HasInvalidFiles) { throw "Authenticode verification failed. There is one or more invalid files." } - if ($WrongCert.Count -gt 0) { - $WrongCert - throw "Certificate should have the subject starts with 'Microsoft Corporation'" - } - displayName: 'Verify the signed files' - - # Generate a Software Bill of Materials (SBOM) - - template: Sbom.yml@ComplianceRepo - parameters: - BuildDropPath: '$(PSReadLine)' - Build_Repository_Uri: 'https://github.com/PowerShell/PSReadLine.git' - displayName: Generate SBOM - - - pwsh: | - try { - $RepoName = "PSRLLocal" - Register-PSRepository -Name $RepoName -SourceLocation $(NuGetPackage) -PublishLocation $(NuGetPackage) -InstallationPolicy Trusted - Publish-Module -Repository $RepoName -Path $(PSReadLine) - } finally { - Unregister-PSRepository -Name $RepoName -ErrorAction SilentlyContinue - } - Get-ChildItem -Path $(NuGetPackage) - displayName: 'Create the NuGet package' - - - pwsh: | - Get-ChildItem -Path $(PSReadLine), $(NuGetPackage) - Write-Host "##vso[artifact.upload containerfolder=PSReadLine;artifactname=PSReadLine]$(PSReadLine)" - Write-Host "##vso[artifact.upload containerfolder=NuGetPackage;artifactname=NuGetPackage]$(NuGetPackage)" - displayName: 'Upload artifacts' - -- stage: compliance - displayName: Compliance - dependsOn: Build - pool: - name: PowerShell1ES - demands: - - ImageOverride -equals PSMMS2019-Secure - jobs: - - job: Compliance_Job - displayName: PSReadLine Compliance - variables: - - group: APIScan - - group: ApiScanMeta - # APIScan can take a long time - timeoutInMinutes: 240 - - steps: - - checkout: self - - checkout: ComplianceRepo - - download: current - artifact: PSReadLine - - - pwsh: | - Get-ChildItem -Path "$(Pipeline.Workspace)\PSReadLine" -Recurse - displayName: Capture downloaded artifacts - - - pwsh: | - function Send-VstsCommand ($vstsCommandString) { - Write-Host ("sending: " + $vstsCommandString) - Write-Host "##$vstsCommandString" - } - - # Get module version - $psd1Data = Import-PowerShellDataFile -Path "$(Pipeline.Workspace)\PSReadLine\PSReadLine.psd1" - $moduleVersion = $psd1Data.ModuleVersion - $prerelease = $psd1Data.PrivateData.PSData.Prerelease - if ($prerelease) { $moduleVersion = "$moduleVersion-$prerelease" } - Send-VstsCommand "vso[task.setvariable variable=ModuleVersion]$moduleVersion" - displayName: Get Module Version - - - template: assembly-module-compliance.yml@ComplianceRepo - parameters: - # binskim - AnalyzeTarget: '$(Pipeline.Workspace)\PSReadLine\*.dll' - AnalyzeSymPath: 'SRV*' - # component-governance - sourceScanPath: '' - # credscan - suppressionsFile: '' - # TermCheck - optionsRulesDBPath: '' - optionsFTPath: '' - # tsa-upload - codeBaseName: 'PSReadLine_201912' - # apiscan - softwareFolder: '$(Pipeline.Workspace)\PSReadLine' - softwareName: 'PSReadLine' - softwareVersion: '$(ModuleVersion)' - connectionString: 'RunAs=App;AppId=$(APIScanClient);TenantId=$(APIScanTenant);AppKey=$(APIScanSecret)'