diff --git a/Test/include/database.mock.ps1 b/Test/include/database.mock.ps1 new file mode 100644 index 0000000..bdd8838 --- /dev/null +++ b/Test/include/database.mock.ps1 @@ -0,0 +1,40 @@ +# DATABASE MOCK +# +# This file is used to mock the database path and the database file +# for the tests. It creates a mock database path and a mock database file +# and sets the database path to the mock database path. +# +# THIS INCLUDE REQURED module.helper.ps1 +if(-not $MODULE_NAME){ throw "Missing MODULE_NAME varaible initialization. Check for module.helerp.ps1 file." } + +$DB_INVOKE_GET_ROOT_PATH_CMD = "Invoke-$($MODULE_NAME)GetDbRootPath" +$MOCK_DATABASE_PATH = "test_database_path" + +function Mock_Database([switch]$ResetDatabase){ + + MockCallToString $DB_INVOKE_GET_ROOT_PATH_CMD -OutString $MOCK_DATABASE_PATH + + $dbstore = Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_CMD + Assert-AreEqual -Expected $MOCK_DATABASE_PATH -Presented $dbstore + + if($ResetDatabase){ + Reset-DatabaseStore + } + +} + +function Get-Mock_DatabaseStore{ + $dbstore = Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_CMD + return $dbstore +} + +function Reset-DatabaseStore{ + [CmdletBinding()] + param() + + # Get actual store path + $databaseRoot = Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_CMD + + # Remove the database root directory + Remove-Item -Path $databaseRoot -Recurse -Force -ErrorAction SilentlyContinue +} \ No newline at end of file diff --git a/Test/private/run_BeforeAfter.ps1 b/Test/private/run_BeforeAfter.ps1 index c36700a..6830d5e 100644 --- a/Test/private/run_BeforeAfter.ps1 +++ b/Test/private/run_BeforeAfter.ps1 @@ -19,12 +19,7 @@ function Run_BeforeEach{ # Write-Verbose "Run_BeforeEach" Reset-InvokeCommandMock - - Invoke-PrivateContext { - # Clear the repo list cache to ensure tests are isolated - $script:projectlist = $null - $script:repoList = @{} - } + Mock_Database } # function Run_AfterEach{ diff --git a/Test/public/projects/GcProjects.test.ps1 b/Test/public/projects/GcProjects.test.ps1 index d98823a..bbfac56 100644 --- a/Test/public/projects/GcProjects.test.ps1 +++ b/Test/public/projects/GcProjects.test.ps1 @@ -9,8 +9,8 @@ function Test_GetGcProjects { Assert-Count -Expected 16 -Presented $projects # Pick one random and check the structure - $testProject = $projects."bit21" - Assert-AreEqual -Expected "bit21" -Presented $testProject.Title + $testProject = $projects."BiT21" + Assert-AreEqual -Expected "BiT21" -Presented $testProject.Title Assert-AreEqual -Expected "githubcustomers" -Presented $testProject.Owner Assert-AreEqual -Expected 2683 -Presented $testProject.ProjectNumber Assert-AreEqual -Expected "https://github.com/orgs/githubcustomers/projects/2683" -Presented $testProject.Url diff --git a/include/databaseV2.ps1 b/include/databaseV2.ps1 new file mode 100644 index 0000000..55edf91 --- /dev/null +++ b/include/databaseV2.ps1 @@ -0,0 +1,172 @@ +# DATABASE V2 +# +# Database driver to store the cache +# +# Include design description +# This is the function ps1. This file is the same for all modules. +# Create a public psq with variables, Set-MyInvokeCommandAlias call and Invoke public function. +# Invoke function will call back `GetDatabaseRootPath` to use production root path +# Mock this Invoke function with Set-MyInvokeCommandAlias to set the Store elsewhere +# This ps1 has function `GetDatabaseFile` that will call `Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_ALIAS` +# to use the store path, mocked or not, to create the final store file name. +# All functions of this ps1 will depend on `GetDatabaseFile` for functionality. +# + +$MODULE_ROOT_PATH = $PSScriptRoot | Split-Path -Parent +$MODULE_NAME = (Get-ChildItem -Path $MODULE_ROOT_PATH -Filter *.psd1 | Select-Object -First 1).BaseName +$DATABASE_ROOT = [System.Environment]::GetFolderPath('UserProfile') | Join-Path -ChildPath ".helpers" -AdditionalChildPath $MODULE_NAME, "databaseCache" + +$DB_INVOKE_GET_ROOT_PATH_ALIAS = "$($MODULE_NAME)GetDbRootPath" + +$function = "Invoke-$($MODULE_NAME)GetDbRootPath" +if(-not (Test-Path -Path function:$function)){ + + # PUBLIC FUNCTION + function Invoke-MyModuleGetDbRootPath{ + [CmdletBinding()] + param() + + $databaseRoot = GetDatabaseRootPath + return $databaseRoot + + } + Rename-Item -path Function:Invoke-MyModuleGetDbRootPath -NewName $function + Export-ModuleMember -Function $function + Set-MyInvokeCommandAlias -Alias $DB_INVOKE_GET_ROOT_PATH_ALIAS -Command $function +} + +# Extra functions not needed by INCLUDE DATABASE V2 +$function = "Reset-$($MODULE_NAME)DatabaseStore" +if(-not (Test-Path -Path function:$function)){ + function Reset-MyModuleDatabaseStore{ + [CmdletBinding()] + param() + + $databaseRoot = Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_ALIAS + + Remove-Item -Path $databaseRoot -Recurse -Force -ErrorAction SilentlyContinue + + New-Item -Path $databaseRoot -ItemType Directory + + } + + Rename-Item -path Function:Reset-MyModuleDatabaseStore -NewName $function + Export-ModuleMember -Function $function +} + +# PRIVATE FUNCTIONS +function GetDatabaseRootPath { + [CmdletBinding()] + param() + + $databaseRoot = $DATABASE_ROOT + return $databaseRoot +} + +function GetDatabaseFile{ + [CmdletBinding()] + param( + [Parameter(Mandatory, Position = 0)][string]$Key, + [Parameter(Position = 1)][ValidateSet("JSON","XML","TXT")][string]$DBFormat = "JSON" + ) + + $databaseRoot = Invoke-MyCommand -Command $DB_INVOKE_GET_ROOT_PATH_ALIAS + + if(-not (Test-Path -Path $databaseRoot)){ + New-Item -Path $databaseRoot -ItemType Directory -Force | Out-Null + } + + $ext = GetFileExtension -DbFormat $DBFormat + + $path = $databaseRoot | Join-Path -ChildPath "$Key$ext" + + return $path +} + +function GetFileExtension{ + [CmdletBinding()] + param( + [Parameter(Mandatory)][string]$DbFormat + ) + + switch ($DbFormat.ToUpper()){ + "JSON" { $ret = ".json" ; Break } + "XML" { $ret = ".xml" ; Break } + "TXT" { $ret = ".txt" ; Break } + default { throw "Unsupported database format $DbFormat" } + } + return $ret +} + +function Get-DatabaseKey{ + [CmdletBinding()] + param( + [Parameter(Mandatory, Position = 0)][string]$Key, + [Parameter(Position = 1)][ValidateSet("JSON","XML","TXT")][string]$DBFormat = "JSON", + [Parameter()][switch]$AsHashtable + ) + + if(-Not (Test-DatabaseKey $Key -DBFormat $DBFormat)){ + return $null + } + + $path = GetDatabaseFile $Key -DBFormat $DBFormat + + switch ($DBFormat) { + "JSON" { $ret = Get-Content $path | ConvertFrom-Json -AsHashtable:$AsHashtable ; Break } + "XML" { $ret = Import-Clixml -Path $path ; Break } + "TXT" { $ret = Get-Content $path ; Break } + default { throw "Unsupported database format $DbFormat" } + } + + return $ret +} + +function Reset-DatabaseKey{ + [CmdletBinding()] + param( + [Parameter(Mandatory, Position = 0)][string]$Key, + [Parameter(Position = 1)][ValidateSet("JSON","XML","TXT")][string]$DBFormat = "JSON" + ) + $path = GetDatabaseFile -Key $Key -DBFormat $DBFormat + Remove-Item -Path $path -Force -ErrorAction SilentlyContinue + return +} + +function Save-DatabaseKey{ + [CmdletBinding()] + param( + [Parameter(Mandatory, Position = 0)][string]$Key, + [Parameter(Mandatory, Position = 2)][Object]$Value, + [Parameter(Position = 3)][ValidateSet("JSON","XML","TXT")][string]$DbFormat = "JSON" + ) + + $path = GetDatabaseFile -Key $Key -DBFormat $DbFormat + + switch ($DbFormat) { + "JSON" { $Value | ConvertTo-Json -Depth 10 | Set-Content $path -Encoding UTF8 -Force ; Break } + "XML" { $Value | Export-Clixml -Path $path -Force ; Break } + "TXT" { $Value | Set-Content -Path $path -Encoding UTF8 -Force ; Break } + default { throw "Unsupported database format $DbFormat" + } + } +} + +function Test-DatabaseKey{ + [CmdletBinding()] + param( + [Parameter(Mandatory, Position = 0)][string]$Key, + [Parameter(Position = 1)][ValidateSet("JSON","XML","TXT")][string]$DBFormat = "JSON" + ) + + $path = GetDatabaseFile -Key $Key -DBFormat $DBFormat + + # Key file not exists + if(-Not (Test-Path $path)){ + return $false + } + + # TODO: Return $false if cache has expired + + return $true +} diff --git a/include/openFilesUrls.ps1 b/include/openFilesUrls.ps1 new file mode 100644 index 0000000..033269d --- /dev/null +++ b/include/openFilesUrls.ps1 @@ -0,0 +1,130 @@ + +# Include openFilesUrls.ps1 +# Provides controls to open files and URLs in the default system applications. +# Use $MODULE_NAME variable to set up functions names + +Set-MyInvokeCommandAlias -Alias OpenUrl -Command $('Invoke-{modulename}OpenUrl -Url "{url}"' -replace "{modulename}", $MODULE_NAME) + +function Invoke-ModuleNameOpenUrl{ + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)][string]$Url + ) + + try { + # Determine the operating system + if ($IsWindows -or $env:OS -match "Windows") { + # Windows - use Start-Process + Start-Process $Url + } + elseif ($IsMacOS) { + # macOS - use open command + Start-Process "open" -ArgumentList $Url + } + elseif ($IsLinux) { + # Linux - try xdg-open + Start-Process "xdg-open" -ArgumentList $Url + } + else { + # Fallback for older PowerShell versions without OS variables + switch ([System.Environment]::OSVersion.Platform) { + "Win32NT" { + Start-Process $Url + } + "Unix" { + # Try to determine if macOS or Linux + if (Test-Path "/System/Library/CoreServices/Finder.app") { + # macOS + Start-Process "open" -ArgumentList $Url + } + else { + # Assume Linux + Start-Process "xdg-open" -ArgumentList $Url + } + } + default { + throw "Unsupported operating system" + } + } + } + } + catch { + Write-Error "Failed to open URL: $_" + } +} +Copy-Item -path Function:Invoke-ModuleNameOpenUrl -Destination Function:"Invoke-$($MODULE_NAME)OpenUrl" +Export-ModuleMember -Function "Invoke-$($MODULE_NAME)OpenUrl" + + +function Open-Url { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] + [ValidateNotNullOrEmpty()] + [string]$Url + ) + + process { + Invoke-MyCommand -Command OpenUrl -Parameters @{url = $Url} + } +} + +function Open-File { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] + [ValidateNotNullOrEmpty()] + [string]$Path + ) + + process { + try { + # Ensure the file exists + if (-not (Test-Path -Path $Path)) { + throw "File not found: $Path" + } + + # Get absolute path + $absolutePath = (Resolve-Path -Path $Path).Path + + # Determine the operating system + if ($IsWindows -or $env:OS -match "Windows") { + # Windows - use Invoke-Item + Invoke-Item -Path $absolutePath + } + elseif ($IsMacOS) { + # macOS - use open command + Start-Process "open" -ArgumentList $absolutePath + } + elseif ($IsLinux) { + # Linux - try xdg-open + Start-Process "xdg-open" -ArgumentList $absolutePath + } + else { + # Fallback for older PowerShell versions without OS variables + switch ([System.Environment]::OSVersion.Platform) { + "Win32NT" { + Invoke-Item -Path $absolutePath + } + "Unix" { + # Try to determine if macOS or Linux + if (Test-Path "/System/Library/CoreServices/Finder.app") { + # macOS + Start-Process "open" -ArgumentList $absolutePath + } + else { + # Assume Linux + Start-Process "xdg-open" -ArgumentList $absolutePath + } + } + default { + throw "Unsupported operating system" + } + } + } + } + catch { + Write-Error "Failed to open file: $_" + } + } +} diff --git a/private/gc_database_project.ps1 b/private/gc_database_project.ps1 new file mode 100644 index 0000000..d234cbd --- /dev/null +++ b/private/gc_database_project.ps1 @@ -0,0 +1,39 @@ +function Get-GcDatabaseProjects{ + [CmdletBinding()] + param( + + [Parameter(Mandatory)][string]$Owner, + [Parameter(Mandatory)][string]$Handle + ) + + $key = getprojectkey $Owner $Handle + + $db = Get-DatabaseKey -Key $key -AsHashtable + + return $db +} + +function Save-GcDatabaseProjects{ + [CmdletBinding()] + param( + [Parameter(Mandatory)][string]$Owner, + [Parameter(Mandatory)][string]$Handle, + [Parameter(Mandatory)][Object]$Value + ) + + $key = getprojectkey $Owner $Handle + + Save-DatabaseKey -Key $key -Value $Value +} + +function getprojectkey{ + [CmdletBinding()] + param( + [Parameter(Mandatory, Position = 0)][string]$Owner, + [Parameter(Mandatory, Position = 1)][string]$Handle + ) + + $key = "gcprojects_{0}_{1}" -f $Owner, $Handle + + return $key +} \ No newline at end of file diff --git a/private/gc_database_repo.ps1 b/private/gc_database_repo.ps1 new file mode 100644 index 0000000..76efd7d --- /dev/null +++ b/private/gc_database_repo.ps1 @@ -0,0 +1,42 @@ +function Get-GcDatabaseRepos{ + [CmdletBinding()] + param( + + [Parameter(Mandatory)][string]$Owner, + [Parameter(Mandatory)][string]$PropertyName, + [Parameter(Mandatory)][string]$Handle + ) + + $key = getrepokey -Owner $Owner -PropertyName $PropertyName -Handle $Handle + + $db = Get-DatabaseKey -Key $key -AsHashtable + + return $db +} + +function Save-GcDatabaseRepos{ + [CmdletBinding()] + param( + [Parameter(Mandatory)][string]$Owner, + [Parameter(Mandatory)][string]$PropertyName, + [Parameter(Mandatory)][string]$Handle, + [Parameter(Mandatory)][Object]$Value + ) + + $key = getrepokey -Owner $Owner -PropertyName $PropertyName -Handle $Handle + + Save-DatabaseKey -Key $key -Value $Value +} + +function getrepokey{ + [CmdletBinding()] + param( + [Parameter(Mandatory, Position = 0)][string]$Owner, + [Parameter(Mandatory, Position = 1)][string]$PropertyName, + [Parameter(Mandatory, Position = 2)][string]$Handle + ) + + $key = "gcrepos_{0}_{1}_{2}" -f $Owner, $PropertyName, $Handle + + return $key +} \ No newline at end of file diff --git a/private/org.ps1 b/private/org.ps1 new file mode 100644 index 0000000..acff4ad --- /dev/null +++ b/private/org.ps1 @@ -0,0 +1,5 @@ +function Get-OrgName { + + return "githubcustomers" + +} \ No newline at end of file diff --git a/public/collaborators/collaborators.ps1 b/public/collaborators/collaborators.ps1 new file mode 100644 index 0000000..d0ebc0b --- /dev/null +++ b/public/collaborators/collaborators.ps1 @@ -0,0 +1,17 @@ +class ValidRepoNames : System.Management.Automation.IValidateSetValuesGenerator { [String[]] GetValidValues() { return GetValidRepoNames}} + +# https://github.com/githubcustomers/bbva/edit/main/.github/collaborators.yml + +function Get-GcRepoCollaboratorsUrl{ + [cmdletbinding()] + param( + [Parameter()][ValidateSet([ValidRepoNames])][string]$Name + ) + + $repo = Get-GcRepo -Name $Name + + $url = $repo.url + "/edit/main/.github/collaborators.yml" + + return $url + +} Export-ModuleMember -Function Get-GcRepoCollaboratorsUrl \ No newline at end of file diff --git a/public/items/GcProjectItem.ps1 b/public/items/GcProjectItem.ps1 index a7e900e..0cdf105 100644 --- a/public/items/GcProjectItem.ps1 +++ b/public/items/GcProjectItem.ps1 @@ -5,18 +5,23 @@ function Get-GcProjectItem { [Parameter(Mandatory,ValueFromPipelineByPropertyName, ValueFromPipeline, Position = 0)][Alias("id")][string]$ItemId ) - $all = Get-AllItems + begin{ + $all = Get-AllItems + } - if([string]::IsNullOrEmpty($ItemId)){ - return $all - } else { - $item = $all.$ItemId + process{ - if(-Not $item){ - throw ("Item not found: "+$ItemId) + if([string]::IsNullOrEmpty($ItemId)){ + return $all + } else { + $item = $all.$ItemId + + if(-Not $item){ + throw ("Item not found: "+$ItemId) + } + + return $item } - - return $item } } Export-ModuleMember -Function Get-GcProjectItem -Alias gcpi \ No newline at end of file diff --git a/public/items/NewGcProjectItemIssue.ps1 b/public/items/NewGcProjectItemIssue.ps1 new file mode 100644 index 0000000..4a8b5fd --- /dev/null +++ b/public/items/NewGcProjectItemIssue.ps1 @@ -0,0 +1,30 @@ +class ValidRepoNames : System.Management.Automation.IValidateSetValuesGenerator { [String[]] GetValidValues() { return GetValidRepoNames}} + +function New-GcProjectItemIssue{ + [CmdletBinding()] + [Alias("ncpi")] + param( + #ProjectOwner + [Parameter()][ValidateSet([ValidRepoNames])][string]$RepositoryName, + + [Parameter(Mandatory, Position = 3)][string]$Title, + [Parameter(Position = 4)][string]$Body, + [Parameter()][switch]$OpenOnCreation + ) + + $org = Get-OrgName + + # Create Issue + $url = ProjectHelper\New-ProjectIssueDirect -RepoOwner $org -RepoName $RepositoryName -Title $Title -Body $Body + + if(! $url ){ + "Issue could not be created" | Write-MyError + return $null + } + + if( $OpenOnCreation ) { + Open-Url $url + } + + return $url +} Export-ModuleMember -Function New-GcProjectItemIssue -Alias ncpi \ No newline at end of file diff --git a/public/projects/GcProjects.ps1 b/public/projects/GcProjects.ps1 index 6a861d6..9e4f8be 100644 --- a/public/projects/GcProjects.ps1 +++ b/public/projects/GcProjects.ps1 @@ -2,8 +2,6 @@ Set-MyInvokeCommandAlias -Alias FindProjectByCreator -Command 'Find-Project -own class ValidProjectNames : System.Management.Automation.IValidateSetValuesGenerator { [String[]] GetValidValues() { return GetValidProjectNames}} -$script:projectlist = $null - function Get-GcProjects { [CmdletBinding()] param( @@ -11,43 +9,49 @@ function Get-GcProjects { [parameter()][switch]$Force ) - if(! $Force -and $null -ne $script:projectlist){ - return $script:projectlist - } - $me = Get-MyHandle - $owner = 'githubcustomers' + $owner = Get-OrgName $params = @{ owner = $owner handle = $me } - $result = Invoke-MyCommand -Command FindProjectByCreator -Parameters $params + $cache = Get-GcDatabaseProjects -Owner $owner -Handle $me - # Filter closed projects - # TODO: this filter is not working. Ignore the filter for the moment - # $filtered = $result | Where-Object {$null -ne $_.closedAt} - $filtered = $result + if($Force -or ! $cache){ - # Select attributes to return - $ret = @{} - - foreach($p in $filtered) { - $name = $p.title -replace '[^a-zA-Z0-9]', '_' - $n = [pscustomobject]@{ - Title = $p.title - Owner = $owner - ProjectNumber = $p.number - Url = $p.url + + $result = Invoke-MyCommand -Command FindProjectByCreator -Parameters $params + + # Filter closed projects + # TODO: this filter is not working. Ignore the filter for the moment + # $filtered = $result | Where-Object {$null -ne $_.closedAt} + $filtered = $result + + # Select attributes to return + $list = @{} + + foreach($p in $filtered) { + $name = $p.title -replace '[^a-zA-Z0-9]', '_' + $n = [pscustomobject]@{ + Title = $p.title + Owner = $owner + ProjectNumber = $p.number + Url = $p.url + } + + $list.$name= $n } - $ret.$name= $n + # Save to cache + Save-GcDatabaseProjects -Owner $owner -Handle $me -Value $list + + $cache = Get-GcDatabaseProjects -Owner $owner -Handle $me } - $script:projectlist = $ret - return $ret + return $cache } Export-ModuleMember -Function Get-GcProjects diff --git a/public/repos/getrepos.ps1 b/public/repos/getrepos.ps1 index 4b11662..c9c8a94 100644 --- a/public/repos/getrepos.ps1 +++ b/public/repos/getrepos.ps1 @@ -3,8 +3,6 @@ Set-MyInvokeCommandAlias -Alias SearchRepos -Command 'Invoke-SearchRepo -SearchS class ValidRepoNames : System.Management.Automation.IValidateSetValuesGenerator { [String[]] GetValidValues() { return GetValidRepoNames}} -$script:repoList = @{} - function Get-GcReposMy{ [CmdletBinding()] param( @@ -13,14 +11,8 @@ function Get-GcReposMy{ $handle = Get-MyHandle - if($null -ne $script:repoList.$handle -and -not $Force){ - return $script:repoList.$handle - } - $ret = Get-GcRepos -PropertyValue $handle -Force:$Force - - $script:repoList.$handle = $ret - + return $ret } Export-ModuleMember -Function Get-GcReposMy @@ -41,36 +33,52 @@ function Get-GcRepos { [Parameter()][switch]$Force ) - $SearchString = "org:githubcustomers props.{property}:{value}" + $Owner = Get-OrgName - $SearchString = $SearchString -replace '{value}', $PropertyValue - $SearchString = $SearchString -replace '{property}', $PropertyName + # Get cache + $cache = Get-GcDatabaseRepos -Owner $Owner -PropertyName $PropertyName -Handle $PropertyValue - $SearchString | Write-Verbose + # check if empty + if($Force -or ! $cache){ - $response = Invoke-MyCommand -Command SearchRepos -Parameters @{searchstring=$SearchString} + $SearchString = "org:{org} props.{property}:{value}" - $ret = @{} - foreach($r in $response){ - $n = [pscustomobject]@{ - name = $r.name - url = $r.url + $SearchString = $SearchString -replace '{org}', $Owner + $SearchString = $SearchString -replace '{value}', $PropertyValue + $SearchString = $SearchString -replace '{property}', $PropertyName + + $SearchString | Write-Verbose + + $response = Invoke-MyCommand -Command SearchRepos -Parameters @{searchstring=$SearchString} + + $list = @{} + foreach($r in $response){ + $n = [pscustomobject]@{ + name = $r.name + url = $r.url + } + $list.$($r.name) = $n } - $ret.$($r.name) = $n + + # Save to cache + Save-GcDatabaseRepos -Owner $Owner -PropertyName $PropertyName -Handle $PropertyValue -Value $list + + $cache = Get-GcDatabaseRepos -Owner $Owner -PropertyName $PropertyName -Handle $PropertyValue } - return $ret + #return cache + return $cache } Export-ModuleMember -Function Get-GcRepos function Get-GcRepo{ [CmdletBinding()] param( - [Parameter(Mandatory,Position=0)][ValidateSet([ValidRepoNames])][string]$RepositoryName + [Parameter(Mandatory,Position=0)][ValidateSet([ValidRepoNames])][string]$Name ) $repos = Get-GcReposMy - return $repos.$RepositoryName + return $repos.$Name } Export-ModuleMember -Function Get-GcRepo function Invoke-SearchRepo{