1
Fork 0

Compare commits

..

No commits in common. "7cbab0f4b7829fa85603dd1225d07472c222472b" and "159329f7e04db6953f99923d218eda08287a003a" have entirely different histories.

14 changed files with 309 additions and 61 deletions

View File

@ -2,6 +2,7 @@
## Scripts ## Scripts
- [`bulk`]: bulk doer of things.
- [`codium-extensions`]: save and install VS Codium extensions using their identifier. - [`codium-extensions`]: save and install VS Codium extensions using their identifier.
- [`copy-nixos-config`]: copies NixOS configuration from `$BAUKE_DIR/nix/<hostname>/` to `/etc/nixos/`. - [`copy-nixos-config`]: copies NixOS configuration from `$BAUKE_DIR/nix/<hostname>/` to `/etc/nixos/`.
- [`desktop-wallpaper`]: desktop wallpaper changer. - [`desktop-wallpaper`]: desktop wallpaper changer.
@ -11,6 +12,7 @@
- [`simple-git-push`]: `git push` with extra semantics. - [`simple-git-push`]: `git push` with extra semantics.
- [`tauon-controls`]: remote control CLI for Tauon Music Box. - [`tauon-controls`]: remote control CLI for Tauon Music Box.
[`bulk`]: ./scripts/bulk/bulk.ts
[`codium-extensions`]: ./scripts/codium-extensions.ts [`codium-extensions`]: ./scripts/codium-extensions.ts
[`copy-nixos-config`]: ./scripts/copy-nixos-config.ts [`copy-nixos-config`]: ./scripts/copy-nixos-config.ts
[`desktop-wallpaper`]: ./scripts/desktop-wallpaper.ts [`desktop-wallpaper`]: ./scripts/desktop-wallpaper.ts
@ -43,6 +45,8 @@ const { options } = await new Command()
console.log(options.file); console.log(options.file);
``` ```
* An example of subcommands can be seen in [`bulk`], each command is created like above but added to the main CLI via `.command("name", Command)`.
[Cliffy]: https://cliffy.io [Cliffy]: https://cliffy.io
## Why `bin/` + `scripts/` ## Why `bin/` + `scripts/`

7
.bauke/bin/bulk Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env zsh
deno run \
--allow-read \
--allow-run \
"$BAUKE_DIR/scripts/bulk/bulk.ts" \
"$@"

View File

@ -0,0 +1,16 @@
import { Command } from "../dependencies.ts";
import { moveCommand } from "./move.ts";
import { runCommand } from "./run.ts";
async function main(): Promise<void> {
await new Command()
.name("bulk")
.description("Bulk doer of things.")
.command("move", moveCommand)
.command("run", runCommand)
.parse(Deno.args);
}
if (import.meta.main) {
void main();
}

View File

@ -0,0 +1,45 @@
import { Command, prompt } from "../dependencies.ts";
export const moveCommand = new Command()
.name("move")
.description("Interactively move a group of files.")
.option(
"-d, --directory <directory:string>",
"Directories to include files from.",
{
collect: true,
},
)
.action(async ({
directory,
}) => {
await actionHandler({ directories: directory ?? [] });
});
async function actionHandler(
options: {
directories: string[];
},
): Promise<void> {
for (const directory of options.directories) {
for await (const file of Deno.readDir(directory)) {
if (!file.isFile) {
continue;
}
const filename = file.name;
const { destination } = await prompt.prompt([{
type: prompt.Input,
name: "destination",
message: `${filename} Destination:`,
}]);
if (destination === undefined || destination === "") {
continue;
}
// TODO: Move the file.
console.log(filename, destination);
}
}
}

180
.bauke/scripts/bulk/run.ts Normal file
View File

