Skip to content

Commit 107e6d1

Browse files
committed
✨ Initial implementation
1 parent c505097 commit 107e6d1

File tree

7 files changed

+227
-0
lines changed

7 files changed

+227
-0
lines changed

.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
4.3.2

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
## AWS Lambda bins API
2+
3+
A setup tools for aws-lambda-bins.
4+
5+
## Usage
6+
7+
```js
8+
const api = require('aws-lambda-bins');
9+
const exec = require('child_process').exec;
10+
11+
exports.handler = (event, context, callback) => {
12+
api.install({bin: "git"}, () => {
13+
exec('git --version', (error, stdout, stderr) => {
14+
console.log(stdout);
15+
//=> git version 2.11.0
16+
callback(null);
17+
});
18+
});
19+
};
20+
```

index.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
'use strict';
2+
3+
const API = require('./lib/api');
4+
module.exports = new API();

lib/api.js

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
'use strict';
2+
3+
const child_process = require('child_process');
4+
const fs = require('fs');
5+
const https = require('https');
6+
const path = require('path');
7+
8+
const tmpPath = '/tmp'
9+
const usrPath = path.join(tmpPath, 'usr');
10+
const vendorPath = path.join(tmpPath, 'vendor');
11+
12+
const defaultOptions = {
13+
verify: false,
14+
url: 'https://s3-ap-northeast-1.amazonaws.com/aws-lambda-bins'
15+
};
16+
17+
class API {
18+
constructor() {
19+
this.options = Object.assign({}, defaultOptions);
20+
}
21+
22+
set(options) {
23+
Object.assign(this.options, options);
24+
}
25+
26+
resetOptions() {
27+
this.options = Object.assign({}, defaultOptions);
28+
}
29+
30+
install(options, callback) {
31+
this.appendPATH(path.join(usrPath, 'bin'));
32+
const url = this.url_for(options)
33+
this.download(url, callback);
34+
}
35+
36+
37+
appendPATH(path) {
38+
const paths = process.env.PATH.split(':');
39+
if (paths.indexOf(path) === -1) {
40+
paths.unshift(path);
41+
process.env.PATH = paths.join(':');
42+
}
43+
}
44+
45+
download(url, callback) {
46+
fs.mkdir(vendorPath, () => {
47+
const filename = path.basename(url);
48+
const downloadPath = path.join(vendorPath, filename);
49+
50+
fs.stat(downloadPath, (err, stats) => {
51+
if (err) {
52+
const file = fs.createWriteStream(downloadPath);
53+
const unarchiveCmd = `tar -C ${tmpPath} -xf ${downloadPath}`;
54+
https.get(url, (res) => res.pipe(file))
55+
.on('close', () => child_process.exec(unarchiveCmd, callback));
56+
} else {
57+
callback();
58+
// TODO: should verify a file
59+
}
60+
});
61+
});
62+
}
63+
64+
url_for(options) {
65+
const bin = options.bin;
66+
const tag = options.tag || 'master';
67+
return `${this.options.url}/${bin}/${bin}-${tag}.tar.gz`;
68+
}
69+
}
70+
71+
module.exports = API;

package.json

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
{
2+
"name": "aws-lambda-bins",
3+
"version": "0.1.0",
4+
"description": "A setup tools for aws-lambda-bins",
5+
"main": "index.js",
6+
"scripts": {
7+
"test": "mocha"
8+
},
9+
"repository": {
10+
"type": "git",
11+
"url": "git+https://github.com/aws-lambda-bins/api-node.git"
12+
},
13+
"author": "sinsoku",
14+
"license": "MIT",
15+
"bugs": {
16+
"url": "https://github.com/aws-lambda-bins/api-node/issues"
17+
},
18+
"homepage": "https://github.com/aws-lambda-bins/api-node#readme",
19+
"engines": {
20+
"node": "4.3.2"
21+
},
22+
"devDependencies": {
23+
"aws-sdk": "2.6.9",
24+
"intelli-espower-loader": "^1.0.1",
25+
"mocha": "^3.2.0",
26+
"power-assert": "^1.4.2",
27+
"sinon": "^1.17.7"
28+
}
29+
}

