Public/Get-GitHubUserRepo.ps1

function Get-GitHubUserRepo {
  <#
      .Synopsis
      Download GitHub User Gists & Repositories

      .DESCRIPTION
      Uses git.exe to clone the gists and repositories of a github user.
      Can Exclude repositories with names that match the string/strings defined with -Exclude
      Requires Module - PowerShellForGitHub
      Requires git.exe
      
      I included the source file for PForEach because it is no longer visible in the powershellgallery and github
      Vasily Larionov - https://www.powershellgallery.com/profiles/vlariono | https://github.com/vlariono
      PForEach - https://www.powershellgallery.com/packages/PForEach

      .EXAMPLE
      Get-GitHubUserRepos -UserName WozNet -Path 'V:\git\users' -Exclude 'docs'

      .EXAMPLE
      'WozNet','PowerShell','Microsoft' | Get-GitHubUserRepos -Path 'V:\git\users' -Exclude 'azure,'office365'
  #>

  [CmdletBinding()]
  [Alias('dlgit')]
  Param(
    # Param1 help - GitHub Usernames
    [Parameter(
        Mandatory,
        HelpMessage='Github UserName',
        ValueFromPipeline
    )]
    [ValidateNotNullOrEmpty()]
    [String[]]$UserName,

    # Param2 help - Directory to save User Gists and Repositories
    [ValidateScript({
          Test-Path -Path $_ -PathType Container
    })]
    [String]$Path = 'V:\git\users',

    # Param3 help - Exclude Repositories with Names matching these strings
    [String[]]$Exclude = 'docs',

    # Param4 help - ThrottleLimit for Invoke-ForEachParallel
    [int]$ThrottleLimit = 5
  )
  Begin {

    try{
      if (-not (Get-Command -Name git.exe)) { throw 'git.exe is missing' }
      if (-not (Get-Module -ListAvailable -Name PowerShellForGitHub)) { throw 'Install Module - PowerShellForGitHub' }
      Import-Module -Name PowerShellForGitHub -PassThru:$false
      if (-not (Get-Command -Name Invoke-ForEachParallel)) {
        Import-Module -Name (Join-Path -Path (Split-Path -Path $PSScriptRoot -Parent) -ChildPath 'Lib\PForEach\PForEach.dll' -Resolve) -PassThru:$false -ErrorAction Stop
      }
      if (-not (Get-GitHubConfiguration -Name DisableTelemetry)) { Set-GitHubConfiguration -DisableTelemetry }
      if (-not (Test-GitHubAuthenticationConfigured)) { $Host.UI.WriteErrorLine('PowerShellForGitHub is not Authenticated') }
    }
    catch {
      [System.Management.Automation.ErrorRecord]$e = $_
      [PSCustomObject]@{
        Type      = $e.Exception.GetType().FullName
        Exception = $e.Exception.Message
        Reason    = $e.CategoryInfo.Reason
        Target    = $e.CategoryInfo.TargetName
        Script    = $e.InvocationInfo.ScriptName
        Line      = $e.InvocationInfo.ScriptLineNumber
        Column    = $e.InvocationInfo.OffsetInLine
      }
    }
    $HTML = @'
<script src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
<div id="ph"></div>
<script>
var username = '---'
$.getJSON('https://api.github.com/users/' + username + '/gists', function (data) {
    for (var i in data) {
        var oldDocumentWrite = document.write
        document.write = function (scr_content) {
            for (var i in data) {
                if ( $.trim( $("#" + data[i].id ).text() ).length == 0 ) {
                    $("#" + data[i].id ).append(scr_content);
                    return;
                }
            }
        }
        var scr = document.createElement('script');
        scr.src = 'https://gist.github.com/' + username + '/' + data[i].id + '.js';
        $("#ph").append("<div><h2>" + data[i].description + "</h2></div>");
        $("#ph").append(scr.outerHTML);
        $("#ph").append('<div id="' + data[i].id + '"></div>');
    }
    document.write = oldDocumentWrite;
});
</script>
'@

    $UserPathList = [System.Collections.ArrayList]@()
    $StopWatch = [System.Diagnostics.Stopwatch]::New()
    $StopWatch.Start()
  }
  Process {

    $DelDir = [System.Collections.ArrayList]@()
    foreach ($GitUser in $UserName) {
      $UserPath = Join-Path -Path $Path -ChildPath $GitUser
      if (Test-Path -Path $UserPath -PathType Container) {
        Get-GitHubRepository -OwnerName $GitUser | Sort-Object -Property updated_at -Descending | ForEach-Object -Process {
          if ( $LPath = Join-Path -Path $UserPath -ChildPath $_.Name -Resolve -ErrorAction SilentlyContinue | Get-Item -ErrorAction SilentlyContinue ) {
            [PSCustomObject]@{
              Name = $_.Name
              Git_Updated = $_.updated_at
              Local_Updated = $LPath.LastWriteTime
              GetItem = $LPath
            }
          }
        } | Where-Object {$_.Git_Updated -ge $_.Local_Updated} | Select-Object -ExpandProperty GetItem | ForEach-Object {
          $null = $DelDir.Add($PSItem)
        }
      }
    }
    if ($DelDir) {
      Remove-Item -Path $DelDir -Recurse -Force
      if (Resolve-Path -Path $DelDir -ErrorAction Ignore) { Remove-Item -Path $DelDir -Recurse -Force }
    }
    Remove-Variable -Name DelDir -ErrorAction Ignore

    # Download
    foreach ($GitUser in $UserName) {
      $UserPath = Join-Path -Path $Path -ChildPath $GitUser
      $null = $UserPathList.Add($UserPath)

      if (-not (Test-Path -Path $UserPath)) { New-Item -Path $UserPath -ItemType Directory }

      # Get Gist
      $UserGist = Get-GitHubGist -UserName $GitUser
      if ($UserGist) {
        $GistDir = Join-Path -Path $UserPath -ChildPath '_gist'
        if (-not (Test-Path -Path $GistDir)) { New-Item -Path $GistDir -ItemType Directory }

        Get-ChildItem -Path $GistDir | Remove-Item -Recurse -Force
        Set-Content -Value ($HTML.Replace('---',$GitUser)) -Path ([System.IO.Path]::Combine($UserPath,'_gist.html')) -Force
        Write-Output ('{2}{0} Gists - {1}' -f $GitUser,$UserGist.Count,("`n"))
        $UserGist | ForEach-Object -Process {
          $UGist = $PSItem
          Start-Process -WorkingDirectory $GistDir -FilePath git.exe -ArgumentList ('clone --recursive {0}' -f $UGist.git_pull_url) -WindowStyle Hidden -Wait

          $GistDLDir = Join-Path -Path $GistDir -ChildPath $UGist.Id -Resolve

          switch (($UGist.files|Get-Member -MemberType NoteProperty | Measure-Object -Property Name).Count) {
            1 {
              try {
                Join-Path -Path $GistDLDir -ChildPath ($UGist.files | Get-Member -MemberType NoteProperty).Name -Resolve | Move-Item -Destination $GistDir -ErrorAction Stop
              }
              catch [System.IO.IOException] {
                Join-Path -Path $GistDLDir -ChildPath ($UGist.files | Get-Member -MemberType NoteProperty).Name -Resolve | Get-Item | Rename-Item -NewName {$_.Name.Replace($_.BaseName,('{0}-{1}' -f $_.BaseName,$_.Directory.Name))} -PassThru | Move-Item -Destination $GistDir -ErrorAction Stop
              }
              finally {
                Remove-Item -Path $GistDLDir -Recurse -ErrorAction SilentlyContinue -Force
              }
              break
            }
            {$_ -gt 1} {
              Rename-Item -Path $GistDLDir -NewName ($UGist.files|Get-Member -MemberType NoteProperty)[0].Name
              break
            }
            default { Write-Warning 'Something Went Wrong' ; break }
          }
          Start-Sleep -Milliseconds 350
        }
      }

      # Get Repo
      $UserRepo = Get-GitHubRepository -OwnerName $GitUser
      $FilteredUserRepo = switch ($Exclude.Count) {
        {$_ -ge 1} { $UserRepo | Where-Object {$_.name -notmatch ($Exclude -join '|')} ; break }
        default { $UserRepo ; break }
      }
      Write-Output ('{0}{1} Repositories - {2} (excluded - {3})' -f "`n",$GitUser,$FilteredUserRepo.Count,($UserRepo.Count - $FilteredUserRepo.Count))
      $FilteredUserRepo.name| Select-Object -Property @{e={if ($_.Length -gt 27) {$_.Substring(0,24) + '...'} else{$_}}} | Format-Wide -AutoSize
      $FilteredUserRepo | Invoke-ForEachParallel -ThrottleLimit $ThrottleLimit -Process {
        Start-Process -WorkingDirectory $UserPath -FilePath git.exe -ArgumentList ('clone --recursive {0}' -f $PSItem.clone_url) -WindowStyle Hidden -Wait

        Start-Sleep -Milliseconds 150

        $RepoDir = Get-Item -Path (Join-Path -Path $UserPath -ChildPath $PSItem.name -Resolve)
        $RepoDir.LastWriteTime = $PSItem.updated_at
      }
    }
  }
  End {
    $StopWatch.Stop()
    'Time - {0:m\:ss}{1}' -f $StopWatch.Elapsed,("`n")
    'Updated User Directories:'
    $UserPathList
  }
}