Skip to main content
Glama
deleonio
by deleonio
task-runner.ts5 kB
import chalk from 'chalk'; import fs from 'fs'; import path from 'path'; import semver from 'semver'; import { Configuration, Migrate } from '../../types'; import { logAndCreateError } from '../shares/reuse'; import { AbstractTask } from './abstract-task'; const MIN_VERSION_OF_PUBLIC_UI = '1.4.2'; export class TaskRunner { private readonly tasks: Map<string, AbstractTask> = new Map(); private baseDir: string = '/'; private cliVersion: string = '0.0.0'; private projectVersion: string = '0.0.0'; private readonly config: Configuration = { migrate: { tasks: {}, }, }; public constructor(baseDir: string, cliVersion: string, projectVersion: string, config: Configuration) { this.setBaseDir(baseDir); this.setCliVersion(cliVersion); this.setProjectVersion(projectVersion); this.setConfig({ ...config }); // clone config } private setBaseDir(baseDir: string): void { if (!fs.existsSync(path.resolve(process.cwd(), baseDir))) { throw logAndCreateError(`Base directory "${baseDir}" does not exist`); } this.baseDir = baseDir; } private setCliVersion(version: string): void { if (semver.valid(version) === null) { throw logAndCreateError(`Invalid CLI version: ${version}`); } this.cliVersion = version; } public setProjectVersion(version: string): void { if (semver.valid(version) === null) { throw logAndCreateError(`Invalid project version: ${version}`); } if (this.projectVersion !== version) { this.projectVersion = version; } } private setConfig(config: Configuration): void { if (config.migrate?.tasks) { this.config.migrate!.tasks = { ...this.config.migrate!.tasks, ...config.migrate?.tasks, }; } } public registerTasks(tasks: AbstractTask[]): void { tasks.forEach((task) => { const taskDependencies = task.getTaskDependencies(); if (taskDependencies.length > 0) { this.registerTasks(taskDependencies); } if ( semver.gtr(this.projectVersion, task.getVersionRange(), { includePrerelease: true, }) ) { console.log( `Task "${task.getTitle()}" will be excluded. The current version (${ this.projectVersion }) is greater than the task version range (${task.getVersionRange()}).`, ); this.config.migrate!.tasks[task.getIdentifier()] = false; } else if ( semver.ltr(this.cliVersion, task.getVersionRange(), { includePrerelease: true, }) ) { console.log( `Task "${task.getTitle()}" will be excluded. The target version (${ this.cliVersion }) is lower than the task version range (${task.getVersionRange()}).`, ); this.config.migrate!.tasks[task.getIdentifier()] = false; } else { this.tasks.set(task.getIdentifier(), task); } }); } private registerTask(task: AbstractTask): void { this.registerTasks([task]); } private runTask(task: AbstractTask): void { if (this.config.migrate?.tasks[task.getIdentifier()] === false) { task.setStatus('skipped'); } else { (this.config.migrate as unknown as Migrate).tasks[task.getIdentifier()] = true; if ( task.getStatus() === 'pending' && semver.satisfies(this.projectVersion, task.getVersionRange(), { includePrerelease: true, }) ) { // task.setStatus('running'); only of the task is async if (!this.tasks.has(task.getIdentifier())) { this.registerTask(task); } task.run(this.baseDir); task.setStatus('done'); } } } private dependentTaskRun(task: AbstractTask, taskDependencies: AbstractTask[]) { taskDependencies.forEach((dependentTask) => { this.dependentTaskRun(dependentTask, dependentTask.getTaskDependencies()); }); if (taskDependencies.every((dependentTask) => dependentTask.getStatus() === 'done')) { this.runTask(task); } } public run(): void { this.tasks.forEach((task) => { this.dependentTaskRun(task, task.getTaskDependencies()); }); } public getPendingMinVersion(): string { let version: string = this.cliVersion; this.tasks.forEach((task) => { if (task.getStatus() === 'pending') { const minVersion = semver.minVersion(task.getVersionRange())?.raw ?? this.cliVersion; if (semver.gt(version, minVersion)) { version = minVersion; } } }); return semver.maxSatisfying([version], `>=${MIN_VERSION_OF_PUBLIC_UI}`) ?? MIN_VERSION_OF_PUBLIC_UI; } public getStatus(outline = false): { done: number; pending: number; total: number; nextVersion: string | null; config: Configuration; } { let done = 0; let pending = 0; this.tasks.forEach((task) => { switch (task.getStatus()) { case 'done': done++; break; case 'pending': pending++; break; } if (outline) { const status = task.getStatus(); console.log(`- ${task.getTitle()}:`, status === 'done' ? chalk.green(status) : chalk.yellow(status)); } }); return { done: done, pending: pending, total: this.tasks.size, nextVersion: this.getPendingMinVersion(), config: this.config, }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/deleonio/public-ui-kolibri'

If you have feedback or need assistance with the MCP directory API, please join our Discord server