72 Commits
dev ... master

Author SHA1 Message Date
Bryan Gerlach
5c904d0e02 show link even on failure 2026-03-12 11:30:55 -05:00
Bryan Gerlach
6d9c13affa get status from github api 2026-03-11 00:27:16 -05:00
Bryan Gerlach
411622016b get status from github api 2026-03-11 00:24:33 -05:00
Bryan Gerlach
88fb3b7da0 get status from github api 2026-03-11 00:20:38 -05:00
Bryan Gerlach
e32ed80003 get status from github api 2026-03-11 00:15:15 -05:00
Bryan Gerlach
8adecbfc1d get status from github api 2026-03-10 23:58:13 -05:00
Bryan Gerlach
d186b7dd02 get status from github api 2026-03-10 23:54:09 -05:00
Bryan Gerlach
c24d4ab5a5 fix save/load config 2026-03-09 14:26:24 -05:00
Dominik
1dde44596d Update version choices in GenerateForm (#219) 2026-03-06 13:46:17 -06:00
Bryan Gerlach
f45cd77923 gunicorn stuff 2026-02-17 16:36:58 -06:00
Bryan Gerlach
e2389cc6f3 docker stuff 2026-02-15 19:19:25 -06:00
Bryan Gerlach
569fee8e52 docker stuff 2026-02-15 19:16:59 -06:00
Bryan Gerlach
3e8936998e docker stuff 2026-02-15 19:12:24 -06:00
Bryan Gerlach
5aa976dea4 retry on failed download 2026-02-15 09:13:24 -06:00
Bryan Gerlach
94381d02df self hosted setup 2026-02-14 12:24:03 -06:00
Bryan Gerlach
e8f54731bc self hosted testing 2026-02-12 16:59:19 -06:00
Bryan Gerlach
f62cd9cf86 self hosted testing 2026-02-12 16:43:37 -06:00
Bryan Gerlach
7d2f124eb6 self hosted testing 2026-02-12 16:03:28 -06:00
Bryan Gerlach
f06610989a self hosted testing 2026-02-12 15:54:56 -06:00
Bryan Gerlach
0c14df90eb self hosted testing 2026-02-12 15:53:59 -06:00
Bryan Gerlach
910556d627 self hosted testing 2026-02-12 15:33:56 -06:00
Bryan Gerlach
194cfb502b self hosted testing 2026-02-12 15:23:58 -06:00
Bryan Gerlach
f1c386869f self hosted testing 2026-02-12 15:20:50 -06:00
Bryan Gerlach
95bd0e0743 self hosted testing 2026-02-12 14:21:05 -06:00
Bryan Gerlach
7cd47a99bd self hosted testing 2026-02-12 14:20:12 -06:00
Bryan Gerlach
0947330a97 self hosted testing 2026-02-12 14:15:52 -06:00
Bryan Gerlach
52d1f711ec self hosted testing 2026-02-12 14:14:56 -06:00
Bryan Gerlach
85afc2fca4 self hosted testing 2026-02-12 14:11:54 -06:00
Bryan Gerlach
f933b2ee84 self hosted testing 2026-02-12 14:11:29 -06:00
Bryan Gerlach
2214d60adb self hosted testing 2026-02-12 14:10:19 -06:00
Bryan Gerlach
35c7324669 self hosted testing 2026-02-12 13:52:37 -06:00
Bryan Gerlach
cb464bdf37 vcpkg 2026-02-12 12:45:53 -06:00
Bryan Gerlach
9af16d5a32 vcpkg 2026-02-12 12:42:58 -06:00
Bryan Gerlach
2ab2b54f66 vcpkg 2026-02-12 12:41:33 -06:00
Bryan Gerlach
0853fdeed8 vcpkg 2026-02-12 12:37:07 -06:00
Bryan Gerlach
1728c51e93 vcpkg 2026-02-12 12:27:35 -06:00
Bryan Gerlach
971586decb retry downloads 2026-02-12 11:55:11 -06:00
Bryan Gerlach
7df38d7ffd windows x86 2026-02-12 08:47:15 -06:00
Bryan Gerlach
7ca5a67d6c delete secrets artifact, update readme 2026-02-11 22:57:32 -06:00
Bryan Gerlach
19bab19ab5 linux failure messages 2026-02-11 20:03:12 -06:00
Bryan Gerlach
1d4edcfab5 set waiting refresh to 20 seconds 2026-02-11 19:54:00 -06:00
Bryan Gerlach
7c5cf83628 android 2026-02-11 19:27:42 -06:00
Bryan Gerlach
4d2d956a68 windows 2026-02-11 19:22:29 -06:00
Bryan Gerlach
f5773378d8 linux 2026-02-11 19:20:55 -06:00
Bryan Gerlach
fce82c76e1 linux, android 2026-02-11 18:35:54 -06:00
Bryan Gerlach
9c9e7ccb00 macos 2026-02-11 18:20:01 -06:00
Bryan Gerlach
f2b3df06fd secrets 2026-02-11 16:03:26 -06:00
Bryan Gerlach
98128704a5 secrets 2026-02-11 16:01:06 -06:00
Bryan Gerlach
461a8a56ff . 2026-02-11 15:48:33 -06:00
Bryan Gerlach
60dc527cad . 2026-02-11 15:45:32 -06:00
Bryan Gerlach
bb43e468ac . 2026-02-11 15:43:14 -06:00
Bryan Gerlach
0cfe1ffb4c . 2026-02-11 15:37:42 -06:00
Bryan Gerlach
8ea114d36b . 2026-02-11 15:36:34 -06:00
Bryan Gerlach
6e7a5ba84a . 2026-02-11 15:34:41 -06:00
Bryan Gerlach
6dc19ccd05 . 2026-02-11 15:24:06 -06:00
Bryan Gerlach
28e48c8eb8 . 2026-02-11 15:22:06 -06:00
Bryan Gerlach
365af0fceb . 2026-02-11 15:19:04 -06:00
Bryan Gerlach
962c98f682 change timeout 2026-02-11 13:40:33 -06:00
Aleksey Lapunov
3a2ed3d23b Change app dir name inside data_local_dir (#207) 2026-02-06 13:52:13 -06:00
Bryan Gerlach
1db75f1b33 readme 2026-02-06 13:51:26 -06:00
Bryan Gerlach
533ee4f1bb . 2026-01-26 10:22:54 -06:00
Bryan Gerlach
f0943ddf1a privacy screen 2026-01-23 22:11:58 -06:00
Bryan Gerlach
55317863c0 privacy screen 2026-01-23 22:08:48 -06:00
Bryan Gerlach
b9fdac7d8c privacy screen 2026-01-23 22:04:10 -06:00
Bryan Gerlach
adc69d499a privacy screen 2026-01-23 21:56:39 -06:00
Bryan Gerlach
dd5291c9ba fix license on msi 2026-01-20 09:40:05 -06:00
Bryan Gerlach
f9901f2f74 allow retry on connection loss 2026-01-13 13:15:44 -06:00
Bryan Gerlach
27b52d80e9 fix linux 2026-01-13 07:11:56 -06:00
Bryan Gerlach
9c471075e3 fix win32 2026-01-13 07:06:27 -06:00
Bryan Gerlach
e06ef09d75 fix delete zip issue 2026-01-12 19:05:25 -06:00
Bryan Gerlach
cbe2b4e60f fix delete zip issue 2026-01-12 18:55:30 -06:00
Bryan
3c312dad7b Hide inputs from action logs 2026-01-12 17:31:05 -06:00
25 changed files with 2198 additions and 1175 deletions

View File

@@ -0,0 +1,37 @@
name: 'Decrypt and Mask Secrets'
description: 'Decrypts a zip and masks the JSON contents as env vars'
inputs:
zip_password:
description: 'Password for the Zip'
required: true
zip_path:
description: 'Path to the encrypted zip'
required: false
default: 'secrets.zip'
runs:
using: "composite"
steps:
- name: install python deps
shell: bash
run: |
pip install pyzipper
- name: Decrypt and Mask
shell: python
run: |
import pyzipper
import json
import os
with pyzipper.AESZipFile('${{ inputs.zip_path }}') as zf:
zf.setpassword('${{ inputs.zip_password }}'.encode())
with zf.open('secrets.json') as f:
secrets = json.load(f)
with open(os.environ['GITHUB_ENV'], 'a') as env_file:
for key, value in secrets.items():
if value:
print(f"::add-mask::{value}")
env_file.write(f"{key}={value}\n")
print(f"Successfully masked {len(secrets)} secrets.")

32
.github/patches/privacyScreen.py vendored Normal file
View File

@@ -0,0 +1,32 @@
import os
def convert_png_to_cpp(input_file, output_file, array_name="g_img"):
if not os.path.exists(input_file):
print(f"Error: {input_file} not found.")
return
with open(input_file, "rb") as f:
data = f.read()
with open(output_file, "w") as f:
f.write('#include "pch.h"\n')
f.write('#include "./img.h"\n\n')
f.write(f"const unsigned char {array_name}[] = {{\n")
for i in range(0, len(data), 20):
chunk = data[i : i + 20]
hex_chunk = [f"0x{b:02x}" for b in chunk]
line = ", ".join(hex_chunk)
if i + 20 < len(data):
f.write(f"{line},\n")
else:
f.write(f"{line}\n")
f.write("};\n\n")
f.write(f"const long long {array_name}Len = sizeof({array_name});\n")
#print(f"Successfully converted {input_file} to {output_file}")
convert_png_to_cpp("privacy.png", "img.cpp")

View File

@@ -0,0 +1,46 @@
name: Fetch Encrypted Secrets
on:
workflow_call:
inputs:
zip_url_json:
required: true
type: string
jobs:
download-zip:
runs-on: ubuntu-latest
steps:
- name: Download with Retry
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: python
command: |
import requests
import json
import time
input_data = json.loads('${{ inputs.zip_url_json }}')
url = f"{input_data['url']}/get_zip?filename={input_data['file']}"
for attempt in range(5):
try:
print(f"Downloading (Attempt {attempt + 1})...")
r = requests.get(url, timeout=20)
r.raise_for_status()
with open('secrets.zip', 'wb') as f:
f.write(r.content)
break
except Exception as e:
if attempt < 4:
time.sleep(5 * (2 ** attempt))
else: raise e
- name: Upload Encrypted Artifact
uses: actions/upload-artifact@v4
with:
name: encrypted-secrets-zip
path: secrets.zip
retention-days: 1

View File

@@ -3,56 +3,16 @@ run-name: Custom Android Client Generator
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
server: version:
description: 'Rendezvous Server' description: 'version to buld'
required: true required: true
default: '' default: ''
type: string type: string
key: zip_url:
description: 'Public Key' description: 'url to zip of json'
required: true required: true
default: '' default: ''
type: string type: string
apiServer:
description: 'API Server'
required: true
default: ''
type: string
custom:
description: "Custom JSON"
required: true
default: ''
type: string
uuid:
description: "uuid of request"
required: true
default: ''
type: string
iconlink:
description: "icon link"
required: false
default: 'false'
type: string
logolink:
description: "logo link"
required: false
default: 'false'
type: string
appname:
description: "app name"
required: true
default: 'rustdesk'
type: string
filename:
description: "Filename"
required: true
default: 'rustdesk'
type: string
extras:
description: "extra inputs in json"
required: true
default: '{}'
type: string
env: env:
@@ -71,7 +31,7 @@ env:
# vcpkg version: 2024.07.12 # vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
VERSION: "${{ fromJson(inputs.extras).version }}" VERSION: "${{ inputs.version }}"
NDK_VERSION: "r27c" NDK_VERSION: "r27c"
#signing keys env variable checks #signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
@@ -81,13 +41,18 @@ env:
STATUS_URL: "${{ secrets.GENURL }}/updategh" STATUS_URL: "${{ secrets.GENURL }}/updategh"
jobs: jobs:
setup:
uses: ./.github/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
generate-bridge-linux: generate-bridge-linux:
uses: ./.github/workflows/bridge.yml uses: ./.github/workflows/bridge.yml
with: with:
version: ${{ fromJson(inputs.extras).version }} version: ${{ inputs.version }}
build-rustdesk-android: build-rustdesk-android:
needs: [generate-bridge-linux] needs: [generate-bridge-linux, setup]
name: build rustdesk android apk ${{ matrix.job.target }} name: build rustdesk android apk ${{ matrix.job.target }}
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
strategy: strategy:
@@ -116,6 +81,28 @@ jobs:
suffix: "", suffix: "",
} }
steps: steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/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 }}"}'
- name: Free Disk Space (Ubuntu) - name: Free Disk Space (Ubuntu)
uses: jlumbroso/free-disk-space@main uses: jlumbroso/free-disk-space@main
with: with:
@@ -135,23 +122,14 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
run: | run: |
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
run: | run: |
echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $GITHUB_ENV echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}'
- name: Install dependencies - name: Install dependencies
run: | run: |
@@ -281,16 +259,19 @@ jobs:
prefix-key: rustdesk-lib-cache-android # TODO: drop '-android' part after caches are invalidated prefix-key: rustdesk-lib-cache-android # TODO: drop '-android' part after caches are invalidated
key: ${{ matrix.job.target }} key: ${{ matrix.job.target }}
###########################################################echo "${{ inputs.iconbase64 }}" | base64 -d > ./res/icon.png ###########################################################echo "${{ env.iconbase64 }}" | base64 -d > ./res/icon.png
- name: icon stuff - name: icon stuff
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
mv ./res/icon.ico ./res/icon.ico.bak mv ./res/icon.ico ./res/icon.ico.bak
mv ./res/icon.png ./res/icon.png.bak mv ./res/icon.png ./res/icon.png.bak
mv ./res/tray-icon.ico ./res/tray-icon.ico.bak mv ./res/tray-icon.ico ./res/tray-icon.ico.bak
wget -O ./res/icon.png ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} wget -T 30 -t 2 -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/32x32.png ./res/32x32.png.bak
mv ./res/64x64.png ./res/64x64.png.bak mv ./res/64x64.png ./res/64x64.png.bak
mv ./res/128x128.png ./res/128x128.png.bak mv ./res/128x128.png ./res/128x128.png.bak
@@ -315,74 +296,75 @@ jobs:
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs
# ./flutter/pubspec.yaml # ./flutter/pubspec.yaml
#sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml #sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml
- name: change appname to custom - name: change appname to custom
if: inputs.appname != 'rustdesk' if: env.appname != 'rustdesk'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./Cargo.toml sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml
sed -i 's|name = "RustDesk"|name = "${{ inputs.appname }}"|' ./Cargo.toml sed -i 's|name = "RustDesk"|name = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.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 = "${{ inputs.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 = "${{ inputs.appname }}.exe"|' ./libs/portable/Cargo.toml sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml
sed -i -e 's|"RustDesk Remote Desktop"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs
sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ inputs.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"RustDesk Remote Desktop"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ env.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|"rustdesk.exe"|"${{ inputs.filename }}"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"rustdesk.exe"|"${{ env.filename }}"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc
find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \;
sed -i -e 's|RustDesk|${{ inputs.appname }}|' ./flutter/android/app/src/main/res/values/strings.xml sed -i -e 's|RustDesk|${{ env.appname }}|' ./flutter/android/app/src/main/res/values/strings.xml
sed -i -e "s|title: 'RustDesk'|title: '${{ inputs.appname }}'|" ./flutter/lib/main.dart sed -i -e "s|title: 'RustDesk'|title: '${{ env.appname }}'|" ./flutter/lib/main.dart
sed -i -e "s|return 'RustDesk';|return '${{ inputs.appname }}';|" ./flutter/lib/web/bridge.dart sed -i -e "s|return 'RustDesk';|return '${{ env.appname }}';|" ./flutter/lib/web/bridge.dart
sed -i 's|android:label="RustDesk"|android:label="${{ inputs.appname }}"|' ./flutter/android/app/src/main/AndroidManifest.xml sed -i 's|android:label="RustDesk"|android:label="${{ env.appname }}"|' ./flutter/android/app/src/main/AndroidManifest.xml
sed -i 's|android:label="RustDesk Input"|android:label="${{ inputs.appname }} Input"|' ./flutter/android/app/src/main/AndroidManifest.xml sed -i 's|android:label="RustDesk Input"|android:label="${{ env.appname }} Input"|' ./flutter/android/app/src/main/AndroidManifest.xml
sed -i 's|RustDesk is Open|${{ inputs.appname }} is Open|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt sed -i 's|RustDesk is Open|${{ env.appname }} is Open|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt
sed -i 's|Show Rustdesk|Show ${{ inputs.appname }}|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt sed -i 's|Show Rustdesk|Show ${{ env.appname }}|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt
sed -i 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt sed -i 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
sed -i 's|"RustDesk Service|"${{ inputs.appname }} Service|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt sed -i 's|"RustDesk Service|"${{ env.appname }} Service|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt
sed -i 's|RustDesk|${{ inputs.appname }}|' ./flutter/lib/main.dart sed -i 's|RustDesk|${{ env.appname }}|' ./flutter/lib/main.dart
sed -i 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/lib/desktop/widgets/tabbar_widget.dart sed -i 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/lib/desktop/widgets/tabbar_widget.dart
sed -i 's|"RustDesk"|"${{ inputs.appname }}"|' ./libs/hbb_common/src/config.rs sed -i 's|"RustDesk"|"${{ env.appname }}"|' ./libs/hbb_common/src/config.rs
- name: change url to custom - name: change url to custom
if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' if: env.urlLink != 'https://rustdesk.com'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).urlLink }}|' ./build.py 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('${{ fromJson(inputs.extras).urlLink }}'));|" ./flutter/lib/common.dart 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('${{ fromJson(inputs.extras).urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.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('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./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 = '${{ fromJson(inputs.extras).urlLink }}';|" ./flutter/lib/mobile/pages/settings_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('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./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|child: Text('rustdesk.com',|child: Text('${{ fromJson(inputs.extras).urlLink }}',|" ./flutter/lib/mobile/pages/settings_page.dart sed -i -e "s|child: Text('rustdesk.com',|child: Text('${{ env.urlLink }}',|" ./flutter/lib/mobile/pages/settings_page.dart
sed -i -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart
- name: change app id to custom - name: change app id to custom
if: fromJson(inputs.extras).androidappid != 'com.carriez.flutter_hbb' if: env.androidappid != 'com.carriez.flutter_hbb'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|com.carriez.flutter_hbb|${{ fromJson(inputs.extras).androidappid }}|' ./flutter/android/app/build.gradle sed -i -e 's|com.carriez.flutter_hbb|${{ env.androidappid }}|' ./flutter/android/app/build.gradle
- name: change download link to custom - name: change download link to custom
if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' if: env.downloadLink != 'https://rustdesk.com/download'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart 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|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_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|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis
- name: allow custom.txt - name: allow custom.txt
continue-on-error: true continue-on-error: true
@@ -394,50 +376,56 @@ jobs:
- name: fix connection delay - name: fix connection delay
continue-on-error: true continue-on-error: true
if: ${{ fromJson(inputs.extras).delayFix == 'true' }} if: ${{ env.delayFix == 'true' }}
shell: bash shell: bash
run: | run: |
sed -i -e 's|!key.is_empty()|false|' ./src/client.rs sed -i -e 's|!key.is_empty()|false|' ./src/client.rs
- name: add cycle monitors to toolbar - name: add cycle monitors to toolbar
continue-on-error: true if: env.cycleMonitor == 'true'
if: fromJson(inputs.extras).cycleMonitor == 'true' uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: bash
continue_on_error: true
command: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff
git apply cycle_monitor.diff git apply cycle_monitor.diff
- name: use X for offline display instead of orange circle - name: use X for offline display instead of orange circle
continue-on-error: true if: env.xOffline == 'true'
if: fromJson(inputs.extras).xOffline == 'true' uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: bash
continue_on_error: true
command: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff
git apply xoffline.diff git apply xoffline.diff
- name: hide-cm - name: hide-cm
continue-on-error: true if: env.hidecm == 'true'
if: fromJson(inputs.extras).hidecm == 'true' uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: bash
continue_on_error: true
command: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/hidecm.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/hidecm.diff
git apply hidecm.diff git apply hidecm.diff
- name: removeNewVersionNotif - name: removeNewVersionNotif
continue-on-error: true continue-on-error: true
if: fromJson(inputs.extras).removeNewVersionNotif == 'true' if: env.removeNewVersionNotif == 'true'
run: | run: |
sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart 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 sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "35% complete"}'
- name: replace flutter icons - name: replace flutter icons
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
run: | run: |
pushd ./flutter pushd ./flutter
flutter pub get flutter pub get
@@ -482,17 +470,8 @@ jobs:
name: librustdesk.so.${{ matrix.job.target }} name: librustdesk.so.${{ matrix.job.target }}
path: ./target/${{ matrix.job.target }}/release/liblibrustdesk.so path: ./target/${{ matrix.job.target }}/release/liblibrustdesk.so
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "45% complete"}'
- name: icons - name: icons
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true continue-on-error: true
run: | run: |
mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak
@@ -515,7 +494,7 @@ jobs:
mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/ cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so
echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt
#sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml
# build flutter # build flutter
pushd flutter pushd flutter
@@ -526,7 +505,7 @@ jobs:
mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/ cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so
echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt
#sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml
# build flutter # build flutter
pushd flutter pushd flutter
@@ -537,7 +516,7 @@ jobs:
mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64 mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/ cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so
echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt
#sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml
# build flutter # build flutter
pushd flutter pushd flutter
@@ -548,7 +527,7 @@ jobs:
mkdir -p ./flutter/android/app/src/main/jniLibs/x86 mkdir -p ./flutter/android/app/src/main/jniLibs/x86
cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/ cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/
cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so
echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt
#sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml
# build flutter # build flutter
pushd flutter pushd flutter
@@ -558,18 +537,9 @@ jobs:
esac esac
popd popd
mkdir -p signed-apk; pushd signed-apk mkdir -p signed-apk; pushd signed-apk
mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk ./${{ inputs.filename }}-${{ matrix.job.arch }}.apk mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk ./${{ env.filename }}-${{ matrix.job.arch }}.apk
popd popd
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "75% complete"}'
- uses: r0adkll/sign-android-release@v1 - uses: r0adkll/sign-android-release@v1
name: Sign app APK name: Sign app APK
continue-on-error: true continue-on-error: true
@@ -586,40 +556,73 @@ jobs:
BUILD_TOOLS_VERSION: "30.0.2" BUILD_TOOLS_VERSION: "30.0.2"
- name: send file to rdgen server - name: send file to rdgen server
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./signed-apk/${{ inputs.filename }}-${{ matrix.job.arch }}.apk" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./signed-apk/${{ env.filename }}-${{ matrix.job.arch }}.apk" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
- name: send file to api server - name: send file to api server
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./signed-apk/${{ inputs.filename }}-${{ matrix.job.arch }}.apk" ${{ inputs.apiServer }}/api/save_custom_client curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./signed-apk/${{ env.filename }}-${{ matrix.job.arch }}.apk" ${{ env.apiServer }}/api/save_custom_client
- name: Report Status deploy:
needs: [build-rustdesk-android]
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/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 uses: fjogeleit/http-request-action@v1
with:
url: "${{ secrets.GENURL }}/cleanzip"
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ env.uuid }}"}'
- 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
- uses: geekyeggo/delete-artifact@v5
continue-on-error: true continue-on-error: true
with: with:
url: ${{ env.STATUS_URL }} name: ${{ env.filename }}-*.deb
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}'
- name: failed cleanup:
if: failure() needs: [build-rustdesk-android, deploy]
uses: fjogeleit/http-request-action@v1 runs-on: ubuntu-latest
continue-on-error: true
if: always()
steps:
- name: Delete secrets artifact
uses: geekyeggo/delete-artifact@v5
with: with:
url: ${{ env.STATUS_URL }} name: encrypted-secrets-zip
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}'
- name: failed
if: cancelled()
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}'

