mirror of
https://github.com/Start9Labs/start-sdk.git
synced 2026-03-26 02:11:56 +00:00
feat: Effects that shouldn't be needed removed from utils
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Effects } from "../../types"
|
import { Effects } from "../../types"
|
||||||
import { createUtils } from "../../util"
|
import { createUtils } from "../../util"
|
||||||
|
import { stringFromStdErrOut } from "../../util/stringFromStdErrOut"
|
||||||
import { CheckResult } from "./CheckResult"
|
import { CheckResult } from "./CheckResult"
|
||||||
export function containsAddress(x: string, port: number) {
|
export function containsAddress(x: string, port: number) {
|
||||||
const readPorts = x
|
const readPorts = x
|
||||||
@@ -32,10 +33,17 @@ export async function checkPortListening(
|
|||||||
Promise.resolve().then(async () => {
|
Promise.resolve().then(async () => {
|
||||||
const hasAddress =
|
const hasAddress =
|
||||||
containsAddress(
|
containsAddress(
|
||||||
await utils.runCommand(`cat /proc/net/tcp`, {}),
|
await utils.childProcess
|
||||||
|
.exec(`cat /proc/net/tcp`, {})
|
||||||
|
.then(stringFromStdErrOut),
|
||||||
port,
|
port,
|
||||||
) ||
|
) ||
|
||||||
containsAddress(await utils.runCommand("cat /proc/net/udp", {}), port)
|
containsAddress(
|
||||||
|
await utils.childProcess
|
||||||
|
.exec("cat /proc/net/udp", {})
|
||||||
|
.then(stringFromStdErrOut),
|
||||||
|
port,
|
||||||
|
)
|
||||||
if (hasAddress) {
|
if (hasAddress) {
|
||||||
return { status: "passing", message: options.successMessage }
|
return { status: "passing", message: options.successMessage }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { CommandType, Effects } from "../../types"
|
import { CommandType, Effects } from "../../types"
|
||||||
import { createUtils } from "../../util"
|
import { createUtils } from "../../util"
|
||||||
|
import { stringFromStdErrOut } from "../../util/stringFromStdErrOut"
|
||||||
import { CheckResult } from "./CheckResult"
|
import { CheckResult } from "./CheckResult"
|
||||||
import { timeoutPromise } from "./index"
|
import { timeoutPromise } from "./index"
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ export const runHealthScript = async (
|
|||||||
): Promise<CheckResult> => {
|
): Promise<CheckResult> => {
|
||||||
const utils = createUtils(effects)
|
const utils = createUtils(effects)
|
||||||
const res = await Promise.race([
|
const res = await Promise.race([
|
||||||
utils.runCommand(runCommand, { timeout }),
|
utils.childProcess.exec(runCommand, { timeout }).then(stringFromStdErrOut),
|
||||||
timeoutPromise(timeout),
|
timeoutPromise(timeout),
|
||||||
]).catch((e) => {
|
]).catch((e) => {
|
||||||
console.warn(errorMessage)
|
console.warn(errorMessage)
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { DaemonReturned, Effects, ValidIfNoStupidEscape } from "../types"
|
|||||||
import { createUtils } from "../util"
|
import { createUtils } from "../util"
|
||||||
type Daemon<Ids extends string, Command extends string, Id extends string> = {
|
type Daemon<Ids extends string, Command extends string, Id extends string> = {
|
||||||
id: "" extends Id ? never : Id
|
id: "" extends Id ? never : Id
|
||||||
command: string
|
command: ValidIfNoStupidEscape<Command> | [string, ...string[]]
|
||||||
env?: Record<string, string>
|
env?: Record<string, string>
|
||||||
ready: {
|
ready: {
|
||||||
display: string | null
|
display: string | null
|
||||||
|
|||||||
38
lib/test/utils.splitCommand.test.ts
Normal file
38
lib/test/utils.splitCommand.test.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { getHostname } from "../util/getNetworkInterface"
|
||||||
|
import { splitCommand } from "../util/splitCommand"
|
||||||
|
|
||||||
|
describe("splitCommand ", () => {
|
||||||
|
const inputToExpected = [
|
||||||
|
["cat", ["cat"]],
|
||||||
|
[["cat"], ["cat"]],
|
||||||
|
[
|
||||||
|
["cat", "hello all my homies"],
|
||||||
|
["cat", "hello all my homies"],
|
||||||
|
],
|
||||||
|
["cat hello world", ["cat", "hello", "world"]],
|
||||||
|
["cat hello 'big world'", ["cat", "hello", "big world"]],
|
||||||
|
[`cat hello "big world"`, ["cat", "hello", "big world"]],
|
||||||
|
// Too many spaces
|
||||||
|
["cat ", ["cat"]],
|
||||||
|
[["cat "], ["cat "]],
|
||||||
|
[
|
||||||
|
["cat ", "hello all my homies "],
|
||||||
|
["cat ", "hello all my homies "],
|
||||||
|
],
|
||||||
|
["cat hello world ", ["cat", "hello", "world"]],
|
||||||
|
[
|
||||||
|
" cat hello 'big world' ",
|
||||||
|
["cat", "hello", "big world"],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
` cat hello "big world" `,
|
||||||
|
["cat", "hello", "big world"],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const [input, expectValue] of inputToExpected) {
|
||||||
|
test(`should return ${expectValue} for ${input}`, () => {
|
||||||
|
expect(splitCommand(input as any)).toEqual(expectValue)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -10,7 +10,6 @@ export const getHostname = (url: string): HostName | null => {
|
|||||||
if (!founds) return null
|
if (!founds) return null
|
||||||
const parts = founds.split("@")
|
const parts = founds.split("@")
|
||||||
const last = parts[parts.length - 1] as HostName | null
|
const last = parts[parts.length - 1] as HostName | null
|
||||||
console.log({ url, parts, founds, last })
|
|
||||||
return last
|
return last
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
lib/util/splitCommand.ts
Normal file
17
lib/util/splitCommand.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { arrayOf, string } from "ts-matches"
|
||||||
|
import { ValidIfNoStupidEscape } from "../types"
|
||||||
|
|
||||||
|
export const splitCommand = <A>(
|
||||||
|
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
||||||
|
): string[] => {
|
||||||
|
if (arrayOf(string).test(command)) return command
|
||||||
|
return String(command)
|
||||||
|
.split('"')
|
||||||
|
.flatMap((x, i) =>
|
||||||
|
i % 2 !== 0
|
||||||
|
? [x]
|
||||||
|
: x.split("'").flatMap((x, i) => (i % 2 !== 0 ? [x] : x.split(" "))),
|
||||||
|
)
|
||||||
|
.map((x) => x.trim())
|
||||||
|
.filter(Boolean)
|
||||||
|
}
|
||||||
6
lib/util/stringFromStdErrOut.ts
Normal file
6
lib/util/stringFromStdErrOut.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export async function stringFromStdErrOut(x: {
|
||||||
|
stdout: string
|
||||||
|
stderr: string
|
||||||
|
}) {
|
||||||
|
return x?.stderr ? Promise.reject(x.stderr) : x.stdout
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import FileHelper from "./fileHelper"
|
|
||||||
import nullIfEmpty from "./nullIfEmpty"
|
import nullIfEmpty from "./nullIfEmpty"
|
||||||
import {
|
import {
|
||||||
CheckResult,
|
CheckResult,
|
||||||
@@ -12,6 +11,7 @@ import {
|
|||||||
ExtractStore,
|
ExtractStore,
|
||||||
InterfaceId,
|
InterfaceId,
|
||||||
PackageId,
|
PackageId,
|
||||||
|
ValidIfNoStupidEscape,
|
||||||
} from "../types"
|
} from "../types"
|
||||||
import { GetSystemSmtp } from "./GetSystemSmtp"
|
import { GetSystemSmtp } from "./GetSystemSmtp"
|
||||||
import { DefaultString } from "../config/configTypes"
|
import { DefaultString } from "../config/configTypes"
|
||||||
@@ -34,8 +34,14 @@ import {
|
|||||||
GetNetworkInterfaces,
|
GetNetworkInterfaces,
|
||||||
getNetworkInterfaces,
|
getNetworkInterfaces,
|
||||||
} from "./getNetworkInterfaces"
|
} from "./getNetworkInterfaces"
|
||||||
|
import * as CP from "node:child_process"
|
||||||
|
import { promisify } from "node:util"
|
||||||
|
import { splitCommand } from "./splitCommand"
|
||||||
|
|
||||||
import { exec } from "node:child_process"
|
const childProcess = {
|
||||||
|
exec: promisify(CP.exec),
|
||||||
|
execFile: promisify(CP.execFile),
|
||||||
|
}
|
||||||
|
|
||||||
export type Utils<Store, WrapperOverWrite = { const: never }> = {
|
export type Utils<Store, WrapperOverWrite = { const: never }> = {
|
||||||
checkPortListening(
|
checkPortListening(
|
||||||
@@ -55,6 +61,7 @@ export type Utils<Store, WrapperOverWrite = { const: never }> = {
|
|||||||
errorMessage?: string
|
errorMessage?: string
|
||||||
},
|
},
|
||||||
): Promise<CheckResult>
|
): Promise<CheckResult>
|
||||||
|
childProcess: typeof childProcess
|
||||||
createInterface: (options: {
|
createInterface: (options: {
|
||||||
name: string
|
name: string
|
||||||
id: string
|
id: string
|
||||||
@@ -93,15 +100,10 @@ export type Utils<Store, WrapperOverWrite = { const: never }> = {
|
|||||||
}) => GetNetworkInterfaces & WrapperOverWrite
|
}) => GetNetworkInterfaces & WrapperOverWrite
|
||||||
}
|
}
|
||||||
nullIfEmpty: typeof nullIfEmpty
|
nullIfEmpty: typeof nullIfEmpty
|
||||||
readFile: <A>(fileHelper: FileHelper<A>) => ReturnType<FileHelper<A>["read"]>
|
runDaemon: <A extends string>(
|
||||||
runDaemon: (
|
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
||||||
command: string,
|
|
||||||
options: { env?: Record<string, string> },
|
options: { env?: Record<string, string> },
|
||||||
) => Promise<DaemonReturned>
|
) => Promise<DaemonReturned>
|
||||||
runCommand: (
|
|
||||||
command: string,
|
|
||||||
options: { env?: Record<string, string>; timeout?: number },
|
|
||||||
) => Promise<string>
|
|
||||||
store: {
|
store: {
|
||||||
get: <Path extends string>(
|
get: <Path extends string>(
|
||||||
packageId: string,
|
packageId: string,
|
||||||
@@ -115,10 +117,6 @@ export type Utils<Store, WrapperOverWrite = { const: never }> = {
|
|||||||
value: ExtractStore<Store, Path>,
|
value: ExtractStore<Store, Path>,
|
||||||
) => Promise<void>
|
) => Promise<void>
|
||||||
}
|
}
|
||||||
writeFile: <A>(
|
|
||||||
fileHelper: FileHelper<A>,
|
|
||||||
data: A,
|
|
||||||
) => ReturnType<FileHelper<A>["write"]>
|
|
||||||
}
|
}
|
||||||
export const utils = <Store = never, WrapperOverWrite = { const: never }>(
|
export const utils = <Store = never, WrapperOverWrite = { const: never }>(
|
||||||
effects: Effects,
|
effects: Effects,
|
||||||
@@ -135,6 +133,7 @@ export const utils = <Store = never, WrapperOverWrite = { const: never }>(
|
|||||||
path: string
|
path: string
|
||||||
search: Record<string, string>
|
search: Record<string, string>
|
||||||
}) => new NetworkInterfaceBuilder({ ...options, effects }),
|
}) => new NetworkInterfaceBuilder({ ...options, effects }),
|
||||||
|
childProcess,
|
||||||
getSystemSmtp: () =>
|
getSystemSmtp: () =>
|
||||||
new GetSystemSmtp(effects) as GetSystemSmtp & WrapperOverWrite,
|
new GetSystemSmtp(effects) as GetSystemSmtp & WrapperOverWrite,
|
||||||
|
|
||||||
@@ -143,9 +142,6 @@ export const utils = <Store = never, WrapperOverWrite = { const: never }>(
|
|||||||
single: (id: string) => new SingleHost({ id, effects }),
|
single: (id: string) => new SingleHost({ id, effects }),
|
||||||
multi: (id: string) => new MultiHost({ id, effects }),
|
multi: (id: string) => new MultiHost({ id, effects }),
|
||||||
},
|
},
|
||||||
readFile: <A>(fileHelper: FileHelper<A>) => fileHelper.read(effects),
|
|
||||||
writeFile: <A>(fileHelper: FileHelper<A>, data: A) =>
|
|
||||||
fileHelper.write(data, effects),
|
|
||||||
nullIfEmpty,
|
nullIfEmpty,
|
||||||
|
|
||||||
networkInterface: {
|
networkInterface: {
|
||||||
@@ -178,45 +174,39 @@ export const utils = <Store = never, WrapperOverWrite = { const: never }>(
|
|||||||
) => effects.store.set<Store, Path>({ value, path: path as any }),
|
) => effects.store.set<Store, Path>({ value, path: path as any }),
|
||||||
},
|
},
|
||||||
|
|
||||||
runDaemon: async (
|
runDaemon: async <A extends string>(
|
||||||
command: string,
|
command: ValidIfNoStupidEscape<A> | [string, ...string[]],
|
||||||
options: { env?: Record<string, string> },
|
options: { env?: Record<string, string> },
|
||||||
): Promise<DaemonReturned> => {
|
): Promise<DaemonReturned> => {
|
||||||
let childProcess: null | {
|
const commands = splitCommand(command)
|
||||||
kill(signal?: number | string): void
|
const childProcess = CP.spawn(commands[0], commands.slice(1), options)
|
||||||
} = null
|
const answer = new Promise<string>((resolve, reject) => {
|
||||||
const answer = new Promise<string>(
|
const output: string[] = []
|
||||||
(resolve, reject) =>
|
childProcess.stdout.on("data", (data) => {
|
||||||
(childProcess = exec(command, options, (error, stdout, stderr) => {
|
output.push(data.toString())
|
||||||
if (error) return reject(error.toString())
|
})
|
||||||
if (stderr) return reject(stderr.toString())
|
const outputError: string[] = []
|
||||||
return resolve(stdout.toString())
|
childProcess.stderr.on("data", (data) => {
|
||||||
})),
|
outputError.push(data.toString())
|
||||||
)
|
})
|
||||||
|
|
||||||
|
childProcess.on("close", (code) => {
|
||||||
|
if (code === 0) {
|
||||||
|
return resolve(output.join(""))
|
||||||
|
}
|
||||||
|
return reject(outputError.join(""))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
wait() {
|
wait() {
|
||||||
return answer
|
return answer
|
||||||
},
|
},
|
||||||
async term() {
|
async term() {
|
||||||
childProcess?.kill()
|
childProcess.kill()
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
runCommand: async (
|
|
||||||
command: string,
|
|
||||||
options: { env?: Record<string, string>; timeout?: number },
|
|
||||||
): Promise<string> => {
|
|
||||||
const answer = new Promise<string>((resolve, reject) =>
|
|
||||||
exec(command, options, (error, stdout, stderr) => {
|
|
||||||
if (error) return reject(error.toString())
|
|
||||||
if (stderr) return reject(stderr.toString())
|
|
||||||
return resolve(stdout.toString())
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
|
|
||||||
return answer
|
|
||||||
},
|
|
||||||
checkPortListening: checkPortListening.bind(null, effects),
|
checkPortListening: checkPortListening.bind(null, effects),
|
||||||
checkWebUrl: checkWebUrl.bind(null, effects),
|
checkWebUrl: checkWebUrl.bind(null, effects),
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user