Compare commits

..

4 commits

Author SHA1 Message Date
63c9f4ed50
Merge 5bd51575fe into a6aa66f925 2025-03-03 15:06:33 +00:00
Alexis Métaireau
5bd51575fe
Display the {podman,docker} pull progress when installing a new image
The progressbars we see when using this same commands on the
command line doesn't seem to be passed to the python process here,
unfortunately.
2025-03-03 12:59:36 +01:00
Alexis Métaireau
052c35213d
Add a dangerzone-image store-signature CLI command
This can be useful when signatures are missing from the system, for an
already present image, and can be used as a way to fix user issues.
2025-03-03 12:58:27 +01:00
Alexis Métaireau
264f1d12a9
Replace the updater_check setting by updater_check_all
This new setting triggers the same user prompts, but the actual meaning of
it differs, since users will now be accepting to upgrade the container image
rather than just checking for new releases.

Changing the name of the setting will trigger this prompt for all users, effectively
ensuring they want their image to be automatically upgraded.
2025-03-01 15:50:32 +01:00
12 changed files with 127 additions and 90 deletions

View file

@ -3,13 +3,13 @@ import logging
import platform import platform
import shutil import shutil
import subprocess import subprocess
from typing import List, Optional, Tuple from typing import IO, Callable, List, Optional, Tuple
from . import errors from . import errors
from .util import get_resource_path, get_subprocess_startupinfo from .util import get_resource_path, get_subprocess_startupinfo
OLD_CONTAINER_NAME = "dangerzone.rocks/dangerzone" OLD_CONTAINER_NAME = "dangerzone.rocks/dangerzone"
CONTAINER_NAME = "ghcr.io/freedomofpress/dangerzone/dangerzone" CONTAINER_NAME = "ghcr.io/almet/dangerzone/dangerzone" # FIXME: Change this to the correct container name
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
@ -180,15 +180,26 @@ def get_image_id_by_digest(digest: str) -> str:
return process.stdout.decode().strip().split("\n")[0] return process.stdout.decode().strip().split("\n")[0]
def container_pull(image: str, manifest_digest: str): def container_pull(image: str, manifest_digest: str, callback: Callable):
"""Pull a container image from a registry.""" """Pull a container image from a registry."""
cmd = [get_runtime_name(), "pull", f"{image}@sha256:{manifest_digest}"] cmd = [get_runtime_name(), "pull", f"{image}@sha256:{manifest_digest}"]
try: process = subprocess.Popen(
subprocess_run(cmd, check=True) cmd,
except subprocess.CalledProcessError as e: stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
universal_newlines=True,
)
for line in process.stdout: # type: ignore
callback(line)
process.wait()
if process.returncode != 0:
raise errors.ContainerPullException( raise errors.ContainerPullException(
f"Could not pull the container image: {e}" f"Could not pull the container image: {process.returncode}"
) from e )
def get_local_image_digest(image: str) -> str: def get_local_image_digest(image: str) -> str:

View file

