Skip to content

Commit

Permalink
feat(images): Support rebuild on other file changes
Browse files Browse the repository at this point in the history
  • Loading branch information
TomFrost committed Jan 11, 2018
1 parent 922e0d0 commit 27f6438
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 13 deletions.
14 changes: 14 additions & 0 deletions README.md
Expand Up @@ -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.
Expand Down
32 changes: 23 additions & 9 deletions src/images.js
Expand Up @@ -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 = {
/**
Expand Down Expand Up @@ -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.
Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/index.js
Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions test/src/images.spec.js
Expand Up @@ -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()
})
})
Expand Down
2 changes: 1 addition & 1 deletion test/src/index.spec.js
Expand Up @@ -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'])
})
})
})
Expand Down

0 comments on commit 27f6438

Please sign in to comment.