diff --git a/appveyor.yml b/appveyor.yml index 89fb91a..4ef5d69 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -38,6 +38,7 @@ install: build_script: - yarn run build:dir -w --c.directories.output=./dist/ - SET BUILD_VERSION=%APPVEYOR_BUILD_VERSION% +- yarn run version-file %BUILD_VERSION% --commit %APPVEYOR_REPO_COMMIT% - >- "C:\\Program Files (x86)\\Inno Setup 5\\ISCC.exe" /Odist /FFIRST_LEGO_League_TMS_Setup_%BUILD_VERSION%_%PLATFORM% .\\windows\\setup\\main.iss diff --git a/dev-scripts/version-file.js b/dev-scripts/version-file.js new file mode 100644 index 0000000..0cae5dc --- /dev/null +++ b/dev-scripts/version-file.js @@ -0,0 +1,30 @@ + +const caporal = require('caporal') +const fs = require('fs') +const path = require('path') + +caporal + .name('version-file') + .version('v1') + .option('--commit, -c ', 'The git commit of the version') + .option('--ts ', 'The timestamp of the build') + .option('--output ', 'The output file') + .argument('', 'The semver version for the file') + .action((args, options) => { + const timestamp = options.ts ? Number(options.ts) : Date.now() + + if (Number.isNaN(timestamp)) { + console.error('ERROR: timestamp should be in unix timestamp format') + return + } + + const outputFile = path.resolve(options.output || './dist/version.json') + + fs.writeFileSync(outputFile, JSON.stringify({ + semver: args.semver || 'none', + timestamp: timestamp, + commit: options.commit || 'NA' + })) + }) + +caporal.parse(process.argv) diff --git a/package.json b/package.json index 97c8dcd..c30cfc4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "fll-launcher", - "version": "2.0.0", + "name": "first-lego-league-tms", + "version": "0.0.0", "author": "Roy Shmueli <2903801+2roy999@users.noreply.github.com>", "description": "A Launcher to the FIRST LEGO League tournament managment system", "main": "./dist/electron/main.js", @@ -20,12 +20,13 @@ "pack:main": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.main.config.js", "pack:renderer": "cross-env NODE_ENV=production webpack --progress --colors --config .electron-vue/webpack.renderer.config.js", "postinstall": "electron-builder install-app-deps", - "start": "node .electron-vue/dev-runner.js" + "start": "node .electron-vue/dev-runner.js", + "version-file": "node ./dev-scripts/version-file.js" }, "build": { "appId": "com.fll-tools.launcher", "productName": "FIRST LEGO League TMS", - "artifactName": "fll-launcher-${version}", + "artifactName": "fll-launcher-${env.BUILD_VERSION || 'snapshot'}", "directories": { "buildResources": "resources", "output": "dist" diff --git a/src/main/app-paths.js b/src/main/app-paths.js new file mode 100644 index 0000000..1f6acfe --- /dev/null +++ b/src/main/app-paths.js @@ -0,0 +1,13 @@ + +const { app } = require('electron') +const path = require('path') + +const version = require('./version') + +const useDevPaths = (process.env.NODE_ENV !== 'development') || Boolean(process.env.PRODUCTION_DIRS) + +const versionHash = `${version.semver}$${version.timestamp}$${version.commit}` + +exports.DATA_DIR = useDevPaths ? path.join(app.getPath('userData'), versionHash, 'data') : path.resolve('./data') +exports.LOG_DIR = useDevPaths ? path.join(app.getPath('userData'), versionHash, 'logs') : path.resolve('./logs') +exports.TEMP_DIR = useDevPaths ? path.join(app.getPath('temp'), versionHash) : path.resolve('./tmp') diff --git a/src/main/caddy/index.js b/src/main/caddy/index.js index 447406b..4fd7b14 100644 --- a/src/main/caddy/index.js +++ b/src/main/caddy/index.js @@ -1,11 +1,13 @@ -const fs = require('fs') +const Promise = require('bluebird') const ejs = require('ejs') -const path = require('path') +const fs = require('fs') const mkdirp = require('mkdirp') -const rimraf = require('rimraf') -const Promise = require('bluebird') +const path = require('path') const randomatic = require('randomatic') +const rimraf = require('rimraf') + +const { TEMP_DIR } = require('../app-paths') Promise.promisifyAll(fs) Promise.promisifyAll(ejs) @@ -15,8 +17,8 @@ const rimrafAsync = Promise.promisify(rimraf) const FILE_EXTENSION = (process.platform === 'win32') ? '.exe' : '' const CADDY_EXECUTABLE_PATH = path.resolve(`./internals/caddy/caddy${FILE_EXTENSION}`) const CADDY_FILE_TEMPLATE = path.join(__static, 'caddy-file.ejs') -const CADDY_FILE_PATH = path.resolve('./tmp/$CaddyFile') -const CADDY_ENV_DIR = path.resolve('./tmp/$caddy/') +const CADDY_FILE_PATH = path.resolve(TEMP_DIR, '$CaddyFile') +const CADDY_ENV_DIR = path.resolve(TEMP_DIR, '$caddy') function generateCaddyFileContent (caddyFile, sites) { return mkdirpAsync(path.dirname(caddyFile)) diff --git a/src/main/configurator/index.js b/src/main/configurator/index.js index 83a3e04..e2646ee 100644 --- a/src/main/configurator/index.js +++ b/src/main/configurator/index.js @@ -7,9 +7,11 @@ const EventEmitter = require('events') const { getDefaultValue, getUpdatedValue, isValidValue } = require('./types') +const { DATA_DIR } = require('../app-paths') + const mkdirpAsync = Promise.promisify(mkdirp) -const STORAGE_PATH = exports.STORAGE_PATH = path.resolve('./data/$config.sqlite') +const STORAGE_PATH = exports.STORAGE_PATH = path.join(DATA_DIR, '$config.sqlite') exports.Configurator = class extends EventEmitter { constructor () { @@ -17,6 +19,7 @@ exports.Configurator = class extends EventEmitter { this.configMetadata = {} this.sealed = false this.started = false + this.storagePath = path.join(DATA_DIR, '$config.sqlite') this.storagePromise = mkdirpAsync(path.dirname(STORAGE_PATH)) .then(() => new Keyv(`sqlite://${STORAGE_PATH}`)) diff --git a/src/main/logs.js b/src/main/logs.js index d15328f..edfe803 100644 --- a/src/main/logs.js +++ b/src/main/logs.js @@ -1,13 +1,14 @@ -const path = require('path') +const Promise = require('bluebird') const mkdirp = require('mkdirp') +const path = require('path') const rotate = require('rotating-file-stream') const winston = require('winston') -const Promise = require('bluebird') + +const { LOG_DIR } = require('./app-paths') const LOG_SIZE = '10M' const LOG_INTERVAL = '1d' -const LOG_DIR = exports.LOG_DIR = path.resolve('./logs/') const { combine, timestamp, json } = winston.format diff --git a/src/main/mhub/index.js b/src/main/mhub/index.js index fc6701a..783f696 100644 --- a/src/main/mhub/index.js +++ b/src/main/mhub/index.js @@ -11,6 +11,7 @@ const randomatic = require('randomatic') const { MClient } = require('mhub') const { logger } = require('../logs') +const { TEMP_DIR } = require('../app-paths') Promise.promisifyAll(fs) Promise.promisifyAll(ejs) @@ -21,7 +22,7 @@ const MHUB_CONNECTION_STRING = 'ws://127.0.0.1:13900' // const MHUB_NODE_NAME = 'default' const MHUB_EXECUTABLE_PATH = path.resolve('./internals/mhub/bin/mhub-server') const MHUB_FILE_TEMPLATE = path.join(__static, 'mhub-config.ejs') -const MHUB_FILE_PATH = path.resolve('./tmp/$mhub.config.json') +const MHUB_FILE_PATH = path.join(TEMP_DIR, '$mhub.config.json') const MAX_RETRIES = 5 diff --git a/src/main/mongo.js b/src/main/mongo.js index fad43e8..b492cc5 100644 --- a/src/main/mongo.js +++ b/src/main/mongo.js @@ -6,6 +6,7 @@ const Promise = require('bluebird') const { spawn } = require('child_process') const { logger } = require('./logs') +const { TEMP_DIR, DATA_DIR } = require('./app-paths') Promise.promisifyAll(fs) @@ -13,12 +14,13 @@ const mkdirpAsync = Promise.promisify(mkdirp) const FILE_EXTENSION = (process.platform === 'win32') ? '.exe' : '' const MONGO_BIN_DIR = path.resolve(`./internals/mongo/bin`) -const MONGO_EXECUTABLE_PATH = path.resolve(MONGO_BIN_DIR, `mongod${FILE_EXTENSION}`) -const MONGO_ARGUMENTS = ['--dbpath', './data/$mongo'] +const MONGO_EXECUTABLE_PATH = path.join(MONGO_BIN_DIR, `mongod${FILE_EXTENSION}`) +const MONGO_DATA_PATH = path.join(DATA_DIR, '$mongo') +const MONGO_ARGUMENTS = ['--dbpath', MONGO_DATA_PATH] .concat((process.arch === 'ia32') ? ['--storageEngine=mmapv1'] : []) -const MONGODUMP_EXECUTABLE_PATH = path.resolve(MONGO_BIN_DIR, `mongodump${FILE_EXTENSION}`) -const DUMP_PATH = exports.DUMP_PATH = path.resolve('./dump') +const MONGODUMP_EXECUTABLE_PATH = path.join(MONGO_BIN_DIR, `mongodump${FILE_EXTENSION}`) +const DUMP_PATH = exports.DUMP_PATH = path.join(TEMP_DIR, 'dump') exports.dump = function () { return mkdirpAsync(DUMP_PATH).then(() => { @@ -63,7 +65,7 @@ class Mongo { start () { logger.info(`process.arch:${process.arch} MONGO_ARGUMENTS:${MONGO_ARGUMENTS}`) - return mkdirpAsync('./data/$mongo') + return mkdirpAsync(MONGO_DATA_PATH) .then(() => this.serviceManager.startService({ serviceName: 'mongo', serviceId: this.serviceId, diff --git a/src/main/server.js b/src/main/server.js index e1091cc..d970950 100644 --- a/src/main/server.js +++ b/src/main/server.js @@ -1,24 +1,23 @@ const fs = require('fs') -const path = require('path') const Promise = require('bluebird') const randomatic = require('randomatic') -const { Mhub } = require('./mhub') +const { DATA_DIR } = require('./app-paths') const { Caddy } = require('./caddy') +const { Configurator } = require('./configurator') +const { loadLogsOptions, createLogStream, logger } = require('./logs') +const { globalModuleConfig } = require('./global-config') +const { Mhub } = require('./mhub') +const { loadModules } = require('./module-loader') const { Mongo } = require('./mongo') const { getIp } = require('./network') -const { loadModules } = require('./module-loader') -const { loadLogsOptions, createLogStream, logger } = require('./logs') const { ServiceManager } = require('./services') -const { Configurator } = require('./configurator') -const { globalModuleConfig } = require('./global-config') Promise.promisifyAll(fs) const STARTING_PORT = 2828 const SECRET_LENGTH = 12 -const DATA_DIR = path.resolve('./data/') exports.Server = class { constructor (modulesFile) { diff --git a/src/main/version.js b/src/main/version.js new file mode 100644 index 0000000..304589b --- /dev/null +++ b/src/main/version.js @@ -0,0 +1,25 @@ +const fs = require('fs') +const path = require('path') + +function getGitCommit () { + try { + const gitHead = fs.readFileSync(path.resolve('./.git/HEAD'), 'utf8').trim() + return fs.readFileSync(path.resolve('./.git', gitHead.substring('ref: '.length)), 'utf8').trim() + } catch (e) { + console.error('WARNING: Error getting current git commit') + } +} + +function getDefaultVersionObject () { + return { + semver: 'snapshot', + timestamp: Date.now(), + commit: getGitCommit() || 'NA' + } +} + +if (process.env.NODE_ENV !== 'development') { + Object.assign(exports, JSON.parse(fs.readFileSync(path.resolve('./version.json')))) +} else { + Object.assign(exports, getDefaultVersionObject()) +} diff --git a/windows/setup/main.iss b/windows/setup/main.iss index c49a364..ea8e285 100644 --- a/windows/setup/main.iss +++ b/windows/setup/main.iss @@ -9,6 +9,7 @@ #define modules "..\..\modules" #define internals "..\..\internals" #define static "..\..\static" +#define dist "..\..\dist" #define executable "FIRST LEGO League TMS.exe" #define add_firewall_rule "advfirewall firewall add rule protocol=tcp dir=in enable=yes action=allow profile=private" @@ -23,16 +24,12 @@ DisableProgramGroupPage=yes ArchitecturesInstallIn64BitMode=x64 #endif -[Dirs] -Name: "{app}\data" -Name: "{app}\logs" -Name: "{app}\tmp" - [Files] Source: "{#app}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; Source: "{#modules}\*"; DestDir: "{app}\modules"; Flags: ignoreversion recursesubdirs; Source: "{#internals}\*"; DestDir: "{app}\internals"; Flags: ignoreversion recursesubdirs; Source: "{#static}\repair-mongo.bat"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs; +Source: "{#dist}\version.json"; DestDir: "{app}"; Flags: ignoreversion; [Run] Filename: "{sys}\netsh.exe"; StatusMsg: "Adding firewall rules..."; Parameters: "{#add_firewall_rule} name=""FIRST LEGO League TMS"" program=""{app}\{#executable}"""