Skip to content
This repository was archived by the owner on Oct 1, 2021. It is now read-only.

Commit 9477d57

Browse files
committed
feat: confirm migration of irreversible migrations
1 parent 0068eaa commit 9477d57

File tree

9 files changed

+51
-43
lines changed

9 files changed

+51
-43
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,8 @@ write the rest of the migration!
221221

222222
### Integration with js-ipfs
223223

224-
When a new migration is created, the repo version in [`js-ipfs-repo`](https://github.com/ipfs/js-ipfs-repo) should be updated with the new version,
225-
together with updated version of this package. Then the updated version should be propagated to `js-ipfs`.
224+
When a new migration is created, new version of this package have to be released. Afterwards version of this package in [`js-ipfs-repo`](https://github.com/ipfs/js-ipfs-repo) have to be updated
225+
together with the repo version that `IPFSRepo` expects. Then the updated version of `js-ipfs-repo` should be propagated to `js-ipfs`.
226226

227227
### Tests
228228

src/commands.js

+23-5
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,33 @@ const path = require('path')
55
const fs = require('fs')
66
const process = require('process')
77
const util = require('util')
8+
const readline = require('readline')
89

910
const writeFile = util.promisify(fs.writeFile)
1011
const mkdir = util.promisify(fs.mkdir)
1112

1213
const chalk = require('chalk')
1314

1415
const repoVersion = require('./repo/version')
16+
const migratorUtils = require('./utils')
1517
const migrator = require('./index')
1618
const templates = require('./migration-templates')
1719
const migrations = require('../migrations')
1820

21+
function shouldContinueWithIrreversibleMigration () {
22+
return new Promise((resolve, reject) => {
23+
const rl = readline.createInterface({
24+
input: process.stdin,
25+
output: process.stdout
26+
})
27+
28+
rl.question(chalk.yellow('There is irreversible migration in the migrations plan. Do you want to continue? [y/N]'), (answer) => {
29+
rl.close()
30+
resolve(answer === 'y')
31+
})
32+
})
33+
}
34+
1935
function asyncClosure (fnc) {
2036
return function asyncWrapper ({ resolve, ...options }) {
2137
resolve(fnc(options))
@@ -29,11 +45,7 @@ function reportingClosure (action) {
2945

3046
async function migrate ({ repoPath, migrations, to, dry, revertOk }) {
3147
repoPath = repoPath || process.env.IPFS_PATH || path.join(os.homedir(), '.jsipfs')
32-
33-
// Import migrations if set
34-
if (migrations) {
35-
migrations = require(migrations)
36-
}
48+
migrations = migrations === undefined ? require('../migrations') : require(migrations)
3749

3850
if (!to) {
3951
to = migrator.getLatestMigrationVersion(migrations)
@@ -63,6 +75,12 @@ async function migrate ({ repoPath, migrations, to, dry, revertOk }) {
6375
if (version === to) {
6476
return chalk.yellow('Nothing to migrate! Versions matches')
6577
} else if (version < to) {
78+
if (migratorUtils.containsIrreversibleMigration(version, to, migrations)) {
79+
if (!(await shouldContinueWithIrreversibleMigration())) {
80+
return 'Migration aborted'
81+
}
82+
}
83+
6684
await migrator.migrate(repoPath, to, options)
6785
} else if (revertOk) {
6886
await migrator.revert(repoPath, to, options)

src/errors.js

-15
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,6 @@ class NonReversibleMigrationError extends Error {
1616
NonReversibleMigrationError.code = 'ERR_NON_REVERSIBLE_MIGRATION'
1717
exports.NonReversibleMigrationError = NonReversibleMigrationError
1818

19-
/**
20-
* Exception raised when structure of a repo is not as expected.
21-
*/
22-
class UnknownRepoStructureError extends Error {
23-
constructor (message) {
24-
super(message)
25-
this.name = 'UnknownRepoStructureError'
26-
this.code = 'ERR_UNKNOWN_REPO_STRUCTURE'
27-
this.message = message
28-
}
29-
}
30-
31-
UnknownRepoStructureError.code = 'ERR_UNKNOWN_REPO_STRUCTURE'
32-
exports.UnknownRepoStructureError = UnknownRepoStructureError
33-
3419
/**
3520
* Exception raised when repo is not initialized.
3621
*/

src/index.js

+1-10
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
const defaultMigrations = require('../migrations')
44
const repoVersion = require('./repo/version')
55
const repoLock = require('./repo/lock')
6-
const repoInit = require('./repo/init')
76
const errors = require('./errors')
87
const isElectron = require('is-electron')
98

@@ -51,18 +50,14 @@ exports.getLatestMigrationVersion = getLatestMigrationVersion
5150
* @param {array?} options.migrations - Array of migrations to migrate. If undefined, the bundled migrations are used. Mainly for testing purpose.
5251
* @returns {Promise<void>}
5352
*/
54-
async function migrate (path, toVersion, {ignoreLock = false, repoOptions, onProgress, isDryRun = false, migrations }) {
53+
async function migrate (path, toVersion, { ignoreLock = false, repoOptions, onProgress, isDryRun = false, migrations }) {
5554
migrations = migrations || defaultMigrations
5655
onProgress = onProgress || (() => {})
5756

5857
if (!path) {
5958
throw new errors.RequiredParameterError('Path argument is required!')
6059
}
6160

62-
if (!(await repoInit.isRepoInitialized(path))) {
63-
throw new errors.NotInitializedRepoError(`Repo in path ${path} is not initialized!`)
64-
}
65-
6661
if (!toVersion) {
6762
throw new errors.RequiredParameterError('toVersion argument is required!')
6863
}
@@ -150,10 +145,6 @@ async function revert (path, toVersion, { ignoreLock = false, repoOptions, onPro
150145
throw new errors.RequiredParameterError('Path argument is required!')
151146
}
152147

153-
if (!(await repoInit.isRepoInitialized(path))) {
154-
throw new errors.NotInitializedRepoError(`Repo in path ${path} is not initialized!`)
155-
}
156-
157148
if (!toVersion) {
158149
throw new errors.RequiredParameterError('When reverting migrations, you have to specify to which version to revert!')
159150
}

src/repo/version.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const errors = require('../errors')
4+
const repoInit = require('./init')
45
const Datastore = require('datastore-fs')
56

67
const Key = require('interface-datastore').Key
@@ -18,13 +19,13 @@ exports.getVersion = getVersion
1819
* @returns {Promise<int>}
1920
*/
2021
async function getVersion (path) {
22+
if (!(await repoInit.isRepoInitialized(path))) {
23+
throw new errors.NotInitializedRepoError(`Repo in path ${path} is not initialized!`)
24+
}
25+
2126
const store = new Datastore(path, { extension: '', createIfMissing: false })
2227
await store.open()
2328

24-
if (!await store.has(versionKey)) {
25-
throw new errors.UnknownRepoStructureError('Repo does not have version file! Is the repo initialized?')
26-
}
27-
2829
const version = parseInt(await store.get(versionKey))
2930
await store.close()
3031

src/utils.js

+6
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,9 @@ exports.getDatastoreAndOptions = function getDatastoreAndOptions (options, key,
2727
storageOptions: storageBackendOptions
2828
}
2929
}
30+
31+
exports.containsIrreversibleMigration = function containsIrreversibleMigration (from, to, migrations) {
32+
return migrations
33+
.filter(migration => migration.version > from && migration.version <= to)
34+
.find(migration => migration.revert === undefined) !== undefined
35+
}

test/index.spec.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ function createMigrations () {
4040

4141
function createOptions () {
4242
return {
43-
migrations: createMigrations(),
43+
migrations: createMigrations()
4444
}
4545
}
4646

@@ -350,7 +350,7 @@ describe('index.js', () => {
350350
options.ignoreLock = true
351351
getVersionStub.returns(2)
352352

353-
await expect(migrator.migrate('/some/path', 4, options))
353+
await expect(migrator.migrate('/some/path', 4, options))
354354
.to.eventually.be.fulfilled()
355355

356356
expect(lockCloseStub.called).to.be.false()

test/test-migrations/migration-2/index.js

-1
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ async function revert (repoPath, options, isBrowser) {
118118
module.exports = {
119119
version: 2,
120120
description: 'Updates config',
121-
reversible: true,
122121
migrate,
123122
revert,
124123
newApiAddr: NEW_API_ADDRESS

test/version-test.js

+12-4
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const errors = require('../src/errors')
1616
module.exports = (setup, cleanup) => {
1717
it('getVersion should fail without any version in repo', async () => {
1818
const dir = await setup()
19-
await expect(version.getVersion(dir)).to.be.eventually.rejectedWith(errors.UnknownRepoStructureError).with.property('code', errors.UnknownRepoStructureError.code)
19+
await expect(version.getVersion(dir)).to.be.eventually.rejectedWith(errors.NotInitializedRepoError).with.property('code', errors.NotInitializedRepoError.code)
2020
return cleanup(dir)
2121
})
2222

@@ -32,17 +32,25 @@ module.exports = (setup, cleanup) => {
3232

3333
it('should get version number', async () => {
3434
// Create version file
35-
const versionKey = new Key('version')
3635
const store = new Datastore(dir, { extension: '', createIfMissing: false })
3736
await store.open()
38-
await store.put(versionKey, Buffer.from('7'))
37+
await store.put(new Key('config'), Buffer.from('some dummy config'))
38+
await store.put(new Key('version'), Buffer.from('7'))
3939
await store.close()
4040

4141
expect(await version.getVersion(dir)).to.be.equal(7)
4242
})
4343

4444
it('should set version number', async () => {
45-
await expect(version.getVersion(dir)).to.be.eventually.rejectedWith(errors.UnknownRepoStructureError).with.property('code', errors.UnknownRepoStructureError.code)
45+
await expect(version.getVersion(dir)).to.be.eventually.rejectedWith(errors.NotInitializedRepoError).with.property('code', errors.NotInitializedRepoError.code)
46+
47+
// Create version file
48+
const store = new Datastore(dir, { extension: '', createIfMissing: false })
49+
await store.open()
50+
await store.put(new Key('config'), Buffer.from('some dummy config'))
51+
await store.put(new Key('version'), Buffer.from('5'))
52+
await store.close()
53+
4654
await version.setVersion(dir, 7)
4755
expect(await version.getVersion(dir)).to.be.equal(7)
4856
})

0 commit comments

Comments
 (0)