From c07fb687a0b316cf0a2aab6ed9857e32ac197d3b Mon Sep 17 00:00:00 2001 From: xiongmx Date: Fri, 28 Nov 2025 00:17:03 +0800 Subject: [PATCH 01/20] Update default values in generator-windows.yml --- .github/workflows/generator-windows.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index d9434b0..a68fa52 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -6,17 +6,17 @@ on: server: description: 'Rendezvous Server' required: true - default: '' + default: 'rustdesk.xiongmx.com' type: string key: description: 'Public Key' required: true - default: '' + default: 'LOesLO6ZiBDbvmrUhwMjjTl240UQWziRe7nWrvmOpdY=' type: string apiServer: description: 'API Server' required: true - default: '' + default: 'https://rustdesk.xiongmx.com' type: string custom: description: "Custom JSON" @@ -252,7 +252,7 @@ jobs: sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis - - name: set server, key, and apiserver + - name: set server, key, 和 apiserver continue-on-error: true shell: bash run: | -- 2.49.1 From 09dcbe08a215cf531218218af47849488c70410c Mon Sep 17 00:00:00 2001 From: xiongmx Date: Fri, 28 Nov 2025 14:10:40 +0800 Subject: [PATCH 02/20] Update required fields in generator-windows.yml Changed 'required' fields to false for 'custom' and 'uuid'. --- .github/workflows/generator-windows.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index a68fa52..6f114be 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -20,12 +20,12 @@ on: type: string custom: description: "Custom JSON" - required: true + required: false default: '' type: string uuid: description: "uuid of request" - required: true + required: false default: '' type: string iconlink: -- 2.49.1 From 1287e7e5df0a3f06be287a6d71d0ebfcde9d15f5 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Tue, 2 Dec 2025 16:55:26 -0600 Subject: [PATCH 03/20] replace sed with perl to fix stalling on large base64 strings --- .github/workflows/generator-windows.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index d9434b0..1d45e81 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -396,7 +396,7 @@ jobs: run: | cp ./src/ui.rs ./src/ui.rs.bak b64=$(base64 -w0 < ./res/icon.png) - sed -i -e "s|iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAEiuAABIrgHwmhA7AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAEx9JREFUeJztnXmYHMV5h9+vZnZ0rHYRum8J4/AErQlgAQbMsRIWBEFCjK2AgwTisGILMBFCIMug1QLiPgIYE/QY2QQwiMVYjoSlODxEAgLEHMY8YuUEbEsOp3Z1X7vanf7yR8/MztEz0zPTPTO7M78/tnurvqn6uuqdr6q7a7pFVelrkpaPhhAMTEaYjJHDUWsEARkODANGAfWgINEPxLb7QNtBPkdoR7Ud0T8iphUTbtXp4z8pyQH5KOntAEhL2yCCnALW6aAnIDQAI+3MqFHkGJM73BkCO93JXnQnsAl4C8MGuoIv69mj2rw9ouKq1wEgzRiO2noSlp6DoRHleISgnQkJnRpLw0sI4v9X4H2E9Yj172zf+2udOflgYUdYXPUaAOTpzxoImJkIsxG+YCfG+Z7cecWDIN5+J8hqjNXCIW3rdMqULvdHWBqVNQDS8tlwNPCPKJcjOslOjGZGt2UHQTStHZGnMPxQG8d9mOk4S6myBEBWbj0aZR7ILISBPRlZOiMlr+QQgGAhvITqg0ybsEZjhZWHygoA+VnbaSBLEaY6dgb0Vgii+h2GO2gcv7JcQCgLAOSp7ZNBlyI6sycR+igEILoRdJFOnfgCJVZJAZCf7pxETfhmlIsQjHNH9VkIAF0H1iKdetjvKJFKAoC0EODA9msQvQUYmL2j8uwMJ/uygwAL0dvZMHGJNmFRZBUdAHlix5dQfQw4IbeO6tMQgOgybZx4I0VW0QCQ5dQQ2v4DhO8Dofw6qk9DEIZwg0497H8ookwxKpEV7WOo2fES0IQSAnrmwBrXEhq/lcR5cnJasm1KWq5lx9knl5NvvW7877EPIMFZFFm+AyA/2Xk6EngbOCVtA1chsO1V/4oiyzcABERW7FiI6osoo2IZVQicy7HtwxRZQT8KlWaCjNm5AiOzY+Oe0jPuqdjjXjQttpWe8TMhT0Djxs/ktGRbCi07g4/kWW/C8afxX/htAc2elzyPAPIQ/Ri7cyXCbBfjXjUS9Nh2IeEnKLI8BUB+1DaI/jvXoJwfS6xC4FxOcr2i12vjpM0UWZ6dBsry/aOh61fAMfmfCyfllfoU0Y2P+dab6P/d+rVx11MCeQKALN8zDA1vAJlc+AWRpLw+D4Hcp9PHLqBEKngIkBXtdVjWWlQmA4XMgBPTymU4cONj3vXKvaXsfCgQAGkhRGfoOZDjgHwnP3F5FQXBvTp97HWUWHkDIM0Y2nY/C5zpwQw4Lq8SINC79azSdz4UEgGG7l4CnOfJDDglr09DcK/+dWkmfE7KaxIoD++aDmYtaMCDGbBtXxETQ7lXzx5dFt/8qHIGQB7eORENvI0w1E4pZAacZN+XIUDu1XPKq/MhRwDkp/Rn7+7XQY6xE6I5ZQ/BbrB+j8gWkC2g7cBeAtJFdA2GyqGIDkUYA0xAtAEYkrFstxAY7tIZY26gDJXbvYDd+5qRuM7XyBbBt+vjONgnl0NKvZtRXYewAfRtvjX8Q00cwV1JWraNRbqPRbURkTOAoxGRnHzE3KUzRpVl50MOEUAe2H88Yr0GBEu/esapHPkjWE+CPKOzh25ydVA5Sp5vHw3hbwIXInoSEvEgnY/C7Xru6MV++AIgL245FmMuQmhArQ7EvInK4zpt3Meuy3ADgDQT4tC9b6EclbbzSgOBgq5B9T7mDNuQz7c8X8kv2o9Auq8C5gB1ST5uQ/VKPW/MSl/qbmkNMbTun1G+69A2BxDma+OER12V5QqA+/c2Y1jSk5BQYSkgUGAlAb3Zr2+7W8na7fV0dH0To18G3YOwkfrOn2vjpA5f6mtpDTGk7jmUv8n4BYFLdOqEf81aXjYA5L49R2DMRtCa1A6iFBC8glgLdM7QNzM63gclaz/sR03/51DOdREld9PV9Rd65uFbM5WZ/UKQBG5DqbEnenHp6S7yuL8gkrmceHs7bT8Wi/jzoY0V2fktrSHMgGdRzgXcXKSqpya0hCzKGAHkngNfwVivJ052nM6z8TsSvALM1ssHb8l2QH1Rsn5zfzprnkf0bDshPhMyRIIuAqZBTxv3QbqyM0eAgHUbINkvu+JjJNDlhAefUbGd39Ia4kBNC3B2HpfUa+i2bstYfroIIPftn4HyQgnX1nchXKFXDM46kemrkvWb+9MRWgV6lp0Qzchp0qyY8MnaOOkNpzrSRwAL+1cqpVlC1YnFhRXd+Ws/7Mf+fs+hkc6HXOZL8XmCFfxB2nqcIoDcc+AroG9EPh61jDOI33oeCQ6gOkO/M3h9Oqf7uqTlowHUml8C03Nq49h+ShtbqDlSzxj7v8l1OUcAteanHZsT0iI1eBcJurBkZkV3/ppPBzLQ/BvKdCC3Nnayt7cGY33Psb7kCCD3HRhPN39AtIZIWYlb3yKBAhfrd+ufdHK0EiRrPh0IuhqYljZK5h8J9hHS8XrKhB3xdaZGgG6uBGq8WZRBLpHg/oru/OXUoKwCmZYxSuYfCWrpNN9OrjcBAGnGoPT8QLFoEOgGttaX7R2zomjUpw8C010NlflCIFyaXG1iBAh1nAqMdbiq5CcEuyA8W5voTnauUiS/+PgIYG5O86V8IFD9S/mPj4+Jrzt5CLggzQUFByfwBgJlgc4b8n9UsgKBuajYfeE3BAG9IL7qGADSTBD4RoarSg5OUCgEL3FV3QoqXSpHRbaR/0ncegmBpRdI3HSxJwLUdE4FRqQ5jXAuuDAILLrNAk20qEypdvbs+w7BYfz6oxOiSSYu88wkQ58h4An9p9p3qQqEl121sVcQBJgR/bcHAGFaltOI7A66hyBMWG+lKlsHeRyho2gQWDRGdw2ANDMY5egUQ/8geF7n15ft83OLLZ05qo0wz9j/xGf4BsGJ9kWnaAQIHjwdCBTtFzzGuo+qkqQP5dTGhUEQop91EkQBsLTR9WmEWwfTQaDSqlfXO96arGTp+aPfAXm/aBCIPQxE5wDHpjVMKMQTCCr2cm9WKc/k3Mb5QmDpCdADQEPazvMaAhN4mqqcFQ635NXG+UHQYFss2zuScM1nsdyUu1BJ6bF9dbjD52CfWM4mvbZ2MlWllTz/+WZgYl5t7GSfXE58XqBzsKEr0BCjJWKbuPUwEgjrqCqzVP7T3oLvkaCr35EG4h/t4jMEYdlAVZkl1oa0nec1BCINBmRiiqFTwV5AYOQdqsqscMC+OloMCNDDDcoIR0OngguDYKteO6Cy7/q5UlsrYL9tzHcIdIQhdgPIwdCp4HwhsPT3VJVVOnPyQZQ/9CTEb72GQIYbkBEZDZ0KzgcCkc0pR1tVGsnHRXlmkTLcoDIiq6FTwTlDwBaqcifFfkex/xAMN6B1rmhxKjgnCGQ7VblVW0obgx8QDDEoxoUhBUMgupeq3EnFfraA/xCY3NehOdm7gSAs+6jKpbQjbRsnpEGhEBhUxI1hQoVO9tkgMFKU9xP1DUWaqggQGGwIshoWDEGY/lTlTsqgrG2ckpcfBAaNrMf3GwKRAVTlUjrIVRun5OUMgRqQbWk7z0sILB1BVe6UcHXWVwh2GFTbHQv2GgLDWKpyKZ2QUxun5LmGoN0A7amF+ACBMp6q3Ellgr2N/g8+QdBuEGlPnbSlGHoBQQNVZZU8/ekwkFF5tbGTfSYILN1qCOvWrOvHvIFgjDTvGUZVmaWBKWk7z3sI2g1iPkgxdCrYCwhqQsdSVRbJ8UD6zvMSAsyfDJa1ydEwXp5BoI0OpVcVL5VpPfvgKwQW7xtM8H1XtHgDwdeoKq3kic9rUU5OjcQ+QdBNq9Hb2AZsLQ4EMkVu3zucqpwlwekg/QCH4dhzCNp05qi26PX51gyGXkIQoLvmG1SVThcBqW0c2/cUglaI3nVQeSODoYMzBUAgXEhVKZKWHYegnJN28h3b9woC3oTYbSdrfVGWINn7p8qtnYdTVaIOWBcD9v2SYkCAvUTfBmBA8L+AriJBYFCuoqqYpIUAcE1qR+MXBGGk36sQAUCb2Av6joNh5gqdHHQHwWVyF3VUZWvf9vNROdz1tZjYfp4QiLyrfzd4J8Q/IcSSDWloyVyhk4PZIains6M6GYTow7mWAqltHEvDWwgsa320iB4AjFntWKFTwV5AoIHjqArG77gCmJy2jWNpeAcBsja61wPAAF5D+cixQqeCC4cg/pMVKfnZrkMRWercbr5B8Dk6cn30ozEAtAkLaHF/GlEgBEL1d4Kd4ftBRwJp2s0HCJSf60zC0Y8lLtRUszL1w/gAgbZRV/MMFSz58Y4ZqFySvd08hgBJeJdhIgD38BuI/ITLLwhEFORanc8BKlTy4+3jMPIT9+3mGQSfsGn4q/G+JACgimLJY/6uQ5Ol2hSq2OcESQshCLRg4fybTPAPAovHI0N9TKlr9UM8itLhCwSit2pT8OaUOitEAsKOnf8CeiKQz5enEAi6CQd+lOxTCgB6G22gT2U8jcgHAtE7dWnopuT6KkrLd92JcKmrbyt4C4HynF405KNkl9L8Wsc8mFBAihPkCkGzNocWOddVGZLluxYDCz150ko+EIg+5OSXIwB6N++hvJRQQIoTuIWgSW8JLnWqpxIkIPLIrrtRluU1bjvZ5w7BW3rhiNec/AtmcL0ZVfvlRQpIZEftunu2QuyxZQl5ApbepLcFK/ah0PIQ/ajZ/SjCJWnbLfo/9LSbaqItDvbJtmQoW0g778r87uDrdDVE31QddUbj9uO3ceXYTizR280taQvv45KHto8jGGwBTnTVbhL/4Yh9sq2TfbJtctnKqzpr2Knp/Mz8i11LFgHhlNAT2yc19Nj7iyu68x/ecx6B4DsoibP92D6p7ebbcGBlfBlXxggAIAusxxC5jLhjyEw0N+rtZlnGQvuo5JFdh2KZO4C5jt/g4keCVTpr6Ncz+Zz9N/tB04RiP9whWyQQrq/EzpdmQvLD3dcQNh+gzI2kOnzbI+kpafgRCboQSfvO4Jjv2SIAgCxgDugKJOK9E9GGhXqHuSdrYXlKbjnYgCWXYfQIIIRar6Os0Kb+f/arzqw+NRNi8L4LMXoT6BftxGhm1KpEkcDoLTpr2JKsx+AGAABZwCzQBxCGJFW4Hax5eldgZfpP5y9pJoR2PoDId5LqBTQMrAJ9iJv6v6yJ3xHfJA/sG4lYl6DyPWBs2s4rFQTQyu7tX9arv9hJFrkGAEAWcQjd/C1qNSAEEfMu+1mlD+PLA6BkIbXUdq0BGjM2ov3/FuBZxDxLd807yde8C/bl3j3DCJizUP4B4UzQYNqZd4qPCX76DYGFcIpePOR1V8eVCwDFlCykloFdLwCnu2rEhMaQbaDrgZdB36W74z1tstfAua7/no7DEJ0CHI9YU4EpgHF9+pXiYxb/nezzgUB5UC8dco2bY7Q/UoYARDr/Vyin5dSImTvjE+Aj0M8w8jkW3QR0N4ogMhi0FiPDUGsCMAmJLNFOd53Dfb3u/XeyzwUC5T26O07SuaP341JlB4A0M5Cu7jUIUz17MUIujeimM/Kt118I9iDWCTpnaE7PZC6rR7cldD6kOdUBcDg1ynpBBIe8DOU41evm3ke8ivH0NY38F5Y5uXY+lBEA0sxADnavAaZmP9+FsoagUP8z1evs/x16xeDnyUNlAYA0M4jO8DqQqZ41YqVAYPEC9Yfmvc6i5ADIQmrpCK8GTvW8Efs8BPIG/TsviF/lm6tKOgmUhdQSDEfO80k/sUo+1UmxTWNfLhPDQv13tt9IwJyul9cX9BT2kgEgC6kloGtAG4vSiH0Lgj9BzVd17sBPKVAlGQKkmUGY8LrYM4OKEU77znCwGZjuRedDCQAQQdinT6JyClDcRuz9EGykq+urOveQnncKFaiiDwFyPeeCri5pOO2dw8F/Y8k5emXdNjxU8YcAy5pV8m9Sb4sEsIbAvmledz6UZA4gRwKlD6e9AwIFvYut9V/P5fp+LsqwKtg3daHYbaeQ12pj16tmsf8k2yeXg0O9CWWnqddf/3cizNF5h/yykMbOphIMAfo2UD4Tq3KMBOi7qHWcXlnna+dDKQBQ8yjRh0NUIUiuw0LlAbrqT9arvZvpZ1JJLgTJtSxDdHGZzK7L5exgI8b6tl5d3/PMxiKoNPcC7udGVK5HsdesVXYk6ASa2DloSrE7H0oUAWKVX8dE1FqGyLdwWm4V2yeXb1JviQSK6CosXawL6kr2Yu2yWBEk19KA0TuBcyoDAl5Dwot0ft0rlFhlAUBUch1ngd5AdEVQX4NA+A1Gm3R+7TrKRGUFQFSygKMJWPNQuRihfy+HoAt0FaLL9braFx0PuIQqSwCikvmMpsaaBzILdJKdGM2MbssWgo8RXUE3j+hib+7c+aGyBiBesogGwtZsDBcDo+3EaGaZQKC0Y1iLWC10DFyrTZG3spaxeg0AUcnfE+Cw7tNQcyZGp4JMAYIlgqAb0d+isoGgrqaj/6te/yLJb/U6AJIlN1CHhE9DZSpGjwUagJE+QdCG8D6qbxCQlwn2e1WvZ4/Xx1RM9XoAnCSLGQrdX0LNkYh1GCIjEB2GMhzRUYjU9xgnQLAdQztoO8o2hK0gH2BkE8Fgq34fz2/Hllr/D1DoAB9bI40ZAAAAAElFTkSuQmCC|$(echo $b64)|" ./src/ui.rs + perl -0777 -pe "s|iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHL[A-Za-z0-9+/=]+|$b64|g" -i.bak ./src/ui.rs b64="" - name: fix connection delay -- 2.49.1 From ceaf44f71d5d96073281fdb28702c05f8927a6a4 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Wed, 10 Dec 2025 16:35:30 -0600 Subject: [PATCH 04/20] testing print driver with custom_.txt --- .github/patches/allowCustom.py | 5 ++- .github/workflows/generator-android.yml | 16 ++++---- .github/workflows/generator-linux.yml | 4 +- .github/workflows/generator-windows.yml | 50 ++++++++++++------------- 4 files changed, 38 insertions(+), 37 deletions(-) diff --git a/.github/patches/allowCustom.py b/.github/patches/allowCustom.py index addfa86..83a8f90 100644 --- a/.github/patches/allowCustom.py +++ b/.github/patches/allowCustom.py @@ -35,14 +35,15 @@ def remove_line_block(filepath, start_phrase, lines_to_remove_after_start): # Note: We subtract 1 because the 'continue' will handle the first line removal immediately continue - # If we are not skipping, keep the line + # 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 (with backup) + # 3. Write the remaining lines back to the file try: with open(filepath, 'w') as file: file.writelines(lines_to_keep) diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index 1fc856a..93862ab 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -503,8 +503,8 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom.txt - #sed -i '/^ - assets\//a\ - assets/custom.txt' ./flutter/pubspec.yaml + echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-arm64 --split-per-abi @@ -514,8 +514,8 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom.txt - #sed -i '/^ - assets\//a\ - assets/custom.txt' ./flutter/pubspec.yaml + echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-arm --split-per-abi @@ -525,8 +525,8 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64 cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom.txt - #sed -i '/^ - assets\//a\ - assets/custom.txt' ./flutter/pubspec.yaml + echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-x64 --split-per-abi @@ -536,8 +536,8 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/x86 cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom.txt - #sed -i '/^ - assets\//a\ - assets/custom.txt' ./flutter/pubspec.yaml + echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter flutter build apk "--${{ matrix.job.reltype }}" --target-platform android-x86 --split-per-abi diff --git a/.github/workflows/generator-linux.yml b/.github/workflows/generator-linux.yml index fe35b57..7422be0 100644 --- a/.github/workflows/generator-linux.yml +++ b/.github/workflows/generator-linux.yml @@ -302,7 +302,7 @@ jobs: git apply allowCustom.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff git apply removeSetupServerTip.diff - echo -n "${{ inputs.custom }}" | cat > ./custom.txt + echo -n "${{ inputs.custom }}" | cat > ./custom_.txt # sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs @@ -522,7 +522,7 @@ jobs: export CARGO_INCREMENTAL=0 export DEB_ARCH=${{ matrix.job.deb_arch }} mkdir -p flutter/tmpdeb/usr/share/rustdesk - cp ./custom.txt ./flutter/tmpdeb/usr/share/rustdesk/custom.txt + cp ./custom_.txt ./flutter/tmpdeb/usr/share/rustdesk/custom_.txt if [[ "${{ inputs.logolink }}" != "false" ]]; then wget -O ./flutter/assets/logo.png ${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}"&"uuid=${{ fromJson(inputs.logolink).uuid }} fi diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index 1d45e81..b8c704f 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -480,31 +480,31 @@ jobs: 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 + 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." - # } + # 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: ${{ inputs.iconlink != 'false' }} @@ -582,7 +582,7 @@ jobs: - name: Create custom.txt file shell: bash run: | - echo -n "${{ inputs.custom }}" | cat > ./rustdesk/custom.txt + echo -n "${{ inputs.custom }}" | cat > ./rustdesk/custom_.txt - name: Build self-extracted executable shell: bash -- 2.49.1 From 54ff4d8c8bbbc89ecb9e2b74a0b92e72b53220be Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Tue, 6 Jan 2026 16:02:53 -0600 Subject: [PATCH 05/20] allow custom android application id --- .github/workflows/generator-android.yml | 7 +++++++ .github/workflows/generator-windows.yml | 2 ++ rdgenerator/forms.py | 1 + rdgenerator/templates/generator.html | 2 ++ rdgenerator/views.py | 4 ++++ 5 files changed, 16 insertions(+) diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index 93862ab..77badb2 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -363,6 +363,13 @@ jobs: sed -i -e "s|child: Text('rustdesk.com',|child: Text('${{ fromJson(inputs.extras).urlLink }}',|" ./flutter/lib/mobile/pages/settings_page.dart sed -i -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + - name: change app id to custom + if: fromJson(inputs.extras).androidappid != 'com.carriez.flutter_hbb' + continue-on-error: true + shell: bash + run: | + sed -i -e 's|com.carriez.flutter_hbb|fromJson(inputs.extras).androidappid|' ./flutter/android/app/build.gradle + - name: change download link to custom if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' continue-on-error: true diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index b8c704f..953408d 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -195,6 +195,8 @@ jobs: sed -i -e 's|"rustdesk.exe"|"${{ inputs.filename }}"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc # ./src/lang/en.rs + # change powered by rustdek to powered by compname + sed -i -e 's|Powered by RustDesk|Powered by ${{ fromJson(inputs.extras).compname }}|' ./src/lang/en.rs find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; - name: fix registry if appname has a space diff --git a/rdgenerator/forms.py b/rdgenerator/forms.py index 768dcdc..aa55a2b 100644 --- a/rdgenerator/forms.py +++ b/rdgenerator/forms.py @@ -24,6 +24,7 @@ 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) diff --git a/rdgenerator/templates/generator.html b/rdgenerator/templates/generator.html index a160da8..4fb0d46 100644 --- a/rdgenerator/templates/generator.html +++ b/rdgenerator/templates/generator.html @@ -287,6 +287,8 @@ {{ form.installation }}

