mirror of
https://github.com/rbalsleyMSFT/FFU.git
synced 2026-06-14 02:09:35 -06:00
Refactor dependency resolution to be app-specific
Change the dependency resolution logic from a single global scan to an isolated, per-application scan. This prevents potential version conflicts that could occur if multiple applications require different versions of the same dependency package. Each application now resolves its dependencies only from its own dedicated 'Dependencies' folder, ensuring the correct versions are used and making the installation process more robust and reliable.
This commit is contained in:
@@ -38,70 +38,7 @@ if (-not (Test-Path -Path $basePath)) {
|
|||||||
}
|
}
|
||||||
Write-Host "Installing Store Apps: Base path '$basePath' exists."
|
Write-Host "Installing Store Apps: Base path '$basePath' exists."
|
||||||
|
|
||||||
# 2. Pre-scan and catalog all available dependencies from all app folders
|
# 2. Process and install each main application
|
||||||
Write-Host "Scanning for all available application dependencies..."
|
|
||||||
$allAvailableDependencies = @{}
|
|
||||||
$dependencyFoldersToScan = [System.Collections.Generic.List[string]]::new()
|
|
||||||
|
|
||||||
# Find all 'Dependencies' subfolders
|
|
||||||
Get-ChildItem -Path $basePath -Directory | ForEach-Object {
|
|
||||||
$dependenciesPath = Join-Path -Path $_.FullName -ChildPath "Dependencies"
|
|
||||||
if (Test-Path -Path $dependenciesPath) {
|
|
||||||
$dependencyFoldersToScan.Add($dependenciesPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Handle zipped dependencies by extracting them to a temp location
|
|
||||||
$dependencyFoldersToScan.ToArray() | ForEach-Object {
|
|
||||||
$folder = $_
|
|
||||||
Get-ChildItem -Path $folder -Filter "*.zip" -File | ForEach-Object {
|
|
||||||
$zipFile = $_
|
|
||||||
$extractPath = Join-Path -Path $tempBasePath -ChildPath $zipFile.BaseName
|
|
||||||
Write-Host "Extracting zipped dependencies from '$($zipFile.FullName)' to '$extractPath'..."
|
|
||||||
try {
|
|
||||||
Expand-Archive -Path $zipFile.FullName -DestinationPath $extractPath -Force
|
|
||||||
$script:dependencyFoldersToScan.Add($extractPath)
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Error "Failed to extract '$($zipFile.FullName)'. Error: $($_.Exception.Message)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Regex to parse package filenames: Name_Version_Architecture__PublisherID (PublisherID and other tags are optional)
|
|
||||||
$packageFileRegex = '^(?<Name>.+?)_(?<Version>(?:\d+\.){2,3}\d+)_(?:[^_]+_)*(?<Arch>x64|x86|arm|arm64|neutral)(?:__.*)?$'
|
|
||||||
|
|
||||||
# Catalog all package files found in the dependency folders
|
|
||||||
foreach ($folder in $dependencyFoldersToScan.ToArray() | Select-Object -Unique) {
|
|
||||||
Get-ChildItem -Path $folder -Recurse -File | Where-Object { $_.Extension -in '.appx', '.msix', '.appxbundle' } | ForEach-Object {
|
|
||||||
$file = $_
|
|
||||||
$match = $file.BaseName -imatch $packageFileRegex
|
|
||||||
if ($match) {
|
|
||||||
$dependencyName = $matches.Name
|
|
||||||
try {
|
|
||||||
$dependencyVersion = [System.Version]$matches.Version
|
|
||||||
$dependencyArch = $matches.Arch
|
|
||||||
|
|
||||||
if (-not $allAvailableDependencies.ContainsKey($dependencyName)) {
|
|
||||||
$allAvailableDependencies[$dependencyName] = [System.Collections.Generic.List[object]]::new()
|
|
||||||
}
|
|
||||||
$allAvailableDependencies[$dependencyName].Add([pscustomobject]@{
|
|
||||||
Name = $dependencyName
|
|
||||||
Version = $dependencyVersion
|
|
||||||
Arch = $dependencyArch
|
|
||||||
Path = $file.FullName
|
|
||||||
})
|
|
||||||
}
|
|
||||||
catch {
|
|
||||||
Write-Warning "Could not parse version for file '$($file.Name)'. Skipping."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Write-Host "Dependency scan complete. Found $($allAvailableDependencies.Keys.Count) unique dependency packages."
|
|
||||||
Write-Output ""
|
|
||||||
|
|
||||||
# 3. Process and install each main application
|
|
||||||
Write-Host "Starting main application installation process..."
|
Write-Host "Starting main application installation process..."
|
||||||
foreach ($appFolder in Get-ChildItem -Path $basePath -Directory) {
|
foreach ($appFolder in Get-ChildItem -Path $basePath -Directory) {
|
||||||
Write-Host "--- Processing application in folder: $($appFolder.Name) ---"
|
Write-Host "--- Processing application in folder: $($appFolder.Name) ---"
|
||||||
@@ -193,40 +130,102 @@ foreach ($appFolder in Get-ChildItem -Path $basePath -Directory) {
|
|||||||
Write-Error "Failed to read or parse manifest from '$($mainPackage.FullName)'. Error: $($_.Exception.Message)"
|
Write-Error "Failed to read or parse manifest from '$($mainPackage.FullName)'. Error: $($_.Exception.Message)"
|
||||||
}
|
}
|
||||||
|
|
||||||
if (-not $requiredDependencies) {
|
# Scan for and resolve dependencies only if the manifest lists actual package dependencies.
|
||||||
Write-Warning "Could not read dependencies from manifest for '$($mainPackage.Name)'. Proceeding without explicit dependencies."
|
|
||||||
}
|
|
||||||
|
|
||||||
# Resolve all required dependencies
|
|
||||||
$resolvedDependencyPaths = [System.Collections.Generic.List[string]]::new()
|
$resolvedDependencyPaths = [System.Collections.Generic.List[string]]::new()
|
||||||
foreach ($req in $requiredDependencies) {
|
|
||||||
$reqName = $req.Name
|
|
||||||
$reqMinVersion = [System.Version]$req.MinVersion
|
|
||||||
Write-Host "Resolving dependency: $reqName (MinVersion: $reqMinVersion)"
|
|
||||||
|
|
||||||
if ($allAvailableDependencies.ContainsKey($reqName)) {
|
if ($null -ne $requiredDependencies -and $requiredDependencies.Count -gt 0) {
|
||||||
# Find all available packages that meet the minimum version and architecture requirements
|
$appDependenciesPath = Join-Path -Path $appFolder.FullName -ChildPath "Dependencies"
|
||||||
$candidates = $allAvailableDependencies[$reqName] | Where-Object {
|
|
||||||
$_.Version -ge $reqMinVersion -and
|
if (Test-Path -Path $appDependenciesPath) {
|
||||||
$_.Arch -in ($applicableArchitectures + 'neutral')
|
Write-Host "Scanning for dependencies in '$appDependenciesPath'..."
|
||||||
|
$appSpecificDependencies = @{}
|
||||||
|
$dependencyFoldersToScan = [System.Collections.Generic.List[string]]::new()
|
||||||
|
$dependencyFoldersToScan.Add($appDependenciesPath)
|
||||||
|
|
||||||
|
# Handle zipped dependencies by extracting them to a temp location
|
||||||
|
Get-ChildItem -Path $appDependenciesPath -Filter "*.zip" -File | ForEach-Object {
|
||||||
|
$zipFile = $_
|
||||||
|
# Ensure unique extract path per app to avoid conflicts
|
||||||
|
$extractPath = Join-Path -Path $tempBasePath -ChildPath "$($appFolder.Name)_$($zipFile.BaseName)"
|
||||||
|
Write-Host "Extracting zipped dependencies from '$($zipFile.FullName)' to '$extractPath'..."
|
||||||
|
try {
|
||||||
|
Expand-Archive -Path $zipFile.FullName -DestinationPath $extractPath -Force
|
||||||
|
$dependencyFoldersToScan.Add($extractPath)
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Error "Failed to extract '$($zipFile.FullName)'. Error: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($candidates) {
|
# Regex to parse package filenames
|
||||||
# Group by architecture and find the single latest version for each applicable arch
|
$packageFileRegex = '^(?<Name>.+?)_(?<Version>(?:\d+\.){2,3}\d+)_(?:[^_]+_)*(?<Arch>x64|x86|arm|arm64|neutral)(?:__.*)?$'
|
||||||
$bestCandidates = $candidates | Group-Object -Property Arch | ForEach-Object {
|
|
||||||
$_.Group | Sort-Object -Property Version -Descending | Select-Object -First 1
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach($best in $bestCandidates) {
|
# Catalog all package files found in the dependency folders for this app
|
||||||
Write-Host " - Found best match: $($best.Path.Replace($basePath, '...'))"
|
foreach ($folder in $dependencyFoldersToScan.ToArray() | Select-Object -Unique) {
|
||||||
$resolvedDependencyPaths.Add($best.Path)
|
Get-ChildItem -Path $folder -Recurse -File | Where-Object { $_.Extension -in '.appx', '.msix', '.appxbundle' } | ForEach-Object {
|
||||||
|
$file = $_
|
||||||
|
$match = $file.BaseName -imatch $packageFileRegex
|
||||||
|
if ($match) {
|
||||||
|
$dependencyName = $matches.Name
|
||||||
|
try {
|
||||||
|
$dependencyVersion = [System.Version]$matches.Version
|
||||||
|
$dependencyArch = $matches.Arch
|
||||||
|
|
||||||
|
if (-not $appSpecificDependencies.ContainsKey($dependencyName)) {
|
||||||
|
$appSpecificDependencies[$dependencyName] = [System.Collections.Generic.List[object]]::new()
|
||||||
|
}
|
||||||
|
$appSpecificDependencies[$dependencyName].Add([pscustomobject]@{
|
||||||
|
Name = $dependencyName
|
||||||
|
Version = $dependencyVersion
|
||||||
|
Arch = $dependencyArch
|
||||||
|
Path = $file.FullName
|
||||||
|
})
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
Write-Warning "Could not parse version for file '$($file.Name)'. Skipping."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Write-Host "Dependency scan for '$($appFolder.Name)' complete."
|
||||||
|
|
||||||
|
# Resolve all required dependencies using the app-specific catalog
|
||||||
|
foreach ($req in $requiredDependencies) {
|
||||||
|
$reqName = $req.Name
|
||||||
|
$reqMinVersion = [System.Version]$req.MinVersion
|
||||||
|
Write-Host "Resolving dependency: $reqName (MinVersion: $reqMinVersion)"
|
||||||
|
|
||||||
|
if ($appSpecificDependencies.ContainsKey($reqName)) {
|
||||||
|
# Find all available packages that meet the minimum version and architecture requirements
|
||||||
|
$candidates = $appSpecificDependencies[$reqName] | Where-Object {
|
||||||
|
$_.Version -ge $reqMinVersion -and
|
||||||
|
$_.Arch -in ($applicableArchitectures + 'neutral')
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($candidates) {
|
||||||
|
# Group by architecture and find the single latest version for each applicable arch
|
||||||
|
$bestCandidates = $candidates | Group-Object -Property Arch | ForEach-Object {
|
||||||
|
$_.Group | Sort-Object -Property Version -Descending | Select-Object -First 1
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($best in $bestCandidates) {
|
||||||
|
Write-Host " - Found best match: $($best.Path.Replace($basePath, '...'))"
|
||||||
|
$resolvedDependencyPaths.Add($best.Path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Warning " - No suitable package found for dependency '$reqName' with MinVersion '$reqMinVersion' for applicable architectures."
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Write-Warning " - Dependency '$reqName' not found in this app's dependency folder."
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Write-Warning " - No suitable package found for dependency '$reqName' with MinVersion '$reqMinVersion' for applicable architectures."
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Write-Warning " - Dependency '$reqName' not found in any scanned dependency folders."
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
Write-Warning "Dependencies are required by manifest, but no 'Dependencies' folder found for '$($appFolder.Name)'."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Write-Host "No actual package dependencies listed in manifest for '$($appFolder.Name)'. Proceeding without dependency resolution."
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build the DISM command
|
# Build the DISM command
|
||||||
@@ -235,7 +234,7 @@ foreach ($appFolder in Get-ChildItem -Path $basePath -Directory) {
|
|||||||
"/Add-ProvisionedAppxPackage"
|
"/Add-ProvisionedAppxPackage"
|
||||||
"/PackagePath:`"$($mainPackage.FullName)`""
|
"/PackagePath:`"$($mainPackage.FullName)`""
|
||||||
"/Region:all"
|
"/Region:all"
|
||||||
"/StubPackageOption:installfull"
|
# "/StubPackageOption:installfull"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Add resolved dependencies, ensuring no duplicates
|
# Add resolved dependencies, ensuring no duplicates
|
||||||
|
|||||||
Reference in New Issue
Block a user