PSDependScripts/Git.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
<#
    .SYNOPSIS
        Clone a git repository

    .DESCRIPTION
        Clone a git repository

        Note: We require git in your path

        Relevant Dependency metadata:
            DependencyName (Key): Git URL
                You can override this with the 'Name'.
                If you specify only an Account/Repository, we assume GitHub is the source
            Name: Optional override for the Git URL, same rules as DependencyName (key)
            Version: Used with git checkout. Specify a branch name, commit hash, or tags/<tag name>, for example. Defaults to master
            Target: Path to clone this repository. e.g C:\Temp would result in C:\Temp\RepoName. Defaults to nothing (current path/repo name)
            AddToPath: Prepend the Target to ENV:PATH and ENV:PSModulePath

    .PARAMETER Force
        If specified and target does not exist, create directory tree up to the target folder

    .PARAMETER PSDependAction
        Test, Install, or Import the module. Defaults to Install

        Test: Return true or false on whether the dependency is in place (Note: Currently only checks if path exists)
        Install: Install the dependency
        Import: Import the dependency 'Target'. Override with ImportPath

    .PARAMETER ImportPath
        If specified with PSDependAction Import, we import this path, instead of Target, the default

    .EXAMPLE
        @{
            'buildhelpers' = @{
                Name = 'https://github.com/RamblingCookieMonster/BuildHelpers.git'
                Version = 'd32a9495c39046c851ceccfb7b1a85b17d5be051'
                Target = 'C:\git'
            }
        }

        # Full syntax
          # DependencyName (key) uses (unique) name 'buildhelpers'
          # Override DependencyName as URL the name https://github.com/RamblingCookieMonster/BuildHelpers.git
          # Specify a commit to checkout (version)
          # Clone in C:\git

    .EXAMPLE

        @{
            'https://github.com/RamblingCookieMonster/PSDeploy.git' = 'master'
            'https://internal.gitlab.fqdn/jdoe/BuildHelpers.git' = 'd32a9495c39046c851ceccfb7b1a85b17d5be051'
        }

        # Simple syntax
          # First example shows cloning PSDeploy from ramblingcookiemonster's GitHub repo
          # Second example shows clonging BuildHelpers from jdoe's internal GitLab account and checking out a specific commit
          # Both are cloned to the current path (e.g. .\<repo name>)
          # This syntax assumes git as a source. The right hand side is the version (branch, commit, tags/<tag name>, etc.)
#>

[cmdletbinding()]
param(
    [PSTypeName('PSDepend.Dependency')]
    [psobject[]]$Dependency,

    [switch]$Force,

    [ValidateSet('Test', 'Install')]
    [string[]]$PSDependAction = @('Install'),

    [string]$ImportPath,
    
    [bool]$ExtractProject = $False
)

# Extract data from Dependency
$DependencyName = $Dependency.DependencyName
$Name = $Dependency.Name
if(-not $Name)
{
    $Name = $DependencyName
}

#Name is in account/repo format, default to GitHub as source
#This likely needs work, and will need to change if GitHub changes valid characters for usernames
if($Name -match "^[a-zA-Z0-9]+/[a-zA-Z0-9_-]+$")
{
    $Name = "https://github.com/$Name.git"
}
$GitName = $Name.trimend('/').split('/')[-1] -replace "\.git$", ''
if($Dependency.Target -and ($Target = (Get-Item $Dependency.Target -ErrorAction SilentlyContinue).FullName))
{
    Write-Debug "Target resolved to $Target"
}
else
{
    $Target = $PWD.Path
    Write-Debug "Target defaulted to current dir: $Target"
}
$RepoPath = Join-Path $Target $GitName
$GottaInstall = $True

if(-not (Test-Path $Target) -and $PSDependAction -contains 'Install')
{
    Write-Verbose "Creating folder [$Target] for git dependency [$Name]"
    $null = New-Item $Target -ItemType Directory -Force
}

if(-not (Test-Path $RepoPath))
{
    # Nothing found, return test output
    if( $PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1)
    {
        return $False
    }
}
else # Target exists
{
    $GottaTest = $True
}

