diff --git a/.ci/ci_auto.yml b/.ci/ci_auto.yml index f9a72d7..7e18486 100644 --- a/.ci/ci_auto.yml +++ b/.ci/ci_auto.yml @@ -124,7 +124,7 @@ stages: } else { - $(Build.SourcesDirectory)/build.ps1 -Publish -Signed + $(Build.SourcesDirectory)/build.ps1 -Publish } displayName: Publish module nuget package and upload package artifact condition: succeeded() 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..75e50ad --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +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..9f80330 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,5 @@ xhtml/ **/.vscode/** **/out/** **/bin/** +.vs/**/* +**/*.sln diff --git a/.pipelines/threadjobs-official.yml b/.pipelines/threadjobs-official.yml new file mode 100644 index 0000000..18848ce --- /dev/null +++ b/.pipelines/threadjobs-official.yml @@ -0,0 +1,276 @@ +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 + + pipelines: + - pipeline: PSPackagesOfficial + source: 'PowerShell-Packages-Official' + +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.ArtifactStagingDirectory)' + - name: ob_sdl_sbom_enabled + value: true + #- name: ob_sdl_credscan_suppressionsFile + # value: $(Build.SourcesDirectory)\.config\suppress.json + steps: + - pwsh: | + Write-Verbose -Verbose $(Build.SourcesDirectory) + 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" + $proxyManifestData = Import-PowerShellDataFile -Path src/ThreadJob/ThreadJob.psd1 + $proxyModuleVersion = $proxyManifestData.ModuleVersion + Write-Host "##vso[task.setvariable variable=proxyVersion;isOutput=true]$proxyModuleVersion" + 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 + - 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 $(Build.SourcesDirectory) + 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" + - pwsh: | + Get-ChildItem -Path $(Build.SourcesDirectory)/out -Filter '*.nupkg' -Recurse + | ForEach-Object { + Write-Verbose -Verbose "Copying $($_.FullName) to $(ob_outputDirectory)" + Copy-Item -Path $_.FullName -Destination $(ob_outputDirectory) -Force + } + Get-ChildItem -Path $(ob_outputDirectory) -Recurse -File -Name | Write-Verbose -Verbose + displayName: Upload Signed Nupkgs + - 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: Production + 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 + - input: pipelineArtifact + pipeline: PSPackagesOfficial + artifactName: drop_upload_upload_packages + displayName: Publish to PowerShell Gallery + pool: + type: release + os: windows + variables: + - group: ThreadJob_Gallery_API + steps: + - task: PowerShell@2 + inputs: + targetType: inline + script: | + $localInstallerPath = Get-ChildItem -Path "$(Pipeline.Workspace)/GitHubPackages" -Filter '*win-x64.msi' | Select-Object -First 1 -ExpandProperty FullName + if (Test-Path -Path $localInstallerPath) { + Write-Verbose -Verbose "Installer found at $localInstallerPath" + } else { + throw "Installer not found" + } + Write-Verbose -Verbose "Installing PowerShell via msiexec" + Start-Process -FilePath msiexec -ArgumentList "/package $localInstallerPath /quiet REGISTER_MANIFEST=1" -Wait -NoNewWindow + $pwshPath = Get-ChildItem -Directory -Path 'C:\Program Files\PowerShell\7*' | Select-Object -First 1 -ExpandProperty FullName + if (Test-Path -Path $pwshPath) { + Write-Verbose -Verbose "PowerShell installed at $pwshPath" + Write-Verbose -Verbose "Adding pwsh to env:PATH" + Write-Host "##vso[task.prependpath]$pwshPath" + } else { + throw "PowerShell not installed" + } + displayName: Install pwsh 7 + - task: PowerShell@2 + inputs: + targetType: inline + pwsh: true + script: | + Write-Verbose -Verbose "Pwsh 7 Installed" + Write-Verbose -Verbose "env:Path: " + $env:PATH -split ';' | ForEach-Object { + Write-Verbose -Verbose $_ + } + displayName: Check pwsh 7 installation + - task: Powershell@2 + inputs: + pwsh: true + targetType: inline + script: | + Write-Verbose -Verbose -Message "Publish module to PSGallery" + Publish-PSResource -ApiKey $(GalleryKey) -Repository PSGallery -Path $(Pipeline.Workspace)/Microsoft.PowerShell.ThreadJob.($version).nupkg + displayName: Publish to PowerShell Gallery + - stage: release_proxy_ThreadJob_module + displayName: release proxy + variables: + ob_release_environment: Production + drop: $(Pipeline.Workspace)/drop_build_main + version: $[ stageDependencies.build.main.outputs['package.proxyVersion'] ] + dependsOn: [build, manual] + condition: ${{ parameters.publishProxyToPowerShellGallery }} + jobs: + - job: publish + templateContext: + inputs: + - input: pipelineArtifact + artifactName: drop_build_main + - input: pipelineArtifact + pipeline: PSPackagesOfficial + artifactName: drop_upload_upload_packages + displayName: Publish to PowerShell Gallery + pool: + type: release + os: windows + variables: + - group: ThreadJob_Gallery_API + steps: + - task: PowerShell@2 + inputs: + targetType: inline + script: | + $localInstallerPath = Get-ChildItem -Path "$(Pipeline.Workspace)/GitHubPackages" -Filter '*win-x64.msi' | Select-Object -First 1 -ExpandProperty FullName + if (Test-Path -Path $localInstallerPath) { + Write-Verbose -Verbose "Installer found at $localInstallerPath" + } else { + throw "Installer not found" + } + Write-Verbose -Verbose "Installing PowerShell via msiexec" + Start-Process -FilePath msiexec -ArgumentList "/package $localInstallerPath /quiet REGISTER_MANIFEST=1" -Wait -NoNewWindow + $pwshPath = Get-ChildItem -Directory -Path 'C:\Program Files\PowerShell\7*' | Select-Object -First 1 -ExpandProperty FullName + if (Test-Path -Path $pwshPath) { + Write-Verbose -Verbose "PowerShell installed at $pwshPath" + Write-Verbose -Verbose "Adding pwsh to env:PATH" + Write-Host "##vso[task.prependpath]$pwshPath" + } else { + throw "PowerShell not installed" + } + displayName: Install pwsh 7 + - task: PowerShell@2 + inputs: + targetType: inline + pwsh: true + script: | + Write-Verbose -Verbose "Pwsh 7 Installed" + Write-Verbose -Verbose "env:Path: " + $env:PATH -split ';' | ForEach-Object { + Write-Verbose -Verbose $_ + } + displayName: Check pwsh 7 installation + - task: Powershell@2 + inputs: + pwsh: true + targetType: inline + script: | + 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 $(GalleryKey) -Repository PSGallery -Path $(Pipeline.Workspace)/ThreadJob + displayName: Publish to PowerShell Gallery + \ No newline at end of file diff --git a/build.ps1 b/build.ps1 index 898920a..5e7ff30 100644 --- a/build.ps1 +++ b/build.ps1 @@ -15,15 +15,11 @@ param ( [switch] $Publish, - [Parameter(ParameterSetName="publish")] - [switch] - $Signed, - [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,9 +27,9 @@ 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:SignedDirectory = $config.SignedOutputPath $script:TestPath = $config.TestPath $script:ModuleRoot = $PSScriptRoot @@ -47,13 +43,9 @@ if ($env:TF_BUILD) { $vstsCommandString = "vso[task.setvariable variable=BUILD_OUTPUT_PATH]$OutDirectory" Write-Host ("sending " + $vstsCommandString) Write-Host "##$vstsCommandString" - - $vstsCommandString = "vso[task.setvariable variable=SIGNED_OUTPUT_PATH]$SignedDirectory" - Write-Host ("sending " + $vstsCommandString) - Write-Host "##$vstsCommandString" } -. $PSScriptRoot/dobuild.ps1 +. $PSScriptRoot/doBuild.ps1 if ($Clean -and (Test-Path $OutDirectory)) { @@ -73,6 +65,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 { @@ -87,5 +80,6 @@ if ($Build.IsPresent) if ($Publish.IsPresent) { - Publish-ModulePackage -Signed:$Signed.IsPresent + Publish-ModulePackage } + diff --git a/buildtools.psd1 b/buildtools.psd1 index 6542e45..fc65756 100644 --- a/buildtools.psd1 +++ b/buildtools.psd1 @@ -42,7 +42,7 @@ # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. FunctionsToExport = @( 'Get-BuildConfiguration', 'Invoke-ModuleBuild', 'Publish-ModulePackage', 'Install-ModulePackageForTest', 'Invoke-ModuleTests') - + # Variables to export from this module VariablesToExport = '*' diff --git a/buildtools.psm1 b/buildtools.psm1 index 686e1d5..5d84cde 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,12 +36,6 @@ 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 - } - else { - $configObj | Add-Member -MemberType NoteProperty -Name SignedOutputPath -Value (Join-Path $projectRoot -ChildPath 'signed') - } return $configObj } @@ -62,39 +56,29 @@ function Invoke-ModuleBuild { function Publish-ModulePackage { - [CmdletBinding()] - param ( - [Parameter()] - [Switch] $Signed - ) - 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..cbb005b 100644 --- a/doBuild.ps1 +++ b/doBuild.ps1 @@ -11,7 +11,11 @@ function DoBuild # Module build out path $BuildOutPath = "${OutDirectory}/${ModuleName}" - Write-Verbose -Verbose -Message "Module output file path: '$BuildOutPath'" + Write-Verbose -Verbose -Message "Official Module output file path: '$BuildOutPath'" + + # Proxy module out path + $ProxyOutPath = "${OutDirectory}/${ProxyModuleName}" + Write-Verbose -Verbose -Message "Proxy Module output file path: '$ProxyOutPath'" # Module build source path $BuildSrcPath = "bin/${BuildConfiguration}/${BuildFramework}/publish" @@ -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,13 +80,15 @@ 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 - - # 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 + # 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 # Place build results if (! (Test-Path -Path "$BuildSrcPath/${ModuleName}.dll")) diff --git a/global.json b/global.json new file mode 100644 index 0000000..7d7a1c7 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "8.0.401" + } +} diff --git a/nuget.config b/nuget.config index 6548586..5c704bf 100644 --- a/nuget.config +++ b/nuget.config @@ -2,7 +2,7 @@ - + diff --git a/package.config.json b/package.config.json index 0ae6b35..12c3666 100644 --- a/package.config.json +++ b/package.config.json @@ -1,8 +1,8 @@ { "ModuleName": "Microsoft.PowerShell.ThreadJob", + "ProxyModuleName" : "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..7b127d4 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,7 @@ PrivateData = @{ } # End of PrivateData hashtable -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=2113345' +# A URL to get more information about this module +HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=2305300' } diff --git a/src/ThreadJob/ThreadJob.psd1 b/src/ThreadJob/ThreadJob.psd1 new file mode 100644 index 0000000..4bf812c --- /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.1.0' + +# 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 = "The ThreadJob module is now deprecated. Please use the 'Microsoft.PowerShell.ThreadJob' module instead." + +# 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') + +# 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 + +} diff --git a/src/ThreadJob/ThreadJob.psm1 b/src/ThreadJob/ThreadJob.psm1 new file mode 100644 index 0000000..a02343b --- /dev/null +++ b/src/ThreadJob/ThreadJob.psm1 @@ -0,0 +1,5 @@ +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 + +Export-ModuleMember -Alias Start-ThreadJob diff --git a/src/code/Microsoft.PowerShell.ThreadJob.cs b/src/code/Microsoft.PowerShell.ThreadJob.cs index 6bfe042..3d9c7f9 100644 --- a/src/code/Microsoft.PowerShell.ThreadJob.cs +++ b/src/code/Microsoft.PowerShell.ThreadJob.cs @@ -11,9 +11,10 @@ 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.Runtime.InteropServices; namespace Microsoft.PowerShell.ThreadJob { @@ -476,6 +477,14 @@ public sealed class ThreadJob : Job2, IJobDebugger private Debugger _jobDebugger; private string _currentLocationPath; + /// + /// Reflection members for setting job state. + /// + private static readonly MethodInfo s_getSystemLockdownPolicy; + private static readonly MethodInfo s_getFileLockdownPolicy; + private static readonly MethodInfo s_setJobState; + private static readonly object s_enforceValue; + private const string VERBATIM_ARGUMENT = "--%"; private static ThreadJobQueue s_JobQueue; @@ -501,6 +510,23 @@ public JobDefinition ThreadJobDefinition static ThreadJob() { s_JobQueue = new ThreadJobQueue(5); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + Assembly assembly = typeof(PSObject).Assembly; + Type systemPolicy = assembly.GetType("System.Management.Automation.Security.SystemPolicy"); + s_getSystemLockdownPolicy = systemPolicy.GetMethod("GetSystemLockdownPolicy", BindingFlags.Public | BindingFlags.Static); + Type systemEnforcementMode = assembly.GetType("System.Management.Automation.Security.SystemEnforcementMode"); + FieldInfo enforce = systemEnforcementMode.GetField("Enforce"); + s_enforceValue = enforce.GetValue(null); + s_getFileLockdownPolicy = systemPolicy.GetMethod("GetLockdownPolicy", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(string), typeof(SafeHandle) }, null); + } + + s_setJobState = typeof(Job2).GetMethod( + "SetJobState", + BindingFlags.Instance | BindingFlags.NonPublic, + binder: null, + new Type[] { typeof(JobState), typeof(Exception) }, + null); } private ThreadJob() @@ -593,13 +619,16 @@ public ThreadJob( // Determine session language mode for Windows platforms WarningRecord lockdownWarning = null; - if (Environment.OSVersion.Platform.ToString().Equals("Win32NT", StringComparison.OrdinalIgnoreCase)) + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - bool enforceLockdown = (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce); + object lockdownPolicy = s_getSystemLockdownPolicy.Invoke(null, Array.Empty()); + bool enforceLockdown = s_enforceValue.Equals(lockdownPolicy); + 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 = s_getFileLockdownPolicy.Invoke(null, new object[] { _filePath, null }); + enforceLockdown = s_enforceValue.Equals(lockdownPolicy); if (!enforceLockdown && (_initSb != null)) { @@ -639,11 +668,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 +684,7 @@ public ThreadJob( } else { - SetJobState(JobState.Completed, newStateInfo.Reason, disposeRunspace:true); + SetJobState(JobState.Completed, newStateInfo.Reason, disposeRunspace: true); } break; } @@ -1087,7 +1116,10 @@ private ScriptBlock GetScriptBlockFromFile(string filePath, PSCmdlet psCmdlet) private void SetJobState(JobState jobState, Exception reason, bool disposeRunspace = false) { - base.SetJobState(jobState, reason); + // base.SetJobState(jobState, reason); + // Using Reflection here because this method is using a newer SetJobState method overload that takes in jobstate and reason. + s_setJobState.Invoke(this, new object[] { jobState, reason }); + 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 @@ Library Microsoft.PowerShell.ThreadJob Microsoft.PowerShell.ThreadJob - 2.1.1.0 - 2.1.1 - 2.1.1 - net461 + 2.2.0.0 + 2.2.0 + 2.2.0 + netstandard2.0 - - - - False - C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll - - - + - + diff --git a/test/Microsoft.PowerShell.ThreadJob.Tests.ps1 b/test/Microsoft.PowerShell.ThreadJob.Tests.ps1 index c69b598..1b58596 100644 --- a/test/Microsoft.PowerShell.ThreadJob.Tests.ps1 +++ b/test/Microsoft.PowerShell.ThreadJob.Tests.ps1 @@ -13,7 +13,7 @@ function Wait-ForJobRunning $iteration = 10 Do { - Start-Sleep -Milliseconds 100 + Start-Sleep -Milliseconds 1000 } Until (($job.State -match "Running|Completed|Failed") -or (--$iteration -eq 0)) @@ -373,7 +373,7 @@ Describe 'Basic ThreadJob Tests' -Tags 'CI' { It 'ThreadJob jobs should run in the current working directory' { $threadJobCurrentLocation = Start-ThreadJob -ScriptBlock { $pwd } | Wait-Job | Receive-Job - $threadJobCurrentLocation.Path | Should -BeExactly $pwd.Path + $threadJobCurrentLocation.Path.Trim() | Should -BeExactly $pwd.ToString().Trim() } }