239 Commits

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
Bryan Gerlach
1650994efa 1.4.5 2026-01-09 23:02:42 -06:00
Bryan Gerlach
c74dd0a2e4 .. 2026-01-08 15:57:04 -06:00
Bryan Gerlach
5fb84b739c fix android app id 2026-01-07 08:27:01 -06:00
Bryan Gerlach
278234dd08 icon stuff 2026-01-06 16:13:15 -06:00
Bryan Gerlach
54ff4d8c8b allow custom android application id 2026-01-06 16:02:53 -06:00
Bryan Gerlach
ceaf44f71d testing print driver with custom_.txt 2025-12-10 16:35:30 -06:00
Bryan Gerlach
1287e7e5df replace sed with perl to fix stalling on large base64 strings 2025-12-02 16:55:26 -06:00
Bryan Gerlach
d6d6165c4b remove printer driver for now 2025-11-25 10:28:01 -06:00
Bryan Gerlach
b133ba0d66 .. 2025-11-23 14:18:20 -06:00
Bryan Gerlach
47da6243dd .. 2025-11-23 14:16:52 -06:00
Bryan Gerlach
661f39543c . 2025-11-21 21:49:17 -06:00
Bryan Gerlach
f72d9be70c change permissions on custom.txt 2025-11-20 19:58:20 -06:00
Bryan Gerlach
88dca7235b change permissions on custom.txt 2025-11-20 19:54:44 -06:00
Bryan Gerlach
05906539d7 change permissions on custom.txt 2025-11-20 17:06:49 -06:00
Bryan Gerlach
851d8e9771 1.4.4 2025-11-20 16:22:12 -06:00
Bryan Gerlach
e4420c24c8 1.4.4 2025-11-20 15:33:33 -06:00
Bryan Gerlach
fae826ef53 1.4.4 2025-11-20 13:04:05 -06:00
Bryan Gerlach
5052bec676 remove run as admin option 2025-11-19 11:24:35 -06:00
Bryan Gerlach
e46d0bd0ef 1.4.3 2025-10-19 16:45:17 -05:00
Bryan
67e2bc7df9 Merge pull request #137 from Maxetto/patch-1
Fix Windows 64 and 32 Bit both active at same time
2025-10-06 16:16:22 -05:00
Maxetto
2e229d0800 Fix Windows 64 and 32 Bit both active at same time 2025-09-29 17:51:23 +02:00
Bryan Gerlach
89ee4822dd add icon for 32 bit windows 2025-09-25 13:16:11 -05:00
Bryan Gerlach
c429836057 add icon for 32 bit windows 2025-09-25 13:12:22 -05:00
Bryan Gerlach
6f1bb528bd add settings for camera,terminal, and printer 2025-09-25 13:02:58 -05:00
Bryan
13114d0bc3 Merge pull request #128 from Maxetto/website-changes
Proposal for Website changes
2025-09-25 12:49:32 -05:00
Bryan
56c4d9f0b7 Merge branch 'master' into website-changes 2025-09-25 12:49:24 -05:00
Bryan
6c7c1ed3e7 Merge pull request #127 from Maxetto/x86-build
Create X86 Windows Generator
2025-09-25 12:45:47 -05:00
Bryan
abbf4d899b Merge pull request #126 from Maxetto/generator-fixes
Generator fixes
2025-09-25 12:45:21 -05:00
Bryan
3e22a34906 Merge branch 'master' into generator-fixes 2025-09-25 12:45:02 -05:00
Bryan Gerlach
b4ec36c59a v1.4.2 2025-09-18 21:31:40 -05:00
Bryan Gerlach
7d578df4c2 v1.4.2 2025-09-18 20:57:26 -05:00
Maxetto
3169440af6 Revert moving Setup Server Tip block 2025-09-08 17:23:16 +02:00
Maxetto
12b5fe1e30 Update Waiting Page 2025-09-05 12:17:13 +02:00
Maxetto
23e91a5ea7 Update Generator Page 2025-09-05 12:16:40 +02:00
Maxetto
be47bcbe46 Update Generated page 2025-09-05 12:14:05 +02:00
Maxetto
668998e390 Update Views 2025-09-05 12:12:48 +02:00
Maxetto
fabbc91fae Add x86 in Forms 2025-09-05 12:08:32 +02:00
Maxetto
50082d5566 Create X86 Windows Generator 2025-09-05 11:56:19 +02:00
Maxetto
ac3d60cae1 Fix Hide CM and Remove Wallpaper options 2025-09-05 11:49:16 +02:00
Maxetto
6755dc1fa0 Remove Hide CM patch and move around things 2025-09-05 11:36:21 +02:00
Bryan Gerlach
c53548dcce linux appimage 2025-07-28 15:11:58 -05:00
Bryan Gerlach
e36790addc v1.4.1 2025-07-24 10:39:18 -05:00
Bryan Gerlach
94f1560196 v1.4.1 2025-07-24 10:38:20 -05:00
Bryan Gerlach
6ac5f4b283 . 2025-06-17 07:59:47 -05:00
Bryan Gerlach
606a5ee956 . 2025-06-17 07:51:55 -05:00
Bryan Gerlach
2548477390 fix code changes 2025-06-06 11:59:30 -05:00
Bryan Gerlach
885523e2f9 1.4.0 2025-05-11 14:57:46 -05:00
Bryan Gerlach
253749df21 macos 2025-05-01 16:45:34 -05:00
Bryan Gerlach
050f4b3130 fix the & symbol in company name 2025-04-30 07:06:07 -05:00
Bryan Gerlach
cc59f1d58d macos stuff 2025-04-19 00:57:00 -05:00
Bryan Gerlach
a56f965344 macos stuff 2025-04-19 00:04:05 -05:00
Bryan Gerlach
a45bc0acdd macos stuff 2025-04-18 20:58:18 -05:00
Bryan
5ea49e7a00 Merge pull request #72 from MLPCR/master
Continue on Update status timeout & Fix missed rustdesk.com to custom url text on android client
2025-04-18 20:57:59 -05:00
MLPCR
131ec1759d Merge branch 'bryangerlach:master' into master 2025-04-18 18:37:48 +00:00
MLPCR
bcef3a4026 Update generator-android.yml
Correct missed website link on android settings page
2025-04-18 15:34:28 +00:00
Bryan Gerlach
0240a905f9 fix macos 2025-04-18 10:04:35 -05:00
Bryan Gerlach
8577a25657 fix macos 2025-04-18 09:53:00 -05:00
Bryan Gerlach
465a9d3554 fix macos 2025-04-18 09:41:49 -05:00
MLPCR
f0f67f5be1 Update generator-linux.yml
Continue on error when update status timesout
2025-04-17 22:55:28 +00:00
MLPCR
2c1a5f93b5 Update generator-android.yml
Continue on error when update status timeout
2025-04-17 22:19:35 +00:00
MLPCR
55c41599be Update generator-windows.yml
Continue on Error or timeout of update status
2025-04-17 21:02:55 +00:00
Bryan Gerlach
9f23f1c94a fix linux deploy for api server 2025-04-17 08:35:42 -05:00
Bryan Gerlach
7479fbbd8f flatpak,appimage 2025-04-16 21:35:52 -05:00
Bryan Gerlach
c0ede165ab flatpak,appimage 2025-04-16 20:38:56 -05:00
Bryan Gerlach
78ec60d272 flatpak,appimage 2025-04-16 19:50:07 -05:00
Bryan Gerlach
807f26360e flatpak,appimage 2025-04-16 18:16:15 -05:00
Bryan Gerlach
bad75caf0b flatpak,appimage 2025-04-16 17:38:56 -05:00
Bryan Gerlach
b76e34aa1c delete artifact 2025-04-16 16:57:19 -05:00
Bryan Gerlach
e3c41ee258 flatpak & appimage 2025-04-16 16:52:45 -05:00
Bryan Gerlach
c76a63d269 flatpak & appimage 2025-04-16 16:42:42 -05:00
Bryan
88918cde01 Merge pull request #65 from Draakoor/master
Update generator-linux.yml
2025-04-15 21:17:12 -05:00
Dominik
6725aa21b0 Update generator-linux.yml 2025-04-16 03:29:04 +02:00
Bryan Gerlach
1978be9edf build web github action 2025-04-15 17:41:58 -05:00
Bryan Gerlach
4edff33ebd build web github action 2025-04-15 17:14:38 -05:00
Bryan Gerlach
49fd150fe5 build web github action 2025-04-15 17:02:06 -05:00
Bryan Gerlach
c8c1fac268 build web github action 2025-04-15 16:59:28 -05:00
Bryan Gerlach
a91d66007d build web github action 2025-04-15 16:58:08 -05:00
Bryan Gerlach
ca1aa3fcc5 max memory size 2025-04-14 21:42:26 -05:00
Bryan Gerlach
409ed5cfa3 debug false 2025-04-14 21:38:36 -05:00
Bryan Gerlach
2ca25d3a39 hidecm 2025-04-14 11:05:13 -05:00
Bryan Gerlach
0e975e73db docker 2025-04-14 08:09:13 -05:00
Bryan Gerlach
62ac82ec2e debug 2025-04-14 08:05:22 -05:00
Bryan Gerlach
fc239ef7b5 hidecm, layout changes 2025-04-14 06:49:25 -05:00
Bryan Gerlach
71eb691a52 hidecm, layout changes 2025-04-14 06:45:11 -05:00
Bryan Gerlach
256d647f04 fix direct-ip setting 2025-04-13 13:32:44 -05:00
Bryan Gerlach
7644222adf fix windows registry for appname with spaces? 2025-04-09 23:52:01 -05:00
Bryan Gerlach
fdfebbb01a fix windows registry for appname with spaces? 2025-04-09 22:01:01 -05:00
Bryan Gerlach
cd1915a4bf remove the calls to api.rustdesk.com when checking for updates 2025-04-08 16:27:45 -05:00
Bryan Gerlach
785d03e46f fix 2025-04-04 21:18:22 -05:00
Bryan Gerlach
996b3c66b0 maintenance 2025-04-02 07:06:18 -05:00
Bryan Gerlach
08af79b930 maintenance 2025-04-02 07:04:04 -05:00
Bryan Gerlach
f6be77c68a fix? 2025-04-01 21:57:33 -05:00
Bryan Gerlach
a9718c4f5d fix? 2025-04-01 21:37:37 -05:00
Bryan Gerlach
119b272564 fix? 2025-04-01 15:51:34 -05:00
Bryan Gerlach
50236e59ff fix? 2025-04-01 15:37:23 -05:00
Bryan Gerlach
877ec790d6 github depricating ubuntu 20.04? change to 24.04 2025-04-01 15:16:51 -05:00
Bryan Gerlach
972451709c github depricating ubuntu 20.04? change to 24.04 2025-04-01 15:11:40 -05:00
Bryan Gerlach
e2c20567d6 fix macos 2025-04-01 14:08:07 -05:00
Bryan Gerlach
6718464f57 1.3.9 2025-04-01 13:34:09 -05:00
Bryan Gerlach
5148b792e6 hopefully fix 500 error on download links 2025-03-30 19:34:25 -05:00
Bryan
e79572d55d Merge pull request #48 from sonnyyin/master
modify for arm64 compile and add download links for linux output file
2025-03-28 12:32:41 -05:00
zhengyin
9c1ab4de3c fix linux file name 2025-03-28 19:28:33 +08:00
zhengyin
d2b36d3876 fix a bug 2025-03-28 19:04:26 +08:00
zhengyin
16d0818a28 delete test on xbxiot 2025-03-28 18:24:51 +08:00
zhengyin
634a77a054 test on xbxiot branch 2025-03-28 18:21:01 +08:00
zhengyin
3ff33a6efb add aarch64 linux compile and download link for it 2025-03-28 18:05:50 +08:00
Bryan Gerlach
897fe1a487 fix linux rustdesk dir 2025-03-26 14:31:18 -05:00
Bryan Gerlach
8b04b051fd remove new version notification fix 2025-03-26 14:11:18 -05:00
Bryan Gerlach
c59a838f7b revert 2025-03-25 15:59:24 -05:00
Bryan Gerlach
ebaedbcf55 status on clients page 2025-03-25 15:58:02 -05:00
Bryan Gerlach
21a5dc1bf6 fix android server/key/api settings 2025-03-01 16:36:46 -06:00
Bryan Gerlach
e8af50a5fc fix linux/android builds? 2025-02-27 22:28:55 -06:00
Bryan Gerlach
124af41206 fix win build 2025-02-27 22:15:11 -06:00
Bryan Gerlach
2e216e9d26 test 2025-02-27 22:09:21 -06:00
Bryan Gerlach
8cd0e78ec7 test 2025-02-27 19:44:11 -06:00
Bryan Gerlach
581dcd62c3 test 2025-02-27 19:15:08 -06:00
Bryan Gerlach
60ec06b3ea changes 2025-02-27 14:05:54 -06:00
Bryan Gerlach
8253cce1bf changes 2025-02-27 13:19:57 -06:00
Bryan Gerlach
6167b8f61d update bridge 2025-02-27 11:37:46 -06:00
Bryan Gerlach
9fd8f52815 update bridge 2025-02-27 11:34:44 -06:00
Bryan
50f15f18b4 Merge pull request #43 from VenimK/patch-13
Update generator.html
2025-02-27 11:22:08 -06:00
Bryan
c4cc4a167e Merge pull request #42 from VenimK/patch-12
Update forms.py
2025-02-27 11:21:32 -06:00
VenimK
1d51a43a47 Update generator.html
More info for the user about master/nightly
2025-02-27 06:32:03 +01:00
VenimK
905d8039ef Update forms.py
Some info for the user regarding master/nightly
2025-02-27 06:25:29 +01:00
Bryan Gerlach
8b8aff445a try fixing imagamagick issues 2025-02-26 16:10:57 -06:00
Bryan Gerlach
1b56fa5833 try fixing imagamagick issues 2025-02-26 16:10:48 -06:00
Bryan Gerlach
ac114152de try fixing imagamagick issues 2025-02-25 09:58:55 -06:00
Bryan Gerlach
ca0d321873 v1.3.8 2025-02-24 12:22:03 -06:00
Bryan Gerlach
80fc9f6a84 v1.3.8 2025-02-24 12:17:52 -06:00
Bryan Gerlach
b567c74902 v1.3.8 2025-02-24 12:16:04 -06:00
Bryan Gerlach
cc47350fab fix setup_server_tip 2025-02-19 19:54:50 -06:00
Bryan Gerlach
e882a604d7 fix setup_server_tip 2025-02-19 18:05:33 -06:00
Bryan Gerlach
1b7468a1f2 remove unsigned and signed zip files 2025-02-18 07:42:22 -06:00
Bryan Gerlach
1424caffb0 fix api again 2025-02-18 06:29:06 -06:00
Bryan Gerlach
47cf713307 only allow ascii characters on appname 2025-02-17 09:10:00 -06:00
Bryan Gerlach
fe505ed35c fix master build 2025-02-16 23:41:18 -06:00
Bryan Gerlach
a68e9d8a83 fix missing api server settings due to rustdesk code changes 2025-02-16 15:33:30 -06:00
Bryan Gerlach
995f78b6a1 force icon dimensions to be square 2025-02-14 07:13:57 -06:00
Bryan Gerlach
7951080c6c force filename to have no spaces, and be all ascii characters 2025-02-14 00:13:53 -06:00
Bryan Gerlach
6792760e57 added company name 2025-02-13 08:05:07 -06:00
Bryan Gerlach
da33c6f5fd added company name 2025-02-13 08:03:23 -06:00
Bryan Gerlach
6c2e2f39e9 change copyright name to appname, fix bug in import configuration settings 2025-02-12 13:32:12 -06:00
Bryan Gerlach
f420425ecb fix archlinux 2025-02-11 13:54:27 -06:00
Bryan Gerlach
4a5dce088a fix archlinux 2025-02-11 12:47:55 -06:00
Bryan Gerlach
51cb240b13 fix archlinux 2025-02-11 11:48:25 -06:00
Bryan Gerlach
a9bf03b34b fix archlinux 2025-02-11 10:47:39 -06:00
Bryan Gerlach
286320cd81 add archlinux, change RustDesk in all language files 2025-02-10 23:12:29 -06:00
Bryan Gerlach
0f1b1d1d55 refactored 2025-02-10 17:24:36 -06:00
Bryan Gerlach
ee673c2667 Merge branch 'master' of https://github.com/bryangerlach/rdgen into dev 2025-02-08 09:41:29 -06:00
Bryan Gerlach
2c970680ef remove upload artifact 2025-02-08 09:38:57 -06:00
Bryan
c973d5363f Merge pull request #29 from bryangerlach/dev
Dev
2025-02-06 07:05:51 -06:00
Bryan Gerlach
433b02dbfd fix typo 2025-02-06 07:02:01 -06:00
Bryan Gerlach
3699490b88 Merge branch 'master' of https://github.com/bryangerlach/rdgen into dev 2025-02-06 07:00:59 -06:00
Bryan
743781d662 Merge pull request #26 from VenimK/patch-9
Update generator-macos.yml
2025-02-06 06:59:03 -06:00
Bryan
7e81a75b0d Merge pull request #27 from VenimK/patch-10
Update generator-windows.yml
2025-02-06 06:58:50 -06:00
Bryan
dc965aa566 Merge pull request #28 from VenimK/patch-11
Update pre137-generator-windows.yml
2025-02-06 06:58:32 -06:00
VenimK
3c2959fd95 Update pre137-generator-windows.yml 2025-02-06 05:31:24 +01:00
VenimK
2020c85cce Update generator-windows.yml
Check if signed .. no error on build
2025-02-06 05:29:02 +01:00
VenimK
f1ab7ea43c Update generator-macos.yml
ADDED For SAVE AND LOAD CONF

