Skip to content

Commit d7aab2b

Browse files
authored
Merge pull request #116 from imagej/update-mac-bundle
Archive Fiji.app folder atomically
2 parents 6c707c5 + 051ee1a commit d7aab2b

File tree

1 file changed

+72
-1
lines changed

1 file changed

+72
-1
lines changed

src/main/java/net/imagej/updater/Installer.java

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333

3434
import java.io.File;
3535
import java.io.IOException;
36+
import java.nio.file.*;
37+
import java.nio.file.attribute.BasicFileAttributes;
3638
import java.util.ArrayList;
3739
import java.util.HashSet;
3840
import java.util.List;
@@ -139,6 +141,35 @@ public synchronized void start() throws IOException {
139141
}
140142

141143
final List<Downloadable> list = new ArrayList<>();
144+
145+
// Special care must be taken when updating files in the Mac bundle (Fiji.app)
146+
// These files are all signed together as one unit, so individual files can not be modified.
147+
// First we scan the update list to see if there's anything in Fiji.app to update
148+
boolean updateMacApp = false;
149+
for (final FileObject file : files.toInstallOrUpdate()) {
150+
if (file.filename.contains("Fiji.app")) {
151+
updateMacApp = true;
152+
break;
153+
}
154+
}
155+
// If there is a Mac app update, we need to flag ALL installed files in Fiji.app for update
156+
if (updateMacApp) {
157+
for (FileObject installed : files.installed()) {
158+
if (installed.filename.contains("Fiji.app")) {
159+
installed.stageForUpdate(files, true);
160+
}
161+
}
162+
Path macBundlePath = files.prefix("Fiji.app").toPath();
163+
File backupAppFile = files.prefix("Fiji.old.app");
164+
Path backupAppPath = backupAppFile.toPath();
165+
// Remove the existing Fiji.old.app if it exists
166+
if (backupAppFile.exists()) {
167+
deleteDirectory(backupAppPath);
168+
}
169+
// Create a new Fiji.old.app backup
170+
copyDirectory(macBundlePath, backupAppPath);
171+
}
172+
142173
for (final FileObject file : files.toInstallOrUpdate()) {
143174
final String name = file.filename;
144175
File saveTo = files.prefixUpdate(name);
@@ -150,7 +181,12 @@ public synchronized void start() throws IOException {
150181
// put them in the update subdir to migrate over as they must be in place
151182
// for the launch itself. So we rename the old file to a backup (.old) and
152183
// set up the download of the new file directly to replace it.
153-
if (file.executable ||
184+
//
185+
// However - in the case of Fiji.app files, we already handled the backup process atomically above,
186+
// so all we need to do is update the saveTo to download directly and not to the update directory
187+
if (name.contains("Fiji.app")) {
188+
saveTo = files.prefix(name);
189+
} else if (file.executable ||
154190
saveTo.getAbsolutePath().contains("config" + File.separator + "jaunch")) {
155191
saveTo = files.prefix(name);
156192
String oldName = saveTo.getAbsolutePath() + ".old";
@@ -416,4 +452,39 @@ protected static boolean moveOutOfTheWay(final File file) {
416452
return file.renameTo(backup);
417453
}
418454

455+
private static void deleteDirectory(Path directory) throws IOException {
456+
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
457+
@Override
458+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
459+
Files.delete(file); // Delete each file
460+
return FileVisitResult.CONTINUE;
461+
}
462+
463+
@Override
464+
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
465+
Files.delete(dir); // Delete directory after contents
466+
return FileVisitResult.CONTINUE;
467+
}
468+
});
469+
}
470+
471+
private static void copyDirectory(Path sourceDir, Path targetDir) throws IOException {
472+
Files.walkFileTree(sourceDir, new SimpleFileVisitor<Path>() {
473+
@Override
474+
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
475+
Path targetPath = targetDir.resolve(sourceDir.relativize(dir));
476+
if (!Files.exists(targetPath)) {
477+
Files.createDirectories(targetPath);
478+
}
479+
return FileVisitResult.CONTINUE;
480+
}
481+
482+
@Override
483+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
484+
Path targetPath = targetDir.resolve(sourceDir.relativize(file));
485+
Files.copy(file, targetPath, StandardCopyOption.REPLACE_EXISTING);
486+
return FileVisitResult.CONTINUE;
487+
}
488+
});
489+
}
419490
}

0 commit comments

Comments
 (0)