Skip to content

fix: https://github.com/wailsapp/wails/issues/4636 - #4682

Merged
leaanthony merged 7 commits into
wailsapp:v3-alphafrom
rxliuli:fix/4636
Dec 10, 2025
Merged

leaanthony merged 7 commits into
wailsapp:v3-alphafrom
rxliuli:fix/4636

Conversation

@rxliuli

@rxliuli rxliuli commented Oct 31, 2025 •

Copy link
Copy Markdown
Contributor

Description

Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.

Fixes #4636

Type of change

Please select the option that is relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration using wails doctor.

  • Windows
  • macOS
  • Linux

If you checked Linux, please specify the distro and version.

Test Configuration

Please paste the output of wails doctor. If you are unable to run this command, please describe your environment in as much detail as possible.

Checklist:

  • I have updated website/src/pages/changelog.mdx with details of this PR
  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • New Features

    • Asset extraction now backs up plist configuration files, merges prior values into new files, and cleans up backups; improved error handling during merge.
  • Bug Fixes

    • Fixed an issue where plist configuration files were overwritten; they are now merged to preserve existing and nested values.
  • Tests

    • Added tests covering plist merge behavior, nested-structure merges, and the updated extraction workflow.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Oct 31, 2025 •

Copy link
Copy Markdown
Contributor

Walkthrough

Adds plist backup-and-merge to UpdateBuildAssets: existing .plist files are backed up, assets are extracted to a temp dir, extracted plist files are merged recursively with backups and rewritten, non-plist files are copied, and backups/temp are cleaned up. Errors are propagated.

Changes