@ -0,0 +1,180 @@
import { Command, prompt } from "../dependencies.ts";
export const runCommand = new Command()
.name("run")
.description("Run a command over a group of files and directories.")
.option(
"-d, --directory <directory:string>",
"Directories to include files from.",
{
collect: true,
required: true,
},
)
.option(
"--include-directories",
"Include directories found inside the directories.",
)
.option(
"--output-script",
"Output the commands as a shell script instead of running them.",
)
.action(async ({ directory, includeDirectories, outputScript }) => {
await actionHandler({
directories: directory,
includeDirectories: includeDirectories ?? false,
outputScript: outputScript ?? false,
});
});
async function actionHandler(
options: {
directories: string[];
includeDirectories: boolean;
outputScript: boolean;
},
): Promise<void> {
let command: string[] = [];
const previousPromptArgs: Map<number, string> = new Map();
const suggestedPromptArgs: Set<string> = new Set();
const substituteMarkers = {
absoluteFile: "$absoluteFile",
filename: "$filename",
outWithFile: "$outWithFile",
};
for (const directory of options.directories) {
console.log(`\n## Input for ${directory}`);
const constructedCommands: string[][] = [];
const prompts = await prompt.prompt([
{
type: prompt.List,
name: "command",
message: "Arguments of the command to run separated by comma",
default: command,
suggestions: Object.values(substituteMarkers),
},
{
type: prompt.Confirm,
name: "confirmCommands",
message: "Manually approve each command invocation",
default: true,
},
]);
command = prompts.command ?? command;
for await (const file of Deno.readDir(directory)) {
if (!options.includeDirectories && file.isDirectory) {
continue;
}
const absoluteFile = await Deno.realPath(`${directory}/${file.name}`);
const constructedCommand: string[] = [];
let promptArgCount = 1;
const substitutes = [
[substituteMarkers.absoluteFile, absoluteFile],
[substituteMarkers.filename, file.name],
[substituteMarkers.outWithFile, `$out/${file.name}`],
];
argumentLoop:
for (const argument of command) {
if (argument === "$prompt") {
const { promptedArg } = await prompt.prompt([{
type: prompt.Input,
name: "promptedArg",
message: `$prompt ${promptArgCount} for ${absoluteFile}`,
default: previousPromptArgs.get(promptArgCount) ?? "",
suggestions: Array.from(suggestedPromptArgs),
}]);
previousPromptArgs.set(promptArgCount, promptedArg!);
suggestedPromptArgs.add(promptedArg!);
constructedCommand.push(promptedArg!);
promptArgCount += 1;
} else {
for (const [marker, substitute] of substitutes) {
if (argument === marker) {
constructedCommand.push(substitute);
continue argumentLoop;
}
}
constructedCommand.push(argument);
}
}
if (prompts.confirmCommands) {
const { confirmedRun } = await prompt.prompt([
{
type: prompt.Confirm,
name: "confirmedRun",
message: `Run "${constructedCommand.join(" ")}"`,
default: true,
},
]);
if (!confirmedRun) {
continue;
}
}
constructedCommands.push(constructedCommand);
}
if (constructedCommands.length === 0) {
console.log("\n No commands to run.");
continue;
}
console.log("\n## Commands");
console.log(constructedCommands.map((c) => c.join(" ")).join("\n"));
const { confirmedRun } = await prompt.prompt([
{
type: prompt.Confirm,
name: "confirmedRun",
message: "Is this correct",
default: true,
},
]);
if (!confirmedRun) {
continue;
}
if (options.outputScript) {
const defaultFilename = "bulk-run-script.zsh";
const { filename, outputDirectory } = await prompt.prompt([
{
type: prompt.Input,
name: "outputDirectory",
message: "Output directory to set as $out",
default: ".",
},
{
type: prompt.Input,
name: "filename",
message: "Filename for the script",
default: defaultFilename,
},
]);
const commands = constructedCommands.map((c) => c.join(" ")).join("\n");
await Deno.writeTextFile(
filename ?? defaultFilename,
`#!/usr/bin/env zsh
source "$HOME/.aliases.zsh"
out="${outputDirectory}"
${commands}`,
);
} else {
console.log("\n## Output");
for (const constructedCommand of constructedCommands) {
await Deno.run({
cmd: constructedCommand,
}).status();
}
}
}
}

View File

