Skip to content

Commit

Permalink
Strict cli (subquery#2465)
Browse files Browse the repository at this point in the history
* enable strict in cli

* ts strict change

* some change

* some change

* some changes

* some changes

* some changes

* package import issues

* some change

* some change

* some change

* some change

* some change

* some change

* some change

* some change

* change log

* some change

* some change

* some changes

* some change

* some changes

* some changes

* some change

* Fix types and publish cid assertion

* Address comments

---------

Co-authored-by: JQQQ <[email protected]>
  • Loading branch information
yoozo and jiqiang90 authored Jul 4, 2024
1 parent 067540d commit 0ca0c46
Show file tree
Hide file tree
Showing 55 changed files with 324 additions and 274 deletions.
8 changes: 8 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,12 @@ module.exports = {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
},
overrides:[
{
files: ['*.test.ts', '*.spec.ts'],
rules: {
'@typescript-eslint/no-non-null-assertion': 'off',
},
},
]
};
3 changes: 3 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## Added
- Enable ts strict mode

### Fixed
- The IPFS CID for multi-chain requires the directory CID.

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export default class Build extends Command {
this.log('Building and packing code ...');
this.log('Done!');
}
} catch (e) {
} catch (e: any) {
this.error(e);
}
}
Expand Down
5 changes: 3 additions & 2 deletions packages/cli/src/commands/codegen/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import fs, {lstatSync} from 'fs';
import path from 'path';
import type {EventFragment, FunctionFragment} from '@ethersproject/abi/src.ts/fragments';
import type {EventFragment, FunctionFragment} from '@ethersproject/abi';
import {Command, Flags} from '@oclif/core';
import {DEFAULT_MANIFEST, DEFAULT_TS_MANIFEST, extensionIsTs, NETWORK_FAMILY} from '@subql/common';
import type {SubqlRuntimeDatasource as EthereumDs} from '@subql/types-ethereum';
Expand Down Expand Up @@ -101,6 +101,7 @@ export default class Generate extends Command {
} else {
this.error('Invalid manifest path');
}

const ethModule = loadDependency(NETWORK_FAMILY.ethereum);
const abiName = ethModule.parseContractPath(abiPath).name;

