Wildcard in powershell string for call operator - powershell

In a powershell script I need to call a funcion like this
$SpecRunCall = "./packages/SpecRun.Runner.1.2.0/tools/SpecRun.exe"
$MSTestArguments = #('run', 'Default.srprofile', "/baseFolder:.\TestResults", '/log:specrun.log')
if($tag) {
$MSTestArguments += '/filter:#' + $tag
}
& $SpecRunCall $MSTestArguments
But I have to put there the version of the SpecRun Runner, I was thinking of putting a wildcard for that and then find whichever version I have (provided I have only one there) but I'm struggling to find a working solution for it.
Thanks,

Assuming the only version exists, Resolve-Path ...SpecRun.Runner.*... should work:
$SpecRunCall = Resolve-Path ./packages/SpecRun.Runner.*/tools/SpecRun.exe

You could the following assuming that all the SpecRunner version are all in different folders labeled with the version number. And that only one version folder will be on the computer at a time.
$version = "1.1.0","1.2.0","1.3.0"
$SpecRunCall = $null
$version | Foreach {
# Set spec runner version folder
$SpecFolder = "./packages/SpecRun.Runner.$_/"
# Test folder
If(Test-path $SpecFolder){
# Version folder found use it
$SpecRunCall = $SpecFolder+"tools/SpecRun.exe"
}
}
# Check if a version was found
If($SpecRunCall -ne $null){
# ** Your code **
$MSTestArguments = #('run', 'Default.srprofile', "/baseFolder:.\TestResults", '/log:specrun.log')
If($tag) {$MSTestArguments += '/filter:#' + $tag}
& $SpecRunCall $MSTestArguments
}
This code will take a list of version numbers and look for the corresponding SpecRunner folder. If found that version of SpecRunner is selected.

Related

Powershell to Loop through each user profile to get Version number of software and then create uninstall string and run the command

Hoping someone can give me an idea on how to proceed with the remaining script.
The script is to get Version number of Installed Chrome from that build Build a string for the uninstall as shown below.
I'm stuck on the second part, fine on getting the version number.
What would the logic be next to then iterate through each user profile to run the setup.exe from C:\Users[username]\appdata\Local\Google\Chrome\Application\90.0.4430.72\Installer. The error I am getting is unrecognized cmdlet on the { & $unin}
Thank you
#UserHives - Find all the user profiles in the registry
$UserHives = Get-ChildItem Registry::HKEY_USERS\ |Where-Object {$_.Name -match '^HKEY_USERS\\S-1-5-21-[\d\-]+$'}
$UserProfile = $Env:USERPROFILE
#
foreach($user in $UserHives)
{
#1.Get Version Of chrome
#1. PATH TO SEARCH FOR
$Path = Join-Path $user.PSPath "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Google Chrome"
If (Test-Path $Path){
$GetVersion = Get-ItemProperty -Path $Path | Select-Object -Property Version
$VersionInstalled = $GetVersion.Version
#create uninstallstring
$UninString = "\Google\Chrome\Application\$VersionInstalled\Installer\setup.exe --uninstall --Channel --chrome --force-uninstall"
$unin = $UserProfile + "" + $UninString
If($VersionInstalled){ & $unin}
}
}
Quote from the docs:
The call operator does not parse strings. This means that you cannot
use command parameters within a string when you use the call operator.
Pass the arguments separately:
$uninArgs = "--uninstall", "--Channel", "--chrome", "--force-uninstall"
$uninExe = "$UserProfile\Google\Chrome\Application\$VersionInstalled\Installer\setup.exe"
if ($VersionInstalled) {
& $uninExe $uninArgs
}

Open or extract files nupkg with Powershell

