Skip to content
Open
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
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,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: this is redundant because Codegen.flags and Compile.flags already include Functions.flags, but I'd keep it anyway. To be explicit.

...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
124 changes: 124 additions & 0 deletions packages/cli/src/commands/functions.ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having an empty mimic.yaml, when I run yarn mimic compile, I get:
Image
Note ": Required" message.
It's an edge case, though.

Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
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 const MimicConfigSchema = z.object({
tasks: z.array(FunctionConfigSchema).min(1, 'At least one task is required'),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
tasks: z.array(FunctionConfigSchema).min(1, 'At least one task is required'),
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

export type FunctionConfig = {
name: string
manifest: string
function: string
'build-directory': string
'types-directory': string
}
Comment on lines +31 to +37
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alternatively:

export type FunctionConfig = z.infer<typeof FunctionConfigSchema>


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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's weird to have a command that can't be run.
We should at least hide it from the user. In other words, mimic --help shouldn't show functions command.
And/or, we could log a message instead of throwing an error. Something like:

console.log('The "functions" command is not intended to be run directly')

Same with authenticate.

}

static override description = 'Filters tasks based on a mimic.yaml configuration file'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static override description = 'Filters tasks based on a mimic.yaml configuration file'
static override description = `Filters functions based on a ${Functions.MIMIC_CONFIG_FILE} configuration file`


static MIMIC_CONFIG_FILE = 'mimic.yaml'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels a bit disorganized that the DefaultFunctionConfig constant is defined outside the class, while this other constant is defined as a static class property, doesn't it? I’d move this one outside.


static flags = {
'config-file': Flags.string({
description: `Path to the ${Functions.MIMIC_CONFIG_FILE} file, this overrides other parameters like build-directory and function`,
default: Functions.MIMIC_CONFIG_FILE,
}),
'no-config': Flags.boolean({
description: `Do not read ${Functions.MIMIC_CONFIG_FILE}; use defaults and explicit flags instead`,
default: false,
}),
include: Flags.string({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I run: yarn mimic codegen --include f1 f2 where f1 is defined in mimic.yaml but f2 isn't, the command only runs for f1 and silently ignores f2.
Should we include a message saying something like f2 not found?
Same with exclude.

description: `When ${Functions.MIMIC_CONFIG_FILE} exists, only run tasks with these names (space-separated)`,
multiple: true,
exclusive: ['exclude'],
char: 'i',
}),
exclude: Flags.string({
description: `When ${Functions.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(`Starting ${cmdActions} for function ${func.name}`)
Copy link
Member

@lgalende lgalende Feb 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit
When running a single function manually, the log is e.g.: Starting compilation for function ... ✔️ (note the space between "..." and "✔️")
this is because func.name = ''.
In this case, shall we show func.function instead?

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'] !== Functions.MIMIC_CONFIG_FILE) {
cmd.error(`Could not find ${flags['config-file']}`, { code: 'ConfigNotFound' })
}

// If doesn't exists return the default with the flags the user added
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo

Suggested change
// If doesn't exists return the default with the flags the user added
// 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)

try {
const config = MimicConfigSchema.parse(rawConfig)

let tasks = config.tasks || []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config.tasks is guaranteed by MimicConfigSchema

Suggested change
let tasks = config.tasks || []
let tasks = config.tasks


if (flags.include && flags.include.length > 0) {
tasks = tasks.filter((task) => flags.include!.includes(task.name))
}

if (flags.exclude && flags.exclude.length > 0) {
tasks = tasks.filter((task) => !flags.exclude!.includes(task.name))
}

return tasks
} catch (error) {
if (error instanceof z.ZodError) {
const errors = error.errors.map((e) => `${e.path.join('.')}: ${e.message}`).join('\n')
cmd.error(`Invalid ${Functions.MIMIC_CONFIG_FILE} configuration:\n${errors}`, { code: 'InvalidConfig' })
}
throw error
}
}
}
4 changes: 2 additions & 2 deletions packages/cli/src/commands/init.ts
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we create mimic.yaml containing default config on init?
This way, users easily know the expected structure.
We have the docs site for that tho...

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
4 changes: 3 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,7 @@ export default class Test extends Command {
static override examples = ['<%= config.bin %> <%= command.id %> --directory ./tests']

static override flags = {
...Functions.flags,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add a comment saying that exclude and include commands only affect the build, but not the test execution per se?

...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 +31,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
Loading