Needs little work on cleaning it up
2025-02-05 16:52:50 +01:00
Bryan Gerlach
423100c9ea Merge branch 'master' of https://github.com/bryangerlach/rdgen into dev 2025-02-05 00:40:49 -06:00
Bryan
8e11af77b8 Merge pull request #24 from VenimK/patch-8
Rename remove_new_version_notification.patch to removeNewVersionNotif…
2025-02-05 00:39:36 -06:00
Bryan
8ce7e6ac50 Add save/load configurations (#25) 2025-02-05 00:39:04 -06:00
Bryan Gerlach
87f587699c add save/load configuration 2025-02-05 00:32:59 -06:00
Bryan Gerlach
f294a1feda add save/load configuration 2025-02-04 23:59:33 -06:00
Bryan Gerlach
7c370dbcdf add save/load configuration 2025-02-04 23:44:34 -06:00
Bryan Gerlach
646ad8d4a1 add save/load configuration 2025-02-04 23:18:14 -06:00
VenimK
d78c1f7961 Rename remove_new_version_notification.patch to removeNewVersionNotif.diff 2025-02-05 05:21:18 +01:00
45 changed files with 3843 additions and 5977 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.")

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

29
.github/patches/allowCustom.diff vendored Normal file
View File

@@ -0,0 +1,29 @@
diff --git a/src/common.rs b/src/common.rs
index 56361a5da..92b221e9a 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -1474,15 +1474,15 @@ pub fn read_custom_client(config: &str) {
log::error!("Failed to decode custom client config");
return;
};
- const KEY: &str = "5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=";
- let Some(pk) = get_rs_pk(KEY) else {
- log::error!("Failed to parse public key of custom client");
- return;
- };
- let Ok(data) = sign::verify(&data, &pk) else {
- log::error!("Failed to dec custom client config");
- return;
- };
+ // const KEY: &str = "5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=";
+ // let Some(pk) = get_rs_pk(KEY) else {
+ // log::error!("Failed to parse public key of custom client");
+ // return;
+ // };
+ // let Ok(data) = sign::verify(&data, &pk) else {
+ // log::error!("Failed to dec custom client config");
+ // return;
+ // };
let Ok(mut data) =
serde_json::from_slice::<std::collections::HashMap<String, serde_json::Value>>(&data)
else {

63
.github/patches/allowCustom.py vendored Normal file
View File

@@ -0,0 +1,63 @@
import os
import shutil
def remove_line_block(filepath, start_phrase, lines_to_remove_after_start):
"""
Removes a starting line and a fixed number of lines immediately following it.
:param filepath: The path to the file to modify.
:param start_phrase: The unique string to identify the first line of the block.
:param lines_to_remove_after_start: The number of lines to remove after the starting line.
"""
# 1. Configuration for the removal logic
# The starting line is: const KEY: &str = "5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=";
# The block contains this line plus 8 following lines, so we want to skip 9 lines in total.
total_lines_to_skip = 1 + lines_to_remove_after_start # 1 (start line) + 8 (following lines) = 9
lines_to_keep = []
skip_count = 0
# 2. Read and filter the file content
try:
with open(filepath, 'r') as file:
for line in file:
# If we are currently in the process of skipping lines, decrement the counter and continue
if skip_count > 0:
skip_count -= 1
continue
# Check if the line matches the start phrase (we use .strip() to ignore indentation/whitespace)
if line.strip().startswith(start_phrase.strip()):
# Start skipping the block (including the current line)
skip_count = total_lines_to_skip - 1
# Note: We subtract 1 because the 'continue' will handle the first line removal immediately
continue
# If we are not skipping, keep the line, but change custom.txt to custom_.txt
line = line.replace("custom.txt", "custom_.txt")
lines_to_keep.append(line)
except FileNotFoundError:
print(f"Error: File not found at {filepath}")
return
# 3. Write the remaining lines back to the file
try:
with open(filepath, 'w') as file:
file.writelines(lines_to_keep)
print(f"Success! Removed the 9-line block starting with '{start_phrase.strip()}' from {filepath}.")
except IOError as e:
print(f"An error occurred while writing to the file: {e}")
def main():
file_path = 'src/common.rs'
start_phrase = 'const KEY: &str = "5Qbwsde3unUcJBtrx9ZkvUmwFNoExHzpryHuPUdqlWM=";'
lines_to_remove_after_start = 8
remove_line_block(file_path, start_phrase, lines_to_remove_after_start)
if __name__ == "__main__":
main()

View File

@@ -1,8 +1,8 @@
diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart
index 839ea1a81..9cee52263 100644
index bc3757f1e..ba6509802 100644
--- a/flutter/lib/desktop/widgets/remote_toolbar.dart
+++ b/flutter/lib/desktop/widgets/remote_toolbar.dart
@@ -437,6 +437,7 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
@@ -317,6 +317,7 @@ class _RemoteToolbarState extends State<RemoteToolbar> {
borderRadius: borderRadius,
child: _DraggableShowHide(
id: widget.id,
@@ -10,7 +10,7 @@ index 839ea1a81..9cee52263 100644
sessionId: widget.ffi.sessionId,
dragging: _dragging,
fractionX: _fractionX,
@@ -2234,6 +2235,7 @@ class RdoMenuButton<T> extends StatelessWidget {
@@ -2460,6 +2461,7 @@ class RdoMenuButton<T> extends StatelessWidget {
class _DraggableShowHide extends StatefulWidget {
final String id;
@@ -18,7 +18,7 @@ index 839ea1a81..9cee52263 100644
final SessionID sessionId;
final RxDouble fractionX;
final RxBool dragging;
@@ -2246,6 +2248,7 @@ class _DraggableShowHide extends StatefulWidget {
@@ -2472,6 +2474,7 @@ class _DraggableShowHide extends StatefulWidget {
const _DraggableShowHide({
Key? key,
required this.id,
@@ -26,7 +26,7 @@ index 839ea1a81..9cee52263 100644
required this.sessionId,
required this.fractionX,
required this.dragging,
@@ -2357,6 +2360,7 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
@@ -2583,6 +2586,7 @@ class _DraggableShowHideState extends State<_DraggableShowHide> {
mainAxisSize: MainAxisSize.min,
children: [
_buildDraggable(context),
@@ -34,9 +34,9 @@ index 839ea1a81..9cee52263 100644
Obx(() => buttonWrapper(
() {
widget.setFullscreen(!isFullscreen.value);
@@ -2463,3 +2467,50 @@ Widget _buildPointerTrackWidget(Widget child, FFI? ffi) {
),
);
@@ -2742,3 +2746,50 @@ class EdgeThicknessControl extends StatelessWidget {
return slider;
}
}
+
+class _CycleMonitorMenu extends StatelessWidget {

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,41 @@
diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart
index ba724eed5..1604c429f 100644
--- a/flutter/lib/desktop/pages/desktop_home_page.dart
+++ b/flutter/lib/desktop/pages/desktop_home_page.dart
@@ -424,21 +424,21 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
Widget buildHelpCards(String updateUrl) {
- if (!bind.isCustomClient() &&
- updateUrl.isNotEmpty &&
- !isCardClosed &&
- bind.mainUriPrefixSync().contains('rustdesk')) {
- return buildInstallCard(
- "Status",
- "${translate("new-version-of-{${bind.mainGetAppNameSync()}}-tip")} (${bind.mainGetNewVersion()}).",
- "Click to download", () async {
- final Uri url = Uri.parse('https://rustdesk.com/download');
- await launchUrl(url);
- }, closeButton: true);
- }
- if (systemError.isNotEmpty) {
- return buildInstallCard("", systemError, "", () {});
- }
+ // if (!bind.isCustomClient() &&
+ // updateUrl.isNotEmpty &&
+ // !isCardClosed &&
+ // bind.mainUriPrefixSync().contains('rustdesk')) {
+ // return buildInstallCard(
+ // "Status",
+ // "${translate("new-version-of-{${bind.mainGetAppNameSync()}}-tip")} (${bind.mainGetNewVersion()}).",
+ // "Click to download", () async {
+ // final Uri url = Uri.parse('https://rustdesk.com/download');
+ // await launchUrl(url);
+ // }, closeButton: true);
+ // }
+ // if (systemError.isNotEmpty) {
+ // return buildInstallCard("", systemError, "", () {});
+ // }
if (isWindows && !bind.isDisableInstallation()) {
if (!bind.mainIsInstalled()) {

View File

@@ -0,0 +1,13 @@
diff --git a/flutter/lib/desktop/pages/connection_page.dart b/flutter/lib/desktop/pages/connection_page.dart
index d9dc3eec4..76f386b76 100644
--- a/flutter/lib/desktop/pages/connection_page.dart
+++ b/flutter/lib/desktop/pages/connection_page.dart
@@ -131,7 +131,7 @@ class _OnlineStatusWidgetState extends State<OnlineStatusWidget> {
if (!isIncomingOnly) startServiceWidget(),
// ready && public
// No need to show the guide if is custom client.
- if (!isIncomingOnly) setupServerWidget(),
+ //if (!isIncomingOnly) setupServerWidget(),
],
);

View File

@@ -1,22 +0,0 @@
diff --git a/flutter/lib/desktop/pages/desktop_home_page.dart b/flutter/lib/desktop/pages/desktop_home_page.dart
index ba724eed5..cb66cdaed 100644
--- a/flutter/lib/desktop/pages/desktop_home_page.dart
+++ b/flutter/lib/desktop/pages/desktop_home_page.dart
@@ -424,7 +424,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
}
Widget buildHelpCards(String updateUrl) {
- if (!bind.isCustomClient() &&
+ /*if (!bind.isCustomClient() &&
updateUrl.isNotEmpty &&
!isCardClosed &&
bind.mainUriPrefixSync().contains('rustdesk')) {
@@ -435,7 +435,7 @@ class _DesktopHomePageState extends State<DesktopHomePage>
final Uri url = Uri.parse('https://rustdesk.com/download');
await launchUrl(url);
}, closeButton: true);
- }
+ }*/
if (systemError.isNotEmpty) {
return buildInstallCard("", systemError, "", () {});
}

View File

@@ -1,33 +0,0 @@
diff --git a/flutter/lib/common/widgets/peers_view.dart b/flutter/lib/common/widgets/peers_view.dart
index 3e34f882d..68d82116b 100644
--- a/flutter/lib/common/widgets/peers_view.dart
+++ b/flutter/lib/common/widgets/peers_view.dart
@@ -25,13 +25,13 @@ class PeerSortType {
static const String remoteId = 'Remote ID';
static const String remoteHost = 'Remote Host';
static const String username = 'Username';
- // static const String status = 'Status';
+ static const String status = 'Status';
static List<String> values = [
PeerSortType.remoteId,
PeerSortType.remoteHost,
PeerSortType.username,
- // PeerSortType.status
+ PeerSortType.status
];
}
@@ -384,9 +384,9 @@ class _PeersViewState extends State<_PeersView>
peers.sort((p1, p2) =>
p1.username.toLowerCase().compareTo(p2.username.toLowerCase()));
break;
- // case PeerSortType.status:
- // peers.sort((p1, p2) => p1.online ? -1 : 1);
- // break;
+ case PeerSortType.status:
+ peers.sort((p1, p2) => p1.online ? -1 : 1);
+ break;
}
}

View File

@@ -12,6 +12,7 @@ on:
type: string
env:
CARGO_EXPAND_VERSION: "1.0.95"
FLUTTER_VERSION: "3.22.3"
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503
@@ -25,7 +26,7 @@ jobs:
job:
- {
target: x86_64-unknown-linux-gnu,
os: ubuntu-20.04,
os: ubuntu-24.04,
extra-build-args: "",
}
steps:
@@ -55,9 +56,9 @@ jobs:
gcc \
git \
g++ \
libclang-10-dev \
libclang-dev \
libgtk-3-dev \
llvm-10-dev \
llvm-dev \
nasm \
ninja-build \
pkg-config \
@@ -91,6 +92,7 @@ jobs:
- name: Install flutter rust bridge deps
shell: bash
run: |
cargo install cargo-expand --version ${{ env.CARGO_EXPAND_VERSION }} --locked
cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked
pushd flutter && sed -i -e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' pubspec.yaml && flutter pub get && popd

69
.github/workflows/build-web.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Build web
on:
workflow_dispatch:
env:
FLUTTER_VERSION: "3.24.5"
TAG_NAME: "nightly"
VERSION: "1.3.9"
jobs:
build-rustdesk-web:
name: build-rustdesk-web
runs-on: ubuntu-24.04
strategy:
fail-fast: false
env:
RELEASE_NAME: web-basic
steps:
- name: Checkout source code
uses: actions/checkout@v4
with:
submodules: recursive
- name: Prepare env
run: |
sudo apt-get update -y
sudo apt-get install -y wget npm
- name: Install flutter
uses: subosito/flutter-action@v2.12.0 #https://github.com/subosito/flutter-action/issues/277
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
# https://rustdesk.com/docs/en/dev/build/web/
- name: Build web
shell: bash
run: |
pushd flutter/web/v2
npm install yarn -g
npm install typescript -g
npm install protoc -g
# Install protoc first, see: https://google.github.io/proto-lens/installing-protoc.html
npm install ts-proto
# Only works with vite <= 2.8, see: https://github.com/vitejs/vite/blob/main/docs/guide/build.md#chunking-strategy
npm install vite@2.8
yarn install && yarn build
popd
pushd flutter/web/v2
wget https://github.com/rustdesk/doc.rustdesk.com/releases/download/console/web_deps.tar.gz
tar xzf web_deps.tar.gz
popd
pushd flutter
flutter build web --release
cd build
#cp ../web/README.md web
# TODO: Remove the following line when the web is almost complete.
#echo -e "\n\nThis build is for preview and not full functionality." >> web/README.md
dir_name="rustdesk-${{ env.VERSION }}-${{ env.RELEASE_NAME }}"
mv web "${dir_name}" && tar czf "${dir_name}".tar.gz "${dir_name}"
sha256sum "${dir_name}".tar.gz
popd
- name: Upload APK artifact
uses: actions/upload-artifact@v4
with:
name: web
path: flutter/build/rustdesk-${{ env.VERSION }}-${{ env.RELEASE_NAME }}.tar.gz

View File

@@ -20,6 +20,9 @@ jobs:
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
echo "IMAGE_TAG=${{ github.ref_name }}" >> "$GITHUB_ENV"
echo "CREATE_IMAGE=true" >> "$GITHUB_ENV"
elif [[ "${{ github.ref }}" == "refs/heads/master" ]]; then
echo "IMAGE_TAG=latest" >> "$GITHUB_ENV"
echo "CREATE_IMAGE=true" >> "$GITHUB_ENV"
else
echo "CREATE_IMAGE=false" >> "$GITHUB_ENV"
fi

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -3,56 +3,16 @@ run-name: Custom macOS Client Generator
on:
workflow_dispatch:
inputs:
server:
description: 'Rendezvous Server'
version:
description: 'version to buld'
required: true
default: ''
type: string
key:
description: 'Public Key'
zip_url:
description: 'url to zip of json'
required: true
default: ''
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:
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
@@ -69,53 +29,76 @@ env:
TAG_NAME: "${{ inputs.upload-tag }}"
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "b2cb0da531c2f1f740045bfe7c4dac59f0b2b69c"
VERSION: "${{ fromJson(inputs.extras).version }}"
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 }}"
# To make a custom build with your own servers set the below secret values
RS_PUB_KEY: "${{ inputs.key }}"
RENDEZVOUS_SERVER: "${{ inputs.server }}"
CUSTOM: "${{ inputs.custom }}"
UUIDFOLDER: "${{ inputs.uuid }}"
API_SERVER: "${{ inputs.apiServer }}"
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: ${{ fromJson(inputs.extras).version }}
version: ${{ inputs.version }}
build-for-macos-flutter:
name: Build macOS
runs-on: macos-latest
needs: [generate-bridge]
build-for-macos:
name: ${{ matrix.job.target }}
runs-on: ${{ matrix.job.os }}
needs: [generate-bridge, setup]
strategy:
fail-fast: false
matrix:
job:
# - {
# 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
# extra-build-args: "",
# arch: x86_64,
# vcpkg-triplet: x64-osx,
# }
- {
target: x86_64-apple-darwin,
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: "",
arch: x86_64,
vcpkg-triplet: x64-osx,
}
- {
target: aarch64-apple-darwin,
os: macos-latest,
os: macos-14,
# extra-build-args: "--disable-flutter-texture-render", # disable this for mac, because we see a lot of users reporting flickering both on arm and x64, and we can not confirm if texture rendering has better performance if htere is no vram, https://github.com/rustdesk/rustdesk/issues/6296
extra-build-args: "--screencapturekit",
arch: aarch64,
vcpkg-triplet: arm64-osx,
}
env:
STATUS_URL: "${{ secrets.GENURL }}/updategh"
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:
@@ -123,24 +106,6 @@ jobs:
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: ${{ fromJson(inputs.extras).rdgen == 'true' }}
run: |
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV
- name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'false' }}
run: |
echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $env: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": "5% complete"}'
- name: Checkout source code
if: ${{ env.VERSION != 'master' }}
uses: actions/checkout@v4
@@ -156,12 +121,6 @@ jobs:
repository: rustdesk/rustdesk
submodules: recursive
- name: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Install imagemagick and potrace and nasm and and
shell: bash
run: |
@@ -172,88 +131,130 @@ jobs:
continue-on-error: false
shell: bash
run: |
# MACSTUFF Backup the original Info.plist
cp ./flutter/macos/Runner/Info.plist ./flutter/macos/Runner/Info.plist.bak
# MACSTUFF Update application name and display name
sed -i '' -e 's|<key>CFBundleName</key>\s*<string>$(PRODUCT_NAME)</string>|<key>CFBundleName</key>\n\t<string>${{ inputs.appname }}</string>|' ./flutter/macos/Runner/Info.plist
sed -i '' -e 's|<key>CFBundleDisplayName</key>\s*<string>$(PRODUCT_NAME)</string>|<key>CFBundleDisplayName</key>\n\t<string>${{ inputs.appname }}</string>|' ./flutter/macos/Runner/Info.plist
sed -i '' -e 's|<key>CFBundleIdentifier</key>\s*<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>|<key>CFBundleIdentifier</key>\n\t<string>com.${{ inputs.appname }}.app</string>|' ./flutter/macos/Runner/Info.plist
# MACSTUFF Update copyright information if needed
sed -i '' -e 's|<key>NSHumanReadableCopyright</key>\s*<string>$(PRODUCT_COPYRIGHT)</string>|<key>NSHumanReadableCopyright</key>\n\t<string>${{ inputs.appname }}</string>|' ./flutter/macos/Runner/Info.plist
# MACSTUFF Update window title and bundle settings
# MACSTUFF Backup the original files
cp ./flutter/macos/Runner/Info.plist ./flutter/macos/Runner/Info.plist.bak
cp ./flutter/macos/Runner/Configs/AppInfo.xcconfig ./flutter/macos/Runner/Configs/AppInfo.xcconfig.bak
sed -i '' -e 's|PRODUCT_NAME = .*|PRODUCT_NAME = ${{ inputs.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_COPYRIGHT = .*|PRODUCT_COPYRIGHT = ${{ inputs.appname }} All rights reserved.|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig
# MACSTUFF Update 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>${{ env.appname }}</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 ${{ env.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/Info.plist
# MACSTUFF Update 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.${{ env.appname }}.app|' ./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
sed -i -e 's|Purslane Ltd.|${{ env.appname }}|' ./Cargo.toml
sed -i -e 's|Purslane Ltd|${{ env.appname }}|' ./libs/portable/Cargo.toml
# 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_BUNDLE_IDENTIFIER = ".*"/PRODUCT_BUNDLE_IDENTIFIER = "com.${{ inputs.appname }}.app"/' ./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.${{ env.appname }}.app"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj
# Don't modify DEVELOPMENT_TEAM in project.pbxproj
# Update CMake settings
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
# 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
cp ./src/lang/en.rs ./src/lang/en.rs.bak
cp ./src/lang/nl.rs ./src/lang/nl.rs.bak
sed -i '' -e 's|RustDesk|${{ inputs.appname }}|' ./src/lang/en.rs
sed -i '' -e 's|RustDesk|${{ inputs.appname }}|' ./src/lang/nl.rs
find ./src/lang -name "*.rs" -exec sed -i '' -e 's|RustDesk|${{ env.appname }}|' {} \;
sed -i '' -e 's|RustDesk|${{ env.appname }}|' ./src/lang/nl.rs
sed -i '' -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).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|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/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).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|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).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 '/const KEY:/,/};/d' ./src/common.rs
sed -i '' -e '/let Ok(data) = sign::verify(&data, &pk)/,/};/d' ./src/common.rs
sed -i '' -e 's|https://rustdesk.com|${{ env.urlLink }}|' ./build.py
sed -i '' -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart
sed -i '' -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i '' -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart
sed -i '' -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart
sed -i '' -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart
sed -i '' -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart
- name: change download link to custom
if: env.downloadLink != 'https://rustdesk.com/download'
continue-on-error: true
shell: bash
run: |
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart
sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis
# Update slogan
#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
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 ${{ env.appname }}")/' ./src/lang/en.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 ${{ env.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 ${{ env.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|${{ 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
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.py
python allowCustom.py
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff
git apply removeSetupServerTip.diff
# Update pubspec.yaml with proper YAML formatting
cp ./flutter/pubspec.yaml ./flutter/pubspec.yaml.bak
echo " archive: ^3.6.1" > ./flutter/temp_dependency.txt
awk '/intl:/{print;system("cat ./flutter/temp_dependency.txt");next}1' ./flutter/pubspec.yaml > ./flutter/pubspec.yaml.tmp
mv ./flutter/pubspec.yaml.tmp ./flutter/pubspec.yaml
rm ./flutter/temp_dependency.txt
rm ./flutter/temp_dependency.txt
- name: Install build runtime
run: |
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
if command -v pkg-config &>/dev/null; then
echo "pkg-config is already installed"
else
brew install pkg-config
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": "10% complete"}'
- 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
uses: subosito/flutter-action@v2.12.0
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Patch flutter
continue-on-error: true
run: |
cp .github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff $(dirname $(dirname $(which flutter)))
cd $(dirname $(dirname $(which flutter)))
[[ "3.24.4" == 3.24.5 ]] && git apply flutter_3.24.4_dropdown_menu_enableFilter.diff
[[ "3.24.5" == ${{env.FLUTTER_VERSION}} ]] && git apply ${{ github.workspace }}/.github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff
- name: Workaround for flutter issue
shell: bash
run: |
cd "$(dirname "$(which flutter)")"
# https://github.com/flutter/flutter/issues/1.3.43
# https://github.com/flutter/flutter/issues/133533
sed -i -e 's/_setFramesEnabledState(false);/\/\/_setFramesEnabledState(false);/g' ../packages/flutter/lib/src/scheduler/binding.dart
grep -n '_setFramesEnabledState(false);' ../packages/flutter/lib/src/scheduler/binding.dart
@@ -264,13 +265,12 @@ jobs:
targets: ${{ matrix.job.target }}
components: "rustfmt"
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- name: Magick stuff for macOS
if: ${{ inputs.iconlink != 'false' }}
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: false
shell: bash
run: |
@@ -284,8 +284,8 @@ jobs:
# Download icon using curl with additional SSL options
curl -k -L --tlsv1.2 --proto =https --ssl-reqd \
-H "User-Agent: Mozilla/5.0" \
"${{ 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 "${{ 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 "${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}&uuid=${{ env.iconlink_uuid }}"
# Backup existing files (if they exist)
[ -f "./res/32x32.png" ] && mv ./res/32x32.png ./res/32x32.png.bak
@@ -380,7 +380,7 @@ jobs:
ls -lh rustdesk/data/flutter_assets/assets/
- name: replace flutter icons
if: ${{ inputs.iconlink != 'false' }}
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: false
shell: bash
run: |
@@ -388,8 +388,8 @@ jobs:
# Create required directories and files
mkdir -p web
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 '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>${{ inputs.appname }}</title></head><body></body></html>' > web/index.html
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>${{ env.appname }}</title></head><body></body></html>' > web/index.html
# Ensure the AppIcon.appiconset directory exists
mkdir -p macos/Runner/Assets.xcassets/AppIcon.appiconset
@@ -404,7 +404,7 @@ jobs:
cd ..
- name: ui.rs
if: ${{ inputs.iconlink != 'false' }}
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: true
shell: bash
run: |
@@ -417,73 +417,51 @@ jobs:
- name: fix connection delay
continue-on-error: false
if: ${{ fromJson(inputs.extras).delayFix == 'true' }}
if: ${{ env.delayFix == 'true' }}
shell: bash
run: |
sed -i '' -e '/if !key.is_empty() && !token.is_empty() {/,/}/d' ./src/client.rs
sed -i -e 's|!key.is_empty()|false|' ./src/client.rs
- name: add cycle monitors to toolbar
continue-on-error: true
if: fromJson(inputs.extras).cycleMonitor == 'true'
if: env.cycleMonitor == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff
git apply cycle_monitor.diff
- name: use X for offline display instead of orange circle
continue-on-error: true
if: fromJson(inputs.extras).xOffline == 'true'
if: env.xOffline == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff
git apply xoffline.diff
- name: hide-cm
continue-on-error: true
if: fromJson(inputs.extras).hidecm == 'true'
if: env.hidecm == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/hidecm.diff
git apply hidecm.diff
- name: statussort
continue-on-error: true
if: fromJson(inputs.extras).statussort == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/statussort.diff
git apply statussort.diff
- name: removeNewVersionNotif
continue-on-error: true
if: fromJson(inputs.extras).removeNewVersionNotif == 'true'
if: env.removeNewVersionNotif == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeNewVersionNotif.diff
git apply removeNewVersionNotif.diff
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: Report Status
uses: fjogeleit/http-request-action@v1
- name: Restore bridge files
uses: actions/download-artifact@master
with:
url: ${{ env.STATUS_URL }}
method: 'POST'
customHeaders: '{"Content-Type": "application/json"}'
data: '{"uuid": "${{ inputs.uuid }}", "status": "15% complete"}'
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- 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: bridge-artifact
path: ./
- name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
run: |
if ! $VCPKG_ROOT/vcpkg \
@@ -498,14 +476,7 @@ jobs:
done
exit 1
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": "25% complete"}'
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
- name: Create MacOS directory structure
run: |
@@ -520,20 +491,12 @@ 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/MACOSX_DEPLOYMENT_TARGET = [0-9]*.[0-9]*;/MACOSX_DEPLOYMENT_TARGET = ${MIN_MACOS_VERSION};/" flutter/macos/Runner.xcodeproj/project.pbxproj
fi
sed -i -e "s/RustDesk.app/\"${{ inputs.appname }}.app\"/" build.py
./build.py --flutter --hwcodec ${{ matrix.job.extra-build-args }}
sed -i -e "s/RustDesk.app/\"${{ env.appname }}.app\"/" build.py
./build.py --flutter --hwcodec --unix-file-copy-paste ${{ matrix.job.extra-build-args }}
# - name: Copy service file
# run: |
# 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
if: env.MACOS_P12_BASE64 != null
@@ -554,14 +517,6 @@ jobs:
else
brew install pkg-config
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)
shell: bash
@@ -574,7 +529,7 @@ jobs:
rustc -V
- name: icon svg handling
if: ${{ inputs.iconlink != 'false' }}
if: ${{ env.iconlink_url != 'false' }}
continue-on-error: false
shell: bash
run: |
@@ -590,7 +545,7 @@ jobs:
rm ./temp_icon.pbm
- name: logo handling
if: ${{ inputs.logolink != 'false' }}
if: ${{ env.logolink_url != 'false' }}
continue-on-error: false
shell: bash
run: |
@@ -598,19 +553,11 @@ jobs:
mkdir -p "$ASSETS_DIR"
curl -k -L --tlsv1.2 --proto =https --ssl-reqd \
-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" || \
wget --no-check-certificate \
-O "$ASSETS_DIR/logo.png" \
"${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}&uuid=${{ fromJson(inputs.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"}'
"${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}&uuid=${{ env.logolink_uuid }}"
- name: Sign macOS app bundle
if: env.MACOS_P12_BASE64 != ''
@@ -623,21 +570,21 @@ jobs:
# Rename RustDesk.app to the custom app name first
if [ -d "RustDesk.app" ]; then
# First rename the app if it's still called RustDesk.app
mv "RustDesk.app" "${{ inputs.appname }}.app"
echo "Renamed RustDesk.app to ${{ inputs.appname }}.app"
mv "RustDesk.app" "${{ env.appname }}.app"
echo "Renamed RustDesk.app to ${{ env.appname }}.app"
fi
echo "App bundle contents after rename:"
ls -la "${{ inputs.appname }}.app" || echo "App not found"
ls -la "${{ inputs.appname }}.app/Contents" || echo "Contents not found"
ls -la "${{ env.appname }}.app" || echo "App not found"
ls -la "${{ env.appname }}.app/Contents" || echo "Contents not found"
# Decode the certificate
echo "${{ secrets.MACOS_P12_BASE64 }}" | base64 --decode > certificate.p12
# 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..."
MAIN_EXECUTABLE="${{ inputs.appname }}.app/Contents/MacOS/${{ inputs.appname }}"
MAIN_EXECUTABLE="${{ env.appname }}.app/Contents/MacOS/${{ env.appname }}"
if [ -f "$MAIN_EXECUTABLE" ]; then
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime "$MAIN_EXECUTABLE"
@@ -645,23 +592,23 @@ jobs:
echo "Main executable not found at expected path: $MAIN_EXECUTABLE"
# Try to find the actual executable
echo "Available executables in MacOS directory:"
ls -la "${{ inputs.appname }}.app/Contents/MacOS/"
ACTUAL_EXECUTABLE=$(ls "${{ inputs.appname }}.app/Contents/MacOS/" | head -n 1)
ls -la "${{ env.appname }}.app/Contents/MacOS/"
ACTUAL_EXECUTABLE=$(ls "${{ env.appname }}.app/Contents/MacOS/" | head -n 1)
if [ -n "$ACTUAL_EXECUTABLE" ]; then
echo "Found executable: $ACTUAL_EXECUTABLE"
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
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 }}" \
--code-signature-flags runtime {} \;
echo "Signing main bundle..."
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
echo "Error: Invalid app bundle structure"
exit 1
@@ -679,33 +626,25 @@ jobs:
# Find the actual .app bundle
if [ -d "RustDesk.app" ]; then
# First rename the app if it's still called RustDesk.app
mv "RustDesk.app" "${{ inputs.appname }}.app"
mv "RustDesk.app" "${{ env.appname }}.app"
fi
if [ ! -d "${{ inputs.appname }}.app" ]; then
if [ ! -d "${{ env.appname }}.app" ]; then
echo "Could not find .app bundle!"
exit 1
fi
echo "Creating DMG for ${{ inputs.appname }}.app"
echo "Creating DMG for ${{ env.appname }}.app"
create-dmg \
--volname "${{ inputs.appname }}" \
--volname "${{ env.appname }}" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "${{ inputs.appname }}.app" 200 190 \
--hide-extension "${{ inputs.appname }}.app" \
--icon "${{ env.appname }}.app" 200 190 \
--hide-extension "${{ env.appname }}.app" \
--app-drop-link 600 185 \
"${{ inputs.appname }}-${{ matrix.job.arch }}.dmg" \
"${{ inputs.appname }}.app"
mv "${{ inputs.appname }}-${{ matrix.job.arch }}.dmg" $GITHUB_WORKSPACE/
#- name: Upload unsigned macOS app
# if: env.UPLOAD_ARTIFACT == 'true'
# uses: actions/upload-artifact@master
# with:
# name: ${{ inputs.appname }}-${{ matrix.job.arch }}
# path: ${{ inputs.appname }}-${{ matrix.job.arch }}.dmg
"${{ env.appname }}-${{ matrix.job.arch }}.dmg" \
"${{ env.appname }}.app"
mv "${{ env.appname }}-${{ matrix.job.arch }}.dmg" $GITHUB_WORKSPACE/
- name: Rename rustdesk
if: env.UPLOAD_ARTIFACT == 'true'
run: |
@@ -714,70 +653,46 @@ jobs:
ls -la
# 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
echo "Found DMG file: $DMG_FILE"
mv "$DMG_FILE" "${{ inputs.filename }}.dmg"
echo "Renamed to ${{ inputs.filename }}.dmg"
mv "$DMG_FILE" "${{ env.filename }}-${{ matrix.job.arch }}.dmg"
echo "Renamed to ${{ env.filename }}-${{ matrix.job.arch }}.dmg"
else
echo "No DMG file found matching the pattern"
exit 1
fi
- name: send file to rdgen server
if: ${{ fromJson(inputs.extras).rdgen == 'true' }}
if: ${{ env.rdgen == 'true' }}
shell: bash
run: |
curl -i -X POST \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" \
-F "file=@$GITHUB_WORKSPACE/${{ inputs.filename }}.dmg" \
-F "uuid=${{ inputs.uuid }}" \
-H "Authorization: Bearer ${{ env.token }}" \
-F "file=@$GITHUB_WORKSPACE/${{ env.filename }}-${{ matrix.job.arch }}.dmg" \
-F "uuid=${{ env.uuid }}" \
"${{ secrets.GENURL }}/save_custom_client"
- name: send file to api server
if: ${{ fromJson(inputs.extras).rdgen == 'false' }}
if: ${{ env.rdgen == 'false' }}
shell: bash
run: |
curl -i -X POST \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" \
-F "file=@$GITHUB_WORKSPACE/${{ inputs.filename }}.dmg" \
"${{ inputs.apiServer }}/api/save_custom_client"
-H "Authorization: Bearer ${{ env.token }}" \
-F "file=@$GITHUB_WORKSPACE/${{ env.filename }}-${{ matrix.job.arch }}.dmg" \
"${{ env.apiServer }}/api/save_custom_client"
- name: Report Status
uses: fjogeleit/http-request-action@v1
cleanup:
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:
url: ${{ env.STATUS_URL }}
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"}'
#- name: Publish DMG package
# if: env.UPLOAD_ARTIFACT == 'true'
# uses: softprops/action-gh-release@v1
# with:
# prerelease: true
# tag_name: ${{ inputs.upload-tag }}
# files: |
# ${{ inputs.appname }}*-${{ matrix.job.arch }}.dmg
name: encrypted-secrets-zip

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,110 +0,0 @@
# This yaml shares the build bridge steps with ci and nightly.
name: Build flutter-rust-bridge
# 2023-11-23 18:00:00+00:00
on:
workflow_call:
inputs:
version:
description: 'Rustdesk Version'
required: true
default: '1.3.1'
type: string
env:
FLUTTER_VERSION: "3.22.3"
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1"
RUST_VERSION: "1.75" # https://github.com/rustdesk/rustdesk/discussions/7503
jobs:
generate_bridge:
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- {
target: x86_64-unknown-linux-gnu,
os: ubuntu-20.04,
extra-build-args: "",
}
steps:
- name: Checkout source code
if: ${{ inputs.version != 'master' }}
uses: actions/checkout@v4
with:
repository: rustdesk/rustdesk
ref: refs/tags/${{ inputs.version }}
- name: Checkout source code
if: ${{ inputs.version == 'master' }}
uses: actions/checkout@v4
with:
repository: rustdesk/rustdesk
- name: Install prerequisites
run: |
sudo apt-get install ca-certificates -y
sudo apt-get update -y
sudo apt-get install -y \
clang \
cmake \
curl \
gcc \
git \
g++ \
libclang-10-dev \
libgtk-3-dev \
llvm-10-dev \
nasm \
ninja-build \
pkg-config \
wget
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: "rustfmt"
- uses: Swatinem/rust-cache@v2
with:
prefix-key: bridge-${{ matrix.job.os }}
- name: Cache Bridge
id: cache-bridge
uses: actions/cache@v3
with:
path: /tmp/flutter_rust_bridge
key: vcpkg-${{ matrix.job.arch }}
- name: Install flutter
uses: subosito/flutter-action@v2
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install flutter rust bridge deps
shell: bash
run: |
cargo install flutter_rust_bridge_codegen --version ${{ env.FLUTTER_RUST_BRIDGE_VERSION }} --features "uuid" --locked
pushd flutter && sed -i -e 's/extended_text: 14.0.0/extended_text: 13.0.0/g' pubspec.yaml && flutter pub get && popd
- name: Run flutter rust bridge
run: |
~/.cargo/bin/flutter_rust_bridge_codegen --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart --c-output ./flutter/macos/Runner/bridge_generated.h
cp ./flutter/macos/Runner/bridge_generated.h ./flutter/ios/Runner/bridge_generated.h
- name: Upload Artifact
uses: actions/upload-artifact@master
with:
name: bridge-artifact
path: |
./src/bridge_generated.rs
./src/bridge_generated.io.rs
./flutter/lib/generated_bridge.dart
./flutter/lib/generated_bridge.freezed.dart
./flutter/macos/Runner/bridge_generated.h
./flutter/ios/Runner/bridge_generated.h

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,798 +0,0 @@
name: Custom macOS Client Generator
run-name: Custom macOS Client Generator
on:
workflow_dispatch:
inputs:
server:
description: 'Rendezvous Server'
required: true
default: ''
type: string
key:
description: 'Public Key'
required: true
default: ''
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:
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
MAC_RUST_VERSION: "1.81"
CARGO_NDK_VERSION: "3.1.2"
SCITER_ARMV7_CMAKE_VERSION: "3.29.7"
SCITER_NASM_DEBVERSION: "2.14-1"
LLVM_VERSION: "15.0.6"
FLUTTER_VERSION: "3.24.5"
ANDROID_FLUTTER_VERSION: "3.24.5" # >= 3.16 is very slow on my android phone, but work well on most of others. We may switch to new flutter after changing to texture rendering (I believe it can solve my problem).
FLUTTER_RUST_BRIDGE_VERSION: "1.80.1" # for arm64 linux because official Dart SDK does not work
FLUTTER_ELINUX_VERSION: "3.16.9"
TAG_NAME: "${{ inputs.upload-tag }}"
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
# vcpkg version: 2024.07.12
VCPKG_COMMIT_ID: "b2cb0da531c2f1f740045bfe7c4dac59f0b2b69c"
VERSION: "${{ fromJson(inputs.extras).version }}"
NDK_VERSION: "r27c"
#signing keys env variable checks
ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}"
MACOS_P12_BASE64: "${{ secrets.MACOS_P12_BASE64 }}"
# To make a custom build with your own servers set the below secret values
RS_PUB_KEY: "${{ inputs.key }}"
RENDEZVOUS_SERVER: "${{ inputs.server }}"
CUSTOM: "${{ inputs.custom }}"
UUIDFOLDER: "${{ inputs.uuid }}"
API_SERVER: "${{ inputs.apiServer }}"
UPLOAD_ARTIFACT: 'true'
SIGN_BASE_URL: "${{ secrets.SIGN_BASE_URL }}"
STATUS_URL: "${{ secrets.GENURL }}/updategh"
jobs:
generate-bridge:
uses: ./.github/workflows/pre137-bridge.yml
with:
version: ${{ fromJson(inputs.extras).version }}
build-for-macos-flutter:
name: Build macOS
runs-on: macos-latest
needs: [generate-bridge]
strategy:
fail-fast: false
matrix:
job:
- {
target: aarch64-apple-darwin,
os: macos-latest,
# extra-build-args: "--disable-flutter-texture-render", # disable this for mac, because we see a lot of users reporting flickering both on arm and x64, and we can not confirm if texture rendering has better performance if htere is no vram, https://github.com/rustdesk/rustdesk/issues/6296
extra-build-args: "--screencapturekit",
arch: aarch64,
vcpkg-triplet: arm64-osx,
}
steps:
- 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: ${{ fromJson(inputs.extras).rdgen == 'true' }}
run: |
echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV
- name: Set rdgen value
if: ${{ fromJson(inputs.extras).rdgen == 'false' }}
run: |
echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $env: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": "5% complete"}'
- name: Checkout source code
if: ${{ env.VERSION != 'master' }}
uses: actions/checkout@v4
with:
repository: rustdesk/rustdesk
ref: refs/tags/${{ env.VERSION }}
- name: Checkout source code
if: ${{ env.VERSION == 'master' }}
uses: actions/checkout@v4
with:
repository: rustdesk/rustdesk
- name: Restore bridge files
uses: actions/download-artifact@master
with:
name: bridge-artifact
path: ./
- name: Install imagemagick and potrace and nasm and and
shell: bash
run: |
brew install imagemagick potrace nasm cmake gcc wget ninja
echo "$(brew --prefix imagemagick)/bin" >> $GITHUB_PATH
- name: Update macOS Info.plist and settings
continue-on-error: false
shell: bash
run: |
# MACSTUFF Backup the original Info.plist
cp ./flutter/macos/Runner/Info.plist ./flutter/macos/Runner/Info.plist.bak
# MACSTUFF Update application name and display name
sed -i '' -e 's|<key>CFBundleName</key>\s*<string>$(PRODUCT_NAME)</string>|<key>CFBundleName</key>\n\t<string>${{ inputs.appname }}</string>|' ./flutter/macos/Runner/Info.plist
sed -i '' -e 's|<key>CFBundleDisplayName</key>\s*<string>$(PRODUCT_NAME)</string>|<key>CFBundleDisplayName</key>\n\t<string>${{ inputs.appname }}</string>|' ./flutter/macos/Runner/Info.plist
sed -i '' -e 's|<key>CFBundleIdentifier</key>\s*<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>|<key>CFBundleIdentifier</key>\n\t<string>com.${{ inputs.appname }}.app</string>|' ./flutter/macos/Runner/Info.plist
# MACSTUFF Update copyright information if needed
sed -i '' -e 's|<key>NSHumanReadableCopyright</key>\s*<string>$(PRODUCT_COPYRIGHT)</string>|<key>NSHumanReadableCopyright</key>\n\t<string>${{ inputs.appname }}</string>|' ./flutter/macos/Runner/Info.plist
# MACSTUFF Update window title and bundle settings
cp ./flutter/macos/Runner/Configs/AppInfo.xcconfig ./flutter/macos/Runner/Configs/AppInfo.xcconfig.bak
sed -i '' -e 's|PRODUCT_NAME = .*|PRODUCT_NAME = ${{ inputs.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
# Keep DEVELOPMENT_TEAM if it exists, don't blank it out
# 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_BUNDLE_IDENTIFIER = ".*"/PRODUCT_BUNDLE_IDENTIFIER = "com.${{ inputs.appname }}.app"/' ./flutter/macos/Runner.xcodeproj/project.pbxproj
# Don't modify DEVELOPMENT_TEAM in project.pbxproj
# Update CMake settings
if [ -f "./flutter/macos/CMakeLists.txt" ]; then
sed -i '' -e 's/set(BINARY_NAME ".*")/set(BINARY_NAME "${{ inputs.appname }}")/' ./flutter/macos/CMakeLists.txt
fi
# 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 '"'"'Runner'"'"' do/' ./flutter/macos/Podfile
cp ./src/lang/en.rs ./src/lang/en.rs.bak
cp ./src/lang/nl.rs ./src/lang/nl.rs.bak
sed -i '' -e 's|RustDesk|${{ inputs.appname }}|' ./src/lang/en.rs
sed -i '' -e 's|RustDesk|${{ inputs.appname }}|' ./src/lang/nl.rs
sed -i '' -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).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|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/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).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|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ fromJson(inputs.extras).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 '/const KEY:/,/};/d' ./src/common.rs
sed -i '' -e '/let Ok(data) = sign::verify(&data, &pk)/,/};/d' ./src/common.rs
# Update pubspec.yaml with proper YAML formatting
cp ./flutter/pubspec.yaml ./flutter/pubspec.yaml.bak
echo " archive: ^3.6.1" > ./flutter/temp_dependency.txt
awk '/intl:/{print;system("cat ./flutter/temp_dependency.txt");next}1' ./flutter/pubspec.yaml > ./flutter/pubspec.yaml.tmp
mv ./flutter/pubspec.yaml.tmp ./flutter/pubspec.yaml
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 flutter
uses: subosito/flutter-action@v2.12.0
with:
channel: "stable"
flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Patch flutter
continue-on-error: true
run: |
cp .github/patches/flutter_3.24.4_dropdown_menu_enableFilter.diff $(dirname $(dirname $(which flutter)))
cd $(dirname $(dirname $(which flutter)))
[[ "3.24.4" == 3.24.5 ]] && git apply flutter_3.24.4_dropdown_menu_enableFilter.diff
- name: Workaround for flutter issue
shell: bash
run: |
cd "$(dirname "$(which flutter)")"
# https://github.com/flutter/flutter/issues/1.3.43
sed -i -e 's/_setFramesEnabledState(false);/\/\/_setFramesEnabledState(false);/g' ../packages/flutter/lib/src/scheduler/binding.dart
grep -n '_setFramesEnabledState(false);' ../packages/flutter/lib/src/scheduler/binding.dart
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.MAC_RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: "rustfmt"
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- name: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
run: |
if ! $VCPKG_ROOT/vcpkg \
install \
--x-install-root="$VCPKG_ROOT/installed"; then
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
echo "$_1:"
echo "======"
cat "$_1"
echo "======"
echo ""
done
exit 1
fi
head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true
shell: bash
- name: Magick stuff for macOS
if: ${{ inputs.iconlink != 'false' }}
continue-on-error: false
shell: bash
run: |
# Create all necessary directories first
mkdir -p ./res
mkdir -p ./flutter/assets
mkdir -p ./flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset
mkdir -p ./macos/Runner/Assets.xcassets/AppIcon.appiconset
mkdir -p ./rustdesk/data/flutter_assets/assets
# Download icon using curl with additional SSL options
curl -k -L --tlsv1.2 --proto =https --ssl-reqd \
-H "User-Agent: Mozilla/5.0" \
"${{ 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 "${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}&uuid=${{ fromJson(inputs.iconlink).uuid }}"
# Backup existing files (if they exist)
[ -f "./res/32x32.png" ] && mv ./res/32x32.png ./res/32x32.png.bak
[ -f "./res/64x64.png" ] && mv ./res/64x64.png ./res/64x64.png.bak
[ -f "./res/128x128.png" ] && mv ./res/128x128.png ./res/128x128.png.bak
[ -f "./res/mac-icon.png" ] && mv ./res/mac-icon.png ./res/mac-icon.png.bak
[ -f "./flutter/assets/icon.png" ] && mv ./flutter/assets/icon.png ./flutter/assets/icon.png.bak
[ -f "./flutter/assets/icon.svg" ] && mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak
[ -f "./rustdesk/data/flutter_assets/assets/icon.svg" ] && mv ./rustdesk/data/flutter_assets/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg.bak
# Create standard app icons
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
# Copy icon to Flutter assets
cp ./res/icon.png ./flutter/assets/icon.png
cp ./res/icon.png ./rustdesk/data/flutter_assets/assets/icon.png
# Convert PNG to SVG using potrace
magick ./res/icon.png -flatten ./temp_icon.pbm
potrace --svg -o ./flutter/assets/icon.svg ./temp_icon.pbm
cp ./flutter/assets/icon.svg ./rustdesk/data/flutter_assets/assets/icon.svg
rm ./temp_icon.pbm
# Create macOS app icons
magick ./res/icon.png -resize 16x16 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png"
magick ./res/icon.png -resize 32x32 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png"
magick ./res/icon.png -resize 64x64 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png"
magick ./res/icon.png -resize 128x128 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png"
magick ./res/icon.png -resize 256x256 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png"
magick ./res/icon.png -resize 512x512 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png"
magick ./res/icon.png -resize 1024x1024 "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png"
# Create macOS specific icons
magick ./res/icon.png -resize 128x128 ./res/mac-icon.png
# Create dark mode tray icon (optimized for macOS menu bar)
magick ./res/icon.png -resize 22x22 -colorspace gray -alpha set -background none -channel A -evaluate set 100% ./res/mac-tray-dark-x2.png
# Create light mode tray icon (optimized for macOS menu bar)
magick ./res/icon.png -resize 22x22 -negate -colorspace gray -alpha set -background none -channel A -evaluate set 100% ./res/mac-tray-light-x2.png
# Create AppIcon.icns (macOS native icon format)
mkdir -p ./iconset.iconset
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png" "./iconset.iconset/icon_16x16.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png" "./iconset.iconset/icon_16x16@2x.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png" "./iconset.iconset/icon_32x32.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png" "./iconset.iconset/icon_32x32@2x.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png" "./iconset.iconset/icon_128x128.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png" "./iconset.iconset/icon_128x128@2x.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png" "./iconset.iconset/icon_256x256.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png" "./iconset.iconset/icon_256x256@2x.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png" "./iconset.iconset/icon_512x512.png"
cp "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png" "./iconset.iconset/icon_512x512@2x.png"
iconutil -c icns ./iconset.iconset -o ./flutter/macos/Runner/AppIcon.icns
rm -rf ./iconset.iconset
# Create Contents.json for macOS app icon
echo '{
"images": [
{"size":"16x16","idiom":"mac","filename":"app_icon_16.png","scale":"1x"},
{"size":"16x16","idiom":"mac","filename":"app_icon_32.png","scale":"2x"},
{"size":"32x32","idiom":"mac","filename":"app_icon_32.png","scale":"1x"},
{"size":"32x32","idiom":"mac","filename":"app_icon_64.png","scale":"2x"},
{"size":"128x128","idiom":"mac","filename":"app_icon_128.png","scale":"1x"},
{"size":"128x128","idiom":"mac","filename":"app_icon_256.png","scale":"2x"},
{"size":"256x256","idiom":"mac","filename":"app_icon_256.png","scale":"1x"},
{"size":"256x256","idiom":"mac","filename":"app_icon_512.png","scale":"2x"},
{"size":"512x512","idiom":"mac","filename":"app_icon_512.png","scale":"1x"},
{"size":"512x512","idiom":"mac","filename":"app_icon_1024.png","scale":"2x"}
],
"info": {
"version": 1,
"author": "xcode"
}
}' > "flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json"
# Copy icons and Contents.json to both locations
cp -r flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/* macos/Runner/Assets.xcassets/AppIcon.appiconset/
# Verify files exist and show their sizes
echo "Verifying generated files:"
ls -lh ./res/mac-tray-dark-x2.png
ls -lh ./res/mac-tray-light-x2.png
ls -lh ./res/mac-icon.png
echo "Flutter macOS app icons:"
ls -lh flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/
echo "Flutter assets:"
ls -lh flutter/assets/icon.*
echo "RustDesk Flutter assets:"
ls -lh rustdesk/data/flutter_assets/assets/
- name: replace flutter icons
if: ${{ inputs.iconlink != 'false' }}
continue-on-error: false
shell: bash
run: |
cd ./flutter
# Create required directories and files
mkdir -p web
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 '<!DOCTYPE html><html><head><meta charset="UTF-8"><title>${{ inputs.appname }}</title></head><body></body></html>' > web/index.html
# Ensure the AppIcon.appiconset directory exists
mkdir -p macos/Runner/Assets.xcassets/AppIcon.appiconset
# Copy the processed icons to Flutter locations
cp ../res/mac-icon.png ./assets/icon.png
cp ../flutter/assets/icon.svg ./assets/icon.svg || true
flutter pub upgrade win32
flutter pub get
flutter pub run flutter_launcher_icons
cd ..
- name: ui.rs
if: ${{ inputs.iconlink != 'false' }}
continue-on-error: true
shell: bash
run: |
cp ./src/ui.rs ./src/ui.rs.bak
if [ -f "./res/icon.png" ]; then
SEARCH_STR="iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAEiuAABIrgHwmhA7AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAEx9JREFUeJztnXmYHMV5h9"
b64=$(base64 < ./res/icon.png)
sed -i '' -e "s~$SEARCH_STR.*\"~$b64\"~" ./src/ui.rs
fi
- name: fix connection delay
continue-on-error: false
if: ${{ fromJson(inputs.extras).delayFix == 'true' }}
shell: bash
run: |
sed -i '' -e '/if !key.is_empty() && !token.is_empty() {/,/}/d' ./src/client.rs
- name: add cycle monitors to toolbar
continue-on-error: true
if: fromJson(inputs.extras).cycleMonitor == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/cycle_monitor.diff
git apply cycle_monitor.diff
- name: use X for offline display instead of orange circle
continue-on-error: true
if: fromJson(inputs.extras).xOffline == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/xoffline.diff
git apply xoffline.diff
- name: hide-cm
continue-on-error: true
if: fromJson(inputs.extras).hidecm == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/hidecm.diff
git apply hidecm.diff
- name: statussort
continue-on-error: true
if: fromJson(inputs.extras).statussort == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/statussort.diff
git apply statussort.diff
- name: removeNewVersionNotif
continue-on-error: true
if: fromJson(inputs.extras).removeNewVersionNotif == 'true'
run: |
wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeNewVersionNotif.diff
git apply removeNewVersionNotif.diff
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@v1
with:
toolchain: ${{ env.MAC_RUST_VERSION }}
targets: ${{ matrix.job.target }}
components: "rustfmt"
- 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": "15% complete"}'
- uses: Swatinem/rust-cache@v2
with:
prefix-key: ${{ matrix.job.os }}
- 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: Setup vcpkg with Github Actions binary cache
uses: lukka/run-vcpkg@v11
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT_ID }}
doNotCache: false
- name: Install vcpkg dependencies
run: |
if ! $VCPKG_ROOT/vcpkg \
install \
--x-install-root="$VCPKG_ROOT/installed"; then
find "${VCPKG_ROOT}/" -name "*.log" | while read -r _1; do
echo "$_1:"
echo "======"
cat "$_1"
echo "======"
echo ""
done
exit 1
fi
- 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: Build rustdesk
run: |
if [ "${{ matrix.job.target }}" = "aarch64-apple-darwin" ]; then
MIN_MACOS_VERSION="12.3"
sed -i -e "s/MACOSX_DEPLOYMENT_TARGET\=[0-9]*.[0-9]*/MACOSX_DEPLOYMENT_TARGET=${MIN_MACOS_VERSION}/" build.py
sed -i -e "s/platform :osx, '.*'/platform :osx, '${MIN_MACOS_VERSION}'/" flutter/macos/Podfile
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
fi
sed -i -e "s/RustDesk.app/\"${{ inputs.appname }}.app\"/" build.py
./build.py --flutter --hwcodec ${{ matrix.job.extra-build-args }}
- 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
if: env.MACOS_P12_BASE64 != null
shell: bash
run: |
pushd /tmp
wget https://github.com/indygreg/apple-platform-rs/releases/download/apple-codesign%2F0.22.0/apple-codesign-0.22.0-macos-universal.tar.gz
tar -zxvf apple-codesign-0.22.0-macos-universal.tar.gz
mv apple-codesign-0.22.0-macos-universal/rcodesign /usr/local/bin
popd
- name: Install build runtime
run: |
brew install llvm create-dmg nasm cmake gcc wget ninja
# 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
echo "pkg-config is already installed"
else
brew install pkg-config
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)
shell: bash
run: |
clang --version || true
rustup -V
rustup toolchain list
rustup default
cargo -V
rustc -V
- name: icon svg handling
if: ${{ inputs.iconlink != 'false' }}
continue-on-error: false
shell: bash
run: |
ASSETS_DIR="build/macos/Build/Products/Release/RustDesk.app/Contents/Frameworks/App.framework/Versions/Current/Resources/flutter_assets/assets"
mkdir -p "$ASSETS_DIR"
if [ -f "$ASSETS_DIR/icon.svg" ]; then
mv "$ASSETS_DIR/icon.svg" "$ASSETS_DIR/icon.svg.bak"
fi
# First convert PNG to PBM (bitmap)
magick convert ./res/icon.png ./temp_icon.pbm
# Then use potrace to convert to SVG
potrace --svg -o "$ASSETS_DIR/icon.svg" ./temp_icon.pbm
rm ./temp_icon.pbm
- name: logo handling
if: ${{ inputs.logolink != 'false' }}
continue-on-error: false
shell: bash
run: |
ASSETS_DIR="build/macos/Build/Products/Release/RustDesk.app/Contents/Frameworks/App.framework/Versions/Current/Resources/flutter_assets/assets"
mkdir -p "$ASSETS_DIR"
curl -k -L --tlsv1.2 --proto =https --ssl-reqd \
-H "User-Agent: Mozilla/5.0" \
"${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}&uuid=${{ fromJson(inputs.logolink).uuid }}" \
-o "$ASSETS_DIR/logo.png" || \
wget --no-check-certificate \
-O "$ASSETS_DIR/logo.png" \
"${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}&uuid=${{ fromJson(inputs.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
if: env.MACOS_P12_BASE64 != ''
run: |
cd flutter/build/macos/Build/Products/Release
# Debug info
echo "Current directory contents:"
ls -la
# Rename RustDesk.app to the custom app name first
if [ -d "RustDesk.app" ]; then
# First rename the app if it's still called RustDesk.app
mv "RustDesk.app" "${{ inputs.appname }}.app"
echo "Renamed RustDesk.app to ${{ inputs.appname }}.app"
fi
echo "App bundle contents after rename:"
ls -la "${{ inputs.appname }}.app" || echo "App not found"
ls -la "${{ inputs.appname }}.app/Contents" || echo "Contents not found"
# Decode the certificate
echo "${{ secrets.MACOS_P12_BASE64 }}" | base64 --decode > certificate.p12
# Sign the app bundle and its contents
if [ -d "${{ inputs.appname }}.app/Contents/MacOS" ]; then
echo "Signing main executable..."
MAIN_EXECUTABLE="${{ inputs.appname }}.app/Contents/MacOS/${{ inputs.appname }}"
if [ -f "$MAIN_EXECUTABLE" ]; then
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime "$MAIN_EXECUTABLE"
else
echo "Main executable not found at expected path: $MAIN_EXECUTABLE"
# Try to find the actual executable
echo "Available executables in MacOS directory:"
ls -la "${{ inputs.appname }}.app/Contents/MacOS/"
ACTUAL_EXECUTABLE=$(ls "${{ inputs.appname }}.app/Contents/MacOS/" | head -n 1)
if [ -n "$ACTUAL_EXECUTABLE" ]; then
echo "Found executable: $ACTUAL_EXECUTABLE"
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime "${{ inputs.appname }}.app/Contents/MacOS/$ACTUAL_EXECUTABLE"
fi
fi
echo "Signing frameworks..."
find "${{ inputs.appname }}.app/Contents/Frameworks" -type f -not -name ".*" -exec \
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime {} \;
echo "Signing main bundle..."
rcodesign sign --p12-file certificate.p12 --p12-password "${{ secrets.MACOS_P12_PASSWORD }}" \
--code-signature-flags runtime "${{ inputs.appname }}.app"
else
echo "Error: Invalid app bundle structure"
exit 1
fi
# Clean up
rm certificate.p12
- name: Create DMG
run: |
cd /Users/runner/work/${{ github.event.repository.name }}/${{ github.event.repository.name }}/flutter/build/macos/Build/Products/Release
# Print directory contents for debugging
echo "Directory contents:"
ls -la
# Find the actual .app bundle
if [ -d "RustDesk.app" ]; then
# First rename the app if it's still called RustDesk.app
mv "RustDesk.app" "${{ inputs.appname }}.app"
fi
if [ ! -d "${{ inputs.appname }}.app" ]; then
echo "Could not find .app bundle!"
exit 1
fi
echo "Creating DMG for ${{ inputs.appname }}.app"
create-dmg \
--volname "${{ inputs.appname }}" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "${{ inputs.appname }}.app" 200 190 \
--hide-extension "${{ inputs.appname }}.app" \
--app-drop-link 600 185 \
"${{ inputs.appname }}-${{ matrix.job.arch }}.dmg" \
"${{ inputs.appname }}.app"
mv "${{ inputs.appname }}-${{ matrix.job.arch }}.dmg" $GITHUB_WORKSPACE/
#- name: Upload unsigned macOS app
# if: env.UPLOAD_ARTIFACT == 'true'
# uses: actions/upload-artifact@master
# with:
# name: ${{ inputs.appname }}-${{ matrix.job.arch }}
# path: ${{ inputs.appname }}-${{ matrix.job.arch }}.dmg
- name: Rename rustdesk
if: env.UPLOAD_ARTIFACT == 'true'
run: |
cd $GITHUB_WORKSPACE
echo "Directory contents:"
ls -la
# Find the DMG file dynamically
DMG_FILE=$(find . -name "${{ inputs.appname }}-${{ matrix.job.arch }}.dmg")
if [ -n "$DMG_FILE" ]; then
echo "Found DMG file: $DMG_FILE"
mv "$DMG_FILE" "${{ inputs.filename }}.dmg"
echo "Renamed to ${{ inputs.filename }}.dmg"
else
echo "No DMG file found matching the pattern"
exit 1
fi
- name: send file to rdgen server
if: ${{ fromJson(inputs.extras).rdgen == 'true' }}
shell: bash
run: |
curl -i -X POST \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" \
-F "file=@$GITHUB_WORKSPACE/${{ inputs.filename }}.dmg" \
-F "uuid=${{ inputs.uuid }}" \
"${{ secrets.GENURL }}/save_custom_client"
- name: send file to api server
if: ${{ fromJson(inputs.extras).rdgen == 'false' }}
shell: bash
run: |
curl -i -X POST \
-H "Content-Type: multipart/form-data" \
-H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" \
-F "file=@$GITHUB_WORKSPACE/${{ inputs.filename }}.dmg" \
"${{ inputs.apiServer }}/api/save_custom_client"
- 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"}'
- 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"}'
#- name: Publish DMG package
# if: env.UPLOAD_ARTIFACT == 'true'
# uses: softprops/action-gh-release@v1
# with:
# prerelease: true
# tag_name: ${{ inputs.upload-tag }}
# files: |
# ${{ inputs.appname }}*-${{ matrix.job.arch }}.dmg

File diff suppressed because one or more lines are too long

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,15 +41,49 @@ jobs:
- name: Add MSBuild to PATH
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
run: |
git clone https://github.com/rustdesk-org/RustDeskTempTopMostWindow RustDeskTempTopMostWindow
# Build. commit 53b548a5398624f7149a382000397993542ad796 is tag v0.3
- name: Build the project
run: |
cd RustDeskTempTopMostWindow && git checkout 53b548a5398624f7149a382000397993542ad796
msbuild ${{ env.project_path }} -p:Configuration=${{ inputs.configuration }} -p:Platform=${{ inputs.platform }} /p:TargetVersion=${{ inputs.target_version }}
uses: nick-fields/retry@v3
with:
timeout_minutes: 1
max_attempts: 3
shell: pwsh
command: |
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 }}
- name: Archive build artifacts
uses: actions/upload-artifact@master

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).
If you would like to host the generator yourself, see [here](setup.md)
This client generator is currently integrated into my rustdesk [api
server](https://github.com/bryangerlach/rustdesk-api-server), which is a fork
of [rustdesk-api-server](https://github.com/kingmo888/rustdesk-api-server). If
you are running my api server, then you will still need to fork RDGen and go
through the setup process, but you won't need to actually run the rdgen server.
## Features
- Embed server and key into client
- Custom app name
- 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,16 +1,24 @@
services:
rdgen:
# use bryangerlach/rdgen:dev for the latest dev build
image: bryangerlach/rdgen:v0.3.1
# use bryangerlach/rdgen:latest for the latest build
#build: .
image: bryangerlach/rdgen:latest
restart: unless-stopped
environment:
SECRET_KEY: "django-insecure-!(t-!f#6g#sr%yfded9(xha)g+=!6craeez^cp+*&bz_7vdk61"
GHUSER: "github_username"
GHBEARER: "github_access_token"
GENURL: "accessible_url_of_server"
ZIP_PASSWORD: ""
GHBRANCH: "master"
PROTOCOL: "https"
REPONAME: "rdgen"
SH_SECRET: ""
ports:
- "8000:8000"
dns:
- 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
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
# 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", '')
GHBEARER = os.environ.get("GHBEARER", '')
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')
REPONAME = os.environ.get("REPONAME", 'rdgen')
SH_SECRET = os.environ.get('SH_SECRET', 'secret')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# 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 = ['*']
#CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS', '').split()
@@ -131,3 +135,5 @@ STATIC_URL = 'static/'
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
DATA_UPLOAD_MAX_MEMORY_SIZE = None

View File

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

View File

@@ -1,9 +1,12 @@
from django import forms
from PIL import Image
class GenerateForm(forms.Form):
sh_secret_field = forms.CharField(required=False)
#Platform
platform = forms.ChoiceField(choices=[('windows','Windows'),('linux','Linux (currently unavailable)'),('android','Android'),('macos','macOS')], initial='windows')
version = forms.ChoiceField(choices=[('master','nightly'),('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.3.7')
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.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"
delayFix = forms.BooleanField(initial=True, required=False)
#General
@@ -22,16 +25,23 @@ class GenerateForm(forms.Form):
('settingsY', 'No, enable settings'),
('settingsN', 'Yes, DISABLE settings')
], initial='settingsY')
androidappid = forms.CharField(label="Custom Android App ID (replaces 'com.carriez.flutter_hbb')", required=False)
#Custom Server
serverIP = forms.CharField(label="Host", required=False)
apiServer = forms.CharField(label="API Server", required=False)
key = forms.CharField(label="Key", required=False)
urlLink = forms.CharField(label="Custom URL for links", required=False)
downloadLink = forms.CharField(label="Custom URL for downloading new versions", required=False)
compname = forms.CharField(label="Company name",required=False)
#Visual
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'}))
privacyfile = forms.FileField(label="Custom privacy screen (in .png format)", required=False, widget=forms.FileInput(attrs={'accept': 'image/png'}))
iconbase64 = forms.CharField(required=False)
logobase64 = forms.CharField(required=False)
privacybase64 = forms.CharField(required=False)
theme = forms.ChoiceField(choices=[
('light', 'Light'),
('dark', 'Dark'),
@@ -42,7 +52,7 @@ class GenerateForm(forms.Form):
#Security
passApproveMode = forms.ChoiceField(choices=[('password','Accept sessions via password'),('click','Accept sessions via click'),('password-click','Accepts sessions via both')],initial='password-click')
permanentPassword = forms.CharField(widget=forms.PasswordInput(), required=False)
runasadmin = forms.ChoiceField(choices=[('false','No'),('true','Yes')], initial='false')
#runasadmin = forms.ChoiceField(choices=[('false','No'),('true','Yes')], initial='false')
denyLan = forms.BooleanField(initial=False, required=False)
enableDirectIP = forms.BooleanField(initial=False, required=False)
#ipWhitelist = forms.BooleanField(initial=False, required=False)
@@ -60,6 +70,10 @@ class GenerateForm(forms.Form):
enableRecording = forms.BooleanField(initial=True, required=False)
enableBlockingInput = forms.BooleanField(initial=True, required=False)
enableRemoteModi = forms.BooleanField(initial=False, required=False)
hidecm = forms.BooleanField(initial=False, required=False)
enablePrinter = forms.BooleanField(initial=True, required=False)
enableCamera = forms.BooleanField(initial=True, required=False)
enableTerminal = forms.BooleanField(initial=True, required=False)
#Other
removeWallpaper = forms.BooleanField(initial=True, required=False)
@@ -70,6 +84,29 @@ class GenerateForm(forms.Form):
#custom added features
cycleMonitor = forms.BooleanField(initial=False, required=False)
xOffline = forms.BooleanField(initial=False, required=False)
hidecm = forms.BooleanField(initial=False, required=False)
statussort = forms.BooleanField(initial=False, required=False)
removeNewVersionNotif = forms.BooleanField(initial=False, required=False)
removeNewVersionNotif = forms.BooleanField(initial=False, required=False)
def clean_iconfile(self):
print("checking icon")
image = self.cleaned_data['iconfile']
if image:
try:
# Open the image using Pillow
img = Image.open(image)
# Check if the image is a PNG (optional, but good practice)
if img.format != 'PNG':
raise forms.ValidationError("Only PNG images are allowed.")
# Get image dimensions
width, height = img.size
# Check for square dimensions
if width != height:
raise forms.ValidationError("Custom App Icon dimensions must be square.")
return image
except OSError: # Handle cases where the uploaded file is not a valid image
raise forms.ValidationError("Invalid icon file.")
except Exception as e: # Catch any other image processing errors
raise forms.ValidationError(f"Error processing icon: {e}")

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)
uuid = models.CharField(verbose_name="uuid", 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

@@ -129,16 +129,28 @@
{% 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}}.deb&uuid={{uuid}}' class="download-link">Download {{filename}}.deb</a>
<a href='/download?filename={{filename}}.rpm&uuid={{uuid}}' class="download-link">Download {{filename}}.rpm</a>
<a href='/download?filename={{filename}}-suse.rpm&uuid={{uuid}}' class="download-link">Download {{filename}}-suse.rpm</a>
<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}}.dmg&uuid={{uuid}}' class="download-link">Download {{filename}}.dmg</a>
<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 %}
@@ -167,7 +179,7 @@
platformLogos.macos.style.display = 'block';
platformNote.textContent = 'Note: For macOS, you may need to adjust security settings to run the application.';
platformNote.style.display = 'block';
} else if (platform === 'windows') {
} else if (platform === 'windows' || platform === 'windows-x86') {
document.getElementById('pageTitle').textContent = 'Windows Build Generated';
platformLogos.windows.style.display = 'block';
platformNote.textContent = 'Note: You might need to disable SmartScreen or adjust Windows security settings.';
@@ -189,4 +201,4 @@
updatePlatformUI();
</script>
</body>
</html>{{ ... }}
</html>{{ ... }}

View File

@@ -90,7 +90,26 @@
transition: color 0.3s ease;
}
.platform-icon:hover, .platform-icon.active {
color: #fff;
color: #2e52f7;
}
.text-64 {
font-size: 0.5em;
font-weight: bold;
bottom: -0.2em;
right: -0.2em;
position: absolute;
color: white;
transform: translate(-50%, -50%);
}
.text-32 {
font-size: 0.5em;
font-weight: bold;
bottom: -0.2em;
right: -0.2em;
position: absolute;
color: white;
transform: translate(-50%, -50%);
}
.checkbox-group {
display: grid;
@@ -106,27 +125,152 @@
max-height: 100px;
margin-top: 10px;
}
.save-load-section-container { /* New container for fixed positioning */
position: fixed;
top: 20px; /* Adjust as needed */
left: 20px; /* Adjust as needed */
background-color: #111; /* Match your section background */
padding: 0px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
z-index: 100; /* Ensure it's above other content */
}
.save-load-section {
display: none; /* Initially hidden */
}
.error {
color: red;
}
.errorlist {
color: red;
display: flex; /* Enable flexbox for centering */
justify-content: center; /* Center horizontally */
align-items: center; /* Center vertically (if needed) */
list-style: none; /* Remove bullet points if it's a list */
padding: 0; /* Remove default padding */
margin: 10px auto; /* Center the list itself, add some top/bottom margin */
width: fit-content; /* Make the width fit the content */
}
.errorlist li {
margin: 5px; /* Add some spacing between list items */
}
@keyframes blink {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
.help-text {
color: #ffd700;
font-style: italic;
animation: blink 2s infinite;
padding: 5px;
border-radius: 4px;
display: inline-block;
margin-left: 10px;
}
.password-requirement {
display: none; /* Hidden by default */
color: orange;
font-size: 0.9em;
margin-top: 5px;
}
.sponsor-button {
display: inline-flex;
align-items: center;
background: linear-gradient(135deg, #00457C 0%, #0079C1 100%);
color: white;
padding: 12px 28px;
border-radius: 50px;
text-decoration: none;
font-weight: 600;
font-size: 16px;
letter-spacing: 0.5px;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 69, 124, 0.2);
border: 2px solid rgba(255, 255, 255, 0.1);
text-transform: uppercase;
}
.sponsor-button:hover {
transform: translateY(-2px);
box-shadow: 0 8px 25px rgba(0, 69, 124, 0.3);
background: linear-gradient(135deg, #005AA7 0%, #0095EA 100%);
border-color: rgba(255, 255, 255, 0.2);
}
.sponsor-button i {
margin-right: 12px;
font-size: 20px;
background: white;
color: #00457C;
padding: 8px;
border-radius: 50%;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
}
.sponsor-button:hover i {
transform: rotate(360deg);
color: #0095EA;
}
</style>
</head>
<body>
<h1><i class="fas fa-cogs"></i> RustDesk Custom Client Builder</h1>
<form action="/generator" method="post" enctype="multipart/form-data">
<form id="myForm" action="/generator" method="post" enctype="multipart/form-data">
<div class="save-load-section-container">
<div class="section">
<h2 id="saveLoadTitle">Save/Load Configuration <i class="fas fa-chevron-down"></i></h2>
<div class="save-load-section">
<button type="button" onclick="saveFormData()">Save Configuration</button>
<input type="file" id="fileInput" style="display:none;" accept=".json">
<button type="button" onclick="loadFormData()">Load Configuration</button>
</div>
</div>
</div>
{% if form.iconfile.errors %}
<ul class="errorlist">
{% for error in form.iconfile.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<div class="platform">
<h2><i class="fas fa-desktop"></i> Select Platform</h2>
<div class="platform-icons">
<i class="fab fa-windows platform-icon active" data-platform="windows"></i>
<span class="fa-stack fa-lg">
<i class="fab fa-windows fa-stack-2x platform-icon active" data-platform="windows"></i>
<strong class="fa-stack-1x text-64">64</strong>
</span>
<span class="fa-stack fa-lg">
<i class="fab fa-windows fa-stack-2x platform-icon" data-platform="windows-x86"></i>
<strong class="fa-stack-1x text-32">32</strong>
</span>
<i class="fab fa-linux platform-icon" data-platform="linux"></i>
<i class="fab fa-android platform-icon" data-platform="android"></i>
<i class="fab fa-apple platform-icon" data-platform="macos"></i>
</div>
<select name="platform" id="id_platform">
<option value="windows" selected>Windows</option>
<option value="windows" selected>Windows 64Bit</option>
<option value="windows-x86">Windows 32Bit</option>
<option value="linux">Linux</option>
<option value="android">Android</option>
<option value="macos">macOS</option>
</select>
<label for="{{ form.version.id_for_label }}">Rustdesk Version:</label>
{{ form.version }}
{% if form.version.help_text %}
<span class="help-text">{{ form.version.help_text }}</span>
{% endif %}
<div class="help-text">{{ form.version.help_text }}</div>
<label for="{{ form.delayFix.id_for_label }}">{{ form.delayFix }} Fix connection delay when using third-party API</label>
</div>
</div>
@@ -134,7 +278,7 @@
<div class="section">
<h2><i class="fas fa-sliders-h"></i> General</h2>
<label for="{{ form.exename.id_for_label }}">Name of the configuration (no spaces or special characters, English characters only):</label>
{{ form.exename }}<br><br>
{{ form.exename }}<span id="filenameError" class="error"></span><br><br>
<label for="{{ form.appname.id_for_label }}">Custom Application Name (leave blank to use default):</label>
{{ form.appname }}<br><br>
<label for="{{ form.direction.id_for_label }}">Connection Type:</label>
@@ -143,6 +287,8 @@
{{ form.installation }}<br><br>
<label for="{{ form.settings.id_for_label }}">Disable Settings:</label>
{{ form.settings }}<br><br>
<label for="{{ form.androidappid.id_for_label }}">Custom Android App ID (replaces 'com.carriez.flutter_hbb', leave blank to use default):</label>
{{ form.androidappid }}<br><br>
</div>
<div class="section">
@@ -155,15 +301,18 @@
{{ form.apiServer }}<br><br>
<label for="{{ form.urlLink.id_for_label }}">Custom URL for links (replaces https://rustdesk.com):</label>
{{ form.urlLink }}<br><br>
<label for="{{ form.downloadLink.id_for_label }}">Custom URL for downloading updates (replaces https://rustdesk.com/download):</label>
{{ form.downloadLink }}<br><br>
<label for="{{ form.compname.id_for_label }}">Company name for copyright (replaces Purslane Ltd):</label>
{{ form.compname }}<br><br>
</div>
</div>
<div class="container">
<div class="section">
<h2><i class="fas fa-shield-alt"></i> Security</h2>
<label for="{{ form.runasadmin.id_for_label }}">Always run as Administrator?</label>
{{ form.runasadmin }}<br><br>
<label for="{{ form.passApproveMode.id_for_label }}">Password Approve mode:</label>
{{ form.passApproveMode }}<br><br>
<div id="passwordRequirement" class="password-requirement">To use the hide connection window feature, please set a permanent password.</div>
<label for="{{ form.permanentPassword.id_for_label }}">Set Permanent Password:</label>
{{ form.permanentPassword }} *The password is used as default, but can be changed by the client<br><br>
@@ -172,11 +321,16 @@
<label for="{{ form.enableDirectIP.id_for_label }}">{{ form.enableDirectIP }} Enable direct IP access</label><br>
<label for="{{ form.autoClose.id_for_label }}">{{ form.autoClose }} Automatically close incoming sessions on user inactivity</label><br>
<label for="{{ form.autoClose.id_for_label }}">{{ form.autoClose }} Automatically close incoming sessions on user inactivity</label><br>
<label for="{{ form.hidecm.id_for_label }}">{{ form.hidecm }} Allow hiding the connection window from remote screen.</label><br>
</div>
<div class="section">
<h2><i class="fas fa-paint-brush"></i> Visual</h2>
{{ form.iconbase64.as_hidden }}
{{ form.logobase64.as_hidden }}
{{ form.privacybase64.as_hidden }}
<label for="{{ form.iconfile.id_for_label }}">Custom App Icon (in .png format)</label>
{{ form.iconfile }}<br><br>
<!-- <input type="file" name="iconfile" id="iconfile" accept="image/png"> -->
@@ -185,6 +339,10 @@
{{ form.logofile }}<br><br>
<!-- <input type="file" name="logofile" id="logofile" accept="image/png"> -->
<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>
{{ form.theme }} {{ form.themeDorO }} *Default sets the theme but allows the client to change it, Override sets the theme permanently.<br><br>
</div>
@@ -192,7 +350,7 @@
<div class="container">
<div class="section">
<h2><i class="fas fa-lock"></i> Permissions</h2>
The following Permissions can be set as default (the user can change the settins) or override (the settings cannot be changed).<br>
The following Permissions can be set as default (the user can change the settings) or override (the settings cannot be changed).<br>
{{ form.permissionsDorO }}
<label for="{{ form.permissionsType.id_for_label }}">Permission type:</label>
{{ form.permissionsType }}<br><br>
@@ -206,18 +364,20 @@
<label for="{{ form.enableRecording.id_for_label }}">{{ form.enableRecording }} Enable recording session</label>
<label for="{{ form.enableBlockingInput.id_for_label }}">{{ form.enableBlockingInput }} Enable blocking user input</label>
<label for="{{ form.enableRemoteModi.id_for_label }}">{{ form.enableRemoteModi }} Enable remote configuration modification</label>
<label for="{{ form.enablePrinter.id_for_label }}">{{ form.enablePrinter }} Enable remote printer</label>
<label for="{{ form.enableCamera.id_for_label }}">{{ form.enableCamera }} Enable remote camera</label>
<label for="{{ form.enableTerminal.id_for_label }}">{{ form.enableTerminal }} Enable remote terminal</label>
</div><br>
<h2><i class="fas fa-code"></i> Code Changes</h2>
<label for="{{ form.cycleMonitor.id_for_label }}">{{ form.cycleMonitor }} Add a button to cycle through available monitors to the minimized toolbar.</label><br>
<label for="{{ form.xOffline.id_for_label }}">{{ form.xOffline }} Display an X for offline devices in the addressbook.</label><br>
<label for="{{ form.hidecm.id_for_label }}">{{ form.hidecm }} Allow hiding the connection window from remote screen.</label><br>
<label for="{{ form.statussort.id_for_label }}">{{ form.statussort }} Allow sorting by online status.</label><br>
<label for="{{ form.removeNewVersionNotif.id_for_label }}">{{ form.removeNewVersionNotif }} Remove notification for new versions.</label><br>
</div>
<div class="section">
<h2><i class="fas fa-cog"></i> Other</h2>
<label for="{{ form.removeWallpaper.id_for_label }}">{{ form.removeWallpaper }} Remove wallpaper during incoming sessions</label><br>
<a href="https://rustdesk.com/docs/en/self-host/client-configuration/advanced-settings/">Click here for a list of Default/Override settings</a>
<label for="{{ form.defaultManual.id_for_label }}">Default settings</label><br>
{{ form.defaultManual }}<br><br>
<label for="{{ form.overrideManual.id_for_label }}">Override settings</label><br>
@@ -227,10 +387,24 @@
<div class="platform">
<div class="section">
<button type="submit"><i class="fas fa-rocket"></i> Generate Custom Client</button>
<a href="https://github.com/bryangerlach/rdgen">Source Code on github</a>
<iframe src="https://github.com/sponsors/bryangerlach/button" title="Sponsor bryangerlach" height="32" width="114" style="border: 0; border-radius: 6px;"></iframe>
</div>
</div>
<div style="text-align: center; margin: 30px 0;">
<a href="https://github.com/bryangerlach/rdgen"
target="_blank"
class="sponsor-button">
<i class="fab fa-github"></i>
Source Code on Github
</a>
</div>
<div style="text-align: center; margin: 30px 0;">
<a href="https://github.com/sponsors/bryangerlach?o=esb"
target="_blank"
class="sponsor-button">
<i class="fab fa-github"></i>
Donate
</a>
</div>
</form>
<script>
document.querySelectorAll('.platform-icon').forEach(icon => {
@@ -246,6 +420,101 @@
document.getElementById("{{ form.logofile.id_for_label }}").addEventListener('change', function(event) {
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() {
if (this.checked) {
document.getElementById("passwordRequirement").style.display = 'block';
document.getElementById("{{ form.permanentPassword.id_for_label }}").focus();
document.getElementById("{{ form.passApproveMode.id_for_label }}").value = 'password';
} else {
document.getElementById("passwordRequirement").style.display = 'none';
document.getElementById("{{ form.passApproveMode.id_for_label }}").value = 'password-click';
}
});
const enableKeyboard = document.getElementById("{{ form.enableKeyboard.id_for_label }}");
const enableClipboard = document.getElementById("{{ form.enableClipboard.id_for_label }}");
const enableFileTransfer = document.getElementById("{{ form.enableFileTransfer.id_for_label }}");
const enableAudio = document.getElementById("{{ form.enableAudio.id_for_label }}");
const enableTCP = document.getElementById("{{ form.enableTCP.id_for_label }}");
const enableRemoteRestart = document.getElementById("{{ form.enableRemoteRestart.id_for_label }}");
const enableRecording = document.getElementById("{{ form.enableRecording.id_for_label }}");
const enableBlockingInput = document.getElementById("{{ form.enableBlockingInput.id_for_label }}");
const enableRemoteModi = document.getElementById("{{ form.enableRemoteModi.id_for_label }}");
const enablePrinter = document.getElementById("{{ form.enablePrinter.id_for_label }}");
const enableCamera = document.getElementById("{{ form.enableCamera.id_for_label }}");
const enableTerminal = document.getElementById("{{ form.enableTerminal.id_for_label }}");
document.getElementById("{{ form.permissionsType.id_for_label }}").addEventListener('change', function() {
if (this.value === 'full') {
enableKeyboard.checked = true;
enableClipboard.checked = true;
enableFileTransfer.checked = true;
enableAudio.checked = true;
enableTCP.checked = true;
enableRemoteRestart.checked = true;
enableRecording.checked = true;
enableBlockingInput.checked = true;
enableRemoteModi.checked = true;
enablePrinter.checked = true;
enableCamera.checked = true;
enableTerminal.checked = true;
enableKeyboard.disabled = true;
enableClipboard.disabled = true;
enableFileTransfer.disabled = true;
enableAudio.disabled = true;
enableTCP.disabled = true;
enableRemoteRestart.disabled = true;
enableRecording.disabled = true;
enableBlockingInput.disabled = true;
enableRemoteModi.disabled = true;
enablePrinter.disabled = true;
enableCamera.disabled = true;
enableTerminal.disable = true;
} else if (this.value === 'view') {
enableKeyboard.checked = false;
enableClipboard.checked = false;
enableFileTransfer.checked = false;
enableAudio.checked = false;
enableTCP.checked = false;
enableRemoteRestart.checked = false;
enableRecording.checked = false;
enableBlockingInput.checked = false;
enableRemoteModi.checked = false;
enablePrinter.checked = false;
enableCamera.checked = false;
enableTerminal.checked = false;
enableKeyboard.disabled = true;
enableClipboard.disabled = true;
enableFileTransfer.disabled = true;
enableAudio.disabled = true;
enableTCP.disabled = true;
enableRemoteRestart.disabled = true;
enableRecording.disabled = true;
enableBlockingInput.disabled = true;
enableRemoteModi.disabled = true;
enablePrinter.disabled = true;
enableCamera.disabled = true;
enableTerminal.disable = true;
} else if (this.value === 'custom') {
enableKeyboard.disabled = false;
enableClipboard.disabled = false;
enableFileTransfer.disabled = false;
enableAudio.disabled = false;
enableTCP.disabled = false;
enableRemoteRestart.disabled = false;
enableRecording.disabled = false;
enableBlockingInput.disabled = false;
enableRemoteModi.disabled = false;
enablePrinter.checked = false;
enableCamera.checked = false;
enableTerminal.checked = false;
}
});
function previewImage(input, previewContainerId) {
if (input.files && input.files[0]) {
var reader = new FileReader();
@@ -260,6 +529,165 @@
reader.readAsDataURL(input.files[0]);
}
}
const saveLoadTitle = document.getElementById("saveLoadTitle");
const saveLoadSection = document.querySelector(".save-load-section");
saveLoadTitle.addEventListener("click", () => {
if (!saveLoadSection.style.display || saveLoadSection.style.display === "none") {
saveLoadSection.style.display = "block";
} else {
saveLoadSection.style.display = "none";
}
const icon = saveLoadTitle.querySelector("i");
icon.classList.toggle("fa-chevron-down");
icon.classList.toggle("fa-chevron-up");
});
async function saveFormData() {
const filename = document.getElementById("{{ form.exename.id_for_label }}").value;
if (!filename) {
document.getElementById("filenameError").textContent = "Filename is required.";
return;
} else {
document.getElementById("filenameError").textContent = "";
}
const formData = {};
const form = document.getElementById("myForm"); // Get the form element
// 1. Use FormData for robust data collection (handles most input types):
const formDataObj = new FormData(form); // Create a FormData object
formDataObj.forEach((value, key) => {
formData[key] = value; // Add each key/value pair to the formData object.
});
// 2. Handle select elements separately (more reliable):
const selectElements = form.querySelectorAll('select');
selectElements.forEach(select => {
formData[select.name] = select.value;
});
// 3. Handle checkboxes and radio buttons (important!):
const checkboxRadioElements = form.querySelectorAll('input[type="checkbox"], input[type="radio"]');
checkboxRadioElements.forEach(input => {
if (input.name) { //only add to form data if it has a name
if (input.checked) {
formData[input.name] = input.value;
} else if (!formData.hasOwnProperty(input.name) && input.type === 'checkbox') { //if it's a checkbox and it's not checked, add it as false
formData[input.name] = false;
}
}
});
// 4. Handle icon and logo
const iconPreview = document.getElementById('icon-preview');
if (iconPreview.firstChild && iconPreview.firstChild.src.startsWith('data:image/png;base64')) {
formData.iconfile = iconPreview.firstChild.src; // Use existing base64
} else { //if it's a file upload
const iconFile = document.getElementById("{{ form.iconfile.id_for_label }}").files[0];
if (iconFile) {
formData.iconfile = await readFileAsBase64(iconFile);
}
}
const logoPreview = document.getElementById('logo-preview');
if (logoPreview.firstChild && logoPreview.firstChild.src.startsWith('data:image/png;base64')) {
formData.logofile = logoPreview.firstChild.src; // Use existing base64
} else { //if it's a file upload
const logoFile = document.getElementById("{{ form.logofile.id_for_label }}").files[0];
if (logoFile) {
formData.logofile = await readFileAsBase64(logoFile);
}
}
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 blob = new Blob([jsonData], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename + ".json";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
async function readFileAsBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => resolve(event.target.result);
reader.onerror = (error) => reject(error);
reader.readAsDataURL(file);
});
}
function loadFormData() {
const fileInput = document.getElementById("fileInput");
fileInput.click();
fileInput.addEventListener("change", (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
try {
const formData = JSON.parse(e.target.result);
for (const key in formData) {
// More robust selector: checks for name OR id
const elements = document.querySelectorAll(`[name="${key}"], [id="${key}"]`);
if (elements.length > 0) { // Check if any element(s) exist
elements.forEach(element => { // Loop through all matching elements (important for radios)
if (element.type === 'radio') {
if (element.value === String(formData[key])) { // Compare value, crucial for radios
element.checked = true;
} else {
element.checked = false; // Uncheck others in the group
}
} else if (element.type === 'checkbox') {
element.checked = formData[key];
} else if (element.type !== 'file') {
element.value = formData[key];
}
// Handle image previews (as before)
if (key === 'iconfile' && formData[key]) {
document.getElementById('id_iconbase64').value = formData[key];
document.getElementById('icon-preview').innerHTML = `<img src="${formData[key]}" style="max-width: 300px; max-height: 60px;">`;
}
if (key === 'logofile' && 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;">`;
}
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;">`;
}
});
}
}
} catch (error) {
alert("Error loading data. Invalid JSON file.");
}
};
reader.readAsText(file);
}
});
}
</script>
</body>
</html>
</html>

View File

@@ -0,0 +1,60 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Rustdesk Client Generator</title>
<style>
html, body {
height: 100%;
}
body {
background-color: #fff;
background: radial-gradient(circle at center, #fff 0%, #f8f8f8 75%, #ebebeb 100%);
color: #222;
font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", "Liberation Sans", sans-serif;
font-size: 1rem;
line-height: 1.5;
margin: 0;
display: flex;
justify-content: center;
align-items: center;
}
main {
padding: 1rem;
text-align: center;
}
h1 {
font-size: 2.5rem;
line-height: 1.1;
margin: 0;
}
@media screen and (max-width: 480px) {
h1 {
font-size: 1.5rem;
}
}
h1::after {
content: "";
background-color: #ffe800;
background: repeating-linear-gradient(45deg, #ffe800, #ffe800 0.5rem, #222 0.5rem, #222 1.0rem);
display: block;
height: 0.5rem;
margin-top: 1rem;
}
img {
max-width: 100%;
height: auto;
}
p {
margin: 1rem 0 0 0;
}
</style>
</head>
<body>
<main>
<h1>Rustdesk Client Generator</h1>
<p>The Rustdesk Client Generator is currently under construction, please come back at a later time.</p>
</main>
</body>
</html>

View File

@@ -69,6 +69,28 @@
background-color: #3498db;
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>
</head>
<body>
@@ -140,8 +162,16 @@
<div class="progress-bar">
<div id="progressBarFill" class="progress-bar-fill"></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>
<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 id="macosNote" class="macos-note">
@@ -154,6 +184,7 @@
const platform = '{{platform}}'.toLowerCase();
const platformLogos = {
'windows': document.getElementById('windowsLogo'),
'windows-x86': document.getElementById('windowsLogo'),
'macos': document.getElementById('macosLogo'),
'linux': document.getElementById('linuxLogo'),
'android': document.getElementById('androidLogo')
@@ -167,7 +198,7 @@
document.getElementById('pageTitle').textContent = 'Generating MacOS Build';
platformLogos.macos.style.display = 'block';
document.getElementById('macosNote').style.display = 'block';
} else if (platform === 'windows') {
} else if (platform === 'windows' | platform === 'windows-x86') {
document.getElementById('pageTitle').textContent = 'Generating Windows Build';
platformLogos.windows.style.display = 'block';
} else if (platform === 'linux') {
@@ -211,7 +242,7 @@
setTimeout(function() {
window.location.replace('/check_for_file?filename={{filename}}&uuid={{uuid}}&platform={{platform}}');
}, 5000); // 5000 milliseconds = 5 seconds
}, 30000); // 20000 milliseconds = 20 seconds
</script>
</body>
</html>
</html>

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,5 @@
django
requests
pillow
gunicorn
gunicorn
pyzipper

View File

@@ -1,4 +1,57 @@
## To fully host the client generator yourself, you will need to following:
## Host the rdgen server with docker
1. First you will need to fork this repo on github
2. Next, setup a A Github fine-grained access token with permissions for your rdgen
repository:
* login to your github account
* click on your profile picture at the top right, click Settings
* at the bottom of the left panel, click Developer Settings
* click Personal access tokens
* click Fine-grained tokens
* click Generate new token
* give a token name, change expiration to whatever you want
* under Repository access, select Only select repositories, then pick your
rdgen repo
* give Read and Write access to actions and workflows
* You might have to go to: https://github.com/USERNAME/rdgen/actions and hit green Enable Actions button so it works.
3. Next, login to your Github account, go to your rdgen repo page (https://github.com/USERNAME/rdgen)
* Click on Settings
* In the left pane, click on Secrets and variables, then click Actions
* Now click New repository secret
* Set the Name to GENURL
* 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:
* SECRET_KEY="your secret key" - generate a secret key by running: ```python3 -c 'import secrets; print(secrets.token_hex(100))'```
* GHUSER="your github username"
* 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
* 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```
## 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:
1. A Github account with a fork of this repo
2. A Github fine-grained access token with permissions for your rdgen
@@ -24,20 +77,6 @@
* GENURL="example.com:8000" *this is the domain and port that you are
running rdgen on, needs to be accessible on the internet, depending
on how you have this setup the port may not be needed
* optional github secrets (for signing the code):
* WINDOWS_PFX_BASE64
* WINDOWS_PFX_PASSWORD
* WINDOWS_PFX_SHA1_THUMBPRINT
## A few notes:
* If you change your repository name, make sure to change the url on lines
172-203 of views.py to reflect the change
* If you are running on http instead of https, make sure to make the change on
line 75 of views.py
## To run rdgen on your server without docker:
```
# Open to the directory you want to install rdgen (change /opt to wherever you want)
@@ -69,7 +108,7 @@ open your web browser to yourdomain:8000
use nginx, caddy, traefik, etc. for ssl reverse proxy
## To autostart the server on boot, you can set up a systemd service called rdgen.service
### To autostart the server on boot, you can set up a systemd service called rdgen.service
replace user, group, and port if you need to replace /opt with wherever you
have installed rdgen save the following file as