Skip to content

Commit

Permalink
Merge branch 'borgbase:master' into issue1869
Browse files Browse the repository at this point in the history
  • Loading branch information
SAMAD101 authored Feb 20, 2024
2 parents 3bffd79 + 472c7c8 commit 259c6d5
Show file tree
Hide file tree
Showing 27 changed files with 216 additions and 87 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_form.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: "Bug Report Form"
description: "Report a bug or a similiar issue."
description: "Report a bug or a similar issue."
body:
- type: markdown
attributes:
Expand Down
4 changes: 2 additions & 2 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: Bug Report
about: Report a bug or a similiar issue - the classic way
about: Report a bug or a similar issue - the classic way
title: ''
labels: ''
assignees: ''
Expand All @@ -18,7 +18,7 @@ If you want to suggest a feature or have any other question, please use our
#### Description

<!-- Description
Please decribe your issue and its context in a clear and concise way.
Please describe your issue and its context in a clear and concise way.
Please try to reproduce the issue and provide the steps to reproduce it.
-->

Expand Down
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
- [ ] All new and existing tests passed.


*I provide my contribution under the terms of the [license](./../../LICENSE.txt) of this repository and I affirm the [Developer Certificate of Origin][dco].*
*I provide my contribution under the terms of the [license](./../LICENSE.txt) of this repository and I affirm the [Developer Certificate of Origin][dco].*

[dco]: https://developercertificate.org/

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

