Sharing files between Visual Studio projects, where the file is included in the project

We have a standard deployment script that runs within the scope of the web app. It’s an Octopus PreDeploy.ps1 script. It uses things like the name of the project to make decisions about what to call the user account on the app pool, the web site name, the app pool name, etc. There is a few things we haven’t that can’t be covered by the standard octopus IIS step (e.g. one is that we deploy our web services to a versioned url, https://myservivce.com/v1.1/endpoint/).

If you are starting from scratch I might be inclined to not do what we did, and instead start from using separate steps for this, the new features in Octopus 3.3 support storing a script in a package that you could use for this.

So to share this between our projects we decided to put it into a nuget package and install it that way, this means though that we need to treat it like content, and not a dll, but it needs to be included in the project, so that octopack will bundle it up into the package.

To do this we created an install.ps1 and uninstall.ps1 files to include the files from the nuget package as a linked item in the visual studio project.

So the nuspec file needed to be modified as follows.

You will note the target of the (un)install files is set to tools, this will make them get executed by visual studio. And our file we want to add is added in the root.


<files>
<file src="PreDeploy.ps1" target="." />
<file src="install.ps1" target="tools" />
<file src="uninstall.ps1" target="tools" />
</files>

Then the install.ps1 file looks as follows.

You will note it uses MS Build libraries in the powershell to execute inside of visual studio. This allows us to use the handy “GetItems” method on the project, and return all content items, so we can check for previous versions and remove.

It needs to be a content item because octopack will only package content items out of the box.

This is further filtered for items which have the packge name in the path (e.g. it would look something like this “packges\MyDeploymentPackage\predeploy.ps1″). If you had multiple files to add you could use an array here to remove all files isntead of one.

We store this in a delegate because we can’t call remove mid loop (you’ll get an error), then remove after the loop has completed.

Then prepare a new content item and save it into the project. You could do a dir listing on that folder and add all files, if you wanted to do multiple.


param
(
$installPath,
$toolsPath,
$package,
$project
)
$predeployfilename = "predeploy.ps1"
# Need to load MSBuild assembly if it's not loaded yet.
Add-Type -AssemblyName 'Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

# Grab the loaded MSBuild project for the projectcontent
$buildProject = [Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.GetLoadedProjects($project.FullName) | Select-Object -First 1

Write-Host ("Adding $predeployfilename into project " + $project.Name);
$PackName = $package.id;
Write-Host '$package.id' = $package.id
$nodeDeligate = $null

$buildProject.GetItems('Content') | Where-Object { $_.EvaluatedInclude -match $PackName } | ForEach-Object {
Write-Host "Removing Previous $predeployfilename Item"
$nodeDeligate = $_;
}

Write-Host '$nodeDeligate' = $nodeDeligate
if($nodeDeligate -ne $null)
{
$buildProject.RemoveItem($nodeDeligate);
Write-Host ("Removing old item: " + $predeployfilename);
}

$projectItem = Get-ChildItem $project.FullName;
$predeployfile = Resolve-Path ($installPath + "\" + $predeployfilename);
Set-Location $projectItem.Directory
$predeployrel = Get-Item $predeployfile | Resolve-Path -Relative

# For linked items the Include attribute is the relative path to that item, and the Link subproperty is the local display name.
$metadata = New-Object 'System.Collections.Generic.Dictionary[System.String, System.String]';
$metadata.Add('Link', $predeployfilename);

$target = $buildProject.AddItem("Content", $predeployrel, $metadata);

$buildProject.Save();
$buildProject.ReevaluateIfNecessary();

Write-Host ("$predeployfilename added.");

The uninstall.ps1 looks the same except it only has the step to remove not add. And its that easy!

Also to note that the contentFiles feature in nuget 3.3 which is not support by Visual studio yet may solve this too, i haven’t see it in action yet.

 

 

 

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s