feat: some basic action core logic

This commit is contained in:
Louis Seubert 2026-07-11 20:44:43 +02:00
commit 917952884e
Signed by: louis9902
GPG key ID: 4B9DB28F826553BD
7 changed files with 552 additions and 0 deletions

3
eng/Containerfile Normal file
View file

@ -0,0 +1,3 @@
FROM mcr.microsoft.com/dotnet/sdk:10.0
WORKDIR /work
COPY ./src /work

View file

@ -0,0 +1,37 @@
@{
RootModule = 'Geekeey.Build.Pwsh.psm1'
ModuleVersion = '1.0.0'
GUID = '234baa68-d26f-4bf9-996d-45ec3520cb95'
Author = 'The Geekeey Authors'
CompanyName = 'Geekeey'
Copyright = '(c) The Geekeey Authors. Licensed under EUPL-1.2.'
Description = 'Forgejo Actions helper functions for PowerShell.'
FunctionsToExport = @(
'Set-ActionOutput',
'Set-ActionEnv',
'Add-ActionPath',
'Add-ActionMask',
'Add-ActionMatcher',
'Remove-ActionMatcher',
'Save-ActionState',
'Add-ActionStepSummary',
'Get-ActionInput',
'Write-ActionDebug',
'Write-ActionNotice',
'Write-ActionWarning',
'Write-ActionError',
'Stop-Action',
'Invoke-ForgejoApi',
'Get-ForgejoContext'
)
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
PowerShellVersion = '7.0'
PrivateData = @{
PSData = @{
LicenseUri = 'https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12'
Tags = @('Forgejo', 'Actions', 'CI')
}
}
}

488
src/Geekeey.Build.Pwsh.psm1 Normal file
View file

