change architecture
This commit is contained in:
parent
cf8c7d280e
commit
0669ada3cc
26 changed files with 163 additions and 282 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/bin
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"deno.enable": true
|
||||||
|
}
|
16
build.sh
Executable file
16
build.sh
Executable file
|
@ -0,0 +1,16 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -e
|
||||||
|
cd "$(dirname "$(realpath "$0")")"
|
||||||
|
|
||||||
|
|
||||||
|
programs="gen_module install"
|
||||||
|
|
||||||
|
|
||||||
|
rm -fr bin
|
||||||
|
mkdir -p bin
|
||||||
|
|
||||||
|
|
||||||
|
for program in $programs
|
||||||
|
do
|
||||||
|
deno compile --output="bin/mbztr_$program" --allow-all "src/$program.ts"
|
||||||
|
done
|
60
complete.ts
60
complete.ts
|
@ -1,60 +0,0 @@
|
||||||
import { success_format, value_format, process_format, prompt, failure_format } from "./lib/utilities.ts"
|
|
||||||
import { Context, Config } from "./lib/context.ts"
|
|
||||||
|
|
||||||
// import { HelloWorldSetup } from "./parts/setup-hello-world.ts"
|
|
||||||
import { BatSetup } from "./parts/bat.ts"
|
|
||||||
|
|
||||||
const config: Config = {
|
|
||||||
working_directory: "./barnulfizator-wd"
|
|
||||||
}
|
|
||||||
const all = [
|
|
||||||
//new HelloWorldSetup(),
|
|
||||||
// nano
|
|
||||||
// sudo
|
|
||||||
// rustup
|
|
||||||
// paru
|
|
||||||
// zsh
|
|
||||||
// kitty
|
|
||||||
// lvim
|
|
||||||
// n
|
|
||||||
// tldr
|
|
||||||
new BatSetup(),
|
|
||||||
// rc
|
|
||||||
];
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
success_format("all components:\n")
|
|
||||||
+ all
|
|
||||||
.map(s => `- '${value_format(s.name)}'`)
|
|
||||||
.join("\n")
|
|
||||||
+ "\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
const context = new Context(config);
|
|
||||||
|
|
||||||
for (const setup of all) {
|
|
||||||
const input = await prompt(`Install '${setup.name}' ?`, ["y", "n"], "y");
|
|
||||||
if (input == "y") context.push_to_install(setup);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const setup = context.next_to_install();
|
|
||||||
if (setup == undefined) break;
|
|
||||||
|
|
||||||
console.log(process_format("Installing '") + value_format(setup.name) + process_format("' ..."))
|
|
||||||
const result = await setup.install(context);
|
|
||||||
|
|
||||||
if (result == "Ok") {
|
|
||||||
console.log(success_format("Installed '") + value_format(setup.name) + success_format("' successfully."));
|
|
||||||
context.set_installed(setup);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log(failure_format("Failed to install '") + value_format(setup.name) + failure_format("'."));
|
|
||||||
context.set_failed(setup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await main()
|
|
6
deno.json
Normal file
6
deno.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"fmt": {
|
||||||
|
"useTabs": true,
|
||||||
|
"lineWidth": 120
|
||||||
|
}
|
||||||
|
}
|
7
deno.lock
generated
Normal file
7
deno.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"version": "3",
|
||||||
|
"redirects": {
|
||||||
|
"https://deno.land/x/zod/mod.ts": "https://deno.land/x/zod@v3.22.4/mod.ts"
|
||||||
|
},
|
||||||
|
"remote": {}
|
||||||
|
}
|
1
install.sh
Executable file
1
install.sh
Executable file
|
@ -0,0 +1 @@
|
||||||
|
#!/bin/sh
|
|
@ -1,36 +0,0 @@
|
||||||
import { Setup } from "./setup.ts"
|
|
||||||
|
|
||||||
export type Config = {
|
|
||||||
working_directory: string,
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Context {
|
|
||||||
installed_names: string[];
|
|
||||||
to_install: Setup[];
|
|
||||||
working_dir: string;
|
|
||||||
|
|
||||||
constructor(config: Config) {
|
|
||||||
this.installed_names = [];
|
|
||||||
this.to_install = [];
|
|
||||||
this.working_dir = config.working_directory;
|
|
||||||
}
|
|
||||||
|
|
||||||
push_to_install(setup: Setup) {
|
|
||||||
this.to_install.push(setup)
|
|
||||||
}
|
|
||||||
|
|
||||||
set_installed(setup: Setup) {
|
|
||||||
const index = this.to_install.findIndex(e => e.name == setup.name);
|
|
||||||
if (index != -1) this.to_install.splice(index, 1);
|
|
||||||
this.installed_names.push(setup.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
set_failed(setup: Setup) {
|
|
||||||
const index = this.to_install.findIndex(e => e.name == setup.name);
|
|
||||||
if (index != -1) this.to_install.splice(index, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
next_to_install(): Setup | undefined {
|
|
||||||
return this.to_install[0]
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
export { styles } from "https://deno.land/x/ansi_styles@1.0.0/mod.ts";
|
|
15
lib/setup.ts
15
lib/setup.ts
|
@ -1,15 +0,0 @@
|
||||||
import { Context } from "./context.ts"
|
|
||||||
|
|
||||||
export type SetupResult = "Ok" | "Error";
|
|
||||||
|
|
||||||
export abstract class Setup {
|
|
||||||
name: string;
|
|
||||||
dependencies: string[];
|
|
||||||
|
|
||||||
constructor(options: { name: string, dependencies?: string[] }) {
|
|
||||||
this.dependencies = options.dependencies ?? [];
|
|
||||||
this.name = options.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract install(context: Context): Promise<SetupResult>;
|
|
||||||
}
|
|
105
lib/utilities.ts
105
lib/utilities.ts
|
@ -1,105 +0,0 @@
|
||||||
import { readLines } from "https://deno.land/std@0.140.0/io/mod.ts";
|
|
||||||
import { writeAll } from "https://deno.land/std@0.140.0/streams/conversion.ts ";
|
|
||||||
import { styles } from "./deps.ts"
|
|
||||||
|
|
||||||
export type CommandResult = {
|
|
||||||
status: "ok"
|
|
||||||
} | {
|
|
||||||
status: "Error",
|
|
||||||
stdout: string,
|
|
||||||
stderr: string
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Synthesize platform dependent shell command arguments. */
|
|
||||||
function shArgs(command: string): string[] {
|
|
||||||
if (Deno.build.os === "windows") {
|
|
||||||
return ["PowerShell.exe", "-Command", command];
|
|
||||||
} else {
|
|
||||||
const shellExe = Deno.env.get("SHELL") ?? "/bin/sh";
|
|
||||||
return [shellExe, "-c", command];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
0x6a j ┘
|
|
||||||
0x6b k ┐
|
|
||||||
0x6c l ┌
|
|
||||||
0x6d m └
|
|
||||||
0x6e n ┼
|
|
||||||
0x71 q ─
|
|
||||||
0x74 t ├
|
|
||||||
0x75 u ┤
|
|
||||||
0x76 v ┴
|
|
||||||
0x77 w ┬
|
|
||||||
0x78 x │
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
export function value_format(text: string): string {
|
|
||||||
return `${styles.bold.open}${styles.white.open}${text}${styles.white.close}${styles.bold.close}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function process_format(text: string): string {
|
|
||||||
return `${styles.blue.open}${text}${styles.blue.close}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function prompt_format(text: string): string {
|
|
||||||
return `${styles.yellow.open}${text}${styles.yellow.close}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function success_format(text: string): string {
|
|
||||||
return `${styles.green.open}${text}${styles.green.close}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function failure_format(text: string): string {
|
|
||||||
return `${styles.red.open}${text}${styles.red.close}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function run(command: string): Promise<number> {
|
|
||||||
//throw "todo";
|
|
||||||
|
|
||||||
console.log(`${value_format("┌")} ${process_format("running '")}${value_format(command)}${process_format("'")}`);
|
|
||||||
|
|
||||||
const cmd = shArgs(command);
|
|
||||||
const process = Deno.run({
|
|
||||||
cmd: cmd,
|
|
||||||
stdin: "piped",
|
|
||||||
stdout: "piped",
|
|
||||||
stderr: "piped"
|
|
||||||
});
|
|
||||||
|
|
||||||
const [_o, _e, satus] = await Promise.all([pipe_out(process.stdout), pipe_out(process.stderr), process.status()]);
|
|
||||||
const code = satus.code;
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function pipe_out(out: Deno.Reader) {
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
for await (const line of readLines(out))
|
|
||||||
await writeAll(Deno.stdout, encoder.encode(`${value_format("│")} ${line}\n`));
|
|
||||||
}
|
|
||||||
|
|
||||||
// asks a question to the user
|
|
||||||
export async function prompt(line: string, options?: string[], default_?: string) {
|
|
||||||
|
|
||||||
let options_part = "";
|
|
||||||
if (options != undefined) options_part = `[${options.map(s => value_format(s)).join('/')}]`;
|
|
||||||
|
|
||||||
let default_part = "";
|
|
||||||
if (default_ != undefined) default_part = `(default: '${default_}')`;
|
|
||||||
|
|
||||||
const text = `─> ${prompt_format(line)} ${options_part} ${default_part}`;
|
|
||||||
console.log(text);
|
|
||||||
|
|
||||||
for await (const line of readLines(Deno.stdin)) {
|
|
||||||
if (default_ != undefined && line == "") return default_;
|
|
||||||
|
|
||||||
if (options != undefined && !options.includes(line)) {
|
|
||||||
console.log(text);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return line
|
|
||||||
}
|
|
||||||
}
|
|
33
parts/bat.ts
33
parts/bat.ts
|
@ -1,33 +0,0 @@
|
||||||
import { Setup, SetupResult } from "../lib/setup.ts"
|
|
||||||
import { Context } from "../lib/context.ts"
|
|
||||||
import { prompt, run, prompt_format } from "../lib/utilities.ts"
|
|
||||||
|
|
||||||
export class BatSetup extends Setup {
|
|
||||||
constructor() {
|
|
||||||
super({ name: "bat" })
|
|
||||||
}
|
|
||||||
|
|
||||||
async install(context: Context): Promise<SetupResult> {
|
|
||||||
const method = await prompt("Which method ?", ["pacman", "github", "cargo"], "pacman");
|
|
||||||
|
|
||||||
if (method == "pacman") {
|
|
||||||
await run("sudo pacman -S --noconfirm community/bat");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method == "github") {
|
|
||||||
await run(`mkdir -p ${context.working_dir}`);
|
|
||||||
await run(`wget "https://github.com/sharkdp/bat/releases/download/v0.21.0/bat-v0.21.0-i686-unknown-linux-gnu.tar.gz" -O "${context.working_dir}/bat-bin-linux.tar.gz"`);
|
|
||||||
await run(`tar xf "${context.working_dir}/bat-bin-linux.tar.gz" -C "${context.working_dir}"`);
|
|
||||||
await run(`mkdir -p "$HOME/.local/bin"`);
|
|
||||||
await run(`cp "${context.working_dir}/bat-v0.21.0-i686-unknown-linux-gnu/bat" "$HOME/.local/bin/"`);
|
|
||||||
await run(`rm -rf "${context.working_dir}/bat-v0.21.0-i686-unknown-linux-gnu" "${context.working_dir}/bat-bin-linux.tar.gz"`)
|
|
||||||
console.log(prompt_format("Don't forget to add '$HOME/.local/bin' to your path."));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method == "cargo") {
|
|
||||||
throw "TODO"
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Ok"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
import { run } from "../lib/utilities.ts"
|
|
||||||
import { Setup, SetupResult } from "../lib/setup.ts"
|
|
||||||
|
|
||||||
export class HelloWorldSetup extends Setup {
|
|
||||||
constructor() {
|
|
||||||
super({ name: "hello-world" });
|
|
||||||
}
|
|
||||||
|
|
||||||
async install(): Promise<SetupResult> {
|
|
||||||
await run("echo hello yorld");
|
|
||||||
return "Ok"
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
import { Setup, SetupResult } from "../lib/setup.ts"
|
|
||||||
|
|
||||||
export class RcSetup extends Setup {
|
|
||||||
async install(): Promise<SetupResult> {
|
|
||||||
return "Ok";
|
|
||||||
}
|
|
||||||
}
|
|
12
prelude.sh
12
prelude.sh
|
@ -1,12 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# installing deno without sudo (necessary for continuation)
|
|
||||||
curl -fsSL https://deno.land/x/install/install.sh | sh
|
|
||||||
|
|
||||||
# adding deno to the environment
|
|
||||||
export DENO_INSTALL="~/.deno"
|
|
||||||
export PATH="$DENO_INSTALL/bin:$PATH"
|
|
||||||
|
|
||||||
# indications
|
|
||||||
echo "# run with :
|
|
||||||
deno run -A \"https://raw.githubusercontent.com/MajorBarnulf/mbztr/main/complete.ts\""
|
|
64
src/gen_module.ts
Normal file
64
src/gen_module.ts
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import * as cli from "https://deno.land/std@0.219.0/cli/mod.ts";
|
||||||
|
|
||||||
|
const USAGE = `mbztr_gen_module - Interactively generate a MBZTR module.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
mbztr_gen_module [options] <name> [...file]
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
name Name for the module.
|
||||||
|
file List of files or directories in the module.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--help, -h Prints help message.
|
||||||
|
`;
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const { module_name, file_roots } = parse_args(Deno.args);
|
||||||
|
const file_paths = await select_files(file_roots);
|
||||||
|
}
|
||||||
|
|
||||||
|
function fail_with_invalid_args() {
|
||||||
|
console.log(USAGE);
|
||||||
|
Deno.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parse_args(ags: string[]) {
|
||||||
|
const parsed_args = cli.parseArgs(ags);
|
||||||
|
for (const h of ["-h", "--help"]) if (parsed_args[h] !== undefined) fail_with_invalid_args();
|
||||||
|
for (const arg of parsed_args._) if (typeof arg !== "string") fail_with_invalid_args();
|
||||||
|
const [module_name, ...file_roots] = parsed_args._;
|
||||||
|
if (module_name === undefined) fail_with_invalid_args();
|
||||||
|
return { module_name, file_roots };
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
cases :
|
||||||
|
- contains bin : ask for all bins
|
||||||
|
- contains dir/files > 1Mb : walk dir
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function* select_files(file_roots: string[]) {
|
||||||
|
for (const root of file_roots) {
|
||||||
|
const all_files = await discover_files(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* discover_files(root: string) {
|
||||||
|
if (await disk_usage(root) > 5_000_000) for await (const selected of prompt_select_per_size(root)) yield selected;
|
||||||
|
else for await (const file of walk_dir(root)) yield file;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function* prompt_select_per_size(root: string) {
|
||||||
|
throw "todo";
|
||||||
|
yield "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function disk_usage(path: string) {
|
||||||
|
throw "todo";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
await main();
|
65
src/lib/module.ts
Normal file
65
src/lib/module.ts
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
import { z } from "https://deno.land/x/zod@v3.22.4/mod.ts";
|
||||||
|
|
||||||
|
export type Rule = {
|
||||||
|
path: string;
|
||||||
|
kind: "include" | "exclude";
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ModuleLayout {
|
||||||
|
rules;
|
||||||
|
|
||||||
|
constructor(rules: Rule[]) {
|
||||||
|
this.rules = rules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static new_empty() {
|
||||||
|
return new ModuleLayout([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public append_rule(rule: Rule) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
public package_to() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ModuleFile = {
|
||||||
|
path: string;
|
||||||
|
kind: "text" | "bin";
|
||||||
|
content: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export class Module {
|
||||||
|
layout;
|
||||||
|
content;
|
||||||
|
|
||||||
|
constructor(layout: ModuleLayout, content: ModuleFile[]) {
|
||||||
|
this.layout = layout;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
public serialize() {
|
||||||
|
const layout = { rules: this.layout.rules };
|
||||||
|
const content = this.content;
|
||||||
|
JSON.stringify({ layout, content });
|
||||||
|
}
|
||||||
|
|
||||||
|
public static deserialize(serialized: string) {
|
||||||
|
const { layout: { rules }, content } = z.object({
|
||||||
|
layout: z.object({
|
||||||
|
rules: z.array(z.object({
|
||||||
|
path: z.string(),
|
||||||
|
kind: z.literal("include").or(z.literal("exclude")),
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
content: z.array(z.object({
|
||||||
|
path: z.string(),
|
||||||
|
kind: z.literal("text").or(z.literal("bin")),
|
||||||
|
content: z.string(),
|
||||||
|
})),
|
||||||
|
}).parse(JSON.parse(serialized));
|
||||||
|
const layout = new ModuleLayout(rules);
|
||||||
|
return new Module(layout, content);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue