forked from Murmele/Gittyup
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUpdater_mac.mm
More file actions
122 lines (105 loc) · 3.15 KB
/
Copy pathUpdater_mac.mm
File metadata and controls
122 lines (105 loc) · 3.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//
// Copyright (c) 2016, Scientific Toolworks, Inc.
//
// This software is licensed under the MIT License. The LICENSE.md file
// describes the conditions under which this software may be distributed.
//
// Author: Jason Haslam
//
#include "Updater.h"
#include <QCoreApplication>
#include <QDir>
#include <QEventLoop>
#include <QProcess>
#include <QSharedPointer>
#include <QTemporaryDir>
#include <QTemporaryFile>
#include <AppKit/NSWorkspace.h>
#include <Foundation/NSBundle.h>
#include <Foundation/NSFileManager.h>
namespace {
const QString kBundleFmt = "%1.app";
void unmount(const QString &point)
{
QProcess::execute("hdiutil", {"detach", "-quiet", point});
}
bool mount(const QString &path, const QString &root)
{
// Mount the disk image.
QProcess process;
process.setStandardOutputFile(QProcess::nullDevice());
process.start("hdiutil", {"attach", "-mountroot", root, path});
process.closeWriteChannel();
process.waitForFinished();
return (!process.exitStatus() && !process.exitCode());
}
class DiskImage
{
public:
DiskImage(const QString &path)
{
// Mount the disk image.
if (mDir.isValid())
mValid = mount(path, mDir.path());
}
~DiskImage()
{
// Unmount the disk image.
if (mValid)
unmount(mountPoint());
}
bool isValid() const { return mValid; }
QString mountPoint() const
{
return QDir(mDir.path()).filePath(QCoreApplication::applicationName());
}
private:
bool mValid = false;
QTemporaryDir mDir;
};
} // anon. namespace
bool Updater::install(const DownloadRef &download, QString &error)
{
DiskImage image(download->file()->fileName());
if (!image.isValid()) {
error = tr("The disk image failed to mount successfully");
return false;
}
// Move the existing bundle to the trash.
__block NSError *recycleError = nil;
QSharedPointer<QEventLoop> loop(new QEventLoop);
NSBundle *bundle = [NSBundle mainBundle];
NSURL *url = [bundle bundleURL];
NSString *path = [bundle bundlePath];
[[NSWorkspace sharedWorkspace] recycleURLs:@[url] completionHandler:
^(NSDictionary<NSURL *, NSURL *> *newURLs, NSError *error) {
recycleError = error;
loop->quit();
}];
// Wait for recycle.
loop->exec();
if (recycleError) {
error = tr("The existing bundle could not be moved to the trash");
return false;
}
// Copy the new bundle from the disk image.
// FIXME: There's no way to undo the recycle if copy fails.
NSError *copyError = nil;
NSFileManager *mgr = [NSFileManager defaultManager];
QString name = kBundleFmt.arg(QCoreApplication::applicationName());
QString src = QDir(image.mountPoint()).filePath(name);
[mgr copyItemAtPath:src.toNSString() toPath:path error:©Error];
if (copyError) {
error = tr("The new bundle could not be copied into place");
return false;
}
// Start the relaunch helper.
QDir dir(QCoreApplication::applicationDirPath());
QString app = QCoreApplication::applicationFilePath();
QString pid = QString::number(QCoreApplication::applicationPid());
if (!QProcess::startDetached(dir.filePath("gittyup-relauncher"), {app, pid})) {
error = tr("Helper application failed to start");
return false;
}
return true;
}