18f-pages-server
Advanced tools
Comparing version 0.2.1 to 0.3.0
@@ -7,5 +7,6 @@ 'use strict'; | ||
function CommandRunner(sitePath, repoName) { | ||
function CommandRunner(sitePath, repoName, logger) { | ||
this.sitePath = sitePath; | ||
this.repoName = repoName; | ||
this.logger = logger; | ||
} | ||
@@ -19,6 +20,19 @@ | ||
return new Promise(function(resolve, reject) { | ||
var options = opts || {cwd: runner.sitePath, stdio: 'inherit'}, | ||
var command, | ||
options = opts || {cwd: runner.sitePath}, | ||
msg = message || 'rebuild failed for'; | ||
childProcess.spawn(path, args, options).on('close', function(code) { | ||
command = childProcess.spawn(path, args, options); | ||
command.stdout.setEncoding('utf8'); | ||
command.stdout.on('data', function(data) { | ||
runner.logger.log(data); | ||
}); | ||
command.stderr.setEncoding('utf8'); | ||
command.stderr.on('data', function(data) { | ||
runner.logger.error(data); | ||
}); | ||
command.on('close', function(code) { | ||
if (code !== 0) { | ||
@@ -25,0 +39,0 @@ reject('Error: ' + msg + ' ' + runner.repoName + ' with exit code ' + |
@@ -8,2 +8,3 @@ 'use strict'; | ||
var JekyllCommandHelper = require('./jekyll-command-helper'); | ||
var Sync = require('./sync'); | ||
var FileLockedOperation = require('file-locked-operation'); | ||
@@ -16,3 +17,3 @@ var path = require('path'); | ||
this.commandRunner = new CommandRunner( | ||
builderOpts.sitePath, builderOpts.repoName); | ||
builderOpts.sitePath, builderOpts.repoName, logger); | ||
this.configHandler = new ConfigHandler( | ||
@@ -24,2 +25,3 @@ builderOpts, branch, | ||
config, builderOpts, this.commandRunner, logger); | ||
this.sync = new Sync(config, this.commandRunner, logger); | ||
this.updateLock = new FileLockedOperation( | ||
@@ -26,0 +28,0 @@ path.join(builderOpts.destDir, '.update-lock-' + builderOpts.repoName), |
@@ -70,3 +70,3 @@ 'use strict'; | ||
if (handler.baseurl !== undefined) { | ||
if (handler.baseurl) { | ||
setBuildDestinationFromBaseurl(handler, handler.baseurl); | ||
@@ -77,4 +77,10 @@ } | ||
function setBuildDestinationFromBaseurl(handler, baseurl) { | ||
var destDirPrefix = path.join(handler.destDir, path.sep); | ||
handler.buildDestination = path.join(handler.destDir, baseurl); | ||
if (handler.buildDestination.substr(0, destDirPrefix.length) !== | ||
destDirPrefix) { | ||
throw new Error('baseurl contains relative components: ' + baseurl); | ||
} | ||
if (handler.internalBuildDestination) { | ||
@@ -135,3 +141,6 @@ handler.internalBuildDestination = path.join( | ||
baseurl = handler.baseurl || ('/' + handler.repoName); | ||
baseurl = handler.baseurl; | ||
if (baseurl === undefined) { | ||
baseurl = '/' + handler.repoName; | ||
} | ||
assetRoot = handler.assetRoot; | ||
@@ -138,0 +147,0 @@ |
@@ -46,3 +46,5 @@ 'use strict'; | ||
if (builder.configHandler.usesBundler) { | ||
return builder.commandRunner.run(config.bundler, ['install']); | ||
return builder.commandRunner.run( | ||
config.bundler, ['install', | ||
'--path=' + path.join(config.home, config.bundlerCacheDir)]); | ||
} | ||
@@ -53,2 +55,5 @@ }) | ||
buildJekyll(builder) : rsync(builder); | ||
}) | ||
.then(function() { | ||
return syncResults(builder); | ||
}); | ||
@@ -61,6 +66,16 @@ }; | ||
function rsync(builder) { | ||
return builder.commandRunner.run(config.rsync, | ||
config.rsyncOpts.concat(['./', builder.configHandler.buildDestination])); | ||
return builder.configHandler.buildConfigurations().reduce( | ||
function(previousRsync, buildConfig) { | ||
return generateRsyncOp(builder, previousRsync, buildConfig); | ||
}, | ||
Promise.resolve()); | ||
} | ||
function generateRsyncOp(builder, previousRsync, buildConfig) { | ||
return previousRsync.then(function() { | ||
return builder.commandRunner.run(config.rsync, | ||
config.rsyncOpts.concat(['./', buildConfig.destination])); | ||
}); | ||
} | ||
function buildJekyll(builder) { | ||
@@ -80,3 +95,17 @@ var cleanup = function(err) { | ||
SiteBuilder.launchBuilder = function(info, branch, builderConfig, done) { | ||
function syncResults(builder) { | ||
return builder.configHandler.buildConfigurations().reduce( | ||
function(previousSync, buildConfig) { | ||
return generateSyncOp(builder, previousSync, buildConfig); | ||
}, | ||
Promise.resolve()); | ||
} | ||
function generateSyncOp(builder, previousSync, buildConfig) { | ||
return previousSync.then(function() { | ||
return builder.sync.sync(buildConfig.destination); | ||
}); | ||
} | ||
SiteBuilder.launchBuilder = function(info, branch, builderConfig) { | ||
var builderOpts = new Options(info, config, builderConfig), | ||
@@ -100,22 +129,28 @@ commit = info.head_commit, | ||
finishBuild = function(err) { | ||
if (err !== undefined) { | ||
logger.error(err); | ||
logger.error(builderOpts.repoName + ': build failed'); | ||
} else { | ||
logger.log(builderOpts.repoName + ': build successful'); | ||
} | ||
return new Promise(function(resolve, reject) { | ||
// Provides https://pages.18f.gov/REPO-NAME/build.log as an indicator of | ||
// latest status. | ||
var newLogPath = path.join( | ||
builder.configHandler.buildDestination, 'build.log'); | ||
// Provides https://pages.18f.gov/REPO-NAME/build.log as an indicator of | ||
// latest status. | ||
var newLogPath = path.join( | ||
builder.configHandler.buildDestination, 'build.log'); | ||
fs.rename(buildLog, newLogPath, function(err) { | ||
if (err !== null) { | ||
console.error('Error moving build log from', buildLog, 'to', | ||
newLogPath); | ||
if (err) { | ||
logger.error(err.message ? err.message : err); | ||
logger.error(builderOpts.repoName + ': build failed'); | ||
} else { | ||
logger.log(builderOpts.repoName + ': build successful'); | ||
} | ||
logger.close(function() { | ||
if (done) { | ||
done(err); | ||
fs.rename(buildLog, newLogPath, function(renameErr) { | ||
var errMsg; | ||
if (renameErr) { | ||
errMsg = 'Error moving build log: ' + renameErr; | ||
console.error(errMsg); | ||
if (!err) { | ||
reject(errMsg); | ||
} | ||
} | ||
logger.close(function() { | ||
return err ? reject(err) : resolve(); | ||
}); | ||
}); | ||
@@ -128,3 +163,3 @@ }); | ||
SiteBuilder.makeBuilderListener = function(webhook, builderConfig, done) { | ||
SiteBuilder.makeBuilderListener = function(webhook, builderConfig) { | ||
var org = builderConfig.githubOrg || config.githubOrg, | ||
@@ -141,3 +176,3 @@ branchPattern = builderConfig.branchInUrlPattern || builderConfig.branch, | ||
if (branch && (info.repository.organization === org)) { | ||
SiteBuilder.launchBuilder(info, branch[1], builderConfig, done); | ||
return SiteBuilder.launchBuilder(info, branch[1], builderConfig); | ||
} | ||
@@ -147,2 +182,3 @@ }; | ||
webhook.on('push', handler); | ||
return handler; | ||
}; |
{ | ||
"name": "18f-pages-server", | ||
"version": "0.2.1", | ||
"version": "0.3.0", | ||
"description": "Static website publishing server for 18F Pages", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
{ | ||
"port": 5000, | ||
"home": "/home/ubuntu", | ||
"home": "/usr/local/18f/pages", | ||
"git": "/usr/bin/git", | ||
"bundler": "/usr/local/rbenv/shims/bundle", | ||
"jekyll": "/usr/local/rbenv/shims/jekyll", | ||
"bundler": "/usr/local/18f/rbenv/shims/bundle", | ||
"bundlerCacheDir": "bundler-cache", | ||
"jekyll": "/usr/local/18f/rbenv/shims/jekyll", | ||
"rsync": "/usr/bin/rsync", | ||
"rsyncOpts": [ | ||
"-vaxp", "--delete", "--ignore-errors", "--exclude=.[A-Za-z0-9]*" | ||
"-vaxp", | ||
"--delete", | ||
"--ignore-errors", | ||
"--exclude=.[A-Za-z0-9]*" | ||
], | ||
"s3": { | ||
"awscli": "/usr/local/18f/pyenv/shims/aws", | ||
"bucket": "s3://18f-pages" | ||
}, | ||
"payloadLimit": 1048576, | ||
@@ -18,27 +26,27 @@ "githubOrg": "18F", | ||
"fileLockPollTime": 1000, | ||
"secretKeyFile": "/home/ubuntu/.18f-pages.secret", | ||
"secretKeyFile": "/usr/local/18f/pages/config/pages.secret", | ||
"builders": [ | ||
{ | ||
"branch": "18f-pages", | ||
"repositoryDir": "pages-repos", | ||
"generatedSiteDir": "pages-generated", | ||
"internalSiteDir": "pages-internal" | ||
"repositoryDir": "repos/pages.18f.gov", | ||
"generatedSiteDir": "sites/pages.18f.gov", | ||
"internalSiteDir": "sites/pages-internal.18f.gov" | ||
}, | ||
{ | ||
"branch": "18f-pages-staging", | ||
"repositoryDir": "pages-repos-staging", | ||
"generatedSiteDir": "pages-staging", | ||
"internalSiteDir": "pages-internal" | ||
"repositoryDir": "repos/pages-staging.18f.gov", | ||
"generatedSiteDir": "sites/pages-staging.18f.gov", | ||
"internalSiteDir": "sites/pages-internal.18f.gov" | ||
}, | ||
{ | ||
"branch": "18f-pages-internal", | ||
"repositoryDir": "pages-repos-internal", | ||
"generatedSiteDir": "pages-internal" | ||
"repositoryDir": "repos/pages-internal.18f.gov", | ||
"generatedSiteDir": "sites/pages-internal.18f.gov" | ||
}, | ||
{ | ||
"branchInUrlPattern": "v[0-9]+.[0-9]+.[0-9]*[a-z]+", | ||
"repositoryDir": "pages-repos-releases", | ||
"generatedSiteDir": "pages-releases" | ||
"branchInUrlPattern": "v[0-9]+.[0-9]+.[0-9]*[a-z]*", | ||
"repositoryDir": "repos/pages-releases.18f.gov", | ||
"generatedSiteDir": "sites/pages-releases.18f.gov" | ||
} | ||
] | ||
} |
@@ -228,2 +228,7 @@ # 18f-pages-server | ||
OS X installations in particular may need to adjust these | ||
* **s3 (optional)**: if present, will back up each generated site to | ||
[Amazon S3](https://aws.amazon.com/s3/); attributes are: | ||
* **awscli**: path to the [`aws` command](https://aws.amazon.com/cli/) on | ||
the host machine | ||
* **bucket**: address of the S3 bucket to which to sync generated sites | ||
* **payloadLimit**: maximum allowable size (in bytes) for incoming webhooks | ||
@@ -230,0 +235,0 @@ * **githubOrg**: GitHub organization to which all published repositories |
'use strict'; | ||
var BuildLogger = require('../lib/build-logger.js'); | ||
var BuildLogger = require('../lib/build-logger'); | ||
var path = require('path'); | ||
@@ -5,0 +5,0 @@ var fs = require('fs'); |
@@ -6,2 +6,3 @@ 'use strict'; | ||
var BuildLogger = require('../lib/build-logger'); | ||
var Options = require('../lib/options'); | ||
var pagesConfig = require('../pages-config.json'); | ||
@@ -21,14 +22,21 @@ var path = require('path'); | ||
before(function() { | ||
var payload, builderConfig; | ||
payload = { | ||
repository: { | ||
name: 'repo_name' | ||
}, | ||
ref: 'refs/heads/18f-pages' | ||
}; | ||
config = JSON.parse(JSON.stringify(pagesConfig)); | ||
opts = { | ||
pagesConfig: config.pagesConfig, | ||
pagesYaml: config.pagesYaml, | ||
repoName: 'repo_name', | ||
destDir: 'dest_dir', | ||
internalDestDir: 'internal_dest_dir', | ||
assetRoot: '/guides-template', | ||
branchInUrlPattern: '' | ||
config.home = '/usr/local/18f/pages'; | ||
config.assetRoot = '/guides-template'; | ||
builderConfig = { | ||
'branch': '18f-pages', | ||
'repositoryDir': 'repo_dir', | ||
'generatedSiteDir': 'dest_dir', | ||
'internalSiteDir': 'internal_dest_dir' | ||
}; | ||
opts.sitePath = path.join('some/test/dir', opts.repoName); | ||
fileHandler = new RepositoryFileHandler(opts); | ||
opts = new Options(payload, config, builderConfig); | ||
fileHandler = new RepositoryFileHandler(opts.sitePath); | ||
logger = new BuildLogger(); | ||
@@ -45,2 +53,3 @@ }); | ||
sinon.stub(fileHandler, 'readFile'); | ||
sinon.stub(fileHandler, 'writeFile'); | ||
fileHandler.exists.returns(Promise.resolve(false)); | ||
@@ -54,2 +63,3 @@ fileHandler.exists.withArgs('_config.yml') | ||
logger.log.restore(); | ||
fileHandler.writeFile.restore(); | ||
fileHandler.readFile.restore(); | ||
@@ -75,3 +85,3 @@ fileHandler.exists.restore(); | ||
handler.buildConfigurations().should.eql([ | ||
{ destination: path.join('dest_dir/repo_name'), | ||
{ destination: path.join(config.home, 'dest_dir/repo_name'), | ||
configurations: '_config.yml,' + config.pagesConfig | ||
@@ -111,7 +121,8 @@ } | ||
handler.buildConfigurations().should.eql([ | ||
{ destination: path.join('internal_dest_dir/repo_name'), | ||
{ destination: path.join( | ||
config.home, 'internal_dest_dir/repo_name'), | ||
configurations: '_config.yml,_config_internal.yml,' + | ||
config.pagesConfig | ||
}, | ||
{ destination: path.join('dest_dir/repo_name'), | ||
{ destination: path.join(config.home, 'dest_dir/repo_name'), | ||
configurations: '_config.yml,' + config.pagesConfig | ||
@@ -131,7 +142,8 @@ } | ||
handler.buildConfigurations().should.eql([ | ||
{ destination: path.join('internal_dest_dir/repo_name'), | ||
{ destination: path.join( | ||
config.home, 'internal_dest_dir/repo_name'), | ||
configurations: '_config.yml,_config_internal.yml,' + | ||
config.pagesConfig | ||
}, | ||
{ destination: path.join('dest_dir/repo_name'), | ||
{ destination: path.join(config.home, 'dest_dir/repo_name'), | ||
configurations: '_config.yml,_config_external.yml,' + | ||
@@ -156,7 +168,8 @@ config.pagesConfig | ||
handler.buildConfigurations().should.eql([ | ||
{ destination: path.join('internal_dest_dir/repo_name/v0.9.0'), | ||
{ destination: path.join(config.home, | ||
'internal_dest_dir/repo_name/v0.9.0'), | ||
configurations: '_config.yml,_config_internal.yml,' + | ||
config.pagesConfig | ||
}, | ||
{ destination: path.join('dest_dir/repo_name/v0.9.0'), | ||
{ destination: path.join(config.home, 'dest_dir/repo_name/v0.9.0'), | ||
configurations: '_config.yml,' + config.pagesConfig | ||
@@ -200,2 +213,5 @@ } | ||
.returns(Promise.resolve('baseurl: /new-baseurl\n')); | ||
fileHandler.exists.withArgs(pagesConfig.pagesConfig) | ||
.returns(Promise.resolve(false)); | ||
fileHandler.writeFile.returns(Promise.resolve()); | ||
@@ -215,6 +231,16 @@ handler.branchInUrlPattern = new RegExp( | ||
handler.buildConfigurations().should.eql([ | ||
{ destination: path.join('dest_dir/new-baseurl/v0.9.0'), | ||
{ destination: path.join( | ||
config.home, 'dest_dir/new-baseurl/v0.9.0'), | ||
configurations: '_config.yml,' + config.pagesConfig | ||
} | ||
]); | ||
return handler.readOrWriteConfig(); | ||
}) | ||
.should.be.fulfilled.then(function() { | ||
fileHandler.writeFile.args.should.eql([ | ||
[config.pagesConfig, | ||
'baseurl: /new-baseurl/v0.9.0\n' + | ||
'asset_root: /new-baseurl/v0.9.0\n' | ||
] | ||
]); | ||
}); | ||
@@ -228,2 +254,5 @@ }); | ||
.returns(Promise.resolve('baseurl:\n')); | ||
fileHandler.exists.withArgs(pagesConfig.pagesConfig) | ||
.returns(Promise.resolve(false)); | ||
fileHandler.writeFile.returns(Promise.resolve()); | ||
@@ -234,10 +263,20 @@ return handler.init().should.be.fulfilled | ||
handler.baseurl.should.eql(''); | ||
handler.buildDestination.should.eql(path.join(handler.destDir)); | ||
handler.buildDestination.should.eql( | ||
path.join(handler.destDir, handler.repoName)); | ||
handler.internalBuildDestination.should.eql( | ||
path.join(handler.internalDestDir)); | ||
path.join(handler.internalDestDir, handler.repoName)); | ||
handler.buildConfigurations().should.eql([ | ||
{ destination: path.join('dest_dir'), | ||
{ destination: path.join(config.home, 'dest_dir', handler.repoName), | ||
configurations: '_config.yml,' + config.pagesConfig | ||
} | ||
]); | ||
return handler.readOrWriteConfig(); | ||
}) | ||
.should.be.fulfilled.then(function() { | ||
fileHandler.writeFile.args.should.eql([ | ||
[config.pagesConfig, | ||
'baseurl: \n' + | ||
'asset_root: ' + config.assetRoot + '\n' | ||
] | ||
]); | ||
}); | ||
@@ -256,4 +295,14 @@ }); | ||
it('should reject baseurl with relative components', function() { | ||
fileHandler.exists.withArgs(pagesConfig.pagesYaml) | ||
.returns(Promise.resolve(true)); | ||
fileHandler.readFile.withArgs(pagesConfig.pagesYaml) | ||
.returns(Promise.resolve('baseurl: ../dest_dir_not')); | ||
return handler.init().should.be.rejectedWith( | ||
'Error: baseurl contains relative components: ../dest_dir_not'); | ||
}); | ||
it('should not detect YAML file presence if not configured', function() { | ||
// This can happen if the pages-config.json file does not have | ||
// This can happen if the pages-config.json file does not have | ||
// a pagesYaml property defined. | ||
@@ -277,3 +326,4 @@ fileHandler.exists.withArgs(undefined).throws(); | ||
handler.parseDestinationFromConfigData(''); | ||
handler.buildDestination.should.equal(path.join('dest_dir/repo_name')); | ||
handler.buildDestination.should.equal( | ||
path.join(config.home, 'dest_dir/repo_name')); | ||
}); | ||
@@ -283,3 +333,4 @@ | ||
handler.parseDestinationFromConfigData('baseurl:\n'); | ||
handler.buildDestination.should.equal(path.join('dest_dir/repo_name')); | ||
handler.buildDestination.should.equal( | ||
path.join(config.home, 'dest_dir/repo_name')); | ||
}); | ||
@@ -289,3 +340,4 @@ | ||
handler.parseDestinationFromConfigData('baseurl: \n'); | ||
handler.buildDestination.should.equal(path.join('dest_dir/repo_name')); | ||
handler.buildDestination.should.equal( | ||
path.join(config.home, 'dest_dir/repo_name')); | ||
}); | ||
@@ -295,3 +347,4 @@ | ||
handler.parseDestinationFromConfigData('baseurl: /\n'); | ||
handler.buildDestination.should.equal(path.join('dest_dir/repo_name')); | ||
handler.buildDestination.should.equal( | ||
path.join(config.home, 'dest_dir/repo_name')); | ||
}); | ||
@@ -302,12 +355,12 @@ | ||
handler.buildDestination.should.equal( | ||
path.join('dest_dir/new-destination')); | ||
path.join(config.home, 'dest_dir/new-destination')); | ||
}); | ||
it('should set the internal destination from config data', function() { | ||
handler.internalDestDir = 'internal_dest_dir'; | ||
handler.internalDestDir = path.join(config.home, 'internal_dest_dir'); | ||
handler.parseDestinationFromConfigData('baseurl: /new-destination\n'); | ||
handler.buildDestination.should.equal( | ||
path.join('dest_dir/new-destination')); | ||
path.join(config.home, 'dest_dir/new-destination')); | ||
handler.internalBuildDestination.should.equal( | ||
path.join('internal_dest_dir/new-destination')); | ||
path.join(config.home, 'internal_dest_dir/new-destination')); | ||
}); | ||
@@ -318,3 +371,3 @@ | ||
handler.buildDestination.should.equal( | ||
path.join('dest_dir/new-destination')); | ||
path.join(config.home, 'dest_dir/new-destination')); | ||
}); | ||
@@ -326,4 +379,12 @@ | ||
handler.buildDestination.should.equal( | ||
path.join('dest_dir/new-destination')); | ||
path.join(config.home, 'dest_dir/new-destination')); | ||
}); | ||
it('should fail if the baseurl contains relative components', function() { | ||
var parse = function() { | ||
handler.parseDestinationFromConfigData('baseurl: ../dest_dir_not'); | ||
}; | ||
expect(parse).to.throw( | ||
Error, 'baseurl contains relative components: ../dest_dir_not'); | ||
}); | ||
}); | ||
@@ -330,0 +391,0 @@ |
@@ -13,21 +13,15 @@ 'use strict'; | ||
FilesHelper.prototype.init = function(config) { | ||
FilesHelper.prototype.init = function() { | ||
var helper = this; | ||
this.dirs = []; | ||
this.files = []; | ||
return new Promise(function(resolve, reject) { | ||
temp.mkdir(scriptName + '-test-files-', function(err, tempDir) { | ||
var testRepoDir = path.resolve(tempDir, 'site_builder_test'), | ||
lockDir = path.resolve(tempDir, 'site_builder_test_lock_dir'); | ||
if (err) { | ||
return reject(err); | ||
} | ||
fs.mkdir(lockDir, '0700', function(err) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
initHelper(helper, config, tempDir, testRepoDir, lockDir); | ||
resolve(); | ||
}); | ||
helper.tempDir = tempDir; | ||
resolve(); | ||
}); | ||
@@ -37,33 +31,8 @@ }); | ||
function initHelper(helper, config, tempDir, testRepoDir, lockDir) { | ||
helper.dirs = { | ||
testRepoDir: testRepoDir, | ||
lockDir: lockDir, | ||
tempDir: tempDir | ||
}; | ||
helper.files = { | ||
gemfile: path.resolve(testRepoDir, 'Gemfile'), | ||
pagesConfig: path.resolve(testRepoDir, config.pagesConfig), | ||
configYml: path.resolve(testRepoDir, '_config.yml'), | ||
internalConfig: path.resolve(testRepoDir, '_config_internal.yml'), | ||
externalConfig: path.resolve(testRepoDir, '_config_external.yml'), | ||
lockfilePath: path.resolve(lockDir, '.update-lock-repo_name') | ||
}; | ||
helper.filesToDelete = []; | ||
} | ||
FilesHelper.prototype.afterEach = function() { | ||
var helper = this, | ||
files = helper.filesToDelete.slice(); | ||
var helper = this; | ||
helper.filesToDelete = []; | ||
files = files.concat(Object.keys(helper.files).map(function(key) { | ||
return helper.files[key]; | ||
})); | ||
return removeItems(files, 'unlink') | ||
return removeItems(this.tempDir, this.files, 'unlink') | ||
.then(function() { | ||
return helper.removeDir(helper.dirs.testRepoDir); | ||
return removeItems(helper.tempDir, helper.dirs.reverse(), 'rmdir'); | ||
}); | ||
@@ -73,19 +42,15 @@ }; | ||
FilesHelper.prototype.after = function() { | ||
return removeItems([this.dirs.lockDir, this.dirs.tempDir], 'rmdir'); | ||
return removeItems(this.tempDir, [''], 'rmdir'); | ||
}; | ||
FilesHelper.prototype.createRepoDir = function() { | ||
FilesHelper.prototype.createDir = function(dirName) { | ||
var helper = this; | ||
return new Promise(function(resolve, reject) { | ||
fs.mkdir(helper.dirs.testRepoDir, '0700', function(err) { | ||
fs.mkdir(path.join(helper.tempDir, dirName), '0700', function(err) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
fs.writeFile(helper.files.configYml, '', function(err) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
resolve(); | ||
}); | ||
helper.dirs.push(dirName); | ||
resolve(); | ||
}); | ||
@@ -95,33 +60,3 @@ }); | ||
FilesHelper.prototype.createRepoWithFiles = function(nameToContents) { | ||
var helper = this, | ||
writeFile; | ||
this.filesToDelete = Object.keys(nameToContents); | ||
writeFile = function(filename) { | ||
return new Promise(function(resolve, reject) { | ||
fs.writeFile(filename, nameToContents[filename], function(err) { | ||
if (err) { | ||
return reject(err); | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
}; | ||
return this.createRepoDir() | ||
.then(function() { | ||
return Promise.all(helper.filesToDelete.map(writeFile)); | ||
}); | ||
}; | ||
FilesHelper.prototype.removeFile = function(filename) { | ||
return removeItem(filename, 'unlink'); | ||
}; | ||
FilesHelper.prototype.removeDir = function(dirname) { | ||
return removeItem(dirname, 'rmdir'); | ||
}; | ||
function removeItems(items, operation) { | ||
function removeItems(parentDir, items, operation) { | ||
var remover; | ||
@@ -131,3 +66,3 @@ | ||
return result.then(function() { | ||
return removeItem(item, operation); | ||
return removeItem(parentDir, item, operation); | ||
}); | ||
@@ -138,9 +73,11 @@ }; | ||
function removeItem(name, operation) { | ||
function removeItem(parentDir, name, operation) { | ||
var itemPath = path.join(parentDir, name); | ||
return new Promise(function(resolve, reject) { | ||
fs.exists(name, function(exists) { | ||
fs.exists(itemPath, function(exists) { | ||
if (!exists) { | ||
return resolve(); | ||
} | ||
fs[operation](name, function(err) { | ||
fs[operation](itemPath, function(err) { | ||
if (err) { | ||
@@ -147,0 +84,0 @@ return reject(err); |
@@ -34,8 +34,8 @@ 'use strict'; | ||
var opts = new Options(info, config, builderConfig); | ||
expect(opts.repoDir).to.equal(path.join('/home/ubuntu/repo_dir')); | ||
expect(opts.repoDir).to.equal(path.join(config.home, 'repo_dir')); | ||
expect(opts.repoName).to.equal('repo_name'); | ||
expect(opts.sitePath).to.equal( | ||
path.join('/home/ubuntu/repo_dir/repo_name')); | ||
path.join(config.home, 'repo_dir/repo_name')); | ||
expect(opts.branch).to.equal('18f-pages'); | ||
expect(opts.destDir).to.equal(path.join('/home/ubuntu/dest_dir')); | ||
expect(opts.destDir).to.equal(path.join(config.home, 'dest_dir')); | ||
expect(opts.internalDestDir).to.be.undefined; | ||
@@ -67,8 +67,8 @@ expect(opts.githubOrg).to.equal('18F'); | ||
var opts = new Options(info, config, builderConfig); | ||
expect(opts.repoDir).to.equal(path.join('/home/ubuntu/repo_dir')); | ||
expect(opts.repoDir).to.equal(path.join(config.home, 'repo_dir')); | ||
expect(opts.repoName).to.equal('repo_name'); | ||
expect(opts.sitePath).to.equal( | ||
path.join('/home/ubuntu/repo_dir/repo_name')); | ||
path.join(config.home, 'repo_dir/repo_name')); | ||
expect(opts.branch).to.equal('foobar-pages'); | ||
expect(opts.destDir).to.equal(path.join('/home/ubuntu/dest_dir')); | ||
expect(opts.destDir).to.equal(path.join(config.home, 'dest_dir')); | ||
expect(opts.internalDestDir).to.be.undefined; | ||
@@ -99,6 +99,6 @@ expect(opts.githubOrg).to.equal('foobar'); | ||
var opts = new Options(info, config, builderConfig); | ||
expect(opts.destDir).to.equal(path.join('/home/ubuntu/dest_dir')); | ||
expect(opts.destDir).to.equal(path.join(config.home, 'dest_dir')); | ||
expect(opts.internalDestDir).to.equal( | ||
path.join('/home/ubuntu/internal_dest_dir')); | ||
path.join(config.home, 'internal_dest_dir')); | ||
}); | ||
}); |
@@ -7,3 +7,2 @@ 'use strict'; | ||
var ComponentFactory = require('../lib/component-factory'); | ||
var FileLockedOperation = require('file-locked-operation'); | ||
var fs = require('fs'); | ||
@@ -25,9 +24,9 @@ var path = require('path'); | ||
describe('SiteBuilder', function() { | ||
var config, origSpawn, mySpawn, logger, filesHelper, expectLogMessages; | ||
var config, origSpawn, mySpawn, logger, expectLogMessages; | ||
function cloneConfig() { | ||
config = JSON.parse(JSON.stringify(OrigConfig)); | ||
config.home = ''; | ||
config.git = 'git'; | ||
config.bundler = 'bundle'; | ||
config.bundlerCacheDir = 'bundler_cache_dir'; | ||
config.jekyll = 'jekyll'; | ||
@@ -40,10 +39,4 @@ config.rsync = 'rsync'; | ||
SiteBuilder.setConfiguration(config); | ||
filesHelper = new FilesHelper(); | ||
return filesHelper.init(config); | ||
}); | ||
after(function() { | ||
return filesHelper.after(); | ||
}); | ||
beforeEach(function() { | ||
@@ -58,9 +51,4 @@ origSpawn = childProcess.spawn; | ||
childProcess.spawn = origSpawn; | ||
return filesHelper.afterEach(); | ||
}); | ||
var check = function(done, cb) { | ||
return function(err) { try { cb(err); done(); } catch (e) { done(e); } }; | ||
}; | ||
var makeOpts = function() { | ||
@@ -87,6 +75,3 @@ var info = { | ||
opts.sitePath = filesHelper.dirs.testRepoDir; | ||
components = new ComponentFactory(config, opts, targetBranch, logger); | ||
components.updateLock = new FileLockedOperation( | ||
filesHelper.files.lockfilePath); | ||
return new SiteBuilder(targetBranch, components); | ||
@@ -108,3 +93,3 @@ }; | ||
buildConfigs = [{ | ||
destination: 'dest_dir', | ||
destination: path.join(config.home, 'dest_dir/repo_name'), | ||
configurations: '_config.yml,' + config.pagesConfig | ||
@@ -127,2 +112,3 @@ }]; | ||
.returns(Promise.resolve()); | ||
sinon.stub(builder.sync, 'sync').returns(Promise.resolve()); | ||
sinon.stub(builder.updateLock, 'doLockedOperation') | ||
@@ -152,2 +138,5 @@ .returns(Promise.resolve()); | ||
]); | ||
builder.sync.sync.args.should.eql([ | ||
[path.join(config.home, 'dest_dir/repo_name')] | ||
]); | ||
builder.configHandler.removeGeneratedConfig.called.should.be.true; | ||
@@ -166,3 +155,5 @@ }); | ||
builder.commandRunner.run.args.should.eql([ | ||
[config.bundler, ['install']] | ||
[config.bundler, | ||
['install', | ||
'--path=' + path.join(config.home, config.bundlerCacheDir)]] | ||
]); | ||
@@ -173,2 +164,5 @@ builder.configHandler.readOrWriteConfig.called.should.be.true; | ||
]); | ||
builder.sync.sync.args.should.eql([ | ||
[path.join(config.home, 'dest_dir/repo_name')] | ||
]); | ||
builder.configHandler.removeGeneratedConfig.called.should.be.true; | ||
@@ -179,5 +173,6 @@ }); | ||
it('should perform a successful rsync build', function() { | ||
var buildDestination = path.join(config.home, 'dest_dir/repo_name'); | ||
builder.configHandler.usesJekyll = false; | ||
builder.configHandler.usesBundler = false; | ||
builder.configHandler.buildDestination = 'dest_dir'; | ||
@@ -189,4 +184,5 @@ return runBuild().should.be.fulfilled | ||
builder.commandRunner.run.args.should.eql([ | ||
[config.rsync, config.rsyncOpts.concat(['./', 'dest_dir'])] | ||
[config.rsync, config.rsyncOpts.concat(['./', buildDestination])] | ||
]); | ||
builder.sync.sync.args.should.eql([[buildDestination]]); | ||
builder.configHandler.readOrWriteConfig.called.should.be.false; | ||
@@ -208,2 +204,3 @@ builder.jekyllHelper.build.called.should.be.false; | ||
builder.jekyllHelper.build.called.should.be.false; | ||
builder.sync.sync.called.should.be.false; | ||
builder.configHandler.removeGeneratedConfig.called.should.be.false; | ||
@@ -215,3 +212,4 @@ }); | ||
describe('makeBuilderListener and launchBuilder', function() { | ||
var webhook, incomingPayload, builderConfig, cloneDir, outputDir, buildLog; | ||
var webhook, incomingPayload, builderConfig, cloneDir, outputDir, buildLog, | ||
filesHelper; | ||
@@ -238,36 +236,38 @@ before(function() { | ||
'branch': '18f-pages', | ||
'repositoryDir': path.join(filesHelper.dirs.testRepoDir, 'repo_dir'), | ||
'generatedSiteDir': path.join(filesHelper.dirs.testRepoDir, 'dest_dir') | ||
'repositoryDir': 'repo_dir', | ||
'generatedSiteDir': 'dest_dir' | ||
}; | ||
cloneDir = path.join(filesHelper.dirs.testRepoDir, 'repo_dir/foo'); | ||
outputDir = path.join(filesHelper.dirs.testRepoDir, 'dest_dir/foo'); | ||
cloneDir = path.join('repo_dir/foo'); | ||
outputDir = path.join('dest_dir/foo'); | ||
buildLog = path.join(outputDir, 'build.log'); | ||
filesHelper = new FilesHelper(); | ||
return filesHelper.init(config) | ||
.then(function() { | ||
config.home = filesHelper.tempDir; | ||
}); | ||
}); | ||
beforeEach(function(done) { | ||
after(function() { | ||
return filesHelper.after(); | ||
}); | ||
beforeEach(function() { | ||
webhook = { on: sinon.spy() }; | ||
fs.mkdir(filesHelper.dirs.testRepoDir, function(err) { | ||
if (err) { return done(err); } | ||
fs.mkdir(builderConfig.repositoryDir, function(err) { | ||
if (err) { return done(err); } | ||
fs.mkdir(builderConfig.generatedSiteDir, function(err) { | ||
if (err) { return done(err); } | ||
fs.mkdir(outputDir, done); | ||
}); | ||
filesHelper.files.push(buildLog); | ||
// Note that the site builder will not create the parent directory for | ||
// the generated sites. That should be done before launching the server. | ||
return filesHelper.createDir('repo_dir') | ||
.then(function() { | ||
return filesHelper.createDir('dest_dir'); | ||
}) | ||
.then(function() { | ||
return filesHelper.createDir(path.join('dest_dir', 'foo')); | ||
}); | ||
}); | ||
}); | ||
// The outer afterEach() will remove the testRepoDir. | ||
afterEach(function(done) { | ||
fs.unlink(buildLog, function() { | ||
fs.rmdir(outputDir, function(err) { | ||
if (err) { return done(err); } | ||
fs.rmdir(builderConfig.generatedSiteDir, function(err) { | ||
if (err) { return done(err); } | ||
fs.rmdir(builderConfig.repositoryDir, done); | ||
}); | ||
}); | ||
}); | ||
afterEach(function() { | ||
return filesHelper.afterEach(); | ||
}); | ||
@@ -280,96 +280,92 @@ | ||
var restoreLogs = function() { | ||
console.error.restore(); | ||
console.log.restore(); | ||
var restoreLogs = function(err) { | ||
return new Promise(function(resolve, reject) { | ||
console.error.restore(); | ||
console.log.restore(); | ||
err ? reject(err) : resolve(); | ||
}); | ||
}; | ||
it('should create a function to launch a builder', function() { | ||
SiteBuilder.makeBuilderListener(webhook, builderConfig); | ||
var handler = SiteBuilder.makeBuilderListener(webhook, builderConfig); | ||
expect(handler).to.be.a.Function; | ||
expect(webhook.on.calledTwice).to.be.true; | ||
expect(webhook.on.args[0].length).to.equal(2); | ||
expect(webhook.on.args[0][0]).to.equal('create'); | ||
expect(webhook.on.args[0][1]).to.be.handler; | ||
expect(webhook.on.args[1].length).to.equal(2); | ||
expect(webhook.on.args[1][0]).to.equal('push'); | ||
expect(webhook.on.args[1].length).to.equal(2); | ||
expect(webhook.on.args[0][1]).to.be.a.Function; | ||
expect(webhook.on.args[1][1]).to.be.a.Function; | ||
expect(webhook.on.args[0][1]).to.equal(webhook.on.args[1][1]); | ||
expect(webhook.on.args[1][1]).to.be.handler; | ||
}); | ||
it('should create a builder that builds the site', function(done) { | ||
var checkResult = check(done, function(err) { | ||
var logMsgs = console.log.args, | ||
errorMsgs = console.error.args, | ||
expectedMessages = [ | ||
'18F/foo: starting build at commit deadbeef', | ||
'description: Build me', | ||
'timestamp: 2015-09-25', | ||
'committer: michael.bland@gsa.gov', | ||
'pusher: Mike Bland michael.bland@gsa.gov', | ||
'sender: mbland', | ||
'cloning foo into ' + cloneDir, | ||
'foo: build successful' | ||
], | ||
expectedLog = expectedMessages.join('\n') + '\n'; | ||
it('should create a builder that builds the site', function() { | ||
var handler = SiteBuilder.makeBuilderListener(webhook, builderConfig); | ||
restoreLogs(); | ||
expect(err).to.be.null; | ||
expectLogMessages(logMsgs, expectedMessages); | ||
expect(errorMsgs).to.be.empty; | ||
expect(fs.readFileSync(buildLog, 'utf8')).to.equal(expectedLog); | ||
}); | ||
SiteBuilder.makeBuilderListener(webhook, builderConfig, checkResult); | ||
var launcher = webhook.on.args[0][1]; | ||
mySpawn.setDefault(mySpawn.simple(0)); | ||
captureLogs(); | ||
launcher(incomingPayload); | ||
return handler(incomingPayload).should.be.fulfilled | ||
.then(function() { | ||
var logMsgs = console.log.args, | ||
errorMsgs = console.error.args, | ||
expectedMessages = [ | ||
'18F/foo: starting build at commit deadbeef', | ||
'description: Build me', | ||
'timestamp: 2015-09-25', | ||
'committer: michael.bland@gsa.gov', | ||
'pusher: Mike Bland michael.bland@gsa.gov', | ||
'sender: mbland', | ||
'cloning foo into ' + path.join(config.home, cloneDir), | ||
'syncing to ' + config.s3.bucket + '/' + | ||
outputDir.replace(/\\/g, '/'), | ||
'foo: build successful' | ||
], | ||
expectedLog = expectedMessages.join('\n') + '\n'; | ||
expectLogMessages(logMsgs, expectedMessages); | ||
expect(errorMsgs).to.be.empty; | ||
expect(fs.readFileSync(path.join(config.home, buildLog), 'utf8')) | ||
.to.equal(expectedLog); | ||
}) | ||
.then(restoreLogs, restoreLogs); | ||
}); | ||
it('should create a builder that fails to build the site', function(done) { | ||
var checkResult = check(done, function(err) { | ||
var logMsgs = console.log.args, | ||
errorMsgs = console.error.args, | ||
expectedMessages = [ | ||
'18F/foo: starting build at commit deadbeef', | ||
'description: Build me', | ||
'timestamp: 2015-09-25', | ||
'committer: michael.bland@gsa.gov', | ||
'pusher: Mike Bland michael.bland@gsa.gov', | ||
'sender: mbland', | ||
'cloning foo into ' + cloneDir | ||
], | ||
expectedErrors = [ | ||
'Error: failed to clone foo with exit code 1 from command: ' + | ||
'git clone git@github.com:18F/foo.git --branch 18f-pages', | ||
'foo: build failed' | ||
], | ||
expectedLog = expectedMessages.concat(expectedErrors) | ||
.join('\n') + '\n'; | ||
it('should create a builder that fails to build the site', function() { | ||
var handler = SiteBuilder.makeBuilderListener(webhook, builderConfig), | ||
errMsg = 'Error: failed to clone foo ' + | ||
'with exit code 1 from command: ' + | ||
'git clone git@github.com:18F/foo.git --branch 18f-pages'; | ||
restoreLogs(); | ||
expect(err).to.be.null; | ||
expectLogMessages(logMsgs, expectedMessages); | ||
expectLogMessages(errorMsgs, expectedErrors); | ||
expect(fs.readFileSync(buildLog, 'utf8')).to.equal(expectedLog); | ||
}); | ||
SiteBuilder.makeBuilderListener(webhook, builderConfig, checkResult); | ||
var launcher = webhook.on.args[0][1]; | ||
mySpawn.setDefault(mySpawn.simple(1)); | ||
captureLogs(); | ||
launcher(incomingPayload); | ||
return handler(incomingPayload).should.be.rejectedWith(errMsg) | ||
.then(function() { | ||
var logMsgs = console.log.args, | ||
errorMsgs = console.error.args, | ||
expectedMessages = [ | ||
'18F/foo: starting build at commit deadbeef', | ||
'description: Build me', | ||
'timestamp: 2015-09-25', | ||
'committer: michael.bland@gsa.gov', | ||
'pusher: Mike Bland michael.bland@gsa.gov', | ||
'sender: mbland', | ||
'cloning foo into ' + path.join(config.home, cloneDir) | ||
], | ||
expectedErrors = [errMsg, 'foo: build failed'], | ||
expectedLog = expectedMessages.concat(expectedErrors) | ||
.join('\n') + '\n'; | ||
expectLogMessages(logMsgs, expectedMessages); | ||
expectLogMessages(errorMsgs, expectedErrors); | ||
expect(fs.readFileSync(path.join(config.home, buildLog), 'utf8')) | ||
.to.equal(expectedLog); | ||
}) | ||
.then(restoreLogs, restoreLogs); | ||
}); | ||
it('should ignore payloads from other organizations', function() { | ||
SiteBuilder.makeBuilderListener(webhook, builderConfig); | ||
var launcher = webhook.on.args[0][1]; | ||
sinon.spy(SiteBuilder, 'launchBuilder'); | ||
var internalLauncher = SiteBuilder.launchBuilder; | ||
var handler = SiteBuilder.makeBuilderListener(webhook, builderConfig); | ||
incomingPayload.repository.organization = 'not18F'; | ||
launcher(incomingPayload); | ||
SiteBuilder.launchBuilder.restore(); | ||
expect(internalLauncher.called).to.be.false; | ||
expect(handler(incomingPayload)).to.be.undefined; | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
127863
41
2290
457