View File

@@ -3,56 +3,16 @@ run-name: Custom Linux Client Generator
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
server: version:
description: 'Rendezvous Server' description: 'version to buld'
required: true required: true
default: '' default: ''
type: string type: string
key: zip_url:
description: 'Public Key' description: 'url to zip of json'
required: true required: true
default: '' default: ''
type: string type: string
apiServer:
description: 'API Server'
required: true
default: ''
type: string
custom:
description: "Custom JSON"
required: true
default: ''
type: string
uuid:
description: "uuid of request"
required: true
default: ''
type: string
iconlink:
description: "icon link"
required: false
default: 'false'
type: string
logolink:
description: "logo link"
required: false
default: 'false'
type: string
appname:
description: "app name"
required: true
default: 'rustdesk'
type: string
filename:
description: "Filename"
required: true
default: 'rustdesk'
type: string
extras:
description: "extra inputs in json"
required: true
default: '{}'
type: string
env: 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 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
@@ -71,7 +31,7 @@ env:
# vcpkg version: 2024.07.12 # vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
VERSION: "${{ fromJson(inputs.extras).version }}" VERSION: "${{ inputs.version }}"
NDK_VERSION: "r27c" NDK_VERSION: "r27c"
#signing keys env variable checks #signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
@@ -81,13 +41,18 @@ env:
STATUS_URL: "${{ secrets.GENURL }}/updategh" STATUS_URL: "${{ secrets.GENURL }}/updategh"
jobs: jobs:
setup:
uses: ./.github/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
generate-bridge-linux: generate-bridge-linux:
uses: ./.github/workflows/bridge.yml uses: ./.github/workflows/bridge.yml
with: with:
version: ${{ fromJson(inputs.extras).version }} version: ${{ inputs.version }}
build-rustdesk-linux: build-rustdesk-linux:
needs: [generate-bridge-linux] needs: [generate-bridge-linux, setup]
name: build rustdesk linux ${{ matrix.job.target }} name: build rustdesk linux ${{ matrix.job.target }}
runs-on: ${{ matrix.job.on }} runs-on: ${{ matrix.job.on }}
strategy: strategy:
@@ -112,6 +77,18 @@ jobs:
vcpkg-triplet: arm64-linux, vcpkg-triplet: arm64-linux,
} }
steps: steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/actions/decrypt-secrets
with:
zip_password: ${{ secrets.ZIP_PASSWORD }}
- name: Export GitHub Actions cache environment variables - name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6 uses: actions/github-script@v6
with: with:
@@ -120,23 +97,14 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
run: | run: |
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
run: | run: |
echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $GITHUB_ENV echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}'
- name: Maximize build space - name: Maximize build space
run: | run: |
@@ -198,15 +166,6 @@ jobs:
# only build cdylib # only build cdylib
sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml sed -i "s/\[\"cdylib\", \"staticlib\", \"rlib\"\]/\[\"cdylib\"\]/g" Cargo.toml
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "15% complete"}'
- name: Restore bridge files - name: Restore bridge files
if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true'
uses: actions/download-artifact@master uses: actions/download-artifact@master
@@ -243,14 +202,17 @@ jobs:
shell: bash shell: bash
- name: icon stuff - name: icon stuff
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
mv ./res/icon.ico ./res/icon.ico.bak mv ./res/icon.ico ./res/icon.ico.bak
mv ./res/icon.png ./res/icon.png.bak mv ./res/icon.png ./res/icon.png.bak
mv ./res/tray-icon.ico ./res/tray-icon.ico.bak mv ./res/tray-icon.ico ./res/tray-icon.ico.bak
wget -O ./res/icon.png ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} 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/32x32.png ./res/32x32.png.bak
mv ./res/64x64.png ./res/64x64.png.bak mv ./res/64x64.png ./res/64x64.png.bak
mv ./res/128x128.png ./res/128x128.png.bak mv ./res/128x128.png ./res/128x128.png.bak
@@ -268,97 +230,113 @@ jobs:
b64="" b64=""
- name: change appname to custom - name: change appname to custom
if: inputs.appname != 'rustdesk' if: env.appname != 'rustdesk'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./Cargo.toml sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.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 = "${{ inputs.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 = "${{ inputs.appname }}.exe"|' ./libs/portable/Cargo.toml sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml
find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; 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 sed -i -e '/-p tmpdeb\/usr\/lib\/rustdesk/d' ./build.py
- name: change company name - name: change company name
if: fromJson(inputs.extras).compname != 'Purslane Ltd' if: env.compname != 'Purslane Ltd'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./Cargo.toml sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./Cargo.toml
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./libs/portable/Cargo.toml sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./libs/portable/Cargo.toml
- name: allow custom.txt - name: allow custom.txt
continue-on-error: true uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py
git apply allowCustom.diff python allowCustom.py
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff
git apply removeSetupServerTip.diff git apply removeSetupServerTip.diff
echo -n "${{ inputs.custom }}" | cat > ./custom_.txt echo -n "${{ env.custom }}" | cat > ./custom_.txt
# sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml # sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml
sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs
- name: change url to custom - name: change url to custom
if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' if: env.urlLink != 'https://rustdesk.com'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).urlLink }}|' ./build.py 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('${{ fromJson(inputs.extras).urlLink }}'));|" ./flutter/lib/common.dart 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('${{ fromJson(inputs.extras).urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.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('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./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 = '${{ fromJson(inputs.extras).urlLink }}';|" ./flutter/lib/mobile/pages/settings_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('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./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|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_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 - name: change download link to custom
if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' if: env.downloadLink != 'https://rustdesk.com/download'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart 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|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_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|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis
- name: fix connection delay - name: fix connection delay
continue-on-error: true continue-on-error: true
if: ${{ fromJson(inputs.extras).delayFix == 'true' }} if: ${{ env.delayFix == 'true' }}
shell: bash shell: bash
run: | run: |
sed -i -e 's|!key.is_empty()|false|' ./src/client.rs sed -i -e 's|!key.is_empty()|false|' ./src/client.rs
- name: add cycle monitors to toolbar - name: add cycle monitors to toolbar
continue-on-error: true if: env.cycleMonitor == 'true'
if: fromJson(inputs.extras).cycleMonitor == 'true' uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: bash
command: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff
git apply cycle_monitor.diff git apply cycle_monitor.diff
- name: use X for offline display instead of orange circle - name: use X for offline display instead of orange circle
continue-on-error: true if: env.xOffline == 'true'
if: fromJson(inputs.extras).xOffline == 'true' uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: bash
command: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff
git apply xoffline.diff git apply xoffline.diff
- name: hide-cm - name: hide-cm
continue-on-error: true if: env.hidecm == 'true'
if: fromJson(inputs.extras).hidecm == 'true' uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: bash
command: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/hidecm.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/hidecm.diff
git apply hidecm.diff git apply hidecm.diff
- name: removeNewVersionNotif - name: removeNewVersionNotif
continue-on-error: true continue-on-error: true
if: fromJson(inputs.extras).removeNewVersionNotif == 'true' if: env.removeNewVersionNotif == 'true'
run: | run: |
sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart 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 sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs
@@ -370,15 +348,6 @@ jobs:
name: bridge-artifact name: bridge-artifact
path: ./ path: ./
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "65% complete"}'
- uses: rustdesk-org/run-on-arch-action@amd64-support - uses: rustdesk-org/run-on-arch-action@amd64-support
name: Build rustdesk name: Build rustdesk
id: vcpkg id: vcpkg
@@ -523,10 +492,10 @@ jobs:
export DEB_ARCH=${{ matrix.job.deb_arch }} export DEB_ARCH=${{ matrix.job.deb_arch }}
mkdir -p flutter/tmpdeb/usr/share/rustdesk mkdir -p flutter/tmpdeb/usr/share/rustdesk
cp ./custom_.txt ./flutter/tmpdeb/usr/share/rustdesk/custom_.txt cp ./custom_.txt ./flutter/tmpdeb/usr/share/rustdesk/custom_.txt
if [[ "${{ inputs.logolink }}" != "false" ]]; then if [[ "${{ env.logolink_url }}" != "false" ]]; then
wget -O ./flutter/assets/logo.png ${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}"&"uuid=${{ fromJson(inputs.logolink).uuid }} wget -O ./flutter/assets/logo.png ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }}
fi fi
if [[ "${{ inputs.iconlink }}" != "false" ]]; then if [[ "${{ env.iconlink_url }}" != "false" ]]; then
mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak
convert ./res/icon.png ./flutter/assets/icon.svg convert ./res/icon.png ./flutter/assets/icon.svg
convert ./res/128x128.png -resize 200% ./flutter/assets/128x128@2x.png || true convert ./res/128x128.png -resize 200% ./flutter/assets/128x128@2x.png || true
@@ -541,7 +510,7 @@ jobs:
fi fi
python3 ./build.py --flutter --skip-cargo python3 ./build.py --flutter --skip-cargo
for name in rustdesk*??.deb; do for name in rustdesk*??.deb; do
mv "$name" /workspace/output/"${{ inputs.filename }}-${{ matrix.job.arch }}.deb" mv "$name" /workspace/output/"${{ env.filename }}-${{ matrix.job.arch }}.deb"
done done
# rpm package # rpm package
@@ -555,7 +524,7 @@ jobs:
HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb
pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }}
for name in rustdesk*??.rpm; do for name in rustdesk*??.rpm; do
mv "$name" /workspace/output/"${{ inputs.filename }}-${{ matrix.job.arch }}.rpm" mv "$name" /workspace/output/"${{ env.filename }}-${{ matrix.job.arch }}.rpm"
done done
# rpm suse package # rpm suse package
@@ -569,7 +538,7 @@ jobs:
HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb
pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }}
for name in rustdesk*??.rpm; do for name in rustdesk*??.rpm; do
mv "$name" /workspace/output/"${{ inputs.filename }}-suse-${{ matrix.job.arch }}.rpm" mv "$name" /workspace/output/"${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm"
done done
# only x86_64 for arch since we can not find newest arm64 docker image to build # only x86_64 for arch since we can not find newest arm64 docker image to build
@@ -597,59 +566,40 @@ jobs:
continue-on-error: true continue-on-error: true
if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true' && env.VERSION != 'master' if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true' && env.VERSION != 'master'
run: | run: |
cp ./res/rustdesk-${{ env.VERSION }}-0-x86_64.pkg.tar.zst ./output/${{ inputs.filename }}-${{ matrix.job.arch }}.pkg.tar.zst 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 - name: send file to rdgen server
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.deb" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client curl -i -X POST -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 -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.rpm" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client curl -i -X POST -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 -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-suse-${{ matrix.job.arch }}.rpm" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client curl -i -X POST -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 -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true curl -i -X POST -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 - name: send file to api server
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.deb" ${{ inputs.apiServer }}/api/save_custom_client curl -i -X POST -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 -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.rpm" ${{ inputs.apiServer }}/api/save_custom_client curl -i -X POST -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 -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-suse-${{ matrix.job.arch }}.rpm" ${{ inputs.apiServer }}/api/save_custom_client curl -i -X POST -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 -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" ${{ inputs.apiServer }}/api/save_custom_client || true curl -i -X POST -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 - name: Upload deb
uses: actions/upload-artifact@master uses: actions/upload-artifact@master
if: env.UPLOAD_ARTIFACT == 'true' if: env.UPLOAD_ARTIFACT == 'true'
with: with:
name: ${{ inputs.filename }}-${{ matrix.job.arch }}.deb name: ${{ env.filename }}-${{ matrix.job.arch }}.deb
path: ./output/${{ inputs.filename }}-${{ matrix.job.arch }}.deb path: ./output/${{ env.filename }}-${{ matrix.job.arch }}.deb
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Finished ${{ matrix.job.arch }}"}'
- name: failed
if: failure()
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}'
- name: failed
if: cancelled()
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}'
build-appimage: build-appimage:
name: Build appimage ${{ matrix.job.target }} name: Build appimage ${{ matrix.job.target }}
@@ -662,6 +612,18 @@ jobs:
- { target: x86_64-unknown-linux-gnu, arch: x86_64 } - { target: x86_64-unknown-linux-gnu, arch: x86_64 }
- { target: aarch64-unknown-linux-gnu, arch: aarch64 } - { target: aarch64-unknown-linux-gnu, arch: aarch64 }
steps: steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/actions/decrypt-secrets
with:
zip_password: ${{ secrets.ZIP_PASSWORD }}
- name: Checkout source code - name: Checkout source code
if: ${{ env.VERSION != 'master' }} if: ${{ env.VERSION != 'master' }}
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -680,12 +642,12 @@ jobs:
- name: Download Binary - name: Download Binary
uses: actions/download-artifact@master uses: actions/download-artifact@master
with: with:
name: ${{ inputs.filename }}-${{ matrix.job.arch }}.deb name: ${{ env.filename }}-${{ matrix.job.arch }}.deb
path: . path: .
- name: Rename Binary - name: Rename Binary
run: | run: |
mv ${{ inputs.filename }}-${{ matrix.job.arch }}.deb appimage/rustdesk.deb mv ${{ env.filename }}-${{ matrix.job.arch }}.deb appimage/rustdesk.deb
- name: Build appimage package - name: Build appimage package
shell: bash shell: bash
@@ -698,19 +660,27 @@ jobs:
# run appimage-builder # run appimage-builder
pushd appimage pushd appimage
sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml
sudo mv ./rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.AppImage ./${{ inputs.filename }}-${{ matrix.job.arch }}.AppImage sudo mv ./rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.AppImage ./${{ env.filename }}-${{ matrix.job.arch }}.AppImage
- name: send file to rdgen server - name: send file to rdgen server
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./appimage/${{ inputs.filename }}-${{ matrix.job.arch }}.AppImage" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client curl -i -X POST -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 - name: send file to api server
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./appimage/${{ inputs.filename }}-${{ matrix.job.arch }}.AppImage" ${{ inputs.apiServer }}/api/save_custom_client curl -i -X POST -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: build-flatpak:
name: Build flatpak ${{ matrix.job.target }}${{ matrix.job.suffix }} name: Build flatpak ${{ matrix.job.target }}${{ matrix.job.suffix }}
@@ -737,6 +707,18 @@ jobs:
suffix: "", suffix: "",
} }
steps: steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/actions/decrypt-secrets
with:
zip_password: ${{ secrets.ZIP_PASSWORD }}
- name: Checkout source code - name: Checkout source code
if: ${{ env.VERSION != 'master' }} if: ${{ env.VERSION != 'master' }}
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -755,12 +737,12 @@ jobs:
- name: Download Binary - name: Download Binary
uses: actions/download-artifact@master uses: actions/download-artifact@master
with: with:
name: ${{ inputs.filename }}-${{ matrix.job.arch }}.deb name: ${{ env.filename }}-${{ matrix.job.arch }}.deb
path: . path: .
- name: Rename Binary - name: Rename Binary
run: | run: |
mv ${{ inputs.filename }}-${{ matrix.job.arch }}.deb flatpak/rustdesk.deb mv ${{ env.filename }}-${{ matrix.job.arch }}.deb flatpak/rustdesk.deb
- uses: rustdesk-org/run-on-arch-action@amd64-support - uses: rustdesk-org/run-on-arch-action@amd64-support
name: Build rustdesk flatpak package for ${{ matrix.job.arch }} name: Build rustdesk flatpak package for ${{ matrix.job.arch }}
@@ -789,48 +771,77 @@ jobs:
pushd flatpak pushd flatpak
git clone https://github.com/flathub/shared-modules.git --depth=1 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-builder --user --install-deps-from=flathub -y --force-clean --repo=repo ./build ./rustdesk.json
flatpak build-bundle ./repo ${{ inputs.filename }}-${{ matrix.job.arch }}.flatpak com.rustdesk.RustDesk flatpak build-bundle ./repo ${{ env.filename }}-${{ matrix.job.arch }}.flatpak com.rustdesk.RustDesk
- name: send file to rdgen server - name: send file to rdgen server
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
continue-on-error: true uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./flatpak/${{ inputs.filename }}-${{ matrix.job.arch }}.flatpak" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client curl -i -X POST -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 - name: send file to api server
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
continue-on-error: true uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./flatpak/${{ inputs.filename }}-${{ matrix.job.arch }}.flatpak" ${{ inputs.apiServer }}/api/save_custom_client curl -i -X POST -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: deploy:
needs: [build-rustdesk-linux,build-flatpak,build-appimage] needs: [build-rustdesk-linux,build-flatpak,build-appimage]
if: always()
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/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 }}"}'
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
run: | run: |
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
run: | run: |
echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $GITHUB_ENV echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV
- name: Report Status
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}'
- uses: geekyeggo/delete-artifact@v5 - uses: geekyeggo/delete-artifact@v5
continue-on-error: true continue-on-error: true
with: with:
name: ${{ inputs.filename }}-*.deb 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@v5
with:
name: encrypted-secrets-zip

