diff --git a/.editorconfig b/.editorconfig index e3711b6b..b0b333da 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,5 +11,5 @@ indent_size = 4 [*.md] trim_trailing_whitespace = false -[package.json] +[{package.json,package-lock.json,.travis.yml}] indent_size = 2 diff --git a/.travis.yml b/.travis.yml index 61349451..723c1101 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,40 @@ language: node_js node_js: -- stable - -branches: - only: - - master +- 11 addons: apt: packages: - libsecret-1-dev + +jobs: + include: + - stage: test + - stage: release + script: + - npm run update_release + - npm run package_release + - npm run build_debs + deploy: + provider: releases + api_key: "$GITHUB_OAUTH_TOKEN" + draft: true + file: + # BetterDiscord installer/updater + - release/releaseinfo.json + - release/core.tar.gz + - release/client.tar.gz + - release/editor.tar.gz + + # dpkg + - release/betterdiscord_*.deb + - release/betterdiscord-ptb_*.deb + - release/betterdiscord-canary_*.deb + - release/betterdiscord-core_*.deb + - release/betterdiscord-client_*.deb + - release/betterdiscord-editor_*.deb + file_glob: true + skip_cleanup: true + on: + tags: true diff --git a/client/src/modules/globals.js b/client/src/modules/globals.js index 98618c72..220cf525 100644 --- a/client/src/modules/globals.js +++ b/client/src/modules/globals.js @@ -101,4 +101,8 @@ export default new class extends Module { return this.config.versions.core; } + get disableUpdater() { + return this.config.disableUpdater; + } + } diff --git a/client/src/modules/updater.js b/client/src/modules/updater.js index a48c32c8..41f41240 100644 --- a/client/src/modules/updater.js +++ b/client/src/modules/updater.js @@ -98,7 +98,7 @@ export default new class extends Module { update.text = `${update.id.charAt(0).toUpperCase()}${update.id.slice(1)}`; update.hint = `Current: ${update.currentVersion} | Latest: ${update.version}`; update.status = { - update: true, + update: !Globals.disableUpdater.includes(update.id), updating: false, updated: false, error: null @@ -164,7 +164,7 @@ export default new class extends Module { } toggleUpdate(update) { - update.status.update = !update.status.update; + update.status.update = !update.status.update && !Globals.disableUpdater.includes(update.id); } async startUpdate() { diff --git a/client/src/ui/components/bd/UpdaterStatus.vue b/client/src/ui/components/bd/UpdaterStatus.vue index 76ef0b24..3439e59a 100644 --- a/client/src/ui/components/bd/UpdaterStatus.vue +++ b/client/src/ui/components/bd/UpdaterStatus.vue @@ -12,7 +12,8 @@

{{item.text}}

-

Update Failed!

+

Use your package manager to install

+

Update Failed!

Done

Unknown

