mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-31 04:23:40 +00:00
Feature/sdk040dependencies (#2609)
* update registry upload to take id for new admin permissions (#2605) * wip * wip: Get the get dependencies * wip check_dependencies * wip: Get the build working to the vm * wip: Add in the last of the things that where needed for the new sdk * Add fix * wip: implement the changes * wip: Fix the naming --------- Co-authored-by: Lucy <12953208+elvece@users.noreply.github.com>
This commit is contained in:
44
sdk/lib/dependencies/DependencyConfig.ts
Normal file
44
sdk/lib/dependencies/DependencyConfig.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import {
|
||||
DependencyConfig as DependencyConfigType,
|
||||
DeepPartial,
|
||||
Effects,
|
||||
} from "../types"
|
||||
import { deepEqual } from "../util/deepEqual"
|
||||
import { deepMerge } from "../util/deepMerge"
|
||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||
|
||||
export type Update<QueryResults, RemoteConfig> = (options: {
|
||||
remoteConfig: RemoteConfig
|
||||
queryResults: QueryResults
|
||||
}) => Promise<RemoteConfig>
|
||||
|
||||
export class DependencyConfig<
|
||||
Manifest extends SDKManifest,
|
||||
Store,
|
||||
Input extends Record<string, any>,
|
||||
RemoteConfig extends Record<string, any>,
|
||||
> {
|
||||
static defaultUpdate = async (options: {
|
||||
queryResults: unknown
|
||||
remoteConfig: unknown
|
||||
}): Promise<unknown> => {
|
||||
return deepMerge({}, options.remoteConfig, options.queryResults || {})
|
||||
}
|
||||
constructor(
|
||||
readonly dependencyConfig: (options: {
|
||||
effects: Effects
|
||||
localConfig: Input
|
||||
}) => Promise<void | DeepPartial<RemoteConfig>>,
|
||||
readonly update: Update<
|
||||
void | DeepPartial<RemoteConfig>,
|
||||
RemoteConfig
|
||||
> = DependencyConfig.defaultUpdate as any,
|
||||
) {}
|
||||
|
||||
async query(options: { effects: Effects; localConfig: unknown }) {
|
||||
return this.dependencyConfig({
|
||||
localConfig: options.localConfig as Input,
|
||||
effects: options.effects,
|
||||
})
|
||||
}
|
||||
}
|
||||
115
sdk/lib/dependencies/dependencies.ts
Normal file
115
sdk/lib/dependencies/dependencies.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import {
|
||||
Effects,
|
||||
PackageId,
|
||||
DependencyRequirement,
|
||||
SetHealth,
|
||||
CheckDependencyResult,
|
||||
} from "../types"
|
||||
|
||||
export type CheckAllDependencies = {
|
||||
notRunning: () => Promise<CheckDependencyResult[]>
|
||||
|
||||
notInstalled: () => Promise<CheckDependencyResult[]>
|
||||
|
||||
healthErrors: () => Promise<{ [id: string]: SetHealth[] }>
|
||||
throwIfNotRunning: () => Promise<void>
|
||||
throwIfNotValid: () => Promise<undefined>
|
||||
throwIfNotInstalled: () => Promise<void>
|
||||
throwIfError: () => Promise<void>
|
||||
isValid: () => Promise<boolean>
|
||||
}
|
||||
export function checkAllDependencies(effects: Effects): CheckAllDependencies {
|
||||
const dependenciesPromise = effects.getDependencies()
|
||||
const resultsPromise = dependenciesPromise.then((dependencies) =>
|
||||
effects.checkDependencies({
|
||||
packageIds: dependencies.map((dep) => dep.id),
|
||||
}),
|
||||
)
|
||||
|
||||
const dependenciesByIdPromise = dependenciesPromise.then((d) =>
|
||||
d.reduce(
|
||||
(acc, dep) => {
|
||||
acc[dep.id] = dep
|
||||
return acc
|
||||
},
|
||||
{} as { [id: PackageId]: DependencyRequirement },
|
||||
),
|
||||
)
|
||||
|
||||
const healthErrors = async () => {
|
||||
const results = await resultsPromise
|
||||
const dependenciesById = await dependenciesByIdPromise
|
||||
const answer: { [id: PackageId]: SetHealth[] } = {}
|
||||
for (const result of results) {
|
||||
const dependency = dependenciesById[result.packageId]
|
||||
if (!dependency) continue
|
||||
if (dependency.kind !== "running") continue
|
||||
|
||||
const healthChecks = result.healthChecks
|
||||
.filter((x) => dependency.healthChecks.includes(x.id))
|
||||
.filter((x) => !!x.message)
|
||||
if (healthChecks.length === 0) continue
|
||||
answer[result.packageId] = healthChecks
|
||||
}
|
||||
return answer
|
||||
}
|
||||
const notInstalled = () =>
|
||||
resultsPromise.then((x) => x.filter((x) => !x.isInstalled))
|
||||
const notRunning = async () => {
|
||||
const results = await resultsPromise
|
||||
const dependenciesById = await dependenciesByIdPromise
|
||||
return results.filter((x) => {
|
||||
const dependency = dependenciesById[x.packageId]
|
||||
if (!dependency) return false
|
||||
if (dependency.kind !== "running") return false
|
||||
return !x.isRunning
|
||||
})
|
||||
}
|
||||
const entries = <B>(x: { [k: string]: B }) => Object.entries(x)
|
||||
const first = <A>(x: A[]): A | undefined => x[0]
|
||||
const sinkVoid = <A>(x: A) => void 0
|
||||
const throwIfError = () =>
|
||||
healthErrors()
|
||||
.then(entries)
|
||||
.then(first)
|
||||
.then((x) => {
|
||||
if (!x) return
|
||||
const [id, healthChecks] = x
|
||||
if (healthChecks.length > 0)
|
||||
throw `Package ${id} has the following errors: ${healthChecks.map((x) => x.message).join(", ")}`
|
||||
})
|
||||
const throwIfNotRunning = () =>
|
||||
notRunning().then((results) => {
|
||||
if (results[0])
|
||||
throw new Error(`Package ${results[0].packageId} is not running`)
|
||||
})
|
||||
|
||||
const throwIfNotInstalled = () =>
|
||||
notInstalled().then((results) => {
|
||||
if (results[0])
|
||||
throw new Error(`Package ${results[0].packageId} is not installed`)
|
||||
})
|
||||
const throwIfNotValid = async () =>
|
||||
Promise.all([
|
||||
throwIfNotRunning(),
|
||||
throwIfNotInstalled(),
|
||||
throwIfError(),
|
||||
]).then(sinkVoid)
|
||||
|
||||
const isValid = () =>
|
||||
throwIfNotValid().then(
|
||||
() => true,
|
||||
() => false,
|
||||
)
|
||||
|
||||
return {
|
||||
notRunning,
|
||||
notInstalled,
|
||||
healthErrors,
|
||||
throwIfNotRunning,
|
||||
throwIfNotValid,
|
||||
throwIfNotInstalled,
|
||||
throwIfError,
|
||||
isValid,
|
||||
}
|
||||
}
|
||||
9
sdk/lib/dependencies/index.ts
Normal file
9
sdk/lib/dependencies/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
// prettier-ignore
|
||||
export type ReadonlyDeep<A> =
|
||||
A extends Function ? A :
|
||||
A extends {} ? { readonly [K in keyof A]: ReadonlyDeep<A[K]> } : A;
|
||||
export type MaybePromise<A> = Promise<A> | A
|
||||
export type Message = string
|
||||
|
||||
import "./DependencyConfig"
|
||||
import "./setupDependencyConfig"
|
||||
22
sdk/lib/dependencies/setupDependencyConfig.ts
Normal file
22
sdk/lib/dependencies/setupDependencyConfig.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Config } from "../config/builder/config"
|
||||
import { SDKManifest } from "../manifest/ManifestTypes"
|
||||
import { ExpectedExports } from "../types"
|
||||
import { DependencyConfig } from "./DependencyConfig"
|
||||
|
||||
export function setupDependencyConfig<
|
||||
Store,
|
||||
Input extends Record<string, any>,
|
||||
Manifest extends SDKManifest,
|
||||
>(
|
||||
_config: Config<Input, Store> | Config<Input, never>,
|
||||
autoConfigs: {
|
||||
[key in keyof Manifest["dependencies"] & string]: DependencyConfig<
|
||||
Manifest,
|
||||
Store,
|
||||
Input,
|
||||
any
|
||||
> | null
|
||||
},
|
||||
): ExpectedExports.dependencyConfig {
|
||||
return autoConfigs
|
||||
}
|
||||
Reference in New Issue
Block a user