Permalink
Browse files

feat(images): Support rebuild on other file changes

  • Loading branch information...
TomFrost committed Jan 11, 2018
1 parent 922e0d0 commit 27f64386e5a879e267f42a461ad22067d0bbf6b3
Showing with 46 additions and 13 deletions.
  1. +14 −0 README.md
  2. +23 −9 src/images.js
  3. +1 −1 src/index.js
  4. +7 −2 test/src/images.spec.js
  5. +1 −1 test/src/index.spec.js
View
@@ -126,6 +126,20 @@ The above binci.yml will, in the event that Binci needs to build a new container
addition to the two listed tags. When Binci is done running, the command `docker push myorg/myrepo:latest` would work
as expected.
## Rebuilding
When a `dockerfile` is used, Binci will automatically rebuild it if the `dockerfile` changes. However, it may be necessary to trigger a rebuild when other files change that may impact the build. To list these files, specify `rebuildOnChange`:
```yaml
dockerfile: ./Dockerfile
rebuildOnChange:
- ./Gemfile
- ./Gemfile.lock
- ./Rakefile
```
When any of those files are changed, created, or deleted, the next Binci run will rebuild the image from the dockerfile before executing the task.
## Services
Services add links into the primary container, exposing the services for utilization. For the most part, services utilize the same format for definition as the primary container.
View
@@ -6,6 +6,7 @@ const cp = require('child_process')
const fs = require('fs')
const path = require('path')
const crypto = require('crypto')
const Promise = require('bluebird')
const images = {
/**
@@ -72,19 +73,29 @@ const images = {
}))
}),
/**
* Gets the SHA-1 checksum, truncated to 12 hexadecimal digits, of the contents of the file at path.
* @param {string} path The path of the file for the checksum
* @returns {Promise.<string|null>} The sha1 as a hex string, or null if the file does not exist.
* Gets the SHA-1 checksum, truncated to 12 hexadecimal digits, of the combined contents of the
* files at the paths provided.
* @param {Array<string>} paths An array of paths for the files to include in the checksum
* @returns {Promise.<string>} The sha1 as a hex string.
*/
getHash: (path) => new Promise((resolve, reject) => {
const shasum = crypto.createHash('sha1')
getHash: (paths) => {
return Promise.reduce(paths, images.updateHash, crypto.createHash('sha1'))
.then(hash => hash ? hash.digest('hex').substr(0, 12) : null)
},
/**
* Updates an existing hash object with the contents of a file at a given path.
* @param {Hash} hash A crypto hash object to be updated
* @param {string} path The path to a file which will be calculated into the hash
* @returns {Promise<Hash>} resolves with the updated Hash object
*/
updateHash: (hash, path) => new Promise((resolve, reject) => {
const stream = fs.createReadStream(path)
stream.on('error', err => {
if (err.code && err.code === 'ENOENT') resolve(null)
else reject(err)
})
stream.on('data', data => shasum.update(data))
stream.on('close', () => resolve(shasum.digest('hex').substr(0, 12)))
stream.on('data', data => hash.update(data))
stream.on('close', () => resolve(hash))
}),
/**
* Gets a valid image name:tag that can be used to run a new docker container.
@@ -94,13 +105,16 @@ const images = {
* deleted (if one exists).
* @param {String} [dockerfile="./Dockerfile"] The path to the dockerfile to be
* used for building the new image or retrieving the existing one
* @param {Array<string>} [monitorPaths=[]] An optional array of file paths to monitor
* for changes, causing a rebuild of the docker container if any of them are updated
* @param {Array<string>} [tags=[]] An optional array of tags with which to tag a new
* image, if one needs to be built
* @returns {Promise.<string>} the name:tag of the image to be used
*/
getImage: (dockerfile = './Dockerfile', tags = []) => {
getImage: (dockerfile = './Dockerfile', monitorPaths = [], tags = []) => {
monitorPaths.push(dockerfile)
return Promise.all([
images.getHash(dockerfile),
images.getHash(monitorPaths),
images.getBuiltImages()
]).then(([ hash, imgs ]) => {
if (!hash) {
View
@@ -121,7 +121,7 @@ const instance = {
*/
attachFrom: (cfg) => {
if (!cfg.from) {
return images.getImage(cfg.dockerfile, cfg.tags || [])
return images.getImage(cfg.dockerfile, cfg.rebuildOnChange, cfg.tags)
.then(imageId => {
cfg.from = imageId
return cfg
View
@@ -29,12 +29,17 @@ describe('images', () => {
})
describe('getHash', () => {
it('determines the SHA-1 hash of an existing file', () => {
return images.getHash(knownShaPath).then(hash => {
return images.getHash([knownShaPath]).then(hash => {
expect(hash).to.equal(knownSha.substr(0, 12))
})
})
it('determines the SHA-1 hash of multiple files', () => {
return images.getHash([knownShaPath, knownShaPath]).then(hash => {
expect(hash).to.equal('cdb39cef22e6')
})
})
it('returns null if the file does not exist', () => {
return images.getHash(knownShaPath + 'notfound').then(hash => {
return images.getHash([knownShaPath + 'notfound']).then(hash => {
expect(hash).to.be.null()
})
})
View
@@ -164,7 +164,7 @@ describe('index', () => {
conf.tags = ['foo', 'bar']
const stub = sandbox.stub(images, 'getImage', () => Promise.resolve('foo'))
return instance.attachFrom(conf).then(() => {
expect(stub).to.be.calledWith(undefined, ['foo', 'bar'])
expect(stub).to.be.calledWith(undefined, undefined, ['foo', 'bar'])
})
})
})

0 comments on commit 27f6438

Please sign in to comment.