Back to skills
extension
Category: Productivity & OfficeNo API key required

build-windows-rustdesk-client

自动完成自定义 RustDesk Windows 客户端的编译全流程,且保证用户的服务器地址、密码、密钥等敏感信息不会泄露。 核心流程: 向用户收集 ID 服务器、中继服务器、API 服务器、公钥、固定密码等配置 检查本机 git 和 GitHub CLI(gh)是否已登录认证 创建 GitHub Actions 工作流文件(文件中只含 ${{ secrets.XXX }} 引用,不写明文) 以 rustdesk-YYYYMMDD 格式创建公开 GitHub 仓库 通过 gh secret set 将用户的配置存入 GitHub Secrets(加密存储,仓库中不可见,日志自动打码) 推送 workflow 到仓库,触发 Actions 编译 返回 Actions 运行链接供用户下载产物 安全性设计: 由于免费版 GitHub Actions 要求仓库必须是公开的,如果直接把服务器地址、密码写在 YAML 文件里推送到公开仓库,会导致信息泄露。本技能通过 GitHub Secrets 机制解决:YAML 中只写 ${{ secrets.XXX }},真实值通过 gh secret set 单独存入加密的 Secrets。运行时 GitHub 自动解密注入,日志中也会自动遮盖敏感字段。 内置故障处理: YAML 缩进错误、LLVM 版本冲突、vcpkg 依赖缺失、bridge 生成失败、私库计费问题等常见编译失败的诊断和修复方案。

personAuthor: zhm1321391634hubModelScope

Build Custom RustDesk Client

This skill automates building a custom Windows RustDesk client via GitHub Actions. It captures everything learned from a verified end-to-end build process.

Workflow Overview

User gives config → You create workflow YAML → Create git repo → Push → Trigger Actions → Return URL

Step 1: Collect Configuration from User

Ask the user for these values. All are required unless noted. If they don't provide one, ask clearly.

| Variable | Example | Notes | |---|---|---|---| | ID_SERVER | your-server.com | The rendezvous/ID server hostname (no protocol) | | RELAY_SERVER | your-server.com | The relay server hostname (usually same as ID server) | | API_SERVER | http://your-server.com:21114 | Full URL with protocol and port. Optional — if user doesn't have one, set to "" | | PUBLIC_KEY | ==YourServerPublicKeyHere== | The server's public key string. Optional — set to "" to disable encryption | | FIXED_PASSWORD | YourPassword123 | Password for unattended access. Optional — set to "" for no fixed password |

Default everything to empty strings if user doesn't specify, EXCEPT ID_SERVER and RELAY_SERVER which MUST be provided.

Ask: "Do you also need an API server URL (for user management) and a fixed password (for unattended access)?"

Step 2: Check Git and GitHub Authentication

Before doing anything else, verify the environment is ready:

# Check git config
git config --global user.name
git config --global user.email

# Check gh auth
gh auth status

If gh is not logged in: Tell the user they need to run gh auth login in their terminal, then tell you when it's done. Wait for them to confirm. Do NOT proceed without authentication.

If git user.name/email is not set: Tell the user to set them:

git config --global user.name "YourName"
git config --global user.email "your@email.com"

Step 3: Create the Workflow YAML

Read the template from references/workflow-template.yml. Do NOT replace the ${{ secrets.XXX }} placeholders — they MUST remain as-is in the YAML file. The actual values will be stored in GitHub Secrets (Step 4.5).

The workflow reads secrets at runtime via ${{ secrets.ID_SERVER }} etc. This means the YAML file contains zero sensitive data even though the repo is public.

Step 4: Create Repository

Generate a date-based repo name:

$date = Get-Date -Format "yyyyMMdd"
$repoName = "rustdesk-$date"

Create the repo as PUBLIC (required for free GitHub Actions):

gh repo create $repoName --public --description "Custom RustDesk Windows build"

If the command fails with "already exists", append a counter: rustdesk-20260722-2, etc.

Step 4.5: Store Config in GitHub Secrets (Security Critical)

This prevents sensitive data from being visible in the public repo. After creating the repo, use gh secret set to store each value:

gh secret set ID_SERVER --repo $repoName --body "<id_server_value>"
gh secret set RELAY_SERVER --repo $repoName --body "<relay_server_value>"
gh secret set API_SERVER --repo $repoName --body "<api_server_value>"
gh secret set FIXED_PASSWORD --repo $repoName --body "<fixed_password_value>"
gh secret set PUBLIC_KEY --repo $repoName --body "<public_key_value>"