@ -21,11 +21,13 @@ async function main(): Promise<void> {
if (options.install) { if (options.install) {
const extensions = await getSavedExtensions(options.file); const extensions = await getSavedExtensions(options.file);
await new Deno.Command("codium", { const process = Deno.run({
args: [ cmd: [
"codium",
...extensions.flatMap((id) => ["--install-extension", id]), ...extensions.flatMap((id) => ["--install-extension", id]),
], ],
}).output(); });
await process.status();
} }
if (options.list) { if (options.list) {
@ -46,8 +48,8 @@ async function main(): Promise<void> {
} }
async function getInstalledExtensions(): Promise<string[]> { async function getInstalledExtensions(): Promise<string[]> {
const extensions = await runAndReturnStdout("codium", { const extensions = await runAndReturnStdout({
args: ["--list-extensions"], cmd: ["codium", "--list-extensions"],
}); });
return extensions.trim().split("\n"); return extensions.trim().split("\n");
} }

View File

@ -8,7 +8,7 @@ async function main(): Promise<void> {
'Copy NixOS configuration from "$BAUKE_DIR/nix/<hostname>/" to "/etc/nixos/"', 'Copy NixOS configuration from "$BAUKE_DIR/nix/<hostname>/" to "/etc/nixos/"',
) )
.option("--hostname", "The machine's configuration to copy.", { .option("--hostname", "The machine's configuration to copy.", {
default: (await runAndReturnStdout("hostname")).trim(), default: (await runAndReturnStdout({ cmd: ["hostname"] })).trim(),
}) })
.option("--diff", 'Output diffs between local and "/etc/nixos/" files.', { .option("--diff", 'Output diffs between local and "/etc/nixos/" files.', {
standalone: true, standalone: true,
@ -27,28 +27,29 @@ async function main(): Promise<void> {
if (options.diff) { if (options.diff) {
for (const file of files) { for (const file of files) {
const filename = file.slice(file.lastIndexOf("/") + 1); const filename = file.slice(file.lastIndexOf("/") + 1);
await new Deno.Command("delta", { await Deno.run({
args: [`/etc/nixos/${filename}`, file], cmd: ["delta", `/etc/nixos/${filename}`, file],
}).output(); }).status();
} }
return; return;
} }
await new Deno.Command("sudo", { await Deno.run({
args: [ cmd: [
"sudo",
"cp", "cp",
"--preserve=timestamps", "--preserve=timestamps",
"--verbose", "--verbose",
...files, ...files,
"/etc/nixos/", "/etc/nixos/",
], ],
}).output(); }).status();
if (options.rebuild) { if (options.rebuild) {
await new Deno.Command("sudo", { await Deno.run({
args: ["nixos-rebuild", options.rebuild], cmd: ["sudo", "nixos-rebuild", options.rebuild],
}).output(); }).status();
} }
} }

View File

@ -1,4 +1,6 @@
export { Command } from "https://deno.land/x/cliffy@v0.25.7/command/mod.ts"; export { Command } from "https://deno.land/x/cliffy@v0.25.7/command/mod.ts";
export * as prompt from "https://deno.land/x/cliffy@v0.25.7/prompt/mod.ts"; export * as prompt from "https://deno.land/x/cliffy@v0.25.7/prompt/mod.ts";
export * as toml from "https://deno.land/std@0.190.0/toml/mod.ts"; export * as nodeFs from "https://deno.land/std@0.167.0/node/fs.ts";
export * as nodeUtil from "https://deno.land/std@0.167.0/node/util.ts";
export * as toml from "https://deno.land/std@0.167.0/encoding/toml.ts";

View File

@ -41,16 +41,17 @@ async function main(): Promise<void> {
} }
async function downloadImage(url: string): Promise<void> { async function downloadImage(url: string): Promise<void> {
await new Deno.Command("curl", { await Deno.run({
args: ["-fsLS", url, "-o", imagePath], cmd: ["curl", "-fsLS", url, "-o", imagePath],
}).output(); }).status();
} }
async function setWallpaper(file: string = imagePath): Promise<void> { async function setWallpaper(file: string = imagePath): Promise<void> {
const monitors = ["monitorHDMI-0", "monitorHDMI-1"]; const monitors = ["monitorHDMI-0", "monitorHDMI-1"];
for (const monitor of monitors) { for (const monitor of monitors) {
await new Deno.Command("xfconf-query", { await Deno.run({
args: [ cmd: [
"xfconf-query",
"-c", "-c",
"xfce4-desktop", "xfce4-desktop",
"-p", "-p",
@ -58,7 +59,7 @@ async function setWallpaper(file: string = imagePath): Promise<void> {
"-s", "-s",
file, file,
], ],
}).output(); }).status();
} }
} }

View File

@ -49,8 +49,9 @@ async function main(): Promise<void> {
return; return;
} }
await new Deno.Command("youtube3", { await Deno.run({
args: [ cmd: [
"youtube3",
"videos", "videos",
"update", "update",
"-r", "-r",
@ -62,7 +63,7 @@ async function main(): Promise<void> {
"-r", "-r",
`snippet.title=${title}`, `snippet.title=${title}`,
], ],
}).output(); }).status();
} }
function formatDescription(frontmatter: Frontmatter): string { function formatDescription(frontmatter: Frontmatter): string {

View File

@ -1,5 +1,4 @@
import { Command } from "./dependencies.ts"; import { Command, nodeFs } from "./dependencies.ts";
import { pathExists } from "./utilities.ts";
async function main(): Promise<void> { async function main(): Promise<void> {
const { args, options } = await new Command() const { args, options } = await new Command()
@ -21,7 +20,7 @@ async function main(): Promise<void> {
const [file, text] = args; const [file, text] = args;
if (await pathExists(file)) { if (nodeFs.existsSync(file)) {
if (options.overwrite) { if (options.overwrite) {
await Deno.remove(file); await Deno.remove(file);
} else { } else {
@ -30,8 +29,9 @@ async function main(): Promise<void> {
} }
} }
await new Deno.Command("gegl", { await Deno.run({
args: [ cmd: [
"gegl",
"-o", "-o",
file, file,
"--", "--",
@ -42,15 +42,16 @@ async function main(): Promise<void> {
width: options.width, width: options.width,
}), }),
], ],
}).output(); }).status();
if (!await pathExists(file)) { if (!nodeFs.existsSync(file)) {
console.log("Something went wrong with GEGL."); console.log("Something went wrong with GEGL.");
Deno.exit(1); Deno.exit(1);
} }
await new Deno.Command("convert", { await Deno.run({
args: [ cmd: [
"convert",
file, file,
"-background", "-background",
"transparent", "transparent",
@ -60,10 +61,10 @@ async function main(): Promise<void> {
`${options.width}x${options.height}`, `${options.width}x${options.height}`,
file, file,
], ],
}).output(); }).status();
if (options.clean) { if (options.clean) {
await new Deno.Command("mat2", { args: ["--inplace", file] }).output(); await Deno.run({ cmd: ["mat2", "--inplace", file] }).status();
} }
} }

View File

@ -24,19 +24,19 @@ async function main(): Promise<void> {
} }
async function gitPush(remote: string, args: string[]): Promise<void> { async function gitPush(remote: string, args: string[]): Promise<void> {
await new Deno.Command("git", { await Deno.run({
args: [ cmd: [
"git", "git",
"push", "push",
"--follow-tags", "--follow-tags",
remote, remote,
...args, ...args,
], ],
}).output(); }).status();
} }
async function gitRemote(): Promise<string[]> { async function gitRemote(): Promise<string[]> {
const output = await runAndReturnStdout("git", { args: ["remote"] }); const output = await runAndReturnStdout({ cmd: ["git", "remote"] });
return output.trim().split("\n").filter((remote) => remote.length > 0); return output.trim().split("\n").filter((remote) => remote.length > 0);
} }

View File

@ -1,5 +1,4 @@
import { Command } from "./dependencies.ts"; import { Command, nodeFs } from "./dependencies.ts";
import { pathExists } from "./utilities.ts";
const hiddenApi = "http://127.0.0.1:7813"; const hiddenApi = "http://127.0.0.1:7813";
const remoteApi = "http://127.0.0.1:7814/api1"; const remoteApi = "http://127.0.0.1:7814/api1";
@ -63,7 +62,7 @@ async function main(): Promise<void> {
if (options.writeImage) { if (options.writeImage) {
const status = await getStatus(); const status = await getStatus();
const path = `/tmp/tauon-cover-${status.id}.jpg`; const path = `/tmp/tauon-cover-${status.id}.jpg`;
if (await pathExists(path)) { if (nodeFs.existsSync(path)) {
console.log(path); console.log(path);
return; return;
} }
@ -96,9 +95,9 @@ async function getStatus(): Promise<Status> {
async function notifyCurrentSong(): Promise<void> { async function notifyCurrentSong(): Promise<void> {
const status = await getStatus(); const status = await getStatus();
await new Deno.Command("notify-send", { await Deno.run({
args: [status.title, status.artist], cmd: ["notify-send", status.title, status.artist],
}).output(); }).status();
} }
if (import.meta.main) { if (import.meta.main) {

View File

@ -1,25 +1,14 @@
import { toml } from "./dependencies.ts"; import { nodeUtil, toml } from "./dependencies.ts";
export async function pathExists(path: string): Promise<boolean> {
try {
await Deno.stat(path);
return true;
} catch {
return false;
}
}
export function stringifyJsonPretty(input: unknown): string { export function stringifyJsonPretty(input: unknown): string {
return JSON.stringify(input, null, 2); return JSON.stringify(input, null, 2);
} }
export async function runAndReturnStdout( export async function runAndReturnStdout(
command: string, options: Deno.RunOptions,
options: Deno.CommandOptions = {},
): Promise<string> { ): Promise<string> {
const process = new Deno.Command(command, { stdout: "piped", ...options }); const process = Deno.run({ stdout: "piped", ...options });
const { stdout } = await process.output(); return new nodeUtil.TextDecoder().decode(await process.output());
return new TextDecoder().decode(stdout);
} }
export function tomlFrontmatter<T>( export function tomlFrontmatter<T>(