Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New API + use bin-wrapper (bundle in all arch without any concern) + global install #89

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
0523e82
added bin-wrapper dep
JGAntunes Jul 5, 2016
a83f657
base bin wrapper structure defined
JGAntunes Jul 7, 2016
6977aa0
binary wrapper finally working
JGAntunes Jul 12, 2016
503aa15
all tests passing
JGAntunes Jul 12, 2016
258fbcb
parse config async, bump version to 0.4.1
JGAntunes Jul 13, 2016
cf54866
use class
JGAntunes Jul 18, 2016
0de6a35
parse config sync
JGAntunes Jul 18, 2016
c7cadba
use a separate config file
JGAntunes Jul 18, 2016
e6b4f77
updated npmignore and gitignore files with vendor folder
JGAntunes Jul 18, 2016
331deab
use chai for test assertion
JGAntunes Jul 18, 2016
67bee6a
move config to src
JGAntunes Jul 18, 2016
a837058
tests to check bin state
JGAntunes Jul 20, 2016
e738f0d
fix shutdown logic
JGAntunes Jul 20, 2016
e92eaf4
fix examples
JGAntunes Jul 20, 2016
97a305d
add more config related tests
JGAntunes Jul 21, 2016
ce0ef0e
bump ipfs dist version & some minor changes
JGAntunes Jul 28, 2016
88054c8
replaced subcomandante by execa, bumped aegir version & bumped bin rc…
JGAntunes Aug 19, 2016
3253762
Update README.md
JGAntunes Aug 19, 2016
a4a3d2b
improved exec source & get config parses response & new replace confi…
JGAntunes Aug 20, 2016
0b1b625
solving linting issues on tests
JGAntunes Aug 20, 2016
289d65b
remove default params
JGAntunes Aug 20, 2016
e95741c
bump aegir version
JGAntunes Aug 21, 2016
05fb3ef
updated classes to match new API & solved a couple of bugs
JGAntunes Sep 14, 2016
c03c3ed
bump aegir to 8.1.0
JGAntunes Sep 14, 2016
fe852d4
refactor binary wrapper & added postinstall hook
JGAntunes Sep 14, 2016
1214340
typo on README
JGAntunes Sep 14, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,6 @@ node_modules

lib
dist

# Bin default dir
vendor
3 changes: 3 additions & 0 deletions .npmignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ build
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# Bin default dir
vendor
57 changes: 50 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Install:
npm install --save ipfsd-ctl
```

__The current go ipfs version used is v0.4.3-rc3__

## Usage

IPFS daemons are already easy to start and stop, but this module is here to do it from javascript itself.
Expand All @@ -36,17 +38,58 @@ IPFS daemons are already easy to start and stop, but this module is here to do i
// IPFS_PATH will point to /tmp/ipfs_***** and will be
// cleaned up when the process exits.

var ipfsd = require('ipfsd-ctl')

ipfsd.disposableApi(function (err, ipfs) {
ipfs.id(function (err, id) {
console.log(id)
process.kill()
const ipfsd = require('ipfsd-ctl')

ipfsd.create((err, node) => {
if (err) throw err
node.startDaemon((err) => {
if (err) throw err
const ipfs = node.apiCtl()
ipfs.id((err, id) => {
console.log(id)
process.kill()
})
})
})
```

If you need want to use an existing ipfs installation you can set `$IPFS_EXEC=/path/to/ipfs` to ensure it uses that.
The daemon controller safely spawns the node for you and exposes you an ipfs API client through `node.apiCtl()`. __If the parent process exits, the daemon will also be killed__ ensuring that the daemon isn't left hanging.

This module works by downloading the binary once, on first use, if it detects that no current binary is available to use. So keep in mind that the first command executed might throw in some overhead.

If you want to use an existing ipfs installation you can set `$IPFS_EXEC=/path/to/ipfs` to ensure it uses that.

## API

## ipfsd

#### ipfsd.local(path, done)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should be methods of the node object

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The version and local methods? @diasdavid


