Skip to content

Commit 051ee1a

Browse files
committed
Archive the Fiji.app folder if any file changes
Because the app folder is signed as a whole, if any file changes the signing will have to change. This also means that if any file in Fiji.app is updated, we do not want to leave a .old version in the app folder as that will invalidate the signing. Instead, if we now detect any file in the .app folder is updated, we archive the complete state of the app folder to a single .old.app, and flag all files in the .app folder for installation/update.
1 parent 1815442 commit 051ee1a

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)