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
11 changes: 2 additions & 9 deletions packages/cli/src/commands/authenticate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,11 @@ import { FlagsType } from '../types'
export type AuthenticateFlags = FlagsType<typeof Authenticate>

export default class Authenticate extends Command {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
run(): Promise<any> {
run(): Promise<void> {
throw new Error('Method not implemented.')
}

static override description = 'Authenticate with Mimic by storing your API key locally'

static override examples = [
'<%= config.bin %> <%= command.id %>',
'<%= config.bin %> <%= command.id %> --profile staging',
'<%= config.bin %> <%= command.id %> --profile production --api-key YOUR_API_KEY',
]
static override hidden = true

static flags = {
profile: Flags.string({
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/commands/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FlagsType } from '../types'

import Codegen from './codegen'
import Compile from './compile'
import Functions from './functions'

export type BuildFlags = FlagsType<typeof Build>

Expand All @@ -15,13 +16,14 @@ export default class Build extends Command {
]

static override flags = {
...Functions.flags,
...Codegen.flags,
...Compile.flags,
}

public async run(): Promise<void> {
const { flags } = await this.parse(Build)
await Build.build(this, flags)
await Functions.runFunctions(this, flags, Build.build, 'build')
}

public static async build(cmd: Command, flags: BuildFlags): Promise<void> {
Expand Down
17 changes: 12 additions & 5 deletions packages/cli/src/commands/codegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { AbisInterfaceGenerator, InputsInterfaceGenerator, ManifestHandler } fro
import log from '../log'
import { FlagsType, Manifest } from '../types'

import Functions, { DefaultFunctionConfig } from './functions'

export type CodegenFlags = FlagsType<typeof Codegen>

export default class Codegen extends Command {
Expand All @@ -17,11 +19,16 @@ export default class Codegen extends Command {
]

static override flags = {
manifest: Flags.string({ char: 'm', description: 'Specify a custom manifest file path', default: 'manifest.yaml' }),
...Functions.flags,
manifest: Flags.string({
char: 'm',
description: 'Specify a custom manifest file path',
default: DefaultFunctionConfig.manifest,
}),
'types-directory': Flags.string({
char: 't',
description: 'Output directory for generated types',
default: './src/types',
default: DefaultFunctionConfig['types-directory'],
}),
clean: Flags.boolean({
char: 'c',
Expand All @@ -32,7 +39,7 @@ export default class Codegen extends Command {

public async run(): Promise<void> {
const { flags } = await this.parse(Codegen)
await Codegen.codegen(this, flags)
await Functions.runFunctions(this, flags, Codegen.codegen, 'code generation')
}

public static async codegen(cmd: Command, flags: CodegenFlags): Promise<void> {
Expand All @@ -59,8 +66,8 @@ export default class Codegen extends Command {
}

if (!fs.existsSync(typesDir)) fs.mkdirSync(typesDir, { recursive: true })
this.generateAbisCode(manifest, typesDir, manifestDir)
this.generateInputsCode(manifest, typesDir)
Codegen.generateAbisCode(manifest, typesDir, manifestDir)
Codegen.generateInputsCode(manifest, typesDir)
log.stopAction()
}

Expand Down
15 changes: 11 additions & 4 deletions packages/cli/src/commands/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import { execBinCommand } from '../lib/packageManager'
import log from '../log'
import { FlagsType } from '../types'

import Functions, { DefaultFunctionConfig } from './functions'

export type CompileFlags = FlagsType<typeof Compile>

export default class Compile extends Command {
Expand All @@ -17,14 +19,19 @@ export default class Compile extends Command {
]

static override flags = {
function: Flags.string({ char: 'f', description: 'Function to compile', default: 'src/function.ts' }),
manifest: Flags.string({ char: 'm', description: 'Manifest to validate', default: 'manifest.yaml' }),
'build-directory': Flags.string({ char: 'b', description: 'Output directory for compilation', default: './build' }),
...Functions.flags,
function: Flags.string({ char: 'f', description: 'Function to compile', default: DefaultFunctionConfig.function }),
manifest: Flags.string({ char: 'm', description: 'Manifest to validate', default: DefaultFunctionConfig.manifest }),
'build-directory': Flags.string({
char: 'b',
description: 'Output directory for compilation',
default: DefaultFunctionConfig['build-directory'],
}),
}

public async run(): Promise<void> {
const { flags } = await this.parse(Compile)
await Compile.compile(this, flags)
await Functions.runFunctions(this, flags, Compile.compile, 'compilation')
}

public static async compile(
Expand Down
6 changes: 4 additions & 2 deletions packages/cli/src/commands/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { FlagsType } from '../types'

import Authenticate from './authenticate'
import Build from './build'
import Functions from './functions'

const MIMIC_REGISTRY_DEFAULT = 'https://api-protocol.mimic.fi'

Expand All @@ -27,6 +28,7 @@ export default class Deploy extends Command {
]

static override flags = {
...Functions.flags,
...Authenticate.flags,
...Build.flags,
'build-directory': Flags.string({
Expand All @@ -40,7 +42,7 @@ export default class Deploy extends Command {

public async run(): Promise<void> {
const { flags } = await this.parse(Deploy)
await Deploy.deploy(this, flags)
await Functions.runFunctions(this, flags, Deploy.deploy, 'deployment')
}

public static async deploy(cmd: Command, flags: DeployFlags): Promise<void> {
Expand Down Expand Up @@ -69,7 +71,7 @@ export default class Deploy extends Command {
}

log.startAction('Uploading to Mimic Registry')
const CID = await this.uploadToRegistry(cmd, neededFiles, credentials, registryUrl)
const CID = await Deploy.uploadToRegistry(cmd, neededFiles, credentials, registryUrl)
console.log(`IPFS CID: ${log.highlightText(CID)}`)
log.stopAction()

Expand Down
133 changes: 133 additions & 0 deletions packages/cli/src/commands/functions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import { Command, Flags } from '@oclif/core'
import * as fs from 'fs'
import * as yaml from 'js-yaml'
import { z } from 'zod'

import log from '../log'
import { FlagsType } from '../types'

export type FunctionsFlags = FlagsType<typeof Functions>

export const FunctionConfigSchema = z.object({
name: z.string().min(1, 'Function name is required'),
manifest: z.string().min(1, 'Manifest path is required'),
function: z.string().min(1, 'Function path is required'),
'build-directory': z.string().min(1, 'Build directory is required'),
'types-directory': z.string().min(1, 'Types directory is required'),
})

export type FunctionConfig = z.infer<typeof FunctionConfigSchema>

export const MimicConfigSchema = z.object({
functions: z.array(FunctionConfigSchema).min(1, 'At least one function is required'),
})

export const DefaultFunctionConfig = {
name: '',
manifest: 'manifest.yaml',
function: 'src/function.ts',
'build-directory': './build',
'types-directory': './src/types',
} as const

const MIMIC_CONFIG_FILE = 'mimic.yaml'

export default class Functions extends Command {
run(): Promise<void> {
throw new Error('Method not implemented.')
}

static override hidden = true

static flags = {
'config-file': Flags.string({
description: `Path to the ${MIMIC_CONFIG_FILE} file, this overrides other parameters like build-directory and function`,
default: MIMIC_CONFIG_FILE,
}),
'no-config': Flags.boolean({
description: `Do not read ${MIMIC_CONFIG_FILE}; use defaults and explicit flags instead`,
default: false,
}),
include: Flags.string({
description: `When ${MIMIC_CONFIG_FILE} exists, only run tasks with these names (space-separated)`,
multiple: true,
exclusive: ['exclude'],
char: 'i',
}),
exclude: Flags.string({
description: `When ${MIMIC_CONFIG_FILE} exists, exclude tasks with these names (space-separated)`,
multiple: true,
exclusive: ['include'],
char: 'e',
}),
}

public static async runFunctions<T extends FunctionsFlags & Partial<FunctionConfig>>(
cmd: Command,
flags: T,
cmdLogic: (cmd: Command, flags: T) => Promise<void>,
cmdActions: string
): Promise<void> {
const functions = Functions.filterFunctions(cmd, flags)
for (const func of functions) {
log.startAction(`\nStarting ${cmdActions} for function ${func.name ? func.name : func.function}`)
await cmdLogic(cmd, { ...flags, ...func } as T)
}
}

public static filterFunctions(cmd: Command, flags: FunctionsFlags & Partial<FunctionConfig>): FunctionConfig[] {
if (flags['no-config']) {
return [{ ...DefaultFunctionConfig, ...flags }]
}

if (!fs.existsSync(flags['config-file'])) {
if (flags['config-file'] !== MIMIC_CONFIG_FILE) {
cmd.error(`Could not find ${flags['config-file']}`, { code: 'ConfigNotFound' })
}

// If doesn't exist return the default with the flags the user added
return [{ ...DefaultFunctionConfig, ...flags }]
}

const fileContents = fs.readFileSync(flags['config-file'], 'utf8')
const rawConfig = yaml.load(fileContents)

if (!rawConfig || (typeof rawConfig === 'object' && Object.keys(rawConfig).length === 0)) {
cmd.error(`Invalid ${MIMIC_CONFIG_FILE} configuration: file is empty.`)
}

try {
let { functions } = MimicConfigSchema.parse(rawConfig)

if (flags.include && flags.include.length > 0) {
Functions.checkMissingFunctions(cmd, functions, flags.include)
functions = functions.filter((fn) => flags.include!.includes(fn.name))
}

if (flags.exclude && flags.exclude.length > 0) {
Functions.checkMissingFunctions(cmd, functions, flags.exclude)
functions = functions.filter((fn) => !flags.exclude!.includes(fn.name))
}

return functions
} catch (error) {
if (error instanceof z.ZodError) {
const errors = error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join('\n')
cmd.error(`Invalid ${MIMIC_CONFIG_FILE} configuration:\n${errors}`, { code: 'InvalidConfig' })
}
throw error
}
}

private static checkMissingFunctions(
cmd: Command,
functions: FunctionConfig[],
filteredFunctionNames: string[]
): void {
const functionNames = new Set(functions.map((fn) => fn.name))
const missingFunctions = filteredFunctionNames.filter((name) => !functionNames.has(name))
if (missingFunctions.length > 0) {
cmd.warn(`Functions not found in ${MIMIC_CONFIG_FILE}: ${missingFunctions.join(', ')}`)
}
}
}
4 changes: 2 additions & 2 deletions packages/cli/src/commands/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ export default class Init extends Command {
cmd.error(`Failed to clone template repository. Details: ${error}`)
}

this.installDependencies(absDir)
this.runCodegen(absDir)
Init.installDependencies(absDir)
Init.runCodegen(absDir)
log.stopAction()
console.log('New project initialized!')
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export default class Login extends Command {
}
}

this.saveAndConfirm(cmd, profileName || CredentialsManager.getDefaultProfileName(), apiKey, flags['force-login'])
Login.saveAndConfirm(cmd, profileName || CredentialsManager.getDefaultProfileName(), apiKey, flags['force-login'])
}

private static async saveAndConfirm(
Expand Down
14 changes: 13 additions & 1 deletion packages/cli/src/commands/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { execBinCommand } from '../lib/packageManager'
import { FlagsType } from '../types'

import Build from './build'
import Functions from './functions'

export type TestFlags = FlagsType<typeof Test>

Expand All @@ -14,6 +15,17 @@ export default class Test extends Command {
static override examples = ['<%= config.bin %> <%= command.id %> --directory ./tests']

static override flags = {
...{
...Functions.flags,
include: {
...Functions.flags.include,
description: Functions.flags.include.description + '. Only for building, it does not affect testing',
},
exclude: {
...Functions.flags.exclude,
description: Functions.flags.exclude.description + '. Only for building, it does not affect testing',
},
},
...Build.flags,
directory: Flags.string({ char: 'd', description: 'Path to the testing directory', default: './tests' }),
'skip-build': Flags.boolean({ description: 'Skip build before testing', default: false }),
Expand All @@ -29,7 +41,7 @@ export default class Test extends Command {
const baseDir = path.resolve('./')
const testPath = path.join(baseDir, directory)

if (!skipBuild) await Build.build(cmd, flags)
if (!skipBuild) await Functions.runFunctions(cmd, flags, Build.build, 'building')

const result = execBinCommand('tsx', ['./node_modules/mocha/bin/mocha.js', `${testPath}/**/*.spec.ts`], baseDir)
cmd.exit(result.status ?? 1)
Expand Down
Loading