could you please help me? How I can open and extract files the "nupkg" package using the PowerShell.
Thanks.
You can use Expand-Archive (you have to rename the file, see Can I use PowerShell `Expand-Archive` upon a zip file with no extension)
Rename-Item "Newtonsoft.Json.12.0.1.nupkg" "Newtonsoft.Json.12.0.1.nupkg.zip"
Expand-Archive "Newtonsoft.Json.12.0.1.nupkg.zip"
I prefer to use nuget cli, because it also intstalls dependencies. All you need is nuget install yourpackage . It's really just a 5MB executable, you can even download it each time you need to get the package:
$nugetUrl = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
Invoke-WebRequest -Uri $nugetUrl -OutFile ".\nuget.exe"
.\nuget.exe install yourpackage
I would not recommend direct use of Expand-Archive. As mentioned by guys at Squirrel and as you can see in NuGet sources file names in archive are escaped using URI escaping.
If you decide to expand raw archive you should afterwards rename all files and directories containing % in their names using Uri.UnescapeDataString.
If you want more optimised approach in terms of file system writes here's the implementation:
function Expand-NugetArchive {
[CmdletBinding()]
param (
# File name of the Package
[Parameter(Mandatory = $true)]
[string]
$FileName,
# Directory
[string]
$ExtractDirectory,
[boolean]
$Overwrite = $false
)
# Reference to the knowledge here https://stackoverflow.com/a/72590215/3299257
$extractPath = [System.IO.Path]::GetFullPath($ExtractDirectory);
# Ensures that the last character on the extraction path
# is the directory separator char.
# Without this, a malicious zip file could try to traverse outside of the expected
# extraction path.
if ( -not $extractPath.EndsWith([System.IO.Path]::DirectorySeparatorChar.ToString(), [StringComparison]::Ordinal)) {
$extractPath += [System.IO.Path]::DirectorySeparatorChar;
}
$archive = [System.IO.Compression.ZipFile]::OpenRead($FileName)
try {
foreach ($entry in $archive.Entries) {
$fullName = $entry.FullName
if ($fullName.Contains('%')) {
$fullName = [Uri]::UnescapeDataString($fullName)
}
# Gets the full path to ensure that relative segments are removed.
$destinationPath = [System.IO.Path]::GetFullPath([System.IO.Path]::Combine($extractPath, $fullName))
[System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($destinationPath)) | Out-Null
# Ordinal match is safest, case-sensitive volumes can be mounted within volumes that
# are case-insensitive.
if ($destinationPath.StartsWith($extractPath, [StringComparison]::Ordinal)) {
[System.IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $destinationPath, $Overwrite)
}
}
}
catch {
if ($null -ne $archive) { $archive.Dispose() }
throw
}
}

PowerShell - Sorry, we couldn't find Microsoft.PowerShell.Core\FileSystem::

