Skip to content

Commit

Permalink
chore: Upgrade electron dependencies (#216)
Browse files Browse the repository at this point in the history
* chore: Upgrade dependencies

* chore: Upgrade packages

* feat: Update transpilation process

* refactor: Move methods to the helpers file and update getFreePort definition

* fix: Circular dependencies

* fix: Library import style

* fix: App Path definition

* fix: Tests

* fix: output folder and building ci

* chore: Remove unused dependencies

* refactor: Update file extension

* fix: file extension

* chore: Update build step to generate the installer

* fix: Windows upload artifacts flow

* fix: path

* fix: GetFreePort logic and Import order

* fix: Update icons

* chore: Update deployment notification message
  • Loading branch information
cyaiox committed Sep 17, 2024
1 parent 5cf5741 commit 34dad61
Show file tree
Hide file tree
Showing 16 changed files with 5,750 additions and 2,077 deletions.
43 changes: 26 additions & 17 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ jobs:

- name: Build Ubuntu (AppImage)
if: ${{github.ref != 'refs/heads/main'}}
env:
MODE: production
run: |
npm run build
npm run build:installer
- name: Build and Release Ubuntu (AppImage)
if: ${{github.ref == 'refs/heads/main'}}
Expand All @@ -50,7 +52,7 @@ jobs:
AWS_BUCKET: ${{secrets.S3_BUCKET}}
if: ${{env.AWS_BUCKET != ''}}
run: |
node scripts/prepare-artifacts.js AppImage
node scripts/prepare-artifacts.cjs AppImage
npx @dcl/cdn-uploader@next --bucket ${{secrets.S3_BUCKET}} --local-folder output --bucket-folder "launcher-branch/${{github.ref_name}}"
build_macos:
Expand All @@ -73,8 +75,10 @@ jobs:

- name: Build MacOS (dmg)
if: ${{github.ref != 'refs/heads/main'}}
env:
MODE: production
run: |
npm run build
npm run build:installer
- name: Build and Release MacOS (dmg)
if: ${{github.ref == 'refs/heads/main'}}
Expand Down Expand Up @@ -118,7 +122,7 @@ jobs:
AWS_BUCKET: ${{secrets.S3_BUCKET}}
if: ${{env.AWS_BUCKET != ''}}
run: |
node scripts/prepare-artifacts.js dmg
node scripts/prepare-artifacts.cjs dmg
npx @dcl/cdn-uploader@next --bucket ${{secrets.S3_BUCKET}} --local-folder output --bucket-folder "launcher-branch/${{github.ref_name}}"
build_windows:
Expand All @@ -141,8 +145,10 @@ jobs:

- name: Build Windows (exe)
if: ${{github.ref != 'refs/heads/main'}}
env:
MODE: production
run: |
npm run build
npm run build:installer
- name: Build and Publish Windows (exe)
if: ${{github.ref == 'refs/heads/main'}}
Expand Down Expand Up @@ -172,8 +178,21 @@ jobs:
run: |
Copy-Item -Path "${env:GITHUB_WORKSPACE}\dist\signed\Install Decentraland.exe" -Destination "${env:GITHUB_WORKSPACE}\dist\Install Decentraland.exe" -Force
- name: Upload artifacts exe
env:
AWS_DEFAULT_REGION: us-east-1
AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
AWS_BUCKET: ${{secrets.S3_BUCKET}}
if: ${{env.AWS_BUCKET != ''}}
run: |
node scripts/prepare-artifacts.cjs exe
npx @dcl/cdn-uploader@next --bucket ${{secrets.S3_BUCKET}} --local-folder output --bucket-folder "launcher-branch/${{github.ref_name}}"
- name: Build Windows (appx)
if: ${{github.ref != 'refs/heads/main'}}
env:
MODE: production
run: |
npm run build:appx
Expand All @@ -193,18 +212,7 @@ jobs:
AWS_BUCKET: ${{secrets.S3_BUCKET}}
if: ${{env.AWS_BUCKET != ''}}
run: |
node scripts/prepare-artifacts.js appx
npx @dcl/cdn-uploader@next --bucket ${{secrets.S3_BUCKET}} --local-folder output --bucket-folder "launcher-branch/${{github.ref_name}}"
- name: Upload artifacts exe
env:
AWS_DEFAULT_REGION: us-east-1
AWS_ACCESS_KEY_ID: ${{secrets.AWS_ACCESS_KEY_ID}}
AWS_SECRET_ACCESS_KEY: ${{secrets.AWS_SECRET_ACCESS_KEY}}
AWS_BUCKET: ${{secrets.S3_BUCKET}}
if: ${{env.AWS_BUCKET != ''}}
run: |
node scripts/prepare-artifacts.js exe
node scripts/prepare-artifacts.cjs appx
npx @dcl/cdn-uploader@next --bucket ${{secrets.S3_BUCKET}} --local-folder output --bucket-folder "launcher-branch/${{github.ref_name}}"
deployment_notification:
Expand All @@ -227,4 +235,5 @@ jobs:
- [Windows AppX](https://renderer-artifacts.decentraland.org/launcher-branch/${{github.ref_name}}/Decentraland.appx)
- [Linux](https://renderer-artifacts.decentraland.org/launcher-branch/${{github.ref_name}}/Decentraland.AppImage)
- [Mac](https://renderer-artifacts.decentraland.org/launcher-branch/${{github.ref_name}}/Decentraland.dmg)
> Mac: After installing the app, run the following command in your console: `sudo xattr -c /Applications/Decentraland.app`
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
2 changes: 1 addition & 1 deletion electron/decompress.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as StreamZip from 'node-stream-zip'
import StreamZip from 'node-stream-zip'

export const unzip = async (zipFilePath: string, destinationPath: string) => {
const zip = new StreamZip.async({ file: zipFilePath })
Expand Down
50 changes: 38 additions & 12 deletions electron/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { app } from 'electron'
import updater from 'electron-updater'
import { getPortPromise } from 'portfinder'
import { main } from './main'
import { autoUpdater } from 'electron-updater'

export const getAppTitle = (): string => {
const currentVersion = autoUpdater.currentVersion.version
const currentVersion = updater.autoUpdater.currentVersion.version
let title = `Decentraland ${currentVersion}`

if (main.config.desktopBranch !== main.defaultConfig.desktopBranch)
Expand All @@ -17,14 +18,13 @@ export const getAppTitle = (): string => {
return title
}

export const getFreePort = (): Promise<number> => {
return new Promise<number>((resolve, reject) => {
var fp = require('find-free-port')
fp(7666, 7679, (err: any, freePort: number) => {
if (err) reject(err)
resolve(freePort)
})
})
export const getFreePort = async (): Promise<number> => {
try {
const resolvedPort = await getPortPromise({ port: 7666, stopPort: 7679 })
return resolvedPort
} catch (error) {
throw error
}
}

export const getIconByPlatform = () => {
Expand Down Expand Up @@ -57,7 +57,7 @@ export const isTrustedCertificate = (url: string, error: string): boolean => {
}

export const getAppBasePath = (): string => {
var applicationFolderName = 'DecentralandLauncher';
var applicationFolderName = 'DecentralandLauncher'
//dev
if (process.env.RUN_ENV === 'development') return './'

Expand All @@ -74,5 +74,31 @@ export const getAppBasePath = (): string => {
return app.getPath('userData')
}

return './';
return './'
}

export const getOSName = (): string | null => {
switch (process.platform) {
case 'darwin':
return 'mac'
case 'linux':
return 'linux'
case 'win32':
return 'windows'
default:
return null
}
}

export const getOSExtension = (): string | null => {
switch (process.platform) {
case 'darwin':
return '.app'
case 'linux':
return ''
case 'win32':
return '.exe'
default:
return null
}
}
212 changes: 212 additions & 0 deletions electron/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import fs from 'fs'
import { exit } from 'process'
import { shell, app, BrowserWindow, ipcMain, crashReporter, systemPreferences } from 'electron'
import updater from 'electron-updater'
import { main } from './main'
import { parseConfig } from './cmdParser'
import { createWindow, hideWindowInTray, loadDecentralandWeb, onOpenUrl, showWindowAndHideTray } from './window'
import { isTrustedCertificate } from './certificateChecker'
import { getAppTitle, getAppBasePath, getFreePort, getOSName } from './helpers'
import { LauncherPaths } from './types'

// all uncaught exceptions are being sent automatically
initializeCrashReport()

parseConfig([...process.argv])

main.openingUrl = process.argv.find((arg) => arg.startsWith('dcl://'))
console.log('main.openingUrl: ', main.openingUrl)

app.setAsDefaultProtocolClient('dcl')

const isAppAllowed = app.requestSingleInstanceLock()

if (!isAppAllowed) {
app.quit()
} else {
app.on('open-url', (event, url) => {
if (main.win) {
onOpenUrl(url, main.win)
} else {
main.openingUrl = url
}
event.preventDefault()
})
}

const osName = getOSName()

console.log('Config:', main.config)
console.log('OS:', osName)

if (getOSName() === null) {
console.error('OS not supported')
exit(1)
}

const launcherPaths: LauncherPaths = {
baseUrl: `https://renderer-artifacts.decentraland.org/desktop/`,
rendererPath: `${app.getPath('appData')}/explorer-desktop-launcher/renderer/`,
executablePath: `/unity-renderer-${osName}`,
versionPath: `/version.json`,
artifactUrl: `unity-renderer-${osName}.zip`,
remoteVersionUrl: `/version.json`
}

if (getOSName() === 'windows') {
launcherPaths.rendererPath = launcherPaths.rendererPath.replace(/\//gi, '\\')
launcherPaths.versionPath = launcherPaths.versionPath.replace(/\//gi, '\\')
launcherPaths.executablePath = launcherPaths.executablePath.replace(/\//gi, '\\')
}

const checkUpdates = async (win: BrowserWindow): Promise<void> => {
try {
const result = await updater.autoUpdater.checkForUpdatesAndNotify()
console.log('Result:', result)
if (result === null || !result.downloadPromise) {
await loadDecentralandWeb(win)
} else {
await result.downloadPromise

console.log('Download completed')
const silent = process.platform === 'darwin' // Silent=true only on Mac
updater.autoUpdater.quitAndInstall(silent, true)
}
} catch (err) {
console.error(`Check Updates error: ${err}`)
await loadDecentralandWeb(win) // Load current version anyway
}
return Promise.resolve()
}

const startApp = async (): Promise<void> => {
main.config.port = await getFreePort()

if (main.openingUrl) {
console.log('opening-url: ', main.openingUrl)
onOpenUrl(main.openingUrl)
}

const appTitle = getAppTitle()
const win = await createWindow(appTitle, launcherPaths)
main.win = win

ipcMain.on('process-terminated', async (event, reloadWebsite: boolean) => {
main.isRendererOpen = false

if (reloadWebsite) {
// (#1457) we should reload the url
await loadDecentralandWeb(win)
}

showWindowAndHideTray(win)
})

ipcMain.on('on-open-renderer', (event) => {
main.isRendererOpen = true
hideWindowInTray(win)
})

win.on('close', (event: { preventDefault: () => void }) => {
// this prevents the launcher from closing when using the X button on the window
if (!main.isExitAllowed && main.isRendererOpen) {
hideWindowInTray(win)
event.preventDefault()
}
})

win.on('minimize', function (event: { preventDefault: () => void }) {
hideWindowInTray(win)
event.preventDefault()
})

app.on('second-instance', (event, commandLine, workingDirectory) => {
if (process.platform !== 'darwin') {
// Find the arg that is our custom protocol url and store it
const url = commandLine.find((arg) => arg.startsWith('dcl://'))
if (url) onOpenUrl(url, win)

console.log('second-instance url: ', url)
}
showWindowAndHideTray(win)
})

win.webContents.setWindowOpenHandler(({ url }) => {
shell.openExternal(url)
return { action: 'deny' }
})

ipcMain.on('loadDecentralandWeb', (event: any, url: string, explorerDesktopBranch: string) => {
main.config.customUrl = url
main.config.desktopBranch = explorerDesktopBranch
loadDecentralandWeb(win)
})

if (!main.config.developerMode && !main.config.previewMode) {
await checkUpdates(win)
}

const microphoneResult = await askForMediaAccess('microphone')
if (!microphoneResult) {
console.log('Microphone permission was not given')
}
return Promise.resolve()
}

// SSL/TSL: this is the self signed certificate support
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
// On certificate error we disable default behaviour (stop loading the page)
// and we then say "it is all fine - true" to the callback
if (isTrustedCertificate(url, error)) {
event.preventDefault()
callback(true)
}
})

app
.whenReady()
.then(async () => {
await startApp()

app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
startApp()
}
})

app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit()
}
})

app.on('before-quit', function () {
//this allows exiting the launcher through command+Q or alt+f4
main.isExitAllowed = true
})
})
.catch(async (error) => {
console.error(`Error starting app: ${error}`)
})

function initializeCrashReport() {
var path = getAppBasePath()
if (!fs.existsSync(path)) fs.mkdir(path, () => {})

app.setPath('crashDumps', path)

crashReporter.start({ uploadToServer: false })
}

const askForMediaAccess = async (mediaType: 'microphone' | 'camera') => {
if (systemPreferences.askForMediaAccess) {
// Electron currently only implements this on macOS
const previous = await systemPreferences.getMediaAccessStatus(mediaType)
const result = await systemPreferences.askForMediaAccess(mediaType)
const next = await systemPreferences.getMediaAccessStatus(mediaType)
console.log(`MediaAccess for ${mediaType} is went from ${previous} to ${next} (askForMediaAccess: ${result})`)
return result
}
// For other platforms we can't reasonably do anything other than assume we have access.
return true
}
Loading

0 comments on commit 34dad61

Please sign in to comment.