Add installer
This commit is contained in:
parent
c9bc8aa8c1
commit
a2a5cafc5f
73 changed files with 4354 additions and 488 deletions
329
deploy/installer/config/controlscript.js
Normal file
329
deploy/installer/config/controlscript.js
Normal file
|
|
@ -0,0 +1,329 @@
|
|||
var requestToQuitFromApp = false;
|
||||
var updaterCompleted = 0;
|
||||
var desktopAppProcessRunning = false;
|
||||
var appInstalledUninstallerPath;
|
||||
|
||||
function appName()
|
||||
{
|
||||
return installer.value("Name");
|
||||
}
|
||||
|
||||
function appAlreadyInstalled()
|
||||
{
|
||||
return installer.fileExists(appInstalledUninstallerPath);
|
||||
}
|
||||
|
||||
function appExecutableFileName()
|
||||
{
|
||||
if (runningOnWindows()) {
|
||||
return appName() + ".exe";
|
||||
} else {
|
||||
return appName();
|
||||
}
|
||||
}
|
||||
|
||||
function appInstalled()
|
||||
{
|
||||
if (runningOnWindows()) {
|
||||
appInstalledUninstallerPath = installer.value("TargetDir") + "\\maintenancetool.exe";
|
||||
} else if (runningOnMacOS()){
|
||||
appInstalledUninstallerPath = "/Applications/" + appName() + ".app/maintenancetool.app/Contents/MacOS/maintenancetool";
|
||||
}
|
||||
|
||||
return appAlreadyInstalled();
|
||||
}
|
||||
|
||||
function endsWith(str, suffix)
|
||||
{
|
||||
return str.indexOf(suffix, str.length - suffix.length) !== -1;
|
||||
}
|
||||
|
||||
function runningOnWindows()
|
||||
{
|
||||
return (installer.value("os") === "win");
|
||||
}
|
||||
|
||||
function runningOnMacOS()
|
||||
{
|
||||
return (installer.value("os") === "mac");
|
||||
}
|
||||
|
||||
function sleep(miliseconds) {
|
||||
var currentTime = new Date().getTime();
|
||||
while (currentTime + miliseconds >= new Date().getTime()) {}
|
||||
}
|
||||
|
||||
function raiseInstallerWindow()
|
||||
{
|
||||
if (!runningOnMacOS()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var result = installer.execute("/bin/bash", ["-c", "ps -A | grep -m1 '" + appName() + "' | awk '{print $1}'"]);
|
||||
if (Number(result[0]) > 0) {
|
||||
var arg = 'tell application \"System Events\" ' +
|
||||
'\n set frontmost of the first process whose unix id is ' + Number(result[0]) + ' to true ' +
|
||||
'\n end tell' +
|
||||
'\n ';
|
||||
installer.execute("osascript", ["-e", arg]);
|
||||
}
|
||||
}
|
||||
|
||||
function appProcessIsRunning()
|
||||
{
|
||||
if (runningOnWindows()) {
|
||||
var cmdArgs = ["/FI", "WINDOWTITLE eq " + appName()];
|
||||
var result = installer.execute("tasklist", cmdArgs);
|
||||
|
||||
if ( Number(result[1]) === 0 ) {
|
||||
if (result[0].indexOf(appExecutableFileName()) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return checkProccesIsRunning("pgrep -x '" + appName() + "'")
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkProccesIsRunning(arg)
|
||||
{
|
||||
var cmdArgs = ["-c", arg];
|
||||
var result = installer.execute("/bin/bash", cmdArgs);
|
||||
var resultArg1 = Number(result[0])
|
||||
if (resultArg1 >= 3) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function requestToQuit(installer,gui)
|
||||
{
|
||||
requestToQuitFromApp = true;
|
||||
|
||||
installer.setDefaultPageVisible(QInstaller.IntroductionPage, false);
|
||||
installer.setDefaultPageVisible(QInstaller.TargetDirectory, false);
|
||||
installer.setDefaultPageVisible(QInstaller.ComponentSelection, false);
|
||||
installer.setDefaultPageVisible(QInstaller.LicenseCheck, false);
|
||||
installer.setDefaultPageVisible(QInstaller.StartMenuSelection, false);
|
||||
installer.setDefaultPageVisible(QInstaller.ReadyForInstallation, false);
|
||||
installer.setDefaultPageVisible(QInstaller.PerformInstallation, false);
|
||||
installer.setDefaultPageVisible(QInstaller.FinishedPage, false);
|
||||
|
||||
gui.clickButton(buttons.NextButton);
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
gui.clickButton(buttons.CancelButton);
|
||||
|
||||
if (runningOnWindows()) {
|
||||
installer.setCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Controller.prototype.PerformInstallationPageCallback = function()
|
||||
{
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
|
||||
Controller.prototype.LicenseAgreementPageCallback = function()
|
||||
{
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
|
||||
Controller.prototype.FinishedPageCallback = function ()
|
||||
{
|
||||
if (desktopAppProcessRunning) {
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
} else if (installer.isUpdater()) {
|
||||
installer.autoAcceptMessageBoxes();
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.prototype.RestartPageCallback = function ()
|
||||
{
|
||||
updaterCompleted = 1;
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
}
|
||||
|
||||
Controller.prototype.StartMenuDirectoryPageCallback = function()
|
||||
{
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
|
||||
Controller.prototype.ComponentSelectionPageCallback = function()
|
||||
{
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
|
||||
Controller.prototype.ReadyForInstallationPageCallback = function()
|
||||
{
|
||||
if (installer.isUpdater()) {
|
||||
gui.clickButton(buttons.CommitButton);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.prototype.TargetDirectoryPageCallback = function ()
|
||||
{
|
||||
var widget = gui.pageById(QInstaller.TargetDirectory);
|
||||
|
||||
if (widget !== null) {
|
||||
widget.BrowseDirectoryButton.clicked.disconnect(onBrowseButtonClicked);
|
||||
widget.BrowseDirectoryButton.clicked.connect(onBrowseButtonClicked);
|
||||
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
}
|
||||
|
||||
Controller.prototype.IntroductionPageCallback = function ()
|
||||
{
|
||||
var widget = gui.currentPageWidget();
|
||||
if (installer.isUpdater() && updaterCompleted === 1) {
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
gui.clickButton(buttons.CancelButton);
|
||||
return;
|
||||
}
|
||||
|
||||
if (installer.isUninstaller()) {
|
||||
if (widget !== null) {
|
||||
widget.findChild("PackageManagerRadioButton").visible = false;
|
||||
widget.findChild("UpdaterRadioButton").visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (installer.isUpdater()) {
|
||||
gui.clickButton(buttons.NextButton);
|
||||
}
|
||||
}
|
||||
|
||||
onBrowseButtonClicked = function()
|
||||
{
|
||||
var widget = gui.pageById(QInstaller.TargetDirectory);
|
||||
if (widget !== null) {
|
||||
if (runningOnWindows()) {
|
||||
// On Windows we are appending \<APP_NAME> if selected path don't ends with <APP_NAME>
|
||||
var targetDir = widget.TargetDirectoryLineEdit.text;
|
||||
if (! endsWith(targetDir, appName())) {
|
||||
targetDir = targetDir + "\\" + appName();
|
||||
}
|
||||
installer.setValue("TargetDir", targetDir);
|
||||
widget.TargetDirectoryLineEdit.setText(installer.value("TargetDir"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onNextButtonClicked = function()
|
||||
{
|
||||
var widget = gui.pageById(QInstaller.TargetDirectory);
|
||||
if (widget !== null) {
|
||||
installer.setValue("APP_BUNDLE_TARGET_DIR", widget.TargetDirectoryLineEdit.text);
|
||||
}
|
||||
}
|
||||
|
||||
function Controller () {
|
||||
console.log("OS: %1, architecture: %2".arg(systemInfo.prettyProductName).arg(systemInfo.currentCpuArchitecture));
|
||||
|
||||
if (installer.isInstaller() || installer.isUpdater()) {
|
||||
console.log("Check if app already installed: " + appInstalled());
|
||||
}
|
||||
|
||||
if (runningOnWindows()) {
|
||||
installer.setValue("AllUsers", "true");
|
||||
}
|
||||
|
||||
if (installer.isInstaller()) {
|
||||
installer.setDefaultPageVisible(QInstaller.ComponentSelection, false);
|
||||
installer.setDefaultPageVisible(QInstaller.TargetDirectory, false);
|
||||
installer.setDefaultPageVisible(QInstaller.StartMenuDirectoryPage, false);
|
||||
installer.setDefaultPageVisible(QInstaller.LicenseCheck, false);
|
||||
|
||||
isDesktopAppProcessRunningMessageLoop();
|
||||
|
||||
if (requestToQuitFromApp === true) {
|
||||
requestToQuit(installer, gui);
|
||||
return;
|
||||
}
|
||||
|
||||
if (runningOnMacOS()) {
|
||||
installer.setMessageBoxAutomaticAnswer("OverwriteTargetDirectory", QMessageBox.Yes);
|
||||
}
|
||||
|
||||
if (appAlreadyInstalled()) {
|
||||
if (QMessageBox.Ok === QMessageBox.information("os.information", appName(),
|
||||
qsTr("The application is already installed.") + " " +
|
||||
qsTr("We need to remove the old installation first. Do you wish to proceed?"),
|
||||
QMessageBox.Ok | QMessageBox.Cancel)) {
|
||||
|
||||
if (appAlreadyInstalled()) {
|
||||
var resultArray = installer.execute(appInstalledUninstallerPath);
|
||||
|
||||
console.log("Uninstaller finished with code: " + resultArray[1])
|
||||
|
||||
if (Number(resultArray[1]) !== 0) {
|
||||
console.log("Uninstallation aborted by user");
|
||||
installer.setCancelled();
|
||||
return;
|
||||
} else {
|
||||
for (var i = 0; i < 100; i++) {
|
||||
sleep(100);
|
||||
if (!installer.fileExists(appInstalledUninstallerPath)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
raiseInstallerWindow();
|
||||
|
||||
} else {
|
||||
console.log("Request to quit from user");
|
||||
installer.setCancelled();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (installer.isUninstaller()) {
|
||||
isDesktopAppProcessRunningMessageLoop();
|
||||
|
||||
if (requestToQuitFromApp === true) {
|
||||
requestToQuit(installer, gui);
|
||||
return;
|
||||
}
|
||||
|
||||
} else if (installer.isUpdater()) {
|
||||
installer.setMessageBoxAutomaticAnswer("cancelInstallation", QMessageBox.No);
|
||||
installer.installationFinished.connect(function() {
|
||||
gui.clickButton(buttons.NextButton);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isDesktopAppProcessRunningMessageLoop = function ()
|
||||
{
|
||||
if (requestToQuitFromApp === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (installer.isUpdater()) {
|
||||
for (var i = 0; i < 400; i++) {
|
||||
desktopAppProcessRunning = appProcessIsRunning();
|
||||
if (!desktopAppProcessRunning) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
desktopAppProcessRunning = appProcessIsRunning();
|
||||
|
||||
if (desktopAppProcessRunning) {
|
||||
var result = QMessageBox.warning("QMessageBox", appName() + " installer",
|
||||
appName() + " is active. Close the app and press \"Retry\" button to continue installation. Press \"Abort\" button to abort the installer and exit.",
|
||||
QMessageBox.Retry | QMessageBox.Abort);
|
||||
if (result === QMessageBox.Retry) {
|
||||
isDesktopAppProcessRunningMessageLoop();
|
||||
} else {
|
||||
requestToQuitFromApp = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
27
deploy/installer/config/macos.xml
Normal file
27
deploy/installer/config/macos.xml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Installer>
|
||||
<Name>AmneziaVPN</Name>
|
||||
<Version>1.0.0</Version>
|
||||
<Title>AmneziaVPN</Title>
|
||||
<Publisher>AmneziaVPN</Publisher>
|
||||
<StartMenuDir>AmneziaVPN</StartMenuDir>
|
||||
<TargetDir>/Applications/AmneziaVPN.app</TargetDir>
|
||||
<WizardDefaultWidth>600</WizardDefaultWidth>
|
||||
<WizardDefaultHeight>380</WizardDefaultHeight>
|
||||
<WizardStyle>Modern</WizardStyle>
|
||||
<RemoveTargetDir>true</RemoveTargetDir>
|
||||
<AllowSpaceInPath>true</AllowSpaceInPath>
|
||||
<AllowNonAsciiCharacters>false</AllowNonAsciiCharacters>
|
||||
<ControlScript>controlscript.js</ControlScript>
|
||||
<RepositorySettingsPageVisible>false</RepositorySettingsPageVisible>
|
||||
<DependsOnLocalInstallerBinary>true</DependsOnLocalInstallerBinary>
|
||||
<SupportsModify>false</SupportsModify>
|
||||
<DisableAuthorizationFallback>true</DisableAuthorizationFallback>
|
||||
<RemoteRepositories>
|
||||
<Repository>
|
||||
<Url>https://amneziavpn.org/updates/macos</Url>
|
||||
<Enabled>true</Enabled>
|
||||
<DisplayName>AmneziaVPN - repository for macOS</DisplayName>
|
||||
</Repository>
|
||||
</RemoteRepositories>
|
||||
</Installer>
|
||||
27
deploy/installer/config/windows.xml
Normal file
27
deploy/installer/config/windows.xml
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Installer>
|
||||
<Name>AmneziaVPN</Name>
|
||||
<Version>1.0.0</Version>
|
||||
<Title>AmneziaVPN</Title>
|
||||
<Publisher>AmneziaVPN</Publisher>
|
||||
<StartMenuDir>AmneziaVPN</StartMenuDir>
|
||||
<TargetDir>@ApplicationsDir@/AmneziaVPN</TargetDir>
|
||||
<WizardDefaultWidth>600</WizardDefaultWidth>
|
||||
<WizardDefaultHeight>380</WizardDefaultHeight>
|
||||
<WizardStyle>Modern</WizardStyle>
|
||||
<RemoveTargetDir>true</RemoveTargetDir>
|
||||
<AllowSpaceInPath>true</AllowSpaceInPath>
|
||||
<AllowNonAsciiCharacters>false</AllowNonAsciiCharacters>
|
||||
<ControlScript>controlscript.js</ControlScript>
|
||||
<RepositorySettingsPageVisible>false</RepositorySettingsPageVisible>
|
||||
<DependsOnLocalInstallerBinary>true</DependsOnLocalInstallerBinary>
|
||||
<SupportsModify>false</SupportsModify>
|
||||
<DisableAuthorizationFallback>true</DisableAuthorizationFallback>
|
||||
<RemoteRepositories>
|
||||
<Repository>
|
||||
<Url>https://amneziavpn.org/updates/windows</Url>
|
||||
<Enabled>true</Enabled>
|
||||
<DisplayName>AmneziaVPN - repository for Windows</DisplayName>
|
||||
</Repository>
|
||||
</RemoteRepositories>
|
||||
</Installer>
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
|
||||
function appName()
|
||||
{
|
||||
return installer.value("Name")
|
||||
}
|
||||
|
||||
function serviceName()
|
||||
{
|
||||
return (appName() + "-service")
|
||||
}
|
||||
|
||||
function appExecutableFileName()
|
||||
{
|
||||
if (runningOnWindows()) {
|
||||
return appName() + ".exe";
|
||||
} else {
|
||||
return appName();
|
||||
}
|
||||
}
|
||||
|
||||
function runningOnWindows()
|
||||
{
|
||||
return (systemInfo.kernelType === "winnt");
|
||||
}
|
||||
|
||||
function runningOnMacOS()
|
||||
{
|
||||
return (systemInfo.kernelType === "darwin");
|
||||
}
|
||||
|
||||
function vcRuntimeIsInstalled()
|
||||
{
|
||||
return (installer.findPath("msvcp140.dll", [installer.value("RootDir")+ "\\Windows\\System32\\"]).length !== 0)
|
||||
}
|
||||
|
||||
function Component()
|
||||
{
|
||||
component.loaded.connect(this, Component.prototype.componentLoaded);
|
||||
installer.installationFinished.connect(this, Component.prototype.installationFinishedPageIsShown);
|
||||
installer.finishButtonClicked.connect(this, Component.prototype.installationFinished);
|
||||
}
|
||||
|
||||
Component.prototype.componentLoaded = function ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Component.prototype.installationFinishedPageIsShown = function()
|
||||
{
|
||||
if (installer.isInstaller() && installer.status === QInstaller.Success) {
|
||||
gui.clickButton(buttons.FinishButton);
|
||||
}
|
||||
}
|
||||
|
||||
Component.prototype.createOperations = function()
|
||||
{
|
||||
component.createOperations();
|
||||
|
||||
if (runningOnWindows()) {
|
||||
|
||||
component.addOperation("CreateShortcut", "@TargetDir@/" + appExecutableFileName(),
|
||||
QDesktopServices.storageLocation(QDesktopServices.DesktopLocation) + "/" + appName() + ".lnk",
|
||||
"workingDirectory=@TargetDir@", "iconPath=@TargetDir@\\" + appExecutableFileName(), "iconId=0");
|
||||
|
||||
|
||||
component.addElevatedOperation("CreateShortcut", "@TargetDir@/" + appExecutableFileName(),
|
||||
installer.value("AllUsersStartMenuProgramsPath") + "/" + appName() + ".lnk",
|
||||
"workingDirectory=@TargetDir@", "iconPath=@TargetDir@\\" + appExecutableFileName(), "iconId=0");
|
||||
|
||||
if (!vcRuntimeIsInstalled()) {
|
||||
component.addElevatedOperation("Execute", "@TargetDir@\\" + "vc_redist.x86.exe", "/install", "/quiet", "/norestart", "/log", "vc_redist_2017_x86.log");
|
||||
} else {
|
||||
console.log("Microsoft Visual C++ 2017 Redistributable already installed");
|
||||
}
|
||||
|
||||
component.addElevatedOperation("Execute",
|
||||
["sc", "create", serviceName(), "binpath=", installer.value("TargetDir").replace(/\//g, '\\') + "\\" + serviceName() + ".exe",
|
||||
"start=", "auto", "depend=", "BFE/nsi"],
|
||||
"UNDOEXECUTE", ["post-uninstall.exe"]);
|
||||
|
||||
} else if (runningOnMacOS()) {
|
||||
component.addElevatedOperation("Execute", "@TargetDir@/post_install.sh", "UNDOEXECUTE", "@TargetDir@/post_uninstall.sh");
|
||||
}
|
||||
}
|
||||
|
||||
Component.prototype.installationFinished = function()
|
||||
{
|
||||
var command = "";
|
||||
var args = [];
|
||||
|
||||
if ((installer.status === QInstaller.Success) && (installer.isInstaller() || installer.isUpdater())) {
|
||||
|
||||
if (!installer.gainAdminRights()) {
|
||||
console.log("Fatal error! Cannot get admin rights!")
|
||||
return
|
||||
}
|
||||
|
||||
if (runningOnWindows()) {
|
||||
command = "@TargetDir@/" + appExecutableFileName()
|
||||
|
||||
var status1 = installer.execute("net", ["start", serviceName()])
|
||||
console.log(("%1 started with status: %2 ").arg(serviceName()).arg(status1))
|
||||
|
||||
var status2 = installer.execute("sc", ["failure", serviceName(), "reset=", "100", "actions=", "restart/2000/restart/2000/restart/2000"])
|
||||
console.log(("Changed settings for %1 with status: %2 ").arg(serviceName()).arg(status2))
|
||||
|
||||
} else if (runningOnMacOS()) {
|
||||
command = "/Applications/" + appName() + ".app/Contents/MacOS/" + appName();
|
||||
}
|
||||
|
||||
installer.dropAdminRights()
|
||||
|
||||
processStatus = installer.executeDetached(command, args, installer.value("TargetDir"));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Package>
|
||||
<DisplayName>AmneziaVPN</DisplayName>
|
||||
<Description>Installation package for AmneziaVPN</Description>
|
||||
<Version>1.0.0</Version>
|
||||
<ReleaseDate>1970-01-01</ReleaseDate>
|
||||
<Default>true</Default>
|
||||
<ForcedInstallation>true</ForcedInstallation>
|
||||
<RequiresAdminRights>true</RequiresAdminRights>
|
||||
<Script>componentscript.js</Script>
|
||||
</Package>
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue