diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 51d3b8bdf71..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,46 +0,0 @@ - - -Steps to reproduce ------------------- - -```powershell - -``` - -Expected behavior ------------------ - -```none - -``` - -Actual behavior ---------------- - -```none - -``` - -Environment data ----------------- - - - -```powershell -> $PSVersionTable - -``` diff --git a/.github/ISSUE_TEMPLATE/Bug_Report.md b/.github/ISSUE_TEMPLATE/Bug_Report.md new file mode 100644 index 00000000000..d5809e24f8b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_Report.md @@ -0,0 +1,44 @@ +--- +name: Bug report 🐛 +about: Report errors or unexpected behavior 🤔 + +--- + + +# Steps to reproduce + +```powershell + +``` + +# Expected behavior + +```none + +``` + +# Actual behavior + +```none + +``` + +# Environment data + + + +```none + +``` diff --git a/.github/ISSUE_TEMPLATE/Documentation_Issue.md b/.github/ISSUE_TEMPLATE/Documentation_Issue.md new file mode 100644 index 00000000000..ca425d00a5f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Documentation_Issue.md @@ -0,0 +1,10 @@ +--- +name: Documentation Issue 📚 +about: File issues regarding documentation within the [PowerShell-Docs](https://github.com/powershell/powershell-docs) repository + +--- + +# Documentation Issue + +Please open documentation issues that are not specifically for documentation within the +PowerShell/PowerShell repository in the [PowerShell Docs](https://github.com/powershell/powershell-docs/issues) repository. diff --git a/.github/ISSUE_TEMPLATE/Feature_Request.md b/.github/ISSUE_TEMPLATE/Feature_Request.md new file mode 100644 index 00000000000..cd9e5144f66 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_Request.md @@ -0,0 +1,16 @@ +--- +name: Feature Request/Idea 🚀 +about: Suggest a new feature or improvement (this does not mean you have to implement it) + +--- + +# Summary of the new feature/enhancement + +A clear and concise description of what the problem is that the new feature would solve. +Try formulating it in user story style (if applicable): +'As a user I want X so that Y.' with X being the being the action and Y being the value of the action. + +# Proposed technical implementation details (optional) + +A clear and concise description of what you want to happen. +Consider providing an example PowerShell experience with expected result. diff --git a/.github/ISSUE_TEMPLATE/Support_Question.md b/.github/ISSUE_TEMPLATE/Support_Question.md new file mode 100644 index 00000000000..cebcec50302 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Support_Question.md @@ -0,0 +1,16 @@ +--- +name: Support Question ❓ +about: If you have a question, you can try asking in the PowerShell Slack channel first. If you need official support, refer to the [PowerShell Support Lifecycle](http://aka.ms/pslifecycle) + +--- + +# Support Question + +## Official support + +[PowerShell Support Lifecycle](http://aka.ms/pslifecycle) + +## Community Resources + +[Slack Community Chat](https://powershell.slack.com) - Interactive chat with other PowerShell enthusiasts +[PowerShell.org Forum](https://powershell.org/forums/) - Search or post new general PowerShell usage questions diff --git a/.github/ISSUE_TEMPLATE/Windows_PowerShell.md b/.github/ISSUE_TEMPLATE/Windows_PowerShell.md new file mode 100644 index 00000000000..f69d94a0a4a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Windows_PowerShell.md @@ -0,0 +1,12 @@ +--- +name: Windows PowerShell +about: Windows PowerShell issues/suggestions need to be reported to [UserVoice](https://windowsserver.uservoice.com/forums/301869-powershell) + +--- + +# Windows PowerShell + +For Windows PowerShell 5.1 issues, suggestions, or feature requests please use the following link instead: +Windows PowerShell [UserVoice](https://windowsserver.uservoice.com/forums/301869-powershell) + +This repository is **ONLY** for PowerShell Core 6 issues. diff --git a/.spelling b/.spelling index 799bb58be65..55657c9772e 100644 --- a/.spelling +++ b/.spelling @@ -726,7 +726,10 @@ v6.0. v6.0.0 v6.0.1 v6.0.2 +v6.0.4 +v6.0.5 v6.1.0 +v6.1.1 v6.2.0 validatenotnullorempty versioned @@ -780,14 +783,29 @@ tommymaynard vmsilvamolina fbehrens - CHANGELOG.md +aavdberg +azkarmoulana chucklu +Claustn +CVE-2018-8256 +CVE-2018-8415 +daviddreher2 honour +iGotenz +jeis2497052 +Jocapear +lassehastrup +markwragg +nycjan +paalbra SeeminglyScience -yurko7 -zhenggu -Claustn +StingyJack ThreeFive-O +tobvil uninstallation +vongrippen +yurko7 +zhenggu - docs/debugging/README.md corehost - docs/testing-guidelines/TestRoadmap.md diff --git a/.vsts-ci/install-ps.yml b/.vsts-ci/install-ps.yml new file mode 100644 index 00000000000..04331a01741 --- /dev/null +++ b/.vsts-ci/install-ps.yml @@ -0,0 +1,47 @@ +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + branches: + include: + - master + - release* + paths: + include: + - /tools/install-powershell.sh + - /tools/installpsh-amazonlinux.sh + - /tools/installpsh-debian.sh + - /tools/installpsh-osx.sh + - /tools/installpsh-redhat.sh + - /tools/installpsh-suse.sh +pr: + branches: + include: + - master + - release* + paths: + include: + - /tools/install-powershell.sh + - /tools/installpsh-amazonlinux.sh + - /tools/installpsh-debian.sh + - /tools/installpsh-osx.sh + - /tools/installpsh-redhat.sh + - /tools/installpsh-suse.sh + +variables: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + +resources: +- repo: self + clean: true +phases: +- template: templates/install-ps-phase.yml + parameters: + scriptName: ./tools/install-powershell.sh + jobName: InstallPowerShellUbuntu + pool: Hosted Ubuntu 1604 + +- template: templates/install-ps-phase.yml + parameters: + scriptName: ./tools/install-powershell.sh + jobName: InstallPowerShellMacOS + pool: Hosted macOS diff --git a/.vsts-ci/spelling.yml b/.vsts-ci/spelling.yml index b6868b97f17..c1f1e877667 100644 --- a/.vsts-ci/spelling.yml +++ b/.vsts-ci/spelling.yml @@ -5,16 +5,17 @@ trigger: - master - release* paths: - include: - - '*.md' + exclude: + - /src/* + pr: branches: include: - master - release* paths: - include: - - '*.md' + exclude: + - /src/* resources: - repo: self diff --git a/.vsts-ci/templates/install-ps-phase.yml b/.vsts-ci/templates/install-ps-phase.yml new file mode 100644 index 00000000000..22149640e48 --- /dev/null +++ b/.vsts-ci/templates/install-ps-phase.yml @@ -0,0 +1,29 @@ +parameters: + pool: 'Hosted Ubuntu 1604' + jobName: 'none' + scriptName: '' + +jobs: +- job: ${{ parameters.jobName }} + variables: + scriptName: ${{ parameters.scriptName }} + + pool: + name: ${{ parameters.pool }} + + displayName: ${{ parameters.jobName }} + + steps: + - powershell: | + Get-ChildItem -Path env: + displayName: Capture environment + condition: succeededOrFailed() + + - powershell: Write-Host "##vso[build.updatebuildnumber]$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhmmss"))" + displayName: Set Build Name for Non-PR + condition: ne(variables['Build.Reason'], 'PullRequest') + + - bash: | + $(scriptName) + displayName: Run Script - $(scriptName) + condition: succeededOrFailed() diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f1a619e263..c925e1794fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,121 @@ # Changelog +## v6.2.0-preview.2 - 2018-11-15 + +### Breaking Changes + +- Honor `-OutputFormat` if specified in non-interactive, redirected, encoded command used with `pwsh` (#8115) +- Load assembly from module base path before trying to load from the `GAC` (#8073) +- Remove tilde from Linux preview packages (#8244) +- Move processing of `-WorkingDirectory` before processing of profiles (#8079) + +### Known Issues + +- PowerShell WSMan remoting does not work on Debian 9 due to missing symbolic links. + For more information and a workaround see issue [#7598](https://github.com/PowerShell/PowerShell/issues/7598) + +### Engine Updates and Fixes + +- Enable case-insensitive tab completion for files and folders on case-sensitive filesystem (#8128) +- Experimental feature: Implicit remoting batching performance improvements (#8038) +- Add a path for checking `ZoneInformation` without throwing an exception (#8025) (Thanks @powercode!) +- Fix [CVE-2018-8256](https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8256), + issues with expanding `ZIP` files with relative paths (#8252) +- Fix [CVE-2018-8415](https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8415), + issue logging when the `scriptblock` has a null character (#8253) +- Make `PSVersionInfo.PSVersion` and `PSVersionInfo.PSEdition` public (#8054) (Thanks @KirkMunro!) +- Enable distinct `ModuleAnalysisCache` files for each installation of `pwsh` (#8174) +- Consolidation of all Windows PowerShell work ported to PowerShell Core (#8257) +- Fix incorrect name check when auto-loading required modules (#8218) +- Adding verbose output for experimental implicit remoting batching feature (#8166) +- Add Type Inference for `$_ / $PSItem in catch{ }` blocks (#8020) (Thanks @vexx32!) +- Fix static method invocation type inference (#8018) (Thanks @SeeminglyScience!) + +### General Cmdlet Updates and Fixes + +- Reduce allocations in `Get-Content` cmdlet (#8103) (Thanks @iSazonov!) +- Enable `Set-Location -LiteralPath` to work with folders named `-` and `+` (#8089) +- Enable `Add-Content` to share read access with other tools while writing content (#8091) +- Add new `Offset` and `Count` parameters to `Format-Hex` and refactor the cmdlet (#7877) (Thanks @iSazonov!) +- Add `-Name`, `-NoUserOverrides` and `-ListAvailable` parameters to `Get-Culture` cmdlet (#7702) (Thanks @iSazonov!) +- Allow dynamic parameter to be returned even if path does not match any provider (#7957) +- Style fixes in `Format-Hex` (#8083) (Thanks @iSazonov!) +- Fix logic to rely on PowerShell major and minor version instead of build number to determine whether to output `formatdata` (#8063) +- Fix `Rename-Item -Path` with wildcard `char` (#7398) (Thanks @kwkam!) +- When using `Start-Transcript` and file exists, empty file rather than deleting (#8131) (Thanks @paalbra!) +- Error message enhancement for `Clear-Content` cmdlet when targeting a directory (#8134) (Thanks @kvprasoon!) +- Make `Select-String` faster by not doing extra work (#7673) (Thanks @powercode!) +- Remove `ShouldProcess` from `Format-Hex` (#8178) + +### Code Cleanup + +- Remove clone of command-line arguments array (#7910) (Thanks @iSazonov!) +- Use `DefaultPathSeparator` `char` instead of `DefaultPathSeparatorString` (#8082) (Thanks @iSazonov!) +- Replace `StringComparision.CurrentCulture` with `StringComparision.Ordinal` (#8068) (Thanks @iSazonov!) +- Fix typo in `-icontains` description from `incase sensitive` to `case insensitive` (#7840) (Thanks @StingyJack!) +- Refactor module version/`GUID` comparison logic (#7125) + +### Tools + +- Update `installpsh-amazonlinux.sh` for container specific issues (#7907) (Thanks @DarwinJS!) +- Update the `codeowners` file (#8017) + +### Tests + +- Filter the `TestPackage` artifact upload by name to avoid other `ZIP` files being uploaded (#8116) +- Adding `fxdependent` PowerShell package tests (#7830) +- Fix Windows Feature tests running in Azure DevOps (#8220) +- Create `$PROFILE` if it does not exist for `-WorkingDirectory` processing test (#8152) +- Add test coverage for additional `Get-Module` parameters (#8137) (Thanks @KevinMarquette!) +- Fix conflict with `Get-AdlStoreChildItem` from `az` module in tab completion tests (#8167) +- Fix static secret in code (#8186) + +### Build and Packaging Improvements + +- Bump `xunit.runner.visualstudio` from `2.4.0` to `2.4.1` (#8139) +- Bump `xunit` from `2.4.0` to `2.4.1` (#8140) +- Bump `Microsoft.ApplicationInsights` from `2.8.0` to `2.8.1` (#8104) +- Bump `NJsonSchema` from `9.11.1` to `9.12.1` (#8183, #8248) +- Fix `Start-PSBuild -Output` (#7504) (Thanks @kwkam!) +- Adding `YML` for Linux builds (#8168) +- Publish test package at `AGENT_WORKFOLDER` if `TEMP` is not available (#8108) +- Fix `psmodulerestore` path when built in Visual Studio Code (#8075) +- Use approved verb instead of `Generate-CrossGenAssembly` (#8151) (Thanks @kvprasoon!) +- Add path filters to CI `YAML` (#8222) +- Update `SignType` in `signing.xml` (#8223) +- Update metadata for `6.0.5` and `6.1.1` releases (#8259) +- Port changes to allow Azure DevOps NuGet feeds for Mac build (Internal 5818) +- Update version for dependencies (Internal 5822) +- Add code to use private NuGet feeds when running in internal CI system (#8187) +- Add title to `Open Here` window for `MSI` installer (#8164) +- Remove build and documentation references to `git` submodules (#8177) (Thanks @andschwa!) +- Add function to create a new `nuget.config` file (#8170) +- Update macOS release build to create the `nuget.config` (#8185) +- Workaround for accessing Azure Artifacts (#8188) +- Fix script path for `PowerShellPackageVsts.ps1` (#8189) +- `Microsoft.PowerShell.Native` now has `MUSL` binaries for Alpine. + +### Documentation and Help Content + +- Fix grammar in `README.md` (#8059) (Thanks @daviddreher2!) +- Update `powershell-beginners-guide.md` to add alias for `Clear-Host` (#7912) (Thanks @aavdberg!) +- Add Microsoft Docs link to FAQ (#8133) (Thanks @vongrippen!) +- Added updated photo of Visual Studio Code due to new version of Code (#8084) (Thanks @lassehastrup!) +- Update `license.rtf` to only have major version (#8127) +- Updated Pester Syntax in Writing Tests Guide (#8039) (Thanks @markwragg!) +- Remove duplicate parts from license file (#8143) (Thanks @azkarmoulana!) +- Fix spellings in `CHANGELOG.md` (#8062) +- Update license RTF to 6.2 (#8065) +- Combine notes about `ITuple` changes in Change Log (#8077) (Thanks @Jocapear!) +- Correct typos in `powershell-beginners-guide.md` (#8088) (Thanks @nycjan!) +- Added `Learn Windows PowerShell in a Month of Lunches` as recommended reading (#8067) (Thanks @tobvil!) +- Update `README.md` for `v6.1.1` (#8255) +- Fix some typos (#8206) (Thanks @jeis2497052!) +- Promote `HTTPS` (#8160) (Thanks @RDIL!) +- Simple grammatical correction in `README.md` file (#7978) (Thanks @iGotenz!) +- Update URLs to use `HTTPS` instead of `HTTP` in the documentation (#8165) (Thanks @RDIL!) +- Remove #7633 from `v6.2.0-preview.1` `CHANGELOG.md` updates. (#8101) (Thanks @stknohg!) + ## v6.2.0-preview.1 - 2018-10-18 ### Breaking Changes @@ -137,6 +253,55 @@ - Update `CONTRIBUTION.md` about adding an empty line after the copyright header (#7706) (Thanks @iSazonov!) - Update docs about .NET Core version `2.0` to be about version `2.x` (#7467) (Thanks @bergmeister!) +## v6.1.1 - 2018-11-13 + +### Engine Updates and Fixes + +- Fix issue with logging the null character in `ScriptBlock` logging (Internal 5607) +- Consolidation of all Windows PowerShell work ported to 6.1 (Internal 5233) + +### General Cmdlet Updates and Fixes + +- Use `ZipFile` and `ExtractToDirectory` APIs to extract zip file (Internal 5608) + +## v6.0.5 - 2018-11-13 + +### Engine updates and fixes + +- Fix issue with logging the null character in `ScriptBlock` logging (Internal 5605) + +### General cmdlet updates and fixes + +- Use `ZipFile` and `ExtractToDirectory` APIs to extract zip file (Internal 4802) + +### Build and Packaging Improvements + +- Update `SignType` in `signing.xml` (Internal 5721) +- Port changes to pull PowerShell Gallery modules from Modules `csproj` (Internal 5713) +- Port macOS Release build changes changes from GitHub (#8189, #8188, #8185) +- Fix script path for `PowerShellPackageVsts.ps1` (#8189) +- Workaround for accessing `AzDevOps` Artifacts (#8188) +- Bump various packages to latest patch version (Internal 5675) +- Update PowerShell SDK NuGet various metadata description (Internal 4527, 4510, 4505) + +## v6.0.4 - 2018-08-10 + +### Build and Packaging Improvements + +- Update the Archive module version (Internal 5671) +- Update to .NET Core `2.1.5` with SDK `2.1.403` (#7936) (Thanks @iSazonov!) +- Disable package major upgrade tests for release branch (Internal 5209) +- Bump versions for dependencies (Internal 5612) +- Port changes to allow `AzDevOps` NuGet feeds for macOS build (Internal 5716) +- Port macOS changes from GitHub (#8189, #8188, #8185) +- Add function to create a new `nuget.config` file (#8170) +- Updated `wxs` file to match published packages (Internal 5660) + +### Tests + +- Change API to match cmdlet which is more reliable in `AzDevOps` Pipelines Windows (#8003) +- Fix conflict with `Get-AdlStoreChildItem` from `az` module in tab completion tests (#8167) + ## v6.1.0 - 2018-09-13 ### Engine Updates and Fixes diff --git a/README.md b/README.md index a576cc0128e..0676efdade3 100644 --- a/README.md +++ b/README.md @@ -77,22 +77,22 @@ You can also download the PowerShell binary archives for Windows, macOS and Linu [rl-raspbian]: https://github.com/PowerShell/PowerShell/releases/download/v6.1.1/powershell-6.1.1-linux-arm32.tar.gz [rl-snap]: https://snapcraft.io/powershell -[pv-windows-64]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/PowerShell-6.2.0-preview.1-win-x64.msi -[pv-windows-86]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/PowerShell-6.2.0-preview.1-win-x86.msi -[pv-ubuntu18]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell_6.2.0-preview.1-1.ubuntu.18.04_amd64.deb -[pv-ubuntu16]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell_6.2.0-preview.1-1.ubuntu.16.04_amd64.deb -[pv-ubuntu14]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell_6.2.0-preview.1-1.ubuntu.14.04_amd64.deb -[pv-debian8]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell_6.2.0-preview.1-1.debian.8_amd64.deb -[pv-debian9]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell_6.2.0-preview.1-1.debian.9_amd64.deb -[pv-centos]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell-6.2.0-preview.1-1.rhel.7.x86_64.rpm -[pv-macos]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell-6.2.0-preview.1-osx-x64.pkg -[pv-winarm]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/PowerShell-6.2.0-preview.1-win-arm32.zip -[pv-winarm64]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/PowerShell-6.2.0-preview.1-win-arm64.zip -[pv-winx86-zip]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/PowerShell-6.2.0-preview.1-win-x86.zip -[pv-winx64-zip]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/PowerShell-6.2.0-preview.1-win-x64.zip -[pv-macos-tar]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell-6.2.0-preview.1-osx-x64.tar.gz -[pv-linux-tar]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell-6.2.0-preview.1-linux-x64.tar.gz -[pv-raspbian]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.1/powershell-6.2.0-preview.1-linux-arm32.tar.gz +[pv-windows-64]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/PowerShell-6.2.0-preview.2-win-x64.msi +[pv-windows-86]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/PowerShell-6.2.0-preview.2-win-x86.msi +[pv-ubuntu18]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell_6.2.0-preview.2-1.ubuntu.18.04_amd64.deb +[pv-ubuntu16]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell_6.2.0-preview.2-1.ubuntu.16.04_amd64.deb +[pv-ubuntu14]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell_6.2.0-preview.2-1.ubuntu.14.04_amd64.deb +[pv-debian8]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell_6.2.0-preview.2-1.debian.8_amd64.deb +[pv-debian9]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell_6.2.0-preview.2-1.debian.9_amd64.deb +[pv-centos]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell-6.2.0-preview.2-1.rhel.7.x86_64.rpm +[pv-macos]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell-6.2.0-preview.2-osx-x64.pkg +[pv-winarm]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/PowerShell-6.2.0-preview.2-win-arm32.zip +[pv-winarm64]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/PowerShell-6.2.0-preview.2-win-arm64.zip +[pv-winx86-zip]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/PowerShell-6.2.0-preview.2-win-x86.zip +[pv-winx64-zip]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/PowerShell-6.2.0-preview.2-win-x64.zip +[pv-macos-tar]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell-6.2.0-preview.2-osx-x64.tar.gz +[pv-linux-tar]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell-6.2.0-preview.2-linux-x64.tar.gz +[pv-raspbian]: https://github.com/PowerShell/PowerShell/releases/download/v6.2.0-preview.2/powershell-6.2.0-preview.2-linux-arm32.tar.gz [pv-snap]: https://snapcraft.io/powershell-preview [in-windows]: https://docs.microsoft.com/powershell/scripting/setup/installing-powershell-core-on-windows?view=powershell-6 diff --git a/assets/Product.wxs b/assets/Product.wxs index 11e91a87574..70aec2abba8 100644 --- a/assets/Product.wxs +++ b/assets/Product.wxs @@ -207,7 +207,7 @@ - + @@ -215,7 +215,7 @@ - + diff --git a/build.psm1 b/build.psm1 index 263ac1232a2..f1c01fc5d09 100644 --- a/build.psm1 +++ b/build.psm1 @@ -148,6 +148,7 @@ function Get-EnvironmentInformation $environment += @{'IsOpenSUSE42.1' = $Environment.IsOpenSUSE -and $LinuxInfo.VERSION_ID -match '42.1'} $environment += @{'IsRedHatFamily' = $Environment.IsCentOS -or $Environment.IsFedora -or $Environment.IsRedHat} $environment += @{'IsSUSEFamily' = $Environment.IsSLES -or $Environment.IsOpenSUSE} + $environment += @{'IsAlpine' = $LinuxInfo.ID -match 'alpine'} # Workaround for temporary LD_LIBRARY_PATH hack for Fedora 24 # https://github.com/PowerShell/PowerShell/issues/2511 @@ -160,7 +161,8 @@ function Get-EnvironmentInformation $environment.IsDebian -or $environment.IsUbuntu -or $environment.IsRedHatFamily -or - $environment.IsSUSEFamily) + $environment.IsSUSEFamily -or + $environment.IsAlpine) ) { throw "The current OS : $($LinuxInfo.ID) is not supported for building PowerShell." } @@ -224,7 +226,7 @@ function Start-PSBuild { # If this parameter is not provided it will get determined automatically. [ValidateSet("fxdependent", "linux-arm", - "linux-musl-x64", + "alpine-x64", "linux-x64", "osx-x64", "win-arm", @@ -563,7 +565,7 @@ function Restore-PSPester [ValidateNotNullOrEmpty()] [string] $Destination = ([IO.Path]::Combine((Split-Path (Get-PSOptions -DefaultToNew).Output), "Modules")) ) - Save-Module -Name Pester -Path $Destination -Repository PSGallery -RequiredVersion "4.4.2" + Save-Module -Name Pester -Path $Destination -Repository PSGallery -RequiredVersion "4.4.1" } function Compress-TestContent { @@ -594,7 +596,7 @@ function New-PSOptions { [ValidateSet("", "fxdependent", "linux-arm", - "linux-musl-x64", + "alpine-x64", "linux-x64", "osx-x64", "win-arm", @@ -1369,7 +1371,6 @@ function Test-PSPesterResults function Start-PSxUnit { [CmdletBinding()]param( - [string] $SequentialTestResultsFile = "SequentialXUnitResults.xml", [string] $ParallelTestResultsFile = "ParallelXUnitResults.xml" ) @@ -1382,10 +1383,9 @@ function Start-PSxUnit { } try { - Push-Location $PSScriptRoot/test/csharp + Push-Location $PSScriptRoot/test/xUnit # Path manipulation to obtain test project output directory - dotnet restore if(-not $Environment.IsWindows) { @@ -1417,30 +1417,22 @@ function Start-PSxUnit { } } - # Run sequential tests first, and then run the tests that can execute in parallel - if (Test-Path $SequentialTestResultsFile) { - Remove-Item $SequentialTestResultsFile -Force -ErrorAction SilentlyContinue - } - dotnet test --configuration $Options.configuration --filter FullyQualifiedName~PSTests.Sequential -p:ParallelizeTestCollections=false --test-adapter-path:. "--logger:xunit;LogFilePath=$SequentialTestResultsFile" - Publish-TestResults -Path $SequentialTestResultsFile -Type 'XUnit' -Title 'Xunit Sequential' + dotnet build --configuration $Options.configuration - $extraParams = @() + if (Test-Path $ParallelTestResultsFile) { + Remove-Item $ParallelTestResultsFile -Force -ErrorAction SilentlyContinue + } # we are having intermittent issues on macOS with these tests failing. # VSTS has suggested forcing them to be sequential if($env:TF_BUILD -and $IsMacOS) { Write-Log 'Forcing parallel xunit tests to run sequentially.' - $extraParams += @( - '-parallel' - 'none' - ) + dotnet test -p:ParallelizeTestCollections=false --configuration $Options.configuration --no-restore --no-build --test-adapter-path:. "--logger:xunit;LogFilePath=$ParallelTestResultsFile" + } else { + dotnet test --configuration $Options.configuration --no-restore --no-build --test-adapter-path:. "--logger:xunit;LogFilePath=$ParallelTestResultsFile" } - if (Test-Path $ParallelTestResultsFile) { - Remove-Item $ParallelTestResultsFile -Force -ErrorAction SilentlyContinue - } - dotnet test --configuration $Options.configuration --filter FullyQualifiedName~PSTests.Parallel --no-build --test-adapter-path:. "--logger:xunit;LogFilePath=$ParallelTestResultsFile" Publish-TestResults -Path $ParallelTestResultsFile -Type 'XUnit' -Title 'Xunit Parallel' } finally { @@ -1633,6 +1625,12 @@ function Start-PSBootstrap { # Install patched version of curl Start-NativeExecution { brew install curl --with-openssl --with-gssapi } -IgnoreExitcode + } elseif ($Environment.IsAlpine) { + $Deps += 'libunwind', 'libcurl', 'bash', 'cmake', 'clang', 'build-base', 'git', 'curl' + + Start-NativeExecution { + Invoke-Expression "apk add $Deps" + } } # Install [fpm](https://github.com/jordansissel/fpm) and [ronn](https://github.com/rtomayko/ronn) @@ -2054,7 +2052,7 @@ function Start-CrossGen { [Parameter(Mandatory=$true)] [ValidateSet("linux-arm", - "linux-musl-x64", + "alpine-x64", "linux-x64", "osx-x64", "win-arm", @@ -2975,6 +2973,31 @@ $script:RESX_TEMPLATE = @' '@ +function Get-UniquePackageFolderName { + param( + [Parameter(Mandatory)] $Root + ) + + $packagePath = Join-Path $Root 'TestPackage' + + $triesLeft = 10 + + while(Test-Path $packagePath) { + $suffix = Get-Random + + # Not using Guid to avoid maxpath problems as in example below. + # Example: 'TestPackage-ba0ae1db-8512-46c5-8b6c-1862d33a2d63\test\powershell\Modules\Microsoft.PowerShell.Security\TestData\CatalogTestData\UserConfigProv\DSCResources\UserConfigProviderModVersion1\UserConfigProviderModVersion1.schema.mof' + $packagePath = Join-Path $Root "TestPackage_$suffix" + $triesLeft-- + + if ($triesLeft -le 0) { + throw "Could find unique folder name for package path" + } + } + + $packagePath +} + function New-TestPackage { [CmdletBinding()] @@ -2995,13 +3018,14 @@ function New-TestPackage $rootFolder = $env:TEMP + # In some build agents, typically macOS on AzDevOps, $env:TEMP might not be set. if (-not $rootFolder -and $env:TF_BUILD) { $rootFolder = $env:AGENT_WORKFOLDER } Write-Verbose -Verbose "RootFolder: $rootFolder" + $packageRoot = Get-UniquePackageFolderName -Root $rootFolder - $packageRoot = Join-Path $rootFolder ('TestPackage-' + (new-guid)) $null = New-Item -ItemType Directory -Path $packageRoot -Force $packagePath = Join-Path $Destination "TestPackage.zip" Write-Verbose -Verbose "PackagePath: $packagePath" diff --git a/docker/community/amazonlinux/Dockerfile b/docker/community/amazonlinux/Dockerfile deleted file mode 100644 index 4045d8352db..00000000000 --- a/docker/community/amazonlinux/Dockerfile +++ /dev/null @@ -1,55 +0,0 @@ -# Docker image file that describes an Amazon Linux image with PowerShell installed from PowerShell Release - -FROM amazonlinux:latest - -ARG POWERSHELL_VERSION=6.0.2 -ARG POWERSHELL_PACKAGE=powershell-6.0.2-linux-x64.tar.gz -ARG IMAGE_NAME=microsoft/powershell:amazonlinux - -LABEL maintainer="PowerShell Team " \ - readme.md="https://github.com/PowerShell/PowerShell/blob/master/docker/README.md" \ - description="This Dockerfile will install the latest release of PS." \ - org.label-schema.usage="https://github.com/PowerShell/PowerShell/tree/master/docker#run-the-docker-image-you-built" \ - org.label-schema.url="https://github.com/PowerShell/PowerShell/blob/master/docker/README.md" \ - org.label-schema.vcs-url="https://github.com/PowerShell/PowerShell" \ - org.label-schema.name="powershell" \ - org.label-schema.vendor="PowerShell" \ - org.label-schema.version=${POWERSHELL_VERSION} \ - org.label-schema.schema-version="1.0" \ - org.label-schema.docker.cmd="docker run ${IMAGE_NAME} pwsh -c '$psversiontable'" \ - org.label-schema.docker.cmd.devel="docker run ${IMAGE_NAME}" \ - org.label-schema.docker.cmd.test="docker run ${IMAGE_NAME} pwsh -c Invoke-Pester" \ - org.label-schema.docker.cmd.help="docker run ${IMAGE_NAME} pwsh -c Get-Help" - -# TODO: addd LABEL org.label-schema.vcs-ref=${VCS_REF} - -# Setup the locale -ENV LANG en_US.UTF-8 -ENV LC_ALL $LANG -RUN localedef --charmap=UTF-8 --inputfile=en_US $LANG - -# Install dependencies and clean up -RUN yum install -y \ - curl \ - libunwind \ - libicu \ - libcurl \ - openssl \ - libuuid.x86_64 \ - && yum clean all - -# Get the InstallTarballPackage.sh script -ADD https://raw.githubusercontent.com/PowerShell/PowerShell/master/docker/InstallTarballPackage.sh /InstallTarballPackage.sh - -# Add execution permission -RUN chmod +x /InstallTarballPackage.sh - -# Install powershell from tarball package -RUN /InstallTarballPackage.sh $POWERSHELL_VERSION $POWERSHELL_PACKAGE - -# Remove the script -RUN rm -f /InstallTarballPackage.sh - -# Use PowerShell as the default shell -# Use array to avoid Docker prepending /bin/sh -c -CMD [ "pwsh" ] diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs index eeb950a6394..25ea2930559 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs @@ -1,11 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Diagnostics; -using System.Reflection; using System.Runtime.CompilerServices; [assembly:InternalsVisibleTo("Microsoft.Windows.DSC.CoreConfProviders,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("Microsoft.Management.Infrastructure.CimCmdlets.Test,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -//This is equal to Debuggable(true,true) which enables IsJITTracking and Disable Optimization. CoreCLR does not have constructor Debuggable(true,true) -[assembly: Debuggable(DebuggableAttribute.DebuggingModes.DisableOptimizations)] diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs index 205b13fd191..ba18db34943 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs @@ -5,16 +5,10 @@ using System.Collections; using System.Diagnostics; using System.Globalization; +using System.Reflection; +using System.Resources; using System.Runtime.InteropServices; using System.Text; -using System.Resources; -using System.Reflection; - -#if CORECLR -using System.ComponentModel; -#else -using System.Threading; -#endif namespace Microsoft.PowerShell.Commands.Diagnostics.Common { @@ -38,7 +32,6 @@ public static string StringArrayToString(IEnumerable input) return ret; } -#if CORECLR private const string LibraryLoadDllName = "api-ms-win-core-libraryloader-l1-2-0.dll"; private const string LocalizationDllName = "api-ms-win-core-localization-l1-2-1.dll"; private const string SysInfoDllName = "api-ms-win-core-sysinfo-l1-2-1.dll"; @@ -62,10 +55,6 @@ internal struct OSVERSIONINFOEX [DllImport(SysInfoDllName, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool GetVersionEx(ref OSVERSIONINFOEX osVerEx); -#else - private const string LibraryLoadDllName = "kernel32.dll"; - private const string LocalizationDllName = "kernel32.dll"; -#endif private const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100; private const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs index eedbeb4855d..2d0f1976684 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs @@ -55,18 +55,12 @@ internal virtual CmdletProviderContext CmdletProviderContext return coreCommandContext; } - } // CmdletProviderContext + } internal virtual SwitchParameter SuppressWildcardExpansion { - get - { - return _suppressWildcardExpansion; - } - set - { - _suppressWildcardExpansion = value; - } + get => _suppressWildcardExpansion; + set => _suppressWildcardExpansion = value; } private bool _suppressWildcardExpansion; @@ -82,10 +76,7 @@ internal virtual SwitchParameter SuppressWildcardExpansion /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - internal virtual object GetDynamicParameters(CmdletProviderContext context) - { - return null; - } + internal virtual object GetDynamicParameters(CmdletProviderContext context) => null; /// /// Called by the base implementation that checks the SupportShouldProcess provider @@ -94,13 +85,7 @@ internal virtual object GetDynamicParameters(CmdletProviderContext context) /// if the provider supports ShouldProcess /// /// - protected virtual bool ProviderSupportsShouldProcess - { - get - { - return true; - } - } // ProviderSupportsShouldProcess + protected virtual bool ProviderSupportsShouldProcess => true; /// /// A helper for derived classes to call to determine if the paths specified @@ -152,13 +137,7 @@ protected bool DoesProviderSupportShouldProcess(string[] paths) /// The dynamic parameters which have already been retrieved from the provider /// and bound by the command processor. /// - protected internal object RetrievedDynamicParameters - { - get - { - return _dynamicParameters; - } // get - } // RetrievedDynamicParameters + protected internal object RetrievedDynamicParameters => _dynamicParameters; /// /// The dynamic parameters for the command. They are retrieved using the /// GetDynamicParameters virtual method. @@ -180,7 +159,8 @@ protected override void StopProcessing() { stopContext.StopProcessing(); } - } // StopProcessing + } + internal Collection stopContextCollection = new Collection(); @@ -240,15 +220,10 @@ public virtual string[] Exclude /// public virtual SwitchParameter Force { - get - { - return _force; - } - set - { - _force = value; - } - } // Force + get => _force; + set => _force = value; + } + private bool _force; /// @@ -279,21 +254,15 @@ public object GetDynamicParameters() } return _dynamicParameters; - } // GetDynamicParameters + } /// /// Determines if the cmdlet and CmdletProvider supports ShouldProcess /// - public bool SupportsShouldProcess - { - get - { - return ProviderSupportsShouldProcess; - } - } // SupportsShouldProcess + public bool SupportsShouldProcess => ProviderSupportsShouldProcess; #endregion Public members - } // class CoreCommandBase + } #endregion CoreCommandBase @@ -345,10 +314,10 @@ internal override CmdletProviderContext CmdletProviderContext return coreCommandContext; } - } // CmdletProviderContext + } #endregion Protected members - } // CoreCommandWithCredentialsBase + } #endregion CoreCommandWithCredentialsBase @@ -361,28 +330,13 @@ internal override CmdletProviderContext CmdletProviderContext /// /// /// - [Cmdlet(VerbsCommon.Get, "Location", DefaultParameterSetName = "Location", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113321")] - [OutputType(typeof(PathInfo), ParameterSetName = new string[] { "locationSet" })] - [OutputType(typeof(PathInfoStack), ParameterSetName = new string[] { "Stack" })] + [Cmdlet(VerbsCommon.Get, "Location", DefaultParameterSetName = LocationParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113321")] + [OutputType(typeof(PathInfo), ParameterSetName = new string[] { LocationParameterSet })] + [OutputType(typeof(PathInfoStack), ParameterSetName = new string[] { StackParameterSet })] public class GetLocationCommand : DriveMatchingCoreCommandBase { - /// - /// The string declaration for the Location parameter set in this command. - /// - /// - /// The "Location" parameter set includes the following parameters: - /// -location - /// - private const string locationSet = "Location"; - - /// - /// The string declaration for the Stack parameter set in this command. - /// - /// - /// The "Stack" parameter set includes the following parameters: - /// -stack - /// - private const string stackSet = "Stack"; + private const string LocationParameterSet = "Location"; + private const string StackParameterSet = "Stack"; #region Command parameters @@ -391,17 +345,17 @@ public class GetLocationCommand : DriveMatchingCoreCommandBase /// /// Gets or sets the provider from which to get the current location. /// - [Parameter(ParameterSetName = locationSet, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = LocationParameterSet, ValueFromPipelineByPropertyName = true)] public string[] PSProvider { - get { return _provider; } - set { _provider = value ?? Utils.EmptyArray(); } + get => _provider; + set => _provider = value ?? Utils.EmptyArray(); } /// /// Gets or sets the drive from which to get the current location. /// - [Parameter(ParameterSetName = locationSet, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = LocationParameterSet, ValueFromPipelineByPropertyName = true)] public string[] PSDrive { get; set; } #endregion Location parameter set parameters @@ -413,17 +367,11 @@ public string[] PSProvider /// to disambiguate parameter sets /// /// - [Parameter(ParameterSetName = stackSet)] + [Parameter(ParameterSetName = StackParameterSet)] public SwitchParameter Stack { - get - { - return _stackSwitch; - } - set - { - _stackSwitch = value; - } + get => _stackSwitch; + set => _stackSwitch = value; } private bool _stackSwitch; @@ -431,19 +379,13 @@ public SwitchParameter Stack /// Gets or sets the stack ID for the location stack that will /// be retrieved. /// - [Parameter(ParameterSetName = stackSet, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = StackParameterSet, ValueFromPipelineByPropertyName = true)] public string[] StackName { - get - { - return _stackNames; - } // get + get => _stackNames; - set - { - _stackNames = value; - } // set - } // StackName + set => _stackNames = value; + } #endregion Stack parameter set parameters @@ -486,7 +428,7 @@ protected override void ProcessRecord() // want a case sensitive comparison in the current culture. switch (ParameterSetName) { - case locationSet: + case LocationParameterSet: PathInfo result = null; if (PSDrive != null && PSDrive.Length > 0) @@ -633,7 +575,7 @@ protected override void ProcessRecord() } break; - case stackSet: + case StackParameterSet: if (_stackNames != null) { foreach (string stackName in _stackNames) @@ -672,11 +614,11 @@ protected override void ProcessRecord() default: Dbg.Diagnostics.Assert(false, String.Format(System.Globalization.CultureInfo.InvariantCulture, "One of the predefined parameter sets should have been specified, instead we got: {0}", ParameterSetName)); break; - } // case (ParameterSetName) - } // ProcessRecord + } + } #endregion command code - } // class GetLocationCommand + } #endregion GetLocationCommand #region SetLocationCommand @@ -685,62 +627,41 @@ protected override void ProcessRecord() /// The core command for setting/changing location. /// This is the equivalent of cd command. /// - [Cmdlet(VerbsCommon.Set, "Location", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113397")] + [Cmdlet(VerbsCommon.Set, "Location", DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113397")] [OutputType(typeof(PathInfo), typeof(PathInfoStack))] public class SetLocationCommand : CoreCommandBase { #region Command parameters - - /// - /// The string declaration for the Location parameter set in this command. - /// - private const string pathSet = "Path"; - - /// - /// The string declaration for the literal location parameter set in this command. - /// - private const string literalPathSet = "LiteralPath"; - - /// - /// The string declaration for the Stack parameter set in this command. - /// - private const string stackSet = "Stack"; + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + private const string StackParameterSet = "Stack"; /// /// Gets or sets the path property /// - [Parameter(Position = 0, ParameterSetName = pathSet, + [Parameter(Position = 0, ParameterSetName = PathParameterSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string Path { - get - { - return _path; - } - set - { - _path = value; - } + get => _path; + set => _path = value; } /// /// Gets or sets the path path property, when bound from the pipeline. /// - [Parameter(ParameterSetName = literalPathSet, + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string LiteralPath { - get - { - return _path; - } + get => _path; set { _path = value; base.SuppressWildcardExpansion = true; } - } // PSPath + } /// /// Gets or sets the parameter -passThru which states output from @@ -749,8 +670,8 @@ public string LiteralPath [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// @@ -758,7 +679,7 @@ public SwitchParameter PassThru /// to use for the push. If the parameter is missing or empty the default /// location stack is used. /// - [Parameter(ParameterSetName = stackSet, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = StackParameterSet, ValueFromPipelineByPropertyName = true)] public string StackName { get; set; } #endregion Command parameters @@ -790,8 +711,8 @@ protected override void ProcessRecord() switch (ParameterSetName) { - case pathSet: - case literalPathSet: + case PathParameterSet: + case LiteralPathParameterSet: try { // Change the current working directory @@ -801,7 +722,7 @@ protected override void ProcessRecord() Path = SessionState.Internal.GetSingleProvider(Commands.FileSystemProvider.ProviderName).Home; } - result = SessionState.Path.SetLocation(Path, CmdletProviderContext, ParameterSetName == literalPathSet); + result = SessionState.Path.SetLocation(Path, CmdletProviderContext, ParameterSetName == LiteralPathParameterSet); } catch (PSNotSupportedException notSupported) { @@ -840,7 +761,7 @@ protected override void ProcessRecord() } break; - case stackSet: + case StackParameterSet: try { @@ -868,10 +789,10 @@ protected override void ProcessRecord() { WriteObject(result); } - } // ProcessRecord + } #endregion Command code - } // SetLocationCommand + } #endregion SetLocationCommand @@ -881,47 +802,39 @@ protected override void ProcessRecord() /// The core command for setting/changing location and pushing it onto a location stack. /// This is the equivalent of the pushd command. /// - [Cmdlet(VerbsCommon.Push, "Location", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113370")] + [Cmdlet(VerbsCommon.Push, "Location", DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113370")] public class PushLocationCommand : CoreCommandBase { #region Command parameters + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; /// /// Gets or sets the path property /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string Path { - get - { - return _path; - } - set - { - _path = value; - } + get => _path; + set => _path = value; } /// /// Gets or sets the literal path parameter to the command /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string LiteralPath { - get - { - return _path; - } // get - + get => _path; set { base.SuppressWildcardExpansion = true; _path = value; - } // set - } // LiteralPath + } + } /// /// Gets or sets the parameter -passThru which states output from @@ -930,15 +843,9 @@ public string LiteralPath [Parameter] public SwitchParameter PassThru { - get - { - return _passThrough; - } // get - set - { - _passThrough = value; - } //set - } // PassThru + get => _passThrough; + set => _passThrough = value; + } /// /// Gets or sets the StackName parameter which determines which location stack @@ -948,15 +855,9 @@ public SwitchParameter PassThru [Parameter(ValueFromPipelineByPropertyName = true)] public string StackName { - get - { - return _stackName; - } // get - set - { - _stackName = value; - } //set - } // StackName + get => _stackName; + set => _stackName = value; + } #endregion Command parameters @@ -1045,11 +946,11 @@ protected override void ProcessRecord() argException)); return; } - } // Path != null - } // ProcessRecord + } + } #endregion Command code - } // PushLocationCommand + } #endregion PushLocationCommand @@ -1071,15 +972,9 @@ public class PopLocationCommand : CoreCommandBase [Parameter] public SwitchParameter PassThru { - get - { - return _passThrough; - } // get - set - { - _passThrough = value; - } //set - } // PassThru + get => _passThrough; + set => _passThrough = value; + } /// /// Gets or sets the StackName parameter which determines which location stack @@ -1089,15 +984,9 @@ public SwitchParameter PassThru [Parameter(ValueFromPipelineByPropertyName = true)] public string StackName { - get - { - return _stackName; - } // get - set - { - _stackName = value; - } //set - } // StackName + get => _stackName; + set => _stackName = value; + } #endregion Command parameters @@ -1167,10 +1056,10 @@ protected override void ProcessRecord() itemNotFound)); return; } - } // ProcessRecord + } #endregion Command code - } // PopLocationCommand + } #endregion PopLocationCommand @@ -1192,16 +1081,8 @@ public class NewPSDriveCommand : CoreCommandWithCredentialsBase [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string Name { - get { return _name; } - set - { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - _name = value; - } + get => _name; + set => _name = value ?? throw PSTraceSource.NewArgumentNullException(nameof(value)); } /// @@ -1210,16 +1091,8 @@ public string Name [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string PSProvider { - get { return _provider; } - set - { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - _provider = value; - } + get => _provider; + set => _provider = value ?? throw PSTraceSource.NewArgumentNullException(nameof(value)); } /// @@ -1230,16 +1103,8 @@ public string PSProvider [AllowEmptyString] public string Root { - get { return _root; } - set - { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - _root = value; - } + get => _root; + set => _root = value ?? throw PSTraceSource.NewArgumentNullException(nameof(value)); } /// @@ -1248,16 +1113,8 @@ public string Root [Parameter(ValueFromPipelineByPropertyName = true)] public string Description { - get { return _description; } - set - { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - _description = value; - } + get => _description; + set => _description = value ?? throw PSTraceSource.NewArgumentNullException(nameof(value)); } /// @@ -1274,8 +1131,8 @@ public string Description [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter Persist { - get { return _persist; } - set { _persist = value; } + get => _persist; + set => _persist = value; } private bool _persist = false; @@ -1298,10 +1155,8 @@ internal override object GetDynamicParameters(CmdletProviderContext context) /// new-psdrive always supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get { return true; } - } + protected override bool ProviderSupportsShouldProcess => true; + #endregion Command parameters #region Command data @@ -1439,7 +1294,7 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } #endregion Command code } @@ -1581,13 +1436,13 @@ internal List GetMatchingDrives( { results.Add(drive); } - } // nameMatcher.IsMatch() - } // foreach Drive + } + } } results.Sort(); return results; } - } // DriveMatchingCoreCommandBase + } #endregion DriveMatchingCoreCommandBase @@ -1596,49 +1451,42 @@ internal List GetMatchingDrives( /// /// Removes a drive that is mounted in the Monad namespace. /// - [Cmdlet(VerbsCommon.Remove, "PSDrive", DefaultParameterSetName = "Name", SupportsShouldProcess = true, SupportsTransactions = true, + [Cmdlet(VerbsCommon.Remove, "PSDrive", DefaultParameterSetName = NameParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113376")] public class RemovePSDriveCommand : DriveMatchingCoreCommandBase { #region Command parameters + private const string NameParameterSet = "Name"; + private const string LiteralNameParameterSet = "LiteralName"; + /// /// Gets or sets the name of the drive to remove. /// - [Parameter(Position = 0, ParameterSetName = "Name", + [Parameter(Position = 0, ParameterSetName = NameParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [AllowNull] [AllowEmptyCollection] public string[] Name { - get - { - return _names; - } - set - { - _names = value; - } - } // Name + get => _names; + set => _names = value; + } /// /// Gets or sets the literal name parameter to the command /// - [Parameter(Position = 0, ParameterSetName = "LiteralName", + [Parameter(Position = 0, ParameterSetName = LiteralNameParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] public string[] LiteralName { - get - { - return _names; - } // get - + get => _names; set { base.SuppressWildcardExpansion = true; _names = value; - } // set - } // LiteralName + } + } /// /// Gets or sets the name provider(s) for which the drives should be removed. @@ -1646,15 +1494,8 @@ public string[] LiteralName [Parameter(ValueFromPipelineByPropertyName = true)] public string[] PSProvider { - get { return _provider; } - set - { - if (value == null) - { - value = Utils.EmptyArray(); - } - _provider = value; - } + get => _provider; + set => _provider = value ?? Utils.EmptyArray(); } /// @@ -1673,18 +1514,15 @@ public string[] PSProvider [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// /// Determines if the provider for the specified path supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get { return true; } - } + protected override bool ProviderSupportsShouldProcess => true; #endregion Command parameters @@ -1777,10 +1615,10 @@ protected override void ProcessRecord() WriteError(new ErrorRecord(e.ErrorRecord, e)); } } - } // ProcessRecord + } #endregion Command code - } // RemovePSDriveCommand + } #endregion RemovePSDriveCommand @@ -1790,12 +1628,15 @@ protected override void ProcessRecord() /// Gets a specified or listing of drives that are mounted in the Monad /// namespace. /// - [Cmdlet(VerbsCommon.Get, "PSDrive", DefaultParameterSetName = "Name", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113327")] + [Cmdlet(VerbsCommon.Get, "PSDrive", DefaultParameterSetName = NameParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113327")] [OutputType(typeof(PSDriveInfo))] public class GetPSDriveCommand : DriveMatchingCoreCommandBase { #region Command parameters + private const string NameParameterSet = "Name"; + private const string LiteralNameParameterSet = "LiteralName"; + /// /// Gets or sets the drive name the user is looking for. /// @@ -1805,29 +1646,22 @@ public class GetPSDriveCommand : DriveMatchingCoreCommandBase /// supplied and any drive names that match the expression /// will be returned. /// - [Parameter(Position = 0, ParameterSetName = "Name", ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, ParameterSetName = NameParameterSet, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] public string[] Name { - get { return _name; } - set - { - if (value == null) - { - value = new string[] { "*" }; - } - _name = value; - } + get => _name; + set => _name = value ?? new string[] { "*" }; } /// /// Gets or sets the literal name parameter to the command /// - [Parameter(Position = 0, ParameterSetName = "LiteralName", + [Parameter(Position = 0, ParameterSetName = LiteralNameParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] public string[] LiteralName { - get { return _name; } + get => _name; set { base.SuppressWildcardExpansion = true; @@ -1854,15 +1688,8 @@ public string[] LiteralName [Parameter(ValueFromPipelineByPropertyName = true)] public string[] PSProvider { - get { return _provider; } - set - { - if (value == null) - { - value = Utils.EmptyArray(); - } - _provider = value; - } + get => _provider; + set => _provider = value ?? Utils.EmptyArray(); } #endregion Command parameters @@ -1969,10 +1796,10 @@ protected override void ProcessRecord() argException)); } } - } // ProcessRecord + } #endregion Command code - } // GetPSDriveCommand + } #endregion GetPSDriveCommand @@ -1985,47 +1812,40 @@ protected override void ProcessRecord() /// /// Gets the specified item using the namespace providers. /// - [Cmdlet(VerbsCommon.Get, "Item", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113319")] + [Cmdlet(VerbsCommon.Get, "Item", DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113319")] public class GetItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// /// Gets or sets the path to item to get. /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// /// Gets or sets the literal path parameter to the command /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string[] LiteralPath { - get - { - return _paths; - } // get - + get => _paths; set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// /// Gets or sets the filter property @@ -2033,14 +1853,8 @@ public string[] LiteralPath [Parameter] public override string Filter { - get - { - return base.Filter; - } - set - { - base.Filter = value; - } + get => base.Filter; + set => base.Filter = value; } /// @@ -2049,16 +1863,9 @@ public override string Filter [Parameter] public override string[] Include { - get - { - return base.Include; - } // get - - set - { - base.Include = value; - } // set - } // Include + get => base.Include; + set => base.Include = value; + } /// /// Gets or sets the exclude property @@ -2066,16 +1873,9 @@ public override string[] Include [Parameter] public override string[] Exclude { - get - { - return base.Exclude; - } // get - - set - { - base.Exclude = value; - } // set - } // Exclude + get => base.Exclude; + set => base.Exclude = value; + } /// /// Gets or sets the force property @@ -2092,15 +1892,9 @@ public override string[] Exclude [Parameter] public override SwitchParameter Force { - get - { - return base.Force; - } - set - { - base.Force = value; - } - } // Force + get => base.Force; + set => base.Force = value; + } /// /// Gets the dynamic parameters for the get-item cmdlet. @@ -2119,7 +1913,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) return InvokeProvider.Item.GetItemDynamicParameters(Path[0], context); } return InvokeProvider.Item.GetItemDynamicParameters(".", context); - } // GetDynamicParameters + } #endregion Command parameters @@ -2173,10 +1967,10 @@ protected override void ProcessRecord() pathNotFound)); } } - } // ProcessRecord + } #endregion Command code - } // GetItemCommand + } #endregion GetItemCommand @@ -2185,26 +1979,26 @@ protected override void ProcessRecord() /// /// Creates the specified item using the namespace providers. /// - [Cmdlet(VerbsCommon.New, "Item", DefaultParameterSetName = "pathSet", SupportsShouldProcess = true, SupportsTransactions = true, + [Cmdlet(VerbsCommon.New, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113353")] public class NewItemCommand : CoreCommandWithCredentialsBase { #region Command parameters - private const string nameSet = "nameSet"; - private const string pathSet = "pathSet"; + private const string NameParameterSet = "nameSet"; + private const string PathParameterSet = "pathSet"; /// /// Gets or sets the container path to create the item in. /// - [Parameter(Position = 0, ParameterSetName = "pathSet", Mandatory = true, ValueFromPipelineByPropertyName = true)] - [Parameter(Position = 0, ParameterSetName = "nameSet", Mandatory = false, ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, ParameterSetName = NameParameterSet, Mandatory = false, ValueFromPipelineByPropertyName = true)] public string[] Path { get; set; } /// /// Gets or sets the name of the item to create /// - [Parameter(ParameterSetName = "nameSet", Mandatory = true, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = NameParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [AllowNull] [AllowEmptyString] public string Name { get; set; } @@ -2238,8 +2032,8 @@ public class NewItemCommand : CoreCommandWithCredentialsBase [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// @@ -2269,13 +2063,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) /// Determines if the provider for the specified path supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(Path); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(Path); #endregion Command parameters @@ -2330,10 +2118,10 @@ protected override void ProcessRecord() pathNotFound)); } } - } // ProcessRecord + } #endregion Command code - } // NewItemCommand + } #endregion NewItemCommand @@ -2342,37 +2130,34 @@ protected override void ProcessRecord() /// /// Sets the specified item using the namespace providers. /// - [Cmdlet(VerbsCommon.Set, "Item", SupportsShouldProcess = true, DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113395")] + [Cmdlet(VerbsCommon.Set, "Item", SupportsShouldProcess = true, DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113395")] public class SetItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// /// Gets or sets the path to item to set. /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// /// Gets or sets the literal path parameter to the command /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return _paths; } + get => _paths; set { base.SuppressWildcardExpansion = true; @@ -2401,8 +2186,8 @@ public string[] LiteralPath [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// @@ -2413,8 +2198,8 @@ public override SwitchParameter Force [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// @@ -2423,8 +2208,8 @@ public SwitchParameter PassThru [Parameter] public override string Filter { - get { return base.Filter; } - set { base.Filter = value; } + get => base.Filter; + set => base.Filter = value; } /// @@ -2433,9 +2218,9 @@ public override string Filter [Parameter] public override string[] Include { - get { return base.Include; } - set { base.Include = value; } - } // Include + get => base.Include; + set => base.Include = value; + } /// /// Gets or sets the exclude property @@ -2443,8 +2228,8 @@ public override string[] Include [Parameter] public override string[] Exclude { - get { return base.Exclude; } - set { base.Exclude = value; } + get => base.Exclude; + set => base.Exclude = value; } /// @@ -2470,13 +2255,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) /// Determines if the provider for the specified path supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters #region Command data @@ -2541,10 +2320,10 @@ protected override void ProcessRecord() pathNotFound)); } } - } // ProcessRecord + } #endregion Command code - } // SetItemCommand + } #endregion SetItemCommand @@ -2553,47 +2332,40 @@ protected override void ProcessRecord() /// /// Removes the specified item using the namespace providers. /// - [Cmdlet(VerbsCommon.Remove, "Item", SupportsShouldProcess = true, DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113373")] + [Cmdlet(VerbsCommon.Remove, "Item", SupportsShouldProcess = true, DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113373")] public class RemoveItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// /// Gets or sets the path property /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// /// Gets or sets the literal path parameter to the command /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string[] LiteralPath { - get - { - return _paths; - } // get - + get => _paths; set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// /// Gets or sets the filter property @@ -2601,14 +2373,8 @@ public string[] LiteralPath [Parameter] public override string Filter { - get - { - return base.Filter; - } - set - { - base.Filter = value; - } + get => base.Filter; + set => base.Filter = value; } /// @@ -2617,16 +2383,9 @@ public override string Filter [Parameter] public override string[] Include { - get - { - return base.Include; - } // get - - set - { - base.Include = value; - } // set - } // Include + get => base.Include; + set => base.Include = value; + } /// /// Gets or sets the exclude property @@ -2634,16 +2393,9 @@ public override string[] Include [Parameter] public override string[] Exclude { - get - { - return base.Exclude; - } // get - - set - { - base.Exclude = value; - } // set - } // Exclude + get => base.Exclude; + set => base.Exclude = value; + } /// /// Gets or sets the recurse property @@ -2651,15 +2403,9 @@ public override string[] Exclude [Parameter] public SwitchParameter Recurse { - get - { - return _recurse; - } - set - { - _recurse = value; - } - } // Recurse + get => _recurse; + set => _recurse = value; + } /// /// Gets or sets the force property @@ -2676,15 +2422,9 @@ public SwitchParameter Recurse [Parameter] public override SwitchParameter Force { - get - { - return base.Force; - } - set - { - base.Force = value; - } - } // Force + get => base.Force; + set => base.Force = value; + } /// /// Gets the dynamic parameters for the remove-item cmdlet. @@ -2703,19 +2443,13 @@ internal override object GetDynamicParameters(CmdletProviderContext context) return InvokeProvider.Item.RemoveItemDynamicParameters(Path[0], Recurse, context); } return InvokeProvider.Item.RemoveItemDynamicParameters(".", Recurse, context); - } // GetDynamicParameters + } /// /// Determines if the provider for the specified path supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters #region Command data @@ -3009,10 +2743,10 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } #endregion Command code - } // RemoveItemCommand + } #endregion RemoveItemCommand @@ -3022,37 +2756,35 @@ protected override void ProcessRecord() /// Moves an item from the specified location to the specified destination using /// the namespace providers. /// - [Cmdlet(VerbsCommon.Move, "Item", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, + [Cmdlet(VerbsCommon.Move, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113350")] public class MoveItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// /// Gets or sets the path property /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// /// Gets or sets the literal path parameter to the command /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return _paths; } + get => _paths; set { base.SuppressWildcardExpansion = true; @@ -3081,8 +2813,8 @@ public string[] LiteralPath [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// @@ -3091,8 +2823,8 @@ public override SwitchParameter Force [Parameter] public override string Filter { - get { return base.Filter; } - set { base.Filter = value; } + get => base.Filter; + set => base.Filter = value; } /// @@ -3101,8 +2833,8 @@ public override string Filter [Parameter] public override string[] Include { - get { return base.Include; } - set { base.Include = value; } + get => base.Include; + set => base.Include = value; } /// @@ -3111,8 +2843,8 @@ public override string[] Include [Parameter] public override string[] Exclude { - get { return base.Exclude; } - set { base.Exclude = value; } + get => base.Exclude; + set => base.Exclude = value; } /// @@ -3123,8 +2855,8 @@ public override string[] Exclude [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// @@ -3150,13 +2882,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) /// Determines if the provider for the specified path supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters @@ -3396,7 +3122,7 @@ private void MoveItem(string path, bool literalPath = false) } #endregion Command code - } // MoveItemCommand + } #endregion MoveItemCommand @@ -3405,30 +3131,33 @@ private void MoveItem(string path, bool literalPath = false) /// /// Renames a specified item to a new name using the namespace providers /// - [Cmdlet(VerbsCommon.Rename, "Item", SupportsShouldProcess = true, SupportsTransactions = true, DefaultParameterSetName = "ByPath", + [Cmdlet(VerbsCommon.Rename, "Item", SupportsShouldProcess = true, SupportsTransactions = true, DefaultParameterSetName = ByPathParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113382")] public class RenameItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + private const string ByPathParameterSet = "ByPath"; + private const string ByLiteralPathParameterSet = "ByLiteralPath"; + /// /// Gets or sets the path property /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByPath")] + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ByPathParameterSet)] public string Path { - get { return _path; } - set { _path = value; } + get => _path; + set => _path = value; } /// /// Gets or sets the literal path property /// - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByLiteralPath")] + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ByLiteralPathParameterSet)] [Alias("PSPath", "LP")] public string LiteralPath { - get { return _path; } + get => _path; set { _path = value; @@ -3457,8 +3186,8 @@ public string LiteralPath [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// @@ -3469,8 +3198,8 @@ public override SwitchParameter Force [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// @@ -3492,13 +3221,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) /// Determines if the provider for the specified path supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(new string[] { _path }); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(new string[] { _path }); #endregion Command parameters @@ -3554,7 +3277,6 @@ private Collection GetResolvedPaths(string path) pathNotFound.ErrorRecord, pathNotFound)); } - return results; } @@ -3568,14 +3290,11 @@ protected override void ProcessRecord() RenameItem(Path, literalPath: true); return; } - Collection resolvedPaths = GetResolvedPaths(Path); - if (resolvedPaths == null) { return; } - if (resolvedPaths.Count == 1) { RenameItem(resolvedPaths[0].Path, literalPath: true); @@ -3697,7 +3416,6 @@ private void RenameItem(string path, bool literalPath = false) // Default to the CmdletProviderContext that will direct output to // the pipeline. - currentContext.PassThru = PassThru; tracer.WriteLine("Rename {0} to {1}", path, NewName); @@ -3742,7 +3460,7 @@ private void RenameItem(string path, bool literalPath = false) } #endregion Command code - } // RenameItemCommand + } #endregion RenameItemCommand @@ -3751,37 +3469,35 @@ private void RenameItem(string path, bool literalPath = false) /// /// Copies a specified item to a new location using the namespace providers /// - [Cmdlet(VerbsCommon.Copy, "Item", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, + [Cmdlet(VerbsCommon.Copy, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113292")] public class CopyItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// /// Gets or sets the path property /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// /// Gets or sets the literal path parameter to the command /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return _paths; } + get => _paths; set { base.SuppressWildcardExpansion = true; @@ -3801,7 +3517,7 @@ public string[] LiteralPath [Parameter] public SwitchParameter Container { - get { return _container; } + get => _container; set { _containerSpecified = true; @@ -3824,8 +3540,8 @@ public SwitchParameter Container [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// @@ -3834,8 +3550,8 @@ public override SwitchParameter Force [Parameter] public override string Filter { - get { return base.Filter; } - set { base.Filter = value; } + get => base.Filter; + set => base.Filter = value; } /// @@ -3844,8 +3560,8 @@ public override string Filter [Parameter] public override string[] Include { - get { return base.Include; } - set { base.Include = value; } + get => base.Include; + set => base.Include = value; } /// @@ -3854,8 +3570,8 @@ public override string[] Include [Parameter] public override string[] Exclude { - get { return base.Exclude; } - set { base.Exclude = value; } + get => base.Exclude; + set => base.Exclude = value; } /// @@ -3864,12 +3580,11 @@ public override string[] Exclude [Parameter] public SwitchParameter Recurse { - get { return _recurse; } + get => _recurse; set { _recurse = value; - // If -Container is not specified but -Recurse // is, then -Container takes on the same value // as -Recurse @@ -3888,8 +3603,8 @@ public SwitchParameter Recurse [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// @@ -3909,19 +3624,13 @@ internal override object GetDynamicParameters(CmdletProviderContext context) return InvokeProvider.Item.CopyItemDynamicParameters(Path[0], Destination, Recurse, context); } return InvokeProvider.Item.CopyItemDynamicParameters(".", Destination, Recurse, context); - } // GetDynamicParameters + } /// /// Determines if the provider for the specified path supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters @@ -4006,10 +3715,10 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord + } #endregion Command code - } // CopyItemCommand + } #endregion CopyItemCommand @@ -4018,47 +3727,41 @@ protected override void ProcessRecord() /// /// Clears an item at the specified location /// - [Cmdlet(VerbsCommon.Clear, "Item", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, + [Cmdlet(VerbsCommon.Clear, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113283")] public class ClearItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// /// Gets or sets the path property /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// /// Gets or sets the literal path parameter to the command /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string[] LiteralPath { - get - { - return _paths; - } // get - + get => _paths; set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// /// Gets or sets the force property @@ -4075,15 +3778,9 @@ public string[] LiteralPath [Parameter] public override SwitchParameter Force { - get - { - return base.Force; - } - set - { - base.Force = value; - } - } // Force + get => base.Force; + set => base.Force = value; + } /// /// Gets or sets the filter property @@ -4091,15 +3788,9 @@ public override SwitchParameter Force [Parameter] public override string Filter { - get - { - return base.Filter; - } - set - { - base.Filter = value; - } - } // Filter + get => base.Filter; + set => base.Filter = value; + } /// /// Gets or sets the include property @@ -4107,16 +3798,9 @@ public override string Filter [Parameter] public override string[] Include { - get - { - return base.Include; - } // get - - set - { - base.Include = value; - } // set - } // Include + get => base.Include; + set => base.Include = value; + } /// /// Gets or sets the exclude property @@ -4124,16 +3808,9 @@ public override string[] Include [Parameter] public override string[] Exclude { - get - { - return base.Exclude; - } // get - - set - { - base.Exclude = value; - } // set - } // Exclude + get => base.Exclude; + set => base.Exclude = value; + } /// /// Gets the dynamic parameters for the clear-item cmdlet. @@ -4152,19 +3829,13 @@ internal override object GetDynamicParameters(CmdletProviderContext context) return InvokeProvider.Item.ClearItemDynamicParameters(Path[0], context); } return InvokeProvider.Item.ClearItemDynamicParameters(".", context); - } // GetDynamicParameters + } /// /// Determines if the provider for the specified path supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters @@ -4232,10 +3903,10 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord - #endregion Command code + } - } // ClearItemCommand + #endregion Command code + } #endregion ClearItemCommand @@ -4244,47 +3915,41 @@ protected override void ProcessRecord() /// /// Invokes an item at the specified location /// - [Cmdlet(VerbsLifecycle.Invoke, "Item", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, + [Cmdlet(VerbsLifecycle.Invoke, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113345")] public class InvokeItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// /// Gets or sets the path property /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// /// Gets or sets the literal path parameter to the command /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] public string[] LiteralPath { - get - { - return _paths; - } // get - + get => _paths; set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// /// Gets or sets the filter property @@ -4292,15 +3957,9 @@ public string[] LiteralPath [Parameter] public override string Filter { - get - { - return base.Filter; - } - set - { - base.Filter = value; - } - } // Filter + get => base.Filter; + set => base.Filter = value; + } /// /// Gets or sets the include property @@ -4308,16 +3967,9 @@ public override string Filter [Parameter] public override string[] Include { - get - { - return base.Include; - } // get - - set - { - base.Include = value; - } // set - } // Include + get => base.Include; + set => base.Include = value; + } /// /// Gets or sets the exclude property @@ -4325,16 +3977,9 @@ public override string[] Include [Parameter] public override string[] Exclude { - get - { - return base.Exclude; - } // get - - set - { - base.Exclude = value; - } // set - } // Exclude + get => base.Exclude; + set => base.Exclude = value; + } /// /// Gets the dynamic parameters for the invoke-item cmdlet. @@ -4353,19 +3998,13 @@ internal override object GetDynamicParameters(CmdletProviderContext context) return InvokeProvider.Item.InvokeItemDynamicParameters(Path[0], context); } return InvokeProvider.Item.InvokeItemDynamicParameters(".", context); - } // GetDynamicParameters + } /// /// Determines if the provider for the specified path supports ShouldProcess /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters @@ -4427,10 +4066,10 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord + } #endregion Command code - } // InvokeItemCommand + } #endregion InvokeItemCommand @@ -4456,8 +4095,8 @@ public class GetPSProviderCommand : CoreCommandBase [ValidateNotNullOrEmpty()] public string[] PSProvider { - get { return _provider; } - set { _provider = value ?? Utils.EmptyArray(); } + get => _provider; + set => _provider = value ?? Utils.EmptyArray(); } #endregion Command parameters @@ -4535,13 +4174,12 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } #endregion Command code - } // GetProviderCommand + } #endregion GetProviderCommand #endregion Provider commands -} // namespace Microsoft.PowerShell.Commands - +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs index 3d50ea95737..e76695a9047 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Management.Automation; using Dbg = System.Management.Automation; @@ -41,6 +42,9 @@ public class TestPathCommand : CoreCommandWithCredentialsBase /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] + [AllowNull] + [AllowEmptyCollection] + [AllowEmptyString] public string[] Path { get { return _paths; } @@ -53,6 +57,9 @@ public string[] Path [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] + [AllowNull] + [AllowEmptyCollection] + [AllowEmptyString] public string[] LiteralPath { get { return _paths; } @@ -124,7 +131,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) if (this.PathType == TestPathType.Any && !IsValid) { - if (Path != null && Path.Length > 0) + if (Path != null && Path.Length > 0 && Path[0] != null) { result = InvokeProvider.Item.ItemExistsDynamicParameters(Path[0], context); } @@ -133,6 +140,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) result = InvokeProvider.Item.ItemExistsDynamicParameters(".", context); } } + return result; } // GetDynamicParameters @@ -154,12 +162,39 @@ internal override object GetDynamicParameters(CmdletProviderContext context) /// protected override void ProcessRecord() { + if (_paths == null || _paths.Length == 0) + { + WriteError(new ErrorRecord( + new ArgumentNullException(TestPathResources.PathIsNullOrEmptyCollection), + "NullPathNotPermitted", + ErrorCategory.InvalidArgument, + Path)); + + return; + } + CmdletProviderContext currentContext = CmdletProviderContext; foreach (string path in _paths) { bool result = false; + if (path == null) + { + WriteError(new ErrorRecord( + new ArgumentNullException(TestPathResources.PathIsNullOrEmptyCollection), + "NullPathNotPermitted", + ErrorCategory.InvalidArgument, + Path)); + continue; + } + + if (string.IsNullOrWhiteSpace(path)) + { + WriteObject(result); + continue; + } + try { if (IsValid) @@ -184,6 +219,7 @@ protected override void ProcessRecord() } } } + // Any of the known exceptions means the path does not exist. catch (PSNotSupportedException) { diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/TestPathResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/TestPathResources.resx new file mode 100644 index 00000000000..c60c4318a64 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Management/resources/TestPathResources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The provided Path argument was null or an empty collection. + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj index 03243558d12..3caf0d2732e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj +++ b/src/Microsoft.PowerShell.Commands.Utility/Microsoft.PowerShell.Commands.Utility.csproj @@ -66,8 +66,8 @@ - - + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/join-string.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/join-string.cs new file mode 100644 index 00000000000..35be218ce5d --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/join-string.cs @@ -0,0 +1,226 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Management.Automation.Language; +using System.Text; + +namespace Microsoft.PowerShell.Commands.Utility +{ + /// + /// Join-Object implementation. + /// + [Cmdlet(VerbsCommon.Join, "String", RemotingCapability = RemotingCapability.None, DefaultParameterSetName = "default")] + [OutputType(typeof(string))] + public sealed class JoinStringCommand : PSCmdlet + { + /// A bigger default to not get re-allocations in common use cases. + private const int DefaultOutputStringCapacity = 256; + private readonly StringBuilder _outputBuilder = new StringBuilder(DefaultOutputStringCapacity); + private CultureInfo _cultureInfo = CultureInfo.InvariantCulture; + private string _separator; + private char _quoteChar; + private bool _firstInputObject = true; + + /// + /// Gets or sets the property name or script block to use as the value to join. + /// + [Parameter(Position = 0)] + [ArgumentCompleter(typeof(PropertyNameCompleter))] + public PSPropertyExpression Property { get; set; } + + /// + /// Gets or sets the delimiter to join the output with. + /// + [Parameter(Position = 1)] + [ArgumentCompleter(typeof(JoinItemCompleter))] + [AllowEmptyString] + public string Separator + { + get => _separator ?? LanguagePrimitives.ConvertTo(GetVariableValue("OFS")); + set => _separator = value; + } + + /// + /// Gets or sets text to include before the joined input text. + /// + [Parameter] + [Alias("op")] + public string OutputPrefix { get; set; } + + /// + /// Gets or sets text to include after the joined input text. + /// + [Parameter] + [Alias("os")] + public string OutputSuffix { get; set; } + + /// + /// Gets or sets if the output items should we wrapped in single quotes. + /// + [Parameter(ParameterSetName = "SingleQuote")] + public SwitchParameter SingleQuote { get; set; } + + /// + /// Gets or sets if the output items should we wrapped in double quotes. + /// + [Parameter(ParameterSetName = "DoubleQuote")] + public SwitchParameter DoubleQuote { get; set; } + + /// + /// Gets or sets a format string that is applied to each input object. + /// + [Parameter(ParameterSetName = "Format")] + [ArgumentCompleter(typeof(JoinItemCompleter))] + public string FormatString { get; set; } + + /// + /// Gets or sets if the current culture should be used with formatting instead of the invariant culture. + /// + [Parameter] + public SwitchParameter UseCulture { get; set; } + + /// + /// Gets or sets the input object to join into text. + /// + [Parameter(ValueFromPipeline = true)] + public PSObject InputObject { get; set; } + + /// + protected override void BeginProcessing() + { + _quoteChar = SingleQuote ? '\'' : DoubleQuote ? '"' : char.MinValue; + _outputBuilder.Append(OutputPrefix); + if (UseCulture) + { + _cultureInfo = CultureInfo.CurrentCulture; + } + } + + /// + protected override void ProcessRecord() + { + if (InputObject != null && InputObject != AutomationNull.Value) + { + var inputValue = Property == null + ? InputObject + : Property.GetValues(InputObject, false, true).FirstOrDefault()?.Result; + + // conversion to string always succeeds. + if (!LanguagePrimitives.TryConvertTo(inputValue, _cultureInfo, out var stringValue)) + { + throw new PSInvalidCastException("InvalidCastFromAnyTypeToString", ExtendedTypeSystem.InvalidCastCannotRetrieveString, null); + } + + if (_firstInputObject) + { + _firstInputObject = false; + } + else + { + _outputBuilder.Append(Separator); + } + + if (_quoteChar != char.MinValue) + { + _outputBuilder.Append(_quoteChar); + _outputBuilder.Append(stringValue); + _outputBuilder.Append(_quoteChar); + } + else if (string.IsNullOrEmpty(FormatString)) + { + _outputBuilder.Append(stringValue); + } + else + { + _outputBuilder.AppendFormat(_cultureInfo, FormatString, stringValue); + } + } + } + + /// + protected override void EndProcessing() + { + _outputBuilder.Append(OutputSuffix); + WriteObject(_outputBuilder.ToString()); + } + } + + internal class JoinItemCompleter : IArgumentCompleter + { + public IEnumerable CompleteArgument( + string commandName, + string parameterName, + string wordToComplete, + CommandAst commandAst, + IDictionary fakeBoundParameters) + { + switch (parameterName) + { + case "Separator": return CompleteSeparator(wordToComplete); + case "FormatString": return CompleteFormatString(wordToComplete); + } + + return null; + } + + private IEnumerable CompleteFormatString(string wordToComplete) + { + var res = new List(); + void AddMatching(string completionText) + { + if (completionText.StartsWith(wordToComplete, StringComparison.OrdinalIgnoreCase)) + { + res.Add(new CompletionResult(completionText)); + } + } + + AddMatching("'[{0}]'"); + AddMatching("'{0:N2}'"); + AddMatching("\"`r`n `${0}\""); + AddMatching("\"`r`n [string] `${0}\""); + + return res; + } + + private IEnumerable CompleteSeparator(string wordToComplete) + { + var res = new List(10); + + void AddMatching(string completionText, string listText, string toolTip) + { + if (completionText.StartsWith(wordToComplete, StringComparison.OrdinalIgnoreCase)) + { + res.Add(new CompletionResult(completionText, listText, CompletionResultType.ParameterValue, toolTip)); + } + } + + AddMatching("', '", "Comma-Space", "', ' - Comma-Space"); + AddMatching("';'", "Semi-Colon", "';' - Semi-Colon "); + AddMatching("'; '", "Semi-Colon-Space", "'; ' - Semi-Colon-Space"); + AddMatching($"\"{NewLineText}\"", "Newline", $"{NewLineText} - Newline"); + AddMatching("','", "Comma", "',' - Comma"); + AddMatching("'-'", "Dash", "'-' - Dash"); + AddMatching("' '", "Space", "' ' - Space"); + return res; + } + + public string NewLineText + { + get + { +#if UNIX + return "`n"; +#else + return "`r`n"; +#endif + } + } + } +} diff --git a/src/Microsoft.PowerShell.ConsoleHost/AssemblyInfo.cs b/src/Microsoft.PowerShell.ConsoleHost/AssemblyInfo.cs index 617b095eeec..7a8ebd5fbd2 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/AssemblyInfo.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/AssemblyInfo.cs @@ -1,25 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Reflection; using System.Runtime.CompilerServices; -#if !CORECLR -using System.Runtime.ConstrainedExecution; -#else -using System.Resources; -#endif [assembly: InternalsVisibleTo("powershell-tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] - -#if !CORECLR -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyInformationalVersionAttribute(@"10.0.10011.16384")] -[assembly: ReliabilityContractAttribute(Consistency.MayCorruptAppDomain, Cer.MayFail)] -[assembly: AssemblyTitle("Microsoft.PowerShell.ConsoleHost")] -[assembly: AssemblyDescription("Microsoft Windows PowerShell Console Host")] - -[assembly: System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.5")] -[assembly: System.Reflection.AssemblyFileVersion("10.0.10011.16384")] -#endif - [assembly: System.Runtime.InteropServices.ComVisible(false)] diff --git a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs index 803750802fb..bb7a734723d 100644 --- a/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs +++ b/src/Microsoft.PowerShell.ConsoleHost/host/msh/CommandLineParameterParser.cs @@ -188,7 +188,8 @@ internal class CommandLineParameterParser "command", "settingsfile", "help", - "workingdirectory" + "workingdirectory", + "removeworkingdirectorytrailingcharacter" }; internal CommandLineParameterParser(PSHostUserInterface hostUI, string bannerText, string helpText) @@ -401,8 +402,24 @@ internal bool NonInteractive internal string WorkingDirectory { - get { return _workingDirectory; } + get + { +#if !UNIX + if (_removeWorkingDirectoryTrailingCharacter && _workingDirectory.Length > 0) + { + return _workingDirectory.Remove(_workingDirectory.Length - 1); + } + #endif + return _workingDirectory; + } + } + +#if !UNIX + internal bool RemoveWorkingDirectoryTrailingCharacter + { + get { return _removeWorkingDirectoryTrailingCharacter; } } +#endif #endregion Internal properties @@ -934,6 +951,12 @@ private void ParseHelper(string[] args) _workingDirectory = args[i]; } +#if !UNIX + else if (MatchSwitch(switchKey, "removeworkingdirectorytrailingcharacter", "removeworkingdirectorytrailingcharacter")) + { + _removeWorkingDirectoryTrailingCharacter = true; + } +#endif else { // The first parameter we fail to recognize marks the beginning of the file string. @@ -1364,6 +1387,10 @@ private bool CollectArgs(string[] args, ref int i) private string _file; private string _executionPolicy; private string _workingDirectory; + +#if !UNIX + private bool _removeWorkingDirectoryTrailingCharacter = false; +#endif } } // namespace diff --git a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj index ea3f7e187ab..e4152f09f68 100644 --- a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj +++ b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/Microsoft.WSMan.Management/CredSSP.cs b/src/Microsoft.WSMan.Management/CredSSP.cs index b3e6b40edf4..cf08ea831cc 100644 --- a/src/Microsoft.WSMan.Management/CredSSP.cs +++ b/src/Microsoft.WSMan.Management/CredSSP.cs @@ -18,9 +18,6 @@ using System.Globalization; using System.Security; using System.Threading; -#if CORECLR -using System.Xml.XPath; -#endif using Dbg = System.Management.Automation; diff --git a/src/Microsoft.WSMan.Management/WsManHelper.cs b/src/Microsoft.WSMan.Management/WsManHelper.cs index 2ef2164e166..89c8c52afa0 100644 --- a/src/Microsoft.WSMan.Management/WsManHelper.cs +++ b/src/Microsoft.WSMan.Management/WsManHelper.cs @@ -17,9 +17,6 @@ using System.Management.Automation; using System.Management.Automation.Provider; using System.Threading; -#if CORECLR -using System.Xml.XPath; -#endif namespace Microsoft.WSMan.Management { diff --git a/src/Modules/PSGalleryModules.csproj b/src/Modules/PSGalleryModules.csproj index 1cdb70e200e..0eb23e1e85d 100644 --- a/src/Modules/PSGalleryModules.csproj +++ b/src/Modules/PSGalleryModules.csproj @@ -3,8 +3,8 @@ - - + + diff --git a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 86e09756362..e901694f4f9 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -14,7 +14,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", "Export-Csv", "Import-Csv", "ConvertTo-Csv", "ConvertFrom-Csv", "Export-Alias", "Invoke-Expression", "Get-Alias", "Get-Culture", "Get-Date", "Get-Host", "Get-Member", "Get-Random", "Get-UICulture", "Get-Unique", "Export-PSSession", "Import-PSSession", "Import-Alias", "Import-LocalizedData", - "Select-String", "Measure-Object", "New-Alias", "New-TimeSpan", "Read-Host", "Set-Alias", "Set-Date", + "Join-String", "Select-String", "Measure-Object", "New-Alias", "New-TimeSpan", "Read-Host", "Set-Alias", "Set-Date", "Start-Sleep", "Tee-Object", "Measure-Command", "Update-TypeData", "Update-FormatData", "Remove-TypeData", "Get-TypeData", "Write-Host", "Write-Progress", "New-Object", "Select-Object", "Group-Object", "Sort-Object", "Get-Variable", "New-Variable", "Set-Variable", "Remove-Variable", diff --git a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index ea7e3690574..316852c1728 100644 --- a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -14,7 +14,7 @@ CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", "Export-Csv", "Import-Csv", "ConvertTo-Csv", "ConvertFrom-Csv", "Export-Alias", "Invoke-Expression", "Get-Alias", "Get-Culture", "Get-Date", "Get-Host", "Get-Member", "Get-Random", "Get-UICulture", "Get-Unique", "Export-PSSession", "Import-PSSession", "Import-Alias", "Import-LocalizedData", - "Select-String", "Measure-Object", "New-Alias", "New-TimeSpan", "Read-Host", "Set-Alias", "Set-Date", + "Join-String", "Select-String", "Measure-Object", "New-Alias", "New-TimeSpan", "Read-Host", "Set-Alias", "Set-Date", "Start-Sleep", "Tee-Object", "Measure-Command", "Update-TypeData", "Update-FormatData", "Remove-TypeData", "Get-TypeData", "Write-Host", "Write-Progress", "New-Object", "Select-Object", "Group-Object", "Sort-Object", "Get-Variable", "New-Variable", "Set-Variable", "Remove-Variable", diff --git a/src/System.Management.Automation/AssemblyInfo.cs b/src/System.Management.Automation/AssemblyInfo.cs index 265370dbe36..6d650e3f65f 100644 --- a/src/System.Management.Automation/AssemblyInfo.cs +++ b/src/System.Management.Automation/AssemblyInfo.cs @@ -12,33 +12,19 @@ #if NOT_SIGNED // These attributes aren't every used, it's just a hack to get VS to not complain // about access when editing using the project files that don't actually build. -[assembly: InternalsVisibleTo(@"System.Management.Automation.Help")] [assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Commands.Utility")] [assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Commands.Management")] [assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Security")] [assembly: InternalsVisibleTo(@"System.Management.Automation.Remoting")] [assembly: InternalsVisibleTo(@"Export-Command")] [assembly: InternalsVisibleTo(@"Microsoft.PowerShell.ConsoleHost")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.PowerShellLanguageService")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.GraphicalHost")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.GPowerShell")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.ISECommon")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Editor")] -[assembly: InternalsVisibleTo(@"powershell_ise")] #else -[assembly: InternalsVisibleTo(@"System.Management.Automation.Help" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Commands.Utility" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Commands.Management" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Security" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo(@"System.Management.Automation.Remoting" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo(@"Export-Command" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] [assembly: InternalsVisibleTo(@"Microsoft.PowerShell.ConsoleHost" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.PowerShellLanguageService" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.GraphicalHost" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.GPowerShell" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.ISECommon" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo(@"Microsoft.PowerShell.Editor" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly: InternalsVisibleTo(@"powershell_ise" + @",PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] #endif namespace System.Management.Automation diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index 10f2ae45ae7..ae4e955bd92 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -9,7 +9,7 @@ - + @@ -23,7 +23,7 @@ - + @@ -138,8 +138,6 @@ - - diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index 2c393e2a5a9..d3ef4128bd5 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -3746,7 +3746,7 @@ private static void NativeCompletionMemberName(CompletionContext context, List CompleteMember(CompletionContext context, if (inferredTypes != null && inferredTypes.Length > 0) { // Use inferred types if we have any - CompleteMemberByInferredType(context, inferredTypes, results, memberName, filter: null, isStatic: @static); + CompleteMemberByInferredType(context.TypeInferenceContext, inferredTypes, results, memberName, filter: null, isStatic: @static); } else { @@ -5131,7 +5131,7 @@ private static bool IsInDscContext(ExpressionAst expression) return Ast.GetAncestorAst(expression) != null; } - private static void CompleteMemberByInferredType(CompletionContext context, IEnumerable inferredTypes, List results, string memberName, Func filter, bool isStatic) + internal static void CompleteMemberByInferredType(TypeInferenceContext context, IEnumerable inferredTypes, List results, string memberName, Func filter, bool isStatic) { bool extensionMethodsAdded = false; HashSet typeNameUsed = new HashSet(StringComparer.OrdinalIgnoreCase); @@ -5142,8 +5142,9 @@ private static void CompleteMemberByInferredType(CompletionContext context, IEnu { continue; } + typeNameUsed.Add(psTypeName.Name); - var members = context.TypeInferenceContext.GetMembersByInferredType(psTypeName, isStatic, filter); + var members = context.GetMembersByInferredType(psTypeName, isStatic, filter); foreach (var member in members) { AddInferredMember(member, memberNamePattern, results); @@ -5276,7 +5277,7 @@ private static bool IsWriteablePropertyMember(object member) return false; } - private static bool IsPropertyMember(object member) + internal static bool IsPropertyMember(object member) { return member is PropertyInfo || member is FieldInfo @@ -6154,7 +6155,7 @@ internal static List CompleteHashtableKey(CompletionContext co { var result = new List(); CompleteMemberByInferredType( - completionContext, AstTypeInference.InferTypeOf(typeAst, completionContext.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval), + completionContext.TypeInferenceContext, AstTypeInference.InferTypeOf(typeAst, completionContext.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval), result, completionContext.WordToComplete + "*", IsWriteablePropertyMember, isStatic: false); return result; } @@ -6265,7 +6266,7 @@ internal static List CompleteHashtableKey(CompletionContext co var inferredType = AstTypeInference.InferTypeOf(commandAst, completionContext.TypeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval); var result = new List(); CompleteMemberByInferredType( - completionContext, inferredType, + completionContext.TypeInferenceContext, inferredType, result, completionContext.WordToComplete + "*", IsWriteablePropertyMember, isStatic: false); return result; case "Select-Object": @@ -6910,4 +6911,79 @@ public object VisitParenExpression(ParenExpressionAst parenExpressionAst) return parenExpressionAst.Pipeline.Accept(this); } } + + /// + /// Completes with the property names of the InputObject. + /// + internal class PropertyNameCompleter : IArgumentCompleter + { + private readonly string _parameterNameOfInput; + + /// + /// Initializes a new instance of the class. + /// + public PropertyNameCompleter() + { + _parameterNameOfInput = "InputObject"; + } + + /// + /// Initializes a new instance of the class. + /// + /// The name of the property of the input object for witch to complete with property names. + public PropertyNameCompleter(string parameterNameOfInput) + { + _parameterNameOfInput = parameterNameOfInput; + } + + IEnumerable IArgumentCompleter.CompleteArgument( + string commandName, + string parameterName, + string wordToComplete, + CommandAst commandAst, + IDictionary fakeBoundParameters) + { + if (!(commandAst.Parent is PipelineAst pipelineAst)) + { + return null; + } + + int i; + for (i = 0; i < pipelineAst.PipelineElements.Count; i++) + { + if (pipelineAst.PipelineElements[i] == commandAst) + { + break; + } + } + + var typeInferenceContext = new TypeInferenceContext(); + IEnumerable prevType; + if (i == 0) + { + var parameterAst = (CommandParameterAst)commandAst.Find(ast => ast is CommandParameterAst cpa && cpa.ParameterName == "PropertyName", false); + var pseudoBinding = new PseudoParameterBinder().DoPseudoParameterBinding(commandAst, null, parameterAst, PseudoParameterBinder.BindingType.ParameterCompletion); + if (!pseudoBinding.BoundArguments.TryGetValue(_parameterNameOfInput, out var pair) || !pair.ArgumentSpecified) + { + return null; + } + + if (pair is AstPair astPair && astPair.Argument != null) + { + prevType = AstTypeInference.InferTypeOf(astPair.Argument, typeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval); + } + + return null; + } + else + { + prevType = AstTypeInference.InferTypeOf(pipelineAst.PipelineElements[i - 1], typeInferenceContext, TypeInferenceRuntimePermissions.AllowSafeEval); + } + + var result = new List(); + + CompletionCompleters.CompleteMemberByInferredType(typeInferenceContext, prevType, result, wordToComplete + "*", filter: CompletionCompleters.IsPropertyMember, isStatic: false); + return result; + } + } } diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index ad5d32f06d0..f1ba2f2914b 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -5259,15 +5259,11 @@ private static void InitializeCoreCmdletsAndProviders( #if !UNIX {"Disable-PSRemoting", new SessionStateCmdletEntry("Disable-PSRemoting", typeof(DisablePSRemotingCommand), helpFile) }, {"Enable-PSRemoting", new SessionStateCmdletEntry("Enable-PSRemoting", typeof(EnablePSRemotingCommand), helpFile) }, - {"Get-PSHostProcessInfo", new SessionStateCmdletEntry("Get-PSHostProcessInfo", typeof(GetPSHostProcessInfoCommand), helpFile) }, - {"Enter-PSHostProcess", new SessionStateCmdletEntry("Enter-PSHostProcess", typeof(EnterPSHostProcessCommand), helpFile) }, - {"Exit-PSHostProcess", new SessionStateCmdletEntry("Exit-PSHostProcess", typeof(ExitPSHostProcessCommand), helpFile) }, {"Disable-PSSessionConfiguration", new SessionStateCmdletEntry("Disable-PSSessionConfiguration", typeof(DisablePSSessionConfigurationCommand), helpFile) }, {"Enable-PSSessionConfiguration", new SessionStateCmdletEntry("Enable-PSSessionConfiguration", typeof(EnablePSSessionConfigurationCommand), helpFile) }, {"Get-PSSessionCapability", new SessionStateCmdletEntry("Get-PSSessionCapability", typeof(GetPSSessionCapabilityCommand), helpFile) }, {"Get-PSSessionConfiguration", new SessionStateCmdletEntry("Get-PSSessionConfiguration", typeof(GetPSSessionConfigurationCommand), helpFile) }, {"New-PSSessionConfigurationFile", new SessionStateCmdletEntry("New-PSSessionConfigurationFile", typeof(NewPSSessionConfigurationFileCommand), helpFile) }, - {"New-PSSessionOption", new SessionStateCmdletEntry("New-PSSessionOption", typeof(NewPSSessionOptionCommand), helpFile) }, {"Receive-PSSession", new SessionStateCmdletEntry("Receive-PSSession", typeof(ReceivePSSessionCommand), helpFile) }, {"Register-PSSessionConfiguration", new SessionStateCmdletEntry("Register-PSSessionConfiguration", typeof(RegisterPSSessionConfigurationCommand), helpFile) }, {"Unregister-PSSessionConfiguration", new SessionStateCmdletEntry("Unregister-PSSessionConfiguration", typeof(UnregisterPSSessionConfigurationCommand), helpFile) }, @@ -5276,7 +5272,9 @@ private static void InitializeCoreCmdletsAndProviders( {"Connect-PSSession", new SessionStateCmdletEntry("Connect-PSSession", typeof(ConnectPSSessionCommand), helpFile) }, {"Disconnect-PSSession", new SessionStateCmdletEntry("Disconnect-PSSession", typeof(DisconnectPSSessionCommand), helpFile) }, #endif + {"Enter-PSHostProcess", new SessionStateCmdletEntry("Enter-PSHostProcess", typeof(EnterPSHostProcessCommand), helpFile) }, {"Enter-PSSession", new SessionStateCmdletEntry("Enter-PSSession", typeof(EnterPSSessionCommand), helpFile) }, + {"Exit-PSHostProcess", new SessionStateCmdletEntry("Exit-PSHostProcess", typeof(ExitPSHostProcessCommand), helpFile) }, {"Exit-PSSession", new SessionStateCmdletEntry("Exit-PSSession", typeof(ExitPSSessionCommand), helpFile) }, {"Export-ModuleMember", new SessionStateCmdletEntry("Export-ModuleMember", typeof(ExportModuleMemberCommand), helpFile) }, {"ForEach-Object", new SessionStateCmdletEntry("ForEach-Object", typeof(ForEachObjectCommand), helpFile) }, @@ -5286,6 +5284,7 @@ private static void InitializeCoreCmdletsAndProviders( {"Get-History", new SessionStateCmdletEntry("Get-History", typeof(GetHistoryCommand), helpFile) }, {"Get-Job", new SessionStateCmdletEntry("Get-Job", typeof(GetJobCommand), helpFile) }, {"Get-Module", new SessionStateCmdletEntry("Get-Module", typeof(GetModuleCommand), helpFile) }, + {"Get-PSHostProcessInfo", new SessionStateCmdletEntry("Get-PSHostProcessInfo", typeof(GetPSHostProcessInfoCommand), helpFile) }, {"Get-PSSession", new SessionStateCmdletEntry("Get-PSSession", typeof(GetPSSessionCommand), helpFile) }, {"Import-Module", new SessionStateCmdletEntry("Import-Module", typeof(ImportModuleCommand), helpFile) }, {"Invoke-Command", new SessionStateCmdletEntry("Invoke-Command", typeof(InvokeCommandCommand), helpFile) }, @@ -5294,6 +5293,7 @@ private static void InitializeCoreCmdletsAndProviders( {"New-ModuleManifest", new SessionStateCmdletEntry("New-ModuleManifest", typeof(NewModuleManifestCommand), helpFile) }, {"New-PSRoleCapabilityFile", new SessionStateCmdletEntry("New-PSRoleCapabilityFile", typeof(NewPSRoleCapabilityFileCommand), helpFile) }, {"New-PSSession", new SessionStateCmdletEntry("New-PSSession", typeof(NewPSSessionCommand), helpFile) }, + {"New-PSSessionOption", new SessionStateCmdletEntry("New-PSSessionOption", typeof(NewPSSessionOptionCommand), helpFile) }, {"New-PSTransportOption", new SessionStateCmdletEntry("New-PSTransportOption", typeof(NewPSTransportOptionCommand), helpFile) }, {"Out-Default", new SessionStateCmdletEntry("Out-Default", typeof(OutDefaultCommand), helpFile) }, {"Out-Host", new SessionStateCmdletEntry("Out-Host", typeof(OutHostCommand), helpFile) }, diff --git a/src/System.Management.Automation/engine/LanguagePrimitives.cs b/src/System.Management.Automation/engine/LanguagePrimitives.cs index 8f2398d93a8..b869a845f94 100644 --- a/src/System.Management.Automation/engine/LanguagePrimitives.cs +++ b/src/System.Management.Automation/engine/LanguagePrimitives.cs @@ -3244,7 +3244,7 @@ private static string ConvertNonNumericToString(object valueToConvert, try { typeConversion.WriteLine("Converting object to string."); - return PSObject.ToStringParser(ecFromTLS, valueToConvert); + return PSObject.ToStringParser(ecFromTLS, valueToConvert, formatProvider); } catch (ExtendedTypeSystemException e) { diff --git a/src/System.Management.Automation/engine/MshObject.cs b/src/System.Management.Automation/engine/MshObject.cs index 764b8f9876f..13d9ce73cd7 100644 --- a/src/System.Management.Automation/engine/MshObject.cs +++ b/src/System.Management.Automation/engine/MshObject.cs @@ -1149,10 +1149,31 @@ private static string ToStringEmptyBaseObject(ExecutionContext context, PSObject /// When there is a brokered ToString but it failed, or when the ToString on obj throws an exception. /// internal static string ToStringParser(ExecutionContext context, object obj) + { + return ToStringParser(context, obj, CultureInfo.InvariantCulture); + } + + /// + /// Returns the string representation of obj. + /// + /// ExecutionContext used to fetch the separator. + /// + /// object we are trying to call ToString on. If this is not an PSObject we try + /// enumerating and if that fails we call obj.ToString. + /// If this is an PSObject, we look for a brokered ToString. + /// If it is not present, and the BaseObject is null we try listing the properties. + /// If the BaseObject is not null we try enumerating. If that fails we try the BaseObject's ToString. + /// + /// The formatProvider to be passed to ToString. + /// A string representation of the object. + /// + /// When there is a brokered ToString but it failed, or when the ToString on obj throws an exception. + /// + internal static string ToStringParser(ExecutionContext context, object obj, IFormatProvider formatProvider) { try { - return ToString(context, obj, null, null, CultureInfo.InvariantCulture, true, true); + return ToString(context, obj, null, null, formatProvider, true, true); } catch (ExtendedTypeSystemException etse) { diff --git a/src/System.Management.Automation/engine/PSConfiguration.cs b/src/System.Management.Automation/engine/PSConfiguration.cs index 7f3597f8405..643d8d08a87 100644 --- a/src/System.Management.Automation/engine/PSConfiguration.cs +++ b/src/System.Management.Automation/engine/PSConfiguration.cs @@ -355,7 +355,10 @@ internal PSKeyword GetLogKeywords() fileLock.EnterReadLock(); try { - using (var streamReader = new StreamReader(fileName)) + // The config file can be locked by another process + // so we wait some milliseconds in 'WaitForFile()' for recovery before stop current process. + using (var readerStream = WaitForFile(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (var streamReader = new StreamReader(readerStream)) using (var jsonReader = new JsonTextReader(streamReader)) { var settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.None, MaxDepth = 10 }; @@ -376,6 +379,29 @@ internal PSKeyword GetLogKeywords() return defaultValue; } + private FileStream WaitForFile(string fullPath, FileMode mode, FileAccess access, FileShare share) + { + const int MaxTries = 5; + for (int numTries = 0; numTries < MaxTries; numTries++) + { + try + { + return new FileStream(fullPath, mode, access, share); + } + catch (IOException) + { + if (numTries == (MaxTries - 1)) + { + throw; + } + + Thread.Sleep(50); + } + } + + throw new IOException(nameof(WaitForFile)); + } + /// /// Update a value in the configuration file. /// diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs index a4f58a6a207..8f1c971f5aa 100644 --- a/src/System.Management.Automation/engine/Utils.cs +++ b/src/System.Management.Automation/engine/Utils.cs @@ -902,39 +902,6 @@ internal static bool IsAdministrator() #endif } - internal static void NativeEnumerateDirectory(string directory, out List directories, out List files) - { - IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - NativeMethods.WIN32_FIND_DATA findData; - - files = new List(); - directories = new List(); - - IntPtr findHandle; - - findHandle = NativeMethods.FindFirstFile(directory + "\\*", out findData); - if (findHandle != INVALID_HANDLE_VALUE) - { - do - { - if ((findData.dwFileAttributes & NativeMethods.FileAttributes.Directory) != 0) - { - if ((!String.Equals(".", findData.cFileName, StringComparison.OrdinalIgnoreCase)) && - (!String.Equals("..", findData.cFileName, StringComparison.OrdinalIgnoreCase))) - { - directories.Add(directory + "\\" + findData.cFileName); - } - } - else - { - files.Add(directory + "\\" + findData.cFileName); - } - } - while (NativeMethods.FindNextFile(findHandle, out findData)); - NativeMethods.FindClose(findHandle); - } - } - internal static bool IsReservedDeviceName(string destinationPath) { #if !UNIX diff --git a/src/System.Management.Automation/engine/hostifaces/InternalHost.cs b/src/System.Management.Automation/engine/hostifaces/InternalHost.cs index bb94e75ef9d..8615cb40f68 100644 --- a/src/System.Management.Automation/engine/hostifaces/InternalHost.cs +++ b/src/System.Management.Automation/engine/hostifaces/InternalHost.cs @@ -190,11 +190,7 @@ public override CultureInfo CurrentUICulture { get { -#if CORECLR // No CultureInfo.InstalledUICulture In CoreCLR. Locale cannot be changed On CSS. - CultureInfo ci = _externalHostRef.Value.CurrentUICulture ?? CultureInfo.CurrentUICulture; -#else CultureInfo ci = _externalHostRef.Value.CurrentUICulture ?? CultureInfo.InstalledUICulture; -#endif return ci; } } diff --git a/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs b/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs index 54737851650..3d93afc65e7 100644 --- a/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs +++ b/src/System.Management.Automation/engine/hostifaces/LocalConnection.cs @@ -823,7 +823,7 @@ private void DoCloseHelper() // When closing the primary runspace, ensure all other local runspaces are closed. var closeAllOpenRunspaces = isPrimaryRunspace && haveOpenRunspaces; - // Stop all transcriptions and unitialize AMSI if we're the last runspace to exit or we are exiting the primary runspace. + // Stop all transcriptions and un-initialize AMSI if we're the last runspace to exit or we are exiting the primary runspace. if (!haveOpenRunspaces) { ExecutionContext executionContext = this.GetExecutionContext; diff --git a/src/System.Management.Automation/engine/parser/Compiler.cs b/src/System.Management.Automation/engine/parser/Compiler.cs index 41084c4db2b..d2814ed129b 100644 --- a/src/System.Management.Automation/engine/parser/Compiler.cs +++ b/src/System.Management.Automation/engine/parser/Compiler.cs @@ -368,7 +368,7 @@ internal static class CachedReflectionInfo internal static readonly FieldInfo PSObject_isDeserialized = typeof(PSObject).GetField(nameof(PSObject.isDeserialized), instanceFlags); internal static readonly MethodInfo PSObject_ToStringParser = - typeof(PSObject).GetMethod(nameof(PSObject.ToStringParser), staticFlags); + typeof(PSObject).GetMethod(nameof(PSObject.ToStringParser), staticFlags, null, new[]{typeof(ExecutionContext), typeof(object)}, null); internal static readonly PropertyInfo PSReference_Value = typeof(PSReference).GetProperty(nameof(PSReference.Value)); @@ -1535,7 +1535,7 @@ private static RuntimeDefinedParameter GetRuntimeDefinedParameter(ParameterAst p if (attribute is ExperimentalAttribute expAttribute) { - // Only honor the first seen experimental attribute, ignore the others. + // Only honor the first seen experimental attribute, ignore the others. if (!hasSeenExpAttribute && expAttribute.ToHide) { return null; } // Do not add experimental attributes to the attribute list. diff --git a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs index 39b7b0962f3..54bd3e07e39 100644 --- a/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/EnterPSHostProcessCommand.cs @@ -2,16 +2,17 @@ // Licensed under the MIT License. using System; -using System.Diagnostics; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.IO; using System.Management.Automation; using System.Management.Automation.Host; using System.Management.Automation.Internal; -using System.Management.Automation.Runspaces; using System.Management.Automation.Remoting; -using System.Diagnostics.CodeAnalysis; +using System.Management.Automation.Runspaces; namespace Microsoft.PowerShell.Commands { @@ -393,7 +394,14 @@ public sealed class GetPSHostProcessInfoCommand : PSCmdlet private const string ProcessParameterSet = "ProcessParameterSet"; private const string ProcessIdParameterSet = "ProcessIdParameterSet"; private const string ProcessNameParameterSet = "ProcessNameParameterSet"; + +#if UNIX + // CoreFx uses the system temp path to store the file used for named pipes and is not settable. + // This member is only used by Get-PSHostProcessInfo to know where to look for the named pipe files. + private static readonly string NamedPipePath = Path.GetTempPath(); +#else private const string NamedPipePath = @"\\.\pipe\"; +#endif #endregion @@ -524,9 +532,12 @@ internal static IReadOnlyCollection GetAppDomainNamesFromProc var procAppDomainInfo = new List(); // Get all named pipe 'files' on local machine. - List directories; - List namedPipes; - Utils.NativeEnumerateDirectory(NamedPipePath, out directories, out namedPipes); + List namedPipes = new List(); + var namedPipeDirectory = new DirectoryInfo(NamedPipePath); + foreach (var pipeFileInfo in namedPipeDirectory.EnumerateFiles(NamedPipeUtils.NamedPipeNamePrefixSearch)) + { + namedPipes.Add(Path.Combine(pipeFileInfo.DirectoryName, pipeFileInfo.Name)); + } // Collect all PowerShell named pipes for given process Ids. foreach (string namedPipe in namedPipes) @@ -534,14 +545,13 @@ internal static IReadOnlyCollection GetAppDomainNamesFromProc int startIndex = namedPipe.IndexOf(NamedPipeUtils.NamedPipeNamePrefix, StringComparison.OrdinalIgnoreCase); if (startIndex > -1) { - // This is a PowerShell named pipe. Parse the process Id, AppDomain name, and process name. - int pStartTimeIndex = namedPipe.IndexOf(".", startIndex, StringComparison.OrdinalIgnoreCase); + int pStartTimeIndex = namedPipe.IndexOf('.', startIndex); if (pStartTimeIndex > -1) { - int pIdIndex = namedPipe.IndexOf(".", pStartTimeIndex + 1, StringComparison.OrdinalIgnoreCase); + int pIdIndex = namedPipe.IndexOf('.', pStartTimeIndex + 1); if (pIdIndex > -1) { - int pAppDomainIndex = namedPipe.IndexOf(".", pIdIndex + 1, StringComparison.OrdinalIgnoreCase); + int pAppDomainIndex = namedPipe.IndexOf('.', pIdIndex + 1); if (pAppDomainIndex > -1) { string idString = namedPipe.Substring(pIdIndex + 1, (pAppDomainIndex - pIdIndex - 1)); @@ -564,15 +574,47 @@ internal static IReadOnlyCollection GetAppDomainNamesFromProc if (!found) { continue; } } } + else + { + // Process id is not valid so we'll skip + continue; + } - int pNameIndex = namedPipe.IndexOf(".", pAppDomainIndex + 1, StringComparison.OrdinalIgnoreCase); + int pNameIndex = namedPipe.IndexOf('.', pAppDomainIndex + 1); if (pNameIndex > -1) { string appDomainName = namedPipe.Substring(pAppDomainIndex + 1, (pNameIndex - pAppDomainIndex - 1)); string pName = namedPipe.Substring(pNameIndex + 1); - procAppDomainInfo.Add( - new PSHostProcessInfo(pName, id, appDomainName)); + Process process = null; + + try + { + process = System.Diagnostics.Process.GetProcessById(id); + } + catch (Exception) + { + // Do nothing if the process no longer exists + } + + if (process == null) + { + try + { + // If the process is gone, try removing the PSHost named pipe + var pipeFile = new FileInfo(namedPipe); + pipeFile.Delete(); + } + catch (Exception) + { + // best effort to cleanup + } + } + else if (process.ProcessName.Equals(pName, StringComparison.Ordinal)) + { + // only add if the process name matches + procAppDomainInfo.Add(new PSHostProcessInfo(pName, id, appDomainName)); + } } } } @@ -661,7 +703,7 @@ internal PSHostProcessInfo(string processName, int processId, string appDomainNa MainWindowTitle = String.Empty; try { - var proc = System.Diagnostics.Process.GetProcessById(processId); + var proc = Process.GetProcessById(processId); MainWindowTitle = proc.MainWindowTitle ?? string.Empty; } catch (ArgumentException) { } diff --git a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs index 4252ba0389f..340a181eafa 100644 --- a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs @@ -20,6 +20,7 @@ public sealed class NewPSSessionOptionCommand : PSCmdlet { #region Parameters (specific to PSSessionOption) +#if !UNIX /// /// The MaximumRedirection parameter enables the implicit redirection functionality /// -1 = no limit @@ -178,11 +179,13 @@ public int IdleTimeout set { _idleTimeout = value; } } private int? _idleTimeout; +#endif #endregion Parameters #region Parameters copied from New-WSManSessionOption +#if !UNIX /// /// By default, ProxyAccessType is None, that means Proxy information (ProxyAccessType, /// ProxyAuthenticationMechanism and ProxyCredential)is not passed to WSMan at all. @@ -210,6 +213,7 @@ public int IdleTimeout [ValidateNotNullOrEmpty] [Credential] public PSCredential ProxyCredential { get; set; } +#endif /// /// The following is the definition of the input parameter "SkipCACheck". @@ -241,6 +245,8 @@ public SwitchParameter SkipCNCheck } private bool _skipcncheck; +#if !UNIX + /// /// The following is the definition of the input parameter "SkipRevocation". /// Indicates that certificate common name (CN) of the server need not match the @@ -319,6 +325,8 @@ public SwitchParameter IncludePortInSPN } private bool _includePortInSPN; +#endif + #endregion #region Implementation @@ -330,11 +338,14 @@ protected override void BeginProcessing() { PSSessionOption result = new PSSessionOption(); // Begin: WSMan specific options +#if !UNIX result.ProxyAccessType = this.ProxyAccessType; result.ProxyAuthentication = this.ProxyAuthentication; result.ProxyCredential = this.ProxyCredential; +#endif result.SkipCACheck = this.SkipCACheck; result.SkipCNCheck = this.SkipCNCheck; +#if !UNIX result.SkipRevocationCheck = this.SkipRevocationCheck; if (_operationtimeout.HasValue) { @@ -385,6 +396,7 @@ protected override void BeginProcessing() { result.ApplicationArguments = this.ApplicationArguments; } +#endif this.WriteObject(result); } diff --git a/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs b/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs index fc027aebd29..c9287dd0a85 100644 --- a/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs +++ b/src/System.Management.Automation/engine/remoting/common/RemoteSessionNamedPipe.cs @@ -1,18 +1,19 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Management.Automation.Tracing; -using System.Management.Automation.Internal; -using System.Management.Automation.Remoting.Server; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Pipes; -using System.Threading; +using System.Management.Automation.Internal; +using System.Management.Automation.Remoting.Server; +using System.Management.Automation.Tracing; +using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Principal; -using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; using Microsoft.Win32.SafeHandles; -using System.Diagnostics.CodeAnalysis; using Dbg = System.Diagnostics.Debug; namespace System.Management.Automation.Remoting @@ -24,9 +25,18 @@ internal static class NamedPipeUtils { #region Strings - internal const string DefaultAppDomainName = "DefaultAppDomain"; + internal const string NamedPipeNamePrefix = "PSHost."; +#if UNIX + internal const string DefaultAppDomainName = "None"; + // This `CoreFxPipe` prefix is defined by CoreFx + internal const string NamedPipeNamePrefixSearch = "CoreFxPipe_PSHost*"; +#else + internal const string DefaultAppDomainName = "DefaultAppDomain"; internal const string NamedPipeNamePrefixSearch = "PSHost*"; +#endif + // On non-Windows, .NET named pipes are limited to up to 104 characters + internal const int MaxNamedPipeNameSize = 104; #endregion @@ -92,11 +102,35 @@ internal static string CreateProcessPipeName( appDomainName = DefaultAppDomainName; } - return NamedPipeNamePrefix + - proc.StartTime.ToFileTime().ToString(CultureInfo.InvariantCulture) + "." + - proc.Id.ToString(CultureInfo.InvariantCulture) + "." + - CleanAppDomainNameForPipeName(appDomainName) + "." + - proc.ProcessName; + System.Text.StringBuilder pipeNameBuilder = new System.Text.StringBuilder(MaxNamedPipeNameSize); + pipeNameBuilder.Append(NamedPipeNamePrefix) + // The starttime is there to prevent another process easily guessing the pipe name + // and squatting on it. + // There is a limit of 104 characters in total including the temp path to the named pipe file + // on non-Windows systems, so we'll convert the starttime to hex and just take the first 8 characters. +#if UNIX + .Append(proc.StartTime.ToFileTime().ToString("X8").Substring(1,8)) +#else + .Append(proc.StartTime.ToFileTime().ToString(CultureInfo.InvariantCulture)) +#endif + .Append('.') + .Append(proc.Id.ToString(CultureInfo.InvariantCulture)) + .Append('.') + .Append(CleanAppDomainNameForPipeName(appDomainName)) + .Append('.') + .Append(proc.ProcessName); +#if UNIX + int charsToTrim = pipeNameBuilder.Length - MaxNamedPipeNameSize; + if (charsToTrim > 0) + { + // TODO: In the case the pipe name is truncated, the user cannot connect to it using the cmdlet + // unless we add a `-Force` type switch as it attempts to validate the current process name + // matches the process name in the pipe name + pipeNameBuilder.Remove(MaxNamedPipeNameSize + 1, charsToTrim); + } +#endif + + return pipeNameBuilder.ToString(); } private static string CleanAppDomainNameForPipeName(string appDomainName) @@ -444,6 +478,7 @@ private NamedPipeServerStream CreateNamedPipe( if (namespaceName == null) { throw new PSArgumentNullException("namespaceName"); } if (coreName == null) { throw new PSArgumentNullException("coreName"); } +#if !UNIX string fullPipeName = @"\\" + serverName + @"\" + namespaceName + @"\" + coreName; // Create optional security attributes based on provided PipeSecurity. @@ -494,6 +529,16 @@ private NamedPipeServerStream CreateNamedPipe( pipeHandle.Dispose(); throw; } +#else + return new NamedPipeServerStream( + pipeName: coreName, + direction: PipeDirection.InOut, + maxNumberOfServerInstances: 1, + transmissionMode: PipeTransmissionMode.Byte, + options: PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly, + inBufferSize: _namedPipeBufferSizeForRemoting, + outBufferSize: _namedPipeBufferSizeForRemoting); +#endif } static RemoteSessionNamedPipeServer() @@ -502,10 +547,7 @@ static RemoteSessionNamedPipeServer() // All PowerShell instances will start with the named pipe // and listener created and running. - if (Platform.IsWindows) - { - IPCNamedPipeServerEnabled = true; - } + IPCNamedPipeServerEnabled = true; CreateIPCNamedPipeServerSingleton(); #if !CORECLR // There is only one AppDomain per application in CoreCLR, which would be the default @@ -590,6 +632,9 @@ public void StartListening( internal static CommonSecurityDescriptor GetServerPipeSecurity() { +#if UNIX + return null; +#else // Built-in Admin SID SecurityIdentifier adminSID = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null); DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, 1); @@ -618,6 +663,7 @@ internal static CommonSecurityDescriptor GetServerPipeSecurity() } return securityDesc; +#endif } /// @@ -658,7 +704,11 @@ private void ProcessListeningThread(object state) try { +#if UNIX + userName = System.Environment.UserName; +#else userName = WindowsIdentity.GetCurrent().Name; +#endif } catch (System.Security.SecurityException) { } @@ -1046,7 +1096,7 @@ internal RemoteSessionNamedPipeClient( throw new PSArgumentNullException("pipeName"); } - _pipeName = @"\\.\pipe\" + pipeName; + _pipeName = pipeName; // Defer creating the .Net NamedPipeClientStream object until we connect. // _clientPipeStream == null. @@ -1096,17 +1146,25 @@ protected override NamedPipeClientStream DoConnect(int timeout) int elapsedTime = 0; _connecting = true; + NamedPipeClientStream namedPipeClientStream = new NamedPipeClientStream( + serverName: ".", + pipeName: _pipeName, + direction: PipeDirection.InOut, + options: PipeOptions.Asynchronous); + + namedPipeClientStream.Connect(); + do { - // Wait in 100 mSec increments. - if (!NamedPipeNative.WaitNamedPipe(_pipeName, 100)) + if (!namedPipeClientStream.IsConnected) { + Thread.Sleep(100); elapsedTime = unchecked(Environment.TickCount - startTime); continue; } _connecting = false; - return OpenNamedPipe(); + return namedPipeClientStream; } while (_connecting && (elapsedTime < timeout)); _connecting = false; @@ -1115,50 +1173,6 @@ protected override NamedPipeClientStream DoConnect(int timeout) } #endregion - - #region Private Methods - - /// - /// Helper method to open a named pipe via native APIs and return in - /// .Net NamedPipeClientStream wrapper object. - /// - private NamedPipeClientStream OpenNamedPipe() - { - // Create pipe flags. - uint pipeFlags = NamedPipeNative.FILE_FLAG_OVERLAPPED; - - // Get handle to pipe. - SafePipeHandle pipeHandle = NamedPipeNative.CreateFile( - _pipeName, - NamedPipeNative.GENERIC_READ | NamedPipeNative.GENERIC_WRITE, - 0, - IntPtr.Zero, - NamedPipeNative.OPEN_EXISTING, - pipeFlags, - IntPtr.Zero); - - int lastError = Marshal.GetLastWin32Error(); - if (pipeHandle.IsInvalid) - { - throw new System.ComponentModel.Win32Exception(lastError); - } - - try - { - return new NamedPipeClientStream( - PipeDirection.InOut, - true, // IsAsync - true, // IsConnected - pipeHandle); - } - catch (Exception) - { - pipeHandle.Dispose(); - throw; - } - } - - #endregion } /// diff --git a/src/System.Management.Automation/engine/remoting/common/remotingexceptions.cs b/src/System.Management.Automation/engine/remoting/common/remotingexceptions.cs index 1c7af327103..8eb3b1c0164 100644 --- a/src/System.Management.Automation/engine/remoting/common/remotingexceptions.cs +++ b/src/System.Management.Automation/engine/remoting/common/remotingexceptions.cs @@ -127,6 +127,7 @@ internal enum PSRemotingErrorId : uint CannotSetStdOutHandle = 822, CannotSetStdErrHandle = 823, InvalidConfigurationName = 824, + ConnectSkipCheckFailed = 825, // Error codes added to support new WSMan Fan-In Model API CreateSessionFailed = 851, CreateExFailed = 853, diff --git a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs index 1cf899b48f1..69aca329a64 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/OutOfProcTransportManager.cs @@ -1821,7 +1821,7 @@ private void ProcessReaderThread(object state) { if (e is ArgumentOutOfRangeException) { - Dbg.Assert(false, "Need to adjust transport fragmentor to accomodate read buffer size."); + Dbg.Assert(false, "Need to adjust transport fragmentor to accommodate read buffer size."); } string errorMsg = (e.Message != null) ? e.Message : string.Empty; diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs index 6f32cf8a0ee..7d981fc2251 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs @@ -1924,8 +1924,11 @@ internal static WSManReceiveDataResult UnMarshal(IntPtr unmanagedData) 0, result1.data.binaryData.bufferLength); } + +#if !UNIX Dbg.Assert(result1.data.type == (uint)WSManDataType.WSMAN_DATA_TYPE_BINARY, "ReceiveDataResult can receive only binary data"); +#endif WSManReceiveDataResult result = new WSManReceiveDataResult(); result.data = dataRecvd; diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs index dea3d3af50b..1ed9072e90e 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs @@ -1528,11 +1528,17 @@ private void Initialize(Uri connectionUri, WSManConnectionInfo connectionInfo) } #if UNIX - // explicitly disallow Basic auth over HTTP on Unix. + // Explicitly disallow Basic auth over HTTP on Unix. if (connectionInfo.AuthenticationMechanism == AuthenticationMechanism.Basic && !isSSLSpecified && connectionUri.Scheme != Uri.UriSchemeHttps) { throw new PSRemotingTransportException(PSRemotingErrorId.ConnectFailed, RemotingErrorIdStrings.BasicAuthOverHttpNotSupported); } + + // Allow HTTPS on Unix only if SkipCACheck and SkipCNCheck are selected, because OMI client does not support validating server certificates. + if (isSSLSpecified && (!connectionInfo.SkipCACheck || !connectionInfo.SkipCNCheck)) + { + throw new PSRemotingTransportException(PSRemotingErrorId.ConnectSkipCheckFailed, RemotingErrorIdStrings.UnixOnlyHttpsWithoutSkipCACheckNotSupported); + } #endif if (connectionInfo.NoEncryption) diff --git a/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs b/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs index 313b7eee56e..f80fd8c30fe 100644 --- a/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs +++ b/src/System.Management.Automation/engine/remoting/server/OutOfProcServerMediator.cs @@ -590,6 +590,7 @@ private NamedPipeProcessMediator( originalStdOut = new OutOfProcessTextWriter(namedPipeServer.TextWriter); originalStdErr = new NamedPipeErrorTextWriter(namedPipeServer.TextWriter); +#if !UNIX // Flow impersonation if requested. WindowsIdentity currentIdentity = null; try @@ -599,6 +600,7 @@ private NamedPipeProcessMediator( catch (System.Security.SecurityException) { } _windowsIdentityToImpersonate = ((currentIdentity != null) && (currentIdentity.ImpersonationLevel == TokenImpersonationLevel.Impersonation)) ? currentIdentity : null; +#endif } #endregion diff --git a/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs b/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs index 79a43bb42b9..9035e16195f 100644 --- a/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs +++ b/src/System.Management.Automation/engine/remoting/server/ServerRunspacePoolDriver.cs @@ -546,7 +546,9 @@ private PSDataCollection InvokeScript(Command cmdToRun, RunspaceCreate { Debug.Assert(cmdToRun != null, "cmdToRun shouldn't be null"); - cmdToRun.CommandOrigin = CommandOrigin.Internal; + // Don't invoke initialization script as trusted (CommandOrigin == Internal) if the system is in lock down mode. + cmdToRun.CommandOrigin = (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) ? CommandOrigin.Runspace : CommandOrigin.Internal; + cmdToRun.MergeMyResults(PipelineResultTypes.Error, PipelineResultTypes.Output); PowerShell powershell = PowerShell.Create(); powershell.AddCommand(cmdToRun).AddCommand("out-default"); diff --git a/src/System.Management.Automation/help/HelpCommentsParser.cs b/src/System.Management.Automation/help/HelpCommentsParser.cs index 833a8cb2097..50d884bf9c2 100644 --- a/src/System.Management.Automation/help/HelpCommentsParser.cs +++ b/src/System.Management.Automation/help/HelpCommentsParser.cs @@ -1154,10 +1154,10 @@ private static List GetParameterComments(Language.Token[] tokens, IParam // $sb = { } // set-item function:foo $sb // help foo - startTokenIndex = savedStartIndex = FirstTokenInExtent(tokens, ast.Extent) - 1; + startTokenIndex = savedStartIndex = FirstTokenInExtent(tokens, ast.Extent) + 1; lastTokenIndex = LastTokenInExtent(tokens, ast.Extent, startTokenIndex); - Diagnostics.Assert(tokens[startTokenIndex + 1].Kind == TokenKind.LCurly, + Diagnostics.Assert(tokens[startTokenIndex - 1].Kind == TokenKind.LCurly, "Unexpected first token in script block"); Diagnostics.Assert(tokens[lastTokenIndex].Kind == TokenKind.RCurly, "Unexpected last token in script block"); diff --git a/src/System.Management.Automation/resources/MshSnapInCmdletResources.resx b/src/System.Management.Automation/resources/MshSnapInCmdletResources.resx index d23249ed109..225ef930619 100644 --- a/src/System.Management.Automation/resources/MshSnapInCmdletResources.resx +++ b/src/System.Management.Automation/resources/MshSnapInCmdletResources.resx @@ -129,7 +129,4 @@ Cannot add PowerShell snap-in {0} because it is a system PowerShell module. Use Import-Module to load the module. - - The custom PowerShell snap-in is not supported in PowerShell Core. - diff --git a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx index 37f976684fd..ef1c0554f91 100644 --- a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx +++ b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx @@ -1670,4 +1670,7 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro Host system does not have the correct version of Hyper-V schema. + + HTTPS on Unix does not currently support CA or CN checks. Use the PSSessionOption -SkipCACheck and -SkipCNCheck if you are certain you trust the server you are connecting to and the network in between. + diff --git a/src/System.Management.Automation/singleshell/config/MshSnapinInfo.cs b/src/System.Management.Automation/singleshell/config/MshSnapinInfo.cs index b69ad68273b..514518be55f 100644 --- a/src/System.Management.Automation/singleshell/config/MshSnapinInfo.cs +++ b/src/System.Management.Automation/singleshell/config/MshSnapinInfo.cs @@ -3,16 +3,17 @@ using System.Collections.Generic; using System.Collections.ObjectModel; -using Microsoft.Win32; -using System.Security; +using System.Globalization; using System.IO; using System.Reflection; using System.Text; -using System.Globalization; -using Regex = System.Text.RegularExpressions.Regex; +using System.Security; -using Dbg = System.Management.Automation.Diagnostics; using Microsoft.PowerShell.Commands; +using Microsoft.Win32; + +using Dbg = System.Management.Automation.Diagnostics; +using Regex = System.Text.RegularExpressions.Regex; namespace System.Management.Automation { @@ -50,7 +51,6 @@ internal static class RegistryStrings internal const string MshSnapin_AssemblyName = "AssemblyName"; internal const string MshSnapin_ModuleName = "ModuleName"; internal const string MshSnapin_MonadVersion = "PowerShellVersion"; - internal const string MshSnapin_CustomPSSnapInType = "CustomPSSnapInType"; internal const string MshSnapin_BuiltInTypes = "Types"; internal const string MshSnapin_BuiltInFormats = "Formats"; internal const string MshSnapin_Description = "Description"; @@ -85,8 +85,7 @@ internal PSSnapInInfo Collection types, Collection formats, string descriptionFallback, - string vendorFallback, - string customPSSnapInType + string vendorFallback ) { if (string.IsNullOrEmpty(name)) @@ -114,16 +113,6 @@ string customPSSnapInType throw PSTraceSource.NewArgumentNullException("psVersion"); } -#if CORECLR // CustomPSSnapIn Not Supported On CSS. - // CustomPSSnapIn derives from System.Configuration.Install, which is not in CoreCLR. - if (customPSSnapInType != null) - { - throw PSTraceSource.NewArgumentException( - "customPSSnapInType", - MshSnapInCmdletResources.CustomPSSnapInNotSupportedInPowerShellCore); - } -#endif - if (version == null) { version = new Version("0.0"); @@ -158,7 +147,6 @@ string customPSSnapInType Version = version; Types = types; Formats = formats; - CustomPSSnapInType = customPSSnapInType; _descriptionFallback = descriptionFallback; _vendorFallback = vendorFallback; } @@ -177,10 +165,9 @@ internal PSSnapInInfo string description, string descriptionFallback, string vendor, - string vendorFallback, - string customPSSnapInType + string vendorFallback ) - : this(name, isDefault, applicationBase, assemblyName, moduleName, psVersion, version, types, formats, descriptionFallback, vendorFallback, customPSSnapInType) + : this(name, isDefault, applicationBase, assemblyName, moduleName, psVersion, version, types, formats, descriptionFallback, vendorFallback) { _description = description; _vendor = vendor; @@ -202,9 +189,8 @@ internal PSSnapInInfo string descriptionIndirect, string vendor, string vendorFallback, - string vendorIndirect, - string customPSSnapInType - ) : this(name, isDefault, applicationBase, assemblyName, moduleName, psVersion, version, types, formats, description, descriptionFallback, vendor, vendorFallback, customPSSnapInType) + string vendorIndirect + ) : this(name, isDefault, applicationBase, assemblyName, moduleName, psVersion, version, types, formats, description, descriptionFallback, vendor, vendorFallback) { // add descriptionIndirect and vendorIndirect only if the mshsnapin is a default mshsnapin if (isDefault) @@ -256,11 +242,6 @@ internal string AbsoluteModulePath } } - /// - /// Type of custom mshsnapin. - /// - internal string CustomPSSnapInType { get; } - /// /// Monad version used by mshsnapin /// @@ -414,13 +395,22 @@ internal void LoadIndirectResources(RegistryStringResourceIndirect resourceReade internal PSSnapInInfo Clone() { - PSSnapInInfo cloned = new PSSnapInInfo(Name, - IsDefault, ApplicationBase, AssemblyName, - ModuleName, PSVersion, Version, new Collection(Types), - new Collection(Formats), _description, - _descriptionFallback, _descriptionIndirect, _vendor, _vendorFallback, _vendorIndirect, - CustomPSSnapInType - ); + PSSnapInInfo cloned = new PSSnapInInfo( + Name, + IsDefault, + ApplicationBase, + AssemblyName, + ModuleName, + PSVersion, + Version, + new Collection(Types), + new Collection(Formats), + _description, + _descriptionFallback, + _descriptionIndirect, + _vendor, + _vendorFallback, + _vendorIndirect); return cloned; } @@ -728,17 +718,11 @@ private static PSSnapInInfo ReadOne(RegistryKey mshSnapInRoot, string mshsnapinI logPipelineExecutionDetails = true; } - string customPSSnapInType = ReadStringValue(mshsnapinKey, RegistryStrings.MshSnapin_CustomPSSnapInType, false); - if (string.IsNullOrEmpty(customPSSnapInType)) - { - customPSSnapInType = null; - } - Collection types = ReadMultiStringValue(mshsnapinKey, RegistryStrings.MshSnapin_BuiltInTypes, false); Collection formats = ReadMultiStringValue(mshsnapinKey, RegistryStrings.MshSnapin_BuiltInFormats, false); s_mshsnapinTracer.WriteLine("Successfully read registry values for mshsnapin {0}. Constructing PSSnapInInfo object.", mshsnapinId); - PSSnapInInfo mshSnapinInfo = new PSSnapInInfo(mshsnapinId, false, applicationBase, assemblyName, moduleName, monadVersion, version, types, formats, description, vendor, customPSSnapInType); + PSSnapInInfo mshSnapinInfo = new PSSnapInInfo(mshsnapinId, false, applicationBase, assemblyName, moduleName, monadVersion, version, types, formats, description, vendor); mshSnapinInfo.LogPipelineExecutionDetails = logPipelineExecutionDetails; return mshSnapinInfo; @@ -963,10 +947,22 @@ internal static PSSnapInInfo ReadCoreEngineSnapIn() string moduleName = Path.Combine(applicationBase, s_coreSnapin.AssemblyName + ".dll"); - PSSnapInInfo coreMshSnapin = new PSSnapInInfo(s_coreSnapin.PSSnapInName, true, applicationBase, - strongName, moduleName, psVersion, assemblyVersion, types, formats, null, - s_coreSnapin.Description, s_coreSnapin.DescriptionIndirect, null, null, - s_coreSnapin.VendorIndirect, null); + PSSnapInInfo coreMshSnapin = new PSSnapInInfo( + s_coreSnapin.PSSnapInName, + isDefault: true, + applicationBase, + strongName, + moduleName, + psVersion, + assemblyVersion, + types, + formats, + description: null, + s_coreSnapin.Description, + s_coreSnapin.DescriptionIndirect, + vendor: null, + vendorFallback: null, + s_coreSnapin.VendorIndirect); #if !UNIX // NOTE: On Unix, logging has to be deferred until after command-line parsing // complete. On Windows, deferring the call is not needed @@ -1040,10 +1036,22 @@ internal static Collection ReadEnginePSSnapIns() moduleName = defaultMshSnapinInfo.AssemblyName; } - PSSnapInInfo defaultMshSnapin = new PSSnapInInfo(defaultMshSnapinInfo.PSSnapInName, true, applicationBase, - strongName, moduleName, psVersion, assemblyVersion, types, formats, null, - defaultMshSnapinInfo.Description, defaultMshSnapinInfo.DescriptionIndirect, null, null, - defaultMshSnapinInfo.VendorIndirect, null); + PSSnapInInfo defaultMshSnapin = new PSSnapInInfo( + defaultMshSnapinInfo.PSSnapInName, + isDefault: true, + applicationBase, + strongName, + moduleName, + psVersion, + assemblyVersion, + types, + formats, + description: null, + defaultMshSnapinInfo.Description, + defaultMshSnapinInfo.DescriptionIndirect, + vendor: null, + vendorFallback: null, + defaultMshSnapinInfo.VendorIndirect); SetSnapInLoggingInformation(defaultMshSnapin); engineMshSnapins.Add(defaultMshSnapin); diff --git a/test/README.md b/test/README.md index 08e07d7a54b..d511995cbe0 100644 --- a/test/README.md +++ b/test/README.md @@ -4,8 +4,7 @@ Testing The tests are organized by testing language. Thus Pester tests, which are written in the PowerShell language, are in [./powershell](./powershell) and xUnit tests, written in C#, are in -[./csharp](./csharp). The sanity tests for the Full .NET build of -PowerShell are in [./fullclr](./fullclr), and the third-party +[./xUnit](./xUnit). The third-party [shebang][] test is in [./shebang](./shebang). [shebang]: https://en.wikipedia.org/wiki/Shebang_(Unix) diff --git a/test/csharp/README.md b/test/csharp/README.md deleted file mode 100644 index 18979b0552a..00000000000 --- a/test/csharp/README.md +++ /dev/null @@ -1,20 +0,0 @@ -xUnit Tests -=========== - -These tests are completely Linux specific. - -Every test class *must* belong to -`[Collection("AssemblyLoadContext")]`. This ensures that PowerShell's -AssemblyLoadContext is initialized before any other code is executed. -When this is not the case, late initialization fails with -`System.InvalidOperationException : Binding model is already locked -for the AppDomain and cannot be reset.` - -Having every class in the same collection is as close to an xUnit -global init hook as can be done. - -Running xUnit Tests -------------------- - -Go to the top level of the PowerShell repository and run: -`Start-PSxUnit` inside a self-hosted copy of PowerShell. diff --git a/test/hosting/hosting.tests.csproj b/test/hosting/hosting.tests.csproj index fbc26ac44ef..9a928732906 100644 --- a/test/hosting/hosting.tests.csproj +++ b/test/hosting/hosting.tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/test/powershell/Host/Logging.Tests.ps1 b/test/powershell/Host/Logging.Tests.ps1 index 245906f9e07..42f637e4fa1 100644 --- a/test/powershell/Host/Logging.Tests.ps1 +++ b/test/powershell/Host/Logging.Tests.ps1 @@ -178,12 +178,13 @@ Creating Scriptblock text \(1 of 1\):#012{0}(#012)*ScriptBlock ID: [0-9a-z\-]*#0 $items | Should -Not -Be $null $items.Length | Should -BeGreaterThan 1 $items[0].EventId | Should -BeExactly 'Perftrack_ConsoleStartupStart:PowershellConsoleStartup.WinStart.Informational' - $items[1].EventId | Should -BeExactly 'Perftrack_ConsoleStartupStop:PowershellConsoleStartup.WinStop.Informational' + $items[1].EventId | Should -BeExactly 'NamedPipeIPC_ServerListenerStarted:NamedPipe.Open.Informational' + $items[2].EventId | Should -BeExactly 'Perftrack_ConsoleStartupStop:PowershellConsoleStartup.WinStop.Informational' # if there are more items than expected... - if ($items.Length -gt 2) + if ($items.Length -gt 3) { # Force reporting of the first unexpected item to help diagnosis - $items[2] | Should -Be $null + $items[3] | Should -Be $null } } @@ -317,12 +318,13 @@ Path:.* $items | Should -Not -Be $null $items.Count | Should -BeGreaterThan 1 $items[0].EventId | Should -BeExactly 'Perftrack_ConsoleStartupStart:PowershellConsoleStartup.WinStart.Informational' - $items[1].EventId | Should -BeExactly 'Perftrack_ConsoleStartupStop:PowershellConsoleStartup.WinStop.Informational' + $items[1].EventId | Should -BeExactly 'NamedPipeIPC_ServerListenerStarted:NamedPipe.Open.Informational' + $items[2].EventId | Should -BeExactly 'Perftrack_ConsoleStartupStop:PowershellConsoleStartup.WinStop.Informational' # if there are more items than expected... - if ($items.Count -gt 2) + if ($items.Count -gt 3) { # Force reporting of the first unexpected item to help diagnosis - $items[2] | Should -Be $null + $items[3] | Should -Be $null } } catch { diff --git a/test/powershell/Language/Scripting/ErrorPosition.Tests.ps1 b/test/powershell/Language/Scripting/ErrorPosition.Tests.ps1 index 62777bda32e..1853e9cba46 100644 --- a/test/powershell/Language/Scripting/ErrorPosition.Tests.ps1 +++ b/test/powershell/Language/Scripting/ErrorPosition.Tests.ps1 @@ -17,7 +17,7 @@ for ($null[0]; '@ $for_pipeline_initializer_script = @' $test = 1 -for (Test-Path $null; +for (Test-Path $null -ErrorAction Stop; $test -gt 1;) { } '@ $for_expression_condition_script = @' @@ -27,7 +27,7 @@ for (;$null[0];) '@ $for_pipeline_condition_script = @' $test = 1 -for (;Test-Path $null;) +for (;Test-Path $null -ErrorAction Stop;) { } '@ $do_while_expression_condition_script = @' @@ -38,7 +38,7 @@ while ($null[0]) $do_while_pipeline_condition_script = @' $test = 1 do {} -while (Test-Path $null) +while (Test-Path $null -ErrorAction Stop) '@ $do_until_expression_condition_script = @' $test = 1 @@ -48,7 +48,7 @@ until ($null[0]) $do_until_pipeline_condition_script = @' $test = 1 do {} -until (Test-Path $null) +until (Test-Path $null -ErrorAction Stop) '@ $testCases = @( diff --git a/test/powershell/Language/Scripting/ScriptHelp.Tests.ps1 b/test/powershell/Language/Scripting/ScriptHelp.Tests.ps1 index 5536ea745f2..588e58a043d 100644 --- a/test/powershell/Language/Scripting/ScriptHelp.Tests.ps1 +++ b/test/powershell/Language/Scripting/ScriptHelp.Tests.ps1 @@ -80,6 +80,57 @@ Describe 'get-help HelpFunc1' -Tags "Feature" { # Useless # function helpFunc1 {} + + Set-Item function:dynamicHelpFunc1 -Value { + # .SYNOPSIS + # + # A relatively useless function. + # + # .DESCRIPTION + # + # A description + # + # with indented text and a blank line. + # + # .NOTES + # + # This function is mostly harmless. + # + # .LINK + # + # http://blogs.msdn.com/powershell + # + # .LINK + # + # other commands + # + # .EXAMPLE + # + # If you need an example, you're hopeless. + # + # .INPUTS + # + # Anything you like. + # + # .OUTPUTS + # + # Nothing. + # + # .COMPONENT + # + # Something + # + # .ROLE + # + # CrazyUser + # + # .FUNCTIONALITY + # + # Useless + # + + process { } + } } Context 'Get-Help helpFunc1' { @@ -87,6 +138,11 @@ Describe 'get-help HelpFunc1' -Tags "Feature" { TestHelpFunc1 $x } + Context 'Get-Help dynamicHelpFunc1' { + $x = get-help dynamicHelpFunc1 + TestHelpFunc1 $x + } + Context 'get-help helpFunc1 -component blah' { $x = get-help helpFunc1 -component blah -ErrorAction SilentlyContinue -ErrorVariable e TestHelpError $x $e 'HelpNotFound,Microsoft.PowerShell.Commands.GetHelpCommand' diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/Enter-PSHostProcess.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/Enter-PSHostProcess.Tests.ps1 new file mode 100644 index 00000000000..721ea25c003 --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/Enter-PSHostProcess.Tests.ps1 @@ -0,0 +1,54 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +Describe "Enter-PSHostProcess tests" -Tag Feature { + BeforeAll { + $si = [System.Diagnostics.ProcessStartInfo]::new() + $si.FileName = "pwsh" + $si.Arguments = "-noexit" + $si.RedirectStandardInput = $true + $si.RedirectStandardOutput = $true + $si.RedirectStandardError = $true + $pwsh = [System.Diagnostics.Process]::Start($si) + + if ($IsWindows) { + $si.FileName = "powershell" + $powershell = [System.Diagnostics.Process]::Start($si) + } + + if ($env:AppVeyor) { + $IsAppveyor = $true + } + else { + $IsAppveyor = $false + } + } + + AfterAll { + $pwsh | Stop-Process + + if ($IsWindows) { + $powershell | Stop-Process + } + } + + # Skip on Appveyor due to PSReadline issue. + It "Can enter and exit another PSHost" -Skip:$IsAppVeyor { + "enter-pshostprocess -id $($pwsh.Id)`n`$pid`nexit-pshostprocess" | pwsh -c - | Should -Be $pwsh.Id + } + + # Skip on Appveyor due to PSReadline issue. + It "Can enter and exit another Windows PowerShell PSHost" -Skip:(!$IsWindows -or $IsAppVeyor) { + "enter-pshostprocess -id $($powershell.Id)`n`$pid`nexit-pshostprocess" | pwsh -c - | Should -Be $powershell.Id + } + + It "Can enter using NamedPipeConnectionInfo" { + $npInfo = [System.Management.Automation.Runspaces.NamedPipeConnectionInfo]::new($pwsh.Id) + $rs = [runspacefactory]::CreateRunspace($npInfo) + $rs.Open() + $ps = [powershell]::Create() + $ps.Runspace = $rs + $ps.AddScript('$pid').Invoke() | Should -Be $pwsh.Id + $rs.Dispose() + } +} diff --git a/test/powershell/Modules/Microsoft.PowerShell.Core/Get-PSHostProcessInfo.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Core/Get-PSHostProcessInfo.Tests.ps1 new file mode 100644 index 00000000000..ba6849046b3 --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Core/Get-PSHostProcessInfo.Tests.ps1 @@ -0,0 +1,51 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +Describe "Get-PSHostProcessInfo tests" -Tag CI { + BeforeAll { + $si = [System.Diagnostics.ProcessStartInfo]::new() + $si.FileName = "pwsh" + $si.Arguments = "-noexit" + $si.RedirectStandardInput = $true + $si.RedirectStandardOutput = $true + $si.RedirectStandardError = $true + $pwsh = [System.Diagnostics.Process]::Start($si) + + if ($IsWindows) { + $si.FileName = "powershell" + $powershell = [System.Diagnostics.Process]::Start($si) + } + } + + AfterAll { + $pwsh | Stop-Process + + if ($IsWindows) { + $powershell | Stop-Process + } + } + + It "Should return own self" { + (Get-PSHostProcessInfo).ProcessId | Should -Contain $pid + } + + It "Should list info for other PowerShell hosted processes" { + # Creation of the named pipe is async + Wait-UntilTrue { + Get-PSHostProcessInfo | Where-Object { $_.ProcessId -eq $pwsh.Id } + } + $pshosts = Get-PSHostProcessInfo + $pshosts.Count | Should -BeGreaterOrEqual 1 + $pshosts.ProcessId | Should -Contain $pwsh.Id + } + + It "Should list Windows PowerShell process" -Skip:(!$IsWindows) { + # Creation of the named pipe is async + Wait-UntilTrue { + Get-PSHostProcessInfo | Where-Object { $_.ProcessId -eq $powershell.Id } + } + $psProcess = Get-PSHostProcessInfo | Where-Object { $_.ProcessName -eq "powershell" } + $psProcess.Count | Should -BeGreaterOrEqual 1 + $psProcess.ProcessId | Should -Contain $powershell.id + } +} diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 index 694a7ae8606..48a80d8e0b8 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 @@ -2,123 +2,143 @@ # Licensed under the MIT License. Describe "Test-Path" -Tags "CI" { BeforeAll { - $testdirectory = $TestDrive - $testfilename = New-Item -path $testdirectory -Name testfile.txt -ItemType file -Value 1 -force + $testdirectory = $TestDrive + $testfilename = New-Item -path $testdirectory -Name testfile.txt -ItemType file -Value 1 -force - # populate with additional files - New-Item -Path $testdirectory -Name datestfile -value 1 -ItemType file | Out-Null - New-Item -Path $testdirectory -Name gatestfile -value 1 -ItemType file | Out-Null - New-Item -Path $testdirectory -Name usr -value 1 -ItemType directory | Out-Null + # populate with additional files + New-Item -Path $testdirectory -Name datestfile -value 1 -ItemType file | Out-Null + New-Item -Path $testdirectory -Name gatestfile -value 1 -ItemType file | Out-Null + New-Item -Path $testdirectory -Name usr -value 1 -ItemType directory | Out-Null - $nonExistentDir = Join-Path -Path (Join-Path -Path $testdirectory -ChildPath usr) -ChildPath bin - $nonExistentPath = Join-Path -Path (Join-Path -Path (Join-Path -Path $testdirectory -ChildPath usr) -ChildPath bin) -ChildPath error + $nonExistentDir = Join-Path -Path (Join-Path -Path $testdirectory -ChildPath usr) -ChildPath bin + $nonExistentPath = Join-Path -Path (Join-Path -Path (Join-Path -Path $testdirectory -ChildPath usr) -ChildPath bin) -ChildPath error } It "Should be called on an existing path without error" { - { Test-Path $testdirectory } | Should -Not -Throw - { Test-Path -Path $testdirectory } | Should -Not -Throw - { Test-Path -LiteralPath $testdirectory } | Should -Not -Throw + { Test-Path $testdirectory } | Should -Not -Throw + { Test-Path -Path $testdirectory } | Should -Not -Throw + { Test-Path -LiteralPath $testdirectory } | Should -Not -Throw } It "Should allow piping objects to it" { - { $testdirectory | Test-Path } | Should -Not -Throw + { $testdirectory | Test-Path } | Should -Not -Throw - $testdirectory | Test-Path | Should -BeTrue - $nonExistentDir | Test-Path | Should -BeFalse + $testdirectory | Test-Path | Should -BeTrue + $nonExistentDir | Test-Path | Should -BeFalse } It "Should be called on a nonexistent path without error" { - { Test-Path -Path $nonExistentPath } | Should -Not -Throw + { Test-Path -Path $nonExistentPath } | Should -Not -Throw } It "Should return false for a nonexistent path" { - Test-Path -Path $nonExistentPath | Should -BeFalse + Test-Path -Path $nonExistentPath | Should -BeFalse } It "Should return true for an existing path" { - { Test-Path -Path $testdirectory } | Should -BeTrue + { Test-Path -Path $testdirectory } | Should -BeTrue + } + + It 'Should return false for an empty string' { + Test-Path -Path '' | Should -BeFalse + } + + It 'Should return false for a whitespace string' { + Test-Path -Path ' ' | Should -BeFalse + } + + It 'Should write a non-terminating error when given a null path' { + # This ensures the error is non-terminating; a terminating error would fail the first test + { Test-Path -Path $null -ErrorAction SilentlyContinue } | Should -Not -Throw + { Test-Path -Path $null -ErrorAction Stop } | Should -Throw -ErrorId 'NullPathNotPermitted,Microsoft.PowerShell.Commands.TestPathCommand' + } + + It 'Should write a non-terminating error when given an array of null paths' { + # This ensures the error is non-terminating; a terminating error would fail the first test + { Test-Path -Path $null, $null -ErrorAction SilentlyContinue } | Should -Not -Throw + { Test-Path -Path $null, $null -ErrorAction Stop } | Should -Throw -ErrorId 'NullPathNotPermitted,Microsoft.PowerShell.Commands.TestPathCommand' } It "Should be able to accept a regular expression" { - { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u*") } | Should -Not -Throw - { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") } | Should -Not -Throw + { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u*") } | Should -Not -Throw + { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") } | Should -Not -Throw } It "Should be able to return the correct result when a regular expression is used" { - Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeTrue - Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[a-z]*") | Should -BeTrue + Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeTrue + Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[a-z]*") | Should -BeTrue - Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "aoeu*") | Should -BeFalse - Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[A-Z]") | Should -BeFalse + Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "aoeu*") | Should -BeFalse + Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[A-Z]") | Should -BeFalse } It "Should return false when the Leaf pathtype is used on a directory" { - Test-Path -Path $testdirectory -PathType Leaf | Should -BeFalse + Test-Path -Path $testdirectory -PathType Leaf | Should -BeFalse } It "Should return true when the Leaf pathtype is used on an existing endpoint" { - Test-Path -Path $testfilename -PathType Leaf | Should -BeTrue + Test-Path -Path $testfilename -PathType Leaf | Should -BeTrue } It "Should return false when the Leaf pathtype is used on a nonexistent file" { - Test-Path -Path "aoeu" -PathType Leaf | Should -BeFalse + Test-Path -Path "aoeu" -PathType Leaf | Should -BeFalse } It "Should return true when the Leaf pathtype is used on a file using the Type alias instead of PathType" { - Test-Path -Path $testfilename -Type Leaf | Should -BeTrue + Test-Path -Path $testfilename -Type Leaf | Should -BeTrue } It "Should be able to search multiple regular expressions using the include switch" { - { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "*") -Include t* } | Should -BeTrue + { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "*") -Include t* } | Should -BeTrue } It "Should be able to exclude a regular expression using the exclude switch" { - { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "*") -Exclude v* } | Should -BeTrue + { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "*") -Exclude v* } | Should -BeTrue } It "Should be able to exclude multiple regular expressions using the exclude switch" { - # tests whether there's any files in the usr directory that don't start with 'd' or 'g' - { Test-Path -Path $testfilename -Exclude d*, g* } | Should -BeTrue + # tests whether there's any files in the usr directory that don't start with 'd' or 'g' + { Test-Path -Path $testfilename -Exclude d*, g* } | Should -BeTrue } It "Should return true if the syntax of the path is correct when using the IsValid switch" { - { Test-Path -Path $nonExistentPath -IsValid } | Should -BeTrue + { Test-Path -Path $nonExistentPath -IsValid } | Should -BeTrue } It "Should return false if the syntax of the path is incorrect when using the IsValid switch" { - $badPath = " :;!@#$%^&*(){}?+|_-" - Test-Path -Path $badPath -IsValid | Should -BeFalse + $badPath = " :;!@#$%^&*(){}?+|_-" + Test-Path -Path $badPath -IsValid | Should -BeFalse } It "Should return true on paths containing spaces when the path is surrounded in quotes" { - Test-Path -Path "/totally a valid/path" -IsValid | Should -BeTrue + Test-Path -Path "/totally a valid/path" -IsValid | Should -BeTrue } It "Should throw on paths containing spaces when the path is not surrounded in quotes" { - { Test-Path -Path /a path/without quotes/around/it -IsValid } | Should -Throw -ErrorId "PositionalParameterNotFound,Microsoft.PowerShell.Commands.TestPathCommand" + { Test-Path -Path /a path/without quotes/around/it -IsValid } | Should -Throw -ErrorId "PositionalParameterNotFound,Microsoft.PowerShell.Commands.TestPathCommand" } It "Should return true if a directory leads or trails with a space when surrounded by quotes" { - Test-Path -Path "/a path / with/funkyspaces" -IsValid | Should -BeTrue + Test-Path -Path "/a path / with/funkyspaces" -IsValid | Should -BeTrue } It "Should return true on a valid path when the LiteralPath switch is used" { - Test-Path -LiteralPath $testfilename | Should -BeTrue + Test-Path -LiteralPath $testfilename | Should -BeTrue } It "Should return false if regular expressions are used with the LiteralPath switch" { - Test-Path -LiteralPath (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeFalse - Test-Path -LiteralPath (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") | Should -BeFalse + Test-Path -LiteralPath (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeFalse + Test-Path -LiteralPath (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") | Should -BeFalse } It "Should return false if regular expressions are used with the LiteralPath alias PSPath switch" { - Test-Path -PSPath (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeFalse - Test-Path -PSPath (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") | Should -BeFalse + Test-Path -PSPath (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeFalse + Test-Path -PSPath (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") | Should -BeFalse } It "Should return true if used on components other than filesystem objects" { - Test-Path Alias:\gci | Should -BeTrue - Test-Path Env:\PATH | Should -BeTrue + Test-Path Alias:\gci | Should -BeTrue + Test-Path Env:\PATH | Should -BeTrue } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageValidation.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageValidation.Tests.ps1 index 09ec771fe13..f148b2fb727 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageValidation.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Security/ConstrainedLanguageValidation.Tests.ps1 @@ -54,6 +54,28 @@ try } } + Describe "Start-Job initialization script should work in system lock down" -Tags 'Feature','RequireAdminOnWindows' { + + It "Verifies that Start-Job initialization script runs successfully in system lock down" { + + try + { + Invoke-LanguageModeTestingSupportCmdlet -SetLockdownMode + $ExecutionContext.SessionState.LanguageMode = "ConstrainedLanguage" + + $job = Start-Job -InitializationScript { function Hello { "Hello" } } -ScriptBlock { Hello } + $result = $job | Wait-Job | Receive-Job + } + finally + { + Invoke-LanguageModeTestingSupportCmdlet -RevertLockdownMode -EnableFullLanguageMode + } + + $result | Should -BeExactly "Hello" + $job | Remove-Job + } + } + # End Describe blocks } finally diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/Join-String.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/Join-String.Tests.ps1 new file mode 100644 index 00000000000..ce979da8af2 --- /dev/null +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/Join-String.Tests.ps1 @@ -0,0 +1,114 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +Describe "Join-String" -Tags "CI" { + + BeforeAll { + $testObject = Get-ChildItem + } + + It "Should be called using the InputObject without error with no other switches" { + { Join-String -InputObject $testObject } | Should -Not -Throw + } + + It "Should return a single string" { + $actual = $testObject | Join-String + + $actual.Count | Should -Be 1 + $actual | Should -BeOfType System.String + } + + It "Should join property values with default separator" { + $expected = $testObject.Name -join $ofs + $actual = $testObject | Join-String -Property Name + $actual | Should -BeExactly $expected + } + + It "Should join property values positionally with default separator" { + $expected = $testObject.Name -join $ofs + $actual = $testObject | Join-String Name + $actual | Should -BeExactly $expected + } + + It "Should join property values with custom Separator" { + $expected = $testObject.Name -join "; " + $actual = $testObject | Join-String -Property Name -Separator "; " + $actual | Should -BeExactly $expected + } + + It "Should join property values SingleQuoted" { + $expected = ($testObject.Name).Foreach{"'$_'"} -join "; " + $actual = $testObject | Join-String -Property Name -Separator "; " -SingleQuote + $actual | Should -BeExactly $expected + } + + It "Should join property values DoubleQuoted" { + $expected = ($testObject.Name).Foreach{"""$_"""} -join "; " + $actual = $testObject | Join-String -Property Name -Separator "; " -DoubleQuote + $actual | Should -BeExactly $expected + } + + It "Should join property values Formatted" { + $expected = ($testObject.Name).Foreach{"[$_]"} -join "; " + $actual = $testObject | Join-String -Property Name -Separator "; " -Format "[{0}]" + $actual | Should -BeExactly $expected + } + + It "Should join script block results with default separator" { + $sb = {$_.Name + $_.Length} + $expected = ($testObject | ForEach-Object $sb) -join $ofs + $actual = $testObject | Join-String -Property $sb + $actual | Should -BeExactly $expected + } + + It "Should join script block results with custom separator" { + $sb = {$_.Name + $_.Length} + $expected = ($testObject | ForEach-Object $sb) -join "; " + $actual = $testObject | Join-String -Property $sb -Separator "; " + $actual | Should -BeExactly $expected + } + + It "Should join script block results SingleQuoted" { + $sb = {$_.Name + $_.Length} + $expected = ($testObject | ForEach-Object $sb).Foreach{"'$_'"} -join $ofs + $actual = $testObject | Join-String -Property $sb -SingleQuote + $actual | Should -BeExactly $expected + } + It "Should join script block results DoubleQuoted" { + $sb = {$_.Name + $_.Length} + $expected = ($testObject | ForEach-Object $sb).Foreach{"""$_"""} -join $ofs + $actual = $testObject | Join-String -Property $sb -DoubleQuote + $actual | Should -BeExactly $expected + } + + It "Should join script block results with Format and separator" { + $sb = {$_.Name + $_.Length} + $expected = ($testObject | ForEach-Object $sb).Foreach{"[{0}]" -f $_} -join "; " + $actual = $testObject | Join-String -Property $sb -Separator "; " -Format "[{0}]" + $actual | Should -BeExactly $expected + } + + It "Should Handle OutputPrefix and OutputSuffix" { + $ofs = ',' + $expected = "A 1,2,3 B" + $actual = 1..3 | Join-String -OutputPrefix "A " -OutputSuffix " B" + $actual | Should -BeExactly $expected + } + + It "Should handle null separator" { + $expected = -join 'hello'.tochararray() + $actual = "hello" | Join-String -separator $null + $actual | Should -BeExactly $expected + } + + It "Should tabcomplete InputObject properties" { + $cmd = '[io.fileinfo]::new("c:\temp") | Join-String -Property ' + $res = tabexpansion2 $cmd $cmd.length + $completionTexts = $res.CompletionMatches.CompletionText + $Propertys = [io.fileinfo]::new($PSScriptRoot).psobject.properties.Name + foreach ($n in $Propertys) { + $n -in $completionTexts | Should -BeTrue + } + } + +} diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 index cc60c28641f..5fd62c5f895 100644 --- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 +++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 @@ -221,9 +221,9 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Enable-PSSessionConfiguration", , $($FullCLR -or $CoreWindows ) "Cmdlet", "Enable-RunspaceDebug", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Enable-WSManCredSSP", , $($FullCLR -or $CoreWindows ) -"Cmdlet", "Enter-PSHostProcess", , $($FullCLR -or $CoreWindows ) +"Cmdlet", "Enter-PSHostProcess", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Enter-PSSession", , $($FullCLR -or $CoreWindows -or $CoreUnix) -"Cmdlet", "Exit-PSHostProcess", , $($FullCLR -or $CoreWindows ) +"Cmdlet", "Exit-PSHostProcess", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Exit-PSSession", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Export-Alias", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Export-Clixml", , $($FullCLR -or $CoreWindows -or $CoreUnix) @@ -279,7 +279,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Get-PSBreakpoint", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-PSCallStack", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-PSDrive", , $($FullCLR -or $CoreWindows -or $CoreUnix) -"Cmdlet", "Get-PSHostProcessInfo", , $($FullCLR -or $CoreWindows ) +"Cmdlet", "Get-PSHostProcessInfo", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-PSProvider", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-PSSession", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Get-PSSessionCapability", , $($FullCLR -or $CoreWindows ) @@ -320,6 +320,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "Invoke-WmiMethod", , $($FullCLR ) "Cmdlet", "Invoke-WSManAction", , $($FullCLR -or $CoreWindows ) "Cmdlet", "Join-Path", , $($FullCLR -or $CoreWindows -or $CoreUnix) +"Cmdlet", "Join-String", , $( $CoreWindows -or $CoreUnix) "Cmdlet", "Limit-EventLog", , $($FullCLR ) "Cmdlet", "Measure-Command", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "Measure-Object", , $($FullCLR -or $CoreWindows -or $CoreUnix) @@ -339,7 +340,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "New-PSRoleCapabilityFile", , $( $CoreWindows -or $CoreUnix) "Cmdlet", "New-PSSession", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "New-PSSessionConfigurationFile", , $($FullCLR -or $CoreWindows ) -"Cmdlet", "New-PSSessionOption", , $($FullCLR -or $CoreWindows ) +"Cmdlet", "New-PSSessionOption", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "New-PSTransportOption", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "New-Service", , $($FullCLR -or $CoreWindows ) "Cmdlet", "New-TemporaryFile", , $( $CoreWindows -or $CoreUnix) diff --git a/test/powershell/engine/Remoting/PSSession.Tests.ps1 b/test/powershell/engine/Remoting/PSSession.Tests.ps1 new file mode 100644 index 00000000000..8221e405e95 --- /dev/null +++ b/test/powershell/engine/Remoting/PSSession.Tests.ps1 @@ -0,0 +1,79 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# +# PSSession tests for non-Windows platforms +# + +Describe "New-PSSessionOption parameters for non-Windows platforms" -Tag "CI" { + + BeforeAll { + $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() + + if ($IsWindows) { + $PSDefaultParameterValues['it:skip'] = $true + } + } + + AfterAll { + $global:PSDefaultParameterValues = $originalDefaultParameterValues + } + + It "Verifies New-PSSessionOption parameters" { + + $cmdInfo = Get-Command New-PSSessionOption + + $commonParameterCount = [System.Management.Automation.Internal.CommonParameters].GetProperties().Length + $cmdInfo.Parameters.Count | Should -Be ($commonParameterCount + 2) -Because "Only -SkipCACheck and -SkipCNCheck switch parameters are available" + + { $null = $cmdInfo.ResolveParameter("SkipCACheck") } | Should -Not -Throw -Because "SkipCACheck parameter should be available" + { $null = $cmdInfo.ResolveParameter("SkipCNCheck") } | Should -Not -Throw -Because "SkipCNCheck parameter should be available" + } +} + +Describe "SkipCACheck and SkipCNCheck PSSession options are required for New-PSSession on non-Windows platforms" -Tag "CI" { + + BeforeAll { + $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() + + if ($IsWindows) { + $PSDefaultParameterValues['it:skip'] = $true + } + else { + $userName = "User_$(Get-Random -Maximum 99999)" + $userPassword = "Password_$(Get-Random -Maximum 99999)" + $cred = [pscredential]::new($userName, (ConvertTo-SecureString -String $userPassword -AsPlainText -Force)) + $soSkipCA = New-PSSessionOption -SkipCACheck + $soSkipCN = New-PSSessionOption -SkipCNCheck + } + } + + AfterAll { + $global:PSDefaultParameterValues = $originalDefaultParameterValues + } + + $testCases = @( + @{ + Name = 'Verifies expected error when session options is missing' + ScriptBlock = { New-PSSession -cn localhost -Credential $cred -Authentication Basic -UseSSL } + ExpectedErrorCode = 825 + }, + @{ + Name = 'Verifies expected error when SkipCACheck option is missing' + ScriptBlock = { New-PSSession -cn localhost -Credential $cred -Authentication Basic -UseSSl -SessionOption $soSkipCN } + ExpectedErrorCode = 825 + }, + @{ + Name = 'Verifies expected error when SkipCNCheck option is missing' + ScriptBlock = { New-PSSession -cn localhost -Credential $cred -Authentication Basic -UseSSl -SessionOption $soSkipCA } + ExpectedErrorCode = 825 + } + ) + + It "" -TestCases $testCases { + param ($scriptBlock, $expectedErrorCode) + + $er = { & $scriptBlock } | Should -Throw -ErrorId 'System.Management.Automation.Remoting.PSRemotingDataStructureException,Microsoft.PowerShell.Commands.NewPSSessionCommand' -PassThru + $er.Exception.ErrorCode | Should -Be $expectedErrorCode + } +} diff --git a/test/xUnit/Asserts/PriorityAttribute.cs b/test/xUnit/Asserts/PriorityAttribute.cs new file mode 100644 index 00000000000..741dd12391d --- /dev/null +++ b/test/xUnit/Asserts/PriorityAttribute.cs @@ -0,0 +1,12 @@ +using System; + +[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] +public class TestPriorityAttribute : Attribute +{ + public TestPriorityAttribute(int priority) + { + Priority = priority; + } + + public int Priority { get; private set; } +} diff --git a/test/xUnit/Asserts/PriorityOrderer.cs b/test/xUnit/Asserts/PriorityOrderer.cs new file mode 100644 index 00000000000..b0675b55f1d --- /dev/null +++ b/test/xUnit/Asserts/PriorityOrderer.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace TestOrder.TestCaseOrdering +{ + public class PriorityOrderer : ITestCaseOrderer + { + public IEnumerable OrderTestCases(IEnumerable testCases) where TTestCase : ITestCase + { + var sortedMethods = new SortedDictionary>(); + + foreach (TTestCase testCase in testCases) + { + int priority = 0; + + foreach (IAttributeInfo attr in testCase.TestMethod.Method.GetCustomAttributes((typeof(TestPriorityAttribute).AssemblyQualifiedName))) + priority = attr.GetNamedArgument("Priority"); + + GetOrCreate(sortedMethods, priority).Add(testCase); + } + + foreach (var list in sortedMethods.Keys.Select(priority => sortedMethods[priority])) + { + list.Sort((x, y) => StringComparer.OrdinalIgnoreCase.Compare(x.TestMethod.Method.Name, y.TestMethod.Method.Name)); + foreach (TTestCase testCase in list) + yield return testCase; + } + } + + static TValue GetOrCreate(IDictionary dictionary, TKey key) where TValue : new() + { + TValue result; + + if (dictionary.TryGetValue(key, out result)) return result; + + result = new TValue(); + dictionary[key] = result; + + return result; + } + } +} diff --git a/test/xUnit/README.md b/test/xUnit/README.md new file mode 100644 index 00000000000..9be03d2e61a --- /dev/null +++ b/test/xUnit/README.md @@ -0,0 +1,29 @@ +# xUnit Tests + +The folder contains xUnit tests for PowerShell Core project. + +## Running xUnit Tests + +Go to the top level of the PowerShell repository and run full set of tests: +`Start-PSxUnit` inside a self-hosted copy of PowerShell. + +Go to the test project folder and run `dotnet test -c Release`. + +Use [`filter`](xunit-filter) parameter to run only needed tests: +```powershell +dotnet test -c Release --filter "FullyQualifiedName~UnitTest1 # Runs tests which have UnitTest1 in FullyQualifiedName +dotnet test --filter Name~TestMethod1 # Runs tests whose name contains TestMethod1 +``` + +## Creating xUnit Tests + +Keep the folder structure that is for Pester [../../test/powershell](../../test/powershell) and C# files [../../src](../../src). + +Use names of namespaces started with `PSTests`. +```c# +namespace PSTests.YourNameSpace +{ +} +``` + +[xunit-filter]: https://docs.microsoft.com/en-us/dotnet/core/testing/selective-unit-tests \ No newline at end of file diff --git a/test/csharp/test_Binders.cs b/test/xUnit/csharp/test_Binders.cs similarity index 100% rename from test/csharp/test_Binders.cs rename to test/xUnit/csharp/test_Binders.cs diff --git a/test/csharp/test_CorePsPlatform.cs b/test/xUnit/csharp/test_CorePsPlatform.cs similarity index 100% rename from test/csharp/test_CorePsPlatform.cs rename to test/xUnit/csharp/test_CorePsPlatform.cs diff --git a/test/csharp/test_ExtensionMethods.cs b/test/xUnit/csharp/test_ExtensionMethods.cs similarity index 100% rename from test/csharp/test_ExtensionMethods.cs rename to test/xUnit/csharp/test_ExtensionMethods.cs diff --git a/test/csharp/test_FileSystemProvider.cs b/test/xUnit/csharp/test_FileSystemProvider.cs similarity index 100% rename from test/csharp/test_FileSystemProvider.cs rename to test/xUnit/csharp/test_FileSystemProvider.cs diff --git a/test/csharp/test_MshSnapinInfo.cs b/test/xUnit/csharp/test_MshSnapinInfo.cs similarity index 100% rename from test/csharp/test_MshSnapinInfo.cs rename to test/xUnit/csharp/test_MshSnapinInfo.cs diff --git a/test/csharp/test_PSConfiguration.cs b/test/xUnit/csharp/test_PSConfiguration.cs similarity index 99% rename from test/csharp/test_PSConfiguration.cs rename to test/xUnit/csharp/test_PSConfiguration.cs index e474ff699a0..2480225879b 100644 --- a/test/csharp/test_PSConfiguration.cs +++ b/test/xUnit/csharp/test_PSConfiguration.cs @@ -12,6 +12,7 @@ namespace PSTests.Sequential { + [TestCaseOrderer("TestOrder.TestCaseOrdering.PriorityOrderer", "powershell-tests")] public class PowerShellPolicyFixture : IDisposable { private const string configFileName = "powershell.config.json"; @@ -344,7 +345,7 @@ public PowerShellPolicyTests(PowerShellPolicyFixture fixture) this.fixture = fixture; } - [Fact] + [Fact, TestPriority(1)] public void PowerShellConfig_GetPowerShellPolicies_BothConfigFilesNotEmpty() { fixture.SetupConfigFile1(); @@ -358,7 +359,7 @@ public void PowerShellConfig_GetPowerShellPolicies_BothConfigFilesNotEmpty() fixture.CompareTwoPolicies(userPolicies, fixture.CurrentUserPolicies); } - [Fact] + [Fact, TestPriority(2)] public void PowerShellConfig_GetPowerShellPolicies_EmptyUserConfig() { fixture.SetupConfigFile2(); @@ -371,7 +372,7 @@ public void PowerShellConfig_GetPowerShellPolicies_EmptyUserConfig() fixture.CompareTwoPolicies(sysPolicies, fixture.SystemWidePolicies); } - [Fact] + [Fact, TestPriority(3)] public void PowerShellConfig_GetPowerShellPolicies_EmptySystemConfig() { fixture.SetupConfigFile3(); @@ -384,7 +385,7 @@ public void PowerShellConfig_GetPowerShellPolicies_EmptySystemConfig() fixture.CompareTwoPolicies(userPolicies, fixture.CurrentUserPolicies); } - [Fact] + [Fact, TestPriority(4)] public void PowerShellConfig_GetPowerShellPolicies_BothConfigFilesEmpty() { fixture.SetupConfigFile4(); @@ -395,7 +396,7 @@ public void PowerShellConfig_GetPowerShellPolicies_BothConfigFilesEmpty() Assert.Null(userPolicies); } - [Fact] + [Fact, TestPriority(5)] public void PowerShellConfig_GetPowerShellPolicies_BothConfigFilesNotExist() { fixture.CleanupConfigFiles(); @@ -406,7 +407,7 @@ public void PowerShellConfig_GetPowerShellPolicies_BothConfigFilesNotExist() Assert.Null(userPolicies); } - [Fact] + [Fact, TestPriority(6)] public void Utils_GetPolicySetting_BothConfigFilesNotEmpty() { fixture.SetupConfigFile1(); @@ -504,7 +505,7 @@ public void Utils_GetPolicySetting_BothConfigFilesNotEmpty() fixture.CompareConsoleSessionConfiguration(consoleSessionConfiguration, fixture.SystemWidePolicies.ConsoleSessionConfiguration); } - [Fact] + [Fact, TestPriority(7)] public void Utils_GetPolicySetting_EmptyUserConfig() { fixture.SetupConfigFile2(); @@ -602,7 +603,7 @@ public void Utils_GetPolicySetting_EmptyUserConfig() fixture.CompareConsoleSessionConfiguration(consoleSessionConfiguration, fixture.SystemWidePolicies.ConsoleSessionConfiguration); } - [Fact] + [Fact, TestPriority(8)] public void Utils_GetPolicySetting_EmptySystemConfig() { fixture.SetupConfigFile3(); @@ -701,7 +702,7 @@ public void Utils_GetPolicySetting_EmptySystemConfig() fixture.CompareConsoleSessionConfiguration(consoleSessionConfiguration, null); } - [Fact] + [Fact, TestPriority(9)] public void Utils_GetPolicySetting_BothConfigFilesEmpty() { fixture.SetupConfigFile4(); @@ -800,7 +801,7 @@ public void Utils_GetPolicySetting_BothConfigFilesEmpty() fixture.CompareConsoleSessionConfiguration(consoleSessionConfiguration, null); } - [Fact] + [Fact, TestPriority(10)] public void Utils_GetPolicySetting_BothConfigFilesNotExist() { fixture.CleanupConfigFiles(); diff --git a/test/csharp/test_PSVersionInfo.cs b/test/xUnit/csharp/test_PSVersionInfo.cs similarity index 100% rename from test/csharp/test_PSVersionInfo.cs rename to test/xUnit/csharp/test_PSVersionInfo.cs diff --git a/test/csharp/test_Runspace.cs b/test/xUnit/csharp/test_Runspace.cs similarity index 98% rename from test/csharp/test_Runspace.cs rename to test/xUnit/csharp/test_Runspace.cs index c9f06112407..5b72618026e 100644 --- a/test/csharp/test_Runspace.cs +++ b/test/xUnit/csharp/test_Runspace.cs @@ -66,7 +66,7 @@ public void TestRunspaceWithPowerShell() [Fact] public void TestRunspaceWithPowerShellAndInitialSessionState() { - InitialSessionState iss = InitialSessionState.CreateDefault2(); + InitialSessionState iss = InitialSessionState.CreateDefault(); // NOTE: instantiate custom host myHost for the next line to capture stdout and stderr output // in addition to just the PSObjects @@ -90,9 +90,11 @@ public void TestRunspaceWithPowerShellAndInitialSessionState() ++objCount; Assert.NotNull(result); } + Assert.Equal(count, objCount); - powerShell.Dispose(); } + + runspace.Close(); } } } diff --git a/test/csharp/test_SecuritySupport.cs b/test/xUnit/csharp/test_SecuritySupport.cs similarity index 100% rename from test/csharp/test_SecuritySupport.cs rename to test/xUnit/csharp/test_SecuritySupport.cs diff --git a/test/csharp/test_SessionState.cs b/test/xUnit/csharp/test_SessionState.cs similarity index 100% rename from test/csharp/test_SessionState.cs rename to test/xUnit/csharp/test_SessionState.cs diff --git a/test/csharp/test_Utils.cs b/test/xUnit/csharp/test_Utils.cs similarity index 100% rename from test/csharp/test_Utils.cs rename to test/xUnit/csharp/test_Utils.cs diff --git a/test/csharp/csharp.tests.csproj b/test/xUnit/xUnit.tests.csproj similarity index 95% rename from test/csharp/csharp.tests.csproj rename to test/xUnit/xUnit.tests.csproj index af62b5447e6..72f52593915 100644 --- a/test/csharp/csharp.tests.csproj +++ b/test/xUnit/xUnit.tests.csproj @@ -5,6 +5,7 @@ PowerShell xUnit Tests powershell-tests + true win7-x86;win7-x64;osx-x64;linux-x64 @@ -26,5 +27,4 @@ - diff --git a/tools/appveyor.psm1 b/tools/appveyor.psm1 index 270e21d73fb..227f1847fb5 100644 --- a/tools/appveyor.psm1 +++ b/tools/appveyor.psm1 @@ -369,7 +369,6 @@ function Invoke-AppVeyorTest Write-Host -Foreground Green 'Run CoreCLR tests' $testResultsNonAdminFile = "$pwd\TestsResultsNonAdmin.xml" $testResultsAdminFile = "$pwd\TestsResultsAdmin.xml" - $SequentialXUnitTestResultsFile = "$pwd\SequentialXUnitTestResults.xml" $ParallelXUnitTestResultsFile = "$pwd\ParallelXUnitTestResults.xml" if(!(Test-Path "$env:CoreOutput\pwsh.exe")) { @@ -443,19 +442,13 @@ function Invoke-AppVeyorTest Write-Host -Foreground Green 'Upload CoreCLR Admin test results' Update-AppVeyorTestResults -resultsFile $testResultsAdminFile - Start-PSxUnit -SequentialTestResultsFile $SequentialXUnitTestResultsFile -ParallelTestResultsFile $ParallelXUnitTestResultsFile + Start-PSxUnit -ParallelTestResultsFile $ParallelXUnitTestResultsFile Write-Host -ForegroundColor Green 'Uploading PSxUnit test results' - Update-AppVeyorTestResults -resultsFile $SequentialXUnitTestResultsFile Update-AppVeyorTestResults -resultsFile $ParallelXUnitTestResultsFile # Fail the build, if tests failed Test-PSPesterResults -TestResultsFile $testResultsAdminFile - @( - $SequentialXUnitTestResultsFile, - $ParallelXUnitTestResultsFile - ) | ForEach-Object { - Test-XUnitTestResults -TestResultsFile $_ - } + Test-XUnitTestResults -TestResultsFile $ParallelXUnitTestResultsFile # Run tests with specified experimental features enabled foreach ($entry in $ExperimentalFeatureTests.GetEnumerator()) { @@ -502,6 +495,12 @@ function Invoke-AppVeyorAfterTest $codeCoverageArtifacts | ForEach-Object { Push-Artifact -Path $_ } + + New-TestPackage -Destination (Get-Location).Path + $testPackageFullName = Join-Path $pwd 'TestPackage.zip' + Write-Verbose "Created TestPackage.zip" -Verbose + Write-Host -ForegroundColor Green 'Upload test package' + Push-Artifact $testPackageFullName } } diff --git a/tools/install-powershell.sh b/tools/install-powershell.sh index 673a861dc85..ab22c01994e 100755 --- a/tools/install-powershell.sh +++ b/tools/install-powershell.sh @@ -1,10 +1,12 @@ #!/bin/bash +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. install(){ #Companion code for the blog https://cloudywindows.com #call this code direction from the web with: - #bash <(wget -O - https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh) + #bash <(wget -qO - https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh) #wget -O - https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh | bash -s #bash <(curl -s https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh) @@ -123,29 +125,41 @@ install(){ if [[ "'$*'" =~ appimage ]] ; then if [ -f $SCRIPTFOLDER/appimage.sh ]; then - #Script files were copied local - use them - . $SCRIPTFOLDER/appimage.sh + #Script files were copied local - use them + . $SCRIPTFOLDER/appimage.sh else - #Script files are not local - pull from remote - echo "Could not find \"appimage.sh\" next to this script..." - echo "Pulling it from \"$gitreposcriptroot/appimage.sh\"" - bash <(wget -qO- $gitreposcriptroot/appimage.sh) $@ - fi + #Script files are not local - pull from remote + echo "Could not find \"appimage.sh\" next to this script..." + echo "Pulling and executing it from \"$gitreposcriptroot/appimage.sh\"" + if [ -n "$(command -v curl)" ]; then + echo "found and using curl" + bash <(curl -s $gitreposcriptroot/appimage.sh) $@ + elif [ -n "$(command -v wget)" ]; then + echo "found and using wget" + bash <(wget -qO- $gitreposcriptroot/appimage.sh) $@ + else + echo "Could not find curl or wget, install one of these or manually download \"$gitreposcriptroot/appimage.sh\"" + fi + fi elif [ "$DistroBasedOn" == "redhat" ] || [ "$DistroBasedOn" == "debian" ] || [ "$DistroBasedOn" == "osx" ] || [ "$DistroBasedOn" == "suse" ] || [ "$DistroBasedOn" == "amazonlinux" ]; then echo "Configuring PowerShell Core Environment for: $DistroBasedOn $DIST $REV" if [ -f $SCRIPTFOLDER/installpsh-$DistroBasedOn.sh ]; then - #Script files were copied local - use them - . $SCRIPTFOLDER/installpsh-$DistroBasedOn.sh + #Script files were copied local - use them + . $SCRIPTFOLDER/installpsh-$DistroBasedOn.sh else - #Script files are not local - pull from remote - echo "Could not find \"installpsh-$DistroBasedOn.sh\" next to this script..." - echo "Pulling it from \"$gitreposcriptroot/installpsh-$DistroBasedOn.sh\"" - if [ "$OS" == "osx" ]; then - bash <(curl -s $gitreposcriptroot/installpsh-$DistroBasedOn.sh) $@ - else - bash <(wget -qO- $gitreposcriptroot/installpsh-$DistroBasedOn.sh) $@ + #Script files are not local - pull from remote + echo "Could not find \"installpsh-$DistroBasedOn.sh\" next to this script..." + echo "Pulling and executing it from \"$gitreposcriptroot/installpsh-$DistroBasedOn.sh\"" + if [ -n "$(command -v curl)" ]; then + echo "found and using curl" + bash <(curl -s $gitreposcriptroot/installpsh-$DistroBasedOn.sh) $@ + elif [ -n "$(command -v wget)" ]; then + echo "found and using wget" + bash <(wget -qO- $gitreposcriptroot/installpsh-$DistroBasedOn.sh) $@ + else + echo "Could not find curl or wget, install one of these or manually download \"$gitreposcriptroot/installpsh-$DistroBasedOn.sh\"" + fi fi - fi else echo "Sorry, your operating system is based on $DistroBasedOn and is not supported by PowerShell Core or this installer at this time." fi diff --git a/tools/metadata.json b/tools/metadata.json index 38adc09179d..8018e2e9cd6 100644 --- a/tools/metadata.json +++ b/tools/metadata.json @@ -1,7 +1,7 @@ { "StableReleaseTag": "v6.1.1", - "PreviewReleaseTag": "v6.2.0-preview.1", + "PreviewReleaseTag": "v6.2.0-preview.2", "ServicingReleaseTag": "v6.0.5", "ReleaseTag": "v6.1.1", - "NextReleaseTag": "v6.2.0-preview.1" + "NextReleaseTag": "v6.2.0-preview.2" } diff --git a/tools/packaging/packaging.psm1 b/tools/packaging/packaging.psm1 index be0ff24edc5..611bc96db67 100644 --- a/tools/packaging/packaging.psm1 +++ b/tools/packaging/packaging.psm1 @@ -25,7 +25,7 @@ function Start-PSPackage { [string]$Name = "powershell", # Ubuntu, CentOS, Fedora, macOS, and Windows packages are supported - [ValidateSet("deb", "osxpkg", "rpm", "msi", "zip", "AppImage", "nupkg", "tar", "tar-arm", 'tar-musl', 'fxdependent')] + [ValidateSet("deb", "osxpkg", "rpm", "msi", "zip", "AppImage", "nupkg", "tar", "tar-arm", 'tar-alpine', 'fxdependent')] [string[]]$Type, # Generate windows downlevel package @@ -67,8 +67,8 @@ function Start-PSPackage { $WindowsRuntime, "Release" } elseif ($Type -eq "tar-arm") { New-PSOptions -Configuration "Release" -Runtime "Linux-ARM" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } - } elseif ($Type -eq "tar-musl") { - New-PSOptions -Configuration "Release" -Runtime "Linux-musl-x64" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } + } elseif ($Type -eq "tar-alpine") { + New-PSOptions -Configuration "Release" -Runtime "alpine-x64" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } } else { New-PSOptions -Configuration "Release" -WarningAction SilentlyContinue | ForEach-Object { $_.Runtime, $_.Configuration } } @@ -112,7 +112,7 @@ function Start-PSPackage { $PSModuleRestoreCorrect = $true } - $precheckFailed = if ($Type -eq 'fxdependent') { + $precheckFailed = if ($Type -eq 'fxdependent' -or $Type -eq 'tar-alpine') { ## We do not check for runtime and crossgen for framework dependent package. -not $Script:Options -or ## Start-PSBuild hasn't been executed yet -not $PSModuleRestoreCorrect -or ## Last build didn't specify '-PSModuleRestore' correctly @@ -376,13 +376,13 @@ function Start-PSPackage { New-TarballPackage @Arguments } } - "tar-musl" { + "tar-alpine" { $Arguments = @{ PackageSourcePath = $Source Name = $Name Version = $Version Force = $Force - Architecture = "musl-x64" + Architecture = "alpine-x64" } if ($PSCmdlet.ShouldProcess("Create tar.gz Package")) { @@ -767,6 +767,7 @@ function New-UnixPackage { $ManGzipInfo = New-ManGzip -IsPreview:$IsPreview # Change permissions for packaging + Write-Log "Setting permissions..." Start-NativeExecution { find $Staging -type d | xargs chmod 755 find $Staging -type f | xargs chmod 644 @@ -779,6 +780,7 @@ function New-UnixPackage { # Add macOS powershell launcher if ($Type -eq "osxpkg") { + Write-Log "Adding macOS launch opplication..." if ($pscmdlet.ShouldProcess("Add macOS launch application")) { # Generate launcher app folder @@ -816,10 +818,12 @@ function New-UnixPackage { # Build package try { if ($pscmdlet.ShouldProcess("Create $type package")) { + Write-Log "Creating package with fpm..." $Output = Start-NativeExecution { fpm $Arguments } } } finally { if ($Environment.IsMacOS) { + Write-Log "Starting Cleanup for mac packaging..." if ($pscmdlet.ShouldProcess("Cleanup macOS launcher")) { Clear-MacOSLauncher @@ -1203,6 +1207,7 @@ function New-ManGzip $IsPreview ) + Write-Log "Creating man gz..." # run ronn to convert man page to roff $RonnFile = "$RepoRoot/assets/pwsh.1.ronn" if ($IsPreview.IsPresent) @@ -1215,7 +1220,8 @@ function New-ManGzip $RoffFile = $RonnFile -replace "\.ronn$" # Run ronn on assets file - Start-NativeExecution { ronn --roff $RonnFile } -VerboseOutputOnError + Write-Log "Creating man gz - running ronn..." + Start-NativeExecution { ronn --roff $RonnFile } if ($IsPreview.IsPresent) { @@ -1224,6 +1230,7 @@ function New-ManGzip # gzip in assets directory $GzipFile = "$RoffFile.gz" + Write-Log "Creating man gz - running gzip..." Start-NativeExecution { gzip -f $RoffFile } -VerboseOutputOnError $ManFile = Join-Path "/usr/local/share/man/man1" (Split-Path -Leaf $GzipFile) @@ -1492,7 +1499,7 @@ function New-UnifiedNugetPackage [string] $LinuxArm32BinPath, [Parameter(Mandatory = $false)] - [string] $LinuxMuslBinPath, + [string] $LinuxAlpineBinPath, [Parameter(Mandatory = $true)] [string] $LinuxBinPath, @@ -1561,9 +1568,9 @@ function New-UnifiedNugetPackage { CreateNugetPlatformFolder -Platform 'linux-arm' -PackageRuntimesFolder $packageRuntimesFolderPath -PlatformBinPath $linuxArm32BinPath - if ($linuxMuslBinPath) + if ($linuxAlpineBinPath) { - CreateNugetPlatformFolder -Platform 'linux-musl-x64' -PackageRuntimesFolder $packageRuntimesFolderPath -PlatformBinPath $linuxMuslBinPath + CreateNugetPlatformFolder -Platform 'alpine-x64' -PackageRuntimesFolder $packageRuntimesFolderPath -PlatformBinPath $LinuxAlpineBinPath } CreateNugetPlatformFolder -Platform 'linux-x64' -PackageRuntimesFolder $packageRuntimesFolderPath -PlatformBinPath $linuxBinPath diff --git a/tools/releaseBuild/Images/GenericLinuxFiles/PowerShellPackage.ps1 b/tools/releaseBuild/Images/GenericLinuxFiles/PowerShellPackage.ps1 index 67a6992a257..377ad694058 100644 --- a/tools/releaseBuild/Images/GenericLinuxFiles/PowerShellPackage.ps1 +++ b/tools/releaseBuild/Images/GenericLinuxFiles/PowerShellPackage.ps1 @@ -18,7 +18,8 @@ param ( [switch]$AppImage, [switch]$TarX64, [switch]$TarArm, - [switch]$FxDependent + [switch]$FxDependent, + [switch]$Alpine ) $releaseTagParam = @{} @@ -39,6 +40,8 @@ try { if($FxDependent.IsPresent) { $buildParams.Add("Runtime", "fxdependent") + } elseif ($Alpine.IsPresent) { + $buildParams.Add("Runtime", 'alpine-x64') } else { $buildParams.Add("Crossgen", $true) } @@ -47,6 +50,8 @@ try { if($FxDependent) { Start-PSPackage -Type 'fxdependent' @releaseTagParam + } elseif ($Alpine) { + Start-PSPackage -Type 'tar-alpine' @releaseTagParam } else { Start-PSPackage @releaseTagParam } diff --git a/tools/releaseBuild/Images/microsoft_powershell_alpine3/Dockerfile b/tools/releaseBuild/Images/microsoft_powershell_alpine3/Dockerfile index 4bdbd2a0afd..af7ec8229be 100644 --- a/tools/releaseBuild/Images/microsoft_powershell_alpine3/Dockerfile +++ b/tools/releaseBuild/Images/microsoft_powershell_alpine3/Dockerfile @@ -1,12 +1,14 @@ # Docker image file that describes an Centos7 image with PowerShell installed from Microsoft YUM Repo -FROM microsoft/dotnet:2.1-sdk-alpine +FROM mcr.microsoft.com/powershell:6.1.0-alpine-3.8 LABEL maintainer="PowerShell Team " # Install dependencies and clean up RUN apk update \ - && apk add cmake clang build-base git bash + && apk add libunwind libcurl cmake clang build-base git bash curl + +COPY PowerShellPackage.ps1 / ENV DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 -COPY build-and-run-pwsh.sh / +ENTRYPOINT [ "pwsh" ] diff --git a/tools/releaseBuild/Images/microsoft_powershell_alpine3/build-and-run-pwsh.sh b/tools/releaseBuild/Images/microsoft_powershell_alpine3/build-and-run-pwsh.sh deleted file mode 100755 index e7144c9c505..00000000000 --- a/tools/releaseBuild/Images/microsoft_powershell_alpine3/build-and-run-pwsh.sh +++ /dev/null @@ -1,104 +0,0 @@ -#!/bin/sh - -set -e - -# this script needs to be run from within Alpine with dotnet 2.1 SDK installed, example: -# docker run -it -v ~/repos/PowerShell:/PowerShell microsoft/dotnet:2.1-sdk-alpine - -# build tools required: -# apk update -# apk add build-base gcc abuild binutils git python bash cmake - -# run from root of PowerShell repo: -# tools/releaseBuild/Images/microsoft_powershell_alpine3/build-and-run-pwsh.sh /PowerShell /PowerShell 6.1.0 - -repoRoot=$1 -destination=$2 -releaseTag=$3 - -# in Alpine, the currently supported architectures are: - -# x86_64 -# x86 -# aarch64 -# armhf -# ppc64le -# s390x - -# from https://pkgs.alpinelinux.org/packages (Arch dropdown menu) - -arch=`uname -m` - -case $arch in - x86_64) - arch=x64 - ;; - aarch64) - arch=arm64 - ;; - armhf) - arch=arm - ;; - *) - echo "Error: Unsupported OS architecture $arch detected" - exit 1 - ;; -esac - -# set variables depending on releaseTag -# remove v from release tag (v3.5 => 3.5) -if [ "${releaseTag:0:1}" = "v" ]; then - releaseTag=${releaseTag:1} - tarName=$destination/powershell-$releaseTag-linux-musl-$arch.tar.gz - dotnetArguments=/p:ReleaseTag=$releaseTag; -else - tarName=$destination/powershell-linux-musl-$arch.tar.gz -fi - -# Build libpsl-native -cd $repoRoot/src/libpsl-native - -cmake -DCMAKE_BUILD_TYPE=Debug . -make -j - -# Restore packages -cd ../.. -dotnet restore $dotnetArguments - -# run ResGen -cd src/ResGen -dotnet run - -# Create typeCatalog -cd .. -targetFile="Microsoft.PowerShell.SDK/obj/Microsoft.PowerShell.SDK.csproj.TypeCatalog.targets" -cat > $targetFile <<-"EOF" - - - - <_RefAssemblyPath Include="%(_ReferencesFromRAR.HintPath)%3B" Condition=" '%(_ReferencesFromRAR.NuGetPackageId)' != 'Microsoft.Management.Infrastructure' "/> - - - - -EOF - -dotnet msbuild Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj /t:_GetDependencies "/property:DesignTimeBuild=true;_DependencyFile=$(pwd)/TypeCatalogGen/powershell.inc" /nologo - -cd TypeCatalogGen -dotnet run ../System.Management.Automation/CoreCLR/CorePsTypeCatalog.cs powershell.inc - -# build PowerShell -cd ../powershell-unix -dotnet publish --configuration Release --runtime linux-musl-x64 $dotnetArguments - -# add libpsl-native to build -mv libpsl-native.so bin/Release/netcoreapp2.1/linux-musl-x64/publish - -# tar build for output -cd bin/Release/netcoreapp2.1/linux-musl-x64/publish - -tar -czvf $tarName . - -echo "Created $tarName" diff --git a/tools/releaseBuild/azureDevOps/AzArtifactFeed/PSGalleryToAzArtifacts.yml b/tools/releaseBuild/azureDevOps/AzArtifactFeed/PSGalleryToAzArtifacts.yml new file mode 100644 index 00000000000..997f6030ab0 --- /dev/null +++ b/tools/releaseBuild/azureDevOps/AzArtifactFeed/PSGalleryToAzArtifacts.yml @@ -0,0 +1,33 @@ +# Sync packages from PSGallery to Azure DevOps Artifacts feed + +resources: +- repo: self + clean: true + +queue: + name: Hosted VS2017 +steps: + - powershell: | + Install-Module -Name PowerShellGet -MinimumVersion 2.0.1 -Force + Import-Module PowerShellGet -Force -Verbose + displayName: Update PSGet and PackageManagement + condition: succeededOrFailed() + + - powershell: | + Import-Module -Force "$(Build.SourcesDirectory)/tools/releaseBuild/azureDevOps/AzArtifactFeed/SyncGalleryToAzArtifacts.psm1" + SyncGalleryToAzArtifacts -AzDevOpsPAT $(AzDevOpsPAT) -Destination $(Build.ArtifactStagingDirectory) + displayName: Download packages from PSGallery that need to be updated + condition: succeededOrFailed() + + - powershell: | + Write-Verbose -Verbose "Packages to upload" + if(Test-Path $(Build.ArtifactStagingDirectory)) { Get-ChildItem "$(Build.ArtifactStagingDirectory)/*.nupkg" | ForEach-Object { $_.FullName }} + displayName: List packages to upload + condition: succeededOrFailed() + + - task: NuGetCommand@2 + displayName: 'NuGet push' + inputs: + command: push + publishVstsFeed: 'https://mscodehub.pkgs.visualstudio.com/_packaging/pscore-release/nuget/v3/index.json' + publishFeedCredentials: 'AzArtifactsFeed' diff --git a/tools/releaseBuild/azureDevOps/AzArtifactFeed/SyncGalleryToAzArtifacts.psm1 b/tools/releaseBuild/azureDevOps/AzArtifactFeed/SyncGalleryToAzArtifacts.psm1 new file mode 100644 index 00000000000..a75c2af9052 --- /dev/null +++ b/tools/releaseBuild/azureDevOps/AzArtifactFeed/SyncGalleryToAzArtifacts.psm1 @@ -0,0 +1,114 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +<# +.SYNOPSIS +Downloads to packages from PowerShell Gallery which are missing from the Azure DevOps Artifacts feed. + +.PARAMETER AzureDevOpsPAT +PAT for the username used for authenticating to the Azure DevOps Artifacts feed. + +.PARAMETER Destination +Path to the folder where the packages should be stored for uploading to Azure DevOps Artifacts feed. + +#> +function SyncGalleryToAzArtifacts { + param( + [Parameter(Mandatory = $true)] [string] $AzDevOpsPAT, + [Parameter(Mandatory = $true)] [string] $Destination + ) + + $csproj = [xml] (Get-Content 'src/Modules/PSGalleryModules.csproj') + $packages = @($csproj.Project.ItemGroup.PackageReference | ForEach-Object { [ordered] @{Name = $_.Include; Version = $_.Version }}) + + $galleryPackages = @() + $azArtifactsPackages = @() + $modulesToUpdate = @() + + $galleryUrl = 'https://www.powershellgallery.com/api/v2/' + $azArtifactsUrl = 'https://mscodehub.pkgs.visualstudio.com/_packaging/pscore-release/nuget/v2' + + $azDevOpsCreds = [pscredential]::new($env:AzDevOpsUserName, (ConvertTo-SecureString -String $AzDevOpsPAT -AsPlainText -Force)) + + foreach ($package in $packages) { + try { + # Get module from gallery + $foundPackageOnGallery = Find-Package -ProviderName NuGet -Source $galleryUrl -AllVersions -Name $package.Name -Force -AllowPreReleaseVersion | Sort-Object -Property Version -Descending | Select-Object -First 1 + Write-Verbose -Verbose "Found module $($package.Name) - $($foundPackageOnGallery.Version) in gallery" + $galleryPackages += $foundPackageOnGallery + } catch { + if ($_.FullyQualifiedErrorId -eq 'NoMatchFoundForCriteria,Microsoft.PowerShell.PackageManagement.Cmdlets.FindPackage') { + # Log and ignore failure is required version is not found on gallery. + Write-Warning "Module not found on gallery $($package.Name) - $($package.Version)" + } + else { + Write-Error $_ + } + } + + try { + # Get module from Az Artifacts + # There seems to be a bug in the feed with RequiredVersion matching. Adding workaround with post filtering. + # Issue: https://github.com/OneGet/oneget/issues/397 + $foundPackageOnAz = Find-Package -ProviderName NuGet -Source $azArtifactsUrl -AllVersions -Name $package.Name -Force -Credential $azDevOpsCreds -AllowPreReleaseVersion | Sort-Object -Property Version -Descending | Select-Object -First 1 + Write-Verbose -Verbose "Found module $($package.Name) - $($foundPackageOnAz.Version) in azArtifacts" + $azArtifactsPackages += $foundPackageOnAz + } catch { + if ($_.FullyQualifiedErrorId -eq 'NoMatchFoundForCriteria,Microsoft.PowerShell.PackageManagement.Cmdlets.FindPackage') { + # Log and add the module to update list. + Write-Verbose -Verbose "Az Artifacts Module needs update to - $($package.Name) - $($package.Version)" + $modulesToUpdate += $package + } + else { + Write-Error $_ + } + } + + # Check if Az package version is less that gallery version + if ($foundPackageOnAz.Version -lt $foundPackageOnGallery.Version) { + Write-Verbose -Verbose "Module needs to be updated $($package.Name) - $($foundPackageOnGallery.Version)" + $modulesToUpdate += $foundPackageOnGallery + } elseif ($foundPackageOnGallery.Version -lt $foundPackageOnAz.Version) { + Write-Warning "Newer version found on Az Artifacts - $($foundPackageOnAz.Name) - $($foundPackageOnAz.Version)" + } else { + Write-Verbose -Verbose "Module is in sync - $($package.Name)" + } + } + + "Gallery Packages:`n" + $galleryPackages + + "Az Artifacts Packages:`n" + $azArtifactsPackages + + "Modules to update:`n" + $modulesToUpdate + + foreach ($package in $modulesToUpdate) { + Save-Package -Provider NuGet -Source $galleryUrl -Name $package.Name -RequiredVersion $package.Version -Path $Destination + } + + # Remove dependent packages downloaded by Save-Module if there are already present in AzArtifacts feed. + try { + Register-PackageSource -Name local -Location $Destination -ProviderName NuGet -Force + $packageNamesToKeep = @() + $savedPackages = Find-Package -Source local -AllVersions -AllowPreReleaseVersion + + foreach($package in $savedPackages) { + $foundMatch = $azArtifactsPackages | Where-Object { $_.Name -eq $package.Name -and $_.Version -eq $package.Version } + + if(-not $foundMatch) { + Write-Verbose "Keeping package $($package.PackageFileName)" + $packageNamesToKeep += $package.PackageFilename + } + } + + Remove-Item -Path $Destination -Exclude $packageNamesToKeep -Recurse -Force + } + finally { + Unregister-PackageSource -Name local -Force -ErrorAction SilentlyContinue + } + +} + +Export-ModuleMember -Function 'SyncGalleryToAzArtifacts' diff --git a/tools/releaseBuild/build.json b/tools/releaseBuild/build.json index 214ee44c413..1785bce8971 100644 --- a/tools/releaseBuild/build.json +++ b/tools/releaseBuild/build.json @@ -288,8 +288,8 @@ { "Name": "alpine.3", "RepoDestinationPath": "/PowerShell", - "BuildCommand": "/build-and-run-pwsh.sh _RepoDestinationPath_ _DockerVolume_ _ReleaseTag_", - "AdditionalContextFiles" :[ "./tools/releaseBuild/Images/microsoft_powershell_alpine3/build-and-run-pwsh.sh"], + "BuildCommand": "/PowerShellPackage.ps1 -location _RepoDestinationPath_ -destination _DockerVolume_ -ReleaseTag _ReleaseTag_ -Alpine", + "AdditionalContextFiles" :[ "./tools/releaseBuild/Images/GenericLinuxFiles/PowerShellPackage.ps1"], "DockerFile": "./tools/releaseBuild/Images/microsoft_powershell_alpine3/Dockerfile", "DockerImageName": "ps-alpine-3", "BinaryBucket": "release" diff --git a/tools/releaseBuild/macOS/PowerShellPackageVsts.ps1 b/tools/releaseBuild/macOS/PowerShellPackageVsts.ps1 index 80236574910..5ebed79d344 100644 --- a/tools/releaseBuild/macOS/PowerShellPackageVsts.ps1 +++ b/tools/releaseBuild/macOS/PowerShellPackageVsts.ps1 @@ -29,27 +29,15 @@ param ( [switch] $Build ) -# We must build in /PowerShell -$repoRoot = '/PowerShell' -if ($BootStrap.IsPresent) { - $repoRoot = $location -} +$repoRoot = $location if ($Build.IsPresent) { - # cleanup the folder but don't delete it or the build agent will loose ownership of the folder - Write-Verbose -Message "cleaning /PowerShell" -Verbose - Get-ChildItem -Path /PowerShell/* -Attributes Hidden, Normal, Directory | Remove-Item -Recurse -Force - - # clone the repository to the location we must build from - Write-Verbose -Message "cloning to /PowerShell" -Verbose - git clone $location /PowerShell $releaseTagParam = @{} if ($ReleaseTag) { $releaseTagParam = @{ 'ReleaseTag' = $ReleaseTag } } } - Push-Location try { Write-Verbose -Message "Init..." -Verbose diff --git a/tools/releaseBuild/macOS/vsts.yml b/tools/releaseBuild/macOS/vsts.yml index 10e8c01bfb3..57cdb507380 100644 --- a/tools/releaseBuild/macOS/vsts.yml +++ b/tools/releaseBuild/macOS/vsts.yml @@ -14,16 +14,6 @@ steps: scriptPath: 'tools/releaseBuild/setReleaseTag.sh' args: '-ReleaseTag $(ReleaseTagVar) -Variable "ReleaseTagVar"' displayName: 'Calculate Release Tag' -- powershell: | - Import-Module $(Build.SourcesDirectory)/build.psm1 -Force - New-NugetConfigFile -NugetFeedUrl $(AzDevOpsFeed) -UserName $(AzDevOpsFeedUserName) -ClearTextPAT $(AzDevOpsFeedPAT) -FeedName AzDevOpsFeed -Destination $(Build.SourcesDirectory)/src/Modules - - if(-not (Test-Path "$(Build.SourcesDirectory)/src/Modules/nuget.config")) - { - throw "nuget.config is not created" - } - displayName: 'Add nuget.config for AzDevOps feed for PSGallery modules ' - condition: ne(Variable['SkipFxDependent'], 'true') - task: ShellScript@2 inputs: scriptPath: 'tools/installpsh-osx.sh' @@ -32,13 +22,26 @@ steps: inputs: scriptPath: 'tools/releaseBuild/macOS/createPowerShell.sh' displayName: 'Create /PowerShell' +- powershell: | + Write-Host "##vso[task.setvariable variable=PowerShellRoot]/PowerShell" + git clone $env:BUILD_REPOSITORY_LOCALPATH /PowerShell + displayName: Clone PowerShell Repo to /PowerShell - task: ShellScript@2 inputs: scriptPath: 'tools/releaseBuild/macOS/PowerShellPackageVsts.sh' - args: '-location $(Build.SourcesDirectory) -BootStrap' + args: '-location $(PowerShellRoot) -BootStrap' displayName: 'Bootstrap VM' +- powershell: | + Import-Module $(Build.SourcesDirectory)/build.psm1 -Force + New-NugetConfigFile -NugetFeedUrl $(AzDevOpsFeed) -UserName $(AzDevOpsFeedUserName) -ClearTextPAT $(AzDevOpsFeedPAT) -FeedName AzDevOpsFeed -Destination "$(PowerShellRoot)/src/Modules" + + if(-not (Test-Path "$(PowerShellRoot)/src/Modules/nuget.config")) + { + throw "nuget.config is not created" + } + displayName: 'Add nuget.config for AzDevOps feed for PSGallery modules ' - powershell: | $env:AZDEVOPSFEEDPAT = '$(AzDevOpsFeedPAT)' - $(Build.SourcesDirectory)/tools/releaseBuild/macOS/PowerShellPackageVsts.ps1 -ReleaseTag $(ReleaseTagVar) -Destination $(System.ArtifactsDirectory) -ExtraPackage "tar" -location $(Build.SourcesDirectory) -Build + $(Build.SourcesDirectory)/tools/releaseBuild/macOS/PowerShellPackageVsts.ps1 -ReleaseTag $(ReleaseTagVar) -Destination $(System.ArtifactsDirectory) -ExtraPackage "tar" -location $(PowerShellRoot) -Build $env:AZDEVOPSFEEDPAT = $null displayName: 'Build and Package' diff --git a/tools/travis.ps1 b/tools/travis.ps1 index a9823e2a6cd..33498d89ec7 100644 --- a/tools/travis.ps1 +++ b/tools/travis.ps1 @@ -330,12 +330,11 @@ elseif($Stage -eq 'Build') } try { - $SequentialXUnitTestResultsFile = "$pwd/SequentialXUnitTestResults.xml" $ParallelXUnitTestResultsFile = "$pwd/ParallelXUnitTestResults.xml" - Start-PSxUnit -SequentialTestResultsFile $SequentialXUnitTestResultsFile -ParallelTestResultsFile $ParallelXUnitTestResultsFile + Start-PSxUnit -ParallelTestResultsFile $ParallelXUnitTestResultsFile # If there are failures, Test-XUnitTestResults throws - $SequentialXUnitTestResultsFile, $ParallelXUnitTestResultsFile | ForEach-Object { Test-XUnitTestResults -TestResultsFile $_ } + Test-XUnitTestResults -TestResultsFile $ParallelXUnitTestResultsFile } catch { $result = "FAIL"