@ -1,3 +1,4 @@
import io
import logging import logging
import os import os
import platform import platform
@ -5,22 +6,24 @@ import tempfile
import typing import typing
from multiprocessing.pool import ThreadPool from multiprocessing.pool import ThreadPool
from pathlib import Path from pathlib import Path
from typing import List, Optional from typing import Callable, List, Optional
# FIXME: See https://github.com/freedomofpress/dangerzone/issues/320 for more details. # FIXME: See https://github.com/freedomofpress/dangerzone/issues/320 for more details.
if typing.TYPE_CHECKING: if typing.TYPE_CHECKING:
from PySide2 import QtCore, QtGui, QtSvg, QtWidgets from PySide2 import QtCore, QtGui, QtSvg, QtWidgets
from PySide2.QtCore import Qt from PySide2.QtCore import Qt
from PySide2.QtGui import QTextCursor
from PySide2.QtWidgets import QAction, QTextEdit from PySide2.QtWidgets import QAction, QTextEdit
else: else:
try: try:
from PySide6 import QtCore, QtGui, QtSvg, QtWidgets from PySide6 import QtCore, QtGui, QtSvg, QtWidgets
from PySide6.QtCore import Qt from PySide6.QtCore import Qt
from PySide6.QtGui import QAction from PySide6.QtGui import QAction, QTextCursor
from PySide6.QtWidgets import QTextEdit from PySide6.QtWidgets import QTextEdit
except ImportError: except ImportError:
from PySide2 import QtCore, QtGui, QtSvg, QtWidgets from PySide2 import QtCore, QtGui, QtSvg, QtWidgets
from PySide2.QtCore import Qt from PySide2.QtCore import Qt
from PySide2.QtGui import QTextCursor
from PySide2.QtWidgets import QAction, QTextEdit from PySide2.QtWidgets import QAction, QTextEdit
from .. import errors from .. import errors
@ -171,7 +174,7 @@ class MainWindow(QtWidgets.QMainWindow):
self.toggle_updates_action.triggered.connect(self.toggle_updates_triggered) self.toggle_updates_action.triggered.connect(self.toggle_updates_triggered)
self.toggle_updates_action.setCheckable(True) self.toggle_updates_action.setCheckable(True)
self.toggle_updates_action.setChecked( self.toggle_updates_action.setChecked(
bool(self.dangerzone.settings.get("updater_check")) bool(self.dangerzone.settings.get("updater_check_all"))
) )
# Add the "Exit" action # Add the "Exit" action
@ -284,7 +287,7 @@ class MainWindow(QtWidgets.QMainWindow):
def toggle_updates_triggered(self) -> None: def toggle_updates_triggered(self) -> None:
"""Change the underlying update check settings based on the user's choice.""" """Change the underlying update check settings based on the user's choice."""
check = self.toggle_updates_action.isChecked() check = self.toggle_updates_action.isChecked()
self.dangerzone.settings.set("updater_check", check) self.dangerzone.settings.set("updater_check_all", check)
self.dangerzone.settings.save() self.dangerzone.settings.save()
def handle_docker_desktop_version_check( def handle_docker_desktop_version_check(
@ -439,15 +442,21 @@ class MainWindow(QtWidgets.QMainWindow):
class InstallContainerThread(QtCore.QThread): class InstallContainerThread(QtCore.QThread):
finished = QtCore.Signal(str) finished = QtCore.Signal(str)
process_stdout = QtCore.Signal(str)
def __init__(self, dangerzone: DangerzoneGui) -> None: def __init__(
self, dangerzone: DangerzoneGui, callback: Optional[Callable] = None
) -> None:
super(InstallContainerThread, self).__init__() super(InstallContainerThread, self).__init__()
self.dangerzone = dangerzone self.dangerzone = dangerzone
def run(self) -> None: def run(self) -> None:
error = None error = None
try: try:
installed = self.dangerzone.isolation_provider.install() should_upgrade = self.dangerzone.settings.get("updater_check_all")
installed = self.dangerzone.isolation_provider.install(
should_upgrade=bool(should_upgrade), callback=self.process_stdout.emit
)
except Exception as e: except Exception as e:
log.error("Container installation problem") log.error("Container installation problem")
error = format_exception(e) error = format_exception(e)
@ -482,11 +491,20 @@ class TracebackWidget(QTextEdit):
# Enable copying # Enable copying
self.setTextInteractionFlags(Qt.TextSelectableByMouse) self.setTextInteractionFlags(Qt.TextSelectableByMouse)
self.current_output = ""
def set_content(self, error: Optional[str] = None) -> None: def set_content(self, error: Optional[str] = None) -> None:
if error: if error:
self.setPlainText(error) self.setPlainText(error)
self.setVisible(True) self.setVisible(True)
def process_output(self, line):
self.current_output += line
self.setText(self.current_output)
cursor = self.textCursor()
cursor.movePosition(QTextCursor.MoveOperation.End)
self.setTextCursor(cursor)
class WaitingWidgetContainer(WaitingWidget): class WaitingWidgetContainer(WaitingWidget):
# These are the possible states that the WaitingWidget can show. # These are the possible states that the WaitingWidget can show.
@ -613,8 +631,14 @@ class WaitingWidgetContainer(WaitingWidget):
"Installing the Dangerzone container image.<br><br>" "Installing the Dangerzone container image.<br><br>"
"This might take a few minutes..." "This might take a few minutes..."
) )
self.traceback.setVisible(True)
self.install_container_t = InstallContainerThread(self.dangerzone) self.install_container_t = InstallContainerThread(self.dangerzone)
self.install_container_t.finished.connect(self.installation_finished) self.install_container_t.finished.connect(self.installation_finished)
self.install_container_t.process_stdout.connect(
self.traceback.process_output
)
self.install_container_t.start() self.install_container_t.start()