View File

@@ -3,56 +3,16 @@ run-name: Custom macOS Client Generator
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
server: version:
description: 'Rendezvous Server' description: 'version to buld'
required: true required: true
default: '' default: ''
type: string type: string
key: zip_url:
description: 'Public Key' description: 'url to zip of json'
required: true required: true
default: '' default: ''
type: string type: string
apiServer:
description: 'API Server'
required: true
default: ''
type: string
custom:
description: "Custom JSON"
required: true
default: ''
type: string
uuid:
description: "uuid of request"
required: true
default: ''
type: string
iconlink:
description: "icon link"
required: false
default: 'false'
type: string
logolink:
description: "logo link"
required: false
default: 'false'
type: string
appname:
description: "app name"
required: true
default: 'rustdesk'
type: string
filename:
description: "Filename"
required: true
default: 'rustdesk'
type: string
extras:
description: "extra inputs in json"
required: true
default: '{}'
type: string
env: 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 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
@@ -71,7 +31,7 @@ env:
# vcpkg version: 2024.07.12 # vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
VERSION: "${{ fromJson(inputs.extras).version }}" VERSION: "${{ inputs.version }}"
NDK_VERSION: "r27c" NDK_VERSION: "r27c"
#signing keys env variable checks #signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
@@ -80,22 +40,27 @@ env:
SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}" SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}"
jobs: jobs:
setup:
uses: ./.github/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
generate-bridge: generate-bridge:
uses: ./.github/workflows/bridge.yml uses: ./.github/workflows/bridge.yml
with: with:
version: ${{ fromJson(inputs.extras).version }} version: ${{ inputs.version }}
build-for-macos: build-for-macos:
name: ${{ matrix.job.target }} name: ${{ matrix.job.target }}
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
needs: [generate-bridge] needs: [generate-bridge, setup]
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
job: job:
- { - {
target: x86_64-apple-darwin, target: x86_64-apple-darwin,
os: macos-13, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel os: macos-15-intel, #macos-latest or macos-14 use M1 now, https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners#:~:text=14%20GB-,macos%2Dlatest%20or%20macos%2D14,-The%20macos%2Dlatestlabel
extra-build-args: "", extra-build-args: "",
arch: x86_64, arch: x86_64,
vcpkg-triplet: x64-osx, vcpkg-triplet: x64-osx,
@@ -109,9 +74,31 @@ jobs:
vcpkg-triplet: arm64-osx, vcpkg-triplet: arm64-osx,
} }
env: env:
STATUS_URL: ${{ fromJson(inputs.extras).rdgen == 'true' && format('{0}/updategh', secrets.GENURL) || format('{0}/api/updategh', inputs.apiServer) }} STATUS_URL: "${{ secrets.GENURL }}/updategh"
steps: steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/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 }}"}'
- name: Export GitHub Actions cache environment variables - name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6 uses: actions/github-script@v6
with: with:
@@ -119,14 +106,6 @@ jobs:
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Report Status
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}'
- name: Checkout source code - name: Checkout source code
if: ${{ env.VERSION != 'master' }} if: ${{ env.VERSION != 'master' }}
uses: actions/checkout@v4 uses: actions/checkout@v4
@@ -158,76 +137,76 @@ jobs:
cp ./flutter/macos/Runner/Configs/AppInfo.xcconfig ./flutter/macos/Runner/Configs/AppInfo.xcconfig.bak cp ./flutter/macos/Runner/Configs/AppInfo.xcconfig ./flutter/macos/Runner/Configs/AppInfo.xcconfig.bak
# MACSTUFF Update Info.plist # MACSTUFF Update Info.plist
sed -i '' -e 's|<key>CFBundleName</key>.*<string>.*</string>|<key>CFBundleName</key>\n\t<string>${{ inputs.appname }}</string>|' ./flutter/macos/Runner/Info.plist sed -i '' -e 's|<key>CFBundleName</key>.*<string>.*</string>|<key>CFBundleName</key>\n\t<string>${{ env.appname }}</string>|' ./flutter/macos/Runner/Info.plist
sed -i '' -e 's|<key>CFBundleDisplayName</key>.*<string>.*</string>|<key>CFBundleDisplayName</key>\n\t<string>${{ inputs.appname }}</string>|' ./flutter/macos/Runner/Info.plist sed -i '' -e 's|<key>CFBundleDisplayName</key>.*<string>.*</string>|<key>CFBundleDisplayName</key>\n\t<string>${{ env.appname }}</string>|' ./flutter/macos/Runner/Info.plist
sed -i '' -e 's|<key>CFBundleIdentifier</key>.*<string>.*</string>|<key>CFBundleIdentifier</key>\n\t<string>com.${{ inputs.appname }}.app</string>|' ./flutter/macos/Runner/Info.plist sed -i '' -e 's|<key>CFBundleIdentifier</key>.*<string>.*</string>|<key>CFBundleIdentifier</key>\n\t<string>com.${{ env.appname }}.app</string>|' ./flutter/macos/Runner/Info.plist
# sed -i '' '/<key>NSHumanReadableCopyright<\/key>/{n;s/<string>.*<\/string>/<string>Copyright 2025 ${{ inputs.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/Info.plist # sed -i '' '/<key>NSHumanReadableCopyright<\/key>/{n;s/<string>.*<\/string>/<string>Copyright 2025 ${{ env.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/Info.plist
# MACSTUFF Update AppInfo.xcconfig # MACSTUFF Update AppInfo.xcconfig
sed -i '' -e 's|PRODUCT_NAME = .*|PRODUCT_NAME = ${{ inputs.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig sed -i '' -e 's|PRODUCT_NAME = .*|PRODUCT_NAME = ${{ env.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig
sed -i '' -e 's|PRODUCT_BUNDLE_IDENTIFIER = .*|PRODUCT_BUNDLE_IDENTIFIER = com.${{ inputs.appname }}.app|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig sed -i '' -e 's|PRODUCT_BUNDLE_IDENTIFIER = .*|PRODUCT_BUNDLE_IDENTIFIER = com.${{ env.appname }}.app|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig
sed -i '' -e 's|Purslane Ltd.|${{ inputs.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig sed -i '' -e 's|Purslane Ltd.|${{ env.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig
# Keep DEVELOPMENT_TEAM if it exists, don't blank it out # Keep DEVELOPMENT_TEAM if it exists, don't blank it out
sed -i -e 's|Purslane Ltd.|${{ inputs.appname }}|' ./Cargo.toml sed -i -e 's|Purslane Ltd.|${{ env.appname }}|' ./Cargo.toml
sed -i -e 's|Purslane Ltd|${{ inputs.appname }}|' ./libs/portable/Cargo.toml sed -i -e 's|Purslane Ltd|${{ env.appname }}|' ./libs/portable/Cargo.toml
# Update Xcode project settings # Update Xcode project settings
sed -i '' -e 's/PRODUCT_NAME = "RustDesk"/PRODUCT_NAME = "${{ inputs.appname }}"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj sed -i '' -e 's/PRODUCT_NAME = "RustDesk"/PRODUCT_NAME = "${{ env.appname }}"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj
sed -i '' -e 's/PRODUCT_BUNDLE_IDENTIFIER = ".*"/PRODUCT_BUNDLE_IDENTIFIER = "com.${{ inputs.appname }}.app"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj sed -i '' -e 's/PRODUCT_BUNDLE_IDENTIFIER = ".*"/PRODUCT_BUNDLE_IDENTIFIER = "com.${{ env.appname }}.app"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj
# Don't modify DEVELOPMENT_TEAM in project.pbxproj # Don't modify DEVELOPMENT_TEAM in project.pbxproj
# Update CMake settings # Update CMake settings
if [ -f "./flutter/macos/CMakeLists.txt" ]; then if [ -f "./flutter/macos/CMakeLists.txt" ]; then
sed -i '' -e 's/set(BINARY_NAME ".*")/set(BINARY_NAME "${{ inputs.appname }}")/' ./flutter/macos/CMakeLists.txt sed -i '' -e 's/set(BINARY_NAME ".*")/set(BINARY_NAME "${{ env.appname }}")/' ./flutter/macos/CMakeLists.txt
fi fi
# Update Podfile - keep the target as 'Runner' # Update Podfile - keep the target as 'Runner'
# sed -i '' -e 's/target '"'"'Runner'"'"' do/target '"'"'${{ inputs.appname }}'"'"' do/' ./flutter/macos/Podfile # sed -i '' -e 's/target '"'"'Runner'"'"' do/target '"'"'${{ env.appname }}'"'"' do/' ./flutter/macos/Podfile
sed -i '' -e 's/target '"'"'Runner'"'"' do/target '"'"'Runner'"'"' do/' ./flutter/macos/Podfile sed -i '' -e 's/target '"'"'Runner'"'"' do/target '"'"'Runner'"'"' do/' ./flutter/macos/Podfile
cp ./src/lang/en.rs ./src/lang/en.rs.bak cp ./src/lang/en.rs ./src/lang/en.rs.bak
cp ./src/lang/nl.rs ./src/lang/nl.rs.bak cp ./src/lang/nl.rs ./src/lang/nl.rs.bak
find ./src/lang -name "*.rs" -exec sed -i '' -e 's|RustDesk|${{ inputs.appname }}|' {} \; find ./src/lang -name "*.rs" -exec sed -i '' -e 's|RustDesk|${{ env.appname }}|' {} \;
sed -i '' -e 's|RustDesk|${{ inputs.appname }}|' ./src/lang/nl.rs sed -i '' -e 's|RustDesk|${{ env.appname }}|' ./src/lang/nl.rs
sed -i '' -e 's|https://rustdesk.com|${{ fromJson(inputs.extras).urlLink }}|' ./build.py sed -i '' -e 's|https://rustdesk.com|${{ env.urlLink }}|' ./build.py
sed -i '' -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ fromJson(inputs.extras).urlLink }}'));|" ./flutter/lib/common.dart 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('${{ fromJson(inputs.extras).urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.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('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./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 = '${{ fromJson(inputs.extras).urlLink }}';|" ./flutter/lib/mobile/pages/settings_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('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./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|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_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 - name: change download link to custom
if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' if: env.downloadLink != 'https://rustdesk.com/download'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart 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|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_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|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis
# Update slogan # Update slogan
#sed -i '' '/<key>NSHumanReadableCopyright<\/key>/{n;s/<string>.*<\/string>/<string>Copyright 2025 ${{ inputs.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/Info.plist #sed -i '' '/<key>NSHumanReadableCopyright<\/key>/{n;s/<string>.*<\/string>/<string>Copyright 2025 ${{ env.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/Info.plist
# Update slogan - About in en.rs # Update slogan - About in en.rs
sed -i '' -e 's/("Slogan_tip", "Made with heart in this chaotic world!")/("Slogan_tip", "Powered by ${{ inputs.appname }}")/' ./src/lang/en.rs sed -i '' -e 's/("Slogan_tip", "Made with heart in this chaotic world!")/("Slogan_tip", "Powered by ${{ env.appname }}")/' ./src/lang/en.rs
sed -i '' -e 's/("About RustDesk", "")/("About RustDesk", "About ${{ inputs.appname }}")/' ./src/lang/en.rs sed -i '' -e 's/("About RustDesk", "")/("About RustDesk", "About ${{ env.appname }}")/' ./src/lang/en.rs
# Update slogan - About in nl.rs # Update slogan - About in nl.rs
sed -i '' -e 's/("Slogan_tip", "Ontwikkeld met het hart voor deze chaotische wereld!")/("Slogan_tip", "Powered by ${{ inputs.appname }}")/' ./src/lang/nl.rs sed -i '' -e 's/("Slogan_tip", "Ontwikkeld met het hart voor deze chaotische wereld!")/("Slogan_tip", "Powered by ${{ env.appname }}")/' ./src/lang/nl.rs
sed -i '' -e 's/("Your Desktop", "Uw Bureaublad")/("Your Desktop", "Uw ${{ inputs.appname }}")/' ./src/lang/nl.rs sed -i '' -e 's/("Your Desktop", "Uw Bureaublad")/("Your Desktop", "Uw ${{ env.appname }}")/' ./src/lang/nl.rs
sed -i '' -e 's/("About RustDesk", "Over RustDesk")/("About RustDesk", "Over ${{ inputs.appname }}")/' ./src/lang/nl.rs sed -i '' -e 's/("About RustDesk", "Over RustDesk")/("About RustDesk", "Over ${{ env.appname }}")/' ./src/lang/nl.rs
sed -i '' -e 's/("About", "Over")/("About", "Over ${{ inputs.appname }}")/' ./src/lang/nl.rs sed -i '' -e 's/("About", "Over")/("About", "Over ${{ env.appname }}")/' ./src/lang/nl.rs
sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py
git apply allowCustom.diff python allowCustom.py
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff
git apply removeSetupServerTip.diff git apply removeSetupServerTip.diff
@@ -238,17 +217,9 @@ jobs:
mv ./flutter/pubspec.yaml.tmp ./flutter/pubspec.yaml mv ./flutter/pubspec.yaml.tmp ./flutter/pubspec.yaml
rm ./flutter/temp_dependency.txt rm ./flutter/temp_dependency.txt
- name: Report Status
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "10% complete"}'
- name: Install build runtime - name: Install build runtime
run: | run: |
brew install llvm create-dmg nasm brew install llvm create-dmg
# pkg-config is handled in a separate step, because it may be already installed by `macos-latest`(14.7.1) runner # pkg-config is handled in a separate step, because it may be already installed by `macos-latest`(14.7.1) runner
if command -v pkg-config &>/dev/null; then if command -v pkg-config &>/dev/null; then
echo "pkg-config is already installed" echo "pkg-config is already installed"
@@ -256,6 +227,17 @@ jobs:
brew install pkg-config brew install pkg-config
fi fi
- name: Install NASM
run: |
# Install NASM 2.16.x from official release.
# Do NOT use `brew install nasm` which installs NASM 3.x.
# NASM 3.x is a complete rewrite with incompatible CLI options and removed features.
# aom and other multimedia libraries require NASM 2.x for x86/x86_64 assembly.
wget https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/macosx/nasm-2.16.03-macosx.zip
unzip nasm-2.16.03-macosx.zip
sudo cp nasm-2.16.03/nasm /usr/local/bin/nasm
nasm --version
- name: Install flutter - name: Install flutter
uses: subosito/flutter-action@v2 uses: subosito/flutter-action@v2
with: with:
@@ -288,7 +270,7 @@ jobs:
prefix-key: ${{ matrix.job.os }} prefix-key: ${{ matrix.job.os }}
- name: Magick stuff for macOS - name: Magick stuff for macOS
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: false continue-on-error: false
shell: bash shell: bash
run: | run: |
@@ -302,8 +284,8 @@ jobs:
# Download icon using curl with additional SSL options # Download icon using curl with additional SSL options
curl -k -L --tlsv1.2 --proto =https --ssl-reqd \ curl -k -L --tlsv1.2 --proto =https --ssl-reqd \
-H "User-Agent: Mozilla/5.0" \ -H "User-Agent: Mozilla/5.0" \
"${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}&uuid=${{ fromJson(inputs.iconlink).uuid }}" \ "${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}&uuid=${{ env.iconlink_uuid }}" \
-o ./res/icon.png || wget --no-check-certificate -O ./res/icon.png "${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}&uuid=${{ fromJson(inputs.iconlink).uuid }}" -o ./res/icon.png || wget --no-check-certificate -O ./res/icon.png "${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}&uuid=${{ env.iconlink_uuid }}"
# Backup existing files (if they exist) # Backup existing files (if they exist)
[ -f "./res/32x32.png" ] && mv ./res/32x32.png ./res/32x32.png.bak [ -f "./res/32x32.png" ] && mv ./res/32x32.png ./res/32x32.png.bak
@@ -398,7 +380,7 @@ jobs:
ls -lh rustdesk/data/flutter_assets/assets/ ls -lh rustdesk/data/flutter_assets/assets/
- name: replace flutter icons - name: replace flutter icons
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: false continue-on-error: false
shell: bash shell: bash
run: | run: |
@@ -406,8 +388,8 @@ jobs:
# Create required directories and files # Create required directories and files
mkdir -p web mkdir -p web
mkdir -p assets mkdir -p assets
echo '{"name":"${{ inputs.appname }}","short_name":"${{ inputs.appname }}","start_url":"/","display":"standalone","background_color":"#ffffff","theme_color":"#ffffff","description":"A remote desktop software."}' > web/manifest.json echo '{"name":"${{ env.appname }}","short_name":"${{ env.appname }}","start_url":"/","display":"standalone","background_color":"#ffffff","theme_color":"#ffffff","description":"A remote desktop software."}' > web/manifest.json
echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>${{ inputs.appname }}</title></head><body></body></html>' > web/index.html echo '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>${{ env.appname }}</title></head><body></body></html>' > web/index.html
# Ensure the AppIcon.appiconset directory exists # Ensure the AppIcon.appiconset directory exists
mkdir -p macos/Runner/Assets.xcassets/AppIcon.appiconset mkdir -p macos/Runner/Assets.xcassets/AppIcon.appiconset
@@ -422,7 +404,7 @@ jobs:
cd .. cd ..
- name: ui.rs - name: ui.rs
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
@@ -435,47 +417,39 @@ jobs:
- name: fix connection delay - name: fix connection delay
continue-on-error: false continue-on-error: false
if: ${{ fromJson(inputs.extras).delayFix == 'true' }} if: ${{ env.delayFix == 'true' }}
shell: bash shell: bash
run: | run: |
sed -i -e 's|!key.is_empty()|false|' ./src/client.rs sed -i -e 's|!key.is_empty()|false|' ./src/client.rs
- name: add cycle monitors to toolbar - name: add cycle monitors to toolbar
continue-on-error: true continue-on-error: true
if: fromJson(inputs.extras).cycleMonitor == 'true' if: env.cycleMonitor == 'true'
run: | run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff
git apply cycle_monitor.diff git apply cycle_monitor.diff
- name: use X for offline display instead of orange circle - name: use X for offline display instead of orange circle
continue-on-error: true continue-on-error: true
if: fromJson(inputs.extras).xOffline == 'true' if: env.xOffline == 'true'
run: | run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff
git apply xoffline.diff git apply xoffline.diff
- name: hide-cm - name: hide-cm
continue-on-error: true continue-on-error: true
if: fromJson(inputs.extras).hidecm == 'true' if: env.hidecm == 'true'
run: | run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/hidecm.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/hidecm.diff
git apply hidecm.diff git apply hidecm.diff
- name: removeNewVersionNotif - name: removeNewVersionNotif
continue-on-error: true continue-on-error: true
if: fromJson(inputs.extras).removeNewVersionNotif == 'true' if: env.removeNewVersionNotif == 'true'
run: | run: |
sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart 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 sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs
- name: Report Status
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "20% complete"}'
- name: Restore bridge files - name: Restore bridge files
uses: actions/download-artifact@master uses: actions/download-artifact@master
with: with:
@@ -504,14 +478,6 @@ jobs:
fi fi
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
- name: Report Status
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "25% complete"}'
- name: Create MacOS directory structure - name: Create MacOS directory structure
run: | run: |
mkdir -p ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS mkdir -p ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS
@@ -525,21 +491,13 @@ jobs:
sed -i -e "s/osx_minimum_system_version = \"[0-9]*.[0-9]*\"/osx_minimum_system_version = \"${MIN_MACOS_VERSION}\"/" Cargo.toml sed -i -e "s/osx_minimum_system_version = \"[0-9]*.[0-9]*\"/osx_minimum_system_version = \"${MIN_MACOS_VERSION}\"/" Cargo.toml
sed -i -e "s/MACOSX_DEPLOYMENT_TARGET = [0-9]*.[0-9]*;/MACOSX_DEPLOYMENT_TARGET = ${MIN_MACOS_VERSION};/" flutter/macos/Runner.xcodeproj/project.pbxproj sed -i -e "s/MACOSX_DEPLOYMENT_TARGET = [0-9]*.[0-9]*;/MACOSX_DEPLOYMENT_TARGET = ${MIN_MACOS_VERSION};/" flutter/macos/Runner.xcodeproj/project.pbxproj
fi fi
sed -i -e "s/RustDesk.app/\"${{ inputs.appname }}.app\"/" build.py sed -i -e "s/RustDesk.app/\"${{ env.appname }}.app\"/" build.py
./build.py --flutter --hwcodec --unix-file-copy-paste ${{ matrix.job.extra-build-args }} ./build.py --flutter --hwcodec --unix-file-copy-paste ${{ matrix.job.extra-build-args }}
# - name: Copy service file # - name: Copy service file
# run: | # run: |
# cp -rf ../target/release/service ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS/ # cp -rf ../target/release/service ./build/macos/Build/Products/Release/RustDesk.app/Contents/MacOS/
- name: Report Status
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}'
- name: Install rcodesign tool - name: Install rcodesign tool
if: env.MACOS_P12_BASE64 != null if: env.MACOS_P12_BASE64 != null
shell: bash shell: bash
@@ -560,14 +518,6 @@ jobs:
brew install pkg-config brew install pkg-config
fi fi
- name: Report Status
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}'
- name: Show version information (Rust, cargo, Clang) - name: Show version information (Rust, cargo, Clang)
shell: bash shell: bash
run: | run: |
@@ -579,7 +529,7 @@ jobs:
rustc -V rustc -V
- name: icon svg handling - name: icon svg handling
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: false continue-on-error: false
shell: bash shell: bash
run: | run: |
@@ -595,7 +545,7 @@ jobs:
rm ./temp_icon.pbm rm ./temp_icon.pbm
- name: logo handling - name: logo handling
if: ${{ inputs.logolink != 'false' }} if: ${{ env.logolink_url != 'false' }}
continue-on-error: false continue-on-error: false
shell: bash shell: bash
run: | run: |
@@ -603,19 +553,11 @@ jobs:
mkdir -p "$ASSETS_DIR" mkdir -p "$ASSETS_DIR"
curl -k -L --tlsv1.2 --proto =https --ssl-reqd \ curl -k -L --tlsv1.2 --proto =https --ssl-reqd \
-H "User-Agent: Mozilla/5.0" \ -H "User-Agent: Mozilla/5.0" \
"${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}&uuid=${{ fromJson(inputs.logolink).uuid }}" \ "${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}&uuid=${{ env.logolink_uuid }}" \
-o "$ASSETS_DIR/logo.png" || \ -o "$ASSETS_DIR/logo.png" || \
wget --no-check-certificate \ wget --no-check-certificate \
-O "$ASSETS_DIR/logo.png" \ -O "$ASSETS_DIR/logo.png" \
"${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}&uuid=${{ fromJson(inputs.logolink).uuid }}" "${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}&uuid=${{ env.logolink_uuid }}"
- name: Report Status
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "85% complete"}'
- name: Sign macOS app bundle - name: Sign macOS app bundle
if: env.MACOS_P12_BASE64 != '' if: env.MACOS_P12_BASE64 != ''
@@ -628,21 +570,21 @@ jobs:
# Rename RustDesk.app to the custom app name first # Rename RustDesk.app to the custom app name first
if [ -d "RustDesk.app" ]; then if [ -d "RustDesk.app" ]; then
# First rename the app if it's still called RustDesk.app # First rename the app if it's still called RustDesk.app
mv "RustDesk.app" "${{ inputs.appname }}.app" mv "RustDesk.app" "${{ env.appname }}.app"
echo "Renamed RustDesk.app to ${{ inputs.appname }}.app" echo "Renamed RustDesk.app to ${{ env.appname }}.app"
fi fi
echo "App bundle contents after rename:" echo "App bundle contents after rename:"
ls -la "${{ inputs.appname }}.app" || echo "App not found" ls -la "${{ env.appname }}.app" || echo "App not found"
ls -la "${{ inputs.appname }}.app/Contents" || echo "Contents not found" ls -la "${{ env.appname }}.app/Contents" || echo "Contents not found"
# Decode the certificate # Decode the certificate
echo "${{ secrets.MACOS_P12_BASE64 }}" | base64 --decode > certificate.p12 echo "${{ secrets.MACOS_P12_BASE64 }}" | base64 --decode > certificate.p12
# Sign the app bundle and its contents # Sign the app bundle and its contents
if [ -d "${{ inputs.appname }}.app/Contents/MacOS" ]; then if [ -d "${{ env.appname }}.app/Contents/MacOS" ]; then
echo "Signing main executable..." echo "Signing main executable..."
MAIN_EXECUTABLE="${{ inputs.appname }}.app/Contents/MacOS/${{ inputs.appname }}" MAIN_EXECUTABLE="${{ env.appname }}.app/Contents/MacOS/${{ env.appname }}"
if [ -f "$MAIN_EXECUTABLE" ]; then if [ -f "$MAIN_EXECUTABLE" ]; then
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \ rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime "$MAIN_EXECUTABLE" --code-signature-flags runtime "$MAIN_EXECUTABLE"
@@ -650,23 +592,23 @@ jobs:
echo "Main executable not found at expected path: $MAIN_EXECUTABLE" echo "Main executable not found at expected path: $MAIN_EXECUTABLE"
# Try to find the actual executable # Try to find the actual executable
echo "Available executables in MacOS directory:" echo "Available executables in MacOS directory:"
ls -la "${{ inputs.appname }}.app/Contents/MacOS/" ls -la "${{ env.appname }}.app/Contents/MacOS/"
ACTUAL_EXECUTABLE=$(ls "${{ inputs.appname }}.app/Contents/MacOS/" | head -n 1) ACTUAL_EXECUTABLE=$(ls "${{ env.appname }}.app/Contents/MacOS/" | head -n 1)
if [ -n "$ACTUAL_EXECUTABLE" ]; then if [ -n "$ACTUAL_EXECUTABLE" ]; then
echo "Found executable: $ACTUAL_EXECUTABLE" echo "Found executable: $ACTUAL_EXECUTABLE"
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \ rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime "${{ inputs.appname }}.app/Contents/MacOS/$ACTUAL_EXECUTABLE" --code-signature-flags runtime "${{ env.appname }}.app/Contents/MacOS/$ACTUAL_EXECUTABLE"
fi fi
fi fi
echo "Signing frameworks..." echo "Signing frameworks..."
find "${{ inputs.appname }}.app/Contents/Frameworks" -type f -not -name ".*" -exec \ find "${{ env.appname }}.app/Contents/Frameworks" -type f -not -name ".*" -exec \
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \ rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime {} \; --code-signature-flags runtime {} \;
echo "Signing main bundle..." echo "Signing main bundle..."
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \ rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime "${{ inputs.appname }}.app" --code-signature-flags runtime "${{ env.appname }}.app"
else else
echo "Error: Invalid app bundle structure" echo "Error: Invalid app bundle structure"
exit 1 exit 1
@@ -684,24 +626,24 @@ jobs:
# Find the actual .app bundle # Find the actual .app bundle
if [ -d "RustDesk.app" ]; then if [ -d "RustDesk.app" ]; then
# First rename the app if it's still called RustDesk.app # First rename the app if it's still called RustDesk.app
mv "RustDesk.app" "${{ inputs.appname }}.app" mv "RustDesk.app" "${{ env.appname }}.app"
fi fi
if [ ! -d "${{ inputs.appname }}.app" ]; then if [ ! -d "${{ env.appname }}.app" ]; then
echo "Could not find .app bundle!" echo "Could not find .app bundle!"
exit 1 exit 1
fi fi
echo "Creating DMG for ${{ inputs.appname }}.app" echo "Creating DMG for ${{ env.appname }}.app"
create-dmg \ create-dmg \
--volname "${{ inputs.appname }}" \ --volname "${{ env.appname }}" \
--window-pos 200 120 \ --window-pos 200 120 \
--window-size 800 400 \ --window-size 800 400 \
--icon-size 100 \ --icon-size 100 \
--icon "${{ inputs.appname }}.app" 200 190 \ --icon "${{ env.appname }}.app" 200 190 \
--hide-extension "${{ inputs.appname }}.app" \ --hide-extension "${{ env.appname }}.app" \
--app-drop-link 600 185 \ --app-drop-link 600 185 \
"${{ inputs.appname }}-${{ matrix.job.arch }}.dmg" \ "${{ env.appname }}-${{ matrix.job.arch }}.dmg" \
"${{ inputs.appname }}.app" "${{ env.appname }}.app"
mv "${{ inputs.appname }}-${{ matrix.job.arch }}.dmg" $GITHUB_WORKSPACE/ mv "${{ env.appname }}-${{ matrix.job.arch }}.dmg" $GITHUB_WORKSPACE/
- name: Rename rustdesk - name: Rename rustdesk
if: env.UPLOAD_ARTIFACT == 'true' if: env.UPLOAD_ARTIFACT == 'true'
@@ -711,61 +653,46 @@ jobs:
ls -la ls -la
# Find the DMG file dynamically # Find the DMG file dynamically
DMG_FILE=$(find . -name "${{ inputs.appname }}-${{ matrix.job.arch }}.dmg") DMG_FILE=$(find . -name "${{ env.appname }}-${{ matrix.job.arch }}.dmg")
if [ -n "$DMG_FILE" ]; then if [ -n "$DMG_FILE" ]; then
echo "Found DMG file: $DMG_FILE" echo "Found DMG file: $DMG_FILE"
mv "$DMG_FILE" "${{ inputs.filename }}-${{ matrix.job.arch }}.dmg" mv "$DMG_FILE" "${{ env.filename }}-${{ matrix.job.arch }}.dmg"
echo "Renamed to ${{ inputs.filename }}-${{ matrix.job.arch }}.dmg" echo "Renamed to ${{ env.filename }}-${{ matrix.job.arch }}.dmg"
else else
echo "No DMG file found matching the pattern" echo "No DMG file found matching the pattern"
exit 1 exit 1
fi fi
- name: send file to rdgen server - name: send file to rdgen server
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
shell: bash shell: bash
run: | run: |
curl -i -X POST \ curl -i -X POST \
-H "Content-Type: multipart/form-data" \ -H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" \ -H "Authorization: Bearer ${{ env.token }}" \
-F "file=@$GITHUB_WORKSPACE/${{ inputs.filename }}-${{ matrix.job.arch }}.dmg" \ -F "file=@$GITHUB_WORKSPACE/${{ env.filename }}-${{ matrix.job.arch }}.dmg" \
-F "uuid=${{ inputs.uuid }}" \ -F "uuid=${{ env.uuid }}" \
"${{ secrets.GENURL }}/save_custom_client" "${{ secrets.GENURL }}/save_custom_client"
- name: send file to api server - name: send file to api server
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
shell: bash shell: bash
run: | run: |
curl -i -X POST \ curl -i -X POST \
-H "Content-Type: multipart/form-data" \ -H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" \ -H "Authorization: Bearer ${{ env.token }}" \
-F "file=@$GITHUB_WORKSPACE/${{ inputs.filename }}-${{ matrix.job.arch }}.dmg" \ -F "file=@$GITHUB_WORKSPACE/${{ env.filename }}-${{ matrix.job.arch }}.dmg" \
"${{ inputs.apiServer }}/api/save_custom_client" "${{ env.apiServer }}/api/save_custom_client"
- name: Report Status cleanup:
uses: fjogeleit/http-request-action@v1 needs: [build-for-macos]
runs-on: ubuntu-latest
continue-on-error: true
if: always()
steps:
- name: Delete secrets artifact
uses: geekyeggo/delete-artifact@v5
with: with:
url: ${{ env.STATUS_URL }} name: encrypted-secrets-zip
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}'
- name: failed
if: failure()
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}'
- name: failed
if: cancelled()
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}'

