update deployer to fix some bugs I found

master
Yorick van Pelt 2023-04-23 10:32:27 +02:00
parent d2f5ba3ca2
commit 3a1c468c06
Signed by: yorick
GPG Key ID: A36E70F9DC014A15
2 changed files with 43 additions and 14 deletions

View File

@ -1,9 +1,10 @@
import { ssh, SSH } from './ssh.js'
import { Expression, BuiltOutput } from './nix.js'
import { Expression } from './nix.js'
type Command = () => Promise<void>
class Cmd {
registry: Record<string, () => Promise<void>> = {};
register(obj: string, action: string, fn: () => Promise<void>) {
registry: Record<string, Command> = {};
register(obj: string, action: string, fn: Command) {
this.registry[`${obj}.${action}`] = fn
}
registerAll(obj: string, intf: Object & { _commands?: string[] }) {
@ -43,10 +44,11 @@ class Machine {
name: string;
hasHome: boolean = false;
hostname?: string;
constructor(args: {name?: string, hasHome?: boolean, hostname?: string}) {
Object.assign(this, args)
// todo
this.name = ""
constructor({name = "", hasHome = false, hostname}: {name?: string, hasHome?: boolean, hostname?: string}) {
this.name = name
this.hasHome = hasHome
this.hostname = hostname
// name can be set later
}
isLocal() {
return os.hostname() == this.name
@ -93,7 +95,7 @@ const machines = {
for (const [name, machine] of Object.entries(machines))
machine.name = name
function cmd<R>(target: { _commands?: string[] }, propertyKey: string, descriptor: PropertyDescriptor): void {
function cmd(target: { _commands?: string[] }, propertyKey: string, _descriptor: PropertyDescriptor): void {
if (target._commands == undefined) target._commands = []
target._commands.push(propertyKey)
}

View File

@ -1,10 +1,30 @@
import 'zx/globals';
import { retry } from 'zx/experimental'
import { spawn, SpawnOptions, ChildProcess } from 'child_process'
import { spawn, ChildProcess } from 'child_process'
import { Socket } from 'node:net'
async function promiseWithTimeout<T>(timeoutMs: number, promise: Promise<T>, failureMessage?: string) {
let timeoutHandle: NodeJS.Timeout | null = null
const timeoutPromise = new Promise<never>((_resolve, reject) => {
timeoutHandle = setTimeout(() => reject(new Error(failureMessage)), timeoutMs)
})
const result = await Promise.race([promise, timeoutPromise])
if (timeoutHandle) clearTimeout(timeoutHandle)
return result
}
const to_kill: ProcessPromise[] = []
// sad that I can't chain these
process.on("uncaughtException", err => {
console.error("uncaught exception", err)
console.log("killing", to_kill.length, "child processes")
for (const k of to_kill) if (k.child?.pid) process.kill(k.child.pid)
to_kill.length = 0
process.exit(1)
})
export function ssh(host: string): Promise<SSH>
export function ssh<R>(host: string, cb: () => Promise<R>): Promise<R>
export async function ssh<R>(host: string, cb?: () => Promise<R>) {
@ -14,10 +34,14 @@ export async function ssh<R>(host: string, cb?: () => Promise<R>) {
} catch (p) {
if (p instanceof ProcessOutput && p.exitCode == 255) {
console.log("Spawning ssh master")
let x = $`ssh ${host} -M -N -o compression=no`
await sleep(1000)
await retry(20, '1s', () => $`ssh ${host} -O check`.quiet())
console.log("SSH connected")
const x = $`ssh ${host} -M -N -o Compression=no -o PermitLocalCommand=yes -o LocalCommand="echo connected"`
to_kill.push(x)
await promiseWithTimeout(60000, new Promise<void>((resolve, _reject) => {
x.stdout.on('data', (d: Buffer) => {
if (d.toString('utf8').trim() == "connected")
resolve()
})
}))
if (x.child) {
x.child.unref()
if (x.child.stderr instanceof Socket) x.child.stderr.unref()
@ -42,6 +66,9 @@ export class SSH {
constructor(public host: string) { }
within<R>(cb: () => Promise<R>): Promise<R> {
return within(async () => {
// todo: the default options.shell is set to local which.sync('bash')
// which doesn't neccesarily work on the remote
$.shell = "bash"
$.spawn = (command: string, options: any): any => {
const stdio = ["pipe", options.stdio[1], options.stdio[2]]
const proc: ChildProcess = spawn("ssh", [this.host, options.shell],