This repository has been archived by the owner on Apr 15, 2020. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6027007
commit 916ddba
Showing
9 changed files
with
365 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
.vs/ | ||
*.vsix |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
[CmdletBinding()] | ||
param( | ||
# repository name | ||
[string] | ||
[parameter(mandatory=$true)] | ||
$RepositoryUrl, | ||
|
||
# the root directory to store all git repositories | ||
[string] | ||
[parameter(mandatory=$false)] | ||
$RepositoryPath, | ||
|
||
# branch/tag name to checkout | ||
[parameter(mandatory=$false)] | ||
[string] | ||
$BranchTag = 'master', | ||
|
||
# determine whether to clean the folder before downloading the repository or not (default: false) | ||
[parameter(mandatory=$false)] | ||
[string] | ||
[ValidateSet('true', 'false', 'yes', 'no')] | ||
$Clean = 'false' | ||
) | ||
|
||
Function Get-CurrentBranch { | ||
$branch = (git symbolic-ref -q --short HEAD) | ||
If (-not ([string]::IsNullOrEmpty($branch)) -and ($branch -ne 'HEAD')) { | ||
Return $branch | ||
} | ||
$tag = (git describe --tags --exact-match) | ||
Return $tag | ||
} | ||
|
||
Function Invoke-VerboseCommand { | ||
param( | ||
[ScriptBlock]$Command, | ||
[string] $StderrPrefix = "", | ||
[int[]]$AllowedExitCodes = @(0) | ||
) | ||
$Script = $Command.ToString() | ||
$Captures = Select-String '\$(\w+)' -Input $Script -AllMatches | ||
ForEach ($Capture in $Captures.Matches) { | ||
$Variable = $Capture.Groups[1].Value | ||
$Value = Get-Variable -Name $Variable -ValueOnly | ||
$Script = $Script.Replace("`$$($Variable)", $Value) | ||
} | ||
Write-Host $Script | ||
If ($script:ErrorActionPreference -ne $null) { | ||
$backupErrorActionPreference = $script:ErrorActionPreference | ||
} ElseIf ($ErrorActionPreference -ne $null) { | ||
$backupErrorActionPreference = $ErrorActionPreference | ||
} | ||
$script:ErrorActionPreference = "Continue" | ||
try | ||
{ | ||
& $Command 2>&1 | ForEach-Object -Process ` | ||
{ | ||
if ($_ -is [System.Management.Automation.ErrorRecord]) | ||
{ | ||
"$StderrPrefix$_" | ||
} | ||
else | ||
{ | ||
"$_" | ||
} | ||
} | ||
if ($AllowedExitCodes -notcontains $LASTEXITCODE) | ||
{ | ||
throw "Execution failed with exit code $LASTEXITCODE" | ||
} | ||
} | ||
finally | ||
{ | ||
$script:ErrorActionPreference = $backupErrorActionPreference | ||
} | ||
} | ||
|
||
Function Update-GitRepository { | ||
param( | ||
[string]$Path, | ||
[string]$BranchTag | ||
) | ||
|
||
Write-Host "Updating repository inside $Path" | ||
Set-Location $Path | Out-Null | ||
$CurrentBranch = Get-CurrentBranch | ||
Invoke-VerboseCommand -Command { git stash } | ||
If ($CurrentBranch -ine $BranchTag) { | ||
Write-Host "Undoing any pending changes in $Path" | ||
Invoke-VerboseCommand -Command { | ||
git checkout "$BranchTag" | ||
git checkout -- . | ||
git clean -fdx | ||
} | ||
} | ||
Write-Host "Pulling update from branch/tag $BranchTag" | ||
Invoke-VerboseCommand -Command { git config credential.interactive never } | ||
# try to use token provided by TFS server | ||
If (Test-SameTfsServer -Uri $Uri) { | ||
$AuthHeader = "Authorization: bearer $($Env:SYSTEM_ACCESSTOKEN)" | ||
Invoke-VerboseCommand -Command { git -c http.extraheader="$AuthHeader" pull origin "$BranchTag" } | ||
} | ||
Else { | ||
Invoke-VerboseCommand -Command { git pull origin "$BranchTag" } | ||
} | ||
} | ||
|
||
Function Start-CloneGitRepository { | ||
param( | ||
[string]$Uri, | ||
[string]$BranchTag, | ||
[string]$Path | ||
) | ||
|
||
Write-Host "Cloning $Uri for branch/tag '$BranchTag' into $Path" | ||
New-Item -Path $Path -ItemType Directory -Force | Out-Null | ||
Set-Location -Path $Path | Out-Null | ||
Invoke-VerboseCommand -Command { | ||
git init "$Path" | ||
git config credential.interactive never | ||
git remote add origin "$Uri" | ||
} | ||
# try to embed authentication from system token for same TFS server | ||
If ((Test-SameTfsServer -Uri $Uri)) { | ||
$AuthHeader = "Authorization: bearer $($Env:SYSTEM_ACCESSTOKEN)" | ||
Invoke-VerboseCommand -Command { git -c http.extraheader="$AuthHeader" fetch --progress --prune origin "$BranchTag" } | ||
} | ||
Else { | ||
Invoke-VerboseCommand -Command { git fetch --progress --prune origin "$BranchTag" } | ||
} | ||
If ($LastExitCode -ne 0) { | ||
Write-Error $output -ErrorAction Stop | ||
} | ||
Invoke-VerboseCommand -Command { git checkout --progress --force "$BranchTag" } | ||
} | ||
|
||
Function Get-GitRepositoryUri { | ||
$Uris = @($Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI, $Env:SYSTEM_TEAMPROJECT, '_git') | %{ $_.Trim('/') } | ||
Return $Uris -join '/' | ||
} | ||
|
||
Function Test-SameTfsServer { | ||
param([string]$Uri) | ||
$DefaultUri = $Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI | ||
# we don't care the protocol, either http or https | ||
$DefaultUri = $DefaultUri -replace '^https?:', '' | ||
$Uri = $Uri -replace '^https?:', '' | ||
$escapedPattern = [regex]::Escape($DefaultUri) | ||
Return $Uri -match "^($($escapedPattern))" | ||
} | ||
|
||
try { | ||
# try to find in PATH environment | ||
Get-Command -Name git -CommandType Application -ErrorAction Stop | Out-Null | ||
} catch [System.Management.Automation.CommandNotFoundException] { | ||
# try to find git in default location | ||
If (-not (Test-Path -Path "$($env:ProgramFiles)\Git\bin\git.exe" -PathType Leaf)) { | ||
Write-Error "Git command line not found or not installed" -ErrorAction Stop | ||
} | ||
Set-Alias -Name git -Value $env:ProgramFiles\Git\bin\git.exe -Force | Out-Null | ||
} | ||
|
||
$GitRepositoryUri = Get-GitRepositoryUri | ||
Write-Host "##vso[task.setvariable variable=Build.Repository.GitUri]$GitRepositoryUri" | ||
$RepositoryUrl = $RepositoryUrl -replace ([regex]::Escape('$(Build.Repository.GitUri)')), $GitRepositoryUri | ||
$BuildDirectory = $Env:AGENT_BUILDDIRECTORY | ||
$GitDirectory = [System.IO.Path]::Combine($BuildDirectory, 'git') | ||
Write-Host "##vso[task.setvariable variable=Build.GitDirectory]$GitDirectory" | ||
If ([string]::IsNullOrWhiteSpace($RepositoryPath)) { | ||
# set default repository path | ||
$RepositoryName = $RepositoryUrl.Substring($RepositoryUrl.TrimEnd('/').LastIndexOf('/') + 1) | ||
$RepositoryPath = "`$(Build.GitDirectory)\$RepositoryName" | ||
} | ||
$RepositoryPath = $RepositoryPath -replace ([regex]::Escape('$(Build.GitDirectory)')), $GitDirectory | ||
|
||
# ensure containing git folder exists | ||
$RepositoryFolder = [System.IO.Path]::GetDirectoryName($RepositoryPath) | ||
If (-not (Test-Path -Path "$RepositoryFolder" -PathType Container)) { | ||
Write-Host "Creating git directory: $RepositoryFolder" | ||
New-Item -Path "$RepositoryFolder" -ItemType Directory -Force | Out-Null | ||
} | ||
|
||
$CurrentDirectory = (Get-Location).Path | ||
If (Test-Path -Path "$RepositoryPath" -PathType Container) { | ||
If (@('true', 'yes').Contains($Clean.ToLower())) { | ||
Write-Host "Cleaning directory $RepositoryPath" | ||
Remove-Item -Path "$RepositoryPath" -Recurse -Force | Out-Null | ||
Start-CloneGitRepository -Path "$RepositoryPath" -Uri "$RepositoryUrl" -BranchTag $BranchTag | ||
} | ||
Else { | ||
Update-GitRepository -Path "$RepositoryPath" -BranchTag $BranchTag | ||
} | ||
} | ||
Else { | ||
Start-CloneGitRepository -Path "$RepositoryPath" -Uri "$RepositoryUrl" -BranchTag $BranchTag | ||
} | ||
|
||
Set-Location "$CurrentDirectory" |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
{ | ||
"id": "56033F91-AEB8-4316-B19A-BCB721F4705B", | ||
"name": "GitDownloader", | ||
"friendlyName": "Git Repository Downloader", | ||
"description": "Download additional git repository from public repository or this TFS", | ||
"helpMarkDown": "This task will download git repository as an addition to default source. Consider this task as workaround where TFS build with git repository can only download 1 repository. This task assumes that git is already installed in %ProgramFiles%\\Git within TFS build agent's machine.", | ||
"category": "Utility", | ||
"visibility": [ | ||
"Build", | ||
"Release" | ||
], | ||
"runsOn": ["Agent"], | ||
"author": "Fakhrulhilal Maktum", | ||
"version": { | ||
"Major": 0, | ||
"Minor": 2, | ||
"Patch": 8 | ||
}, | ||
"instanceNameFormat": "Fetch git: $(Repository)", | ||
"groups": [ | ||
{ | ||
"name": "advanced", | ||
"displayName": "Advanced", | ||
"isExpanded": false | ||
} | ||
], | ||
"inputs": [ | ||
{ | ||
"name": "RepositoryUrl", | ||
"type": "string", | ||
"label": "Repository URL", | ||
"defaultValue": "", | ||
"required": true, | ||
"helpMarkDown": "Repository URL to download. Use $(Build.Repository.GitUri) to refer relative URI for git URL, ex: https://your-onpremise-tfs.com/tfs/DefaultCollection/TeamProject/_git/GitRepo or just $(Build.Repository.GitUri)/GitRepo. This repository should be able to be cloned without authentication." | ||
}, | ||
{ | ||
"name": "RepositoryPath", | ||
"type": "string", | ||
"label": "Repository Path", | ||
"defaultValue": "", | ||
"required": false, | ||
"helpMarkDown": "Full path to store the git repository. If not specified, then it will be located in $(Build.GitDirectory)\\[repo name]." | ||
}, | ||
{ | ||
"name": "BranchTag", | ||
"type": "string", | ||
"label": "Branch/Tag", | ||
"defaultValue": "master", | ||
"required": true, | ||
"helpMarkDown": "Branch/tag to checkout. By default, it will download single branch/tag and will checkout to that branch/tag automatically." | ||
}, | ||
{ | ||
"name": "Clean", | ||
"type": "boolean", | ||
"label": "Clean", | ||
"defaultValue": "false", | ||
"required": false, | ||
"helpMarkDown": "If this is true, will remove previously downloaded repository and use clone command to download. Otherwise, it will stash everything and pull the remote branch/tag." | ||
} | ||
], | ||
"execution": { | ||
"Powershell": { | ||
"target": "GitDownloader.ps1" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Git Downloader | ||
|
||
This task will download git repository as an addition to default source. Consider this task as workaround where TFS build with git repository can only download 1 repository. Then this task will download another git repository required by your TFS build definition. This task assumes that git is already installed in %ProgramFiles%\\Git within TFS build agent's machine. | ||
|
||
## Parameters | ||
|
||
1. Repository URL | ||
|
||
URL of git repository. The repository should be public that can be downloaded without authentication. If it's the same location of main repository (specified in `Get sources`/first task of the build), then it's automatically uses the same credential. | ||
|
||
Use `$(Build.Repository.GitUri)` to refer relative URI for git URL, examples: | ||
- https://your-onpremise-tfs.com/tfs/DefaultCollection/TeamProject/_git/GitRepo | ||
- https://youraccount.visualstudio.com/TeamProject/_git/GitRepo | ||
- $(Build.Repository.GitUri)/GitRepo | ||
|
||
2. Repository Path | ||
|
||
Full path to store the git repository. If not specified, then it will be located in $(Build.GitDirectory)\\[repo name]. | ||
|
||
3. Branch/tag | ||
|
||
Branch/tag to checkout. By default, it will download single branch/tag and will checkout to that branch/tag automatically. | ||
|
||
4. Clean | ||
|
||
If this is true, it will remove previously downloaded repository and use `git clone` command to download. Otherwise, it will stash everything and pull the remote branch/tag. | ||
|
||
![screenshot](images/task.jpg "Task") | ||
|
||
By default, this task expects public git repository that can be downloaded without authentication. But when the git repository is located in same TFS server, than it requires OAuth token for successful download. You can enable it in **Options** tab and check for **Allow scripts to access OAuth token** like screenshot below: | ||
![enable OAuth token](images/enable_oauth_token.jpg "Options") | ||
|
||
The git URL is considered the same TFS server when it's located in the same URL as TFS server (ignoring HTTP protocol). Supposed we have TFS public URL http://yourdomain/, so these URLs are considered the same TFS server: | ||
|
||
- https://yourdomain/tfs/DefaultCollection/TeamProject/_git/GitRepo | ||
- http://yourdomain/tfs/DefaultCollection/_git/TeamProject | ||
- https://yourdomain/tfs/DefaultCollection/AnotherTeamProject/_git/GitRepo | ||
- https://yourdomain/tfs/DefaultCollection/_git/AnotherTeamProject | ||
- $(Build.Repository.GitUri)/GitRepo | ||
|
||
And these are considered as different TFS server (means should be downloadable without authentication): | ||
|
||
- https://yourdomain.root.domain.com/tfs/DefaultCollection/TeamProject/_git/GitRepo | ||
- http://yourdomain.root.domain.com/tfs/DefaultCollection/_git/TeamProject | ||
- https://yourdomain.root.domain.com/tfs/DefaultCollection/AnotherTeamProject/_git/GitRepo | ||
- https://yourdomain.root.domain.com/tfs/DefaultCollection/_git/AnotherTeamProject | ||
|
||
This extension relies on [`$(System.TeamFoundationCollectionUri)`]( | ||
https://docs.microsoft.com/en-us/vsts/build-release/concepts/definitions/build/variables#systemteamfoundationcollectionuri) variable to check for same server. To ensure which domain your TFS server is running on, try to create dummy build definition and create one PowerShell task containing this code: | ||
```PowerShell | ||
Write-Host "$Env:SYSTEM_TEAMFOUNDATIONCOLLECTIONURI" | ||
``` |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
{ | ||
"manifestVersion": 1, | ||
"id": "GitDownloader", | ||
"name": "Git Repository Downloader", | ||
"version": "0.2.8", | ||
"publisher": "fakhrulhilal-maktum", | ||
"targets": [ | ||
{ | ||
"id": "Microsoft.VisualStudio.Services" | ||
} | ||
], | ||
"description": "Download additional git repository from public repository or this TFS", | ||
"categories": [ | ||
"Build and release" | ||
], | ||
"tags": [ | ||
"build", | ||
"git" | ||
], | ||
"icons": { | ||
"default": "images/logo.png" | ||
}, | ||
"content": { | ||
"details": { "path": "README.md"} | ||
}, | ||
"files": [ | ||
{ | ||
"path": "GitDownloader" | ||
}, | ||
{ | ||
"path": "images", | ||
"addressable": true | ||
} | ||
], | ||
"contributions": [ | ||
{ | ||
"id": "git-downloader", | ||
"type": "ms.vss-distributed-task.task", | ||
"targets": [ | ||
"ms.vss-distributed-task.tasks" | ||
], | ||
"properties": { | ||
"name": "GitDownloader" | ||
} | ||
} | ||
] | ||
} |