Expand Down Expand Up @@ -168,7 +169,7 @@ export default class Generate extends Command {
this.log(`Event: ${event.name} successfully generated`);
});
this.log('-------------------------------');
} catch (e) {
} catch (e: any) {
this.error(e);
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/codegen/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export default class Codegen extends Command {

try {
await codegen(root, manifests);
} catch (err) {
} catch (err: any) {
console.error(err.message, err.cause);
process.exit(1);
}
Expand Down
18 changes: 11 additions & 7 deletions packages/cli/src/commands/deployment/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,17 @@ export default class Delete extends Command {
async run(): Promise<void> {
const {flags} = await this.parse(Delete);
const authToken = await checkToken();
let deploymentID: number = +flags.deploymentID;
let project_name: string = flags.project_name;
let org: string = flags.org;

org = await valueOrPrompt(org, 'Enter organisation', 'Organisation is required');
project_name = await valueOrPrompt(project_name, 'Enter project name', 'Project name is required');
deploymentID = await valueOrPrompt(deploymentID, 'Enter deployment ID', 'Deployment ID is required');
const org: string = await valueOrPrompt(flags.org, 'Enter organisation', 'Organisation is required');
const project_name: string = await valueOrPrompt(
flags.project_name,
'Enter project name',
'Project name is required'
);
const deploymentID: number = +(await valueOrPrompt(
flags.deploymentID,
'Enter deployment ID',
'Deployment ID is required'
));

this.log(`Removing deployment: ${deploymentID}`);
const delete_output = await deleteDeployment(org, project_name, authToken, +deploymentID, ROOT_API_URL_PROD).catch(
Expand Down
18 changes: 14 additions & 4 deletions packages/cli/src/commands/deployment/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0

import assert from 'assert';
import {Command, Flags} from '@oclif/core';
import chalk from 'chalk';
import cli from 'cli-ux';
Expand Down Expand Up @@ -51,19 +52,21 @@ export default class Deploy extends Command {
throw new Error(chalk.red('Please ensure a valid is passed using --endpoint flag'));
}

flags.endpoint = await promptWithDefaultValues(cli, 'Enter endpoint', undefined, null, true);
flags.endpoint = await promptWithDefaultValues(cli, 'Enter endpoint', undefined, undefined, true);
}

if (!flags.dict) {
assert(validator.chainId, 'Please set chainId in your project');
const validateDictEndpoint = processEndpoints(await dictionaryEndpoints(ROOT_API_URL_PROD), validator.chainId);
if (!flags.useDefaults && !validateDictEndpoint) {
flags.dict = await promptWithDefaultValues(cli, 'Enter dictionary', validateDictEndpoint, null, false);
flags.dict = await promptWithDefaultValues(cli, 'Enter dictionary', validateDictEndpoint, undefined, false);
} else {
flags.dict = validateDictEndpoint;
}
}

if (!flags.indexerVersion) {
assert(validator.manifestRunner, 'Please set manifestRunner in your project');
try {
const indexerVersions = await imageVersions(
validator.manifestRunner.node.name,
Expand All @@ -75,7 +78,7 @@ export default class Deploy extends Command {
flags.indexerVersion = await promptWithDefaultValues(
inquirer,
'Enter indexer version',
null,
undefined,
indexerVersions,
true
);
Expand All @@ -87,6 +90,7 @@ export default class Deploy extends Command {
}
}
if (!flags.queryVersion) {
assert(validator.manifestRunner, 'Please set manifestRunner in your project');
try {
const queryVersions = await imageVersions(
validator.manifestRunner.query.name,
Expand All @@ -95,7 +99,13 @@ export default class Deploy extends Command {
ROOT_API_URL_PROD
);
if (!flags.useDefaults) {
const response = await promptWithDefaultValues(inquirer, 'Enter query version', null, queryVersions, true);
const response = await promptWithDefaultValues(
inquirer,
'Enter query version',
undefined,
queryVersions,
true
);
flags.queryVersion = response;
} else {
flags.queryVersion = queryVersions[0];
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/deployment/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export default class Deployment extends Command {
// removes arguments -> deployment and everything before it from the process.argv
const stripped_argv: string[] = process.argv
.filter((v, idx) => idx > process.argv.indexOf('deployment') && !v.includes('--options'))
.reduce((acc, val) => acc.concat(val.split('=')), []);
.reduce((acc, val) => acc.concat(val.split('=')), [] as string[]);

await handler.run(stripped_argv);
} catch (e) {
Expand Down
18 changes: 11 additions & 7 deletions packages/cli/src/commands/deployment/promote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ export default class Promote extends Command {
const {flags} = await this.parse(Promote);
const authToken = await checkToken();

let deploymentID: number = +flags.deploymentID;
let org: string = flags.org;
let project_name: string = flags.project_name;

org = await valueOrPrompt(org, 'Enter organisation', 'Organisation is required');
project_name = await valueOrPrompt(project_name, 'Enter project name', 'Project name is required');
deploymentID = await valueOrPrompt(deploymentID, 'Enter deployment ID', 'Deployment ID is required');
const deploymentID: number = +(await valueOrPrompt(
flags.deploymentID,
'Enter deployment ID',
'Deployment ID is required'
));
const org: string = await valueOrPrompt(flags.org, 'Enter organisation', 'Organisation is required');
const project_name: string = await valueOrPrompt(
flags.project_name,
'Enter project name',
'Project name is required'
);

const promote_output = await promoteDeployment(
org,
Expand Down
84 changes: 40 additions & 44 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0

import assert from 'assert';
import fs from 'fs';
import path from 'path';
import {URL} from 'url';
Expand Down Expand Up @@ -43,12 +44,12 @@ function filterInput(arr: string[]) {

async function promptValidRemoteAndBranch(): Promise<string[]> {
let isValid = false;
let remote: string;
let remote: string | undefined;
while (!isValid) {
try {
remote = await cli.prompt('Custom template git remote', {
remote = (await cli.prompt('Custom template git remote', {
required: true,
});
})) as string;
new URL(remote);
isValid = true;
} catch (e) {
Expand Down Expand Up @@ -78,29 +79,24 @@ export default class Init extends Command {
description: 'Give the starter project name',
}),
};
private projectPath: string; //path on GitHub
private project: ProjectSpecBase;
private location: string;
private networkFamily: NETWORK_FAMILY;
private network: string;

async run(): Promise<void> {
const {args, flags} = await this.parse(Init);

this.location = flags.location ? resolveToAbsolutePath(flags.location) : process.cwd();
this.project = {} as ProjectSpecBase;
this.project.name = args.projectName
const location = flags.location ? resolveToAbsolutePath(flags.location) : process.cwd();
const project = {} as ProjectSpecBase;
project.name = args.projectName
? args.projectName
: await cli.prompt('Project name', {default: 'subql-starter', required: true});
if (fs.existsSync(path.join(this.location, `${this.project.name}`))) {
throw new Error(`Directory ${this.project.name} exists, try another project name`);
if (fs.existsSync(path.join(location, `${project.name}`))) {
throw new Error(`Directory ${project.name} exists, try another project name`);
}

const networkTemplates = await fetchNetworks();

//Family selection
const families = networkTemplates.map(({name}) => name);
await inquirer
const networkFamily: NETWORK_FAMILY = await inquirer
.prompt([
{
name: 'familyResponse',
Expand All @@ -112,17 +108,16 @@ export default class Init extends Command {
source: filterInput(families),
},
])
.then(({familyResponse}) => {
this.networkFamily = familyResponse;
});
.then(({familyResponse}) => familyResponse);

// if network family is of ethereum, then should prompt them an abiPath
const selectedFamily = networkTemplates.find((family) => family.name === this.networkFamily);
const selectedFamily = networkTemplates.find((family) => family.name === networkFamily);
assert(selectedFamily, 'No network family selected');

// Network selection
const networkStrArr = selectedFamily.networks.map((n) => n.name);

await inquirer
const network: string = await inquirer
.prompt([
{
name: 'networkResponse',
Expand All @@ -134,14 +129,14 @@ export default class Init extends Command {
source: filterInput(networkStrArr),
},
])
.then(({networkResponse}) => {
this.network = networkResponse;
});
const selectedNetwork = selectedFamily.networks.find((network) => this.network === network.name);
.then(({networkResponse}) => networkResponse);

const selectedNetwork = selectedFamily.networks.find((v) => network === v.name);
assert(selectedNetwork, 'No network selected');

const candidateProjects = await fetchExampleProjects(selectedFamily.code, selectedNetwork.code);

let selectedProject: ExampleProjectInterface;
let selectedProject: ExampleProjectInterface | undefined;
// Templates selection
const paddingWidth = candidateProjects.map(({name}) => name.length).reduce((acc, xs) => Math.max(acc, xs)) + 5;
const templateDisplays = candidateProjects.map(
Expand All @@ -162,16 +157,17 @@ export default class Init extends Command {
.then(async ({templateDisplay}) => {
const templateName = (templateDisplay as string).split(' ')[0];
if (templateName === 'Other') {
await this.cloneCustomRepo();
await this.cloneCustomRepo(project, projectPath, location);
} else {
selectedProject = candidateProjects.find((project) => project.name === templateName);
}
});
this.projectPath = await cloneProjectTemplate(this.location, this.project.name, selectedProject);
assert(selectedProject, 'No project selected');
const projectPath: string = await cloneProjectTemplate(location, project.name, selectedProject);

await this.setupProject(flags);
await this.setupProject(project, projectPath, flags);

if (await validateEthereumProjectManifest(this.projectPath)) {
if (await validateEthereumProjectManifest(projectPath)) {
const {loadAbi} = await inquirer.prompt([
{
type: 'confirm',
Expand All @@ -182,30 +178,30 @@ export default class Init extends Command {
]);

if (loadAbi) {
await this.createProjectScaffold();
await this.createProjectScaffold(projectPath);
}
}
}

async cloneCustomRepo(): Promise<void> {
async cloneCustomRepo(project: ProjectSpecBase, projectPath: string, location: string): Promise<void> {
const [gitRemote, gitBranch] = await promptValidRemoteAndBranch();
this.projectPath = await cloneProjectGit(this.location, this.project.name, gitRemote, gitBranch);
projectPath = await cloneProjectGit(location, project.name, gitRemote, gitBranch);
}

async setupProject(flags: any): Promise<void> {
const [defaultEndpoint, defaultAuthor, defaultDescription] = await readDefaults(this.projectPath);
async setupProject(project: ProjectSpecBase, projectPath: string, flags: any): Promise<void> {
const [defaultEndpoint, defaultAuthor, defaultDescription] = await readDefaults(projectPath);

this.project.endpoint = !Array.isArray(defaultEndpoint) ? [defaultEndpoint] : defaultEndpoint;
project.endpoint = !Array.isArray(defaultEndpoint) ? [defaultEndpoint] : defaultEndpoint;
const userInput = await cli.prompt('RPC endpoint:', {
default: defaultEndpoint[0] ?? 'wss://polkadot.api.onfinality.io/public-ws',
required: false,
});
if (!this.project.endpoint.includes(userInput)) {
(this.project.endpoint as string[]).push(userInput);
if (!project.endpoint.includes(userInput)) {
(project.endpoint as string[]).push(userInput);
}
const descriptionHint = defaultDescription.substring(0, 40).concat('...');
this.project.author = await cli.prompt('Author', {required: true, default: defaultAuthor});
this.project.description = await cli
project.author = await cli.prompt('Author', {required: true, default: defaultAuthor});
project.description = await cli
.prompt('Description', {
required: false,
default: descriptionHint,
Expand All @@ -215,17 +211,17 @@ export default class Init extends Command {
});

cli.action.start('Preparing project');
await prepare(this.projectPath, this.project);
await prepare(projectPath, project);
cli.action.stop();
if (flags['install-dependencies']) {
cli.action.start('Installing dependencies');
installDependencies(this.projectPath, flags.npm);
installDependencies(projectPath, flags.npm);
cli.action.stop();
}
this.log(`${this.project.name} is ready`);
this.log(`${project.name} is ready`);
}
async createProjectScaffold(): Promise<void> {
await prepareProjectScaffold(this.projectPath);
async createProjectScaffold(projectPath: string): Promise<void> {
await prepareProjectScaffold(projectPath);

const {abiFilePath} = await inquirer.prompt([
{
Expand Down Expand Up @@ -257,7 +253,7 @@ export default class Init extends Command {
this.log(`Generating scaffold handlers and manifest from ${abiFilePath}`);
await Generate.run([
'-f',
this.projectPath,
projectPath,
'--abiPath',
`${abiFilePath}`,
'--address',
Expand Down
Loading

0 comments on commit 0ca0c46

Please sign in to comment.