GitDsc.psm1
# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. using namespace System.Collections.Generic enum Ensure { Absent Present } enum ConfigLocation { none global system worktree local } # Assert once that Git is already installed on the system. Assert-Git #region DSCResources [DSCResource()] class GitClone { [DscProperty()] [Ensure]$Ensure = [Ensure]::Present [DscProperty(Key)] [string]$HttpsUrl [DscProperty()] [string]$RemoteName # The root directory where the project will be cloned to. (i.e. the directory where you expect to run `git clone`) [DscProperty(Mandatory)] [string]$RootDirectory [GitClone] Get() { $currentState = [GitClone]::new() $currentState.HttpsUrl = $this.HttpsUrl $currentState.RootDirectory = $this.RootDirectory $currentState.Ensure = [Ensure]::Absent $currentState.RemoteName = ($null -eq $this.RemoteName) ? "origin" : $this.RemoteName if (-not(Test-Path -Path $this.RootDirectory)) { return $currentState } Set-Location $this.RootDirectory $projectName = GetGitProjectName($this.HttpsUrl) $expectedDirectory = Join-Path -Path $this.RootDirectory -ChildPath $projectName if (Test-Path $expectedDirectory) { Set-Location -Path $expectedDirectory try { $gitRemoteValue = Invoke-GitRemote("get-url $($currentState.RemoteName)") if ($gitRemoteValue -like $this.HttpsUrl) { $currentState.Ensure = [Ensure]::Present } } catch { # Failed to execute `git remote`. Ensure state is `absent` } } return $currentState; } [bool] Test() { $currentState = $this.Get() return $currentState.Ensure -eq $this.Ensure } [void] Set() { if ($this.Ensure -eq [Ensure]::Absent) { throw "This resource does not support removing a cloned repository." } if (-not(Test-Path $this.RootDirectory)) { New-Item -ItemType Directory -Path $this.RootDirectory } Set-Location $this.RootDirectory Invoke-GitClone($this.HttpsUrl) } } [DSCResource()] class GitRemote { [DscProperty()] [Ensure]$Ensure = [Ensure]::Present [DscProperty(Key)] [string]$RemoteName [DscProperty(Key)] [string]$RemoteUrl # The root directory where the project will be cloned to. (i.e. the directory where you expect to run `git clone`) [DscProperty(Mandatory)] [string]$ProjectDirectory [GitRemote] Get() { $currentState = [GitRemote]::new() $currentState.RemoteName = $this.RemoteName $currentState.RemoteUrl = $this.RemoteUrl $currentState.ProjectDirectory = $this.ProjectDirectory if (-not(Test-Path -Path $this.ProjectDirectory)) { throw "Project directory does not exist." } Set-Location $this.ProjectDirectory try { $gitRemoteValue = Invoke-GitRemote("get-url $($this.RemoteName)") $currentState.Ensure = ($gitRemoteValue -like $this.RemoteUrl) ? [Ensure]::Present : [Ensure]::Absent } catch { $currentState.Ensure = [Ensure]::Absent } return $currentState } [bool] Test() { $currentState = $this.Get() return $currentState.Ensure -eq $this.Ensure } [void] Set() { Set-Location $this.ProjectDirectory if ($this.Ensure -eq [Ensure]::Present) { try { Invoke-GitRemote("add $($this.RemoteName) $($this.RemoteUrl)") } catch { throw "Failed to add remote repository." } } else { try { Invoke-GitRemote("remove $($this.RemoteName)") } catch { throw "Failed to remove remote repository." } } } } [DSCResource()] class GitConfigUserName { [DscProperty()] [Ensure]$Ensure = [Ensure]::Present [DscProperty(Key)] [string]$UserName [DscProperty()] [ConfigLocation]$ConfigLocation [DscProperty()] [string]$ProjectDirectory [GitConfigUserName] Get() { $currentState = [GitConfigUserName]::new() $currentState.UserName = $this.UserName $currentState.ConfigLocation = $this.ConfigLocation $currentState.ProjectDirectory = $this.ProjectDirectory if ($this.ConfigLocation -ne [ConfigLocation]::global -and $this.ConfigLocation -ne [ConfigLocation]::system) { # Project directory is not required for --global or --system configurations if ($this.ProjectDirectory) { if (Test-Path -Path $this.ProjectDirectory) { Set-Location $this.ProjectDirectory } else { throw "Project directory does not exist." } } else { throw "Project directory parameter must be specified for non-system and non-global configurations." } } $configArgs = ConstructGitConfigUserArguments -Arguments "user.name" -ConfigLocation $this.ConfigLocation $result = Invoke-GitConfig($configArgs) $currentState.Ensure = ($currentState.UserName -eq $result) ? [Ensure]::Present : [Ensure]::Absent return $currentState } [bool] Test() { $currentState = $this.Get() return $currentState.Ensure -eq $this.Ensure } [void] Set() { if ($this.ConfigLocation -eq [ConfigLocation]::system) { Assert-IsAdministrator } if ($this.ConfigLocation -ne [ConfigLocation]::global -and $this.ConfigLocation -ne [ConfigLocation]::system) { Set-Location $this.ProjectDirectory } if ($this.Ensure -eq [Ensure]::Present) { $configArgs = ConstructGitConfigUserArguments -Arguments "user.name $($this.UserName)" -ConfigLocation $this.ConfigLocation } else { $configArgs = ConstructGitConfigUserArguments -Arguments "--unset user.name" -ConfigLocation $this.ConfigLocation } Invoke-GitConfig($configArgs) } } [DSCResource()] class GitConfigUserEmail { [DscProperty()] [Ensure]$Ensure = [Ensure]::Present [DscProperty(Key)] [string]$UserEmail [DscProperty()] [ConfigLocation]$ConfigLocation [DscProperty()] [string]$ProjectDirectory [GitConfigUserEmail] Get() { $currentState = [GitConfigUserEmail]::new() $currentState.UserEmail = $this.UserEmail $currentState.ConfigLocation = $this.ConfigLocation $currentState.ProjectDirectory = $this.ProjectDirectory if ($this.ConfigLocation -ne [ConfigLocation]::global -and $this.ConfigLocation -ne [ConfigLocation]::system) { # Project directory is not required for --global or --system configurations if ($this.ProjectDirectory) { if (Test-Path -Path $this.ProjectDirectory) { Set-Location $this.ProjectDirectory } else { throw "Project directory does not exist." } } else { throw "Project directory parameter must be specified for non-system and non-global configurations." } } $configArgs = ConstructGitConfigUserArguments -Arguments "user.email" -ConfigLocation $this.ConfigLocation $result = Invoke-GitConfig($configArgs) $currentState.Ensure = ($currentState.UserEmail -eq $result) ? [Ensure]::Present : [Ensure]::Absent return $currentState } [bool] Test() { $currentState = $this.Get() return $currentState.Ensure -eq $this.Ensure } [void] Set() { if ($this.ConfigLocation -eq [ConfigLocation]::system) { Assert-IsAdministrator } if ($this.ConfigLocation -ne [ConfigLocation]::global -and $this.ConfigLocation -ne [ConfigLocation]::system) { Set-Location $this.ProjectDirectory } if ($this.Ensure -eq [Ensure]::Present) { $configArgs = ConstructGitConfigUserArguments -Arguments "user.email $($this.UserEmail)" -ConfigLocation $this.ConfigLocation } else { $configArgs = ConstructGitConfigUserArguments -Arguments "--unset user.email" -ConfigLocation $this.ConfigLocation } Invoke-GitConfig($configArgs) } } #endregion DSCResources #region Functions function Assert-Git { # Refresh session $path value before invoking 'git' $env:Path = [System.Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [System.Environment]::GetEnvironmentVariable("Path","User") try { Invoke-Git -Command 'help' return } catch { throw "Git is not installed" } } function GetGitProjectName { param( [Parameter()] [string]$HttpsUrl ) $projectName = ($HttpsUrl.split('/')[-1]).split('.')[0] return $projectName } function Invoke-GitConfig { param( [Parameter()] [string]$Arguments ) $command = [List[string]]::new() $command.Add("config") $command.Add($Arguments) return Invoke-Git -Command $command } function Invoke-GitRemote { param( [Parameter()] [string]$Arguments ) $command = [List[string]]::new() $command.Add("remote") $command.Add($Arguments) return Invoke-Git -Command $command } function Invoke-GitClone { param( [Parameter()] [string]$Arguments ) $command = [List[string]]::new() $command.Add("clone") $command.Add($Arguments) return Invoke-Git -Command $command } function Invoke-Git { param ( [Parameter(Mandatory = $true)] [string]$Command ) return Invoke-Expression -Command "git $Command" } function ConstructGitConfigUserArguments { param( [Parameter(Mandatory)] [string]$Arguments, [Parameter(Mandatory)] [ConfigLocation]$ConfigLocation ) $ConfigArguments = $Arguments if ([ConfigLocation]::None -ne $this.ConfigLocation) { $ConfigArguments = "--$($this.ConfigLocation) $($ConfigArguments)" } return $ConfigArguments } function Assert-IsAdministrator { $windowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() $windowsPrincipal = New-Object -TypeName 'System.Security.Principal.WindowsPrincipal' -ArgumentList @( $windowsIdentity ) $adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator if (-not $windowsPrincipal.IsInRole($adminRole)) { throw "This resource must be run as an Administrator to modify system settings." } } #endregion Functions |