@ -0,0 +1,488 @@
# Copyright (c) The Geekeey Authors
# SPDX-License-Identifier: EUPL-1.2
Set-StrictMode -Version Latest
$Script:MULTILINE_DELIMITER = "234baa68-d26f-4bf9-996d-45ec3520cb95"
function Write-FileInstruction
{
param(
[Parameter(Mandatory)]
[string]$EnvVar,
[Parameter(Mandatory)]
[string]$Name,
[Parameter(Mandatory)]
[string]$Value
)
$filePath = [System.Environment]::GetEnvironmentVariable($EnvVar)
if ([string]::IsNullOrEmpty($filePath))
{
throw "$EnvVar is not set"
}
$content = "${Name}<<${MULTILINE_DELIMITER}`n${Value}`n${MULTILINE_DELIMITER}"
Add-Content -Path $filePath -Value $content
}
function Set-ActionOutput
{
<#
.SYNOPSIS
Sets an output variable for the workflow.
.PARAMETER Name
The name of the output variable.
.PARAMETER Value
The value to set.
#>
param(
[Parameter(Mandatory)]
[string]$Name,
[Parameter(Mandatory)]
[string]$Value
)
Write-FileInstruction -EnvVar "FORGEJO_OUTPUT" -Name $Name -Value $Value
}
function Set-ActionEnv
{
<#
.SYNOPSIS
Sets an environment variable for subsequent steps.
.PARAMETER Name
The name of the environment variable.
.PARAMETER Value
The value to set.
#>
param(
[Parameter(Mandatory)]
[string]$Name,
[Parameter(Mandatory)]
[string]$Value
)
Write-FileInstruction -EnvVar "FORGEJO_ENV" -Name $Name -Value $Value
}
function Add-ActionPath
{
<#
.SYNOPSIS
Adds a directory to the PATH for subsequent steps.
.PARAMETER Path
The directory path to add.
#>
param(
[Parameter(Mandatory)]
[string]$Path
)
$filePath = [System.Environment]::GetEnvironmentVariable("FORGEJO_PATH")
if ([string]::IsNullOrEmpty($filePath))
{
throw "FORGEJO_PATH is not set"
}
Add-Content -Path $filePath -Value $Path
}
function Save-ActionState
{
<#
.SYNOPSIS
Saves a state value for use in subsequent steps of the same job.
.PARAMETER Name
The name of the state variable.
.PARAMETER Value
The value to save.
#>
param(
[Parameter(Mandatory)]
[string]$Name,
[Parameter(Mandatory)]
[string]$Value
)
Write-FileInstruction -EnvVar "FORGEJO_STATE" -Name $Name -Value $Value
}
function Add-ActionStepSummary
{
<#
.SYNOPSIS
Writes a summary for the workflow run step.
.PARAMETER Summary
The markdown summary text.
#>
param(
[Parameter(Mandatory)]
[string]$Summary
)
$envPath = $env:FORGEJO_STEP_SUMMARY
if ([string]::IsNullOrEmpty($envPath))
{
throw "FORGEJO_STEP_SUMMARY is not set"
}
Add-Content -Path $envPath -Value $Summary
}
function Add-ActionMask
{
<#
.SYNOPSIS
Masks a value in log output. Future attempts to log this value will show "***".
.PARAMETER Value
The value to mask.
#>
param(
[Parameter(Mandatory)]
[string]$Value
)
Write-Host "::add-mask::$Value"
}
function Add-ActionMatcher
{
<#
.SYNOPSIS
Registers a problem matcher from a JSON file.
.PARAMETER Path
The path to the problem matcher JSON file.
#>
param(
[Parameter(Mandatory)]
[string]$Path
)
Write-Host "::add-matcher::$Path"
}
function Remove-ActionMatcher
{
<#
.SYNOPSIS
Removes a previously registered problem matcher by owner.
.PARAMETER Owner
The owner of the problem matcher to remove.
#>
param(
[Parameter(Mandatory)]
[string]$Owner
)
Write-Host "::remove-matcher owner=$Owner"
}
function Start-ActionGroup
{
<#
.SYNOPSIS
Starts an expandable group in the log.
.PARAMETER Title
The group title.
#>
param(
[Parameter(Mandatory)]
[string]$Title
)
Write-Host "::group::$Title"
}
function Stop-ActionGroup
{
<#
.SYNOPSIS
Ends the most recently started log group.
#>
Write-Host "::endgroup::"
}
function Set-ActionCommandEcho
{
<#
.SYNOPSIS
Enables or disables echoing of workflow commands to the log.
.PARAMETER Enabled
If $true, workflow commands are echoed; otherwise they are not.
#>
param(
[Parameter(Mandatory)]
[bool]$Enabled
)
$value = if ($Enabled) { "on" } else { "off" }
Write-Host "::echo::$value"
}
function Suspend-ActionCommands
{
<#
.SYNOPSIS
Stops processing of workflow commands until resumed.
.PARAMETER Token
A unique token used to later resume command processing.
#>
param(
[Parameter(Mandatory)]
[string]$Token
)
Write-Host "::stop-commands::$Token"
}
function Resume-ActionCommands
{
<#
.SYNOPSIS
Resumes processing of workflow commands.
.PARAMETER Token
The same token previously passed to Suspend-ActionCommands.
#>
param(
[Parameter(Mandatory)]
[string]$Token
)
Write-Host "::$Token::"
}
function Get-ActionInput
{
<#
.SYNOPSIS
Gets an input value by name.
.PARAMETER Name
The input name (spaces are replaced with underscores, uppercased).
.PARAMETER Required
If $true, throws when the input is not set.
#>
param(
[Parameter(Mandatory)]
[string]$Name,
[switch]$Required
)
$value = [System.Environment]::GetEnvironmentVariable("INPUT_$($Name.Replace(' ', '_').ToUpper())")?.Trim()
if ($Required -and [string]::IsNullOrEmpty($value))
{
Stop-Action "Input '$Name' is required but not provided."
}
return $value
}
function Get-ActionState
{
<#
.SYNOPSIS
Gets a state value saved by Save-ActionState.
.PARAMETER Name
The name of the state variable.
#>
param(
[Parameter(Mandatory)]
[string]$Name
)
$value = [System.Environment]::GetEnvironmentVariable("STATE_$($Name.Replace(' ', '_').ToUpper())")?.Trim()
return $value
}
function Test-ActionDebug
{
<#
.SYNOPSIS
Returns $true when debug logging is enabled (RUNNER_DEBUG=1).
#>
$value = [System.Environment]::GetEnvironmentVariable("RUNNER_DEBUG")
return $value -eq "1"
}
function Write-ActionDebug
{
<#
.SYNOPSIS
Writes a debug-level message.
.PARAMETER Message
The message to write.
#>
param(
[Parameter(Mandatory)]
[string]$Message
)
Write-Host "::debug::$Message"
}
function Write-ActionNotice
{
<#
.SYNOPSIS
Writes a notice-level message.
.PARAMETER Message
The message to write.
#>
param(
[Parameter(Mandatory)]
[string]$Message
)
Write-Host "::notice::$Message"
}
function Write-ActionWarning
{
<#
.SYNOPSIS
Writes a warning-level message.
.PARAMETER Message
The message to write.
#>
param(
[Parameter(Mandatory)]
[string]$Message
)
Write-Host "::warning::$Message"
}
function Write-ActionError
{
<#
.SYNOPSIS
Writes an error-level message.
.PARAMETER Message
The message to write.
#>
param(
[Parameter(Mandatory)]
[string]$Message
)
Write-Host "::error::$Message"
}
function Stop-Action
{
<#
.SYNOPSIS
Fails the action with an error message.
.PARAMETER Message
The error message.
.PARAMETER ExitCode
The exit code (default: 1).
#>
param(
[Parameter(Mandatory)]
[string]$Message,
[int]$ExitCode = 1
)
Write-ActionError $Message
exit $ExitCode
}
function Invoke-ForgejoApi
{
<#
.SYNOPSIS
Makes an authenticated Forgejo API call.
.PARAMETER Method
The HTTP method (GET, POST, PUT, PATCH, DELETE).
.PARAMETER Path
The API path (e.g., "/repos/{owner}/{repo}/issues").
.PARAMETER Body
Optional request body (will be converted to JSON).
.PARAMETER ContentType
The content type (default: application/json).
.OUTPUTS
The deserialized JSON response.
#>
param(
[Parameter(Mandatory)]
[ValidateSet("GET", "POST", "PUT", "PATCH", "DELETE")]
[string]$Method,
[Parameter(Mandatory)]
[string]$Path,
[object]$Body,
[string]$ContentType = "application/json"
)
$token = $env:FORGEJO_TOKEN
if ([string]::IsNullOrEmpty($token))
{
throw "FORGEJO_TOKEN is not set."
}
$serverUrl = $env:FORGEJO_SERVER_URL
if ([string]::IsNullOrEmpty($serverUrl))
{
throw "FORGEJO_SERVER_URL is not set."
}
$headers = @{
"Authorization" = "token $token"
"Accept" = "application/json"
}
$params = @{
Method = $Method
Uri = "$serverUrl/api/v1$Path"
Headers = $headers
ContentType = $ContentType
}
if ($Body)
{
$params.Body = ($Body | ConvertTo-Json -Depth 100 -Compress)
}
try
{
return (Invoke-RestMethod @params)
}
catch
{
$statusCode = $_.Exception.Response.StatusCode.value__
$message = $_.ErrorDetails.Message
if ([string]::IsNullOrEmpty($message))
{
$message = $_.Exception.Message
}
throw "Forgejo API call failed: $Method $Path -> $statusCode $message"
}
}
function Get-ForgejoContext
{
<#
.SYNOPSIS
Returns a hashtable with the Forgejo Actions context.
.DESCRIPTION
Provides access to ev payload, repository info, and a convenience
method for making authenticated API calls.
.OUTPUTS
Hashtable with Context, Repository, ServerUrl, Token, Ref, Sha, RunId,
and an InvokeApi scriptblock.
#>
$ev = @{}
if ($env:FORGEJO_EVENT_PATH -and (Test-Path $env:FORGEJO_EVENT_PATH))
{
try
{
$ev = (Get-Content $env:FORGEJO_EVENT_PATH -Raw | ConvertFrom-Json -AsHashtable)
}
catch
{
Write-ActionWarning "Failed to parse FORGEJO_EVENT_PATH: $_"
}
}
return @{
Event = $ev
Repository = $env:FORGEJO_REPOSITORY
ServerUrl = $env:FORGEJO_SERVER_URL
Token = $env:FORGEJO_TOKEN
Ref = $env:FORGEJO_REF
Sha = $env:FORGEJO_SHA
RunId = $env:FORGEJO_RUN_ID
RunNumber = $env:FORGEJO_RUN_NUMBER
Actor = $env:FORGEJO_ACTOR
Workflow = $env:FORGEJO_WORKFLOW
Job = $env:FORGEJO_JOB
Action = $env:FORGEJO_ACTION
# Convenience: $Forgejo.InvokeApi("GET", "/repos/{owner}/{repo}/issues")
InvokeApi = [ScriptBlock] {
param($Method, $Path, $Body)
Invoke-ForgejoApi -Method $Method -Path $Path -Body $Body
}
}
}

View file

@ -0,0 +1 @@
using module ../Geekeey.Build.Pwsh.psd1

22
src/collection/pwsh.ps1 Normal file
View file

@ -0,0 +1,22 @@
# Copyright (c) The Geekeey Authors
# SPDX-License-Identifier: EUPL-1.2
using module ../Geekeey.Build.Pwsh.psd1
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'Ignore'
$PSNativeCommandUseErrorActionPreference = $true
$script = Get-ActionInput -Name "script" -Required
try
{
Invoke-Expression $script
}
catch
{
Write-ActionError "Script failed: $_"
exit 1
}

View file

@ -0,0 +1 @@
using module ../Geekeey.Build.Pwsh.psm1

0
src/core/context.ps1 Normal file
View file