if(-not (Get-Command git -ErrorAction SilentlyContinue))
{
    Write-Error "Git dependency type requires git. Ensure this is in your path, or explicitly specified in $ModuleRoot\PSDepend.Config's GitPath. Skipping [$DependencyName]"
}

$Version = $Dependency.Version
if(-not $Version)
{
    $Version = 'master'
}

if($GottaTest)
{
    Push-Location
    Set-Location $RepoPath
    $Branch = Invoke-ExternalCommand git -Arguments (Write-Output rev-parse --abbrev-ref HEAD) -Passthru
    $Commit = Invoke-ExternalCommand git -Arguments (Write-Output rev-parse HEAD) -Passthru
    Pop-Location
    if($Version -eq $Branch -or $Version -eq $Commit)
    {
        Write-Verbose "[$RepoPath] exists and is already at version [$Version]"
        if($PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1)
        {
            return $true
        }
        $GottaInstall = $False
    }
    elseif($PSDependAction -contains 'Test' -and $PSDependAction.count -eq 1)
    {
        Write-Verbose "[$RepoPath] exists and is at branch [$Branch], commit [$Commit].`nWe don't currently support moving to the requested version [$Version]"
        return $false
    }
    else
    {
        Write-Verbose "[$RepoPath] exists and is at branch [$Branch], commit [$Commit].`nWe don't currently support moving to the requested version [$Version]"
        $GottaInstall = $False
    }
}

if($PSDependAction -notcontains 'Install')
{
    return
}

if($GottaInstall -and !$ExtractProject)
{
    Push-Location
    Set-Location $Target
    Write-Verbose -Message "Cloning dependency [$Name] with git from [$($Target)]"
    Invoke-ExternalCommand git 'clone', $Name

    #TODO: Should we do a fetch, once existing repo is found?
    Set-Location $RepoPath
    Write-Verbose -Message "Checking out [$Version] of [$Name] from [$RepoPath]"
    Invoke-ExternalCommand git 'checkout', $Version
    Pop-Location
}
elseif($GottaInstall -and $ExtractProject) {
    $OutPath = Join-Path ([System.IO.Path]::GetTempPath()) ([guid]::NewGuid().guid)
    $RepoFolder = Join-Path -Path $OutPath -ChildPath $GitName

    $null = New-Item -ItemType Directory -Path $OutPath -Force
    Push-Location $OutPath
    
    Write-Verbose -Message "Cloning dependency [$GitName] with git from [$($Target)]"
    Invoke-ExternalCommand git 'clone', $Name

    Push-Location $GitName
    Write-Verbose -Message "Checking out [$Version] of [$GitName] from [$RepoFolder]"
    Invoke-ExternalCommand git 'checkout', $Version
    Pop-Location


    $ProjectDetails = Get-ProjectDetail -Path $RepoFolder
    [string[]]$ToCopy = $ProjectDetails.Path
    Pop-Location

    #TODO: Implement test and import PSDependActions.
    if(-not (Test-Path $Target))
    {
        $null = New-Item -ItemType Directory -Path $Target -Force
    }
    foreach($Item in $ToCopy)
    {
        Write-Verbose "Copy From: $ToCopy To: $Target"
        Copy-Item -Path $Item -Destination $Target -Force -Confirm:$False -Recurse
    }
    Remove-Item $OutPath -Force -Recurse
}

if($Dependency.AddToPath)
{
    Write-Verbose "Setting PSModulePath to`n$($Target, $env:PSModulePath -join ';' | Out-String)"
    Add-ToItemCollection -Reference Env:\PSModulePath -Item (Get-Item $Target).FullName
    
    Write-Verbose "Setting PATH to`n$($RepoPath, $env:PATH -join ';' | Out-String)"
    Add-ToItemCollection -Reference Env:\PATH -Item (Get-Item $Target).FullName
}

$ToImport = $Target
if($ImportPath)
{
    $ToImport = $ImportPath
}
Import-PSDependModule $ToImport