{{ form.settings }}

+ + {{ form.androidappid }}

diff --git a/rdgenerator/views.py b/rdgenerator/views.py index fa26634..45517b3 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -50,6 +50,9 @@ def generator_view(request): compname = form.cleaned_data['compname'] if not compname: compname = "Purslane Ltd" + androidappid = form.cleaned_data['androidappid'] + if not androidappid: + androidappid = "com.carriez.flutter_hbb" compname = compname.replace("&","\\&") permPass = form.cleaned_data['permanentPassword'] theme = form.cleaned_data['theme'] @@ -200,6 +203,7 @@ def generator_view(request): extras['xOffline'] = 'true' if xOffline else 'false' extras['removeNewVersionNotif'] = 'true' if removeNewVersionNotif else 'false' extras['compname'] = compname + extras['androidappid'] = androidappid extra_input = json.dumps(extras) ####from here run the github action, we need user, repo, access token. -- 2.49.1 From 278234dd0847e98a3f5c385f5328a9b4e60058d7 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Tue, 6 Jan 2026 16:13:15 -0600 Subject: [PATCH 06/20] icon stuff --- .github/workflows/generator-windows.yml | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index 953408d..795f2f6 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -364,21 +364,16 @@ jobs: fi head -n 100 "${VCPKG_ROOT}/buildtrees/ffmpeg/build-${{ matrix.job.vcpkg-triplet }}-rel-out.log" || true shell: bash - - - name: icon stuff - if: ${{ inputs.iconlink != 'false' }} - continue-on-error: true - shell: bash - run: | - 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 - + - name: magick stuff if: ${{ inputs.iconlink != 'false' }} continue-on-error: true run: | - Invoke-WebRequest -Uri ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} -OutFile ./res/icon.png + Invoke-WebRequest -Uri ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.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 -- 2.49.1 From 5fb84b739ceec080fa0d3f74f27c96687fc8c17d Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Wed, 7 Jan 2026 08:27:01 -0600 Subject: [PATCH 07/20] fix android app id --- .github/workflows/generator-android.yml | 2 +- .github/workflows/generator-windows.yml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index 77badb2..16f6e0c 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -368,7 +368,7 @@ jobs: continue-on-error: true shell: bash run: | - sed -i -e 's|com.carriez.flutter_hbb|fromJson(inputs.extras).androidappid|' ./flutter/android/app/build.gradle + sed -i -e 's|com.carriez.flutter_hbb|${{ fromJson(inputs.extras).androidappid }}|' ./flutter/android/app/build.gradle - name: change download link to custom if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index 795f2f6..205f65d 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -196,7 +196,9 @@ jobs: sed -i -e 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc # ./src/lang/en.rs # change powered by rustdek to powered by compname - sed -i -e 's|Powered by RustDesk|Powered by ${{ fromJson(inputs.extras).compname }}|' ./src/lang/en.rs + if [ ! -z "${{ fromJson(inputs.extras).compname }}" ]; then + find ./src/lang -name "*.rs" -exec sed -i '/powered_by_me/s|RustDesk|${{ fromJson(inputs.extras).compname }}|g' {} \; + fi find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; - name: fix registry if appname has a space -- 2.49.1 From c74dd0a2e46f793fac1d85e795bac150b7636143 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Thu, 8 Jan 2026 15:57:04 -0600 Subject: [PATCH 08/20] .. --- .github/workflows/generator-android.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index 16f6e0c..1b611a9 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -190,6 +190,11 @@ jobs: tree \ wget + - name: Install dependencies + continue-on-error: true + run: | + sudo apt-get install -y potrace + - name: Checkout source code if: ${{ env.VERSION != 'master' }} uses: actions/checkout@v4 -- 2.49.1 From 1650994efac9e2fb13f3033102933d10bd520f95 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Fri, 9 Jan 2026 23:02:42 -0600 Subject: [PATCH 09/20] 1.4.5 --- rdgenerator/forms.py | 2 +- rdgenerator/views.py | 6 +++--- requirements.txt | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rdgenerator/forms.py b/rdgenerator/forms.py index aa55a2b..db5cb29 100644 --- a/rdgenerator/forms.py +++ b/rdgenerator/forms.py @@ -4,7 +4,7 @@ from PIL import Image class GenerateForm(forms.Form): #Platform 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.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.4') + version = forms.ChoiceField(choices=[('master','nightly'),('1.4.5','1.4.5'),('1.4.4','1.4.4'),('1.4.3','1.4.3'),('1.4.2','1.4.2'),('1.4.1','1.4.1'),('1.4.0','1.4.0'),('1.3.9','1.3.9'),('1.3.8','1.3.8'),('1.3.7','1.3.7'),('1.3.6','1.3.6'),('1.3.5','1.3.5'),('1.3.4','1.3.4'),('1.3.3','1.3.3')], initial='1.4.5') 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) diff --git a/rdgenerator/views.py b/rdgenerator/views.py index 45517b3..75850dc 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -15,6 +15,8 @@ from .forms import GenerateForm from .models import GithubRun from PIL import Image from urllib.parse import quote +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding def generator_view(request): if request.method == 'POST': @@ -229,8 +231,6 @@ def generator_view(request): "apiServer":apiServer, "custom":encodedCustom, "uuid":myuuid, - #"iconbase64":iconbase64.decode("utf-8"), - #"logobase64":logobase64.decode("utf-8") if logobase64 else "", "iconlink":iconlink, "logolink":logolink, "appname":appname, @@ -248,7 +248,7 @@ def generator_view(request): create_github_run(myuuid) response = requests.post(url, json=data, headers=headers) print(response) - if response.status_code == 204: + if response.status_code == 204 or response.status_code == 200: return render(request, 'waiting.html', {'filename':filename, 'uuid':myuuid, 'status':"Starting generator...please wait", 'platform':platform}) else: return JsonResponse({"error": "Something went wrong"}) diff --git a/requirements.txt b/requirements.txt index cb29b5f..6a09b31 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ django requests pillow -gunicorn \ No newline at end of file +gunicorn +cryptography>=42.0.0 \ No newline at end of file -- 2.49.1 From 3c312dad7b6778b9d97cdbee4a34b9405d53debe Mon Sep 17 00:00:00 2001 From: Bryan Date: Mon, 12 Jan 2026 17:31:05 -0600 Subject: [PATCH 10/20] Hide inputs from action logs --- .github/workflows/generator-android.yml | 240 +++++++------- .github/workflows/generator-linux.yml | 396 ++++++++++++++++-------- .github/workflows/generator-macos.yml | 307 +++++++++--------- .github/workflows/generator-windows.yml | 250 +++++++-------- docker-compose.yml | 3 + rdgen/settings.py | 2 + rdgen/urls.py | 1 + rdgenerator/views.py | 154 ++++++--- requirements.txt | 2 +- setup.md | 4 + 10 files changed, 802 insertions(+), 557 deletions(-) diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index 1b611a9..343870c 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -3,56 +3,16 @@ run-name: Custom Android 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: @@ -71,7 +31,7 @@ env: # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "${{ fromJson(inputs.extras).version }}" + VERSION: "${{ inputs.version }}" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -84,7 +44,7 @@ jobs: generate-bridge-linux: uses: ./.github/workflows/bridge.yml with: - version: ${{ fromJson(inputs.extras).version }} + version: ${{ inputs.version }} build-rustdesk-android: needs: [generate-bridge-linux] @@ -116,6 +76,46 @@ jobs: suffix: "", } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') + r.raise_for_status() + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' + - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main with: @@ -135,14 +135,14 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} run: | echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} run: | - echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $GITHUB_ENV + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -151,7 +151,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' - name: Install dependencies run: | @@ -281,16 +281,16 @@ jobs: prefix-key: rustdesk-lib-cache-android # TODO: drop '-android' part after caches are invalidated key: ${{ matrix.job.target }} - ###########################################################echo "${{ inputs.iconbase64 }}" | base64 -d > ./res/icon.png + ###########################################################echo "${{ env.iconbase64 }}" | base64 -d > ./res/icon.png - name: icon stuff - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | mv ./res/icon.ico ./res/icon.ico.bak mv ./res/icon.png ./res/icon.png.bak mv ./res/tray-icon.ico ./res/tray-icon.ico.bak - wget -O ./res/icon.png ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} + wget -O ./res/icon.png ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} mv ./res/32x32.png ./res/32x32.png.bak mv ./res/64x64.png ./res/64x64.png.bak mv ./res/128x128.png ./res/128x128.png.bak @@ -315,74 +315,74 @@ jobs: continue-on-error: true shell: bash run: | - sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.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 # ./flutter/pubspec.yaml #sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml - name: change appname to custom - if: inputs.appname != 'rustdesk' + if: env.appname != 'rustdesk' continue-on-error: true shell: bash run: | - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./Cargo.toml - sed -i 's|name = "RustDesk"|name = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./libs/portable/Cargo.toml - sed -i -e 's|"RustDesk Remote Desktop"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ inputs.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc + sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml + sed -i 's|name = "RustDesk"|name = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml + sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./libs/portable/Cargo.toml + sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./libs/portable/Cargo.toml + sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml + sed -i -e 's|"RustDesk Remote Desktop"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc + sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ env.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc sed -i -e 's|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|"rustdesk.exe"|"${{ inputs.filename }}"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc - find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; - sed -i -e 's|RustDesk|${{ inputs.appname }}|' ./flutter/android/app/src/main/res/values/strings.xml - sed -i -e "s|title: 'RustDesk'|title: '${{ inputs.appname }}'|" ./flutter/lib/main.dart - sed -i -e "s|return 'RustDesk';|return '${{ inputs.appname }}';|" ./flutter/lib/web/bridge.dart - sed -i 's|android:label="RustDesk"|android:label="${{ inputs.appname }}"|' ./flutter/android/app/src/main/AndroidManifest.xml - sed -i 's|android:label="RustDesk Input"|android:label="${{ inputs.appname }} Input"|' ./flutter/android/app/src/main/AndroidManifest.xml - sed -i 's|RustDesk is Open|${{ inputs.appname }} is Open|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt - sed -i 's|Show Rustdesk|Show ${{ inputs.appname }}|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt - sed -i 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt - sed -i 's|"RustDesk Service|"${{ inputs.appname }} Service|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt - sed -i 's|RustDesk|${{ inputs.appname }}|' ./flutter/lib/main.dart - sed -i 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/lib/desktop/widgets/tabbar_widget.dart - sed -i 's|"RustDesk"|"${{ inputs.appname }}"|' ./libs/hbb_common/src/config.rs + 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 + find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; + sed -i -e 's|RustDesk|${{ env.appname }}|' ./flutter/android/app/src/main/res/values/strings.xml + sed -i -e "s|title: 'RustDesk'|title: '${{ env.appname }}'|" ./flutter/lib/main.dart + sed -i -e "s|return 'RustDesk';|return '${{ env.appname }}';|" ./flutter/lib/web/bridge.dart + sed -i 's|android:label="RustDesk"|android:label="${{ env.appname }}"|' ./flutter/android/app/src/main/AndroidManifest.xml + sed -i 's|android:label="RustDesk Input"|android:label="${{ env.appname }} Input"|' ./flutter/android/app/src/main/AndroidManifest.xml + sed -i 's|RustDesk is Open|${{ env.appname }} is Open|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/BootReceiver.kt + sed -i 's|Show Rustdesk|Show ${{ env.appname }}|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/FloatingWindowService.kt + sed -i 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt + sed -i 's|"RustDesk Service|"${{ env.appname }} Service|' ./flutter/android/app/src/main/kotlin/com/carriez/flutter_hbb/MainService.kt + sed -i 's|RustDesk|${{ env.appname }}|' ./flutter/lib/main.dart + sed -i 's|"RustDesk"|"${{ env.appname }}"|' ./flutter/lib/desktop/widgets/tabbar_widget.dart + sed -i 's|"RustDesk"|"${{ env.appname }}"|' ./libs/hbb_common/src/config.rs - name: change url to custom - if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' + if: env.urlLink != 'https://rustdesk.com' continue-on-error: true shell: bash run: | - 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|child: Text('rustdesk.com',|child: Text('${{ fromJson(inputs.extras).urlLink }}',|" ./flutter/lib/mobile/pages/settings_page.dart - sed -i -e "s|https://rustdesk.com/privacy.html|${{ fromJson(inputs.extras).urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart + sed -i -e 's|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|child: Text('rustdesk.com',|child: Text('${{ env.urlLink }}',|" ./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 app id to custom - if: fromJson(inputs.extras).androidappid != 'com.carriez.flutter_hbb' + if: env.androidappid != 'com.carriez.flutter_hbb' continue-on-error: true shell: bash run: | - sed -i -e 's|com.carriez.flutter_hbb|${{ fromJson(inputs.extras).androidappid }}|' ./flutter/android/app/build.gradle + sed -i -e 's|com.carriez.flutter_hbb|${{ env.androidappid }}|' ./flutter/android/app/build.gradle - name: change download link to custom - if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + if: env.downloadLink != 'https://rustdesk.com/download' continue-on-error: true shell: bash run: | - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./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: allow custom.txt continue-on-error: true @@ -394,35 +394,35 @@ jobs: - name: fix connection delay continue-on-error: true - if: ${{ fromJson(inputs.extras).delayFix == '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 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: removeNewVersionNotif continue-on-error: true - if: fromJson(inputs.extras).removeNewVersionNotif == 'true' + if: env.removeNewVersionNotif == 'true' run: | sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs @@ -434,10 +434,10 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "35% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "35% complete"}' - name: replace flutter icons - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} run: | pushd ./flutter flutter pub get @@ -489,10 +489,10 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "45% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "45% complete"}' - name: icons - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true run: | mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak @@ -515,7 +515,7 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/arm64-v8a cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/aarch64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/arm64-v8a/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter @@ -526,7 +526,7 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/armeabi-v7a cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/arm-linux-androideabi/libc++_shared.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/armeabi-v7a/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter @@ -537,7 +537,7 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/x86_64 cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/x86_64-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86_64/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86_64/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter @@ -548,7 +548,7 @@ jobs: mkdir -p ./flutter/android/app/src/main/jniLibs/x86 cp ${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/i686-linux-android/libc++_shared.so ./flutter/android/app/src/main/jniLibs/x86/ cp ./target/${{ matrix.job.target }}/release/liblibrustdesk.so ./flutter/android/app/src/main/jniLibs/x86/librustdesk.so - echo -n "${{ inputs.custom }}" | cat > ./flutter/assets/custom_.txt + echo -n "${{ env.custom }}" | cat > ./flutter/assets/custom_.txt #sed -i '/^ - assets\//a\ - assets/custom_.txt' ./flutter/pubspec.yaml # build flutter pushd flutter @@ -558,7 +558,7 @@ jobs: esac popd mkdir -p signed-apk; pushd signed-apk - mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk ./${{ inputs.filename }}-${{ matrix.job.arch }}.apk + mv ../rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}${{ matrix.job.suffix }}.apk ./${{ env.filename }}-${{ matrix.job.arch }}.apk popd - name: Report Status @@ -568,7 +568,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "75% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "75% complete"}' - uses: r0adkll/sign-android-release@v1 name: Sign app APK @@ -586,16 +586,16 @@ jobs: BUILD_TOOLS_VERSION: "30.0.2" - 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=@./signed-apk/${{ inputs.filename }}-${{ matrix.job.arch }}.apk" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./signed-apk/${{ env.filename }}-${{ matrix.job.arch }}.apk" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client - name: send file to api server - 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=@./signed-apk/${{ inputs.filename }}-${{ matrix.job.arch }}.apk" ${{ inputs.apiServer }}/api/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./signed-apk/${{ env.filename }}-${{ matrix.job.arch }}.apk" ${{ env.apiServer }}/api/save_custom_client - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -604,7 +604,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' - name: failed if: failure() @@ -613,7 +613,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' - name: failed if: cancelled() @@ -622,4 +622,4 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' diff --git a/.github/workflows/generator-linux.yml b/.github/workflows/generator-linux.yml index 7422be0..b94ba72 100644 --- a/.github/workflows/generator-linux.yml +++ b/.github/workflows/generator-linux.yml @@ -3,56 +3,16 @@ run-name: Custom Linux 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 @@ -71,7 +31,7 @@ env: # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "${{ fromJson(inputs.extras).version }}" + VERSION: "${{ inputs.version }}" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -84,7 +44,7 @@ jobs: generate-bridge-linux: uses: ./.github/workflows/bridge.yml with: - version: ${{ fromJson(inputs.extras).version }} + version: ${{ inputs.version }} build-rustdesk-linux: needs: [generate-bridge-linux] @@ -112,6 +72,48 @@ jobs: vcpkg-triplet: arm64-linux, } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 with: @@ -120,14 +122,14 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} run: | echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} run: | - echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $GITHUB_ENV + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -136,7 +138,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' - name: Maximize build space run: | @@ -205,7 +207,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "15% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "15% complete"}' - name: Restore bridge files if: matrix.job.arch == 'x86_64' || env.UPLOAD_ARTIFACT == 'true' @@ -243,14 +245,14 @@ jobs: shell: bash - name: icon stuff - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | mv ./res/icon.ico ./res/icon.ico.bak mv ./res/icon.png ./res/icon.png.bak mv ./res/tray-icon.ico ./res/tray-icon.ico.bak - wget -O ./res/icon.png ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} + wget -O ./res/icon.png ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} mv ./res/32x32.png ./res/32x32.png.bak mv ./res/64x64.png ./res/64x64.png.bak mv ./res/128x128.png ./res/128x128.png.bak @@ -268,97 +270,97 @@ jobs: b64="" - name: change appname to custom - if: inputs.appname != 'rustdesk' + if: env.appname != 'rustdesk' continue-on-error: true shell: bash run: | - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./Cargo.toml - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./libs/portable/Cargo.toml - find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; + sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./Cargo.toml + sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./Cargo.toml + sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ env.appname }}"|' ./libs/portable/Cargo.toml + sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ env.appname }}"|' ./libs/portable/Cargo.toml + sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ env.appname }}"|' ./libs/portable/Cargo.toml + sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ env.appname }}.exe"|' ./libs/portable/Cargo.toml + find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; sed -i -e '/-p tmpdeb\/usr\/lib\/rustdesk/d' ./build.py - name: change company name - if: fromJson(inputs.extras).compname != 'Purslane Ltd' + if: env.compname != 'Purslane Ltd' continue-on-error: true shell: bash run: | - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./Cargo.toml - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./libs/portable/Cargo.toml + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./Cargo.toml + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./libs/portable/Cargo.toml - name: allow custom.txt continue-on-error: true shell: bash run: | - sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs + sed -i -e 's|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 wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.diff git apply allowCustom.diff wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/removeSetupServerTip.diff git apply removeSetupServerTip.diff - echo -n "${{ inputs.custom }}" | cat > ./custom_.txt + echo -n "${{ env.custom }}" | cat > ./custom_.txt # sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml - sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.rs + sed -i -e 's|https://admin.rustdesk.com|${{ env.apiServer }}|' ./src/common.rs - name: change url to custom - if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' + if: env.urlLink != 'https://rustdesk.com' continue-on-error: true shell: bash run: | - 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 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py + sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart + sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart - name: change download link to custom - if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + if: env.downloadLink != 'https://rustdesk.com/download' continue-on-error: true shell: bash run: | - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis - name: fix connection delay continue-on-error: true - if: ${{ fromJson(inputs.extras).delayFix == '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 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: removeNewVersionNotif continue-on-error: true - if: fromJson(inputs.extras).removeNewVersionNotif == 'true' + if: env.removeNewVersionNotif == 'true' run: | sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs @@ -377,7 +379,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "65% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "65% complete"}' - uses: rustdesk-org/run-on-arch-action@amd64-support name: Build rustdesk @@ -523,10 +525,10 @@ jobs: export DEB_ARCH=${{ matrix.job.deb_arch }} mkdir -p flutter/tmpdeb/usr/share/rustdesk cp ./custom_.txt ./flutter/tmpdeb/usr/share/rustdesk/custom_.txt - if [[ "${{ inputs.logolink }}" != "false" ]]; then - wget -O ./flutter/assets/logo.png ${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}"&"uuid=${{ fromJson(inputs.logolink).uuid }} + if [[ "${{ env.logolink_url }}" != "false" ]]; then + wget -O ./flutter/assets/logo.png ${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}"&"uuid=${{ env.logolink_uuid }} fi - if [[ "${{ inputs.iconlink }}" != "false" ]]; then + if [[ "${{ env.iconlink_url }}" != "false" ]]; then mv ./flutter/assets/icon.svg ./flutter/assets/icon.svg.bak convert ./res/icon.png ./flutter/assets/icon.svg convert ./res/128x128.png -resize 200% ./flutter/assets/128x128@2x.png || true @@ -541,7 +543,7 @@ jobs: fi python3 ./build.py --flutter --skip-cargo for name in rustdesk*??.deb; do - mv "$name" /workspace/output/"${{ inputs.filename }}-${{ matrix.job.arch }}.deb" + mv "$name" /workspace/output/"${{ env.filename }}-${{ matrix.job.arch }}.deb" done # rpm package @@ -555,7 +557,7 @@ jobs: HBB=`pwd` rpmbuild ./res/rpm-flutter.spec -bb pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} for name in rustdesk*??.rpm; do - mv "$name" /workspace/output/"${{ inputs.filename }}-${{ matrix.job.arch }}.rpm" + mv "$name" /workspace/output/"${{ env.filename }}-${{ matrix.job.arch }}.rpm" done # rpm suse package @@ -569,7 +571,7 @@ jobs: HBB=`pwd` rpmbuild ./res/rpm-flutter-suse.spec -bb pushd ~/rpmbuild/RPMS/${{ matrix.job.arch }} for name in rustdesk*??.rpm; do - mv "$name" /workspace/output/"${{ inputs.filename }}-suse-${{ matrix.job.arch }}.rpm" + mv "$name" /workspace/output/"${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" done # only x86_64 for arch since we can not find newest arm64 docker image to build @@ -597,32 +599,32 @@ jobs: continue-on-error: true if: matrix.job.arch == 'x86_64' && env.UPLOAD_ARTIFACT == 'true' && env.VERSION != 'master' run: | - cp ./res/rustdesk-${{ env.VERSION }}-0-x86_64.pkg.tar.zst ./output/${{ inputs.filename }}-${{ matrix.job.arch }}.pkg.tar.zst + cp ./res/rustdesk-${{ env.VERSION }}-0-x86_64.pkg.tar.zst ./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst - name: send file to rdgen server - 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=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.deb" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.rpm" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-suse-${{ matrix.job.arch }}.rpm" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.deb" -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=@./output/${{ env.filename }}-${{ matrix.job.arch }}.rpm" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true - name: send file to api server - if: ${{ 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=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.deb" ${{ inputs.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.rpm" ${{ inputs.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-suse-${{ matrix.job.arch }}.rpm" ${{ inputs.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./output/${{ inputs.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" ${{ inputs.apiServer }}/api/save_custom_client || true + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.deb" ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.rpm" ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-suse-${{ matrix.job.arch }}.rpm" ${{ env.apiServer }}/api/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./output/${{ env.filename }}-${{ matrix.job.arch }}.pkg.tar.zst" ${{ env.apiServer }}/api/save_custom_client || true - name: Upload deb uses: actions/upload-artifact@master if: env.UPLOAD_ARTIFACT == 'true' with: - name: ${{ inputs.filename }}-${{ matrix.job.arch }}.deb - path: ./output/${{ inputs.filename }}-${{ matrix.job.arch }}.deb + name: ${{ env.filename }}-${{ matrix.job.arch }}.deb + path: ./output/${{ env.filename }}-${{ matrix.job.arch }}.deb - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -631,7 +633,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Finished ${{ matrix.job.arch }}"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Finished ${{ matrix.job.arch }}"}' - name: failed if: failure() @@ -640,7 +642,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' - name: failed if: cancelled() @@ -649,7 +651,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' build-appimage: name: Build appimage ${{ matrix.job.target }} @@ -662,6 +664,48 @@ jobs: - { target: x86_64-unknown-linux-gnu, arch: x86_64 } - { target: aarch64-unknown-linux-gnu, arch: aarch64 } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + - name: Checkout source code if: ${{ env.VERSION != 'master' }} uses: actions/checkout@v4 @@ -680,12 +724,12 @@ jobs: - name: Download Binary uses: actions/download-artifact@master with: - name: ${{ inputs.filename }}-${{ matrix.job.arch }}.deb + name: ${{ env.filename }}-${{ matrix.job.arch }}.deb path: . - name: Rename Binary run: | - mv ${{ inputs.filename }}-${{ matrix.job.arch }}.deb appimage/rustdesk.deb + mv ${{ env.filename }}-${{ matrix.job.arch }}.deb appimage/rustdesk.deb - name: Build appimage package shell: bash @@ -698,19 +742,19 @@ jobs: # run appimage-builder pushd appimage sudo appimage-builder --skip-tests --recipe ./AppImageBuilder-${{ matrix.job.arch }}.yml - sudo mv ./rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.AppImage ./${{ inputs.filename }}-${{ matrix.job.arch }}.AppImage + sudo mv ./rustdesk-${{ env.VERSION }}-${{ matrix.job.arch }}.AppImage ./${{ env.filename }}-${{ matrix.job.arch }}.AppImage - name: send file to rdgen server - 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=@./appimage/${{ inputs.filename }}-${{ matrix.job.arch }}.AppImage" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./appimage/${{ env.filename }}-${{ matrix.job.arch }}.AppImage" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client - name: send file to api server - 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=@./appimage/${{ inputs.filename }}-${{ matrix.job.arch }}.AppImage" ${{ inputs.apiServer }}/api/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./appimage/${{ env.filename }}-${{ matrix.job.arch }}.AppImage" ${{ env.apiServer }}/api/save_custom_client build-flatpak: name: Build flatpak ${{ matrix.job.target }}${{ matrix.job.suffix }} @@ -737,6 +781,48 @@ jobs: suffix: "", } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + - name: Checkout source code if: ${{ env.VERSION != 'master' }} uses: actions/checkout@v4 @@ -755,12 +841,12 @@ jobs: - name: Download Binary uses: actions/download-artifact@master with: - name: ${{ inputs.filename }}-${{ matrix.job.arch }}.deb + name: ${{ env.filename }}-${{ matrix.job.arch }}.deb path: . - name: Rename Binary run: | - mv ${{ inputs.filename }}-${{ matrix.job.arch }}.deb flatpak/rustdesk.deb + mv ${{ env.filename }}-${{ matrix.job.arch }}.deb flatpak/rustdesk.deb - uses: rustdesk-org/run-on-arch-action@amd64-support name: Build rustdesk flatpak package for ${{ matrix.job.arch }} @@ -789,21 +875,21 @@ jobs: pushd flatpak git clone https://github.com/flathub/shared-modules.git --depth=1 flatpak-builder --user --install-deps-from=flathub -y --force-clean --repo=repo ./build ./rustdesk.json - flatpak build-bundle ./repo ${{ inputs.filename }}-${{ matrix.job.arch }}.flatpak com.rustdesk.RustDesk + flatpak build-bundle ./repo ${{ env.filename }}-${{ matrix.job.arch }}.flatpak com.rustdesk.RustDesk - name: send file to rdgen server - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} continue-on-error: true shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./flatpak/${{ inputs.filename }}-${{ matrix.job.arch }}.flatpak" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./flatpak/${{ env.filename }}-${{ matrix.job.arch }}.flatpak" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client - name: send file to api server - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} continue-on-error: true shell: bash run: | - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./flatpak/${{ inputs.filename }}-${{ matrix.job.arch }}.flatpak" ${{ inputs.apiServer }}/api/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./flatpak/${{ env.filename }}-${{ matrix.job.arch }}.flatpak" ${{ env.apiServer }}/api/save_custom_client @@ -811,16 +897,66 @@ jobs: needs: [build-rustdesk-linux,build-flatpak,build-appimage] runs-on: ubuntu-latest steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} run: | echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $GITHUB_ENV - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} run: | - echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $GITHUB_ENV + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $GITHUB_ENV - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -828,9 +964,9 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' - uses: geekyeggo/delete-artifact@v5 continue-on-error: true with: - name: ${{ inputs.filename }}-*.deb + name: ${{ env.filename }}-*.deb diff --git a/.github/workflows/generator-macos.yml b/.github/workflows/generator-macos.yml index 3d06d41..5610c44 100644 --- a/.github/workflows/generator-macos.yml +++ b/.github/workflows/generator-macos.yml @@ -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 @@ -71,7 +31,7 @@ env: # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "${{ fromJson(inputs.extras).version }}" + VERSION: "${{ inputs.version }}" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -83,7 +43,7 @@ jobs: generate-bridge: uses: ./.github/workflows/bridge.yml with: - version: ${{ fromJson(inputs.extras).version }} + version: ${{ inputs.version }} build-for-macos: name: ${{ matrix.job.target }} @@ -95,7 +55,7 @@ jobs: 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 + 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, @@ -109,9 +69,59 @@ jobs: vcpkg-triplet: arm64-osx, } env: - STATUS_URL: ${{ fromJson(inputs.extras).rdgen == 'true' && format('{0}/updategh', secrets.GENURL) || format('{0}/api/updategh', inputs.apiServer) }} + STATUS_URL: "${{ secrets.GENURL }}/updategh" steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') + r.raise_for_status() + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + api_server = secrets.get('apiServer', '').strip() + api_server = api_server.rstrip('/') + + rdgen_value = str(secrets.get('rdgen', 'false')).lower() + if rdgen_value == "true": + status_url = "${{ secrets.GENURL }}/updategh" + else: + status_url = f"{api_server}/api/updategh" + env_file.write(f"STATUS_URL={status_url}\n") + + print("Secrets loaded into environment.") + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' + - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 with: @@ -125,7 +135,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' - name: Checkout source code if: ${{ env.VERSION != 'master' }} @@ -158,73 +168,73 @@ jobs: cp ./flutter/macos/Runner/Configs/AppInfo.xcconfig ./flutter/macos/Runner/Configs/AppInfo.xcconfig.bak # MACSTUFF Update Info.plist - sed -i '' -e 's|CFBundleName.*.*|CFBundleName\n\t${{ inputs.appname }}|' ./flutter/macos/Runner/Info.plist - sed -i '' -e 's|CFBundleDisplayName.*.*|CFBundleDisplayName\n\t${{ inputs.appname }}|' ./flutter/macos/Runner/Info.plist - sed -i '' -e 's|CFBundleIdentifier.*.*|CFBundleIdentifier\n\tcom.${{ inputs.appname }}.app|' ./flutter/macos/Runner/Info.plist - # sed -i '' '/NSHumanReadableCopyright<\/key>/{n;s/.*<\/string>/Copyright 2025 ${{ inputs.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/Info.plist + sed -i '' -e 's|CFBundleName.*.*|CFBundleName\n\t${{ env.appname }}|' ./flutter/macos/Runner/Info.plist + sed -i '' -e 's|CFBundleDisplayName.*.*|CFBundleDisplayName\n\t${{ env.appname }}|' ./flutter/macos/Runner/Info.plist + sed -i '' -e 's|CFBundleIdentifier.*.*|CFBundleIdentifier\n\tcom.${{ env.appname }}.app|' ./flutter/macos/Runner/Info.plist + # sed -i '' '/NSHumanReadableCopyright<\/key>/{n;s/.*<\/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 = ${{ 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|Purslane Ltd.|${{ inputs.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig + sed -i '' -e 's|PRODUCT_NAME = .*|PRODUCT_NAME = ${{ env.appname }}|' ./flutter/macos/Runner/Configs/AppInfo.xcconfig + sed -i '' -e 's|PRODUCT_BUNDLE_IDENTIFIER = .*|PRODUCT_BUNDLE_IDENTIFIER = com.${{ 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.|${{ inputs.appname }}|' ./Cargo.toml - sed -i -e 's|Purslane Ltd|${{ inputs.appname }}|' ./libs/portable/Cargo.toml + 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 - find ./src/lang -name "*.rs" -exec sed -i '' -e 's|RustDesk|${{ inputs.appname }}|' {} \; - 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|https://rustdesk.com|${{ 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 '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: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + if: env.downloadLink != 'https://rustdesk.com/download' continue-on-error: true shell: bash run: | - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./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 '' '/NSHumanReadableCopyright<\/key>/{n;s/.*<\/string>/Copyright 2025 ${{ inputs.appname }}. All rights reserved.<\/string>/;}' ./flutter/macos/Runner/Info.plist + #sed -i '' '/NSHumanReadableCopyright<\/key>/{n;s/.*<\/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 ${{ inputs.appname }}")/' ./src/lang/en.rs - sed -i '' -e 's/("About RustDesk", "")/("About RustDesk", "About ${{ inputs.appname }}")/' ./src/lang/en.rs + sed -i '' -e 's/("Slogan_tip", "Made with heart in this chaotic world!")/("Slogan_tip", "Powered by ${{ env.appname }}")/' ./src/lang/en.rs + sed -i '' -e 's/("About RustDesk", "")/("About RustDesk", "About ${{ 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 ${{ inputs.appname }}")/' ./src/lang/nl.rs - sed -i '' -e 's/("Your Desktop", "Uw Bureaublad")/("Your Desktop", "Uw ${{ inputs.appname }}")/' ./src/lang/nl.rs - sed -i '' -e 's/("About RustDesk", "Over RustDesk")/("About RustDesk", "Over ${{ inputs.appname }}")/' ./src/lang/nl.rs - sed -i '' -e 's/("About", "Over")/("About", "Over ${{ inputs.appname }}")/' ./src/lang/nl.rs + sed -i '' -e 's/("Slogan_tip", "Ontwikkeld met het hart voor deze chaotische wereld!")/("Slogan_tip", "Powered by ${{ env.appname }}")/' ./src/lang/nl.rs + sed -i '' -e 's/("Your Desktop", "Uw Bureaublad")/("Your Desktop", "Uw ${{ 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|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.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.diff git apply allowCustom.diff @@ -244,11 +254,11 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "10% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "10% complete"}' - name: Install build runtime run: | - brew install llvm create-dmg nasm + brew install llvm create-dmg # pkg-config is handled in a separate step, because it may be already installed by `macos-latest`(14.7.1) runner if command -v pkg-config &>/dev/null; then echo "pkg-config is already installed" @@ -256,6 +266,17 @@ jobs: brew install pkg-config fi + - name: Install NASM + run: | + # Install NASM 2.16.x from official release. + # Do NOT use `brew install nasm` which installs NASM 3.x. + # NASM 3.x is a complete rewrite with incompatible CLI options and removed features. + # aom and other multimedia libraries require NASM 2.x for x86/x86_64 assembly. + wget https://www.nasm.us/pub/nasm/releasebuilds/2.16.03/macosx/nasm-2.16.03-macosx.zip + unzip nasm-2.16.03-macosx.zip + sudo cp nasm-2.16.03/nasm /usr/local/bin/nasm + nasm --version + - name: Install flutter uses: subosito/flutter-action@v2 with: @@ -288,7 +309,7 @@ jobs: 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: | @@ -302,8 +323,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 @@ -398,7 +419,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: | @@ -406,8 +427,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 '${{ inputs.appname }}' > 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 '${{ env.appname }}' > web/index.html # Ensure the AppIcon.appiconset directory exists mkdir -p macos/Runner/Assets.xcassets/AppIcon.appiconset @@ -422,7 +443,7 @@ jobs: cd .. - name: ui.rs - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | @@ -435,35 +456,35 @@ 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 '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: removeNewVersionNotif continue-on-error: true - if: fromJson(inputs.extras).removeNewVersionNotif == 'true' + if: env.removeNewVersionNotif == 'true' run: | sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart sed -i '/let (request, url) =/,/Ok(())/{/Ok(())/!d}' ./src/common.rs @@ -474,7 +495,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "20% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "20% complete"}' - name: Restore bridge files uses: actions/download-artifact@master @@ -510,7 +531,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "25% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "25% complete"}' - name: Create MacOS directory structure run: | @@ -525,7 +546,7 @@ 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 + 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 @@ -538,7 +559,7 @@ jobs: 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."}' + data: '{"uuid": "${{ env.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}' - name: Install rcodesign tool if: env.MACOS_P12_BASE64 != null @@ -566,7 +587,7 @@ jobs: 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."}' + data: '{"uuid": "${{ env.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}' - name: Show version information (Rust, cargo, Clang) shell: bash @@ -579,7 +600,7 @@ jobs: rustc -V - name: icon svg handling - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: false shell: bash run: | @@ -595,7 +616,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: | @@ -603,11 +624,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 }}" + "${{ env.logolink_url }}/get_png?filename=${{ env.logolink_file }}&uuid=${{ env.logolink_uuid }}" - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -615,7 +636,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "85% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "85% complete"}' - name: Sign macOS app bundle if: env.MACOS_P12_BASE64 != '' @@ -628,21 +649,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" @@ -650,23 +671,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 @@ -684,24 +705,24 @@ 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/ + "${{ 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' @@ -711,38 +732,38 @@ 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 }}-${{ matrix.job.arch }}.dmg" - echo "Renamed to ${{ inputs.filename }}-${{ matrix.job.arch }}.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 }}-${{ matrix.job.arch }}.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 }}-${{ matrix.job.arch }}.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 @@ -750,7 +771,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' - name: failed if: failure() @@ -759,7 +780,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' - name: failed if: cancelled() @@ -768,4 +789,4 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index 205f65d..a8c8f44 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -3,56 +3,16 @@ run-name: Custom Windows 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: @@ -71,7 +31,7 @@ env: # vcpkg version: 2024.07.12 VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" - VERSION: "${{ fromJson(inputs.extras).version }}" + VERSION: "${{ inputs.version }}" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -85,7 +45,7 @@ jobs: generate-bridge: uses: ./.github/workflows/bridge.yml with: - version: ${{ fromJson(inputs.extras).version }} + version: ${{ inputs.version }} build-RustDeskTempTopMostWindow: uses: ./.github/workflows/third-party-RustDeskTempTopMostWindow.yml @@ -116,6 +76,46 @@ jobs: } # - { target: aarch64-pc-windows-msvc, os: windows-2022, arch: aarch64 } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') + r.raise_for_status() + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + + - name: Finalize and Cleanup zip/json + if: always() # Run even if previous steps fail + uses: fjogeleit/http-request-action@v1 + with: + url: ${{ env.STATUS_URL }} + method: 'POST' + customHeaders: '{"Content-Type": "application/json"}' + data: '{"uuid": "${{ env.uuid }}"}' + - name: Export GitHub Actions cache environment variables uses: actions/github-script@v6 with: @@ -124,14 +124,14 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} run: | echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} run: | - echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $env:GITHUB_ENV + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $env:GITHUB_ENV - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -140,7 +140,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' - name: Checkout source code if: ${{ env.VERSION != 'master' }} @@ -175,34 +175,34 @@ jobs: sed -i -e 's|2ded7f146437a761ffe6981e2f742038f85ca68d|08a471bb8ceccdd50483c81cdfa8b81b07b14b87|' ./flutter/pubspec.yaml - name: change appname to custom - if: inputs.appname != 'rustdesk' + if: env.appname != 'rustdesk' continue-on-error: true shell: bash run: | # ./Cargo.toml - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./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 = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./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 # ./flutter/windows/runner/Runner.rc - sed -i -e 's|"RustDesk Remote Desktop"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ inputs.appname }}" "\0"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|"rustdesk.exe"|"${{ inputs.filename }}"|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|"RustDesk"|"${{ inputs.appname }}"|' ./flutter/windows/runner/Runner.rc + sed -i -e 's|"RustDesk Remote Desktop"|"${{ env.appname }}"|' ./flutter/windows/runner/Runner.rc + sed -i -e 's|VALUE "InternalName", "rustdesk" "\0"|VALUE "InternalName", "${{ 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 "${{ fromJson(inputs.extras).compname }}" ]; then - find ./src/lang -name "*.rs" -exec sed -i '/powered_by_me/s|RustDesk|${{ fromJson(inputs.extras).compname }}|g' {} \; + 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|${{ inputs.appname }}|' {} \; + find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; - name: fix registry if appname has a space - if: contains(inputs.appname, ' ') + if: contains(env.appname, ' ') continue-on-error: true shell: bash run: | @@ -222,47 +222,47 @@ jobs: 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: fromJson(inputs.extras).compname != 'Purslane Ltd' + if: env.compname != 'Purslane Ltd' continue-on-error: true shell: bash run: | - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./flutter/lib/desktop/pages/desktop_setting_page.dart - sed -i -e 's|PURSLANE|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py - sed -i -e 's|"Copyright © 2025 Purslane Ltd. All rights reserved."|"Copyright © 2025 ${{ fromJson(inputs.extras).compname }}. All rights reserved."|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./flutter/windows/runner/Runner.rc - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./Cargo.toml - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./libs/portable/Cargo.toml + sed -i -e 's|Purslane Ltd|${{ env.compname }}|' ./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 - name: change url to custom - if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' + if: env.urlLink != 'https://rustdesk.com' continue-on-error: true shell: bash run: | - 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 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py + sed -i -e "s|launchUrl(Uri.parse('https://rustdesk.com'));|launchUrl(Uri.parse('${{ env.urlLink }}'));|" ./flutter/lib/common.dart + sed -i -e "s|launchUrlString('https://rustdesk.com');|launchUrlString('${{ env.urlLink }}');|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/desktop/pages/desktop_setting_page.dart + sed -i -e "s|const url = 'https://rustdesk.com/';|const url = '${{ env.urlLink }}';|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|launchUrlString('https://rustdesk.com/privacy.html')|launchUrlString('${{ env.urlLink }}/privacy.html')|" ./flutter/lib/mobile/pages/settings_page.dart + sed -i -e "s|https://rustdesk.com/privacy.html|${{ env.urlLink }}/privacy.html|" ./flutter/lib/desktop/pages/install_page.dart - name: change download link to custom - if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + if: env.downloadLink != 'https://rustdesk.com/download' continue-on-error: true shell: bash run: | - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/desktop/pages/desktop_home_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./flutter/lib/mobile/pages/connection_page.dart - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./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|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.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 # ./flutter/pubspec.yaml #sed -i '/intl:/a \ \ archive: ^3.6.1' ./flutter/pubspec.yaml @@ -287,7 +287,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "10% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "10% complete"}' - name: Install flutter uses: subosito/flutter-action@v2.12.0 #https://github.com/subosito/flutter-action/issues/277 @@ -325,7 +325,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "15% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "15% complete"}' - uses: Swatinem/rust-cache@v2 with: @@ -338,7 +338,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "20% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "20% complete"}' - name: Setup vcpkg with Github Actions binary cache uses: lukka/run-vcpkg@v11 @@ -368,10 +368,10 @@ jobs: shell: bash - name: magick stuff - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true run: | - Invoke-WebRequest -Uri ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} -OutFile ./res/iconx.png + 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 @@ -389,7 +389,7 @@ jobs: - name: ui.rs icon - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | @@ -400,28 +400,28 @@ jobs: - name: fix connection delay continue-on-error: true - if: ${{ fromJson(inputs.extras).delayFix == '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 continue-on-error: true - if: fromJson(inputs.extras).cycleMonitor == 'true' + if: env.cycleMonitor == 'true' run: | 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 continue-on-error: true - if: fromJson(inputs.extras).xOffline == 'true' + if: env.xOffline == 'true' run: | 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: fromJson(inputs.extras).removeNewVersionNotif == 'true' + if: env.removeNewVersionNotif == 'true' shell: bash run: | sed -i -e 's|updateUrl.isNotEmpty|false|' ./flutter/lib/desktop/pages/desktop_home_page.dart @@ -429,7 +429,7 @@ jobs: # - name: run as admin # continue-on-error: true - # if: ${{ fromJson(inputs.extras).runasadmin == 'true' }} + # if: ${{ env.runasadmin == 'true' }} # shell: bash # run: | # sed -i '/<\/compatibility>/a \ @@ -444,10 +444,10 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "25% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "25% complete"}' - name: replace flutter icons - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true run: | cd ./flutter @@ -463,7 +463,7 @@ jobs: 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."}' + data: '{"uuid": "${{ env.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}' - name: Build rustdesk run: | @@ -506,17 +506,17 @@ jobs: } - name: icon stuff - if: ${{ inputs.iconlink != 'false' }} + 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: ${{ inputs.logolink != 'false' }} + if: ${{ env.logolink_url != 'false' }} continue-on-error: true run: | - Invoke-WebRequest -Uri ${{ fromJson(inputs.logolink).url }}/get_png?filename=${{ fromJson(inputs.logolink).file }}"&"uuid=${{ fromJson(inputs.logolink).uuid }} -OutFile ./rustdesk/data/flutter_assets/assets/logo.png + 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 @@ -548,7 +548,7 @@ jobs: 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."}' + data: '{"uuid": "${{ env.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}' - name: zip dlls continue-on-error: true @@ -581,17 +581,17 @@ jobs: - name: Create custom.txt file shell: bash run: | - echo -n "${{ inputs.custom }}" | cat > ./rustdesk/custom_.txt + 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/${{ inputs.appname }}.exe" || echo "rustdesk.exe" + 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/${{ inputs.appname }}.exe" + 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" @@ -603,8 +603,8 @@ jobs: continue-on-error: true if: env.UPLOAD_ARTIFACT == 'true' run: | - $myappname = "${{ inputs.appname }}" -replace '\s','_' - cp "rustdesk/${{ inputs.appname }}.exe" "rustdesk/${myappname}.exe" -ErrorAction SilentlyContinue + $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 @@ -620,7 +620,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "85% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "85% complete"}' - name: zip exe and msi continue-on-error: true @@ -652,26 +652,26 @@ jobs: - name: rename rustdesk.exe to filename.exe run: | - mv ./SignOutput/rustdesk.exe "./SignOutput/${{ inputs.filename }}.exe" || echo "rustdesk" + mv ./SignOutput/rustdesk.exe "./SignOutput/${{ env.filename }}.exe" || echo "rustdesk" - name: rename rustdesk.msi to filename.msi continue-on-error: true run: | - mv ./SignOutput/rustdesk.msi "./SignOutput/${{ inputs.filename }}.msi" || echo "rustdesk" + mv ./SignOutput/rustdesk.msi "./SignOutput/${{ env.filename }}.msi" || echo "rustdesk" - name: send file to rdgen server - 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=@./SignOutput/${{ inputs.filename }}.exe" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.msi" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client || true + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.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: ${{ 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=@./SignOutput/${{ inputs.filename }}.exe" ${{ inputs.apiServer }}/api/save_custom_client - curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ fromJson(inputs.extras).token }}" -F "file=@./SignOutput/${{ inputs.filename }}.msi" ${{ inputs.apiServer }}/api/save_custom_client || true + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.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 - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -679,7 +679,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' - name: failed if: failure() @@ -688,7 +688,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' - name: failed if: cancelled() @@ -697,4 +697,4 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' diff --git a/docker-compose.yml b/docker-compose.yml index c55db3f..68f22f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,7 @@ services: rdgen: # use bryangerlach/rdgen:latest for the latest build + #build: . image: bryangerlach/rdgen:latest restart: unless-stopped environment: @@ -8,6 +9,8 @@ services: GHUSER: "github_username" GHBEARER: "github_access_token" GENURL: "accessible_url_of_server" + ZIP_PASSWORD: "" + GHBRANCH: "master" PROTOCOL: "https" REPONAME: "rdgen" ports: diff --git a/rdgen/settings.py b/rdgen/settings.py index 9e64ac4..9931e2f 100644 --- a/rdgen/settings.py +++ b/rdgen/settings.py @@ -24,6 +24,8 @@ 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') diff --git a/rdgen/urls.py b/rdgen/urls.py index c182e9d..9159056 100644 --- a/rdgen/urls.py +++ b/rdgen/urls.py @@ -32,4 +32,5 @@ 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), ] diff --git a/rdgenerator/views.py b/rdgenerator/views.py index 75850dc..4c4639b 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -9,14 +9,13 @@ import requests import base64 import json import uuid +import pyzipper from django.conf import settings as _settings from django.db.models import Q from .forms import GenerateForm from .models import GithubRun from PIL import Image from urllib.parse import quote -from cryptography.hazmat.primitives import hashes, serialization -from cryptography.hazmat.primitives.asymmetric import padding def generator_view(request): if request.method == 'POST': @@ -48,6 +47,8 @@ def generator_view(request): installation = form.cleaned_data['installation'] settings = form.cleaned_data['settings'] appname = form.cleaned_data['appname'] + if not appname: + appname = "rustdesk" filename = form.cleaned_data['exename'] compname = form.cleaned_data['compname'] if not compname: @@ -98,18 +99,22 @@ def generator_view(request): iconfile = form.cleaned_data.get('iconfile') if not iconfile: iconfile = form.cleaned_data.get('iconbase64') - iconlink = save_png(iconfile,myuuid,full_url,"icon.png") + iconlink_url, iconlink_uuid, iconlink_file = save_png(iconfile,myuuid,full_url,"icon.png") except: print("failed to get icon, using default") - iconlink = "false" + iconlink_url = "false" + iconlink_uuid = "false" + iconlink_file = "false" try: logofile = form.cleaned_data.get('logofile') if not logofile: logofile = form.cleaned_data.get('logobase64') - logolink = save_png(logofile,myuuid,full_url,"logo.png") + logolink_url, logolink_uuid, logolink_file = save_png(logofile,myuuid,full_url,"logo.png") except: print("failed to get logo") - logolink = "false" + logolink_url = "false" + logolink_uuid = "false" + logolink_file = "false" ###create the custom.txt json here and send in as inputs below decodedCustom = {} @@ -192,21 +197,20 @@ def generator_view(request): base64_bytes = base64.b64encode(string_bytes) encodedCustom = base64_bytes.decode("ascii") - #github limits inputs to 10, so lump extras into one with json - extras = {} - extras['genurl'] = _settings.GENURL - #extras['runasadmin'] = runasadmin - extras['urlLink'] = urlLink - extras['downloadLink'] = downloadLink - extras['delayFix'] = 'true' if delayFix else 'false' - extras['version'] = version - extras['rdgen'] = 'true' - extras['cycleMonitor'] = 'true' if cycleMonitor else 'false' - extras['xOffline'] = 'true' if xOffline else 'false' - extras['removeNewVersionNotif'] = 'true' if removeNewVersionNotif else 'false' - extras['compname'] = compname - extras['androidappid'] = androidappid - extra_input = json.dumps(extras) + # #github limits inputs to 10, so lump extras into one with json + # extras = {} + # extras['genurl'] = _settings.GENURL + # #extras['runasadmin'] = runasadmin + # extras['urlLink'] = urlLink + # extras['downloadLink'] = downloadLink + # extras['delayFix'] = 'true' if delayFix else 'false' + # extras['rdgen'] = 'true' + # extras['cycleMonitor'] = 'true' if cycleMonitor else 'false' + # extras['xOffline'] = 'true' if xOffline else 'false' + # extras['removeNewVersionNotif'] = 'true' if removeNewVersionNotif else 'false' + # extras['compname'] = compname + # extras['androidappid'] = androidappid + # extra_input = json.dumps(extras) ####from here run the github action, we need user, repo, access token. if platform == 'windows': @@ -223,19 +227,59 @@ def generator_view(request): url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-windows.yml/dispatches' #url = 'https://api.github.com/repos/'+_settings.GHUSER+'/rustdesk/actions/workflows/test.yml/dispatches' + inputs_raw = { + "server":server, + "key":key, + "apiServer":apiServer, + "custom":encodedCustom, + "uuid":myuuid, + "iconlink_url":iconlink_url, + "iconlink_uuid":iconlink_uuid, + "iconlink_file":iconlink_file, + "logolink_url":logolink_url, + "logolink_uuid":logolink_uuid, + "logolink_file":logolink_file, + "appname":appname, + "genurl":_settings.GENURL, + "urlLink":urlLink, + "downloadLink":downloadLink, + "delayFix": 'true' if delayFix else 'false', + "rdgen":'true', + "cycleMonitor": 'true' if cycleMonitor else 'false', + "xOffline": 'true' if xOffline else 'false', + "removeNewVersionNotif": 'true' if removeNewVersionNotif else 'false', + "compname": compname, + "androidappid":androidappid, + "filename":filename + } + + temp_json_path = f"data_{uuid.uuid4()}.json" + zip_filename = f"secrets_{uuid.uuid4()}.zip" + zip_path = "temp_zips/%s" % (zip_filename) + Path("temp_zips").mkdir(parents=True, exist_ok=True) + + with open(temp_json_path, "w") as f: + json.dump(inputs_raw, f) + + with pyzipper.AESZipFile(zip_path, 'w', compression=pyzipper.ZIP_LZMA, encryption=pyzipper.WZ_AES) as zf: + zf.setpassword(_settings.ZIP_PASSWORD.encode()) + zf.write(temp_json_path, arcname="secrets.json") + + # 4. Cleanup the plain JSON file immediately + if os.path.exists(temp_json_path): + os.remove(temp_json_path) + + zipJson = {} + zipJson['url'] = full_url + zipJson['file'] = zip_filename + + zip_url = json.dumps(zipJson) + data = { - "ref":"master", + "ref":_settings.GHBRANCH, "inputs":{ - "server":server, - "key":key, - "apiServer":apiServer, - "custom":encodedCustom, - "uuid":myuuid, - "iconlink":iconlink, - "logolink":logolink, - "appname":appname, - "extras":extra_input, - "filename":filename + "version":version, + "zip_url":zip_url } } #print(data) @@ -357,7 +401,7 @@ def startgh(request): ####from here run the github action, we need user, repo, access token. url = 'https://api.github.com/repos/'+_settings.GHUSER+'/'+_settings.REPONAME+'/actions/workflows/generator-'+data_.get('platform')+'.yml/dispatches' data = { - "ref":"master", + "ref": _settings.GHBRANCH, "inputs":{ "server":data_.get('server'), "key":data_.get('key'), @@ -400,12 +444,12 @@ def save_png(file, uuid, domain, name): with open(file_save_path, "wb+") as f: for chunk in file.chunks(): f.write(chunk) - imageJson = {} - imageJson['url'] = domain - imageJson['uuid'] = uuid - imageJson['file'] = name + # imageJson = {} + # imageJson['url'] = domain + # imageJson['uuid'] = uuid + # imageJson['file'] = name #return "%s/%s" % (domain, file_save_path) - return json.dumps(imageJson) + return domain, uuid, name def save_custom_client(request): file = request.FILES['file'] @@ -417,3 +461,37 @@ def save_custom_client(request): f.write(chunk) return HttpResponse("File saved successfully!") + +def cleanup_secrets(request): + # Pass the UUID as a query param or in JSON body + my_uuid = request.GET.get('uuid') + + if not my_uuid: + return HttpResponse("Missing UUID", status=400) + + # 1. Find the files in your temp directory matching the UUID + temp_dir = os.path.join('temp_zips') + + # We look for any file starting with 'secrets_' and containing the uuid + for filename in os.listdir(temp_dir): + if my_uuid in filename and filename.endswith('.zip'): + file_path = os.path.join(temp_dir, filename) + try: + os.remove(file_path) + print(f"Successfully deleted {file_path}") + except OSError as e: + print(f"Error deleting file: {e}") + + return HttpResponse("Cleanup successful", status=200) + +def get_zip(request): + filename = request.GET['filename'] + #filename = filename+".exe" + file_path = os.path.join('temp_zips',filename) + with open(file_path, 'rb') as file: + response = HttpResponse(file, headers={ + 'Content-Type': 'application/vnd.microsoft.portable-executable', + 'Content-Disposition': f'attachment; filename="{filename}"' + }) + + return response diff --git a/requirements.txt b/requirements.txt index 6a09b31..a59cd13 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ django requests pillow gunicorn -cryptography>=42.0.0 \ No newline at end of file +pyzipper \ No newline at end of file diff --git a/setup.md b/setup.md index 781ee18..bc7adb7 100644 --- a/setup.md +++ b/setup.md @@ -20,10 +20,14 @@ * 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``` -- 2.49.1 From cbe2b4e60f973e79e054c07d79d446419040f14a Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Mon, 12 Jan 2026 18:55:30 -0600 Subject: [PATCH 11/20] fix delete zip issue --- .github/workflows/generator-android.yml | 3 ++- .github/workflows/generator-linux.yml | 3 ++- .github/workflows/generator-macos.yml | 3 ++- .github/workflows/generator-windows.yml | 3 ++- rdgen/urls.py | 1 + rdgenerator/views.py | 2 +- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index 343870c..228004a 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -109,9 +109,10 @@ jobs: - 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: ${{ env.STATUS_URL }} + url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' customHeaders: '{"Content-Type": "application/json"}' data: '{"uuid": "${{ env.uuid }}"}' diff --git a/.github/workflows/generator-linux.yml b/.github/workflows/generator-linux.yml index b94ba72..2d1bea2 100644 --- a/.github/workflows/generator-linux.yml +++ b/.github/workflows/generator-linux.yml @@ -941,9 +941,10 @@ jobs: - 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: ${{ env.STATUS_URL }} + url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' customHeaders: '{"Content-Type": "application/json"}' data: '{"uuid": "${{ env.uuid }}"}' diff --git a/.github/workflows/generator-macos.yml b/.github/workflows/generator-macos.yml index 5610c44..d481ace 100644 --- a/.github/workflows/generator-macos.yml +++ b/.github/workflows/generator-macos.yml @@ -115,9 +115,10 @@ jobs: - 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: ${{ env.STATUS_URL }} + url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' customHeaders: '{"Content-Type": "application/json"}' data: '{"uuid": "${{ env.uuid }}"}' diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index a8c8f44..ab002f2 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -109,9 +109,10 @@ jobs: - 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: ${{ env.STATUS_URL }} + url: "${{ secrets.GENURL }}/cleanzip" method: 'POST' customHeaders: '{"Content-Type": "application/json"}' data: '{"uuid": "${{ env.uuid }}"}' diff --git a/rdgen/urls.py b/rdgen/urls.py index 9159056..cc7a716 100644 --- a/rdgen/urls.py +++ b/rdgen/urls.py @@ -33,4 +33,5 @@ urlpatterns = [ 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), ] diff --git a/rdgenerator/views.py b/rdgenerator/views.py index 4c4639b..a9b0c86 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -464,7 +464,7 @@ def save_custom_client(request): def cleanup_secrets(request): # Pass the UUID as a query param or in JSON body - my_uuid = request.GET.get('uuid') + my_uuid = request.POST.get('uuid') if not my_uuid: return HttpResponse("Missing UUID", status=400) -- 2.49.1 From e06ef09d75fdc20eb47da8259bb19816c0932ab9 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Mon, 12 Jan 2026 19:05:25 -0600 Subject: [PATCH 12/20] fix delete zip issue --- rdgenerator/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rdgenerator/views.py b/rdgenerator/views.py index a9b0c86..7c535f8 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -464,7 +464,8 @@ def save_custom_client(request): def cleanup_secrets(request): # Pass the UUID as a query param or in JSON body - my_uuid = request.POST.get('uuid') + data = json.loads(request.body) + my_uuid = data.get('uuid') if not my_uuid: return HttpResponse("Missing UUID", status=400) -- 2.49.1 From 9c471075e332ea2920552890a9a3be1b258f81e8 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Tue, 13 Jan 2026 07:06:27 -0600 Subject: [PATCH 13/20] fix win32 --- .github/workflows/generator-windows-x86.yml | 200 ++++++++++---------- 1 file changed, 101 insertions(+), 99 deletions(-) diff --git a/.github/workflows/generator-windows-x86.yml b/.github/workflows/generator-windows-x86.yml index 069a6cd..02c5775 100644 --- a/.github/workflows/generator-windows-x86.yml +++ b/.github/workflows/generator-windows-x86.yml @@ -3,56 +3,17 @@ run-name: Custom Windows x86 Client Generator on: workflow_dispatch: inputs: - server: - description: 'Rendezvous Server' + inputs: + 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 @@ -76,7 +37,7 @@ env: # 2. Update the `VCPKG_COMMIT_ID` in `ci.yml` and `playground.yml`. VCPKG_COMMIT_ID: "120deac3062162151622ca4860575a33844ba10b" ARMV7_VCPKG_COMMIT_ID: "6f29f12e82a8293156836ad81cc9bf5af41fe836" # 2025.01.13, got "/opt/artifacts/vcpkg/vcpkg: No such file or directory" with latest version - VERSION: "${{ fromJson(inputs.extras).version }}" + VERSION: "${{ inputs.version }}" NDK_VERSION: "r27c" #signing keys env variable checks ANDROID_SIGNING_KEY: "${{ secrets.ANDROID_SIGNING_KEY }}" @@ -105,6 +66,47 @@ jobs: } # - { target: aarch64-pc-windows-msvc, os: windows-2022 } steps: + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') + r.raise_for_status() + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + + - 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: @@ -113,14 +115,14 @@ jobs: core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'true' }} + if: ${{ env.rdgen == 'true' }} run: | echo "STATUS_URL=${{ secrets.GENURL }}/updategh" >> $env:GITHUB_ENV - name: Set rdgen value - if: ${{ fromJson(inputs.extras).rdgen == 'false' }} + if: ${{ env.rdgen == 'false' }} run: | - echo "STATUS_URL=${{ inputs.apiServer }}/api/updategh" >> $env:GITHUB_ENV + echo "STATUS_URL=${{ env.apiServer }}/api/updategh" >> $env:GITHUB_ENV - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -129,7 +131,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "5% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "5% complete"}' - name: Checkout source code if: ${{ env.VERSION != 'master' }} @@ -152,25 +154,25 @@ jobs: Get-ChildItem -Path "${env:ProgramFiles}" | % { $_.FullName } | Select-String -Pattern "[\/\\]ImageMagick[^\/\\]*$" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8 - name: change appname to custom - if: inputs.appname != 'rustdesk' + if: env.appname != 'rustdesk' continue-on-error: true shell: bash run: | # ./Cargo.toml - sed -i -e 's|description = "RustDesk Remote Desktop"|description = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./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 = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|ProductName = "RustDesk"|ProductName = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|FileDescription = "RustDesk Remote Desktop"|FileDescription = "${{ inputs.appname }}"|' ./libs/portable/Cargo.toml - sed -i -e 's|OriginalFilename = "rustdesk.exe"|OriginalFilename = "${{ inputs.appname }}.exe"|' ./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 # ./src/lang/en.rs - find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ inputs.appname }}|' {} \; + find ./src/lang -name "*.rs" -exec sed -i -e 's|RustDesk|${{ env.appname }}|' {} \; - name: fix registry if appname has a space - if: contains(inputs.appname, ' ') + if: contains(env.appname, ' ') continue-on-error: true shell: bash run: | @@ -190,41 +192,41 @@ jobs: 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: fromJson(inputs.extras).compname != 'Purslane Ltd' + if: env.compname != 'Purslane Ltd' continue-on-error: true shell: bash run: | - sed -i -e 's|PURSLANE|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./res/msi/preprocess.py - sed -i -e 's|Copyright © 2025 Purslane Ltd.|Copyright \© 2025 ${{ fromJson(inputs.extras).compname }}|' ./src/ui/index.tis - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./Cargo.toml - sed -i -e 's|Purslane Ltd|${{ fromJson(inputs.extras).compname }}|' ./libs/portable/Cargo.toml - sed -i -e 's|Purslane Ltd.|${{ fromJson(inputs.extras).compname }}|' ./res/setup.nsi + 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.|Copyright \© 2025 ${{ env.compname }}|' ./src/ui/index.tis + 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/setup.nsi - name: change url to custom - if: fromJson(inputs.extras).urlLink != 'https://rustdesk.com' + if: env.urlLink != 'https://rustdesk.com' continue-on-error: true shell: bash run: | - sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ fromJson(inputs.extras).urlLink }}|' ./build.py - sed -i -e "s|
|
|" ./src/ui/index.tis - sed -i -e "s|
|
|" ./src/ui/index.tis - sed -i -e "s|https://rustdesk.com/|${{fromJson(inputs.extras).urlLink }}|" ./res/setup.nsi + sed -i -e 's|Homepage: https://rustdesk.com|Homepage: ${{ env.urlLink }}|' ./build.py + sed -i -e "s|
|
|" ./src/ui/index.tis + sed -i -e "s|
|
|" ./src/ui/index.tis + sed -i -e "s|https://rustdesk.com/|${{env.urlLink }}|" ./res/setup.nsi - name: change download link to custom - if: fromJson(inputs.extras).downloadLink != 'https://rustdesk.com/download' + if: env.downloadLink != 'https://rustdesk.com/download' continue-on-error: true shell: bash run: | - sed -i -e 's|https://rustdesk.com/download|${{ fromJson(inputs.extras).downloadLink }}|' ./src/ui/index.tis + sed -i -e 's|https://rustdesk.com/download|${{ env.downloadLink }}|' ./src/ui/index.tis - name: set server, key, and apiserver continue-on-error: true shell: bash run: | - sed -i -e 's|rs-ny.rustdesk.com|${{ inputs.server }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|OeVuKk5nlHiXp+APNn0Y3pC1Iwpwn44JGqrQCsWqmBw=|${{ inputs.key }}|' ./libs/hbb_common/src/config.rs - sed -i -e 's|https://admin.rustdesk.com|${{ inputs.apiServer }}|' ./src/common.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 sed -i -e 's|{translate("Ready")}, {translate("setup_server_tip")}|translate("Ready")|' ./src/ui/index.tis - name: allow custom.txt @@ -245,7 +247,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "10% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "10% complete"}' - name: Install Rust toolchain uses: dtolnay/rust-toolchain@v1 @@ -265,7 +267,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "20% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "20% complete"}' - name: Setup vcpkg with Github Actions binary cache uses: lukka/run-vcpkg@v11 @@ -295,7 +297,7 @@ jobs: shell: bash - name: icon stuff - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | @@ -304,10 +306,10 @@ jobs: mv ./res/tray-icon.ico ./res/tray-icon.ico.bak - name: magick stuff - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true run: | - Invoke-WebRequest -Uri ${{ fromJson(inputs.iconlink).url }}/get_png?filename=${{ fromJson(inputs.iconlink).file }}"&"uuid=${{ fromJson(inputs.iconlink).uuid }} -OutFile ./res/icon.png + Invoke-WebRequest -Uri ${{ env.iconlink_url }}/get_png?filename=${{ env.iconlink_file }}"&"uuid=${{ env.iconlink_uuid }} -OutFile ./res/icon.png mv ./res/32x32.png ./res/32x32.png.bak mv ./res/64x64.png ./res/64x64.png.bak mv ./res/128x128.png ./res/128x128.png.bak @@ -321,7 +323,7 @@ jobs: - name: ui.rs icon - if: ${{ inputs.iconlink != 'false' }} + if: ${{ env.iconlink_url != 'false' }} continue-on-error: true shell: bash run: | @@ -332,14 +334,14 @@ jobs: - name: fix connection delay continue-on-error: true - if: ${{ fromJson(inputs.extras).delayFix == 'true' }} + if: ${{ env.delayFix == 'true' }} shell: bash run: | sed -i -e 's|!key.is_empty()|false|' ./src/client.rs - name: removeNewVersionNotif continue-on-error: true - if: fromJson(inputs.extras).removeNewVersionNotif == 'true' + if: env.removeNewVersionNotif == 'true' shell: bash run: | sed -i -e 's|{software_update_url ? : ""}||' ./src/ui/index.tis @@ -352,7 +354,7 @@ jobs: 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."}' + data: '{"uuid": "${{ env.uuid }}", "status": "50% complete, this step takes about 5 minutes, be patient."}' - name: Build rustdesk id: build @@ -397,7 +399,7 @@ jobs: 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."}' + data: '{"uuid": "${{ env.uuid }}", "status": "70% complete, this step takes about 5 minutes, be patient."}' - name: zip dlls continue-on-error: true @@ -430,16 +432,16 @@ jobs: - name: Create custom.txt file shell: bash run: | - echo -n "${{ inputs.custom }}" | cat > ./Release/custom.txt + echo -n "${{ env.custom }}" | cat > ./Release/custom.txt - name: Build self-extracted executable shell: bash run: | - mv "./Release/rustdesk.exe" "./Release/${{ inputs.appname }}.exe" || echo "rustdesk.exe" + mv "./Release/rustdesk.exe" "./Release/${{ env.appname }}.exe" || echo "rustdesk.exe" sed -i '/dpiAware/d' res/manifest.xml pushd ./libs/portable pip3 install -r requirements.txt - python3 ./generate.py -f ../../Release/ -o . -e "../../Release/${{ inputs.appname }}.exe" + python3 ./generate.py -f ../../Release/ -o . -e "../../Release/${{ env.appname }}.exe" popd mkdir -p ./SignOutput mv ./target/release/rustdesk-portable-packer.exe "./SignOutput/rustdesk.exe" @@ -451,7 +453,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "85% complete"}' + data: '{"uuid": "${{ env.uuid }}", "status": "85% complete"}' - name: zip exe continue-on-error: true @@ -483,19 +485,19 @@ jobs: - name: rename rustdesk.exe to filename.exe run: | - mv ./SignOutput/rustdesk.exe "./SignOutput/${{ inputs.filename }}.exe" || echo "rustdesk" + mv ./SignOutput/rustdesk.exe "./SignOutput/${{ env.filename }}.exe" || echo "rustdesk" - name: send file to rdgen server - 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=@./SignOutput/${{ inputs.filename }}.exe" -F "uuid=${{ inputs.uuid }}" ${{ secrets.GENURL }}/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" -F "uuid=${{ env.uuid }}" ${{ secrets.GENURL }}/save_custom_client - name: send file to api server - 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=@./SignOutput/${{ inputs.filename }}.exe" ${{ inputs.apiServer }}/api/save_custom_client + curl -i -X POST -H "Content-Type: multipart/form-data" -H "Authorization: Bearer ${{ env.token }}" -F "file=@./SignOutput/${{ env.filename }}.exe" ${{ env.apiServer }}/api/save_custom_client - name: Report Status uses: fjogeleit/http-request-action@v1 @@ -503,7 +505,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Success"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Success"}' - name: failed if: failure() @@ -512,7 +514,7 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation failed, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation failed, try again"}' - name: failed if: cancelled() @@ -521,4 +523,4 @@ jobs: url: ${{ env.STATUS_URL }} method: 'POST' customHeaders: '{"Content-Type": "application/json"}' - data: '{"uuid": "${{ inputs.uuid }}", "status": "Generation cancelled, try again"}' + data: '{"uuid": "${{ env.uuid }}", "status": "Generation cancelled, try again"}' -- 2.49.1 From 27b52d80e9bedf4c278170755e4d929fa8824b7b Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Tue, 13 Jan 2026 07:11:56 -0600 Subject: [PATCH 14/20] fix linux --- .github/workflows/generator-linux.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/generator-linux.yml b/.github/workflows/generator-linux.yml index 2d1bea2..bead508 100644 --- a/.github/workflows/generator-linux.yml +++ b/.github/workflows/generator-linux.yml @@ -83,6 +83,7 @@ jobs: import io import os import json + import time for attempt in range(5): try: @@ -675,6 +676,7 @@ jobs: import io import os import json + import time for attempt in range(5): try: @@ -792,6 +794,7 @@ jobs: import io import os import json + import time for attempt in range(5): try: @@ -908,6 +911,7 @@ jobs: import io import os import json + import time for attempt in range(5): try: -- 2.49.1 From f9901f2f7468f780c81e305335f0c286dc478eb2 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Tue, 13 Jan 2026 13:15:44 -0600 Subject: [PATCH 15/20] allow retry on connection loss --- .github/workflows/generator-android.yml | 34 +++++++++++++------- .github/workflows/generator-windows-x86.yml | 35 ++++++++++++++------- .github/workflows/generator-windows.yml | 34 +++++++++++++------- 3 files changed, 69 insertions(+), 34 deletions(-) diff --git a/.github/workflows/generator-android.yml b/.github/workflows/generator-android.yml index 228004a..a2f01da 100644 --- a/.github/workflows/generator-android.yml +++ b/.github/workflows/generator-android.yml @@ -87,23 +87,35 @@ jobs: import io import os import json + import time - r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') - r.raise_for_status() + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e try: - with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: - zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) - with zf.open('secrets.json') as f: - secrets = json.load(f) + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) except Exception as e: - print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") - exit(1) + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) with open(os.environ['GITHUB_ENV'], 'a') as env_file: - for key, value in secrets.items(): - print(f"::add-mask::{value}") - env_file.write(f"{key}={value}\n") + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") print("Secrets loaded into environment.") diff --git a/.github/workflows/generator-windows-x86.yml b/.github/workflows/generator-windows-x86.yml index 02c5775..4d6fbcb 100644 --- a/.github/workflows/generator-windows-x86.yml +++ b/.github/workflows/generator-windows-x86.yml @@ -3,7 +3,6 @@ run-name: Custom Windows x86 Client Generator on: workflow_dispatch: inputs: - inputs: version: description: 'version to buld' required: true @@ -77,23 +76,35 @@ jobs: import io import os import json + import time - r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') - r.raise_for_status() + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e try: - with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: - zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) - with zf.open('secrets.json') as f: - secrets = json.load(f) + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) except Exception as e: - print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") - exit(1) + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) with open(os.environ['GITHUB_ENV'], 'a') as env_file: - for key, value in secrets.items(): - print(f"::add-mask::{value}") - env_file.write(f"{key}={value}\n") + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") print("Secrets loaded into environment.") diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index ab002f2..d8b64eb 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -87,23 +87,35 @@ jobs: import io import os import json + import time - r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}') - r.raise_for_status() + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e try: - with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: - zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) - with zf.open('secrets.json') as f: - secrets = json.load(f) + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) except Exception as e: - print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") - exit(1) + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) with open(os.environ['GITHUB_ENV'], 'a') as env_file: - for key, value in secrets.items(): - print(f"::add-mask::{value}") - env_file.write(f"{key}={value}\n") + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") print("Secrets loaded into environment.") -- 2.49.1 From dd5291c9ba69d6436277d15f5fa573e090bbedeb Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Tue, 20 Jan 2026 09:40:05 -0600 Subject: [PATCH 16/20] fix license on msi --- .github/workflows/generator-windows.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index d8b64eb..5c66871 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -213,6 +213,7 @@ jobs: 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, ' ') @@ -246,6 +247,7 @@ jobs: 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' @@ -259,6 +261,7 @@ jobs: 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' -- 2.49.1 From adc69d499a20f96f647c2018c8e867bd65749083 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Fri, 23 Jan 2026 21:56:39 -0600 Subject: [PATCH 17/20] privacy screen --- .github/patches/privacyScreen.py | 32 ++++++++++ .github/workflows/generator-linux.yml | 4 +- .github/workflows/generator-macos.yml | 4 +- .../third-party-RustDeskTempTopMostWindow.yml | 60 +++++++++++++++++++ rdgenerator/forms.py | 2 + rdgenerator/templates/generator.html | 22 +++++++ rdgenerator/views.py | 13 ++++ 7 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 .github/patches/privacyScreen.py diff --git a/.github/patches/privacyScreen.py b/.github/patches/privacyScreen.py new file mode 100644 index 0000000..5197c4e --- /dev/null +++ b/.github/patches/privacyScreen.py @@ -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") \ No newline at end of file diff --git a/.github/workflows/generator-linux.yml b/.github/workflows/generator-linux.yml index bead508..3998c90 100644 --- a/.github/workflows/generator-linux.yml +++ b/.github/workflows/generator-linux.yml @@ -301,8 +301,8 @@ jobs: 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 - wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/allowCustom.diff - git apply allowCustom.diff + 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 echo -n "${{ env.custom }}" | cat > ./custom_.txt diff --git a/.github/workflows/generator-macos.yml b/.github/workflows/generator-macos.yml index d481ace..07a4c2a 100644 --- a/.github/workflows/generator-macos.yml +++ b/.github/workflows/generator-macos.yml @@ -237,8 +237,8 @@ jobs: 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.diff - git apply allowCustom.diff + 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 diff --git a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml index 2f89092..5af0879 100644 --- a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml +++ b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml @@ -45,10 +45,70 @@ jobs: run: | git clone https://github.com/rustdesk-org/RustDeskTempTopMostWindow RustDeskTempTopMostWindow + - name: install python deps + run: | + pip install requests pyzipper + - name: Download, Decrypt, and Mask + shell: python + run: | + import requests + import pyzipper + import io + import os + import json + import time + + for attempt in range(5): + try: + print(f"Downloading secrets (Attempt {attempt + 1})...") + r = requests.get('${{ fromJson(inputs.zip_url).url }}/get_zip?filename=${{ fromJson(inputs.zip_url).file }}', timeout=60) + r.raise_for_status() + break + except (requests.exceptions.RequestException, requests.exceptions.Timeout) as e: + if attempt < 4: + print(f"Timeout/Error occurred: {e}. Retrying in 5 seconds...") + time.sleep(5) + else: + print("Max retries reached. Failing.") + raise e + + try: + with pyzipper.AESZipFile(io.BytesIO(r.content)) as zf: + zf.setpassword('${{ secrets.ZIP_PASSWORD }}'.encode()) + with zf.open('secrets.json') as f: + secrets = json.load(f) + except Exception as e: + print(f"Error: Could not decrypt ZIP. Check if password matches. {e}") + exit(1) + + with open(os.environ['GITHUB_ENV'], 'a') as env_file: + for key, value in secrets.items(): + print(f"::add-mask::{value}") + env_file.write(f"{key}={value}\n") + + print("Secrets loaded into environment.") + + - 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 }}"}' + # Build. commit 53b548a5398624f7149a382000397993542ad796 is tag v0.3 - name: Build the project run: | cd RustDeskTempTopMostWindow && git checkout 53b548a5398624f7149a382000397993542ad796 + if [[ "${{ env.logolink_url }}" != "false" ]]; then + wget -O ./privacy.png ${{ env.privacylink_url }}/get_png?filename=${{ env.privacylink_file }}"&"uuid=${{ env.privacylink_uuid }} + wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/privacyScreen.py + python privacyScreen.py + rm ./WindowInjection/img.cpp + mv img.cpp ./WindowInjection/img.cpp + fi msbuild ${{ env.project_path }} -p:Configuration=${{ inputs.configuration }} -p:Platform=${{ inputs.platform }} /p:TargetVersion=${{ inputs.target_version }} - name: Archive build artifacts diff --git a/rdgenerator/forms.py b/rdgenerator/forms.py index db5cb29..7ae27d0 100644 --- a/rdgenerator/forms.py +++ b/rdgenerator/forms.py @@ -37,8 +37,10 @@ class GenerateForm(forms.Form): #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'), diff --git a/rdgenerator/templates/generator.html b/rdgenerator/templates/generator.html index 4fb0d46..babfba3 100644 --- a/rdgenerator/templates/generator.html +++ b/rdgenerator/templates/generator.html @@ -330,6 +330,7 @@

