diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..de3e3780 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,23 @@ +environment: + matrix: + - nodejs_version: "6" + - nodejs_version: "8" + +# cache: +# - node_modules + +platform: + - x64 + +install: + - ps: Install-Product node $env:nodejs_version $env:platform + - npm install + +test_script: + - node --version + - npm --version + - npm test + +build: off + +version: "{build}" diff --git a/README.md b/README.md index de87c919..6e972541 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,21 @@ If you need want to use an existing ipfs installation you can set `$IPFS_EXEC=/p For more details see https://ipfs.github.io/js-ipfsd-ctl/. +### Packaging + +`ipfsd-ctl` can be packaged in Electron applications, but the ipfs binary +has to be excluded from asar (Electron Archives), +[read more about unpack files from asar](https://electron.atom.io/docs/tutorial/application-packaging/#adding-unpacked-files-in-asar-archive). +`ipfsd-ctl` will try to detect if used from within an `app.asar` archive +and tries to resolve ipfs from `app.asar.unpacked`. The ipfs binary is part of +the `go-ipfs-dep` module. + +```bash +electron-packager ./ --asar.unpackDir=node_modules/go-ipfs-dep +``` + +See [electron asar example](https://github.com/ipfs/js-ipfsd-ctl/tree/master/examples/electron-asar/) + ## Contribute Feel free to join in. All welcome. Open an [issue](https://github.com/ipfs/js-ipfsd-ctl/issues)! diff --git a/examples/disposableApi.js b/examples/disposableApi.js index c232a875..0889d698 100644 --- a/examples/disposableApi.js +++ b/examples/disposableApi.js @@ -1,3 +1,4 @@ +/* eslint no-console: 0 */ 'use strict' // Start a disposable node, and get access to the api diff --git a/examples/electron-asar/.gitignore b/examples/electron-asar/.gitignore new file mode 100644 index 00000000..6b1514c3 --- /dev/null +++ b/examples/electron-asar/.gitignore @@ -0,0 +1,2 @@ +node_modules +test-ipfs-app* diff --git a/examples/electron-asar/app.js b/examples/electron-asar/app.js new file mode 100644 index 00000000..61fbb99c --- /dev/null +++ b/examples/electron-asar/app.js @@ -0,0 +1,34 @@ +/* eslint no-console: 0 */ +'use strict' + +const { app, ipcMain, BrowserWindow } = require('electron') +const ipfsd = require('ipfsd-ctl') + +app.on('ready', () => { + const win = new BrowserWindow({ + title: 'loading' + }) + win.loadURL(`file://${app.getAppPath()}/public/index.html`) +}) + +ipcMain.on('start', ({ sender }) => { + console.log('starting disposable IPFS') + sender.send('message', 'starting disposable IPFS') + + ipfsd.disposableApi((err, ipfs) => { + if (err) { + sender.send('error', err) + throw err + } + console.log('get id') + sender.send('message', 'get id') + ipfs.id(function (err, id) { + if (err) { + sender.send('error', err) + throw err + } + console.log('got id', id) + sender.send('id', JSON.stringify(id)) + }) + }) +}) diff --git a/examples/electron-asar/package.json b/examples/electron-asar/package.json new file mode 100644 index 00000000..5be4c215 --- /dev/null +++ b/examples/electron-asar/package.json @@ -0,0 +1,16 @@ +{ + "name": "test-ipfs-app", + "private": true, + "main": "./app.js", + "dependencies": { + "ipfsd-ctl": "*" + }, + "devDependencies": { + "electron": "^1.7.6", + "electron-packager": "^9.0.0" + }, + "scripts": { + "start": "electron .", + "package": "electron-packager ./ --overwrite --asar.unpackDir=node_modules/go-ipfs-dep" + } +} diff --git a/examples/electron-asar/public/index.html b/examples/electron-asar/public/index.html new file mode 100644 index 00000000..6a9f24f1 --- /dev/null +++ b/examples/electron-asar/public/index.html @@ -0,0 +1,21 @@ + +loading + + diff --git a/examples/electron-asar/readme.md b/examples/electron-asar/readme.md new file mode 100644 index 00000000..84bca385 --- /dev/null +++ b/examples/electron-asar/readme.md @@ -0,0 +1,6 @@ +# Packaging + +```bash +npm install +npm run package +``` diff --git a/examples/id.js b/examples/id.js index cd079d8e..68c61b59 100644 --- a/examples/id.js +++ b/examples/id.js @@ -1,3 +1,4 @@ +/* eslint no-console: 0 */ 'use strict' var ipfsd = require('../') diff --git a/examples/local.js b/examples/local.js index 49621d4b..79cdfe97 100644 --- a/examples/local.js +++ b/examples/local.js @@ -1,3 +1,4 @@ +/* eslint no-console: 0 */ 'use strict' var ipfsd = require('../') diff --git a/src/node.js b/src/node.js index 3bad47f4..9c409767 100644 --- a/src/node.js +++ b/src/node.js @@ -19,7 +19,15 @@ const GRACE_PERIOD = 7500 // amount of ms to wait before sigkill function findIpfsExecutable () { const rootPath = process.env.testpath ? process.env.testpath : __dirname - const appRoot = path.join(rootPath, '..') + let appRoot = path.join(rootPath, '..') + // If inside .asar try to load from .asar.unpacked + // this only works if asar was built with + // asar --unpack-dir=node_modules/go-ipfs-dep/* (not tested) + // or + // electron-packager ./ --asar.unpackDir=node_modules/go-ipfs-dep + if (appRoot.includes(`.asar${path.sep}`)) { + appRoot = appRoot.replace(`.asar${path.sep}`, `.asar.unpacked${path.sep}`) + } const depPath = path.join('go-ipfs-dep', 'go-ipfs', 'ipfs') const npm3Path = path.join(appRoot, '../', depPath) const npm2Path = path.join(appRoot, 'node_modules', depPath) diff --git a/test/index.spec.js b/test/index.spec.js index ebf06960..2bb5020d 100644 --- a/test/index.spec.js +++ b/test/index.spec.js @@ -20,6 +20,8 @@ const os = require('os') const exec = require('../src/exec') const ipfsd = require('../src') +const isWindows = os.platform() === 'win32' + describe('ipfs executable path', () => { let Node @@ -37,7 +39,7 @@ describe('ipfs executable path', () => { Node = require('../src/node.js') var node = new Node() expect(node.exec) - .to.eql('/tmp/ipfsd-ctl-test/node_modules/go-ipfs-dep/go-ipfs/ipfs') + .to.eql(path.normalize('/tmp/ipfsd-ctl-test/node_modules/go-ipfs-dep/go-ipfs/ipfs')) rimraf('/tmp/ipfsd-ctl-test', done) }) }) @@ -58,7 +60,7 @@ describe('ipfs executable path', () => { expect( node.exec ).to.be.eql( - '/tmp/ipfsd-ctl-test/node_modules/ipfsd-ctl/node_modules/go-ipfs-dep/go-ipfs/ipfs' + path.normalize('/tmp/ipfsd-ctl-test/node_modules/ipfsd-ctl/node_modules/go-ipfs-dep/go-ipfs/ipfs') ) rimraf('/tmp/ipfsd-ctl-test', done) }) @@ -384,16 +386,46 @@ describe('daemons', () => { }) }) + // skip on windows for now + // https://github.com/ipfs/js-ipfsd-ctl/pull/155#issuecomment-326970190 + // fails on windows see https://github.com/ipfs/js-ipfs-api/issues/408 + if (isWindows) { + it.skip('uses the correct ipfs-api') + return // does not continue this test on win + } + // NOTE: if you change ./fixtures, the hash will need to be changed it('uses the correct ipfs-api', (done) => { ipfs.util.addFromFs(path.join(__dirname, 'fixtures/'), { recursive: true }, (err, res) => { if (err) throw err const added = res[res.length - 1] + + // Temporary: Need to see what is going on on windows + expect(res).to.deep.equal([ + { + path: 'fixtures/test.txt', + hash: 'Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD', + size: 19 + }, + { + path: 'fixtures', + hash: 'QmXkiTdnfRJjiQREtF5dWf2X4V9awNHQSn9YGofwVY4qUU', + size: 73 + } + ]) + + expect(res.length).to.equal(2) + expect(added).to.have.property('path', 'fixtures') expect(added).to.have.property( 'hash', 'QmXkiTdnfRJjiQREtF5dWf2X4V9awNHQSn9YGofwVY4qUU' ) + expect(res[0]).to.have.property('path', 'fixtures/test.txt') + expect(res[0]).to.have.property( + 'hash', + 'Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD' + ) done() }) }) @@ -404,11 +436,15 @@ describe('daemons', () => { const dir = `${os.tmpdir()}/tmp-${Date.now() + '-' + Math.random().toString(36)}` const check = (cb) => { - if (fs.existsSync(path.join(dir, 'repo.lock'))) { - cb(new Error('repo.lock not removed')) - } - if (fs.existsSync(path.join(dir, 'api'))) { - cb(new Error('api file not removed')) + // skip on windows + // https://github.com/ipfs/js-ipfsd-ctl/pull/155#issuecomment-326983530 + if (!isWindows) { + if (fs.existsSync(path.join(dir, 'repo.lock'))) { + cb(new Error('repo.lock not removed')) + } + if (fs.existsSync(path.join(dir, 'api'))) { + cb(new Error('api file not removed')) + } } cb() }