@tangelo/tangelo-configuration-toolkit
Version:
Tangelo Configuration Toolkit is a command-line toolkit which offers support for developing a Tangelo configuration.
179 lines (154 loc) • 8.23 kB
JavaScript
const babel_pe = require('@babel/preset-env');
const execGitCommand = require('../../lib/exec-git-command');
const fs = require('fs-extra');
const globby = require('globby');
const gulp = require('gulp');
const g_tcl = require('../../lib/gulp-tcl-config');
const through2 = require('through2');
const {spawnSync} = require('child_process');
const g_babel = require('gulp-babel');
const g_eol = require('gulp-eol');
const g_filter = require('gulp-filter');
const g_plumber = require('gulp-plumber');
const g_print = require('gulp-print');
const g_replace = require('../../lib/gulp-batch-replace-with-filter');
const g_resolveIncl = require('../../lib/gulp-resolve-includes');
const g_sass = require('gulp-sass')(require('sass'));
const g_sftp = require('../../lib/gulp-sftp');
const g_sourcemaps = require('gulp-sourcemaps');
const c = require('./config');
const s = require('./srcset');
const createDeliveryPack = () => { // create install scripts if necessary, zip output and remove temp folder
const dbiSql = [];
const dbiPs1 = [];
globby.sync('**/database/**/*install.sql', {cwd: c.server.remotedir}).forEach(p => { // add install-script calls and make sure tdi comes first
const [dir, file] = p.match(/(.+)\/([^/]+)$/).slice(1);
if (/config.*\/..._install/.test(p)) dbiSql.push(`@${p}`);
else if (dir.endsWith('tdi')) dbiPs1.unshift(`cd $cwd/${dir}`, `sqlplus /nolog "@${file}"`);
else dbiPs1.push(`cd $cwd/${dir}`, `sqlplus /nolog "@${file}"`);
});
if (dbiSql[0]) { // complete and output sql script
dbiSql.unshift(
`accept l_tns char prompt 'Database TNS name : '`,
`accept l_pw_tancms char prompt 'TANCMS password : '`,
`define l_env = 'prd'`,
'whenever sqlerror exit sql.sqlcode',
'prompt ',
'connect tancms/&l_pw_tancms@&l_tns',
'prompt ',
'set verify off define off'
);
dbiSql.push('prompt ', 'pause Press ENTER to exit', 'exit');
fs.outputFileSync(`${c.server.remotedir}/config-install.sql`, dbiSql.join('\n'));
dbiPs1.push('cd $cwd', 'sqlplus /nolog "@config-install.sql"');
_info('SQL script for database config install created');
}
if (dbiPs1[0]) { // complete and output powershell script
dbiPs1.unshift('$cwd = Get-Location');
dbiPs1.push('cd $cwd');
fs.outputFileSync(`${c.server.remotedir}/database-install.ps1`, dbiPs1.join('\n'));
_info('PowerShell script for database install created');
}
const result = spawnSync('powershell.exe', [`Compress-Archive -Path * -DestinationPath ../${c.deliveryPackName}`], {cwd: c.server.remotedir});
if (result.stderr!='') {_warn(`\nLong paths may cause zip-creation to fail; Try to set a short remotedir, for example 'C:/deliveryPacks'.`);_error(`${result.stderr}\n`);}
fs.removeSync(c.server.remotedir);
_info(`Zipped package as: ${c.deliveryPackName}`);
};
module.exports = function transfer (paths, {watch, lrServer} = {}) {
const jsFGlob = _settingsTdi().transpileToES5 ? ['**/*.js', '!**/hce/**/template_*', '!**/fonto/**', '!**/vendor/**', '!**/*.min.js'] : [];
const jsF = g_filter(jsFGlob, {restore: true});
const xpsF = g_filter(['**/xopus/**/*.x*'], {restore: true});
const sassF = g_filter(['**/*.scss'], {restore: true});
const shF = g_filter(['**/*.sh'], {restore: true});
const srcset = s.create(paths, {watch});
const files = [];
// local git repo must be on latest commit containing fonto changes, and fonto build files must be newer than that commit, unless that commit belongs to the current user
const fontoPaths = {outdated: [], uptodate: []};
const removeOutdatedFontoBuild = g_filter(file => {
const prPath = 'config\\'+file.relative.replace(/fonto.+$/, '');
if (fontoPaths.outdated.includes(prPath)) return false;
if (fontoPaths.uptodate.includes(prPath)) return true;
if (file.relative.match(/fonto[\\/]dist/)) {
const paths = `${prPath}\\fonto ${prPath}\\schema ${_paths.tdi}`; // only check paths containing fonto sources
const lastFontoCommit = execGitCommand(`log -1 --format=%ae;%cd --date=iso-strict origin/${_git.commitLocal().branch} ${paths}`, _paths.repo, ['author', 'date']);
if (
lastFontoCommit.author != _git.user() &&
(lastFontoCommit.date > _git.commitLocal().date || lastFontoCommit.date > fs.statSync(file.relative).mtime)
) {
fontoPaths.outdated.push(prPath);
return false;
}
else fontoPaths.uptodate.push(prPath);
}
return true;
});
// disable file logging for ftp (gulp-sftp does it already)
g_print.setLogFunction(filepath => c.server.ftpConfig ? null : _write(filepath.nostyle));
_info('Start transferring', true);
gulp.src(srcset, {base: '.', follow: true, allowEmpty: true})
.pipe(g_plumber()) // catch errors in plugins so watch doesnt break
.pipe(removeOutdatedFontoBuild)
.pipe(xpsF)
.pipe(g_resolveIncl())
.pipe(xpsF.restore)
.pipe(g_tcl())
.pipe(jsF)
.pipe(g_sourcemaps.init())
.pipe(g_babel({presets: [[babel_pe, {modules: false}]], comments: false, minified: true})) // function must be executed here, otherwise second execution during watch fails
.pipe(g_sourcemaps.write('.'))
.pipe(jsF.restore)
.pipe(sassF)
.pipe(g_sourcemaps.init())
.pipe(g_sass().on('error', g_sass.logError))
.pipe(g_sourcemaps.write('.'))
.pipe(sassF.restore)
.pipe(shF)
.pipe(g_eol('\n'))
.pipe(shF.restore)
.pipe(g_plumber.stop())
.pipe(g_replace(c.replaceStrings))
.pipe(through2.obj(combineTranslations))
.pipe(through2.obj((file, enc, cb) => {
file.originalRelativePath = file.relative; // original path needed for sftp.fastPut
file.path = file.path.replace(/(fonto).(?:dev|dist|packages.sx-shell-.+src)(.+)/, '$1$2'); // change destination path for fonto build files
if (!file.relative.endsWith('.map')) files.push(file.relative); // collect all file paths in array for livereload
cb(null, file);
}))
.pipe(g_print.default())
.pipe(c.server.ftpConfig ? g_sftp(c.server.ftpConfig, c.server.remotedir) : gulp.dest(c.server.remotedir))
.on('end', () => {
_info('Finished transferring\n', true);
if (fontoPaths.outdated[0]) _warn(`Fonto build files in the following folders were outdated and therefore skipped:\n ${fontoPaths.outdated.map(v => v.slice(0, -1)).join('\n ')}`);
if (lrServer) { // reload specific resources if files are all image or css, else reload page
const reloadPage = !files.every(f => /.(?:css|jpe?g|png|gif)$/i.test(f));
lrServer.changed({params: {files: reloadPage ? ['page'] : files}});
_info(`${reloadPage ? 'Page' : 'Resources'} reloaded\n`);
}
if (c.deliveryPack) createDeliveryPack();
});
};
// Combine custom translations/resource bundle with tdi translations
async function combineTranslations(file, enc, cb) {
const isTranslationFile = file.basename.match(/custom-UI-text.*\.properties/);
// Ignore non-translation files
if (!isTranslationFile) return cb(null, file);
// Remove tdi translations from pipe
if (file.path.match(/tdi/)) return cb();
// Get language from file name
const language = file.basename.match(/custom-UI-text(_.+?)?\.properties/)[1] ?? '';
// Read matching tdi translation file
const tdiTranslationPath = `cmscustom/tdi/custom-UI-text/custom-UI-text${language}.properties`;
let tdiTranslations;
try {
tdiTranslations = await fs.readFile(tdiTranslationPath, enc);
}
catch {
// If tdi translation file does not exist (<5.8), leave custom translations unchanged
return cb(null, file);
}
// Combine file content
const separator = `# Contents of ${tdiTranslationPath}`;
const newText = [file.contents, separator, tdiTranslations].join('\n');
file.contents = Buffer.from(newText);
return cb(null, file);
}