I've look through many posts but still don't get it. I need a powershell script to download latest version of a specific project.
Let's say I just want to download the latest copies of files from ProjectA branch dfe. I saw some PS script that deletes the old files and download all the files again but that takes too long. script that would overwrite existing files would be much better. I tried update-tfsworkspace, it downloads everything but I don't want to download everything.
server: http://servername:8080/tfs/DefaultCollection
DefaultCollection
ProjectA
-Folder1
- branch dfe
-Folder2
- branch abc
ProjectB
-Folder1
- branch
- branch
First you need a good workspace mapping. You can create one manually, or from an xml file as below. Also, the functionality you are after to overwrite is [Microsoft.TeamFoundation.VersionControl.Client.GetOptions]::Overwrite
function Get-ProjectXml {
[CmdletBinding()]
Param(
[string] $tfs13Url,
[string] $workspaceName ,
[string] $workspaceDescription,
[string] $fileName,
[bool] $autoDeleteCreate)
try
{
#Connect to TFS:
$tfs13 = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($tfs13url)
$vcs13 = $tfs13.GetService([Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer])
$workspaces=$vcs13.QueryWorkspaces($workspaceName, $vcs13.AuthenticatedUser, [Environment]::MachineName);
if($autoDeleteCreate ){
#Delete if Workspace exists, as this is an automated process...
if ($workspaces.count -gt 0)
{
$vcs13.DeleteWorkspace($workspaceName, $vcs13.AuthenticatedUser);
}
$workspace = $tfs10.VCS.CreateWorkspace($workspaceName, $vcs13.AuthenticatedUser, $workspaceDescription);
}
else
{
$workspace=$workspaces[0]
}
#Get XML File
[xml]$xmlMappings= New-Object System.Xml.XmlDocument
$xmlMappings.Load($fileName)
$rootLocalItem=$xmlMappings.Root.LocalItem
#Iterate through each collection
foreach($collection in $xmlMappings.Collection){
write-host "Mapping project $($collection.Name)"
#Iterate through each project
foreach($project in $collection.Project){
write-host "Mapping project $($project.Name)"
foreach($mapping in $project.Mapping){
$workspace.Map($mapping.ServerItem, $mapping.LocalItem);
}
}
}
$resultTime= Measure-Command {
$result=$workspace.Get([Microsoft.TeamFoundation.VersionControl.Client.VersionSpec]::Latest,[Microsoft.TeamFoundation.VersionControl.Client.GetOptions]::Overwrite)
}
$resultAlerts=$result.NumConflicts+ $result.NumFailures+ $result.NumWarnings
if ($resultAlerts -eq 0) {
Write-host "Get Operation completed without any errors. $($result.NumUpdated) files are updated on workspace: $($workspace.name)" }
else {
Write-Host "Get Operation completed with errors. Please check errors: $($result.GetFailures)"}
$resultTime.ToString()
}
catch
{
return $_.exception
}
}
[string] $tfs13Url="http://tfs:8080/tfs/defaultcollection",
[string] $workspaceName = "AutomatedWorkspace",
[string] $workspaceDescription = "AutomatedWorkspace",
[string] $fileName="D:\projects\ProjectMapping.xml",
[bool] $autoDeleteCreate =$true
Get-ProjectXml $tfs13Url $workspaceName $workspaceDescription $fileName $autoDeleteCreate
Assuming your xml is like:
<?xml version="1.0"?>
<Collection Name="DefaultCollection">
<Root>
<ServerItem>$/</ServerItem>
<LocalItem>d:\Src</LocalItem>
</Root>
<Project name="ProjectA">
<Mapping>
<ServerItem>$/ProjectA/Folder1/BranchDEF</ServerItem>
<LocalItem>d:\Src\ProjectA\Folder1\BranchDEF\</LocalItem>
</Mapping>
<Mapping>
<ServerItem>$/ProjectA/Folder2\BranchABC</ServerItem>
<LocalItem>d:\Src\ProjectA\Folder2\BranchABC</LocalItem>
</Mapping>
</Project>
<Project Name="ProjectB">
<Mapping>
<ServerItem>$/ProjectB/Folder5\BranchXYZ</ServerItem>
<LocalItem>d:\Src\ProjectB\Folder5\BranchXYZ</LocalItem>
</Mapping>
</Project>
</Collection>
Related
I have Multiple folders, for Example, www contains Folders A, B, C and So on and this same structure exist in a different environment like INT, REG, DR, PROD. each contains different web.config.
So Here comes the challenge we need to provide an automated way to check-in each web.config.
Named like web_A_int.config, web_A_REG.config , Web_A_Prod.config, Web_A_DR.config and Web_B_int.config , Web_B_Reg_config and so on .
The environment is TFS (2015) Source Control and solution is .net based solution.
Please let me know if anything you might need to understand the question.
How to maintain this structure post build compilation. or How we can use proj or Powershell script to achieve it?
Desired Output:
INT--> www--> A, B,C Folders and each web.config placed inside respective folder.
REG--> www--> A, B, C --> with each web.config and each module level placed correctly.
One way to solve this is to use web transforms and create a web.{environment}.config file and then apply that transformation on deployment.
The Microsoft docs should get you started on how to use web transforms. I can provide you with some Powershell code on how to do this if that's how you decide to go.
I'm not familiar web server deployment in TFS, but here are some regular powershell functions you could use to build loops:
$environmentTypes = #('INT', 'REG', 'DR', 'PROD')
Function Get-WebConfig([String]$Path)
{
try {
$configContent = Get-Content \\path\to\config\file
}
catch {
Write-Debug "ERROR: Could not get content of $($Path)"
}
return $configContent
}
Function Create-FolderStructure() {
[cmdletBinding()]
param(
[Parameter(Mandatory)]
[ValidateSet('INT','REG','DR','PROD')]
[String]$EnvironmentType,
[Parameter(Mandatory)]
[String]$Server,
[Parameter(Mandatory)]
[String[]]$FolderNames,
[Parameter(Mandatory=$false)]
[Switch]$CheckServerUp
)
Begin {}
Process {
try {
if($CheckServerUp)
{
try {
Test-Connection -ComputerName $Server
}
catch {
Write-Debug "ERROR: Unable to test connection to $Server"
}
}
foreach($item in $FolderNames)
{
try {
New-Item -Path "\\$Server\c$\www\$item" -ItemType Directory
}
catch {
Write-Debug "ERROR: Unable to create folder $item"
}
try {
switch($EnvironmentType)
{
'INT'
{
$neededConfig = Get-WebConfig -Path \\path\to\your\intconfig
break
}
'REG'
{
$neededConfig = Get-WebConfig -Path \\path\to\your\regconfig
break
}
'DR'
{
$neededConfig = Get-WebConfig -Path \\path\to\your\drconfig
break
}
'PROD'
{
$neededConfig = Get-WebConfig -Path \\path\to\your\prodconfig
break
}
try {
New-Item -Path "\\$Server\c$\www\$item\Web_$EnvironmentType.config"
Set-Content -Path "\\$Server\c$\www\$item\Web_$EnvironmentType.config" -Value $neededConfig
}
catch {
Write-Debug "ERROR: Unable to create file"
}
}
}
catch {
Write-Debug "ERROR: Unable to get web config content"
}
}
}
catch {
Write-Debug "ERROR: Could not get content of $($Path)"
}
}
End{}
}
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
}
}
I'm working on a TFS build with a pre-build PowerShell script that (in addition to building my app) automatically checks out a file where we maintain version, increments the build number, then checks the file in.
I have been able to do this, except that I get an error from the script which results in a partially successful build (orange). I need to have a fully successful (green) build.
Here's the check-in line (using TFS Power Tools for VS 2013):
New-TfsChangeset -Item $versionFile -Override "Automated" -Notes "Code Reviewer=tfs" -Comment "Automated"
The error I receive is that the changeset is not associated with a work item, but the -Override should handle that. The funny thing is that it checks in anyway.
Running locally on my machine instead of the build server, I get the same thing, except that I also see a line that says The policies have been overridden. This tells me that the override is working, but it still outputs the error.
I've tried adding -ErrorAction SilentlyContinue, but it has no effect.
I need one of three options:
A way to suppress output of the checkin error,
A way to create a work item and associate it to the checkin, or
Some other third option that will result in a green build.
Any ideas?
Credit goes to Eddie - MSFT for leading me the right direction, but I wanted to consolidate everything here.
WARNING This will check in all pending changes in the workspace.
Creating a new work item (source)
I did modify it quite a bit to support automation. It connects to TFS and generates a new work item.
function New-WorkItem()
{
# These *should* be registered in the GAC.
# The version numbers will likely have to change as new versions of Visual Studio are installed on the server.
Add-Type -Assembly "Microsoft.TeamFoundation.Client, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
Add-Type -Assembly "Microsoft.TeamFoundation.WorkItemTracking.Client, Version=12.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
$server = "http://YOURTFSSERVER:8080/tfs"
$tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($server)
$type = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]
$store = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore] $tfs.GetService($type)
$workItem = New-Object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem($store.Projects[0].WorkItemTypes[0])
$workItem.Title = "Automated work item"
$workItem
}
Associating the work item and checking in
Slight modifications to the code from the link given by Eddie, we get the following:
function CheckIn()
{
param([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem] $workItem)
$col = Get-TfsCollection("http://YOURTFSSERVER:8080/tfs/YOURCOLLECTION")
$vcs = Get-TfsVersionControlServer($col)
$ws = $vcs.GetWorkspace([System.IO.Path]::GetDirectoryName($anyPathInWorkspace))
$pc = $ws.GetPendingChanges()
$wici = Get-TfsWorkItemCheckinInfo($workItem)
$changeset = $ws.CheckIn($pc, "Automated check in", $null, $wici, $null)
}
That post doesn't tell you that Get-TfsCollection, Get-TfsVersionControlServer, and Get-TfsWorkItemCheckinInfo aren't defined. I had to find them.
I found the first two on http://nkdagility.com/powershell-tfs-2013-api-1-get-tfscollection-and-tfs-services/. I didn't have to change anything.
function Get-TfsCollection
{
param([string] $CollectionUrl)
if ($CollectionUrl -ne "")
{
#if collection is passed then use it and select all projects
$tfs = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($CollectionUrl)
}
else
{
#if no collection specified, open project picker to select it via gui
$picker = New-Object Microsoft.TeamFoundation.Client.TeamProjectPicker([Microsoft.TeamFoundation.Client.TeamProjectPickerMode]::NoProject, $false)
$dialogResult = $picker.ShowDialog()
if ($dialogResult -ne "OK")
{
#exit
}
$tfs = $picker.SelectedTeamProjectCollection
}
$tfs
}
function Get-TfsVersionControlServer
{
param([Microsoft.TeamFoundation.Client.TfsTeamProjectCollection] $TfsCollection)
$TfsCollection.GetService("Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer")
}
But I couldn't find Get-TfsWorkItemCheckinInfo. The only Google hit was the kinook link from Eddie (and soon probably this answer). Here's what I came up with:
function Get-TfsWorkItemCheckinInfo
{
param([Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem] $workItem)
$wi = New-Object Microsoft.TeamFoundation.VersionControl.Client.WorkItemCheckinInfo($workItem, [Microsoft.TeamFoundation.VersionControl.Client.WorkItemCheckinAction]::Resolve)
$wi
}
Now we can use it
CheckIn (New-WorkItem)
That's it!
You can create a work item from PowerShell by following this article: http://halanstevens.com/blog/powershell-script-to-create-a-workitem/
Quote the code here for reference:
$key = Get-ItemProperty HKLM:\SOFTWARE\Microsoft\VisualStudio\8.0
$dir = [string] (Get-ItemProperty $key.InstallDir)
$dir += "PrivateAssemblies\"
$lib = $dir + "Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
[Reflection.Assembly]::LoadFrom($lib)
$lib = $dir + "Microsoft.TeamFoundation.Client.dll"
[Reflection.Assembly]::LoadFrom($lib)
"Please enter your Team Foundation Server Name:"
$server = [Console]::ReadLine()
$server = $server.Trim()
"Connecting to " + $server + "..."
$tfs = [Microsoft.TeamFoundation.Client.TeamFoundationServerFactory]::GetServer($server)
$type = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore]
$store = [Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore] $tfs.GetService($type)
$workItem = new-object Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem($store.Projects[0].WorkItemTypes[0])
"Created a new work item of type " + $workItem.Type.Name
$workItem.Title = "Created by Windows PowerShell!"
$workItem.Save()
And then refer to this article to associate the work item to changeset: http://www.kinook.com/Forum/showthread.php?t=4502
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
I want to write nuspec for MEF plugin.
I can copy xxx.dll to content directory, like below.
<files>
<file src="Alhambra\bin\Release\Plugins\Alhambra.Plugin.SqlServer.dll" target="content\Plugins\Alhambra.Plugin.SqlServer.dll" />
<file src="Alhambra\bin\Release\Alhambra.dll" target="lib\Alhambra.dll" />
</files>
but I can't set file property in user project to copy output directory.
Thanks for any suggestions or code snippets.
My approach would be to add the plugin to the project as a separate file. For that you need an install script (see NuGet docs for this).
My solution for this is the following script:
param($installPath, $toolsPath, $package, $project)
Function add_file($file)
{
$do_add = 1
foreach($item in $project.DTE.ActiveSolutionProjects[0].ProjectItems)
{
if ($item -eq $file)
{ $do_add = 0 }
}
if ($do_add -eq 1)
{
$added = $project.DTE.ItemOperations.AddExistingItem($file)
$added.Properties.Item("CopyToOutputDirectory").Value = 2
$added.Properties.Item("BuildAction").Value = 0
}
}
add_file(<file1>)
add_file(<file2>)
Of course, when the user uninstalls the package, you need to clean up:
param($installPath, $toolsPath, $package, $project)
Function remove_file($file)
{
foreach($item in $project.DTE.ActiveSolutionProjects[0].ProjectItems)
{
if ($item.Name -eq $file)
{
$item.Delete()
}
}
}
remove_file(<file1>)
remove_file(<file2>)