diff --git a/Jenkinsfile b/Jenkinsfile index 75a6bd384b..70658064d1 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -62,14 +62,12 @@ pipeline { environment { NODE_ENV = "" FIREFOX_BIN = "/opt/firefox/firefox-bin" + DOCKER_HOST = "${env.DOCKER_HOST_2}" } steps { nodejs('node-lts') { sh 'karma start --junit' - - sh 'gulp docker' - // sh 'gulp backendUnitTest --junit' // FIXME: Docker isn't at localhost - sh 'docker rm -f salix-db' + sh 'gulp backTestDocker --junit --random --run-chown' } } } diff --git a/gulpfile.js b/gulpfile.js index eb071a931c..49541cd981 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -16,6 +16,13 @@ let langs = ['es', 'en']; let srcDir = './front'; let modulesDir = './modules'; let buildDir = 'dist'; +let containerId = 'salix-db'; + +let dataSources = require('./loopback/server/datasources.json'); +let dbConf = dataSources.vn; + +if (process.env.DOCKER_HOST) + dbConf.host = process.env.DOCKER_HOST; let backSources = [ '!node_modules', @@ -66,43 +73,55 @@ defaultTask.description = `Starts all application services`; // Backend tests -function backendUnitTest() { +async function backTestOnly() { + let bootOptions; + + if (argv['random']) + bootOptions = {dataSources}; + let app = require(`./loopback/server/server`); + app.boot(bootOptions); - let specFiles = [ - 'back/**/*.spec.js', - 'loopback/**/*.spec.js', - 'modules/*/back/**/*.spec.js' - ]; + await new Promise((resolve, reject) => { + const jasmine = require('gulp-jasmine'); - const jasmine = require('gulp-jasmine'); - let options = {errorOnFail: false}; + let options = {errorOnFail: false}; - if (argv.junit || argv.j) { - const reporters = require('jasmine-reporters'); - options.reporter = new reporters.JUnitXmlReporter(); - } + if (argv.junit) { + const reporters = require('jasmine-reporters'); + options.reporter = new reporters.JUnitXmlReporter(); + } - return gulp.src(specFiles) - .pipe(jasmine(options)) - .on('jasmineDone', function() { - app.disconnect(); - }); + let backSpecFiles = [ + 'back/**/*.spec.js', + 'loopback/**/*.spec.js', + 'modules/*/back/**/*.spec.js' + ]; + + gulp.src(backSpecFiles) + .pipe(jasmine(options)) + .on('end', resolve) + .resume(); + }); + + await app.disconnect(); } -backendUnitTest.description = `Runs the backend tests only, can receive args --junit or -j to save reports on a xml file`; +backTestOnly.description = `Runs the backend tests only, can receive --junit arg to save reports on a xml file`; -const dockerAndBackTest = gulp.series(docker, backendUnitTest); -dockerAndBackTest.description = `Restarts database and runs the backend tests`; +async function backTestDocker() { + let containerId = await docker(); + await backTestOnly(); + if (argv['random']) + await execP(`docker rm -fv ${containerId}`); +} +backTestDocker.description = `Runs backend tests using in site container`; function backTest(done) { const nodemon = require('gulp-nodemon'); - let gulpBin = isWindows - ? 'node_modules/.bin/gulp.cmd' - : 'node_modules/.bin/gulp'; nodemon({ - exec: gulpBin, - args: ['backendUnitTest'], + exec: ['node ./node_modules/gulp/bin/gulp.js'], + args: ['backTestOnly'], watch: backSources, done: done }); @@ -324,20 +343,35 @@ watch.description = `Watches for changes in routes and locale files`; * to avoid a bug with OverlayFS driver on MacOS. */ async function docker() { - try { - await execP('docker rm -fv salix-db'); - } catch (e) {} - let d = new Date(); let pad = v => v < 10 ? '0' + v : v; let stamp = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; await execP(`docker build --build-arg STAMP=${stamp} -t salix-db ./services/db`); + let dockerArgs = `--name ${containerId} -p 3306:${dbConf.port}`; + + if (argv['random']) + dockerArgs = '-p 3306'; + else { + try { + await execP(`docker rm -fv ${containerId}`); + } catch (e) {} + } + let runChown = process.platform != 'linux'; if (argv['run-chown']) runChown = true; - await execP(`docker run --env RUN_CHOWN=${runChown} -d --name salix-db -p 3306:3306 salix-db`); + let result = await execP(`docker run --env RUN_CHOWN=${runChown} -d ${dockerArgs} salix-db`); + containerId = result.stdout; + + if (argv['random']) { + let inspect = await execP(`docker inspect -f "{{json .NetworkSettings.Ports}}" ${containerId}`); + let ports = JSON.parse(inspect.stdout); + dbConf.port = ports['3306/tcp'][0]['HostPort']; + } + if (runChown) await dockerWait(); + return containerId; } docker.description = `Builds the database image and runs a container`; @@ -350,7 +384,7 @@ docker.description = `Builds the database image and runs a container`; async function dockerStart() { let state; try { - let result = await execP('docker container inspect -f "{{json .State}}" salix-db'); + let result = await execP(`docker inspect -f "{{json .State}}" ${containerId}`); state = JSON.parse(result.stdout); } catch (err) { return await docker(); @@ -360,7 +394,7 @@ async function dockerStart() { case 'running': return; case 'exited': - await execP('docker start salix-db'); + await execP(`docker start ${containerId}`); await dockerWait(); return; default: @@ -377,6 +411,13 @@ function dockerWait() { let elapsedTime = 0; let maxInterval = 5 * 60 * 1000; + let myConf = { + user: dbConf.username, + password: dbConf.password, + host: dbConf.host, + port: dbConf.port + }; + log('Waiting for MySQL init process...'); checker(); @@ -385,7 +426,7 @@ function dockerWait() { let state; try { - let result = await execP('docker container inspect -f "{{json .State}}" salix-db'); + let result = await execP(`docker container inspect -f "{{json .State}}" ${containerId}`); state = JSON.parse(result.stdout); } catch (err) { return reject(new Error(err.message)); @@ -394,11 +435,7 @@ function dockerWait() { if (state.Status === 'exited') return reject(new Error('Docker exited, please see the docker logs for more info')); - let conn = mysql.createConnection({ - host: 'localhost', - user: 'root', - password: 'root' - }); + let conn = mysql.createConnection(myConf); conn.on('error', () => {}); conn.connect(err => { conn.destroy(); @@ -443,21 +480,21 @@ module.exports = { back, backOnly, backWatch, - backendUnitTest, - dockerAndBackTest, backTest, - e2eOnly, + backTestOnly, + backTestDocker, e2e, - smokesOnly, + e2eOnly, smokes, - install, + smokesOnly, i, + install, build, clean, webpack, webpackDevServer, - locales, routes, + locales, localesRoutes, watch, docker, diff --git a/loopback/server/server.js b/loopback/server/server.js index 3d84136710..0e2e123ca8 100644 --- a/loopback/server/server.js +++ b/loopback/server/server.js @@ -34,98 +34,106 @@ let modulesDir = `${appDir}/modules`; // Internationalization -if (fs.existsSync(localeDir)) { - i18n.configure({ - directory: localeDir, - defaultLocale: 'es' - }); - - app.use(i18n.init); -} - -// Initialization - app.disconnect = async function() { let promises = []; - for (let ds in app.dataSources) - promises.push(app.dataSources[ds].disconnect()); + for (let ds in this.dataSources) + promises.push(this.dataSources[ds].disconnect()); return await Promise.all(promises); }; -app.start = function(port) { - function onListen() { - let baseUrl = app.get('url').replace(/\/$/, ''); - let explorerPath = app.get('loopback-component-explorer').mountPath; +app.start = function(bootOptions, port, callback) { + let onListen = () => { + let baseUrl = this.get('url').replace(/\/$/, ''); + let explorerPath = this.get('loopback-component-explorer').mountPath; console.log(`Browse your REST API at: %s`, `${baseUrl}${explorerPath}`); - app.emit('started'); - } + this.emit('started'); + callback && callback(); + }; - let args = port ? [port, onListen] : [onListen]; - return app.listen(...args); + this.boot(bootOptions, err => { + if (err) throw err; + let args = port ? [port, onListen] : [onListen]; + return this.listen(...args); + }); }; -let config = require('./config.json'); +app.boot = function(myBootOptions, callback) { + // Internatinalization -for (let key in config) - app.set(key, config[key]); + if (fs.existsSync(localeDir)) { + i18n.configure({ + directory: localeDir, + defaultLocale: 'es' + }); -let modelConfigFiles = [ - `${__dirname}/model-config.json` -]; -let modelSources = [ - `loopback/common/models`, - `loopback/server/models`, - `${__dirname}/../common/models` -]; -let mixinDirs = [ - `loopback/common/mixins`, - `loopback/server/mixins`, - `${__dirname}/../common/mixins` -]; -let bootDirs = [ - `${__dirname}/boot` -]; + this.use(i18n.init); + } -addPath(`${appDir}/back`); + // Initialization -let modules = fs.readdirSync(modulesDir); -for (let mod of modules) - addPath(`${modulesDir}/${mod}/back`); + let config = require('./config.json'); -function addPath(path) { - modelConfigFiles.push(`${path}/model-config.json`); - modelSources.push(`${path}/models`); - mixinDirs.push(`${path}/mixins`); - bootDirs.push(`${path}/boot`); -} + for (let key in config) + this.set(key, config[key]); -let models = {}; -for (file of modelConfigFiles) { - if (fs.existsSync(file)) { - let fileModels = require(file); - for (let key in fileModels) { - if (models[key]) - console.warn(`Redeclaration of '${key}' at ${file}`); + let modelConfigFiles = [ + `${__dirname}/model-config.json` + ]; + let modelSources = [ + `loopback/common/models`, + `loopback/server/models`, + `${__dirname}/../common/models` + ]; + let mixinDirs = [ + `loopback/common/mixins`, + `loopback/server/mixins`, + `${__dirname}/../common/mixins` + ]; + let bootDirs = [ + `${__dirname}/boot` + ]; + + addPath(`${appDir}/back`); + + let modules = fs.readdirSync(modulesDir); + for (let mod of modules) + addPath(`${modulesDir}/${mod}/back`); + + function addPath(path) { + modelConfigFiles.push(`${path}/model-config.json`); + modelSources.push(`${path}/models`); + mixinDirs.push(`${path}/mixins`); + bootDirs.push(`${path}/boot`); + } + + let models = {}; + for (file of modelConfigFiles) { + if (fs.existsSync(file)) { + let fileModels = require(file); + for (let key in fileModels) { + if (models[key]) + console.warn(`Redeclaration of '${key}' at ${file}`); + } + Object.assign(models, fileModels); } - Object.assign(models, fileModels); } -} -let bootOptions = { - appRootDir: __dirname, - appConfigRootDir: rootDir, - modelsRootDir: rootDir, - models: models, - modelSources: modelSources, - mixinDirs: mixinDirs, - bootDirs: bootDirs + let bootOptions = { + appRootDir: __dirname, + appConfigRootDir: rootDir, + modelsRootDir: rootDir, + models: models, + modelSources: modelSources, + mixinDirs: mixinDirs, + bootDirs: bootDirs + }; + + if (fs.existsSync(`/etc/salix`)) + bootOptions.dsRootDir = `/etc/salix`; + + Object.assign(bootOptions, myBootOptions); + boot(this, bootOptions, callback); }; -if (fs.existsSync(`/etc/salix`)) - bootOptions.dsRootDir = `/etc/salix`; - -boot(app, bootOptions, function(err) { - if (err) throw err; - if (require.main === module) - app.start(); -}); +if (require.main === module) + app.start();