#### ipfsd.create(opts, done)

## IPFSNode(path, opts, disposable)

#### node.init(initOpts, done)

#### node.shutdown(done)

#### node.startDaemon(done)

#### node.stopDaemon(done)

#### node.apiCtl(done)

#### node.daemonPid()

#### node.getConfig(key, done)

#### node.setConfig(key, value, done)

#### node.replaceConf(file, done)

#### node.version(done)

#### node.subprocess

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dignifiedquire @haadcode please review the above, for methods you would like to see on ipfsd-ctl :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be honest, I would like to use:
ipfsd.start()
ipfs.stop()

and that's it. start() would automatically init a repo if one doesn't exist at the given path.

However, I reckon that this would require a bigger re-think of the library and as I don't want to bikeshed it too much, I'm happy to use the current API if that's an easier way to bring this to a working state.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@haadcode now it is the time to bikeshed, since we have the opportunity to make a new version of ipfsd-ctl. Changing API is always harder as adoption increases, because that means breaking the interface for more people and harder to migrate. You are one of the primary users of this module and so, your input is super valuable when it comes to its API.

## Contribute

Expand Down
11 changes: 11 additions & 0 deletions bin/check-binary
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env node
'use strict'

console.log('Checking IPFS binary...')
const binary = require('../src/binary')()
const version = require('../src/config').version

binary.check((err) => {
if (err) throw err
console.log(`IPFS binary version ${version} working`)
})
2 changes: 1 addition & 1 deletion examples/id.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

var ipfsd = require('../index.js')
var ipfsd = require('../src')

ipfsd.disposableApi(function (err, ipfs) {
if (err) throw err
Expand Down
2 changes: 1 addition & 1 deletion examples/local.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

var ipfsd = require('../index.js')
var ipfsd = require('../src')

// opens an api connection to local running ipfs node

Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"main": "lib/index.js",
"jsxnext:main": "src/index.js",
"scripts": {
"postinstall": "./bin/check-binary",
"lint": "aegir-lint",
"coverage": "aegir-coverage",
"test": "aegir-test --env node",
Expand Down Expand Up @@ -43,17 +44,17 @@
],
"license": "MIT",
"dependencies": {
"go-ipfs-dep": "0.4.1",
"ipfs-api": "^4.1.0",
"multiaddr": "^2.0.0",
"rimraf": "^2.4.5",
"run-series": "^1.1.4",
"shutdown": "^0.2.4",
"subcomandante": "^1.0.5"
"execa": "git://github.com/sindresorhus/execa.git#master",
"bin-wrapper": "^3.0.2"
},
"devDependencies": {
"aegir": "^8.0.1",
"mkdirp": "^0.5.1",
"aegir": "^8.1.0",
"chai": "^3.5.0",
"pre-commit": "^1.1.2"
},
"repository": {
Expand All @@ -68,4 +69,4 @@
"example": "examples",
"test": "test"
}
}
}
36 changes: 36 additions & 0 deletions src/binary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
'use strict'

const BinWrapper = require('bin-wrapper')
const config = require('./config')

const binWrapper = new BinWrapper()
.src(config.baseUrl + 'darwin-386.tar.gz', 'darwin', 'ia32')
.src(config.baseUrl + 'darwin-amd64.tar.gz', 'darwin', 'x64')
.src(config.baseUrl + 'freebsd-amd64.tar.gz', 'freebsd', 'x64')
.src(config.baseUrl + 'linux-386.tar.gz', 'linux', 'ia32')
.src(config.baseUrl + 'linux-amd64.tar.gz', 'linux', 'x64')
.src(config.baseUrl + 'linux-arm.tar.gz', 'linux', 'arm')
.src(config.baseUrl + 'windows-386.tar.gz', 'win32', 'ia32')
.src(config.baseUrl + 'windows-amd64.tar.gz', 'win32', 'x64')
.use(process.platform === 'win32' ? 'ipfs.exe' : 'ipfs')

