mirror of
https://github.com/freedomofpress/dangerzone.git
synced 2025-04-28 18:02:38 +02:00
In macOS, download Docker Desktop for the user if it is not installed
This commit is contained in:
parent
70889646be
commit
abb56b68ac
6 changed files with 209 additions and 31 deletions
1
Pipfile
1
Pipfile
|
@ -10,6 +10,7 @@ appdirs = "*"
|
|||
pyxdg = {version = "*",platform_system = "== 'Linux'"}
|
||||
pyobjc-core = {version = "*",platform_system = "== 'Darwin'"}
|
||||
pyobjc-framework-launchservices = {version = "*",platform_system = "== 'Darwin'"}
|
||||
requests = "*"
|
||||
|
||||
[dev-packages]
|
||||
black = "*"
|
||||
|
|
94
Pipfile.lock
generated
94
Pipfile.lock
generated
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "0ec85e528a50d2e61151ca453c5d6fc217b1695fee0673e86db6eea60491de02"
|
||||
"sha256": "695116394343f7849640651aff1c6957762111bbe2905e3e4d4473be4d6dfb43"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
|
@ -24,6 +24,20 @@
|
|||
"index": "pypi",
|
||||
"version": "==1.4.3"
|
||||
},
|
||||
"certifi": {
|
||||
"hashes": [
|
||||
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3",
|
||||
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f"
|
||||
],
|
||||
"version": "==2019.11.28"
|
||||
},
|
||||
"chardet": {
|
||||
"hashes": [
|
||||
"sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae",
|
||||
"sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"
|
||||
],
|
||||
"version": "==3.0.4"
|
||||
},
|
||||
"click": {
|
||||
"hashes": [
|
||||
"sha256:2335065e6395b9e67ca716de5f7526736bfa6ceead690adf616d925bdc622b13",
|
||||
|
@ -32,6 +46,13 @@
|
|||
"index": "pypi",
|
||||
"version": "==7.0"
|
||||
},
|
||||
"idna": {
|
||||
"hashes": [
|
||||
"sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407",
|
||||
"sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c"
|
||||
],
|
||||
"version": "==2.8"
|
||||
},
|
||||
"pyobjc-core": {
|
||||
"hashes": [
|
||||
"sha256:1a0fbf012fb575e0adf8c18cfd4453e657cc2c0deb2660c529bf524ba4c9149a",
|
||||
|
@ -81,36 +102,36 @@
|
|||
},
|
||||
"pyqt5": {
|
||||
"hashes": [
|
||||
"sha256:2b79209aa6e4688f6ac46e6d2694236dcf91db5f3a87270150d0f82082e3d360",
|
||||
"sha256:2d94ec761fb656707050c68b41958e3a9f755bb1df96c064470f4096d2899e32",
|
||||
"sha256:2f230f2dbd767099de7a0cb915abdf0cbc3256a0b5bb910eb09b99117db7a65b",
|
||||
"sha256:3d6e315e6e2d6489a2e1e0148d00e784e277c6590c189227d6060f15b9be690a",
|
||||
"sha256:812233bd155735377e2e9c7eea7a28815f357440334db51788d941e2a8b62f64",
|
||||
"sha256:be10fa95e6bdc9cad616ebf368c51b3f5748138b2b3a600cf7c4f80b78cb9852"
|
||||
"sha256:31b142a868152d60c6323e0527edb692fdf05fd7cb4fe2fe9ce07d1ce560221a",
|
||||
"sha256:713b9a201f5e7b2fca8691373e5d5c8c2552a51d87ca9ffbb1461e34e3241211",
|
||||
"sha256:a0bfe9fd718bca4de3e33000347e048f73126b6dc46530eb020b0251a638ee9d"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==5.14.1"
|
||||
},
|
||||
"pyqt5-sip": {
|
||||
"hashes": [
|
||||
"sha256:02d94786bada670ab17a2b62ce95b3cf8e3b40c99d36007593a6334d551840bb",
|
||||
"sha256:06bc66b50556fb949f14875a4c224423dbf03f972497ccb883fb19b7b7c3b346",
|
||||
"sha256:091fbbe10a7aebadc0e8897a9449cda08d3c3f663460d812eca3001ca1ed3526",
|
||||
"sha256:0a067ade558befe4d46335b13d8b602b5044363bfd601419b556d4ec659bca18",
|
||||
"sha256:1910c1cb5a388d4e59ebb2895d7015f360f3f6eeb1700e7e33e866c53137eb9e",
|
||||
"sha256:1c7ad791ec86247f35243bbbdd29cd59989afbe0ab678e0a41211f4407f21dd8",
|
||||
"sha256:3c330ff1f70b3eaa6f63dce9274df996dffea82ad9726aa8e3d6cbe38e986b2f",
|
||||
"sha256:482a910fa73ee0e36c258d7646ef38f8061774bbc1765a7da68c65056b573341",
|
||||
"sha256:7695dfafb4f5549ce1290ae643d6508dfc2646a9003c989218be3ce42a1aa422",
|
||||
"sha256:8274ed50f4ffbe91d0f4cc5454394631edfecd75dc327aa01be8bc5818a57e88",
|
||||
"sha256:9047d887d97663790d811ac4e0d2e895f1bf2ecac4041691487de40c30239480",
|
||||
"sha256:9f6ab1417ecfa6c1ce6ce941e0cebc03e3ec9cd9925058043229a5f003ae5e40",
|
||||
"sha256:b43ba2f18999d41c3df72f590348152e14cd4f6dcea2058c734d688dfb1ec61f",
|
||||
"sha256:c3ab9ea1bc3f4ce8c57ebc66fb25cd044ef92ed1ca2afa3729854ecc59658905",
|
||||
"sha256:da69ba17f6ece9a85617743cb19de689f2d63025bf8001e2facee2ec9bcff18f",
|
||||
"sha256:ef3c7a0bf78674b0dda86ff5809d8495019903a096c128e1f160984b37848f73",
|
||||
"sha256:fabff832046643cdb93920ddaa8f77344df90768930fbe6bb33d211c4dcd0b5e"
|
||||
"sha256:1115728644bbadcde5fc8a16e7918bd31915a42dd6fb36b10d4afb78c582753e",
|
||||
"sha256:1f4289276d355b6521dc2cc956189315da6f13adfb6bbab8f25ebd15e3bce1d4",
|
||||
"sha256:288c6dc18a8d6a20981c07b715b5695d9b66880778565f3792bc6e38f14f20fb",
|
||||
"sha256:3f665376d9e52faa9855c3736a66ce6d825f85c86d7774d3c393f09da23f4f86",
|
||||
"sha256:6b4860c4305980db509415d0af802f111d15f92016c9422eb753bc8883463456",
|
||||
"sha256:7ffa39763097f64de129cf5cc770a651c3f65d2466b4fe05bef2bd2efbaa38e6",
|
||||
"sha256:8a18e6f45d482ddfe381789979d09ee13aa6450caa3a0476503891bccb3ac709",
|
||||
"sha256:8da842d3d7bf8931d1093105fb92702276b6dbb7e801abbaaa869405d616171a",
|
||||
"sha256:b42021229424aa44e99b3b49520b799fd64ff6ae8b53f79f903bbd85719a28e4",
|
||||
"sha256:b5b4906445fe980aee76f20400116b6904bf5f30d0767489c13370e42a764020",
|
||||
"sha256:c1e730a9eb2ec3869ed5d81b0f99f6e2460fb4d77750444c0ec183b771d798f7",
|
||||
"sha256:cbeeae6b45234a1654657f79943f8bccd3d14b4e7496746c62cf6fbce69442c7",
|
||||
"sha256:d46b0f8effc554de52a1466b1bd80e5cb4bce635a75ac4e7ad6247c965dec5b9",
|
||||
"sha256:e28c3abc9b62a1b7e796891648b9f14f8167b31c8e7990fae79654777252bb4d",
|
||||
"sha256:e6078f5ee7d31c102910d0c277a110e1c2a20a3fc88cd017a39e170120586d3f",
|
||||
"sha256:ee1a12f09d5af2304273bfd2f6b43835c1467d5ed501a6c95f5405637fa7750a",
|
||||
"sha256:f314f31f5fd39b06897f013f425137e511d45967150eb4e424a363d8138521c6"
|
||||
],
|
||||
"version": "==12.7.0"
|
||||
"version": "==12.7.1"
|
||||
},
|
||||
"pyxdg": {
|
||||
"hashes": [
|
||||
|
@ -120,15 +141,30 @@
|
|||
"index": "pypi",
|
||||
"markers": "platform_system == 'Linux'",
|
||||
"version": "==0.26"
|
||||
},
|
||||
"requests": {
|
||||
"hashes": [
|
||||
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
|
||||
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==2.22.0"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc",
|
||||
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc"
|
||||
],
|
||||
"version": "==1.25.8"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
"altgraph": {
|
||||
"hashes": [
|
||||
"sha256:d6814989f242b2b43025cba7161fc1b8fb487a62cd49c49245d6fd01c18ac997",
|
||||
"sha256:ddf5320017147ba7b810198e0b6619bd7b5563aa034da388cea8546b877f9b0c"
|
||||
"sha256:1f05a47122542f97028caf78775a095fbe6a2699b5089de8477eb583167d69aa",
|
||||
"sha256:c623e5f3408ca61d4016f23a681b9adb100802ca3e3da5e718915a9e4052cebe"
|
||||
],
|
||||
"version": "==0.16.1"
|
||||
"version": "==0.17"
|
||||
},
|
||||
"appdirs": {
|
||||
"hashes": [
|
||||
|
@ -163,10 +199,10 @@
|
|||
},
|
||||
"macholib": {
|
||||
"hashes": [
|
||||
"sha256:b71afea242d5ad4caacbdb79d80e75815d033fbc30f45954b2f3397f39683fd6",
|
||||
"sha256:c72bda118afe7799570fcb4114315d5c9c5416e48eacf1198da39b4d77201559"
|
||||
"sha256:0c436bc847e7b1d9bda0560351bf76d7caf930fb585a828d13608839ef42c432",
|
||||
"sha256:c500f02867515e6c60a27875b408920d18332ddf96b4035ef03beddd782d4281"
|
||||
],
|
||||
"version": "==1.13"
|
||||
"version": "==1.14"
|
||||
},
|
||||
"pathspec": {
|
||||
"hashes": [
|
||||
|
|
|
@ -2,10 +2,12 @@ from PyQt5 import QtCore, QtWidgets
|
|||
import os
|
||||
import sys
|
||||
import signal
|
||||
import platform
|
||||
import click
|
||||
|
||||
from .common import Common
|
||||
from .main_window import MainWindow
|
||||
from .docker_installer import is_docker_installed, DockerInstaller
|
||||
|
||||
dangerzone_version = "0.1.0"
|
||||
|
||||
|
@ -25,6 +27,14 @@ def main(filename):
|
|||
# Common object
|
||||
common = Common(app)
|
||||
|
||||
# See if we need to install Docker...
|
||||
if platform.system() == "Darwin" and not is_docker_installed(common):
|
||||
print("Docker is not installed!")
|
||||
docker_installer = DockerInstaller(common)
|
||||
if docker_installer.launch():
|
||||
main(filename)
|
||||
return
|
||||
|
||||
# Main window
|
||||
main_window = MainWindow(common)
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ class Common(object):
|
|||
|
||||
# Container runtime
|
||||
if platform.system() == "Darwin":
|
||||
self.container_runtime = "docker"
|
||||
self.container_runtime = "/usr/local/bin/docker"
|
||||
else:
|
||||
self.container_runtime = "podman"
|
||||
|
||||
|
|
132
dangerzone/docker_installer.py
Normal file
132
dangerzone/docker_installer.py
Normal file
|
@ -0,0 +1,132 @@
|
|||
import os
|
||||
import stat
|
||||
import requests
|
||||
import tempfile
|
||||
from PyQt5 import QtCore, QtGui, QtWidgets
|
||||
|
||||
|
||||
def is_docker_installed(common):
|
||||
# Soes the docker binary exist?
|
||||
if os.path.isdir("/Applications/Docker.app") and os.path.exists(
|
||||
common.container_runtime
|
||||
):
|
||||
# Is it executable?
|
||||
st = os.stat(common.container_runtime)
|
||||
return bool(st.st_mode & stat.S_IXOTH)
|
||||
return False
|
||||
|
||||
|
||||
class DockerInstaller(QtWidgets.QDialog):
|
||||
def __init__(self, common):
|
||||
super(DockerInstaller, self).__init__()
|
||||
self.common = common
|
||||
|
||||
self.setWindowTitle("dangerzone")
|
||||
self.setWindowIcon(QtGui.QIcon(self.common.get_resource_path("logo.png")))
|
||||
|
||||
label = QtWidgets.QLabel("Dangerzone for macOS requires Docker")
|
||||
label.setStyleSheet("QLabel { font-weight: bold; }")
|
||||
|
||||
self.task_label = QtWidgets.QLabel()
|
||||
|
||||
self.progress = QtWidgets.QProgressBar()
|
||||
self.progress.setMinimum(0)
|
||||
|
||||
self.install_button = QtWidgets.QPushButton("Install Docker")
|
||||
self.install_button.setStyleSheet("QPushButton { font-weight: bold; }")
|
||||
self.install_button.clicked.connect(self.install_clicked)
|
||||
self.install_button.hide()
|
||||
self.launch_button = QtWidgets.QPushButton("Launch Docker")
|
||||
self.launch_button.setStyleSheet("QPushButton { font-weight: bold; }")
|
||||
self.launch_button.clicked.connect(self.launch_clicked)
|
||||
self.launch_button.hide()
|
||||
cancel_button = QtWidgets.QPushButton("Cancel")
|
||||
cancel_button.clicked.connect(self.cancel_clicked)
|
||||
|
||||
buttons_layout = QtWidgets.QHBoxLayout()
|
||||
buttons_layout.addStretch()
|
||||
buttons_layout.addWidget(self.install_button)
|
||||
buttons_layout.addWidget(self.launch_button)
|
||||
buttons_layout.addWidget(cancel_button)
|
||||
buttons_layout.addStretch()
|
||||
|
||||
layout = QtWidgets.QVBoxLayout()
|
||||
layout.addWidget(label)
|
||||
layout.addWidget(self.task_label)
|
||||
layout.addWidget(self.progress)
|
||||
layout.addLayout(buttons_layout)
|
||||
self.setLayout(layout)
|
||||
|
||||
# Threads
|
||||
self.download_t = None
|
||||
|
||||
def cancel_clicked(self):
|
||||
if self.download_t:
|
||||
self.download_t.terminate()
|
||||
self.reject()
|
||||
|
||||
def install_clicked(self):
|
||||
print("Install clicked")
|
||||
|
||||
def launch_clicked(self):
|
||||
print("Launch clicked")
|
||||
|
||||
def update_progress(self, value, maximum):
|
||||
self.progress.setMaximum(maximum)
|
||||
self.progress.setValue(value)
|
||||
|
||||
def download_finished(self):
|
||||
self.task_label.setText("Finished downloading Docker")
|
||||
self.download_t = None
|
||||
self.install_button.show()
|
||||
|
||||
def download_failed(self, status_code):
|
||||
print(f"Download failed: status code {status_code}")
|
||||
self.download_t = None
|
||||
|
||||
def download(self):
|
||||
self.task_label.setText("Downloading Docker")
|
||||
self.download_t = Downloader()
|
||||
self.download_t.download_finished.connect(self.download_finished)
|
||||
self.download_t.download_failed.connect(self.download_failed)
|
||||
self.download_t.update_progress.connect(self.update_progress)
|
||||
self.download_t.start()
|
||||
|
||||
def install(self):
|
||||
pass
|
||||
|
||||
def launch(self):
|
||||
self.download()
|
||||
return self.exec_() == QtWidgets.QDialog.Accepted
|
||||
|
||||
|
||||
class Downloader(QtCore.QThread):
|
||||
download_finished = QtCore.pyqtSignal()
|
||||
download_failed = QtCore.pyqtSignal(int)
|
||||
update_progress = QtCore.pyqtSignal(int, int)
|
||||
|
||||
def __init__(self):
|
||||
super(Downloader, self).__init__()
|
||||
self.tmp_dir = tempfile.TemporaryDirectory(prefix="/tmp/dangerzone-docker-")
|
||||
self.dmg_filename = os.path.join(self.tmp_dir.name, "Docker.dmg")
|
||||
|
||||
def run(self):
|
||||
print(f"Downloading docker to {self.dmg_filename}")
|
||||
with requests.get(
|
||||
"https://download.docker.com/mac/stable/Docker.dmg", stream=True
|
||||
) as r:
|
||||
if r.status_code != 200:
|
||||
self.download_failed.emit(r.status_code)
|
||||
return
|
||||
total_bytes = int(r.headers.get("content-length"))
|
||||
downloaded_bytes = 0
|
||||
|
||||
with open(self.dmg_filename, "wb") as f:
|
||||
for chunk in r.iter_content(chunk_size=8192):
|
||||
if chunk: # filter out keep-alive new chunks
|
||||
downloaded_bytes += f.write(chunk)
|
||||
|
||||
self.update_progress.emit(downloaded_bytes, total_bytes)
|
||||
|
||||
self.download_finished.emit()
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import subprocess
|
||||
import time
|
||||
import tempfile
|
||||
import os
|
||||
import pipes
|
||||
import platform
|
||||
|
|
Loading…
Reference in a new issue