Add scripts for building and managing RustDesk versions
Some checks failed
rustdesk-auto-build / build-and-publish (push) Has been cancelled

- Implement Build-CustomRustDesk.ps1 to build RustDesk with specified version and configuration.
- Create Get-LastBuiltVersion.ps1 to retrieve the last built version from a state file.
- Add Get-LatestRustDeskVersion.ps1 to fetch the latest version from GitHub releases.
- Develop Invoke-RustDeskPipeline.ps1 to orchestrate the build and deployment process.
- Introduce Publish-Artifacts.ps1 for publishing build artifacts via SMB or SCP.
- Implement Set-LastBuiltVersion.ps1 to update the state file with the last built version.
This commit is contained in:
2026-03-17 14:20:29 +01:00
commit 472cd9e57c
9 changed files with 349 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
name: rustdesk-auto-build
on:
schedule:
- cron: "17 * * * *"
workflow_dispatch:
jobs:
build-and-publish:
runs-on:
- self-hosted
- windows
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run RustDesk automation
shell: pwsh
env:
STATE_FILE_PATH: ${{ vars.STATE_FILE_PATH }}
CONFIG_PATH: ${{ vars.CONFIG_PATH }}
BUILD_OUTPUT_DIR: ${{ vars.BUILD_OUTPUT_DIR }}
BUILDER_COMMAND: ${{ secrets.BUILDER_COMMAND }}
DEPLOY_MODE: ${{ vars.DEPLOY_MODE }}
DESTINATION_PATH: ${{ vars.DESTINATION_PATH }}
SCP_DESTINATION: ${{ vars.SCP_DESTINATION }}
run: |
./scripts/Invoke-RustDeskPipeline.ps1

87
README.md Normal file
View File

@@ -0,0 +1,87 @@
# RustDesk Automated Build and Publish (Gitea + Windows Runner)
This repo automates your current manual process:
1. Check latest RustDesk release.
2. Compare with last built version.
3. Build your custom client only when a new version exists.
4. Publish artifacts to your download location.
5. Persist last built version on runner disk.
## Files
- `.gitea/workflows/rustdesk-auto-build.yml`
- `scripts/Invoke-RustDeskPipeline.ps1`
- `scripts/Get-LatestRustDeskVersion.ps1`
- `scripts/Get-LastBuiltVersion.ps1`
- `scripts/Set-LastBuiltVersion.ps1`
- `scripts/Build-CustomRustDesk.ps1`
- `scripts/Publish-Artifacts.ps1`
## What you must configure in Gitea
Use repo Variables and Secrets in Gitea Actions settings.
### Variables
- `STATE_FILE_PATH`
- Example: `C:\gitea-runner-state\rustdesk\last-built-version.txt`
- `CONFIG_PATH`
- Example: `infernalQS.json`
- `BUILD_OUTPUT_DIR`
- Example: `.\dist`
- `DEPLOY_MODE`
- `smb` or `scp`
- `DESTINATION_PATH` (required for `smb` mode)
- Example: `\\fileserver\rustdesk\downloads`
- `SCP_DESTINATION` (required for `scp` mode)
- Example: `deploy@your-host:/var/www/rustdesk/`
### Secrets
- `BUILDER_COMMAND`
- A command template to build your custom RustDesk client.
- Supported placeholders:
- `{{version}}` -> latest RustDesk tag (for example `1.4.2`)
- `{{config}}` -> absolute path to config file
- `{{output}}` -> absolute output directory path
Example `BUILDER_COMMAND` values:
```powershell
.\tools\rdgen-cli.exe build --config "{{config}}" --version "{{version}}" --output "{{output}}"
```
```powershell
powershell -File .\tools\build-custom-client.ps1 -Version "{{version}}" -Config "{{config}}" -OutDir "{{output}}"
```
## Runner prerequisites
Your existing self-hosted Windows runner should have:
- Internet access to GitHub API (`api.github.com`).
- Access to the destination path (SMB share or SCP target).
- Build toolchain needed by your `BUILDER_COMMAND`.
- `scp` available only if using `DEPLOY_MODE=scp`.
## First run checklist
1. Put your real build command in `BUILDER_COMMAND` secret.
2. Make sure `infernalQS.json` in repo is the config you want.
3. Set `STATE_FILE_PATH` to a persistent local path on runner machine.
4. Set deploy mode and destination.
5. Trigger workflow manually once (`workflow_dispatch`).
6. Confirm artifacts were copied and `latest-version.txt` appears in output.
## Behavior notes
- Schedule runs hourly (`17 * * * *`).
- If latest RustDesk version equals saved state, build is skipped.
- On successful publish, state file is updated.
## Common adjustments
- Change schedule in `.gitea/workflows/rustdesk-auto-build.yml`.
- Add runner labels to `runs-on` if your runner uses custom labels.
- Extend `Publish-Artifacts.ps1` if you need extra deployment steps.

51
infernalQS.json Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,39 @@
function Build-CustomRustDesk {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$Version,
[Parameter(Mandatory = $true)]
[string]$ConfigPath,
[Parameter(Mandatory = $true)]
[string]$OutputDir,
[Parameter(Mandatory = $true)]
[string]$BuilderCommandTemplate
)
if (-not (Test-Path -LiteralPath $ConfigPath)) {
throw "Config file not found: $ConfigPath"
}
New-Item -Path $OutputDir -ItemType Directory -Force | Out-Null
$resolvedConfig = Resolve-Path -LiteralPath $ConfigPath
$resolvedOutput = Resolve-Path -LiteralPath $OutputDir
$command = $BuilderCommandTemplate
$command = $command.Replace("{{version}}", $Version)
$command = $command.Replace("{{config}}", $resolvedConfig.Path)
$command = $command.Replace("{{output}}", $resolvedOutput.Path)
Write-Host "Executing builder command:"
Write-Host $command
Invoke-Expression $command
if ($LASTEXITCODE -ne 0) {
throw "Custom builder command failed with exit code $LASTEXITCODE"
}
}