View file

@ -106,7 +106,7 @@ class UpdaterThread(QtCore.QThread):
should_check = self.prompt_for_checks() should_check = self.prompt_for_checks()
if should_check is not None: if should_check is not None:
self.dangerzone.settings.set( self.dangerzone.settings.set(
"updater_check", should_check, autosave=True "updater_check_all", should_check, autosave=True
) )
return bool(should_check) return bool(should_check)

View file

@ -95,7 +95,7 @@ class IsolationProvider(ABC):
return self.debug or getattr(sys, "dangerzone_dev", False) return self.debug or getattr(sys, "dangerzone_dev", False)
@abstractmethod @abstractmethod
def install(self) -> bool: def install(self, should_upgrade: bool, callback: Callable) -> bool:
pass pass
def convert( def convert(

View file

@ -3,7 +3,7 @@ import os
import platform import platform
import shlex import shlex
import subprocess import subprocess
from typing import List, Tuple from typing import Callable, List, Tuple
from .. import container_utils, errors, updater from .. import container_utils, errors, updater
from ..document import Document from ..document import Document
@ -77,25 +77,38 @@ class Container(IsolationProvider):
return security_args return security_args
@staticmethod @staticmethod
def install() -> bool: def install(
should_upgrade: bool, callback: Callable, last_try: bool = False
) -> bool:
"""Check if an update is available and install it if necessary.""" """Check if an update is available and install it if necessary."""
# XXX Do this only if users have opted in to auto-updates if not should_upgrade:
log.debug("Skipping container upgrade check as requested by the settings")
if False: # Comment this for now, just as an exemple of this can be implemented else:
# # Load the image tarball into the container runtime.
update_available, image_digest = updater.is_update_available( update_available, image_digest = updater.is_update_available(
container_utils.CONTAINER_NAME container_utils.CONTAINER_NAME,
updater.DEFAULT_PUBKEY_LOCATION,
) )
if update_available and image_digest: if update_available and image_digest:
log.debug("Upgrading container image to %s", image_digest)
updater.upgrade_container_image( updater.upgrade_container_image(
container_utils.CONTAINER_NAME, container_utils.CONTAINER_NAME,
image_digest, image_digest,
updater.DEFAULT_PUBKEY_LOCATION, updater.DEFAULT_PUBKEY_LOCATION,
callback=callback,
) )
else:
updater.verify_local_image( log.debug("No update available for the container")
container_utils.CONTAINER_NAME, updater.DEFAULT_PUBKEY_LOCATION try:
) updater.verify_local_image(
container_utils.CONTAINER_NAME, updater.DEFAULT_PUBKEY_LOCATION
)
except errors.ImageNotPresentException:
if last_try:
raise
log.debug("Container image not found, trying to install it.")
return Container.install(
should_upgrade=should_upgrade, callback=callback, last_try=True
)
return True return True

View file

@ -1,7 +1,6 @@
import json import json
import logging import logging
import os import os
import platform
from typing import TYPE_CHECKING, Any, Dict from typing import TYPE_CHECKING, Any, Dict
from packaging import version from packaging import version
@ -38,7 +37,7 @@ class Settings:
"open": True, "open": True,
"open_app": None, "open_app": None,
"safe_extension": SAFE_EXTENSION, "safe_extension": SAFE_EXTENSION,
"updater_check": None, "updater_check_all": None,
"updater_last_check": None, # last check in UNIX epoch (secs since 1970) "updater_last_check": None, # last check in UNIX epoch (secs since 1970)
# FIXME: How to invalidate those if they change upstream? # FIXME: How to invalidate those if they change upstream?
"updater_latest_version": get_version(), "updater_latest_version": get_version(),

View file

@ -42,6 +42,17 @@ def upgrade(image: str, pubkey: str) -> None:
raise click.Abort() raise click.Abort()
@main.command()
@click.argument("image", default=DEFAULT_IMAGE_NAME)
@click.option("--pubkey", default=signatures.DEFAULT_PUBKEY_LOCATION)
def store_signatures(image: str, pubkey: str) -> None:
manifest_digest = registry.get_manifest_digest(image)
sigs = signatures.get_remote_signatures(image, manifest_digest)
signatures.verify_signatures(sigs, manifest_digest, pubkey)
signatures.store_signatures(sigs, manifest_digest, pubkey, update_logindex=False)
click.echo(f"✅ Signatures has been verified and stored locally")
@main.command() @main.command()
@click.argument("image_filename") @click.argument("image_filename")
@click.option("--pubkey", default=signatures.DEFAULT_PUBKEY_LOCATION) @click.option("--pubkey", default=signatures.DEFAULT_PUBKEY_LOCATION)

View file

@ -114,14 +114,14 @@ def should_check_for_releases(settings: Settings) -> bool:
* Thus we will always show to the user the cached info about the new version, * Thus we will always show to the user the cached info about the new version,
and won't make a new update check. and won't make a new update check.
""" """
check = settings.get("updater_check") check = settings.get("updater_check_all")
log.debug("Checking platform type") log.debug("Checking platform type")
# TODO: Disable updates for Homebrew installations. # TODO: Disable updates for Homebrew installations.
if platform.system() == "Linux" and not getattr(sys, "dangerzone_dev", False): if platform.system() == "Linux" and not getattr(sys, "dangerzone_dev", False):
log.debug("Running on Linux, disabling updates") log.debug("Running on Linux, disabling updates")
if not check: # if not overidden by user if not check: # if not overidden by user
settings.set("updater_check", False, autosave=True) settings.set("updater_check_all", False, autosave=True)
return False return False
log.debug("Checking if first run of Dangerzone") log.debug("Checking if first run of Dangerzone")

View file

@ -9,7 +9,7 @@ from hashlib import sha256
from io import BytesIO from io import BytesIO
from pathlib import Path from pathlib import Path
from tempfile import NamedTemporaryFile, TemporaryDirectory from tempfile import NamedTemporaryFile, TemporaryDirectory
from typing import Dict, List, Optional, Tuple from typing import Callable, Dict, List, Optional, Tuple
from .. import container_utils as runtime from .. import container_utils as runtime
from .. import errors as dzerrors from .. import errors as dzerrors
@ -369,7 +369,9 @@ def load_and_verify_signatures(
return signatures return signatures
def store_signatures(signatures: list[Dict], image_digest: str, pubkey: str) -> None: def store_signatures(
signatures: list[Dict], image_digest: str, pubkey: str, update_logindex: bool = True
) -> None:
""" """
Store signatures locally in the SIGNATURE_PATH folder, like this: Store signatures locally in the SIGNATURE_PATH folder, like this:
@ -414,7 +416,8 @@ def store_signatures(signatures: list[Dict], image_digest: str, pubkey: str) ->
) )
json.dump(signatures, f) json.dump(signatures, f)
write_log_index(get_log_index_from_signatures(signatures)) if update_logindex:
write_log_index(get_log_index_from_signatures(signatures))
def verify_local_image(image: str, pubkey: str) -> bool: def verify_local_image(image: str, pubkey: str) -> bool:
@ -478,14 +481,16 @@ def prepare_airgapped_archive(image_name: str, destination: str) -> None:
archive.add(tmpdir, arcname=".") archive.add(tmpdir, arcname=".")
def upgrade_container_image(image: str, manifest_digest: str, pubkey: str) -> str: def upgrade_container_image(
image: str, manifest_digest: str, pubkey: str, callback: Callable
) -> str:
"""Verify and upgrade the image to the latest, if signed.""" """Verify and upgrade the image to the latest, if signed."""
update_available, remote_digest = registry.is_new_remote_image_available(image) update_available, remote_digest = registry.is_new_remote_image_available(image)
if not update_available: if not update_available:
raise errors.ImageAlreadyUpToDate("The image is already up to date") raise errors.ImageAlreadyUpToDate("The image is already up to date")
signatures = check_signatures_and_logindex(image, remote_digest, pubkey) signatures = check_signatures_and_logindex(image, remote_digest, pubkey)
runtime.container_pull(image, manifest_digest) runtime.container_pull(image, manifest_digest, callback=callback)
# Store the signatures just now to avoid storing them unverified # Store the signatures just now to avoid storing them unverified
store_signatures(signatures, manifest_digest, pubkey) store_signatures(signatures, manifest_digest, pubkey)

View file

@ -11,8 +11,7 @@ https://github.com/freedomofpress/dangerzone/wiki/Updates
## Design overview ## Design overview
This feature introduces a hamburger icon that will be visible across almost all A hamburger icon is visible across almost all of the Dangerzone windows, and is used to notify the users when there are new releases.
of the Dangerzone windows. This will be used to notify the users about updates.
### First run ### First run
@ -21,8 +20,7 @@ _We detect it's the first time Dangerzone runs because the
Add the following keys in our `settings.json` file. Add the following keys in our `settings.json` file.
* `"updater_check": None`: Whether to check for updates or not. `None` means * `"updater_check_all": True`: Whether or not to check and apply independent container updates and check for new releases.
that the user has not decided yet, and is the default.
* `"updater_last_check": None`: The last time we checked for updates (in seconds * `"updater_last_check": None`: The last time we checked for updates (in seconds
from Unix epoch). None means that we haven't checked yet. from Unix epoch). None means that we haven't checked yet.
* `"updater_latest_version": "0.4.2"`: The latest version that the Dangerzone * `"updater_latest_version": "0.4.2"`: The latest version that the Dangerzone
@ -32,43 +30,19 @@ Add the following keys in our `settings.json` file.
* `"updater_errors: 0`: The number of update check errors that we have * `"updater_errors: 0`: The number of update check errors that we have
encountered in a row. encountered in a row.
Note: Previously, `"updater_check"` was used to determine if we should check for new releases, and has been replaced by `"updater_check_all"` when adding support for independent container updates.
* If on Linux, make `"updater_check": False`, since we normally have
other update channels for these platforms.
### Second run ### Second run
_We detect it's the second time Dangerzone runs because _We detect it's the second time Dangerzone runs because
`settings["updater_check"] is not None and settings["updater_last_check"] is `settings["updater_check_all"] is not None and settings["updater_last_check"] is
None`._ None`._
Before starting up the main window, show this window: Before starting up the main window, the user is prompted if they want to enable update checks.
* Title: Dangerzone Updater
* Body:
> Do you want Dangerzone to automatically check for updates?
>
> If you accept, Dangerzone will check the latest releases page in github.com
> on startup. Otherwise it will make no network requests and won't inform you
> about new releases.
>
> If you prefer another way of getting notified about new releases, we suggest adding
> to your RSS reader our [Mastodon feed](https://fosstodon.org/@dangerzone.rss). For more information
> about updates, check [this webpage](https://github.com/freedomofpress/dangerzone/wiki/Updates).
* Buttons:
- Check Automaticaly: Store `settings["updater_check"] = True`
- Don't Check: Store `settings["updater_check"] = False`
Note:
* Users will be able to change their choice from the hamburger menu, which will
contain an entry called "Check for updates", that users can check and uncheck.
### Subsequent runs ### Subsequent runs
_We perform the following only if `settings["updater_check"] == True`._ _We perform the following only if `settings["updater_check_all"] == True`._
1. Spawn a new thread so that we don't block the main window. 1. Spawn a new thread so that we don't block the main window.
2. Check if we have cached information about a release (version and changelog). 2. Check if we have cached information about a release (version and changelog).

View file

@ -97,7 +97,7 @@ def test_default_menu(
updater: UpdaterThread, updater: UpdaterThread,
) -> None: ) -> None:
"""Check that the default menu entries are in order.""" """Check that the default menu entries are in order."""
updater.dangerzone.settings.set("updater_check", True) updater.dangerzone.settings.set("updater_check_all", True)
window = MainWindow(updater.dangerzone) window = MainWindow(updater.dangerzone)
menu_actions = window.hamburger_button.menu().actions() menu_actions = window.hamburger_button.menu().actions()
@ -115,7 +115,7 @@ def test_default_menu(
toggle_updates_action.trigger() toggle_updates_action.trigger()
assert not toggle_updates_action.isChecked() assert not toggle_updates_action.isChecked()
assert updater.dangerzone.settings.get("updater_check") is False assert updater.dangerzone.settings.get("updater_check_all") is False
def test_no_update( def test_no_update(
@ -128,12 +128,12 @@ def test_no_update(
# Check that when no update is detected, e.g., due to update cooldown, an empty # Check that when no update is detected, e.g., due to update cooldown, an empty
# report is received that does not affect the menu entries. # report is received that does not affect the menu entries.
curtime = int(time.time()) curtime = int(time.time())
updater.dangerzone.settings.set("updater_check", True) updater.dangerzone.settings.set("updater_check_all", True)
updater.dangerzone.settings.set("updater_errors", 9) updater.dangerzone.settings.set("updater_errors", 9)
updater.dangerzone.settings.set("updater_last_check", curtime) updater.dangerzone.settings.set("updater_last_check", curtime)
expected_settings = default_updater_settings() expected_settings = default_updater_settings()
expected_settings["updater_check"] = True expected_settings["updater_check_all"] = True
expected_settings["updater_errors"] = 0 # errors must be cleared expected_settings["updater_errors"] = 0 # errors must be cleared
expected_settings["updater_last_check"] = curtime expected_settings["updater_last_check"] = curtime
@ -166,7 +166,7 @@ def test_update_detected(
) -> None: ) -> None:
"""Test that a newly detected version leads to a notification to the user.""" """Test that a newly detected version leads to a notification to the user."""
qt_updater.dangerzone.settings.set("updater_check", True) qt_updater.dangerzone.settings.set("updater_check_all", True)
qt_updater.dangerzone.settings.set("updater_last_check", 0) qt_updater.dangerzone.settings.set("updater_last_check", 0)
qt_updater.dangerzone.settings.set("updater_errors", 9) qt_updater.dangerzone.settings.set("updater_errors", 9)
@ -198,7 +198,7 @@ def test_update_detected(
# Check that the settings have been updated properly. # Check that the settings have been updated properly.
expected_settings = default_updater_settings() expected_settings = default_updater_settings()
expected_settings["updater_check"] = True expected_settings["updater_check_all"] = True
expected_settings["updater_last_check"] = qt_updater.dangerzone.settings.get( expected_settings["updater_last_check"] = qt_updater.dangerzone.settings.get(
"updater_last_check" "updater_last_check"
) )
@ -279,7 +279,7 @@ def test_update_error(
) -> None: ) -> None:
"""Test that an error during an update check leads to a notification to the user.""" """Test that an error during an update check leads to a notification to the user."""
# Test 1 - Check that the first error does not notify the user. # Test 1 - Check that the first error does not notify the user.
qt_updater.dangerzone.settings.set("updater_check", True) qt_updater.dangerzone.settings.set("updater_check_all", True)
qt_updater.dangerzone.settings.set("updater_last_check", 0) qt_updater.dangerzone.settings.set("updater_last_check", 0)
qt_updater.dangerzone.settings.set("updater_errors", 0) qt_updater.dangerzone.settings.set("updater_errors", 0)
@ -306,7 +306,7 @@ def test_update_error(
# Check that the settings have been updated properly. # Check that the settings have been updated properly.
expected_settings = default_updater_settings() expected_settings = default_updater_settings()
expected_settings["updater_check"] = True expected_settings["updater_check_all"] = True
expected_settings["updater_last_check"] = qt_updater.dangerzone.settings.get( expected_settings["updater_last_check"] = qt_updater.dangerzone.settings.get(
"updater_last_check" "updater_last_check"
) )

View file

@ -110,7 +110,7 @@ def test_post_0_4_2_settings(
def test_linux_no_check(updater: UpdaterThread, monkeypatch: MonkeyPatch) -> None: def test_linux_no_check(updater: UpdaterThread, monkeypatch: MonkeyPatch) -> None:
"""Ensure that Dangerzone on Linux does not make any update check.""" """Ensure that Dangerzone on Linux does not make any update check."""
expected_settings = default_updater_settings() expected_settings = default_updater_settings()
expected_settings["updater_check"] = False expected_settings["updater_check_all"] = False
expected_settings["updater_last_check"] = None expected_settings["updater_last_check"] = None
# XXX: Simulate Dangerzone installed via package manager. # XXX: Simulate Dangerzone installed via package manager.
@ -130,7 +130,7 @@ def test_user_prompts(
# When Dangerzone runs for the first time, users should not be asked to enable # When Dangerzone runs for the first time, users should not be asked to enable
# updates. # updates.
expected_settings = default_updater_settings() expected_settings = default_updater_settings()
expected_settings["updater_check"] = None expected_settings["updater_check_all"] = None
expected_settings["updater_last_check"] = 0 expected_settings["updater_last_check"] = 0
assert updater.should_check_for_updates() is False assert updater.should_check_for_updates() is False
assert settings.get_updater_settings() == expected_settings assert settings.get_updater_settings() == expected_settings
@ -145,14 +145,14 @@ def test_user_prompts(
# Check disabling update checks. # Check disabling update checks.
prompt_mock().launch.return_value = False # type: ignore [attr-defined] prompt_mock().launch.return_value = False # type: ignore [attr-defined]
expected_settings["updater_check"] = False expected_settings["updater_check_all"] = False
assert updater.should_check_for_updates() is False assert updater.should_check_for_updates() is False
assert settings.get_updater_settings() == expected_settings assert settings.get_updater_settings() == expected_settings
# Reset the "updater_check" field and check enabling update checks. # Reset the "updater_check_all" field and check enabling update checks.
settings.set("updater_check", None) settings.set("updater_check_all", None)
prompt_mock().launch.return_value = True # type: ignore [attr-defined] prompt_mock().launch.return_value = True # type: ignore [attr-defined]
expected_settings["updater_check"] = True expected_settings["updater_check_all"] = True
assert updater.should_check_for_updates() is True assert updater.should_check_for_updates() is True
assert settings.get_updater_settings() == expected_settings assert settings.get_updater_settings() == expected_settings
@ -162,7 +162,7 @@ def test_user_prompts(
# checks. # checks.
prompt_mock().side_effect = RuntimeError("Should not be called") # type: ignore [attr-defined] prompt_mock().side_effect = RuntimeError("Should not be called") # type: ignore [attr-defined]
for check in [True, False]: for check in [True, False]:
settings.set("updater_check", check) settings.set("updater_check_all", check)
assert updater.should_check_for_updates() == check assert updater.should_check_for_updates() == check
@ -217,7 +217,7 @@ def test_update_checks_cooldown(updater: UpdaterThread, mocker: MockerFixture) -
"""Make sure Dangerzone only checks for updates every X hours""" """Make sure Dangerzone only checks for updates every X hours"""
settings = updater.dangerzone.settings settings = updater.dangerzone.settings
settings.set("updater_check", True) settings.set("updater_check_all", True)
settings.set("updater_last_check", 0) settings.set("updater_last_check", 0)
# Mock some functions before the tests start # Mock some functions before the tests start
@ -401,7 +401,7 @@ def test_update_check_prompt(
# Test 2 - Check that when the user chooses to enable update checks, we # Test 2 - Check that when the user chooses to enable update checks, we
# store that decision in the settings. # store that decision in the settings.
settings.set("updater_check", None, autosave=True) settings.set("updater_check_all", None, autosave=True)
def click_ok() -> None: def click_ok() -> None:
dialog = qt_updater.dangerzone.app.activeWindow() dialog = qt_updater.dangerzone.app.activeWindow()
@ -411,11 +411,11 @@ def test_update_check_prompt(
res = qt_updater.should_check_for_updates() res = qt_updater.should_check_for_updates()
assert res is True assert res is True
assert settings.get("updater_check") is True assert settings.get("updater_check_all") is True
# Test 3 - Same as the previous test, but check that clicking on cancel stores the # Test 3 - Same as the previous test, but check that clicking on cancel stores the
# opposite decision. # opposite decision.
settings.set("updater_check", None) # type: ignore [unreachable] settings.set("updater_check_all", None) # type: ignore [unreachable]
def click_cancel() -> None: def click_cancel() -> None:
dialog = qt_updater.dangerzone.app.activeWindow() dialog = qt_updater.dangerzone.app.activeWindow()
@ -425,11 +425,11 @@ def test_update_check_prompt(
res = qt_updater.should_check_for_updates() res = qt_updater.should_check_for_updates()
assert res is False assert res is False
assert settings.get("updater_check") is False assert settings.get("updater_check_all") is False
# Test 4 - Same as the previous test, but check that clicking on "X" does not store # Test 4 - Same as the previous test, but check that clicking on "X" does not store
# any decision. # any decision.
settings.set("updater_check", None, autosave=True) settings.set("updater_check_all", None, autosave=True)
def click_x() -> None: def click_x() -> None:
dialog = qt_updater.dangerzone.app.activeWindow() dialog = qt_updater.dangerzone.app.activeWindow()
@ -439,4 +439,4 @@ def test_update_check_prompt(
res = qt_updater.should_check_for_updates() res = qt_updater.should_check_for_updates()
assert res is False assert res is False
assert settings.get("updater_check") is None assert settings.get("updater_check_all") is None