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( "CmNvbnN0IGZzID0gcmVxdWlyZSgnZnMnKTsKCmNvbnN0IGRvd25sb2Fkc1BhdGggPSAnZmx1eGVyLXNyYy9mbHV4ZXJfZGVza3RvcC9zcmMvbWFpbi9VcGRhdGVyRG93bmxvYWRzLnRzJzsKY29uc3QgdXBkYXRlclBhdGggPSAnZmx1eGVyLXNyYy9mbHV4ZXJfZGVza3RvcC9zcmMvbWFpbi9VcGRhdGVyLnRzJzsKCmZ1bmN0aW9uIHJlcGxhY2VPbmNlKGlucHV0LCBwYXR0ZXJuLCByZXBsYWNlbWVudCwgbGFiZWwpIHsKICBjb25zdCBbYmVmb3JlLCAuLi5yZXN0XSA9IGlucHV0LnNwbGl0KHBhdHRlcm4pOwogIGlmIChyZXN0Lmxlbmd0aCA9PT0gMCkgewogICAgdGhyb3cgbmV3IEVycm9yKGBVbmFibGUgdG8gcGF0Y2ggJHtsYWJlbH1gKTsKICB9CiAgaWYgKHJlc3QubGVuZ3RoID4gMSkgewogICAgdGhyb3cgbmV3IEVycm9yKGBQYXRjaCB0YXJnZXQgaXMgYW1iaWd1b3VzIGZvciAke2xhYmVsfWApOwogIH0KICByZXR1cm4gYmVmb3JlICsgcmVwbGFjZW1lbnQgKyByZXN0WzBdOwp9CgpmdW5jdGlvbiByZXBsYWNlUmVnZXhPbmNlKGlucHV0LCBwYXR0ZXJuLCByZXBsYWNlbWVudCwgbGFiZWwpIHsKICBsZXQgY291bnQgPSAwOwogIGNvbnN0IG91dHB1dCA9IGlucHV0LnJlcGxhY2UocGF0dGVybiwgKC4uLmFyZ3MpID0+IHsKICAgIGNvdW50ICs9IDE7CiAgICByZXR1cm4gdHlwZW9mIHJlcGxhY2VtZW50ID09PSAnZnVuY3Rpb24nID8gcmVwbGFjZW1lbnQoLi4uYXJncykgOiByZXBsYWNlbWVudDsKICB9KTsKICBpZiAoY291bnQgIT09IDEpIHsKICAgIHRocm93IG5ldyBFcnJvcihgVW5hYmxlIHRvIHBhdGNoICR7bGFiZWx9OiBtYXRjaGVzPSR7Y291bnR9YCk7CiAgfQogIHJldHVybiBvdXRwdXQ7Cn0KCmxldCBkb3dubG9hZHMgPSBmcy5yZWFkRmlsZVN5bmMoZG93bmxvYWRzUGF0aCwgJ3V0ZjgnKTsKCmRvd25sb2FkcyA9IHJlcGxhY2VPbmNlKAogIGRvd25sb2FkcywKICAiY29uc3QgUEtHU19CQVNFX1VSTCA9ICdodHRwczovL3BrZ3MuZmx1eGVyLmNvbSc7XG4iICsKICAgICJleHBvcnQgY29uc3QgVVBEQVRFX0JBU0VfVVJMID0gYCR7UEtHU19CQVNFX1VSTH0vZGVza3RvcC8ke0JVSUxEX0NIQU5ORUx9LyR7cHJvY2Vzcy5wbGF0Zm9ybX0vJHtERVNLVE9QX0RPV05MT0FEX0FSQ0h9YDtcbiIgKwogICAgImV4cG9ydCBjb25zdCBET1dOTE9BRF9QQUdFX1VSTCA9XG4iICsKICAgICJcdEJVSUxEX0NIQU5ORUwgPT09ICdjYW5hcnknID8gJ2h0dHBzOi8vY2FuYXJ5LmZsdXhlci5hcHAvZG93bmxvYWQnIDogJ2h0dHBzOi8vZmx1eGVyLmFwcC9kb3dubG9hZCc7IiwKICAiZXhwb3J0IGNvbnN0IFVQREFURV9CQVNFX1VSTCA9ICdodHRwczovL2dpdC5nYW5kYy5ydS9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMnO1xuIiArCiAgICAiZXhwb3J0IGNvbnN0IERPV05MT0FEX1BBR0VfVVJMID0gVVBEQVRFX0JBU0VfVVJMOyIsCiAgJ1VwZGF0ZXJEb3dubG9hZHMgdXBkYXRlIHNvdXJjZScsCik7Cgpkb3dubG9hZHMgPSByZXBsYWNlT25jZSgKICBkb3dubG9hZHMsCiAgIlx0cmV0dXJuIGAke1VQREFURV9CQVNFX1VSTH0vJHt2ZXJzaW9ufS8ke2Zvcm1hdH1gOyIsCiAgIlx0dm9pZCB2ZXJzaW9uO1xuXHR2b2lkIGZvcm1hdDtcblx0cmV0dXJuIERPV05MT0FEX1BBR0VfVVJMOyIsCiAgJ21hbnVhbCBmYWxsYmFjayBVUkwnLAopOwoKZG93bmxvYWRzID0gcmVwbGFjZU9uY2UoCiAgZG93bmxvYWRzLAogICJcdFx0XHRcdHVybDogYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmwoaW5mby52ZXJzaW9uLCBmb3JtYXQpLCIsCiAgIlx0XHRcdFx0dXJsOiBmaWxlPy51cmwgPz8gYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmwoaW5mby52ZXJzaW9uLCBmb3JtYXQpLCIsCiAgJ21hbnVhbCBhc3NldCBVUkwnLAopOwoKaWYgKGRvd25sb2Fkcy5pbmNsdWRlcygncGtncy5mbHV4ZXIuY29tJykpIHsKICB0aHJvdyBuZXcgRXJyb3IoJ3BrZ3MuZmx1eGVyLmNvbSBpcyBzdGlsbCBwcmVzZW50IGluIFVwZGF0ZXJEb3dubG9hZHMudHMnKTsKfQoKZnMud3JpdGVGaWxlU3luYyhkb3dubG9hZHNQYXRoLCBkb3dubG9hZHMsICd1dGY4Jyk7CgpsZXQgdXBkYXRlciA9IGZzLnJlYWRGaWxlU3luYyh1cGRhdGVyUGF0aCwgJ3V0ZjgnKTsKCmNvbnN0IGdpdGVhRmV0Y2ggPSBgYXN5bmMgZnVuY3Rpb24gZmV0Y2hNYW51YWxMYXRlc3Qob3B0aW9uczoge2ZvcmNlUmVmcmVzaD86IGJvb2xlYW59ID0ge30pOiBQcm9taXNlPE1hbnVhbExhdGVzdEluZm8+IHsKCWNvbnN0IG5vdyA9IERhdGUubm93KCk7CglpZiAoIW9wdGlvbnMuZm9yY2VSZWZyZXNoICYmIG1hbnVhbExhdGVzdENhY2hlICYmIG5vdyAtIG1hbnVhbExhdGVzdENhY2hlLmF0IDwgTUFOVUFMX0NBQ0hFX1RUTF9NUykgewoJCXJldHVybiBtYW51YWxMYXRlc3RDYWNoZS5pbmZvOwoJfQoKCWNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goCgkJJ2h0dHBzOi8vZ2l0LmdhbmRjLnJ1L2FwaS92MS9yZXBvcy9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMvbGF0ZXN0JywKCQl7CgkJCWNhY2hlOiAnbm8tc3RvcmUnLAoJCQloZWFkZXJzOiB7CgkJCQlBY2NlcHQ6ICdhcHBsaWNhdGlvbi9qc29uJywKCQkJCSdDYWNoZS1Db250cm9sJzogJ25vLWNhY2hlJywKCQkJCVByYWdtYTogJ25vLWNhY2hlJywKCQkJfSwKCQl9LAoJKTsKCglpZiAoIXJlc3BvbnNlLm9rKSB7CgkJdGhyb3cgbmV3IEVycm9yKFxgR1NDT1JEIGxhdGVzdCByZWxlYXNlIHJlcXVlc3QgZmFpbGVkOiBcJHtyZXNwb25zZS5zdGF0dXN9XGApOwoJfQoKCWNvbnN0IHBheWxvYWQgPSAoYXdhaXQgcmVzcG9uc2UuanNvbigpKSBhcyB7CgkJdGFnX25hbWU/OiB1bmtub3duOwoJCXB1Ymxpc2hlZF9hdD86IHVua25vd247CgkJY3JlYXRlZF9hdD86IHVua25vd247CgkJYXNzZXRzPzogdW5rbm93bjsKCX07CgoJaWYgKHR5cGVvZiBwYXlsb2FkLnRhZ19uYW1lICE9PSAnc3RyaW5nJyB8fCBwYXlsb2FkLnRhZ19uYW1lLnRyaW0oKS5sZW5ndGggPT09IDApIHsKCQl0aHJvdyBuZXcgRXJyb3IoJ0dTQ09SRCBsYXRlc3QgcmVsZWFzZSByZXNwb25zZSBtaXNzaW5nIHRhZ19uYW1lJyk7Cgl9CgoJY29uc3QgdmVyc2lvbiA9IHBheWxvYWQudGFnX25hbWUudHJpbSgpLnJlcGxhY2UoL152LywgJycpOwoJY29uc3QgYXNzZXRzID0gQXJyYXkuaXNBcnJheShwYXlsb2FkLmFzc2V0cykgPyBwYXlsb2FkLmFzc2V0cyA6IFtdOwoJY29uc3QgZmlsZXM6IFBhcnRpYWw8UmVjb3JkPE1hbnVhbERlc2t0b3BGb3JtYXQsIE1hbnVhbExhdGVzdEZpbGU+PiA9IHt9OwoJY29uc3QgaXNBcm02NCA9IHByb2Nlc3MuYXJjaCA9PT0gJ2FybTY0JzsKCglmb3IgKGNvbnN0IHJhd0Fzc2V0IG9mIGFzc2V0cykgewoJCWlmICghaXNSZWNvcmQocmF3QXNzZXQpKSBjb250aW51ZTsKCgkJY29uc3QgbmFtZSA9IHR5cGVvZiByYXdBc3NldC5uYW1lID09PSAnc3RyaW5nJyA/IHJhd0Fzc2V0Lm5hbWUgOiAnJzsKCQljb25zdCB1cmwgPQoJCQl0eXBlb2YgcmF3QXNzZXQuYnJvd3Nlcl9kb3dubG9hZF91cmwgPT09ICdzdHJpbmcnCgkJCQk/IHJhd0Fzc2V0LmJyb3dzZXJfZG93bmxvYWRfdXJsCgkJCQk6ICcnOwoKCQlpZiAoIW5hbWUgfHwgIXVybCkgY29udGludWU7CgoJCWNvbnN0IGxvd2VyID0gbmFtZS50b0xvd2VyQ2FzZSgpOwoJCWNvbnN0IGxvb2tzQXJtNjQgPSBsb3dlci5pbmNsdWRlcygnYXJtNjQnKSB8fCBsb3dlci5pbmNsdWRlcygnYWFyY2g2NCcpOwoJCWlmIChpc0FybTY0ICE9PSBsb29rc0FybTY0ICYmIChsb29rc0FybTY0IHx8IC8oPzp4NjR8eDg2XzY0fGFtZDY0KS9pLnRlc3QobmFtZSkpKSB7CgkJCWNvbnRpbnVlOwoJCX0KCgkJaWYgKC9cXC5BcHBJbWFnZSQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLmFwcGltYWdlID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoJCWlmICgvXFwuZGViJC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMuZGViID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoJCWlmICgvXFwucnBtJC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMucnBtID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoJCWlmICgvXFwudGFyXFwuZ3okL2kudGVzdChuYW1lKSkgewoJCQlmaWxlcy50YXJfZ3ogPSB7dXJsLCBzaGEyNTY6IG51bGx9OwoJCQljb250aW51ZTsKCQl9CgkJaWYgKC9cXC5leGUkL2kudGVzdChuYW1lKSkgewoJCQlmaWxlcy5zZXR1cCA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCQlpZiAoL1xcLmRtZyQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLmRtZyA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCQlpZiAoL1xcLnppcCQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLnppcCA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJfQoJfQoKCWNvbnN0IGluZm86IE1hbnVhbExhdGVzdEluZm8gPSB7CgkJdmVyc2lvbiwKCQlwdWJEYXRlOgoJCQl0eXBlb2YgcGF5bG9hZC5wdWJsaXNoZWRfYXQgPT09ICdzdHJpbmcnCgkJCQk/IHBheWxvYWQucHVibGlzaGVkX2F0CgkJCQk6IHR5cGVvZiBwYXlsb2FkLmNyZWF0ZWRfYXQgPT09ICdzdHJpbmcnCgkJCQkJPyBwYXlsb2FkLmNyZWF0ZWRfYXQKCQkJCQk6IG51bGwsCgkJZmlsZXMsCgl9OwoKCW1hbnVhbExhdGVzdENhY2hlID0ge2F0OiBub3csIGluZm99OwoJcmV0dXJuIGluZm87Cn1gOwoKdXBkYXRlciA9IHJlcGxhY2VSZWdleE9uY2UoCiAgdXBkYXRlciwKICAvYXN5bmMgZnVuY3Rpb24gZmV0Y2hNYW51YWxMYXRlc3RcKG9wdGlvbnM6IFx7Zm9yY2VSZWZyZXNoXD86IGJvb2xlYW5cfSA9IFx7XH1cKTogUHJvbWlzZTxNYW51YWxMYXRlc3RJbmZvPiBce1tcc1xTXSo/XG5cfVxuZnVuY3Rpb24gc2VuZE1hbnVhbFVwZGF0ZUF2YWlsYWJsZS8sCiAgYCR7Z2l0ZWFGZXRjaH1cbmZ1bmN0aW9uIHNlbmRNYW51YWxVcGRhdGVBdmFpbGFibGVgLAogICdHaXRlYSBsYXRlc3QgcmVsZWFzZSByZXNvbHZlcicsCik7Cgp1cGRhdGVyID0gcmVwbGFjZU9uY2UoCiAgdXBkYXRlciwKICAiXHRjb25zdCB1cmwgPSBidWlsZE1hbnVhbFZlcnNpb25Eb3dubG9hZFVybCh2ZXJzaW9uLCAnYXBwaW1hZ2UnKTsiLAogICJcdGNvbnN0IHVybCA9IGxhdGVzdC5maWxlcy5hcHBpbWFnZT8udXJsID8/IGJ1aWxkTWFudWFsVmVyc2lvbkRvd25sb2FkVXJsKHZlcnNpb24sICdhcHBpbWFnZScpOyIsCiAgJ0FwcEltYWdlIGFzc2V0IFVSTCcsCik7CgppZiAoIXVwZGF0ZXIuaW5jbHVkZXMoJ2h0dHBzOi8vZ2l0LmdhbmRjLnJ1L2FwaS92MS9yZXBvcy9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMvbGF0ZXN0JykpIHsKICB0aHJvdyBuZXcgRXJyb3IoJ0dpdGVhIHJlbGVhc2VzIEFQSSBVUkwgd2FzIG5vdCBpbnN0YWxsZWQnKTsKfQoKZnMud3JpdGVGaWxlU3luYyh1cGRhdGVyUGF0aCwgdXBkYXRlciwgJ3V0ZjgnKTsKCi8vIFN0YXRpYyB2YWxpZGF0aW9uIGZvciB0aGUgV2luZG93cyBwb3J0YWJsZSBicmFuY2guCi8vIEluIHRoZSBjdXJyZW50IHVwc3RyZWFtIHJlZ2lzdGVyVXBkYXRlcigpLCBpc1BvcnRhYmxlTW9kZSgpIGlzIGNoZWNrZWQgYmVmb3JlIHdpbjMyLAovLyBzbyB0aGUgcG9ydGFibGUgLmV4ZSB1c2VzIHJlZ2lzdGVyTWFudWFsVXBkYXRlcigpLCBpLmUuIHRoZSBzYW1lIEdpdGVhIGZldGNoIGFib3ZlLgpjb25zdCBwb3J0YWJsZUluZGV4ID0gdXBkYXRlci5pbmRleE9mKCdpZiAoaXNQb3J0YWJsZU1vZGUoKSknKTsKY29uc3Qgd2luMzJJbmRleCA9IHVwZGF0ZXIuaW5kZXhPZigiaWYgKHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicpIik7CmlmIChwb3J0YWJsZUluZGV4IDwgMCB8fCB3aW4zMkluZGV4IDwgMCB8fCBwb3J0YWJsZUluZGV4ID4gd2luMzJJbmRleCkgewogIHRocm93IG5ldyBFcnJvcignVW5leHBlY3RlZCB1cGRhdGVyIGJyYW5jaCBvcmRlcjogcG9ydGFibGUgbW9kZSBpcyBub3QgY2hlY2tlZCBiZWZvcmUgd2luMzInKTsKfQoKY29uc29sZS5sb2coJ0dTQ09SRCB1cGRhdGVyIGNvbmZpZ3VyZWQgc3VjY2Vzc2Z1bGx5Jyk7CmNvbnNvbGUubG9nKCcgIFJlbGVhc2VzOiBodHRwczovL2dpdC5nYW5kYy5ydS9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMnKTsKY29uc29sZS5sb2coJyAgQVBJOiAgICAgIGh0dHBzOi8vZ2l0LmdhbmRjLnJ1L2FwaS92MS9yZXBvcy9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMvbGF0ZXN0Jyk7CmNvbnNvbGUubG9nKCcgIExpbnV4IG1hbnVhbC9BcHBJbWFnZSBzb3VyY2U6IEdpdGVhJyk7CmNvbnNvbGUubG9nKCcgIFdpbmRvd3MgcG9ydGFibGUgc291cmNlOiBHaXRlYSBtYW51YWwgdXBkYXRlcicpOwpjb25zb2xlLmxvZygnICBwa2dzLmZsdXhlci5jb20gcmVtb3ZlZCBmcm9tIFVwZGF0ZXJEb3dubG9hZHMudHMnKTsK" ) ) $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 steps: - 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 corepack prepare pnpm@10.29.3 --activate node --version pnpm --version - 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 "$HOME/.cargo/bin" >> "$GITHUB_PATH" "$HOME/.cargo/bin/rustup" update stable "$HOME/.cargo/bin/rustup" default stable "$HOME/.cargo/bin/rustc" --version "$HOME/.cargo/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' 'CmNvbnN0IGZzID0gcmVxdWlyZSgnZnMnKTsKCmNvbnN0IGRvd25sb2Fkc1BhdGggPSAnZmx1eGVyLXNyYy9mbHV4ZXJfZGVza3RvcC9zcmMvbWFpbi9VcGRhdGVyRG93bmxvYWRzLnRzJzsKY29uc3QgdXBkYXRlclBhdGggPSAnZmx1eGVyLXNyYy9mbHV4ZXJfZGVza3RvcC9zcmMvbWFpbi9VcGRhdGVyLnRzJzsKCmZ1bmN0aW9uIHJlcGxhY2VPbmNlKGlucHV0LCBwYXR0ZXJuLCByZXBsYWNlbWVudCwgbGFiZWwpIHsKICBjb25zdCBbYmVmb3JlLCAuLi5yZXN0XSA9IGlucHV0LnNwbGl0KHBhdHRlcm4pOwogIGlmIChyZXN0Lmxlbmd0aCA9PT0gMCkgewogICAgdGhyb3cgbmV3IEVycm9yKGBVbmFibGUgdG8gcGF0Y2ggJHtsYWJlbH1gKTsKICB9CiAgaWYgKHJlc3QubGVuZ3RoID4gMSkgewogICAgdGhyb3cgbmV3IEVycm9yKGBQYXRjaCB0YXJnZXQgaXMgYW1iaWd1b3VzIGZvciAke2xhYmVsfWApOwogIH0KICByZXR1cm4gYmVmb3JlICsgcmVwbGFjZW1lbnQgKyByZXN0WzBdOwp9CgpmdW5jdGlvbiByZXBsYWNlUmVnZXhPbmNlKGlucHV0LCBwYXR0ZXJuLCByZXBsYWNlbWVudCwgbGFiZWwpIHsKICBsZXQgY291bnQgPSAwOwogIGNvbnN0IG91dHB1dCA9IGlucHV0LnJlcGxhY2UocGF0dGVybiwgKC4uLmFyZ3MpID0+IHsKICAgIGNvdW50ICs9IDE7CiAgICByZXR1cm4gdHlwZW9mIHJlcGxhY2VtZW50ID09PSAnZnVuY3Rpb24nID8gcmVwbGFjZW1lbnQoLi4uYXJncykgOiByZXBsYWNlbWVudDsKICB9KTsKICBpZiAoY291bnQgIT09IDEpIHsKICAgIHRocm93IG5ldyBFcnJvcihgVW5hYmxlIHRvIHBhdGNoICR7bGFiZWx9OiBtYXRjaGVzPSR7Y291bnR9YCk7CiAgfQogIHJldHVybiBvdXRwdXQ7Cn0KCmxldCBkb3dubG9hZHMgPSBmcy5yZWFkRmlsZVN5bmMoZG93bmxvYWRzUGF0aCwgJ3V0ZjgnKTsKCmRvd25sb2FkcyA9IHJlcGxhY2VPbmNlKAogIGRvd25sb2FkcywKICAiY29uc3QgUEtHU19CQVNFX1VSTCA9ICdodHRwczovL3BrZ3MuZmx1eGVyLmNvbSc7XG4iICsKICAgICJleHBvcnQgY29uc3QgVVBEQVRFX0JBU0VfVVJMID0gYCR7UEtHU19CQVNFX1VSTH0vZGVza3RvcC8ke0JVSUxEX0NIQU5ORUx9LyR7cHJvY2Vzcy5wbGF0Zm9ybX0vJHtERVNLVE9QX0RPV05MT0FEX0FSQ0h9YDtcbiIgKwogICAgImV4cG9ydCBjb25zdCBET1dOTE9BRF9QQUdFX1VSTCA9XG4iICsKICAgICJcdEJVSUxEX0NIQU5ORUwgPT09ICdjYW5hcnknID8gJ2h0dHBzOi8vY2FuYXJ5LmZsdXhlci5hcHAvZG93bmxvYWQnIDogJ2h0dHBzOi8vZmx1eGVyLmFwcC9kb3dubG9hZCc7IiwKICAiZXhwb3J0IGNvbnN0IFVQREFURV9CQVNFX1VSTCA9ICdodHRwczovL2dpdC5nYW5kYy5ydS9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMnO1xuIiArCiAgICAiZXhwb3J0IGNvbnN0IERPV05MT0FEX1BBR0VfVVJMID0gVVBEQVRFX0JBU0VfVVJMOyIsCiAgJ1VwZGF0ZXJEb3dubG9hZHMgdXBkYXRlIHNvdXJjZScsCik7Cgpkb3dubG9hZHMgPSByZXBsYWNlT25jZSgKICBkb3dubG9hZHMsCiAgIlx0cmV0dXJuIGAke1VQREFURV9CQVNFX1VSTH0vJHt2ZXJzaW9ufS8ke2Zvcm1hdH1gOyIsCiAgIlx0dm9pZCB2ZXJzaW9uO1xuXHR2b2lkIGZvcm1hdDtcblx0cmV0dXJuIERPV05MT0FEX1BBR0VfVVJMOyIsCiAgJ21hbnVhbCBmYWxsYmFjayBVUkwnLAopOwoKZG93bmxvYWRzID0gcmVwbGFjZU9uY2UoCiAgZG93bmxvYWRzLAogICJcdFx0XHRcdHVybDogYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmwoaW5mby52ZXJzaW9uLCBmb3JtYXQpLCIsCiAgIlx0XHRcdFx0dXJsOiBmaWxlPy51cmwgPz8gYnVpbGRNYW51YWxWZXJzaW9uRG93bmxvYWRVcmwoaW5mby52ZXJzaW9uLCBmb3JtYXQpLCIsCiAgJ21hbnVhbCBhc3NldCBVUkwnLAopOwoKaWYgKGRvd25sb2Fkcy5pbmNsdWRlcygncGtncy5mbHV4ZXIuY29tJykpIHsKICB0aHJvdyBuZXcgRXJyb3IoJ3BrZ3MuZmx1eGVyLmNvbSBpcyBzdGlsbCBwcmVzZW50IGluIFVwZGF0ZXJEb3dubG9hZHMudHMnKTsKfQoKZnMud3JpdGVGaWxlU3luYyhkb3dubG9hZHNQYXRoLCBkb3dubG9hZHMsICd1dGY4Jyk7CgpsZXQgdXBkYXRlciA9IGZzLnJlYWRGaWxlU3luYyh1cGRhdGVyUGF0aCwgJ3V0ZjgnKTsKCmNvbnN0IGdpdGVhRmV0Y2ggPSBgYXN5bmMgZnVuY3Rpb24gZmV0Y2hNYW51YWxMYXRlc3Qob3B0aW9uczoge2ZvcmNlUmVmcmVzaD86IGJvb2xlYW59ID0ge30pOiBQcm9taXNlPE1hbnVhbExhdGVzdEluZm8+IHsKCWNvbnN0IG5vdyA9IERhdGUubm93KCk7CglpZiAoIW9wdGlvbnMuZm9yY2VSZWZyZXNoICYmIG1hbnVhbExhdGVzdENhY2hlICYmIG5vdyAtIG1hbnVhbExhdGVzdENhY2hlLmF0IDwgTUFOVUFMX0NBQ0hFX1RUTF9NUykgewoJCXJldHVybiBtYW51YWxMYXRlc3RDYWNoZS5pbmZvOwoJfQoKCWNvbnN0IHJlc3BvbnNlID0gYXdhaXQgZmV0Y2goCgkJJ2h0dHBzOi8vZ2l0LmdhbmRjLnJ1L2FwaS92MS9yZXBvcy9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMvbGF0ZXN0JywKCQl7CgkJCWNhY2hlOiAnbm8tc3RvcmUnLAoJCQloZWFkZXJzOiB7CgkJCQlBY2NlcHQ6ICdhcHBsaWNhdGlvbi9qc29uJywKCQkJCSdDYWNoZS1Db250cm9sJzogJ25vLWNhY2hlJywKCQkJCVByYWdtYTogJ25vLWNhY2hlJywKCQkJfSwKCQl9LAoJKTsKCglpZiAoIXJlc3BvbnNlLm9rKSB7CgkJdGhyb3cgbmV3IEVycm9yKFxgR1NDT1JEIGxhdGVzdCByZWxlYXNlIHJlcXVlc3QgZmFpbGVkOiBcJHtyZXNwb25zZS5zdGF0dXN9XGApOwoJfQoKCWNvbnN0IHBheWxvYWQgPSAoYXdhaXQgcmVzcG9uc2UuanNvbigpKSBhcyB7CgkJdGFnX25hbWU/OiB1bmtub3duOwoJCXB1Ymxpc2hlZF9hdD86IHVua25vd247CgkJY3JlYXRlZF9hdD86IHVua25vd247CgkJYXNzZXRzPzogdW5rbm93bjsKCX07CgoJaWYgKHR5cGVvZiBwYXlsb2FkLnRhZ19uYW1lICE9PSAnc3RyaW5nJyB8fCBwYXlsb2FkLnRhZ19uYW1lLnRyaW0oKS5sZW5ndGggPT09IDApIHsKCQl0aHJvdyBuZXcgRXJyb3IoJ0dTQ09SRCBsYXRlc3QgcmVsZWFzZSByZXNwb25zZSBtaXNzaW5nIHRhZ19uYW1lJyk7Cgl9CgoJY29uc3QgdmVyc2lvbiA9IHBheWxvYWQudGFnX25hbWUudHJpbSgpLnJlcGxhY2UoL152LywgJycpOwoJY29uc3QgYXNzZXRzID0gQXJyYXkuaXNBcnJheShwYXlsb2FkLmFzc2V0cykgPyBwYXlsb2FkLmFzc2V0cyA6IFtdOwoJY29uc3QgZmlsZXM6IFBhcnRpYWw8UmVjb3JkPE1hbnVhbERlc2t0b3BGb3JtYXQsIE1hbnVhbExhdGVzdEZpbGU+PiA9IHt9OwoJY29uc3QgaXNBcm02NCA9IHByb2Nlc3MuYXJjaCA9PT0gJ2FybTY0JzsKCglmb3IgKGNvbnN0IHJhd0Fzc2V0IG9mIGFzc2V0cykgewoJCWlmICghaXNSZWNvcmQocmF3QXNzZXQpKSBjb250aW51ZTsKCgkJY29uc3QgbmFtZSA9IHR5cGVvZiByYXdBc3NldC5uYW1lID09PSAnc3RyaW5nJyA/IHJhd0Fzc2V0Lm5hbWUgOiAnJzsKCQljb25zdCB1cmwgPQoJCQl0eXBlb2YgcmF3QXNzZXQuYnJvd3Nlcl9kb3dubG9hZF91cmwgPT09ICdzdHJpbmcnCgkJCQk/IHJhd0Fzc2V0LmJyb3dzZXJfZG93bmxvYWRfdXJsCgkJCQk6ICcnOwoKCQlpZiAoIW5hbWUgfHwgIXVybCkgY29udGludWU7CgoJCWNvbnN0IGxvd2VyID0gbmFtZS50b0xvd2VyQ2FzZSgpOwoJCWNvbnN0IGxvb2tzQXJtNjQgPSBsb3dlci5pbmNsdWRlcygnYXJtNjQnKSB8fCBsb3dlci5pbmNsdWRlcygnYWFyY2g2NCcpOwoJCWlmIChpc0FybTY0ICE9PSBsb29rc0FybTY0ICYmIChsb29rc0FybTY0IHx8IC8oPzp4NjR8eDg2XzY0fGFtZDY0KS9pLnRlc3QobmFtZSkpKSB7CgkJCWNvbnRpbnVlOwoJCX0KCgkJaWYgKC9cXC5BcHBJbWFnZSQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLmFwcGltYWdlID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoJCWlmICgvXFwuZGViJC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMuZGViID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoJCWlmICgvXFwucnBtJC9pLnRlc3QobmFtZSkpIHsKCQkJZmlsZXMucnBtID0ge3VybCwgc2hhMjU2OiBudWxsfTsKCQkJY29udGludWU7CgkJfQoJCWlmICgvXFwudGFyXFwuZ3okL2kudGVzdChuYW1lKSkgewoJCQlmaWxlcy50YXJfZ3ogPSB7dXJsLCBzaGEyNTY6IG51bGx9OwoJCQljb250aW51ZTsKCQl9CgkJaWYgKC9cXC5leGUkL2kudGVzdChuYW1lKSkgewoJCQlmaWxlcy5zZXR1cCA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCQlpZiAoL1xcLmRtZyQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLmRtZyA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJCWNvbnRpbnVlOwoJCX0KCQlpZiAoL1xcLnppcCQvaS50ZXN0KG5hbWUpKSB7CgkJCWZpbGVzLnppcCA9IHt1cmwsIHNoYTI1NjogbnVsbH07CgkJfQoJfQoKCWNvbnN0IGluZm86IE1hbnVhbExhdGVzdEluZm8gPSB7CgkJdmVyc2lvbiwKCQlwdWJEYXRlOgoJCQl0eXBlb2YgcGF5bG9hZC5wdWJsaXNoZWRfYXQgPT09ICdzdHJpbmcnCgkJCQk/IHBheWxvYWQucHVibGlzaGVkX2F0CgkJCQk6IHR5cGVvZiBwYXlsb2FkLmNyZWF0ZWRfYXQgPT09ICdzdHJpbmcnCgkJCQkJPyBwYXlsb2FkLmNyZWF0ZWRfYXQKCQkJCQk6IG51bGwsCgkJZmlsZXMsCgl9OwoKCW1hbnVhbExhdGVzdENhY2hlID0ge2F0OiBub3csIGluZm99OwoJcmV0dXJuIGluZm87Cn1gOwoKdXBkYXRlciA9IHJlcGxhY2VSZWdleE9uY2UoCiAgdXBkYXRlciwKICAvYXN5bmMgZnVuY3Rpb24gZmV0Y2hNYW51YWxMYXRlc3RcKG9wdGlvbnM6IFx7Zm9yY2VSZWZyZXNoXD86IGJvb2xlYW5cfSA9IFx7XH1cKTogUHJvbWlzZTxNYW51YWxMYXRlc3RJbmZvPiBce1tcc1xTXSo/XG5cfVxuZnVuY3Rpb24gc2VuZE1hbnVhbFVwZGF0ZUF2YWlsYWJsZS8sCiAgYCR7Z2l0ZWFGZXRjaH1cbmZ1bmN0aW9uIHNlbmRNYW51YWxVcGRhdGVBdmFpbGFibGVgLAogICdHaXRlYSBsYXRlc3QgcmVsZWFzZSByZXNvbHZlcicsCik7Cgp1cGRhdGVyID0gcmVwbGFjZU9uY2UoCiAgdXBkYXRlciwKICAiXHRjb25zdCB1cmwgPSBidWlsZE1hbnVhbFZlcnNpb25Eb3dubG9hZFVybCh2ZXJzaW9uLCAnYXBwaW1hZ2UnKTsiLAogICJcdGNvbnN0IHVybCA9IGxhdGVzdC5maWxlcy5hcHBpbWFnZT8udXJsID8/IGJ1aWxkTWFudWFsVmVyc2lvbkRvd25sb2FkVXJsKHZlcnNpb24sICdhcHBpbWFnZScpOyIsCiAgJ0FwcEltYWdlIGFzc2V0IFVSTCcsCik7CgppZiAoIXVwZGF0ZXIuaW5jbHVkZXMoJ2h0dHBzOi8vZ2l0LmdhbmRjLnJ1L2FwaS92MS9yZXBvcy9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMvbGF0ZXN0JykpIHsKICB0aHJvdyBuZXcgRXJyb3IoJ0dpdGVhIHJlbGVhc2VzIEFQSSBVUkwgd2FzIG5vdCBpbnN0YWxsZWQnKTsKfQoKZnMud3JpdGVGaWxlU3luYyh1cGRhdGVyUGF0aCwgdXBkYXRlciwgJ3V0ZjgnKTsKCi8vIFN0YXRpYyB2YWxpZGF0aW9uIGZvciB0aGUgV2luZG93cyBwb3J0YWJsZSBicmFuY2guCi8vIEluIHRoZSBjdXJyZW50IHVwc3RyZWFtIHJlZ2lzdGVyVXBkYXRlcigpLCBpc1BvcnRhYmxlTW9kZSgpIGlzIGNoZWNrZWQgYmVmb3JlIHdpbjMyLAovLyBzbyB0aGUgcG9ydGFibGUgLmV4ZSB1c2VzIHJlZ2lzdGVyTWFudWFsVXBkYXRlcigpLCBpLmUuIHRoZSBzYW1lIEdpdGVhIGZldGNoIGFib3ZlLgpjb25zdCBwb3J0YWJsZUluZGV4ID0gdXBkYXRlci5pbmRleE9mKCdpZiAoaXNQb3J0YWJsZU1vZGUoKSknKTsKY29uc3Qgd2luMzJJbmRleCA9IHVwZGF0ZXIuaW5kZXhPZigiaWYgKHByb2Nlc3MucGxhdGZvcm0gPT09ICd3aW4zMicpIik7CmlmIChwb3J0YWJsZUluZGV4IDwgMCB8fCB3aW4zMkluZGV4IDwgMCB8fCBwb3J0YWJsZUluZGV4ID4gd2luMzJJbmRleCkgewogIHRocm93IG5ldyBFcnJvcignVW5leHBlY3RlZCB1cGRhdGVyIGJyYW5jaCBvcmRlcjogcG9ydGFibGUgbW9kZSBpcyBub3QgY2hlY2tlZCBiZWZvcmUgd2luMzInKTsKfQoKY29uc29sZS5sb2coJ0dTQ09SRCB1cGRhdGVyIGNvbmZpZ3VyZWQgc3VjY2Vzc2Z1bGx5Jyk7CmNvbnNvbGUubG9nKCcgIFJlbGVhc2VzOiBodHRwczovL2dpdC5nYW5kYy5ydS9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMnKTsKY29uc29sZS5sb2coJyAgQVBJOiAgICAgIGh0dHBzOi8vZ2l0LmdhbmRjLnJ1L2FwaS92MS9yZXBvcy9nc2NvcmQvZ3Njb3JkLWRlc2t0b3AvcmVsZWFzZXMvbGF0ZXN0Jyk7CmNvbnNvbGUubG9nKCcgIExpbnV4IG1hbnVhbC9BcHBJbWFnZSBzb3VyY2U6IEdpdGVhJyk7CmNvbnNvbGUubG9nKCcgIFdpbmRvd3MgcG9ydGFibGUgc291cmNlOiBHaXRlYSBtYW51YWwgdXBkYXRlcicpOwpjb25zb2xlLmxvZygnICBwa2dzLmZsdXhlci5jb20gcmVtb3ZlZCBmcm9tIFVwZGF0ZXJEb3dubG9hZHMudHMnKTsK' | 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