'use strict'; const fs = require('fs-extra'); const handleTilde = require('./handle-tilde'); const path = require('path'); const template = require('lodash.template'); const startCase = require('lodash.startcase'); const execSync = require('child_process').execSync; const CONFIG = require('../config'); function install(installationDirPath, packagedAppFileName, packagedAppPath) { if (fs.existsSync(installationDirPath)) { console.log( `Removing previously installed "${packagedAppFileName}" at "${installationDirPath}"` ); fs.removeSync(installationDirPath); } console.log( `Installing "${packagedAppFileName}" at "${installationDirPath}"` ); fs.copySync(packagedAppPath, installationDirPath); } /** * Finds the path to the base directory of the icon default icon theme * This follows the freedesktop Icon Theme Specification: * https://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html#install_icons * and the XDG Base Directory Specification: * https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html#variables */ function findBaseIconThemeDirPath() { const defaultBaseIconThemeDir = '/usr/share/icons/hicolor'; const dataDirsString = process.env.XDG_DATA_DIRS; if (dataDirsString) { const dataDirs = dataDirsString.split(path.delimiter); if (dataDirs.includes('/usr/share/') || dataDirs.includes('/usr/share')) { return defaultBaseIconThemeDir; } else { return path.join(dataDirs[0], 'icons', 'hicolor'); } } else { return defaultBaseIconThemeDir; } } module.exports = function(packagedAppPath, installDir) { const packagedAppFileName = path.basename(packagedAppPath); if (process.platform === 'darwin') { const installPrefix = installDir !== '' ? handleTilde(installDir) : path.join(path.sep, 'Applications'); const installationDirPath = path.join(installPrefix, packagedAppFileName); install(installationDirPath, packagedAppFileName, packagedAppPath); } else if (process.platform === 'win32') { const installPrefix = installDir !== '' ? installDir : process.env.LOCALAPPDATA; const installationDirPath = path.join( installPrefix, packagedAppFileName, 'app-dev' ); try { install(installationDirPath, packagedAppFileName, packagedAppPath); } catch (e) { console.log( `Administrator elevation required to install into "${installationDirPath}"` ); const fsAdmin = require('fs-admin'); return new Promise((resolve, reject) => { fsAdmin.recursiveCopy(packagedAppPath, installationDirPath, error => { error ? reject(error) : resolve(); }); }); } } else { const atomExecutableName = CONFIG.channel === 'stable' ? 'atom' : 'atom-' + CONFIG.channel; const apmExecutableName = CONFIG.channel === 'stable' ? 'apm' : 'apm-' + CONFIG.channel; const appName = CONFIG.channel === 'stable' ? 'Atom' : startCase('Atom ' + CONFIG.channel); const appDescription = CONFIG.appMetadata.description; const prefixDirPath = installDir !== '' ? handleTilde(installDir) : path.join('/usr', 'local'); const shareDirPath = path.join(prefixDirPath, 'share'); const installationDirPath = path.join(shareDirPath, atomExecutableName); const applicationsDirPath = path.join(shareDirPath, 'applications'); const binDirPath = path.join(prefixDirPath, 'bin'); fs.mkdirpSync(applicationsDirPath); fs.mkdirpSync(binDirPath); install(installationDirPath, packagedAppFileName, packagedAppPath); { // Install icons const baseIconThemeDirPath = findBaseIconThemeDirPath(); const fullIconName = atomExecutableName + '.png'; let existingIconsFound = false; fs.readdirSync(baseIconThemeDirPath).forEach(size => { const iconPath = path.join( baseIconThemeDirPath, size, 'apps', fullIconName ); if (fs.existsSync(iconPath)) { if (!existingIconsFound) { console.log( `Removing existing icons from "${baseIconThemeDirPath}"` ); } existingIconsFound = true; fs.removeSync(iconPath); } }); console.log(`Installing icons at "${baseIconThemeDirPath}"`); const appIconsPath = path.join( CONFIG.repositoryRootPath, 'resources', 'app-icons', CONFIG.channel, 'png' ); fs.readdirSync(appIconsPath).forEach(imageName => { if (/\.png$/.test(imageName)) { const size = path.basename(imageName, '.png'); const iconPath = path.join(appIconsPath, imageName); fs.copySync( iconPath, path.join( baseIconThemeDirPath, `${size}x${size}`, 'apps', fullIconName ) ); } }); console.log(`Updating icon cache for "${baseIconThemeDirPath}"`); try { execSync(`gtk-update-icon-cache ${baseIconThemeDirPath} --force`); } catch (e) {} } { // Install xdg desktop file const desktopEntryPath = path.join( applicationsDirPath, `${atomExecutableName}.desktop` ); if (fs.existsSync(desktopEntryPath)) { console.log( `Removing existing desktop entry file at "${desktopEntryPath}"` ); fs.removeSync(desktopEntryPath); } console.log(`Writing desktop entry file at "${desktopEntryPath}"`); const desktopEntryTemplate = fs.readFileSync( path.join( CONFIG.repositoryRootPath, 'resources', 'linux', 'atom.desktop.in' ) ); const desktopEntryContents = template(desktopEntryTemplate)({ appName, appFileName: atomExecutableName, description: appDescription, installDir: prefixDirPath, iconPath: atomExecutableName }); fs.writeFileSync(desktopEntryPath, desktopEntryContents); } { // Add atom executable to the PATH const atomBinDestinationPath = path.join(binDirPath, atomExecutableName); if (fs.existsSync(atomBinDestinationPath)) { console.log( `Removing existing executable at "${atomBinDestinationPath}"` ); fs.removeSync(atomBinDestinationPath); } console.log(`Copying atom.sh to "${atomBinDestinationPath}"`); fs.copySync( path.join(CONFIG.repositoryRootPath, 'atom.sh'), atomBinDestinationPath ); } { // Link apm executable to the PATH const apmBinDestinationPath = path.join(binDirPath, apmExecutableName); try { fs.lstatSync(apmBinDestinationPath); console.log( `Removing existing executable at "${apmBinDestinationPath}"` ); fs.removeSync(apmBinDestinationPath); } catch (e) {} console.log(`Symlinking apm to "${apmBinDestinationPath}"`); fs.symlinkSync( path.join( '..', 'share', atomExecutableName, 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm' ), apmBinDestinationPath ); } console.log(`Changing permissions to 755 for "${installationDirPath}"`); fs.chmodSync(installationDirPath, '755'); } return Promise.resolve(); };