Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to CLI managed service commands #2542

Merged
merged 9 commits into from
Sep 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ jobs:
code-style:
name: code-style
runs-on: ubuntu-latest
env:
SUBQL_ACCESS_TOKEN: ${{ secrets.SUBQL_ACCESS_TOKEN }}
SUBQL_ACCESS_TOKEN_TEST: ${{ secrets.SUBQL_ACCESS_TOKEN_TEST }}
SUBQL_ORG_TEST: ${{ secrets.SUBQL_ORG_TEST }}
steps:
- uses: actions/checkout@v4
- name: Setup Node.js environment
Expand Down Expand Up @@ -46,6 +42,9 @@ jobs:
DB_DATABASE: postgres
DB_HOST: localhost
DB_PORT: 5432
SUBQL_ACCESS_TOKEN: ${{ secrets.SUBQL_ACCESS_TOKEN }}
SUBQL_ACCESS_TOKEN_TEST: ${{ secrets.SUBQL_ACCESS_TOKEN_TEST }}
SUBQL_ORG_TEST: ${{ secrets.SUBQL_ORG_TEST }}
steps:
- uses: actions/checkout@v4

Expand Down
5 changes: 5 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Fixed
- deployment command minor issues (#2542)

### Removed
- deprecated cli-ux dependency, switched to using inquirer and ora (#2542)

## [5.2.6] - 2024-09-09
### Changed
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
"@subql/common": "workspace:*",
"@subql/utils": "workspace:*",
"boxen": "5.1.2",
"cli-ux": "^6.0.9",
"ejs": "^3.1.10",
"fs-extra": "^11.2.0",
"fuzzy": "^0.1.3",
"glob": "^10.4",
"json5": "^2.2.3",
"node-fetch": "2.7.0",
"ora": "^5.4.1",
"rimraf": "^5.0.10",
"semver": "^7.6.3",
"simple-git": "^3.25.0",
Expand Down
12 changes: 0 additions & 12 deletions packages/cli/src/commands/deployment/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@ import chalk from 'chalk';
import {ROOT_API_URL_PROD} from '../../constants';
import {
DefaultDeployFlags,
dictionaryEndpoints,
executeProjectDeployment,
generateDeploymentChain,
imageVersions,
ipfsCID_validate,
processEndpoints,
projectsInfo,
splitEndpoints,
} from '../../controller/deploy-controller';
Expand Down Expand Up @@ -54,16 +52,6 @@ export default class Deploy extends Command {
flags.endpoint = await input({message: 'Enter endpoint', required: 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 input({message: 'Enter dictionary', default: validateDictEndpoint});
} else {
flags.dict = validateDictEndpoint;
}
}

if (!flags.indexerVersion) {
assert(validator.manifestRunner, 'Please set manifestRunner in your project');
try {
Expand Down
72 changes: 26 additions & 46 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@
import assert from 'assert';
import fs from 'fs';
import path from 'path';
import {URL} from 'url';
import {search, confirm, input} from '@inquirer/prompts';
import {Args, Command, Flags} from '@oclif/core';
import {NETWORK_FAMILY} from '@subql/common';
import chalk from 'chalk';
import cli from 'cli-ux';
import fuzzy from 'fuzzy';
import ora from 'ora';
import {
installDependencies,
cloneProjectTemplate,
cloneProjectGit,
readDefaults,
prepare,
prepareProjectScaffold,
Expand All @@ -29,33 +27,12 @@ import Generate from './codegen/generate';

// Helper function for fuzzy search on prompt input
function filterInput<T>(arr: T[]) {
return (input: string | undefined, opt: {signal: any}): Promise<ReadonlyArray<{value: T}>> => {
return (input: string | undefined): Promise<ReadonlyArray<{value: T}>> => {
input ??= '';
return Promise.resolve(fuzzy.filter(input, arr).map((r) => ({value: r.original})));
};
}

async function promptValidRemoteAndBranch(): Promise<string[]> {
let isValid = false;
let remote: string | undefined;
while (!isValid) {
try {
remote = (await cli.prompt('Custom template git remote', {
required: true,
})) as string;
new URL(remote);
isValid = true;
} catch (e) {
console.log(`Not a valid git remote URL: '${remote}', try again`);
continue;
}
}
const branch = await cli.prompt('Custom template git branch', {
required: true,
});
return [remote, branch];
}

export default class Init extends Command {
static description = 'Initialize a scaffold subquery project';

Expand All @@ -80,7 +57,11 @@ export default class Init extends Command {
const project = {} as ProjectSpecBase;
project.name = args.projectName
? args.projectName
: await cli.prompt('Project name', {default: 'subql-starter', required: true});
: await input({
message: 'Project name',
default: 'subql-starter',
required: true,
});
if (fs.existsSync(path.join(location, `${project.name}`))) {
throw new Error(`Directory ${project.name} exists, try another project name`);
}
Expand Down Expand Up @@ -162,40 +143,39 @@ export default class Init extends Command {
}
}

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

async setupProject(project: ProjectSpecBase, projectPath: string, flags: any): Promise<void> {
async setupProject(
project: ProjectSpecBase,
projectPath: string,
flags: {npm: boolean; 'install-dependencies': boolean}
): Promise<void> {
const [defaultEndpoint, defaultAuthor, defaultDescription] = await readDefaults(projectPath);

project.endpoint = !Array.isArray(defaultEndpoint) ? [defaultEndpoint] : defaultEndpoint;
const userInput = await cli.prompt('RPC endpoint:', {
const userInput = await input({
message: 'RPC endpoint:',
default: defaultEndpoint[0] ?? 'wss://polkadot.api.onfinality.io/public-ws',
required: false,
});
if (!project.endpoint.includes(userInput)) {
(project.endpoint as string[]).push(userInput);
}
const descriptionHint = defaultDescription.substring(0, 40).concat('...');
project.author = await cli.prompt('Author', {required: true, default: defaultAuthor});
project.description = await cli
.prompt('Description', {
required: false,
default: descriptionHint,
})
.then((description) => {
return description === descriptionHint ? defaultDescription : description;
});
project.author = await input({message: 'Author', required: true, default: defaultAuthor});
project.description = await input({
message: 'Description',
required: false,
default: descriptionHint,
}).then((description) => {
return description === descriptionHint ? defaultDescription : description;
});

cli.action.start('Preparing project');
const spinner = ora('Preparing project').start();
await prepare(projectPath, project);
cli.action.stop();
spinner.stop();
if (flags['install-dependencies']) {
cli.action.start('Installing dependencies');
const spinner = ora('Installing dependencies').start();
installDependencies(projectPath, flags.npm);
cli.action.stop();
spinner.stop();
}
this.log(`${project.name} is ready`);
}
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/commands/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import assert from 'assert';
import fs, {lstatSync} from 'fs';
import path from 'path';
import {input} from '@inquirer/prompts';
import {Command, Flags} from '@oclif/core';
import {makeTempDir} from '@subql/common';
import cli from 'cli-ux';
import git from 'simple-git';
import {
DEFAULT_SUBGRAPH_MANIFEST,
Expand Down Expand Up @@ -41,8 +41,8 @@ export default class Migrate extends Command {
const {flags} = await this.parse(Migrate);
const {file, gitSubDirectory, output} = flags;

const subgraphPath = file ?? (await cli.prompt('Subgraph project path, local or git', {required: true}));
const subqlPath = output ?? (await cli.prompt('SubQuery project path, local or git', {required: true}));
const subgraphPath = file ?? (await input({message: 'Subgraph project path, local or git', required: true}));
const subqlPath = output ?? (await input({message: 'SubQuery project path, local or git', required: true}));

const gitMatch = extractGitInfo(subgraphPath);
// will return false if directory not exist
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/commands/multi-chain/add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
// SPDX-License-Identifier: GPL-3.0

import assert from 'assert';
import {input} from '@inquirer/prompts';
import {Command, Flags} from '@oclif/core';
import {cli} from 'cli-ux';
import {addChain} from '../../controller/add-chain-controller';
import {resolveToAbsolutePath} from '../../utils';

Expand All @@ -22,7 +22,7 @@ export default class MultiChainAdd extends Command {
let {chainManifestPath} = flags;

if (!chainManifestPath) {
chainManifestPath = await cli.prompt('Enter the path to the new chain manifest');
chainManifestPath = await input({message: 'Enter the path to the new chain manifest'});
}
assert(chainManifestPath, 'Chain manifest path is required');

Expand Down
30 changes: 8 additions & 22 deletions packages/cli/src/commands/multi-chain/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,19 @@ import {input} from '@inquirer/prompts';
import {Command, Flags} from '@oclif/core';
import {getMultichainManifestPath, getProjectRootAndManifest} from '@subql/common';
import chalk from 'chalk';
import cli from 'cli-ux';
import ora from 'ora';
import YAML from 'yaml';
import {ROOT_API_URL_PROD} from '../../constants';
import {
DefaultDeployFlags,
dictionaryEndpoints,
executeProjectDeployment,
generateDeploymentChain,
ipfsCID_validate,
processEndpoints,
projectsInfo,
splitMultichainDataFields,
} from '../../controller/deploy-controller';
import {getDirectoryCid, uploadToIpfs} from '../../controller/publish-controller';
import {MultichainDataFieldType, V3DeploymentIndexerType} from '../../types';
import {V3DeploymentIndexerType} from '../../types';
import {addV, checkToken, resolveToAbsolutePath, valueOrPrompt} from '../../utils';
import {promptImageVersion} from '../deployment/deploy';

Expand Down Expand Up @@ -59,14 +57,14 @@ export default class MultiChainDeploy extends Command {
multichainManifestPath = path.join(project.root, multichainManifestPath);
const multichainManifestObject = YAML.parse(fs.readFileSync(multichainManifestPath, 'utf8'));

cli.action.start('Uploading project to IPFS');
const spinner = ora('Uploading project to IPFS').start();
const fileToCidMap = await uploadToIpfs(fullPaths, authToken.trim(), multichainManifestPath, flags.ipfs).catch(
(e) => {
cli.action.stop();
spinner.fail(e.message);
this.error(e);
}
);
cli.action.stop('DONE');
spinner.succeed('Uploaded project to IPFS');

flags.org = await valueOrPrompt(flags.org, 'Enter organisation', 'Organisation is required');
flags.projectName = await valueOrPrompt(flags.projectName, 'Enter project name', 'Project name is required');
Expand All @@ -78,9 +76,9 @@ export default class MultiChainDeploy extends Command {
const projectInfo = await projectsInfo(authToken, flags.org, flags.projectName, ROOT_API_URL_PROD, flags.type);
const chains: V3DeploymentIndexerType[] = [];

const endpoints: MultichainDataFieldType = splitMultichainDataFields(flags.endpoint);
const dictionaries: MultichainDataFieldType = splitMultichainDataFields(flags.dict);
const indexerVersions: MultichainDataFieldType = splitMultichainDataFields(flags.indexerVersion);
const endpoints = splitMultichainDataFields(flags.endpoint);
const dictionaries = splitMultichainDataFields(flags.dict);
const indexerVersions = splitMultichainDataFields(flags.indexerVersion);

if (!flags.queryVersion) {
try {
Expand Down Expand Up @@ -139,18 +137,6 @@ export default class MultiChainDeploy extends Command {
});
}

if (!dictionaries[validator.chainId]) {
const validateDictEndpoint = processEndpoints(await dictionaryEndpoints(ROOT_API_URL_PROD), validator.chainId);
if (!flags.useDefaults && !validateDictEndpoint) {
dictionaries[validator.chainId] = await input({
message: `Enter dictionary for ${multichainProjectPath}`,
required: false,
});
} else if (validateDictEndpoint) {
dictionaries[validator.chainId] = validateDictEndpoint;
}
}

chains.push(
generateDeploymentChain({
cid: multichainProjectCid,
Expand Down
32 changes: 13 additions & 19 deletions packages/cli/src/commands/project/create-project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@ export default class Create_project extends Command {
static flags = {
org: Flags.string({description: 'Enter organization name'}),
projectName: Flags.string({description: 'Enter project name'}),
gitRepo: Flags.string({description: 'Enter git repository'}),

logoURL: Flags.string({description: 'Enter logo URL', default: '', required: false}),
subtitle: Flags.string({description: 'Enter subtitle', default: '', required: false}),
description: Flags.string({description: 'Enter description', default: '', required: false}),
apiVersion: Flags.string({description: 'Enter api version', default: '2', required: false}),
dedicatedDB: Flags.string({description: 'Enter dedicated DataBase', required: false}),
projectType: Flags.string({
description: 'Enter project type [subquery|subgraph]',
Expand All @@ -30,7 +27,7 @@ export default class Create_project extends Command {
async run(): Promise<void> {
const {flags} = await this.parse(Create_project);

let {gitRepo, org, projectName} = flags;
let {org, projectName} = flags;
assert(
['subquery'].includes(flags.projectType),
'Invalid project type, only "subquery" is supported. Please deploy Subgraphs through the website.'
Expand All @@ -39,21 +36,18 @@ export default class Create_project extends Command {

org = await valueOrPrompt(org, 'Enter organisation', 'Organisation is required');
projectName = await valueOrPrompt(projectName, 'Enter project name', 'Project name is required');
gitRepo = await valueOrPrompt(gitRepo, 'Enter git repository', 'Git repository is required');

const result = await createProject(
org,
flags.subtitle,
flags.logoURL,
flags.projectType === 'subquery' ? 1 : 3,
projectName,
authToken,
gitRepo,
flags.description,
flags.apiVersion,
flags.dedicatedDB,
ROOT_API_URL_PROD
).catch((e) => this.error(e));

const result = await createProject(ROOT_API_URL_PROD, authToken, {
apiVersion: 'v3',
description: flags.description,
key: `${org}/${projectName}`,
logoUrl: flags.logoURL,
name: projectName,
subtitle: flags.subtitle,
dedicateDBKey: flags.dedicatedDB,
tag: [],
type: flags.projectType === 'subquery' ? 1 : 3,
}).catch((e) => this.error(e));

const [account, name] = result.key.split('/');
this.log(`Successfully created project: ${result.key}
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@

import assert from 'assert';
import path from 'path';
import {DeploymentType} from './types';

//DEPLOYMENT
export const DEFAULT_DEPLOYMENT_TYPE = 'primary';
export const DEFAULT_DEPLOYMENT_TYPE = 'primary' satisfies DeploymentType;
//PROJECT
export const ROOT_API_URL_DEV = 'https://api.thechaindata.com';
export const ROOT_API_URL_PROD = 'https://api.subquery.network';

export const BASE_PROJECT_URL = 'https://project.subquery.network';
export const BASE_PROJECT_URL = 'https://managedservice.subquery.network';

export const BASE_TEMPLATE_URl = 'https://templates.subquery.network';

Expand Down
Loading
Loading