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

Commit 1d12ffb

Browse files
AuHauAlan Shaw
authored and
Alan Shaw
committed
feat: integrate ipfs-repo-migrations tool (#2527)
Resolves: #1115 Supersedes: #2044 Depends on: * [x] ipfs/js-ipfs-repo#202
1 parent b852460 commit 1d12ffb

40 files changed

+548
-28
lines changed

.aegir.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const preloadNode = MockPreloadNode.createNode()
1111
const echoServer = EchoServer.createServer()
1212

1313
module.exports = {
14-
bundlesize: { maxSize: '685kB' },
14+
bundlesize: { maxSize: '650kB' },
1515
webpack: {
1616
resolve: {
1717
mainFields: ['browser', 'main'],

README.md

+12
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,18 @@ Example:
284284
const node = await IPFS.create({ repo: '/var/ipfs/data' })
285285
```
286286

287+
##### `options.repoAutoMigrate`
288+
289+
| Type | Default |
290+
|------|---------|
291+
| boolean | `true` |
292+
293+
`js-ipfs` comes bundled with a tool that automatically migrates your IPFS repository when a new version is available.
294+
295+
**For apps that build on top of `js-ipfs` and run in the browser environment, be aware that disabling automatic
296+
migrations leaves the user with no way to run the migrations because there is no CLI in the browser. In such
297+
a case, you should provide a way to trigger migrations manually.**
298+
287299
##### `options.init`
288300

289301
| Type | Default |

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@
104104
"ipfs-http-response": "~0.4.0",
105105
"ipfs-mfs": "^0.13.0",
106106
"ipfs-multipart": "^0.2.0",
107-
"ipfs-repo": "^0.28.1",
107+
"ipfs-repo": "^0.29.0",
108108
"ipfs-unixfs": "~0.1.16",
109109
"ipfs-unixfs-exporter": "^0.38.0",
110110
"ipfs-unixfs-importer": "^0.40.0",

src/cli/bin.js

+7
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ if (!semver.satisfies(process.versions.node, pkg.engines.node)) {
2626
const YargsPromise = require('yargs-promise')
2727
const updateNotifier = require('update-notifier')
2828
const debug = require('debug')('ipfs:cli')
29+
const { errors: { InvalidRepoVersionError } } = require('ipfs-repo')
2930
const parser = require('./parser')
3031
const commandAlias = require('./command-alias')
3132
const { print } = require('./utils')
@@ -50,13 +51,19 @@ cli
5051
})
5152
.catch(({ error, argv }) => {
5253
getIpfs = argv && argv.getIpfs
54+
55+
if (error.code === InvalidRepoVersionError.code) {
56+
error.message = 'Incompatible repo version. Migration needed. Pass --migrate for automatic migration'
57+
}
58+
5359
if (error.message) {
5460
print(error.message)
5561
debug(error)
5662
} else {
5763
print('Unknown error, please re-run the command with DEBUG=ipfs:cli to see debug output')
5864
debug(error)
5965
}
66+
6067
process.exit(1)
6168
})
6269
.finally(() => {

src/cli/commands/daemon.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ module.exports = {
7373
config,
7474
silent: argv.silent,
7575
repo: process.env.IPFS_PATH,
76+
repoAutoMigrate: argv.migrate,
7677
offline: argv.offline,
7778
pass: argv.pass,
7879
preload: { enabled: argv.enablePreload },
@@ -96,10 +97,8 @@ module.exports = {
9697
print(`Web UI available at ${toUri(apiServer.info.ma)}/webui`)
9798
})
9899
} catch (err) {
99-
if (err.code === 'ENOENT' && err.message.match(/uninitialized/i)) {
100-
print('Error: no initialized ipfs repo found in ' + repoPath)
101-
print('please run: jsipfs init')
102-
process.exit(1)
100+
if (err.code === 'ERR_REPO_NOT_INITIALIZED' || err.message.match(/uninitialized/i)) {
101+
err.message = 'no initialized ipfs repo found in ' + repoPath + '\nplease run: jsipfs init'
103102
}
104103
throw err
105104
}

src/cli/daemon.js

+2-11
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,8 @@ class Daemon {
5454
}
5555

5656
// start the daemon
57-
const ipfsOpts = Object.assign({ }, { init: true, start: true, libp2p }, this._options)
58-
const ipfs = new IPFS(ipfsOpts)
59-
60-
await new Promise((resolve, reject) => {
61-
ipfs.once('error', err => {
62-
this._log('error starting core', err)
63-
err.code = 'ENOENT'
64-
reject(err)
65-
})
66-
ipfs.once('start', resolve)
67-
})
57+
const ipfsOpts = Object.assign({}, { init: true, start: true, libp2p }, this._options)
58+
const ipfs = await IPFS.create(ipfsOpts)
6859

6960
this._ipfs = ipfs
7061

src/cli/parser.js

+5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ const parser = yargs
1919
type: 'string',
2020
default: ''
2121
})
22+
.option('migrate', {
23+
desc: 'Enable/disable automatic repo migrations',
24+
type: 'boolean',
25+
default: false
26+
})
2227
.epilog(utils.ipfsPathHelp)
2328
.demandCommand(1)
2429
.fail((msg, err, yargs) => {

src/cli/utils.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ const fs = require('fs')
44
const os = require('os')
55
const multiaddr = require('multiaddr')
66
const path = require('path')
7-
const debug = require('debug')
8-
const log = debug('cli')
9-
log.error = debug('cli:error')
7+
const log = require('debug')('ipfs:cli:utils')
108
const Progress = require('progress')
119
const byteman = require('byteman')
1210
const promisify = require('promisify-es6')
@@ -47,6 +45,7 @@ exports.getIPFS = (argv, callback) => {
4745
const IPFS = require('../core')
4846
const node = new IPFS({
4947
silent: argv.silent,
48+
repoAutoMigrate: argv.migrate,
5049
repo: exports.getRepoPath(),
5150
init: false,
5251
start: false,
@@ -60,7 +59,7 @@ exports.getIPFS = (argv, callback) => {
6059
})
6160

6261
node.on('error', (err) => {
63-
throw err
62+
callback(err)
6463
})
6564

6665
node.once('ready', () => {

src/core/config.js

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const s = superstruct({
2828
const configSchema = s({
2929
repo: optional(s('object|string')),
3030
repoOwner: 'boolean?',
31+
repoAutoMigrate: 'boolean?',
3132
preload: s({
3233
enabled: 'boolean?',
3334
addresses: optional(s(['multiaddr'])),

src/core/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ class IPFS extends EventEmitter {
7070

7171
if (typeof options.repo === 'string' ||
7272
options.repo === undefined) {
73-
this._repo = defaultRepo(options.repo)
73+
this._repo = defaultRepo(options)
7474
} else {
7575
this._repo = options.repo
7676
}

src/core/runtime/repo-browser.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const IPFSRepo = require('ipfs-repo')
44

5-
module.exports = (dir) => {
6-
const repoPath = dir || 'ipfs'
7-
return new IPFSRepo(repoPath)
5+
module.exports = (options) => {
6+
const repoPath = options.repo || 'ipfs'
7+
return new IPFSRepo(repoPath, { autoMigrate: options.repoAutoMigrate })
88
}

src/core/runtime/repo-nodejs.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ const os = require('os')
44
const IPFSRepo = require('ipfs-repo')
55
const path = require('path')
66

7-
module.exports = (dir) => {
8-
const repoPath = dir || path.join(os.homedir(), '.jsipfs')
7+
module.exports = (options) => {
8+
const repoPath = options.repo || path.join(os.homedir(), '.jsipfs')
99

10-
return new IPFSRepo(repoPath)
10+
return new IPFSRepo(repoPath, { autoMigrate: options.repoAutoMigrate })
1111
}

test/cli/general.js

+89
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
/* eslint-env mocha */
22
'use strict'
33

4+
const os = require('os')
5+
const fs = require('fs').promises
6+
const path = require('path')
7+
const hat = require('hat')
48
const { expect } = require('interface-ipfs-core/src/utils/mocha')
9+
const { repoVersion } = require('ipfs-repo')
10+
const promisify = require('promisify-es6')
11+
const ncp = promisify(require('ncp').ncp)
512
const runOnAndOff = require('../utils/on-and-off')
13+
const ipfsExec = require('../utils/ipfs-exec')
14+
const clean = require('../utils/clean')
615

716
describe('general cli options', () => runOnAndOff.off((thing) => {
817
it('should handle --silent flag', async () => {
@@ -17,3 +26,83 @@ describe('general cli options', () => runOnAndOff.off((thing) => {
1726
expect(out).to.include('again')
1827
})
1928
}))
29+
30+
describe('--migrate', () => {
31+
let ipfs, repoPath
32+
33+
async function setRepoVersion (version) {
34+
await fs.writeFile(path.join(repoPath, 'version'), version)
35+
}
36+
37+
async function getRepoVersion () {
38+
return parseInt(await fs.readFile(path.join(repoPath, 'version'), 'utf8'))
39+
}
40+
41+
beforeEach(async () => {
42+
repoPath = path.join(os.tmpdir(), `ipfs-${hat()}`)
43+
const v7RepoPath = path.join(__dirname, '../fixtures/v7-repo')
44+
await ncp(v7RepoPath, repoPath)
45+
ipfs = ipfsExec(repoPath)
46+
})
47+
48+
afterEach(() => clean(repoPath))
49+
50+
it('should not migrate for daemon command when --migrate flag not set', async () => {
51+
// There are no migrations prior to 7 so it's safe to set version to 5 since
52+
// the repo is the same. We set to 5 because version 6 & 7 are considered
53+
// the same in repo.version.check.
54+
await setRepoVersion(5)
55+
const err = await ipfs.fail('daemon')
56+
expect(err.stdout).to.include('Pass --migrate for automatic migration')
57+
const version = await getRepoVersion()
58+
expect(version).to.equal(5) // Should not have migrated
59+
})
60+
61+
it('should not migrate for other commands when --migrate flag not set', async () => {
62+
// There are no migrations prior to 7 so it's safe to set version to 5 since
63+
// the repo is the same. We set to 5 because version 6 & 7 are considered
64+
// the same in repo.version.check.
65+
await setRepoVersion(5)
66+
const err = await ipfs.fail('files ls')
67+
expect(err.stdout).to.include('Pass --migrate for automatic migration')
68+
const version = await getRepoVersion()
69+
expect(version).to.equal(5) // Should not have migrated
70+
})
71+
72+
it('should migrate for daemon command when --migrate flag set', async () => {
73+
// There are no migrations prior to 7 so it's safe to set version to 5 since
74+
// the repo is the same. We set to 5 because version 6 & 7 are considered
75+
// the same in repo.version.check.
76+
await setRepoVersion(5)
77+
78+
const daemon = ipfs('daemon --migrate')
79+
let stdout = ''
80+
81+
daemon.stdout.on('data', data => {
82+
stdout += data.toString('utf8')
83+
84+
if (stdout.includes('Daemon is ready')) {
85+
daemon.kill()
86+
}
87+
})
88+
89+
await expect(daemon)
90+
.to.eventually.be.rejected()
91+
.and.to.include({
92+
killed: true
93+
})
94+
95+
const version = await getRepoVersion()
96+
expect(version).to.equal(repoVersion) // Should have migrated to latest
97+
})
98+
99+
it('should migrate for other commands when --migrate flag set', async () => {
100+
// There are no migrations prior to 7 so it's safe to set version to 5 since
101+
// the repo is the same. We set to 5 because version 6 & 7 are considered
102+
// the same in repo.version.check.
103+
await setRepoVersion(5)
104+
await ipfs('files ls --migrate')
105+
const version = await getRepoVersion()
106+
expect(version).to.equal(repoVersion) // Should have migrated to latest
107+
})
108+
})

test/fixtures/v7-repo/blocks/7J/CIQKKLBWAIBQZOIS5X7E32LQAL6236OUKZTMHPQSFIXPWXNZHQOV7JQ.data

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
2+
��
3+
IPFS -- Inter-Planetary File system
4+
5+
IPFS is a global, versioned, peer-to-peer filesystem. It combines good ideas
6+
from Git, BitTorrent, Kademlia, SFS, and the Web. It is like a single bit-
7+
torrent swarm, exchanging git objects. IPFS provides an interface as simple
8+
as the HTTP web, but with permanence built in. You can also mount the world
9+
at /ipfs.
10+
11+
IPFS is a protocol:
12+
- defines a content-addressed file system
13+
- coordinates content delivery
14+
- combines Kademlia + BitTorrent + Git
15+
16+
IPFS is a filesystem:
17+
- has directories and files
18+
- mountable filesystem (via FUSE)
19+
20+
IPFS is a web:
21+
- can be used to view documents like the web
22+
- files accessible via HTTP at `http://ipfs.io/<path>`
23+
- browsers or extensions can learn to use `ipfs://` directly
24+
- hash-addressed content guarantees authenticity
25+
26+
IPFS is modular:
27+
- connection layer over any network protocol
28+
- routing layer
29+
- uses a routing layer DHT (kademlia/coral)
30+
- uses a path-based naming service
31+
- uses bittorrent-inspired block exchange
32+
33+
IPFS uses crypto:
34+
- cryptographic-hash content addressing
35+
- block-level deduplication
36+
- file integrity + versioning
37+
- filesystem-level encryption + signing support
38+
39+
IPFS is p2p:
40+
- worldwide peer-to-peer file transfers
41+
- completely decentralized architecture
42+
- **no** central point of failure
43+
44+
IPFS is a cdn:
45+
- add a file to the filesystem locally, and it's now available to the world
46+
- caching-friendly (content-hash naming)
47+
- bittorrent-based bandwidth distribution
48+
49+
IPFS has a name service:
50+
- IPNS, an SFS inspired name system
51+
- global namespace based on PKI
52+
- serves to build trust chains
53+
- compatible with other NSes
54+
- can map DNS, .onion, .bit, etc to IPNS
55+
�
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/
2+
" �I%�s�!@��<��'����8���@:�шo_�direct�V2
3+
" �e��I�\(&PD�
4+
� �� 2�hO.߃o� recursive�V
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
��Come hang out in our IRC chat room if you have any questions.
3+
4+
Contact the ipfs dev team:
5+
- Bugs: https://github.com/ipfs/go-ipfs/issues
6+
- Help: irc.freenode.org/#ipfs
7+
8+
�
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
��Some helpful resources for finding your way around ipfs:
3+
4+
- quick-start: a quick show of various ipfs features.
5+
- ipfs commands: a list of all commands
6+
- ipfs --help: every command describes itself
7+
- https://github.com/ipfs/go-ipfs -- the src repository
8+
- #ipfs on irc.freenode.org -- the community irc channel
9+
�

0 commit comments

Comments
 (0)