GitHub Secrets are encrypted and:

  • Never visible in the repository UI or file content
  • Automatically masked in Actions logs (even if accidentally printed)
  • Only accessible to Actions runners at runtime
  • The ${{ secrets.XXX }} references in the YAML file are expanded by GitHub at runtime, not stored in the repo

Do NOT skip this step. Without it, the workflow will fail because the secret references in the YAML will be empty.

Step 5: Initialize Git and Push

# Initialize repo
git init
git branch -m main

# Create directory structure
New-Item -ItemType Directory -Path ".github\workflows" -Force

# Write the workflow YAML
# (Use the template with placeholders replaced)

Write the workflow file to .github\workflows\build-rustdesk-windows.yml.

# Commit and push
git add -A
git commit -m "init: custom RustDesk build"
git remote add origin "https://github.com/<username>/$repoName.git"
git push -u origin main

Step 6: Trigger Build

gh workflow run build-rustdesk-windows.yml --repo <username>/$repoName

Wait a few seconds, then verify:

gh run list --repo <username>/$repoName --limit 1 --json name,status,conclusion,databaseId

Return the Actions URL to the user:

gh run view --repo <username>/$repoName --json url

Known Failure Modes

If the build fails, here's how to diagnose and fix:

YAML parsing error (0-second failure)

  • Check the workflow YAML for indentation issues in run: | blocks
  • All lines in a YAML literal block scalar must have SAME or MORE indentation than the first line
  • The python3 << 'PYEOF' here-doc approach avoids this; make sure it's used for the config patching step

LLVM version conflict

  • The windows-2022 runner has LLVM 20.x pre-installed
  • The workflow uses KyleMayes/install-llvm-action@v2.0.9 which handles this
  • Do NOT change to choco install llvm

vcpkg ffmpeg headers missing

  • Symptom: fatal error C1083: Cannot open include file: 'libavutil/pixfmt.h'
  • Fix confirmed: The rm -rf "$VCPKG_ROOT/installed/x64-windows-static" before vcpkg install is critical
  • Also ensure VCPKG_BINARY_SOURCES and VCPKG_DEFAULT_HOST_TRIPLET env vars are set

Bridge generation fails

  • The flutter-rust-bridge codegen MUST run on Linux (ubuntu-22.04), NOT on Windows
  • Must use Flutter 3.22.3 for bridge generation (not the 3.24.5 used for Windows build)
  • The sed line s/extended_text: 14.0.0/extended_text: 13.0.0/g is required for Flutter 3.22.3 compat

option_env!("FIXED_PASSWORD") not found

  • This error means the injected Rust code references a function that doesn't exist
  • The load_marker string must EXACTLY match fn load() -> Config {\n let mut config = Config::load_::<Config>("");
  • If upstream changes this signature, update the marker string
  • Do NOT use a separate default_password() function — inject the check inline

Private repo billing

  • Free GitHub accounts cannot run Actions on private repos
  • Always create repo as PUBLIC
  • If user later wants it private, they can change after the artifact is downloaded

Important: Upstream Verification

Before writing the workflow, verify the current upstream config.rs patterns match. Use:

gh api repos/rustdesk/hbb_common/contents/src/config.rs | jq -r '.content' | base64 -d | grep -E "PROD_RENDEZVOUS_SERVER|DEFAULT_SETTINGS|RS_PUB_KEY"

If the patterns differ from what's in the template, update the Python regex patterns in the workflow template accordingly. The three critical patterns:

# Pattern 1: PROD_RENDEZVOUS_SERVER
r'pub static ref PROD_RENDEZVOUS_SERVER: RwLock<String> = RwLock::new\(""\.to_owned\(\)\);'

# Pattern 2: DEFAULT_SETTINGS
r'pub static ref DEFAULT_SETTINGS: RwLock<HashMap<String, String>> = Default::default\(\);'

# Pattern 3: RS_PUB_KEY
r'pub const RS_PUB_KEY: &str = ".*?";'

# Pattern 4: Config::load() marker (must match exactly)
'fn load() -> Config {\n        let mut config = Config::load_::<Config>("");'

Also verify the fn load() -> Config marker hasn't changed:

gh api repos/rustdesk/hbb_common/contents/src/config.rs | jq -r '.content' | base64 -d | grep -A1 "^fn load()"