0

I'm using a local TFS 2018 instance for a customer project in which he owns the source code. I want him to have complete access to this code in the event I become unexpectedly unavailable.

My proposal for this is to create a TFS release step that syncs my TFS origin with a repo in his VSTS account.

I've been able to do this manually, from my workstation, using this approach:

cd /path/to/local/repo
git remote add tfs url://tfs/git/repo
git push --mirror tfs

But I'm prompted for his VSTS credentials, which obviously won't work in a TFS release step.

Also, I encounter this error when attempting to remote add from the desktop of my TFS server:

D:\Agent\_work\37\s>git remote add Application https://customer.visualstudio.com/Applications/_git/Application
Rename from 'D:/Agent/_work/37/s/.git/config.lock' to 'D:/Agent/_work/37/s/.git/config' failed. Should I try again? (y/n)

I can add it at my workstation, although it doesn't show up as a change that can be committed for a push to TFS. So it seems I'm unable to tell TFS about the remote VSTS repository.

How can I fully automate this task in a TFS release step?

InteXX
  • 6,135
  • 6
  • 43
  • 80

1 Answers1

0

I was able to get this working using a combination of these answers:

  1. https://stackoverflow.com/a/45224858/722393
  2. https://stackoverflow.com/a/50925741/722393

The trick is to use the Personal Access Token (PAT), found in the security section of the VSTS user profile.

Here're the scripts I ended up with:

SyncCode.ps1, called from the build step

[CmdletBinding()]
param(
    [Parameter(Mandatory)][string] $Application,
    [Parameter(Mandatory)][string] $LocalPath,
    [Parameter(Mandatory)][string] $Token
)

. \\SERVER\Scripts\TfsBuild\Invoke-Git.ps1 

$RemoteExists = $false
$AllRemotes = git remote

ForEach($Remote in $AllRemotes) {
    If($Remote = $Application) {
        $RemoteExists = $true
        Break
    }
}

If($RemoteExists) {
    Invoke-Git -Command "remote remove $Application"
}

Invoke-Git -Command "remote add $Application https://Personal%20Access%20Token:$Token@customer.visualstudio.com/Applications/_git/$Application"
Set-Location $LocalPath
Invoke-Git -Command "push --mirror HydraMonitor"

Invoke-Git.ps1, called from SyncCode.ps1

<#
.Synopsis
    Invoke git, handling its quirky stderr that isn't error

.Outputs
    Git messages, and lastly the exit code

.Example
    Invoke-Git push

.Example
    Invoke-Git "add ."
#>
Function Invoke-Git
{
param(
[Parameter(Mandatory)]
[string] $Command )

    Write-Output "##[command]. git $Command"

    Try {
        $Exit = 0
        $Path = [System.IO.Path]::GetTempFileName()

        Invoke-Expression "git $Command 2> $Path"

        $Exit = $LASTEXITCODE

        If ( $Exit -gt 0 ) {
            Write-Error (Get-Content $Path).ToString()
        }
        Else {
            Get-Content $Path
        }

        "Exit code: $Exit"
    }
    Catch {
        Write-Host "Error: $_`n$($_.ScriptStackTrace)"
    }
    Finally {
        If ( Test-Path $Path ) {
            Remove-Item $Path
        }
    }
}
InteXX
  • 6,135
  • 6
  • 43
  • 80