|
| 1 | +#Requires -PSEdition Core |
| 2 | +#Requires -Version 7.2 |
| 3 | +Import-Module -Name @( |
| 4 | + (Join-Path -Path $PSScriptRoot -ChildPath 'nodejs-invoke.psm1'), |
| 5 | + (Join-Path -Path $PSScriptRoot -ChildPath 'utility.psm1') |
| 6 | +) -Prefix 'GitHubActions' -Scope 'Local' |
| 7 | +<# |
| 8 | +.SYNOPSIS |
| 9 | +GitHub Actions - Export Artifact |
| 10 | +.DESCRIPTION |
| 11 | +Export artifact to persist data after a job has completed, and share that data with another job in the same workflow. |
| 12 | +.PARAMETER Name |
| 13 | +Artifact name. |
| 14 | +.PARAMETER Path |
| 15 | +Absolute and/or relative path to the file that need to export as artifact. |
| 16 | +.PARAMETER LiteralPath |
| 17 | +Absolute and/or relative literal path to the file that need to export as artifact. |
| 18 | +.PARAMETER BaseRoot |
| 19 | +A (literal) path that denote the base root directory of the files for control files structure. |
| 20 | +.PARAMETER IgnoreIssuePaths |
| 21 | +Whether the export should continue in the event of files fail to export; If set to `$False` and issue is encountered, export will stop and queued files will not export; The partial artifact availables which include files up until the issue; If set to `$True` and issue is encountered, the issue file will ignore and skip, and queued files will still export; The partial artifact availables which include everything but exclude issue files. |
| 22 | +.PARAMETER RetentionTime |
| 23 | +Duration of artifact expire by days. |
| 24 | +.OUTPUTS |
| 25 | +[PSCustomObject] Exported artifact's metadata. |
| 26 | +#> |
| 27 | +Function Export-Artifact { |
| 28 | + [CmdletBinding(DefaultParameterSetName = 'Path', HelpUri = 'https://github.com/hugoalh-studio/ghactions-toolkit-powershell/wiki/api_function_export-githubactionsartifact#Export-GitHubActionsArtifact')] |
| 29 | + [OutputType([PSCustomObject])] |
| 30 | + Param ( |
| 31 | + [Parameter(Mandatory = $True, Position = 0)][ValidateScript({ |
| 32 | + Return (Test-ArtifactName -InputObject $_) |
| 33 | + }, ErrorMessage = '`{0}` is not a valid GitHub Actions artifact name!')][String]$Name, |
| 34 | + [Parameter(Mandatory = $True, ParameterSetName = 'Path', Position = 1, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)][SupportsWildcards()][Alias('File', 'Files', 'Paths')][String[]]$Path, |
| 35 | + [Parameter(Mandatory = $True, ParameterSetName = 'LiteralPath', ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)][Alias('LiteralFile', 'LiteralFiles', 'LiteralPaths', 'LP', 'PSPath', 'PSPaths')][String[]]$LiteralPath, |
| 36 | + [Alias('Root')][String]$BaseRoot = $Env:GITHUB_WORKSPACE, |
| 37 | + [Alias('ContinueOnError', 'ContinueOnIssue', 'ContinueOnIssues', 'IgnoreIssuePath')][Switch]$IgnoreIssuePaths, |
| 38 | + [Alias('RetentionDay')][ValidateRange(1, 90)][Byte]$RetentionTime |
| 39 | + ) |
| 40 | + Begin { |
| 41 | + If ( |
| 42 | + ![System.IO.Path]::IsPathRooted($BaseRoot) -or |
| 43 | + !(Test-Path -LiteralPath $BaseRoot -PathType 'Container') |
| 44 | + ) { |
| 45 | + Return (Write-Error -Message "``$BaseRoot`` is not an exist and valid GitHub Actions artifact base root!" -Category 'SyntaxError') |
| 46 | + Break# This is the best way to early terminate this function without terminate caller/invoker process. |
| 47 | + } |
| 48 | + [String]$BaseRootRegularExpression = "^$([RegEx]::Escape((Resolve-Path -LiteralPath $BaseRoot)))" |
| 49 | + [String[]]$PathsValid = @() |
| 50 | + [String[]]$PathsInvalid = @() |
| 51 | + } |
| 52 | + Process { |
| 53 | + Switch ($PSCmdlet.ParameterSetName) { |
| 54 | + 'Path' { |
| 55 | + ForEach ($ItemPath In $Path) { |
| 56 | + Try { |
| 57 | + ForEach ($ItemResolve In [String[]](Resolve-Path -Path ([System.IO.Path]::IsPathRooted($ItemPath) ? $ItemPath : (Join-Path -Path $BaseRoot -ChildPath $ItemPath)))) { |
| 58 | + If (!(Test-Path -LiteralPath $ItemResolve -PathType 'Leaf')) { |
| 59 | + Continue |
| 60 | + } |
| 61 | + If ( |
| 62 | + $ItemResolve -inotmatch $BaseRootRegularExpression -or |
| 63 | + !(Test-ArtifactPath -InputObject $ItemResolve) |
| 64 | + ) { |
| 65 | + $PathsInvalid += $ItemResolve |
| 66 | + Continue |
| 67 | + } |
| 68 | + $PathsValid += $ItemResolve |
| 69 | + } |
| 70 | + } Catch { |
| 71 | + $PathsInvalid += $ItemPath |
| 72 | + } |
| 73 | + } |
| 74 | + } |
| 75 | + 'LiteralPath' { |
| 76 | + ForEach ($ItemLiteralPath In $LiteralPath) { |
| 77 | + Try { |
| 78 | + ForEach ($ItemResolve In [String[]](Resolve-Path -LiteralPath ([System.IO.Path]::IsPathRooted($ItemLiteralPath) ? $ItemLiteralPath : (Join-Path -Path $BaseRoot -ChildPath $ItemLiteralPath)))) { |
| 79 | + If (!(Test-Path -LiteralPath $ItemResolve -PathType 'Leaf')) { |
| 80 | + Continue |
| 81 | + } |
| 82 | + If ( |
| 83 | + $ItemResolve -inotmatch $BaseRootRegularExpression -or |
| 84 | + !(Test-ArtifactPath -InputObject $ItemResolve) |
| 85 | + ) { |
| 86 | + $PathsInvalid += $ItemResolve |
| 87 | + Continue |
| 88 | + } |
| 89 | + $PathsValid += $ItemResolve |
| 90 | + } |
| 91 | + } Catch { |
| 92 | + $PathsInvalid += $ItemLiteralPath |
| 93 | + } |
| 94 | + } |
| 95 | + } |
| 96 | + } |
| 97 | + } |
| 98 | + End { |
| 99 | + If (!(Test-GitHubActionsEnvironment -Artifact)) { |
| 100 | + Return (Write-Error -Message 'Unable to get GitHub Actions artifact resources!' -Category 'ResourceUnavailable') |
| 101 | + } |
| 102 | + If ($PathsInvalid.Count -igt 0 -and !$IgnoreIssuePaths.IsPresent) { |
| 103 | + Return ($PathsInvalid | ForEach-Object -Process { |
| 104 | + Return (Write-Error -Message "``$_`` is not an exist and valid file path!" -Category 'SyntaxError') |
| 105 | + }) |
| 106 | + } |
| 107 | + If ($PathsValid.Count -ieq 0) { |
| 108 | + Return (Write-Error -Message 'No valid path is defined!' -Category 'NotSpecified') |
| 109 | + } |
| 110 | + [Hashtable]$InputObject = @{ |
| 111 | + Name = $Name |
| 112 | + Path = ($PathsValid | ForEach-Object -Process { |
| 113 | + Return ($_ -ireplace $BaseRootRegularExpression, '' -ireplace '\\', '/') |
| 114 | + }) |
| 115 | + BaseRoot = $BaseRoot |
| 116 | + IgnoreIssuePaths = $IgnoreIssuePaths.IsPresent |
| 117 | + } |
| 118 | + If ($Null -ine $RetentionTime) { |
| 119 | + $InputObject.RetentionTIme = $RetentionTime |
| 120 | + } |
| 121 | + $ResultRaw = Invoke-GitHubActionsNodeJsWrapper -Path 'artifact\upload.js' -InputObject ([PSCustomObject]$InputObject | ConvertTo-Json -Depth 100 -Compress) |
| 122 | + If ($ResultRaw -ieq $False) { |
| 123 | + Return |
| 124 | + } |
| 125 | + [Hashtable]$Result = ($ResultRaw | ConvertFrom-Json -AsHashtable -Depth 100) |
| 126 | + $Result.FailedItem += $PathsInvalid |
| 127 | + $Result.FailedItems += $PathsInvalid |
| 128 | + Return [PSCustomObject]$Result |
| 129 | + } |
| 130 | +} |
| 131 | +<# |
| 132 | +.SYNOPSIS |
| 133 | +GitHub Actions - Import Artifact |
| 134 | +.DESCRIPTION |
| 135 | +Import artifact that shared data from previous job in the same workflow. |
| 136 | +.PARAMETER Name |
| 137 | +Artifact name. |
| 138 | +.PARAMETER Destination |
| 139 | +Artifact destination. |
| 140 | +.PARAMETER CreateSubfolder |
| 141 | +Create a subfolder with artifact name and put data into here. |
| 142 | +.PARAMETER All |
| 143 | +Import all artifacts that shared data from previous job in the same workflow. |
| 144 | +.OUTPUTS |
| 145 | +[PSCustomObject[]] Imported artifacts' metadata. |
| 146 | +#> |
| 147 | +Function Import-Artifact { |
| 148 | + [CmdletBinding(DefaultParameterSetName = 'Select', HelpUri = 'https://github.com/hugoalh-studio/ghactions-toolkit-powershell/wiki/api_function_import-githubactionsartifact#Import-GitHubActionsArtifact')] |
| 149 | + [OutputType([PSCustomObject[]])] |
| 150 | + Param ( |
| 151 | + [Parameter(Mandatory = $True, ParameterSetName = 'Select', Position = 0, ValueFromPipeline = $True, ValueFromPipelineByPropertyName = $True)][String[]]$Name, |
| 152 | + [String]$Destination = $Env:GITHUB_WORKSPACE, |
| 153 | + [Parameter(ParameterSetName = 'Select')][Switch]$CreateSubfolder, |
| 154 | + [Parameter(Mandatory = $True, ParameterSetName = 'All')][Switch]$All |
| 155 | + ) |
| 156 | + Begin { |
| 157 | + If (!(Test-GitHubActionsEnvironment -Artifact)) { |
| 158 | + Return (Write-Error -Message 'Unable to get GitHub Actions artifact resources!' -Category 'ResourceUnavailable') |
| 159 | + Break# This is the best way to early terminate this function without terminate caller/invoker process. |
| 160 | + } |
| 161 | + [PSCustomObject[]]$OutputObject = @() |
| 162 | + } |
| 163 | + Process { |
| 164 | + Switch ($PSCmdlet.ParameterSetName) { |
| 165 | + 'All' { |
| 166 | + $ResultRaw = Invoke-GitHubActionsNodeJsWrapper -Path 'artifact\download-all.js' -InputObject ([PSCustomObject]@{ |
| 167 | + Destination = $Destination |
| 168 | + } | ConvertTo-Json -Depth 100 -Compress) |
| 169 | + If ($ResultRaw -ieq $False) { |
| 170 | + Continue |
| 171 | + } |
| 172 | + $OutputObject = ($ResultRaw | ConvertFrom-Json -Depth 100) |
| 173 | + } |
| 174 | + 'Select' { |
| 175 | + ForEach ($Item In $Name) { |
| 176 | + $ResultRaw = Invoke-GitHubActionsNodeJsWrapper -Path 'artifact\download.js' -InputObject ([PSCustomObject]@{ |
| 177 | + Name = $Item |
| 178 | + Destination = $Destination |
| 179 | + CreateSubfolder = $CreateSubfolder.IsPresent |
| 180 | + } | ConvertTo-Json -Depth 100 -Compress) |
| 181 | + If ($ResultRaw -ieq $False) { |
| 182 | + Continue |
| 183 | + } |
| 184 | + $OutputObject += ($ResultRaw | ConvertFrom-Json -Depth 100) |
| 185 | + } |
| 186 | + } |
| 187 | + } |
| 188 | + } |
| 189 | + End { |
| 190 | + Return $OutputObject |
| 191 | + } |
| 192 | +} |
| 193 | +<# |
| 194 | +.SYNOPSIS |
| 195 | +GitHub Actions (Internal) - Test Artifact Name |
| 196 | +.DESCRIPTION |
| 197 | +Test artifact name whether is valid. |
| 198 | +.PARAMETER InputObject |
| 199 | +Artifact name that need to test. |
| 200 | +.OUTPUTS |
| 201 | +[Boolean] Test result. |
| 202 | +#> |
| 203 | +Function Test-ArtifactName { |
| 204 | + [CmdletBinding()] |
| 205 | + [OutputType([Boolean])] |
| 206 | + Param ( |
| 207 | + [Parameter(Mandatory = $True, Position = 0)][Alias('Input', 'Object')][String]$InputObject |
| 208 | + ) |
| 209 | + Return ((Test-ArtifactPath -InputObject $InputObject) -and $InputObject -imatch '^[^\\/]+$') |
| 210 | +} |
| 211 | +<# |
| 212 | +.SYNOPSIS |
| 213 | +GitHub Actions (Internal) - Test Artifact Path |
| 214 | +.DESCRIPTION |
| 215 | +Test artifact path whether is valid. |
| 216 | +.PARAMETER InputObject |
| 217 | +Artifact path that need to test. |
| 218 | +.OUTPUTS |
| 219 | +[Boolean] Test result. |
| 220 | +#> |
| 221 | +Function Test-ArtifactPath { |
| 222 | + [CmdletBinding()] |
| 223 | + [OutputType([Boolean])] |
| 224 | + Param ( |
| 225 | + [Parameter(Mandatory = $True, Position = 0)][Alias('Input', 'Object')][String]$InputObject |
| 226 | + ) |
| 227 | + Return ($InputObject -imatch '^[^":<>|*?\n\r\t]+$') |
| 228 | +} |
| 229 | +Export-ModuleMember -Function @( |
| 230 | + 'Export-Artifact', |
| 231 | + 'Import-Artifact' |
| 232 | +) |
0 commit comments