NuGet: How can I change property of files with Install.ps1 file? - powershell

I am creating NuGet package and for that I have created Nuspec manifest file. In content folder I have two files, test.exe and test.config. Now I would like to change property "Copy To Output Directory" of these the files to "Copy Always" in project, when any user installs this package.
I found related question NuGet how to apply properties to files, that shows can do this using PowerShell install.ps1 script, but I have no idea how to create that file.

Your install.ps1 file should look something like this.
param($installPath, $toolsPath, $package, $project)
$file1 = $project.ProjectItems.Item("test.exe")
$file2 = $project.ProjectItems.Item("test.config")
# set 'Copy To Output Directory' to 'Copy if newer'
$copyToOutput1 = $file1.Properties.Item("CopyToOutputDirectory")
$copyToOutput1.Value = 2
$copyToOutput2 = $file2.Properties.Item("CopyToOutputDirectory")
$copyToOutput2.Value = 2

Here is a bit more detail on how to solve this problem end to end:
You need to do two things to ensure the status are set correctly on install...
Write the install.ps1 script to mark the status of the files.
Ensure the install.ps1 script is in the Tools directory in the nuget package.
Install.ps1 Script
The following example script will recursively mark every file in the "Content" and "View" directory as "Copy to newer". Note this example script is written to make it clear to read and understand. It will mark every file in the Content and Views folders in the root directory of the Visual Studios project.
param($installPath, $toolsPath, $package, $project)
function MarkDirectoryAsCopyToOutputRecursive($item)
{
$item.ProjectItems | ForEach-Object { MarkFileASCopyToOutputDirectory($_) }
}
function MarkFileASCopyToOutputDirectory($item)
{
Try
{
Write-Host Try set $item.Name
$item.Properties.Item("CopyToOutputDirectory").Value = 2
}
Catch
{
Write-Host RecurseOn $item.Name
MarkDirectoryAsCopyToOutputRecursive($item)
}
}
#Now mark everything in the a directory as "Copy to newer"
MarkDirectoryAsCopyToOutputRecursive($project.ProjectItems.Item("Content"))
MarkDirectoryAsCopyToOutputRecursive($project.ProjectItems.Item("Views"))
Copy To Tools
You must copy the install.ps1 file to the Tools directory for script to be executed by nuget. You can add the following to the nuspec template to do this.
<files>
<file src="install.ps1" target="Tools"/>
</files>
Note in this case I have the install.ps1 file in the root of my Visual Studios project and marked as "Copy if newer".

Related

Nuget package init.ps1 script and PackageReference modification

I want to have referenced dll(s) in my package in "copy local = false" mode. In other words, I don't want that the dll(s) my package ships is/are copied into a output directory after the build.
The way this can be done in "packages.config" scenario is by having a following script in the tools/install.ps1 file.
param($installPath, $toolsPath, $package, $project)
$asms = $package.AssemblyReferences | %{$_.Name}
foreach ($reference in $project.Object.References)
{
if ($asms -contains $reference.Name + ".dll")
{
$reference.CopyLocal = $false;
}
}
I know that the newest Nuget version with PackageReference functionality uses only init.ps1 script. But I don't have any clue what so ever, what shall I write into that script.
I also know that the package user can add...
<ExcludeAssets>Runtime</ExcludeAssets>
to make this happen. But as a package author, I really would like to enforce that behaviour. So would it be possible to add that in the script?

How to delete files a dependency installed after your package is installed

I'm working on a nuget package that depends on the Unity and Unity.Mvc4 packages. My nuspec file has them listed as dependencies. Everything works, but when my package is installed the dependencies are installed first.
The Unity dependencies deploy files to the root of the project that my package has moved to a different location and customized, as such I don't want those files to exist in the root after my package is installed.
Is there any way to override dependency files in nuspec, or run a powershell script after install to clean them up?
You might add a Powershell script that move those files created by Unity to your actual project root.
For information about executing Powershell scripts during Nuget package installation, see the Nuget documentation. Note that these scripts must be placed within the tools folder of your Nuget package to be automatically executed.
Here's the code in my install.ps1 that did the trick if anyone wants it for reference.
# Runs every time a package is installed in a project
param($installPath, $toolsPath, $package, $project)
# $installPath is the path to the folder where the package is installed.
# $toolsPath is the path to the tools directory in the folder where the package is installed.
# $package is a reference to the package object.
# $project is a reference to the project the package was installed to.
#EnvDTE Project Reference
#https://msdn.microsoft.com/en-us/library/envdte.project.projectitems.aspx
function deleteProjectItem($fileName) {
$file = $null
foreach ($item in $project.ProjectItems) {
if ($item.Name.Equals($fileName, "InvariantCultureIgnoreCase")) {
$file = $item
break
}
}
if ($file -ne $null) {
Write-Host "Deleting: $($item.Name) ..." -NoNewline
$item.Delete()
Write-Host "Deleted!"
}
}
deleteProjectItem "BootStrapper.cs"
deleteProjectItem "job_scheduling_data_2_0.xsd"
deleteProjectItem "Unity.Mvc4.README.txt"

How do you create a nuget package in a TFS post build script?