Visual

{{ form.iconbase64.as_hidden }} {{ form.logobase64.as_hidden }} + {{ form.privacybase64.as_hidden }} {{ form.iconfile }}

@@ -338,6 +339,10 @@ {{ form.logofile }}



+ + {{ form.privacyfile }}

+ +


{{ form.theme }} {{ form.themeDorO }} *Default sets the theme but allows the client to change it, Override sets the theme permanently.

@@ -415,6 +420,9 @@ 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) { @@ -594,6 +602,16 @@ } } + const privacyPreview = document.getElementById('privacy-preview'); + if (privacyPreview.firstChild && logoPrprivacyPrevieweview.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); @@ -655,6 +673,10 @@ document.getElementById('id_logobase64').value = formData[key]; document.getElementById('logo-preview').innerHTML = ``; } + if (key === 'privacyfile' && formData[key]) { + document.getElementById('id_privacybase64').value = formData[key]; + document.getElementById('privacy-preview').innerHTML = ``; + } }); } } diff --git a/rdgenerator/views.py b/rdgenerator/views.py index 7c535f8..65d8e39 100644 --- a/rdgenerator/views.py +++ b/rdgenerator/views.py @@ -115,6 +115,16 @@ def generator_view(request): logolink_url = "false" logolink_uuid = "false" logolink_file = "false" + try: + privacyfile = form.cleaned_data.get('privacyfile') + if not privacyfile: + privacyfile = form.cleaned_data.get('privacybase64') + privacylink_url, privacylink_uuid, privacylink_file = save_png(privacyfile,myuuid,full_url,"privacy.png") + except: + print("failed to get logo") + privacylink_url = "false" + privacylink_uuid = "false" + privacylink_file = "false" ###create the custom.txt json here and send in as inputs below decodedCustom = {} @@ -239,6 +249,9 @@ def generator_view(request): "logolink_url":logolink_url, "logolink_uuid":logolink_uuid, "logolink_file":logolink_file, + "privacylink_url":privacylink_url, + "privacylink_uuid":privacylink_uuid, + "privacylink_file":privacylink_file, "appname":appname, "genurl":_settings.GENURL, "urlLink":urlLink, -- 2.49.1 From b9fdac7d8c2e5109f4b45f7b546531110cb9c0a8 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Fri, 23 Jan 2026 22:04:10 -0600 Subject: [PATCH 18/20] privacy screen --- .github/workflows/generator-windows.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/generator-windows.yml b/.github/workflows/generator-windows.yml index 5c66871..e83d85b 100644 --- a/.github/workflows/generator-windows.yml +++ b/.github/workflows/generator-windows.yml @@ -55,6 +55,7 @@ jobs: configuration: Release platform: x64 target_version: Windows10 + secrets: inherit strategy: fail-fast: false -- 2.49.1 From 55317863c0a94da7cc224251ae6f85bac9453fa6 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Fri, 23 Jan 2026 22:08:48 -0600 Subject: [PATCH 19/20] privacy screen --- .../workflows/third-party-RustDeskTempTopMostWindow.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml index 5af0879..c3c1f32 100644 --- a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml +++ b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml @@ -102,13 +102,13 @@ jobs: - name: Build the project run: | cd RustDeskTempTopMostWindow && git checkout 53b548a5398624f7149a382000397993542ad796 - if [[ "${{ env.logolink_url }}" != "false" ]]; then - wget -O ./privacy.png ${{ env.privacylink_url }}/get_png?filename=${{ env.privacylink_file }}"&"uuid=${{ env.privacylink_uuid }} - wget https://raw.githubusercontent.com/bryangerlach/rdgen/refs/heads/master/.github/patches/privacyScreen.py + 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 - fi + } msbuild ${{ env.project_path }} -p:Configuration=${{ inputs.configuration }} -p:Platform=${{ inputs.platform }} /p:TargetVersion=${{ inputs.target_version }} - name: Archive build artifacts -- 2.49.1 From f0943ddf1a29bde04812c7a23ae2a91a672920a4 Mon Sep 17 00:00:00 2001 From: Bryan Gerlach Date: Fri, 23 Jan 2026 22:11:58 -0600 Subject: [PATCH 20/20] privacy screen --- .github/workflows/third-party-RustDeskTempTopMostWindow.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml index c3c1f32..6d00ebc 100644 --- a/.github/workflows/third-party-RustDeskTempTopMostWindow.yml +++ b/.github/workflows/third-party-RustDeskTempTopMostWindow.yml @@ -104,7 +104,7 @@ jobs: 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 + 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 -- 2.49.1