Rebrand to Grace Solutions; add README, about_ help, Gitea CI/CD, track Module bin #1

Merged
gsadmin merged 8 commits from dev into main 2026-06-03 01:53:11 +00:00
7 changed files with 1332 additions and 15 deletions
Showing only changes of commit eaffeedf12 - Show all commits
+221 -11
View File
@@ -6,7 +6,7 @@ on:
branches: [main]
jobs:
publish:
build:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
steps:
@@ -41,16 +41,6 @@ jobs:
sudo apt-get install -y powershell
pwsh --version
- name: Bootstrap PowerShellGet / NuGet provider
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
if (-not (Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue)) {
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser | Out-Null
}
Get-PackageProvider -Name NuGet | Format-Table Name,Version
- name: Build and test module
shell: pwsh
run: ./build.ps1 -RunTests
@@ -63,6 +53,218 @@ jobs:
$manifest = Test-ModuleManifest -Path $manifestPath
Write-Host "Manifest OK: $($manifest.Name) $($manifest.Version)"
- name: Upload module artifact
uses: actions/upload-artifact@v4
with:
name: PSInfisicalAPI-module
path: Module/PSInfisicalAPI
if-no-files-found: error
retention-days: 7
release:
needs: build
if: ${{ success() && github.event.pull_request.merged == true }}
runs-on: ubuntu-latest
outputs:
version: ${{ steps.meta.outputs.version }}
tag: ${{ steps.meta.outputs.tag }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install PowerShell 7 (if not present)
shell: bash
run: |
set -euo pipefail
if command -v pwsh >/dev/null 2>&1; then
echo "pwsh already installed: $(pwsh --version)"
exit 0
fi
sudo apt-get update
sudo apt-get install -y --no-install-recommends wget ca-certificates apt-transport-https gnupg
source /etc/os-release
wget -q "https://packages.microsoft.com/config/ubuntu/${VERSION_ID}/packages-microsoft-prod.deb" -O /tmp/ms-prod.deb
sudo dpkg -i /tmp/ms-prod.deb
rm -f /tmp/ms-prod.deb
sudo apt-get update
sudo apt-get install -y powershell
pwsh --version
- name: Download module artifact
uses: actions/download-artifact@v4
with:
name: PSInfisicalAPI-module
path: Module/PSInfisicalAPI
- name: Resolve module version and tag
id: meta
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$manifestPath = Join-Path $PWD 'Module/PSInfisicalAPI/PSInfisicalAPI.psd1'
$manifest = Test-ModuleManifest -Path $manifestPath
$version = $manifest.Version.ToString()
$tag = $version
Write-Host "Module version: $version"
Write-Host "Release tag: $tag"
"version=$version" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
"tag=$tag" | Out-File -FilePath $env:GITHUB_OUTPUT -Append -Encoding utf8
- name: Package module as release asset
shell: pwsh
env:
VERSION: ${{ steps.meta.outputs.version }}
run: |
$ErrorActionPreference = 'Stop'
$zipPath = Join-Path $PWD "PSInfisicalAPI-$($env:VERSION).zip"
if (Test-Path $zipPath) { Remove-Item $zipPath -Force }
Compress-Archive -Path 'Module/PSInfisicalAPI/*' -DestinationPath $zipPath -Force
Write-Host "Created: $zipPath ($([math]::Round((Get-Item $zipPath).Length / 1KB, 1)) KB)"
- name: Create Gitea release
shell: pwsh
env:
GITEA_TOKEN: ${{ github.token }}
API_URL: ${{ github.api_url }}
REPO: ${{ github.repository }}
TAG: ${{ steps.meta.outputs.tag }}
VERSION: ${{ steps.meta.outputs.version }}
COMMIT_SHA: ${{ github.sha }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
SERVER_URL: ${{ github.server_url }}
RUN_ID: ${{ github.run_id }}
run: |
$ErrorActionPreference = 'Stop'
if ([string]::IsNullOrWhiteSpace($env:GITEA_TOKEN)) {
throw "github.token is not available; cannot call the Gitea release API."
}
$shortSha = $env:COMMIT_SHA.Substring(0, [Math]::Min(12, $env:COMMIT_SHA.Length))
$buildUtc = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ')
$runUrl = "$($env:SERVER_URL)/$($env:REPO)/actions/runs/$($env:RUN_ID)"
$prUrl = "$($env:SERVER_URL)/$($env:REPO)/pulls/$($env:PR_NUMBER)"
$changelogSection = ''
if (Test-Path 'CHANGELOG.md') {
$lines = [System.IO.File]::ReadAllLines('CHANGELOG.md')
$start = -1; $end = $lines.Length
for ($i = 0; $i -lt $lines.Length; $i++) {
if ($lines[$i] -match "^##\s+$([regex]::Escape($env:VERSION))\s*$") { $start = $i + 1; continue }
if ($start -ge 0 -and $lines[$i] -match '^##\s+') { $end = $i; break }
}
if ($start -ge 0) {
$changelogSection = ($lines[$start..($end - 1)] -join "`n").Trim()
}
}
$body = @"
**PSInfisicalAPI $($env:VERSION)**
| Field | Value |
| --- | --- |
| Version | ``$($env:VERSION)`` |
| Tag | ``$($env:TAG)`` |
| Commit | [``$shortSha``]($($env:SERVER_URL)/$($env:REPO)/commit/$($env:COMMIT_SHA)) |
| Built (UTC) | $buildUtc |
| Merged PR | [#$($env:PR_NUMBER) $($env:PR_TITLE)]($prUrl) by @$($env:PR_AUTHOR) |
| Workflow run | [$($env:RUN_ID)]($runUrl) |
## Changes
$(if ($changelogSection) { $changelogSection } else { '_No CHANGELOG section found for this version._' })
## Install
``````powershell
Install-Module -Name PSInfisicalAPI -RequiredVersion $($env:VERSION) -Scope CurrentUser
``````
"@
$headers = @{
Authorization = "token $($env:GITEA_TOKEN)"
Accept = 'application/json'
}
$createUri = "$($env:API_URL)/repos/$($env:REPO)/releases"
$existing = $null
try {
$existing = Invoke-RestMethod -Method Get -Headers $headers `
-Uri "$createUri/tags/$($env:TAG)" -ErrorAction Stop
} catch {
if ($_.Exception.Response.StatusCode.value__ -ne 404) { throw }
}
if ($existing) {
Write-Host "Release tag '$($env:TAG)' already exists (id=$($existing.id)); skipping release creation."
return
}
$payload = @{
tag_name = $env:TAG
target_commitish = $env:COMMIT_SHA
name = "PSInfisicalAPI $($env:VERSION)"
body = $body
draft = $false
prerelease = $false
} | ConvertTo-Json -Depth 4
$release = Invoke-RestMethod -Method Post -Uri $createUri -Headers $headers `
-ContentType 'application/json' -Body $payload
Write-Host "Created release id=$($release.id) at $($release.html_url)"
$assetPath = Join-Path $PWD "PSInfisicalAPI-$($env:VERSION).zip"
$uploadUri = "$createUri/$($release.id)/assets?name=PSInfisicalAPI-$($env:VERSION).zip"
$fileBytes = [System.IO.File]::ReadAllBytes($assetPath)
Invoke-RestMethod -Method Post -Uri $uploadUri -Headers $headers `
-ContentType 'application/zip' -Body $fileBytes | Out-Null
Write-Host "Uploaded asset: PSInfisicalAPI-$($env:VERSION).zip"
publish:
needs: release
if: ${{ success() && github.event.pull_request.merged == true }}
runs-on: ubuntu-latest
steps:
- name: Install PowerShell 7 (if not present)
shell: bash
run: |
set -euo pipefail
if command -v pwsh >/dev/null 2>&1; then
echo "pwsh already installed: $(pwsh --version)"
exit 0
fi
sudo apt-get update
sudo apt-get install -y --no-install-recommends wget ca-certificates apt-transport-https gnupg
source /etc/os-release
wget -q "https://packages.microsoft.com/config/ubuntu/${VERSION_ID}/packages-microsoft-prod.deb" -O /tmp/ms-prod.deb
sudo dpkg -i /tmp/ms-prod.deb
rm -f /tmp/ms-prod.deb
sudo apt-get update
sudo apt-get install -y powershell
pwsh --version
- name: Download module artifact
uses: actions/download-artifact@v4
with:
name: PSInfisicalAPI-module
path: Module/PSInfisicalAPI
- name: Bootstrap PowerShellGet / NuGet provider
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
Set-PSRepository -Name PSGallery -InstallationPolicy Trusted
if (-not (Get-PackageProvider -Name NuGet -ListAvailable -ErrorAction SilentlyContinue)) {
Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force -Scope CurrentUser | Out-Null
}
Get-PackageProvider -Name NuGet | Format-Table Name,Version
- name: Verify PowerShell Gallery API key is configured
shell: pwsh
env:
@@ -72,6 +274,14 @@ jobs:
throw "Repository secret 'PSGALLERY_API_KEY' is not configured."
}
- name: Re-validate downloaded module manifest
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$manifestPath = Join-Path $PWD 'Module/PSInfisicalAPI/PSInfisicalAPI.psd1'
$manifest = Test-ModuleManifest -Path $manifestPath
Write-Host "Manifest OK: $($manifest.Name) $($manifest.Version)"
- name: Publish to PowerShell Gallery
shell: pwsh
env:
+674 -2
View File
@@ -6,47 +6,383 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward)
## 2026.06.02.1724
- Build produced from commit 5801b4774af5.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward) (carried forward)
## 2026.06.02.1648
- Build produced from commit 430e3a00c921.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward)
## 2026.06.02.1724
- Build produced from commit 5801b4774af5.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward) (carried forward) (carried forward)
## 2026.06.02.1638
- Build produced from commit 3c47d6ff30ec.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward)
## 2026.06.02.1724
- Build produced from commit 5801b4774af5.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward) (carried forward)
## 2026.06.02.1648
- Build produced from commit 430e3a00c921.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward)
## 2026.06.02.1724
- Build produced from commit 5801b4774af5.
## Unreleased (carried forward) (carried forward) (carried forward)
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward) (carried forward) (carried forward) (carried forward)
## 2026.06.02.1611
@@ -54,47 +390,383 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) loos
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward)
## 2026.06.02.1724
- Build produced from commit 5801b4774af5.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward) (carried forward)
## 2026.06.02.1648
- Build produced from commit 430e3a00c921.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward)
## 2026.06.02.1724
- Build produced from commit 5801b4774af5.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward) (carried forward) (carried forward)
## 2026.06.02.1638
- Build produced from commit 3c47d6ff30ec.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward)
## 2026.06.02.1724
- Build produced from commit 5801b4774af5.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward) (carried forward)
## 2026.06.02.1648
- Build produced from commit 430e3a00c921.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward)
## 2026.06.02.1724
- Build produced from commit 5801b4774af5.
## Unreleased (carried forward) (carried forward) (carried forward) (carried forward)
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward)
## 2026.06.02.1737
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward)
## 2026.06.02.1902
- Build produced from commit fa65c18bc171.
## Unreleased
## 2026.06.02.1907
- Build produced from commit fa65c18bc171.
## Unreleased (carried forward) (carried forward) (carried forward) (carried forward) (carried forward) (carried forward) (carried forward)
### Added
+2 -2
View File
@@ -1,6 +1,6 @@
@{
RootModule = 'PSInfisicalAPI.psm1'
ModuleVersion = '2026.06.02.1724'
ModuleVersion = '2026.06.02.1907'
GUID = 'b8a2f3d4-7c51-4d2f-9e6a-1f0c8b3d4e51'
Author = 'Grace Solutions'
CompanyName = 'Grace Solutions'
@@ -27,7 +27,7 @@
LicenseUri = 'https://www.gnu.org/licenses/agpl-3.0.html'
ProjectUri = 'https://prod.git.gracesolution.info/gsadmin/PSInfisicalAPI'
ReleaseNotes = 'See CHANGELOG.md in the project repository for release history.'
CommitHash = '5801b4774af5'
CommitHash = 'fa65c18bc171'
}
}
}
Binary file not shown.
+426
View File
@@ -0,0 +1,426 @@
#Requires -Version 7.0
<#
.SYNOPSIS
Downloads the Gitea act_runner binary, installs it as a system daemon, and registers it.
.DESCRIPTION
Cross-platform installer for the Gitea Actions runner. Idempotent: re-running upgrades
in place, skips work when the requested version already matches, and reuses an existing
registration unless -Force is supplied.
Resolution order for InstanceUrl and RegistrationToken when not passed explicitly:
Process scope env vars -> User scope env vars -> Machine scope env vars
Explicit candidate variable names tried in order:
InstanceUrl: GITEA_INSTANCE_URL, GITEA_URL,
CLOUDINIT_GITEA_INSTANCE_URL, CLOUDINIT_GITEA_URL
RegistrationToken: GITEA_RUNNER_REGISTRATION_TOKEN, GITEA_RUNNER_TOKEN,
GITEA_REGISTRATION_TOKEN,
CLOUDINIT_GITEA_RUNNER_REGISTRATION_TOKEN,
CLOUDINIT_GITEA_RUNNER_TOKEN,
CLOUDINIT_GITEA_REGISTRATION_TOKEN
.PARAMETER InstanceUrl
Base URL of the Gitea instance (e.g. https://prod.git.gracesolution.info).
.PARAMETER RegistrationToken
Runner registration token. Treated as secret; never logged.
.PARAMETER RunnerName
Name presented to Gitea. Defaults to the machine hostname.
.PARAMETER Labels
Optional comma-separated runner labels (e.g. linux-amd64,docker). When omitted,
no --labels argument is passed to act_runner register and the runner is
registered with whatever labels are configured server-side or in config.yaml.
.PARAMETER Version
Specific release tag (e.g. v1.0.7) or 'latest'. Defaults to latest.
.PARAMETER InstallRoot
Override directory. Defaults are /opt/gitea/runner on Linux,
/usr/local/gitea/runner on macOS, and %ProgramData%\Gitea\Runner on Windows.
.PARAMETER BinaryName
Renamed binary on disk. Defaults to act_runner (act_runner.exe on Windows).
.PARAMETER ServiceName
System service identifier (used as the systemd unit name, launchd label suffix,
and Windows service name). Must not contain spaces or characters illegal in
a unit/service identifier. Defaults to 'gitea-runner'.
.PARAMETER ServiceDisplayName
Friendly human-readable name shown in systemctl status, Windows Services MMC,
and similar UIs. Defaults to 'Gitea Runner'.
.PARAMETER Force
Re-download binary and re-register the runner even when already present.
.EXAMPLE
pwsh -File Install-GiteaRunner.ps1 -InstanceUrl https://prod.git.gracesolution.info
.EXAMPLE
Remote one-liner using env vars for InstanceUrl / RegistrationToken:
$env:GITEA_INSTANCE_URL = 'https://prod.git.gracesolution.info'
$env:GITEA_RUNNER_REGISTRATION_TOKEN = '<token>'
irm 'https://prod.git.gracesolution.info/gsadmin/Gitea-Bootstrap/raw/branch/main/scripts/Install-GiteaRunner.ps1' | iex
#>
[CmdletBinding()]
param(
[string] $InstanceUrl,
[string] $RegistrationToken,
[string] $RunnerName = [System.Net.Dns]::GetHostName(),
[string] $Labels,
[string] $Version = 'latest',
[string] $InstallRoot,
[string] $BinaryName,
[string] $ServiceName = 'gitea-runner',
[string] $ServiceDisplayName = 'Gitea Runner',
[switch] $Force
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
if ($PSVersionTable.PSVersion.Major -lt 7) {
throw "Install-GiteaRunner requires PowerShell 7 or later. Detected: $($PSVersionTable.PSVersion)."
}
$script:Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
function Write-Stage {
param([string] $Message)
$stamp = [DateTimeOffset]::UtcNow.UtcDateTime.ToString('yyyy-MM-ddTHH:mm:ss.fffffffZ')
Write-Host "[$stamp] - [Install-GiteaRunner] - $Message"
}
function Resolve-FromEnvVarNames {
param(
[Parameter(Mandatory)][string[]] $Names,
[string] $DisplayName
)
$scopes = @('Process', 'User', 'Machine')
foreach ($name in $Names) {
foreach ($scope in $scopes) {
try { $value = [System.Environment]::GetEnvironmentVariable($name, [System.EnvironmentVariableTarget]::$scope) }
catch { continue }
if (-not [string]::IsNullOrWhiteSpace($value)) {
Write-Stage "Resolved $DisplayName from $scope env var '$name'."
return [string] $value
}
}
}
return $null
}
function Get-PlatformDescriptor {
$arch = [System.Runtime.InteropServices.RuntimeInformation]::ProcessArchitecture.ToString().ToLowerInvariant()
switch ($arch) {
'x64' { $assetArch = 'amd64' }
'arm64' { $assetArch = 'arm64' }
'arm' { $assetArch = 'arm-7' }
default { throw "Unsupported architecture: $arch" }
}
if ($IsWindows) { return @{ Os = 'windows'; Arch = $assetArch; Suffix = '.exe' } }
if ($IsMacOS) { return @{ Os = 'darwin'; Arch = $assetArch; Suffix = '' } }
if ($IsLinux) { return @{ Os = 'linux'; Arch = $assetArch; Suffix = '' } }
throw 'Unable to determine host operating system.'
}
function Get-DefaultInstallRoot {
if ($IsWindows) { return (Join-Path $env:ProgramData 'Gitea' 'Runner') }
if ($IsMacOS) { return '/usr/local/gitea/runner' }
return '/opt/gitea/runner'
}
function Assert-Elevated {
if ($IsWindows) {
$principal = New-Object Security.Principal.WindowsPrincipal ([Security.Principal.WindowsIdentity]::GetCurrent())
if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
throw 'This script must be run from an elevated PowerShell session (Administrator).'
}
} else {
$uid = & id -u
if ($uid -ne '0') { throw 'This script must be run as root (use sudo).' }
}
}
function Resolve-ReleaseTag {
param([string] $Requested)
if ($Requested -and $Requested -ne 'latest') { return ($Requested.TrimStart('v')) }
Write-Stage 'Querying Gitea for latest runner release.'
$proxyUri = [System.Net.WebRequest]::GetSystemWebProxy().GetProxy([Uri] 'https://gitea.com')
$invokeArgs = @{
Uri = 'https://gitea.com/api/v1/repos/gitea/act_runner/releases/latest'
UseBasicParsing = $true
UseDefaultCredentials = $true
}
if ($proxyUri -and $proxyUri.AbsoluteUri -ne 'https://gitea.com/') {
$invokeArgs.Proxy = $proxyUri.AbsoluteUri
$invokeArgs.ProxyUseDefaultCredentials = $true
}
$release = Invoke-RestMethod @invokeArgs
if (-not $release.tag_name) { throw 'Failed to resolve latest act_runner release tag.' }
return ($release.tag_name.TrimStart('v'))
}
function Get-InstalledBinaryVersion {
param([string] $BinaryPath)
if (-not (Test-Path -LiteralPath $BinaryPath)) { return $null }
try {
$raw = & $BinaryPath --version 2>$null
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($raw)) { return $null }
$match = [regex]::Match($raw, '(\d+\.\d+\.\d+(?:[\-\.][0-9A-Za-z\.\-]+)?)')
if ($match.Success) { return $match.Groups[1].Value }
} catch { }
return $null
}
function Save-BinaryViaWebClient {
param(
[Parameter(Mandatory)][string] $Uri,
[Parameter(Mandatory)][string] $Destination
)
Write-Stage "Downloading $Uri"
$client = New-Object System.Net.WebClient
try {
$client.Headers['User-Agent'] = 'Install-GiteaRunner'
$client.UseDefaultCredentials = $true
$proxy = [System.Net.WebRequest]::GetSystemWebProxy()
$proxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
$client.Proxy = $proxy
$client.DownloadFile($Uri, $Destination)
} finally {
$client.Dispose()
}
}
function Add-ToMachinePath {
param([Parameter(Mandatory)][string] $Directory)
if ($IsWindows) {
$current = [System.Environment]::GetEnvironmentVariable('Path', 'Machine')
$parts = @()
if ($current) { $parts = $current.Split(';', [System.StringSplitOptions]::RemoveEmptyEntries) }
if ($parts -notcontains $Directory) {
$updated = ($parts + $Directory) -join ';'
[System.Environment]::SetEnvironmentVariable('Path', $updated, 'Machine')
Write-Stage "Added $Directory to Machine PATH."
}
if (-not ($env:Path -split ';' -contains $Directory)) { $env:Path = "$env:Path;$Directory" }
return
}
$profilePath = '/etc/profile.d/gitea-runner.sh'
$line = "export PATH=`"$Directory`":`$PATH"
$existing = if ([System.IO.File]::Exists($profilePath)) { [System.IO.File]::ReadAllText($profilePath) } else { '' }
if ($existing -notmatch [regex]::Escape($Directory)) {
[System.IO.File]::WriteAllText($profilePath, $line, $script:Utf8NoBom)
& chmod 0644 $profilePath | Out-Null
Write-Stage "Wrote $profilePath for system-wide PATH."
}
if (($env:PATH -split ':') -notcontains $Directory) { $env:PATH = "$Directory`:$env:PATH" }
}
function Register-Runner {
param(
[Parameter(Mandatory)][string] $BinaryPath,
[Parameter(Mandatory)][string] $WorkingDirectory,
[Parameter(Mandatory)][string] $InstanceUrl,
[Parameter(Mandatory)][string] $Token,
[Parameter(Mandatory)][string] $RunnerName,
[string] $Labels,
[switch] $Force
)
$configPath = Join-Path $WorkingDirectory 'config.yaml'
if ((-not [System.IO.File]::Exists($configPath)) -or $Force) {
Write-Stage 'Generating config.yaml.'
Push-Location $WorkingDirectory
try {
$configContent = (& $BinaryPath generate-config) | Out-String
[System.IO.File]::WriteAllText($configPath, $configContent, $script:Utf8NoBom)
} finally { Pop-Location }
}
$runnerStateFile = Join-Path $WorkingDirectory '.runner'
if ((Test-Path -LiteralPath $runnerStateFile) -and -not $Force) {
Write-Stage 'Runner already registered (.runner present); skipping registration.'
return
}
$labelDescription = if ([string]::IsNullOrWhiteSpace($Labels)) { '(no labels specified; using server/config defaults)' } else { "with labels '$Labels'" }
Write-Stage "Registering runner '$RunnerName' $labelDescription."
Push-Location $WorkingDirectory
try {
$registerArgs = @('register', '--no-interactive', '--instance', $InstanceUrl,
'--token', $Token, '--name', $RunnerName, '--config', $configPath)
if (-not [string]::IsNullOrWhiteSpace($Labels)) { $registerArgs += @('--labels', $Labels) }
& $BinaryPath @registerArgs
if ($LASTEXITCODE -ne 0) { throw "act_runner register exited with code $LASTEXITCODE." }
} finally { Pop-Location }
}
function Install-SystemdUnit {
param(
[Parameter(Mandatory)][string] $ServiceName,
[Parameter(Mandatory)][string] $ServiceDisplayName,
[Parameter(Mandatory)][string] $BinaryPath,
[Parameter(Mandatory)][string] $WorkingDirectory
)
$unitPath = "/etc/systemd/system/$ServiceName.service"
$configPath = Join-Path $WorkingDirectory 'config.yaml'
$unit = @"
[Unit]
Description=$ServiceDisplayName
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
WorkingDirectory=$WorkingDirectory
ExecStart=$BinaryPath daemon --config $configPath
Restart=always
RestartSec=5
User=root
[Install]
WantedBy=multi-user.target
"@
[System.IO.File]::WriteAllText($unitPath, $unit, $script:Utf8NoBom)
& systemctl daemon-reload | Out-Null
& systemctl enable $ServiceName | Out-Null
& systemctl restart $ServiceName | Out-Null
Write-Stage "Systemd unit $unitPath installed and started."
}
function Install-LaunchdPlist {
param(
[Parameter(Mandatory)][string] $ServiceName,
[Parameter(Mandatory)][string] $ServiceDisplayName,
[Parameter(Mandatory)][string] $BinaryPath,
[Parameter(Mandatory)][string] $WorkingDirectory
)
$label = "com.gitea.$ServiceName"
$plistPath = "/Library/LaunchDaemons/$label.plist"
$configPath = Join-Path $WorkingDirectory 'config.yaml'
$plist = @"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key><string>$label</string>
<key>ProgramArguments</key>
<array>
<string>$BinaryPath</string>
<string>daemon</string>
<string>--config</string>
<string>$configPath</string>
</array>
<key>WorkingDirectory</key><string>$WorkingDirectory</string>
<key>RunAtLoad</key><true/>
<key>KeepAlive</key><true/>
</dict>
</plist>
"@
[System.IO.File]::WriteAllText($plistPath, $plist, $script:Utf8NoBom)
& chmod 0644 $plistPath | Out-Null
& launchctl unload $plistPath 2>$null | Out-Null
& launchctl load -w $plistPath | Out-Null
Write-Stage "Launchd plist $plistPath installed and loaded."
}
function Install-WindowsService {
param(
[Parameter(Mandatory)][string] $ServiceName,
[Parameter(Mandatory)][string] $ServiceDisplayName,
[Parameter(Mandatory)][string] $BinaryPath,
[Parameter(Mandatory)][string] $WorkingDirectory
)
$configPath = Join-Path $WorkingDirectory 'config.yaml'
$binPath = "`"$BinaryPath`" daemon --config `"$configPath`""
$existing = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($existing) {
Write-Stage "Service $ServiceName exists; updating binary path and restarting."
& sc.exe config $ServiceName binPath= $binPath DisplayName= $ServiceDisplayName | Out-Null
if ($existing.Status -eq 'Running') { Stop-Service -Name $ServiceName -Force }
} else {
Write-Stage "Creating Windows service $ServiceName."
& sc.exe create $ServiceName binPath= $binPath start= auto DisplayName= $ServiceDisplayName | Out-Null
if ($LASTEXITCODE -ne 0) { throw "sc.exe create failed with exit code $LASTEXITCODE." }
}
Start-Service -Name $ServiceName
Write-Stage "Service $ServiceName started."
}
# --- Main ----------------------------------------------------------------
Assert-Elevated
if ([string]::IsNullOrWhiteSpace($ServiceName) -or ($ServiceName -match '\s')) {
throw "ServiceName must be a non-empty identifier with no whitespace (got: '$ServiceName'). Use -ServiceDisplayName for the friendly label."
}
if ([string]::IsNullOrWhiteSpace($ServiceDisplayName)) { $ServiceDisplayName = $ServiceName }
if ([string]::IsNullOrWhiteSpace($InstanceUrl)) {
$InstanceUrl = Resolve-FromEnvVarNames -DisplayName 'InstanceUrl' -Names @(
'GITEA_INSTANCE_URL',
'GITEA_URL',
'CLOUDINIT_GITEA_INSTANCE_URL',
'CLOUDINIT_GITEA_URL'
)
}
if ([string]::IsNullOrWhiteSpace($RegistrationToken)) {
$RegistrationToken = Resolve-FromEnvVarNames -DisplayName 'RegistrationToken' -Names @(
'GITEA_RUNNER_REGISTRATION_TOKEN',
'GITEA_RUNNER_TOKEN',
'GITEA_REGISTRATION_TOKEN',
'CLOUDINIT_GITEA_RUNNER_REGISTRATION_TOKEN',
'CLOUDINIT_GITEA_RUNNER_TOKEN',
'CLOUDINIT_GITEA_REGISTRATION_TOKEN'
)
}
if ([string]::IsNullOrWhiteSpace($InstanceUrl)) { throw 'InstanceUrl not provided and no matching env var found.' }
if ([string]::IsNullOrWhiteSpace($RegistrationToken)) { throw 'RegistrationToken not provided and no matching env var found.' }
$platform = Get-PlatformDescriptor
if (-not $InstallRoot) { $InstallRoot = Get-DefaultInstallRoot }
if (-not $BinaryName) { $BinaryName = if ($IsWindows) { 'act_runner.exe' } else { 'act_runner' } }
$resolvedVersion = Resolve-ReleaseTag -Requested $Version
$assetName = "gitea-runner-$resolvedVersion-$($platform.Os)-$($platform.Arch)$($platform.Suffix)"
$downloadUri = "https://gitea.com/gitea/runner/releases/download/v$resolvedVersion/$assetName"
if (-not (Test-Path -LiteralPath $InstallRoot)) {
Write-Stage "Creating install root $InstallRoot."
New-Item -ItemType Directory -Path $InstallRoot -Force | Out-Null
}
$binaryPath = Join-Path $InstallRoot $BinaryName
$installedVersion = Get-InstalledBinaryVersion -BinaryPath $binaryPath
if ($installedVersion -eq $resolvedVersion -and -not $Force) {
Write-Stage "act_runner $installedVersion already installed; skipping download."
} else {
if ($installedVersion) { Write-Stage "Upgrading act_runner $installedVersion -> $resolvedVersion." }
Save-BinaryViaWebClient -Uri $downloadUri -Destination $binaryPath
if (-not $IsWindows) { & chmod 0755 $binaryPath | Out-Null }
}
Add-ToMachinePath -Directory $InstallRoot
Register-Runner -BinaryPath $binaryPath -WorkingDirectory $InstallRoot `
-InstanceUrl $InstanceUrl -Token $RegistrationToken `
-RunnerName $RunnerName -Labels $Labels -Force:$Force
if ($IsLinux) { Install-SystemdUnit -ServiceName $ServiceName -ServiceDisplayName $ServiceDisplayName -BinaryPath $binaryPath -WorkingDirectory $InstallRoot }
elseif ($IsMacOS) { Install-LaunchdPlist -ServiceName $ServiceName -ServiceDisplayName $ServiceDisplayName -BinaryPath $binaryPath -WorkingDirectory $InstallRoot }
elseif ($IsWindows) { Install-WindowsService -ServiceName $ServiceName -ServiceDisplayName $ServiceDisplayName -BinaryPath $binaryPath -WorkingDirectory $InstallRoot }
Write-Stage "Gitea runner installation complete (version $resolvedVersion, service $ServiceName / '$ServiceDisplayName')."
@@ -89,6 +89,7 @@ namespace PSInfisicalAPI.Cmdlets
{
switch (encoding)
{
case InfisicalExportEncoding.UTF8: return new UTF8Encoding(false);
case InfisicalExportEncoding.UTF8Bom: return new UTF8Encoding(true);
case InfisicalExportEncoding.Unicode: return new UnicodeEncoding();
default: return new UTF8Encoding(false);
@@ -42,6 +42,14 @@ namespace PSInfisicalAPI.Http
webRequest.UserAgent = "PSInfisicalAPI";
webRequest.Timeout = _timeoutSeconds * 1000;
webRequest.ReadWriteTimeout = _timeoutSeconds * 1000;
webRequest.UseDefaultCredentials = true;
IWebProxy systemProxy = WebRequest.GetSystemWebProxy();
if (systemProxy != null)
{
systemProxy.Credentials = CredentialCache.DefaultNetworkCredentials;
webRequest.Proxy = systemProxy;
}
ApplyHeaders(webRequest, request.Headers);