Skip to content

Commit d64c637

Browse files
committedAug 8, 2019
init
0 parents  commit d64c637

11 files changed

+439
-0
lines changed
 

‎.editorconfig

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# http://editorconfig.org
2+
root = true
3+
4+
[*]
5+
indent_style = space
6+
indent_size = 2
7+
end_of_line = lf
8+
charset = utf-8
9+
trim_trailing_whitespace = true
10+
insert_final_newline = true
11+
12+
[*.md]
13+
trim_trailing_whitespace = false
14+
15+
[Makefile]
16+
indent_style = tab

‎.eslintrc

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
extends: standard
2+
3+
env:
4+
node: true
5+
mocha: true
6+
7+
rules:
8+
camelcase: 0
9+
semi: [2, always]
10+
object-curly-spacing: [2, always]

‎.gitignore

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
*.iml
2+
.idea/
3+
.ipr
4+
.iws
5+
*~
6+
~*
7+
*.diff
8+
*.patch
9+
*.bak
10+
.DS_Store
11+
Thumbs.db
12+
.project
13+
.*proj
14+
.svn/
15+
*.swp
16+
*.swo
17+
*.log
18+
*.sublime-project
19+
*.sublime-workspace
20+
node_modules/*
21+
npm-debug.log
22+
package-lock.json
23+
tmp/
24+
.buildpath
25+
.settings
26+
.yml
27+
_site
28+
coverage
29+
.nyc_output/

‎.npmignore

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.idea
2+
.DS_Store
3+
.nyc_output/
4+
.babelrc
5+
.editorconfig
6+
.eslintrc
7+
.travis.yml
8+
tmp
9+
node_modules
10+
package-lock.json
11+
coverage
12+
test

‎.travis.yml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
language: node_js
2+
3+
node_js:
4+
- 8
5+
- 10
6+
- 12
7+
8+
after_success:
9+
- npm run coveralls

‎README.md

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
s3-up
2+
========
3+
4+
> File system and globbing utilities
5+
6+
[![NPM version](https://img.shields.io/npm/v/s3-up.svg)](https://www.npmjs.com/package/s3-up)
7+
[![NPM downloads](https://img.shields.io/npm/dm/s3-up.svg)](https://www.npmjs.com/package/s3-up)
8+
[![Build Status](https://travis-ci.org/d-band/s3-up.svg?branch=master)](https://travis-ci.org/d-band/s3-up)
9+
[![Coverage Status](https://coveralls.io/repos/github/d-band/s3-up/badge.svg?branch=master)](https://coveralls.io/github/d-band/s3-up?branch=master)
10+
[![Dependency Status](https://david-dm.org/d-band/s3-up.svg)](https://david-dm.org/d-band/s3-up)
11+
[![Greenkeeper badge](https://badges.greenkeeper.io/d-band/s3-up.svg)](https://greenkeeper.io/)
12+
13+
---
14+
15+
## Install
16+
17+
```bash
18+
$ npm install s3-up -D
19+
```
20+
21+
## Usage
22+
23+
```bash
24+
$ up -h
25+
26+
Usage: up [options]
27+
28+
Options:
29+
-v, --version output the version number
30+
-c, --config [config] config file path (default: "up-config.js")
31+
-d, --dist [dist] upload files glob patten (default: "./dist/**")
32+
-b, --bucket [bucket] S3 bucket name
33+
-r, --region [region] S3 region name
34+
-a, --accessKey [accessKey] S3 client accessKey
35+
-s, --secretKey [secretKey] S3 client secretKey
36+
--port [port] S3 endpoint port
37+
--host [host] S3 endpoint host
38+
--base [base] base path for dist
39+
--prefix [prefix] object path prefix
40+
-h, --help output usage information
41+
42+
$ up --region=qn:cn-east-1 --bucket=test --accessKey=$AK --secretKey=$SK
43+
```
44+
45+
## Config
46+
47+
> Default config file is `up-config.json` or `up-config.js`
48+
49+
```js
50+
// up-config.js
51+
module.exports = {
52+
region: 'qn:cn-east-1',
53+
bucket: 'bucketName',
54+
accessKey: process.env.ACCESS_KEY,
55+
secretKey: process.env.SECRET_KEY,
56+
before: [{
57+
task: 'copy',
58+
actions: [{
59+
src: 'lib/**',
60+
dest: 'dist',
61+
base: '.'
62+
}]
63+
}],
64+
after: [{
65+
task: 'delete',
66+
actions: [{
67+
src: 'dist/**'
68+
}]
69+
}]
70+
};
71+
```
72+
73+
## Report a issue
74+
75+
* [All issues](https://github.com/d-band/s3-up/issues)
76+
* [New issue](https://github.com/d-band/s3-up/issues/new)
77+
78+
## License
79+
80+
s3-up is available under the terms of the MIT License.

‎cli.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/usr/bin/env node
2+
3+
const program = require('commander');
4+
5+
program
6+
.version(require('./package').version, '-v, --version')
7+
.option('-c, --config [config]', 'config file path', 'up-config.js')
8+
.option('-d, --dist [dist]', 'upload files glob patten', './dist/**')
9+
.option('-b, --bucket [bucket]', 'S3 bucket name')
10+
.option('-r, --region [region]', 'S3 region name')
11+
.option('-a, --accessKey [accessKey]', 'S3 client accessKey')
12+
.option('-s, --secretKey [secretKey]', 'S3 client secretKey')
13+
.option('--port [port]', 'S3 endpoint port')
14+
.option('--host [host]', 'S3 endpoint host')
15+
.option('--base [base]', 'base path for dist')
16+
.option('--prefix [prefix]', 'object path prefix')
17+
.parse(process.argv);
18+
19+
require('./lib')(program).then(() => {
20+
console.log('Done.');
21+
process.exit();
22+
}).catch(e => {
23+
console.log('Fail.');
24+
console.error(e);
25+
process.exit(1);
26+
});

‎lib/index.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
const ora = require('ora');
2+
const mfs = require('micro-fs');
3+
const upload = require('./upload');
4+
const { loadOptions, runTask } = require('./utils');
5+
6+
module.exports = async (options) => {
7+
options = loadOptions(options);
8+
const tasks = {
9+
...options.tasks,
10+
upload,
11+
copy: opts => mfs.copy(opts.src, opts.dest, opts),
12+
move: opts => mfs.move(opts.src, opts.dest, opts),
13+
delete: opts => mfs.delete(opts.src, opts),
14+
archive: opts => mfs.archive(opts.src, opts.dest, opts)
15+
};
16+
await runTask(options.before, tasks);
17+
await upload(options);
18+
await runTask(options.after, tasks);
19+
};
20+
21+
module.exports.mfs = mfs;
22+
module.exports.ora = ora;
23+
module.exports.upload = upload;

‎lib/upload.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const path = require('path');
2+
const ora = require('ora');
3+
const mfs = require('micro-fs');
4+
const Minio = require('minio');
5+
const {
6+
defaultPrefix, exists, colors, getEndPoint
7+
} = require('./utils');
8+
9+
module.exports = async (options) => {
10+
// Options
11+
const cwd = options.cwd || process.cwd();
12+
const base = options.base;
13+
const dist = options.dist || './dist/**';
14+
const prefix = options.prefix || defaultPrefix(cwd);
15+
const bucket = options.bucket;
16+
// Create client
17+
const client = new Minio.Client({
18+
useSSL: true,
19+
port: parseInt(options.port) || 443,
20+
endPoint: options.host,
21+
accessKey: options.accessKey,
22+
secretKey: options.secretKey,
23+
...getEndPoint(options.region),
24+
...options.client
25+
});
26+
const files = await mfs.glob(dist, {
27+
cwd, base, nodir: true
28+
});
29+
30+
let count = 0;
31+
for (const file of files) {
32+
const relative = path.relative(file.base, file.path);
33+
const dest = path.join(prefix, relative);
34+
const spin = ora(`Upload → ${dest}`).start();
35+
const skip = await exists(client, bucket, dest, file.path);
36+
if (skip) {
37+
spin.info(`Skip → ${colors.gray(dest)}`).stop();
38+
continue;
39+
}
40+
try {
41+
await client.fPutObject(bucket, dest, file.path, {});
42+
count++;
43+
spin.succeed(`Done → ${colors.green(dest)}`).stop();
44+
} catch (e) {
45+
spin.fail(`Fail → ${colors.red(dest)}`).stop();
46+
console.error(e);
47+
}
48+
}
49+
const done = `${count}/${files.length}`;
50+
console.log(` Total → ${colors.green(done)}\n`);
51+
};

‎lib/utils.js

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
const fs = require('fs');
2+
const path = require('path');
3+
const crypto = require('crypto');
4+
const ora = require('ora');
5+
6+
const colors = {
7+
red: s => `\x1b[31m${s}\x1b[39m`,
8+
gray: s => `\x1b[90m${s}\x1b[39m`,
9+
green: s => `\x1b[32m${s}\x1b[39m`
10+
};
11+
12+
function loadOptions (args) {
13+
args.cwd = args.cwd || process.cwd();
14+
const name = args.config || 'up-config.js';
15+
const file = path.join(args.cwd, name);
16+
try {
17+
const options = require(file);
18+
if (typeof options === 'function') {
19+
return options(args);
20+
} else {
21+
return Object.assign({}, args, options);
22+
}
23+
} catch (e) {
24+
return args;
25+
}
26+
}
27+
28+
function checksum (algorithm, file) {
29+
return new Promise((resolve, reject) => {
30+
const hash = crypto.createHash(algorithm);
31+
const stream = fs.createReadStream(file);
32+
stream.on('error', err => reject(err));
33+
stream.on('data', chunk => hash.update(chunk));
34+
stream.on('end', () => resolve(hash.digest('hex')));
35+
});
36+
}
37+
38+
exports.runTask = async (list, tasks) => {
39+
if (!list || !list.length) return;
40+
for (const item of list) {
41+
const name = item.task;
42+
const fn = tasks[name];
43+
if (!fn) {
44+
throw new Error(`Task '${item.task}' not found.`);
45+
}
46+
const spin = ora(`Task → ${name}`).start();
47+
const actions = item.actions || [];
48+
const len = actions.length;
49+
for (let i = 0; i < len; i++) {
50+
spin.text = `Task → ${name} (${i + 1}/${len})`;
51+
await fn(actions[i]);
52+
}
53+
spin.succeed();
54+
}
55+
console.log();
56+
};
57+
58+
exports.getEndPoint = (region) => {
59+
const map = {
60+
// 华东
61+
'qn:cn-east-1': {
62+
port: 443,
63+
region: 'cn-east-1',
64+
endPoint: 's3-cn-east-1.qiniucs.com'
65+
},
66+
// 华北
67+
'qn:cn-north-1': {
68+
port: 443,
69+
region: 'cn-north-1',
70+
endPoint: 's3-cn-north-1.qiniucs.com'
71+
},
72+
// 华南
73+
'qn:cn-south-1': {
74+
port: 443,
75+
region: 'cn-south-1',
76+
endPoint: 's3-cn-south-1.qiniucs.com'
77+
},
78+
// 北美
79+
'qn:us-north-1': {
80+
port: 443,
81+
region: 'us-north-1',
82+
endPoint: 's3-us-north-1.qiniucs.com'
83+
},
84+
// 东南亚
85+
'qn:ap-southeast-1': {
86+
port: 443,
87+
region: 'ap-southeast-1',
88+
endPoint: 's3-ap-southeast-1.qiniucs.com'
89+
}
90+
};
91+
if (map[region]) {
92+
return map[region];
93+
}
94+
return { region };
95+
};
96+
97+
exports.colors = colors;
98+
exports.checksum = checksum;
99+
exports.loadOptions = loadOptions;
100+
101+
exports.defaultPrefix = (cwd) => {
102+
const pkgPath = path.join(cwd, 'package.json');
103+
if (fs.existsSync(pkgPath)) {
104+
const pkg = require(pkgPath);
105+
if (pkg.name && pkg.version) {
106+
return `${pkg.name}/${pkg.version}`;
107+
}
108+
return '';
109+
}
110+
};
111+
112+
exports.exists = async (client, bucket, object, file) => {
113+
try {
114+
const stat = await client.statObject(bucket, object);
115+
const hash = await checksum('md5', file);
116+
return stat.etag === hash;
117+
} catch (e) {
118+
return false;
119+
}
120+
};

‎package.json

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"name": "s3-up",
3+
"version": "1.0.0",
4+
"description": "Uploads compiled assets to Amazon S3",
5+
"main": "lib/index.js",
6+
"bin": {
7+
"up": "cli.js"
8+
},
9+
"scripts": {
10+
"lint": "eslint lib cli.js",
11+
"test": "nyc mocha",
12+
"report": "nyc report --reporter=html",
13+
"coveralls": "nyc report --reporter=text-lcov | coveralls"
14+
},
15+
"nyc": {
16+
"all": true,
17+
"include": [
18+
"lib/**/*.js"
19+
]
20+
},
21+
"pre-commit": [
22+
"lint"
23+
],
24+
"repository": {
25+
"type": "git",
26+
"url": "git+https://github.com/d-band/s3-up.git"
27+
},
28+
"keywords": [
29+
"s3",
30+
"glob",
31+
"aws",
32+
"deploy",
33+
"upload",
34+
"batch"
35+
],
36+
"author": "d-band",
37+
"license": "MIT",
38+
"engines": {
39+
"node": ">=8"
40+
},
41+
"bugs": {
42+
"url": "https://github.com/d-band/s3-up/issues"
43+
},
44+
"homepage": "https://github.com/d-band/s3-up#readme",
45+
"dependencies": {
46+
"commander": "^2.20.0",
47+
"micro-fs": "^1.0.2",
48+
"minio": "^7.0.11",
49+
"ora": "^3.4.0"
50+
},
51+
"devDependencies": {
52+
"coveralls": "^3.0.5",
53+
"eslint": "^6.1.0",
54+
"eslint-config-standard": "^13.0.1",
55+
"eslint-plugin-import": "^2.18.2",
56+
"eslint-plugin-node": "^9.1.0",
57+
"eslint-plugin-promise": "^4.2.1",
58+
"eslint-plugin-standard": "^4.0.0",
59+
"mocha": "^6.2.0",
60+
"nyc": "^14.1.1",
61+
"pre-commit": "^1.2.2"
62+
}
63+
}

0 commit comments

Comments
 (0)
Please sign in to comment.