Skip to content
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
75 changes: 69 additions & 6 deletions src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,16 @@ import chalk from 'chalk';
import inquirer from 'inquirer';
import { isNil, uniqBy } from 'lodash';

import { ApiError, getOrganization, getSquid, listOrganizations, listUserSquids, SquidRequest } from './api';
import {
ApiError,
getOrganization,
getSquid,
listOrganizations,
listSquids,
listUserSquids,
Squid,
SquidRequest,
} from './api';
import { getTTY } from './tty';
import { formatSquidReference, printSquid } from './utils';

Expand All @@ -13,7 +22,7 @@ export const SUCCESS_CHECK_MARK = chalk.green('✓');
export abstract class CliCommand extends Command {
static baseFlags = {
interactive: Flags.boolean({
description: 'Disable interactive mode',
description: 'Enable interactive mode. Use --no-interactive to disable prompts for CI/scripts.',
required: false,
default: true,
allowNo: true,
Expand All @@ -32,7 +41,6 @@ export abstract class CliCommand extends Command {
this.log(chalk.dim(message));
}

// Haven't find a way to do it with native settings
validateSquidNameFlags(flags: { reference?: any; name?: any }) {
if (flags.reference || flags.name) return;

Expand All @@ -41,7 +49,13 @@ export abstract class CliCommand extends Command {
{
name: 'squid name',
validationFn: 'validateSquidName',
reason: 'One of the following must be provided: --reference, --name',
reason: [
'One of the following must be provided: --reference or --name',
'',
'Examples:',
' sqd <command> --reference my-squid@v1',
' sqd <command> --name my-squid --slot <slot>',
].join('\n'),
status: 'failed',
},
],
Expand Down Expand Up @@ -165,6 +179,53 @@ export abstract class CliCommand extends Command {
return await this.getOrganizationPrompt(organizations, { using, interactive });
}

async promptSquid(organization: { code: string }, { interactive }: { interactive?: boolean } = {}): Promise<Squid> {
const squids = await listSquids({ organization });

if (squids.length === 0) {
return this.error(`No squids found in organization "${organization.code}".`);
}

if (squids.length === 1) {
return squids[0];
}

const { stdin, stdout } = getTTY();
if (!stdin || !stdout || !interactive) {
return this.error(
[
`Organization "${organization.code}" has ${squids.length} squids:`,
...squids.map((s) => ` - ${formatSquidReference({ name: s.name, slot: s.slot })}`),
``,
`Please specify the squid using "--reference" or "--name" flag.`,
`Example: sqd <command> --reference ${squids[0].name}@${squids[0].slot}`,
].join('\n'),
);
}

const prompt = inquirer.createPromptModule({ input: stdin, output: stdout });
const { squid } = await prompt([
{
name: 'squid',
type: 'list',
message: 'Please choose a squid:',
choices: squids.map((s) => {
const ref = formatSquidReference({ name: s.name, slot: s.slot });
const tags = s.tags.length ? ` (${s.tags.map((t) => t.name).join(', ')})` : '';
return {
name: `${ref}${tags}`,
value: s,
};
}),
},
]);

stdin.destroy();
stdout.destroy();

return squid;
}

private async getOrganizationPrompt<T extends { code: string; name: string }>(
organizations: T[],
{
Expand All @@ -186,8 +247,10 @@ export abstract class CliCommand extends Command {
return this.error(
[
`You have ${organizations.length} organizations:`,
...organizations.map((o) => `${chalk.dim(' - ')}${chalk.dim(o.code)}`),
`Please specify one of them explicitly ${using}`,
...organizations.map((o) => ` - ${o.code}`),
``,
`Please specify one of them explicitly ${using}.`,
`Example: sqd <command> --org ${organizations[0].code}`,
].join('\n'),
);
}
Expand Down
4 changes: 3 additions & 1 deletion src/commands/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import { DEFAULT_API_URL, setConfig } from '../config';
export default class Auth extends CliCommand {
static description = `Log in to the Cloud`;

static examples = ['sqd auth -k sqd_xyz123...'];

static flags = {
key: Flags.string({
char: 'k',
description: 'Cloud auth key. Log in to https://app.subsquid.io to create or update your key.',
description: 'Cloud auth key. Log in to https://cloud.sqd.dev to create or update your key.',
required: true,
}),
host: Flags.string({
Expand Down
50 changes: 35 additions & 15 deletions src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export function resolveManifest(
}

function example(command: string, description: string) {
// return [chalk.dim(`// ${description}`), command].join('\r\n');
return `${command} ${chalk.dim(`// ${description}`)}`;
}

Expand All @@ -77,7 +76,7 @@ export default class Deploy extends DeployCommand {
),
];

static help = 'If squid flags are not specified, the they will be retrieved from the manifest or prompted.';
static help = 'If squid flags are not specified, they will be retrieved from the manifest or prompted.';

static args = {
source: Args.directory({
Expand Down Expand Up @@ -149,6 +148,11 @@ export default class Deploy extends DeployCommand {
required: false,
default: false,
}),
'allow-postgres-deletion': Flags.boolean({
description: 'Allow deleting an existing Postgres addon when deploying a manifest without one',
required: false,
default: false,
}),
};

async run(): Promise<void> {
Expand All @@ -160,17 +164,12 @@ export default class Deploy extends DeployCommand {
'hard-reset': hardReset,
'stream-logs': streamLogs,
'add-tag': addTag,
'allow-postgres-deletion': allowPostgresDeletion,
reference,
...flags
},
} = await this.parse(Deploy);

const isUrl = source.startsWith('http://') || source.startsWith('https://');
if (isUrl) {
this.log(`🦑 Releasing the squid from remote`);
return this.error('Not implemented yet');
}

if (interactive && hardReset) {
const { confirm } = await inquirer.prompt([
{
Expand All @@ -193,7 +192,6 @@ export default class Deploy extends DeployCommand {
const overrides = reference || (pick(flags, 'slot', 'name', 'tag', 'org') as Partial<ParsedSquidReference>);

let manifest = res.manifest;
// FIXME: it is not possible to override org atm
if (entries(overrides).some(([k, v]) => k !== 'org' && get(manifest, k) !== v)) {
// we need to do it to keep formatting the same
const manifestRaw = Manifest.replace(res.manifestRaw, {});
Expand Down Expand Up @@ -272,7 +270,7 @@ export default class Deploy extends DeployCommand {
/**
* Warn if the existing squid has a Postgres addon but the new manifest removes it
*/
if (!hardReset && target?.addons?.postgres && !manifest.deploy?.addons?.postgres) {
if (!hardReset && target?.addons?.postgres && !manifest.deploy?.addons?.postgres && !allowPostgresDeletion) {
const confirmed = await this.promptPostgresDeletion(target, { interactive });
if (!confirmed) return;
}
Expand Down Expand Up @@ -307,6 +305,12 @@ export default class Deploy extends DeployCommand {
const deployment = await this.pollDeploy({ organization, deploy });
if (!deployment || !deployment.squid) return;

const squidRef = formatSquidReference({
org: deployment.organization.code,
name: deployment.squid.name,
slot: deployment.squid.slot,
});

if (target) {
this.logDeployResult(UPDATE_COLOR, `The squid ${printSquid(target)} has been successfully updated`);
} else {
Expand Down Expand Up @@ -344,7 +348,13 @@ export default class Deploy extends DeployCommand {
if (interactive) {
this.warn(warning.join('\n'));
} else {
this.error([...warning, `Please do it explicitly ${using}`].join('\n'));
this.error(
[
...warning,
`Please do it explicitly ${using}.`,
`Example: sqd deploy . --name ${squid.name} --allow-update`,
].join('\n'),
);
}

const { confirm } = await inquirer.prompt([
Expand All @@ -362,7 +372,7 @@ export default class Deploy extends DeployCommand {
private async promptOverrideConflict(
dest: string,
src: string,
{ using = 'using "--allow--manifest-override" flag', interactive }: { using?: string; interactive?: boolean } = {},
{ using = 'using "--allow-manifest-override" flag', interactive }: { using?: string; interactive?: boolean } = {},
) {
const warning = [
'Conflict detected!',
Expand All @@ -375,7 +385,9 @@ export default class Deploy extends DeployCommand {
if (interactive) {
this.warn(warning);
} else {
this.error([warning, `Please do it explicitly ${using}`].join('\n'));
this.error(
[warning, `Please do it explicitly ${using}.`, `Example: sqd deploy . --allow-manifest-override`].join('\n'),
);
}

this.log(
Expand All @@ -400,7 +412,13 @@ export default class Deploy extends DeployCommand {
const warning = `The new manifest does not include "addons.postgres", but the squid ${printSquid(squid)} currently has a Postgres database. Deploying will permanently delete the database and all its data.`;

if (!interactive) {
this.error([warning, `Please do it explicitly ${using}`].join('\n'));
this.error(
[
warning,
`Please do it explicitly using "--allow-postgres-deletion" flag.`,
`Example: sqd deploy . --allow-postgres-deletion`,
].join('\n'),
);
}

this.warn(warning);
Expand Down Expand Up @@ -428,7 +446,9 @@ export default class Deploy extends DeployCommand {
if (interactive) {
this.warn(warning);
} else {
this.error([warning, `Please specify it explicitly ${using}`].join('\n'));
this.error(
[warning, `Please specify it explicitly ${using}.`, `Example: sqd deploy . --name my-squid`].join('\n'),
);
}

const { input } = await inquirer.prompt([
Expand Down
4 changes: 1 addition & 3 deletions src/commands/deploy.unit.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,7 @@ describe('Deploy', () => {
? { postgres: { connections: [], disk: { usageStatus: 'NORMAL', usedBytes: 0, totalBytes: 0 } } }
: undefined,
manifest: {
current: pgVersion
? { deploy: { addons: { postgres: { version: pgVersion } } } }
: { deploy: {} },
current: pgVersion ? { deploy: { addons: { postgres: { version: pgVersion } } } } : { deploy: {} },
raw: '',
},
} as any;
Expand Down
7 changes: 1 addition & 6 deletions src/commands/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export default class Explorer extends CliCommand {
static hidden = true;

static description = 'Open a visual explorer for the Cloud deployments';
// static hidden = true;

static flags = {
org: Flags.string({
char: 'o',
Expand All @@ -27,9 +27,6 @@ export default class Explorer extends CliCommand {
const screen = blessed.screen({
smartCSR: true,
fastCSR: true,
// dockBorders: true,
debug: true,
// autoPadding: true,
fullUnicode: true,
});

Expand Down Expand Up @@ -59,8 +56,6 @@ export default class Explorer extends CliCommand {
return process.exit(0);
});

// screen.program.disableMouse();

manager.focus();
screen.render();
}
Expand Down
8 changes: 7 additions & 1 deletion src/commands/gateways/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ export default class Ls extends CliCommand {

static description = 'List available gateways';

static examples = [
'sqd gateways list',
'sqd gateways list --type evm',
'sqd gateways list --type evm --name ethereum --chain 1',
];

static flags = {
type: Flags.string({
char: 't',
Expand Down Expand Up @@ -97,7 +103,7 @@ export default class Ls extends CliCommand {
},
});

gateways.map(({ chainName, chainId, chainSS58Prefix, providers }) => {
gateways.forEach(({ chainName, chainId, chainSS58Prefix, providers }) => {
const row = [chainName, chalk.dim(chainId || chainSS58Prefix || '-'), providers[0].dataSourceUrl];
table.push(row);
});
Expand Down
25 changes: 23 additions & 2 deletions src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ const SQUID_TEMPLATE_DESC = [
export default class Init extends CliCommand {
static description = 'Setup a new squid project from a template or github repo';

static examples = [
'sqd init my-squid --template evm',
'sqd init my-squid -t substrate',
'sqd init my-squid -t https://github.com/user/repo -d ./target-dir',
'sqd init my-squid -t evm -r',
];

static args = {
name: Args.string({ description: SQUID_NAME_DESC.join('\n'), required: true }),
};
Expand All @@ -105,7 +112,7 @@ export default class Init extends CliCommand {
async run() {
const {
args: { name },
flags: { template, dir, remove },
flags: { template, dir, remove, interactive },
} = await this.parse(Init);

const localDir = path.resolve(dir || name);
Expand All @@ -122,6 +129,18 @@ export default class Init extends CliCommand {

let resolvedTemplate = template || '';
if (!template) {
if (!interactive) {
const templateNames = Object.keys(TEMPLATE_ALIASES).join(', ');
return this.error(
[
`No template specified.`,
`Available templates: ${templateNames}`,
``,
`Example: sqd init ${name} --template evm`,
].join('\n'),
);
}

const { alias } = await inquirer.prompt({
name: 'alias',
message: `Please select one of the templates for your "${name}" squid:`,
Expand Down Expand Up @@ -157,7 +176,9 @@ export default class Init extends CliCommand {
/** Remove deprecated files from repositories **/
try {
await asyncFs.rm(path.resolve(localDir, 'Dockerfile'));
} catch (e) {}
} catch (e: any) {
if (e.code !== 'ENOENT') throw e;
}

const manifestPath = path.resolve(localDir, 'squid.yaml');
try {
Expand Down
Loading
Loading