Cohort / File(s) Summary
Build Assets Implementation
v3/internal/commands/build-assets.go
Adds plist backup, merge, and cleanup into UpdateBuildAssets: backupPlistFiles, mergeBackupPlists, cleanupBackups, mergeMaps, and plistBackup support; backs up existing *.plist → *.plist.bak, extracts assets to temp, merges backup maps into extracted plists, rewrites merged plists, copies non-plist files (preserving mode/paths), and propagates errors.
Build Assets Tests
v3/internal/commands/build-assets_test.go
Adds TestPlistMerge and TestNestedPlistMerge (imports howett.net/plist) validating plist merge behavior (preserve existing keys, update bundle name/version, nested dict merges, mixed-type conflict handling) and helper functions deepCopyMap, mergeMaps, mapsEqual.
Changelog
v3/UNRELEASED_CHANGELOG.md
Adds an entry noting common:update:build-assets now merges Info.plist instead of overwriting (references issue/PR #4636).

Sequence Diagram(s)

sequenceDiagram
    participant Caller
    participant UpdateBuildAssets
    participant FS as Filesystem
    participant Temp as TempDir
    participant PlistWorkflow

    Caller->>UpdateBuildAssets: Invoke UpdateBuildAssets(opts)
    UpdateBuildAssets->>FS: Locate target assets dir
    UpdateBuildAssets->>PlistWorkflow: backupPlistFiles(target)
    PlistWorkflow->>FS: Rename `*.plist` -> `*.plist.bak`
    UpdateBuildAssets->>Temp: Create temp extraction dir
    UpdateBuildAssets->>Temp: Extract archive into temp
    UpdateBuildAssets->>Temp: Walk extracted files
    alt file is plist
        UpdateBuildAssets->>FS: Read extracted plist
        UpdateBuildAssets->>PlistWorkflow: mergeBackupPlists(extractedPath)
        PlistWorkflow->>FS: Read `*.plist.bak` (if exists)
        PlistWorkflow->>PlistWorkflow: mergeMaps(backupMap, extractedMap)
        PlistWorkflow->>FS: Write merged plist to target
    else file is non-plist
        UpdateBuildAssets->>FS: Copy file to target (preserve mode & path)
    end
    UpdateBuildAssets->>PlistWorkflow: cleanupBackups(target)
    PlistWorkflow->>FS: Remove `*.plist.bak` files
    UpdateBuildAssets->>Temp: Remove temp extraction dir
    UpdateBuildAssets-->>Caller: Return result / error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review recursive merge logic (mergeMaps) for type conflicts, arrays, and edge cases.
  • Check backup/rename behavior and error paths to ensure backups are cleaned on failures.
  • Verify plist encoding/decoding error handling and that file mode/path preservation is correct.

Possibly related PRs

Suggested labels

Bug, go, size:M, lgtm

Poem

🐰 I hopped through temp dirs, light and spry,

Backed up the plists with a practiced sigh,
Merged nested keys with a twitch of my nose,
Cleaned up the crumbs where the temp-folder goes,
A bunny's patch — your build now grows 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.56% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The PR title references issue #4636 but lacks detail about what was actually fixed; it should describe the fix itself rather than just the issue number. Revise the title to describe the specific change, such as 'fix: merge custom keys when updating Info.plist in build-assets' to better communicate the fix.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description follows the template structure and includes the required issue reference (Fixes #4636), bug fix classification, and test information, though some checklist items remain unchecked.
Linked Issues check ✅ Passed The code changes implement plist backup, merge, and cleanup functionality that directly addresses issue #4636's requirement to merge rather than overwrite Info.plist.
Out of Scope Changes check ✅ Passed All changes are within scope: plist backup/merge functionality, supporting helper functions, comprehensive tests, and changelog update directly address the Info.plist merge requirement.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 034d524 and 16a14cf.

📒 Files selected for processing (1)
  • v3/UNRELEASED_CHANGELOG.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • v3/UNRELEASED_CHANGELOG.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: semgrep-cloud-platform/scan

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
v3/internal/commands/build-assets_test.go (1)

270-352: Consider expanding test coverage for edge cases.

The test effectively verifies basic plist merging (updated keys and preserved keys), but additional test cases would strengthen confidence:

  • New plist file creation (when target doesn't exist)
  • Multiple plist files in nested directories
  • Non-plist file copying (files other than .plist)
  • Nested dictionary handling
  • Array value merging
  • Error scenarios (malformed plist, I/O errors)

These additions would help catch regressions and clarify expected behavior for edge cases.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57d5564 and 158e08e.

📒 Files selected for processing (2)
  • v3/internal/commands/build-assets.go (3 hunks)
  • v3/internal/commands/build-assets_test.go (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
v3/internal/commands/build-assets_test.go (1)
v3/internal/commands/build-assets.go (2)
  • UpdateBuildAssetsOptions (63-76)
  • UpdateBuildAssets (194-281)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (6)
v3/internal/commands/build-assets.go (5)

16-16: LGTM!

The plist library import is appropriate for the new merge functionality.


255-274: Well-structured two-phase extraction workflow.

The temporary directory approach cleanly separates extraction from merging, allowing plist files to be merged while other files are copied directly. Error handling and cleanup are properly implemented.


287-324: Verify shallow merge behavior meets requirements.

The merge logic performs a shallow (non-recursive) merge where each key from the new plist completely overwrites the corresponding key in the existing plist. If a key's value is a nested dictionary, the entire nested dictionary is replaced, not merged recursively.

Example:

  • Existing: {"LSApplicationQueriesSchemes": ["http", "https", "custom"]}
  • New: {"LSApplicationQueriesSchemes": ["http"]}
  • Result: {"LSApplicationQueriesSchemes": ["http"]} ← "https" and "custom" are lost

Confirm this aligns with the intended merge semantics for build assets. If users have custom nested configurations they want to preserve while updating top-level keys, a recursive merge would be needed.


326-362: LGTM!

The directory walk, filtering, path computation, and merge invocation are all correctly implemented with proper error handling.


364-400: LGTM!

The function correctly copies non-plist files while preserving file modes and directory structure. The inverse filtering logic (.plist files excluded) properly complements mergePlistFiles.

v3/internal/commands/build-assets_test.go (1)

9-9: LGTM!

The plist import is necessary for unmarshaling and validating the merged plist content in the test.

@github-actions github-actions Bot added the Documentation Improvements or additions to documentation label Oct 31, 2025
@sonarqubecloud

Copy link
Copy Markdown

Previously, the plist merge was shallow - nested dictionaries were
completely replaced rather than recursively merged. This caused custom
nested configurations to be lost during build asset updates.

Now nested dictionaries are recursively merged, preserving custom keys
at all levels while still allowing new keys to be added and existing
keys to be updated.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
v3/internal/commands/build-assets.go (1)

287-303: Consider enhancing the documentation.

The merge logic is correct, but the comment could be more explicit about the precedence behavior: when types differ or values are not maps, the new value completely replaces the old value.

Consider this enhanced comment:

-// mergeMaps recursively merges src into dst.
-// For nested maps, it merges recursively. For other types, src overwrites dst.
+// mergeMaps recursively merges src into dst, modifying dst in place.
+// - When both dst[key] and src[key] are maps: merges recursively
+// - When types differ or values are not maps: src[key] completely replaces dst[key]
+// - When key exists only in src: adds src[key] to dst
+// - When key exists only in dst: preserves dst[key]
 func mergeMaps(dst, src map[string]any) {
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 158e08e and db0c43b.

📒 Files selected for processing (3)
  • v3/UNRELEASED_CHANGELOG.md (1 hunks)
  • v3/internal/commands/build-assets.go (3 hunks)
  • v3/internal/commands/build-assets_test.go (2 hunks)
✅ Files skipped from review due to trivial changes (1)
  • v3/UNRELEASED_CHANGELOG.md
🧰 Additional context used
🧬 Code graph analysis (2)
v3/internal/commands/build-assets_test.go (1)
v3/internal/commands/build-assets.go (2)
  • UpdateBuildAssetsOptions (63-76)
  • UpdateBuildAssets (194-281)
v3/internal/commands/build-assets.go (1)
v2/pkg/buildassets/buildassets.go (1)
  • ReadFile (48-66)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (8)
v3/internal/commands/build-assets.go (5)

255-274: LGTM! Well-structured refactor for plist merging.

The refactored flow correctly addresses the issue by:

  • Extracting assets to a temporary directory first
  • Merging plist files to preserve existing keys
  • Copying non-plist files separately
  • Using defer to ensure cleanup even on errors

This approach successfully prevents overwriting custom plist modifications.


342-378: LGTM! File walking and merging logic is sound.

The function correctly:

  • Walks the temp directory recursively
  • Filters for .plist files
  • Creates target directories as needed
  • Merges each plist file into its target location

Note: Line 352 uses a case-sensitive suffix check. This should be fine since macOS conventions use lowercase .plist, but be aware this won't catch .PLIST or other case variations if they exist.


380-416: LGTM! Non-plist file copying is implemented correctly.

The function properly:

  • Skips plist files (which are handled by mergePlistFiles)
  • Copies all other files from temp to target
  • Preserves file permissions using info.Mode()
  • Creates target directories as needed

This complements the plist merging workflow well.


16-16: Keep the howett.net/plist dependency updated and validate plist inputs as untrusted data.

The library has no known public CVE vulnerabilities, but like all plist parsers, it should be kept current and used only with validated input. Ensure untrusted plist files are parsed in a controlled context with resource limits to prevent potential DoS vectors, as similar libraries in other ecosystems have had vulnerability history.


305-340: LGTM! Tab indentation is appropriate for plist files.

The merge logic correctly handles:

  • Parsing new plist content
  • Reading existing plist (if present)
  • Merging maps to preserve existing keys
  • Writing the result back

Line 332 uses encoder.Indent("\t") for tab indentation, which is valid for plist XML output. Plist indentation is purely cosmetic—Apple's system ignores it entirely, and Xcode has no enforced convention. If your team needs consistent indentation across projects, consider adding .editorconfig rules for *.plist files.

v3/internal/commands/build-assets_test.go (3)

270-352: LGTM! Comprehensive integration test for plist merging.

The test effectively validates the core fix:

  • Creates an existing plist with custom keys (NSCameraUsageDescription, NSMicrophoneUsageDescription)
  • Runs UpdateBuildAssets with new product information
  • Verifies that:
    • New values are properly updated (CFBundleName, CFBundleVersion, CFBundleIdentifier)
    • Existing custom keys are preserved

This directly tests the bug fix described in issue #4636.


354-480: Excellent test coverage for merge scenarios!

The table-driven tests comprehensively cover:

  • Simple overwrites
  • Key preservation
  • Nested dictionary merging
  • Deep nesting (multiple levels)
  • Type conflicts (map ↔ non-map replacements)

The use of deepCopyMap to avoid mutation during tests is good practice. The test cases validate that the merge behavior correctly implements the "preserve existing custom keys while updating standard keys" requirement.


482-516: LGTM! Helper functions are implemented correctly.

Both deepCopyMap and mapsEqual properly handle nested structures:

  • deepCopyMap recursively clones nested maps to avoid test mutation issues
  • mapsEqual recursively compares maps including type checking

These helpers enable thorough testing of the merge behavior.

leaanthony and others added 2 commits December 10, 2025 09:42
Instead of extracting to a temp directory and copying files over,
we now:
1. Rename existing plists to .plist.bak
2. Extract new assets directly to target
3. Merge backup content into newly extracted plists
4. Clean up backup files

This is simpler, more crash-safe (backups remain if process crashes),
and avoids the overhead of a temp directory and file copying.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@leaanthony

Copy link
Copy Markdown
Member

@rxliuli thanks for this 🙏 I made a few modifications. LMK if you're good with these and will merge 👍

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
v3/internal/commands/build-assets.go (1)

364-375: Use defer to ensure file is always closed.

The file handle is closed explicitly in both error and success paths, but won't be closed if encoder.Encode panics. Using defer is more idiomatic and handles all edge cases.

Apply this diff:

 		// Write merged result
 		file, err := os.Create(backup.originalPath)
 		if err != nil {
 			return fmt.Errorf("failed to create merged plist %s: %w", backup.originalPath, err)
 		}
+		defer file.Close()
 
 		encoder := plist.NewEncoder(file)
 		encoder.Indent("\t")
 		if err := encoder.Encode(backupDict); err != nil {
-			file.Close()
 			return fmt.Errorf("failed to encode merged plist %s: %w", backup.originalPath, err)
 		}
-		file.Close()
 	}
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between db0c43b and 034d524.

📒 Files selected for processing (1)
  • v3/internal/commands/build-assets.go (3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Run Go Tests v3 (macos-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (windows-latest, 1.24)
  • GitHub Check: Run Go Tests v3 (ubuntu-latest, 1.24)
  • GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (5)
v3/internal/commands/build-assets.go (5)

16-16: LGTM: Appropriate library for plist handling.

The howett.net/plist library is the standard Go library for working with property list files, and version 1.0.1 is specified in the library context.


255-274: LGTM: Backup-merge-cleanup workflow is well integrated.

The workflow correctly implements the backup → extract → merge → cleanup sequence, with appropriate error handling for the critical steps.


287-303: LGTM: Merge semantics correctly preserve custom keys.

The recursive merge properly implements the requirement: user-added custom keys (like LSApplicationCategoryType) are preserved, while template values update standard keys (like CFBundleVersion). The type assertions are safe and recursion will terminate at leaf values.


305-309: LGTM: Clear structure for tracking backups.


380-385: LGTM: Acceptable to ignore cleanup errors.

For cleanup operations after the main merge has succeeded, ignoring os.Remove errors is reasonable. Leftover .plist.bak files won't impact functionality.

Comment on lines +311 to +332
// backupPlistFiles finds all .plist files in dir and renames them to .plist.bak
func backupPlistFiles(dir string) ([]plistBackup, error) {
var backups []plistBackup

err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() || !strings.HasSuffix(path, ".plist") {
return nil
}

backupPath := path + ".bak"
if err := os.Rename(path, backupPath); err != nil {
return fmt.Errorf("failed to backup plist %s: %w", path, err)
}
backups = append(backups, plistBackup{originalPath: path, backupPath: backupPath})
return nil
})

return backups, err
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider partial failure handling.

If os.Rename fails partway through the walk (e.g., permissions issue on one file), some plists will be backed up while others won't, with no rollback mechanism. This could leave the directory in an inconsistent state if the subsequent extraction proceeds.

However, since the error is returned and would prevent the extraction step from running, this is likely acceptable for a build tool.

🤖 Prompt for AI Agents
v3/internal/commands/build-assets.go lines 311-332: the current backupPlistFiles
renames .plist files to .plist.bak but does not undo partial progress if a later
os.Rename fails, leaving a mixed state; change the function to perform
atomic-like behavior by tracking successful backups and, if any rename returns
an error, iterate over the already-backed-up entries to restore them (rename
backupPath back to originalPath), accumulate and return the original error (or a
wrapped error that includes both the rename failure and any restore failures) so
callers see the root cause while the filesystem is left in the original state.

Comment on lines +349 to +353
newContent, err := os.ReadFile(backup.originalPath)
if err != nil {
// New file might not exist if template didn't generate one for this path
continue
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: User data loss when template doesn't generate a plist file.

If the newly extracted plist doesn't exist (line 349), the function continues without restoring or merging the backup. Since the original file was renamed to .plist.bak during backup, and cleanup will delete all .plist.bak files, the user's original plist file is permanently deleted.

Example scenario:

  1. User has a custom CustomSettings.plist file
  2. Backup renames it to CustomSettings.plist.bak
  3. New template doesn't generate CustomSettings.plist
  4. Merge continues (line 352) without restoring the backup
  5. Cleanup deletes CustomSettings.plist.bak
  6. User's original file is gone

Apply this fix to restore the backup when the new file doesn't exist:

 		// Read the newly extracted plist
 		newContent, err := os.ReadFile(backup.originalPath)
 		if err != nil {
-			// New file might not exist if template didn't generate one for this path
-			continue
+			// New file doesn't exist; restore the backup
+			if err := os.Rename(backup.backupPath, backup.originalPath); err != nil {
+				return fmt.Errorf("failed to restore backup %s: %w", backup.backupPath, err)
+			}
+			continue
 		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
newContent, err := os.ReadFile(backup.originalPath)
if err != nil {
// New file might not exist if template didn't generate one for this path
continue
}
newContent, err := os.ReadFile(backup.originalPath)
if err != nil {
// New file doesn't exist; restore the backup
if err := os.Rename(backup.backupPath, backup.originalPath); err != nil {
return fmt.Errorf("failed to restore backup %s: %w", backup.backupPath, err)
}
continue
}

@rxliuli

rxliuli commented Dec 10, 2025

Copy link
Copy Markdown
Contributor Author

@rxliuli thanks for this 🙏 I made a few modifications. LMK if you're good with these and will merge 👍

Looks good, please merge it.

@leaanthony
leaanthony enabled auto-merge (squash) December 10, 2025 05:15
@sonarqubecloud

Copy link
Copy Markdown

@leaanthony
leaanthony merged commit 9d9d679 into wailsapp:v3-alpha Dec 10, 2025
49 of 50 checks passed
@leaanthony

Copy link
Copy Markdown
Member

Thanks! 🙏

Grantmartin2002 pushed a commit to Grantmartin2002/wails that referenced this pull request Apr 29, 2026
* fix(v3): fixed update plist, close wailsapp#4636

* chore: update changelog

* feat: add recursive merge support for nested plist dictionaries

Previously, the plist merge was shallow - nested dictionaries were
completely replaced rather than recursively merged. This caused custom
nested configurations to be lost during build asset updates.

Now nested dictionaries are recursively merged, preserving custom keys
at all levels while still allowing new keys to be added and existing
keys to be updated.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: replace temp directory with backup-based plist merge

Instead of extracting to a temp directory and copying files over,
we now:
1. Rename existing plists to .plist.bak
2. Extract new assets directly to target
3. Merge backup content into newly extracted plists
4. Clean up backup files

This is simpler, more crash-safe (backups remain if process crashes),
and avoids the overhead of a temp directory and file copying.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@taliesin-ai taliesin-ai added v3 and removed v3-alpha labels Aug 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli Documentation Improvements or additions to documentation v3

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants