diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..36bd853 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [StartAutomating] diff --git a/.github/workflows/BuildGradient.yml b/.github/workflows/BuildGradient.yml new file mode 100644 index 0000000..64c510f --- /dev/null +++ b/.github/workflows/BuildGradient.yml @@ -0,0 +1,502 @@ + +name: Build Module +on: + push: + pull_request: + workflow_dispatch: +jobs: + TestPowerShellOnLinux: + runs-on: ubuntu-latest + steps: + - name: InstallPester + id: InstallPester + shell: pwsh + run: | + $Parameters = @{} + $Parameters.PesterMaxVersion = ${env:PesterMaxVersion} + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: InstallPester $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {<# + .Synopsis + Installs Pester + .Description + Installs Pester + #> + param( + # The maximum pester version. Defaults to 4.99.99. + [string] + $PesterMaxVersion = '4.99.99' + ) + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + Install-Module -Name Pester -Repository PSGallery -Force -Scope CurrentUser -MaximumVersion $PesterMaxVersion -SkipPublisherCheck -AllowClobber + Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion} @Parameters + - name: Check out repository + uses: actions/checkout@v2 + - name: RunPester + id: RunPester + shell: pwsh + run: | + $Parameters = @{} + $Parameters.ModulePath = ${env:ModulePath} + $Parameters.PesterMaxVersion = ${env:PesterMaxVersion} + $Parameters.NoCoverage = ${env:NoCoverage} + $Parameters.NoCoverage = $parameters.NoCoverage -match 'true'; + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: RunPester $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {<# + .Synopsis + Runs Pester + .Description + Runs Pester tests after importing a PowerShell module + #> + param( + # The module path. If not provided, will default to the second half of the repository ID. + [string] + $ModulePath, + # The Pester max version. By default, this is pinned to 4.99.99. + [string] + $PesterMaxVersion = '4.99.99', + + # If set, will not collect code coverage. + [switch] + $NoCoverage + ) + + $global:ErrorActionPreference = 'continue' + $global:ProgressPreference = 'silentlycontinue' + + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + if (-not $ModulePath) { $ModulePath = ".\$moduleName.psd1" } + $importedPester = Import-Module Pester -Force -PassThru -MaximumVersion $PesterMaxVersion + $importedModule = Import-Module $ModulePath -Force -PassThru + $importedPester, $importedModule | Out-Host + + $codeCoverageParameters = @{ + CodeCoverage = "$($importedModule | Split-Path)\*-*.ps1" + CodeCoverageOutputFile = ".\$moduleName.Coverage.xml" + } + + if ($NoCoverage) { + $codeCoverageParameters = @{} + } + + + $result = + Invoke-Pester -PassThru -Verbose -OutputFile ".\$moduleName.TestResults.xml" -OutputFormat NUnitXml @codeCoverageParameters + + "::set-output name=TotalCount::$($result.TotalCount)", + "::set-output name=PassedCount::$($result.PassedCount)", + "::set-output name=FailedCount::$($result.FailedCount)" | Out-Host + if ($result.FailedCount -gt 0) { + "::debug:: $($result.FailedCount) tests failed" + foreach ($r in $result.TestResult) { + if (-not $r.Passed) { + "::error::$($r.describe, $r.context, $r.name -join ' ') $($r.FailureMessage)" + } + } + throw "::error:: $($result.FailedCount) tests failed" + } + } @Parameters + - name: PublishTestResults + uses: actions/upload-artifact@main + with: + name: PesterResults + path: '**.TestResults.xml' + if: ${{always()}} + TagReleaseAndPublish: + runs-on: ubuntu-latest + if: ${{ success() }} + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: TagModuleVersion + id: TagModuleVersion + shell: pwsh + run: | + $Parameters = @{} + $Parameters.ModulePath = ${env:ModulePath} + $Parameters.UserEmail = ${env:UserEmail} + $Parameters.UserName = ${env:UserName} + $Parameters.TagVersionFormat = ${env:TagVersionFormat} + $Parameters.TagAnnotationFormat = ${env:TagAnnotationFormat} + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: TagModuleVersion $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {param( + [string] + $ModulePath, + + # The user email associated with a git commit. + [string] + $UserEmail, + + # The user name associated with a git commit. + [string] + $UserName, + + # The tag version format (default value: 'v$(imported.Version)') + # This can expand variables. $imported will contain the imported module. + [string] + $TagVersionFormat = 'v$($imported.Version)', + + # The tag version format (default value: '$($imported.Name) $(imported.Version)') + # This can expand variables. $imported will contain the imported module. + [string] + $TagAnnotationFormat = '$($imported.Name) $($imported.Version)' + ) + + + $gitHubEvent = if ($env:GITHUB_EVENT_PATH) { + [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json + } else { $null } + + + @" + ::group::GitHubEvent + $($gitHubEvent | ConvertTo-Json -Depth 100) + ::endgroup:: + "@ | Out-Host + + if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and + (-not $gitHubEvent.psobject.properties['inputs'])) { + "::warning::Pull Request has not merged, skipping Tagging" | Out-Host + return + } + + + + $imported = + if (-not $ModulePath) { + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + Import-Module ".\$moduleName.psd1" -Force -PassThru -Global + } else { + Import-Module $modulePath -Force -PassThru -Global + } + + if (-not $imported) { return } + + $targetVersion =$ExecutionContext.InvokeCommand.ExpandString($TagVersionFormat) + $existingTags = git tag --list + + @" + Target Version: $targetVersion + + Existing Tags: + $($existingTags -join [Environment]::NewLine) + "@ | Out-Host + + $versionTagExists = $existingTags | Where-Object { $_ -match $targetVersion } + + if ($versionTagExists) { + "::warning::Version $($versionTagExists)" + return + } + + if (-not $UserName) { $UserName = $env:GITHUB_ACTOR } + if (-not $UserEmail) { $UserEmail = "$UserName@github.com" } + git config --global user.email $UserEmail + git config --global user.name $UserName + + git tag -a $targetVersion -m $ExecutionContext.InvokeCommand.ExpandString($TagAnnotationFormat) + git push origin --tags + + if ($env:GITHUB_ACTOR) { + exit 0 + }} @Parameters + - name: ReleaseModule + id: ReleaseModule + shell: pwsh + run: | + $Parameters = @{} + $Parameters.ModulePath = ${env:ModulePath} + $Parameters.UserEmail = ${env:UserEmail} + $Parameters.UserName = ${env:UserName} + $Parameters.TagVersionFormat = ${env:TagVersionFormat} + $Parameters.ReleaseNameFormat = ${env:ReleaseNameFormat} + $Parameters.ReleaseAsset = ${env:ReleaseAsset} + $Parameters.ReleaseAsset = $parameters.ReleaseAsset -split ';' -replace '^[''"]' -replace '[''"]$' + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: ReleaseModule $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {param( + [string] + $ModulePath, + + # The user email associated with a git commit. + [string] + $UserEmail, + + # The user name associated with a git commit. + [string] + $UserName, + + # The tag version format (default value: 'v$(imported.Version)') + # This can expand variables. $imported will contain the imported module. + [string] + $TagVersionFormat = 'v$($imported.Version)', + + # The release name format (default value: '$($imported.Name) $($imported.Version)') + [string] + $ReleaseNameFormat = '$($imported.Name) $($imported.Version)', + + # Any assets to attach to the release. Can be a wildcard or file name. + [string[]] + $ReleaseAsset + ) + + + $gitHubEvent = if ($env:GITHUB_EVENT_PATH) { + [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json + } else { $null } + + + @" + ::group::GitHubEvent + $($gitHubEvent | ConvertTo-Json -Depth 100) + ::endgroup:: + "@ | Out-Host + + if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and + (-not $gitHubEvent.psobject.properties['inputs'])) { + "::warning::Pull Request has not merged, skipping GitHub release" | Out-Host + return + } + + + + $imported = + if (-not $ModulePath) { + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + Import-Module ".\$moduleName.psd1" -Force -PassThru -Global + } else { + Import-Module $modulePath -Force -PassThru -Global + } + + if (-not $imported) { return } + + $targetVersion =$ExecutionContext.InvokeCommand.ExpandString($TagVersionFormat) + $targetReleaseName = $targetVersion + $releasesURL = 'https://api.github.com/repos/${{github.repository}}/releases' + "Release URL: $releasesURL" | Out-Host + $listOfReleases = Invoke-RestMethod -Uri $releasesURL -Method Get -Headers @{ + "Accept" = "application/vnd.github.v3+json" + "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' + } + + $releaseExists = $listOfReleases | Where-Object tag_name -eq $targetVersion + + if ($releaseExists) { + "::warning::Release '$($releaseExists.Name )' Already Exists" | Out-Host + $releasedIt = $releaseExists + } else { + $releasedIt = Invoke-RestMethod -Uri $releasesURL -Method Post -Body ( + [Ordered]@{ + owner = '${{github.owner}}' + repo = '${{github.repository}}' + tag_name = $targetVersion + name = $ExecutionContext.InvokeCommand.ExpandString($ReleaseNameFormat) + body = + if ($env:RELEASENOTES) { + $env:RELEASENOTES + } elseif ($imported.PrivateData.PSData.ReleaseNotes) { + $imported.PrivateData.PSData.ReleaseNotes + } else { + "$($imported.Name) $targetVersion" + } + draft = if ($env:RELEASEISDRAFT) { [bool]::Parse($env:RELEASEISDRAFT) } else { $false } + prerelease = if ($env:PRERELEASE) { [bool]::Parse($env:PRERELEASE) } else { $false } + } | ConvertTo-Json + ) -Headers @{ + "Accept" = "application/vnd.github.v3+json" + "Content-type" = "application/json" + "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' + } + } + + + + + + if (-not $releasedIt) { + throw "Release failed" + } else { + $releasedIt | Out-Host + } + + $releaseUploadUrl = $releasedIt.upload_url -replace '\{.+$' + + if ($ReleaseAsset) { + $fileList = Get-ChildItem -Recurse + $filesToRelease = + @(:nextFile foreach ($file in $fileList) { + foreach ($relAsset in $ReleaseAsset) { + if ($relAsset -match '[\*\?]') { + if ($file.Name -like $relAsset) { + $file; continue nextFile + } + } elseif ($file.Name -eq $relAsset -or $file.FullName -eq $relAsset) { + $file; continue nextFile + } + } + }) + + $releasedFiles = @{} + foreach ($file in $filesToRelease) { + if ($releasedFiles[$file.Name]) { + Write-Warning "Already attached file $($file.Name)" + continue + } else { + $fileBytes = [IO.File]::ReadAllBytes($file.FullName) + $releasedFiles[$file.Name] = + Invoke-RestMethod -Uri "${releaseUploadUrl}?name=$($file.Name)" -Headers @{ + "Accept" = "application/vnd.github+json" + "Authorization" = 'Bearer ${{ secrets.GITHUB_TOKEN }}' + } -Body $fileBytes -ContentType Application/octet-stream + $releasedFiles[$file.Name] + } + } + + "Attached $($releasedFiles.Count) file(s) to release" | Out-Host + } + + + + } @Parameters + - name: PublishPowerShellGallery + id: PublishPowerShellGallery + shell: pwsh + run: | + $Parameters = @{} + $Parameters.ModulePath = ${env:ModulePath} + $Parameters.Exclude = ${env:Exclude} + $Parameters.Exclude = $parameters.Exclude -split ';' -replace '^[''"]' -replace '[''"]$' + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: PublishPowerShellGallery $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {param( + [string] + $ModulePath, + + [string[]] + $Exclude = @('*.png', '*.mp4', '*.jpg','*.jpeg', '*.gif', 'docs[/\]*') + ) + + $gitHubEvent = if ($env:GITHUB_EVENT_PATH) { + [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) | ConvertFrom-Json + } else { $null } + + if (-not $Exclude) { + $Exclude = @('*.png', '*.mp4', '*.jpg','*.jpeg', '*.gif','docs[/\]*') + } + + + @" + ::group::GitHubEvent + $($gitHubEvent | ConvertTo-Json -Depth 100) + ::endgroup:: + "@ | Out-Host + + @" + ::group::PSBoundParameters + $($PSBoundParameters | ConvertTo-Json -Depth 100) + ::endgroup:: + "@ | Out-Host + + if (-not ($gitHubEvent.head_commit.message -match "Merge Pull Request #(?\d+)") -and + (-not $gitHubEvent.psobject.properties['inputs'])) { + "::warning::Pull Request has not merged, skipping Gallery Publish" | Out-Host + return + } + + + $imported = + if (-not $ModulePath) { + $orgName, $moduleName = $env:GITHUB_REPOSITORY -split "/" + Import-Module ".\$moduleName.psd1" -Force -PassThru -Global + } else { + Import-Module $modulePath -Force -PassThru -Global + } + + if (-not $imported) { return } + + $foundModule = try { Find-Module -Name $imported.Name -ErrorAction SilentlyContinue} catch {} + + if ($foundModule -and (([Version]$foundModule.Version) -ge ([Version]$imported.Version))) { + "::warning::Gallery Version of $moduleName is more recent ($($foundModule.Version) >= $($imported.Version))" | Out-Host + } else { + + $gk = '${{secrets.GALLERYKEY}}' + + $rn = Get-Random + $moduleTempFolder = Join-Path $pwd "$rn" + $moduleTempPath = Join-Path $moduleTempFolder $moduleName + New-Item -ItemType Directory -Path $moduleTempPath -Force | Out-Host + + Write-Host "Staging Directory: $ModuleTempPath" + + $imported | Split-Path | + Get-ChildItem -Force | + Where-Object Name -NE $rn | + Copy-Item -Destination $moduleTempPath -Recurse + + $moduleGitPath = Join-Path $moduleTempPath '.git' + Write-Host "Removing .git directory" + if (Test-Path $moduleGitPath) { + Remove-Item -Recurse -Force $moduleGitPath + } + + if ($Exclude) { + "::notice::Attempting to Exlcude $exclude" | Out-Host + Get-ChildItem $moduleTempPath -Recurse | + Where-Object { + foreach ($ex in $exclude) { + if ($_.FullName -like $ex) { + "::notice::Excluding $($_.FullName)" | Out-Host + return $true + } + } + } | + Remove-Item + } + + Write-Host "Module Files:" + Get-ChildItem $moduleTempPath -Recurse + Write-Host "Publishing $moduleName [$($imported.Version)] to Gallery" + Publish-Module -Path $moduleTempPath -NuGetApiKey $gk + if ($?) { + Write-Host "Published to Gallery" + } else { + Write-Host "Gallery Publish Failed" + exit 1 + } + } + } @Parameters + BuildGradient: + runs-on: ubuntu-latest + if: ${{ success() }} + steps: + - name: Check out repository + uses: actions/checkout@main + - name: UseEZOut + uses: StartAutomating/EZOut@master + - name: Run Action (on branch) + if: ${{github.ref_name != 'main'}} + uses: ./ + diff --git a/Build/GitHub/Actions/GradientAction.ps1 b/Build/GitHub/Actions/GradientAction.ps1 new file mode 100644 index 0000000..d100448 --- /dev/null +++ b/Build/GitHub/Actions/GradientAction.ps1 @@ -0,0 +1,351 @@ +<# +.Synopsis + GitHub Action for Gradient +.Description + GitHub Action for Gradient. This will: + + * Import Gradient + * If `-Run` is provided, run that script + * Otherwise, unless `-SkipScriptFile` is passed, run all *.Gradient.ps1 files beneath the workflow directory + * If any `-ActionScript` was provided, run scripts from the action path that match a wildcard pattern. + + If you will be making changes using the GitHubAPI, you should provide a -GitHubToken + + If none is provided, and ENV:GITHUB_TOKEN is set, this will be used instead. + + Any files changed can be outputted by the script. + Those changes can be checked back into the repo if `-AutoCommit` is set. +#> + +param( +# A PowerShell Script that uses Gradient. +# Any files outputted from the script will be added to the repository. +# If those files have a .Message attached to them, they will be committed with that message. +[string] +$Run, + +# If set, will not process any files named *.Gradient.ps1 +[switch] +$SkipScriptFile, + +# A list of modules to be installed from the PowerShell gallery before scripts run. +[string[]] +$InstallModule, + +# If provided, will commit any remaining changes made to the workspace with this commit message. +[string] +$CommitMessage, + +# If provided, will checkout a new branch before making the changes. +# If not provided, will use the current branch. +[string] +$TargetBranch, + +# The name of one or more scripts to run, from this action's path. +[string[]] +$ActionScript, + +# The github token to use for requests. +[string] +$GitHubToken = '{{ secrets.GITHUB_TOKEN }}', + +# The user email associated with a git commit. If this is not provided, it will be set to the username@noreply.github.com. +[string] +$UserEmail, + +# The user name associated with a git commit. +[string] +$UserName, + +# If set, will not push any changes made to the repository. +# (they will still be committed if `-AutoCommit` is passed) +[switch] +$NoPush, + +# If set, will commit any changes made to the repository. +# (this also implies `-NoPush`) +[switch] +$AutoCommit +) + +$ErrorActionPreference = 'continue' +"::group::Parameters" | Out-Host +[PSCustomObject]$PSBoundParameters | Format-List | Out-Host +"::endgroup::" | Out-Host + +$gitHubEventJson = [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) +$gitHubEvent = + if ($env:GITHUB_EVENT_PATH) { + $gitHubEventJson | ConvertFrom-Json + } else { $null } +"::group::Parameters" | Out-Host +$gitHubEvent | Format-List | Out-Host +"::endgroup::" | Out-Host + + +$anyFilesChanged = $false +$ActionModuleName = 'Gradient' +$actorInfo = $null + + +$checkDetached = git symbolic-ref -q HEAD +if ($LASTEXITCODE) { + "::warning::On detached head, skipping action" | Out-Host + exit 0 +} + +function InstallActionModule { + param([string]$ModuleToInstall) + $moduleInWorkspace = Get-ChildItem -Path $env:GITHUB_WORKSPACE -Recurse -File | + Where-Object Name -eq "$($moduleToInstall).psd1" | + Where-Object { + $(Get-Content $_.FullName -Raw) -match 'ModuleVersion' + } + if (-not $moduleInWorkspace) { + $availableModules = Get-Module -ListAvailable + if ($availableModules.Name -notcontains $moduleToInstall) { + Install-Module $moduleToInstall -Scope CurrentUser -Force -AcceptLicense -AllowClobber + } + Import-Module $moduleToInstall -Force -PassThru | Out-Host + } else { + Import-Module $moduleInWorkspace.FullName -Force -PassThru | Out-Host + } +} +function ImportActionModule { + #region -InstallModule + if ($InstallModule) { + "::group::Installing Modules" | Out-Host + foreach ($moduleToInstall in $InstallModule) { + InstallActionModule -ModuleToInstall $moduleToInstall + } + "::endgroup::" | Out-Host + } + #endregion -InstallModule + + if ($env:GITHUB_ACTION_PATH) { + $LocalModulePath = Join-Path $env:GITHUB_ACTION_PATH "$ActionModuleName.psd1" + if (Test-path $LocalModulePath) { + Import-Module $LocalModulePath -Force -PassThru | Out-String + } else { + throw "Module '$ActionModuleName' not found" + } + } elseif (-not (Get-Module $ActionModuleName)) { + throw "Module '$ActionModuleName' not found" + } + + "::notice title=ModuleLoaded::$ActionModuleName Loaded from Path - $($LocalModulePath)" | Out-Host + if ($env:GITHUB_STEP_SUMMARY) { + "# $($ActionModuleName)" | + Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } +} +function InitializeAction { + #region Custom + #endregion Custom + + # Configure git based on the $env:GITHUB_ACTOR + if (-not $UserName) { $UserName = $env:GITHUB_ACTOR } + if (-not $actorID) { $actorID = $env:GITHUB_ACTOR_ID } + + $actorInfo = + Invoke-RestMethod -Uri "https://api.github.com/user/$actorID" + + + if (-not $UserEmail) { $UserEmail = "$UserName@noreply.github.com" } + git config --global user.email $UserEmail + git config --global user.name $actorInfo.name + + # Pull down any changes + git pull | Out-Host + + if ($TargetBranch) { + "::notice title=Expanding target branch string $targetBranch" | Out-Host + $TargetBranch = $ExecutionContext.SessionState.InvokeCommand.ExpandString($TargetBranch) + "::notice title=Checking out target branch::$targetBranch" | Out-Host + git checkout -b $TargetBranch | Out-Host + git pull | Out-Host + } +} + +function InvokeActionModule { + $myScriptStart = [DateTime]::Now + $myScript = $ExecutionContext.SessionState.PSVariable.Get("Run").Value + if ($myScript) { + Invoke-Expression -Command $myScript | + . ProcessOutput | + Out-Host + return + } + $myScriptTook = [Datetime]::Now - $myScriptStart + $MyScriptFilesStart = [DateTime]::Now + + $myScriptList = @() + $shouldSkip = $ExecutionContext.SessionState.PSVariable.Get("SkipScriptFile").Value + if ($shouldSkip) { + return + } + $scriptFiles = @( + Get-ChildItem -Recurse -Path $env:GITHUB_WORKSPACE | + Where-Object Name -Match "\.$($ActionModuleName)\.ps1$" + if ($ActionScript) { + if ($ActionScript -match '^\s{0,}/' -and $ActionScript -match '/\s{0,}$') { + $ActionScriptPattern = $ActionScript.Trim('/').Trim() -as [regex] + if ($ActionScriptPattern) { + $ActionScriptPattern = [regex]::new($ActionScript.Trim('/').Trim(), 'IgnoreCase,IgnorePatternWhitespace', [timespan]::FromSeconds(0.5)) + Get-ChildItem -Recurse -Path $env:GITHUB_ACTION_PATH | + Where-Object { $_.Name -Match "\.$($ActionModuleName)\.ps1$" -and $_.FullName -match $ActionScriptPattern } + } + } else { + Get-ChildItem -Recurse -Path $env:GITHUB_ACTION_PATH | + Where-Object Name -Match "\.$($ActionModuleName)\.ps1$" | + Where-Object FullName -Like $ActionScript + } + } + ) | Select-Object -Unique + $scriptFiles | + ForEach-Object -Begin { + if ($env:GITHUB_STEP_SUMMARY) { + "## $ActionModuleName Scripts" | + Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + } -Process { + $myScriptList += $_.FullName.Replace($env:GITHUB_WORKSPACE, '').TrimStart('/') + $myScriptCount++ + $scriptFile = $_ + if ($env:GITHUB_STEP_SUMMARY) { + "### $($scriptFile.Fullname -replace [Regex]::Escape($env:GITHUB_WORKSPACE))" | + Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + $scriptCmd = $ExecutionContext.SessionState.InvokeCommand.GetCommand($scriptFile.FullName, 'ExternalScript') + foreach ($requiredModule in $CommandInfo.ScriptBlock.Ast.ScriptRequirements.RequiredModules) { + if ($requiredModule.Name -and + (-not $requiredModule.MaximumVersion) -and + (-not $requiredModule.RequiredVersion) + ) { + InstallActionModule $requiredModule.Name + } + } + Push-Location $scriptFile.Directory.Fullname + $scriptFileOutputs = . $scriptCmd + $scriptFileOutputs | + . ProcessOutput | + Out-Host + Pop-Location + } + + $MyScriptFilesTook = [Datetime]::Now - $MyScriptFilesStart + $SummaryOfMyScripts = "$myScriptCount $ActionModuleName scripts took $($MyScriptFilesTook.TotalSeconds) seconds" + $SummaryOfMyScripts | + Out-Host + if ($env:GITHUB_STEP_SUMMARY) { + $SummaryOfMyScripts | + Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + #region Custom + #endregion Custom +} + +function OutError { + $anyRuntimeExceptions = $false + foreach ($err in $error) { + $errParts = @( + "::error " + @( + if ($err.InvocationInfo.ScriptName) { + "file=$($err.InvocationInfo.ScriptName)" + } + if ($err.InvocationInfo.ScriptLineNumber -ge 1) { + "line=$($err.InvocationInfo.ScriptLineNumber)" + if ($err.InvocationInfo.OffsetInLine -ge 1) { + "col=$($err.InvocationInfo.OffsetInLine)" + } + } + if ($err.CategoryInfo.Activity) { + "title=$($err.CategoryInfo.Activity)" + } + ) -join ',' + "::" + $err.Exception.Message + if ($err.CategoryInfo.Category -eq 'OperationStopped' -and + $err.CategoryInfo.Reason -eq 'RuntimeException') { + $anyRuntimeExceptions = $true + } + ) -join '' + $errParts | Out-Host + if ($anyRuntimeExceptions) { + exit 1 + } + } +} + +function PushActionOutput { + if ($anyFilesChanged) { + "::notice::$($anyFilesChanged) Files Changed" | Out-Host + } + if ($CommitMessage -or $anyFilesChanged) { + if ($CommitMessage) { + Get-ChildItem $env:GITHUB_WORKSPACE -Recurse | + ForEach-Object { + $gitStatusOutput = git status $_.Fullname -s + if ($gitStatusOutput) { + git add $_.Fullname + } + } + + git commit -m $ExecutionContext.SessionState.InvokeCommand.ExpandString($CommitMessage) + } + + $checkDetached = git symbolic-ref -q HEAD + if (-not $LASTEXITCODE -and -not $NoPush -and $AutoCommit) { + if ($TargetBranch -and $anyFilesChanged) { + "::notice::Pushing Changes to $targetBranch" | Out-Host + git push --set-upstream origin $TargetBranch + } elseif ($anyFilesChanged) { + "::notice::Pushing Changes" | Out-Host + git push + } + "Git Push Output: $($gitPushed | Out-String)" + } else { + "::notice::Not pushing changes (on detached head)" | Out-Host + $LASTEXITCODE = 0 + exit 0 + } + } +} + +filter ProcessOutput { + $out = $_ + $outItem = Get-Item -Path $out -ErrorAction Ignore + if (-not $outItem -and $out -is [string]) { + $out | Out-Host + if ($env:GITHUB_STEP_SUMMARY) { + "> $out" | Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + return + } + $fullName, $shouldCommit = + if ($out -is [IO.FileInfo]) { + $out.FullName, (git status $out.Fullname -s) + } elseif ($outItem) { + $outItem.FullName, (git status $outItem.Fullname -s) + } + if ($shouldCommit -and $AutoCommit) { + "$fullName has changed, and should be committed" | Out-Host + git add $fullName + if ($out.Message) { + git commit -m "$($out.Message)" | Out-Host + } elseif ($out.CommitMessage) { + git commit -m "$($out.CommitMessage)" | Out-Host + } elseif ($gitHubEvent.head_commit.message) { + git commit -m "$($gitHubEvent.head_commit.message)" | Out-Host + } + $anyFilesChanged = $true + } + $out +} + +. ImportActionModule +. InitializeAction +. InvokeActionModule +. PushActionOutput +. OutError \ No newline at end of file diff --git a/Build/GitHub/Jobs/BuildGradient.psd1 b/Build/GitHub/Jobs/BuildGradient.psd1 new file mode 100644 index 0000000..ff4344b --- /dev/null +++ b/Build/GitHub/Jobs/BuildGradient.psd1 @@ -0,0 +1,16 @@ +@{ + "runs-on" = "ubuntu-latest" + if = '${{ success() }}' + steps = @( + @{ + name = 'Check out repository' + uses = 'actions/checkout@main' + } + 'RunEZOut' + @{ + name = 'Run Action (on branch)' + if = '${{github.ref_name != ''main''}}' + uses = './' + } + ) +} \ No newline at end of file diff --git a/Build/GitHub/Steps/PublishTestResults.psd1 b/Build/GitHub/Steps/PublishTestResults.psd1 new file mode 100644 index 0000000..e8111e8 --- /dev/null +++ b/Build/GitHub/Steps/PublishTestResults.psd1 @@ -0,0 +1,10 @@ +@{ + name = 'PublishTestResults' + uses = 'actions/upload-artifact@main' + with = @{ + name = 'PesterResults' + path = '**.TestResults.xml' + } + if = '${{always()}}' +} + diff --git a/Build/Gradient.GitHubAction.PSDevOps.ps1 b/Build/Gradient.GitHubAction.PSDevOps.ps1 new file mode 100644 index 0000000..fd61adb --- /dev/null +++ b/Build/Gradient.GitHubAction.PSDevOps.ps1 @@ -0,0 +1,10 @@ +#requires -Module PSDevOps +Import-BuildStep -SourcePath ( + Join-Path $PSScriptRoot 'GitHub' +) -BuildSystem GitHubAction + +$PSScriptRoot | Split-Path | Push-Location + +New-GitHubAction -Name "UseGradient" -Description 'Gradient Generator' -Action GradientAction -Icon chevron-right -OutputPath .\action.yml + +Pop-Location \ No newline at end of file diff --git a/Build/Gradient.GitHubWorkflow.PSDevOps.ps1 b/Build/Gradient.GitHubWorkflow.PSDevOps.ps1 new file mode 100644 index 0000000..381886a --- /dev/null +++ b/Build/Gradient.GitHubWorkflow.PSDevOps.ps1 @@ -0,0 +1,12 @@ +#requires -Module PSDevOps +Import-BuildStep -SourcePath ( + Join-Path $PSScriptRoot 'GitHub' +) -BuildSystem GitHubWorkflow + +Push-Location ($PSScriptRoot | Split-Path) +New-GitHubWorkflow -Name "Build Module" -On Push, + PullRequest, + Demand -Job TestPowerShellOnLinux, + TagReleaseAndPublish, BuildGradient -OutputPath .\.github\workflows\BuildGradient.yml + +Pop-Location \ No newline at end of file diff --git a/Build/Gradient.ezout.ps1 b/Build/Gradient.ezout.ps1 new file mode 100644 index 0000000..197c9c6 --- /dev/null +++ b/Build/Gradient.ezout.ps1 @@ -0,0 +1,39 @@ +#requires -Module EZOut +# Install-Module EZOut or https://github.com/StartAutomating/EZOut +$myFile = $MyInvocation.MyCommand.ScriptBlock.File +$myModuleName = 'Gradient' +$myRoot = $myFile | Split-Path | Split-Path +Push-Location $myRoot +$formatting = @( + # Add your own Write-FormatView here, + # or put them in a Formatting or Views directory + foreach ($potentialDirectory in 'Formatting','Views','Types') { + Join-Path $myRoot $potentialDirectory | + Get-ChildItem -ea ignore | + Import-FormatView -FilePath {$_.Fullname} + } +) + +$destinationRoot = $myRoot + +if ($formatting) { + $myFormatFilePath = Join-Path $destinationRoot "$myModuleName.format.ps1xml" + # You can also output to multiple paths by passing a hashtable to -OutputPath. + $formatting | Out-FormatData -Module $MyModuleName -OutputPath $myFormatFilePath +} + +$types = @( + # Add your own Write-TypeView statements here + # or declare them in the 'Types' directory + Join-Path $myRoot Types | + Get-Item -ea ignore | + Import-TypeView + +) + +if ($types) { + $myTypesFilePath = Join-Path $destinationRoot "$myModuleName.types.ps1xml" + # You can also output to multiple paths by passing a hashtable to -OutputPath. + $types | Out-TypeData -OutputPath $myTypesFilePath +} +Pop-Location diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..07ed981 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,22 @@ +## Gradient 0.1: + +> Initial Gradient Module (#1) + +* `Get-Gradient` (aka `Gradient`) gets gradients (#3) +* It returns `Gradient` objects, using the `.Input` + * Have a `.CSS` property (#4) + * Hava a `.SVG` property (#5) + * Have a `.GradientTypePattern` (#15) + * Can get a `.GradientType` (#16) (defaulting to `radial-gradient`) + * Will `.ToString()` to `.CSS` (#17) + * Displays Input and CSS by default (#18) +* Basic Module Scaffolding + * GitHub Workflow (#2) + * GitHub Action (#7) + * Tests (#8) + * Sponsorship (#14) + * Code of Conduct (#9) + * Contributing Guide (#10) + * Security notice (#11) +* Also, initial examples are in `/Examples` (#13) + diff --git a/Commands/Get-Gradient.ps1 b/Commands/Get-Gradient.ps1 new file mode 100644 index 0000000..eed27e7 --- /dev/null +++ b/Commands/Get-Gradient.ps1 @@ -0,0 +1,98 @@ +function Get-Gradient { + <# + .SYNOPSIS + Gets a Gradient + .DESCRIPTION + Gets a Gradient between one or more inputs. + + A gradient can be rendered as CSS or SVG. + + If a gradient type is not specified, it will be a radial gradient. + + Conic gradients are not supported in SVG. + .NOTES + Get-Gradient is implemented a function with no dependencies. + + To use it in your projects, you can simply include it inline. + .EXAMPLE + Gradient '#4488ff', '#224488' # We can easily generate gradients + .EXAMPLE + "$(Gradient '#4488ff', '#224488')" # Stringifying a gradient returns the CSS + .EXAMPLE + # Make a a page with just a gradient + @( + $gradient = Gradient '#4488ff', '#224488' + "" + ) > ./gradient.html + .EXAMPLE + # Make a a page with a random gradient + + $gradient = @( + # Make 2 to 4 random colors + foreach ($n in 1..(Get-Random -Min 2 -Max 4)) { + "#{0:x6}" -f (Get-Random -Max 0xffffff) + } + ) | gradient + # Generate a minimal page with the gradient + @( + + "" + ) > ./randomgradient.html + .EXAMPLE + # Make a page with a random conic gradient + + $gradient = @( + "conic" + # Make 2 to 4 random colors + foreach ($n in 1..(Get-Random -Min 2 -Max 4)) { + "#{0:x6}" -f (Get-Random -Max 0xffffff) + } + ) | gradient + # Generate a minimal page with the gradient + @( + + "" + ) > ./randomconicgradient.html + .EXAMPLE + # Make a page with a random linear gradient + + $gradient = @( + "linear" + # Make 2 to 4 random colors + foreach ($n in 1..(Get-Random -Min 2 -Max 4)) { + "#{0:x6}" -f (Get-Random -Max 0xffffff) + } + ) | gradient + # Generate a minimal page with the gradient + @( + + "" + ) > ./randomlineargradient.html + .EXAMPLE + '#4488ff', '#224488' | Gradient # We can pipe into gradient + + #> + [Alias('Gradient')] + param() + + # All this function does is gather all of the input and arguments + $allIn = @($input) + @($args) + + # and create a custom object. + [PSCustomObject]@{ + PSTypeName = 'Gradient' + Input = $allIn + } + + # This allows us to accept any input, and modify the gradient after it has been created. + + # The implementation of the Gradient logic is in PowerShell extended types. +} diff --git a/Examples/ConicGradient.html b/Examples/ConicGradient.html new file mode 100644 index 0000000..549c86f --- /dev/null +++ b/Examples/ConicGradient.html @@ -0,0 +1,3 @@ + diff --git a/Examples/ConicGradient.html.ps1 b/Examples/ConicGradient.html.ps1 new file mode 100644 index 0000000..99713b7 --- /dev/null +++ b/Examples/ConicGradient.html.ps1 @@ -0,0 +1,17 @@ +<# +.SYNOPSIS + Simple Conic Gradient Example +.DESCRIPTION + Creates a page with a simple conic gradient +.EXAMPLE + ./ConicGradient.html.ps1 > ./ConicGradient.html +#> +if ($PSScriptRoot) { Push-Location $PSScriptRoot} + +$gradient = Gradient 'conic' '#4488ff', '#224488' + +"" + +if ($PSScriptRoot) { Pop-Location } diff --git a/Examples/Gradient.html b/Examples/Gradient.html new file mode 100644 index 0000000..b842bae --- /dev/null +++ b/Examples/Gradient.html @@ -0,0 +1,3 @@ + diff --git a/Examples/Gradient.html.ps1 b/Examples/Gradient.html.ps1 new file mode 100644 index 0000000..510cf12 --- /dev/null +++ b/Examples/Gradient.html.ps1 @@ -0,0 +1,17 @@ +<# +.SYNOPSIS + Simple Gradient Example +.DESCRIPTION + Creates a page with a simple gradient +.EXAMPLE + ./Gradient.html.ps1 > ./Gradient.html +#> +if ($PSScriptRoot) { Push-Location $PSScriptRoot} + +$gradient = Gradient '#4488ff', '#224488' + +"" + +if ($PSScriptRoot) { Pop-Location } diff --git a/Examples/LinearGradient.html b/Examples/LinearGradient.html new file mode 100644 index 0000000..598a79c --- /dev/null +++ b/Examples/LinearGradient.html @@ -0,0 +1,3 @@ + diff --git a/Examples/LinearGradient.html.ps1 b/Examples/LinearGradient.html.ps1 new file mode 100644 index 0000000..3ae3f04 --- /dev/null +++ b/Examples/LinearGradient.html.ps1 @@ -0,0 +1,17 @@ +<# +.SYNOPSIS + Simple Linear Gradient Example +.DESCRIPTION + Creates a page with a simple linear gradient +.EXAMPLE + ./LinearGradient.html.ps1 > ./LinearGradient.html +#> +if ($PSScriptRoot) { Push-Location $PSScriptRoot} + +$gradient = Gradient 'linear' '#4488ff', '#224488' + +"" + +if ($PSScriptRoot) { Pop-Location } diff --git a/Examples/RandomConicGradient.html b/Examples/RandomConicGradient.html new file mode 100644 index 0000000..17859b2 --- /dev/null +++ b/Examples/RandomConicGradient.html @@ -0,0 +1,3 @@ + diff --git a/Examples/RandomConicGradient.html.ps1 b/Examples/RandomConicGradient.html.ps1 new file mode 100644 index 0000000..55eac5b --- /dev/null +++ b/Examples/RandomConicGradient.html.ps1 @@ -0,0 +1,24 @@ +<# +.SYNOPSIS + Random Conic Gradient Example +.DESCRIPTION + Creates a page with a random gradient +.EXAMPLE + ./RandomConicGradient.html.ps1 > ./RandomConicGradient.html +#> +param([byte]$GradientColorMax = 4) +if ($PSScriptRoot) { Push-Location $PSScriptRoot} + +# Make a random 2-N color gradient +$gradient = @( + 'conic' + foreach ($n in 1..(Get-Random -Min 2 -Max $GradientColorMax)) { + "#{0:x6}" -f (Get-Random -Max 0xffffff) + } +) | Gradient + +"" + +if ($PSScriptRoot) { Pop-Location } diff --git a/Examples/RandomGradient.html b/Examples/RandomGradient.html new file mode 100644 index 0000000..2d55a90 --- /dev/null +++ b/Examples/RandomGradient.html @@ -0,0 +1,3 @@ + diff --git a/Examples/RandomGradient.html.ps1 b/Examples/RandomGradient.html.ps1 new file mode 100644 index 0000000..776fc32 --- /dev/null +++ b/Examples/RandomGradient.html.ps1 @@ -0,0 +1,23 @@ +<# +.SYNOPSIS + Random Gradient Example +.DESCRIPTION + Creates a page with a random gradient +.EXAMPLE + ./RandomGradient.html.ps1 > ./RandomGradient.html +#> +param([byte]$GradientColorMax = 4) +if ($PSScriptRoot) { Push-Location $PSScriptRoot} + +# Make a random 2-N color gradient +$gradient = @( + foreach ($n in 1..(Get-Random -Min 2 -Max $GradientColorMax)) { + "#{0:x6}" -f (Get-Random -Max 0xffffff) + } +) | Gradient + +"" + +if ($PSScriptRoot) { Pop-Location } diff --git a/Examples/RandomLinearGradient.html b/Examples/RandomLinearGradient.html new file mode 100644 index 0000000..e8ab94a --- /dev/null +++ b/Examples/RandomLinearGradient.html @@ -0,0 +1,3 @@ + diff --git a/Examples/RandomLinearGradient.html.ps1 b/Examples/RandomLinearGradient.html.ps1 new file mode 100644 index 0000000..0c72de0 --- /dev/null +++ b/Examples/RandomLinearGradient.html.ps1 @@ -0,0 +1,24 @@ +<# +.SYNOPSIS + Random Linear Gradient Example +.DESCRIPTION + Creates a page with a random gradient +.EXAMPLE + ./RandomLinearGradient.html.ps1 > ./RandomLinearGradient.html +#> +param([byte]$GradientColorMax = 4) +if ($PSScriptRoot) { Push-Location $PSScriptRoot} + +# Make a random 2-N color gradient +$gradient = @( + 'linear' + foreach ($n in 1..(Get-Random -Min 2 -Max $GradientColorMax)) { + "#{0:x6}" -f (Get-Random -Max 0xffffff) + } +) | Gradient + +"" + +if ($PSScriptRoot) { Pop-Location } diff --git a/Gradient.psd1 b/Gradient.psd1 new file mode 100644 index 0000000..b6b8510 --- /dev/null +++ b/Gradient.psd1 @@ -0,0 +1,156 @@ +# +# Module manifest for module 'Gradient' +# +# Generated by: James Brundage +# +# Generated on: 12/28/2025 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'Gradient.psm1' + +# Version number of this module. +ModuleVersion = '0.1' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = 'ca7b1e98-616f-4414-bd9a-22f2ab77789d' + +# Author of this module +Author = 'James Brundage' + +# Company or vendor of this module +CompanyName = 'Start Automating' + +# Copyright statement for this module +Copyright = '2025 Start Automating' + +# Description of the functionality provided by this module +Description = 'CSS and SVG Gradient Generator' + +# Minimum version of the PowerShell engine required by this module +# PowerShellVersion = '' + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +TypesToProcess = "Gradient.types.ps1xml" + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# NestedModules = @() + +# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. +FunctionsToExport = 'Get-Gradient' + +# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. +# CmdletsToExport = '*' + +# Variables to export from this module + +# VariablesToExport = '*' + +# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. +AliasesToExport = 'Gradient' + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# FileList = @() + +# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = @('Gradient','CSS','SVG', 'GradientGenerator') + + # A URL to the license for this module. + LicenseUri = 'https://github.com/PowerShellWeb/Gradient/blob/main/LICENSE' + + # A URL to the main website for this project. + ProjectUri = 'https://github.com/PowerShellWeb/Gradient' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + ReleaseNotes = @' +## Gradient 0.1: + +> Initial Gradient Module (#1) + +* `Get-Gradient` (aka `Gradient`) gets gradients (#3) +* It returns `Gradient` objects, using the `.Input` + * Have a `.CSS` property (#4) + * Hava a `.SVG` property (#5) + * Have a `.GradientTypePattern` (#15) + * Can get a `.GradientType` (#16) (defaulting to `radial-gradient`) + * Will `.ToString()` to `.CSS` (#17) + * Displays Input and CSS by default (#18) +* Basic Module Scaffolding + * GitHub Workflow (#2) + * GitHub Action (#7) + * Tests (#8) + * Sponsorship (#14) + * Code of Conduct (#9) + * Contributing Guide (#10) + * Security notice (#11) +* Also, initial examples are in `/Examples` (#13) + +'@ + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + diff --git a/Gradient.psm1 b/Gradient.psm1 new file mode 100644 index 0000000..b0d0bb8 --- /dev/null +++ b/Gradient.psm1 @@ -0,0 +1,6 @@ +$commandsPath = Join-Path $PSScriptRoot Commands +foreach ($file in Get-ChildItem -File -Recurse -Path $commandsPath) { + if ($file.Extension -ne '.ps1') { continue } + if ($file.Name -like '*.*.ps1') { continue } + . $file.FullName +} \ No newline at end of file diff --git a/Gradient.tests.ps1 b/Gradient.tests.ps1 new file mode 100644 index 0000000..bcf3795 --- /dev/null +++ b/Gradient.tests.ps1 @@ -0,0 +1,17 @@ +describe Gradient { + it 'Generates gradients' { + $gradient = '#4488ff', '#224488' | Gradient + "$gradient" | Should -Match '^radial-gradient\(.+?\)' + } + it 'Can stack gradients by joining them with commas' { + $gradients = @( + 'linear', '217deg', 'rgb(255 0 0 / 80%)', 'transparent 70.71%' | Gradient + 'linear', '127deg', 'rgb(0 255 0 / 80%)', 'transparent 70.71%' | Gradient + 'linear', '336deg', 'rgb(0 0 255 / 80%)', 'transparent 70.71%' | Gradient + ) + + $($gradients -join ',') | Should -Match 'linear-gradient.+?,' + + } + +} diff --git a/Gradient.types.ps1xml b/Gradient.types.ps1xml new file mode 100644 index 0000000..3399359 --- /dev/null +++ b/Gradient.types.ps1xml @@ -0,0 +1,167 @@ + + + + Gradient + + + PSStandardMembers + + + DefaultDisplayPropertySet + + Input + CSS + + + + + + GradientTypes + GradientType + + + ToString + + + + CSS + + <# +.SYNOPSIS + Gets Gradient CSS +.DESCRIPTION + Gets the Gradient as CSS. +.EXAMPLE + gradient '#4488ff', '#224488' | + Select-Object -Expand CSS +#> +param() +# Get our gradient type +$gradientTypes = $this.GradientTypes +$gradientValues = @(foreach ($in in $this.input) { + if ($in -notmatch $this.GradientTypePattern) { + $in + } +}) + +if (-not $gradientTypes) { $gradientTypes = 'radial-gradient'} +@(foreach ($gradientType in $gradientTypes) { + "$gradientType($( + @( + $gradientValues + ) -join ', ' + ))" +}) -join ', ' + + + + GradientType + + <# +.SYNOPSIS + Gets the Gradient Type +.DESCRIPTION + Gets the Gradient Types found in the Gradient's input. + + If no type is found, radial-gradient is the default. +#> + +$foundTypes = @(foreach ($in in $this.input) { + if ($in -match $this.GradientTypePattern) { + $in -replace '(?:-gradient)?$', '-gradient' + } +}) + +if (-not $foundTypes) { + $foundTypes = @('radial-gradient') +} +return $foundTypes + + + + + + GradientTypePattern + + <# +.SYNOPSIS + Gets the gradient type pattern +.DESCRIPTION + Gets the regular expression used to identify a gradient type +.EXAMPLE + Get-Gradiant '#4488ff' ,'#224488' | + Select-Object -Expand GradientTypePattern +#> +'(?:repeating-)?(?>conic|linear|radial)(?:-gradient)?$' + + + + SVG + + <# +.SYNOPSIS + Gets a SVG gradient +.DESCRIPTION + Gets the gradient as SVG. + + SVG Gradients are a bit more picky than their CSS counterpart, + and do not support conic gradients. + +#> +$gradientTypeHint = $this.GradientType +if (-not $gradientTypeHint) { + $gradientTypeHint = 'radial' +} +if ($gradientTypeHint) { + $gradientTypeHint = $gradientTypeHint -replace '-gradient', 'Gradient' + $gradientTypeHint = $gradientTypeHint.Substring(0,1).ToLower() + $gradientTypeHint.Substring(1) +} +# AFAIK, SVG does not support conic gradients. +if ($gradientTypeHint -match 'conic') { return } +$gradientValues = @(foreach ($in in $this.input) { + if ($in -match '\#[a-f0-9]{6}') { + $matches.0 + } +}) +if (-not $gradientValues) { return } +if ($gradientValues.Count -eq 1) { + $gradientValues = @($gradientValues) * 2 +} + +# Now we have at least two colors we want to be a gradient +# We need to make sure the offset starts at 0% an ends at 100% +# and so we actually need to divide by one less than our fill color, so we end at 100%. +$offsetStep = 1 / ($gradientValues.Count - 1) + +@( + # Construct our gradient element. + "<${gradientTypeHint}$( + # propagate our attributes + " id='${gradientTypeHint}-$($gradientValues -replace '#' -join '-')'" + )>" + @( + # and put in our stop colors + for ($StopNumber = 0; $StopNumber -lt $gradientValues.Count; $StopNumber++) { + "<stop offset='$($offsetStep * $StopNumber * 100)%' stop-color='$($gradientValues[$StopNumber])' />" + } + ) + "</${gradientTypeHint}>" +) -join [Environment]::NewLine + + + + DefaultDisplay + Input +CSS + + + + \ No newline at end of file diff --git a/Types/Gradient/Alias.psd1 b/Types/Gradient/Alias.psd1 new file mode 100644 index 0000000..7898200 --- /dev/null +++ b/Types/Gradient/Alias.psd1 @@ -0,0 +1,3 @@ +@{ + GradientTypes = 'GradientType' +} \ No newline at end of file diff --git a/Types/Gradient/DefaultDisplay.txt b/Types/Gradient/DefaultDisplay.txt new file mode 100644 index 0000000..d1e852a --- /dev/null +++ b/Types/Gradient/DefaultDisplay.txt @@ -0,0 +1,2 @@ +Input +CSS \ No newline at end of file diff --git a/Types/Gradient/ToString.ps1 b/Types/Gradient/ToString.ps1 new file mode 100644 index 0000000..116b586 --- /dev/null +++ b/Types/Gradient/ToString.ps1 @@ -0,0 +1,7 @@ +<# +.SYNOPSIS + Stringifies the gradient +.DESCRIPTION + Gets the gradient as a string. By default, returns the CSS gradient. +#> +return "$($this.CSS)" \ No newline at end of file diff --git a/Types/Gradient/get_CSS.ps1 b/Types/Gradient/get_CSS.ps1 new file mode 100644 index 0000000..2eb8927 --- /dev/null +++ b/Types/Gradient/get_CSS.ps1 @@ -0,0 +1,26 @@ +<# +.SYNOPSIS + Gets Gradient CSS +.DESCRIPTION + Gets the Gradient as CSS. +.EXAMPLE + gradient '#4488ff', '#224488' | + Select-Object -Expand CSS +#> +param() +# Get our gradient type +$gradientTypes = $this.GradientTypes +$gradientValues = @(foreach ($in in $this.input) { + if ($in -notmatch $this.GradientTypePattern) { + $in + } +}) + +if (-not $gradientTypes) { $gradientTypes = 'radial-gradient'} +@(foreach ($gradientType in $gradientTypes) { + "$gradientType($( + @( + $gradientValues + ) -join ', ' + ))" +}) -join ', ' \ No newline at end of file diff --git a/Types/Gradient/get_GradientType.ps1 b/Types/Gradient/get_GradientType.ps1 new file mode 100644 index 0000000..035046e --- /dev/null +++ b/Types/Gradient/get_GradientType.ps1 @@ -0,0 +1,20 @@ +<# +.SYNOPSIS + Gets the Gradient Type +.DESCRIPTION + Gets the Gradient Types found in the Gradient's input. + + If no type is found, radial-gradient is the default. +#> + +$foundTypes = @(foreach ($in in $this.input) { + if ($in -match $this.GradientTypePattern) { + $in -replace '(?:-gradient)?$', '-gradient' + } +}) + +if (-not $foundTypes) { + $foundTypes = @('radial-gradient') +} +return $foundTypes + diff --git a/Types/Gradient/get_GradientTypePattern.ps1 b/Types/Gradient/get_GradientTypePattern.ps1 new file mode 100644 index 0000000..393be56 --- /dev/null +++ b/Types/Gradient/get_GradientTypePattern.ps1 @@ -0,0 +1,10 @@ +<# +.SYNOPSIS + Gets the gradient type pattern +.DESCRIPTION + Gets the regular expression used to identify a gradient type +.EXAMPLE + Get-Gradiant '#4488ff' ,'#224488' | + Select-Object -Expand GradientTypePattern +#> +'(?:repeating-)?(?>conic|linear|radial)(?:-gradient)?$' \ No newline at end of file diff --git a/Types/Gradient/get_SVG.ps1 b/Types/Gradient/get_SVG.ps1 new file mode 100644 index 0000000..023c7dd --- /dev/null +++ b/Types/Gradient/get_SVG.ps1 @@ -0,0 +1,49 @@ +<# +.SYNOPSIS + Gets a SVG gradient +.DESCRIPTION + Gets the gradient as SVG. + + SVG Gradients are a bit more picky than their CSS counterpart, + and do not support conic gradients. + +#> +$gradientTypeHint = $this.GradientType +if (-not $gradientTypeHint) { + $gradientTypeHint = 'radial' +} +if ($gradientTypeHint) { + $gradientTypeHint = $gradientTypeHint -replace '-gradient', 'Gradient' + $gradientTypeHint = $gradientTypeHint.Substring(0,1).ToLower() + $gradientTypeHint.Substring(1) +} +# AFAIK, SVG does not support conic gradients. +if ($gradientTypeHint -match 'conic') { return } +$gradientValues = @(foreach ($in in $this.input) { + if ($in -match '\#[a-f0-9]{6}') { + $matches.0 + } +}) +if (-not $gradientValues) { return } +if ($gradientValues.Count -eq 1) { + $gradientValues = @($gradientValues) * 2 +} + +# Now we have at least two colors we want to be a gradient +# We need to make sure the offset starts at 0% an ends at 100% +# and so we actually need to divide by one less than our fill color, so we end at 100%. +$offsetStep = 1 / ($gradientValues.Count - 1) + +@( + # Construct our gradient element. + "<${gradientTypeHint}$( + # propagate our attributes + " id='${gradientTypeHint}-$($gradientValues -replace '#' -join '-')'" + )>" + @( + # and put in our stop colors + for ($StopNumber = 0; $StopNumber -lt $gradientValues.Count; $StopNumber++) { + "" + } + ) + "" +) -join [Environment]::NewLine \ No newline at end of file diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..c8e6670 --- /dev/null +++ b/action.yml @@ -0,0 +1,444 @@ + +name: UseGradient +description: Gradient Generator +inputs: + Run: + required: false + description: | + A PowerShell Script that uses Gradient. + Any files outputted from the script will be added to the repository. + If those files have a .Message attached to them, they will be committed with that message. + SkipScriptFile: + required: false + description: 'If set, will not process any files named *.Gradient.ps1' + InstallModule: + required: false + description: A list of modules to be installed from the PowerShell gallery before scripts run. + CommitMessage: + required: false + description: If provided, will commit any remaining changes made to the workspace with this commit message. + TargetBranch: + required: false + description: | + If provided, will checkout a new branch before making the changes. + If not provided, will use the current branch. + ActionScript: + required: false + description: The name of one or more scripts to run, from this action's path. + GitHubToken: + required: false + default: '{{ secrets.GITHUB_TOKEN }}' + description: The github token to use for requests. + UserEmail: + required: false + description: The user email associated with a git commit. If this is not provided, it will be set to the username@noreply.github.com. + UserName: + required: false + description: The user name associated with a git commit. + NoPush: + required: false + description: | + If set, will not push any changes made to the repository. + (they will still be committed if `-AutoCommit` is passed) + AutoCommit: + required: false + description: | + If set, will commit any changes made to the repository. + (this also implies `-NoPush`) +branding: + icon: chevron-right + color: blue +runs: + using: composite + steps: + - name: GradientAction + id: GradientAction + shell: pwsh + env: + TargetBranch: ${{inputs.TargetBranch}} + Run: ${{inputs.Run}} + ActionScript: ${{inputs.ActionScript}} + AutoCommit: ${{inputs.AutoCommit}} + SkipScriptFile: ${{inputs.SkipScriptFile}} + UserName: ${{inputs.UserName}} + InstallModule: ${{inputs.InstallModule}} + NoPush: ${{inputs.NoPush}} + GitHubToken: ${{inputs.GitHubToken}} + CommitMessage: ${{inputs.CommitMessage}} + UserEmail: ${{inputs.UserEmail}} + run: | + $Parameters = @{} + $Parameters.Run = ${env:Run} + $Parameters.SkipScriptFile = ${env:SkipScriptFile} + $Parameters.SkipScriptFile = $parameters.SkipScriptFile -match 'true'; + $Parameters.InstallModule = ${env:InstallModule} + $Parameters.InstallModule = $parameters.InstallModule -split ';' -replace '^[''"]' -replace '[''"]$' + $Parameters.CommitMessage = ${env:CommitMessage} + $Parameters.TargetBranch = ${env:TargetBranch} + $Parameters.ActionScript = ${env:ActionScript} + $Parameters.ActionScript = $parameters.ActionScript -split ';' -replace '^[''"]' -replace '[''"]$' + $Parameters.GitHubToken = ${env:GitHubToken} + $Parameters.UserEmail = ${env:UserEmail} + $Parameters.UserName = ${env:UserName} + $Parameters.NoPush = ${env:NoPush} + $Parameters.NoPush = $parameters.NoPush -match 'true'; + $Parameters.AutoCommit = ${env:AutoCommit} + $Parameters.AutoCommit = $parameters.AutoCommit -match 'true'; + foreach ($k in @($parameters.Keys)) { + if ([String]::IsNullOrEmpty($parameters[$k])) { + $parameters.Remove($k) + } + } + Write-Host "::debug:: GradientAction $(@(foreach ($p in $Parameters.GetEnumerator()) {'-' + $p.Key + ' ' + $p.Value}) -join ' ')" + & {<# + .Synopsis + GitHub Action for Gradient + .Description + GitHub Action for Gradient. This will: + + * Import Gradient + * If `-Run` is provided, run that script + * Otherwise, unless `-SkipScriptFile` is passed, run all *.Gradient.ps1 files beneath the workflow directory + * If any `-ActionScript` was provided, run scripts from the action path that match a wildcard pattern. + + If you will be making changes using the GitHubAPI, you should provide a -GitHubToken + + If none is provided, and ENV:GITHUB_TOKEN is set, this will be used instead. + + Any files changed can be outputted by the script. + Those changes can be checked back into the repo if `-AutoCommit` is set. + #> + + param( + # A PowerShell Script that uses Gradient. + # Any files outputted from the script will be added to the repository. + # If those files have a .Message attached to them, they will be committed with that message. + [string] + $Run, + + # If set, will not process any files named *.Gradient.ps1 + [switch] + $SkipScriptFile, + + # A list of modules to be installed from the PowerShell gallery before scripts run. + [string[]] + $InstallModule, + + # If provided, will commit any remaining changes made to the workspace with this commit message. + [string] + $CommitMessage, + + # If provided, will checkout a new branch before making the changes. + # If not provided, will use the current branch. + [string] + $TargetBranch, + + # The name of one or more scripts to run, from this action's path. + [string[]] + $ActionScript, + + # The github token to use for requests. + [string] + $GitHubToken = '{{ secrets.GITHUB_TOKEN }}', + + # The user email associated with a git commit. If this is not provided, it will be set to the username@noreply.github.com. + [string] + $UserEmail, + + # The user name associated with a git commit. + [string] + $UserName, + + # If set, will not push any changes made to the repository. + # (they will still be committed if `-AutoCommit` is passed) + [switch] + $NoPush, + + # If set, will commit any changes made to the repository. + # (this also implies `-NoPush`) + [switch] + $AutoCommit + ) + + $ErrorActionPreference = 'continue' + "::group::Parameters" | Out-Host + [PSCustomObject]$PSBoundParameters | Format-List | Out-Host + "::endgroup::" | Out-Host + + $gitHubEventJson = [IO.File]::ReadAllText($env:GITHUB_EVENT_PATH) + $gitHubEvent = + if ($env:GITHUB_EVENT_PATH) { + $gitHubEventJson | ConvertFrom-Json + } else { $null } + "::group::Parameters" | Out-Host + $gitHubEvent | Format-List | Out-Host + "::endgroup::" | Out-Host + + + $anyFilesChanged = $false + $ActionModuleName = 'Gradient' + $actorInfo = $null + + + $checkDetached = git symbolic-ref -q HEAD + if ($LASTEXITCODE) { + "::warning::On detached head, skipping action" | Out-Host + exit 0 + } + + function InstallActionModule { + param([string]$ModuleToInstall) + $moduleInWorkspace = Get-ChildItem -Path $env:GITHUB_WORKSPACE -Recurse -File | + Where-Object Name -eq "$($moduleToInstall).psd1" | + Where-Object { + $(Get-Content $_.FullName -Raw) -match 'ModuleVersion' + } + if (-not $moduleInWorkspace) { + $availableModules = Get-Module -ListAvailable + if ($availableModules.Name -notcontains $moduleToInstall) { + Install-Module $moduleToInstall -Scope CurrentUser -Force -AcceptLicense -AllowClobber + } + Import-Module $moduleToInstall -Force -PassThru | Out-Host + } else { + Import-Module $moduleInWorkspace.FullName -Force -PassThru | Out-Host + } + } + function ImportActionModule { + #region -InstallModule + if ($InstallModule) { + "::group::Installing Modules" | Out-Host + foreach ($moduleToInstall in $InstallModule) { + InstallActionModule -ModuleToInstall $moduleToInstall + } + "::endgroup::" | Out-Host + } + #endregion -InstallModule + + if ($env:GITHUB_ACTION_PATH) { + $LocalModulePath = Join-Path $env:GITHUB_ACTION_PATH "$ActionModuleName.psd1" + if (Test-path $LocalModulePath) { + Import-Module $LocalModulePath -Force -PassThru | Out-String + } else { + throw "Module '$ActionModuleName' not found" + } + } elseif (-not (Get-Module $ActionModuleName)) { + throw "Module '$ActionModuleName' not found" + } + + "::notice title=ModuleLoaded::$ActionModuleName Loaded from Path - $($LocalModulePath)" | Out-Host + if ($env:GITHUB_STEP_SUMMARY) { + "# $($ActionModuleName)" | + Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + } + function InitializeAction { + #region Custom + #endregion Custom + + # Configure git based on the $env:GITHUB_ACTOR + if (-not $UserName) { $UserName = $env:GITHUB_ACTOR } + if (-not $actorID) { $actorID = $env:GITHUB_ACTOR_ID } + + $actorInfo = + Invoke-RestMethod -Uri "https://api.github.com/user/$actorID" + + + if (-not $UserEmail) { $UserEmail = "$UserName@noreply.github.com" } + git config --global user.email $UserEmail + git config --global user.name $actorInfo.name + + # Pull down any changes + git pull | Out-Host + + if ($TargetBranch) { + "::notice title=Expanding target branch string $targetBranch" | Out-Host + $TargetBranch = $ExecutionContext.SessionState.InvokeCommand.ExpandString($TargetBranch) + "::notice title=Checking out target branch::$targetBranch" | Out-Host + git checkout -b $TargetBranch | Out-Host + git pull | Out-Host + } + } + + function InvokeActionModule { + $myScriptStart = [DateTime]::Now + $myScript = $ExecutionContext.SessionState.PSVariable.Get("Run").Value + if ($myScript) { + Invoke-Expression -Command $myScript | + . ProcessOutput | + Out-Host + return + } + $myScriptTook = [Datetime]::Now - $myScriptStart + $MyScriptFilesStart = [DateTime]::Now + + $myScriptList = @() + $shouldSkip = $ExecutionContext.SessionState.PSVariable.Get("SkipScriptFile").Value + if ($shouldSkip) { + return + } + $scriptFiles = @( + Get-ChildItem -Recurse -Path $env:GITHUB_WORKSPACE | + Where-Object Name -Match "\.$($ActionModuleName)\.ps1$" + if ($ActionScript) { + if ($ActionScript -match '^\s{0,}/' -and $ActionScript -match '/\s{0,}$') { + $ActionScriptPattern = $ActionScript.Trim('/').Trim() -as [regex] + if ($ActionScriptPattern) { + $ActionScriptPattern = [regex]::new($ActionScript.Trim('/').Trim(), 'IgnoreCase,IgnorePatternWhitespace', [timespan]::FromSeconds(0.5)) + Get-ChildItem -Recurse -Path $env:GITHUB_ACTION_PATH | + Where-Object { $_.Name -Match "\.$($ActionModuleName)\.ps1$" -and $_.FullName -match $ActionScriptPattern } + } + } else { + Get-ChildItem -Recurse -Path $env:GITHUB_ACTION_PATH | + Where-Object Name -Match "\.$($ActionModuleName)\.ps1$" | + Where-Object FullName -Like $ActionScript + } + } + ) | Select-Object -Unique + $scriptFiles | + ForEach-Object -Begin { + if ($env:GITHUB_STEP_SUMMARY) { + "## $ActionModuleName Scripts" | + Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + } -Process { + $myScriptList += $_.FullName.Replace($env:GITHUB_WORKSPACE, '').TrimStart('/') + $myScriptCount++ + $scriptFile = $_ + if ($env:GITHUB_STEP_SUMMARY) { + "### $($scriptFile.Fullname -replace [Regex]::Escape($env:GITHUB_WORKSPACE))" | + Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + $scriptCmd = $ExecutionContext.SessionState.InvokeCommand.GetCommand($scriptFile.FullName, 'ExternalScript') + foreach ($requiredModule in $CommandInfo.ScriptBlock.Ast.ScriptRequirements.RequiredModules) { + if ($requiredModule.Name -and + (-not $requiredModule.MaximumVersion) -and + (-not $requiredModule.RequiredVersion) + ) { + InstallActionModule $requiredModule.Name + } + } + Push-Location $scriptFile.Directory.Fullname + $scriptFileOutputs = . $scriptCmd + $scriptFileOutputs | + . ProcessOutput | + Out-Host + Pop-Location + } + + $MyScriptFilesTook = [Datetime]::Now - $MyScriptFilesStart + $SummaryOfMyScripts = "$myScriptCount $ActionModuleName scripts took $($MyScriptFilesTook.TotalSeconds) seconds" + $SummaryOfMyScripts | + Out-Host + if ($env:GITHUB_STEP_SUMMARY) { + $SummaryOfMyScripts | + Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + #region Custom + #endregion Custom + } + + function OutError { + $anyRuntimeExceptions = $false + foreach ($err in $error) { + $errParts = @( + "::error " + @( + if ($err.InvocationInfo.ScriptName) { + "file=$($err.InvocationInfo.ScriptName)" + } + if ($err.InvocationInfo.ScriptLineNumber -ge 1) { + "line=$($err.InvocationInfo.ScriptLineNumber)" + if ($err.InvocationInfo.OffsetInLine -ge 1) { + "col=$($err.InvocationInfo.OffsetInLine)" + } + } + if ($err.CategoryInfo.Activity) { + "title=$($err.CategoryInfo.Activity)" + } + ) -join ',' + "::" + $err.Exception.Message + if ($err.CategoryInfo.Category -eq 'OperationStopped' -and + $err.CategoryInfo.Reason -eq 'RuntimeException') { + $anyRuntimeExceptions = $true + } + ) -join '' + $errParts | Out-Host + if ($anyRuntimeExceptions) { + exit 1 + } + } + } + + function PushActionOutput { + if ($anyFilesChanged) { + "::notice::$($anyFilesChanged) Files Changed" | Out-Host + } + if ($CommitMessage -or $anyFilesChanged) { + if ($CommitMessage) { + Get-ChildItem $env:GITHUB_WORKSPACE -Recurse | + ForEach-Object { + $gitStatusOutput = git status $_.Fullname -s + if ($gitStatusOutput) { + git add $_.Fullname + } + } + + git commit -m $ExecutionContext.SessionState.InvokeCommand.ExpandString($CommitMessage) + } + + $checkDetached = git symbolic-ref -q HEAD + if (-not $LASTEXITCODE -and -not $NoPush -and $AutoCommit) { + if ($TargetBranch -and $anyFilesChanged) { + "::notice::Pushing Changes to $targetBranch" | Out-Host + git push --set-upstream origin $TargetBranch + } elseif ($anyFilesChanged) { + "::notice::Pushing Changes" | Out-Host + git push + } + "Git Push Output: $($gitPushed | Out-String)" + } else { + "::notice::Not pushing changes (on detached head)" | Out-Host + $LASTEXITCODE = 0 + exit 0 + } + } + } + + filter ProcessOutput { + $out = $_ + $outItem = Get-Item -Path $out -ErrorAction Ignore + if (-not $outItem -and $out -is [string]) { + $out | Out-Host + if ($env:GITHUB_STEP_SUMMARY) { + "> $out" | Out-File -Append -FilePath $env:GITHUB_STEP_SUMMARY + } + return + } + $fullName, $shouldCommit = + if ($out -is [IO.FileInfo]) { + $out.FullName, (git status $out.Fullname -s) + } elseif ($outItem) { + $outItem.FullName, (git status $outItem.Fullname -s) + } + if ($shouldCommit -and $AutoCommit) { + "$fullName has changed, and should be committed" | Out-Host + git add $fullName + if ($out.Message) { + git commit -m "$($out.Message)" | Out-Host + } elseif ($out.CommitMessage) { + git commit -m "$($out.CommitMessage)" | Out-Host + } elseif ($gitHubEvent.head_commit.message) { + git commit -m "$($gitHubEvent.head_commit.message)" | Out-Host + } + $anyFilesChanged = $true + } + $out + } + + . ImportActionModule + . InitializeAction + . InvokeActionModule + . PushActionOutput + . OutError} @Parameters + diff --git a/code_of_conduct.md b/code_of_conduct.md new file mode 100644 index 0000000..e409748 --- /dev/null +++ b/code_of_conduct.md @@ -0,0 +1 @@ +Try to be a good person, and please be polite when providing feedback. \ No newline at end of file diff --git a/contributing.md b/contributing.md new file mode 100644 index 0000000..22def8b --- /dev/null +++ b/contributing.md @@ -0,0 +1,3 @@ +Contributions are welcome. + +If you can think of anything worthwhile to contribute, please file an issue or start a discussion. diff --git a/security.md b/security.md new file mode 100644 index 0000000..fa6ae3f --- /dev/null +++ b/security.md @@ -0,0 +1 @@ +This module has no special security considerations.