Skip to content

Commit b8b06af

Browse files
trigger processing
1 parent 433e2d1 commit b8b06af

3 files changed

Lines changed: 119 additions & 11 deletions

File tree

src/programs/pkg/add.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {graph_query, json_convert_dep_sets_to_arrs, PkgAtVersion, repo_query} from ".";
1+
import {graph_query, json_convert_dep_sets_to_arrs, PkgAtVersion, repo_query, triggers} from ".";
22
import {mount_and_register_with_output} from "../../prog_registry";
33

44
import {ANSI, NEWLINE} from "../../term_ctl";
@@ -250,9 +250,20 @@ export const add_subcommand = async (data: ProgramMainData, depended_by?: PkgAtV
250250
term.writeln(`${FG.green}Package ${pkg_name}@${pkg_version} installed.${STYLE.reset_all}`);
251251

252252
// check for any triggers
253-
// TODO: run the triggers
254-
if (meta && meta.triggers) {
255-
console.log(Object.keys(meta.triggers));
253+
if (meta && meta.triggers && Object.keys(meta.triggers).length > 0) {
254+
term.writeln(`${FG.cyan}Processing install triggers...${STYLE.reset_all}`);
255+
256+
for (const [trigger_name, trigger_data] of Object.entries(meta.triggers)) {
257+
if (!await triggers.trigger_exists(fs, trigger_name)) {
258+
term.writeln(`${FG.yellow}Warning: trigger '${trigger_name}' is not recognised and will be skipped.${STYLE.reset_all}`);
259+
continue;
260+
}
261+
262+
term.writeln(`${FG.cyan}Processing install trigger: ${trigger_name}...${STYLE.reset_all}`);
263+
await triggers.process_install_trigger(term, trigger_name, trigger_data, pkg_name, pkg_version);
264+
}
265+
266+
term.writeln(`${FG.cyan}Install trigger processing complete.${STYLE.reset_all}`);
256267
}
257268
}
258269

src/programs/pkg/index.ts

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {add_subcommand} from "./add";
22
import {remove_subcommand} from "./remove";
33

4-
import {ANSI} from "../../term_ctl";
4+
import {ANSI, WrappedTerminal} from "../../term_ctl";
55
import type {Program} from "../../types";
66
import type {AbstractFileSystem} from "../../filesystem";
77
import {list_subcommand} from "./list";
@@ -19,6 +19,8 @@ const GRAPH_PATH = GRAPH_DIR + "/graph.json";
1919

2020
const BIN_DIR = "/usr/bin";
2121

22+
const TRIGGER_DIR = "/var/lib/pkg/triggers";
23+
2224
// TODO: subcommand template / helper
2325

2426
const append_url_pathnames = (url: URL, pathnames: string[]) => {
@@ -414,6 +416,85 @@ export const graph_query = {
414416
}
415417
}
416418

419+
interface TriggerFile {
420+
install_exec?: string;
421+
uninstall_exec?: string;
422+
}
423+
424+
export const triggers = {
425+
load_trigger_file: async (fs: AbstractFileSystem, trigger_name: string): Promise<TriggerFile | null> => {
426+
const trigger_path = fs.join(TRIGGER_DIR, trigger_name + ".json");
427+
if (!(await fs.exists(trigger_path))) {
428+
return null;
429+
}
430+
431+
const data = await fs.read_file(trigger_path) as string;
432+
return JSON.parse(data) as TriggerFile;
433+
},
434+
435+
trigger_exists: async (fs: AbstractFileSystem, trigger_name: string): Promise<boolean> => {
436+
return (await triggers.load_trigger_file(fs, trigger_name)) !== null;
437+
},
438+
439+
// returns boolean indicating if the trigger was found and processed
440+
process_install_trigger: async (term: WrappedTerminal, trigger_name: string, data: unknown, pkg_name: string, pkg_version: string) => {
441+
const fs = term.get_fs();
442+
443+
const trigger = await triggers.load_trigger_file(fs, trigger_name);
444+
if (!trigger) {
445+
return false;
446+
}
447+
448+
if (!trigger.install_exec) {
449+
// nothing to do
450+
return true;
451+
}
452+
453+
const data_str = JSON.stringify(data);
454+
const spawn_result = term.spawn(trigger.install_exec, [pkg_name, pkg_version, data_str]);
455+
456+
try {
457+
const exit_code = await spawn_result.completion;
458+
if (exit_code !== 0) {
459+
term.writeln(`${ANSI.PREFABS.error}Warning: trigger ${trigger_name} exited with code ${exit_code}.${ANSI.STYLE.reset_all}`);
460+
}
461+
} catch (e) {
462+
term.writeln(`${ANSI.PREFABS.error}Warning: trigger ${trigger_name} failed: ${e}.${ANSI.STYLE.reset_all}`);
463+
}
464+
465+
return true;
466+
},
467+
468+
// returns boolean indicating if the trigger was found and processed
469+
process_uninstall_trigger: async (term: WrappedTerminal, trigger_name: string, data: unknown, pkg_name: string, pkg_version: string) => {
470+
const fs = term.get_fs();
471+
472+
const trigger = await triggers.load_trigger_file(fs, trigger_name);
473+
if (!trigger) {
474+
return false;
475+
}
476+
477+
if (!trigger.uninstall_exec) {
478+
// nothing to do
479+
return true;
480+
}
481+
482+
const data_str = JSON.stringify(data);
483+
const spawn_result = term.spawn(trigger.uninstall_exec, [pkg_name, pkg_version, data_str]);
484+
485+
try {
486+
const exit_code = await spawn_result.completion;
487+
if (exit_code !== 0) {
488+
term.writeln(`${ANSI.PREFABS.error}Warning: trigger ${trigger_name} exited with code ${exit_code}.${ANSI.STYLE.reset_all}`);
489+
}
490+
} catch (e) {
491+
term.writeln(`${ANSI.PREFABS.error}Warning: trigger ${trigger_name} failed: ${e}.${ANSI.STYLE.reset_all}`);
492+
}
493+
494+
return true;
495+
}
496+
}
497+
417498
// extract from ANSI to make code less verbose
418499
const {STYLE, PREFABS} = ANSI;
419500

src/programs/pkg/remove.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {determine_program_name_from_js} from "../../prog_registry";
22
import { ANSI, NEWLINE } from "../../term_ctl";
33
import { ProgramMainData } from "../../types"
4-
import {graph_query} from "./index";
4+
import {graph_query, triggers} from "./index";
55

66
// extract from ANSI to make code less verbose
77
const { STYLE, PREFABS, FG } = ANSI;
@@ -119,13 +119,15 @@ export const remove_subcommand = async (data: ProgramMainData) => {
119119
}
120120
}
121121