View File

@@ -0,0 +1,18 @@
function Get-LastBuiltVersion {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$StateFilePath
)
if (-not (Test-Path -LiteralPath $StateFilePath)) {
return $null
}
$content = Get-Content -LiteralPath $StateFilePath -Raw -ErrorAction Stop
if ([string]::IsNullOrWhiteSpace($content)) {
return $null
}
return $content.Trim()
}

View File

@@ -0,0 +1,16 @@
function Get-LatestRustDeskVersion {
[CmdletBinding()]
param()
$url = "https://api.github.com/repos/rustdesk/rustdesk/releases/latest"
# GitHub API can reject requests without a User-Agent.
$headers = @{ "User-Agent" = "rustdesk-gitea-automation" }
$response = Invoke-RestMethod -Method Get -Uri $url -Headers $headers
if (-not $response.tag_name) {
throw "Unable to read latest RustDesk tag_name from GitHub API response."
}
return [string]$response.tag_name
}

View File

@@ -0,0 +1,48 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
. (Join-Path $scriptDir "Get-LatestRustDeskVersion.ps1")
. (Join-Path $scriptDir "Get-LastBuiltVersion.ps1")
. (Join-Path $scriptDir "Set-LastBuiltVersion.ps1")
. (Join-Path $scriptDir "Build-CustomRustDesk.ps1")
. (Join-Path $scriptDir "Publish-Artifacts.ps1")
$stateFilePath = if ($env:STATE_FILE_PATH) { $env:STATE_FILE_PATH } else { "C:\gitea-runner-state\rustdesk\last-built-version.txt" }
$configPath = if ($env:CONFIG_PATH) { $env:CONFIG_PATH } else { "infernalQS.json" }
$outputDir = if ($env:BUILD_OUTPUT_DIR) { $env:BUILD_OUTPUT_DIR } else { ".\dist" }
$builderCommand = $env:BUILDER_COMMAND
if ([string]::IsNullOrWhiteSpace($builderCommand)) {
throw 'BUILDER_COMMAND is empty. Set it as a Gitea secret. Example: .\\tools\\rdgen-cli.exe build --config "{{config}}" --version "{{version}}" --output "{{output}}"'
}
Write-Host "Resolving latest RustDesk release..."
$latestVersion = Get-LatestRustDeskVersion
Write-Host "Latest version: $latestVersion"
$lastBuiltVersion = Get-LastBuiltVersion -StateFilePath $stateFilePath
if ($lastBuiltVersion) {
Write-Host "Last built version: $lastBuiltVersion"
} else {
Write-Host "No previous built version found."
}
if ($lastBuiltVersion -eq $latestVersion) {
Write-Host "No new version detected. Skipping build."
exit 0
}
Write-Host "New version detected. Running custom builder..."
Build-CustomRustDesk -Version $latestVersion -ConfigPath $configPath -OutputDir $outputDir -BuilderCommandTemplate $builderCommand
# Publish a small marker so humans can quickly check what is deployed.
$versionMarkerPath = Join-Path $outputDir "latest-version.txt"
Set-Content -Path $versionMarkerPath -Value $latestVersion -Encoding Ascii
$deployMode = if ($env:DEPLOY_MODE) { $env:DEPLOY_MODE } else { "smb" }
Publish-Artifacts -OutputDir $outputDir -Mode $deployMode
Set-LastBuiltVersion -StateFilePath $stateFilePath -Version $latestVersion
Write-Host "Pipeline completed successfully."

View File

@@ -0,0 +1,45 @@
function Publish-Artifacts {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$OutputDir,
[Parameter(Mandatory = $true)]
[ValidateSet("smb", "scp")]
[string]$Mode
)
if (-not (Test-Path -LiteralPath $OutputDir)) {
throw "Output directory does not exist: $OutputDir"
}
switch ($Mode) {
"smb" {
if ([string]::IsNullOrWhiteSpace($env:DESTINATION_PATH)) {
throw "DESTINATION_PATH is required for DEPLOY_MODE=smb"
}
New-Item -Path $env:DESTINATION_PATH -ItemType Directory -Force | Out-Null
Copy-Item -Path (Join-Path $OutputDir "*") -Destination $env:DESTINATION_PATH -Recurse -Force
Write-Host "Artifacts copied to SMB/local path: $($env:DESTINATION_PATH)"
}
"scp" {
if ([string]::IsNullOrWhiteSpace($env:SCP_DESTINATION)) {
throw "SCP_DESTINATION is required for DEPLOY_MODE=scp"
}
$scpCommand = Get-Command scp -ErrorAction SilentlyContinue
if (-not $scpCommand) {
throw "scp is not available on this runner. Install OpenSSH client or use DEPLOY_MODE=smb."
}
& scp -r (Join-Path $OutputDir "*") $env:SCP_DESTINATION
if ($LASTEXITCODE -ne 0) {
throw "scp publish failed with exit code $LASTEXITCODE"
}
Write-Host "Artifacts copied using SCP to: $($env:SCP_DESTINATION)"
}
}
}

View File

@@ -0,0 +1,17 @@
function Set-LastBuiltVersion {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$StateFilePath,
[Parameter(Mandatory = $true)]
[string]$Version
)
$parent = Split-Path -Parent $StateFilePath
if (-not (Test-Path -LiteralPath $parent)) {
New-Item -Path $parent -ItemType Directory -Force | Out-Null
}
Set-Content -LiteralPath $StateFilePath -Value $Version -Encoding Ascii
}