test/api.spec.js

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
const assert = require('power-assert');
2+
const child_process = require('child_process');
3+
const fs = require('fs');
4+
const https = require('https');
5+
const sinon = require('sinon');
6+
7+
const api = require('../index.js');
8+
9+
const backetURL = 'https://s3-ap-northeast-1.amazonaws.com/aws-lambda-bins';
10+
11+
describe('API', () => {
12+
describe('.set()', () => {
13+
afterEach(() => api.resetOptions());
14+
15+
it('should update options', () => {
16+
api.set({verify: true});
17+
assert(api.options.verify === true);
18+
});
19+
});
20+
21+
describe('.install', () => {
22+
const originalPATH = process.env.PATH;
23+
beforeEach(() => sinon.stub(api, 'download'));
24+
afterEach(() => {
25+
process.env.PATH = originalPATH
26+
api.download.restore();
27+
});
28+
29+
describe('.use({bin: "git"})', () => {
30+
it('should setup environment for the latest Git', () => {
31+
const callback = sinon.stub();
32+
api.install({bin: "git"}, callback);
33+
34+
assert(process.env.PATH === `/tmp/usr/bin:${originalPATH}`);
35+
const url = `${backetURL}/git/git-master.tar.gz`;
36+
assert(api.download.withArgs(url, callback));
37+
});
38+
});
39+
40+
describe('.use({bin: "git", tag: "v2.11.0"})', () => {
41+
it('should setup environment for Git v2.11.0', () => {
42+
const callback = sinon.stub();
43+
api.install({bin: "git", tag: "v2.11.0"}, callback);
44+
45+
assert(process.env.PATH === `/tmp/usr/bin:${originalPATH}`);
46+
const url = `${backetURL}/git/git-v2.11.0.tar.gz`;
47+
assert(api.download.withArgs(url, callback));
48+
});
49+
});
50+
});
51+
52+
describe('.download()', () => {
53+
beforeEach(() => {
54+
const close = { on: (e, callback) => callback() };
55+
sinon.stub(https, 'get', () => close);
56+
});
57+
afterEach(() => https.get.restore());
58+
59+
context('/tmp/vendor/filename not exists', () => {
60+
beforeEach(() => {
61+
sinon.stub(child_process, 'exec', (_command, callback) => callback());
62+
sinon.stub(fs, 'mkdir', (_path, callback) => callback());
63+
sinon.stub(fs, 'stat', (_path, callback) => callback('no such file or directory'));
64+
});
65+
afterEach(() => {
66+
child_process.exec.restore();
67+
fs.mkdir.restore();
68+
fs.stat.restore();
69+
});
70+
71+
it('should download a file from url', (done) => {
72+
const url = `${backetURL}/git/git-master.tar.gz`;
73+
api.download(url, done);
74+
75+
assert(child_process.exec.calledWith('tar -C /tmp -xf vendor/git-master.tar.gz'));
76+
assert(fs.mkdir.calledWith('/tmp/vendor'));
77+
assert(fs.stat.calledWith('/tmp/vendor/git-master.tar.gz'));
78+
assert(https.get.calledWith(url));
79+
});
80+
});
81+
82+
context('/tmp/vendor/filename already exists', () => {
83+
beforeEach(() => {
84+
sinon.stub(fs, 'mkdir', (_path, callback) => callback());
85+
sinon.stub(fs, 'stat', (_path, callback) => callback());
86+
});
87+
afterEach(() => {
88+
fs.mkdir.restore();
89+
fs.stat.restore();
90+
});
91+
92+
it('should not download', (done) => {
93+
api.download(`${backetURL}/git/git-master.tar.gz`, done);
94+
95+
assert(fs.mkdir.calledWith('/tmp/vendor'));
96+
assert(fs.stat.calledWith('/tmp/vendor/git-master.tar.gz'));
97+
assert(https.get.called === false);
98+
});
99+
});
100+
});
101+
});

test/mocha.opts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
--require intelli-espower-loader

0 commit comments

Comments
 (0)