122-
// last chance to read the triggers from meta.json before deleting
123-
let triggers = {};
122+
// last chance to read the triggers and version from meta.json before deleting
123+
let meta_triggers = {};
124+
let meta_version = "unknown";
124125
if (await fs.exists(fs.join(pkg_dir, "meta.json"))) {
125126
try {
126127
const meta_raw = await fs.read_file(fs.join(pkg_dir, "meta.json")) as string;
127128
const meta = JSON.parse(meta_raw);
128-
triggers = meta.triggers || {};
129+
meta_triggers = meta.triggers || {};
130+
meta_version = meta.version || "unknown";
129131
} catch (e) {
130132
term.writeln(`${FG.yellow + STYLE.bold}Warning: Could not read meta.json for package ${pkg}: ${e.message}${STYLE.reset_all}`);
131133
}
@@ -138,8 +140,22 @@ export const remove_subcommand = async (data: ProgramMainData) => {
138140
term.writeln(`${FG.green}Package '${pkg}' removed.${STYLE.reset_all}`);
139141

140142
// check for any removal triggers
141-
// TODO: run the triggers
142-
console.log(Object.keys(triggers));
143+
// check for any triggers
144+
if (meta_triggers && Object.keys(meta_triggers).length > 0) {
145+
term.writeln(`${FG.cyan}Processing uninstall triggers...${STYLE.reset_all}`);
146+
147+
for (const [trigger_name, trigger_data] of Object.entries(meta_triggers)) {
148+
if (!await triggers.trigger_exists(fs, trigger_name)) {
149+
term.writeln(`${FG.yellow}Warning: trigger '${trigger_name}' is not recognised and will be skipped.${STYLE.reset_all}`);
150+
continue;
151+
}
152+
153+
term.writeln(`${FG.cyan}Processing uninstall trigger: ${trigger_name}...${STYLE.reset_all}`);
154+
await triggers.process_uninstall_trigger(term, trigger_name, trigger_data, pkg, meta_version);
155+
}
156+
157+
term.writeln(`${FG.cyan}Uninstall trigger processing complete.${STYLE.reset_all}`);
158+
}
143159
}
144160

145161
term.writeln(`${NEWLINE}${FG.magenta + STYLE.bold}========================${STYLE.reset_all}${NEWLINE}`);

0 commit comments

Comments
 (0)