View File

@@ -3,56 +3,16 @@ run-name: Custom Windows x86 Client Generator
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
server: version:
description: 'Rendezvous Server' description: 'version to buld'
required: true required: true
default: '' default: ''
type: string type: string
key: zip_url:
description: 'Public Key' description: 'url to zip of json'
required: true required: true
default: '' default: ''
type: string type: string
apiServer:
description: 'API Server'
required: true
default: ''
type: string
custom:
description: "Custom JSON"
required: true
default: ''
type: string
uuid:
description: "uuid of request"
required: true
default: ''
type: string
iconlink:
description: "icon link"
required: false
default: 'false'
type: string
logolink:
description: "logo link"
required: false
default: 'false'
type: string
appname:
description: "app name"
required: true
default: 'rustdesk'
type: string
filename:
description: "Filename"
required: true
default: 'rustdesk'
type: string
extras:
description: "extra inputs in json"
required: true
default: '{}'
type: string
env: 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 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
@@ -76,7 +36,7 @@ env:
# 2. Update the `VCPKG_COMMIT_ID` in `ci.yml` and `playground.yml`. # 2. Update the `VCPKG_COMMIT_ID` in `ci.yml` and `playground.yml`.
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" # 2025.01.13, got "/opt/artifacts/vcpkg/vcpkg: No such file or directory" with latest version ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" # 2025.01.13, got "/opt/artifacts/vcpkg/vcpkg: No such file or directory" with latest version
VERSION: "${{ fromJson(inputs.extras).version }}" VERSION: "${{ inputs.version }}"
NDK_VERSION: "r27c" NDK_VERSION: "r27c"
#signing keys env variable checks #signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
@@ -86,8 +46,14 @@ env:
STATUS_URL: "${{ secrets.GENURL }}/updategh" STATUS_URL: "${{ secrets.GENURL }}/updategh"
jobs: jobs:
setup:
uses: ./.github/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
build-for-windows-sciter: build-for-windows-sciter:
name: ${{ matrix.job.target }} (${{ matrix.job.os }}) name: ${{ matrix.job.target }} (${{ matrix.job.os }})
needs: setup
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
# Temporarily disable this action due to additional test is needed. # Temporarily disable this action due to additional test is needed.
# if: false # if: false
@@ -105,6 +71,28 @@ jobs:
} }
# - { target: aarch64-pc-windows-msvc, os: windows-2022 } # - { target: aarch64-pc-windows-msvc, os: windows-2022 }
steps: steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/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 }}"}'
- name: Export GitHub Actions cache environment variables - name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6 uses: actions/github-script@v6
with: with:
@@ -113,23 +101,14 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
run: | run: |
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
run: | run: |
echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $env:GITHUB_ENV echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $env:GITHUB_ENV
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}'
- name: Checkout source code - name: Checkout source code
if: ${{ env.VERSION != 'master' }} if: ${{ env.VERSION != 'master' }}
@@ -152,25 +131,27 @@ jobs:
Get-ChildItem -Path "${env:ProgramFiles}" | % { $_.FullName } | Select-String -Pattern "[\/\\]ImageMagick[^\/\\]*$" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 Get-ChildItem -Path "${env:ProgramFiles}" | % { $_.FullName } | Select-String -Pattern "[\/\\]ImageMagick[^\/\\]*$" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
- name: change appname to custom - name: change appname to custom
if: inputs.appname != 'rustdesk' if: env.appname != 'rustdesk'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
# ./Cargo.toml # ./Cargo.toml
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./Cargo.toml sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml
# ./libs/portable/Cargo.toml # ./libs/portable/Cargo.toml
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.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 = "${{ inputs.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 = "${{ inputs.appname }}.exe"|' ./libs/portable/Cargo.toml sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml
# ./libs/portable/src/main.rs
sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs
# ./src/lang/en.rs # ./src/lang/en.rs
find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \;
- name: fix registry if appname has a space - name: fix registry if appname has a space
if: contains(inputs.appname, ' ') if: contains(env.appname, ' ')
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
@@ -190,46 +171,51 @@ jobs:
sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs
- name: change company name - name: change company name
if: fromJson(inputs.extras).compname != 'Purslane Ltd' if: env.compname != 'Purslane Ltd'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|PURSLANE|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py sed -i -e 's|PURSLANE|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|Copyright &copy; 2025 Purslane Ltd.|Copyright \&copy; 2025 ${{ fromJson(inputs.extras).compname }}|' ./src/ui/index.tis sed -i -e 's|Copyright &copy; 2025 Purslane Ltd.|Copyright \&copy; 2025 ${{ env.compname }}|' ./src/ui/index.tis
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./Cargo.toml sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./Cargo.toml
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./libs/portable/Cargo.toml sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./libs/portable/Cargo.toml
sed -i -e 's|Purslane Ltd.|${{ fromJson(inputs.extras).compname }}|' ./res/setup.nsi sed -i -e 's|Purslane Ltd.|${{ env.compname }}|' ./res/setup.nsi
- name: change url to custom - name: change url to custom
if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' if: env.urlLink != 'https://rustdesk.com'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).urlLink }}|' ./build.py sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py
sed -i -e "s|<div .link .custom-event url='https://rustdesk.com'>|<div .link .custom-event url='${{ fromJson(inputs.extras).urlLink }}'>|" ./src/ui/index.tis sed -i -e "s|<div .link .custom-event url='https://rustdesk.com'>|<div .link .custom-event url='${{ env.urlLink }}'>|" ./src/ui/index.tis
sed -i -e "s|<div .link .custom-event url='https://rustdesk.com/privacy.html'>|<div .link .custom-event url='${{ fromJson(inputs.extras).urlLink }}/privacy.html'>|" ./src/ui/index.tis sed -i -e "s|<div .link .custom-event url='https://rustdesk.com/privacy.html'>|<div .link .custom-event url='${{ env.urlLink }}/privacy.html'>|" ./src/ui/index.tis
sed -i -e "s|https://rustdesk.com/|${{fromJson(inputs.extras).urlLink }}|" ./res/setup.nsi sed -i -e "s|https://rustdesk.com/|${{env.urlLink }}|" ./res/setup.nsi
- name: change download link to custom - name: change download link to custom
if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' if: env.downloadLink != 'https://rustdesk.com/download'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis
- name: set server, key, and apiserver - name: set server, key, and apiserver
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs
sed -i -e 's|<span>{translate("Ready")}, <span .link #setup-server>{translate("setup_server_tip")}</span></span>|translate("Ready")|' ./src/ui/index.tis sed -i -e 's|<span>{translate("Ready")}, <span .link #setup-server>{translate("setup_server_tip")}</span></span>|translate("Ready")|' ./src/ui/index.tis
- name: allow custom.txt - name: allow custom.txt
continue-on-error: true uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
continue_on_error: true
command: |
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.diff -OutFile allowCustom.diff Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.diff -OutFile allowCustom.diff
git apply allowCustom.diff git apply allowCustom.diff
@@ -238,15 +224,6 @@ jobs:
with: with:
version: ${{ env.LLVM_VERSION }} version: ${{ env.LLVM_VERSION }}
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "10% complete"}'
- name: Install Rust toolchain - name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1 uses: dtolnay/rust-toolchain@v1
with: with:
@@ -258,15 +235,6 @@ jobs:
with: with:
prefix-key: ${{ matrix.job.os }}-sciter prefix-key: ${{ matrix.job.os }}-sciter
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "20% complete"}'
- name: Setup vcpkg with Github Actions binary cache - name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11 uses: lukka/run-vcpkg@v11
with: with:
@@ -295,7 +263,7 @@ jobs:
shell: bash shell: bash
- name: icon stuff - name: icon stuff
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
@@ -304,10 +272,14 @@ jobs:
mv ./res/tray-icon.ico ./res/tray-icon.ico.bak mv ./res/tray-icon.ico ./res/tray-icon.ico.bak
- name: magick stuff - name: magick stuff
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true uses: nick-fields/retry@v3
run: | with:
Invoke-WebRequest -Uri ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} -OutFile ./res/icon.png timeout_minutes: 1
max_attempts: 3
shell: pwsh
command: |
Invoke-WebRequest -Uri ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} -OutFile ./res/icon.png
mv ./res/32x32.png ./res/32x32.png.bak mv ./res/32x32.png ./res/32x32.png.bak
mv ./res/64x64.png ./res/64x64.png.bak mv ./res/64x64.png ./res/64x64.png.bak
mv ./res/128x128.png ./res/128x128.png.bak mv ./res/128x128.png ./res/128x128.png.bak
@@ -321,7 +293,7 @@ jobs:
- name: ui.rs icon - name: ui.rs icon
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
@@ -332,28 +304,19 @@ jobs:
- name: fix connection delay - name: fix connection delay
continue-on-error: true continue-on-error: true
if: ${{ fromJson(inputs.extras).delayFix == 'true' }} if: ${{ env.delayFix == 'true' }}
shell: bash shell: bash
run: | run: |
sed -i -e 's|!key.is_empty()|false|' ./src/client.rs sed -i -e 's|!key.is_empty()|false|' ./src/client.rs
- name: removeNewVersionNotif - name: removeNewVersionNotif
continue-on-error: true continue-on-error: true
if: fromJson(inputs.extras).removeNewVersionNotif == 'true' if: env.removeNewVersionNotif == 'true'
shell: bash shell: bash
run: | run: |
sed -i -e 's|{software_update_url ? <UpdateMe /> : ""}||' ./src/ui/index.tis sed -i -e 's|{software_update_url ? <UpdateMe /> : ""}||' ./src/ui/index.tis
sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}'
- name: Build rustdesk - name: Build rustdesk
id: build id: build
shell: bash shell: bash
@@ -390,15 +353,6 @@ jobs:
ls -l ./libs/portable/Runner.res; ls -l ./libs/portable/Runner.res;
fi fi
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}'
- name: zip dlls - name: zip dlls
continue-on-error: true continue-on-error: true
shell: pwsh shell: pwsh
@@ -430,29 +384,20 @@ jobs:
- name: Create custom.txt file - name: Create custom.txt file
shell: bash shell: bash
run: | run: |
echo -n "${{ inputs.custom }}" | cat > ./Release/custom.txt echo -n "${{ env.custom }}" | cat > ./Release/custom.txt
- name: Build self-extracted executable - name: Build self-extracted executable
shell: bash shell: bash
run: | run: |
mv "./Release/rustdesk.exe" "./Release/${{ inputs.appname }}.exe" || echo "rustdesk.exe" mv "./Release/rustdesk.exe" "./Release/${{ env.appname }}.exe" || echo "rustdesk.exe"
sed -i '/dpiAware/d' res/manifest.xml sed -i '/dpiAware/d' res/manifest.xml
pushd ./libs/portable pushd ./libs/portable
pip3 install -r requirements.txt pip3 install -r requirements.txt
python3 ./generate.py -f ../../Release/ -o . -e "../../Release/${{ inputs.appname }}.exe" python3 ./generate.py -f ../../Release/ -o . -e "../../Release/${{ env.appname }}.exe"
popd popd
mkdir -p ./SignOutput mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe" mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe"
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "85% complete"}'
- name: zip exe - name: zip exe
continue-on-error: true continue-on-error: true
shell: pwsh shell: pwsh
@@ -483,42 +428,27 @@ jobs:
- name: rename rustdesk.exe to filename.exe - name: rename rustdesk.exe to filename.exe
run: | run: |
mv ./SignOutput/rustdesk.exe "./SignOutput/${{ inputs.filename }}.exe" || echo "rustdesk" mv ./SignOutput/rustdesk.exe "./SignOutput/${{ env.filename }}.exe" || echo "rustdesk"
- name: send file to rdgen server - name: send file to rdgen server
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
shell: bash shell: bash
run: | run: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.exe" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
- name: send file to api server - name: send file to api server
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
shell: bash shell: bash
run: | run: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.exe" ${{ inputs.apiServer }}/api/save_custom_client curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" ${{ env.apiServer }}/api/save_custom_client
- name: Report Status cleanup:
uses: fjogeleit/http-request-action@v1 needs: [build-for-windows-sciter]
runs-on: ubuntu-latest
continue-on-error: true
if: always()
steps:
- name: Delete secrets artifact
uses: geekyeggo/delete-artifact@v5
with: with:
url: ${{ env.STATUS_URL }} name: encrypted-secrets-zip
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}'
- name: failed
if: failure()
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}'
- name: failed
if: cancelled()
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}'

