IT:AD:Powershell:HowTo:Run scripts within the Package Manager Console/Background Info
Summary
Very useful: delivering userful company wide tools as powershell scripts, delivered easily as Nuget packages…what's not to love about that?!
Process
There's underlying concepts are that You have
Preparation
Create a Powershell Module
It's not obligatory, but you should prefer making a Powershell Module, rather than a Powershell Script.
Create a Nuget Package
The Nuget Package is how the PowerShell Module will be created.
Create a Package Tools Script
Nuget package has a designated directory for putting scripts in.
And a list of designated filenames to look for.
So, create a \Tools\ Directory as a sibling to the \content\ folder
In it you can create the following files:
Init.ps1:- runs the first time a package is installed in a solution. Not once per project in the solution.
- It also runs every time the solution is opened.
*
Install.ps1: - runs when a package is installed in a project (each project install, even if multiple, in a solution).
- runs after
init.ps1if there is one. - requirement: the package lib folder must have content of some kind, or it will not fire. Q: would a
_placeholder.txtwill do?
Uninstall.ps1:- runs when a package is installed in a project (each project install, even if multiple, in a solution).
We're ony insterested in the `init.ps1` at this point.
At the top of your init.ps1 script file, add this line:
param($installPath, $toolsPath, $package, $project)
Import-Module(Join-Path $toolsPath MyModule.psm1);
$installPathis the path to the folder where the package is installed$toolsPathis the path to the tools directory in the folder where the package is installed$packageis a reference to the package object.$projectis a reference to the EnvDTE project object and represents the project the package is installed into.- Note: This will be
nullinInit.ps1as it is a solution level script, not a project level one like$install.ps1or$uninstall.ps1
When you are testing $project in the console while creating your scripts, you can set it to $project = Get-Project
Result
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>XAct.AdminScripts</id>
<version>0.0.0.12</version>
<authors>xact</authors>
<owners>xact</owners>
<licenseUrl>https://bitbucket.org/xact/cs.ff.xact.lib/wiki/General/Legal/</licenseUrl>
<projectUrl>https://bitbucket.org/xact/cs.ff.xact.lib/</projectUrl>
<!-- <iconUrl/> -->
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Powershell scripts to administer XActLib assemblies in a solution.</description>
<tags>XAct XActLib</tags>
<dependencies>
<!--Turned out I didn't want it...<dependency id="SolutionScripts" version="1.2" /> -->
</dependencies>
</metadata>
<!-- Note:
We're specifying SolutionsScripts\PS.LIB or it will end up in the SolutionScripts folder :-(
We don't forget to put the init.ps1
<files>
<file src="..\PS.LIB\**\*.*" target="SolutionScripts\PS.LIB" />
<file src="..\xact.psm1" target="SolutionScripts" />
<file src="init.ps1" target="Tools" />
</files>
</package>
The idea for our init.ps1 file was clept'ed from the SolutionScripts nuget project – but his solution didn't seem to work with Nuget's distribution mechanism, and only appeared to work with manual installations…hum.
param($installPath, $toolsPath, $package, $project)
Write-Host "InstallPath: $installPath";
Write-Host "toolsPath: $toolsPath";
Write-Host "package: $package";
#$parentFolder = resolve-path "$installPath\..";
$global:solutionScriptsContainer = Join-Path $installPath "SolutionScripts"
function global:Update-XAct-AdminScripts()
{
Write-Host "Scanning $($global:solutionScriptsContainer) folder for PS scripts to mount.";
if(!(test-path $solutionScriptsContainer -pathtype container))
{
new-item $solutionScriptsContainer -type directory
}
$files = Get-ChildItem $solutionScriptsContainer
foreach ($file in $files)
{
if ($file.extension -eq ".ps1")
{
Write-Host " Sourcing: $file"
. $file.fullname
}
if ($file.extension -eq ".psm1")
{
Write-Host "Importing Module: $file"
Import-Module $file.fullname
}
}
}
Update-XAct-AdminScripts
Observations
The package, as it has no lib or content files is intended to be a Solution level package.
But I noticed that at a project level, it's very easy to not see it (it won't show packages of the solution) and …let you reinstall it for the project… ie…a big flaky design.
I've also noticed that
- Best to uninstall the old one first, before installing updates
- Restarting Visual Studio after doing it, to let the scripts settle in correctly.
Errata: Debugging
Note that you can refresh scripts (good for debugging…) by invoking
Update-XAct-AdminScripts
Errata: Profile
Just as Powershell Modules look for the Profile.ps1 file in the Modules path, Nuget does something similar:
$profile C:\Users\philha\Documents\WindowsPowerShell\NuGet_profile.ps1
But…that doesn't scale well (see http://lostechies.com/matthinze/2012/01/05/solutionscripts-little-scripts-for-the-nuget-console/)
Instead
Then put ps1 and psm1 files in a folder called SolutionScripts in the solution root (sibling to packages)
That's easier than having to setup modules, etc….but it doesn't make it easy to distribute the scripts…