Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit 120f072

Browse files
author
Alan Shaw
committed
fix: swallowed errors
The switch to using `yargs-promise` for `ipfs init` and `ipfs daemon` commands caused an unhandled promise rejection and in some cases would cause an error to not be printed to the console. This PR greatly simplifies the code in `src/cli/bin.js`, to always use `yargs-promise`. Command handlers are now passed an async `getIpfs` function instead of an `ipfs` instance. It means that we don't have to differentiate between commands that use an IPFS instance in `src/cli/bin.js`, giving the handler the power to call `getIpfs` or not to obtain an IPFS instance as and when needed. This removes a whole bunch of complexity from `src/cli/bin.js` at the cost of adding a single line to every command handler that needs to use an IPFS instance. This enables operations like `echo "hello" | jsipfs add -q | jsipfs cid base32` to work without `jsipfs cid base32` failing because it's trying to acquire a repo lock when it doesn't use IPFS at all. fixes #1835 refs #1858 refs libp2p/js-libp2p#311 License: MIT Signed-off-by: Alan Shaw <alan.shaw@protocol.ai>
1 parent dba3085 commit 120f072

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+277
-212
lines changed

package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@
110110
"ipfs-block-service": "~0.15.1",
111111
"ipfs-http-client": "^29.0.0",
112112
"ipfs-http-response": "~0.2.1",
113-
"ipfs-mfs": "0.9.0",
113+
"ipfs-mfs": "~0.9.1",
114114
"ipfs-multipart": "~0.1.0",
115115
"ipfs-repo": "~0.26.1",
116116
"ipfs-unixfs": "~0.1.16",
@@ -168,7 +168,6 @@
168168
"pull-stream": "^3.6.9",
169169
"pull-stream-to-stream": "^1.3.4",
170170
"pump": "^3.0.0",
171-
"read-pkg-up": "^4.0.0",
172171
"readable-stream": "^3.1.1",
173172
"receptacle": "^1.3.2",
174173
"stream-to-pull-stream": "^1.7.2",

src/cli/bin.js

