Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions docs/macos.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ After the manager closes, the old app bundle is moved into that folder as `previ

A failed update is reported on the next manager startup, with a recovery path, and recorded outside the signed bundle in `~/Library/Application Support/Craft Apps Manager/self-update-result.json` and in the staging folder's `result.json`. Older library-based plans are rejected; reopen the manager and download again. The app must be in a folder you can write to, such as Applications. Development builds run outside a bundle and cannot update themselves.

The previous manager runs the update, so a fix to the updater applies only from the next update onwards. Managers 0.8.1 to 0.8.3 cannot update themselves when another app in the same folder contains symbolic links, which includes Electron apps such as Visual Studio Code. The update fails with `Archive contains a linked path` and the previous manager is restored. Download the macOS ZIP from the [latest release](https://github.com/CryptoKey98/craft-apps-manager/releases/latest) and replace `Craft Apps Manager.app` manually; your library stays in place. Later updates work from within the manager. Once the new version starts, you can delete the `.craft-manager-update-*` recovery folders that failed attempts left beside the app.

Updates come from the GitHub repository set by `CRAFT_MANAGER_REPOSITORY` (owner/name) at build time. The default is `CryptoKey98/craft-apps-manager`. CI sets it to the repository being built, so a fork's packages update from the fork's releases.

## Not yet available on macOS
Expand Down
2 changes: 1 addition & 1 deletion src/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ pub fn no_links(root: &Path) -> Result<()> {
for e in WalkDir::new(root).follow_links(false) {
let e = e?;
if linked(e.path())? {
bail!("Archive contains a linked path: {}", e.path().display());
bail!("Linked path is not allowed: {}", e.path().display());
}
}
Ok(())
Expand Down
37 changes: 37 additions & 0 deletions src/self_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1742,6 +1742,43 @@ mod macos_tests {
.contains(&fixture.stage.display().to_string()));
}
#[test]
fn full_apply_ignores_links_inside_other_applications() {
// 0.8.1-0.8.3 helpers scanned the whole application folder and rolled
// back beside apps with framework links, such as Electron apps.
let fixture = Fixture::new();
let framework = fixture
.home
.join("Other.app/Contents/Frameworks/Example.framework");
fs::create_dir_all(framework.join("Versions/A")).unwrap();
fs::write(framework.join("Versions/A/Example"), "framework").unwrap();
std::os::unix::fs::symlink("Versions/A/Example", framework.join("Example")).unwrap();
assert!(files::no_links(&fixture.home).is_err());
let destination = fixture.home.join("startup-result.json");
let mut launches = 0;
apply_macos_with(
&fixture.stage.join("plan.json"),
&fixture.stage.join(helper_name()),
&destination,
Duration::from_secs(2),
|_| Ok(false),
|plan| {
assert_eq!(plan.target, fixture.plan.target);
launches += 1;
Ok(())
},
)
.unwrap();
assert_eq!(launches, 1);
assert_eq!(
fs::read(fixture.plan.target.join("Contents/Resources/new-marker")).unwrap(),
b"new bytes"
);
assert!(fixture.stage.join("previous.app").exists());
assert!(framework.join("Example").is_symlink());
let result: UpdateResult = files::read_json(&destination).unwrap();
assert!(matches!(result.status, UpdateStatus::LaunchRequested));
}
#[test]
fn trusted_failure_retains_recovery_path_and_secondary_launch_error() {
let fixture = Fixture::new();
fs::write(staged_executable(&fixture.plan.staged), "corrupt").unwrap();
Expand Down
Loading