function Binary (execPath) {
// Set dest path for the exec file
binWrapper.dest(process.env.IPFS_EXEC || execPath || config.defaultExecPath)
return {
path: () => binWrapper.path(),
// Has the binary been checked?
checked: false,
// Check that the binary exists and works
check (cb) {
if (this.checked) return cb()
binWrapper.run(['version'], (err) => {
// The binary is ok if no error poped up
this.checked = !err
return cb(err)
})
}
}
}

module.exports = Binary
14 changes: 14 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict'

const path = require('path')

const config = {
version: 'v0.4.3-rc4', // ipfs dist version
defaultExecPath: path.join(__dirname, '..', 'vendor'), // default path for the downloaded exec
gracePeriod: 7500 // amount of ms to wait before sigkill
}

const version = config.version
config.baseUrl = `https://dist.ipfs.io/go-ipfs/${version}/go-ipfs_${version}_`

module.exports = config
57 changes: 57 additions & 0 deletions src/exec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use strict'

const execa = require('execa')

function exec (cmd, args, opts, handlers) {
opts = opts || {}
let err = ''
let result = ''
let callback
// Handy method if we just want the result and err returned in a callback
if (typeof handlers === 'function') {
callback = handlers
handlers = {
error: callback,
data: (data) => {
result += data
},
done: () => {
if (err) return callback(new Error(err))
callback(null, result)
}
}
}

// The listeners that will actually be set on the process
const listeners = {
data: handlers.data,
error: (data) => {
err += data
},
done: (code) => {
if (code !== 0) {
return handlers.error(
new Error(`non-zero exit code ${code}\n
while running: ${cmd} ${args.join(' ')}\n\n
${err}`)
)
}
if (handlers.done) handlers.done()
}
}

const command = execa(cmd, args, opts)

if (listeners.data) command.stdout.on('data', listeners.data)

command.stderr.on('data', listeners.error)

// If command fails to execute return directly to the handler
command.on('error', handlers.error)

command.on('close', listeners.done)

return command
}

module.exports = exec
42 changes: 17 additions & 25 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,15 @@ function tempDir () {
return join(os.tmpdir(), `ipfs_${String(Math.random()).substr(2)}`)
}

const defaultOptions = {
'Addresses.Swarm': ['/ip4/0.0.0.0/tcp/0'],
'Addresses.Gateway': '',
'Addresses.API': '/ip4/127.0.0.1/tcp/0',
disposable: true,
init: true
}

module.exports = {
version (done) {
(new Node()).version(done)
},
local (path, done) {
if (!done) {
done = path
Expand All @@ -24,36 +29,23 @@ module.exports = {
done(null, new Node(path))
})
},
disposableApi (opts, done) {
create (opts, done) {
if (typeof opts === 'function') {
done = opts
opts = {}
}
this.disposable(opts, (err, node) => {
if (err) return done(err)
node.startDaemon(done)
})
},
disposable (opts, done) {
if (typeof opts === 'function') {
done = opts
opts = {}
}
opts['Addresses.Swarm'] = ['/ip4/0.0.0.0/tcp/0']
opts['Addresses.Gateway'] = ''
opts['Addresses.API'] = '/ip4/127.0.0.1/tcp/0'

if (opts.apiAddr) {
opts['Addresses.API'] = opts.apiAddr
}
let options = {}
Object.assign(options, defaultOptions, opts || {})

if (opts.gatewayAddr) {
opts['Addresses.Gateway'] = opts.gatewayAddr
}
const repoPath = options.repoPath || tempDir()
const disposable = options.disposable
delete options.disposable
delete options.repoPath

const node = new Node(opts.repoPath || tempDir(), opts, true)
const node = new Node(repoPath, options, disposable)

if (typeof opts.init === 'boolean' && opts.init === false) {
if (typeof options.init === 'boolean' && options.init === false) {
process.nextTick(() => {
done(null, node)
})
Expand Down
Loading