Vorta is a backup client for macOS and Linux desktops. It integrates the mighty [BorgBackup](https://borgbackup.readthedocs.io) with your desktop environment to protect your data from disk failure, ransomware and theft.

![](https://files.qmax.us/vorta/screencast-8-small.gif)
https://github.com/m3nu/vorta/assets/3916435/a622a148-5373-4ae0-87bc-4ca1d6f6202e

## Why is this great? 🤩

Expand Down
4 changes: 2 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@
@nox.parametrize("borgbackup", supported_borgbackup_versions)
def run_tests(session, borgbackup):
# install borgbackup
if (sys.platform == 'darwin'):
if sys.platform == 'darwin':
# in macOS there's currently no fuse package which works with borgbackup directly
session.install(f"borgbackup=={borgbackup}")
elif (borgbackup == "1.1.18"):
elif borgbackup == "1.1.18":
# borgbackup 1.1.18 doesn't support pyfuse3
session.install("llfuse")
session.install(f"borgbackup[llfuse]=={borgbackup}")
Expand Down
18 changes: 9 additions & 9 deletions package/fix_app_qt_folder_names_for_codesign.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ def create_symlink(folder: Path) -> None:
"""Create the appropriate symlink in the MacOS folder
pointing to the Resources folder.
"""
sibbling = Path(str(folder).replace("MacOS", ""))
sibling = Path(str(folder).replace("MacOS", ""))

# PyQt6/Qt/qml/QtQml/Models.2
root = str(sibbling).partition("Contents")[2].lstrip("/")
root = str(sibling).partition("Contents")[2].lstrip("/")
# ../../../../
backward = "../" * (root.count("/") + 1)
# ../../../../Resources/PyQt6/Qt/qml/QtQml/Models.2
Expand All @@ -41,7 +41,7 @@ def fix_dll(dll: Path) -> None:

def match_func(pth: str) -> Optional[str]:
"""Callback function for MachO.rewriteLoadCommands() that is
called on every lookup path setted in the DLL headers.
called on every lookup path set in the DLL headers.
By returning None for system libraries, it changes nothing.
Else we return a relative path pointing to the good file
in the MacOS folder.
Expand Down Expand Up @@ -73,7 +73,7 @@ def find_problematic_folders(folder: Path) -> Generator[Path, None, None]:
"""Recursively yields problematic folders (containing a dot in their name)."""
for path in folder.iterdir():
if not path.is_dir() or path.is_symlink():
# Skip simlinks as they are allowed (even with a dot)
# Skip symlinks as they are allowed (even with a dot)
continue
if "." in path.name:
yield path
Expand All @@ -83,18 +83,18 @@ def find_problematic_folders(folder: Path) -> Generator[Path, None, None]:

def move_contents_to_resources(folder: Path) -> Generator[Path, None, None]:
"""Recursively move any non symlink file from a problematic folder
to the sibbling one in Resources.
to the sibling one in Resources.
"""
for path in folder.iterdir():
if path.is_symlink():
continue
if path.name == "qml":
yield from move_contents_to_resources(path)
else:
sibbling = Path(str(path).replace("MacOS", "Resources"))
sibbling.parent.mkdir(parents=True, exist_ok=True)
shutil.move(path, sibbling)
yield sibbling
sibling = Path(str(path).replace("MacOS", "Resources"))
sibling.parent.mkdir(parents=True, exist_ok=True)
shutil.move(path, sibling)
yield sibling


def main(args: List[str]) -> int:
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ install_requires =
pyobjc-core < 10; sys_platform == 'darwin'
pyobjc-framework-Cocoa < 10; sys_platform == 'darwin'
pyobjc-framework-LaunchServices < 10; sys_platform == 'darwin'
pyobjc-framework-CoreWLAN < 10; sys_platform == 'darwin'
tests_require =
pytest
pytest-qt
Expand Down
2 changes: 1 addition & 1 deletion src/vorta/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '0.9.1-beta3'
__version__ = '0.9.1'
7 changes: 1 addition & 6 deletions src/vorta/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,6 @@ def check_failed_response(self, result: Dict[str, Any]):
Displays a `QMessageBox` with an error message depending on the
return code of the `BorgJob`.
Parameters
----------
repo_url : str
The url of the repo of concern
"""
# extract data from the params for the borg job
repo_url = result['params']['repo_url']
Expand Down Expand Up @@ -344,7 +339,7 @@ def check_failed_response(self, result: Dict[str, Any]):
elif returncode > 128:
# 128+N - killed by signal N (e.g. 137 == kill -9)
signal = returncode - 128
text = self.tr('Repository data check for repo was killed by signal %s.') % (signal)
text = self.tr('Repository data check for repo was killed by signal %s.') % signal
infotext = self.tr('The process running the check job got a kill signal. Try again.')
else:
# Real error
Expand Down
4 changes: 2 additions & 2 deletions src/vorta/assets/UI/abouttab.ui
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/borgbase/vorta&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0984e3;&quot;&gt;Click here&lt;/span&gt;&lt;/a&gt; for view Git repo.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/borgbase/vorta&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0984e3;&quot;&gt;Click here&lt;/span&gt;&lt;/a&gt; to view Git repo.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
Expand Down Expand Up @@ -241,7 +241,7 @@
<number>20</number>
</property>
<item>
<widget class="QLabel" name="label">
<widget class="QLabel" name="copyrightLabel">
<property name="text">
<string>
Vorta is a cross-platform, open-source client designed to simplify the management of Borg backups.
Expand Down
2 changes: 1 addition & 1 deletion src/vorta/assets/icons/settings_wheel.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 4 additions & 7 deletions src/vorta/assets/metadata/com.borgbase.Vorta.appdata.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="desktop-application">
<id>com.borgbase.Vorta</id>
<launchable type="desktop-id">com.borgbase.Vorta.desktop</launchable>
<developer_name>Vorta contributors</developer_name>
<name>Vorta</name>
<project_license>GPL-3.0</project_license>
<metadata_license>CC0-1.0</metadata_license>
Expand Down Expand Up @@ -40,18 +42,13 @@
</screenshot>
</screenshots>
<releases>
<release version="v0.9.1-beta3" date="2023-11-30" urgency="low">
<release version="v0.9.1" date="2024-01-10" urgency="low">
<description>
<ul>
<li>First production 0.9 release</li>
<li>Exclude GUI. By @diivi (#1846)</li>
<li>Backup settings.db before migrations. By @AdwaitSalankar (#1848)</li>
<li>Loosen platformdirs dependency (#1843)</li>
</ul>
</description>
</release>
<release version="v0.9.1-beta2" date="2023-10-27" urgency="low">
<description>
<ul>
<li>Unit test improvements and coverage increase. By @bigtedde (#1787)</li>
<li>Profile sidebar and new setting interface. By @bigtedde (#1809)</li>
<li>Update macOS notarization for use with notarytool (#1831)</li>
Expand Down
31 changes: 17 additions & 14 deletions src/vorta/borg/jobs_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ def repo_id(self):
@abstractmethod
def cancel(self):
"""
Cancel can be called when the job is not started. It is the responsability of FuncJob to not cancel job if
Cancel can be called when the job is not started. It is the responsibility of FuncJob to not cancel job if
no job is running.
The cancel mehod of JobsManager calls the cancel method on the running jobs only. Other jobs are dequeued.
The cancel method of JobsManager calls the cancel method on the running jobs only. Other jobs are dequeued.
"""
pass

Expand All @@ -50,6 +50,7 @@ def __init__(self, jobs):
self.current_job = None

def run(self):
job = None
while True:
try:
job = self.jobs.get(False)
Expand All @@ -58,7 +59,8 @@ def run(self):
job.run()
logger.debug("Finish job for site: %s", job.repo_id())
except queue.Empty:
logger.debug("No more jobs for site: %s", job.repo_id())
if job is not None:
logger.debug("No more jobs for site: %s", job.repo_id())
return


Expand All @@ -77,19 +79,20 @@ def __init__(self):

def is_worker_running(self, site=None):
"""
See if there are any active jobs. The user can't start a backup if a job is
running. The scheduler can.
See if there are any active jobs.
The user can't start a backup if a job is running. The scheduler can.
If site is None, check if there is any worker active for any site (repo).
If site is not None, only check if there is a worker active for the given site (repo).
"""
# Check status for specific site (repo)
if site in self.workers:
return self.workers[site].is_alive()
if site is not None:
if site in self.workers:
if self.workers[site].is_alive():
return True
else:
return False

# Check if *any* worker is active
for _, worker in self.workers.items():
if worker.is_alive():
return True
for _, worker in self.workers.items():
if worker.is_alive():
return True
return False

def add_job(self, job):
Expand Down
2 changes: 1 addition & 1 deletion src/vorta/network_status/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def get_network_status_monitor(cls) -> 'NetworkStatusMonitor':

def is_network_status_available(self):
"""Is the network status really available, and not just a dummy implementation?"""
return type(self) != NetworkStatusMonitor
return type(self) is not NetworkStatusMonitor

def is_network_metered(self) -> bool:
"""Is the currently connected network a metered connection?"""
Expand Down
85 changes: 61 additions & 24 deletions src/vorta/network_status/darwin.py
Original file line number Diff line number Diff line change
@@ -1,53 +1,82 @@
import subprocess
from datetime import datetime as dt
from typing import Iterator, Optional
from typing import Iterator, List, Optional

from CoreWLAN import CWInterface, CWNetwork, CWWiFiClient

from vorta.log import logger
from vorta.network_status.abc import NetworkStatusMonitor, SystemWifiInfo


class DarwinNetworkStatus(NetworkStatusMonitor):
def is_network_metered(self) -> bool:
return any(is_network_metered(d) for d in get_network_devices())
interface: CWInterface = self._get_wifi_interface()
network: Optional[CWNetwork] = interface.lastNetworkJoined()

if network:
is_ios_hotspot = network.isPersonalHotspot()
else:
is_ios_hotspot = False

return is_ios_hotspot or any(is_network_metered_with_android(d) for d in get_network_devices())

def get_current_wifi(self) -> Optional[str]:
"""
Get current SSID or None if Wifi is off.
From https://gist.github.com/keithweaver/00edf356e8194b89ed8d3b7bbead000c
Get current SSID or None if Wi-Fi is off.
"""
cmd = [
'/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport',
'-I',
]
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
out, err = process.communicate()
process.wait()
for line in out.decode(errors='ignore').split('\n'):
split_line = line.strip().split(':')
if split_line[0] == 'SSID':
return split_line[1].strip()

def get_known_wifis(self):
interface: Optional[CWInterface] = self._get_wifi_interface()
if not interface:
return None

# If the user has Wi-Fi turned off lastNetworkJoined will return None.
network: Optional[CWNetwork] = interface.lastNetworkJoined()

if network:
network_name = network.ssid()
return network_name
else:
return None

def get_known_wifis(self) -> List[SystemWifiInfo]:
"""
Listing all known Wifi networks isn't possible any more from macOS 11. Instead we
just return the current Wifi.
Use the program, "networksetup", to get the list of know Wi-Fi networks.
"""

wifis = []
current_wifi = self.get_current_wifi()
if current_wifi is not None:
wifis.append(SystemWifiInfo(ssid=current_wifi, last_connected=dt.now()))
interface: Optional[CWInterface] = self._get_wifi_interface()
if not interface:
return []

interface_name = interface.name()
output = call_networksetup_listpreferredwirelessnetworks(interface_name)

result = []
for line in output.strip().splitlines():
if line.strip().startswith("Preferred networks"):
continue
elif not line.strip():
continue
else:
result.append(line.strip())

for wifi_network_name in result:
wifis.append(SystemWifiInfo(ssid=wifi_network_name, last_connected=dt.now()))

return wifis

def _get_wifi_interface(self) -> Optional[CWInterface]:
wifi_client: CWWiFiClient = CWWiFiClient.sharedWiFiClient()
interface: Optional[CWInterface] = wifi_client.interface()
return interface


def get_network_devices() -> Iterator[str]:
for line in call_networksetup_listallhardwareports().splitlines():
if line.startswith(b'Device: '):
yield line.split()[1].strip().decode('ascii')


def is_network_metered(bsd_device) -> bool:
def is_network_metered_with_android(bsd_device) -> bool:
return b'ANDROID_METERED' in call_ipconfig_getpacket(bsd_device)


Expand All @@ -66,3 +95,11 @@ def call_networksetup_listallhardwareports():
return subprocess.check_output(cmd)
except subprocess.CalledProcessError:
logger.debug("Command %s failed", ' '.join(cmd))


def call_networksetup_listpreferredwirelessnetworks(interface) -> str:
command = ['/usr/sbin/networksetup', '-listpreferredwirelessnetworks', interface]
try:
return subprocess.check_output(command).decode(encoding='utf-8')
except subprocess.CalledProcessError:
logger.debug("Command %s failed", " ".join(command))
2 changes: 1 addition & 1 deletion src/vorta/profile_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def schema_version(self):
def repo_url(self):
if (
'repo' in self._profile_dict
and type(self._profile_dict['repo']) == dict
and isinstance(self._profile_dict['repo'], dict)
and 'url' in self._profile_dict['repo']
):
return self._profile_dict['repo']['url']
Expand Down
Loading

0 comments on commit 259c6d5

Please sign in to comment.