mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-04-01 21:13:09 +00:00
* store, properties, manifest * interfaces * init and backups * fix init and backups * file models * more versions * dependencies * config except dynamic types * clean up config * remove disabled from non-dynamic vaues * actions * standardize example code block formats * wip: actions refactor Co-authored-by: Jade <Blu-J@users.noreply.github.com> * commit types * fix types * update types * update action request type * update apis * add description to actionrequest * clean up imports * revert package json * chore: Remove the recursive to the index * chore: Remove the other thing I was testing * flatten action requests * update container runtime with new config paradigm * new actions strategy * seems to be working * misc backend fixes * fix fe bugs * only show breakages if breakages * only show success modal if result * don't panic on failed removal * hide config from actions page * polyfill autoconfig * use metadata strategy for actions instead of prev * misc fixes * chore: split the sdk into 2 libs (#2736) * follow sideload progress (#2718) * follow sideload progress * small bugfix * shareReplay with no refcount false * don't wrap sideload progress in RPCResult * dont present toast --------- Co-authored-by: Aiden McClelland <me@drbonez.dev> * chore: Add the initial of the creation of the two sdk * chore: Add in the baseDist * chore: Add in the baseDist * chore: Get the web and the runtime-container running * chore: Remove the empty file * chore: Fix it so the container-runtime works --------- Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com> Co-authored-by: Aiden McClelland <me@drbonez.dev> * misc fixes * update todos * minor clean up * fix link script * update node version in CI test * fix node version syntax in ci build * wip: fixing callbacks * fix sdk makefile dependencies * add support for const outside of main * update apis * don't panic! * Chore: Capture weird case on rpc, and log that * fix procedure id issue * pass input value for dep auto config * handle disabled and warning for actions * chore: Fix for link not having node_modules * sdk fixes * fix build * fix build * fix build --------- Co-authored-by: Matt Hill <mattnine@protonmail.com> Co-authored-by: Jade <Blu-J@users.noreply.github.com> Co-authored-by: J H <dragondef@gmail.com> Co-authored-by: Jade <2364004+Blu-J@users.noreply.github.com> Co-authored-by: Matt Hill <MattDHill@users.noreply.github.com>
202 lines
6.8 KiB
TypeScript
202 lines
6.8 KiB
TypeScript
import { ExtendedVersion, VersionRange } from "../exver"
|
|
import { PackageId, HealthCheckId } from "../types"
|
|
import { Effects } from "../Effects"
|
|
|
|
export type CheckDependencies<DependencyId extends PackageId = PackageId> = {
|
|
installedSatisfied: (packageId: DependencyId) => boolean
|
|
installedVersionSatisfied: (packageId: DependencyId) => boolean
|
|
runningSatisfied: (packageId: DependencyId) => boolean
|
|
actionsSatisfied: (packageId: DependencyId) => boolean
|
|
healthCheckSatisfied: (
|
|
packageId: DependencyId,
|
|
healthCheckId: HealthCheckId,
|
|
) => boolean
|
|
satisfied: () => boolean
|
|
|
|
throwIfInstalledNotSatisfied: (packageId: DependencyId) => void
|
|
throwIfInstalledVersionNotSatisfied: (packageId: DependencyId) => void
|
|
throwIfRunningNotSatisfied: (packageId: DependencyId) => void
|
|
throwIfActionsNotSatisfied: (packageId: DependencyId) => void
|
|
throwIfHealthNotSatisfied: (
|
|
packageId: DependencyId,
|
|
healthCheckId?: HealthCheckId,
|
|
) => void
|
|
throwIfNotSatisfied: (packageId?: DependencyId) => void
|
|
}
|
|
export async function checkDependencies<
|
|
DependencyId extends PackageId = PackageId,
|
|
>(
|
|
effects: Effects,
|
|
packageIds?: DependencyId[],
|
|
): Promise<CheckDependencies<DependencyId>> {
|
|
let [dependencies, results] = await Promise.all([
|
|
effects.getDependencies(),
|
|
effects.checkDependencies({
|
|
packageIds,
|
|
}),
|
|
])
|
|
if (packageIds) {
|
|
dependencies = dependencies.filter((d) =>
|
|
(packageIds as PackageId[]).includes(d.id),
|
|
)
|
|
}
|
|
|
|
const find = (packageId: DependencyId) => {
|
|
const dependencyRequirement = dependencies.find((d) => d.id === packageId)
|
|
const dependencyResult = results.find((d) => d.packageId === packageId)
|
|
if (!dependencyRequirement || !dependencyResult) {
|
|
throw new Error(`Unknown DependencyId ${packageId}`)
|
|
}
|
|
return { requirement: dependencyRequirement, result: dependencyResult }
|
|
}
|
|
|
|
const installedSatisfied = (packageId: DependencyId) =>
|
|
!!find(packageId).result.installedVersion
|
|
const installedVersionSatisfied = (packageId: DependencyId) => {
|
|
const dep = find(packageId)
|
|
return (
|
|
!!dep.result.installedVersion &&
|
|
ExtendedVersion.parse(dep.result.installedVersion).satisfies(
|
|
VersionRange.parse(dep.requirement.versionRange),
|
|
)
|
|
)
|
|
}
|
|
const runningSatisfied = (packageId: DependencyId) => {
|
|
const dep = find(packageId)
|
|
return dep.requirement.kind !== "running" || dep.result.isRunning
|
|
}
|
|
const actionsSatisfied = (packageId: DependencyId) =>
|
|
Object.keys(find(packageId).result.requestedActions).length === 0
|
|
const healthCheckSatisfied = (
|
|
packageId: DependencyId,
|
|
healthCheckId?: HealthCheckId,
|
|
) => {
|
|
const dep = find(packageId)
|
|
if (
|
|
healthCheckId &&
|
|
(dep.requirement.kind !== "running" ||
|
|
!dep.requirement.healthChecks.includes(healthCheckId))
|
|
) {
|
|
throw new Error(`Unknown HealthCheckId ${healthCheckId}`)
|
|
}
|
|
const errors = Object.entries(dep.result.healthChecks)
|
|
.filter(([id, _]) => (healthCheckId ? id === healthCheckId : true))
|
|
.filter(([_, res]) => res.result !== "success")
|
|
return errors.length === 0
|
|
}
|
|
const pkgSatisfied = (packageId: DependencyId) =>
|
|
installedSatisfied(packageId) &&
|
|
installedVersionSatisfied(packageId) &&
|
|
runningSatisfied(packageId) &&
|
|
actionsSatisfied(packageId) &&
|
|
healthCheckSatisfied(packageId)
|
|
const satisfied = (packageId?: DependencyId) =>
|
|
packageId
|
|
? pkgSatisfied(packageId)
|
|
: dependencies.every((d) => pkgSatisfied(d.id as DependencyId))
|
|
|
|
const throwIfInstalledNotSatisfied = (packageId: DependencyId) => {
|
|
const dep = find(packageId)
|
|
if (!dep.result.installedVersion) {
|
|
throw new Error(`${dep.result.title || packageId} is not installed`)
|
|
}
|
|
}
|
|
const throwIfInstalledVersionNotSatisfied = (packageId: DependencyId) => {
|
|
const dep = find(packageId)
|
|
if (!dep.result.installedVersion) {
|
|
throw new Error(`${dep.result.title || packageId} is not installed`)
|
|
}
|
|
if (
|
|
![dep.result.installedVersion, ...dep.result.satisfies].find((v) =>
|
|
ExtendedVersion.parse(v).satisfies(
|
|
VersionRange.parse(dep.requirement.versionRange),
|
|
),
|
|
)
|
|
) {
|
|
throw new Error(
|
|
`Installed version ${dep.result.installedVersion} of ${dep.result.title || packageId} does not match expected version range ${dep.requirement.versionRange}`,
|
|
)
|
|
}
|
|
}
|
|
const throwIfRunningNotSatisfied = (packageId: DependencyId) => {
|
|
const dep = find(packageId)
|
|
if (dep.requirement.kind === "running" && !dep.result.isRunning) {
|
|
throw new Error(`${dep.result.title || packageId} is not running`)
|
|
}
|
|
}
|
|
const throwIfActionsNotSatisfied = (packageId: DependencyId) => {
|
|
const dep = find(packageId)
|
|
const reqs = Object.keys(dep.result.requestedActions)
|
|
if (reqs.length) {
|
|
throw new Error(
|
|
`The following action requests have not been fulfilled: ${reqs.join(", ")}`,
|
|
)
|
|
}
|
|
}
|
|
const throwIfHealthNotSatisfied = (
|
|
packageId: DependencyId,
|
|
healthCheckId?: HealthCheckId,
|
|
) => {
|
|
const dep = find(packageId)
|
|
if (
|
|
healthCheckId &&
|
|
(dep.requirement.kind !== "running" ||
|
|
!dep.requirement.healthChecks.includes(healthCheckId))
|
|
) {
|
|
throw new Error(`Unknown HealthCheckId ${healthCheckId}`)
|
|
}
|
|
const errors = Object.entries(dep.result.healthChecks)
|
|
.filter(([id, _]) => (healthCheckId ? id === healthCheckId : true))
|
|
.filter(([_, res]) => res.result !== "success")
|
|
if (errors.length) {
|
|
throw new Error(
|
|
errors
|
|
.map(
|
|
([_, e]) =>
|
|
`Health Check ${e.name} of ${dep.result.title || packageId} failed with status ${e.result}${e.message ? `: ${e.message}` : ""}`,
|
|
)
|
|
.join("; "),
|
|
)
|
|
}
|
|
}
|
|
const throwIfPkgNotSatisfied = (packageId: DependencyId) => {
|
|
throwIfInstalledNotSatisfied(packageId)
|
|
throwIfInstalledVersionNotSatisfied(packageId)
|
|
throwIfRunningNotSatisfied(packageId)
|
|
throwIfActionsNotSatisfied(packageId)
|
|
throwIfHealthNotSatisfied(packageId)
|
|
}
|
|
const throwIfNotSatisfied = (packageId?: DependencyId) =>
|
|
packageId
|
|
? throwIfPkgNotSatisfied(packageId)
|
|
: (() => {
|
|
const err = dependencies.flatMap((d) => {
|
|
try {
|
|
throwIfPkgNotSatisfied(d.id as DependencyId)
|
|
} catch (e) {
|
|
if (e instanceof Error) return [e.message]
|
|
throw e
|
|
}
|
|
return []
|
|
})
|
|
if (err.length) {
|
|
throw new Error(err.join("; "))
|
|
}
|
|
})()
|
|
|
|
return {
|
|
installedSatisfied,
|
|
installedVersionSatisfied,
|
|
runningSatisfied,
|
|
actionsSatisfied,
|
|
healthCheckSatisfied,
|
|
satisfied,
|
|
throwIfInstalledNotSatisfied,
|
|
throwIfInstalledVersionNotSatisfied,
|
|
throwIfRunningNotSatisfied,
|
|
throwIfActionsNotSatisfied,
|
|
throwIfHealthNotSatisfied,
|
|
throwIfNotSatisfied,
|
|
}
|
|
}
|