I'm trying to modify the script created by Boe Prox that combines multiple CSV files to one Excel workbook to run on a network share.
When I run it locally, the script executes great and combines multiple .csv files into one Excel workbook.
Clear-Host
$OutputFile = "ePortalMonthlyReport.xlsx"
$ChildDir = "C:\MonthlyReport\*.csv"
cd "C:\MonthlyReport\"
echo "Combining .csv files into Excel workbook"
. C:\PowerShell\ConvertCSVtoExcel.ps1
Get-ChildItem $ChildDir | ConvertCSVtoExcel -output $OutputFile
echo " "
But when I modify it to run from a network share with the following changes:
Clear-Host
# Variables
$OutputFile = "ePortalMonthlyReport.xlsx"
$NetworkDir = "\\sqltest2\dev_ePortal\Monthly_Report"
$ChildDir = "\\sqltest2\dev_ePortal\Monthly_Report\*.csv"
cd "\\sqltest2\dev_ePortal\Monthly_Report"
echo "Combining .csv files into Excel workbook"
. $NetworkDir\ConvertCSVtoExcel.ps1
Get-ChildItem $ChildDir | ConvertCSVtoExcel -output $OutputFile
echo " "
I am getting an error where it looks like it using the network path twice and I am not sure why:
Combining .csv files into Excel workbook
Converting \sqltest2\dev_ePortal\Monthly_Report\001_StatsByCounty.csv
naming worksheet 001_StatsByCounty
--done
opening csv Microsoft.PowerShell.Core\FileSystem::\sqltest2\dev_ePortal\Monthly_Report\\sqltest2\dev_ePortal\Monthly_Report\001_StatsByCounty.csv) in excel in temp workbook
Sorry, we couldn't find Microsoft.PowerShell.Core\FileSystem::\sqltest2\dev_ePortal\Monthly_Report\\sqltest2\dev_ePortal\Monthly_Report\001_StatsByCounty.csv. Is it possible it was moved, renamed or deleted?
Anyone have any thoughts on resolving this issue?
Thanks,
Because in the script it uses the following regex:
[regex]$regex = "^\w\:\\"
which matches a path beginning with a driveletter, e.g. c:\data\file.csv will match and data\file.csv will not. It uses this because (apparently) Excel needs a complete path, so if the file path does not match, it will add the current directory to the front of it:
#Open the CSV file in Excel, must be converted into complete path if no already done
If ($regex.ismatch($input)) {
$tempcsv = $excel.Workbooks.Open($input)
}
ElseIf ($regex.ismatch("$($input.fullname)")) {
$tempcsv = $excel.Workbooks.Open("$($input.fullname)")
}
Else {
$tempcsv = $excel.Workbooks.Open("$($pwd)\$input")
}
Your file paths will be \\server\share\data\file.csv and it doesn't see a drive letter, so it hits the last option and jams $pwd - an automatic variable of the current working directory - onto the beginning of the file path.
You might get away if you edit his script and change the regex to:
[regex]$regex = "^\w\:\\|^\\\\"
which will match a path beginning with \\ as OK to use without changing it, as well.
Or maybe edit the last option (~ line 111) to say ...Open("$($input.fullname)") as well, like the second option does.
Much of the issues are caused in almost every instance where the script calls $pwd rather than $PSScriptRoot. Replace all instances with a quick find and replace.
$pwd looks like:
PS Microsoft.PowerShell.Core\FileSystem::\\foo\bar
$PSScriptRoot looks like:
\\foo\bar
The second part i fixed for myself is what #TessellatingHeckler pointed out. I took a longer approach.
It's not the most efficient way...but to me it is clear.
[regex]$regex = "^\w\:\\"
[regex]$regex2 = "^\\\\"
$test = 0
If ($regex.ismatch($input) -and $test -eq 0 ) {
$tempcsv = $excel.Workbooks.Open($input)
$test = 1 }
If ($regex.ismatch("$($input.fullname)") -and $test -eq 0) {
$tempcsv = $excel.Workbooks.Open("$($input.fullname)")
$test = 1}
If ($regex2.ismatch($input) -and $test -eq 0) {
$tempcsv = $excel.Workbooks.Open($input)
$test = 1 }
If ($regex2.ismatch("$($input.fullname)") -and $test -eq 0) {
$tempcsv = $excel.Workbooks.Open("$($input.fullname)")
$test = 1}
If ($test -eq 0) {
$tempcsv = $excel.Workbooks.Open("$($PSScriptRoot)\$input")
$test = 0 }

Locate MSTest.exe using powershell

