Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions cli/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2018,6 +2018,84 @@ export async function getRemoteDependencies(supabase: SupabaseClient<Database>,
return convertNativePackages(((remoteNativePackages.version as any)?.native_packages as any) ?? [])
}

interface BundleCompatibilityCompareResponse {
comparisons: {
name: string
candidateVersion?: string
baselineVersion?: string
candidateIosChecksum?: string
baselineIosChecksum?: string
candidateAndroidChecksum?: string
baselineAndroidChecksum?: string
}[]
}

function mapBackendCompatibilityComparison(entry: BundleCompatibilityCompareResponse['comparisons'][number]): Compatibility {
return {
name: entry.name,
localVersion: entry.candidateVersion,
remoteVersion: entry.baselineVersion,
localIosChecksum: entry.candidateIosChecksum,
remoteIosChecksum: entry.baselineIosChecksum,
localAndroidChecksum: entry.candidateAndroidChecksum,
remoteAndroidChecksum: entry.baselineAndroidChecksum,
}
}

export function mapBackendCompatibilityResponse(response: BundleCompatibilityCompareResponse): Compatibility[] {
return response.comparisons.map(mapBackendCompatibilityComparison)
}

async function getChannelBaselineBundleId(supabase: SupabaseClient<Database>, appId: string, channel: string): Promise<number | null> {
const { data, error } = await supabase
.from('channels')
.select('version ( id )')
.eq('name', channel)
.eq('app_id', appId)
.single()

if (error) {
log.error(`Error fetching native packages: ${error.message}`)
throw new Error(`Error fetching native packages: ${error.message}`)
}

const bundleId = (data.version as any)?.id
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
return typeof bundleId === 'number' ? bundleId : null
}

async function checkCompatibilityWithBackend(
supabase: SupabaseClient<Database>,
appId: string,
channel: string,
nativePackages: NativePackage[],
): Promise<Compatibility[] | null> {
const baselineBundleId = await getChannelBaselineBundleId(supabase, appId, channel)
if (!baselineBundleId)
return null

try {
const { data, error } = await supabase.functions.invoke<BundleCompatibilityCompareResponse>('private/bundle_compatibility/compare', {
Comment thread
riderx marked this conversation as resolved.
Outdated
body: {
appId,
candidate: {
nativePackages,
},
baseline: {
bundleId: baselineBundleId,
},
},
})

if (error || !data)
return null

return mapBackendCompatibilityResponse(data)
}
catch {
return null
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

export async function checkChecksum(supabase: SupabaseClient<Database>, appId: string, channel: string, currentChecksum: string) {
const s = spinnerC()
s.start(`Checking bundle checksum compatibility with channel ${channel}`)
Expand Down Expand Up @@ -2146,6 +2224,22 @@ export function isCompatible(pkg: Compatibility): boolean {

export async function checkCompatibilityCloud(supabase: SupabaseClient<Database>, appId: string, channel: string, packageJsonPath: string | undefined, nodeModules: string | undefined) {
const dependenciesObject = await getLocalDependencies(packageJsonPath, nodeModules)
const localNativePackages = dependenciesObject
.filter(a => !!a.native)
.map(({ name, version, ios_checksum, android_checksum }) => ({
name,
version,
...(ios_checksum && { ios_checksum }),
...(android_checksum && { android_checksum }),
}))
const backendCompatibility = await checkCompatibilityWithBackend(supabase, appId, channel, localNativePackages)
if (backendCompatibility) {
return {
finalCompatibility: backendCompatibility,
localDependencies: dependenciesObject,
}
}

const mappedRemoteNativePackages = await getRemoteDependencies(supabase, appId, channel)

const finalDependencies: Compatibility[] = dependenciesObject
Expand Down Expand Up @@ -2194,6 +2288,14 @@ export async function checkCompatibilityCloud(supabase: SupabaseClient<Database>
}

export async function checkCompatibilityNativePackages(supabase: SupabaseClient<Database>, appId: string, channel: string, nativePackages: NativePackage[]) {
const backendCompatibility = await checkCompatibilityWithBackend(supabase, appId, channel, nativePackages)
if (backendCompatibility) {
return {
finalCompatibility: backendCompatibility,
localDependencies: nativePackages,
}
}

const mappedRemoteNativePackages = await getRemoteDependencies(supabase, appId, channel)

const finalDependencies: Compatibility[] = nativePackages
Expand Down
53 changes: 53 additions & 0 deletions tests/cli-backend-compatibility-map.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { describe, expect, it } from 'vitest'
import { getCompatibilityDetails, mapBackendCompatibilityResponse } from '../cli/src/utils.ts'

describe('CLI backend compatibility response mapping', () => {
it.concurrent('maps backend package comparisons to CLI compatibility entries', () => {
const [entry] = mapBackendCompatibilityResponse({
comparisons: [
{
name: '@capacitor/camera',
candidateVersion: '6.0.0',
baselineVersion: '5.0.0',
candidateIosChecksum: 'ios-new',
baselineIosChecksum: 'ios-old',
candidateAndroidChecksum: 'android-new',
baselineAndroidChecksum: 'android-old',
},
],
})

expect(entry).toEqual({
name: '@capacitor/camera',
localVersion: '6.0.0',
remoteVersion: '5.0.0',
localIosChecksum: 'ios-new',
remoteIosChecksum: 'ios-old',
localAndroidChecksum: 'android-new',
remoteAndroidChecksum: 'android-old',
})
expect(getCompatibilityDetails(entry).compatible).toBe(false)
})

it.concurrent('keeps removed remote packages OTA-compatible in the CLI shape', () => {
const [entry] = mapBackendCompatibilityResponse({
comparisons: [
{
name: '@capacitor/camera',
baselineVersion: '5.0.0',
},
],
})

expect(entry).toEqual({
name: '@capacitor/camera',
localVersion: undefined,
remoteVersion: '5.0.0',
localIosChecksum: undefined,
remoteIosChecksum: undefined,
localAndroidChecksum: undefined,
remoteAndroidChecksum: undefined,
})
expect(getCompatibilityDetails(entry).compatible).toBe(true)
})
})
Loading