+68-95
Original file line numberDiff line numberDiff line change
@@ -5,118 +5,91 @@
55
const YargsPromise = require('yargs-promise')
66
const yargs = require('yargs')
77
const updateNotifier = require('update-notifier')
8-
const readPkgUp = require('read-pkg-up')
98
const utils = require('./utils')
109
const print = utils.print
1110
const mfs = require('ipfs-mfs/cli')
1211
const debug = require('debug')('ipfs:cli')
12+
const pkg = require('../../package.json')
13+
14+
async function main (args) {
15+
const oneWeek = 1000 * 60 * 60 * 24 * 7
16+
updateNotifier({ pkg, updateCheckInterval: oneWeek }).notify()
17+
18+
const cli = yargs
19+
.option('silent', {
20+
desc: 'Write no output',
21+
type: 'boolean',
22+
default: false,
23+
coerce: silent => {
24+
if (silent) utils.disablePrinting()
25+
return silent
26+
}
27+
})
28+
.option('pass', {
29+
desc: 'Pass phrase for the keys',
30+
type: 'string',
31+
default: ''
32+
})
33+
.epilog(utils.ipfsPathHelp)
34+
.demandCommand(1)
35+
.fail((msg, err, yargs) => {
36+
if (err) {
37+
throw err // preserve stack
38+
}
1339

14-
const pkg = readPkgUp.sync({ cwd: __dirname }).pkg
15-
updateNotifier({
16-
pkg,
17-
updateCheckInterval: 1000 * 60 * 60 * 24 * 7 // 1 week
18-
}).notify()
19-
20-
const args = process.argv.slice(2)
21-
22-
const cli = yargs
23-
.option('silent', {
24-
desc: 'Write no output',
25-
type: 'boolean',
26-
default: false,
27-
coerce: ('silent', silent => {
28-
if (silent) {
29-
utils.disablePrinting()
40+
if (args.length > 0) {
41+
print(msg)
3042
}
31-
return silent
43+
44+
yargs.showHelp()
3245
})
33-
})
34-
.option('pass', {
35-
desc: 'Pass phrase for the keys',
36-
type: 'string',
37-
default: ''
38-
})
39-
.epilog(utils.ipfsPathHelp)
40-
.demandCommand(1)
41-
.fail((msg, err, yargs) => {
42-
if (err) {
43-
throw err // preserve stack
44-
}
4546

46-
if (args.length > 0) {
47-
print(msg)
48-
}
47+
// Function to get hold of a singleton ipfs instance
48+
const getIpfs = utils.singleton(cb => utils.getIPFS(yargs.argv, cb))
4949

50-
yargs.showHelp()
51-
})
50+
// add MFS (Files API) commands
51+
mfs(cli)
5252

53-
// Need to skip to avoid locking as these commands
54-
// don't require a daemon
55-
if (args[0] === 'daemon' || args[0] === 'init') {
5653
cli
54+
.commandDir('commands')
5755
.help()
5856
.strict()
5957
.completion()
60-
.command(require('./commands/daemon'))
61-
.command(require('./commands/init'))
6258

63-
new YargsPromise(cli).parse(args)
64-
.then(({ data }) => {
65-
if (data) print(data)
66-
})
67-
} else {
68-
// here we have to make a separate yargs instance with
69-
// only the `api` option because we need this before doing
70-
// the final yargs parse where the command handler is invoked..
71-
yargs().option('api').parse(process.argv, (err, argv, output) => {
72-
if (err) {
73-
throw err
59+
let exitCode = 0
60+
61+
try {
62+
const { data } = await new YargsPromise(cli, { getIpfs }).parse(args)
63+
if (data) print(data)
64+
} catch (err) {
65+
debug(err)
66+
67+
// the argument can have a different shape depending on where the error came from
68+
if (err.message) {
69+
print(err.message)
70+
} else if (err.error && err.error.message) {
71+
print(err.error.message)
72+
} else {
73+
print('Unknown error, please re-run the command with DEBUG=ipfs:cli to see debug output')
7474
}
7575

76-
utils.getIPFS(argv, (err, ipfs, cleanup) => {
77-
if (err) {
78-
throw err
76+
exitCode = 1
77+
} finally {
78+
// If an IPFS instance was used in the handler then clean it up here
79+
if (getIpfs.instance) {
80+
try {
81+
const cleanup = getIpfs.rest[0]
82+
await cleanup()
83+
} catch (err) {
84+
debug(err)
85+
exitCode = 1
7986
}
87+
}
88+
}
8089

81-
// add MFS (Files API) commands
82-
mfs(cli)
83-
84-
cli
85-
.commandDir('commands')
86-
.help()
87-
.strict()
88-
.completion()
89-
90-
let exitCode = 0
91-
92-
const parser = new YargsPromise(cli, { ipfs })
93-
parser.parse(args)
94-
.then(({ data, argv }) => {
95-
if (data) {
96-
print(data)
97-
}
98-
})
99-
.catch((arg) => {
100-
debug(arg)
101-
102-
// the argument can have a different shape depending on where the error came from
103-
if (arg.message) {
104-
print(arg.message)
105-
} else if (arg.error && arg.error.message) {
106-
print(arg.error.message)
107-
} else {
108-
print('Unknown error, please re-run the command with DEBUG=ipfs:cli to see debug output')
109-
}
110-
111-
exitCode = 1
112-
})
113-
.then(() => cleanup())
114-
.catch(() => {})
115-
.then(() => {
116-
if (exitCode !== 0) {
117-
process.exit(exitCode)
118-
}
119-
})
120-
})
121-
})
90+
if (exitCode) {
91+
process.exit(exitCode)
92+
}
12293
}
94+
95+
main(process.argv.slice(2))

src/cli/commands/add.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ module.exports = {
145145

146146
handler (argv) {
147147
argv.resolve((async () => {
148-
const { ipfs } = argv
148+
const ipfs = await argv.getIpfs()
149149
const options = {
150150
strategy: argv.trickle ? 'trickle' : 'balanced',
151151
shardSplitThreshold: argv.enableShardingExperiment

src/cli/commands/bitswap/stat.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ module.exports = {
1717
}
1818
},
1919

20-
handler ({ ipfs, cidBase, resolve }) {
20+
handler ({ getIpfs, cidBase, resolve }) {
2121
resolve((async () => {
22+
const ipfs = await getIpfs()
2223
const stats = await ipfs.bitswap.stat()
2324
stats.wantlist = stats.wantlist.map(k => cidToString(k['/'], { base: cidBase, upgrade: false }))
2425
stats.peers = stats.peers || []

src/cli/commands/bitswap/unwant.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ module.exports = {
2121
choices: multibase.names
2222
}
2323
},
24-
handler ({ ipfs, key, cidBase, resolve }) {
24+
handler ({ getIpfs, key, cidBase, resolve }) {
2525
resolve((async () => {
26+
const ipfs = await getIpfs()
2627
await ipfs.bitswap.unwant(key)
2728
print(`Key ${cidToString(key, { base: cidBase, upgrade: false })} removed from wantlist`)
2829
})())

src/cli/commands/bitswap/wantlist.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ module.exports = {
2222
}
2323
},
2424

25-
handler ({ ipfs, peer, cidBase, resolve }) {
25+
handler ({ getIpfs, peer, cidBase, resolve }) {
2626
resolve((async () => {
27+
const ipfs = await getIpfs()
2728
const list = await ipfs.bitswap.wantlist(peer)
2829
list.Keys.forEach(k => print(cidToString(k['/'], { base: cidBase, upgrade: false })))
2930
})())

src/cli/commands/block/get.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ module.exports = {
99

1010
builder: {},
1111

12-
handler ({ ipfs, key, resolve }) {
12+
handler ({ getIpfs, key, resolve }) {
1313
resolve((async () => {
14+
const ipfs = await getIpfs()
1415
const block = await ipfs.block.get(key)
1516
if (block) {
1617
print(block.data, false)

src/cli/commands/block/put.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ module.exports = {
5252
})
5353
}
5454

55-
const { cid } = await argv.ipfs.block.put(data, argv)
55+
const ipfs = await argv.getIpfs()
56+
const { cid } = await ipfs.block.put(data, argv)
5657
print(cidToString(cid, { base: argv.cidBase }))
5758
})())
5859
}

src/cli/commands/block/rm.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ module.exports = {
99

1010
builder: {},
1111

12-
handler ({ ipfs, key, resolve }) {
12+
handler ({ getIpfs, key, resolve }) {
1313
resolve((async () => {
1414
if (isDaemonOn()) {
1515
// TODO implement this once `js-ipfs-http-client` supports it
1616
throw new Error('rm block with daemon running is not yet implemented')
1717
}
1818

19+
const ipfs = await getIpfs()
1920
await ipfs.block.rm(key)
2021
print('removed ' + key)
2122
})())

src/cli/commands/block/stat.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ module.exports = {
1717
}
1818
},
1919

20-
handler ({ ipfs, key, cidBase, resolve }) {
20+
handler ({ getIpfs, key, cidBase, resolve }) {
2121
resolve((async () => {
22+
const ipfs = await getIpfs()
2223
const stats = await ipfs.block.stat(key)
2324
print('Key: ' + cidToString(stats.key, { base: cidBase }))
2425
print('Size: ' + stats.size)

src/cli/commands/bootstrap/add.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@ module.exports = {
1717

1818
handler (argv) {
1919
argv.resolve((async () => {
20-
const list = await argv.ipfs.bootstrap.add(argv.peer, { default: argv.default })
20+
const ipfs = await argv.getIpfs()
21+
const list = await ipfs.bootstrap.add(argv.peer, { default: argv.default })
2122
list.Peers.forEach((peer) => print(peer))
2223
})())
2324
}

src/cli/commands/bootstrap/list.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ module.exports = {
1111

1212
handler (argv) {
1313
argv.resolve((async () => {
14-
const list = await argv.ipfs.bootstrap.list()
14+
const ipfs = await argv.getIpfs()
15+
const list = await ipfs.bootstrap.list()
1516
list.Peers.forEach((node) => print(node))
1617
})())
1718
}

src/cli/commands/bootstrap/rm.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ module.exports = {
2020

2121
handler (argv) {
2222
argv.resolve((async () => {
23-
const list = await argv.ipfs.bootstrap.rm(argv.peer, { all: argv.all })
23+
const ipfs = await argv.getIpfs()
24+
const list = await ipfs.bootstrap.rm(argv.peer, { all: argv.all })
2425
list.Peers.forEach((peer) => print(peer))
2526
})())
2627
}

src/cli/commands/cat.js

+11-7
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,18 @@ module.exports = {
1818
}
1919
},
2020

21-
handler ({ ipfs, ipfsPath, offset, length, resolve }) {
22-
resolve(new Promise((resolve, reject) => {
23-
const stream = ipfs.catReadableStream(ipfsPath, { offset, length })
21+
handler ({ getIpfs, ipfsPath, offset, length, resolve }) {
22+
resolve((async () => {
23+
const ipfs = await getIpfs()
2424

25-
stream.on('error', reject)
26-
stream.on('end', resolve)
25+
return new Promise((resolve, reject) => {
26+
const stream = ipfs.catReadableStream(ipfsPath, { offset, length })
2727

28-
stream.pipe(process.stdout)
29-
}))
28+
stream.on('error', reject)
29+
stream.on('end', resolve)
30+
31+
stream.pipe(process.stdout)
32+
})
33+
})())
3034
}
3135
}

src/cli/commands/config.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ module.exports = {
3030
}
3131
argv._handled = true
3232

33-
const { bool, json, key } = argv
33+
const { bool, json, key, getIpfs } = argv
34+
const ipfs = await getIpfs()
3435
let value = argv.value
3536

3637
if (!value) {
3738
// Get the value of a given key
38-
value = await argv.ipfs.config.get(key)
39+
value = await ipfs.config.get(key)
3940

4041
if (typeof value === 'object') {
4142
print(JSON.stringify(value, null, 2))
@@ -55,7 +56,7 @@ module.exports = {
5556
}
5657
}
5758

58-
await argv.ipfs.config.set(key, value)
59+
await ipfs.config.set(key, value)
5960
}
6061
})())
6162
}

src/cli/commands/config/edit.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ module.exports = {
2424
throw new Error('ENV variable $EDITOR not set')
2525
}
2626

27+
const ipfs = await argv.getIpfs()
28+
2729
async function getConfig () {
2830
try {
29-
await argv.ipfs.config.get()
31+
await ipfs.config.get()
3032
} catch (err) {
3133
throw new Error('failed to get the config')
3234
}
@@ -76,7 +78,7 @@ module.exports = {
7678
? Buffer.from(JSON.stringify(config)) : config
7779

7880
try {
79-
await argv.ipfs.config.replace(config)
81+
await ipfs.config.replace(config)
8082
} catch (err) {
8183
throw new Error('failed to save the config')
8284
}

0 commit comments

Comments
 (0)