functions/_copyFolderFromGitRepo.ps1
|
# <copyright file="_copyFolderFromGitRepo.ps1" company="Endjin Limited"> # Copyright (c) Endjin Limited. All rights reserved. # </copyright> function _copyFolderFromGitRepo { [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)] [string] $RepoUrl, [Parameter(Mandatory)] [string] $RepoFolderPath, [Parameter(Mandatory)] [string] $DestinationPath, [Parameter()] [ValidateNotNullOrEmpty()] [string] $GitRef = 'main', [Parameter()] [string] $Commit, [Parameter()] [ValidateNotNullOrEmpty()] [string] $GitCmd = 'git' ) if (!(Get-Command $GitCmd -ErrorAction Ignore)) { throw "Git CLI is not installed. Please install Git CLI before trying to retrieve extensions from Git repositories." } # Override any line-ending settings in the user's git configuration, so that the checked-out files # are the same on every machine and their content hashes are reproducible. $gitConfigArgs = @( '-c', 'core.autocrlf=false' '-c', 'core.eol=lf' '-c', 'init.defaultBranch=main' '-c', 'advice.detachedHead=false' ) # Runs a git command in the temporary repository, logging its output and failing with # the command's output when it returns a non-zero exit code. $invokeGit = { $PSNativeCommandUseErrorActionPreference = $false $output = & $GitCmd @gitConfigArgs -C $tempDir @args 2>&1 $exitCode = $LASTEXITCODE $output | ForEach-Object { Write-Verbose "git: $_" } if ($exitCode -ne 0) { throw "git $($args -join ' ') failed with exit code $exitCode`n$($output -join "`n")" } $output | Where-Object { $_ -isnot [System.Management.Automation.ErrorRecord] } } # Create a temporary folder for cloning $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) "$(New-Guid)" New-Item -ItemType Directory -Path $tempDir | Out-Null try { & $invokeGit init --quiet | Out-Null if ($Commit) { Write-Verbose "Fetching commit $Commit ($GitRef) from $RepoUrl..." try { & $invokeGit fetch --depth 1 --quiet $RepoUrl $Commit | Out-Null } catch { # Some git servers do not allow a specific commit to be fetched, so fallback to fetching # the full history of the ref and finding the commit in that. Write-Verbose "Unable to fetch commit $Commit directly, fetching the history of '$GitRef' instead" & $invokeGit fetch --quiet $RepoUrl $GitRef | Out-Null } & $invokeGit checkout --quiet $Commit | Out-Null } else { Write-Verbose "Fetching '$GitRef' from $RepoUrl..." & $invokeGit fetch --depth 1 --quiet $RepoUrl $GitRef | Out-Null & $invokeGit checkout --quiet FETCH_HEAD | Out-Null } [string]$resolvedCommit = (& $invokeGit rev-parse HEAD | Select-Object -First 1).ToString().Trim() $sourcePath = [IO.Path]::GetFullPath((Join-Path -Path $tempDir -ChildPath $RepoFolderPath)) if (!(Test-Path $sourcePath -PathType Container)) { throw "The folder '$RepoFolderPath' does not exist in the cloned repository." } # Replace any existing copy, rather than copying the folder inside it if (Test-Path $DestinationPath) { Remove-Item -Path $DestinationPath -Recurse -Force } New-Item -ItemType Directory -Path (Split-Path -Parent $DestinationPath) -Force | Out-Null Write-Verbose "Copying contents from $sourcePath to $DestinationPath..." Copy-Item -Path $sourcePath -Destination $DestinationPath -Recurse -Force # The repository metadata is only copied when the whole repository is the extension, and it is not needed Remove-Item -Path (Join-Path $DestinationPath '.git') -Recurse -Force -ErrorAction Ignore return $resolvedCommit } finally { # Clean up the temporary folder if (Test-Path $tempDir) { Remove-Item -Path $tempDir -Recurse -Force } } } |