Install transitive dependencies of versions properly (#5745)
* install recursive deps properly * fix up
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import { get_project, get_version_many } from '@/helpers/cache.js'
|
||||
import { get_project, get_version, get_version_many } from '@/helpers/cache.js'
|
||||
import { add_project_from_version, check_installed } from '@/helpers/profile.js'
|
||||
import {
|
||||
add_server_to_profile,
|
||||
@@ -49,32 +49,137 @@ export const isVersionCompatible = (version, project, instance) => {
|
||||
}
|
||||
|
||||
export const installVersionDependencies = async (profile, version, onDepInstalling) => {
|
||||
for (const dep of version.dependencies) {
|
||||
if (dep.dependency_type !== 'required') continue
|
||||
// disallow fabric api install on quilt
|
||||
if (dep.project_id === 'P7dR8mSH' && profile.loader === 'quilt') continue
|
||||
if (dep.version_id) {
|
||||
if (dep.project_id && (await check_installed(profile.path, dep.project_id))) continue
|
||||
if (dep.project_id && onDepInstalling) {
|
||||
const depProject = await get_project(dep.project_id, 'bypass').catch(() => null)
|
||||
if (depProject) onDepInstalling(depProject)
|
||||
}
|
||||
await add_project_from_version(profile.path, dep.version_id)
|
||||
} else {
|
||||
if (dep.project_id && (await check_installed(profile.path, dep.project_id))) continue
|
||||
const projectNames = new Map()
|
||||
const storeProjectName = (p) => {
|
||||
if (p?.id && p.title) projectNames.set(p.id, p.title)
|
||||
}
|
||||
|
||||
const depProject = await get_project(dep.project_id, 'bypass')
|
||||
if (onDepInstalling) onDepInstalling(depProject)
|
||||
const visitedVersions = new Set()
|
||||
const announcedProjects = new Set()
|
||||
const queuedVersionIds = new Set()
|
||||
const queuedProjectVersions = new Map()
|
||||
const queuedInstalls = []
|
||||
const installedProjectCache = new Map()
|
||||
|
||||
const depVersions = (await get_version_many(depProject.versions, 'bypass')).sort(
|
||||
(a, b) => dayjs(b.date_published) - dayjs(a.date_published),
|
||||
)
|
||||
|
||||
const latest = findPreferredVersion(depVersions, dep, profile)
|
||||
if (latest) {
|
||||
await add_project_from_version(profile.path, latest.id)
|
||||
}
|
||||
const isProjectInstalled = async (projectId) => {
|
||||
if (!projectId) return false
|
||||
if (installedProjectCache.has(projectId)) {
|
||||
return installedProjectCache.get(projectId)
|
||||
}
|
||||
const installed = await check_installed(profile.path, projectId)
|
||||
installedProjectCache.set(projectId, installed)
|
||||
return installed
|
||||
}
|
||||
|
||||
const queueInstall = async (projectId, resolvedVersion) => {
|
||||
if (!resolvedVersion?.id) return false
|
||||
|
||||
const versionId = resolvedVersion.id
|
||||
const resolvedProjectId = projectId ?? resolvedVersion.project_id ?? null
|
||||
|
||||
if (resolvedProjectId) {
|
||||
if (await isProjectInstalled(resolvedProjectId)) return false
|
||||
|
||||
const existingVersionId = queuedProjectVersions.get(resolvedProjectId)
|
||||
if (existingVersionId && existingVersionId !== versionId) return false
|
||||
if (existingVersionId === versionId) return false
|
||||
}
|
||||
|
||||
if (queuedVersionIds.has(versionId)) return false
|
||||
|
||||
queuedVersionIds.add(versionId)
|
||||
if (resolvedProjectId) {
|
||||
queuedProjectVersions.set(resolvedProjectId, versionId)
|
||||
}
|
||||
queuedInstalls.push({ versionId, projectId: resolvedProjectId })
|
||||
return true
|
||||
}
|
||||
|
||||
const announceDependency = async (projectId, resolvedVersion) => {
|
||||
if (!onDepInstalling || !projectId) return
|
||||
if (announcedProjects.has(projectId)) return
|
||||
|
||||
const depProject = await get_project(projectId, 'bypass').catch(() => null)
|
||||
if (!depProject) return
|
||||
|
||||
storeProjectName(depProject)
|
||||
onDepInstalling(depProject, resolvedVersion ?? undefined)
|
||||
announcedProjects.add(projectId)
|
||||
}
|
||||
|
||||
const resolveDependency = async (dep) => {
|
||||
let depVersion = null
|
||||
let depProjectId = dep.project_id ?? null
|
||||
|
||||
if (dep.version_id) {
|
||||
depVersion = await get_version(dep.version_id, 'bypass').catch(() => null)
|
||||
if (!depVersion) return null
|
||||
|
||||
depProjectId = depProjectId ?? depVersion.project_id ?? null
|
||||
if (depProjectId && !projectNames.has(depProjectId)) {
|
||||
const p = await get_project(depProjectId, 'bypass').catch(() => null)
|
||||
storeProjectName(p)
|
||||
}
|
||||
} else if (dep.project_id) {
|
||||
const depProject = await get_project(dep.project_id, 'bypass').catch(() => null)
|
||||
if (!depProject) return null
|
||||
|
||||
storeProjectName(depProject)
|
||||
|
||||
const depVersions = await get_version_many(depProject.versions, 'bypass').catch(() => [])
|
||||
depVersion = findPreferredVersion(
|
||||
depVersions.sort((a, b) => dayjs(b.date_published) - dayjs(a.date_published)),
|
||||
dep,
|
||||
profile,
|
||||
)
|
||||
if (!depVersion) return null
|
||||
|
||||
depProjectId = dep.project_id
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
|
||||
return { depVersion, depProjectId }
|
||||
}
|
||||
|
||||
const collectDependenciesForVersion = async (inputVersion) => {
|
||||
if (!inputVersion?.id || visitedVersions.has(inputVersion.id)) return
|
||||
visitedVersions.add(inputVersion.id)
|
||||
|
||||
if (inputVersion.project_id && !projectNames.has(inputVersion.project_id)) {
|
||||
const p = await get_project(inputVersion.project_id, 'bypass').catch(() => null)
|
||||
storeProjectName(p)
|
||||
}
|
||||
|
||||
for (const dep of inputVersion.dependencies ?? []) {
|
||||
if (dep.dependency_type !== 'required') continue
|
||||
if (dep.project_id === 'P7dR8mSH' && profile.loader === 'quilt') continue
|
||||
|
||||
const resolved = await resolveDependency(dep, inputVersion)
|
||||
if (!resolved) continue
|
||||
|
||||
const { depVersion, depProjectId } = resolved
|
||||
const queued = await queueInstall(depProjectId, depVersion)
|
||||
if (queued && depProjectId) {
|
||||
await announceDependency(depProjectId, depVersion)
|
||||
}
|
||||
|
||||
await collectDependenciesForVersion(depVersion)
|
||||
}
|
||||
}
|
||||
|
||||
await collectDependenciesForVersion(version)
|
||||
|
||||
if (queuedInstalls.length === 0) return
|
||||
|
||||
const batchSize = 8
|
||||
for (let i = 0; i < queuedInstalls.length; i += batchSize) {
|
||||
const batch = queuedInstalls.slice(i, i + batchSize)
|
||||
await Promise.all(
|
||||
batch.map(async ({ versionId }) => {
|
||||
await add_project_from_version(profile.path, versionId)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user