diff --git a/.travis.yml b/.travis.yml index 83662df00..acee1fe24 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ install: - yarn install script: - yarn run generate + - yarn prepare-build - ./node_modules/.bin/build --em.environment=$SIGNAL_ENV --config.mac.bundleVersion='$TRAVIS_BUILD_NUMBER' --publish=never - ./travis.sh env: diff --git a/Gruntfile.js b/Gruntfile.js index 5ec898f3e..bdab6a77f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,4 +1,5 @@ var path = require('path'); +var packageJson = require('./package.json'); module.exports = function(grunt) { 'use strict'; @@ -204,23 +205,23 @@ module.exports = function(grunt) { }, 'test-release': { osx: { - archive: 'mac/Signal.app/Contents/Resources/app.asar', - appUpdateYML: 'mac/Signal.app/Contents/Resources/app-update.yml', - exe: 'mac/Signal.app/Contents/MacOS/Signal' + archive: 'mac/' + packageJson.productName + '.app/Contents/Resources/app.asar', + appUpdateYML: 'mac/' + packageJson.productName + '.app/Contents/Resources/app-update.yml', + exe: 'mac/' + packageJson.productName + '.app/Contents/MacOS/' + packageJson.productName }, mas: { archive: 'mas/Signal.app/Contents/Resources/app.asar', appUpdateYML: 'mac/Signal.app/Contents/Resources/app-update.yml', - exe: 'mas/Signal.app/Contents/MacOS/Signal' + exe: 'mas/' + packageJson.productName + '.app/Contents/MacOS/' + packageJson.productName }, linux: { archive: 'linux-unpacked/resources/app.asar', - exe: 'linux-unpacked/signal-desktop' + exe: 'linux-unpacked/' + packageJson.name }, win: { archive: 'win-unpacked/resources/app.asar', appUpdateYML: 'win-unpacked/resources/app-update.yml', - exe: 'win-unpacked/Signal.exe' + exe: 'win-unpacked/' + packageJson.productName + '.exe' } }, gitinfo: {} // to be populated by grunt gitinfo @@ -273,7 +274,6 @@ module.exports = function(grunt) { require('mkdirp').sync('release'); var fs = require('fs'); var done = this.async(); - var package_json = grunt.config.get('pkg'); var gitinfo = grunt.config.get('gitinfo'); var https = require('https'); @@ -281,7 +281,7 @@ module.exports = function(grunt) { var keyBase = 'WhisperSystems/Signal-Desktop'; var sha = gitinfo.local.branch.current.SHA; var files = [{ - zip: 'signal-desktop-' + package_json.version + '.zip', + zip: packageJson.name + '-' + packageJson.version + '.zip', extractedTo: 'linux' }]; @@ -451,7 +451,7 @@ module.exports = function(grunt) { return app.client.getTitle(); }).then(function (title) { // Verify the window's title - assert.equal(title, 'Signal'); + assert.equal(title, packageJson.productName); console.log('title ok'); }).then(function () { assert(app.chromeDriver.logLines.indexOf('NODE_ENV ' + environment) > -1); diff --git a/about.html b/about.html index 93eba7e25..338527a2c 100644 --- a/about.html +++ b/about.html @@ -30,6 +30,20 @@ a { document.write('v', window.config.version); +
+ +
signal.org
diff --git a/app/config.js b/app/config.js index 4939cf05b..047d9c15e 100644 --- a/app/config.js +++ b/app/config.js @@ -1,12 +1,9 @@ -const fs = require('fs'); const path = require('path'); -console.log('reading package.json'); -const jsonFile = fs.readFileSync(path.join(__dirname, '..', 'package.json')); -const package_json = JSON.parse(jsonFile, 'utf-8'); -const environment = package_json.environment || process.env.NODE_ENV || 'development'; +const packageJson = require('../package.json'); -console.log('configuring'); + +const environment = packageJson.environment || process.env.NODE_ENV || 'development'; // Set environment vars to configure node-config before requiring it process.env.NODE_ENV = environment; diff --git a/app/user_config.js b/app/user_config.js index 164ffe24e..4f77368b5 100644 --- a/app/user_config.js +++ b/app/user_config.js @@ -1,9 +1,11 @@ -const app = require('electron').app; const path = require('path'); + +const app = require('electron').app; const ElectronConfig = require('electron-config'); const config = require('./config'); + // use a separate data directory for development if (config.has('storageProfile')) { const userData = path.join( diff --git a/appveyor.yml b/appveyor.yml index 249dc1979..671182985 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,7 @@ build_script: - node build\grunt.js - type package.json | findstr /v certificateSubjectName > temp.json - move temp.json package.json + - yarn prepare-build - node_modules\.bin\build --em.environment=%SIGNAL_ENV% --publish=never test_script: diff --git a/aptly.sh b/aptly.sh index 3646bc189..1f7c08e3a 100755 --- a/aptly.sh +++ b/aptly.sh @@ -1,18 +1,40 @@ #!/bin/bash -# Setup: +# Setup - creates the local repo which will be mirrored up to S3, then back-fill it. Your +# future deploys will eliminate all old versions without these backfill steps: # aptly repo create signal-desktop +# aptly mirror create -ignore-signatures backfill-mirror https://updates.signal.org/desktop/apt xenial +# aptly mirror update -ignore-signatures backfill-mirror +# aptly repo import backfill-mirror signal-desktop signal-desktop signal-desktop-beta +# aptly repo show -with-packages signal-desktop +# +# First run on a machine - uncomment the first two 'aptly publish snapshot' commands, +# comment the other two. Sets up the two publish channels, one local, one to S3. +# +# Testing - comment out the lines with s3:$ENDPOINT to publish only locally. To eliminate +# effects of testing, remove package from repo, then move back to old snapshot: +# aptly repo remove signal-desktop signal-desktop_1.0.35_amd64 +# aptly publish switch -gpg-key=57F6FB06 xenial signal-desktop_v1.0.34 # # Release: -# VERSION=X.X.X ./aptly.sh +# NAME=signal-desktop(-beta) VERSION=X.X.X ./aptly.sh + +echo "Releasing $NAME build version $VERSION" REPO=signal-desktop DISTRO=xenial ENDPOINT=signal-desktop-apt # Matches endpoint name in .aptly.conf -DEB_PATH=release SNAPSHOT=signal-desktop_v$VERSION GPG_KEYID=57F6FB06 -aptly repo add $REPO $DEB_PATH/$REPO\_$VERSION\_*.deb +aptly repo add $REPO release/$NAME\_$VERSION\_*.deb aptly snapshot create $SNAPSHOT from repo $REPO + +# run these two only on first release to a given repo from a given machine +# https://www.aptly.info/doc/aptly/publish/snapshot/ +# aptly publish snapshot -gpg-key=$GPG_KEYID $SNAPSHOT +# aptly publish snapshot -gpg-key=$GPG_KEYID -config=.aptly.conf $SNAPSHOT s3:$ENDPOINT: + +# these update already-published repos, run every time after that +# https://www.aptly.info/doc/aptly/publish/switch/ aptly publish switch -gpg-key=$GPG_KEYID $DISTRO $SNAPSHOT aptly publish switch -gpg-key=$GPG_KEYID -config=.aptly.conf $DISTRO s3:$ENDPOINT: $SNAPSHOT diff --git a/js/background.js b/js/background.js index bfcb05b31..443df5d26 100644 --- a/js/background.js +++ b/js/background.js @@ -14,6 +14,15 @@ var initialLoadComplete = false; window.owsDesktopApp = {}; + var title = window.config.name; + if (window.config.environment !== 'production') { + title += ' - ' + window.config.environment; + } + if (window.config.appInstance) { + title += ' - ' + window.config.appInstance; + } + window.config.title = window.document.title = title; + // start a background worker for ecc textsecure.startWorker('js/libsignal-protocol-worker.js'); Whisper.KeyChangeListener.init(textsecure.storage.protocol); diff --git a/js/conversation_controller.js b/js/conversation_controller.js index 1d6b522d2..9fe2c67bb 100644 --- a/js/conversation_controller.js +++ b/js/conversation_controller.js @@ -64,10 +64,10 @@ if (newUnreadCount > 0) { window.setBadgeCount(newUnreadCount); - window.document.title = "Signal (" + newUnreadCount + ")"; + window.document.title = window.config.title + " (" + newUnreadCount + ")"; } else { window.setBadgeCount(0); - window.document.title = "Signal"; + window.document.title = window.config.title; } }, startPruning: function() { diff --git a/js/logging.js b/js/logging.js index f7a687fe0..b2d864f5b 100644 --- a/js/logging.js +++ b/js/logging.js @@ -69,7 +69,12 @@ if (window.console) { // The mechanics of preparing a log for publish function getHeader() { - return window.navigator.userAgent + ' node/' + window.config.node_version; + let header = window.navigator.userAgent; + + header += ' node/' + window.config.node_version; + header += ' env/' + window.config.environment; + + return header; } function getLevel(level) { diff --git a/main.js b/main.js index 36f7d2f42..130b0de3f 100644 --- a/main.js +++ b/main.js @@ -3,7 +3,7 @@ const url = require('url'); const os = require('os'); const _ = require('lodash'); -const electron = require('electron') +const electron = require('electron'); const BrowserWindow = electron.BrowserWindow; const app = electron.app; @@ -11,20 +11,26 @@ const ipc = electron.ipcMain; const Menu = electron.Menu; const shell = electron.shell; +const packageJson = require('./package.json'); const autoUpdate = require('./app/auto_update'); const windowState = require('./app/window_state'); -console.log('setting AUMID'); -app.setAppUserModelId('org.whispersystems.signal-desktop') +const aumid = 'org.whispersystems.' + packageJson.name; +console.log('setting AUMID to ' + aumid); +app.setAppUserModelId(aumid); // Keep a global reference of the window object, if you don't, the window will -// be closed automatically when the JavaScript object is garbage collected. +// be closed automatically when the JavaScript object is garbage collected. let mainWindow; const config = require("./app/config"); -if (config.environment === 'production' && !process.mas) { +// Very important to put before the single instance check, since it is based on the +// userData directory. +const userConfig = require('./app/user_config'); + +if (!process.mas) { console.log('making app single instance'); var shouldQuit = app.makeSingleInstance(function(commandLine, workingDirectory) { // Someone tried to run a second instance, we should focus our window @@ -36,16 +42,15 @@ if (config.environment === 'production' && !process.mas) { }); if (shouldQuit) { - console.log('quitting'); + console.log('quitting; we are the second instance'); app.quit(); return; } } -const userConfig = require('./app/user_config'); const logging = require('./app/logging'); -// this must be after we set up appPath in user_config.js +// This must be after we set up appPath in user_config.js, so we know where logs go logging.initialize(); const logger = logging.getLogger(); @@ -60,6 +65,7 @@ function prepareURL(pathSegments) { protocol: 'file:', slashes: true, query: { + name: packageJson.productName, locale: locale.name, version: app.getVersion(), buildExpiration: config.get('buildExpiration'), @@ -69,6 +75,7 @@ function prepareURL(pathSegments) { environment: config.environment, node_version: process.versions.node, hostname: os.hostname(), + appInstance: process.env.NODE_APP_INSTANCE, } }) } diff --git a/package.json b/package.json index d082d5b24..4d6d825f6 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "build": "build --em.environment=$SIGNAL_ENV", "dist": "npm run generate && npm run build", "pack": "npm run generate && npm run build -- --dir", + "prepare-build": "node prepare_build.js", "pack-prod": "SIGNAL_ENV=production npm run pack", "dist-prod": "SIGNAL_ENV=production npm run dist", "dist-prod-all": "SIGNAL_ENV=production npm run dist -- -mwl", @@ -51,13 +52,13 @@ "prep-release": "npm run generate && grunt prep-release && npm run build-release && npm run build-mas-release && grunt test-release", "release-mac": "npm run build-release -- -m --prepackaged release/mac/Signal.app --publish=always", "release-win": "npm run build-release -- -w --prepackaged release/windows --publish=always", - "release-lin": "npm run build-release -- -l --prepackaged release/linux && VERSION=$npm_package_version ./aptly.sh", + "release-lin": "npm run build-release -- -l --prepackaged release/linux && NAME=$npm_package_name VERSION=$npm_package_version ./aptly.sh", "release": "npm run release-mac && npm run release-win && npm run release-lin" }, "build": { "appId": "org.whispersystems.signal-desktop", "mac": { - "artifactName": "${productName}-mac-${version}.${ext}", + "artifactName": "${name}-mac-${version}.${ext}", "category": "public.app-category.social-networking", "icon": "build/icons/mac/icon.icns", "publish": [ @@ -81,7 +82,7 @@ }, "win": { "asarUnpack": "node_modules/spellchecker/vendor/hunspell_dictionaries", - "artifactName": "${productName}-win-${version}.${ext}", + "artifactName": "${name}-win-${version}.${ext}", "certificateSubjectName": "Signal", "publisherName": "Signal (Quiet Riddle Ventures, LLC)", "icon": "build/icons/win/icon.ico", diff --git a/prepare_build.js b/prepare_build.js new file mode 100644 index 000000000..06d894f65 --- /dev/null +++ b/prepare_build.js @@ -0,0 +1,65 @@ +const fs = require('fs'); +const _ = require('lodash'); + +const packageJson = require('./package.json'); +const version = packageJson.version; +const beta = /beta/; + +// You might be wondering why this file is necessary. It comes down to our desire to allow +// side-by-side installation of production and beta builds. Electron-Builder uses +// top-level data from package.json for many things, like the executable name, the +// debian package name, the install directory under /opt on linux, etc. We tried +// adding the ${channel} macro to these values, but Electron-Builder didn't like that. + +if (!beta.test(version)) { + return; +} + +console.log('prepare_build: updating package.json for beta build'); + +// ------- + +const NAME_PATH = 'name'; +const PRODUCTION_NAME = 'signal-desktop'; +const BETA_NAME = 'signal-desktop-beta'; + +const PRODUCT_NAME_PATH = 'productName'; +const PRODUCTION_PRODUCT_NAME = 'Signal'; +const BETA_PRODUCT_NAME = 'Signal Beta'; + +const APP_ID_PATH = 'build.appId'; +const PRODUCTION_APP_ID = 'org.whispersystems.signal-desktop'; +const BETA_APP_ID = 'org.whispersystems.signal-desktop-beta'; + +const STARTUP_WM_CLASS_PATH = 'build.linux.desktop.StartupWMClass'; +const PRODUCTION_STARTUP_WM_CLASS = 'Signal'; +const BETA_STARTUP_WM_CLASS = 'Signal Beta'; + + + +// ------- + +function checkValue(object, objectPath, expected) { + const actual = _.get(object, objectPath) + if (actual !== expected) { + throw new Error(objectPath + ' was ' + actual + '; expected ' + expected); + } +} + +// ------ + +checkValue(packageJson, NAME_PATH, PRODUCTION_NAME); +checkValue(packageJson, PRODUCT_NAME_PATH, PRODUCTION_PRODUCT_NAME); +checkValue(packageJson, APP_ID_PATH, PRODUCTION_APP_ID); +checkValue(packageJson, STARTUP_WM_CLASS_PATH, PRODUCTION_STARTUP_WM_CLASS); + +// ------- + +_.set(packageJson, NAME_PATH, BETA_NAME); +_.set(packageJson, PRODUCT_NAME_PATH, BETA_PRODUCT_NAME); +_.set(packageJson, APP_ID_PATH, BETA_APP_ID); +_.set(packageJson, STARTUP_WM_CLASS_PATH, BETA_STARTUP_WM_CLASS); + +// ------- + +fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, ' '));