I'm trying to package and publish a library with nuget from a TFS post build script. It seems that TFS build doesn't use the standard project output paths, so it's making things complicated. All I want to do is build the project (which it does) and then pack and publish with nuget.
Here's my script. It runs just fine on my local machine.
param([string]$project, [string]$version)
if (-not $project)
{
Write-Error ("The project parameter is required.")
exit 1
}
if (-not $version)
{
$version = $Env:TF_BUILD_BUILDNUMBER
}
if (-not $version)
{
Write-Error ("Either the version parameter or the TF_BUILD_BUILDNUMBER environment variable is required.")
exit 1
}
$projectFile = "$project\$project.csproj"
$packageFile = "$project.$version.nupkg"
$nugetPath = ".nuget\NuGet.exe"
if ($Env:TF_BUILD_SOURCESDIRECTORY)
{
# relative paths won't work on the build server
$projectFile = "$Env:TF_BUILD_SOURCESDIRECTORY\$projectFile"
$packageFile = "$Env:TF_BUILD_SOURCESDIRECTORY\$packageFile"
$nugetPath = "$Env:TF_BUILD_SOURCESDIRECTORY\$nugetPath"
}
else
{
$nugetPath = ".\$nugetPath"
}
Write-Host ("packing $projectFile...")
& $nugetPath pack "$projectFile" -Version $version -Symbols -IncludeReferencedProjects
And here's the error I'm getting.
& : The term '.\.nuget\NuGet.exe' is not recognized as the name of a cmdlet, function, script file, or operable
program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At C:\a\src\project\.build\PublishPackage.ps1:85 char:3
+ & $nugetPath pack "$projectFile" -Version $version -Symbols -IncludeReferencedPr ...
+ ~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (.\.nuget\NuGet.exe:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
I simplified the script so the line number won't match, but you can see that it's the last line that's causing the error. I'm using Visual Studio Online with a hosted build controller.
Why don't you use the standard mechanism?
Right click on your solution and "Enable Nuget Package Restore"
This will create a folder that contains a NuGet.targets file
Open than file and set to true the Property BuildPackage
<!-- Property that enables building a package from a project -->
<BuildPackage Condition=" &apos;$(BuildPackage)&apos; == &apos;&apos; ">true</BuildPackage>
Edit each project in your solution you want to produce a nuget package from
Right click on the project -> "Unload Project"
Right click on the project again and "Edit [NameofYourProject]"
Set BuildPackage to true by adding the following to the first PropertyGroup
<BuildPackage>true</BuildPackage>
Save the file and Reload the project
Commit your changes queue a new build and you should see your package within the build outcome.
This will basically use the project file (for C# the .csproj) to create your package
If you need more control over your package creation you could include in you project a .nuspec file
Where you can specify more metadata attributes such as: dependencies, tags, title, owners, etc
or even you could use parameters like
<version>$version$</version>
to synchronize the nuget package version with the actual assembly version.
If you need to specify the OutDir on the nuget build command for the build server modify the NuGet.targets file BuildCommand definition as follow.
<BuildCommand>$(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform);OutDir=$(OutDir)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols</BuildCommand>

Extracting Nupkg files using command line

Firstly, I do not want to use Visual Studio at all when dealing with the certain .nupkg files.
I know there is a tool called NuGet Package Explorer and this can export nupkg files to a certain file location using a gui, but I'm looking to setup a MSBuild task to run and unpack about 50 .nupkg files, using the command line.
My question is, is there a tool you can use via the command line which will unpack .nupkg files to a specified file location?
NuPKG files are just zip files, so anything that can process a zip file should be able to process a nupkg file, i.e, 7zip.
You can also use the NuGet command line, by specifying a local host as part of an install. For example if your package is stored in the current directory
nuget install MyPackage -Source %cd% -OutputDirectory packages
will unpack it into the target directory.
Rename it to .zip, then extract it.
did the same thing like this:
clear
cd PACKAGE_DIRECTORY
function Expand-ZIPFile($file, $destination)
{
$shell = New-Object -ComObject Shell.Application
$zip = $shell.NameSpace($file)
foreach($item in $zip.items())
{
$shell.Namespace($destination).copyhere($item)
}
}
Dir *.nupkg | rename-item -newname { $_.name -replace ".nupkg",".zip" }
Expand-ZIPFile "Package.1.0.0.zip" “DESTINATION_PATH”
With PowerShell 5.1 (PackageManagement module)
Install-Package -Name MyPackage -Source (Get-Location).Path -Destination C:\outputdirectory
This worked for me:
Rename-Item -Path A_Package.nupkg -NewName A_Package.zip
Expand-Archive -Path A_Package.zip -DestinationPath C:\Reference
I've expanded Zamarin.Essentials -version 1.6.1 with 7-zip and nuget package manager is not recognizing this package and I have source set to all.
I've tried just my global package source alone too.
Also I've noticed package manager downloads multiple versions to same folder, was wondering if it's ok to put a version folder in a package and copy the package end into it?

NuGet init.ps1 executing twice

I am following the solution found at "Create a NuGet package that shows update notifications" to get a notification of a nuget package update.
However the init.ps1 script is executing twice.
I stripped all the code out so that only the following is in init.ps1.
param($installPath, $toolsPath, $package, $project)
if ($project -eq $null) {
$project = Get-Project
}
Write-Host "Hello, I'm running inside of init.ps1"
When I close the solution and reopen it, in the output window the text is there twice.
I am using VS 2012, NuGet 2.2.31210
I checked the packages.config file and there is only one entry for my package.
Why is it running twice and is there any way to get it to only run once?
Thanks,
Joe
In init.ps1 script, Get-Project will return a random project in the solution but not the particular project the package has been installed.
Use $projects = Get-Project -All to get the list of projects in solution, loop through the projects and search the packages.config to check if the current project has the package installed.