I'm in the process of automating my .Net solution build to be completely in PowerShell. I want to locate MSTest.exe using PowerShell.
I used the following script to locate MSBuild.exe and I hope that I can have something similar to locate MSTest.exe
$msBuildQueryResult = reg.exe query "HKLM\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0" /v MSBuildToolsPath
$msBuildQueryResult = $msBuildQueryResult[2]
$msBuildQueryResult = $msBuildQueryResult.Split(" ")
$msBuildLocation = $msBuildQueryResult[12] + "MSBuild.exe"
Any directions ?
The following works with Visual Studio 2010 and higher[1]:
# Get the tools folder location:
# Option A: Target the *highest version installed*:
$vsToolsDir = (
Get-Item env:VS*COMNTOOLS | Sort-Object {[int]($_.Name -replace '[^\d]')}
)[-1].Value
# Option B: Target a *specific version*; e.g., Visual Studio 2010,
# internally known as version 10.0.
# (See https://en.wikipedia.org/wiki/Microsoft_Visual_Studio#History)
$vsToolsDir = $env:VS100COMNTOOLS
# Now locate msbuild.exe in the "IDE" sibling folder.
$msTestExe = Convert-Path -EA Stop (Join-Path $vsToolsDir '..\IDE\MSTest.exe')
The approach is based on this answer and is generalized and adapted to PowerShell.
It is based on system environment variables VS*COMNTOOLS, created by Visual Studio setup, where * represents the VS version number (e.g., 100 for VS 2010).
Re option A: Sort-Object is used to ensure that the most recent Visual Studio installation is targeted, should multiple ones be installed side by side:
The script block used for sorting first extracts only the embedded version number from the variable name ($_.Name -replace '[^\d]'; e.g., 100 from VS100COMNTOOLS) and converts the result to an integer ([int]); [-1] then extracts the last element from the sorted array - i.e., the variable object whose names has the highest embedded version number - and accesses its value (.Value).
The IDE subfolder, in which MSTest.exe is located is a sibling folder of the tools folder that VS*COMNTOOLS points to.
If MSTest.exe is NOT in the expected location, Convert-Path will throw a non-terminating error by default; adding -EA Stop (short for: -ErrorAction Stop) ensures that the script is aborted instead.
[1]
- I've tried up to Visual Studio 2015; do let me know whether or not it works on higher versions.
- Potentially also works with VS 2008.
Perhaps you are wanting something like this?
$regPath = "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\4.0"
$regValueName = "MSBuildToolsPath"
$msBuildFilename = "MSBUild.exe"
if ( Test-Path $regPath ) {
$toolsPath = (Get-ItemProperty $regPath).$regValueName
if ( $toolsPath ) {
$msBuild = Join-Path $toolsPath $msBuildFilename
if ( -not (Test-Path $msBuild -PathType Leaf) ) {
Write-Error "File not found - '$msBuild'"
}
}
}
# Full path and filename of MSBuild.exe in $msBuild variable
My way of getting mstest path.
GetMSTestPath function is main function which you call and then if first GetMsTestPathFromVswhere function will find something it returns path if not your will be making a long search for mstest.exe. Usually, it takes approximately 10 sec. I know that this is not the best but at least it is something when you struggle to find mstest.exe. Hope it will be helpful for somebody. :)))
function GetMSTestPath
{
function GetTime()
{
$time_now = Get-Date -format "HH:mm:ss"
return $time_now;
}
function GetMsTestPathFromVswhere {
$vswhere = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
$path = & $vswhere -latest -prerelease -products * -requires Microsoft.Component.MSBuild -property installationPath
#write-host $path
if ($path) {
$tool = join-path $path 'Common7\IDE\MSTest.exe'
if (test-path $tool) {
return $tool
}
return ""
}
}
function SeachForMsTestPath
{
write-host $(GetTime)
$path = Get-ChildItem C:\ -Filter MSTest.exe -Recurse -ErrorAction Ignore | ? { $_.VersionInfo.FileDescription -eq 'Test Execution Command Line Tool' } | Select -First 1
write-host $(GetTime)
return $path
}
$msTestExePath = GetMsTestPathFromVswhere
if ([string]::IsNullOrEmpty($msTestExePath))
{
$msTestExePath = SeachForMsTestPath;
if ([string]::IsNullOrEmpty($msTestExePath))
{
Write-host "MsTest path is not found. Exiting with error"
Exit -1
}
}
return $msTestExePath;
}
Thanks #Bill_Stewart , I used your comments to write this working function:
function Get-MSTest-Location {
$msTests = #()
$searchResults = Get-ChildItem C:\* -Filter MSTest.exe -Recurse -ErrorAction Ignore
foreach($searchResult in $searchResults) {
try{
if(($searchResult.VersionInfo -ne $null) -and ($searchResult.VersionInfo.FileDescription -eq "Test Execution Command Line Tool"))
{ $msTests = $msTests + $searchResult.FullName }
}
catch{}
}
if($msTests.Length -eq 0)
{return "MSTest not found."}
return $msTests[0]
}

Update project property with a powershell script

I want to create a nuget package which will run a Powershell script on restore in a C# project. The script will change the values of the project properties on the project e.g.:
Set value of
<AssemblyVersion>1.0.0.0</AssemblyVersion>
to
<AssemblyVersion>$(ReleaseApplicationVersion)</AssemblyVersion>
and some other properties.
Thanks in advance!
UPDATE-------------------------------------------
This is what I have
param([string]$projectName = $(throw 'csproj file is required'))
$proj = Resolve-Path $projectName
$propAssemblyName = $proj.Properties.Item("AssemblyName")
$propAssemblyName.Value = '$(ReleasedAssemblyName)'
But I obviously dont know how to make this work as I get bunch of issues. Thanks
function Set-Version {
Write-Header "Updating version in .csproj files"
try {
Push-Location ".\csprojLocation"
$versionProjFile = Resolve-Path "*.csproj"
$xml = [xml](Get-Content $versionProjFile)
$xml.Project.PropertyGroup.AssemblyVersion = ${version}
$xml.Save($versionPropsFile)
}
finally {
Pop-Location
}
}
Set-Version