function leaveTracked(filenames) { la(check.arrayOfStrings(filenames), 'expected list of filenames', filenames); return q.all(filenames.map(isTracked)) .then(R.zipObj(filenames)) .then(R.pickBy(R.eq(true))) .then(R.keys); }
function sortNames(sort, names) { la(check.unemptyString(sort), 'missing sort order string', sort); la(check.arrayOfStrings(names), 'missing names', names); var sortFn = sortFunctions[sort] || defaultSort; return sortFn(names); }
.then(function saveToFile (topDependents) { la(check.arrayOfStrings(topDependents), 'expected list of top strings', topDependents) // TODO use template library instead of manual concat var str = '// top ' + n + ' most dependent modules by ' + metric + ' for ' + name + '\n' str += '// data from NPM registry on ' + (new Date()).toDateString() + '\n' str += JSON.stringify(topDependents, null, 2) + '\n' return fs.writeFile(dontBreakFilename, str, 'utf-8').then(function () { console.log('saved top', n, 'dependents for', name, 'by', metric, 'to', dontBreakFilename) return topDependents }) })
function updateFromConfig(options) { la(check.object(options), 'missing options'); la(check.unemptyString(options.config), 'cannot find config option', options); la(fs.existsSync(options.config), 'cannot find file', options.config); var config = JSON.parse(stripComments(fs.readFileSync(options.config, 'utf-8'))); la(check.arrayOfStrings(config.repos), 'cannot find repos list', config); var opts = _.cloneDeep(options); _.defaults(opts, config.options); return updateMultipleRepos(opts, config.repos); }
function dontBreakDependents (options, dependents) { la(check.arrayOf(check.object, dependents) || check.arrayOfStrings(dependents), 'invalid dependents', dependents) debug('dependents', dependents) if (check.empty(dependents)) { return Promise.resolve() } banner(' testing the following dependents\n ' + JSON.stringify(dependents)) var logSuccess = function logSuccess () { console.log('all dependents tested') } return testDependents(options, dependents) .then(logSuccess) }
function dontBreak (options) { if (check.unemptyString(options)) { options = { folder: options } } options = options || {} options.folder = options.folder || process.cwd() debug('working in folder %s', options.folder) var start = chdir.to(options.folder) if (check.arrayOfStrings(options.dep)) { start = start.then(function () { return options.dep }) } else { start = start.then(function () { debug('getting dependents') return getDependents(options) }) } var logPass = function logPass () { console.log('PASS: Current version does not break dependents') return true } var logFail = function logFail (err) { console.log('FAIL: Current version breaks dependents') if (err && err.message) { console.error('REPORTED ERROR:', err.message) if (err.stack) { console.error(err.stack) } } return false } return start .then(_.partial(dontBreakDependents, options)) .then(logPass, logFail) .finally(chdir.from) }
// expect common output folder function generateGitLabCiFile (outputFolder, specFiles, dockerImage, script, beforeScript, afterScript, buildScript) { debug(`generating gitlab file for folder "${outputFolder}"`) la(is.maybe.unemptyString(dockerImage), 'docker image should be just string', dockerImage) if (dockerImage) { debug(`based on docker image ${dockerImage}`) } la(is.maybe.arrayOfStrings(script), 'expected test commands', script) const defaultBuildCommands = [ 'echo $CI_BUILD_ID > build.id', 'npm install --quiet', 'npm test', 'npm run build' ] if (!buildScript) { buildScript = defaultBuildCommands } const defaultTestCommands = [ `cypress ci --spec "${outputFolder}/$CI_BUILD_NAME.js"` ] if (!script) { script = defaultTestCommands } debug('test commands') debug(script) const defaultBeforeScriptCommands = [] if (!beforeScript) { beforeScript = defaultBeforeScriptCommands } if (is.not.empty(beforeScript)) { debug('before script commands') debug(beforeScript) } const defaultAfterScriptCommands = [] if (!afterScript) { afterScript = defaultAfterScriptCommands } if (is.not.empty(afterScript)) { debug('after script commands') debug(afterScript) } la(is.arrayOfStrings(specFiles), 'expected list of specs', specFiles) const names = specFiles.map((full) => path.basename(full, '.js')) debug('project names', names) const templateName = relative('./gitlab-ci-template.yml') const start = fs.readFileSync(templateName, 'utf8') var gitlabFile = '# this is a generated file\n' if (dockerImage) { gitlabFile += `image: ${dockerImage} ` } gitlabFile += start gitlabFile += ` # save current build job id to kind of tie together multiple test jobs # because GitLab CI api does not expose the pipeline ID # pass the saved id through the file build-specs: stage: build script: ` buildScript.forEach((buildCommand) => { gitlabFile += ` - ${buildCommand} ` }) gitlabFile += ` artifacts: paths: - build.id - cypress/support - cypress/fixtures - ${outputFolder} # Common build job definition using GitLab YAML features # http://docs.gitlab.com/ce/ci/yaml/README.html#special-yaml-features # we will generate the test definitions per spec bundle # using the common definition ` gitlabFile += ` # Hidden job that defines an anchor named 'e2e_test_definition' # This job will be automatically assigned "test" phase .job_template: &e2e_test_definition artifacts: when: on_failure expire_in: 1 week paths: - cypress/screenshots/ - cypress/videos/ script: ` script.forEach((testCommand) => { gitlabFile += ` - ${testCommand} ` }) if (is.not.empty(beforeScript)) { gitlabFile += ' before_script:\n' beforeScript.forEach((testCommand) => { gitlabFile += ` - ${testCommand} ` }) } if (is.not.empty(afterScript)) { gitlabFile += ' after_script:\n' afterScript.forEach((testCommand) => { gitlabFile += ` - ${testCommand} ` }) } names.forEach((name) => { gitlabFile += ` ${name}: <<: *e2e_test_definition ` }) gitlabFile += '\n' const gitlabFilename = inCurrent('.gitlab-ci.yml') fs.writeFileSync(gitlabFilename, gitlabFile, 'utf8') console.log('saved', gitlabFilename) }