import { BaseCommand, args, flags } from '@adonisjs/core/ace' import type { CommandOptions } from '@adonisjs/core/types/ace' import { readFile } from 'node:fs/promises' import RuleSets from '#services/rule_sets' import { signatureKey } from '#lib/rules/index' /** * Sets the dropped upload signatures (REBUILD.md, rules and recovery) from a * file of "toolgroup" lines, e.g. voyager 1's learned ones: * * psql … -At -F $'\t' -c "SELECT tool, group_name FROM upload_signatures WHERE dropped" * * Publishes a new rule set version (ranges record the version they ran with). */ export default class RulesSignatures extends BaseCommand { static commandName = 'rules:signatures' static description = 'Publishes a rule set with the dropped upload signatures from a file' static options: CommandOptions = { startApp: true } @args.string({ description: 'File of "toolgroup" lines (more columns and # lines are ignored), e.g. database/data/voyager1_signatures.tsv' }) declare file: string @flags.string({ description: 'Note stored with the rule set version' }) declare note?: string @flags.boolean({ description: 'Add to the current signatures instead of replacing them' }) declare add: boolean async run() { const lines = (await readFile(this.file, 'utf8')).split('\n').map((l) => l.trim()).filter((l) => l && !l.startsWith('#')) const signatures = new Set() for (const line of lines) { const [tool, group] = line.split('\t') if (!tool || !group) throw new Error(`Not "toolgroup": ${line}`) signatures.add(signatureKey(tool, group)) } const { version: _v, priorities: _p, ...current } = await RuleSets.current() const before = new Set(current.droppedSignatures) const next = this.add ? new Set([...before, ...signatures]) : signatures if (next.size === before.size && [...next].every((s) => before.has(s))) { this.logger.info(`Unchanged: ${before.size} dropped signatures already.`) return } const version = await RuleSets.publish( { ...current, droppedSignatures: [...next].sort() }, this.note ?? `${next.size} dropped signatures from ${this.file}` ) const added = [...next].filter((s) => !before.has(s)).length const removed = [...before].filter((s) => !next.has(s)).length this.logger.info(`Rule set ${version}: ${next.size} dropped signatures (${added} added, ${removed} removed).`) } }