前端: - 「我的构建」列表「查看详情/查看进度」按钮补充与其他按钮同风格的眼睛小图标 - 顶部导航增加 768px 以下移动端适配: 顶栏纵向排列, 导航单行横向滑动, 修复手机端错位 Gitea Actions 工作流 (仅 .gitea, 不动 .github): - 5 个生成器工作流全部补充 /updategh 终态状态回调 (成功/取消/失败均回调), 修复无回调导致构建 6 小时后被误判为超时的 P0 缺陷 - Windows 两个工作流 job 级默认 shell 设为 pwsh, 兼容 act 在 Windows 默认 bash - 修复 VCPKG_BINARY_SOURCES 中 \v 的 YAML 非法转义 (双引号改单引号) - Flutter 补丁路径改为 .rdgen-src/.github/patches/, run-on-arch 容器内用 /workspace 前缀 - run-on-arch githubToken 置空, 避免 Gitea token 拉 ghcr.io 失败 - runs-on 标签对齐现场 runner: ubuntu-22.04 改为 ubuntu-24.04 - run-vcpkg/setup-ndk 钉到 node20 运行时 SHA, 规避 node24 action 不兼容 - decrypt-secrets 的 pip 安装增加三级回退; cleanzip 请求增加超时重试 - 所有产物上传 curl 增加 --fail/超时/重试参数 - macOS 动态定位 *.app 不再硬编码 RustDesk.app, DMG 路径改用 GITHUB_WORKSPACE - 修复签名条件判断、PowerShell 变量空格等小问题 - setup.md 更新 runner 标签表及 Windows/macOS host 准备要求
1638 lines
94 KiB
YAML
1638 lines
94 KiB
YAML
# Gitea Actions 适配副本:由 .github/workflows 同名文件适配(本地路径/cache/artifact 差异),修改时请同步两边。差异说明见 setup.md「Gitea 部署附录」。
|
|
name: Custom Linux Client Generator
|
|
run-name: Custom Linux Client Generator
|
|
on:
|
|
workflow_dispatch:
|
|
inputs:
|
|
version:
|
|
description: 'version to buld'
|
|
required: true
|
|
default: ''
|
|
type: string
|
|
zip_url:
|
|
description: 'url to zip of json'
|
|
required: true
|
|
default: ''
|
|
type: string
|
|
|
|
env:
|
|
SCITER_RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503, also 1.78 has ABI change which causes our sciter version not working, https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
|
|
RUST_VERSION: "1.75" # sciter failed on m1 with 1.78 because of https://blog.rust-lang.org/2024/03/30/i128-layout-update.html
|
|
CARGO_NDK_VERSION: "3.1.2"
|
|
SCITER_ARMV7_CMAKE_VERSION: "3.29.7"
|
|
SCITER_NASM_DEBVERSION: "2.14-1"
|
|
LLVM_VERSION: "15.0.6"
|
|
FLUTTER_VERSION: "3.24.5"
|
|
ANDROID_FLUTTER_VERSION: "3.24.5"
|
|
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
|
|
# for arm64 linux because official Dart SDK does not work
|
|
FLUTTER_ELINUX_VERSION: "3.16.9"
|
|
TAG_NAME: "${{ inputs.upload-tag }}"
|
|
VCPKG_BINARY_SOURCES: "clear;files,/opt/vcpkg-cache,readwrite"
|
|
# vcpkg version: 2024.07.12
|
|
VCPKG_COMMIT_ID: "${{ inputs.version == 'master' && '9e593bb18ea69cc5095e012465dcd675a822ed0d' || '120deac3062162151622ca4860575a33844ba10b' }}"
|
|
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
|
|
VERSION: "${{ inputs.version }}"
|
|
NDK_VERSION: "r28c"
|
|
#signing keys env variable checks
|
|
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
|
|
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
|
|
UPLOAD_ARTIFACT: 'true'
|
|
SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}"
|
|
STATUS_URL: "${{ secrets.GENURL }}/updategh"
|
|
|
|
jobs:
|
|
setup:
|
|
uses: ./.gitea/workflows/fetch-encrypted-secrets.yml
|
|
with:
|
|
zip_url_json: ${{ inputs.zip_url }}
|
|
|
|
generate-bridge-linux:
|
|
uses: ./.gitea/workflows/bridge.yml
|
|
with:
|
|
version: ${{ inputs.version }}
|
|
|
|
build-rustdesk-linux:
|
|
needs: [generate-bridge-linux, setup]
|
|
name: build rustdesk linux ${{ matrix.job.target }}
|
|
runs-on: ${{ matrix.job.on }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
# use a high level qemu-user-static
|
|
job:
|
|
- {
|
|
arch: x86_64,
|
|
target: x86_64-unknown-linux-gnu,
|
|
distro: ubuntu18.04,
|
|
on: ubuntu-24.04,
|
|
deb_arch: amd64,
|
|
vcpkg-triplet: x64-linux,
|
|
}
|
|
- {
|
|
arch: aarch64,
|
|
target: aarch64-unknown-linux-gnu,
|
|
distro: ubuntu18.04,
|
|
on: ubuntu-24.04-arm,
|
|
deb_arch: arm64,
|
|
vcpkg-triplet: arm64-linux,
|
|
}
|
|
steps:
|
|
- name: Checkout Repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
path: .rdgen-src
|
|
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: encrypted-secrets-zip
|
|
|
|
- name: Load Secrets
|
|
uses: ./.gitea/actions/decrypt-secrets
|
|
with:
|
|
zip_password: ${{ secrets.ZIP_PASSWORD }}
|
|
|
|
|
|
- name: Set rdgen value
|
|
if: ${{ env.rdgen == 'true' }}
|
|
run: |
|
|
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV
|
|
|
|
- name: Set rdgen value
|
|
if: ${{ env.rdgen == 'false' }}
|
|
run: |
|
|
echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV
|
|
|
|
- name: Maximize build space
|
|
run: |
|
|
sudo rm -rf /opt/ghc
|
|
sudo rm -rf /usr/local/lib/android
|
|
sudo rm -rf /usr/share/dotnet
|
|
sudo apt-get update -y
|
|
sudo apt-get install -y nasm
|
|
if [[ "${{ matrix.job.arch }}" == "x86_64" ]]; then
|
|
sudo apt-get install -y qemu-user-static
|
|
fi
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y imagemagick
|
|
|
|
- name: Checkout source code
|
|
if: ${{ env.VERSION != 'master' }}
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: rustdesk/rustdesk
|
|
ref: refs/tags/${{ env.VERSION }}
|
|
submodules: recursive
|
|
|
|
- name: Checkout source code
|
|
if: ${{ env.VERSION == 'master' }}
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: rustdesk/rustdesk
|
|
submodules: recursive
|
|
|
|
- name: Set Swap Space
|
|
if: ${{ matrix.job.arch == 'x86_64' }}
|
|
uses: pierotofy/set-swap-space@master
|
|
with:
|
|
swap-size-gb: 12
|
|
|
|
- name: Free Space
|
|
run: |
|
|
df -h
|
|
free -m
|
|
|
|
- name: Install Rust toolchain
|
|
uses: dtolnay/rust-toolchain@v1
|
|
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
|
|
with:
|
|
toolchain: ${{ env.RUST_VERSION }}
|
|
targets: ${{ matrix.job.target }}
|
|
components: "rustfmt"
|
|
|
|
- name: Save Rust toolchain version
|
|
run: |
|
|
RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
|
|
echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV
|
|
|
|
- name: Disable rust bridge build
|
|
run: |
|
|
# only build cdylib
|
|
sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml
|
|
|
|
- name: Restore bridge files
|
|
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: bridge-artifact
|
|
path: ./
|
|
|
|
- name: Setup vcpkg with Github Actions binary cache
|
|
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
|
|
uses: lukka/run-vcpkg@1737e223b9c3f4fb07a7cf1cfa123cc243fcc0dd # v11 pinned to node20 runtime
|
|
with:
|
|
vcpkgDirectory: /opt/artifacts/vcpkg
|
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
|
doNotCache: false
|
|
|
|
- name: Install vcpkg dependencies
|
|
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
|
|
run: |
|
|
sudo apt install -y libva-dev && apt show libva-dev
|
|
if ! $VCPKG_ROOT/vcpkg \
|
|
install \
|
|
--triplet ${{ matrix.job.vcpkg-triplet }} \
|
|
--x-install-root="$VCPKG_ROOT/installed"; then
|
|
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
|
|
echo "$_1:"
|
|
echo "======"
|
|
cat "$_1"
|
|
echo "======"
|
|
echo ""
|
|
done
|
|
exit 1
|
|
fi
|
|
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
|
|
shell: bash
|
|
|
|
- name: icon stuff
|
|
if: ${{ env.iconlink_url != 'false' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
mv ./res/icon.ico ./res/icon.ico.bak
|
|
mv ./res/icon.png ./res/icon.png.bak
|
|
mv ./res/tray-icon.ico ./res/tray-icon.ico.bak
|
|
wget -O ./res/icon.png ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }}
|
|
mv ./res/32x32.png ./res/32x32.png.bak
|
|
mv ./res/64x64.png ./res/64x64.png.bak
|
|
mv ./res/128x128.png ./res/128x128.png.bak
|
|
mv ./res/128x128@2x.png ./res/128x128@2x.png.bak
|
|
convert ./res/icon.png -define icon:auto-resize=256,64,48,32,16 ./res/icon.ico
|
|
convert ./res/icon.png -define icon:auto-resize=256,64,48,32,16 ./res/tray-icon.ico
|
|
cp ./res/icon.ico ./res/tray-icon.ico
|
|
convert ./res/icon.png -resize 32x32 ./res/32x32.png
|
|
convert ./res/icon.png -resize 64x64 ./res/64x64.png
|
|
convert ./res/icon.png -resize 128x128 ./res/128x128.png
|
|
convert ./res/128x128.png -resize 200% ./res/128x128@2x.png
|
|
cp ./src/ui.rs ./src/ui.rs.bak
|
|
b64=$(base64 < ./res/icon.png)
|
|
sed -i -e 's|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|$(echo "$b64")|' ./src/ui.rs
|
|
b64=""
|
|
|
|
- name: change appname to custom
|
|
if: env.appname != 'rustdesk'
|
|
continue-on-error: true
|
|
shell: bash
|
|
run: |
|
|
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml
|
|
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml
|
|
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml
|
|
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml
|
|
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs
|
|
find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \;
|
|
sed -i -e '/-p tmpdeb\/usr\/lib\/rustdesk/d' ./build.py
|
|
|
|
- name: change company name
|
|
if: env.compname != 'Purslane Ltd'
|
|
continue-on-error: true
|
|
shell: bash
|
|
run: |
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./Cargo.toml
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./src/ui/index.tis
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./src/main.rs
|
|
sed -i -e 's|Purslane Ltd.|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
|
|
sed -i -e 's|Purslane Ltd.|${{ env.compname }}|' ./Cargo.toml
|
|
sed -i -e 's|Purslane Ltd.|${{ env.compname }}|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|Purslane Ltd.|${{ env.compname }}|' ./src/ui/index.tis
|
|
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./src/main.rs
|
|
|
|
- name: set server, serverPort, key, and apiserver
|
|
continue-on-error: true
|
|
shell: bash
|
|
env:
|
|
SERVER_DOMAIN: ${{ env.server }}
|
|
SERVER_PORT_INPUT: ${{ env.serverPort }}
|
|
SERVER_KEY: ${{ env.key }}
|
|
API_SERVER: ${{ env.apiServer }}
|
|
run: |
|
|
# Pass secrets via bash env vars to avoid quoting issues
|
|
if [ ! -f "./libs/hbb_common/src/config.rs" ]; then
|
|
echo "Error: config.rs not found!"
|
|
exit 1
|
|
fi
|
|
if [ ! -f "./src/common.rs" ]; then
|
|
echo "Error: common.rs not found!"
|
|
exit 1
|
|
fi
|
|
|
|
# Port must be a number; views.py defaults it to 21116
|
|
if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then
|
|
echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'"
|
|
exit 1
|
|
fi
|
|
|
|
# Derive the port block from the rendezvous port
|
|
# (defaults 21116/21117/21118/21119)
|
|
SERVER_PORT=$SERVER_PORT_INPUT
|
|
RELAY_PORT=$((SERVER_PORT + 1))
|
|
WS_RENDEZVOUS_PORT=$((RELAY_PORT + 1))
|
|
WS_RELAY_PORT=$((WS_RENDEZVOUS_PORT + 1))
|
|
|
|
echo "Setting server: $SERVER_DOMAIN"
|
|
echo "Setting ports: $SERVER_PORT, $RELAY_PORT, $WS_RENDEZVOUS_PORT, $WS_RELAY_PORT"
|
|
|
|
sed -i -e "s|rs-ny.rustdesk.com|$SERVER_DOMAIN|" ./libs/hbb_common/src/config.rs
|
|
|
|
# Match on numeric value so the step is independent of defaults
|
|
sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const RENDEZVOUS_PORT: i32 = $SERVER_PORT;|" ./libs/hbb_common/src/config.rs
|
|
sed -i -e "s|pub const RELAY_PORT: i32 = [0-9][0-9]*;|pub const RELAY_PORT: i32 = $RELAY_PORT;|" ./libs/hbb_common/src/config.rs
|
|
sed -i -e "s|pub const WS_RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const WS_RENDEZVOUS_PORT: i32 = $WS_RENDEZVOUS_PORT;|" ./libs/hbb_common/src/config.rs
|
|
sed -i -e "s|pub const WS_RELAY_PORT: i32 = [0-9][0-9]*;|pub const WS_RELAY_PORT: i32 = $WS_RELAY_PORT;|" ./libs/hbb_common/src/config.rs
|
|
|
|
sed -i -e "s|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|$SERVER_KEY|" ./libs/hbb_common/src/config.rs
|
|
sed -i -e "s|https://admin.rustdesk.com|$API_SERVER|" ./src/common.rs
|
|
|
|
echo "=== Verification ==="
|
|
grep -nE "RENDEZVOUS_PORT|RELAY_PORT|WS_" ./libs/hbb_common/src/config.rs
|
|
|
|
- name: allow custom.txt
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
cp .rdgen-src/.github/patches/allowCustom.py .
|
|
python allowCustom.py
|
|
cp .rdgen-src/.github/patches/removeSetupServerTip.diff .
|
|
git apply removeSetupServerTip.diff
|
|
# sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml
|
|
echo -n '${{ env.custom }}' > ./custom_.txt
|
|
|
|
- name: change url to custom
|
|
if: env.urlLink != 'https://rustdesk.com'
|
|
continue-on-error: true
|
|
shell: bash
|
|
run: |
|
|
sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py
|
|
sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart
|
|
sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart
|
|
sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart
|
|
sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart
|
|
sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart
|
|
sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart
|
|
|
|
- name: change download link to custom
|
|
if: env.downloadLink != 'https://rustdesk.com/download'
|
|
continue-on-error: true
|
|
shell: bash
|
|
run: |
|
|
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart
|
|
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart
|
|
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis
|
|
|
|
- name: fix connection delay
|
|
continue-on-error: true
|
|
if: ${{ env.delayFix == 'true' }}
|
|
shell: bash
|
|
run: |
|
|
# Precise fix: only extend the direct-connection condition with the
|
|
# key check, instead of globally replacing !key.is_empty() with
|
|
# false (which would also disable TCP encryption and UDP logic).
|
|
sed -i -e 's#if is_local || peer_nat_type == NatType::SYMMETRIC {#if is_local || peer_nat_type == NatType::SYMMETRIC || !key.is_empty() {#' ./src/client.rs
|
|
grep -n "key.is_empty()" ./src/client.rs || true
|
|
|
|
- name: use X for offline display instead of orange circle
|
|
if: env.xOffline == 'true'
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
cp .rdgen-src/.github/patches/xoffline.diff .
|
|
git apply xoffline.diff
|
|
|
|
- name: hide-cm
|
|
if: env.hidecm == 'true'
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
cp .rdgen-src/.github/patches/hidecm.diff .
|
|
git apply hidecm.diff
|
|
|
|
- name: removeNewVersionNotif
|
|
continue-on-error: true
|
|
if: env.removeNewVersionNotif == 'true'
|
|
run: |
|
|
sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart
|
|
sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs
|
|
|
|
- name: Restore bridge files
|
|
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: bridge-artifact
|
|
path: ./
|
|
|
|
- uses: rustdesk-org/run-on-arch-action@d3fcfbb632b84cf7f6bc772bfaaa2c2f4f8789a8
|
|
name: Build rustdesk
|
|
id: vcpkg
|
|
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
|
|
with:
|
|
arch: ${{ matrix.job.arch }}
|
|
distro: ${{ matrix.job.distro }}
|
|
# Gitea Actions tokens cannot authenticate against ghcr.io; leave it
|
|
# empty so run-on-arch pulls the image anonymously.
|
|
githubToken: ''
|
|
setup: |
|
|
ls -l "${PWD}"
|
|
ls -l /opt/artifacts/vcpkg/installed
|
|
dockerRunArgs: |
|
|
--volume "${PWD}:/workspace"
|
|
--volume "/opt/artifacts:/opt/artifacts"
|
|
shell: /bin/bash
|
|
install: |
|
|
apt-get update -y
|
|
echo -e "installing deps"
|
|
apt-get install -y \
|
|
build-essential \
|
|
clang \
|
|
cmake \
|
|
curl \
|
|
gcc \
|
|
git \
|
|
g++ \
|
|
imagemagick \
|
|
libayatana-appindicator3-dev \
|
|
libasound2-dev \
|
|
libclang-10-dev \
|
|
libgstreamer1.0-dev \
|
|
libgstreamer-plugins-base1.0-dev \
|
|
libgtk-3-dev \
|
|
libpam0g-dev \
|
|
libpulse-dev \
|
|
libva-dev \
|
|
libxcb-randr0-dev \
|
|
libxcb-shape0-dev \
|
|
libxcb-xfixes0-dev \
|
|
libxdo-dev \
|
|
libxfixes-dev \
|
|
llvm-10-dev \
|
|
nasm \
|
|
ninja-build \
|
|
pkg-config \
|
|
tree \
|
|
python3 \
|
|
rpm \
|
|
unzip \
|
|
wget \
|
|
xz-utils \
|
|
libssl-dev
|
|
# we have libopus compiled by us.
|
|
apt-get remove -y libopus-dev || true
|
|
# output devs
|
|
ls -l ./
|
|
tree -L 3 /opt/artifacts/vcpkg/installed
|
|
run: |
|
|
# disable git safe.directory
|
|
git config --global --add safe.directory "*"
|
|
# rust
|
|
pushd /opt
|
|
# do not use rustup, because memory overflow in qemu
|
|
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}.tar.gz
|
|
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
|
|
cd rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }} && ./install.sh
|
|
rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-${{ matrix.job.target }}
|
|
# edit config
|
|
mkdir -p ~/.cargo/
|
|
echo """
|
|
[source.crates-io]
|
|
registry = 'https://github.com/rust-lang/crates.io-index'
|
|
""" > ~/.cargo/config
|
|
cat ~/.cargo/config
|
|
# start build
|
|
pushd /workspace
|
|
export VCPKG_ROOT=/opt/artifacts/vcpkg
|
|
if [[ "${{ matrix.job.arch }}" == "aarch64" ]]; then
|
|
export JOBS="--jobs 3"
|
|
else
|
|
export JOBS=""
|
|
fi
|
|
echo $JOBS
|
|
|
|
# Make the right unit stop when the services is stopped by tray
|
|
sed -ie "s|\(systemctl\s\+\S\+\s\){app_name}|\1${{ env.appname }}|g" src/platform/linux.rs
|
|
sed -ie 's|\({home}/{p}/{app_name0}2\?.toml\)|\\"\1\\"|g' src/platform/linux.rs
|
|
sed -ie 's|\(/root/{p}/\)|\\"\1\\"|g' src/platform/linux.rs
|
|
|
|
cargo build --lib $JOBS --features hwcodec,flutter,unix-file-copy-paste --release
|
|
rm -rf target/release/deps target/release/build
|
|
rm -rf ~/.cargo
|
|
|
|
# Setup Flutter
|
|
# disable git safe.directory
|
|
git config --global --add safe.directory "*"
|
|
pushd /workspace
|
|
case ${{ matrix.job.arch }} in
|
|
aarch64)
|
|
export PATH=/opt/flutter-elinux/bin:$PATH
|
|
sed -i "s/flutter build linux --release/flutter-elinux build linux --verbose/g" ./build.py
|
|
sed -i "s/x64\/release/arm64\/release/g" ./build.py
|
|
;;
|
|
x86_64)
|
|
export PATH=/opt/flutter/bin:$PATH
|
|
;;
|
|
esac
|
|
popd
|
|
pushd /opt
|
|
wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz
|
|
tar xf flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz
|
|
case ${{ matrix.job.arch }} in
|
|
aarch64)
|
|
# clone repo and reset to flutter ${{ env.FLUTTER_VERSION }}
|
|
git clone https://github.com/sony/flutter-elinux.git || true
|
|
pushd flutter-elinux
|
|
git fetch
|
|
git reset --hard ${{ env.FLUTTER_VERSION }}
|
|
bin/flutter-elinux doctor -v
|
|
bin/flutter-elinux precache --linux
|
|
popd
|
|
cp -R flutter/bin/cache/artifacts/engine/linux-x64/shader_lib flutter-elinux/flutter/bin/cache/artifacts/engine/linux-arm64
|
|
rm -rf flutter
|
|
;;
|
|
x86_64)
|
|
flutter doctor -v
|
|
;;
|
|
esac
|
|
|
|
if [[ "3.24.5" == ${{ env.FLUTTER_VERSION }} ]]; then
|
|
case ${{ matrix.job.arch }} in
|
|
aarch64)
|
|
pushd /opt/flutter-elinux/flutter
|
|
;;
|
|
x86_64)
|
|
pushd /opt/flutter
|
|
;;
|
|
esac
|
|
# Inside the run-on-arch container the host workspace is mounted
|
|
# at /workspace, and rdgen itself lives in .rdgen-src.
|
|
git apply /workspace/.rdgen-src/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff
|
|
popd
|
|
fi
|
|
|
|
# build flutter
|
|
pushd /workspace
|
|
mkdir output
|
|
chmod 777 output -R
|
|
export CARGO_INCREMENTAL=0
|
|
export DEB_ARCH=${{ matrix.job.deb_arch }}
|
|
mkdir -p flutter/tmpdeb/usr/share/rustdesk
|
|
cp ./custom_.txt ./flutter/tmpdeb/usr/share/rustdesk/custom_.txt
|
|
if [[ "${{ env.logolink_url }}" != "false" ]]; then
|
|
wget -O ./flutter/assets/logo.png ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }}
|
|
fi
|
|
if [[ "${{ env.iconlink_url }}" != "false" ]]; then
|
|
mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak
|
|
convert ./res/icon.png ./flutter/assets/icon.svg
|
|
convert ./res/128x128.png -resize 200% ./flutter/assets/128x128@2x.png || true
|
|
cp ./flutter/assets/icon.svg ./res/scalable.svg
|
|
pushd ./flutter
|
|
if [[ "${{ matrix.job.arch }}" == "aarch64" ]]; then
|
|
export PATH="/opt/flutter-elinux/flutter/bin:$PATH"
|
|
fi
|
|
flutter pub get
|
|
dart run flutter_launcher_icons
|
|
popd
|
|
fi
|
|
|
|
# Only run the whitelabeling steps if the custom app name is actually different
|
|
if [ "${{ env.appname }}" != "rustdesk" ]; then
|
|
# Change files names in the output .deb
|
|
sed -ie "s|\(cp .* \)tmpdeb/usr/share/rustdesk/files/systemd/|\1tmpdeb/usr/share/rustdesk/files/systemd/${{ env.appname }}.service|g" build.py
|
|
sed -ie "s|cp -r \(.\+\)\* \([^ ]\+\)/rustdesk|mv \1rustdesk \1${{ env.appname }}; cp -r \1\* \2/${{ env.appname }}|g" build.py
|
|
sed -ie "s|\(tmpdeb/[^ ]*\)rustdesk|\1${{ env.appname }}|g" build.py
|
|
# The rename above is greedy in \1, so it rewrites only the LAST
|
|
# "rustdesk" of each path and leaves build.py's globs pointing where the
|
|
# staging tree never exists. Three corrections, all confined to the drm
|
|
# code paths (assert_staged_binary_is_drm, measured_glibc_floor); the
|
|
# stock packaging path never executes those lines.
|
|
#
|
|
# 1. Directory component: the cp -r rename above stages into
|
|
# tmpdeb/usr/share/<appname>/, but the globs kept usr/share/rustdesk/.
|
|
sed -ie "s|tmpdeb/usr/share/rustdesk/|tmpdeb/usr/share/${{ env.appname }}/|g" build.py
|
|
# 2. Library filename: build.py renames only the executable and copies the
|
|
# bundle verbatim, so the cdylib stays librustdesk.so. The greedy sed
|
|
# renamed it to lib<appname>.so, and that is the file carrying the drm
|
|
# marker -- the thin Flutter launcher does not.
|
|
sed -ie "s|lib/lib${{ env.appname }}\.so|lib/librustdesk.so|g" build.py
|
|
# 3. libdrmtap must NOT be renamed: drmtap_dl.rs dlopens the absolute path
|
|
# /usr/lib/rustdesk/libdrmtap.so.0, compiled into the binary and
|
|
# rewritten by nothing here. Staging it under /usr/lib/<appname>/
|
|
# packages and installs cleanly, then never loads on the user's machine.
|
|
sed -ie "s|tmpdeb/usr/lib/${{ env.appname }}|tmpdeb/usr/lib/rustdesk|g" build.py
|
|
sed -ie "s|Package: rustdesk|Package: ${{ env.appname }}|g" build.py
|
|
sed -ie "s|RustDesk|${{ env.appname }}|g" res/rustdesk.desktop
|
|
FILES=$(sed -ne "s~.*cp\s\(../\)\?\+\(.\+\.\(desktop\|service\)\)\s\+tmpdeb/.*~\2~p" build.py | sort | uniq)
|
|
FILES=$(echo -e "$FILES\n$(ls --color=never res/DEBIAN/*)")
|
|
for FILE in $(echo $FILES | sed "s/\n/ /g"); do
|
|
sed -ie "s|rustdesk|${{ env.appname }}|g" $FILE
|
|
done
|
|
for file in "./res/rpm-flutter.spec" "./res/rpm-flutter-suse.spec"; do
|
|
sed -ie "s|^\(URL:\s*\).*|\1${{ env.downloadLink }}|g" "$file"
|
|
sed -ie "s|^\(Vendor:\s*\).*|\1${{ env.compname }}|g" "$file"
|
|
sed -ie '/\${\?HBB}\?/!{/\(^Name:\|\/usr\|\/etc\|systemctl\)/s|rustdesk|${{ env.appname }}|g}' "$file"
|
|
sed -ie '/\${\?HBB}\?/{/^install/s|\(\/usr\/.*\/\)rustdesk|\1${{ env.appname }}|g}' "$file"
|
|
sed -ie '/\${\?HBB}\?/{/^mkdir/s|rustdesk|${{ env.appname }}|g}' "$file"
|
|
for extension in ".desktop" "-link.desktop" ".service"; do
|
|
sed -ie "/^%files$/i\mv %{buildroot}/usr/share/${{ env.appname }}/files/rustdesk$extension %{buildroot}/usr/share/${{ env.appname }}/files/${{ env.appname }}$extension" "$file"
|
|
sed -ie "/^%files$/i\sed -ie 's|RustDesk|${{ env.appname }}|g' %{buildroot}/usr/share/${{ env.appname }}/files/${{ env.appname }}$extension" "$file"
|
|
sed -ie "/^%files$/i\sed -ie 's|rustdesk|${{ env.appname }}|g' %{buildroot}/usr/share/${{ env.appname }}/files/${{ env.appname }}$extension" "$file"
|
|
done
|
|
sed -ie '/^%files$/i\\n' "$file"
|
|
done
|
|
fi
|
|
|
|
python3 ./build.py --flutter --skip-cargo
|
|
for name in *.deb; do
|
|
mv "$name" /workspace/output/"${{ env.filename }}-${{ matrix.job.arch }}.deb"
|
|
done
|
|
|
|
# rpm/suse/arch package the flutter BUNDLE (not tmpdeb), so the deb's
|
|
# custom_.txt never reaches them. Copy it into the bundle too, so the
|
|
# baked password/permissions land in every format.
|
|
BUNDLE=flutter/build/linux/x64/release/bundle
|
|
[ "${{ matrix.job.arch }}" = "aarch64" ] && BUNDLE=flutter/build/linux/arm64/release/bundle
|
|
cp ./custom_.txt "$BUNDLE/custom_.txt" || echo "WARN: could not copy custom_.txt into bundle"
|
|
ls -l "$BUNDLE/custom_.txt" || echo "WARN: custom_.txt missing from bundle ($BUNDLE)"
|
|
|
|
# rpm package
|
|
echo -e "start packaging fedora package"
|
|
pushd /workspace
|
|
case ${{ matrix.job.arch }} in
|
|
aarch64)
|
|
sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter.spec
|
|
;;
|
|
esac
|
|
HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb
|
|
pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }}
|
|
for name in *.rpm; do
|
|
mv "$name" /workspace/output/"${{ env.filename }}-${{ matrix.job.arch }}.rpm"
|
|
done
|
|
|
|
# rpm suse package
|
|
echo -e "start packaging suse package"
|
|
pushd /workspace
|
|
case ${{ matrix.job.arch }} in
|
|
aarch64)
|
|
sed -i "s/linux\/x64/linux\/arm64/g" ./res/rpm-flutter-suse.spec
|
|
;;
|
|
esac
|
|
HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb
|
|
pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }}
|
|
for name in *.rpm; do
|
|
mv "$name" /workspace/output/"${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm"
|
|
done
|
|
|
|
# only x86_64 for arch since we can not find newest arm64 docker image to build
|
|
# old arch image does not make sense for arch since it is "arch" which always update to date
|
|
# and failed to makepkg arm64 on x86_64
|
|
- name: Patch archlinux PKGBUILD
|
|
continue-on-error: true
|
|
if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true' && env.VERSION != 'master'
|
|
run: |
|
|
sed -i "s/x86_64/${{ matrix.job.arch }}/g" res/PKGBUILD
|
|
if [[ "${{ matrix.job.arch }}" == "aarch64" ]]; then
|
|
sed -i "s/x86_64/aarch64/g" ./res/PKGBUILD
|
|
fi
|
|
|
|
- name: Build archlinux package
|
|
continue-on-error: true
|
|
if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true' && env.VERSION != 'master'
|
|
uses: rustdesk-org/arch-makepkg-action@master
|
|
with:
|
|
packages:
|
|
scripts: |
|
|
cd res && HBB=`pwd`/.. FLUTTER=1 makepkg -f
|
|
|
|
- name: Rename archlinux package
|
|
continue-on-error: true
|
|
if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true' && env.VERSION != 'master'
|
|
run: |
|
|
cp ./res/rustdesk-${{ env.VERSION }}-0-x86_64.pkg.tar.zst ./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst
|
|
|
|
- name: send file to rdgen server
|
|
if: ${{ env.rdgen == 'true' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 10
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.deb" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.rpm" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true
|
|
|
|
- name: send file to api server
|
|
if: ${{ env.rdgen == 'false' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 10
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.deb" ${{ env.apiServer }}/api/save_custom_client
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.rpm" ${{ env.apiServer }}/api/save_custom_client
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" ${{ env.apiServer }}/api/save_custom_client
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" ${{ env.apiServer }}/api/save_custom_client || true
|
|
|
|
- name: Upload deb
|
|
uses: actions/upload-artifact@v4
|
|
if: env.UPLOAD_ARTIFACT == 'true'
|
|
with:
|
|
name: ${{ env.filename }}-${{ matrix.job.arch }}.deb
|
|
path: ./output/${{ env.filename }}-${{ matrix.job.arch }}.deb
|
|
|
|
build-rustdesk-linux-drm:
|
|
needs: [generate-bridge-linux, setup]
|
|
name: build rustdesk linux drm x86_64
|
|
runs-on: ubuntu-24.04
|
|
# Only rustdesk's master branch carries the drm/libdrmtap support this job
|
|
# drives: build.py at tag 1.4.9 has neither build_libdrmtap_so nor
|
|
# assert_staged_binary_is_drm, so a versioned build dies at
|
|
# AttributeError: module 'b' has no attribute 'build_libdrmtap_so'
|
|
# One failed job marks the whole run failed, so a client that was in fact
|
|
# built got reported as a build failure. No other job consumes this one's
|
|
# artifacts (deploy needs only build-rustdesk-linux, build-flatpak and
|
|
# build-appimage). Gated the way bridge.yml already tells the two sources
|
|
# apart.
|
|
if: ${{ inputs.version == 'master' }}
|
|
steps:
|
|
- name: Checkout Repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
path: .rdgen-src
|
|
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: encrypted-secrets-zip
|
|
|
|
- name: Load Secrets
|
|
uses: ./.gitea/actions/decrypt-secrets
|
|
with:
|
|
zip_password: ${{ secrets.ZIP_PASSWORD }}
|
|
|
|
|
|
- name: Set rdgen value
|
|
if: ${{ env.rdgen == 'true' }}
|
|
run: |
|
|
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV
|
|
|
|
- name: Set rdgen value
|
|
if: ${{ env.rdgen == 'false' }}
|
|
run: |
|
|
echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV
|
|
|
|
- name: Maximize build space
|
|
run: |
|
|
sudo rm -rf /opt/ghc
|
|
sudo rm -rf /usr/local/lib/android
|
|
sudo rm -rf /usr/share/dotnet
|
|
sudo apt-get update -y
|
|
sudo apt-get install -y nasm qemu-user-static
|
|
|
|
- name: Install dependencies
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y imagemagick
|
|
|
|
- name: Checkout source code
|
|
if: ${{ env.VERSION != 'master' }}
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: rustdesk/rustdesk
|
|
ref: refs/tags/${{ env.VERSION }}
|
|
submodules: recursive
|
|
|
|
- name: Checkout source code
|
|
if: ${{ env.VERSION == 'master' }}
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: rustdesk/rustdesk
|
|
submodules: recursive
|
|
|
|
- name: Detect DRM support
|
|
run: |
|
|
if grep -q "'--drm'" build.py; then
|
|
echo "DRM_SUPPORTED=true" >> $GITHUB_ENV
|
|
echo "::notice::DRM/KMS capture backend detected in this RustDesk version"
|
|
else
|
|
echo "DRM_SUPPORTED=false" >> $GITHUB_ENV
|
|
echo "::notice::DRM/KMS capture backend not present in this RustDesk version, skipping DRM build"
|
|
fi
|
|
|
|
- name: Set Swap Space
|
|
uses: pierotofy/set-swap-space@master
|
|
with:
|
|
swap-size-gb: 12
|
|
|
|
- name: Free Space
|
|
run: |
|
|
df -h
|
|
free -m
|
|
|
|
- name: Install Rust toolchain
|
|
uses: dtolnay/rust-toolchain@v1
|
|
with:
|
|
toolchain: ${{ env.RUST_VERSION }}
|
|
targets: x86_64-unknown-linux-gnu
|
|
components: "rustfmt"
|
|
|
|
- name: Save Rust toolchain version
|
|
run: |
|
|
RUST_TOOLCHAIN_VERSION=$(cargo --version | awk '{print $2}')
|
|
echo "RUST_TOOLCHAIN_VERSION=$RUST_TOOLCHAIN_VERSION" >> $GITHUB_ENV
|
|
|
|
- name: Disable rust bridge build
|
|
run: |
|
|
# only build cdylib
|
|
sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml
|
|
|
|
- name: Restore bridge files
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: bridge-artifact
|
|
path: ./
|
|
|
|
- name: Setup vcpkg with Github Actions binary cache
|
|
uses: lukka/run-vcpkg@1737e223b9c3f4fb07a7cf1cfa123cc243fcc0dd # v11 pinned to node20 runtime
|
|
with:
|
|
vcpkgDirectory: /opt/artifacts/vcpkg
|
|
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
|
|
doNotCache: false
|
|
|
|
- name: Install vcpkg dependencies
|
|
run: |
|
|
sudo apt install -y libva-dev && apt show libva-dev
|
|
if ! $VCPKG_ROOT/vcpkg \
|
|
install \
|
|
--triplet x64-linux \
|
|
--x-install-root="$VCPKG_ROOT/installed"; then
|
|
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
|
|
echo "$_1:"
|
|
echo "======"
|
|
cat "$_1"
|
|
echo "======"
|
|
echo ""
|
|
done
|
|
exit 1
|
|
fi
|
|
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-x64-linux-rel-out.log" || true
|
|
shell: bash
|
|
|
|
- name: Build libdrmtap
|
|
if: env.DRM_SUPPORTED == 'true'
|
|
run: |
|
|
sudo apt-get install -y meson ninja-build pkg-config \
|
|
libdrm-dev libegl1-mesa-dev libgles2-mesa-dev
|
|
python3 - <<'PY'
|
|
import importlib.util, sys
|
|
spec = importlib.util.spec_from_file_location("b", "build.py")
|
|
b = importlib.util.module_from_spec(spec)
|
|
sys.argv = ["build.py"]
|
|
spec.loader.exec_module(b)
|
|
# build_libdrmtap_so() exists only in recent RustDesk sources
|
|
# (master); tagged 1.4.x releases don't have it - skip gracefully
|
|
# instead of failing the whole drm job.
|
|
if hasattr(b, 'build_libdrmtap_so'):
|
|
print(f"::notice::built {b.build_libdrmtap_so()}")
|
|
else:
|
|
print("::notice::build_libdrmtap_so not present in this RustDesk version, skipping")
|
|
PY
|
|
shell: bash
|
|
|
|
- name: icon stuff
|
|
if: ${{ env.iconlink_url != 'false' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
mv ./res/icon.ico ./res/icon.ico.bak
|
|
mv ./res/icon.png ./res/icon.png.bak
|
|
mv ./res/tray-icon.ico ./res/tray-icon.ico.bak
|
|
wget -O ./res/icon.png ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }}
|
|
mv ./res/32x32.png ./res/32x32.png.bak
|
|
mv ./res/64x64.png ./res/64x64.png.bak
|
|
mv ./res/128x128.png ./res/128x128.png.bak
|
|
mv ./res/128x128@2x.png ./res/128x128@2x.png.bak
|
|
convert ./res/icon.png -define icon:auto-resize=256,64,48,32,16 ./res/icon.ico
|
|
convert ./res/icon.png -define icon:auto-resize=256,64,48,32,16 ./res/tray-icon.ico
|
|
cp ./res/icon.ico ./res/tray-icon.ico
|
|
convert ./res/icon.png -resize 32x32 ./res/32x32.png
|
|
convert ./res/icon.png -resize 64x64 ./res/64x64.png
|
|
convert ./res/icon.png -resize 128x128 ./res/128x128.png
|
|
convert ./res/128x128.png -resize 200% ./res/128x128@2x.png
|
|
cp ./src/ui.rs ./src/ui.rs.bak
|
|
b64=$(base64 < ./res/icon.png)
|
|
sed -i -e 's|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|$(echo "$b64")|' ./src/ui.rs
|
|
b64=""
|
|
|
|
- name: change appname to custom
|
|
if: env.appname != 'rustdesk'
|
|
continue-on-error: true
|
|
shell: bash
|
|
run: |
|
|
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml
|
|
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml
|
|
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml
|
|
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml
|
|
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs
|
|
find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \;
|
|
sed -i -e '/-p tmpdeb\/usr\/lib\/rustdesk/d' ./build.py
|
|
|
|
- name: change company name
|
|
if: env.compname != 'Purslane Ltd'
|
|
continue-on-error: true
|
|
shell: bash
|
|
run: |
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./Cargo.toml
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./src/ui/index.tis
|
|
sed -i -e 's|Purslane Tech Pte. Ltd.|${{ env.compname }}|' ./src/main.rs
|
|
sed -i -e 's|Purslane Ltd.|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
|
|
sed -i -e 's|Purslane Ltd.|${{ env.compname }}|' ./Cargo.toml
|
|
sed -i -e 's|Purslane Ltd.|${{ env.compname }}|' ./libs/portable/Cargo.toml
|
|
sed -i -e 's|Purslane Ltd.|${{ env.compname }}|' ./src/ui/index.tis
|
|
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./src/main.rs
|
|
|
|
- name: set server, serverPort, key, and apiserver
|
|
continue-on-error: true
|
|
shell: bash
|
|
env:
|
|
SERVER_DOMAIN: ${{ env.server }}
|
|
SERVER_PORT_INPUT: ${{ env.serverPort }}
|
|
SERVER_KEY: ${{ env.key }}
|
|
API_SERVER: ${{ env.apiServer }}
|
|
run: |
|
|
# Pass secrets via bash env vars to avoid quoting issues
|
|
if [ ! -f "./libs/hbb_common/src/config.rs" ]; then
|
|
echo "Error: config.rs not found!"
|
|
exit 1
|
|
fi
|
|
if [ ! -f "./src/common.rs" ]; then
|
|
echo "Error: common.rs not found!"
|
|
exit 1
|
|
fi
|
|
|
|
# Port must be a number; views.py defaults it to 21116
|
|
if ! [[ "$SERVER_PORT_INPUT" =~ ^[0-9]+$ ]]; then
|
|
echo "Error: serverPort must be a number, got: '$SERVER_PORT_INPUT'"
|
|
exit 1
|
|
fi
|
|
|
|
# Derive the port block from the rendezvous port
|
|
# (defaults 21116/21117/21118/21119)
|
|
SERVER_PORT=$SERVER_PORT_INPUT
|
|
RELAY_PORT=$((SERVER_PORT + 1))
|
|
WS_RENDEZVOUS_PORT=$((RELAY_PORT + 1))
|
|
WS_RELAY_PORT=$((WS_RENDEZVOUS_PORT + 1))
|
|
|
|
echo "Setting server: $SERVER_DOMAIN"
|
|
echo "Setting ports: $SERVER_PORT, $RELAY_PORT, $WS_RENDEZVOUS_PORT, $WS_RELAY_PORT"
|
|
|
|
sed -i -e "s|rs-ny.rustdesk.com|$SERVER_DOMAIN|" ./libs/hbb_common/src/config.rs
|
|
|
|
# Match on numeric value so the step is independent of defaults
|
|
sed -i -e "s|pub const RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const RENDEZVOUS_PORT: i32 = $SERVER_PORT;|" ./libs/hbb_common/src/config.rs
|
|
sed -i -e "s|pub const RELAY_PORT: i32 = [0-9][0-9]*;|pub const RELAY_PORT: i32 = $RELAY_PORT;|" ./libs/hbb_common/src/config.rs
|
|
sed -i -e "s|pub const WS_RENDEZVOUS_PORT: i32 = [0-9][0-9]*;|pub const WS_RENDEZVOUS_PORT: i32 = $WS_RENDEZVOUS_PORT;|" ./libs/hbb_common/src/config.rs
|
|
sed -i -e "s|pub const WS_RELAY_PORT: i32 = [0-9][0-9]*;|pub const WS_RELAY_PORT: i32 = $WS_RELAY_PORT;|" ./libs/hbb_common/src/config.rs
|
|
|
|
sed -i -e "s|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|$SERVER_KEY|" ./libs/hbb_common/src/config.rs
|
|
sed -i -e "s|https://admin.rustdesk.com|$API_SERVER|" ./src/common.rs
|
|
|
|
echo "=== Verification ==="
|
|
grep -nE "RENDEZVOUS_PORT|RELAY_PORT|WS_" ./libs/hbb_common/src/config.rs
|
|
|
|
- name: allow custom.txt
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
cp .rdgen-src/.github/patches/allowCustom.py .
|
|
python allowCustom.py
|
|
cp .rdgen-src/.github/patches/removeSetupServerTip.diff .
|
|
git apply removeSetupServerTip.diff
|
|
echo -n '${{ env.custom }}' > ./custom_.txt
|
|
|
|
- name: change url to custom
|
|
if: env.urlLink != 'https://rustdesk.com'
|
|
continue-on-error: true
|
|
shell: bash
|
|
run: |
|
|
sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py
|
|
sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart
|
|
sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart
|
|
sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart
|
|
sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart
|
|
sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart
|
|
sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart
|
|
|
|
- name: change download link to custom
|
|
if: env.downloadLink != 'https://rustdesk.com/download'
|
|
continue-on-error: true
|
|
shell: bash
|
|
run: |
|
|
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart
|
|
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart
|
|
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis
|
|
|
|
- name: fix connection delay
|
|
continue-on-error: true
|
|
if: ${{ env.delayFix == 'true' }}
|
|
shell: bash
|
|
run: |
|
|
# Precise fix: only extend the direct-connection condition with the
|
|
# key check, instead of globally replacing !key.is_empty() with
|
|
# false (which would also disable TCP encryption and UDP logic).
|
|
sed -i -e 's#if is_local || peer_nat_type == NatType::SYMMETRIC {#if is_local || peer_nat_type == NatType::SYMMETRIC || !key.is_empty() {#' ./src/client.rs
|
|
grep -n "key.is_empty()" ./src/client.rs || true
|
|
|
|
- name: use X for offline display instead of orange circle
|
|
if: env.xOffline == 'true'
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
cp .rdgen-src/.github/patches/xoffline.diff .
|
|
git apply xoffline.diff
|
|
|
|
- name: hide-cm
|
|
if: env.hidecm == 'true'
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
cp .rdgen-src/.github/patches/hidecm.diff .
|
|
git apply hidecm.diff
|
|
|
|
- name: removeNewVersionNotif
|
|
continue-on-error: true
|
|
if: env.removeNewVersionNotif == 'true'
|
|
run: |
|
|
sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart
|
|
sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs
|
|
|
|
- name: Restore bridge files
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: bridge-artifact
|
|
path: ./
|
|
|
|
- uses: rustdesk-org/run-on-arch-action@d3fcfbb632b84cf7f6bc772bfaaa2c2f4f8789a8
|
|
if: env.DRM_SUPPORTED == 'true'
|
|
name: Build rustdesk
|
|
id: vcpkg
|
|
with:
|
|
arch: x86_64
|
|
distro: ubuntu18.04
|
|
# Gitea Actions tokens cannot authenticate against ghcr.io; leave it
|
|
# empty so run-on-arch pulls the image anonymously.
|
|
githubToken: ''
|
|
setup: |
|
|
ls -l "${PWD}"
|
|
ls -l /opt/artifacts/vcpkg/installed
|
|
dockerRunArgs: |
|
|
--volume "${PWD}:/workspace"
|
|
--volume "/opt/artifacts:/opt/artifacts"
|
|
shell: /bin/bash
|
|
install: |
|
|
apt-get update -y
|
|
echo -e "installing deps"
|
|
apt-get install -y \
|
|
build-essential \
|
|
clang \
|
|
cmake \
|
|
curl \
|
|
gcc \
|
|
git \
|
|
g++ \
|
|
imagemagick \
|
|
libayatana-appindicator3-dev \
|
|
libasound2-dev \
|
|
libclang-10-dev \
|
|
libgstreamer1.0-dev \
|
|
libgstreamer-plugins-base1.0-dev \
|
|
libgtk-3-dev \
|
|
libpam0g-dev \
|
|
libpulse-dev \
|
|
libva-dev \
|
|
libxcb-randr0-dev \
|
|
libxcb-shape0-dev \
|
|
libxcb-xfixes0-dev \
|
|
libxdo-dev \
|
|
libxfixes-dev \
|
|
llvm-10-dev \
|
|
nasm \
|
|
ninja-build \
|
|
pkg-config \
|
|
tree \
|
|
python3 \
|
|
rpm \
|
|
unzip \
|
|
wget \
|
|
xz-utils \
|
|
libssl-dev
|
|
# we have libopus compiled by us.
|
|
apt-get remove -y libopus-dev || true
|
|
ls -l ./
|
|
tree -L 3 /opt/artifacts/vcpkg/installed
|
|
run: |
|
|
git config --global --add safe.directory "*"
|
|
pushd /opt
|
|
wget -O rust.tar.gz https://static.rust-lang.org/dist/rust-${{env.RUST_TOOLCHAIN_VERSION}}-x86_64-unknown-linux-gnu.tar.gz
|
|
tar -zxvf rust.tar.gz > /dev/null && rm rust.tar.gz
|
|
cd rust-${{env.RUST_TOOLCHAIN_VERSION}}-x86_64-unknown-linux-gnu && ./install.sh
|
|
rm -rf rust-${{env.RUST_TOOLCHAIN_VERSION}}-x86_64-unknown-linux-gnu
|
|
mkdir -p ~/.cargo/
|
|
echo """
|
|
[source.crates-io]
|
|
registry = 'https://github.com/rust-lang/crates.io-index'
|
|
""" > ~/.cargo/config
|
|
cat ~/.cargo/config
|
|
|
|
# Start Cargo compilation with DRM features
|
|
pushd /workspace
|
|
export VCPKG_ROOT=/opt/artifacts/vcpkg
|
|
export DRMTAP_PREBUILT_DIR=/workspace/third_party/libdrmtap/build-pkg
|
|
FEATURES=$(python3 ./build.py --flutter --drm --hwcodec --unix-file-copy-paste --print-features)
|
|
for want in drm drm-wake; do
|
|
case ",$FEATURES," in
|
|
*",$want,"*) ;;
|
|
*) echo "::error::build.py returned no '$want' feature: $FEATURES"; exit 1 ;;
|
|
esac
|
|
done
|
|
|
|
sed -ie "s|\(systemctl\s\+\S\+\s\){app_name}|\1${{ env.appname }}|g" src/platform/linux.rs
|
|
sed -ie 's|\({home}/{p}/{app_name0}2\?.toml\)|\\"\1\\"|g' src/platform/linux.rs
|
|
sed -ie 's|\(/root/{p}/\)|\\"\1\\"|g' src/platform/linux.rs
|
|
|
|
cargo build --locked --lib --features "$FEATURES" --release
|
|
rm -rf target/release/deps target/release/build
|
|
rm -rf ~/.cargo
|
|
|
|
# Setup Flutter
|
|
git config --global --add safe.directory "*"
|
|
export PATH=/opt/flutter/bin:$PATH
|
|
pushd /opt
|
|
wget https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz
|
|
tar xf flutter_linux_${{ env.FLUTTER_VERSION }}-stable.tar.xz
|
|
flutter doctor -v
|
|
|
|
if [[ "3.24.5" == ${{ env.FLUTTER_VERSION }} ]]; then
|
|
pushd /opt/flutter
|
|
# Inside the run-on-arch container the host workspace is mounted
|
|
# at /workspace, and rdgen itself lives in .rdgen-src.
|
|
git apply /workspace/.rdgen-src/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff
|
|
popd
|
|
fi
|
|
|
|
# Build Flutter Bundle & Deb
|
|
pushd /workspace
|
|
mkdir -p output
|
|
chmod 777 output -R
|
|
export CARGO_INCREMENTAL=0
|
|
export DEB_ARCH=amd64
|
|
mkdir -p flutter/tmpdeb/usr/share/rustdesk
|
|
cp ./custom_.txt ./flutter/tmpdeb/usr/share/rustdesk/custom_.txt
|
|
if [[ "${{ env.logolink_url }}" != "false" ]]; then
|
|
wget -O ./flutter/assets/logo.png ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }}
|
|
fi
|
|
if [[ "${{ env.iconlink_url }}" != "false" ]]; then
|
|
mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak
|
|
convert ./res/icon.png ./flutter/assets/icon.svg
|
|
convert ./res/128x128.png -resize 200% ./flutter/assets/128x128@2x.png || true
|
|
cp ./flutter/assets/icon.svg ./res/scalable.svg
|
|
pushd ./flutter
|
|
flutter pub get
|
|
dart run flutter_launcher_icons
|
|
popd
|
|
fi
|
|
|
|
if [ "${{ env.appname }}" != "rustdesk" ]; then
|
|
sed -ie "s|\(cp .* \)tmpdeb/usr/share/rustdesk/files/systemd/|\1tmpdeb/usr/share/rustdesk/files/systemd/${{ env.appname }}.service|g" build.py
|
|
sed -ie "s|cp -r \(.\+\)\* \([^ ]\+\)/rustdesk|mv \1rustdesk \1${{ env.appname }}; cp -r \1\* \2/${{ env.appname }}|g" build.py
|
|
sed -ie "s|\(tmpdeb/[^ ]*\)rustdesk|\1${{ env.appname }}|g" build.py
|
|
# The rename above is greedy in \1, so it rewrites only the LAST
|
|
# "rustdesk" of each path and leaves build.py's globs pointing where the
|
|
# staging tree never exists. Three corrections, all confined to the drm
|
|
# code paths (assert_staged_binary_is_drm, measured_glibc_floor); the
|
|
# stock packaging path never executes those lines.
|
|
#
|
|
# 1. Directory component: the cp -r rename above stages into
|
|
# tmpdeb/usr/share/<appname>/, but the globs kept usr/share/rustdesk/.
|
|
sed -ie "s|tmpdeb/usr/share/rustdesk/|tmpdeb/usr/share/${{ env.appname }}/|g" build.py
|
|
# 2. Library filename: build.py renames only the executable and copies the
|
|
# bundle verbatim, so the cdylib stays librustdesk.so. The greedy sed
|
|
# renamed it to lib<appname>.so, and that is the file carrying the drm
|
|
# marker -- the thin Flutter launcher does not.
|
|
sed -ie "s|lib/lib${{ env.appname }}\.so|lib/librustdesk.so|g" build.py
|
|
# 3. libdrmtap must NOT be renamed: drmtap_dl.rs dlopens the absolute path
|
|
# /usr/lib/rustdesk/libdrmtap.so.0, compiled into the binary and
|
|
# rewritten by nothing here. Staging it under /usr/lib/<appname>/
|
|
# packages and installs cleanly, then never loads on the user's machine.
|
|
sed -ie "s|tmpdeb/usr/lib/${{ env.appname }}|tmpdeb/usr/lib/rustdesk|g" build.py
|
|
sed -ie "s|Package: rustdesk|Package: ${{ env.appname }}|g" build.py
|
|
sed -ie "s|RustDesk|${{ env.appname }}|g" res/rustdesk.desktop
|
|
FILES=$(sed -ne "s~.*cp\s\(../\)\?\+\(.\+\.\(desktop\|service\)\)\s\+tmpdeb/.*~\2~p" build.py | sort | uniq)
|
|
FILES=$(echo -e "$FILES\n$(ls --color=never res/DEBIAN/*)")
|
|
for FILE in $(echo $FILES | sed "s/\n/ /g"); do
|
|
sed -ie "s|rustdesk|${{ env.appname }}|g" $FILE
|
|
done
|
|
fi
|
|
|
|
python3 ./build.py --flutter --drm --hwcodec --unix-file-copy-paste --skip-cargo
|
|
for name in *.deb; do
|
|
mv "$name" /workspace/output/"${{ env.filename }}-unattended-wayland-x86_64.deb"
|
|
done
|
|
|
|
- name: send file to rdgen server
|
|
if: ${{ env.rdgen == 'true' && env.DRM_SUPPORTED == 'true' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 10
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-unattended-wayland-x86_64.deb" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
|
|
|
|
- name: send file to api server
|
|
if: ${{ env.rdgen == 'false' && env.DRM_SUPPORTED == 'true' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 10
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-unattended-wayland-x86_64.deb" ${{ env.apiServer }}/api/save_custom_client
|
|
|
|
- name: Upload deb
|
|
uses: actions/upload-artifact@v4
|
|
if: ${{ env.UPLOAD_ARTIFACT == 'true' && env.DRM_SUPPORTED == 'true' }}
|
|
with:
|
|
name: ${{ env.filename }}-unattended-wayland-x86_64.deb
|
|
path: ./output/${{ env.filename }}-unattended-wayland-x86_64.deb
|
|
|
|
build-appimage:
|
|
name: Build appimage ${{ matrix.job.target }}
|
|
needs: [build-rustdesk-linux]
|
|
runs-on: ubuntu-24.04
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- { target: x86_64-unknown-linux-gnu, arch: x86_64 }
|
|
- { target: aarch64-unknown-linux-gnu, arch: aarch64 }
|
|
steps:
|
|
- name: Checkout Repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
path: .rdgen-src
|
|
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: encrypted-secrets-zip
|
|
|
|
- name: Load Secrets
|
|
uses: ./.gitea/actions/decrypt-secrets
|
|
with:
|
|
zip_password: ${{ secrets.ZIP_PASSWORD }}
|
|
|
|
- name: Checkout source code
|
|
if: ${{ env.VERSION != 'master' }}
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: rustdesk/rustdesk
|
|
ref: refs/tags/${{ env.VERSION }}
|
|
submodules: recursive
|
|
|
|
- name: Checkout source code
|
|
if: ${{ env.VERSION == 'master' }}
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: rustdesk/rustdesk
|
|
submodules: recursive
|
|
|
|
- name: Download Binary
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: ${{ env.filename }}-${{ matrix.job.arch }}.deb
|
|
path: .
|
|
|
|
- name: Rename Binary
|
|
run: |
|
|
mv ${{ env.filename }}-${{ matrix.job.arch }}.deb appimage/rustdesk.deb
|
|
|
|
- name: icon stuff
|
|
if: ${{ env.iconlink_url != 'false' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 5
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
mv ./res/icon.ico ./res/icon.ico.bak
|
|
mv ./res/icon.png ./res/icon.png.bak
|
|
mv ./res/tray-icon.ico ./res/tray-icon.ico.bak
|
|
wget -O ./res/icon.png ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }}
|
|
mv ./res/32x32.png ./res/32x32.png.bak
|
|
mv ./res/64x64.png ./res/64x64.png.bak
|
|
mv ./res/128x128.png ./res/128x128.png.bak
|
|
mv ./res/128x128@2x.png ./res/128x128@2x.png.bak
|
|
convert ./res/icon.png -define icon:auto-resize=256,64,48,32,16 ./res/icon.ico
|
|
convert ./res/icon.png -define icon:auto-resize=256,64,48,32,16 ./res/tray-icon.ico
|
|
cp ./res/icon.ico ./res/tray-icon.ico
|
|
convert ./res/icon.png -resize 32x32 ./res/32x32.png
|
|
convert ./res/icon.png -resize 64x64 ./res/64x64.png
|
|
convert ./res/icon.png -resize 128x128 ./res/128x128.png
|
|
convert ./res/128x128.png -resize 200% ./res/128x128@2x.png
|
|
cp ./src/ui.rs ./src/ui.rs.bak
|
|
b64=$(base64 < ./res/icon.png)
|
|
sed -i -e 's|iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAEiuAABIrgHwmhA7AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAEx9JREFUeJztnXmYHMV5h9+vZnZ0rHYRum8J4/AErQlgAQbMsRIWBEFCjK2AgwTisGILMBFCIMug1QLiPgIYE/QY2QQwiMVYjoSlODxEAgLEHMY8YuUEbEsOp3Z1X7vanf7yR8/MztEz0zPTPTO7M78/tnurvqn6uuqdr6q7a7pFVelrkpaPhhAMTEaYjJHDUWsEARkODANGAfWgINEPxLb7QNtBPkdoR7Ud0T8iphUTbtXp4z8pyQH5KOntAEhL2yCCnALW6aAnIDQAI+3MqFHkGJM73BkCO93JXnQnsAl4C8MGuoIv69mj2rw9ouKq1wEgzRiO2noSlp6DoRHleISgnQkJnRpLw0sI4v9X4H2E9Yj172zf+2udOflgYUdYXPUaAOTpzxoImJkIsxG+YCfG+Z7cecWDIN5+J8hqjNXCIW3rdMqULvdHWBqVNQDS8tlwNPCPKJcjOslOjGZGt2UHQTStHZGnMPxQG8d9mOk4S6myBEBWbj0aZR7ILISBPRlZOiMlr+QQgGAhvITqg0ybsEZjhZWHygoA+VnbaSBLEaY6dgb0Vgii+h2GO2gcv7JcQCgLAOSp7ZNBlyI6sycR+igEILoRdJFOnfgCJVZJAZCf7pxETfhmlIsQjHNH9VkIAF0H1iKdetjvKJFKAoC0EODA9msQvQUYmL2j8uwMJ/uygwAL0dvZMHGJNmFRZBUdAHlix5dQfQw4IbeO6tMQgOgybZx4I0VW0QCQ5dQQ2v4DhO8Dofw6qk9DEIZwg0497H8ookwxKpEV7WOo2fES0IQSAnrmwBrXEhq/lcR5cnJasm1KWq5lx9knl5NvvW7877EPIMFZFFm+AyA/2Xk6EngbOCVtA1chsO1V/4oiyzcABERW7FiI6osoo2IZVQicy7HtwxRZQT8KlWaCjNm5AiOzY+Oe0jPuqdjjXjQttpWe8TMhT0Djxs/ktGRbCi07g4/kWW/C8afxX/htAc2elzyPAPIQ/Ri7cyXCbBfjXjUS9Nh2IeEnKLI8BUB+1DaI/jvXoJwfS6xC4FxOcr2i12vjpM0UWZ6dBsry/aOh61fAMfmfCyfllfoU0Y2P+dab6P/d+rVx11MCeQKALN8zDA1vAJlc+AWRpLw+D4Hcp9PHLqBEKngIkBXtdVjWWlQmA4XMgBPTymU4cONj3vXKvaXsfCgQAGkhRGfoOZDjgHwnP3F5FQXBvTp97HWUWHkDIM0Y2nY/C5zpwQw4Lq8SINC79azSdz4UEgGG7l4CnOfJDDglr09DcK/+dWkmfE7KaxIoD++aDmYtaMCDGbBtXxETQ7lXzx5dFt/8qHIGQB7eORENvI0w1E4pZAacZN+XIUDu1XPKq/MhRwDkp/Rn7+7XQY6xE6I5ZQ/BbrB+j8gWkC2g7cBeAtJFdA2GyqGIDkUYA0xAtAEYkrFstxAY7tIZY26gDJXbvYDd+5qRuM7XyBbBt+vjONgnl0NKvZtRXYewAfRtvjX8Q00cwV1JWraNRbqPRbURkTOAoxGRnHzE3KUzRpVl50MOEUAe2H88Yr0GBEu/esapHPkjWE+CPKOzh25ydVA5Sp5vHw3hbwIXInoSEvEgnY/C7Xru6MV++AIgL245FmMuQmhArQ7EvInK4zpt3Meuy3ADgDQT4tC9b6EclbbzSgOBgq5B9T7mDNuQz7c8X8kv2o9Auq8C5gB1ST5uQ/VKPW/MSl/qbmkNMbTun1G+69A2BxDma+OER12V5QqA+/c2Y1jSk5BQYSkgUGAlAb3Zr2+7W8na7fV0dH0To18G3YOwkfrOn2vjpA5f6mtpDTGk7jmUv8n4BYFLdOqEf81aXjYA5L49R2DMRtCa1A6iFBC8glgLdM7QNzM63gclaz/sR03/51DOdREld9PV9Rd65uFbM5WZ/UKQBG5DqbEnenHp6S7yuL8gkrmceHs7bT8Wi/jzoY0V2fktrSHMgGdRzgXcXKSqpya0hCzKGAHkngNfwVivJ052nM6z8TsSvALM1ssHb8l2QH1Rsn5zfzprnkf0bDshPhMyRIIuAqZBTxv3QbqyM0eAgHUbINkvu+JjJNDlhAefUbGd39Ia4kBNC3B2HpfUa+i2bstYfroIIPftn4HyQgnX1nchXKFXDM46kemrkvWb+9MRWgV6lp0Qzchp0qyY8MnaOOkNpzrSRwAL+1cqpVlC1YnFhRXd+Ws/7Mf+fs+hkc6HXOZL8XmCFfxB2nqcIoDcc+AroG9EPh61jDOI33oeCQ6gOkO/M3h9Oqf7uqTlowHUml8C03Nq49h+ShtbqDlSzxj7v8l1OUcAteanHZsT0iI1eBcJurBkZkV3/ppPBzLQ/BvKdCC3Nnayt7cGY33Psb7kCCD3HRhPN39AtIZIWYlb3yKBAhfrd+ufdHK0EiRrPh0IuhqYljZK5h8J9hHS8XrKhB3xdaZGgG6uBGq8WZRBLpHg/oru/OXUoKwCmZYxSuYfCWrpNN9OrjcBAGnGoPT8QLFoEOgGttaX7R2zomjUpw8C010NlflCIFyaXG1iBAh1nAqMdbiq5CcEuyA8W5voTnauUiS/+PgIYG5O86V8IFD9S/mPj4+Jrzt5CLggzQUFByfwBgJlgc4b8n9UsgKBuajYfeE3BAG9IL7qGADSTBD4RoarSg5OUCgEL3FV3QoqXSpHRbaR/0ncegmBpRdI3HSxJwLUdE4FRqQ5jXAuuDAILLrNAk20qEypdvbs+w7BYfz6oxOiSSYu88wkQ58h4An9p9p3qQqEl121sVcQBJgR/bcHAGFaltOI7A66hyBMWG+lKlsHeRyho2gQWDRGdw2ANDMY5egUQ/8geF7n15ft83OLLZ05qo0wz9j/xGf4BsGJ9kWnaAQIHjwdCBTtFzzGuo+qkqQP5dTGhUEQop91EkQBsLTR9WmEWwfTQaDSqlfXO96arGTp+aPfAXm/aBCIPQxE5wDHpjVMKMQTCCr2cm9WKc/k3Mb5QmDpCdADQEPazvMaAhN4mqqcFQ635NXG+UHQYFss2zuScM1nsdyUu1BJ6bF9dbjD52CfWM4mvbZ2MlWllTz/+WZgYl5t7GSfXE58XqBzsKEr0BCjJWKbuPUwEgjrqCqzVP7T3oLvkaCr35EG4h/t4jMEYdlAVZkl1oa0nec1BCINBmRiiqFTwV5AYOQdqsqscMC+OloMCNDDDcoIR0OngguDYKteO6Cy7/q5UlsrYL9tzHcIdIQhdgPIwdCp4HwhsPT3VJVVOnPyQZQ/9CTEb72GQIYbkBEZDZ0KzgcCkc0pR1tVGsnHRXlmkTLcoDIiq6FTwTlDwBaqcifFfkex/xAMN6B1rmhxKjgnCGQ7VblVW0obgx8QDDEoxoUhBUMgupeq3EnFfraA/xCY3NehOdm7gSAs+6jKpbQjbRsnpEGhEBhUxI1hQoVO9tkgMFKU9xP1DUWaqggQGGwIshoWDEGY/lTlTsqgrG2ckpcfBAaNrMf3GwKRAVTlUjrIVRun5OUMgRqQbWk7z0sILB1BVe6UcHXWVwh2GFTbHQv2GgLDWKpyKZ2QUxun5LmGoN0A7amF+ACBMp6q3Ellgr2N/g8+QdBuEGlPnbSlGHoBQQNVZZU8/ekwkFF5tbGTfSYILN1qCOvWrOvHvIFgjDTvGUZVmaWBKWk7z3sI2g1iPkgxdCrYCwhqQsdSVRbJ8UD6zvMSAsyfDJa1ydEwXp5BoI0OpVcVL5VpPfvgKwQW7xtM8H1XtHgDwdeoKq3kic9rUU5OjcQ+QdBNq9Hb2AZsLQ4EMkVu3zucqpwlwekg/QCH4dhzCNp05qi26PX51gyGXkIQoLvmG1SVThcBqW0c2/cUglaI3nVQeSODoYMzBUAgXEhVKZKWHYegnJN28h3b9woC3oTYbSdrfVGWINn7p8qtnYdTVaIOWBcD9v2SYkCAvUTfBmBA8L+AriJBYFCuoqqYpIUAcE1qR+MXBGGk36sQAUCb2Av6joNh5gqdHHQHwWVyF3VUZWvf9vNROdz1tZjYfp4QiLyrfzd4J8Q/IcSSDWloyVyhk4PZIains6M6GYTow7mWAqltHEvDWwgsa320iB4AjFntWKFTwV5AoIHjqArG77gCmJy2jWNpeAcBsja61wPAAF5D+cixQqeCC4cg/pMVKfnZrkMRWercbr5B8Dk6cn30ozEAtAkLaHF/GlEgBEL1d4Kd4ftBRwJp2s0HCJSf60zC0Y8lLtRUszL1w/gAgbZRV/MMFSz58Y4ZqFySvd08hgBJeJdhIgD38BuI/ITLLwhEFORanc8BKlTy4+3jMPIT9+3mGQSfsGn4q/G+JACgimLJY/6uQ5Ol2hSq2OcESQshCLRg4fybTPAPAovHI0N9TKlr9UM8itLhCwSit2pT8OaUOitEAsKOnf8CeiKQz5enEAi6CQd+lOxTCgB6G22gT2U8jcgHAtE7dWnopuT6KkrLd92JcKmrbyt4C4HynF405KNkl9L8Wsc8mFBAihPkCkGzNocWOddVGZLluxYDCz150ko+EIg+5OSXIwB6N++hvJRQQIoTuIWgSW8JLnWqpxIkIPLIrrtRluU1bjvZ5w7BW3rhiNec/AtmcL0ZVfvlRQpIZEftunu2QuyxZQl5ApbepLcFK/ah0PIQ/ajZ/SjCJWnbLfo/9LSbaqItDvbJtmQoW0g778r87uDrdDVE31QddUbj9uO3ceXYTizR280taQvv45KHto8jGGwBTnTVbhL/4Yh9sq2TfbJtctnKqzpr2Knp/Mz8i11LFgHhlNAT2yc19Nj7iyu68x/ecx6B4DsoibP92D6p7ebbcGBlfBlXxggAIAusxxC5jLhjyEw0N+rtZlnGQvuo5JFdh2KZO4C5jt/g4keCVTpr6Ncz+Zz9N/tB04RiP9whWyQQrq/EzpdmQvLD3dcQNh+gzI2kOnzbI+kpafgRCboQSfvO4Jjv2SIAgCxgDugKJOK9E9GGhXqHuSdrYXlKbjnYgCWXYfQIIIRar6Os0Kb+f/arzqw+NRNi8L4LMXoT6BftxGhm1KpEkcDoLTpr2JKsx+AGAABZwCzQBxCGJFW4Hax5eldgZfpP5y9pJoR2PoDId5LqBTQMrAJ9iJv6v6yJ3xHfJA/sG4lYl6DyPWBs2s4rFQTQyu7tX9arv9hJFrkGAEAWcQjd/C1qNSAEEfMu+1mlD+PLA6BkIbXUdq0BGjM2ov3/FuBZxDxLd807yde8C/bl3j3DCJizUP4B4UzQYNqZd4qPCX76DYGFcIpePOR1V8eVCwDFlCykloFdLwCnu2rEhMaQbaDrgZdB36W74z1tstfAua7/no7DEJ0CHI9YU4EpgHF9+pXiYxb/nezzgUB5UC8dco2bY7Q/UoYARDr/Vyin5dSImTvjE+Aj0M8w8jkW3QR0N4ogMhi0FiPDUGsCMAmJLNFOd53Dfb3u/XeyzwUC5T26O07SuaP341JlB4A0M5Cu7jUIUz17MUIujeimM/Kt118I9iDWCTpnaE7PZC6rR7cldD6kOdUBcDg1ynpBBIe8DOU41evm3ke8ivH0NY38F5Y5uXY+lBEA0sxADnavAaZmP9+FsoagUP8z1evs/x16xeDnyUNlAYA0M4jO8DqQqZ41YqVAYPEC9Yfmvc6i5ADIQmrpCK8GTvW8Efs8BPIG/TsviF/lm6tKOgmUhdQSDEfO80k/sUo+1UmxTWNfLhPDQv13tt9IwJyul9cX9BT2kgEgC6kloGtAG4vSiH0Lgj9BzVd17sBPKVAlGQKkmUGY8LrYM4OKEU77znCwGZjuRedDCQAQQdinT6JyClDcRuz9EGykq+urOveQnncKFaiiDwFyPeeCri5pOO2dw8F/Y8k5emXdNjxU8YcAy5pV8m9Sb4sEsIbAvmledz6UZA4gRwKlD6e9AwIFvYut9V/P5fp+LsqwKtg3daHYbaeQ12pj16tmsf8k2yeXg0O9CWWnqddf/3cizNF5h/yykMbOphIMAfo2UD4Tq3KMBOi7qHWcXlnna+dDKQBQ8yjRh0NUIUiuw0LlAbrqT9arvZvpZ1JJLgTJtSxDdHGZzK7L5exgI8b6tl5d3/PMxiKoNPcC7udGVK5HsdesVXYk6ASa2DloSrE7H0oUAWKVX8dE1FqGyLdwWm4V2yeXb1JviQSK6CosXawL6kr2Yu2yWBEk19KA0TuBcyoDAl5Dwot0ft0rlFhlAUBUch1ngd5AdEVQX4NA+A1Gm3R+7TrKRGUFQFSygKMJWPNQuRihfy+HoAt0FaLL9braFx0PuIQqSwCikvmMpsaaBzILdJKdGM2MbssWgo8RXUE3j+hib+7c+aGyBiBesogGwtZsDBcDo+3EaGaZQKC0Y1iLWC10DFyrTZG3spaxeg0AUcnfE+Cw7tNQcyZGp4JMAYIlgqAb0d+isoGgrqaj/6te/yLJb/U6AJIlN1CHhE9DZSpGjwUagJE+QdCG8D6qbxCQlwn2e1WvZ4/Xx1RM9XoAnCSLGQrdX0LNkYh1GCIjEB2GMhzRUYjU9xgnQLAdQztoO8o2hK0gH2BkE8Fgq34fz2/Hllr/D1DoAB9bI40ZAAAAAElFTkSuQmCC|$(echo "$b64")|' ./src/ui.rs
|
|
b64=""
|
|
|
|
# Embed the PNG into the SVG
|
|
cat <<EOF > ./res/scalable.svg
|
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="$(identify -format "%w" "./res/icon.png")" height="$(identify -format "%h" "./res/icon.png")">
|
|
<image width="$(identify -format "%w" "./res/icon.png")" height="$(identify -format "%h" "./res/icon.png")" xlink:href="data:image/png;base64,$(base64 -w 0 "./res/icon.png")"/>
|
|
</svg>
|
|
EOF
|
|
|
|
- name: Build AppImage
|
|
shell: bash
|
|
run: |
|
|
if [ "${{ env.appname }}" != "rustdesk" ] && [ "${{ env.appname }}" != "RustDesk" ]; then
|
|
# The recipe's script does `bsdtar -zxvf rustdesk.deb`, so after the sed
|
|
# below it looks for <appname>.deb -- the deb must end up under exactly
|
|
# that name. Renaming it to <filename>.deb only matched when appname and
|
|
# filename happened to be equal. The deb is moved out of the way first
|
|
# because `appimage/*` globs it too, and sed over an ar archive rewrites
|
|
# bytes inside it.
|
|
mv appimage/rustdesk.deb "${RUNNER_TEMP}/appimage-payload.deb"
|
|
sed -ie "s|rustdesk|${{ env.appname }}|g" appimage/*
|
|
mv "${RUNNER_TEMP}/appimage-payload.deb" "appimage/${{ env.appname }}.deb"
|
|
fi
|
|
sudo apt-get update -y
|
|
sudo apt-get install -y libarchive-tools libfuse2
|
|
sudo pip3 install "setuptools_scm<10"
|
|
sudo pip3 install git+https://github.com/rustdesk-org/appimage-builder.git
|
|
pushd appimage
|
|
sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml
|
|
# recipe output is <name>-<version>-<arch>.AppImage, where <version> is the
|
|
# version stamped in AppImageBuilder-<arch>.yml -- NOT the workflow's VERSION
|
|
# input. They coincide for a tagged build, which is why globbing on VERSION
|
|
# worked there, but a master build passes VERSION=master while the recipe
|
|
# still carries a release number, so the glob matched nothing:
|
|
# mv: cannot stat './*-master-x86_64.AppImage': No such file or directory
|
|
# Match on the arch suffix instead, and exclude the destination name so this
|
|
# can never try to move a file onto itself.
|
|
dest="${{ env.filename }}-${{ matrix.job.arch }}.AppImage"
|
|
src=$(find . -maxdepth 1 -name "*-${{ matrix.job.arch }}.AppImage" ! -name "$dest" | head -1)
|
|
if [ -z "$src" ]; then
|
|
echo "::error::no *-${{ matrix.job.arch }}.AppImage produced by appimage-builder"; ls -l; exit 1
|
|
fi
|
|
sudo mv "$src" "./$dest"
|
|
popd
|
|
|
|
- name: send file to rdgen server
|
|
if: ${{ env.rdgen == 'true' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 10
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./appimage/${{ env.filename }}-${{ matrix.job.arch }}.AppImage" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
|
|
|
|
- name: send file to api server
|
|
if: ${{ env.rdgen == 'false' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 10
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./appimage/${{ env.filename }}-${{ matrix.job.arch }}.AppImage" ${{ env.apiServer }}/api/save_custom_client
|
|
|
|
build-flatpak:
|
|
name: Build flatpak ${{ matrix.job.target }}${{ matrix.job.suffix }}
|
|
needs:
|
|
- build-rustdesk-linux
|
|
runs-on: ${{ matrix.job.on }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- {
|
|
target: x86_64-unknown-linux-gnu,
|
|
distro: ubuntu22.04,
|
|
on: ubuntu-24.04,
|
|
arch: x86_64,
|
|
suffix: "",
|
|
}
|
|
- {
|
|
target: aarch64-unknown-linux-gnu,
|
|
# try out newer flatpak since error of "error: Nothing matches org.freedesktop.Platform in remote flathub"
|
|
distro: ubuntu22.04,
|
|
on: ubuntu-24.04-arm,
|
|
arch: aarch64,
|
|
suffix: "",
|
|
}
|
|
steps:
|
|
- name: Checkout Repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
path: .rdgen-src
|
|
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: encrypted-secrets-zip
|
|
|
|
- name: Load Secrets
|
|
uses: ./.gitea/actions/decrypt-secrets
|
|
with:
|
|
zip_password: ${{ secrets.ZIP_PASSWORD }}
|
|
|
|
- name: Checkout source code
|
|
if: ${{ env.VERSION != 'master' }}
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: rustdesk/rustdesk
|
|
ref: refs/tags/${{ env.VERSION }}
|
|
submodules: recursive
|
|
|
|
- name: Checkout source code
|
|
if: ${{ env.VERSION == 'master' }}
|
|
uses: actions/checkout@v4
|
|
with:
|
|
repository: rustdesk/rustdesk
|
|
submodules: recursive
|
|
|
|
- name: Download Binary
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
name: ${{ env.filename }}-${{ matrix.job.arch }}.deb
|
|
path: .
|
|
|
|
- name: Rename Binary
|
|
run: |
|
|
mv ${{ env.filename }}-${{ matrix.job.arch }}.deb flatpak/rustdesk.deb
|
|
|
|
- uses: rustdesk-org/run-on-arch-action@d3fcfbb632b84cf7f6bc772bfaaa2c2f4f8789a8
|
|
name: Build rustdesk flatpak package for ${{ matrix.job.arch }}
|
|
continue-on-error: false
|
|
timeout-minutes: 30
|
|
id: flatpak
|
|
with:
|
|
arch: ${{ matrix.job.arch }}
|
|
distro: ${{ matrix.job.distro }}
|
|
# Gitea Actions tokens cannot authenticate against ghcr.io; leave it
|
|
# empty so run-on-arch pulls the image anonymously.
|
|
githubToken: ''
|
|
setup: |
|
|
ls -l "${PWD}"
|
|
dockerRunArgs: |
|
|
--volume "${PWD}:/workspace"
|
|
shell: /bin/bash
|
|
install: |
|
|
apt-get update -y
|
|
apt-get install -y git flatpak flatpak-builder
|
|
run: |
|
|
# Flatpak app IDs are reverse-DNS without spaces, and the two seds
|
|
# below would re-match an inserted name containing "RustDesk"
|
|
# (e.g. MyRustDesk -> MyMyRustDesk), so rewrite via placeholders
|
|
# and keep a space-free ID for the manifest and the bundle.
|
|
BUNDLE_ID="com.rustdesk.RustDesk"
|
|
if [ "${{ env.appname }}" != "rustdesk" ] && [ "${{ env.appname }}" != "RustDesk" ]; then
|
|
# Same as the AppImage job: the manifest's source path is rewritten to
|
|
# <appname>.deb by the seds below, so the file must carry that name, and
|
|
# the deb is moved aside first because `flatpak/*` globs it too.
|
|
mv flatpak/rustdesk.deb /tmp/flatpak-payload.deb
|
|
sed -ie "s|rustdesk|@@RDGEN_FLAT_0@@|g" flatpak/*
|
|
sed -ie "s|RustDesk|@@RDGEN_FLAT_1@@|g" flatpak/*
|
|
sed -ie "s|@@RDGEN_FLAT_0@@|${{ env.appname }}|g" flatpak/*
|
|
sed -ie "s|@@RDGEN_FLAT_1@@|${{ env.appname }}|g" flatpak/*
|
|
mv /tmp/flatpak-payload.deb "flatpak/${{ env.appname }}.deb"
|
|
# The manifest's other source is the metainfo file. Those same seds rewrote
|
|
# the REFERENCE to it without renaming the file on disk, so only the name
|
|
# is left to fix; its contents are already rewritten.
|
|
FLAT="$(echo "${{ env.appname }}" | tr -d ' ')"
|
|
BUNDLE_ID="com.${FLAT}.${FLAT}"
|
|
sed -ie "s|\"id\": \"com[^\"]*\"|\"id\": \"${BUNDLE_ID}\"|" flatpak/rustdesk.json
|
|
sed -ie "s|\"path\": \"com[^\"]*\.metainfo\.xml\"|\"path\": \"${BUNDLE_ID}.metainfo.xml\"|" flatpak/rustdesk.json
|
|
sed -ie "s|<id>com[^<]*</id>|<id>${BUNDLE_ID}</id>|" flatpak/*.metainfo.xml
|
|
mv flatpak/com.rustdesk.RustDesk.metainfo.xml \
|
|
"flatpak/${BUNDLE_ID}.metainfo.xml"
|
|
fi
|
|
# disable git safe.directory
|
|
git config --global --add safe.directory "*"
|
|
pushd /workspace
|
|
# flatpak deps
|
|
flatpak --user remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
|
# package
|
|
pushd flatpak
|
|
git clone https://github.com/flathub/shared-modules.git --depth=1
|
|
flatpak-builder --user --install-deps-from=flathub -y --force-clean --repo=repo ./build ./rustdesk.json
|
|
flatpak build-bundle ./repo ${{ env.filename }}-${{ matrix.job.arch }}.flatpak "$BUNDLE_ID"
|
|
|
|
- name: send file to rdgen server
|
|
if: ${{ env.rdgen == 'true' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 10
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./flatpak/${{ env.filename }}-${{ matrix.job.arch }}.flatpak" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
|
|
|
|
- name: send file to api server
|
|
if: ${{ env.rdgen == 'false' }}
|
|
uses: nick-fields/retry@v3
|
|
with:
|
|
timeout_minutes: 10
|
|
max_attempts: 3
|
|
retry_wait_seconds: 15
|
|
shell: bash
|
|
command: |
|
|
curl --fail -i -X POST --connect-timeout 10 --max-time 300 --retry 3 --retry-delay 5 -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./flatpak/${{ env.filename }}-${{ matrix.job.arch }}.flatpak" ${{ env.apiServer }}/api/save_custom_client
|
|
|
|
deploy:
|
|
needs: [build-rustdesk-linux,build-flatpak,build-appimage]
|
|
if: always()
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- name: Checkout Repository
|
|
uses: actions/checkout@v4
|
|
with:
|
|
path: .rdgen-src
|
|
|
|
- uses: actions/download-artifact@v4
|
|
with:
|
|
name: encrypted-secrets-zip
|
|
|
|
- name: Load Secrets
|
|
uses: ./.gitea/actions/decrypt-secrets
|
|
with:
|
|
zip_password: ${{ secrets.ZIP_PASSWORD }}
|
|
|
|
- name: Finalize and Cleanup zip/json
|
|
if: always() # Run even if previous steps fail
|
|
continue-on-error: true
|
|
uses: fjogeleit/http-request-action@v1
|
|
with:
|
|
url: "${{ secrets.GENURL }}/cleanzip"
|
|
method: 'POST'
|
|
customHeaders: '{"Content-Type": "application/json"}'
|
|
data: '{"uuid": "${{ env.uuid }}"}'
|
|
timeout: '15000'
|
|
retry: '3'
|
|
|
|
- name: Set rdgen value
|
|
if: ${{ env.rdgen == 'true' }}
|
|
run: |
|
|
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV
|
|
|
|
- name: Set rdgen value
|
|
if: ${{ env.rdgen == 'false' }}
|
|
run: |
|
|
echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV
|
|
|
|
- name: Report build status
|
|
if: always()
|
|
shell: bash
|
|
env:
|
|
BUILD_RESULTS: "${{ needs.build-rustdesk-linux.result }}/${{ needs.build-flatpak.result }}/${{ needs.build-appimage.result }}"
|
|
run: |
|
|
# Aggregate the deb/rpm, flatpak and appimage jobs. The optional drm
|
|
# job is not a gating artifact and is intentionally excluded.
|
|
if [ "$BUILD_RESULTS" = "success/success/success" ]; then
|
|
STATUS=success
|
|
elif echo "$BUILD_RESULTS" | grep -q "cancelled"; then
|
|
STATUS=cancelled
|
|
else
|
|
# failure / skipped (e.g. setup failed and all builds were skipped)
|
|
STATUS=failure
|
|
fi
|
|
if [ -z "${{ env.uuid }}" ] || [ -z "$STATUS_URL" ]; then
|
|
echo "::warning::uuid or STATUS_URL missing, skip status callback"
|
|
exit 0
|
|
fi
|
|
echo "Reporting status=$STATUS ($BUILD_RESULTS) for uuid=${{ env.uuid }}"
|
|
curl --fail --silent --show-error \
|
|
--connect-timeout 10 --max-time 30 --retry 3 --retry-delay 5 \
|
|
-X POST -H "Content-Type: application/json" \
|
|
-d "{\"uuid\":\"${{ env.uuid }}\",\"status\":\"$STATUS\"}" \
|
|
"$STATUS_URL" || echo "::warning::status callback failed"
|
|
|
|
- uses: geekyeggo/delete-artifact@v4
|
|
continue-on-error: true
|
|
with:
|
|
name: ${{ env.filename }}-*.deb
|
|
|
|
cleanup:
|
|
needs: [build-rustdesk-linux,build-flatpak,build-appimage,deploy]
|
|
runs-on: ubuntu-latest
|
|
continue-on-error: true
|
|
if: always()
|
|
steps:
|
|
- name: Delete secrets artifact
|
|
uses: geekyeggo/delete-artifact@v4
|
|
with:
|
|
name: encrypted-secrets-zip
|