forked from subquery/subql
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement
publish
command to upload project to IPFS (subquery#486)
* Implement publish command to upload project to IPFS * Revert jest config change * Fix eslint issues * Fix copyright header * Minify and sort keys for yaml files when publishing * Rework publish to use manifest as entry point rather than directory * Explicitly use cid v0 * Update validator and cli command to support validating projects published to ipfs * Update publishing to support project manifest spec 0.2.0 * Update README.md * Clean up * Fix test * Update ipfs endpoints for tests * Increase cli publish test timeout * Increase timeout for db module tests * Fix build issues
- Loading branch information
Showing
20 changed files
with
902 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
// Copyright 2020-2021 OnFinality Limited authors & contributors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import {lstatSync} from 'fs'; | ||
import path from 'path'; | ||
import {Command, flags} from '@oclif/command'; | ||
import {uploadToIpfs} from '../controller/publish-controller'; | ||
import Build from './build'; | ||
|
||
export default class Publish extends Command { | ||
static description = 'Upload this SubQuery project to IPFS'; | ||
|
||
static flags = { | ||
location: flags.string({char: 'l', description: 'local folder'}), | ||
ipfs: flags.string({description: 'IPFS gateway endpoint', default: 'http://localhost:5001/api/v0'}), | ||
}; | ||
|
||
async run(): Promise<void> { | ||
const {flags} = this.parse(Publish); | ||
|
||
const directory = flags.location ? path.resolve(flags.location) : process.cwd(); | ||
|
||
if (!lstatSync(directory).isDirectory()) { | ||
this.error('Argument `location` is not a valid directory'); | ||
} | ||
|
||
// Ensure that the project is built | ||
try { | ||
await Build.run(['--location', directory]); | ||
} catch (e) { | ||
this.log(e); | ||
this.error('Failed to build project'); | ||
} | ||
|
||
this.log('Uploading SupQuery project to ipfs'); | ||
const cid = await uploadToIpfs(flags.ipfs, directory); | ||
|
||
this.log(`SubQuery Project uploaded to IPFS: ${cid}`); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// Copyright 2020-2021 OnFinality Limited authors & contributors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import childProcess from 'child_process'; | ||
import fs from 'fs'; | ||
import os from 'os'; | ||
import path from 'path'; | ||
import {promisify} from 'util'; | ||
import rimraf from 'rimraf'; | ||
import Build from '../commands/build'; | ||
import Codegen from '../commands/codegen'; | ||
import Validate from '../commands/validate'; | ||
import {ProjectSpecBase, ProjectSpecV0_0_1, ProjectSpecV0_2_0} from '../types'; | ||
import {createProject} from './init-controller'; | ||
import {uploadToIpfs} from './publish-controller'; | ||
|
||
const projectSpecV0_0_1: ProjectSpecV0_0_1 = { | ||
name: 'mocked_starter', | ||
repository: '', | ||
endpoint: 'wss://rpc.polkadot.io/public-ws', | ||
author: 'jay', | ||
description: 'this is test for init controller', | ||
version: '', | ||
license: '', | ||
}; | ||
|
||
const projectSpecV0_2_0: ProjectSpecV0_2_0 = { | ||
name: 'mocked_starter', | ||
repository: '', | ||
genesisHash: '0x91b171bb158e2d3848fa23a9f1c25182fb8e20313b2c1eb49219da7a70ce90c3', | ||
author: 'jay', | ||
description: 'this is test for init controller', | ||
version: '', | ||
license: '', | ||
endpoint: '', | ||
}; | ||
|
||
const ipfsEndpoint = 'https://ipfs.thechainhub.com/api/v0'; | ||
|
||
jest.setTimeout(120000); | ||
|
||
async function createTestProject(projectSpec: ProjectSpecBase): Promise<string> { | ||
const tmpdir = await fs.promises.mkdtemp(`${os.tmpdir()}${path.sep}`); | ||
const projectDir = path.join(tmpdir, projectSpec.name); | ||
|
||
await createProject(tmpdir, projectSpec); | ||
|
||
// Install dependencies | ||
childProcess.execSync('npm i', {cwd: projectDir}); | ||
|
||
await Codegen.run(['-l', projectDir]); | ||
await Build.run(['-l', projectDir]); | ||
|
||
return projectDir; | ||
} | ||
|
||
describe('Cli publish', () => { | ||
let projectDir: string; | ||
|
||
afterEach(() => { | ||
promisify(rimraf)(projectDir); | ||
}); | ||
|
||
it('should not allow uploading a v0.0.1 spec version project', async () => { | ||
projectDir = await createTestProject(projectSpecV0_0_1); | ||
|
||
await expect(uploadToIpfs(ipfsEndpoint, projectDir)).rejects.toBeDefined(); | ||
}); | ||
|
||
it('should upload appropriate files to IPFS', async () => { | ||
projectDir = await createTestProject(projectSpecV0_2_0); | ||
const cid = await uploadToIpfs(ipfsEndpoint, projectDir); | ||
|
||
expect(cid).toBeDefined(); | ||
await expect(Validate.run(['-l', cid, '--ipfs', ipfsEndpoint])).resolves.toBe(undefined); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Copyright 2020-2021 OnFinality Limited authors & contributors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
import fs from 'fs'; | ||
import path from 'path'; | ||
import { | ||
loadProjectManifest, | ||
manifestIsV0_2_0, | ||
ProjectManifestV0_0_1Impl, | ||
ProjectManifestV0_2_0Impl, | ||
} from '@subql/common'; | ||
import IPFS from 'ipfs-http-client'; | ||
import yaml from 'js-yaml'; | ||
|
||
// https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/FILES.md#filecontent | ||
type FileContent = Uint8Array | string | Iterable<Uint8Array> | Iterable<number> | AsyncIterable<Uint8Array>; | ||
|
||
// https://github.com/ipfs/js-ipfs/blob/master/docs/core-api/FILES.md#fileobject | ||
type FileObject = { | ||
path?: string; | ||
content?: FileContent; | ||
mode?: number | string; | ||
mtime?: Date | number[] | {secs: number; nsecs?: number}; | ||
}; | ||
|
||
export async function uploadToIpfs(ipfsEndpoint: string, projectDir: string): Promise<string> { | ||
const ipfs = IPFS.create({url: ipfsEndpoint}); | ||
|
||
const projectManifestPath = path.resolve(projectDir, 'project.yaml'); | ||
const manifest = loadProjectManifest(projectManifestPath).asImpl; | ||
|
||
if (manifestIsV0_2_0(manifest)) { | ||
const entryPaths = manifest.dataSources.map((ds) => ds.mapping.file); | ||
const schemaPath = manifest.schema.file; | ||
|
||
// Upload referenced files to IPFS | ||
const [schema, ...entryPoints] = await Promise.all( | ||
[schemaPath, ...entryPaths].map((filePath) => | ||
uploadFile(ipfs, fs.createReadStream(path.resolve(projectDir, filePath))).then((cid) => `ipfs://${cid}`) | ||
) | ||
); | ||
|
||
// Update referenced file paths to IPFS cids | ||
manifest.schema.file = schema; | ||
|
||
entryPoints.forEach((entryPoint, index) => { | ||
manifest.dataSources[index].mapping.file = entryPoint; | ||
}); | ||
} else { | ||
throw new Error('Unsupported project manifest spec, only 0.2.0 is supported'); | ||
} | ||
|
||
// Upload schema | ||
return uploadFile(ipfs, toMinifiedYaml(manifest)); | ||
} | ||
|
||
async function uploadFile(ipfs: IPFS.IPFSHTTPClient, content: FileObject | FileContent): Promise<string> { | ||
const result = await ipfs.add(content, {pin: true, cidVersion: 0}); | ||
return result.cid.toString(); | ||
} | ||
|
||
function toMinifiedYaml(manifest: ProjectManifestV0_0_1Impl | ProjectManifestV0_2_0Impl): string { | ||
return yaml.dump(manifest, { | ||
sortKeys: true, | ||
condenseFlow: true, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.