TFS 2017 vNext Build Get workspace with powershell

2019-07-15 22:08发布

问题:

I have a project where it is needed to change some files during build process. I have to use Powershell to do this. I have already configured all required steps to do this. All steps work on my client pc. The Build server has the same configuration. Vs 2015 (for TF Powertools 2015) and VS 2017 installed. When I queued a Build, the build fails at the point where he tries to get the Workspace. Maybe this is because, the build agent creates only local workspaces? At this point the required changes are already checked out. I Cannot use TF.exe checkin, because there are checkin policies which prevents the checkin without associated workitem. This is what I try to do at this step:

  • Get Workspace via path of the sources ($Env:BUILD_SOURCESDIRECTORY)
  • Get the pending changes of this workspace $pendingChanges = $tfsws.GetPendingChanges()
  • Create workitemcheckininfo with an associated workitem
  • Checkin with created workitemcheckininfo $changesetNumber = $tfsws.CheckIn($pendingChanges,"$CommentString checked in by BuildServer",$null, $workItemChanges,$null)

As I dont get the Workspace (step 1), the following steps will not start.

This is what I tried so far:

1

$binpath   = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer"
#$binpath   = "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ReferenceAssemblies\v2.0"
Add-Type -path "$binpath\Microsoft.TeamFoundation.Client.dll"
Add-Type -Path "$binpath\Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
Add-Type -Path "$binpath\Microsoft.TeamFoundation.VersionControl.Client.dll"
Add-Type -Path "$binpath\Microsoft.TeamFoundation.Common.dll"

$teamProjectCollection = "http://mytfs/tfs/defaultcollection"
$tfs = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($teamProjectCollection)

#The next line fails, as the commandlet is from TF Powertools 2015 and the TFS server is 2017.
#I get the error message "Microsoft.TeamFoundation.Client.TfsTeamProjectCollection cannot be converted to Microsoft.TeamFoundation.Client.TfsTeamProjectCollection"
$tfsws = Get-TfsWorkspace -Server $tfs -Computer $hostname -Owner $Username

2

$binpath   = "C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer"
#$binpath   = "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ReferenceAssemblies\v2.0"
Add-Type -path "$binpath\Microsoft.TeamFoundation.Client.dll"
Add-Type -Path "$binpath\Microsoft.TeamFoundation.WorkItemTracking.Client.dll"
Add-Type -Path "$binpath\Microsoft.TeamFoundation.VersionControl.Client.dll"
Add-Type -Path "$binpath\Microsoft.TeamFoundation.Common.dll"

$localReference = join-path $Env:BUILD_SOURCESDIRECTORY $TargetBranch
$teamProjectCollection = "http://mytfs/tfs/defaultcollection"
$tfsTeamProjectCollection = New-Object Microsoft.TeamFoundation.Client.TfsTeamProjectCollection($teamProjectCollection)
$versioncontrolServer = $tfsTeamProjectCollection.GetService([Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer])

#The next lines fail, as $versioncontrolServer is nothing
[Microsoft.TeamFoundation.VersionControl.Client.Workstation]::Current.EnsureUpdateWorkspaceInfoCache($versionControlServer, $username);
$tfsws = $versioncontrolServer.GetWorkspace($localReference)

Two things, that maybe causing the problems: 1st => build agent only uses local workspaces? 2nd => TFS 2017 and VS 2015 are not compatible enough?

Has someone a good working example or solution?

I thought about other options. Maybe I could program an executable, which does my stuff.

Can I checkin without workspace and later associate the workitem? How to programmatically associate a workitem with an existing Changeset?

回答1:

You cannot checkin without workspace.

However the process will create a local workspace in get source step. So you can directly check in the changes bypass the check in policies, then associate a workitem with the specific Changeset later.

To bypass/override the check in policies, you can run below check in command (You can copy below command and save as a PowerShell/cmd script, then add a PowerShell/Command task to run the script). See Checkin command:

tf Checkin $source_dir /comment:"Change files" /noprompt /force /bypass /override:"Without associating workitem"

Note: Make sure that the agent you are using is 2.122.1 or later version, otherwise you may meet errors, see this related thread for details.

To associate a workitem with an existing Changeset:

C# with Client API:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace APPI
{
    class Program
    {
        static void Main(string[] args)
        {
            string url = "http://xxx.xxx.xxx.xxx:8080/tfs/DefaultCollection";
            TfsTeamProjectCollection ttpc = new TfsTeamProjectCollection(new Uri(url));
            WorkItemStore wis = ttpc.GetService<WorkItemStore>();
            VersionControlServer vcs = ttpc.GetService<VersionControlServer>();
            int wid = 194;
            int cid = 440;
            WorkItem wi = wis.GetWorkItem(wid);
            Changeset cs = vcs.GetChangeset(cid);
            ExternalLink el = new ExternalLink(wis.RegisteredLinkTypes["Fixed in Changeset"], cs.ArtifactUri.AbsoluteUri);
            wi.Links.Add(el);
            wi.Save();     
        }
    }
}

PowerShell with REST API:

Param(
   [string]$collectionurl = "http://server:8080/tfs/DefaultCollection",
   [string]$keepForever = "true",
   [string]$WorkitemID = "194",
   [string]$ChangesetID = "439",
   [string]$user = "UserName",
   [string]$token = "password/token"
)

# Base64-encodes the Personal Access Token (PAT) appropriately
$base64AuthInfo = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(("{0}:{1}" -f $user,$token)))

function CreateJsonBody
{

    $value = @"
[
 {
    "op": "add",
    "path": "/relations/-",
    "value": {
      "rel": "ArtifactLink",
      "url": "vstfs:///VersionControl/Changeset/$ChangesetID",
      "attributes": {
        "name": "Fixed in Changeset"
      }
    }
  }
]

"@

 return $value
}

$json = CreateJsonBody

$uri = "$($collectionurl)/_apis/wit/workitems/$($WorkitemID)?api-version=1.0"
$result = Invoke-RestMethod -Uri $uri -Method Patch -Body $json -ContentType "application/json-patch+json" -Headers @{Authorization=("Basic {0}" -f $base64AuthInfo)}


回答2:

You may want to check out (no pun intended) my TFVC build tasks for VSTS/TFS. It offers most everything you need. The only thing it misses is work item association unless you use the Update Gated Changes.