@@ -23,6 +24,6 @@ diff --git a/client/src/ui/components/bd/UpdaterView.vue b/client/src/ui/components/bd/UpdaterView.vue index e9457d9a..a0c63f21 100644 --- a/client/src/ui/components/bd/UpdaterView.vue +++ b/client/src/ui/components/bd/UpdaterView.vue @@ -18,15 +18,14 @@
- +
-
- Update -
+ + Update @@ -37,6 +36,7 @@ import SettingsWrapper from './SettingsWrapper.vue'; import UpdaterToggle from './UpdaterToggle.vue'; import UpdaterStatus from './UpdaterStatus.vue'; + import FormButton from '../common/FormButton.vue'; export default { data() { @@ -49,7 +49,8 @@ components: { SettingsWrapper, UpdaterToggle, - UpdaterStatus + UpdaterStatus, + FormButton }, computed: { updatesAvailable() { @@ -66,11 +67,20 @@ }, bdUpdates() { return this.updater.bdUpdates; + }, + updatesSelected() { + return this.updater.updates.bd.find(update => update.status.update); + }, + updating() { + return this.updater.updates.bd.find(update => update.status.updating); } }, methods: { update() { this.updater.startUpdate(); + }, + isDisabled(update) { + return Globals.disableUpdater.includes(update.id); } } } diff --git a/core/src/main.js b/core/src/main.js index 85c6dd1a..2cb6e69c 100644 --- a/core/src/main.js +++ b/core/src/main.js @@ -38,7 +38,10 @@ const TEST_ARGS = () => { 'editor': path.resolve(_basePath, 'editor', 'dist'), // tmp: path.join(_basePath, 'tmp') tmp: path.join(os.tmpdir(), 'betterdiscord', `${process.getuid()}`) - } + }, + disableUpdater: [ + 'core', 'client', 'editor' + ] } } const TEST_EDITOR = TESTS && true; diff --git a/core/src/modules/config.js b/core/src/modules/config.js index 43559729..b38e4e95 100644 --- a/core/src/modules/config.js +++ b/core/src/modules/config.js @@ -66,7 +66,8 @@ export default class Config extends Module { return { version: this.version, versions: this.versions, - paths: this.paths + paths: this.paths, + disableUpdater: this.disableUpdater }; } @@ -74,4 +75,9 @@ export default class Config extends Module { compatibility() { this.args.paths = Object.entries(this.args.paths).map(([id, path]) => ({ id, path })); } + + get disableUpdater() { + return this.args.disableUpdater || (this.args.disableUpdater = []); + } + } diff --git a/core/src/modules/updater.js b/core/src/modules/updater.js index 786583d1..72ffc444 100644 --- a/core/src/modules/updater.js +++ b/core/src/modules/updater.js @@ -90,6 +90,10 @@ export default class Updater extends Module { async updateBd(update) { try { + if (this.bd.config.disableUpdater.includes(update.id)) { + throw {message: `Not installing ${update.id} as updates are disabled. Use your package manager to install updates instead.`}; + } + console.log('[BetterDiscord:Updater] Updating', update.id); await this.downloadTarGz(`https://github.com/JsSucks/BetterDiscordApp${update.remote}`, this.bd.config.getPath('base')); this.updateFinished(update); @@ -117,6 +121,12 @@ export default class Updater extends Module { // TODO cleaner if (bd.length) { for (const update of bd) { + if (this.bd.config.disableUpdater.includes(update.id)) { + update.error = {message: `Not installing ${update.id} as updates are disabled. Use your package manager to install updates instead.`}; + this.bd.sendToDiscord('updater-updateError', update); + continue; + } + try { await FileUtils.rm(`${this.bd.config.getPath(update.id)}_old`); // Try to rename dirs first diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 1f1c3332..44ec5b1e 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -1,3 +1,5 @@ +import path from 'path'; +import fs from 'fs'; import gulp from 'gulp'; import pump from 'pump'; import del from 'del'; @@ -8,6 +10,7 @@ import replace from 'gulp-replace'; import copydeps from './scripts/copydeps'; import file from 'gulp-file'; import editjson from 'gulp-json-editor'; +import {mkdeb} from './scripts/dpkg'; import corepkg from './core/package'; import clientpkg from './client/package'; @@ -153,3 +156,50 @@ gulp.task('del-release', function() { gulp.task('dependencies', gulp.series('node-modules', gulp.parallel('node-sass-bindings', 'keytar-bindings'))); gulp.task('build-release', gulp.parallel('core-release', 'client-release', 'editor-release', 'dependencies')); gulp.task('release', gulp.series('del-release', 'build-release')); + +// Debian packages + +gulp.task('build-inject-deb', function () { + const control = fs.readFileSync(path.join(__dirname, 'other', 'deb', 'control', 'control'), 'utf-8'); + const version = (control.match(/^Version:\s*(.*)$/m) || [, '1.0.0'])[1]; + const arch = (control.match(/^Architecture:\s*(.*)$/m) || [, 'all'])[1]; + + return mkdeb(`betterdiscord_${version}-${arch}`, 'other/deb/injector/**/*', '/usr/share/discord/resources/app', 'other/deb/control/**/*'); +}); + +gulp.task('build-inject-deb-ptb', function () { + const control = fs.readFileSync(path.join(__dirname, 'other', 'deb', 'control-ptb', 'control'), 'utf-8'); + const version = (control.match(/^Version:\s*(.*)$/m) || [, '1.0.0'])[1]; + const arch = (control.match(/^Architecture:\s*(.*)$/m) || [, 'all'])[1]; + + return mkdeb(`betterdiscord-ptb_${version}-${arch}`, 'other/deb/injector/**/*', '/usr/share/discord-ptb/resources/app', 'other/deb/control-ptb/**/*'); +}); + +gulp.task('build-inject-deb-canary', function () { + const control = fs.readFileSync(path.join(__dirname, 'other', 'deb', 'control-canary', 'control'), 'utf-8'); + const version = (control.match(/^Version:\s*(.*)$/m) || [, '1.0.0'])[1]; + const arch = (control.match(/^Architecture:\s*(.*)$/m) || [, 'all'])[1]; + + return mkdeb(`betterdiscord-canary_${version}-${arch}`, 'other/deb/injector/**/*', '/usr/share/discord-canary/resources/app', 'other/deb/control-canary/**/*'); +}); + +gulp.task('build-inject-debs', gulp.series('build-inject-deb', 'build-inject-deb-ptb', 'build-inject-deb-canary')); + +gulp.task('build-core-deb', function () { + return mkdeb(`betterdiscord-core_${corepkg.version}-all`, 'release/core/**/*', '/usr/lib/betterdiscord/core', 'other/deb/control-core/**/*', { + VERSION: corepkg.version + }); +}); + +gulp.task('build-client-deb', function () { + return mkdeb(`betterdiscord-client_${clientpkg.version}-all`, 'release/client/**/*', '/usr/lib/betterdiscord/client', 'other/deb/control-client/**/*', { + VERSION: clientpkg.version + }); +}); + +gulp.task('build-editor-deb', function () { + return mkdeb(`betterdiscord-editor_${editorpkg.version}-all`, 'release/editor/**/*', '/usr/lib/betterdiscord/editor', 'other/deb/control-editor/**/*', { + VERSION: editorpkg.version + }); +}); +gulp.task('build-debs', gulp.series('build-inject-debs', 'build-core-deb', 'build-client-deb', 'build-editor-deb')); diff --git a/other/deb/control-canary/conffiles b/other/deb/control-canary/conffiles new file mode 100644 index 00000000..85690f30 --- /dev/null +++ b/other/deb/control-canary/conffiles @@ -0,0 +1 @@ +/usr/share/discord-canary/resources/app/bd.json diff --git a/other/deb/control-canary/control b/other/deb/control-canary/control new file mode 100644 index 00000000..1d9d8e91 --- /dev/null +++ b/other/deb/control-canary/control @@ -0,0 +1,6 @@ +Package: betterdiscord-canary +Version: 1.0.0 +Architecture: all +Maintainer: - +Description: BetterDiscord v2 for Discord Canary +Depends: discord-canary, betterdiscord-core, betterdiscord-client, betterdiscord-editor diff --git a/other/deb/control-client/control b/other/deb/control-client/control new file mode 100644 index 00000000..0f99bbcf --- /dev/null +++ b/other/deb/control-client/control @@ -0,0 +1,5 @@ +Package: betterdiscord-client +Version: ${VERSION} +Architecture: all +Maintainer: - +Description: BetterDiscord v2 client bundle diff --git a/other/deb/control-core/control b/other/deb/control-core/control new file mode 100644 index 00000000..3f24ef1d --- /dev/null +++ b/other/deb/control-core/control @@ -0,0 +1,6 @@ +Package: betterdiscord-core +Version: ${VERSION} +Architecture: all +Maintainer: - +Description: BetterDiscord v2 core files +Depends: libsecret-1-0 diff --git a/other/deb/control-editor/control b/other/deb/control-editor/control new file mode 100644 index 00000000..8e709644 --- /dev/null +++ b/other/deb/control-editor/control @@ -0,0 +1,5 @@ +Package: betterdiscord-editor +Version: ${VERSION} +Architecture: all +Maintainer: - +Description: BetterDiscord v2 editor bundle diff --git a/other/deb/control-ptb/conffiles b/other/deb/control-ptb/conffiles new file mode 100644 index 00000000..bc6d142a --- /dev/null +++ b/other/deb/control-ptb/conffiles @@ -0,0 +1 @@ +/usr/share/discord-ptb/resources/app/bd.json diff --git a/other/deb/control-ptb/control b/other/deb/control-ptb/control new file mode 100644 index 00000000..3197c345 --- /dev/null +++ b/other/deb/control-ptb/control @@ -0,0 +1,6 @@ +Package: betterdiscord-ptb +Version: 1.0.0 +Architecture: all +Maintainer: - +Description: BetterDiscord v2 for Discord PTB +Depends: discord-ptb, betterdiscord-core, betterdiscord-client, betterdiscord-editor diff --git a/other/deb/control/conffiles b/other/deb/control/conffiles new file mode 100644 index 00000000..ba51c5b5 --- /dev/null +++ b/other/deb/control/conffiles @@ -0,0 +1 @@ +/usr/share/discord/resources/app/bd.json diff --git a/other/deb/control/control b/other/deb/control/control new file mode 100644 index 00000000..aa4c66b8 --- /dev/null +++ b/other/deb/control/control @@ -0,0 +1,6 @@ +Package: betterdiscord +Version: 1.0.0 +Architecture: all +Maintainer: - +Description: BetterDiscord v2 +Depends: discord, betterdiscord-core, betterdiscord-client, betterdiscord-editor diff --git a/other/deb/injector/bd.json b/other/deb/injector/bd.json new file mode 100644 index 00000000..2cd85c2a --- /dev/null +++ b/other/deb/injector/bd.json @@ -0,0 +1,19 @@ +{ + "options": { + "autoInject": true, + "commonCore": true, + "commonData": true + }, + "paths": { + "core": "/usr/lib/betterdiscord/core", + "client": "/usr/lib/betterdiscord/client", + "editor": "/usr/lib/betterdiscord/editor", + "data": ".config/betterdiscord/data", + "userconfig": ".config/betterdiscord/bd" + }, + "disableUpdater": [ + "core", + "client", + "editor" + ] +} diff --git a/other/deb/injector/index.js b/other/deb/injector/index.js new file mode 100644 index 00000000..b0e0c530 --- /dev/null +++ b/other/deb/injector/index.js @@ -0,0 +1,23 @@ +const bdinfo = require('./bd'); +const { app } = require('electron'); +const path = require('path'); +const os = require('os'); +const Module = require('module'); + +const packagePath = path.join(__dirname, '..', 'app.asar'); +app.getAppPath = () => packagePath; + +function loadBd() { + const userconfig = (() => { + try { + return require(path.resolve(os.homedir(), bdinfo.paths.userconfig)); + } catch (err) {} + })() || {}; + + const { BetterDiscord } = require(path.resolve(os.homedir(), (userconfig.paths || {}).core || bdinfo.paths.core)); + const instance = new BetterDiscord(bdinfo, userconfig); +} + +app.on('ready', loadBd); + +Module._load(app.getAppPath(), null, true); diff --git a/other/deb/injector/package.json b/other/deb/injector/package.json new file mode 100644 index 00000000..8ff69de1 --- /dev/null +++ b/other/deb/injector/package.json @@ -0,0 +1,6 @@ +{ + "name": "discord", + "description": "Discord Client for Desktop - Bootstrapper", + "main": "index.js", + "private": true +} diff --git a/package-lock.json b/package-lock.json index e198080c..4cbd03c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "betterdiscord", - "version": "2.0.0-beta.4", + "version": "2.0.0-beta.6", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1216,6 +1216,12 @@ "integrity": "sha1-qCJQ3bABXponyoLoLqYDu/pF768=", "dev": true }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=", + "dev": true + }, "anymatch": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", @@ -2008,6 +2014,12 @@ "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "dev": true }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==", + "dev": true + }, "cacache": { "version": "11.3.2", "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz", @@ -5359,6 +5371,32 @@ } } }, + "gulp-gzip": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/gulp-gzip/-/gulp-gzip-1.4.2.tgz", + "integrity": "sha512-ZIxfkUwk2XmZPTT9pPHrHUQlZMyp9nPhg2sfoeN27mBGpi7OaHnOD+WCN41NXjfJQ69lV1nQ9LLm1hYxx4h3UQ==", + "dev": true, + "requires": { + "ansi-colors": "^1.0.1", + "bytes": "^3.0.0", + "fancy-log": "^1.3.2", + "plugin-error": "^1.0.0", + "stream-to-array": "^2.3.0", + "through2": "^2.0.3" + }, + "dependencies": { + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + } + } + }, "gulp-inject-string": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/gulp-inject-string/-/gulp-inject-string-1.1.2.tgz", @@ -5410,6 +5448,141 @@ "replacestream": "^4.0.0" } }, + "gulp-tar": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-tar/-/gulp-tar-2.1.0.tgz", + "integrity": "sha512-Yp/57bpiZPDVajUJix6QRCpL+XCNNFuaEcDVJ33LzenbGUkpJrH8j+8xJoMNlyi902uXV7rBy5sihwBnu5OfFw==", + "dev": true, + "requires": { + "archiver": "^1.0.0", + "plugin-error": "^0.1.2", + "through2": "^2.0.0", + "vinyl": "^2.1.0" + }, + "dependencies": { + "archiver": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-1.3.0.tgz", + "integrity": "sha1-TyGU1tj5nfP1MeaIHxTxXVX6ryI=", + "dev": true, + "requires": { + "archiver-utils": "^1.3.0", + "async": "^2.0.0", + "buffer-crc32": "^0.2.1", + "glob": "^7.0.0", + "lodash": "^4.8.0", + "readable-stream": "^2.0.0", + "tar-stream": "^1.5.0", + "walkdir": "^0.0.11", + "zip-stream": "^1.1.0" + } + }, + "archiver-utils": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-1.3.0.tgz", + "integrity": "sha1-5QtMCccL89aA4y/xt5lOn52JUXQ=", + "dev": true, + "requires": { + "glob": "^7.0.0", + "graceful-fs": "^4.1.0", + "lazystream": "^1.0.0", + "lodash": "^4.8.0", + "normalize-path": "^2.0.0", + "readable-stream": "^2.0.0" + } + }, + "arr-diff": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-1.1.0.tgz", + "integrity": "sha1-aHwydYFjWI/vfeezb6vklesaOZo=", + "dev": true, + "requires": { + "arr-flatten": "^1.0.1", + "array-slice": "^0.2.3" + } + }, + "arr-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-2.1.0.tgz", + "integrity": "sha1-IPnqtexw9cfSFbEHexw5Fh0pLH0=", + "dev": true + }, + "array-slice": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-0.2.3.tgz", + "integrity": "sha1-3Tz7gO15c6dRF82sabC5nshhhvU=", + "dev": true + }, + "async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.2.tgz", + "integrity": "sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "extend-shallow": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-1.1.4.tgz", + "integrity": "sha1-Gda/lN/AnXa6cR85uHLSH/TdkHE=", + "dev": true, + "requires": { + "kind-of": "^1.1.0" + } + }, + "kind-of": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", + "integrity": "sha1-FAo9LUGjbS78+pN3tiwk+ElaXEQ=", + "dev": true + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "plugin-error": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-0.1.2.tgz", + "integrity": "sha1-O5uzM1zPAPQl4HQ34ZJ2ln2kes4=", + "dev": true, + "requires": { + "ansi-cyan": "^0.1.1", + "ansi-red": "^0.1.1", + "arr-diff": "^1.0.1", + "arr-union": "^2.0.1", + "extend-shallow": "^1.1.2" + } + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "zip-stream": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-1.2.0.tgz", + "integrity": "sha1-qLxF9MG0lpnGuQGYuqyqzbzUugQ=", + "dev": true, + "requires": { + "archiver-utils": "^1.3.0", + "compress-commons": "^1.2.0", + "lodash": "^4.8.0", + "readable-stream": "^2.0.0" + } + } + } + }, "gulp-watch": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/gulp-watch/-/gulp-watch-5.0.1.tgz", @@ -7162,6 +7335,12 @@ } } }, + "merge2": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.2.3.tgz", + "integrity": "sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA==", + "dev": true + }, "micromatch": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", @@ -8610,6 +8789,57 @@ "requires": { "speedometer": "~0.1.2", "through2": "~0.2.3" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, + "through2": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", + "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", + "dev": true, + "requires": { + "readable-stream": "~1.1.9", + "xtend": "~2.1.1" + } + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "dev": true, + "requires": { + "object-keys": "~0.4.0" + } + } } }, "promise-inflight": { @@ -10150,6 +10380,15 @@ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, + "stream-to-array": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/stream-to-array/-/stream-to-array-2.3.0.tgz", + "integrity": "sha1-u/azn19D7DC8cbq8s3VXrOzzQ1M=", + "dev": true, + "requires": { + "any-promise": "^1.1.0" + } + }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -10499,54 +10738,12 @@ "dev": true }, "through2": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", - "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/through2/-/through2-3.0.1.tgz", + "integrity": "sha512-M96dvTalPT3YbYLaKaCuwu+j06D/8Jfib0o/PxbVt6Amhv3dUAtW6rTV1jPgJSBG83I/e04Y6xkVdVhSRhi0ww==", "dev": true, "requires": { - "readable-stream": "~1.1.9", - "xtend": "~2.1.1" - }, - "dependencies": { - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", - "dev": true - }, - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", - "dev": true - }, - "readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "xtend": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", - "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", - "dev": true, - "requires": { - "object-keys": "~0.4.0" - } - } + "readable-stream": "2 || 3" } }, "through2-filter": { @@ -11388,6 +11585,12 @@ "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==", "dev": true }, + "walkdir": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.0.11.tgz", + "integrity": "sha1-oW0CXrkxvQO1LzCMrtD0D86+lTI=", + "dev": true + }, "watchpack": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.0.tgz", diff --git a/package.json b/package.json index 6c3584c7..65386fff 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "fs-extra": "^7.0.1", "keytar": "4.4.1", "nedb": "^1.8.0", - "node-sass": "^4.11.0", + "node-sass": "4.11.0", "original-fs": "^1.0.0", "semver": "^5.6.0", "tar-fs": "^2.0.0" @@ -51,15 +51,18 @@ "gulp-babel": "^8.0.0", "gulp-copy": "^4.0.1", "gulp-file": "^0.4.0", + "gulp-gzip": "^1.4.2", "gulp-inject-string": "^1.1.2", "gulp-json-editor": "^2.5.1", "gulp-rename": "^1.4.0", "gulp-replace": "^1.0.0", + "gulp-tar": "^2.1.0", "gulp-watch": "^5.0.1", "hash-files": "^1.1.1", "html-webpack-plugin": "^3.2.0", "jquery": "^3.3.1", "lodash": "^4.17.11", + "merge2": "^1.2.3", "mkdirp": "^0.5.1", "node-gyp": "^3.8.0", "parallel-webpack": "^2.3.0", @@ -67,6 +70,7 @@ "request-promise-native": "1.0.5", "sass-lint": "^1.12.1", "sass-loader": "^7.1.0", + "through2": "^3.0.1", "v-tooltip": "^2.0.0-rc.33", "vue": "^2.6.8", "vue-color": "^2.7.0", @@ -100,6 +104,14 @@ "release": "npm run lint && npm run build_release && gulp release && npm run package_release", "release_test": "npm run build_release && gulp release", "update_release": "npm run build_release && gulp release", + "build_inject_deb": "gulp build-inject-deb", + "build_inject-ptb_deb": "gulp build-inject-deb-ptb", + "build_inject-canary_deb": "gulp build-inject-deb-canary", + "build_inject_debs": "gulp build-inject-debs", + "build_core_deb": "gulp build-core-deb", + "build_client_deb": "gulp build-client-deb", + "build_editor_deb": "gulp build-editor-deb", + "build_debs": "gulp build-debs", "inject": "node scripts/inject.js" } } diff --git a/scripts/dpkg.js b/scripts/dpkg.js new file mode 100644 index 00000000..d29f6d77 --- /dev/null +++ b/scripts/dpkg.js @@ -0,0 +1,155 @@ +const path = require('path'); +const stream = require('stream'); +const through2 = require('through2'); +const gulp = require('gulp'); +const Vinyl = require('vinyl'); +const pump = require('pump'); +const merge = require('merge2'); +const tar = require('gulp-tar'); +const gzip = require('gulp-gzip'); +const file = require('gulp-file'); +const replace = require('gulp-replace'); +const rename = require('gulp-rename'); + +/** + * Return a transform stream that create a Debian package from debian-binary, control.tar and data.tar files. + * @param {string} name The name of the .deb file to create + * @return {stream.TransformStream} + */ +function deb(name) { + const files = []; + + return through2({objectMode: true}, (file, enc, callback) => { + files.push(file); + callback(null, file); + }, function (callback) { (async () => { + const orderedfiles = [new Vinyl({path: '/debian-binary', contents: Buffer.from('2.0\n')})]; + orderedfiles.push(files.find(f => f.path.match(/\/control\.tar(\.gz)?$/))); + orderedfiles.push(files.find(f => f.path.match(/\/data\.tar(\.gz)?$/))); + if (orderedfiles.length !== 3) throw new Error('Must have three files'); + + const contents = new stream.Readable(); + contents._read = () => {}; + + const debfile = new Vinyl({ + cwd: '/', + base: '/', + path: '/' + name, + contents + }); + + contents.push('!' + String.fromCharCode(0x0A)); // Signature + + for (const [index, file] of orderedfiles.entries()) { + const size = file.stat && file.stat.size ? file.stat.size : file.contents && file.contents.length ? file.contents.length : 0; + + contents.push(rightPaddedWithSpaces(16, path.basename(file.path))); // Filename (ASCII, 16 bytes long) + contents.push(rightPaddedWithSpaces(12, (Math.floor(file.stat ? file.stat.mtime / 1000 : 0)).toString())); // File modification timestamp (Decimal, 12 bytes long) + contents.push(rightPaddedWithSpaces(6, '0')); // Owner ID (Decimal, 6 bytes long) + contents.push(rightPaddedWithSpaces(6, '0')); // Group ID (Decimal, 6 bytes long) + contents.push(rightPaddedWithSpaces(8, '100644')); // File mode (Octal, 8 bytes long) + contents.push(rightPaddedWithSpaces(10, size.toString())); // File size in bytes (Decimal, 10 bytes long) + contents.push(String.fromCharCode(0x60) + String.fromCharCode(0x0A)); // Ending characters ("0x60 0x0A") + + if (file.isStream()) await new Promise((resolve, reject) => { + file.contents.on('data', data => contents.push(data)); + file.contents.on('end', resolve); + file.contents.on('error', reject); + }); else contents.push(file.contents); + + // If the data for an archive member ends at an odd byte offset, then a padding byte with value 0x0A is + // used to position the next archive header on an even byte offset. + if (size % 2 === 1 && index !== 2) { + contents.push(String.fromCharCode(0x0A)); + } + } + + contents.push(null); + + this.push(debfile); + callback(); + })().catch(err => { + console.error(err); + callback(err); + }); }); +} + +function rightPaddedWithSpaces(n, string) { + if (!string) string = ''; + if (string.length > n) throw new Error('string is longer than n padding'); + return string + (new Array(n - string.length + 1)).join(String.fromCharCode(0x20)); +} + +function ensuredirectories() { + const directories = []; + + return through2.obj(function (file, enc, callback) { + const name = file.relative; + const nameparts = name.split('/'); + + if (file.isDirectory()) { + if (!directories.includes(file.name)) directories.push(file.name); + } else { + for (let [index, part] of nameparts.slice(0, nameparts.length - 1).entries()) { + let partpath = part; + while (index > 0) { + index--; + partpath = nameparts[index] + '/' + partpath; + } + + if (directories.includes(partpath)) continue; + + const directory = new Vinyl({ + cwd: file.cwd, + base: file.base, + path: path.join(file.base, partpath), + stat: { + isDirectory: () => true + } + }); + + this.push(directory); + directories.push(partpath); + } + } + + callback(null, file); + }); +} + +/** + * Creates a Debian package. + * @param {string} name + * @param {string} datafiles Path to the data directory + * @param {string} dataprefix The directory the files in the data directory should be unpacked to when installing + * @param {string} controlfiles Path to the control directory + * @param {Object} [controlvars] Variables to replace in the control files + * @return {stream.TransformStream} + */ +function mkdeb(name, datafiles, dataprefix, controlfiles, controlvars) { + return pump([ + merge([ + pump([ + gulp.src(controlfiles), + ...Object.keys(controlvars || {}).map(k => replace(`\${${k}}`, controlvars[k])), + tar('control.tar'), + gzip() + ]), + pump([ + gulp.src(datafiles), + rename(p => p.dirname = dataprefix + '/' + p.dirname), + ensuredirectories(), + tar(name + '.tar'), + gzip(), + gulp.dest('release'), + rename(p => p.basename = 'data.tar') + ]) + ]), + + deb(name + '.deb'), + gulp.dest('release') + ]); +} + +exports.deb = deb; +exports.mkdeb = mkdeb;