mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-07-16 16:58:33 +00:00
Compare commits
4 Commits
version-bu
...
matt/be-22
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dac9e5c25f | ||
|
|
e45e7e45ee | ||
|
|
c97298a4e8 | ||
|
|
ae328319d3 |
@@ -0,0 +1,108 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { nextTick, ref } from 'vue'
|
||||
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { usePackUpdateStatus } from '@/workbench/extensions/manager/composables/nodePack/usePackUpdateStatus'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
vi.mock('@/workbench/extensions/manager/stores/comfyManagerStore', () => ({
|
||||
useComfyManagerStore: vi.fn()
|
||||
}))
|
||||
|
||||
type NodePack = components['schemas']['Node']
|
||||
type ManagerStoreReturn = ReturnType<typeof useComfyManagerStore>
|
||||
|
||||
const mockUseComfyManagerStore = vi.mocked(useComfyManagerStore)
|
||||
|
||||
const mockIsPackInstalled = vi.fn()
|
||||
const mockIsPackEnabled = vi.fn()
|
||||
const mockGetInstalledPackVersion = vi.fn()
|
||||
|
||||
function makePack(overrides: Partial<NodePack> = {}): NodePack {
|
||||
return {
|
||||
id: 'pack-1',
|
||||
name: 'Pack',
|
||||
latest_version: { version: '2.0.0' },
|
||||
...overrides
|
||||
} as NodePack
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockIsPackInstalled.mockReturnValue(true)
|
||||
mockIsPackEnabled.mockReturnValue(true)
|
||||
mockGetInstalledPackVersion.mockReturnValue('1.0.0')
|
||||
mockUseComfyManagerStore.mockReturnValue({
|
||||
isPackInstalled: mockIsPackInstalled,
|
||||
isPackEnabled: mockIsPackEnabled,
|
||||
getInstalledPackVersion: mockGetInstalledPackVersion
|
||||
} as Partial<ManagerStoreReturn> as ManagerStoreReturn)
|
||||
})
|
||||
|
||||
describe('usePackUpdateStatus', () => {
|
||||
it('reports an update when the latest release is newer', () => {
|
||||
const { isUpdateAvailable, installedVersion, latestVersion } =
|
||||
usePackUpdateStatus(makePack())
|
||||
|
||||
expect(isUpdateAvailable.value).toBe(true)
|
||||
expect(installedVersion.value).toBe('1.0.0')
|
||||
expect(latestVersion.value).toBe('2.0.0')
|
||||
})
|
||||
|
||||
it('reports no update when installed matches latest', () => {
|
||||
mockGetInstalledPackVersion.mockReturnValue('2.0.0')
|
||||
|
||||
const { isUpdateAvailable } = usePackUpdateStatus(makePack())
|
||||
|
||||
expect(isUpdateAvailable.value).toBe(false)
|
||||
})
|
||||
|
||||
it('reports no update when the pack is not installed', () => {
|
||||
mockIsPackInstalled.mockReturnValue(false)
|
||||
mockGetInstalledPackVersion.mockReturnValue(undefined)
|
||||
|
||||
const { isUpdateAvailable } = usePackUpdateStatus(makePack())
|
||||
|
||||
expect(isUpdateAvailable.value).toBe(false)
|
||||
})
|
||||
|
||||
it('treats a non-semver installed version as a nightly build', () => {
|
||||
mockGetInstalledPackVersion.mockReturnValue('abc1234')
|
||||
|
||||
const { isNightlyPack, isUpdateAvailable, canTryNightlyUpdate } =
|
||||
usePackUpdateStatus(makePack())
|
||||
|
||||
expect(isNightlyPack.value).toBe(true)
|
||||
expect(isUpdateAvailable.value).toBe(false)
|
||||
expect(canTryNightlyUpdate.value).toBe(true)
|
||||
})
|
||||
|
||||
it('only allows a nightly update when installed and enabled', () => {
|
||||
mockGetInstalledPackVersion.mockReturnValue('abc1234')
|
||||
mockIsPackEnabled.mockReturnValue(false)
|
||||
|
||||
const { canTryNightlyUpdate } = usePackUpdateStatus(makePack())
|
||||
|
||||
expect(canTryNightlyUpdate.value).toBe(false)
|
||||
})
|
||||
|
||||
it('reports no update when the registry omits a latest version', () => {
|
||||
const { isUpdateAvailable } = usePackUpdateStatus(
|
||||
makePack({ latest_version: undefined })
|
||||
)
|
||||
|
||||
expect(isUpdateAvailable.value).toBe(false)
|
||||
})
|
||||
|
||||
it('recomputes when the source pack changes', async () => {
|
||||
const source = ref(makePack({ latest_version: { version: '1.0.0' } }))
|
||||
const { isUpdateAvailable } = usePackUpdateStatus(source)
|
||||
|
||||
expect(isUpdateAvailable.value).toBe(false)
|
||||
|
||||
source.value = makePack({ latest_version: { version: '3.0.0' } })
|
||||
await nextTick()
|
||||
|
||||
expect(isUpdateAvailable.value).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -1,42 +1,30 @@
|
||||
import type { MaybeRefOrGetter } from 'vue'
|
||||
import { computed, toValue } from 'vue'
|
||||
|
||||
import { compare, valid } from 'semver'
|
||||
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
import { getPackUpdateStatus } from '@/workbench/extensions/manager/utils/packUpdateStatus'
|
||||
|
||||
export const usePackUpdateStatus = (
|
||||
nodePackSource: MaybeRefOrGetter<components['schemas']['Node']>
|
||||
) => {
|
||||
const { isPackInstalled, isPackEnabled, getInstalledPackVersion } =
|
||||
useComfyManagerStore()
|
||||
const managerStore = useComfyManagerStore()
|
||||
|
||||
// Use toValue to unwrap the source reactively inside computeds
|
||||
const nodePack = computed(() => toValue(nodePackSource))
|
||||
|
||||
const isInstalled = computed(() => isPackInstalled(nodePack.value?.id))
|
||||
const isEnabled = computed(() => isPackEnabled(nodePack.value?.id))
|
||||
const installedVersion = computed(() =>
|
||||
getInstalledPackVersion(nodePack.value?.id ?? '')
|
||||
)
|
||||
const latestVersion = computed(() => nodePack.value?.latest_version?.version)
|
||||
|
||||
const isNightlyPack = computed(
|
||||
() => !!installedVersion.value && !valid(installedVersion.value)
|
||||
const status = computed(() =>
|
||||
getPackUpdateStatus(nodePack.value, managerStore)
|
||||
)
|
||||
|
||||
const isUpdateAvailable = computed(() => {
|
||||
if (
|
||||
!isInstalled.value ||
|
||||
isNightlyPack.value ||
|
||||
!latestVersion.value ||
|
||||
!installedVersion.value
|
||||
) {
|
||||
return false
|
||||
}
|
||||
return compare(latestVersion.value, installedVersion.value) > 0
|
||||
})
|
||||
const isInstalled = computed(() => status.value.isInstalled)
|
||||
const isEnabled = computed(() =>
|
||||
managerStore.isPackEnabled(nodePack.value?.id)
|
||||
)
|
||||
const installedVersion = computed(() => status.value.installedVersion)
|
||||
const latestVersion = computed(() => status.value.latestVersion)
|
||||
const isNightlyPack = computed(() => status.value.isNightly)
|
||||
const isUpdateAvailable = computed(() => status.value.isUpdateAvailable)
|
||||
|
||||
/**
|
||||
* Nightly packs can always "try update" since we cannot compare git hashes
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { valid } from 'semver'
|
||||
import { computed } from 'vue'
|
||||
import type { Ref } from 'vue'
|
||||
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
import { isNightlyVersion } from '@/workbench/extensions/manager/utils/packUpdateStatus'
|
||||
|
||||
type NodePack = components['schemas']['Node']
|
||||
|
||||
@@ -50,9 +50,8 @@ export function usePacksSelection(nodePacks: Ref<NodePack[]>) {
|
||||
installedPacks.value.filter((pack) => {
|
||||
if (!pack.id) return false
|
||||
const version = managerStore.getInstalledPackVersion(pack.id)
|
||||
const isNightly = !!version && !valid(version)
|
||||
const isEnabled = managerStore.isPackEnabled(pack.id)
|
||||
return isNightly && isEnabled
|
||||
return isNightlyVersion(version) && isEnabled
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { compare, valid } from 'semver'
|
||||
import { computed, onMounted } from 'vue'
|
||||
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import { useInstalledPacks } from '@/workbench/extensions/manager/composables/nodePack/useInstalledPacks'
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
import { getPackUpdateStatus } from '@/workbench/extensions/manager/utils/packUpdateStatus'
|
||||
|
||||
/**
|
||||
* Composable to find NodePacks that have updates available
|
||||
@@ -14,24 +14,8 @@ export const useUpdateAvailableNodes = () => {
|
||||
const { installedPacks, isLoading, error, startFetchInstalled } =
|
||||
useInstalledPacks()
|
||||
|
||||
// Check if a pack has updates available (same logic as usePackUpdateStatus)
|
||||
const isOutdatedPack = (pack: components['schemas']['Node']) => {
|
||||
const isInstalled = comfyManagerStore.isPackInstalled(pack?.id)
|
||||
if (!isInstalled) return false
|
||||
|
||||
const installedVersion = comfyManagerStore.getInstalledPackVersion(
|
||||
pack.id ?? ''
|
||||
)
|
||||
const latestVersion = pack.latest_version?.version
|
||||
|
||||
const isNightlyPack = !!installedVersion && !valid(installedVersion)
|
||||
|
||||
if (isNightlyPack || !latestVersion || !installedVersion) {
|
||||
return false
|
||||
}
|
||||
|
||||
return compare(latestVersion, installedVersion) > 0
|
||||
}
|
||||
const isOutdatedPack = (pack: components['schemas']['Node']) =>
|
||||
getPackUpdateStatus(pack, comfyManagerStore).isUpdateAvailable
|
||||
|
||||
const filterOutdatedPacks = (packs: components['schemas']['Node'][]) =>
|
||||
packs.filter(isOutdatedPack)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { whenever } from '@vueuse/core'
|
||||
import { orderBy } from 'es-toolkit/compat'
|
||||
import { compare, valid } from 'semver'
|
||||
import type { Ref } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
@@ -11,6 +10,7 @@ import { useWorkflowPacks } from '@/workbench/extensions/manager/composables/nod
|
||||
import { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
import { useConflictDetectionStore } from '@/workbench/extensions/manager/stores/conflictDetectionStore'
|
||||
import { ManagerTab } from '@/workbench/extensions/manager/types/comfyManagerTypes'
|
||||
import { getPackUpdateStatus } from '@/workbench/extensions/manager/utils/packUpdateStatus'
|
||||
|
||||
type NodePack = components['schemas']['Node']
|
||||
|
||||
@@ -72,21 +72,9 @@ export function useManagerDisplayPacks(
|
||||
)
|
||||
|
||||
const filterOutdated = (packs: NodePack[]) =>
|
||||
packs.filter((p) => {
|
||||
const installedVersion = comfyManagerStore.getInstalledPackVersion(
|
||||
p.id ?? ''
|
||||
)
|
||||
const latestVersion = p.latest_version?.version
|
||||
if (
|
||||
!comfyManagerStore.isPackInstalled(p.id) ||
|
||||
!installedVersion ||
|
||||
!latestVersion ||
|
||||
!valid(installedVersion) // nightly builds
|
||||
) {
|
||||
return false
|
||||
}
|
||||
return compare(latestVersion, installedVersion) > 0
|
||||
})
|
||||
packs.filter(
|
||||
(p) => getPackUpdateStatus(p, comfyManagerStore).isUpdateAvailable
|
||||
)
|
||||
|
||||
// Data fetching triggers using whenever
|
||||
const needsInstalledPacks = computed(() =>
|
||||
|
||||
104
src/workbench/extensions/manager/utils/packUpdateStatus.test.ts
Normal file
104
src/workbench/extensions/manager/utils/packUpdateStatus.test.ts
Normal file
@@ -0,0 +1,104 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import type { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
import {
|
||||
getPackUpdateStatus,
|
||||
isNightlyVersion
|
||||
} from '@/workbench/extensions/manager/utils/packUpdateStatus'
|
||||
|
||||
type NodePack = components['schemas']['Node']
|
||||
type ComfyManagerStore = ReturnType<typeof useComfyManagerStore>
|
||||
|
||||
const createPack = (id: string | undefined, latestVersion?: string): NodePack =>
|
||||
({
|
||||
id,
|
||||
name: `Pack ${id}`,
|
||||
latest_version: latestVersion ? { version: latestVersion } : undefined
|
||||
}) as NodePack
|
||||
|
||||
const createStore = (
|
||||
installed: Record<string, string | undefined>
|
||||
): ComfyManagerStore =>
|
||||
({
|
||||
isPackInstalled: (id: string | undefined) =>
|
||||
id !== undefined && id in installed,
|
||||
getInstalledPackVersion: (id: string) => installed[id]
|
||||
}) as unknown as ComfyManagerStore
|
||||
|
||||
describe('isNightlyVersion', () => {
|
||||
it('is true for a non-semver git hash version', () => {
|
||||
expect(isNightlyVersion('nightly-abc123')).toBe(true)
|
||||
})
|
||||
|
||||
it('is false for a valid semver version', () => {
|
||||
expect(isNightlyVersion('1.2.3')).toBe(false)
|
||||
})
|
||||
|
||||
it('is false for an undefined or empty version', () => {
|
||||
expect(isNightlyVersion(undefined)).toBe(false)
|
||||
expect(isNightlyVersion('')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getPackUpdateStatus', () => {
|
||||
it('reports an update available when the latest version is newer', () => {
|
||||
const store = createStore({ 'pack-1': '1.0.0' })
|
||||
const status = getPackUpdateStatus(createPack('pack-1', '2.0.0'), store)
|
||||
|
||||
expect(status).toEqual({
|
||||
isInstalled: true,
|
||||
installedVersion: '1.0.0',
|
||||
latestVersion: '2.0.0',
|
||||
isNightly: false,
|
||||
isUpdateAvailable: true
|
||||
})
|
||||
})
|
||||
|
||||
it('reports no update when the installed version is up to date', () => {
|
||||
const store = createStore({ 'pack-1': '2.0.0' })
|
||||
const status = getPackUpdateStatus(createPack('pack-1', '2.0.0'), store)
|
||||
|
||||
expect(status.isUpdateAvailable).toBe(false)
|
||||
})
|
||||
|
||||
it('never reports an update for a nightly (non-semver) install', () => {
|
||||
const store = createStore({ 'pack-1': 'nightly-abc123' })
|
||||
const status = getPackUpdateStatus(createPack('pack-1', '2.0.0'), store)
|
||||
|
||||
expect(status.isNightly).toBe(true)
|
||||
expect(status.isUpdateAvailable).toBe(false)
|
||||
})
|
||||
|
||||
it('reports no update when the pack is not installed', () => {
|
||||
const store = createStore({})
|
||||
const status = getPackUpdateStatus(createPack('pack-1', '2.0.0'), store)
|
||||
|
||||
expect(status.isInstalled).toBe(false)
|
||||
expect(status.isUpdateAvailable).toBe(false)
|
||||
})
|
||||
|
||||
it('reports no update when the latest version is not valid semver', () => {
|
||||
const store = createStore({ 'pack-1': '1.0.0' })
|
||||
const status = getPackUpdateStatus(createPack('pack-1', 'deadbeef'), store)
|
||||
|
||||
expect(status.isUpdateAvailable).toBe(false)
|
||||
})
|
||||
|
||||
it('reports no update for an installed pack with no known version', () => {
|
||||
const store = createStore({ 'pack-1': undefined })
|
||||
const status = getPackUpdateStatus(createPack('pack-1', '2.0.0'), store)
|
||||
|
||||
expect(status.isInstalled).toBe(true)
|
||||
expect(status.installedVersion).toBeUndefined()
|
||||
expect(status.isUpdateAvailable).toBe(false)
|
||||
})
|
||||
|
||||
it('reports no update when the pack has no latest version', () => {
|
||||
const store = createStore({ 'pack-1': '1.0.0' })
|
||||
const status = getPackUpdateStatus(createPack('pack-1'), store)
|
||||
|
||||
expect(status.latestVersion).toBeUndefined()
|
||||
expect(status.isUpdateAvailable).toBe(false)
|
||||
})
|
||||
})
|
||||
54
src/workbench/extensions/manager/utils/packUpdateStatus.ts
Normal file
54
src/workbench/extensions/manager/utils/packUpdateStatus.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { compare, valid } from 'semver'
|
||||
|
||||
import type { components } from '@/types/comfyRegistryTypes'
|
||||
import type { useComfyManagerStore } from '@/workbench/extensions/manager/stores/comfyManagerStore'
|
||||
|
||||
type NodePack = components['schemas']['Node']
|
||||
type ComfyManagerStore = ReturnType<typeof useComfyManagerStore>
|
||||
|
||||
interface PackUpdateStatus {
|
||||
isInstalled: boolean
|
||||
installedVersion: string | undefined
|
||||
latestVersion: string | undefined
|
||||
isNightly: boolean
|
||||
isUpdateAvailable: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* A pack is a nightly build when its installed version is not valid semver
|
||||
* (a git hash), so it cannot be compared against the latest release version.
|
||||
*/
|
||||
export function isNightlyVersion(version: string | undefined): boolean {
|
||||
return !!version && !valid(version)
|
||||
}
|
||||
|
||||
/**
|
||||
* Derives the update status of a pack from its installed version and the
|
||||
* latest release version advertised by the registry. A nightly build is never
|
||||
* "outdated" since git hashes cannot be ordered.
|
||||
*/
|
||||
export function getPackUpdateStatus(
|
||||
pack: NodePack | undefined,
|
||||
managerStore: ComfyManagerStore
|
||||
): PackUpdateStatus {
|
||||
const isInstalled = managerStore.isPackInstalled(pack?.id)
|
||||
const installedVersion = managerStore.getInstalledPackVersion(pack?.id ?? '')
|
||||
const latestVersion = pack?.latest_version?.version
|
||||
const isNightly = isNightlyVersion(installedVersion)
|
||||
|
||||
const isUpdateAvailable =
|
||||
isInstalled &&
|
||||
!isNightly &&
|
||||
!!installedVersion &&
|
||||
!!latestVersion &&
|
||||
!!valid(latestVersion) &&
|
||||
compare(latestVersion, installedVersion) > 0
|
||||
|
||||
return {
|
||||
isInstalled,
|
||||
installedVersion,
|
||||
latestVersion,
|
||||
isNightly,
|
||||
isUpdateAvailable
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user