mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 18:02:38 +02:00
Make DockerInstaller also install the Windows version of Docker
This commit is contained in:
parent
fa524d78b6
commit
17a585f614
4 changed files with 161 additions and 57 deletions
22
BUILD.md
22
BUILD.md
|
@ -31,8 +31,6 @@ Create a .deb:
|
||||||
|
|
||||||
## macOS
|
## macOS
|
||||||
|
|
||||||
## macOS
|
|
||||||
|
|
||||||
Install Xcode from the Mac App Store. Once it's installed, run it for the first time to set it up. Also, run this to make sure command line tools are installed: `xcode-select --install`. And finally, open Xcode, go to Preferences > Locations, and make sure under Command Line Tools you select an installed version from the dropdown. (This is required for installing Qt5.)
|
Install Xcode from the Mac App Store. Once it's installed, run it for the first time to set it up. Also, run this to make sure command line tools are installed: `xcode-select --install`. And finally, open Xcode, go to Preferences > Locations, and make sure under Command Line Tools you select an installed version from the dropdown. (This is required for installing Qt5.)
|
||||||
|
|
||||||
Download and install Python 3.7.4 from https://www.python.org/downloads/release/python-374/. I downloaded `python-3.7.4-macosx10.9.pkg`.
|
Download and install Python 3.7.4 from https://www.python.org/downloads/release/python-374/. I downloaded `python-3.7.4-macosx10.9.pkg`.
|
||||||
|
@ -70,4 +68,22 @@ And then run `build_app.py --with-codesign`:
|
||||||
pipenv run ./install/macos/build_app.py --with-codesign
|
pipenv run ./install/macos/build_app.py --with-codesign
|
||||||
```
|
```
|
||||||
|
|
||||||
The output is in the `dist` folder.
|
The output is in the `dist` folder.
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
|
||||||
|
Download Python 3.7.6, 32-bit (x86) from https://www.python.org/downloads/release/python-376/. I downloaded python-3.7.6.exe. When installing it, make sure to check the "Add Python 3.7 to PATH" checkbox on the first page of the installer.
|
||||||
|
|
||||||
|
Open a command prompt and cd to the gpgsync folder. If you don't have it already, install pipenv (`pip install pipenv`). Then install dependencies:
|
||||||
|
|
||||||
|
```
|
||||||
|
python -m pipenv install --dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Install the Qt 5.14.1 from https://www.qt.io/offline-installers. I downloaded qt-opensource-windows-x86-5.14.1.exe. In the installer, unfortunately you have login to an account. Then all you need `Qt` > `Qt 5.14.1` > `MSVC 2017 32-bit`.
|
||||||
|
|
||||||
|
After that you can launch GPG Sync during development with:
|
||||||
|
|
||||||
|
```
|
||||||
|
python -m pipenv run python dev_scripts\dangerzone
|
||||||
|
```
|
||||||
|
|
|
@ -8,7 +8,12 @@ import time
|
||||||
|
|
||||||
from .common import Common
|
from .common import Common
|
||||||
from .main_window import MainWindow
|
from .main_window import MainWindow
|
||||||
from .docker_installer import is_docker_installed, is_docker_ready, DockerInstaller
|
from .docker_installer import (
|
||||||
|
is_docker_installed,
|
||||||
|
is_docker_ready,
|
||||||
|
launch_docker_windows,
|
||||||
|
DockerInstaller,
|
||||||
|
)
|
||||||
|
|
||||||
dangerzone_version = "0.1.0"
|
dangerzone_version = "0.1.0"
|
||||||
|
|
||||||
|
@ -49,6 +54,30 @@ def main(filename):
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
if not is_docker_installed(common):
|
||||||
|
print("Docker is not installed")
|
||||||
|
docker_installer = DockerInstaller(common)
|
||||||
|
docker_installer.start()
|
||||||
|
# Quit after the installer runs, because it requires rebooting
|
||||||
|
return
|
||||||
|
|
||||||
|
if not is_docker_ready(common):
|
||||||
|
print("Docker is not running")
|
||||||
|
launch_docker_windows()
|
||||||
|
|
||||||
|
# Wait up to 20 minutes for docker to be ready
|
||||||
|
for i in range(120):
|
||||||
|
if is_docker_ready(common):
|
||||||
|
main(filename)
|
||||||
|
return
|
||||||
|
|
||||||
|
print("Waiting for docker to be available ...")
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
# Give up
|
||||||
|
print("Docker not available, giving up")
|
||||||
|
|
||||||
# Main window
|
# Main window
|
||||||
main_window = MainWindow(common)
|
main_window = MainWindow(common)
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,14 @@ class Common(object):
|
||||||
|
|
||||||
# Temporary directory to store pixel data
|
# Temporary directory to store pixel data
|
||||||
# Note in macOS, temp dirs must be in /tmp (or a few other paths) for Docker to mount them
|
# Note in macOS, temp dirs must be in /tmp (or a few other paths) for Docker to mount them
|
||||||
self.pixel_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-pixel-")
|
if platform.system() == "Windows":
|
||||||
self.safe_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-safe-")
|
self.pixel_dir = tempfile.TemporaryDirectory(prefix="dangerzone-pixel-")
|
||||||
|
self.safe_dir = tempfile.TemporaryDirectory(prefix="dangerzone-safe-")
|
||||||
|
else:
|
||||||
|
self.pixel_dir = tempfile.TemporaryDirectory(
|
||||||
|
prefix="/tmp/dangerzone-pixel-"
|
||||||
|
)
|
||||||
|
self.safe_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-safe-")
|
||||||
print(
|
print(
|
||||||
f"Temporary directories created, dangerous={self.pixel_dir.name}, safe={self.safe_dir.name}"
|
f"Temporary directories created, dangerous={self.pixel_dir.name}, safe={self.safe_dir.name}"
|
||||||
)
|
)
|
||||||
|
@ -51,6 +57,10 @@ class Common(object):
|
||||||
# Container runtime
|
# Container runtime
|
||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
self.container_runtime = "/usr/local/bin/docker"
|
self.container_runtime = "/usr/local/bin/docker"
|
||||||
|
elif platform.system() == "Windows":
|
||||||
|
self.container_runtime = (
|
||||||
|
"C:\\Program Files\\Docker\\Docker\\resources\\bin\\docker.exe"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
self.container_runtime = "podman"
|
self.container_runtime = "podman"
|
||||||
|
|
||||||
|
|
|
@ -5,17 +5,23 @@ import tempfile
|
||||||
import subprocess
|
import subprocess
|
||||||
import shutil
|
import shutil
|
||||||
import time
|
import time
|
||||||
|
import platform
|
||||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||||
|
|
||||||
|
|
||||||
def is_docker_installed(common):
|
def is_docker_installed(common):
|
||||||
# Does the docker binary exist?
|
if platform.system() == "Darwin":
|
||||||
if os.path.isdir("/Applications/Docker.app") and os.path.exists(
|
# Does the docker binary exist?
|
||||||
common.container_runtime
|
if os.path.isdir("/Applications/Docker.app") and os.path.exists(
|
||||||
):
|
common.container_runtime
|
||||||
# Is it executable?
|
):
|
||||||
st = os.stat(common.container_runtime)
|
# Is it executable?
|
||||||
return bool(st.st_mode & stat.S_IXOTH)
|
st = os.stat(common.container_runtime)
|
||||||
|
return bool(st.st_mode & stat.S_IXOTH)
|
||||||
|
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
return os.path.exists(common.container_runtime)
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +34,11 @@ def is_docker_ready(common):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def launch_docker_windows():
|
||||||
|
docker_desktop_path = "C:\\Program Files\\Docker\\Docker\\Docker Desktop.exe"
|
||||||
|
subprocess.Popen([docker_desktop_path])
|
||||||
|
|
||||||
|
|
||||||
class DockerInstaller(QtWidgets.QDialog):
|
class DockerInstaller(QtWidgets.QDialog):
|
||||||
def __init__(self, common):
|
def __init__(self, common):
|
||||||
super(DockerInstaller, self).__init__()
|
super(DockerInstaller, self).__init__()
|
||||||
|
@ -36,7 +47,11 @@ class DockerInstaller(QtWidgets.QDialog):
|
||||||
self.setWindowTitle("dangerzone")
|
self.setWindowTitle("dangerzone")
|
||||||
self.setWindowIcon(QtGui.QIcon(self.common.get_resource_path("logo.png")))
|
self.setWindowIcon(QtGui.QIcon(self.common.get_resource_path("logo.png")))
|
||||||
|
|
||||||
label = QtWidgets.QLabel("Dangerzone for macOS requires Docker")
|
label = QtWidgets.QLabel()
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
label.setText("Dangerzone for macOS requires Docker")
|
||||||
|
elif platform.system() == "Windows":
|
||||||
|
label.setText("Dangerzone for Windows requires Docker")
|
||||||
label.setStyleSheet("QLabel { font-weight: bold; }")
|
label.setStyleSheet("QLabel { font-weight: bold; }")
|
||||||
label.setAlignment(QtCore.Qt.AlignCenter)
|
label.setAlignment(QtCore.Qt.AlignCenter)
|
||||||
|
|
||||||
|
@ -50,17 +65,21 @@ class DockerInstaller(QtWidgets.QDialog):
|
||||||
self.install_button.setStyleSheet("QPushButton { font-weight: bold; }")
|
self.install_button.setStyleSheet("QPushButton { font-weight: bold; }")
|
||||||
self.install_button.clicked.connect(self.install_clicked)
|
self.install_button.clicked.connect(self.install_clicked)
|
||||||
self.install_button.hide()
|
self.install_button.hide()
|
||||||
self.launch_button = QtWidgets.QPushButton("Launch Docker")
|
|
||||||
self.launch_button.setStyleSheet("QPushButton { font-weight: bold; }")
|
if platform.system() == "Darwin":
|
||||||
self.launch_button.clicked.connect(self.launch_clicked)
|
self.launch_button = QtWidgets.QPushButton("Launch Docker")
|
||||||
self.launch_button.hide()
|
self.launch_button.setStyleSheet("QPushButton { font-weight: bold; }")
|
||||||
|
self.launch_button.clicked.connect(self.launch_clicked)
|
||||||
|
self.launch_button.hide()
|
||||||
|
|
||||||
self.cancel_button = QtWidgets.QPushButton("Cancel")
|
self.cancel_button = QtWidgets.QPushButton("Cancel")
|
||||||
self.cancel_button.clicked.connect(self.cancel_clicked)
|
self.cancel_button.clicked.connect(self.cancel_clicked)
|
||||||
|
|
||||||
buttons_layout = QtWidgets.QHBoxLayout()
|
buttons_layout = QtWidgets.QHBoxLayout()
|
||||||
buttons_layout.addStretch()
|
buttons_layout.addStretch()
|
||||||
buttons_layout.addWidget(self.install_button)
|
buttons_layout.addWidget(self.install_button)
|
||||||
buttons_layout.addWidget(self.launch_button)
|
if platform.system() == "Darwin":
|
||||||
|
buttons_layout.addWidget(self.launch_button)
|
||||||
buttons_layout.addWidget(self.cancel_button)
|
buttons_layout.addWidget(self.cancel_button)
|
||||||
buttons_layout.addStretch()
|
buttons_layout.addStretch()
|
||||||
|
|
||||||
|
@ -72,8 +91,14 @@ class DockerInstaller(QtWidgets.QDialog):
|
||||||
layout.addStretch()
|
layout.addStretch()
|
||||||
self.setLayout(layout)
|
self.setLayout(layout)
|
||||||
|
|
||||||
self.tmp_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-docker-")
|
if platform.system == "Darwin":
|
||||||
self.dmg_filename = os.path.join(self.tmp_dir.name, "Docker.dmg")
|
self.tmp_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-docker-")
|
||||||
|
self.installer_filename = os.path.join(self.tmp_dir.name, "Docker.dmg")
|
||||||
|
else:
|
||||||
|
self.tmp_dir = tempfile.TemporaryDirectory(prefix="dangerzone-docker-")
|
||||||
|
self.installer_filename = os.path.join(
|
||||||
|
self.tmp_dir.name, "Docker for Windows Installer.exe"
|
||||||
|
)
|
||||||
|
|
||||||
# Threads
|
# Threads
|
||||||
self.download_t = None
|
self.download_t = None
|
||||||
|
@ -105,19 +130,22 @@ class DockerInstaller(QtWidgets.QDialog):
|
||||||
self.timer.start(10)
|
self.timer.start(10)
|
||||||
|
|
||||||
def start_download(self):
|
def start_download(self):
|
||||||
self.download_t = Downloader(self.dmg_filename)
|
self.download_t = Downloader(self.installer_filename)
|
||||||
self.download_t.download_finished.connect(self.download_finished)
|
self.download_t.download_finished.connect(self.download_finished)
|
||||||
self.download_t.download_failed.connect(self.download_failed)
|
self.download_t.download_failed.connect(self.download_failed)
|
||||||
self.download_t.update_progress.connect(self.update_progress)
|
self.download_t.update_progress.connect(self.update_progress)
|
||||||
self.download_t.start()
|
self.download_t.start()
|
||||||
|
|
||||||
def install_finished(self):
|
def install_finished(self):
|
||||||
self.task_label.setText("Finished installing Docker")
|
if platform.system() == "Darwin":
|
||||||
|
self.task_label.setText("Finished installing Docker")
|
||||||
|
self.launch_button.show()
|
||||||
|
self.cancel_button.setEnabled(True)
|
||||||
|
elif platform.system == "Windows":
|
||||||
|
self.task_label.setText("Reboot to finish installing Docker")
|
||||||
self.install_t = None
|
self.install_t = None
|
||||||
self.progress.hide()
|
self.progress.hide()
|
||||||
self.install_button.hide()
|
self.install_button.hide()
|
||||||
self.launch_button.show()
|
|
||||||
self.cancel_button.setEnabled(True)
|
|
||||||
|
|
||||||
def install_failed(self, exception):
|
def install_failed(self, exception):
|
||||||
print(f"Install failed: {exception}")
|
print(f"Install failed: {exception}")
|
||||||
|
@ -141,16 +169,17 @@ class DockerInstaller(QtWidgets.QDialog):
|
||||||
self.timer.start(10)
|
self.timer.start(10)
|
||||||
|
|
||||||
def start_installer(self):
|
def start_installer(self):
|
||||||
self.install_t = Installer(self.dmg_filename)
|
self.install_t = Installer(self.installer_filename)
|
||||||
self.install_t.install_finished.connect(self.install_finished)
|
self.install_t.install_finished.connect(self.install_finished)
|
||||||
self.install_t.install_failed.connect(self.install_failed)
|
self.install_t.install_failed.connect(self.install_failed)
|
||||||
self.install_t.update_task_label.connect(self.update_task_label)
|
self.install_t.update_task_label.connect(self.update_task_label)
|
||||||
self.install_t.start()
|
self.install_t.start()
|
||||||
|
|
||||||
def launch_clicked(self):
|
def launch_clicked(self):
|
||||||
print("Launching Docker")
|
if system.platform() == "Darwin":
|
||||||
self.accept()
|
print("Launching Docker")
|
||||||
subprocess.Popen(["open", "-a", "Docker.app"])
|
self.accept()
|
||||||
|
subprocess.Popen(["open", "-a", "Docker.app"])
|
||||||
|
|
||||||
def cancel_clicked(self):
|
def cancel_clicked(self):
|
||||||
self.reject()
|
self.reject()
|
||||||
|
@ -175,22 +204,25 @@ class Downloader(QtCore.QThread):
|
||||||
download_failed = QtCore.pyqtSignal(int)
|
download_failed = QtCore.pyqtSignal(int)
|
||||||
update_progress = QtCore.pyqtSignal(int, int)
|
update_progress = QtCore.pyqtSignal(int, int)
|
||||||
|
|
||||||
def __init__(self, dmg_filename):
|
def __init__(self, installer_filename):
|
||||||
super(Downloader, self).__init__()
|
super(Downloader, self).__init__()
|
||||||
self.dmg_filename = dmg_filename
|
self.installer_filename = installer_filename
|
||||||
|
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
self.installer_url = "https://download.docker.com/mac/stable/Docker.dmg"
|
||||||
|
elif platform.system() == "Windows":
|
||||||
|
self.installer_url = "https://download.docker.com/win/stable/Docker%20for%20Windows%20Installer.exe"
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
print(f"Downloading docker to {self.dmg_filename}")
|
print(f"Downloading docker to {self.installer_filename}")
|
||||||
with requests.get(
|
with requests.get(self.installer_url, stream=True) as r:
|
||||||
"https://download.docker.com/mac/stable/Docker.dmg", stream=True
|
|
||||||
) as r:
|
|
||||||
if r.status_code != 200:
|
if r.status_code != 200:
|
||||||
self.download_failed.emit(r.status_code)
|
self.download_failed.emit(r.status_code)
|
||||||
return
|
return
|
||||||
total_bytes = int(r.headers.get("content-length"))
|
total_bytes = int(r.headers.get("content-length"))
|
||||||
downloaded_bytes = 0
|
downloaded_bytes = 0
|
||||||
|
|
||||||
with open(self.dmg_filename, "wb") as f:
|
with open(self.installer_filename, "wb") as f:
|
||||||
for chunk in r.iter_content(chunk_size=8192):
|
for chunk in r.iter_content(chunk_size=8192):
|
||||||
if chunk: # filter out keep-alive new chunks
|
if chunk: # filter out keep-alive new chunks
|
||||||
downloaded_bytes += f.write(chunk)
|
downloaded_bytes += f.write(chunk)
|
||||||
|
@ -205,34 +237,51 @@ class Installer(QtCore.QThread):
|
||||||
install_failed = QtCore.pyqtSignal(str)
|
install_failed = QtCore.pyqtSignal(str)
|
||||||
update_task_label = QtCore.pyqtSignal(str)
|
update_task_label = QtCore.pyqtSignal(str)
|
||||||
|
|
||||||
def __init__(self, dmg_filename):
|
def __init__(self, installer_filename):
|
||||||
super(Installer, self).__init__()
|
super(Installer, self).__init__()
|
||||||
self.dmg_filename = dmg_filename
|
self.installer_filename = installer_filename
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
print(f"Installing Docker")
|
print(f"Installing Docker")
|
||||||
try:
|
|
||||||
# Mount the dmg
|
|
||||||
self.update_task_label.emit(f"Mounting Docker.dmg")
|
|
||||||
subprocess.run(["hdiutil", "attach", "-nobrowse", self.dmg_filename])
|
|
||||||
|
|
||||||
# Copy Docker.app to Applications
|
if platform.system() == "Darwin":
|
||||||
self.update_task_label.emit("Copying Docker into Applications")
|
try:
|
||||||
shutil.copytree("/Volumes/Docker/Docker.app", "/Applications/Docker.app")
|
# Mount the dmg
|
||||||
|
self.update_task_label.emit(f"Mounting Docker.dmg")
|
||||||
|
subprocess.run(
|
||||||
|
["hdiutil", "attach", "-nobrowse", self.installer_filename]
|
||||||
|
)
|
||||||
|
|
||||||
# Sync
|
# Copy Docker.app to Applications
|
||||||
self.update_task_label.emit("Syncing filesystem")
|
self.update_task_label.emit("Copying Docker into Applications")
|
||||||
subprocess.run(["sync"])
|
shutil.copytree(
|
||||||
|
"/Volumes/Docker/Docker.app", "/Applications/Docker.app"
|
||||||
|
)
|
||||||
|
|
||||||
# Wait, to prevent early crash
|
# Sync
|
||||||
time.sleep(1)
|
self.update_task_label.emit("Syncing filesystem")
|
||||||
|
subprocess.run(["sync"])
|
||||||
|
|
||||||
# Unmount the dmg
|
# Wait, to prevent early crash
|
||||||
self.update_task_label.emit(f"Unmounting /Volumes/Docker")
|
time.sleep(1)
|
||||||
subprocess.run(["hdiutil", "detach", "/Volumes/Docker"])
|
|
||||||
|
|
||||||
self.install_finished.emit()
|
# Unmount the dmg
|
||||||
|
self.update_task_label.emit(f"Unmounting /Volumes/Docker")
|
||||||
|
subprocess.run(["hdiutil", "detach", "/Volumes/Docker"])
|
||||||
|
|
||||||
|
self.install_finished.emit()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.install_failed.emit(str(e))
|
||||||
|
return
|
||||||
|
|
||||||
|
elif platform.system() == "Windows":
|
||||||
|
try:
|
||||||
|
# Run the installer
|
||||||
|
subprocess.run([self.installer_filename])
|
||||||
|
self.install_finished.emit()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.install_failed.emit(str(e))
|
||||||
|
return
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
self.install_failed.emit(str(e))
|
|
||||||
return
|
|
||||||
|
|
Loading…
Reference in a new issue