carp-streamer
Advanced tools
Comparing version 0.5.3 to 0.6.0
@@ -301,7 +301,20 @@ "use strict"; | ||
} | ||
const readdir = util_1.default.promisify(fs_1.default.readdir); | ||
function createDirentFromStats(stats, name) { | ||
return new class { | ||
get name() { return name; } | ||
isBlockDevice() { return stats.isBlockDevice(); } | ||
isCharacterDevice() { return stats.isCharacterDevice(); } | ||
isDirectory() { return stats.isDirectory(); } | ||
isFIFO() { return stats.isFIFO(); } | ||
isFile() { return stats.isFile(); } | ||
isSocket() { return stats.isSocket(); } | ||
isSymbolicLink() { return stats.isSymbolicLink(); } | ||
}; | ||
} | ||
exports.createDirentFromStats = createDirentFromStats; | ||
const readdirAsync = util_1.default.promisify(fs_1.default.readdir); | ||
function listDirectoryEntriesRecursively(root) { | ||
return __asyncGenerator(this, arguments, function* listDirectoryEntriesRecursively_1() { | ||
try { | ||
for (let dirent of yield __await(readdir(root, { withFileTypes: true }))) { | ||
for (let dirent of yield __await(readdirAsync(root, { withFileTypes: true }))) { | ||
const entryPath = path_1.default.join(root, dirent.name); | ||
@@ -308,0 +321,0 @@ yield yield __await(({ path: entryPath, dirent, error: null })); |
@@ -36,3 +36,3 @@ #!/usr/bin/env node | ||
'alias': { t: 'token', v: 'version', c: 'concurrency' }, | ||
'string': ['t', 'as-user', 'exclude'], | ||
'string': ['t', 'as-user', 'exclude', 'log-file'], | ||
'boolean': ['v', 'dry-run', 'progress'], | ||
@@ -47,5 +47,6 @@ 'number': ['c'], | ||
} | ||
const [source, destination] = args._; | ||
if (source === undefined || destination === undefined) { | ||
console.error(`usage: ${npmPackage.name} [options] source destination`); | ||
const sources = args._.slice(0, -1); | ||
const [destination] = args._.slice(-1); | ||
if (sources.length === 0 || destination === undefined) { | ||
console.error(`usage: ${npmPackage.name} [options] source... destination`); | ||
process.exit(1); | ||
@@ -56,15 +57,4 @@ } | ||
const needProgress = args['progress']; | ||
const logFile = args['log-file'] && path_1.default.resolve(process.cwd(), args['log-file']); | ||
const excludes = (args['exclude'] && [].concat(args['exclude']) || []).map(exclude => path_1.default.resolve(process.cwd(), exclude)); | ||
const appConfig = process.env.BOX_APP_CONFIG && JSON.parse(fs_1.default.readFileSync(process.env.BOX_APP_CONFIG).toString()); | ||
const createBoxClient = (params) => { | ||
if (params.token) | ||
return box_node_sdk_1.default.getBasicClient(params.token); | ||
const sdk = box_node_sdk_1.default.getPreconfiguredInstance(appConfig); | ||
return sdk.getAppAuthClient('enterprise'); | ||
}; | ||
const rootPath = path_1.default.resolve(process.cwd(), source); | ||
const client = createBoxClient({ appConfig, token: args.token }); | ||
if (args['as-user']) { | ||
client.asUser(args['as-user']); | ||
} | ||
const nullDevice = new class extends stream_1.Writable { | ||
@@ -76,76 +66,111 @@ _write(chunk, encoding, callback) { | ||
const progressBar = new progress_1.default(' synchronizing... [:bar] :percent (:current/:total) :elapseds :etas', { total: Number.MAX_SAFE_INTEGER, stream: needProgress ? process.stderr : nullDevice }); | ||
const spinner = ora_1.default({ stream: needProgress ? nullDevice : process.stderr }).start('synchronizing...'); | ||
client.folders.get(destination).then((rootFolder) => __awaiter(this, void 0, void 0, function* () { | ||
var e_1, _a; | ||
const q = async_1.default.queue(({ path: absolutePath, dirent, error }, done) => __awaiter(this, void 0, void 0, function* () { | ||
const relativePath = path_1.default.relative(rootPath, absolutePath); | ||
if (error) { | ||
debug('%s: %s\n%s', error.name, error.message, error.stack); | ||
spinner.warn(`Could not access '${relativePath}'.`); | ||
return done(error); | ||
const spinner = ora_1.default({ stream: (logFile && fs_1.default.createWriteStream(logFile)) || (needProgress ? nullDevice : process.stdout) }).start('synchronizing...'); | ||
(() => __awaiter(this, void 0, void 0, function* () { | ||
var e_1, _a, e_2, _b; | ||
try { | ||
const appConfig = process.env.BOX_APP_CONFIG && JSON.parse(fs_1.default.readFileSync(process.env.BOX_APP_CONFIG).toString()); | ||
const client = ((params) => { | ||
if (params.token) | ||
return box_node_sdk_1.default.getBasicClient(params.token); | ||
const sdk = box_node_sdk_1.default.getPreconfiguredInstance(appConfig); | ||
return sdk.getAppAuthClient('enterprise'); | ||
})({ appConfig, token: args.token }); | ||
if (args['as-user']) { | ||
client.asUser(args['as-user']); | ||
} | ||
if (excludes.some(exclude => absolutePath.startsWith(exclude))) { | ||
spinner.info(`'${relativePath}' has been excluded.`); | ||
return done(); | ||
} | ||
const q = async_1.default.queue((task, done) => __awaiter(this, void 0, void 0, function* () { | ||
const { entry: { path: absolutePath, dirent, error }, rootPath, rootFolder } = task; | ||
const relativePath = path_1.default.relative(rootPath, absolutePath); | ||
if (error) { | ||
debug('%s: %s\n%s', error.name, error.message, error.stack); | ||
spinner.warn(`Could not access '${absolutePath}'.`); | ||
return done(error); | ||
} | ||
if (excludes.some(exclude => absolutePath.startsWith(exclude))) { | ||
spinner.info(`'${absolutePath}' has been excluded.`); | ||
return done(); | ||
} | ||
try { | ||
const entry = yield app_1.Entry.create(dirent, rootPath, relativePath, rootFolder, client); | ||
const status = yield entry.synchronize(client, pretend); | ||
switch (status) { | ||
case app_1.ResultStatus.DOWNLOADED: | ||
spinner.succeed(`'${absolutePath}' only exists remotely.`); | ||
break; | ||
case app_1.ResultStatus.SYNCHRONIZED: | ||
spinner.succeed(`'${absolutePath}' is synchronized.`); | ||
break; | ||
case app_1.ResultStatus.UPLOADED: | ||
spinner.succeed(`'${absolutePath}' is newly uploaded.`); | ||
break; | ||
case app_1.ResultStatus.UPGRADED: | ||
spinner.succeed(`A new version of '${absolutePath}' has been uploaded.`); | ||
break; | ||
default: | ||
throw new Error('unknown result status'); | ||
} | ||
done(); | ||
} | ||
catch (error) { | ||
debug('%s: %s\n%s', error.name, error.message, error.stack); | ||
spinner.fail(`Failed to synchronize '${absolutePath}'.`); | ||
done(error); | ||
} | ||
progressBar.tick(); | ||
}), concurrency); | ||
const rootFolder = yield client.folders.get(destination); | ||
let count = 0; | ||
try { | ||
const entry = yield app_1.Entry.create(dirent, rootPath, relativePath, rootFolder, client); | ||
const status = yield entry.synchronize(client, pretend); | ||
switch (status) { | ||
case app_1.ResultStatus.DOWNLOADED: | ||
spinner.succeed(`'${relativePath}' only exists remotely.`); | ||
break; | ||
case app_1.ResultStatus.SYNCHRONIZED: | ||
spinner.succeed(`'${relativePath}' is synchronized.`); | ||
break; | ||
case app_1.ResultStatus.UPLOADED: | ||
spinner.succeed(`'${relativePath}' is newly uploaded.`); | ||
break; | ||
case app_1.ResultStatus.UPGRADED: | ||
spinner.succeed(`A new version of '${relativePath}' has been uploaded.`); | ||
break; | ||
default: | ||
throw new Error('unknown result status'); | ||
for (var sources_1 = __asyncValues(sources), sources_1_1; sources_1_1 = yield sources_1.next(), !sources_1_1.done;) { | ||
let source = sources_1_1.value; | ||
const rootPath = path_1.default.resolve(process.cwd(), source); | ||
const stats = fs_1.default.statSync(rootPath); | ||
if (!stats.isDirectory()) { | ||
const { dir, base } = path_1.default.parse(rootPath); | ||
const entry = { path: rootPath, dirent: app_1.createDirentFromStats(stats, base), error: null }; | ||
q.push({ entry, rootPath: dir, rootFolder }); | ||
count++; | ||
continue; | ||
} | ||
try { | ||
for (var _c = __asyncValues(app_1.listDirectoryEntriesRecursively(rootPath)), _d; _d = yield _c.next(), !_d.done;) { | ||
let entry = _d.value; | ||
q.push({ entry, rootPath, rootFolder }); | ||
count++; | ||
} | ||
} | ||
catch (e_2_1) { e_2 = { error: e_2_1 }; } | ||
finally { | ||
try { | ||
if (_d && !_d.done && (_b = _c.return)) yield _b.call(_c); | ||
} | ||
finally { if (e_2) throw e_2.error; } | ||
} | ||
} | ||
done(); | ||
} | ||
catch (error) { | ||
debug('%s: %s\n%s', error.name, error.message, error.stack); | ||
spinner.fail(`Failed to synchronize '${relativePath}'.`); | ||
done(error); | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (sources_1_1 && !sources_1_1.done && (_a = sources_1.return)) yield _a.call(sources_1); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
} | ||
progressBar.tick(); | ||
}), concurrency); | ||
let count = 0; | ||
try { | ||
for (var _b = __asyncValues(app_1.listDirectoryEntriesRecursively(rootPath)), _c; _c = yield _b.next(), !_c.done;) { | ||
let entry = _c.value; | ||
q.push(entry); | ||
count++; | ||
} | ||
progressBar.total = count; | ||
spinner.info(`${count} entries were found.`); | ||
const results = yield q.drain(); | ||
if (needProgress) | ||
console.error('Successful!'); | ||
progressBar.terminate(); | ||
spinner.info('Successful!'); | ||
process.exit(0); | ||
} | ||
catch (e_1_1) { e_1 = { error: e_1_1 }; } | ||
finally { | ||
try { | ||
if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b); | ||
} | ||
finally { if (e_1) throw e_1.error; } | ||
catch (reason) { | ||
debug('%s: %s\n%s', reason.name, reason.message, reason.stack); | ||
if (needProgress) | ||
console.error(`Failure! ${reason.name}: ${reason.message}`); | ||
progressBar.terminate(); | ||
spinner.warn(`Failure! ${reason.name}: ${reason.message}`); | ||
process.exit(1); | ||
} | ||
progressBar.total = count; | ||
spinner.info(`${count} entries were found.`); | ||
return yield q.drain(); | ||
})).then(results => { | ||
if (needProgress) | ||
console.error('Successful!'); | ||
progressBar.terminate(); | ||
spinner.info('Successful!'); | ||
process.exit(0); | ||
}).catch(reason => { | ||
debug('%s: %s\n%s', reason.name, reason.message, reason.stack); | ||
if (needProgress) | ||
console.error(`Failure! ${reason.name}: ${reason.message}`); | ||
progressBar.terminate(); | ||
spinner.warn(`Failure! ${reason.name}: ${reason.message}`); | ||
process.exit(1); | ||
}); | ||
}))(); | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "carp-streamer", | ||
"version": "0.5.3", | ||
"version": "0.6.0", | ||
"description": "Carp streamer", | ||
@@ -5,0 +5,0 @@ "bin": "dist/index.js", |
@@ -47,7 +47,7 @@ import BoxSDK from 'box-node-sdk'; | ||
static create(dirent: fs.Dirent, root: string, relativePath: string, remoteRoot: BoxSDK.Folder, client: BoxSDK.BoxClient): Promise<Entry> { | ||
static create(dirent: fs.Dirent | null, root: string, relativePath: string, remoteRoot: BoxSDK.Folder, client: BoxSDK.BoxClient): Promise<Entry> { | ||
return Entry._create(dirent, root, relativePath, remoteRoot, client, INIT_RETRY_TIMES, 0); | ||
} | ||
private static async _create(dirent: fs.Dirent, root: string, relativePath: string, remoteRoot: BoxSDK.Folder, client: BoxSDK.BoxClient, retryTimes: number, delay: number): Promise<Entry> { | ||
private static async _create(dirent: fs.Dirent | null, root: string, relativePath: string, remoteRoot: BoxSDK.Folder, client: BoxSDK.BoxClient, retryTimes: number, delay: number): Promise<Entry> { | ||
await sleep(delay); | ||
@@ -234,6 +234,20 @@ try { | ||
const readdir = util.promisify(fs.readdir); | ||
export function createDirentFromStats(stats: fs.Stats, name: string): fs.Dirent { | ||
interface DirentLike extends fs.Dirent { } | ||
return new class implements DirentLike { | ||
get name() { return name; } | ||
isBlockDevice() { return stats.isBlockDevice(); } | ||
isCharacterDevice() { return stats.isCharacterDevice(); } | ||
isDirectory() { return stats.isDirectory(); } | ||
isFIFO() { return stats.isFIFO(); } | ||
isFile() { return stats.isFile(); } | ||
isSocket() { return stats.isSocket(); } | ||
isSymbolicLink() { return stats.isSymbolicLink(); } | ||
}; | ||
} | ||
const readdirAsync = util.promisify(fs.readdir); | ||
export async function* listDirectoryEntriesRecursively(root: string): AsyncIterableIterator<{path: string, dirent: fs.Dirent | null, error:any}> { | ||
try { | ||
for(let dirent of await readdir(root, { withFileTypes: true })) { | ||
for(let dirent of await readdirAsync(root, { withFileTypes: true })) { | ||
const entryPath = path.join(root, dirent.name); | ||
@@ -240,0 +254,0 @@ yield ({ path: entryPath, dirent, error: null }); |
166
src/index.ts
@@ -11,3 +11,3 @@ #!/usr/bin/env node | ||
import util from 'util'; | ||
import {Entry, ResultStatus, listDirectoryEntriesRecursively} from './app' | ||
import { Entry, ResultStatus, listDirectoryEntriesRecursively, createDirentFromStats } from './app' | ||
import { Writable } from 'stream'; | ||
@@ -20,3 +20,3 @@ | ||
'alias': { t: 'token', v: 'version', c: 'concurrency' }, | ||
'string': ['t', 'as-user', 'exclude'], | ||
'string': ['t', 'as-user', 'exclude', 'log-file'], | ||
'boolean': ['v', 'dry-run', 'progress'], | ||
@@ -32,6 +32,7 @@ 'number': ['c'], | ||
const [source, destination] = args._ | ||
const sources = args._.slice(0, -1); | ||
const [destination] = args._.slice(-1); | ||
if (source === undefined || destination === undefined) { | ||
console.error(`usage: ${npmPackage.name} [options] source destination`); | ||
if (sources.length === 0 || destination === undefined) { | ||
console.error(`usage: ${npmPackage.name} [options] source... destination`); | ||
process.exit(1); | ||
@@ -43,19 +44,6 @@ } | ||
const needProgress: boolean = args['progress']; | ||
const logFile = args['log-file'] && path.resolve(process.cwd(), args['log-file']); | ||
const excludes = (args['exclude'] && [].concat(args['exclude']) || []).map(exclude => path.resolve(process.cwd(), exclude)); | ||
const appConfig = process.env.BOX_APP_CONFIG && JSON.parse(fs.readFileSync(process.env.BOX_APP_CONFIG).toString()); | ||
const createBoxClient = (params: { appConfig?: object, token?: string }) => { | ||
if (params.token) return BoxSDK.getBasicClient(params.token); | ||
const sdk = BoxSDK.getPreconfiguredInstance(appConfig); | ||
return sdk.getAppAuthClient('enterprise'); | ||
} | ||
const rootPath = path.resolve(process.cwd(), source); | ||
const client = createBoxClient({ appConfig, token: args.token }); | ||
if (args['as-user']) { | ||
client.asUser(args['as-user']); | ||
} | ||
const nullDevice = new class extends Writable { | ||
_write(chunk: any, encoding:any, callback: (erro? :any) => void) { | ||
_write(chunk: any, encoding: any, callback: (erro?: any) => void) { | ||
callback(); | ||
@@ -65,61 +53,87 @@ } | ||
const progressBar = new progress(' synchronizing... [:bar] :percent (:current/:total) :elapseds :etas', { total: Number.MAX_SAFE_INTEGER, stream: needProgress ? process.stderr : nullDevice }); | ||
const spinner = ora({ stream: needProgress ? nullDevice : process.stderr }).start('synchronizing...'); | ||
client.folders.get(destination).then(async (rootFolder) => { | ||
const q = async.queue(async ({ path: absolutePath, dirent, error }, done) => { | ||
const relativePath = path.relative(rootPath, absolutePath); | ||
if (error) { | ||
debug('%s: %s\n%s', error.name, error.message, error.stack); | ||
spinner.warn(`Could not access '${relativePath}'.`); | ||
return done(error); | ||
const spinner = ora({ stream: (logFile && fs.createWriteStream(logFile)) || (needProgress ? nullDevice : process.stdout) }).start('synchronizing...'); | ||
(async () => { | ||
try { | ||
const appConfig = process.env.BOX_APP_CONFIG && JSON.parse(fs.readFileSync(process.env.BOX_APP_CONFIG).toString()); | ||
const client = ((params: { appConfig?: object, token?: string }) => { | ||
if (params.token) return BoxSDK.getBasicClient(params.token); | ||
const sdk = BoxSDK.getPreconfiguredInstance(appConfig); | ||
return sdk.getAppAuthClient('enterprise'); | ||
})({ appConfig, token: args.token }); | ||
if (args['as-user']) { | ||
client.asUser(args['as-user']); | ||
} | ||
if (excludes.some(exclude => absolutePath.startsWith(exclude))) { | ||
spinner.info(`'${relativePath}' has been excluded.`); | ||
return done(); | ||
} | ||
try { | ||
const entry = await Entry.create(dirent, rootPath, relativePath, rootFolder, client); | ||
const status = await entry.synchronize(client, pretend); | ||
switch (status) { | ||
case ResultStatus.DOWNLOADED: | ||
spinner.succeed(`'${relativePath}' only exists remotely.`); | ||
break; | ||
case ResultStatus.SYNCHRONIZED: | ||
spinner.succeed(`'${relativePath}' is synchronized.`); | ||
break; | ||
case ResultStatus.UPLOADED: | ||
spinner.succeed(`'${relativePath}' is newly uploaded.`); | ||
break; | ||
case ResultStatus.UPGRADED: | ||
spinner.succeed(`A new version of '${relativePath}' has been uploaded.`); | ||
break; | ||
default: | ||
throw new Error('unknown result status'); | ||
const q = async.queue(async (task: {entry: { path: string, dirent: fs.Dirent | null, error: any}, rootPath: string, rootFolder: BoxSDK.Folder}, done) => { | ||
const { entry: { path: absolutePath, dirent, error }, rootPath, rootFolder} = task; | ||
const relativePath = path.relative(rootPath, absolutePath); | ||
if (error) { | ||
debug('%s: %s\n%s', error.name, error.message, error.stack); | ||
spinner.warn(`Could not access '${absolutePath}'.`); | ||
return done(error); | ||
} | ||
done(); | ||
} catch (error) { | ||
debug('%s: %s\n%s', error.name, error.message, error.stack); | ||
spinner.fail(`Failed to synchronize '${relativePath}'.`); | ||
done(error); | ||
if (excludes.some(exclude => absolutePath.startsWith(exclude))) { | ||
spinner.info(`'${absolutePath}' has been excluded.`); | ||
return done(); | ||
} | ||
try { | ||
const entry = await Entry.create(dirent, rootPath, relativePath, rootFolder, client); | ||
const status = await entry.synchronize(client, pretend); | ||
switch (status) { | ||
case ResultStatus.DOWNLOADED: | ||
spinner.succeed(`'${absolutePath}' only exists remotely.`); | ||
break; | ||
case ResultStatus.SYNCHRONIZED: | ||
spinner.succeed(`'${absolutePath}' is synchronized.`); | ||
break; | ||
case ResultStatus.UPLOADED: | ||
spinner.succeed(`'${absolutePath}' is newly uploaded.`); | ||
break; | ||
case ResultStatus.UPGRADED: | ||
spinner.succeed(`A new version of '${absolutePath}' has been uploaded.`); | ||
break; | ||
default: | ||
throw new Error('unknown result status'); | ||
} | ||
done(); | ||
} catch (error) { | ||
debug('%s: %s\n%s', error.name, error.message, error.stack); | ||
spinner.fail(`Failed to synchronize '${absolutePath}'.`); | ||
done(error); | ||
} | ||
progressBar.tick(); | ||
}, concurrency); | ||
const rootFolder = await client.folders.get(destination); | ||
let count = 0; | ||
for await (let source of sources) { | ||
const rootPath = path.resolve(process.cwd(), source); | ||
const stats = fs.statSync(rootPath); | ||
if (!stats.isDirectory()) { | ||
const { dir, base } = path.parse(rootPath); | ||
const entry = { path: rootPath, dirent: createDirentFromStats(stats, base), error: null }; | ||
q.push({entry, rootPath: dir, rootFolder}); | ||
count++; | ||
continue; | ||
} | ||
for await (let entry of listDirectoryEntriesRecursively(rootPath)) { | ||
q.push({entry, rootPath, rootFolder}); | ||
count++; | ||
} | ||
} | ||
progressBar.tick(); | ||
}, concurrency); | ||
let count = 0; | ||
for await (let entry of listDirectoryEntriesRecursively(rootPath)) { | ||
q.push(entry); | ||
count++; | ||
progressBar.total = count; | ||
spinner.info(`${count} entries were found.`); | ||
const results = await q.drain(); | ||
if (needProgress) console.error('Successful!'); | ||
progressBar.terminate(); | ||
spinner.info('Successful!'); | ||
process.exit(0); | ||
} catch (reason) { | ||
debug('%s: %s\n%s', reason.name, reason.message, reason.stack); | ||
if (needProgress) console.error(`Failure! ${reason.name}: ${reason.message}`); | ||
progressBar.terminate(); | ||
spinner.warn(`Failure! ${reason.name}: ${reason.message}`); | ||
process.exit(1); | ||
} | ||
progressBar.total = count; | ||
spinner.info(`${count} entries were found.`); | ||
return await q.drain(); | ||
}).then(results => { | ||
if (needProgress) console.error('Successful!'); | ||
progressBar.terminate(); | ||
spinner.info('Successful!'); | ||
process.exit(0); | ||
}).catch(reason => { | ||
debug('%s: %s\n%s', reason.name, reason.message, reason.stack); | ||
if (needProgress) console.error(`Failure! ${reason.name}: ${reason.message}`); | ||
progressBar.terminate(); | ||
spinner.warn(`Failure! ${reason.name}: ${reason.message}`); | ||
process.exit(1); | ||
}); | ||
})() |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
73891
1148