diff --git a/.ci/compliance.yml b/.ci/compliance.yml new file mode 100644 index 0000000..06b92f7 --- /dev/null +++ b/.ci/compliance.yml @@ -0,0 +1,146 @@ +steps: + +- powershell: | + $powerShellPath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'powershell' + Invoke-WebRequest -Uri https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.ps1 -outfile ./install-powershell.ps1 + ./install-powershell.ps1 -Destination $powerShellPath + $vstsCommandString = "vso[task.setvariable variable=PATH]$powerShellPath;$env:PATH" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: Install PowerShell Core + +- task: DownloadBuildArtifacts@0 + displayName: 'Download artifacts' + inputs: + buildType: current + downloadType: specifc + itemPattern: '**/*.nupkg' + downloadPath: '$(System.ArtifactsDirectory)' + +- pwsh: | + Get-ChildItem -Path "$(System.ArtifactsDirectory)" -Recurse + displayName: Capture artifacts directory + +- pwsh: | + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + if (Test-Path -Path $modulePath) { + Write-Verbose -Verbose "Deleting existing temp module path: $modulePath" + Remove-Item -Path $modulePath -Recurse -Force -ErrorAction Ignore + } + if (! (Test-Path -Path $modulePath)) { + Write-Verbose -Verbose "Creating new temp module path: $modulePath" + $null = New-Item -Path $modulePath -ItemType Directory + } + displayName: Create temporary module path + +- pwsh: | + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + Write-Verbose -Verbose "Install PowerShellGet V3 to temp module path" + Save-Module -Name PowerShellGet -Path $modulePath -MinimumVersion 3.0.0-beta10 -AllowPrerelease -Force + Write-Verbose -Verbose "Install PlatyPS to temp module path" + Save-Module -Name "platyPS" -Path $modulePath -Force + Write-Verbose -Verbose "Install PSScriptAnalyzer to temp module path" + Save-Module -Name "PSScriptAnalyzer" -Path $modulePath -RequiredVersion 1.18.0 -Force + Write-Verbose -Verbose "Install Pester 4.X to temp module path" + Save-Module -Name "Pester" -MaximumVersion 4.99 -Path $modulePath -Force + Write-Verbose -Verbose "Install PSPackageProject to temp module path" + Save-Module -Name PSPackageProject -Path $modulePath -Force + displayName: Install PSPackageProject and dependencies + +- pwsh: | + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + $env:PSModulePath = $modulePath + [System.IO.Path]::PathSeparator + $env:PSModulePath + $sourceName = 'pspackageproject-local-repo' + Register-PackageSource -Name $sourceName -Location "$(System.ArtifactsDirectory)" -ProviderName PowerShellGet -Force -ErrorAction Ignore + Get-PackageSource -Name $sourceName + $config = Get-PSPackageProjectConfiguration + $buildOutputPath = $config.BuildOutputPath + $null = New-Item -ItemType Directory -Path $buildOutputPath -Verbose + $moduleName = $config.ModuleName + Write-Verbose -Verbose "Saving package $sourceName to $($config.BuildOutputPath)" + Save-Package -Name $moduleName -Source $sourceName -ProviderName PowerShellGet -Path $config.BuildOutputPath -AllowPrereleaseVersions -Force + Write-Verbose -Verbose "Writing BUILD_SOURCE variable" + $vstsCommandString = "vso[task.setvariable variable=BUILD_SOURCE]$($config.BuildOutputPath)" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: Extract product artifact + +- pwsh: | + $modulePath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'TempModules' + $env:PSModulePath = $modulePath + [System.IO.Path]::PathSeparator + $env:PSModulePath + $config = Get-PSPackageProjectConfiguration + dir "$($config.BuildOutputPath)/*" -r 2>$null + displayName: 'BuildOutputPath directory' + +- task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + inputs: + sourceScanPath: '$(Build.SourcesDirectory)' + snapshotForceEnabled: true + +- task: securedevelopmentteam.vss-secure-development-tools.build-task-antimalware.AntiMalware@3 + displayName: 'Run Defender Scan' + +- task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 + displayName: 'Run CredScan' + inputs: + toolMajorVersion: V2 + debugMode: false + continueOnError: true + +- task: securedevelopmentteam.vss-secure-development-tools.build-task-binskim.BinSkim@3 + displayName: 'Run BinSkim ' + inputs: + InputType: Basic + AnalyzeTarget: '$(BUILD_SOURCE)\Microsoft.PowerShell.ThreadJob\Microsoft.PowerShell.ThreadJob.dll' + AnalyzeSymPath: 'SRV*' + AnalyzeVerbose: true + AnalyzeHashes: true + AnalyzeStatistics: true + continueOnError: true + +- task: securedevelopmentteam.vss-secure-development-tools.build-task-policheck.PoliCheck@1 + displayName: 'Run PoliCheck' + inputs: + targetType: F + optionsFC: 0 + optionsXS: 0 + optionsPE: '1|2|3|4' + optionsHMENABLE: 0 +# optionsRulesDBPath: '$(Build.SourcesDirectory)\tools\terms\PowerShell-Terms-Rules.mdb' +# optionsFTPATH: '$(Build.SourcesDirectory)\tools\terms\FileTypeSet.xml' + toolVersion: 5.8.2.1 + continueOnError: true + +- task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@2 + displayName: 'Publish Security Analysis Logs to Build Artifacts' + continueOnError: true + +- task: securedevelopmentteam.vss-secure-development-tools.build-task-uploadtotsa.TSAUpload@1 + displayName: 'TSA upload to Codebase: PSThreadJob_201912 Stamp: Azure' + inputs: + tsaStamp: Azure + codeBaseName: PSThreadJob_202004 + tsaVersion: TsaV2 + uploadFortifySCA: false + uploadFxCop: false + uploadModernCop: false + uploadPREfast: false + uploadRoslyn: false + uploadTSLint: false + uploadAPIScan: false + +- task: securedevelopmentteam.vss-secure-development-tools.build-task-report.SdtReport@1 + displayName: 'Create Security Analysis Report' + inputs: + TsvFile: false + APIScan: false + BinSkim: false + CredScan: true + PoliCheck: true + PoliCheckBreakOn: Severity2Above + +- pwsh: | + Unregister-PSRepository -Name 'pspackageproject-local-repo' -ErrorAction Ignore + displayName: Unregister temporary PSRepository + condition: always() diff --git a/.ci/templates/sign.yml b/.ci/templates/sign.yml new file mode 100644 index 0000000..ac7d32d --- /dev/null +++ b/.ci/templates/sign.yml @@ -0,0 +1,79 @@ +parameters: + - name: "buildOutputPath" + default: "$(Build.ArtifactStagingDirectory)\\build" + - name: "signOutputPath" + default: "$(Build.ArtifactStagingDirectory)\\signed" + - name: "certificateId" + default: "CP-230012" + - name: "pattern" + default: "*.dll,*.exe" + +steps: +- task: UseDotNet@2 + displayName: 'Install .NET Core sdk 2.x for ESRP' + inputs: + version: 2.x + +- pwsh: | + [string] $CertificateId = "${{ parameters.certificateId }}" + Write-Verbose "CertificateId - $CertificateId" -Verbose + + [string] $VariableName = "EsrpJson" + + [string] $SigningServer = '$(SigningServer)' + Write-Verbose "SigningServer - $SigningServer" -Verbose + + $esrpParameters = @( + @{ + ParameterName = "OpusName" + ParameterValue = "Microsoft" + } + @{ + ParameterName = "OpusInfo" + ParameterValue = "http://www.microsoft.com" + } + @{ + ParameterName = "PageHash" + ParameterValue = "/NPH" + } + @{ + ParameterName = "FileDigest" + ParameterValue = "/fd sha256" + } + @{ + ParameterName = "TimeStamp" + ParameterValue = "/tr ""$SigningServer"" /td sha256" + } + ) + + $esrp = @(@{ + keyCode = $CertificateId + operationSetCode = "SigntoolSign" + parameters = $esrpParameters + toolName = "signtool.exe" + toolVersion = "6.2.9304.0" + }) + + $vstsCommandString = "vso[task.setvariable variable=$VariableName][$($esrp | ConvertTo-Json -Compress)]" + Write-Verbose -Message ("sending " + $vstsCommandString) -Verbose + Write-Host "##$vstsCommandString" + displayName: Generate signing JSON + condition: and(and(and(succeeded(), eq(variables['Build.Reason'], 'Manual')), ne(variables['SkipSigning'], 'True')), ne(variables['SigningServer'], '')) + +- pwsh: | + Write-Verbose "BUILD_OUTPUT_PATH- ${{ parameters.buildOutputPath}}" -Verbose + Write-Verbose "SIGNED_OUTPUT_PATH- ${{ parameters.signOutputPath }}" -Verbose + Copy-Item ${{ parameters.buildOutputPath }}\* ${{ parameters.signOutputPath }}\ -Recurse -Force -Verbose + displayName: Prepare output folder + timeoutInMinutes: 10 + +- task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 + displayName: Sign files + inputs: + ConnectedServiceName: pwshSigning + FolderPath: '${{ parameters.signOutputPath }}' + UseMinimatch: false + signConfigType: inlineSignParams + inlineOperation: $(EsrpJson) + Pattern: ${{ parameters.pattern }} + condition: and(and(and(succeeded(), eq(variables['Build.Reason'], 'Manual')), ne(variables['SkipSigning'], 'True')), ne(variables['SigningServer'], '')) diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json new file mode 100644 index 0000000..e4c65c4 --- /dev/null +++ b/.config/tsaoptions.json @@ -0,0 +1,8 @@ +{ + "instanceUrl": "https://msazure.visualstudio.com", + "projectName": "One", + "areaPath": "One\\MGMT\\Compute\\Powershell\\Powershell\\PowerShell Core\\ThreadJob", + "codebaseName": "TFSMSAzure_ThreadJob", + "notificationAliases": [ "chungjustin@microsoft.com", "slee@microsoft.com" ], + "tools": [ "CredScan", "PoliCheck", "BinSkim" ] +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..a5ba660 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: + - package-ecosystem: "nuget" # See documentation for possible values + directory: "/src/code" # Location of package manifests + schedule: + interval: "daily" + + - package-ecosystem: github-actions + directory: / + schedule: + interval: daily + + - package-ecosystem: nuget + directory: / + schedule: + interval: daily \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9587ceb..80167f7 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ xhtml/ **/.vscode/** **/out/** **/bin/** +.vs/**/* +**/*.sln \ No newline at end of file diff --git a/.pipelines/threadjobs-official.yml b/.pipelines/threadjobs-official.yml new file mode 100644 index 0000000..86ce7e3 --- /dev/null +++ b/.pipelines/threadjobs-official.yml @@ -0,0 +1,248 @@ +name: ThreadJob-ModuleBuild-$(Build.BuildId) +trigger: none +pr: none + +schedules: +- cron: '0 3 * * 1' + displayName: Weekly Build + branches: + include: + - onebranch-pipelines + always: true + +parameters: + - name: 'publishOfficialToPowerShellGallery' + displayName: 'Publish official module to PowerShell gallery' + type: boolean + default: false + - name : 'publishProxyToPowerShellGallery' + displayName: 'Publish proxy module to PowerShell gallery' + type: boolean + default: false + +variables: + BuildConfiguration: Release + DOTNET_NOLOGO: true + DOTNET_GENERATE_ASPNET_CERTIFICATE: false + 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: templates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +extends: + # https://aka.ms/obpipelines/templates + template: v2/OneBranch.Official.CrossPlat.yml@templates + parameters: + release: + category: NonAzure + featureFlags: + WindowsHostVersion: + Version: 2022 + Network: Netlock + globalSdl: # https://aka.ms/obpipelines/sdl + asyncSdl: + enabled: true + forStages: [build] + #credscan: + # enabled: true + # scanfolder: $(Build.SourcesDirectory) + # suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json + stages: + - stage: build + jobs: + - job: main + displayName: Build package + pool: + type: windows + variables: + - name: ob_outputDirectory + value: $(Build.SourcesDirectory)/out + #- name: ob_sdl_credscan_suppressionsFile + # value: $(Build.SourcesDirectory)\.config\suppress.json + steps: + - pwsh: | + Write-Verbose -Verbose ((Get-Item $(Build.SourcesDirectory)).FullName) + Get-ChildItem $(Build.SourcesDirectory) -Recurse -File -Name | Write-Verbose -Verbose + $manifestData = Import-PowerShellDataFile -Path src/Microsoft.PowerShell.ThreadJob.psd1 + $moduleVersion = $manifestData.ModuleVersion + Write-Host "##vso[task.setvariable variable=version;isOutput=true]$moduleVersion" + if ($manifestData.PrivateData.PsData.Prerelease) { + $prerel = $manifestData.PrivateData.PSData.Prerelease + $nupkgVersion = "${moduleVersion}-${prerel}" + } else { + $nupkgVersion = $moduleVersion + } + Write-Host "##vso[task.setvariable variable=nupkgversion;isOutput=true]$nupkgVersion" + name: package + displayName: Get version from project properties + - task: onebranch.pipeline.version@1 + displayName: Set OneBranch version + inputs: + system: Custom + customVersion: $(package.version) + - task: UseDotNet@2 + displayName: Use .NET SDK + inputs: + packageType: sdk + useGlobalJson: true + - pwsh: | + Get-ChildItem | Write-Verbose -Verbose + Register-PSRepository -Name CFS -SourceLocation "https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/powershell/nuget/v2" -InstallationPolicy Trusted + Install-Module -Repository CFS -Name Microsoft.PowerShell.PSResourceGet -MinimumVersion 1.0.5 + .\build.ps1 -clean -Build -BuildConfiguration Release -BuildFramework netstandard2.0 + displayName: Build + - task: onebranch.pipeline.signing@1 + displayName: Sign 1st-party files + inputs: + command: sign + signing_profile: external_distribution + search_root: $(Build.SourcesDirectory)/out + files_to_sign: | + **/*.psd1; + **/*.ps1xml; + **/*.psm1; + **/Microsoft.PowerShell.*.dll; + - task: ArchiveFiles@2 + displayName: Zip module + inputs: + rootFolderOrFile: $(Build.SourcesDirectory)/out/Microsoft.PowerShell.ThreadJob + includeRootFolder: false + archiveType: zip + archiveFile: out/Microsoft.PowerShell.ThreadJob-v$(package.version).zip + - task: ArchiveFiles@2 + displayName: Zip module + inputs: + rootFolderOrFile: $(Build.SourcesDirectory)/out/ThreadJob + includeRootFolder: false + archiveType: zip + archiveFile: out/ThreadJob-v$(package.version).zip + - pwsh: | + Get-ChildItem | Write-Verbose -Verbose + Write-Verbose -Verbose -Message "Install Microsoft.PowerShell.ThreadJob module" + Copy-Item -Path $(Build.SourcesDirectory)/out/Microsoft.PowerShell.ThreadJob -Destination ($env:PSModulePath -split ';')[0] -Recurse -Force + Write-Verbose -Verbose -Message "Test ThreadJob module manifest" + Test-ModuleManifest -Path $(Build.SourcesDirectory)/out/ThreadJob/ThreadJob.psd1 + .\build.ps1 -Publish + Write-Verbose -Verbose ((Get-Item .).FullName) + Write-Verbose -Verbose ((Get-Item $(Build.SourcesDirectory)).FullName) + Get-ChildItem $(Build.SourcesDirectory) -Recurse -File -Name | Write-Verbose -Verbose + displayName: Package module + - task: onebranch.pipeline.signing@1 + displayName: Sign NuGet package + inputs: + command: sign + signing_profile: external_distribution + search_root: $(Build.SourcesDirectory) + files_to_sign: "**/*.nupkg" + - stage: manual + dependsOn: build + jobs: + - job: validation + displayName: Manual validation + pool: + type: agentless + timeoutInMinutes: 1440 + steps: + - task: ManualValidation@0 + displayName: Wait 24 hours for validation + inputs: + notifyUsers: $(Build.RequestedForEmail) + instructions: Please validate the release and then publish it! + timeoutInMinutes: 1440 + - stage: release_official_MicrosoftPowerShellThreadJob_module + displayName: release official + variables: + ob_release_environment: PPE + drop: $(Pipeline.Workspace)/drop_build_main + version: $[ stageDependencies.build.main.outputs['package.version'] ] + dependsOn: [build, manual] + condition: ${{ parameters.publishOfficialToPowerShellGallery }} + jobs: + - job: publish + templateContext: + inputs: + - input: pipelineArtifact + artifactName: drop_build_main + displayName: Publish to PowerShell Gallery + pool: + type: release + os: linux + variables: + - group: ThreadJob_Gallery_API + steps: + - task: Bash@3 + inputs: + targetType: inline + script: | + sudo tdnf install -y powershell + displayName: Install PowerShell + - task: powershell@2 + inputs: + pwsh: true + targetType: inline + script: | + Write-Verbose -Verbose ((Get-Item $(Pipeline.Workspace)).FullName) + Get-ChildItem $(Pipeline.Workspace) -Recurse -File -Name | Write-Verbose -Verbose + Write-Verbose -Verbose -Message "Set up CFS repository" + Register-PSRepository -Name CFS -SourceLocation "https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/powershell/nuget/v2" -InstallationPolicy Trusted -ErrorAction Continue + Write-Verbose -Verbose -Message "Install PSResourceGet module" + Install-Module -Repository CFS -Name Microsoft.PowerShell.PSResourceGet -MinimumVersion 1.0.5 -ErrorAction Continue -Force + Write-Verbose -Verbose -Message "Publish module to PSGallery" + Publish-PSResource -ApiKey $env:GalleryPAT -Repository PSGallery -Path $(Pipeline.Workspace)/Microsoft.PowerShell.ThreadJob + env: + GalleryPAT: $(ChungJustinAPIKey) + displayName: Publish to PowerShell Gallery + - stage: release_proxy_ThreadJob_module + displayName: release proxy + variables: + ob_release_environment: PPE + drop: $(Pipeline.Workspace)/drop_build_main + version: $[ stageDependencies.build.main.outputs['package.version'] ] + dependsOn: [build, manual] + condition: ${{ parameters.publishProxyToPowerShellGallery }} + jobs: + - job: publish + templateContext: + inputs: + - input: pipelineArtifact + artifactName: drop_build_main + displayName: Publish to PowerShell Gallery + pool: + type: release + os: linux + variables: + - group: ThreadJob_Gallery_API + steps: + - task: Bash@3 + inputs: + targetType: inline + script: | + sudo tdnf install -y powershell + displayName: Install PowerShell + - task: powershell@2 + inputs: + pwsh: true + targetType: inline + script: | + Write-Verbose -Verbose ((Get-Item $(Pipeline.Workspace)).FullName) + Get-ChildItem $(Pipeline.Workspace) -Recurse -File -Name | Write-Verbose -Verbose + Write-Verbose -Verbose -Message "Set up CFS repository" + Register-PSRepository -Name CFS -SourceLocation "https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/powershell/nuget/v2" -InstallationPolicy Trusted -ErrorAction Continue + Write-Verbose -Verbose -Message "Install PSResourceGet module" + Install-Module -Repository CFS -Name Microsoft.PowerShell.PSResourceGet -MinimumVersion 1.0.5 -ErrorAction Continue -Force + Write-Verbose -Verbose -Message "Install Microsoft.PowerShell.ThreadJob module" + Copy-Item -Path $(Pipeline.Workspace)/Microsoft.PowerShell.ThreadJob -Destination ($env:PSModulePath -split ':')[0] -Recurse -Force -Verbose + Write-Verbose -Verbose -Message "Test ThreadJob module manifest" + Test-ModuleManifest -Path $(Pipeline.Workspace)/ThreadJob/ThreadJob.psd1 + Write-Verbose -Verbose -Message "Publish module to PSGallery" + Publish-PSResource -ApiKey $env:GalleryPAT -Repository PSGallery -Path $(Pipeline.Workspace)/ThreadJob + env: + GalleryPAT: $(ChungJustinAPIKey) + displayName: Publish to PowerShell Gallery \ No newline at end of file diff --git a/build.ps1 b/build.ps1 index 898920a..51cfbd1 100644 --- a/build.ps1 +++ b/build.ps1 @@ -22,8 +22,8 @@ param ( [ValidateSet("Debug", "Release")] [string] $BuildConfiguration = "Debug", - [ValidateSet("net461")] - [string] $BuildFramework = "net461" + [ValidateSet("netstandard2.0")] + [string] $BuildFramework = "netstandard2.0" ) Import-Module -Name "$PSScriptRoot/buildtools.psd1" -Force @@ -31,8 +31,10 @@ Import-Module -Name "$PSScriptRoot/buildtools.psd1" -Force $config = Get-BuildConfiguration -ConfigPath $PSScriptRoot $script:ModuleName = $config.ModuleName +$script:ProxyModuleName = $config.ProxyModuleName $script:SrcPath = $config.SourcePath $script:OutDirectory = $config.BuildOutputPath +$script:ProxyOutDirectory = $config.BuildOutputPath $script:SignedDirectory = $config.SignedOutputPath $script:TestPath = $config.TestPath @@ -53,7 +55,7 @@ if ($env:TF_BUILD) { Write-Host "##$vstsCommandString" } -. $PSScriptRoot/dobuild.ps1 +. $PSScriptRoot/doBuild.ps1 if ($Clean -and (Test-Path $OutDirectory)) { @@ -73,6 +75,7 @@ if ($Clean -and (Test-Path $OutDirectory)) if (-not (Test-Path $OutDirectory)) { $script:OutModule = New-Item -ItemType Directory -Path (Join-Path $OutDirectory $ModuleName) + $script:ProxyOutModule = New-Item -itemType Directory -Path (Join-Path $OutDirectory $ProxyModuleName) } else { diff --git a/buildtools.psm1 b/buildtools.psm1 index 686e1d5..c64c151 100644 --- a/buildtools.psm1 +++ b/buildtools.psm1 @@ -2,7 +2,7 @@ # Licensed under the MIT License. $ConfigurationFileName = 'package.config.json' -Import-Module -Name PowerShellGet -MinimumVersion 3.0.18 +Import-Module -Name Microsoft.PowerShell.PSResourceGet -Force function Get-BuildConfiguration { [CmdletBinding()] @@ -36,6 +36,7 @@ function Get-BuildConfiguration { $configObj.TestPath = Join-Path $projectRoot -ChildPath $configObj.TestPath $configObj.HelpPath = Join-Path $projectRoot -ChildPath $configObj.HelpPath $configObj.BuildOutputPath = Join-Path $projectRoot -ChildPath $configObj.BuildOutputPath + if ($configObj.SignedOutputPath) { $configObj.SignedOutputPath = Join-Path $projectRoot -ChildPath $configObj.SignedOutputPath } @@ -69,32 +70,28 @@ function Publish-ModulePackage ) Write-Verbose -Verbose -Message "Creating new local package repo" + $config = Get-BuildConfiguration $localRepoName = 'packagebuild-local-repo' - $localRepoLocation = Join-Path -Path ([System.io.path]::GetTempPath()) -ChildPath $localRepoName - if (Test-Path -Path $localRepoLocation) { - Remove-Item -Path $localRepoLocation -Recurse -Force -ErrorAction Ignore - } - $null = New-Item -Path $localRepoLocation -ItemType Directory -Force + $localRepoLocation = $config.BuildOutputPath Write-Verbose -Verbose -Message "Registering local package repo: $localRepoName" Register-PSResourceRepository -Name $localRepoName -Uri $localRepoLocation -Trusted -Force Write-Verbose -Verbose -Message "Publishing package to local repo: $localRepoName" - $config = Get-BuildConfiguration - if (! $Signed.IsPresent) { - $modulePath = Join-Path -Path $config.BuildOutputPath -ChildPath $config.ModuleName - } else { - $modulePath = Join-Path -Path $config.SignedOutputPath -ChildPath $config.ModuleName - } + $modulePath = Join-Path -Path $config.BuildOutputPath -ChildPath $config.ModuleName + + # Proxy module + Write-Verbose -Verbose -Message "Publishing proxy module to local repo: $localRepoName" + $proxyModulePath = Join-Path -Path $config.BuildOutputPath -ChildPath $config.ProxyModuleName + Publish-PSResource -Path $proxyModulePath -Repository $localRepoName -SkipDependenciesCheck -Confirm:$false -Verbose + + # Official module + Write-Verbose -Verbose -Message "Publishing official module to local repo: $localRepoName" Publish-PSResource -Path $modulePath -Repository $localRepoName -SkipDependenciesCheck -Confirm:$false -Verbose - if ($env:TF_BUILD) { - Write-Verbose -Verbose -Message "Uploading module nuget package artifact to AzDevOps" - $artifactName = "nupkg" - $artifactPath = (Get-ChildItem -Path $localRepoLocation -Filter "$($config.ModuleName)*.nupkg").FullName - $artifactPath = Resolve-Path -Path $artifactPath - Write-Host "##vso[artifact.upload containerfolder=$artifactName;artifactname=$artifactName;]$artifactPath" - } + $artifactPath = (Get-ChildItem -Path $localRepoLocation -Filter "$($config.ModuleName)*.nupkg").FullName + $artifactPath = Resolve-Path -Path $artifactPath + Write-Verbose -Verbose -Message "ArtifactPath: $artifactPath" Write-Verbose -Verbose -Message "Unregistering local package repo: $localRepoName" Unregister-PSResourceRepository -Name $localRepoName -Confirm:$false diff --git a/doBuild.ps1 b/doBuild.ps1 index 5b8057f..0af7900 100644 --- a/doBuild.ps1 +++ b/doBuild.ps1 @@ -13,6 +13,10 @@ function DoBuild $BuildOutPath = "${OutDirectory}/${ModuleName}" Write-Verbose -Verbose -Message "Module output file path: '$BuildOutPath'" + # Proxy module out path + $ProxyOutPath = "${OutDirectory}/${ProxyModuleName}" + Write-Verbose -Verbose -Message "Module output file path: '$ProxyOutPath'" + # Module build source path $BuildSrcPath = "bin/${BuildConfiguration}/${BuildFramework}/publish" Write-Verbose -Verbose -Message "Module build source path: '$BuildSrcPath'" @@ -21,6 +25,11 @@ function DoBuild Write-Verbose -Verbose "Copy-Item ${SrcPath}/${ModuleName}.psd1 to $BuildOutPath" Copy-Item "${SrcPath}/${ModuleName}.psd1" "$BuildOutPath" + # Copy Proxy psd1 and psm1 file + Write-Verbose -Verbose -Message "Copying proxy module files to '$ProxyOutPath'" + Copy-Item "${SrcPath}/${ProxyModuleName}/${ProxyModuleName}.psd1" "$ProxyOutPath" + Copy-Item "${SrcPath}/${ProxyModuleName}/${ProxyModuleName}.psm1" "$ProxyOutPath" + # Copy help Write-Verbose -Verbose -Message "Copying help files to '$BuildOutPath'" copy-item -Recurse "${HelpPath}/${Culture}" "$BuildOutPath" @@ -43,8 +52,19 @@ function DoBuild # Check for dotnet for Windows (we only build on Windows platforms). if ($null -eq $dotnetCommand) { - Write-Verbose -Verbose -Message "dotnet.exe cannot be found in current path. Looking in ProgramFiles path." - $dotnetCommandPath = Join-Path -Path $env:ProgramFiles -ChildPath "dotnet\dotnet.exe" + if ($IsWindows) { + Write-Verbose -Verbose -Message "dotnet.exe cannot be found in current path. Looking in ProgramFiles path." + $dotnetCommandPath = Join-Path -Path $env:ProgramFiles -ChildPath "dotnet\dotnet.exe" + } elseif ($IsLinux) { + Write-Verbose -Verbose -Message "dotnet cannot be found in current path. Looking in /usr/share/dotnet path." + $dotnetCommandPath = "/usr/share/dotnet/dotnet" + } elseif ($IsMaxOS) { + Write-Verbose -Verbose -Message "dotnet cannot be found in current path. Looking in /usr/local/share/dotnet path." + $dotnetCommandPath = "/usr/local/share/dotnet/dotnet" + } else { + throw "Unsupported operating system." + } + $dotnetCommand = Get-Command -Name $dotnetCommandPath -ErrorAction Ignore if ($null -eq $dotnetCommand) { throw "Dotnet.exe cannot be found: $dotnetCommandPath is unavailable for build." @@ -60,18 +80,20 @@ function DoBuild Write-Verbose -Verbose -Message "Building location: PSScriptRoot: $PSScriptRoot, PWD: $pwd" $buildCommand = "$($dotnetCommand.Name) publish --configuration $BuildConfiguration --framework $BuildFramework --output $BuildSrcPath" Write-Verbose -Verbose -Message "Starting dotnet build command: $buildCommand" - Invoke-Expression -Command $buildCommand + # Capture the output and error streams + $output = Invoke-Expression -Command $buildCommand 2>&1 + Write-Verbose -Verbose -Message "Build output: $output" - # Dump build source output directory - # $outResultsPath = (Resolve-Path -Path ".").ProviderPath - # Write-Verbose -Verbose -Message "Dumping expected results output path: $outResultsPath" - # $outResults = Get-ChildItem -Path $outResultsPath -Recurse | Out-String - # Write-Verbose -Verbose -Message $outResults + #Dump build source output directory + $outResultsPath = (Resolve-Path -Path ".").ProviderPath + Write-Verbose -Verbose -Message "Dumping expected results output path: $outResultsPath" + $outResults = Get-ChildItem -Path $outResultsPath -Recurse | Out-String + Write-Verbose -Verbose -Message $outResults # Place build results if (! (Test-Path -Path "$BuildSrcPath/${ModuleName}.dll")) { - throw "Expected binary was not created: $BuildSrcPath/${ModuleName}.dll" + # throw "Expected binary was not created: $BuildSrcPath/${ModuleName}.dll" } Write-Verbose -Verbose -Message "Copying implementation assembly $BuildSrcPath/${ModuleName}.dll to $BuildOutPath" diff --git a/global.json b/global.json new file mode 100644 index 0000000..ef01b10 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "8.0.401" + } +} + \ No newline at end of file diff --git a/help/en-US/about_Microsoft.PowerShell.ThreadJob.md b/help/en-US/about_Microsoft.PowerShell.ThreadJob.md new file mode 100644 index 0000000..03a0d20 --- /dev/null +++ b/help/en-US/about_Microsoft.PowerShell.ThreadJob.md @@ -0,0 +1,57 @@ +# Microsoft.PowerShell.ThreadJob +## about_Microsoft.PowerShell.ThreadJob + +``` +ABOUT TOPIC NOTE: +The first header of the about topic should be the topic name. +The second header contains the lookup name used by the help system. + +IE: +# Some Help Topic Name +## SomeHelpTopicFileName + +This will be transformed into the text file +as `about_SomeHelpTopicFileName`. +Do not include file extensions. +The second header should have no spaces. +``` + +# SHORT DESCRIPTION +{{ Short Description Placeholder }} + +``` +ABOUT TOPIC NOTE: +About topics can be no longer than 80 characters wide when rendered to text. +Any topics greater than 80 characters will be automatically wrapped. +The generated about topic will be encoded UTF-8. +``` + +# LONG DESCRIPTION +{{ Long Description Placeholder }} + +## Optional Subtopics +{{ Optional Subtopic Placeholder }} + +# EXAMPLES +{{ Code or descriptive examples of how to leverage the functions described. }} + +# NOTE +{{ Note Placeholder - Additional information that a user needs to know.}} + +# TROUBLESHOOTING NOTE +{{ Troubleshooting Placeholder - Warns users of bugs}} + +{{ Explains behavior that is likely to change with fixes }} + +# SEE ALSO +{{ See also placeholder }} + +{{ You can also list related articles, blogs, and video URLs. }} + +# KEYWORDS +{{List alternate names or titles for this topic that readers might use.}} + +- {{ Keyword Placeholder }} +- {{ Keyword Placeholder }} +- {{ Keyword Placeholder }} +- {{ Keyword Placeholder }} diff --git a/nuget.config b/nuget.config index 6548586..a8fdae0 100644 --- a/nuget.config +++ b/nuget.config @@ -2,9 +2,7 @@ <configuration> <packageSources> <clear /> - <add key="nuget.org" value="https://api.nuget.org/v3/index.json" /> + <!-- Please verify that the feed below exists for your project and replace placeholder values. You may also use another feed of your choice. --> + <add key="PowerShellCore_PublicPackages" value="https://pkgs.dev.azure.com/powershell/PowerShell/_packaging/powershell/nuget/v3/index.json" /> </packageSources> - <disabledPackageSources> - <clear /> - </disabledPackageSources> -</configuration> +</configuration> \ No newline at end of file diff --git a/package.config.json b/package.config.json index 0ae6b35..c84e587 100644 --- a/package.config.json +++ b/package.config.json @@ -1,5 +1,6 @@ { "ModuleName": "Microsoft.PowerShell.ThreadJob", + "ProxyModuleName" : "ThreadJob", "Culture": "en-US", "BuildOutputPath": "out", "SignedOutputPath": "signed", diff --git a/pspackageproject.json b/pspackageproject.json new file mode 100644 index 0000000..0ae6b35 --- /dev/null +++ b/pspackageproject.json @@ -0,0 +1,9 @@ +{ + "ModuleName": "Microsoft.PowerShell.ThreadJob", + "Culture": "en-US", + "BuildOutputPath": "out", + "SignedOutputPath": "signed", + "HelpPath": "help", + "TestPath": "test", + "SourcePath": "src" +} diff --git a/src/Microsoft.PowerShell.ThreadJob.psd1 b/src/Microsoft.PowerShell.ThreadJob.psd1 index 5e214f2..c46accb 100644 --- a/src/Microsoft.PowerShell.ThreadJob.psd1 +++ b/src/Microsoft.PowerShell.ThreadJob.psd1 @@ -8,7 +8,7 @@ RootModule = '.\Microsoft.PowerShell.ThreadJob.dll' # Version number of this module. -ModuleVersion = '2.1.1' +ModuleVersion = '2.2.0' # ID used to uniquely identify this module GUID = 'a84b375d-c1d6-4a1c-bcb7-8059bc28cd98' @@ -42,8 +42,6 @@ number of jobs drops below the throttle limit. # Minimum version of the Windows PowerShell engine required by this module PowerShellVersion = '5.1' -DotNetFrameworkVersion = '4.6.1' -CLRVersion = '4.0.0' # Cmdlets to export from this module CmdletsToExport = 'Start-ThreadJob' @@ -80,6 +78,4 @@ PrivateData = @{ } # End of PrivateData hashtable -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=2113345' - } diff --git a/src/ThreadJob/ThreadJob.psd1 b/src/ThreadJob/ThreadJob.psd1 new file mode 100644 index 0000000..69d8ab9 --- /dev/null +++ b/src/ThreadJob/ThreadJob.psd1 @@ -0,0 +1,41 @@ +# +# Module manifest for module 'ThreadJob' +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'ThreadJob.psm1' + +# Version number of this module. +ModuleVersion = '2.0.4' + +# ID used to uniquely identify this module +GUID = '0e7b895d-2fec-43f7-8cae-11e8d16f6e40' + +Author = 'Microsoft Corporation' +CompanyName = 'Microsoft Corporation' +Copyright = '(c) Microsoft Corporation. All rights reserved.' + +# Description of the functionality provided by this module +Description = 'ThreadJob module has been renamed to Microsoft.PowerShell.ThreadJob.' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '5.1' + +RequiredModules = @('Microsoft.PowerShell.ThreadJob') +FunctionsToExport = @() + +# Cmdlets to export from this module +CmdletsToExport = @() +AliasesToExport = @('Start-ThreadJob', 'ThreadJob\Start-ThreadJob') + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + PSData = @{ + LicenseUri = 'https://github.com/PowerShell/ThreadJob/blob/master/LICENSE' + ProjectUri = 'https://github.com/PowerShell/ThreadJob' + } +} # End of PrivateData hashtable + +} \ No newline at end of file diff --git a/src/ThreadJob/ThreadJob.psm1 b/src/ThreadJob/ThreadJob.psm1 new file mode 100644 index 0000000..539c54c --- /dev/null +++ b/src/ThreadJob/ThreadJob.psm1 @@ -0,0 +1,6 @@ +Write-Warning -Message "The ThreadJob module has been renamed to Microsoft.PowerShell.ThreadJob. The ThreadJob module will no longer be included with PowerShell as of 7.6 and above." + +Set-Alias -Name Start-ThreadJob -Value Microsoft.PowerShell.ThreadJob\Start-ThreadJob +Set-Alias -Name ThreadJob\Start-ThreadJob -Value Microsoft.PowerShell.ThreadJob\Start-ThreadJob + +Export-ModuleMember -Alias Start-ThreadJob, ThreadJob\Start-ThreadJob \ No newline at end of file diff --git a/src/code/Microsoft.PowerShell.ThreadJob.cs b/src/code/Microsoft.PowerShell.ThreadJob.cs index 6bfe042..88c8ddf 100644 --- a/src/code/Microsoft.PowerShell.ThreadJob.cs +++ b/src/code/Microsoft.PowerShell.ThreadJob.cs @@ -11,9 +11,11 @@ using System.Management.Automation.Host; using System.Management.Automation.Language; using System.Management.Automation.Runspaces; -using System.Management.Automation.Security; using System.Text; using System.Threading; +using System.Reflection; +using System.Diagnostics; +using SMA = System.Management.Automation; namespace Microsoft.PowerShell.ThreadJob { @@ -283,11 +285,11 @@ public override void RemoveJob(Job2 job) #endregion } - internal sealed class ThreadJobDebugger : Debugger + internal sealed class ThreadJobDebugger : SMA.Debugger { #region Members - private Debugger _wrappedDebugger; + private SMA.Debugger _wrappedDebugger; private string _jobName; #endregion @@ -297,7 +299,7 @@ internal sealed class ThreadJobDebugger : Debugger private ThreadJobDebugger() { } public ThreadJobDebugger( - Debugger debugger, + SMA.Debugger debugger, string jobName) { if (debugger == null) @@ -380,7 +382,7 @@ public override DebuggerStopEventArgs GetDebuggerStopArgs() /// <param name="host">PowerShell host.</param> /// <param name="path">Current path.</param> public override void SetParent( - Debugger parent, + SMA.Debugger parent, IEnumerable<Breakpoint> breakPoints, DebuggerResumeAction? startAction, PSHost host, @@ -473,7 +475,7 @@ public sealed class ThreadJob : Job2, IJobDebugger private PSDataCollection<PSObject> _output; private bool _runningInitScript; private PSHost _streamingHost; - private Debugger _jobDebugger; + private SMA.Debugger _jobDebugger; private string _currentLocationPath; private const string VERBATIM_ARGUMENT = "--%"; @@ -595,11 +597,21 @@ public ThreadJob( WarningRecord lockdownWarning = null; if (Environment.OSVersion.Platform.ToString().Equals("Win32NT", StringComparison.OrdinalIgnoreCase)) { - bool enforceLockdown = (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce); + Assembly assembly = Assembly.LoadFrom(typeof(PSObject).Assembly.Location); + Type systemPolicy = assembly.GetType("System.Management.Automation.Security.SystemPolicy"); + MethodInfo getSystemLockdownPolicy = systemPolicy.GetMethod("GetSystemLockdownPolicy", BindingFlags.Public | BindingFlags.Static); + object lockdownPolicy = getSystemLockdownPolicy.Invoke(null, null); + + Type systemEnforcementMode = assembly.GetType("System.Management.Automation.Security.SystemEnforcementMode"); + FieldInfo enforce = systemEnforcementMode.GetField("Enforce"); + object enforceValue = enforce.GetValue(null); + + bool enforceLockdown = lockdownPolicy.Equals(enforceValue); if (enforceLockdown && !string.IsNullOrEmpty(_filePath)) { // If script source is a file, check to see if it is trusted by the lock down policy - enforceLockdown = (SystemPolicy.GetLockdownPolicy(_filePath, null) == SystemEnforcementMode.Enforce); + lockdownPolicy = getSystemLockdownPolicy.Invoke(null, new object[] { _filePath, null }); + enforceLockdown = lockdownPolicy.Equals(enforceValue); if (!enforceLockdown && (_initSb != null)) { @@ -639,11 +651,11 @@ public ThreadJob( break; case PSInvocationState.Stopped: - SetJobState(JobState.Stopped, newStateInfo.Reason, disposeRunspace:true); + SetJobState(JobState.Stopped, newStateInfo.Reason, disposeRunspace: true); break; case PSInvocationState.Failed: - SetJobState(JobState.Failed, newStateInfo.Reason, disposeRunspace:true); + SetJobState(JobState.Failed, newStateInfo.Reason, disposeRunspace: true); break; case PSInvocationState.Completed: @@ -655,7 +667,7 @@ public ThreadJob( } else { - SetJobState(JobState.Completed, newStateInfo.Reason, disposeRunspace:true); + SetJobState(JobState.Completed, newStateInfo.Reason, disposeRunspace: true); } break; } @@ -1001,7 +1013,7 @@ public override void UnblockJob() /// <summary> /// Job Debugger /// </summary> - public Debugger Debugger + public SMA.Debugger Debugger { get { @@ -1087,7 +1099,7 @@ private ScriptBlock GetScriptBlockFromFile(string filePath, PSCmdlet psCmdlet) private void SetJobState(JobState jobState, Exception reason, bool disposeRunspace = false) { - base.SetJobState(jobState, reason); + base.SetJobState(jobState); if (disposeRunspace) { _rs.Dispose(); diff --git a/src/code/Microsoft.PowerShell.ThreadJob.csproj b/src/code/Microsoft.PowerShell.ThreadJob.csproj index b1c8d40..f6ffa2c 100644 --- a/src/code/Microsoft.PowerShell.ThreadJob.csproj +++ b/src/code/Microsoft.PowerShell.ThreadJob.csproj @@ -5,21 +5,14 @@ <OutputType>Library</OutputType> <RootNamespace>Microsoft.PowerShell.ThreadJob</RootNamespace> <AssemblyName>Microsoft.PowerShell.ThreadJob</AssemblyName> - <AssemblyVersion>2.1.1.0</AssemblyVersion> - <FileVersion>2.1.1</FileVersion> - <InformationalVersion>2.1.1</InformationalVersion> - <TargetFramework>net461</TargetFramework> + <AssemblyVersion>2.2.0.0</AssemblyVersion> + <FileVersion>2.2.0</FileVersion> + <InformationalVersion>2.2.0</InformationalVersion> + <TargetFramework>netstandard2.0</TargetFramework> </PropertyGroup> - - <ItemGroup> - <Reference Include="System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll</HintPath> - </Reference> - </ItemGroup> - + <ItemGroup> - <PackageReference Include="PowerShellStandard.Library" Version="5.1.0-*" /> + <PackageReference Include="PowerShellStandard.Library" Version="5.1.0" PrivateAssets="all" /> <PackageReference Include="Microsoft.CSharp" version="4.5.0-*" /> </ItemGroup>