View File

@@ -3,56 +3,16 @@ run-name: Custom Windows Client Generator
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
server: version:
description: 'Rendezvous Server' description: 'version to buld'
required: true required: true
default: '' default: ''
type: string type: string
key: zip_url:
description: 'Public Key' description: 'url to zip of json'
required: true required: true
default: '' default: ''
type: string type: string
apiServer:
description: 'API Server'
required: true
default: ''
type: string
custom:
description: "Custom JSON"
required: true
default: ''
type: string
uuid:
description: "uuid of request"
required: true
default: ''
type: string
iconlink:
description: "icon link"
required: false
default: 'false'
type: string
logolink:
description: "logo link"
required: false
default: 'false'
type: string
appname:
description: "app name"
required: true
default: 'rustdesk'
type: string
filename:
description: "Filename"
required: true
default: 'rustdesk'
type: string
extras:
description: "extra inputs in json"
required: true
default: '{}'
type: string
env: env:
@@ -71,7 +31,7 @@ env:
# vcpkg version: 2024.07.12 # vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
VERSION: "${{ fromJson(inputs.extras).version }}" VERSION: "${{ inputs.version }}"
NDK_VERSION: "r27c" NDK_VERSION: "r27c"
#signing keys env variable checks #signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
@@ -82,12 +42,18 @@ env:
jobs: jobs:
setup:
uses: ./.github/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
generate-bridge: generate-bridge:
uses: ./.github/workflows/bridge.yml uses: ./.github/workflows/bridge.yml
with: with:
version: ${{ fromJson(inputs.extras).version }} version: ${{ inputs.version }}
build-RustDeskTempTopMostWindow: build-RustDeskTempTopMostWindow:
needs: setup
uses: ./.github/workflows/third-party-RustDeskTempTopMostWindow.yml uses: ./.github/workflows/third-party-RustDeskTempTopMostWindow.yml
with: with:
upload-artifact: true upload-artifact: true
@@ -95,12 +61,13 @@ jobs:
configuration: Release configuration: Release
platform: x64 platform: x64
target_version: Windows10 target_version: Windows10
secrets: inherit
strategy: strategy:
fail-fast: false fail-fast: false
build-for-windows-flutter: build-for-windows-flutter:
name: Build Windows name: Build Windows
needs: [build-RustDeskTempTopMostWindow, generate-bridge] needs: [build-RustDeskTempTopMostWindow, generate-bridge, setup]
runs-on: ${{ matrix.job.os }} runs-on: ${{ matrix.job.os }}
strategy: strategy:
fail-fast: false fail-fast: false
@@ -116,6 +83,28 @@ jobs:
} }
# - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 } # - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 }
steps: steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/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 }}"}'
- name: Export GitHub Actions cache environment variables - name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6 uses: actions/github-script@v6
with: with:
@@ -124,23 +113,14 @@ jobs:
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
run: | run: |
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV
- name: Set rdgen value - name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
run: | run: |
echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $env:GITHUB_ENV echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $env:GITHUB_ENV
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}'
- name: Checkout source code - name: Checkout source code
if: ${{ env.VERSION != 'master' }} if: ${{ env.VERSION != 'master' }}
@@ -175,34 +155,37 @@ jobs:
sed -i -e 's|2ded7f146437a761ffe6981e2f742038f85ca68d|08a471bb8ceccdd50483c81cdfa8b81b07b14b87|' ./flutter/pubspec.yaml sed -i -e 's|2ded7f146437a761ffe6981e2f742038f85ca68d|08a471bb8ceccdd50483c81cdfa8b81b07b14b87|' ./flutter/pubspec.yaml
- name: change appname to custom - name: change appname to custom
if: inputs.appname != 'rustdesk' if: env.appname != 'rustdesk'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
# ./Cargo.toml # ./Cargo.toml
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml
sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./Cargo.toml sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml
# ./libs/portable/Cargo.toml # ./libs/portable/Cargo.toml
sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml
sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.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 = "${{ inputs.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 = "${{ inputs.appname }}.exe"|' ./libs/portable/Cargo.toml sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml
# ./libs/portable/src/main.rs
sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs
# ./flutter/windows/runner/Runner.rc # ./flutter/windows/runner/Runner.rc
sed -i -e 's|"RustDesk Remote Desktop"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"RustDesk Remote Desktop"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ inputs.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ env.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|"rustdesk.exe"|"${{ inputs.filename }}"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"rustdesk.exe"|"${{ env.filename }}"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc
# ./src/lang/en.rs # ./src/lang/en.rs
# change powered by rustdek to powered by compname # change powered by rustdek to powered by compname
if [ ! -z "${{ fromJson(inputs.extras).compname }}" ]; then if [ ! -z "${{ env.compname }}" ]; then
find ./src/lang -name "*.rs" -exec sed -i '/powered_by_me/s|RustDesk|${{ fromJson(inputs.extras).compname }}|g' {} \; find ./src/lang -name "*.rs" -exec sed -i '/powered_by_me/s|RustDesk|${{ env.compname }}|g' {} \;
fi fi
find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \;
sed -i -e 's|RustDesk|${{ env.appname }}|' ./res/msi/Package/License.rtf
- name: fix registry if appname has a space - name: fix registry if appname has a space
if: contains(inputs.appname, ' ') if: contains(env.appname, ' ')
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
@@ -222,53 +205,59 @@ jobs:
sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs
- name: change company name - name: change company name
if: fromJson(inputs.extras).compname != 'Purslane Ltd' if: env.compname != 'Purslane Ltd'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e 's|PURSLANE|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py sed -i -e 's|PURSLANE|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025 ${{ fromJson(inputs.extras).compname }}. All rights reserved."|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025 ${{ env.compname }}. All rights reserved."|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./flutter/windows/runner/Runner.rc sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./Cargo.toml sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./Cargo.toml
sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./libs/portable/Cargo.toml sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./libs/portable/Cargo.toml
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./res/msi/Package/License.rtf
- name: change url to custom - name: change url to custom
if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' if: env.urlLink != 'https://rustdesk.com'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).urlLink }}|' ./build.py 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('${{ fromJson(inputs.extras).urlLink }}'));|" ./flutter/lib/common.dart 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('${{ fromJson(inputs.extras).urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.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('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./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 = '${{ fromJson(inputs.extras).urlLink }}';|" ./flutter/lib/mobile/pages/settings_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('${{ fromJson(inputs.extras).urlLink }}/privacy.html')|" ./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|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart
sed -i -e "s|rustdesk.com|${{ env.urlLink }}|" ./res/msi/Package/License.rtf
- name: change download link to custom - name: change download link to custom
if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' if: env.downloadLink != 'https://rustdesk.com/download'
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart 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|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_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|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis
- name: set server, key, and apiserver - name: set server, key, and apiserver
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs
# ./flutter/pubspec.yaml # ./flutter/pubspec.yaml
#sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml #sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml
- name: allow custom.txt - name: allow custom.txt
continue-on-error: true uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
command: |
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py -OutFile allowCustom.py Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py -OutFile allowCustom.py
python allowCustom.py python allowCustom.py
# Remove Setup Server Tip # Remove Setup Server Tip
@@ -280,15 +269,6 @@ jobs:
with: with:
version: ${{ env.LLVM_VERSION }} version: ${{ env.LLVM_VERSION }}
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "10% complete"}'
- name: Install flutter - name: Install flutter
uses: subosito/flutter-action@v2.12.0 #https://github.com/subosito/flutter-action/issues/277 uses: subosito/flutter-action@v2.12.0 #https://github.com/subosito/flutter-action/issues/277
with: with:
@@ -318,28 +298,10 @@ jobs:
targets: ${{ matrix.job.target }} targets: ${{ matrix.job.target }}
components: "rustfmt" components: "rustfmt"
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "15% complete"}'
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
with: with:
prefix-key: ${{ matrix.job.os }} prefix-key: ${{ matrix.job.os }}
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "20% complete"}'
- name: Setup vcpkg with Github Actions binary cache - name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11 uses: lukka/run-vcpkg@v11
with: with:
@@ -368,10 +330,14 @@ jobs:
shell: bash shell: bash
- name: magick stuff - name: magick stuff
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true uses: nick-fields/retry@v3
run: | with:
Invoke-WebRequest -Uri ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} -OutFile ./res/iconx.png timeout_minutes: 1
max_attempts: 3
shell: pwsh
command: |
Invoke-WebRequest -Uri ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} -OutFile ./res/iconx.png
mv ./res/icon.ico ./res/icon.ico.bak mv ./res/icon.ico ./res/icon.ico.bak
mv ./res/icon.png ./res/icon.png.bak mv ./res/icon.png ./res/icon.png.bak
mv ./res/tray-icon.ico ./res/tray-icon.ico.bak mv ./res/tray-icon.ico ./res/tray-icon.ico.bak
@@ -389,7 +355,7 @@ jobs:
- name: ui.rs icon - name: ui.rs icon
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true continue-on-error: true
shell: bash shell: bash
run: | run: |
@@ -400,28 +366,38 @@ jobs:
- name: fix connection delay - name: fix connection delay
continue-on-error: true continue-on-error: true
if: ${{ fromJson(inputs.extras).delayFix == 'true' }} if: ${{ env.delayFix == 'true' }}
shell: bash shell: bash
run: | run: |
sed -i -e 's|!key.is_empty()|false|' ./src/client.rs sed -i -e 's|!key.is_empty()|false|' ./src/client.rs
- name: add cycle monitors to toolbar - name: add cycle monitors to toolbar
continue-on-error: true if: env.cycleMonitor == 'true'
if: fromJson(inputs.extras).cycleMonitor == 'true' uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
continue_on_error: true
command: |
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff -OutFile cycle_monitor.diff Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff -OutFile cycle_monitor.diff
git apply cycle_monitor.diff git apply cycle_monitor.diff
- name: use X for offline display instead of orange circle - name: use X for offline display instead of orange circle
continue-on-error: true if: env.xOffline == 'true'
if: fromJson(inputs.extras).xOffline == 'true' uses: nick-fields/retry@v3
run: | with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
continue_on_error: true
command: |
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff -OutFile xoffline.diff Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff -OutFile xoffline.diff
git apply xoffline.diff git apply xoffline.diff
- name: removeNewVersionNotif - name: removeNewVersionNotif
continue-on-error: true continue-on-error: true
if: fromJson(inputs.extras).removeNewVersionNotif == 'true' if: env.removeNewVersionNotif == 'true'
shell: bash shell: bash
run: | run: |
sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart
@@ -429,7 +405,7 @@ jobs:
# - name: run as admin # - name: run as admin
# continue-on-error: true # continue-on-error: true
# if: ${{ fromJson(inputs.extras).runasadmin == 'true' }} # if: ${{ env.runasadmin == 'true' }}
# shell: bash # shell: bash
# run: | # run: |
# sed -i '/<\/compatibility>/a \ # sed -i '/<\/compatibility>/a \
@@ -437,17 +413,8 @@ jobs:
# <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> \ # <requestedExecutionLevel level="requireAdministrator" uiAccess="false"/> \
# </security>' ./flutter/windows/runner/runner.exe.manifest # </security>' ./flutter/windows/runner/runner.exe.manifest
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "25% complete"}'
- name: replace flutter icons - name: replace flutter icons
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true continue-on-error: true
run: | run: |
cd ./flutter cd ./flutter
@@ -456,15 +423,6 @@ jobs:
flutter pub run flutter_launcher_icons flutter pub run flutter_launcher_icons
cd .. cd ..
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}'
- name: Build rustdesk - name: Build rustdesk
run: | run: |
# Windows: build RustDesk # Windows: build RustDesk
@@ -506,17 +464,22 @@ jobs:
} }
- name: icon stuff - name: icon stuff
if: ${{ inputs.iconlink != 'false' }} if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true continue-on-error: true
run: | run: |
mv ./rustdesk/data/flutter_assets/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg.bak mv ./rustdesk/data/flutter_assets/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg.bak
magick ./res/icon.png ./rustdesk/data/flutter_assets/assets/icon.svg magick ./res/icon.png ./rustdesk/data/flutter_assets/assets/icon.svg
- name: logo stuff - name: logo stuff
if: ${{ inputs.logolink != 'false' }} if: ${{ env.logolink_url != 'false' }}
continue-on-error: true uses: nick-fields/retry@v3
run: | with:
Invoke-WebRequest -Uri ${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}"&"uuid=${{ fromJson(inputs.logolink).uuid }} -OutFile ./rustdesk/data/flutter_assets/assets/logo.png timeout_minutes: 1
max_attempts: 3
shell: pwsh
continue_on_error: true
command: |
Invoke-WebRequest -Uri ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }} -OutFile ./rustdesk/data/flutter_assets/assets/logo.png
- name: find Runner.res - name: find Runner.res
# Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res # Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res
@@ -541,15 +504,6 @@ jobs:
name: topmostwindow-artifacts name: topmostwindow-artifacts
path: "./rustdesk" path: "./rustdesk"
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}'
- name: zip dlls - name: zip dlls
continue-on-error: true continue-on-error: true
shell: pwsh shell: pwsh
@@ -581,17 +535,17 @@ jobs:
- name: Create custom.txt file - name: Create custom.txt file
shell: bash shell: bash
run: | run: |
echo -n "${{ inputs.custom }}" | cat > ./rustdesk/custom_.txt echo -n "${{ env.custom }}" | cat > ./rustdesk/custom_.txt
- name: Build self-extracted executable - name: Build self-extracted executable
shell: bash shell: bash
if: env.UPLOAD_ARTIFACT == 'true' if: env.UPLOAD_ARTIFACT == 'true'
run: | run: |
mv "./rustdesk/rustdesk.exe" "./rustdesk/${{ inputs.appname }}.exe" || echo "rustdesk.exe" mv "./rustdesk/rustdesk.exe" "./rustdesk/${{ env.appname }}.exe" || echo "rustdesk.exe"
sed -i '/dpiAware/d' res/manifest.xml sed -i '/dpiAware/d' res/manifest.xml
pushd ./libs/portable pushd ./libs/portable
pip3 install -r requirements.txt pip3 install -r requirements.txt
python3 ./generate.py -f ../../rustdesk/ -o . -e "../../rustdesk/${{ inputs.appname }}.exe" python3 ./generate.py -f ../../rustdesk/ -o . -e "../../rustdesk/${{ env.appname }}.exe"
popd popd
mkdir -p ./SignOutput mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe" mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe"
@@ -603,8 +557,8 @@ jobs:
continue-on-error: true continue-on-error: true
if: env.UPLOAD_ARTIFACT == 'true' if: env.UPLOAD_ARTIFACT == 'true'
run: | run: |
$myappname = "${{ inputs.appname }}" -replace '\s','_' $myappname = "${{ env.appname }}" -replace '\s','_'
cp "rustdesk/${{ inputs.appname }}.exe" "rustdesk/${myappname}.exe" -ErrorAction SilentlyContinue cp "rustdesk/${{ env.appname }}.exe" "rustdesk/${myappname}.exe" -ErrorAction SilentlyContinue
pushd ./res/msi pushd ./res/msi
python preprocess.py --app-name "$myappname" --arp -d ../../rustdesk python preprocess.py --app-name "$myappname" --arp -d ../../rustdesk
nuget restore msi.sln nuget restore msi.sln
@@ -613,15 +567,6 @@ jobs:
mv ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk.msi mv ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk.msi
sha256sum ../../SignOutput/rustdesk.msi sha256sum ../../SignOutput/rustdesk.msi
- name: Report Status
uses: fjogeleit/http-request-action@v1
continue-on-error: true
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "85% complete"}'
- name: zip exe and msi - name: zip exe and msi
continue-on-error: true continue-on-error: true
shell: pwsh shell: pwsh
@@ -652,49 +597,42 @@ jobs:
- name: rename rustdesk.exe to filename.exe - name: rename rustdesk.exe to filename.exe
run: | run: |
mv ./SignOutput/rustdesk.exe "./SignOutput/${{ inputs.filename }}.exe" || echo "rustdesk" mv ./SignOutput/rustdesk.exe "./SignOutput/${{ env.filename }}.exe" || echo "rustdesk"
- name: rename rustdesk.msi to filename.msi - name: rename rustdesk.msi to filename.msi
continue-on-error: true continue-on-error: true
run: | run: |
mv ./SignOutput/rustdesk.msi "./SignOutput/${{ inputs.filename }}.msi" || echo "rustdesk" mv ./SignOutput/rustdesk.msi "./SignOutput/${{ env.filename }}.msi" || echo "rustdesk"
- name: send file to rdgen server - name: send file to rdgen server
if: ${{ fromJson(inputs.extras).rdgen == 'true' }} if: ${{ env.rdgen == 'true' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.exe" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.msi" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.msi" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true
- name: send file to api server - name: send file to api server
if: ${{ fromJson(inputs.extras).rdgen == 'false' }} if: ${{ env.rdgen == 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash shell: bash
run: | command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.exe" ${{ inputs.apiServer }}/api/save_custom_client curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" ${{ env.apiServer }}/api/save_custom_client
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.msi" ${{ inputs.apiServer }}/api/save_custom_client || true curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.msi" ${{ env.apiServer }}/api/save_custom_client || true
- name: Report Status cleanup:
uses: fjogeleit/http-request-action@v1 needs: [build-for-windows-flutter]
runs-on: ubuntu-latest
continue-on-error: true
if: always()
steps:
- name: Delete secrets artifact
uses: geekyeggo/delete-artifact@v5
with: with:
url: ${{ env.STATUS_URL }} name: encrypted-secrets-zip
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}'
- name: failed
if: failure()
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}'
- name: failed
if: cancelled()
uses: fjogeleit/http-request-action@v1
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}'

View File

@@ -0,0 +1,562 @@
name: Custom Windows Client Generator
run-name: Custom Windows 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.15.05-1"
LLVM_VERSION: "15.0.6"
FLUTTER_VERSION: "3.24.5"
ANDROID_FLUTTER_VERSION: "3.24.5"
# 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;x-gha,readwrite"
# vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b"
ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836"
VERSION: "${{ inputs.version }}"
NDK_VERSION: "r27c"
#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: ./.github/workflows/fetch-encrypted-secrets.yml
with:
zip_url_json: ${{ inputs.zip_url }}
generate-bridge:
uses: ./.github/workflows/bridge.yml
with:
version: ${{ inputs.version }}
build-RustDeskTempTopMostWindow:
needs: setup
uses: ./.github/workflows/third-party-RustDeskTempTopMostWindow.yml
with:
upload-artifact: true
target: windows-2022
configuration: Release
platform: x64
target_version: Windows10
secrets: inherit
strategy:
fail-fast: false
build-for-windows-flutter:
name: Build Windows
needs: [build-RustDeskTempTopMostWindow, generate-bridge, setup]
runs-on: self-hosted
strategy:
fail-fast: false
matrix:
job:
# - { target: i686-pc-windows-msvc , os: windows-2022 }
# - { target: x86_64-pc-windows-gnu , os: windows-2022 }
- {
target: x86_64-pc-windows-msvc,
os: windows-2022,
arch: x86_64,
vcpkg-triplet: x64-windows-static,
}
# - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 }
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/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 }}"}'
- name: Export GitHub Actions cache environment variables
uses: actions/github-script@v6
with:
script: |
core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Set rdgen value
if: ${{ env.rdgen == 'true' }}
run: |
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV
- name: Set rdgen value
if: ${{ env.rdgen == 'false' }}
run: |
echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $env:GITHUB_ENV
- 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: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Setup NuGet
uses: NuGet/setup-nuget@v2
with:
nuget-version: 'latest'
- name: change appname to custom
if: env.appname != 'rustdesk'
continue-on-error: true
shell: bash
run: |
# ./Cargo.toml
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
# ./libs/portable/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
# ./libs/portable/src/main.rs
sed -i -e 's|const APP_PREFIX: \&str = "rustdesk";|const APP_PREFIX: \&str = "${{ env.appname }}";|' ./libs/portable/src/main.rs
# ./flutter/windows/runner/Runner.rc
sed -i -e 's|"RustDesk Remote Desktop"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ env.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|"rustdesk.exe"|"${{ env.filename }}"|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc
# ./src/lang/en.rs
# change powered by rustdek to powered by compname
if [ ! -z "${{ env.compname }}" ]; then
find ./src/lang -name "*.rs" -exec sed -i '/powered_by_me/s|RustDesk|${{ env.compname }}|g' {} \;
fi
find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \;
sed -i -e 's|RustDesk|${{ env.appname }}|' ./res/msi/Package/License.rtf
- name: fix registry if appname has a space
if: contains(env.appname, ' ')
continue-on-error: true
shell: bash
run: |
#./src/platform/windows.rs
sed -i -e 's|reg add {}|reg add \\\"{}\\\"|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext} /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\DefaultIcon /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\DefaultIcon\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open /f|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open\\\\command|reg add \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\\shell\\\\open\\\\command\\\"|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext} /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext}\\\\shell /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg add HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open\\\\command /f|reg add \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\\shell\\\\open\\\\command\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|{subkey}|\\\"{subkey}\\\"|' ./src/platform/windows.rs
sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\.{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\.{ext}\\\" /f|' ./src/platform/windows.rs
sed -i -e 's|reg delete HKEY_CLASSES_ROOT\\\\{ext} /f|reg delete \\\"HKEY_CLASSES_ROOT\\\\{ext}\\\" /f|' ./src/platform/windows.rs
- name: change company name
if: env.compname != 'Purslane Ltd'
continue-on-error: true
shell: bash
run: |
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i -e 's|PURSLANE|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./res/msi/preprocess.py
sed -i -e 's|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025 ${{ env.compname }}. All rights reserved."|' ./flutter/windows/runner/Runner.rc
sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/windows/runner/Runner.rc
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 }}|' ./res/msi/Package/License.rtf
- 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
sed -i -e "s|rustdesk.com|${{ env.urlLink }}|" ./res/msi/Package/License.rtf
- 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: set server, key, and apiserver
continue-on-error: true
shell: bash
run: |
sed -i -e 's|rs-ny.rustdesk.com|${{ env.server }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ env.key }}|' ./libs/hbb_common/src/config.rs
sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs
# ./flutter/pubspec.yaml
#sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml
- name: allow custom.txt
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
command: |
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py -OutFile allowCustom.py
python allowCustom.py
# Remove Setup Server Tip
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff -OutFile removeSetupServerTip.diff
git apply removeSetupServerTip.diff
- name: magick stuff
if: ${{ env.iconlink_url != 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
command: |
Invoke-WebRequest -Uri ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} -OutFile ./res/iconx.png
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
mv ./res/iconx.png ./res/icon.png
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
magick ./res/icon.png -define icon:auto-resize=256,64,48,32,16 ./res/icon.ico
cp ./res/icon.ico ./res/tray-icon.ico
magick ./res/icon.png -resize 32x32 ./res/32x32.png
magick ./res/icon.png -resize 64x64 ./res/64x64.png
magick ./res/icon.png -resize 128x128 ./res/128x128.png
magick ./res/128x128.png -resize 200% ./res/128x128@2x.png
- name: ui.rs icon
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true
shell: bash
run: |
cp ./src/ui.rs ./src/ui.rs.bak
b64=$(base64 -w0 < ./res/icon.png)
perl -0777 -pe "s|iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHL[A-Za-z0-9+/=]+|$b64|g" -i.bak ./src/ui.rs
b64=""
- name: fix connection delay
continue-on-error: true
if: ${{ env.delayFix == 'true' }}
shell: bash
run: |
sed -i -e 's|!key.is_empty()|false|' ./src/client.rs
- name: add cycle monitors to toolbar
if: env.cycleMonitor == 'true'
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
continue_on_error: true
command: |
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff -OutFile cycle_monitor.diff
git apply cycle_monitor.diff
- name: use X for offline display instead of orange circle
if: env.xOffline == 'true'
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
continue_on_error: true
command: |
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff -OutFile xoffline.diff
git apply xoffline.diff
- name: removeNewVersionNotif
continue-on-error: true
if: env.removeNewVersionNotif == 'true'
shell: bash
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: replace flutter icons
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true
run: |
cd ./flutter
#flutter pub upgrade win32
flutter pub get
flutter pub run flutter_launcher_icons
cd ..
- name: Build rustdesk
run: |
# Windows: build RustDesk
python3 .\build.py --portable --hwcodec --flutter --vram --skip-portable-pack
mv ./flutter/build/windows/x64/runner/Release ./rustdesk
# Download usbmmidd_v2.zip and extract it to ./rustdesk
Invoke-WebRequest -Uri https://github.com/rustdesk-org/rdev/releases/download/usbmmidd_v2/usbmmidd_v2.zip -OutFile usbmmidd_v2.zip
Expand-Archive usbmmidd_v2.zip -DestinationPath . -Force
Remove-Item -Path usbmmidd_v2\Win32 -Recurse
Remove-Item -Path "usbmmidd_v2\deviceinstaller64.exe", "usbmmidd_v2\deviceinstaller.exe", "usbmmidd_v2\usbmmidd.bat"
mv -Force .\usbmmidd_v2 ./rustdesk
# Download printer driver files and extract them to ./rustdesk
try {
Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/rustdesk_printer_driver_v4-1.4.zip -OutFile rustdesk_printer_driver_v4-1.4.zip
Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/printer_driver_adapter.zip -OutFile printer_driver_adapter.zip
Invoke-WebRequest -Uri https://github.com/rustdesk/hbb_common/releases/download/driver/sha256sums -OutFile sha256sums
# Check and move the files
$checksum_driver = (Select-String -Path .\sha256sums -Pattern '^([a-fA-F0-9]{64}) \*rustdesk_printer_driver_v4-1.4\.zip$').Matches.Groups[1].Value
$downloadsum_driver = Get-FileHash -Path rustdesk_printer_driver_v4-1.4.zip -Algorithm SHA256
$checksum_adapter = (Select-String -Path .\sha256sums -Pattern '^([a-fA-F0-9]{64}) \*printer_driver_adapter\.zip$').Matches.Groups[1].Value
$downloadsum_adapter = Get-FileHash -Path printer_driver_adapter.zip -Algorithm SHA256
if ($checksum_driver -eq $downloadsum_driver.Hash -and $checksum_adapter -eq $downloadsum_adapter.Hash) {
Write-Output "rustdesk_printer_driver_v4-1.4, checksums match, extract the file."
Expand-Archive rustdesk_printer_driver_v4-1.4.zip -DestinationPath .
mkdir ./rustdesk/drivers
mv -Force .\rustdesk_printer_driver_v4-1.4 ./rustdesk/drivers/RustDeskPrinterDriver
Expand-Archive printer_driver_adapter.zip -DestinationPath .
mv -Force .\printer_driver_adapter.dll ./rustdesk
} elseif ($checksum_driver -ne $downloadsum_driver.Hash) {
Write-Output "rustdesk_printer_driver_v4-1.4, checksums do not match, ignore the file."
} else {
Write-Output "printer_driver_adapter.dll, checksums do not match, ignore the file."
}
} catch {
Write-Host "Ingore the printer driver error."
}
- name: icon stuff
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true
run: |
mv ./rustdesk/data/flutter_assets/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg.bak
magick ./res/icon.png ./rustdesk/data/flutter_assets/assets/icon.svg
- name: logo stuff
if: ${{ env.logolink_url != 'false' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
continue_on_error: true
command: |
Invoke-WebRequest -Uri ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }} -OutFile ./rustdesk/data/flutter_assets/assets/logo.png
- name: find Runner.res
# Windows: find Runner.res (compiled from ./flutter/windows/runner/Runner.rc), copy to ./Runner.res
# Runner.rc does not contain actual version, but Runner.res does
continue-on-error: true
shell: bash
run: |
runner_res=$(find . -name "Runner.res");
if [ "$runner_res" == "" ]; then
echo "Runner.res: not found";
else
echo "Runner.res: $runner_res";
cp $runner_res ./libs/portable/Runner.res;
echo "list ./libs/portable/Runner.res";
ls -l ./libs/portable/Runner.res;
fi
- name: Download RustDeskTempTopMostWindow artifacts
uses: actions/download-artifact@master
if: env.UPLOAD_ARTIFACT == 'true'
with:
name: topmostwindow-artifacts
path: "./rustdesk"
- name: zip dlls
continue-on-error: true
shell: pwsh
run: |
Compress-Archive -Path ./rustdesk/*.dll, ./rustdesk/*.exe -DestinationPath ./rustdesk/unsigned_files.zip -CompressionLevel Fastest
- name: sign dlls
continue-on-error: true
shell: bash
run: |
if [ ! -z "${{ secrets.SIGN_BASE_URL }}" ] && [ ! -z "${{ secrets.SIGN_API_KEY }}" ]; then
curl -X POST -F "file=@./rustdesk/unsigned_files.zip" \
-H "X-API-KEY: ${{ secrets.SIGN_API_KEY }}" \
-m 900 \
"${{ secrets.SIGN_BASE_URL }}/sign/" -o ./rustdesk/signed_files.zip
else
echo "Signing skipped - signing URL or API key not configured"
cp ./rustdesk/unsigned_files.zip ./rustdesk/signed_files.zip
fi
- name: unzip dlls
continue-on-error: true
shell: pwsh
run: |
Expand-Archive -Path ./rustdesk/signed_files.zip -DestinationPath ./rustdesk/ -Force
Remove-Item ./rustdesk/unsigned_files.zip
Remove-Item ./rustdesk/signed_files.zip
- name: Create custom.txt file
shell: bash
run: |
echo -n "${{ env.custom }}" | cat > ./rustdesk/custom_.txt
- name: Build self-extracted executable
shell: bash
if: env.UPLOAD_ARTIFACT == 'true'
run: |
mv "./rustdesk/rustdesk.exe" "./rustdesk/${{ env.appname }}.exe" || echo "rustdesk.exe"
sed -i '/dpiAware/d' res/manifest.xml
pushd ./libs/portable
pip3 install -r requirements.txt
python3 ./generate.py -f ../../rustdesk/ -o . -e "../../rustdesk/${{ env.appname }}.exe"
popd
mkdir -p ./SignOutput
mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe"
- name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v2
- name: Build msi
continue-on-error: true
if: env.UPLOAD_ARTIFACT == 'true'
run: |
$myappname = "${{ env.appname }}" -replace '\s','_'
cp "rustdesk/${{ env.appname }}.exe" "rustdesk/${myappname}.exe" -ErrorAction SilentlyContinue
pushd ./res/msi
python preprocess.py --app-name "$myappname" --arp -d ../../rustdesk
nuget restore msi.sln
msbuild msi.sln -p:Configuration=Release -p:Platform=x64 /p:TargetVersion=Windows10
cp ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk-latest.msi
mv ./Package/bin/x64/Release/en-us/Package.msi ../../SignOutput/rustdesk.msi
sha256sum ../../SignOutput/rustdesk.msi
- name: zip exe and msi
continue-on-error: true
shell: pwsh
run: |
Compress-Archive -Path ./SignOutput/*.exe, ./SignOutput/*.msi -DestinationPath ./SignOutput/unsigned_files.zip -CompressionLevel Fastest
- name: sign exe and msi
continue-on-error: true
shell: bash
run: |
if [ ! -z "${{ secrets.SIGN_BASE_URL }}" ] && [ ! -z "${{ secrets.SIGN_API_KEY }}" ]; then
curl -X POST -F "file=@./SignOutput/unsigned_files.zip" \
-H "X-API-KEY: ${{ secrets.SIGN_API_KEY }}" \
-m 900 \
"${{ secrets.SIGN_BASE_URL }}/sign/" -o ./SignOutput/signed_files.zip
else
echo "Signing skipped - signing URL or API key not configured"
cp ./SignOutput/unsigned_files.zip ./SignOutput/signed_files.zip
fi
- name: unzip exe and msi
continue-on-error: true
shell: pwsh
run: |
Expand-Archive -Path ./SignOutput/signed_files.zip -DestinationPath ./SignOutput/ -Force
Remove-Item ./SignOutput/unsigned_files.zip
Remove-Item ./SignOutput/signed_files.zip
- name: rename rustdesk.exe to filename.exe
run: |
mv ./SignOutput/rustdesk.exe "./SignOutput/${{ env.filename }}.exe" || echo "rustdesk"
- name: rename rustdesk.msi to filename.msi
continue-on-error: true
run: |
mv ./SignOutput/rustdesk.msi "./SignOutput/${{ env.filename }}.msi" || echo "rustdesk"
- name: send file to rdgen server
if: ${{ env.rdgen == 'true' }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: bash
command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.msi" -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: 1
max_attempts: 3
shell: bash
command: |
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" ${{ env.apiServer }}/api/save_custom_client
curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.msi" ${{ env.apiServer }}/api/save_custom_client || true
cleanup:
needs: [build-for-windows-flutter]
runs-on: ubuntu-latest
continue-on-error: true
if: always()
steps:
- name: Delete secrets artifact
uses: geekyeggo/delete-artifact@v5
with:
name: encrypted-secrets-zip

View File

@@ -41,14 +41,48 @@ jobs:
- name: Add MSBuild to PATH - name: Add MSBuild to PATH
uses: microsoft/setup-msbuild@v2 uses: microsoft/setup-msbuild@v2
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: encrypted-secrets-zip
- name: Load Secrets
uses: ./.github/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 }}"}'
- name: Download the source code - name: Download the source code
run: | run: |
git clone https://github.com/rustdesk-org/RustDeskTempTopMostWindow RustDeskTempTopMostWindow git clone https://github.com/rustdesk-org/RustDeskTempTopMostWindow RustDeskTempTopMostWindow
# Build. commit 53b548a5398624f7149a382000397993542ad796 is tag v0.3 # Build. commit 53b548a5398624f7149a382000397993542ad796 is tag v0.3
- name: Build the project - name: Build the project
run: | uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
command: |
cd RustDeskTempTopMostWindow && git checkout 53b548a5398624f7149a382000397993542ad796 cd RustDeskTempTopMostWindow && git checkout 53b548a5398624f7149a382000397993542ad796
if ($env:privacylink_url-ne "false") {
Invoke-WebRequest -Uri ${{ env.privacylink_url }}/get_png?filename=${{ env.privacylink_file }}"&"uuid=${{ env.privacylink_uuid }} -OutFile privacy.png
Invoke-WebRequest -Uri https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/privacyScreen.py -OutFile privacyScreen.py
python privacyScreen.py
rm ./WindowInjection/img.cpp
mv img.cpp ./WindowInjection/img.cpp
}
msbuild ${{ env.project_path }} -p:Configuration=${{ inputs.configuration }} -p:Platform=${{ inputs.platform }} /p:TargetVersion=${{ inputs.target_version }} msbuild ${{ env.project_path }} -p:Configuration=${{ inputs.configuration }} -p:Platform=${{ inputs.platform }} /p:TargetVersion=${{ inputs.target_version }}
- name: Archive build artifacts - name: Archive build artifacts

67
.github/workflows/vcpkg.yml vendored Normal file
View File

@@ -0,0 +1,67 @@
name: Export vcpkg Dependencies
on:
workflow_dispatch:
inputs:
reason:
description: 'Reason for manual build'
required: false
default: 'Manual dependency update'
env:
VCPKG_COMMIT_ID: '120deac3062162151622ca4860575a33844ba10b'
jobs:
build-and-export:
runs-on: windows-latest
strategy:
matrix:
job:
- vcpkg-triplet: x64-windows-static
steps:
- name: Enable Long Paths
run: git config --global core.longpaths true
- name: Install Build Tools
run: |
choco install nasm
echo "C:\Program Files\NASM" >> $GITHUB_PATH
- name: Checkout code
uses: actions/checkout@v4
- name: Clone RustDesk overlays
run: |
git clone --filter=blob:none --sparse https://github.com/rustdesk/rustdesk.git rustdesk_repo
cd rustdesk_repo
git sparse-checkout set res/vcpkg
git checkout master -- vcpkg.json || git checkout master -- vcpkg.json
cd ..
cp rustdesk_repo/vcpkg.json .
mkdir -p res
cp -r rustdesk_repo/res/vcpkg ./res/
shell: bash
- name: Setup vcpkg
uses: lukka/run-vcpkg@v11
with:
vcpkgDirectory: ${{ github.workspace }}/vcpkg
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
env:
VCPKG_DEFAULT_HOST_TRIPLET: ${{ matrix.job.vcpkg-triplet }}
run: |
./vcpkg/vcpkg install \
--triplet ${{ matrix.job.vcpkg-triplet }} \
--x-install-root="${{ github.workspace }}/installed"
shell: bash
- name: Upload Installed Dependencies
uses: actions/upload-artifact@v4
with:
name: vcpkg-export-${{ matrix.job.vcpkg-triplet }}
path: ${{ github.workspace }}/installed/
if-no-files-found: error

View File

@@ -3,8 +3,21 @@
The client generator is currently hosted [here](https://rdgen.crayoneater.org). The client generator is currently hosted [here](https://rdgen.crayoneater.org).
If you would like to host the generator yourself, see [here](setup.md) If you would like to host the generator yourself, see [here](setup.md)
This client generator is currently integrated into my rustdesk [api ## Features
server](https://github.com/bryangerlach/rustdesk-api-server), which is a fork
of [rustdesk-api-server](https://github.com/kingmo888/rustdesk-api-server). If - Embed server and key into client
you are running my api server, then you will still need to fork RDGen and go - Custom app name
through the setup process, but you won't need to actually run the rdgen server. - Custom icon/logo
- Set default settings for the client
- Support for rustdesk advanced settings (https://rustdesk.com/docs/en/self-host/client-configuration/advanced-settings/)
## Generate RustDesk clients from command line instead of using a web browser
Save your configuration from the rdgen web interface, or generate your own, then use that json file with [@AlekseyLapunov's rdgen-cli](https://github.com/AlekseyLapunov/rdgen-cli) to build from the command line on Windows, Linux, or MacOS like this: `python rdgen-cli -f my_config.json --set-version 1.4.5 --set-platform windows -s https://rdgen.crayoneater.org`
## Notes
- Icons should be square (256x256 recommended)
- Avoid special characters or non-English characters in app name and file name
- Build time is currently 30 - 45 minutes

View File

@@ -1,6 +1,7 @@
services: services:
rdgen: rdgen:
# use bryangerlach/rdgen:latest for the latest build # use bryangerlach/rdgen:latest for the latest build
#build: .
image: bryangerlach/rdgen:latest image: bryangerlach/rdgen:latest
restart: unless-stopped restart: unless-stopped
environment: environment:
@@ -8,9 +9,16 @@ services:
GHUSER: "github_username" GHUSER: "github_username"
GHBEARER: "github_access_token" GHBEARER: "github_access_token"
GENURL: "accessible_url_of_server" GENURL: "accessible_url_of_server"
ZIP_PASSWORD: ""
GHBRANCH: "master"
PROTOCOL: "https" PROTOCOL: "https"
REPONAME: "rdgen" REPONAME: "rdgen"
SH_SECRET: ""
ports: ports:
- "8000:8000" - "8000:8000"
dns: dns:
- 8.8.8.8 - 8.8.8.8
volumes:
- ./exe:/opt/rdgen/exe
- ./png:/opt/rdgen/png
- ./temp_zips:/opt/rdgen/temp_zips

View File

@@ -2,7 +2,8 @@ import os
# Adjust these values as needed # Adjust these values as needed
bind = "0.0.0.0:8000" # Host and port for Gunicorn to listen on bind = "0.0.0.0:8000" # Host and port for Gunicorn to listen on
workers = 3 # The number of worker processes for concurrency (adjust based on system resources) workers = 5 # The number of worker processes for concurrency (adjust based on system resources)
threads = 6
activate_base = True # Activate your virtual environment if applicable activate_base = True # Activate your virtual environment if applicable
# Path to your Django project's main WSGI application file (usually manage.py) # Path to your Django project's main WSGI application file (usually manage.py)

View File

@@ -24,15 +24,19 @@ SECRET_KEY = os.environ.get('SECRET_KEY','django-insecure-!(t-!f#6g#sr%yfded9(xh
GHUSER = os.environ.get("GHUSER", '') GHUSER = os.environ.get("GHUSER", '')
GHBEARER = os.environ.get("GHBEARER", '') GHBEARER = os.environ.get("GHBEARER", '')
GENURL = os.environ.get("GENURL", '') GENURL = os.environ.get("GENURL", '')
GHBRANCH = os.environ.get("GHBRANCH",'master')
ZIP_PASSWORD = os.environ.get("ZIP_PASSWORD",'insecure')
PROTOCOL = os.environ.get("PROTOCOL", 'https') PROTOCOL = os.environ.get("PROTOCOL", 'https')
REPONAME = os.environ.get("REPONAME", 'rdgen') REPONAME = os.environ.get("REPONAME", 'rdgen')
SH_SECRET = os.environ.get('SH_SECRET', 'secret')
MEDIA_URL = '/media/' MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False DEBUG_ENV = os.environ.get("DEBUG", "False")
DEBUG = DEBUG_ENV.lower() in ['true', '1', 't']
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
#CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS', '').split() #CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS', '').split()

View File

@@ -32,4 +32,6 @@ urlpatterns = [
url(r'^startgh',views.startgh), url(r'^startgh',views.startgh),
url(r'^get_png',views.get_png), url(r'^get_png',views.get_png),
url(r'^save_custom_client',views.save_custom_client), url(r'^save_custom_client',views.save_custom_client),
url(r'^get_zip',views.get_zip),
url(r'^cleanzip',views.cleanup_secrets),
] ]

View File

@@ -2,9 +2,10 @@ from django import forms
from PIL import Image from PIL import Image
class GenerateForm(forms.Form): class GenerateForm(forms.Form):
sh_secret_field = forms.CharField(required=False)
#Platform #Platform
platform = forms.ChoiceField(choices=[('windows','Windows 64Bit'),('windows-x86','Windows 32Bit'),('linux','Linux'),('android','Android'),('macos','macOS')], initial='windows') platform = forms.ChoiceField(choices=[('windows','Windows 64Bit'),('windows-x86','Windows 32Bit'),('linux','Linux'),('android','Android'),('macos','macOS')], initial='windows')
version = forms.ChoiceField(choices=[('master','nightly'),('1.4.5','1.4.5'),('1.4.4','1.4.4'),('1.4.3','1.4.3'),('1.4.2','1.4.2'),('1.4.1','1.4.1'),('1.4.0','1.4.0'),('1.3.9','1.3.9'),('1.3.8','1.3.8'),('1.3.7','1.3.7'),('1.3.6','1.3.6'),('1.3.5','1.3.5'),('1.3.4','1.3.4'),('1.3.3','1.3.3')], initial='1.4.5') version = forms.ChoiceField(choices=[('master','nightly'),('1.4.6','1.4.6'),('1.4.5','1.4.5'),('1.4.4','1.4.4'),('1.4.3','1.4.3'),('1.4.2','1.4.2'),('1.4.1','1.4.1'),('1.4.0','1.4.0'),('1.3.9','1.3.9'),('1.3.8','1.3.8'),('1.3.7','1.3.7'),('1.3.6','1.3.6'),('1.3.5','1.3.5'),('1.3.4','1.3.4'),('1.3.3','1.3.3')], initial='1.4.6')
help_text="'master' is the development version (nightly build) with the latest features but may be less stable" help_text="'master' is the development version (nightly build) with the latest features but may be less stable"
delayFix = forms.BooleanField(initial=True, required=False) delayFix = forms.BooleanField(initial=True, required=False)
@@ -37,8 +38,10 @@ class GenerateForm(forms.Form):
#Visual #Visual
iconfile = forms.FileField(label="Custom App Icon (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'})) iconfile = forms.FileField(label="Custom App Icon (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'}))
logofile = forms.FileField(label="Custom App Logo (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'})) logofile = forms.FileField(label="Custom App Logo (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'}))
privacyfile = forms.FileField(label="Custom privacy screen (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'}))
iconbase64 = forms.CharField(required=False) iconbase64 = forms.CharField(required=False)
logobase64 = forms.CharField(required=False) logobase64 = forms.CharField(required=False)
privacybase64 = forms.CharField(required=False)
theme = forms.ChoiceField(choices=[ theme = forms.ChoiceField(choices=[
('light', 'Light'), ('light', 'Light'),
('dark', 'Dark'), ('dark', 'Dark'),

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0.3 on 2026-03-11 04:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('rdgenerator', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='githubrun',
name='github_run_id',
field=models.BigIntegerField(blank=True, null=True),
),
]

View File

@@ -4,3 +4,4 @@ class GithubRun(models.Model):
id = models.IntegerField(verbose_name="ID",primary_key=True) id = models.IntegerField(verbose_name="ID",primary_key=True)
uuid = models.CharField(verbose_name="uuid", max_length=100) uuid = models.CharField(verbose_name="uuid", max_length=100)
status = models.CharField(verbose_name="status", max_length=100) status = models.CharField(verbose_name="status", max_length=100)
github_run_id = models.BigIntegerField(null=True, blank=True)

View File

@@ -0,0 +1,156 @@
<!DOCTYPE html>
<html>
<head>
<title id="pageTitle">Build Failure</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
text-align: center;
background: linear-gradient(135deg, #ffcfcf 0%, #e2c3c3 100%);
}
.platform-logo {
width: 150px;
height: 150px;
margin-bottom: 30px;
display: none;
filter: drop-shadow(0 10px 15px rgba(0,0,0,0.2));
transition: transform 0.3s ease;
}
.error-container {
background: white;
border-radius: 12px;
padding: 30px;
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
max-width: 500px;
width: 90%;
}
.error-header {
color: #c0392b;
font-weight: 700;
margin-bottom: 10px;
font-size: 1.5em;
}
.warning-box {
background-color: #fff3cd;
border: 1px solid #ffeeba;
color: #856404;
padding: 15px;
border-radius: 8px;
margin-bottom: 20px;
font-size: 0.9em;
text-align: left;
}
.download-section {
background: #f8f9fa;
padding: 15px;
border-radius: 10px;
border: 1px dashed #ccc;
}
.download-link {
display: block;
margin: 10px 0;
padding: 12px;
background-color: #34495e;
color: white;
text-decoration: none;
border-radius: 5px;
transition: opacity 0.3s ease;
}
.download-link:hover {
opacity: 0.8;
}
.log-link {
display: inline-block;
margin-top: 15px;
color: #3498db;
text-decoration: none;
font-size: 0.85em;
}
.platform-note {
color: #666;
font-size: 0.85em;
margin-top: 15px;
}
</style>
</head>
<body>
<svg id="windowsLogo" class="platform-logo" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<rect width="256" height="256" rx="50" ry="50" fill="#0078D7"/>
<text x="128" y="160" font-family="Arial" font-size="50" font-weight="bold" text-anchor="middle" fill="white">Win</text>
</svg>
<div class="error-container">
<h2 class="error-header">Workflow Interrupted</h2>
<div class="warning-box">
<strong>Warning:</strong> The build process did not complete successfully. Some files may be missing. You can attempt to download the available files below.
</div>
<div class="download-section">
{% if platform == 'windows' %}
<a href='/download?filename={{filename}}.exe&uuid={{uuid}}' class="download-link">Download {{filename}}.exe</a>
<a href='/download?filename={{filename}}.msi&uuid={{uuid}}' class="download-link">Download {{filename}}.msi</a>
{% elif platform == 'windows-x86' %}
<a href='/download?filename={{filename}}.exe&uuid={{uuid}}' class="download-link">Download {{filename}}.exe</a>
{% elif platform == 'linux' %}
<a href='/download?filename={{filename}}-x86_64.deb&uuid={{uuid}}' class="download-link">Download {{filename}}-x86_64.deb</a>
<a href='/download?filename={{filename}}-x86_64.rpm&uuid={{uuid}}' class="download-link">Download {{filename}}-x86_64.rpm</a>
<a href='/download?filename={{filename}}-suse-x86_64.rpm&uuid={{uuid}}' class="download-link">Download {{filename}}-suse-x86_64.rpm</a>
<a href='/download?filename={{filename}}-x86_64.pkg.tar.zst&uuid={{uuid}}' class="download-link">Download {{filename}}-x86_64.pkg.tar.zst</a>
<a href='/download?filename={{filename}}-aarch64.deb&uuid={{uuid}}' class="download-link">Download {{filename}}-aarch64.deb</a>
<a href='/download?filename={{filename}}-aarch64.rpm&uuid={{uuid}}' class="download-link">Download {{filename}}-aarch64.rpm</a>
<a href='/download?filename={{filename}}-suse-aarch64.rpm&uuid={{uuid}}' class="download-link">Download {{filename}}-suse-aarch64.rpm</a>
<a href='/download?filename={{filename}}-aarch64.pkg.tar.zst&uuid={{uuid}}' class="download-link">Download {{filename}}-aarch64.pkg.tar.zst</a>
<a href='/download?filename={{filename}}-x86_64.AppImage&uuid={{uuid}}' class="download-link">Download {{filename}}-x86_64.AppImage</a>
<a href='/download?filename={{filename}}-aarch64.AppImage&uuid={{uuid}}' class="download-link">Download {{filename}}-aarch64.AppImage</a>
<a href='/download?filename={{filename}}-x86_64.flatpak&uuid={{uuid}}' class="download-link">Download {{filename}}-x86_64.flatpak</a>
<a href='/download?filename={{filename}}-aarch64.flatpak&uuid={{uuid}}' class="download-link">Download {{filename}}-aarch64.flatpak</a>
{% elif platform == 'android' %}
<a href='/download?filename={{filename}}-aarch64.apk&uuid={{uuid}}' class="download-link">Download {{filename}}-aarch64.apk</a>
<a href='/download?filename={{filename}}-x86_64.apk&uuid={{uuid}}' class="download-link">Download {{filename}}-x86_64.apk</a>
<a href='/download?filename={{filename}}-armv7.apk&uuid={{uuid}}' class="download-link">Download {{filename}}-armv7.apk</a>
{% elif platform == 'macos' %}
<a href='/download?filename={{filename}}-x86_64.dmg&uuid={{uuid}}' class="download-link">Download {{filename}}-x86_64.dmg</a>
<a href='/download?filename={{filename}}-aarch64.dmg&uuid={{uuid}}' class="download-link">Download {{filename}}-aarch64.dmg</a>
{% else %}
<p>Error: No file generated</p>
{% endif %}
</div>
<a href="{{ log_url }}" target="_blank" class="log-link">Check GitHub Logs for error details ↗</a>
<div id="platformNote" class="platform-note"></div>
<hr style="border: 0; border-top: 1px solid #eee; margin: 20px 0;">
<a href="/" style="font-size: 0.9em; color: #666; text-decoration: none;">← Return to Form</a>
</div>
<script>
function updatePlatformUI() {
const platform = '{{platform}}'.toLowerCase();
const platformLogos = {
'windows': document.getElementById('windowsLogo'),
'macos': document.getElementById('macosLogo'),
'linux': document.getElementById('linuxLogo'),
'android': document.getElementById('androidLogo')
};
Object.values(platformLogos).forEach(logo => { if(logo) logo.style.display = 'none'; });
if (platform.includes('windows')) {
document.getElementById('pageTitle').textContent = 'Windows Build Failed';
if(platformLogos.windows) platformLogos.windows.style.display = 'block';
} else if (platform === 'macos') {
if(platformLogos.macos) platformLogos.macos.style.display = 'block';
} else if (platform === 'linux') {
if(platformLogos.linux) platformLogos.linux.style.display = 'block';
}
}
updatePlatformUI();
</script>
</body>
</html>

View File

@@ -330,6 +330,7 @@
<h2><i class="fas fa-paint-brush"></i> Visual</h2> <h2><i class="fas fa-paint-brush"></i> Visual</h2>
{{ form.iconbase64.as_hidden }} {{ form.iconbase64.as_hidden }}
{{ form.logobase64.as_hidden }} {{ form.logobase64.as_hidden }}
{{ form.privacybase64.as_hidden }}
<label for="{{ form.iconfile.id_for_label }}">Custom App Icon (in .png format)</label> <label for="{{ form.iconfile.id_for_label }}">Custom App Icon (in .png format)</label>
{{ form.iconfile }}<br><br> {{ form.iconfile }}<br><br>
<!-- <input type="file" name="iconfile" id="iconfile" accept="image/png"> --> <!-- <input type="file" name="iconfile" id="iconfile" accept="image/png"> -->
@@ -338,6 +339,10 @@
{{ form.logofile }}<br><br> {{ form.logofile }}<br><br>
<!-- <input type="file" name="logofile" id="logofile" accept="image/png"> --> <!-- <input type="file" name="logofile" id="logofile" accept="image/png"> -->
<div id="logo-preview"></div><br><br> <div id="logo-preview"></div><br><br>
<label for="{{ form.privacyfile.id_for_label }}">Custom Privacy Screen (in .png format)</label>
{{ form.privacyfile }}<br><br>
<!-- <input type="file" name="iconfile" id="iconfile" accept="image/png"> -->
<div id="privacy-preview"></div><br><br>
<label for="{{ form.theme.id_for_label }}">Theme:</label> <label for="{{ form.theme.id_for_label }}">Theme:</label>
{{ form.theme }} {{ form.themeDorO }} *Default sets the theme but allows the client to change it, Override sets the theme permanently.<br><br> {{ form.theme }} {{ form.themeDorO }} *Default sets the theme but allows the client to change it, Override sets the theme permanently.<br><br>
</div> </div>
@@ -415,6 +420,9 @@
document.getElementById("{{ form.logofile.id_for_label }}").addEventListener('change', function(event) { document.getElementById("{{ form.logofile.id_for_label }}").addEventListener('change', function(event) {
previewImage(event.target, 'logo-preview'); previewImage(event.target, 'logo-preview');
}); });
document.getElementById("{{ form.privacyfile.id_for_label }}").addEventListener('change', function(event) {
previewImage(event.target, 'privacy-preview');
});
document.getElementById("{{ form.hidecm.id_for_label }}").addEventListener('change',function() { document.getElementById("{{ form.hidecm.id_for_label }}").addEventListener('change',function() {
if (this.checked) { if (this.checked) {
@@ -594,6 +602,16 @@
} }
} }
const privacyPreview = document.getElementById('privacy-preview');
if (privacyPreview.firstChild && privacyPreview.firstChild.src.startsWith('data:image/png;base64')) {
formData.privacyfile = privacyPreview.firstChild.src; // Use existing base64
} else { //if it's a file upload
const privacyFile = document.getElementById("{{ form.privacyfile.id_for_label }}").files[0];
if (privacyFile) {
formData.privacyfile = await readFileAsBase64(privacyFile);
}
}
const jsonData = JSON.stringify(formData, null, 2); const jsonData = JSON.stringify(formData, null, 2);
const blob = new Blob([jsonData], { type: "application/json" }); const blob = new Blob([jsonData], { type: "application/json" });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);
@@ -655,6 +673,10 @@
document.getElementById('id_logobase64').value = formData[key]; document.getElementById('id_logobase64').value = formData[key];
document.getElementById('logo-preview').innerHTML = `<img src="${formData[key]}" style="max-width: 300px; max-height: 60px;">`; document.getElementById('logo-preview').innerHTML = `<img src="${formData[key]}" style="max-width: 300px; max-height: 60px;">`;
} }
if (key === 'privacyfile' && formData[key]) {
document.getElementById('id_privacybase64').value = formData[key];
document.getElementById('privacy-preview').innerHTML = `<img src="${formData[key]}" style="max-width: 300px; max-height: 60px;">`;
}
}); });
} }
} }

View File

@@ -69,6 +69,28 @@
background-color: #3498db; background-color: #3498db;
transition: width 0.5s ease-in-out; transition: width 0.5s ease-in-out;
} }
.log-container {
margin-top: 30px;
padding: 15px;
background: rgba(255, 255, 255, 0.5);
border-radius: 12px;
font-size: 0.85em;
max-width: 320px;
margin-left: auto;
margin-right: auto;
border: 1px solid rgba(0,0,0,0.05);
}
.log-link {
color: #3498db;
text-decoration: none;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 5px;
}
.log-link:hover {
text-decoration: underline;
}
</style> </style>
</head> </head>
<body> <body>
@@ -140,8 +162,16 @@
<div class="progress-bar"> <div class="progress-bar">
<div id="progressBarFill" class="progress-bar-fill"></div> <div id="progressBarFill" class="progress-bar-fill"></div>
</div> </div>
<p class="status-text">This can take 20-30 minutes (or longer if there are other users).</p> <p class="status-text">This can take 30-45 minutes. You can leave this page open or come back later.</p>
<p class="status-text">Status: <span id="statusText">{{status}}</span></p> <p class="status-text">Status: <span id="statusText">{{status}}</span></p>
<div class="log-container">
<p style="margin: 0 0 8px 0; color: #888;">Technical View</p>
<a href="{{ log_url }}" target="_blank" class="log-link">
View GitHub Action Logs
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>
</a>
</div>
</div> </div>
<div id="macosNote" class="macos-note"> <div id="macosNote" class="macos-note">
@@ -212,7 +242,7 @@
setTimeout(function() { setTimeout(function() {
window.location.replace('/check_for_file?filename={{filename}}&uuid={{uuid}}&platform={{platform}}'); window.location.replace('/check_for_file?filename={{filename}}&uuid={{uuid}}&platform={{platform}}');
}, 5000); // 5000 milliseconds = 5 seconds }, 30000); // 20000 milliseconds = 20 seconds
</script> </script>
</body> </body>
</html> </html>

View File

@@ -4,24 +4,29 @@ from django.http import HttpResponse, JsonResponse
from django.shortcuts import render from django.shortcuts import render
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
import os import os
import secrets
import re import re
import requests import requests
import base64 import base64
import json import json
import uuid import uuid
import pyzipper
from django.conf import settings as _settings from django.conf import settings as _settings
from django.db.models import Q from django.db.models import Q
from .forms import GenerateForm from .forms import GenerateForm
from .models import GithubRun from .models import GithubRun
from PIL import Image from PIL import Image
from urllib.parse import quote from urllib.parse import quote
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
def generator_view(request): def generator_view(request):
if request.method == 'POST': if request.method == 'POST':
form = GenerateForm(request.POST, request.FILES) form = GenerateForm(request.POST, request.FILES)
if form.is_valid(): if form.is_valid():
user_secret = form.cleaned_data['sh_secret_field']
if _settings.SH_SECRET == user_secret:
selfhosted = True
else:
selfhosted = False
platform = form.cleaned_data['platform'] platform = form.cleaned_data['platform']
version = form.cleaned_data['version'] version = form.cleaned_data['version']
delayFix = form.cleaned_data['delayFix'] delayFix = form.cleaned_data['delayFix']
@@ -48,6 +53,8 @@ def generator_view(request):
installation = form.cleaned_data['installation'] installation = form.cleaned_data['installation']
settings = form.cleaned_data['settings'] settings = form.cleaned_data['settings']
appname = form.cleaned_data['appname'] appname = form.cleaned_data['appname']
if not appname:
appname = "rustdesk"
filename = form.cleaned_data['exename'] filename = form.cleaned_data['exename']
compname = form.cleaned_data['compname'] compname = form.cleaned_data['compname']
if not compname: if not compname:
@@ -98,18 +105,32 @@ def generator_view(request):
iconfile = form.cleaned_data.get('iconfile') iconfile = form.cleaned_data.get('iconfile')
if not iconfile: if not iconfile:
iconfile = form.cleaned_data.get('iconbase64') iconfile = form.cleaned_data.get('iconbase64')
iconlink = save_png(iconfile,myuuid,full_url,"icon.png") iconlink_url, iconlink_uuid, iconlink_file = save_png(iconfile,myuuid,full_url,"icon.png")
except: except:
print("failed to get icon, using default") print("failed to get icon, using default")
iconlink = "false" iconlink_url = "false"
iconlink_uuid = "false"
iconlink_file = "false"
try: try:
logofile = form.cleaned_data.get('logofile') logofile = form.cleaned_data.get('logofile')
if not logofile: if not logofile:
logofile = form.cleaned_data.get('logobase64') logofile = form.cleaned_data.get('logobase64')
logolink = save_png(logofile,myuuid,full_url,"logo.png") logolink_url, logolink_uuid, logolink_file = save_png(logofile,myuuid,full_url,"logo.png")
except: except:
print("failed to get logo") print("failed to get logo")
logolink = "false" logolink_url = "false"
logolink_uuid = "false"
logolink_file = "false"
try:
privacyfile = form.cleaned_data.get('privacyfile')
if not privacyfile:
privacyfile = form.cleaned_data.get('privacybase64')
privacylink_url, privacylink_uuid, privacylink_file = save_png(privacyfile,myuuid,full_url,"privacy.png")
except:
print("failed to get logo")
privacylink_url = "false"
privacylink_uuid = "false"
privacylink_file = "false"
###create the custom.txt json here and send in as inputs below ###create the custom.txt json here and send in as inputs below
decodedCustom = {} decodedCustom = {}
@@ -192,25 +213,26 @@ def generator_view(request):
base64_bytes = base64.b64encode(string_bytes) base64_bytes = base64.b64encode(string_bytes)
encodedCustom = base64_bytes.decode("ascii") encodedCustom = base64_bytes.decode("ascii")
#github limits inputs to 10, so lump extras into one with json # #github limits inputs to 10, so lump extras into one with json
extras = {} # extras = {}
extras['genurl'] = _settings.GENURL # extras['genurl'] = _settings.GENURL
#extras['runasadmin'] = runasadmin # #extras['runasadmin'] = runasadmin
extras['urlLink'] = urlLink # extras['urlLink'] = urlLink
extras['downloadLink'] = downloadLink # extras['downloadLink'] = downloadLink
extras['delayFix'] = 'true' if delayFix else 'false' # extras['delayFix'] = 'true' if delayFix else 'false'
extras['version'] = version # extras['rdgen'] = 'true'
extras['rdgen'] = 'true' # extras['cycleMonitor'] = 'true' if cycleMonitor else 'false'
extras['cycleMonitor'] = 'true' if cycleMonitor else 'false' # extras['xOffline'] = 'true' if xOffline else 'false'
extras['xOffline'] = 'true' if xOffline else 'false' # extras['removeNewVersionNotif'] = 'true' if removeNewVersionNotif else 'false'
extras['removeNewVersionNotif'] = 'true' if removeNewVersionNotif else 'false' # extras['compname'] = compname
extras['compname'] = compname # extras['androidappid'] = androidappid
extras['androidappid'] = androidappid # extra_input = json.dumps(extras)
extra_input = json.dumps(extras)
####from here run the github action, we need user, repo, access token. ####from here run the github action, we need user, repo, access token.
if platform == 'windows': if platform == 'windows':
url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows.yml/dispatches' url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows.yml/dispatches'
if selfhosted:
url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/sh-generator-windows.yml/dispatches'
if platform == 'windows-x86': if platform == 'windows-x86':
url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows-x86.yml/dispatches' url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows-x86.yml/dispatches'
elif platform == 'linux': elif platform == 'linux':
@@ -221,55 +243,154 @@ def generator_view(request):
url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-macos.yml/dispatches' url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-macos.yml/dispatches'
else: else:
url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows.yml/dispatches' url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows.yml/dispatches'
if selfhosted:
url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/sh-generator-windows.yml/dispatches'
#url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/test.yml/dispatches' #url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/test.yml/dispatches'
data = { inputs_raw = {
"ref":"master",
"inputs":{
"server":server, "server":server,
"key":key, "key":key,
"apiServer":apiServer, "apiServer":apiServer,
"custom":encodedCustom, "custom":encodedCustom,
"uuid":myuuid, "uuid":myuuid,
"iconlink":iconlink, "iconlink_url":iconlink_url,
"logolink":logolink, "iconlink_uuid":iconlink_uuid,
"iconlink_file":iconlink_file,
"logolink_url":logolink_url,
"logolink_uuid":logolink_uuid,
"logolink_file":logolink_file,
"privacylink_url":privacylink_url,
"privacylink_uuid":privacylink_uuid,
"privacylink_file":privacylink_file,
"appname":appname, "appname":appname,
"extras":extra_input, "genurl":_settings.GENURL,
"urlLink":urlLink,
"downloadLink":downloadLink,
"delayFix": 'true' if delayFix else 'false',
"rdgen":'true',
"cycleMonitor": 'true' if cycleMonitor else 'false',
"xOffline": 'true' if xOffline else 'false',
"removeNewVersionNotif": 'true' if removeNewVersionNotif else 'false',
"compname": compname,
"androidappid":androidappid,
"filename":filename "filename":filename
} }
temp_json_path = f"data_{uuid.uuid4()}.json"
zip_filename = f"secrets_{uuid.uuid4()}.zip"
zip_path = "temp_zips/%s" % (zip_filename)
Path("temp_zips").mkdir(parents=True, exist_ok=True)
with open(temp_json_path, "w") as f:
json.dump(inputs_raw, f)
with pyzipper.AESZipFile(zip_path, 'w', compression=pyzipper.ZIP_LZMA, encryption=pyzipper.WZ_AES) as zf:
zf.setpassword(_settings.ZIP_PASSWORD.encode())
zf.write(temp_json_path, arcname="secrets.json")
if os.path.exists(temp_json_path):
os.remove(temp_json_path)
zipJson = {}
zipJson['url'] = full_url
zipJson['file'] = zip_filename
zip_url = json.dumps(zipJson)
data = {
"ref":_settings.GHBRANCH,
"inputs":{
"version":version,
"zip_url":zip_url
},
"return_run_details": True
} }
#print(data) #print(data)
headers = { headers = {
'Accept': 'application/vnd.github+json', 'Accept': 'application/vnd.github+json',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': 'Bearer '+_settings.GHBEARER, 'Authorization': 'Bearer '+_settings.GHBEARER,
'X-GitHub-Api-Version': '2022-11-28' 'X-GitHub-Api-Version': '2026-03-10'
} }
create_github_run(myuuid) new_github_run = GithubRun(
uuid=myuuid,
status="Starting generator...please wait"
)
try:
response = requests.post(url, json=data, headers=headers) response = requests.post(url, json=data, headers=headers)
print(response) #print(response)
if response.status_code == 204 or response.status_code == 200: if response.status_code == 204 or response.status_code == 200:
return render(request, 'waiting.html', {'filename':filename, 'uuid':myuuid, 'status':"Starting generator...please wait", 'platform':platform}) github_data = response.json()
print(github_data)
new_github_run.github_run_id = github_data.get('workflow_run_id')
new_github_run.status = "in_progress"
new_github_run.save()
return render(request, 'waiting.html', {'filename':filename, 'uuid':myuuid, 'status':"Starting generator...please wait", 'platform':platform, 'log_url': github_data.get('html_url')})
else: else:
return JsonResponse({"error": "Something went wrong"}) #new_github_run.delete()
return JsonResponse({"error": "GitHub rejected the start request"}, status=500)
except Exception as e:
#new_github_run.delete()
return JsonResponse({"error": f"Connection error: {str(e)}"}, status=500)
else: else:
form = GenerateForm() form = GenerateForm()
#return render(request, 'maintenance.html') #return render(request, 'maintenance.html')
return render(request, 'generator.html', {'form': form}) return render(request, 'generator.html', {'form': form})
def check_for_file(request): from django.shortcuts import render, get_object_or_404
filename = request.GET['filename'] from django.db.models import Q
uuid = request.GET['uuid']
platform = request.GET['platform'] def check_for_file(request):
gh_run = GithubRun.objects.filter(Q(uuid=uuid)).first() filename = request.GET.get('filename')
status = gh_run.status uuid = request.GET.get('uuid')
platform = request.GET.get('platform')
gh_run = get_object_or_404(GithubRun, uuid=uuid)
github_log_url = f"https://github.com/{_settings.GHUSER}/{_settings.REPONAME}/actions/runs/{gh_run.github_run_id}"
if gh_run.status not in ['success', 'failure', 'cancelled', 'timed_out', 'skipped']:
headers = {
"Authorization": f"Bearer {_settings.GHBEARER}",
"Accept": "application/vnd.github+json"
}
api_url = f"https://api.github.com/repos/{_settings.GHUSER}/{_settings.REPONAME}/actions/runs/{gh_run.github_run_id}"
try:
gh_response = requests.get(api_url, headers=headers)
if gh_response.status_code == 200:
gh_data = gh_response.json()
if gh_data['status'] == 'completed':
gh_run.status = gh_data['conclusion']
gh_run.save()
except Exception as e:
print(f"Error checking GitHub: {e}")
if gh_run.status == "success":
return render(request, 'generated.html', {
'filename': filename,
'uuid': uuid,
'platform': platform
})
elif gh_run.status in ['failure', 'cancelled', 'timed_out', 'skipped', 'action_required']:
return render(request, 'failure.html', {
'log_url': github_log_url,
'filename': filename,
'uuid': uuid,
'platform': platform,
'status': gh_run.status
})
#if file_exists:
if status == "Success":
return render(request, 'generated.html', {'filename': filename, 'uuid':uuid, 'platform':platform})
else: else:
return render(request, 'waiting.html', {'filename':filename, 'uuid':uuid, 'status':status, 'platform':platform}) return render(request, 'waiting.html', {
'filename': filename,
'uuid': uuid,
'status': gh_run.status,
'platform': platform,
'log_url': github_log_url
})
def download(request): def download(request):
filename = request.GET['filename'] filename = request.GET['filename']
@@ -357,7 +478,7 @@ def startgh(request):
####from here run the github action, we need user, repo, access token. ####from here run the github action, we need user, repo, access token.
url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-'+data_.get('platform')+'.yml/dispatches' url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-'+data_.get('platform')+'.yml/dispatches'
data = { data = {
"ref":"master", "ref": _settings.GHBRANCH,
"inputs":{ "inputs":{
"server":data_.get('server'), "server":data_.get('server'),
"key":data_.get('key'), "key":data_.get('key'),
@@ -375,7 +496,7 @@ def startgh(request):
'Accept': 'application/vnd.github+json', 'Accept': 'application/vnd.github+json',
'Content-Type': 'application/json', 'Content-Type': 'application/json',
'Authorization': 'Bearer '+_settings.GHBEARER, 'Authorization': 'Bearer '+_settings.GHBEARER,
'X-GitHub-Api-Version': '2022-11-28' 'X-GitHub-Api-Version': '2026-03-10'
} }
response = requests.post(url, json=data, headers=headers) response = requests.post(url, json=data, headers=headers)
print(response) print(response)
@@ -400,12 +521,12 @@ def save_png(file, uuid, domain, name):
with open(file_save_path, "wb+") as f: with open(file_save_path, "wb+") as f:
for chunk in file.chunks(): for chunk in file.chunks():
f.write(chunk) f.write(chunk)
imageJson = {} # imageJson = {}
imageJson['url'] = domain # imageJson['url'] = domain
imageJson['uuid'] = uuid # imageJson['uuid'] = uuid
imageJson['file'] = name # imageJson['file'] = name
#return "%s/%s" % (domain, file_save_path) #return "%s/%s" % (domain, file_save_path)
return json.dumps(imageJson) return domain, uuid, name
def save_custom_client(request): def save_custom_client(request):
file = request.FILES['file'] file = request.FILES['file']
@@ -417,3 +538,38 @@ def save_custom_client(request):
f.write(chunk) f.write(chunk)
return HttpResponse("File saved successfully!") return HttpResponse("File saved successfully!")
def cleanup_secrets(request):
# Pass the UUID as a query param or in JSON body
data = json.loads(request.body)
my_uuid = data.get('uuid')
if not my_uuid:
return HttpResponse("Missing UUID", status=400)
# 1. Find the files in your temp directory matching the UUID
temp_dir = os.path.join('temp_zips')
# We look for any file starting with 'secrets_' and containing the uuid
for filename in os.listdir(temp_dir):
if my_uuid in filename and filename.endswith('.zip'):
file_path = os.path.join(temp_dir, filename)
try:
os.remove(file_path)
print(f"Successfully deleted {file_path}")
except OSError as e:
print(f"Error deleting file: {e}")
return HttpResponse("Cleanup successful", status=200)
def get_zip(request):
filename = request.GET['filename']
#filename = filename+".exe"
file_path = os.path.join('temp_zips',filename)
with open(file_path, 'rb') as file:
response = HttpResponse(file, headers={
'Content-Type': 'application/vnd.microsoft.portable-executable',
'Content-Disposition': f'attachment; filename="{filename}"'
})
return response

View File

@@ -2,4 +2,4 @@ django
requests requests
pillow pillow
gunicorn gunicorn
cryptography>=42.0.0 pyzipper

View File

@@ -20,15 +20,37 @@
* Now click New repository secret * Now click New repository secret
* Set the Name to GENURL * Set the Name to GENURL
* Set the Secret to https://rdgen.hostname.com (or whatever your server will be accessed from) * Set the Secret to https://rdgen.hostname.com (or whatever your server will be accessed from)
* Now click New repository secret again
* Set the Name to ZIP_PASSWORD
* Set the Secret to any password you want (use this in the next step as well) - generate a password by running: ```python3 -c 'import secrets; print(secrets.token_hex(100))'```
4. Now download the docker-compose.yml file and fill in the environment variables: 4. Now download the docker-compose.yml file and fill in the environment variables:
* SECRET_KEY="your secret key" - generate a secret key by running: ```python3 -c 'import secrets; print(secrets.token_hex(100))'``` * SECRET_KEY="your secret key" - generate a secret key by running: ```python3 -c 'import secrets; print(secrets.token_hex(100))'```
* GHUSER="your github username" * GHUSER="your github username"
* GHBEARER="your fine-grained access token" * GHBEARER="your fine-grained access token"
* ZIP_PASSWORD="the same password that you entered as a github secret"
* PROTOCOL="https" *optional - defaults to "https", change to "http" if you need to * PROTOCOL="https" *optional - defaults to "https", change to "http" if you need to
* REPONAME="rdgen" *optional - defaults to "rdgen", change this if you renamed the repo when you forked it * REPONAME="rdgen" *optional - defaults to "rdgen", change this if you renamed the repo when you forked it
5. Now just run ```docker compose up -d``` 5. Now just run ```docker compose up -d```
## Use a self hosted github runner for faster client generation (Windows only right now)
1. First you need to set up a Windows computer that can build rustdesk
2. Once you can build rustdesk, follow github instructions for setting up a self hosted github runner
3. Now you need to add an environment variable SH_SECRET, which has a key/password that you will need to send to the server
4. Save a json configuration file from your rdgen web ui
5. Use the [rdgen-cli] (https://github.com/AlekseyLapunov/rdgen-cli) to submit your json configuration with the added key "sh_secret_field" with the value matching your SH_SECRET
## Use your own Windows code signing token
1. You will need a USB signing token plugged into a Windows computer
2. On the computer with the USB signing token, you need to make sure it is set up correctly to sign using signtool.exe
3. Run a small [signing api](https://github.com/bryangerlach/signing_api) server on the computer with the USB token connected. Follow the setup instructions for this server.
4. Now for your rdgen repo, add github secrets for
- SIGN_BASE_URL (the accesible over the internet URL for the signing api server)
- SIGN_API_KEY (the api key you have set on your signing api server)
## Host manually: ## Host manually:
1. A Github account with a fork of this repo 1. A Github account with a fork of this repo