name: Build Fluxer Desktop on: workflow_dispatch: push: branches: - main env: NODE_VERSION: "24" PNPM_VERSION: "10.29.3" BUILD_CHANNEL: "stable" NODE_ENV: "production" FLUXER_DESKTOP_PRODUCTION: "true" VERSION: "1.0.${{ gitea.run_number }}" PUBLIC_BUILD_VERSION: "1.0.${{ gitea.run_number }}" PUBLIC_RELEASE_CHANNEL: "stable" jobs: windows-x64: name: Windows x64 runs-on: rustdesk-windows steps: - name: Clone Fluxer shell: powershell run: | $ErrorActionPreference = "Stop" if (Test-Path "fluxer-src") { Remove-Item "fluxer-src" -Recurse -Force } git clone ` --depth 1 ` --branch main ` https://github.com/fluxerapp/fluxer.git ` fluxer-src if (-not (Test-Path "fluxer-src\fluxer_desktop\src\common\Constants.ts")) { throw "Fluxer source tree is invalid: Constants.ts not found" } Write-Host "Fluxer revision:" git -C fluxer-src rev-parse HEAD Write-Host "Desktop directory:" Get-ChildItem "fluxer-src\fluxer_desktop" - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "24" - name: Setup pnpm shell: powershell run: | $ErrorActionPreference = "Stop" corepack enable corepack prepare pnpm@10.29.3 --activate node --version pnpm --version - name: Setup Rust shell: powershell run: | $ErrorActionPreference = "Stop" rustup update stable rustup default stable rustup target add x86_64-pc-windows-msvc rustc --version cargo --version - name: Show build tools shell: powershell run: | $ErrorActionPreference = "Stop" Write-Host "Node:" node --version Write-Host "pnpm:" pnpm --version Write-Host "Rust:" rustc --version Write-Host "Cargo:" cargo --version Write-Host "CMake:" cmake --version Write-Host "Ninja:" ninja --version Write-Host "Clang:" clang --version Write-Host "NASM:" nasm --version - name: Configure self-hosted server shell: powershell run: | $ErrorActionPreference = "Stop" $Path = "fluxer-src\fluxer_desktop\src\common\Constants.ts" if (-not (Test-Path $Path)) { throw "Constants.ts not found at $Path" } $Content = Get-Content $Path -Raw $Content = $Content -replace ` "export const STABLE_APP_URL = '[^']+';", ` "export const STABLE_APP_URL = 'https://ds.gandc.ru';" $Content = $Content -replace ` "export const CANARY_APP_URL = '[^']+';", ` "export const CANARY_APP_URL = 'https://ds.gandc.ru';" Set-Content ` -Path $Path ` -Value $Content ` -NoNewline ` -Encoding utf8 Write-Host "Result:" Get-Content $Path $Result = Get-Content $Path -Raw if ($Result -notmatch "STABLE_APP_URL = 'https://ds\.gandc\.ru'") { throw "STABLE_APP_URL was not configured" } if ($Result -notmatch "CANARY_APP_URL = 'https://ds\.gandc\.ru'") { throw "CANARY_APP_URL was not configured" } $PackagePath = "fluxer-src\fluxer_desktop\package.json" if (-not (Test-Path $PackagePath)) { throw "package.json not found at $PackagePath" } $Package = Get-Content $PackagePath -Raw | ConvertFrom-Json $Package.homepage = "https://ds.gandc.ru" $PackageJson = $Package | ConvertTo-Json -Depth 100 [System.IO.File]::WriteAllText( $PackagePath, $PackageJson + [Environment]::NewLine, (New-Object System.Text.UTF8Encoding($false)) ) Write-Host "Desktop homepage configured:" (Get-Content $PackagePath -Raw | ConvertFrom-Json).homepage $PackageBytes = [System.IO.File]::ReadAllBytes($PackagePath) if ( $PackageBytes.Length -ge 3 -and $PackageBytes[0] -eq 0xEF -and $PackageBytes[1] -eq 0xBB -and $PackageBytes[2] -eq 0xBF ) { throw "package.json unexpectedly contains UTF-8 BOM" } Write-Host "package.json UTF-8 BOM check: OK" $WindowPath = "fluxer-src\fluxer_desktop\src\main\Window.ts" if (-not (Test-Path $WindowPath)) { throw "Window.ts not found at $WindowPath" } $WindowContent = Get-Content $WindowPath -Raw $NavigationPattern = "webContents\.on\('will-navigate', \(event, url\) => \{\r?\n\s*if \(!isTrustedOrigin\(url\)\) \{\r?\n\s*event\.preventDefault\(\);\r?\n\s*\}\r?\n\s*\}\);" $NavigationReplacementLines = @( "const isAllowedSsoOrigin = (url: string): boolean => {" "`ttry {" "`t`treturn new URL(url).origin === 'https://sso.gandc.ru';" "`t} catch {" "`t`treturn false;" "`t}" "};" "" "`tconst allowTrustedOrSsoNavigation = (event: Electron.Event, url: string): void => {" "`t`tif (isTrustedOrigin(url) || isAllowedSsoOrigin(url)) {" "`t`t`treturn;" "`t`t}" "" "`t`tlogger.warn('Blocked navigation outside trusted and SSO origins', {url});" "`t`tevent.preventDefault();" "`t};" "" "`twebContents.on('will-navigate', allowTrustedOrSsoNavigation);" "`twebContents.on('will-redirect', allowTrustedOrSsoNavigation);" ) $NavigationReplacement = $NavigationReplacementLines -join "`n" $PatchedWindowContent = [regex]::Replace( $WindowContent, $NavigationPattern, $NavigationReplacement, 1 ) if ($PatchedWindowContent -eq $WindowContent) { throw "Unable to patch Window.ts SSO navigation handler" } Set-Content ` -Path $WindowPath ` -Value $PatchedWindowContent ` -NoNewline ` -Encoding utf8 Write-Host "SSO navigation allowlist patch applied" Select-String ` -Path $WindowPath ` -Pattern "sso.gandc.ru|will-redirect|allowTrustedOrSsoNavigation" - name: Configure GSCORD update source shell: powershell run: | $ErrorActionPreference = "Stop" $PatchScript = [Text.Encoding]::UTF8.GetString( [Convert]::FromBase64String( "CmNvbnN0IGZzID0gcmVxdWlyZSgnZnMnKTsKCmNvbnN0IGRvd25sb2Fkc1BhdGggPSAnZmx1eGVyLXNyYy9mbHV4ZXJfZGVza3RvcC9zcmMvbWFpbi9VcGRhdGVyRG93bmxvYWRzLnRzJzsKY29uc3QgdXBkYXRlclBhdGggPSAnZmx1eGVyLXNyYy9mbHV4ZXJfZGVza3RvcC9zcmMvbWFpbi9VcGRhdGVyLnRzJzsKCmZ1bmN0aW9uIHJlcGxhY2VSZWdleE9uY2UoaW5wdXQsIHBhdHRlcm4sIHJlcGxhY2VtZW50LCBsYWJlbCkgewogIGxldCBjb3VudCA9IDA7CiAgY29uc3Qgb3V0cHV0ID0gaW5wdXQucmVwbGFjZShwYXR0ZXJuLCAoLi4uYXJncykgPT4gewogICAgY291bnQgKz0gMTsKICAgIHJldHVybiB0eXBlb2YgcmVwbGFjZW1lbnQgPT09ICdmdW5jdGlvbicKICAgICAgPyByZXBsYWNlbWVudCguLi5hcmdzKQogICAgICA6IHJlcGxhY2VtZW50OwogIH0pOwoKICBpZiAoY291bnQgIT09IDEpIHsKICAgIHRocm93IG5ldyBFcnJvcihgVW5hYmxlIHRvIHBhdGNoICR7bGFiZWx9OiBtYXRjaGVzPSR7Y291bnR9YCk7CiAgfQoKICByZXR1cm4gb3V0cHV0Owp9CgpsZXQgZG93bmxvYWRzID0gZnMucmVhZEZpbGVTeW5jKGRvd25sb2Fkc1BhdGgsICd1dGY4Jyk7Cgpkb3dubG9hZHMgPSByZXBsYWNlUmVnZXhPbmNlKAogIGRvd25sb2FkcywKICAvY29uc3QgUEtHU19CQVNFX1VSTCA9ICdodHRwczpcL1wvcGtnc1wuZmx1eGVyXC5jb20nO1xyP1xuZXhwb3J0IGNvbnN0IFVQREFURV9CQVNFX1VSTCA9IGBcJFx7UEtHU19CQVNFX1VSTFx9XC9kZXNrdG9wXC9cJFx7QlVJTERfQ0hBTk5FTFx9XC9cJFx7cHJvY2Vzc1wucGxhdGZvcm1cfVwvXCRce0RFU0tUT1BfRE9XTkxPQURfQVJDSFx9YDtccj9cbmV4cG9ydCBjb25zdCBET1dOTE9BRF9QQUdFX1VSTCA9XHI/XG5ccypCVUlMRF9DSEFOTkVMID09PSAnY2FuYXJ5JyBcPyAnaHR0cHM6XC9cL2NhbmFyeVwuZmx1eGVyXC5hcHBcL2Rvd25sb2FkJyA6ICdodHRwczpcL1wvZmx1eGVyXC5hcHBcL2Rvd25sb2FkJzsvLAogICJleHBvcnQgY29uc3QgVVBEQVRFX0JBU0VfVVJMID0gJ2h0dHBzOi8vZ2l0LmdhbmRjLnJ1L2dzY29yZC9nc2NvcmQtZGVza3RvcC9yZWxlYXNlcyc7XG4iICsKICAgICJleHBvcnQgY29uc3QgRE9XTkxPQURfUEFHRV9VUkwgPSBVUERBVEVfQkFTRV9VUkw7IiwKICAnVXBkYXRlckRvd25sb2FkcyB1cGRhdGUgc291cmNlJywKKTsKCmRvd25sb2FkcyA9IHJlcGxhY2VSZWdleE9uY2UoCiAgZG93bmxvYWRzLAogIC9leHBvcnQgZnVuY3Rpb24gYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmxcKHZlcnNpb246IHN0cmluZywgZm9ybWF0OiBNYW51YWxEZXNrdG9wRm9ybWF0XCk6IHN0cmluZyBce1xyP1xuXHMqcmV0dXJuIGBcJFx7VVBEQVRFX0JBU0VfVVJMXH1cL1wkXHt2ZXJzaW9uXH1cL1wkXHtmb3JtYXRcfWA7XHI/XG5cfS8sCiAgImV4cG9ydCBmdW5jdGlvbiBidWlsZE1hbnVhbFZlcnNpb25Eb3dubG9hZFVybCh2ZXJzaW9uOiBzdHJpbmcsIGZvcm1hdDogTWFudWFsRGVza3RvcEZvcm1hdCk6IHN0cmluZyB7XG4iICsKICAgICJcdHZvaWQgdmVyc2lvbjtcbiIgKwogICAgIlx0dm9pZCBmb3JtYXQ7XG4iICsKICAgICJcdHJldHVybiBET1dOTE9BRF9QQUdFX1VSTDtcbiIgKwogICAgIn0iLAogICdtYW51YWwgZmFsbGJhY2sgVVJMJywKKTsKCmRvd25sb2FkcyA9IHJlcGxhY2VSZWdleE9uY2UoCiAgZG93bmxvYWRzLAogIC91cmw6XHMqYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmxcKGluZm9cLnZlcnNpb24sXHMqZm9ybWF0XCksLywKICAidXJsOiBmaWxlPy51cmwgPz8gYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmwoaW5mby52ZXJzaW9uLCBmb3JtYXQpLCIsCiAgJ21hbnVhbCBhc3NldCBVUkwnLAopOwoKaWYgKGRvd25sb2Fkcy5pbmNsdWRlcygncGtncy5mbHV4ZXIuY29tJykpIHsKICB0aHJvdyBuZXcgRXJyb3IoCiAgICAncGtncy5mbHV4ZXIuY29tIGlzIHN0aWxsIHByZXNlbnQgaW4gVXBkYXRlckRvd25sb2Fkcy50cycsCiAgKTsKfQoKaWYgKAogICFkb3dubG9hZHMuaW5jbHVkZXMoCiAgICAnaHR0cHM6Ly9naXQuZ2FuZGMucnUvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzJywKICApCikgewogIHRocm93IG5ldyBFcnJvcignR1NDT1JEIHJlbGVhc2VzIFVSTCB3YXMgbm90IGluc3RhbGxlZCcpOwp9Cgpmcy53cml0ZUZpbGVTeW5jKGRvd25sb2Fkc1BhdGgsIGRvd25sb2FkcywgJ3V0ZjgnKTsKCmxldCB1cGRhdGVyID0gZnMucmVhZEZpbGVTeW5jKHVwZGF0ZXJQYXRoLCAndXRmOCcpOwoKdXBkYXRlciA9IHJlcGxhY2VSZWdleE9uY2UoCiAgdXBkYXRlciwKICAvY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaFwoYFwkXHtVUERBVEVfQkFTRV9VUkxcfVwvbGF0ZXN0YCwgXHsvLAogICJjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKFxuIiArCiAgICAiXHRcdCdodHRwczovL2dpdC5nYW5kYy5ydS9hcGkvdjEvcmVwb3MvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzL2xhdGVzdCcsXG4iICsKICAgICJcdFx0eyIsCiAgJ0dpdGVhIGxhdGVzdCByZWxlYXNlIFVSTCcsCik7Cgpjb25zdCBnaXRlYVBhcnNlciA9IGAJY29uc3QgcGF5bG9hZCA9IChhd2FpdCByZXNwb25zZS5qc29uKCkpIGFzIHsKCQl0YWdfbmFtZT86IHVua25vd247CgkJcHVibGlzaGVkX2F0PzogdW5rbm93bjsKCQljcmVhdGVkX2F0PzogdW5rbm93bjsKCQlhc3NldHM/OiB1bmtub3duOwoJfTsKCglpZiAoCgkJdHlwZW9mIHBheWxvYWQudGFnX25hbWUgIT09ICdzdHJpbmcnIHx8CgkJcGF5bG9hZC50YWdfbmFtZS50cmltKCkubGVuZ3RoID09PSAwCgkpIHsKCQl0aHJvdyBuZXcgRXJyb3IoJ0dTQ09SRCBsYXRlc3QgcmVsZWFzZSByZXNwb25zZSBtaXNzaW5nIHRhZ19uYW1lJyk7Cgl9CgoJY29uc3QgdmVyc2lvbiA9IHBheWxvYWQudGFnX25hbWUudHJpbSgpLnJlcGxhY2UoL152LywgJycpOwoJY29uc3QgYXNzZXRzID0gQXJyYXkuaXNBcnJheShwYXlsb2FkLmFzc2V0cykgPyBwYXlsb2FkLmFzc2V0cyA6IFtdOwoJY29uc3QgZmlsZXM6IFBhcnRpYWw8CgkJUmVjb3JkPE1hbnVhbERlc2t0b3BGb3JtYXQsIE1hbnVhbExhdGVzdEZpbGU+Cgk+ID0ge307CgoJY29uc3QgaXNBcm02NCA9IHByb2Nlc3MuYXJjaCA9PT0gJ2FybTY0JzsKCglmb3IgKGNvbnN0IHJhd0Fzc2V0IG9mIGFzc2V0cykgewoJCWlmICghaXNSZWNvcmQocmF3QXNzZXQpKSB7CgkJCWNvbnRpbnVlOwoJCX0KCgkJY29uc3QgbmFtZSA9CgkJCXR5cGVvZiByYXdBc3NldC5uYW1lID09PSAnc3RyaW5nJwoJCQkJPyByYXdBc3NldC5uYW1lCgkJCQk6ICcnOwoKCQljb25zdCB1cmwgPQoJCQl0eXBlb2YgcmF3QXNzZXQuYnJvd3Nlcl9kb3dubG9hZF91cmwgPT09ICdzdHJpbmcnCgkJCQk/IHJhd0Fzc2V0LmJyb3dzZXJfZG93bmxvYWRfdXJsCgkJCQk6ICcnOwoKCQlpZiAoIW5hbWUgfHwgIXVybCkgewoJCQljb250aW51ZTsKCQl9CgoJCWNvbnN0IGxvd2VyTmFtZSA9IG5hbWUudG9Mb3dlckNhc2UoKTsKCgkJaWYgKGlzQXJtNjQpIHsKCQkJaWYgKAoJCQkJIWxvd2VyTmFtZS5pbmNsdWRlcygnYXJtNjQnKSAmJgoJCQkJIWxvd2VyTmFtZS5pbmNsdWRlcygnYWFyY2g2NCcpCgkJCSkgewoJCQkJY29udGludWU7CgkJCX0KCQl9IGVsc2UgaWYgKAoJCQlsb3dlck5hbWUuaW5jbHVkZXMoJ2FybTY0JykgfHwKCQkJbG93ZXJOYW1lLmluY2x1ZGVzKCdhYXJjaDY0JykKCQkpIHsKCQkJY29udGludWU7CgkJfQoKCQlpZiAoL2xpbnV4LXg4Nl82NFxcLkFwcEltYWdlJC9pLnRlc3QobmFtZSkgfHwKCQkJL2xpbnV4LWFybTY0XFwuQXBwSW1hZ2UkL2kudGVzdChuYW1lKSkgewoJCQlmaWxlcy5hcHBpbWFnZSA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCgkJaWYgKC9saW51eC1hbWQ2NFxcLmRlYiQvaS50ZXN0KG5hbWUpIHx8CgkJCS9saW51eC1hcm02NFxcLmRlYiQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLmRlYiA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCgkJaWYgKC9saW51eC14ODZfNjRcXC5ycG0kL2kudGVzdChuYW1lKSB8fAoJCQkvbGludXgtYWFyY2g2NFxcLnJwbSQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLnJwbSA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCgkJaWYgKC9saW51eC14NjRcXC50YXJcXC5neiQvaS50ZXN0KG5hbWUpIHx8CgkJCS9saW51eC1hcm02NFxcLnRhclxcLmd6JC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMudGFyX2d6ID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoKCQlpZiAoL3BvcnRhYmxlLXdpbi14NjRcXC5leGUkL2kudGVzdChuYW1lKSB8fAoJCQkvcG9ydGFibGUtd2luLWFybTY0XFwuZXhlJC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMuc2V0dXAgPSB7dXJsLCBzaGEyNTY6IG51bGx9OwoJCQljb250aW51ZTsKCQl9CgoJCWlmICgvXFwuZG1nJC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMuZG1nID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoKCQlpZiAoL1xcLnppcCQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLnppcCA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJfQoJfQoKCWNvbnN0IGluZm86IE1hbnVhbExhdGVzdEluZm8gPSB7CgkJdmVyc2lvbiwKCQlwdWJEYXRlOgoJCQl0eXBlb2YgcGF5bG9hZC5wdWJsaXNoZWRfYXQgPT09ICdzdHJpbmcnCgkJCQk/IHBheWxvYWQucHVibGlzaGVkX2F0CgkJCQk6IHR5cGVvZiBwYXlsb2FkLmNyZWF0ZWRfYXQgPT09ICdzdHJpbmcnCgkJCQkJPyBwYXlsb2FkLmNyZWF0ZWRfYXQKCQkJCQk6IG51bGwsCgkJZmlsZXMsCgl9O2A7Cgp1cGRhdGVyID0gcmVwbGFjZVJlZ2V4T25jZSgKICB1cGRhdGVyLAogIC9cdGNvbnN0IHBheWxvYWQgPSBcKGF3YWl0IHJlc3BvbnNlXC5qc29uXChcKVwpIGFzIFx7dmVyc2lvblw/OiB1bmtub3duOyBwdWJfZGF0ZVw/OiB1bmtub3duOyBmaWxlc1w/OiB1bmtub3duXH07W1xzXFNdKj9ccj9cblx0Y29uc3QgaW5mbzogTWFudWFsTGF0ZXN0SW5mbyA9IFx7W1xzXFNdKj9ccj9cblx0XH07LywKICBnaXRlYVBhcnNlciwKICAnR2l0ZWEgbGF0ZXN0IHJlbGVhc2UgcGFyc2VyJywKKTsKCmlmICgKICAhdXBkYXRlci5pbmNsdWRlcygKICAgICdodHRwczovL2dpdC5nYW5kYy5ydS9hcGkvdjEvcmVwb3MvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzL2xhdGVzdCcsCiAgKQopIHsKICB0aHJvdyBuZXcgRXJyb3IoJ0dpdGVhIGxhdGVzdC1yZWxlYXNlIEFQSSBVUkwgd2FzIG5vdCBpbnN0YWxsZWQnKTsKfQoKaWYgKHVwZGF0ZXIuaW5jbHVkZXMoJ0xhdGVzdCB2ZXJzaW9uIHJlc3BvbnNlIG1pc3NpbmcgdmVyc2lvbiBzdHJpbmcnKSkgewogIHRocm93IG5ldyBFcnJvcignT2xkIEZsdXhlciBsYXRlc3QtcmVsZWFzZSBwYXJzZXIgaXMgc3RpbGwgcHJlc2VudCcpOwp9Cgpjb25zdCBwb3J0YWJsZUluZGV4ID0gdXBkYXRlci5pbmRleE9mKCdpZiAoaXNQb3J0YWJsZU1vZGUoKSknKTsKY29uc3Qgd2luMzJJbmRleCA9IHVwZGF0ZXIuaW5kZXhPZigKICAiaWYgKHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicpIiwKKTsKCmlmICgKICBwb3J0YWJsZUluZGV4IDwgMCB8fAogIHdpbjMySW5kZXggPCAwIHx8CiAgcG9ydGFibGVJbmRleCA+IHdpbjMySW5kZXgKKSB7CiAgdGhyb3cgbmV3IEVycm9yKAogICAgJ1BvcnRhYmxlIG1vZGUgaXMgbm90IGNoZWNrZWQgYmVmb3JlIHRoZSB3aW4zMiB1cGRhdGVyIGJyYW5jaCcsCiAgKTsKfQoKZnMud3JpdGVGaWxlU3luYyh1cGRhdGVyUGF0aCwgdXBkYXRlciwgJ3V0ZjgnKTsKCmNvbnNvbGUubG9nKCdHU0NPUkQgdXBkYXRlciBjb25maWd1cmVkIHN1Y2Nlc3NmdWxseScpOwpjb25zb2xlLmxvZygKICAnICBSZWxlYXNlczogaHR0cHM6Ly9naXQuZ2FuZGMucnUvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzJywKKTsKY29uc29sZS5sb2coCiAgJyAgQVBJOiBodHRwczovL2dpdC5nYW5kYy5ydS9hcGkvdjEvcmVwb3MvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzL2xhdGVzdCcsCik7CmNvbnNvbGUubG9nKCcgIExpbnV4IG1hbnVhbC9BcHBJbWFnZSBzb3VyY2U6IEdpdGVhJyk7CmNvbnNvbGUubG9nKCcgIFdpbmRvd3MgcG9ydGFibGUgc291cmNlOiBHaXRlYSBtYW51YWwgdXBkYXRlcicpOwpjb25zb2xlLmxvZygnICBVcHN0cmVhbSBwa2dzLmZsdXhlci5jb20gc291cmNlIHJlbW92ZWQnKTsK" ) ) $PatchPath = Join-Path $env:RUNNER_TEMP "patch-gscord-updater.cjs" [System.IO.File]::WriteAllText( $PatchPath, $PatchScript, (New-Object System.Text.UTF8Encoding($false)) ) node $PatchPath Select-String ` -Path "fluxer-src\fluxer_desktop\src\main\UpdaterDownloads.ts" ` -Pattern "git.gandc.ru/gscord/gscord-desktop/releases" Select-String ` -Path "fluxer-src\fluxer_desktop\src\main\Updater.ts" ` -Pattern "api/v1/repos/gscord/gscord-desktop/releases/latest" - name: Apply Electron LiveKit compatibility fixes shell: powershell run: | $ErrorActionPreference = "Stop" $Path = "fluxer-src\fluxer_desktop\src\preload\index.ts" if (-not (Test-Path $Path)) { throw "Desktop preload not found at $Path" } $Content = Get-Content $Path -Raw $Anchor = "import {contextBridge, ipcRenderer, webFrame} from 'electron';" if ($Content -notmatch [regex]::Escape($Anchor)) { throw "Unable to locate Electron preload import" } if ($Content -match "installForceAllIceTransportPolicy") { throw "WebRTC ICE policy patch is already present" } $Patch = [Text.Encoding]::UTF8.GetString( [Convert]::FromBase64String( "aW1wb3J0IHtjb250ZXh0QnJpZGdlLCBpcGNSZW5kZXJlciwgd2ViRnJhbWV9IGZyb20gJ2VsZWN0cm9uJzsKCmZ1bmN0aW9uIGluc3RhbGxGb3JjZUFsbEljZVRyYW5zcG9ydFBvbGljeSgpOiB2b2lkIHsKCWNvbnRleHRCcmlkZ2UuZXhlY3V0ZUluTWFpbldvcmxkKHsKCQlmdW5jOiAoKSA9PiB7CgkJCWNvbnN0IE5hdGl2ZVJUQ1BlZXJDb25uZWN0aW9uID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uOwoKCQkJaWYgKHR5cGVvZiBOYXRpdmVSVENQZWVyQ29ubmVjdGlvbiAhPT0gJ2Z1bmN0aW9uJykgewoJCQkJcmV0dXJuOwoJCQl9CgoJCQl3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gPSBuZXcgUHJveHkoTmF0aXZlUlRDUGVlckNvbm5lY3Rpb24sIHsKCQkJCWNvbnN0cnVjdChUYXJnZXQsIGFyZ3MsIG5ld1RhcmdldCkgewoJCQkJCWNvbnN0IGNvbmZpZyA9IGFyZ3NbMF0gYXMgUlRDQ29uZmlndXJhdGlvbiB8IHVuZGVmaW5lZDsKCgkJCQkJaWYgKGNvbmZpZz8uaWNlVHJhbnNwb3J0UG9saWN5ID09PSAncmVsYXknKSB7CgkJCQkJCWFyZ3NbMF0gPSB7CgkJCQkJCQkuLi5jb25maWcsCgkJCQkJCQlpY2VUcmFuc3BvcnRQb2xpY3k6ICdhbGwnLAoJCQkJCQl9OwoJCQkJCX0KCgkJCQkJcmV0dXJuIFJlZmxlY3QuY29uc3RydWN0KFRhcmdldCwgYXJncywgbmV3VGFyZ2V0KTsKCQkJCX0sCgkJCX0pOwoKCQkJY29uc29sZS5pbmZvKCdbRmx1eGVyIERlc2t0b3BdIFdlYlJUQyBpY2VUcmFuc3BvcnRQb2xpY3kgZm9yY2VkIHRvIGFsbCcpOwoJCX0sCgl9KTsKfQoKZnVuY3Rpb24gaW5zdGFsbERpc2FibGVMaXZlS2l0T2ZmZXJXaXRoSm9pbigpOiB2b2lkIHsKCWNvbnRleHRCcmlkZ2UuZXhlY3V0ZUluTWFpbldvcmxkKHsKCQlmdW5jOiAoKSA9PiB7CgkJCWNvbnN0IE5hdGl2ZVdlYlNvY2tldCA9IHdpbmRvdy5XZWJTb2NrZXQ7CgkJCWNvbnN0IGNvbXByZXNzaW9uRGVzY3JpcHRvciA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoCgkJCQl3aW5kb3csCgkJCQknQ29tcHJlc3Npb25TdHJlYW0nLAoJCQkpOwoKCQkJbGV0IGNvbXByZXNzaW9uU3VwcHJlc3NlZCA9IGZhbHNlOwoKCQkJY29uc3Qgc3VwcHJlc3NDb21wcmVzc2lvblN0cmVhbSA9ICgpOiB2b2lkID0+IHsKCQkJCWlmIChjb21wcmVzc2lvblN1cHByZXNzZWQpIHsKCQkJCQlyZXR1cm47CgkJCQl9CgoJCQkJT2JqZWN0LmRlZmluZVByb3BlcnR5KHdpbmRvdywgJ0NvbXByZXNzaW9uU3RyZWFtJywgewoJCQkJCWNvbmZpZ3VyYWJsZTogdHJ1ZSwKCQkJCQl3cml0YWJsZTogdHJ1ZSwKCQkJCQl2YWx1ZTogdW5kZWZpbmVkLAoJCQkJfSk7CgoJCQkJY29tcHJlc3Npb25TdXBwcmVzc2VkID0gdHJ1ZTsKCQkJfTsKCgkJCWNvbnN0IHJlc3RvcmVDb21wcmVzc2lvblN0cmVhbSA9ICgpOiB2b2lkID0+IHsKCQkJCWlmICghY29tcHJlc3Npb25TdXBwcmVzc2VkKSB7CgkJCQkJcmV0dXJuOwoJCQkJfQoKCQkJCWlmIChjb21wcmVzc2lvbkRlc2NyaXB0b3IpIHsKCQkJCQlPYmplY3QuZGVmaW5lUHJvcGVydHkoCgkJCQkJCXdpbmRvdywKCQkJCQkJJ0NvbXByZXNzaW9uU3RyZWFtJywKCQkJCQkJY29tcHJlc3Npb25EZXNjcmlwdG9yLAoJCQkJCSk7CgkJCQl9IGVsc2UgewoJCQkJCWRlbGV0ZSAod2luZG93IGFzIHR5cGVvZiB3aW5kb3cgJiB7CgkJCQkJCUNvbXByZXNzaW9uU3RyZWFtPzogdHlwZW9mIENvbXByZXNzaW9uU3RyZWFtOwoJCQkJCX0pLkNvbXByZXNzaW9uU3RyZWFtOwoJCQkJfQoKCQkJCWNvbXByZXNzaW9uU3VwcHJlc3NlZCA9IGZhbHNlOwoJCQl9OwoKCQkJY29uc3QgaXNMaXZlS2l0UnRjU29ja2V0ID0gKHZhbHVlOiBzdHJpbmcgfCBVUkwpOiBib29sZWFuID0+IHsKCQkJCXRyeSB7CgkJCQkJY29uc3QgdXJsID0gbmV3IFVSTChTdHJpbmcodmFsdWUpLCB3aW5kb3cubG9jYXRpb24uaHJlZik7CgkJCQkJcmV0dXJuIC9cL3J0Y1wvdjFcLz8kLy50ZXN0KHVybC5wYXRobmFtZSk7CgkJCQl9IGNhdGNoIHsKCQkJCQlyZXR1cm4gZmFsc2U7CgkJCQl9CgkJCX07CgoJCQl3aW5kb3cuV2ViU29ja2V0ID0gbmV3IFByb3h5KE5hdGl2ZVdlYlNvY2tldCwgewoJCQkJY29uc3RydWN0KFRhcmdldCwgYXJncykgewoJCQkJCWNvbnN0IGlzTGl2ZUtpdFJ0YyA9IGlzTGl2ZUtpdFJ0Y1NvY2tldCgKCQkJCQkJYXJnc1swXSBhcyBzdHJpbmcgfCBVUkwsCgkJCQkJKTsKCgkJCQkJaWYgKGlzTGl2ZUtpdFJ0YykgewoJCQkJCQlyZXN0b3JlQ29tcHJlc3Npb25TdHJlYW0oKTsKCQkJCQl9CgoJCQkJCWxldCBzb2NrZXQ6IFdlYlNvY2tldDsKCgkJCQkJdHJ5IHsKCQkJCQkJc29ja2V0ID0gUmVmbGVjdC5jb25zdHJ1Y3QoVGFyZ2V0LCBhcmdzLCBUYXJnZXQpOwoJCQkJCX0gY2F0Y2ggKGVycm9yKSB7CgkJCQkJCWlmIChpc0xpdmVLaXRSdGMpIHsKCQkJCQkJCXN1cHByZXNzQ29tcHJlc3Npb25TdHJlYW0oKTsKCQkJCQkJfQoJCQkJCQl0aHJvdyBlcnJvcjsKCQkJCQl9CgoJCQkJCWlmIChpc0xpdmVLaXRSdGMpIHsKCQkJCQkJc29ja2V0LmFkZEV2ZW50TGlzdGVuZXIoCgkJCQkJCQknY2xvc2UnLAoJCQkJCQkJKCkgPT4gc3VwcHJlc3NDb21wcmVzc2lvblN0cmVhbSgpLAoJCQkJCQkJe29uY2U6IHRydWV9LAoJCQkJCQkpOwoJCQkJCX0KCgkJCQkJcmV0dXJuIHNvY2tldDsKCQkJCX0sCgkJCX0pIGFzIHR5cGVvZiBXZWJTb2NrZXQ7CgoJCQlzdXBwcmVzc0NvbXByZXNzaW9uU3RyZWFtKCk7CgoJCQljb25zb2xlLmluZm8oCgkJCQknW0ZsdXhlciBEZXNrdG9wXSBMaXZlS2l0IHB1Ymxpc2hlciBvZmZlci13aXRoLWpvaW4gZGlzYWJsZWQnLAoJCQkpOwoJCX0sCgl9KTsKfQoKaW5zdGFsbEZvcmNlQWxsSWNlVHJhbnNwb3J0UG9saWN5KCk7Cmluc3RhbGxEaXNhYmxlTGl2ZUtpdE9mZmVyV2l0aEpvaW4oKTsK" ) ) $Content = $Content.Replace( $Anchor, $Patch ) Set-Content ` -Path $Path ` -Value $Content ` -NoNewline ` -Encoding utf8 $Result = Get-Content $Path -Raw if ($Result -notmatch "installForceAllIceTransportPolicy") { throw "WebRTC ICE policy patch verification failed" } if ($Result -notmatch "installDisableLiveKitOfferWithJoin") { throw "LiveKit offer-with-join workaround verification failed" } Write-Host "Installed Electron WebRTC and LiveKit compatibility fixes" Select-String ` -Path $Path ` -Pattern "installForceAllIceTransportPolicy|installDisableLiveKitOfferWithJoin|iceTransportPolicy forced to all|LiveKit publisher offer-with-join disabled" - name: Install dependencies shell: powershell run: | $ErrorActionPreference = "Stop" Set-Location fluxer-src pnpm install --frozen-lockfile - name: Build Fluxer Desktop shell: powershell env: ELECTRON_ARCH: x64 run: | $ErrorActionPreference = "Stop" Set-Location fluxer-src pnpm --filter fluxer_desktop build - name: Package Windows portable shell: powershell env: ELECTRON_ARCH: x64 CSC_IDENTITY_AUTO_DISCOVERY: "false" run: | $ErrorActionPreference = "Stop" Set-Location fluxer-src\fluxer_desktop pnpm exec electron-builder ` --config electron-builder.config.cjs ` --win portable ` --x64 ` --publish never - name: Show Windows artifacts shell: powershell run: | $ErrorActionPreference = "Stop" Get-ChildItem ` -Path "fluxer-src\fluxer_desktop\dist-electron" ` -Recurse | Select-Object FullName, Length - name: Upload Windows artifact uses: actions/upload-artifact@v4.6.2 with: name: fluxer-windows-x64 path: | fluxer-src/fluxer_desktop/dist-electron/*.exe if-no-files-found: error linux-x64: name: Linux x64 runs-on: ubuntu-22.04 env: NEXUS_CARGO_URL: ${{ secrets.NEXUS_CARGO_URL }} NEXUS_MAVEN_URL: ${{ secrets.NEXUS_MAVEN_URL }} NEXUS_NPM_URL: ${{ secrets.NEXUS_NPM_URL }} NEXUS_NODEJS_URL: ${{ secrets.NEXUS_NODEJS_URL }} steps: - name: Checkout CI repository uses: actions/checkout@v4 - name: Prepare RAM CI environment uses: ./.gitea/actions/prepare-ram-ci - name: Clone Fluxer shell: bash run: | set -euxo pipefail rm -rf fluxer-src git clone \ --depth 1 \ --branch main \ https://github.com/fluxerapp/fluxer.git \ fluxer-src test -f \ fluxer-src/fluxer_desktop/src/common/Constants.ts echo "Fluxer revision:" git -C fluxer-src rev-parse HEAD echo "Desktop directory:" ls -la fluxer-src/fluxer_desktop - name: Install Linux build dependencies shell: bash run: | set -euxo pipefail apt-get update DEBIAN_FRONTEND=noninteractive apt-get install -y \ build-essential \ ca-certificates \ clang \ cmake \ curl \ desktop-file-utils \ dpkg-dev \ fakeroot \ ffmpeg \ git \ libasound2 \ libaom-dev \ libavcodec-dev \ libavfilter-dev \ libavformat-dev \ libavutil-dev \ libcbor-dev \ libdbus-1-dev \ libdav1d-dev \ libde265-dev \ libfido2-dev \ libgbm1 \ libgtk-3-0 \ liblcms2-dev \ libnotify4 \ libnss3 \ libpipewire-0.3-dev \ libpulse-dev \ libsecret-1-0 \ libssl-dev \ libswresample-dev \ libswscale-dev \ libudev-dev \ libvips-dev \ libwayland-dev \ libwebp-dev \ libx11-xcb1 \ libxcb-dri3-0 \ libxcomposite1 \ libxdamage1 \ libxkbcommon-dev \ libxkbcommon0 \ libxrandr2 \ libxshmfence1 \ libxss1 \ libxtst6 \ llvm \ meson \ nasm \ ninja-build \ pkg-config \ python3 \ python3-pip \ rpm \ unzip \ xdg-utils \ xz-utils \ yasm \ zlib1g-dev \ zstd - name: Install modern libfido2 shell: bash run: | set -euxo pipefail rm -rf /tmp/libfido2 git clone \ --depth 1 \ --branch 1.17.0 \ https://github.com/Yubico/libfido2.git \ /tmp/libfido2 cmake \ -S /tmp/libfido2 \ -B /tmp/libfido2/build \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_INSTALL_PREFIX=/usr/local \ -DCMAKE_INSTALL_LIBDIR=lib/x86_64-linux-gnu \ -DBUILD_EXAMPLES=OFF \ -DBUILD_MANPAGES=OFF \ -DBUILD_TOOLS=OFF cmake \ --build /tmp/libfido2/build \ --parallel "$(nproc)" cmake \ --install /tmp/libfido2/build ldconfig echo "=== libfido2 ===" PKG_CONFIG_PATH="/usr/local/lib/x86_64-linux-gnu/pkgconfig:/usr/local/lib/pkgconfig:/usr/lib/x86_64-linux-gnu/pkgconfig" \ pkg-config --modversion libfido2 test -f /usr/local/include/fido/es384.h ls -la /usr/local/include/fido/ echo "=== libraries ===" find /usr/local/lib \ -name 'libfido2*' \ -print - name: Install modern PipeWire shell: bash run: | set -euxo pipefail PIPEWIRE_VERSION="1.0.5" rm -rf /tmp/pipewire git clone \ --depth 1 \ --branch "${PIPEWIRE_VERSION}" \ https://github.com/PipeWire/pipewire.git \ /tmp/pipewire meson setup \ /tmp/pipewire/build \ /tmp/pipewire \ --prefix=/usr/local \ --libdir=lib/x86_64-linux-gnu \ --buildtype=release \ -Ddocs=disabled \ -Dman=disabled \ -Dexamples=disabled \ -Dtests=disabled \ -Dinstalled_tests=disabled \ -Dsession-managers=[] \ -Dgstreamer=disabled \ -Dsystemd=disabled \ -Dselinux=disabled \ -Dpipewire-jack=disabled \ -Dpipewire-v4l2=disabled \ -Dbluez5=disabled \ -Djack=disabled \ -Dv4l2=disabled \ -Dlibcamera=disabled \ -Dvulkan=disabled \ -Dsdl2=disabled \ -Dlibmysofa=disabled \ -Droc=disabled \ -Davahi=disabled \ -Decho-cancel-webrtc=disabled \ -Dlibusb=disabled \ -Draop=disabled \ -Dlv2=disabled \ -Dlibcanberra=disabled \ -Dlibffado=disabled meson compile \ -C /tmp/pipewire/build \ -j "$(nproc)" meson install \ -C /tmp/pipewire/build ldconfig export PKG_CONFIG_PATH="/usr/local/lib/x86_64-linux-gnu/pkgconfig:/usr/local/lib/pkgconfig:/usr/lib/x86_64-linux-gnu/pkgconfig" echo "=== PipeWire ===" pkg-config \ --modversion \ libpipewire-0.3 pkg-config \ --modversion \ libspa-0.2 echo "=== PipeWire CFLAGS ===" pkg-config \ --cflags \ libpipewire-0.3 pkg-config \ --cflags \ libspa-0.2 echo "=== Checking headers ===" grep -R \ "spa_meta_region_is_valid" \ /usr/local/include/spa-0.2/spa \ | head echo "=== PipeWire libraries ===" find /usr/local/lib \ \( -name 'libpipewire-0.3.so*' -o -name 'libspa*.so*' \) \ -print - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "24" - name: Setup pnpm shell: bash run: | set -euxo pipefail corepack enable \ --install-directory "$CI_BIN" corepack prepare \ pnpm@10.29.3 \ --activate node --version pnpm --version pnpm store path - name: Setup Rust shell: bash run: | set -euxo pipefail curl \ --proto '=https' \ --tlsv1.2 \ -sSf \ https://sh.rustup.rs | sh -s -- -y --profile minimal echo "$CARGO_HOME/bin" >> "$GITHUB_PATH" "$CARGO_HOME/bin/rustup" update stable "$CARGO_HOME/bin/rustup" default stable "$CARGO_HOME/bin/rustc" --version "$CARGO_HOME/bin/cargo" --version - name: Configure self-hosted server shell: bash run: | set -euxo pipefail python3 <<'PY' from pathlib import Path import re path = Path( "fluxer-src/fluxer_desktop/src/common/Constants.ts" ) if not path.is_file(): raise SystemExit( f"Constants.ts not found: {path}" ) data = path.read_text( encoding="utf-8" ) data, stable_count = re.subn( r"export const STABLE_APP_URL = '[^']+';", "export const STABLE_APP_URL = 'https://ds.gandc.ru';", data, count=1, ) data, canary_count = re.subn( r"export const CANARY_APP_URL = '[^']+';", "export const CANARY_APP_URL = 'https://ds.gandc.ru';", data, count=1, ) if stable_count != 1: raise SystemExit( "Unable to patch STABLE_APP_URL" ) if canary_count != 1: raise SystemExit( "Unable to patch CANARY_APP_URL" ) path.write_text( data, encoding="utf-8" ) result = path.read_text( encoding="utf-8" ) if ( "export const STABLE_APP_URL = " "'https://ds.gandc.ru';" not in result ): raise SystemExit( "STABLE_APP_URL verification failed" ) if ( "export const CANARY_APP_URL = " "'https://ds.gandc.ru';" not in result ): raise SystemExit( "CANARY_APP_URL verification failed" ) print(result) PY python3 <<'PY' from pathlib import Path import json path = Path( "fluxer-src/fluxer_desktop/package.json" ) if not path.is_file(): raise SystemExit( f"package.json not found: {path}" ) package = json.loads( path.read_text( encoding="utf-8" ) ) package["homepage"] = "https://ds.gandc.ru" path.write_text( json.dumps( package, ensure_ascii=False, indent=2, ) + "\n", encoding="utf-8", ) print( "Desktop homepage configured:", package["homepage"], ) PY python3 <<'PY' from pathlib import Path import re path = Path( "fluxer-src/fluxer_desktop/src/main/Window.ts" ) if not path.is_file(): raise SystemExit( f"Window.ts not found: {path}" ) data = path.read_text( encoding="utf-8" ) pattern = re.compile( r"webContents\.on\('will-navigate', \(event, url\) => \{\s*" r"if \(!isTrustedOrigin\(url\)\) \{\s*" r"event\.preventDefault\(\);\s*" r"\}\s*" r"\}\);" ) replacement = "\n".join([ "const isAllowedSsoOrigin = (url: string): boolean => {", "\ttry {", "\t\treturn new URL(url).origin === 'https://sso.gandc.ru';", "\t} catch {", "\t\treturn false;", "\t}", "};", "", "\tconst allowTrustedOrSsoNavigation = (event: Electron.Event, url: string): void => {", "\t\tif (isTrustedOrigin(url) || isAllowedSsoOrigin(url)) {", "\t\t\treturn;", "\t\t}", "", "\t\tlogger.warn('Blocked navigation outside trusted and SSO origins', {url});", "\t\tevent.preventDefault();", "\t};", "", "\twebContents.on('will-navigate', allowTrustedOrSsoNavigation);", "\twebContents.on('will-redirect', allowTrustedOrSsoNavigation);", ]) data, count = pattern.subn( replacement, data, count=1, ) if count != 1: raise SystemExit( "Unable to patch Window.ts SSO navigation handler" ) path.write_text( data, encoding="utf-8", ) print("SSO navigation allowlist patch applied") PY grep -n \ -E 'sso\.gandc\.ru|will-redirect|allowTrustedOrSsoNavigation' \ fluxer-src/fluxer_desktop/src/main/Window.ts - name: Configure GSCORD update source shell: bash run: | set -euxo pipefail PATCH_PATH="$(mktemp --suffix=.cjs)" printf '%s' 'CmNvbnN0IGZzID0gcmVxdWlyZSgnZnMnKTsKCmNvbnN0IGRvd25sb2Fkc1BhdGggPSAnZmx1eGVyLXNyYy9mbHV4ZXJfZGVza3RvcC9zcmMvbWFpbi9VcGRhdGVyRG93bmxvYWRzLnRzJzsKY29uc3QgdXBkYXRlclBhdGggPSAnZmx1eGVyLXNyYy9mbHV4ZXJfZGVza3RvcC9zcmMvbWFpbi9VcGRhdGVyLnRzJzsKCmZ1bmN0aW9uIHJlcGxhY2VSZWdleE9uY2UoaW5wdXQsIHBhdHRlcm4sIHJlcGxhY2VtZW50LCBsYWJlbCkgewogIGxldCBjb3VudCA9IDA7CiAgY29uc3Qgb3V0cHV0ID0gaW5wdXQucmVwbGFjZShwYXR0ZXJuLCAoLi4uYXJncykgPT4gewogICAgY291bnQgKz0gMTsKICAgIHJldHVybiB0eXBlb2YgcmVwbGFjZW1lbnQgPT09ICdmdW5jdGlvbicKICAgICAgPyByZXBsYWNlbWVudCguLi5hcmdzKQogICAgICA6IHJlcGxhY2VtZW50OwogIH0pOwoKICBpZiAoY291bnQgIT09IDEpIHsKICAgIHRocm93IG5ldyBFcnJvcihgVW5hYmxlIHRvIHBhdGNoICR7bGFiZWx9OiBtYXRjaGVzPSR7Y291bnR9YCk7CiAgfQoKICByZXR1cm4gb3V0cHV0Owp9CgpsZXQgZG93bmxvYWRzID0gZnMucmVhZEZpbGVTeW5jKGRvd25sb2Fkc1BhdGgsICd1dGY4Jyk7Cgpkb3dubG9hZHMgPSByZXBsYWNlUmVnZXhPbmNlKAogIGRvd25sb2FkcywKICAvY29uc3QgUEtHU19CQVNFX1VSTCA9ICdodHRwczpcL1wvcGtnc1wuZmx1eGVyXC5jb20nO1xyP1xuZXhwb3J0IGNvbnN0IFVQREFURV9CQVNFX1VSTCA9IGBcJFx7UEtHU19CQVNFX1VSTFx9XC9kZXNrdG9wXC9cJFx7QlVJTERfQ0hBTk5FTFx9XC9cJFx7cHJvY2Vzc1wucGxhdGZvcm1cfVwvXCRce0RFU0tUT1BfRE9XTkxPQURfQVJDSFx9YDtccj9cbmV4cG9ydCBjb25zdCBET1dOTE9BRF9QQUdFX1VSTCA9XHI/XG5ccypCVUlMRF9DSEFOTkVMID09PSAnY2FuYXJ5JyBcPyAnaHR0cHM6XC9cL2NhbmFyeVwuZmx1eGVyXC5hcHBcL2Rvd25sb2FkJyA6ICdodHRwczpcL1wvZmx1eGVyXC5hcHBcL2Rvd25sb2FkJzsvLAogICJleHBvcnQgY29uc3QgVVBEQVRFX0JBU0VfVVJMID0gJ2h0dHBzOi8vZ2l0LmdhbmRjLnJ1L2dzY29yZC9nc2NvcmQtZGVza3RvcC9yZWxlYXNlcyc7XG4iICsKICAgICJleHBvcnQgY29uc3QgRE9XTkxPQURfUEFHRV9VUkwgPSBVUERBVEVfQkFTRV9VUkw7IiwKICAnVXBkYXRlckRvd25sb2FkcyB1cGRhdGUgc291cmNlJywKKTsKCmRvd25sb2FkcyA9IHJlcGxhY2VSZWdleE9uY2UoCiAgZG93bmxvYWRzLAogIC9leHBvcnQgZnVuY3Rpb24gYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmxcKHZlcnNpb246IHN0cmluZywgZm9ybWF0OiBNYW51YWxEZXNrdG9wRm9ybWF0XCk6IHN0cmluZyBce1xyP1xuXHMqcmV0dXJuIGBcJFx7VVBEQVRFX0JBU0VfVVJMXH1cL1wkXHt2ZXJzaW9uXH1cL1wkXHtmb3JtYXRcfWA7XHI/XG5cfS8sCiAgImV4cG9ydCBmdW5jdGlvbiBidWlsZE1hbnVhbFZlcnNpb25Eb3dubG9hZFVybCh2ZXJzaW9uOiBzdHJpbmcsIGZvcm1hdDogTWFudWFsRGVza3RvcEZvcm1hdCk6IHN0cmluZyB7XG4iICsKICAgICJcdHZvaWQgdmVyc2lvbjtcbiIgKwogICAgIlx0dm9pZCBmb3JtYXQ7XG4iICsKICAgICJcdHJldHVybiBET1dOTE9BRF9QQUdFX1VSTDtcbiIgKwogICAgIn0iLAogICdtYW51YWwgZmFsbGJhY2sgVVJMJywKKTsKCmRvd25sb2FkcyA9IHJlcGxhY2VSZWdleE9uY2UoCiAgZG93bmxvYWRzLAogIC91cmw6XHMqYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmxcKGluZm9cLnZlcnNpb24sXHMqZm9ybWF0XCksLywKICAidXJsOiBmaWxlPy51cmwgPz8gYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmwoaW5mby52ZXJzaW9uLCBmb3JtYXQpLCIsCiAgJ21hbnVhbCBhc3NldCBVUkwnLAopOwoKaWYgKGRvd25sb2Fkcy5pbmNsdWRlcygncGtncy5mbHV4ZXIuY29tJykpIHsKICB0aHJvdyBuZXcgRXJyb3IoCiAgICAncGtncy5mbHV4ZXIuY29tIGlzIHN0aWxsIHByZXNlbnQgaW4gVXBkYXRlckRvd25sb2Fkcy50cycsCiAgKTsKfQoKaWYgKAogICFkb3dubG9hZHMuaW5jbHVkZXMoCiAgICAnaHR0cHM6Ly9naXQuZ2FuZGMucnUvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzJywKICApCikgewogIHRocm93IG5ldyBFcnJvcignR1NDT1JEIHJlbGVhc2VzIFVSTCB3YXMgbm90IGluc3RhbGxlZCcpOwp9Cgpmcy53cml0ZUZpbGVTeW5jKGRvd25sb2Fkc1BhdGgsIGRvd25sb2FkcywgJ3V0ZjgnKTsKCmxldCB1cGRhdGVyID0gZnMucmVhZEZpbGVTeW5jKHVwZGF0ZXJQYXRoLCAndXRmOCcpOwoKdXBkYXRlciA9IHJlcGxhY2VSZWdleE9uY2UoCiAgdXBkYXRlciwKICAvY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBmZXRjaFwoYFwkXHtVUERBVEVfQkFTRV9VUkxcfVwvbGF0ZXN0YCwgXHsvLAogICJjb25zdCByZXNwb25zZSA9IGF3YWl0IGZldGNoKFxuIiArCiAgICAiXHRcdCdodHRwczovL2dpdC5nYW5kYy5ydS9hcGkvdjEvcmVwb3MvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzL2xhdGVzdCcsXG4iICsKICAgICJcdFx0eyIsCiAgJ0dpdGVhIGxhdGVzdCByZWxlYXNlIFVSTCcsCik7Cgpjb25zdCBnaXRlYVBhcnNlciA9IGAJY29uc3QgcGF5bG9hZCA9IChhd2FpdCByZXNwb25zZS5qc29uKCkpIGFzIHsKCQl0YWdfbmFtZT86IHVua25vd247CgkJcHVibGlzaGVkX2F0PzogdW5rbm93bjsKCQljcmVhdGVkX2F0PzogdW5rbm93bjsKCQlhc3NldHM/OiB1bmtub3duOwoJfTsKCglpZiAoCgkJdHlwZW9mIHBheWxvYWQudGFnX25hbWUgIT09ICdzdHJpbmcnIHx8CgkJcGF5bG9hZC50YWdfbmFtZS50cmltKCkubGVuZ3RoID09PSAwCgkpIHsKCQl0aHJvdyBuZXcgRXJyb3IoJ0dTQ09SRCBsYXRlc3QgcmVsZWFzZSByZXNwb25zZSBtaXNzaW5nIHRhZ19uYW1lJyk7Cgl9CgoJY29uc3QgdmVyc2lvbiA9IHBheWxvYWQudGFnX25hbWUudHJpbSgpLnJlcGxhY2UoL152LywgJycpOwoJY29uc3QgYXNzZXRzID0gQXJyYXkuaXNBcnJheShwYXlsb2FkLmFzc2V0cykgPyBwYXlsb2FkLmFzc2V0cyA6IFtdOwoJY29uc3QgZmlsZXM6IFBhcnRpYWw8CgkJUmVjb3JkPE1hbnVhbERlc2t0b3BGb3JtYXQsIE1hbnVhbExhdGVzdEZpbGU+Cgk+ID0ge307CgoJY29uc3QgaXNBcm02NCA9IHByb2Nlc3MuYXJjaCA9PT0gJ2FybTY0JzsKCglmb3IgKGNvbnN0IHJhd0Fzc2V0IG9mIGFzc2V0cykgewoJCWlmICghaXNSZWNvcmQocmF3QXNzZXQpKSB7CgkJCWNvbnRpbnVlOwoJCX0KCgkJY29uc3QgbmFtZSA9CgkJCXR5cGVvZiByYXdBc3NldC5uYW1lID09PSAnc3RyaW5nJwoJCQkJPyByYXdBc3NldC5uYW1lCgkJCQk6ICcnOwoKCQljb25zdCB1cmwgPQoJCQl0eXBlb2YgcmF3QXNzZXQuYnJvd3Nlcl9kb3dubG9hZF91cmwgPT09ICdzdHJpbmcnCgkJCQk/IHJhd0Fzc2V0LmJyb3dzZXJfZG93bmxvYWRfdXJsCgkJCQk6ICcnOwoKCQlpZiAoIW5hbWUgfHwgIXVybCkgewoJCQljb250aW51ZTsKCQl9CgoJCWNvbnN0IGxvd2VyTmFtZSA9IG5hbWUudG9Mb3dlckNhc2UoKTsKCgkJaWYgKGlzQXJtNjQpIHsKCQkJaWYgKAoJCQkJIWxvd2VyTmFtZS5pbmNsdWRlcygnYXJtNjQnKSAmJgoJCQkJIWxvd2VyTmFtZS5pbmNsdWRlcygnYWFyY2g2NCcpCgkJCSkgewoJCQkJY29udGludWU7CgkJCX0KCQl9IGVsc2UgaWYgKAoJCQlsb3dlck5hbWUuaW5jbHVkZXMoJ2FybTY0JykgfHwKCQkJbG93ZXJOYW1lLmluY2x1ZGVzKCdhYXJjaDY0JykKCQkpIHsKCQkJY29udGludWU7CgkJfQoKCQlpZiAoL2xpbnV4LXg4Nl82NFxcLkFwcEltYWdlJC9pLnRlc3QobmFtZSkgfHwKCQkJL2xpbnV4LWFybTY0XFwuQXBwSW1hZ2UkL2kudGVzdChuYW1lKSkgewoJCQlmaWxlcy5hcHBpbWFnZSA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCgkJaWYgKC9saW51eC1hbWQ2NFxcLmRlYiQvaS50ZXN0KG5hbWUpIHx8CgkJCS9saW51eC1hcm02NFxcLmRlYiQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLmRlYiA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCgkJaWYgKC9saW51eC14ODZfNjRcXC5ycG0kL2kudGVzdChuYW1lKSB8fAoJCQkvbGludXgtYWFyY2g2NFxcLnJwbSQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLnJwbSA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCgkJaWYgKC9saW51eC14NjRcXC50YXJcXC5neiQvaS50ZXN0KG5hbWUpIHx8CgkJCS9saW51eC1hcm02NFxcLnRhclxcLmd6JC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMudGFyX2d6ID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoKCQlpZiAoL3BvcnRhYmxlLXdpbi14NjRcXC5leGUkL2kudGVzdChuYW1lKSB8fAoJCQkvcG9ydGFibGUtd2luLWFybTY0XFwuZXhlJC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMuc2V0dXAgPSB7dXJsLCBzaGEyNTY6IG51bGx9OwoJCQljb250aW51ZTsKCQl9CgoJCWlmICgvXFwuZG1nJC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMuZG1nID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoKCQlpZiAoL1xcLnppcCQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLnppcCA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJfQoJfQoKCWNvbnN0IGluZm86IE1hbnVhbExhdGVzdEluZm8gPSB7CgkJdmVyc2lvbiwKCQlwdWJEYXRlOgoJCQl0eXBlb2YgcGF5bG9hZC5wdWJsaXNoZWRfYXQgPT09ICdzdHJpbmcnCgkJCQk/IHBheWxvYWQucHVibGlzaGVkX2F0CgkJCQk6IHR5cGVvZiBwYXlsb2FkLmNyZWF0ZWRfYXQgPT09ICdzdHJpbmcnCgkJCQkJPyBwYXlsb2FkLmNyZWF0ZWRfYXQKCQkJCQk6IG51bGwsCgkJZmlsZXMsCgl9O2A7Cgp1cGRhdGVyID0gcmVwbGFjZVJlZ2V4T25jZSgKICB1cGRhdGVyLAogIC9cdGNvbnN0IHBheWxvYWQgPSBcKGF3YWl0IHJlc3BvbnNlXC5qc29uXChcKVwpIGFzIFx7dmVyc2lvblw/OiB1bmtub3duOyBwdWJfZGF0ZVw/OiB1bmtub3duOyBmaWxlc1w/OiB1bmtub3duXH07W1xzXFNdKj9ccj9cblx0Y29uc3QgaW5mbzogTWFudWFsTGF0ZXN0SW5mbyA9IFx7W1xzXFNdKj9ccj9cblx0XH07LywKICBnaXRlYVBhcnNlciwKICAnR2l0ZWEgbGF0ZXN0IHJlbGVhc2UgcGFyc2VyJywKKTsKCmlmICgKICAhdXBkYXRlci5pbmNsdWRlcygKICAgICdodHRwczovL2dpdC5nYW5kYy5ydS9hcGkvdjEvcmVwb3MvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzL2xhdGVzdCcsCiAgKQopIHsKICB0aHJvdyBuZXcgRXJyb3IoJ0dpdGVhIGxhdGVzdC1yZWxlYXNlIEFQSSBVUkwgd2FzIG5vdCBpbnN0YWxsZWQnKTsKfQoKaWYgKHVwZGF0ZXIuaW5jbHVkZXMoJ0xhdGVzdCB2ZXJzaW9uIHJlc3BvbnNlIG1pc3NpbmcgdmVyc2lvbiBzdHJpbmcnKSkgewogIHRocm93IG5ldyBFcnJvcignT2xkIEZsdXhlciBsYXRlc3QtcmVsZWFzZSBwYXJzZXIgaXMgc3RpbGwgcHJlc2VudCcpOwp9Cgpjb25zdCBwb3J0YWJsZUluZGV4ID0gdXBkYXRlci5pbmRleE9mKCdpZiAoaXNQb3J0YWJsZU1vZGUoKSknKTsKY29uc3Qgd2luMzJJbmRleCA9IHVwZGF0ZXIuaW5kZXhPZigKICAiaWYgKHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicpIiwKKTsKCmlmICgKICBwb3J0YWJsZUluZGV4IDwgMCB8fAogIHdpbjMySW5kZXggPCAwIHx8CiAgcG9ydGFibGVJbmRleCA+IHdpbjMySW5kZXgKKSB7CiAgdGhyb3cgbmV3IEVycm9yKAogICAgJ1BvcnRhYmxlIG1vZGUgaXMgbm90IGNoZWNrZWQgYmVmb3JlIHRoZSB3aW4zMiB1cGRhdGVyIGJyYW5jaCcsCiAgKTsKfQoKZnMud3JpdGVGaWxlU3luYyh1cGRhdGVyUGF0aCwgdXBkYXRlciwgJ3V0ZjgnKTsKCmNvbnNvbGUubG9nKCdHU0NPUkQgdXBkYXRlciBjb25maWd1cmVkIHN1Y2Nlc3NmdWxseScpOwpjb25zb2xlLmxvZygKICAnICBSZWxlYXNlczogaHR0cHM6Ly9naXQuZ2FuZGMucnUvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzJywKKTsKY29uc29sZS5sb2coCiAgJyAgQVBJOiBodHRwczovL2dpdC5nYW5kYy5ydS9hcGkvdjEvcmVwb3MvZ3Njb3JkL2dzY29yZC1kZXNrdG9wL3JlbGVhc2VzL2xhdGVzdCcsCik7CmNvbnNvbGUubG9nKCcgIExpbnV4IG1hbnVhbC9BcHBJbWFnZSBzb3VyY2U6IEdpdGVhJyk7CmNvbnNvbGUubG9nKCcgIFdpbmRvd3MgcG9ydGFibGUgc291cmNlOiBHaXRlYSBtYW51YWwgdXBkYXRlcicpOwpjb25zb2xlLmxvZygnICBVcHN0cmVhbSBwa2dzLmZsdXhlci5jb20gc291cmNlIHJlbW92ZWQnKTsK' | base64 -d > "$PATCH_PATH" node "$PATCH_PATH" grep -n 'git.gandc.ru/gscord/gscord-desktop/releases' fluxer-src/fluxer_desktop/src/main/UpdaterDownloads.ts grep -n 'api/v1/repos/gscord/gscord-desktop/releases/latest' fluxer-src/fluxer_desktop/src/main/Updater.ts - name: Apply Electron LiveKit compatibility fixes shell: bash run: | set -euxo pipefail python3 - <<'PY' from pathlib import Path import base64 path = Path( "fluxer-src/fluxer_desktop/" "src/preload/index.ts" ) if not path.is_file(): raise SystemExit( f"Desktop preload not found: {path}" ) data = path.read_text( encoding="utf-8" ) anchor = "import {contextBridge, ipcRenderer, webFrame} from 'electron';" if anchor not in data: raise SystemExit( "Unable to locate Electron preload import" ) if "installForceAllIceTransportPolicy" in data: raise SystemExit( "WebRTC ICE policy patch is already present" ) patch = base64.b64decode( "aW1wb3J0IHtjb250ZXh0QnJpZGdlLCBpcGNSZW5kZXJlciwgd2ViRnJhbWV9IGZyb20gJ2VsZWN0cm9uJzsKCmZ1bmN0aW9uIGluc3RhbGxGb3JjZUFsbEljZVRyYW5zcG9ydFBvbGljeSgpOiB2b2lkIHsKCWNvbnRleHRCcmlkZ2UuZXhlY3V0ZUluTWFpbldvcmxkKHsKCQlmdW5jOiAoKSA9PiB7CgkJCWNvbnN0IE5hdGl2ZVJUQ1BlZXJDb25uZWN0aW9uID0gd2luZG93LlJUQ1BlZXJDb25uZWN0aW9uOwoKCQkJaWYgKHR5cGVvZiBOYXRpdmVSVENQZWVyQ29ubmVjdGlvbiAhPT0gJ2Z1bmN0aW9uJykgewoJCQkJcmV0dXJuOwoJCQl9CgoJCQl3aW5kb3cuUlRDUGVlckNvbm5lY3Rpb24gPSBuZXcgUHJveHkoTmF0aXZlUlRDUGVlckNvbm5lY3Rpb24sIHsKCQkJCWNvbnN0cnVjdChUYXJnZXQsIGFyZ3MsIG5ld1RhcmdldCkgewoJCQkJCWNvbnN0IGNvbmZpZyA9IGFyZ3NbMF0gYXMgUlRDQ29uZmlndXJhdGlvbiB8IHVuZGVmaW5lZDsKCgkJCQkJaWYgKGNvbmZpZz8uaWNlVHJhbnNwb3J0UG9saWN5ID09PSAncmVsYXknKSB7CgkJCQkJCWFyZ3NbMF0gPSB7CgkJCQkJCQkuLi5jb25maWcsCgkJCQkJCQlpY2VUcmFuc3BvcnRQb2xpY3k6ICdhbGwnLAoJCQkJCQl9OwoJCQkJCX0KCgkJCQkJcmV0dXJuIFJlZmxlY3QuY29uc3RydWN0KFRhcmdldCwgYXJncywgbmV3VGFyZ2V0KTsKCQkJCX0sCgkJCX0pOwoKCQkJY29uc29sZS5pbmZvKCdbRmx1eGVyIERlc2t0b3BdIFdlYlJUQyBpY2VUcmFuc3BvcnRQb2xpY3kgZm9yY2VkIHRvIGFsbCcpOwoJCX0sCgl9KTsKfQoKZnVuY3Rpb24gaW5zdGFsbERpc2FibGVMaXZlS2l0T2ZmZXJXaXRoSm9pbigpOiB2b2lkIHsKCWNvbnRleHRCcmlkZ2UuZXhlY3V0ZUluTWFpbldvcmxkKHsKCQlmdW5jOiAoKSA9PiB7CgkJCWNvbnN0IE5hdGl2ZVdlYlNvY2tldCA9IHdpbmRvdy5XZWJTb2NrZXQ7CgkJCWNvbnN0IGNvbXByZXNzaW9uRGVzY3JpcHRvciA9IE9iamVjdC5nZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoCgkJCQl3aW5kb3csCgkJCQknQ29tcHJlc3Npb25TdHJlYW0nLAoJCQkpOwoKCQkJbGV0IGNvbXByZXNzaW9uU3VwcHJlc3NlZCA9IGZhbHNlOwoKCQkJY29uc3Qgc3VwcHJlc3NDb21wcmVzc2lvblN0cmVhbSA9ICgpOiB2b2lkID0+IHsKCQkJCWlmIChjb21wcmVzc2lvblN1cHByZXNzZWQpIHsKCQkJCQlyZXR1cm47CgkJCQl9CgoJCQkJT2JqZWN0LmRlZmluZVByb3BlcnR5KHdpbmRvdywgJ0NvbXByZXNzaW9uU3RyZWFtJywgewoJCQkJCWNvbmZpZ3VyYWJsZTogdHJ1ZSwKCQkJCQl3cml0YWJsZTogdHJ1ZSwKCQkJCQl2YWx1ZTogdW5kZWZpbmVkLAoJCQkJfSk7CgoJCQkJY29tcHJlc3Npb25TdXBwcmVzc2VkID0gdHJ1ZTsKCQkJfTsKCgkJCWNvbnN0IHJlc3RvcmVDb21wcmVzc2lvblN0cmVhbSA9ICgpOiB2b2lkID0+IHsKCQkJCWlmICghY29tcHJlc3Npb25TdXBwcmVzc2VkKSB7CgkJCQkJcmV0dXJuOwoJCQkJfQoKCQkJCWlmIChjb21wcmVzc2lvbkRlc2NyaXB0b3IpIHsKCQkJCQlPYmplY3QuZGVmaW5lUHJvcGVydHkoCgkJCQkJCXdpbmRvdywKCQkJCQkJJ0NvbXByZXNzaW9uU3RyZWFtJywKCQkJCQkJY29tcHJlc3Npb25EZXNjcmlwdG9yLAoJCQkJCSk7CgkJCQl9IGVsc2UgewoJCQkJCWRlbGV0ZSAod2luZG93IGFzIHR5cGVvZiB3aW5kb3cgJiB7CgkJCQkJCUNvbXByZXNzaW9uU3RyZWFtPzogdHlwZW9mIENvbXByZXNzaW9uU3RyZWFtOwoJCQkJCX0pLkNvbXByZXNzaW9uU3RyZWFtOwoJCQkJfQoKCQkJCWNvbXByZXNzaW9uU3VwcHJlc3NlZCA9IGZhbHNlOwoJCQl9OwoKCQkJY29uc3QgaXNMaXZlS2l0UnRjU29ja2V0ID0gKHZhbHVlOiBzdHJpbmcgfCBVUkwpOiBib29sZWFuID0+IHsKCQkJCXRyeSB7CgkJCQkJY29uc3QgdXJsID0gbmV3IFVSTChTdHJpbmcodmFsdWUpLCB3aW5kb3cubG9jYXRpb24uaHJlZik7CgkJCQkJcmV0dXJuIC9cL3J0Y1wvdjFcLz8kLy50ZXN0KHVybC5wYXRobmFtZSk7CgkJCQl9IGNhdGNoIHsKCQkJCQlyZXR1cm4gZmFsc2U7CgkJCQl9CgkJCX07CgoJCQl3aW5kb3cuV2ViU29ja2V0ID0gbmV3IFByb3h5KE5hdGl2ZVdlYlNvY2tldCwgewoJCQkJY29uc3RydWN0KFRhcmdldCwgYXJncykgewoJCQkJCWNvbnN0IGlzTGl2ZUtpdFJ0YyA9IGlzTGl2ZUtpdFJ0Y1NvY2tldCgKCQkJCQkJYXJnc1swXSBhcyBzdHJpbmcgfCBVUkwsCgkJCQkJKTsKCgkJCQkJaWYgKGlzTGl2ZUtpdFJ0YykgewoJCQkJCQlyZXN0b3JlQ29tcHJlc3Npb25TdHJlYW0oKTsKCQkJCQl9CgoJCQkJCWxldCBzb2NrZXQ6IFdlYlNvY2tldDsKCgkJCQkJdHJ5IHsKCQkJCQkJc29ja2V0ID0gUmVmbGVjdC5jb25zdHJ1Y3QoVGFyZ2V0LCBhcmdzLCBUYXJnZXQpOwoJCQkJCX0gY2F0Y2ggKGVycm9yKSB7CgkJCQkJCWlmIChpc0xpdmVLaXRSdGMpIHsKCQkJCQkJCXN1cHByZXNzQ29tcHJlc3Npb25TdHJlYW0oKTsKCQkJCQkJfQoJCQkJCQl0aHJvdyBlcnJvcjsKCQkJCQl9CgoJCQkJCWlmIChpc0xpdmVLaXRSdGMpIHsKCQkJCQkJc29ja2V0LmFkZEV2ZW50TGlzdGVuZXIoCgkJCQkJCQknY2xvc2UnLAoJCQkJCQkJKCkgPT4gc3VwcHJlc3NDb21wcmVzc2lvblN0cmVhbSgpLAoJCQkJCQkJe29uY2U6IHRydWV9LAoJCQkJCQkpOwoJCQkJCX0KCgkJCQkJcmV0dXJuIHNvY2tldDsKCQkJCX0sCgkJCX0pIGFzIHR5cGVvZiBXZWJTb2NrZXQ7CgoJCQlzdXBwcmVzc0NvbXByZXNzaW9uU3RyZWFtKCk7CgoJCQljb25zb2xlLmluZm8oCgkJCQknW0ZsdXhlciBEZXNrdG9wXSBMaXZlS2l0IHB1Ymxpc2hlciBvZmZlci13aXRoLWpvaW4gZGlzYWJsZWQnLAoJCQkpOwoJCX0sCgl9KTsKfQoKaW5zdGFsbEZvcmNlQWxsSWNlVHJhbnNwb3J0UG9saWN5KCk7Cmluc3RhbGxEaXNhYmxlTGl2ZUtpdE9mZmVyV2l0aEpvaW4oKTsK" ).decode("utf-8") data = data.replace( anchor, patch, 1, ) path.write_text( data, encoding="utf-8", ) result = path.read_text( encoding="utf-8" ) if "installForceAllIceTransportPolicy" not in result: raise SystemExit( "WebRTC ICE policy patch verification failed" ) if "installDisableLiveKitOfferWithJoin" not in result: raise SystemExit( "LiveKit offer-with-join workaround verification failed" ) print( "Installed Electron WebRTC and LiveKit compatibility fixes" ) PY grep -n \ -E 'installForceAllIceTransportPolicy|installDisableLiveKitOfferWithJoin|iceTransportPolicy forced to all|LiveKit publisher offer-with-join disabled' \ fluxer-src/fluxer_desktop/src/preload/index.ts - name: Install dependencies shell: bash run: | set -euxo pipefail cd fluxer-src pnpm install --frozen-lockfile - name: Build Fluxer Desktop shell: bash env: ELECTRON_ARCH: x64 PKG_CONFIG_PATH: /usr/local/lib/x86_64-linux-gnu/pkgconfig:/usr/local/lib/pkgconfig:/usr/lib/x86_64-linux-gnu/pkgconfig LD_LIBRARY_PATH: /usr/local/lib/x86_64-linux-gnu:/usr/local/lib run: | set -euxo pipefail echo "libfido2 version:" pkg-config --modversion libfido2 echo "libfido2 include flags:" pkg-config --cflags libfido2 echo "Checking ES384 header:" test -f /usr/local/include/fido/es384.h cd fluxer-src pnpm --filter fluxer_desktop build - name: Inspect Linux native libraries shell: bash run: | set -euxo pipefail cd fluxer-src echo "Searching for WebAuthn native module..." find fluxer_desktop \ -type f \ \( -name '*.node' -o -name 'libfido2.so*' \) \ -print echo "Dynamic dependencies:" while IFS= read -r file; do echo "===== $file =====" ldd "$file" || true done < <( find fluxer_desktop \ -type f \ -name '*.node' ) - name: Package Linux shell: bash env: ELECTRON_ARCH: x64 CSC_IDENTITY_AUTO_DISCOVERY: "false" run: | set -euxo pipefail cd fluxer-src/fluxer_desktop pnpm exec electron-builder \ --config electron-builder.config.cjs \ --linux AppImage deb \ --x64 \ --publish never - name: Show Linux artifacts shell: bash run: | set -euxo pipefail find \ fluxer-src/fluxer_desktop/dist-electron \ -maxdepth 2 \ -type f \ -printf '%p %s bytes\n' | sort - name: Upload Linux artifacts uses: actions/upload-artifact@v4.6.2 with: name: fluxer-linux-x64 path: | fluxer-src/fluxer_desktop/dist-electron/*.AppImage fluxer-src/fluxer_desktop/dist-electron/*.deb if-no-files-found: error