diff --git a/README.md b/README.md index 323e0a5..81c9f32 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,16 @@ Update Station -========= +============== -GhostBSD system update manager +GhostBSD update manager. + + +## Managing Translations +To create a translation file. +```shell +./setup.py create_translation --locale=fr +``` + +To update translation files +```shell +./setup.py update_translations +``` diff --git a/po/POTFILES.in b/po/POTFILES.in new file mode 100755 index 0000000..9cdb467 --- /dev/null +++ b/po/POTFILES.in @@ -0,0 +1 @@ +update-station diff --git a/po/fr.po b/po/fr.po new file mode 100644 index 0000000..ad81c0a --- /dev/null +++ b/po/fr.po @@ -0,0 +1,272 @@ +# French translations for PACKAGE package. +# Copyright (C) 2024 THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Eric Turgeon , 2024. +# +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-10-02 22:45-0300\n" +"PO-Revision-Date: 2024-10-03 21:32-0300\n" +"Last-Translator: Eric Turgeon \n" +"Language-Team: French \n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: update_station/confirm_update.py:65 +msgid "Create boot environment backup" +msgstr "Créer une sauvegarde de l'environnement de démarrage" + +#: update_station/confirm_update.py:78 update_station/message.py:59 +#: update_station/message.py:148 update_station/message.py:182 +#: update_station/message.py:224 update_station/message.py:258 +#: update_station/message.py:292 update_station/message.py:328 +#: update_station/system_tray.py:47 +msgid "Close" +msgstr "Fermer" + +#: update_station/confirm_update.py:82 +msgid "Install update" +msgstr "Installer la mise à jour" + +#: update_station/confirm_update.py:95 +msgid "Update Manager" +msgstr "Gestionnaire de mise à jour" + +#: update_station/confirm_update.py:107 +msgid "Updates available!" +msgstr "Mises à jour disponibles !" + +#: update_station/confirm_update.py:146 +msgid "Installed packages to be REMOVED:" +msgstr "Paquets installés à SUPPRIMER:" + +#: update_station/confirm_update.py:153 +msgid "Installed packages to be UPGRADED" +msgstr "Packages installés à mettre à niveau" + +#: update_station/confirm_update.py:160 +msgid "New packages to be INSTALLED:" +msgstr "Nouveaux packages à INSTALLER:" + +#: update_station/confirm_update.py:167 +msgid "Installed packages to be REINSTALLED:" +msgstr "Packages installés à RÉINSTALLER:" + +#: update_station/look_for_updated.py:50 +msgid "Looking For Updates" +msgstr "À la recherche de mises à jour" + +#: update_station/look_for_updated.py:89 +msgid "Checking if the repository is online" +msgstr "" + +#: update_station/look_for_updated.py:93 +msgid "The repository is online" +msgstr "" + +#: update_station/look_for_updated.py:97 +msgid "The mirror is Syncing" +msgstr "" + +#: update_station/look_for_updated.py:102 +msgid "Updates are already running" +msgstr "" + +#: update_station/look_for_updated.py:106 +msgid "Checking for updates" +msgstr "" + +#: update_station/look_for_updated.py:110 +msgid "Getting the list of packages" +msgstr "" + +#: update_station/look_for_updated.py:114 +msgid "Open the update window" +msgstr "" + +#: update_station/look_for_updated.py:118 +msgid "No update found" +msgstr "" + +#: update_station/look_for_updated.py:124 +msgid "The Mirror is unreachable" +msgstr "" + +#: update_station/look_for_updated.py:145 +msgid "Major version upgrade" +msgstr "" + +#: update_station/look_for_updated.py:152 +#, python-brace-format +msgid "" +"Would you like to upgrade from {Data.current_abi} to {Data.new_abi}?\n" +"\n" +"If you select No, the upgrade will be skipped until the next boot." +msgstr "" + +#: update_station/message.py:42 +msgid "Update Failed" +msgstr "" + +#: update_station/message.py:48 +msgid "" +"Press \"Detail\" to get information about the failure.\n" +" Get help at https://forums.ghostbsd.org." +msgstr "" + +#: update_station/message.py:57 +msgid "Detail" +msgstr "" + +#: update_station/message.py:92 update_station/message.py:136 +msgid "Update Completed" +msgstr "" + +#: update_station/message.py:97 +msgid "The computer needs to restart to run on the updated software." +msgstr "" + +#: update_station/message.py:105 +msgid "Restart Now" +msgstr "" + +#: update_station/message.py:107 +msgid "Restart Later" +msgstr "" + +#: update_station/message.py:141 +msgid "All software on this system is up to date." +msgstr "" + +#: update_station/message.py:167 +msgid "No Update Available" +msgstr "" + +#: update_station/message.py:175 +msgid "No update available. This system is up to date." +msgstr "" + +#: update_station/message.py:210 +msgid "Update Station already started" +msgstr "" + +#: update_station/message.py:218 +msgid "Update Station already open." +msgstr "" + +#: update_station/message.py:243 update_station/message.py:277 +msgid "Server Unreachable" +msgstr "" + +#: update_station/message.py:251 +msgid "Packages mirrors are syncing with new packages" +msgstr "" + +#: update_station/message.py:285 +msgid "" +"The server is unreachable. Your internet could\n" +"be down or software package server is down." +msgstr "" + +#: update_station/message.py:311 +msgid "Something Is Wrong" +msgstr "" + +#: update_station/message.py:320 +msgid "" +"If you see this message it means that something is wrong.\n" +" Please look at pkg upgrade output." +msgstr "" + +#: update_station/message.py:343 +msgid "Software Station" +msgstr "" + +#: update_station/message.py:349 +msgid "You need to be root" +msgstr "" + +#: update_station/message.py:355 +msgid "OK" +msgstr "" + +#: update_station/notification.py:23 +msgid "Software updates are now available." +msgstr "" + +#: update_station/notification.py:31 +msgid "Major system version upgrade is now available." +msgstr "" + +#: update_station/notification.py:33 +msgid "System and software updates are now available." +msgstr "" + +#: update_station/notification.py:35 +msgid "Update Available" +msgstr "" + +#: update_station/system_tray.py:45 +msgid "Open Update" +msgstr "" + +#: update_station/upgrade_progress.py:49 +msgid "Installing Update" +msgstr "" + +#: update_station/upgrade_progress.py:93 +msgid "Cleaning old boot environment" +msgstr "" + +#: update_station/upgrade_progress.py:99 +msgid "Creating boot environment" +msgstr "" + +#: update_station/upgrade_progress.py:106 +msgid "Setting env and bootstrap pkg to upgrade" +msgstr "" + +#: update_station/upgrade_progress.py:134 +msgid "Downloading packages to upgrade" +msgstr "" + +#: update_station/upgrade_progress.py:161 +msgid "Packages to upgrade downloaded" +msgstr "" + +#: update_station/upgrade_progress.py:164 +msgid "Upgrading packages" +msgstr "" + +#: update_station/upgrade_progress.py:222 +msgid "Reinstalling" +msgstr "" + +#: update_station/upgrade_progress.py:224 +msgid "completed" +msgstr "" + +#: update_station/upgrade_progress.py:236 +msgid "Packages upgraded" +msgstr "" + +#: update_station/upgrade_progress.py:245 +msgid "Downloading packages depending on kernel" +msgstr "" + +#: update_station/upgrade_progress.py:272 +msgid "Packages depending on kernel downloaded" +msgstr "" + +#: update_station/upgrade_progress.py:275 +msgid "Reinstalling packages depending on kernel" +msgstr "" + +#: update_station/upgrade_progress.py:302 +msgid "Packages depending on kernel reinstalled" +msgstr "" diff --git a/src/locale/ru/update-station.po b/po/ru.po similarity index 51% rename from src/locale/ru/update-station.po rename to po/ru.po index ec360c8..213beec 100644 --- a/src/locale/ru/update-station.po +++ b/po/ru.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-07-28 11:48+0300\n" +"POT-Creation-Date: 2024-10-05 21:33-0300\n" "PO-Revision-Date: 2024-07-28 17:57+0300\n" "Last-Translator: Alexander Alexeev \n" "Language-Team: Language locale/ru\n" @@ -16,342 +16,273 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -#: src/update-station.py:101 -msgid "Create boot environment backup" -msgstr "Создать резервную копию загрузочной среды" +#: update_station/dialog.py:31 +msgid "Update Failed" +msgstr "Ошибка обновления" + +#: update_station/dialog.py:37 +#, fuzzy +msgid "" +"Press \"Detail\" to get information about the failure.\n" +" Get help at https://forums.ghostbsd.org or ." +msgstr "" +"Нажмите \"Подробности\" для получения информации об этой ошибке.\n" +" Получите помощь на https://forums.ghostbsd.org." + +#: update_station/dialog.py:46 +msgid "Detail" +msgstr "Подробности" -#: src/update-station.py:114 src/update-station.py:345 -#: src/update-station.py:861 src/update-station.py:954 -#: src/update-station.py:988 src/update-station.py:1138 -#: src/update-station.py:1172 src/update-station.py:1206 -#: src/update-station.py:1242 +#: update_station/dialog.py:48 update_station/dialog.py:114 +#: update_station/dialog.py:148 update_station/dialog.py:181 +#: update_station/dialog.py:215 update_station/dialog.py:249 +#: update_station/dialog.py:285 update_station/dialog.py:313 +#: update_station/frontend.py:105 update_station/notification.py:87 msgid "Close" msgstr "Закрыть" -#: src/update-station.py:118 -msgid "Install update" -msgstr "Установить обновление" - -#: src/update-station.py:131 -msgid "Update Manager" -msgstr "Менеджер обновлений" +#: update_station/dialog.py:68 update_station/dialog.py:102 +msgid "Update Completed" +msgstr "Обновление завершено" -#: src/update-station.py:143 -msgid "Updates available!" -msgstr "Доступны обновления!" +#: update_station/dialog.py:73 +msgid "The computer needs to restart to run on the updated software." +msgstr "" +"Чтобы запустить обновленное программное обеспечение,\n" +"необходимо перезагрузить компьютер." -#: src/update-station.py:182 -msgid "Installed packages to be REMOVED:" -msgstr "Ранее установленные пакеты будут УДАЛЕНЫ:" +#: update_station/dialog.py:81 +msgid "Restart Now" +msgstr "Перезагрузить сейчас" -#: src/update-station.py:189 -msgid "Installed packages to be UPGRADED" -msgstr "Ранее установленные пакеты будут ОБНОВЛЕНЫ:" +#: update_station/dialog.py:83 +msgid "Restart Later" +msgstr "Перезагрузить позже" -#: src/update-station.py:196 -msgid "New packages to be INSTALLED:" -msgstr "Новые пакеты будут УСТАНОВЛЕНЫ:" +#: update_station/dialog.py:107 +msgid "All software on this system is up to date." +msgstr "Все программное обеспечение в этой системе уже обновлено." -#: src/update-station.py:203 -msgid "Installed packages to be REINSTALLED:" -msgstr "Ранее установленные пакеты будут ПЕРЕУСТАНОВЛЕНЫ:" +#: update_station/dialog.py:133 +msgid "No Update Available" +msgstr "Нет доступных обновлений" -#: src/update-station.py:237 -msgid "Software updates are now available." -msgstr "Доступны обновления программного обеспечения." +#: update_station/dialog.py:141 +msgid "No update available. This system is up to date." +msgstr "Нет доступных обновлений. Эта система уже обновлена." -#: src/update-station.py:245 -msgid "Major system version upgrade is now available." -msgstr "Доступно обновление версии основной системы." +#: update_station/dialog.py:167 +msgid "Update Station already started" +msgstr "Центр обновлений уже запущен" -#: src/update-station.py:247 -msgid "System and software updates are now available." -msgstr "Доступны обновления системы и программного обеспечения" +#: update_station/dialog.py:175 +msgid "Update Station already open." +msgstr "Центр обновлений уже открыт" -#: src/update-station.py:249 -msgid "Update Available" -msgstr "Доступно обновление" +#: update_station/dialog.py:200 update_station/dialog.py:234 +msgid "Server Unreachable" +msgstr "Сервер не доступен" -#: src/update-station.py:279 -msgid "Major version upgrade" -msgstr "Обновление основной версии" +#: update_station/dialog.py:208 +msgid "Packages mirrors are syncing with new packages" +msgstr "Идет синхронизация с новыми пакетами на зеркальных серверах" -#: src/update-station.py:286 -#, python-brace-format +#: update_station/dialog.py:242 msgid "" -"Would you like to upgrade from {Data.current_abi} to {Data.new_abi}?\n" -"\n" -"If you select No, the upgrade will be skipped until the next boot." +"The server is unreachable. Your internet could\n" +"be down or software package server is down." msgstr "" -"Вы действительно хотите обновиться с {Data.current_abi} на {Data.new_abi}?\n" -"\n" -"Если Вы виберете Нет, то обновлние будет пропущено до следующей перезагрузки." - -#: src/update-station.py:343 -msgid "Open Update" -msgstr "Открыть обновления" +"Сервер не доступен. Возможно у Вас нас нет доступа в сеть Интернет или\n" +"сервер пакетов программного обеспечения в настоящее время не работает." -#: src/update-station.py:424 -msgid "Upgrade to PKGBASE" -msgstr "Обновление до PKGBASE" +#: update_station/dialog.py:268 +msgid "Something Is Wrong" +msgstr "Есть ошибки" -#: src/update-station.py:429 +#: update_station/dialog.py:277 msgid "" -"To continue receiving system updates, you need to upgrade to PKGBASE.\n" -"\n" -"This upgrade cannot be performed on a UFS installation. If you have \n" -"installed GhostBSD with UFS, please cancel and reinstall using ZFS.\n" -"\n" -"This upgrade will be performed in a new boot environment and will\n" -"automatically reboot into it." +"If you see this message it means that something is wrong.\n" +" Please look at pkg upgrade output." msgstr "" -"Чтобы продолжать получать обновления системы, вам необходимо перейти на PKGBASE.\n" -"\n" -"Это обновление невозможно выполнить при установке системы на UFS. Если вы используете\n" -"GhostBSD установленную на UFS, отмените эту установку и переустановите ее с помощью ZFS.\n" -"\n" -"Это обновление будет выполнено в новой среде загрузки и приведет\n" -"к автоматической перезагрузке в нее." - -#: src/update-station.py:438 src/update-station.py:441 -msgid "Upgrade Now" -msgstr "Обновиться сейчас" - -#: src/update-station.py:469 -msgid "Upgrading to PKGBASE" -msgstr "Обновление до PKGBASE" - -#: src/update-station.py:505 -msgid "Adding PKGBASE repository support" -msgstr "Добавление поддержки репозитория PKGBASE" - -#: src/update-station.py:508 -msgid "Adding PKGBASE repository support completed" -msgstr "Добавление поддержки репозитория PKGBASE завершено" - -#: src/update-station.py:511 -msgid "Creating boot environment pkgbase-upgrade" -msgstr "Создание загрузочной среды pkgbase-upgrade" - -#: src/update-station.py:514 -msgid "Boot environment pkgbase-upgrade created" -msgstr "Загрузочная среда для pkgbase-upgrade создана" - -#: src/update-station.py:517 -msgid "Mounting boot environment pkgbase-upgrade" -msgstr "Монтирование загрузочной среды pkgbase-upgrade" - -#: src/update-station.py:520 -msgid "Boot environment mounted to /tmp/pkgbase-upgrade" -msgstr "Загрузочная среда примонтирована в /tmp/pkgbase-upgrade" +"Если вы видете это сообщение, значит, что-то пошло не так.\n" +"Пожалуйста, посмотрите на вывод о результах этого обновления\n" +"с помощью утилиты pkg." -#: src/update-station.py:523 -msgid "Removing os-generic packages from boot environment" -msgstr "Удаление общих пакетов операционной системы из среды загрузки" +#: update_station/dialog.py:301 +msgid "Software Station" +msgstr "Центр приложений" -#: src/update-station.py:526 -msgid "os-generic packages are removed from boot environment" -msgstr "Общие пакеты операционной системы из среды загрузки удалены" +#: update_station/dialog.py:307 +msgid "You need to be root" +msgstr "У Вас должны быть права root" -#: src/update-station.py:529 -msgid "Installing PGKBASE in the boot environment" -msgstr "Установка PGKBASE в загрузочную среду" +#: update_station/frontend.py:92 +msgid "Create boot environment backup" +msgstr "Создать резервную копию загрузочной среды" -#: src/update-station.py:532 -msgid "PGKBASE installed in the boot environment" -msgstr "PGKBASE установлена в среду загрузки" +#: update_station/frontend.py:109 +msgid "Install update" +msgstr "Установить обновление" -#: src/update-station.py:535 -msgid "Restoring vital files" -msgstr "Восстановление важных файлов" +#: update_station/frontend.py:122 +msgid "Update Manager" +msgstr "Менеджер обновлений" -#: src/update-station.py:538 -msgid "Vital files restored" -msgstr "Важные файлы восстановлены" +#: update_station/frontend.py:134 +msgid "Updates available!" +msgstr "Доступны обновления!" -#: src/update-station.py:541 -msgid "Unmounting boot environment pkgbase-upgrade" -msgstr "Отключение загрузочной среды pkgbase-upgrade" +#: update_station/frontend.py:173 +msgid "Installed packages to be REMOVED:" +msgstr "Ранее установленные пакеты будут УДАЛЕНЫ:" -#: src/update-station.py:544 -msgid "Boot environment unmounted" -msgstr "Загрузочная среда отмонтирована" +#: update_station/frontend.py:180 +msgid "Installed packages to be UPGRADED" +msgstr "Ранее установленные пакеты будут ОБНОВЛЕНЫ:" -#: src/update-station.py:547 -msgid "Activating pkgbase-upgrade boot environment" -msgstr "Активация загрузочной среды pkgbase-upgrade" +#: update_station/frontend.py:187 +msgid "New packages to be INSTALLED:" +msgstr "Новые пакеты будут УСТАНОВЛЕНЫ:" -#: src/update-station.py:550 -msgid "Activating pkgbase-upgrade boot environment completed" -msgstr "Активация загрузочной среды pkgbase-upgrade завершена" +#: update_station/frontend.py:194 +msgid "Installed packages to be REINSTALLED:" +msgstr "Ранее установленные пакеты будут ПЕРЕУСТАНОВЛЕНЫ:" -#: src/update-station.py:577 +#: update_station/frontend.py:234 msgid "Installing Update" msgstr "Установка обновлений" -#: src/update-station.py:632 +#: update_station/frontend.py:278 msgid "Cleaning old boot environment" msgstr "Очистка старой загрузочной среды" -#: src/update-station.py:638 +#: update_station/frontend.py:284 msgid "Creating boot environment" msgstr "Создание загрузочной среды" -#: src/update-station.py:644 src/update-station.py:674 -msgid "Fetching package updates" -msgstr "Получение обновлений" +#: update_station/frontend.py:291 +msgid "Setting env and bootstrap pkg to upgrade" +msgstr "" + +#: update_station/frontend.py:319 +#, fuzzy +msgid "Downloading packages to upgrade" +msgstr "Установка обновлений" -#: src/update-station.py:704 -msgid "Package updates downloaded" +#: update_station/frontend.py:346 +#, fuzzy +msgid "Packages to upgrade downloaded" msgstr "Обновления загружены" -#: src/update-station.py:708 -msgid "Installing package updates" -msgstr "Установка обновлений" +#: update_station/frontend.py:349 +msgid "Upgrading packages" +msgstr "" -#: src/update-station.py:769 +#: update_station/frontend.py:407 msgid "Reinstalling" msgstr "Переустановка" -#: src/update-station.py:771 +#: update_station/frontend.py:409 msgid "completed" msgstr "Завершена" -#: src/update-station.py:784 -msgid "Software packages upgrade completed" -msgstr "Обновление программного обеспечения завершено" - -#: src/update-station.py:844 -msgid "Update Failed" -msgstr "Ошибка обновления" +#: update_station/frontend.py:421 +#, fuzzy +msgid "Packages upgraded" +msgstr "Обновления загружены" -#: src/update-station.py:850 -msgid "" -"Press \"Detail\" to get information about the failure.\n" -" Get help at https://forums.ghostbsd.org." +#: update_station/frontend.py:430 +msgid "Downloading packages depending on kernel" msgstr "" -"Нажмите \"Подробности\" для получения информации об этой ошибке.\n" -" Получите помощь на https://forums.ghostbsd.org." -#: src/update-station.py:859 -msgid "Detail" -msgstr "Подробности" +#: update_station/frontend.py:457 +#, fuzzy +msgid "Packages depending on kernel downloaded" +msgstr "Обновления загружены" -#: src/update-station.py:898 src/update-station.py:942 -msgid "Update Completed" -msgstr "Обновление завершено" +#: update_station/frontend.py:460 +#, fuzzy +msgid "Reinstalling packages depending on kernel" +msgstr "Установка обновлений" -#: src/update-station.py:903 -msgid "The computer needs to restart to run on the updated software." +#: update_station/frontend.py:487 +msgid "Packages depending on kernel reinstalled" msgstr "" -"Чтобы запустить обновленное программное обеспечение,\n" -"необходимо перезагрузить компьютер." - -#: src/update-station.py:911 -msgid "Restart Now" -msgstr "Перезагрузить сейчас" - -#: src/update-station.py:913 -msgid "Restart Later" -msgstr "Перезагрузить позже" - -#: src/update-station.py:947 -msgid "All software on this system is up to date." -msgstr "Все программное обеспечение в этой системе уже обновлено." - -#: src/update-station.py:973 -msgid "No Update Available" -msgstr "Нет доступных обновлений" -#: src/update-station.py:981 -msgid "No update available. This system is up to date." -msgstr "Нет доступных обновлений. Эта система уже обновлена." - -#: src/update-station.py:1015 +#: update_station/frontend.py:541 msgid "Looking For Updates" msgstr "Ищу обновления" -#: src/update-station.py:1054 +#: update_station/frontend.py:580 msgid "Checking if the repository is online" msgstr "Проверка на доступность репозиторий" -#: src/update-station.py:1058 +#: update_station/frontend.py:584 msgid "The repository is online" msgstr "Репозиторий доступен" -#: src/update-station.py:1062 +#: update_station/frontend.py:588 msgid "The mirror is Syncing" msgstr "Зеркало синхронизируется" -#: src/update-station.py:1067 +#: update_station/frontend.py:593 msgid "Updates are already running" msgstr "Ообновления уже запущены" -#: src/update-station.py:1071 +#: update_station/frontend.py:597 msgid "Checking for updates" msgstr "Проверка обновлений" -#: src/update-station.py:1075 +#: update_station/frontend.py:601 msgid "Getting the list of packages" msgstr "Получение списка пакетов" -#: src/update-station.py:1079 +#: update_station/frontend.py:605 msgid "Open the update window" msgstr "Откройте окно обновления" -#: src/update-station.py:1083 +#: update_station/frontend.py:609 msgid "No update found" msgstr "Обновление не найдено" -#: src/update-station.py:1089 +#: update_station/frontend.py:615 msgid "The Mirror is unreachable" msgstr "Зеркало не доступно" -#: src/update-station.py:1124 -msgid "Update Station already started" -msgstr "Центр обновлений уже запущен" +#: update_station/notification.py:25 +msgid "Software updates are now available." +msgstr "Доступны обновления программного обеспечения." -#: src/update-station.py:1132 -msgid "Update Station already open." -msgstr "Центр обновлений уже открыт" +#: update_station/notification.py:33 +msgid "Major system version upgrade is now available." +msgstr "Доступно обновление версии основной системы." -#: src/update-station.py:1157 src/update-station.py:1191 -msgid "Server Unreachable" -msgstr "Сервер не доступен" +#: update_station/notification.py:35 +msgid "System and software updates are now available." +msgstr "Доступны обновления системы и программного обеспечения" -#: src/update-station.py:1165 -msgid "Packages mirrors are syncing with new packages" -msgstr "Идет синхронизация с новыми пакетами на зеркальных серверах" +#: update_station/notification.py:37 +msgid "Update Available" +msgstr "Доступно обновление" -#: src/update-station.py:1199 -msgid "" -"The server is unreachable. Your internet could\n" -"be down or software package server is down." -msgstr "" -"Сервер не доступен. Возможно у Вас нас нет доступа в сеть Интернет или\n" -"сервер пакетов программного обеспечения в настоящее время не работает." +#: update_station/notification.py:85 +msgid "Open Update" +msgstr "Открыть обновления" -#: src/update-station.py:1225 -msgid "Something Is Wrong" -msgstr "Есть ошибки" +#: update_station/notification.py:130 +msgid "Major version upgrade" +msgstr "Обновление основной версии" -#: src/update-station.py:1234 +#: update_station/notification.py:137 +#, python-brace-format msgid "" -"If you see this message it means that something is wrong.\n" -" Please look at pkg upgrade output." +"Would you like to upgrade from {Data.current_abi} to {Data.new_abi}?\n" +"\n" +"If you select No, the upgrade will be skipped until the next boot." msgstr "" -"Если вы видете это сообщение, значит, что-то пошло не так.\n" -"Пожалуйста, посмотрите на вывод о результах этого обновления\n" -"с помощью утилиты pkg." - -#: src/update-station.py:1257 -msgid "Software Station" -msgstr "Центр приложений" - -#: src/update-station.py:1263 -msgid "You need to be root" -msgstr "У Вас должны быть права root" +"Вы действительно хотите обновиться с {Data.current_abi} на {Data.new_abi}?\n" +"\n" +"Если Вы виберете Нет, то обновлние будет пропущено до следующей перезагрузки." -#: src/update-station.py:1269 -msgid "OK" -msgstr "ОК" +#~ msgid "OK" +#~ msgstr "ОК" diff --git a/po/update-station.pot b/po/update-station.pot new file mode 100644 index 0000000..dc2703e --- /dev/null +++ b/po/update-station.pot @@ -0,0 +1,268 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-10-05 21:34-0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: update_station/dialog.py:31 +msgid "Update Failed" +msgstr "" + +#: update_station/dialog.py:37 +msgid "" +"Press \"Detail\" to get information about the failure.\n" +" Get help at https://forums.ghostbsd.org or ." +msgstr "" + +#: update_station/dialog.py:46 +msgid "Detail" +msgstr "" + +#: update_station/dialog.py:48 update_station/dialog.py:114 +#: update_station/dialog.py:148 update_station/dialog.py:181 +#: update_station/dialog.py:215 update_station/dialog.py:249 +#: update_station/dialog.py:285 update_station/dialog.py:313 +#: update_station/frontend.py:105 update_station/notification.py:87 +msgid "Close" +msgstr "" + +#: update_station/dialog.py:68 update_station/dialog.py:102 +msgid "Update Completed" +msgstr "" + +#: update_station/dialog.py:73 +msgid "The computer needs to restart to run on the updated software." +msgstr "" + +#: update_station/dialog.py:81 +msgid "Restart Now" +msgstr "" + +#: update_station/dialog.py:83 +msgid "Restart Later" +msgstr "" + +#: update_station/dialog.py:107 +msgid "All software on this system is up to date." +msgstr "" + +#: update_station/dialog.py:133 +msgid "No Update Available" +msgstr "" + +#: update_station/dialog.py:141 +msgid "No update available. This system is up to date." +msgstr "" + +#: update_station/dialog.py:167 +msgid "Update Station already started" +msgstr "" + +#: update_station/dialog.py:175 +msgid "Update Station already open." +msgstr "" + +#: update_station/dialog.py:200 update_station/dialog.py:234 +msgid "Server Unreachable" +msgstr "" + +#: update_station/dialog.py:208 +msgid "Packages mirrors are syncing with new packages" +msgstr "" + +#: update_station/dialog.py:242 +msgid "" +"The server is unreachable. Your internet could\n" +"be down or software package server is down." +msgstr "" + +#: update_station/dialog.py:268 +msgid "Something Is Wrong" +msgstr "" + +#: update_station/dialog.py:277 +msgid "" +"If you see this message it means that something is wrong.\n" +" Please look at pkg upgrade output." +msgstr "" + +#: update_station/dialog.py:301 +msgid "Software Station" +msgstr "" + +#: update_station/dialog.py:307 +msgid "You need to be root" +msgstr "" + +#: update_station/frontend.py:92 +msgid "Create boot environment backup" +msgstr "" + +#: update_station/frontend.py:109 +msgid "Install update" +msgstr "" + +#: update_station/frontend.py:122 +msgid "Update Manager" +msgstr "" + +#: update_station/frontend.py:134 +msgid "Updates available!" +msgstr "" + +#: update_station/frontend.py:173 +msgid "Installed packages to be REMOVED:" +msgstr "" + +#: update_station/frontend.py:180 +msgid "Installed packages to be UPGRADED" +msgstr "" + +#: update_station/frontend.py:187 +msgid "New packages to be INSTALLED:" +msgstr "" + +#: update_station/frontend.py:194 +msgid "Installed packages to be REINSTALLED:" +msgstr "" + +#: update_station/frontend.py:234 +msgid "Installing Update" +msgstr "" + +#: update_station/frontend.py:278 +msgid "Cleaning old boot environment" +msgstr "" + +#: update_station/frontend.py:284 +msgid "Creating boot environment" +msgstr "" + +#: update_station/frontend.py:291 +msgid "Setting env and bootstrap pkg to upgrade" +msgstr "" + +#: update_station/frontend.py:319 +msgid "Downloading packages to upgrade" +msgstr "" + +#: update_station/frontend.py:346 +msgid "Packages to upgrade downloaded" +msgstr "" + +#: update_station/frontend.py:349 +msgid "Upgrading packages" +msgstr "" + +#: update_station/frontend.py:407 +msgid "Reinstalling" +msgstr "" + +#: update_station/frontend.py:409 +msgid "completed" +msgstr "" + +#: update_station/frontend.py:421 +msgid "Packages upgraded" +msgstr "" + +#: update_station/frontend.py:430 +msgid "Downloading packages depending on kernel" +msgstr "" + +#: update_station/frontend.py:457 +msgid "Packages depending on kernel downloaded" +msgstr "" + +#: update_station/frontend.py:460 +msgid "Reinstalling packages depending on kernel" +msgstr "" + +#: update_station/frontend.py:487 +msgid "Packages depending on kernel reinstalled" +msgstr "" + +#: update_station/frontend.py:541 +msgid "Looking For Updates" +msgstr "" + +#: update_station/frontend.py:580 +msgid "Checking if the repository is online" +msgstr "" + +#: update_station/frontend.py:584 +msgid "The repository is online" +msgstr "" + +#: update_station/frontend.py:588 +msgid "The mirror is Syncing" +msgstr "" + +#: update_station/frontend.py:593 +msgid "Updates are already running" +msgstr "" + +#: update_station/frontend.py:597 +msgid "Checking for updates" +msgstr "" + +#: update_station/frontend.py:601 +msgid "Getting the list of packages" +msgstr "" + +#: update_station/frontend.py:605 +msgid "Open the update window" +msgstr "" + +#: update_station/frontend.py:609 +msgid "No update found" +msgstr "" + +#: update_station/frontend.py:615 +msgid "The Mirror is unreachable" +msgstr "" + +#: update_station/notification.py:25 +msgid "Software updates are now available." +msgstr "" + +#: update_station/notification.py:33 +msgid "Major system version upgrade is now available." +msgstr "" + +#: update_station/notification.py:35 +msgid "System and software updates are now available." +msgstr "" + +#: update_station/notification.py:37 +msgid "Update Available" +msgstr "" + +#: update_station/notification.py:85 +msgid "Open Update" +msgstr "" + +#: update_station/notification.py:130 +msgid "Major version upgrade" +msgstr "" + +#: update_station/notification.py:137 +#, python-brace-format +msgid "" +"Would you like to upgrade from {Data.current_abi} to {Data.new_abi}?\n" +"\n" +"If you select No, the upgrade will be skipped until the next boot." +msgstr "" diff --git a/requirements.txt b/requirements.txt index cc84533..30491bb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,5 @@ +PyGObject requests -setuptools \ No newline at end of file +setuptools +bectl +distro \ No newline at end of file diff --git a/setup.py b/setup.py index 74d1ead..3eb4890 100755 --- a/setup.py +++ b/setup.py @@ -1,14 +1,12 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - import os import sys -# from glob import glob -from setuptools import setup +from setuptools import setup, Command, glob -# import DistUtilsExtra.command.build_extra -# import DistUtilsExtra.command.build_i18n -# import DistUtilsExtra.command.clean_i18n +from DistUtilsExtra.command.build_extra import build_extra +from DistUtilsExtra.command.build_i18n import build_i18n +from DistUtilsExtra.command.clean_i18n import clean_i18n # to update i18n .mo files (and merge .pot file into .po files) run on Linux: # python setup.py build_i18n -m'' @@ -18,9 +16,6 @@ PROGRAM_VERSION = __VERSION__ prefix = sys.prefix -# compiling translations -os.system("sh compile_translations.sh") - def data_file_list(install_base, source_base): data = [] @@ -32,12 +27,71 @@ def data_file_list(install_base, source_base): return data +class UpdateTranslationsCommand(Command): + """Custom command to extract messages and update .po files.""" + + description = 'Extract messages to .pot and update .po' + user_options = [] # No custom options + + def initialize_options(self): + pass + + def finalize_options(self): + pass + + def run(self): + # Define paths + pot_file = 'po/update-station.pot' + po_files = glob.glob('po/*.po') + # Step 1: Extract messages to .pot file + print("Extracting messages to .pot file...") + os.system(f'xgettext --from-code=UTF-8 -L Python -o {pot_file} update_station/*.py update-station') + # Step 2: Update .po files with the new .pot file + print("Updating .po files with new translations...") + for po_file in po_files: + print(f"Updating {po_file}...") + os.system(f'msgmerge -U {po_file} {pot_file}') + print("Translation update complete.") + + +class CreateTranslationCommand(Command): + """Custom command to create a new .po file for a specific language.""" + locale = None + description = 'Create a new .po file for the specified language' + user_options = [ + ('locale=', 'l', 'Locale code for the new translation (e.g., fr, es)') + ] + + def initialize_options(self): + self.locale = None # Initialize the locale option to None + + def finalize_options(self): + if self.locale is None: + raise Exception("You must specify the locale code (e.g., --locale=fr)") + + def run(self): + # Define paths + pot_file = 'po/update-station.pot' + po_dir = 'po' + po_file = os.path.join(po_dir, f'{self.locale}.po') + # Check if the .pot file exists + if not os.path.exists(pot_file): + print("Extracting messages to .pot file...") + os.system(f'xgettext --from-code=UTF-8 -L Python -o {pot_file} update_station/*.py update-station') + # Create the new .po file + if not os.path.exists(po_file): + print(f"Creating new {po_file} for locale '{self.locale}'...") + os.makedirs(po_dir, exist_ok=True) + os.system(f'msginit --locale={self.locale} --input={pot_file} --output-file={po_file}') + else: + print(f"PO file for locale '{self.locale}' already exists: {po_file}") + + data_files = [ (f'{prefix}/etc/xdg/autostart', ['src/update-station.desktop']), (f'{prefix}/share/applications', ['src/update-manager.desktop']), - (f'{prefix}/lib/update-station', ['src/need_reboot.json']), - (f'{prefix}/etc/sudoers.d', ['src/sudoers.d/update-station']), - (f'{prefix}/share/locale/ru/LC_MESSAGES', ['src/locale/ru/update-station.mo']) + (f'{prefix}/lib/update-station', ['src/need_reboot.json', 'src/pkg_to_reinstall']), + (f'{prefix}/etc/sudoers.d', ['src/sudoers.d/update-station']) ] data_files.extend(data_file_list(f'{prefix}/share/locale', 'build/mo')) @@ -49,9 +103,16 @@ def data_file_list(install_base, source_base): license='BSD', author='Eric Turgeon', url='https://github/GhostBSD/update-station/', - package_dir={'': 'src'}, + package_dir={'': '.'}, data_files=data_files, install_requires=['setuptools'], - py_modules=['updateHandler', 'update_data'], - scripts=['src/update-station'] + packages=['update_station'], + scripts=['update-station'], + cmdclass={ + 'create_translation': CreateTranslationCommand, + 'update_translations': UpdateTranslationsCommand, + "build": build_extra, + "build_i18n": build_i18n, + "clean": clean_i18n + } ) diff --git a/src/locale/update-station.pot b/src/locale/update-station.pot deleted file mode 100644 index 0f412a3..0000000 --- a/src/locale/update-station.pot +++ /dev/null @@ -1,339 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-07-28 11:48+0300\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=CHARSET\n" -"Content-Transfer-Encoding: 8bit\n" - -#: src/update-station.py:101 -msgid "Create boot environment backup" -msgstr "" - -#: src/update-station.py:114 src/update-station.py:345 -#: src/update-station.py:861 src/update-station.py:954 -#: src/update-station.py:988 src/update-station.py:1138 -#: src/update-station.py:1172 src/update-station.py:1206 -#: src/update-station.py:1242 -msgid "Close" -msgstr "" - -#: src/update-station.py:118 -msgid "Install update" -msgstr "" - -#: src/update-station.py:131 -msgid "Update Manager" -msgstr "" - -#: src/update-station.py:143 -msgid "Updates available!" -msgstr "" - -#: src/update-station.py:182 -msgid "Installed packages to be REMOVED:" -msgstr "" - -#: src/update-station.py:189 -msgid "Installed packages to be UPGRADED" -msgstr "" - -#: src/update-station.py:196 -msgid "New packages to be INSTALLED:" -msgstr "" - -#: src/update-station.py:203 -msgid "Installed packages to be REINSTALLED:" -msgstr "" - -#: src/update-station.py:237 -msgid "Software updates are now available." -msgstr "" - -#: src/update-station.py:245 -msgid "Major system version upgrade is now available." -msgstr "" - -#: src/update-station.py:247 -msgid "System and software updates are now available." -msgstr "" - -#: src/update-station.py:249 -msgid "Update Available" -msgstr "" - -#: src/update-station.py:279 -msgid "Major version upgrade" -msgstr "" - -#: src/update-station.py:286 -#, python-brace-format -msgid "" -"Would you like to upgrade from {Data.current_abi} to {Data.new_abi}?\n" -"\n" -"If you select No, the upgrade will be skipped until the next boot." -msgstr "" - -#: src/update-station.py:343 -msgid "Open Update" -msgstr "" - -#: src/update-station.py:424 -msgid "Upgrade to PKGBASE" -msgstr "" - -#: src/update-station.py:429 -msgid "" -"To continue receiving system updates, you need to upgrade to PKGBASE.\n" -"\n" -"This upgrade cannot be performed on a UFS installation. If you have \n" -"installed GhostBSD with UFS, please cancel and reinstall using ZFS.\n" -"\n" -"This upgrade will be performed in a new boot environment and will\n" -"automatically reboot into it." -msgstr "" - -#: src/update-station.py:438 src/update-station.py:441 -msgid "Upgrade Now" -msgstr "" - -#: src/update-station.py:469 -msgid "Upgrading to PKGBASE" -msgstr "" - -#: src/update-station.py:505 -msgid "Adding PKGBASE repository support" -msgstr "" - -#: src/update-station.py:508 -msgid "Adding PKGBASE repository support completed" -msgstr "" - -#: src/update-station.py:511 -msgid "Creating boot environment pkgbase-upgrade" -msgstr "" - -#: src/update-station.py:514 -msgid "Boot environment pkgbase-upgrade created" -msgstr "" - -#: src/update-station.py:517 -msgid "Mounting boot environment pkgbase-upgrade" -msgstr "" - -#: src/update-station.py:520 -msgid "Boot environment mounted to /tmp/pkgbase-upgrade" -msgstr "" - -#: src/update-station.py:523 -msgid "Removing os-generic packages from boot environment" -msgstr "" - -#: src/update-station.py:526 -msgid "os-generic packages are removed from boot environment" -msgstr "" - -#: src/update-station.py:529 -msgid "Installing PGKBASE in the boot environment" -msgstr "" - -#: src/update-station.py:532 -msgid "PGKBASE installed in the boot environment" -msgstr "" - -#: src/update-station.py:535 -msgid "Restoring vital files" -msgstr "" - -#: src/update-station.py:538 -msgid "Vital files restored" -msgstr "" - -#: src/update-station.py:541 -msgid "Unmounting boot environment pkgbase-upgrade" -msgstr "" - -#: src/update-station.py:544 -msgid "Boot environment unmounted" -msgstr "" - -#: src/update-station.py:547 -msgid "Activating pkgbase-upgrade boot environment" -msgstr "" - -#: src/update-station.py:550 -msgid "Activating pkgbase-upgrade boot environment completed" -msgstr "" - -#: src/update-station.py:577 -msgid "Installing Update" -msgstr "" - -#: src/update-station.py:632 -msgid "Cleaning old boot environment" -msgstr "" - -#: src/update-station.py:638 -msgid "Creating boot environment" -msgstr "" - -#: src/update-station.py:644 src/update-station.py:674 -msgid "Fetching package updates" -msgstr "" - -#: src/update-station.py:704 -msgid "Package updates downloaded" -msgstr "" - -#: src/update-station.py:708 -msgid "Installing package updates" -msgstr "" - -#: src/update-station.py:769 -msgid "Reinstalling" -msgstr "" - -#: src/update-station.py:771 -msgid "completed" -msgstr "" - -#: src/update-station.py:784 -msgid "Software packages upgrade completed" -msgstr "" - -#: src/update-station.py:844 -msgid "Update Failed" -msgstr "" - -#: src/update-station.py:850 -msgid "" -"Press \"Detail\" to get information about the failure.\n" -" Get help at https://forums.ghostbsd.org." -msgstr "" - -#: src/update-station.py:859 -msgid "Detail" -msgstr "" - -#: src/update-station.py:898 src/update-station.py:942 -msgid "Update Completed" -msgstr "" - -#: src/update-station.py:903 -msgid "The computer needs to restart to run on the updated software." -msgstr "" - -#: src/update-station.py:911 -msgid "Restart Now" -msgstr "" - -#: src/update-station.py:913 -msgid "Restart Later" -msgstr "" - -#: src/update-station.py:947 -msgid "All software on this system is up to date." -msgstr "" - -#: src/update-station.py:973 -msgid "No Update Available" -msgstr "" - -#: src/update-station.py:981 -msgid "No update available. This system is up to date." -msgstr "" - -#: src/update-station.py:1015 -msgid "Looking For Updates" -msgstr "" - -#: src/update-station.py:1054 -msgid "Checking if the repository is online" -msgstr "" - -#: src/update-station.py:1058 -msgid "The repository is online" -msgstr "" - -#: src/update-station.py:1062 -msgid "The mirror is Syncing" -msgstr "" - -#: src/update-station.py:1067 -msgid "Updates are already running" -msgstr "" - -#: src/update-station.py:1071 -msgid "Checking for updates" -msgstr "" - -#: src/update-station.py:1075 -msgid "Getting the list of packages" -msgstr "" - -#: src/update-station.py:1079 -msgid "Open the update window" -msgstr "" - -#: src/update-station.py:1083 -msgid "No update found" -msgstr "" - -#: src/update-station.py:1089 -msgid "The Mirror is unreachable" -msgstr "" - -#: src/update-station.py:1124 -msgid "Update Station already started" -msgstr "" - -#: src/update-station.py:1132 -msgid "Update Station already open." -msgstr "" - -#: src/update-station.py:1157 src/update-station.py:1191 -msgid "Server Unreachable" -msgstr "" - -#: src/update-station.py:1165 -msgid "Packages mirrors are syncing with new packages" -msgstr "" - -#: src/update-station.py:1199 -msgid "" -"The server is unreachable. Your internet could\n" -"be down or software package server is down." -msgstr "" - -#: src/update-station.py:1225 -msgid "Something Is Wrong" -msgstr "" - -#: src/update-station.py:1234 -msgid "" -"If you see this message it means that something is wrong.\n" -" Please look at pkg upgrade output." -msgstr "" - -#: src/update-station.py:1257 -msgid "Software Station" -msgstr "" - -#: src/update-station.py:1263 -msgid "You need to be root" -msgstr "" - -#: src/update-station.py:1269 -msgid "OK" -msgstr "" diff --git a/src/need_reboot.json b/src/need_reboot.json index dc00f1f..6637d75 100644 --- a/src/need_reboot.json +++ b/src/need_reboot.json @@ -1,12 +1,19 @@ [ - "drm-fbsd13-kmod", + "drm-515-kmod", + "drm-61-kmod", "drm-kmod", - "drm-legacy-kmod", "ghostbsd-update-scripts", + "GhostBSD-kernel-generic", + "GhostBSD-openssl", + "GhostBSD-rc", + "GhostBSD-runtime", + "GhostBSD-zfs", + "libva-nvidia-driver", "linux-nvidia-libs", "linux-nvidia-libs-304", "linux-nvidia-libs-340", "linux-nvidia-libs-390", + "linux-nvidia-libs-470", "marco", "mate-applets", "mate-backgrounds", @@ -29,28 +36,10 @@ "nvidia-driver-304", "nvidia-driver-340", "nvidia-driver-390", - "os-generic-buildkernel", - "os-generic-buildkernel-debug", - "os-generic-buildworld", - "os-generic-kernel", - "os-generic-kernel-debug", - "os-generic-kernel-debug-symbols", - "os-generic-kernel-symbols", - "os-generic-userland", - "os-generic-userland-base", - "os-generic-userland-base-bootstrap", - "os-generic-userland-bin", - "os-generic-userland-boot", - "os-generic-userland-conf", - "os-generic-userland-debug", - "os-generic-userland-devtools", - "os-generic-userland-docs", - "os-generic-userland-lib", - "os-generic-userland-lib32", - "os-generic-userland-lib32-development", - "os-generic-userland-rescue", - "os-generic-userland-sbin", - "os-generic-userland-tests", + "nvidia-driver-470", + "nvidia-secondary-driver", + "nvidia-secondary-driver-390", + "nvidia_gpu_prometheus_exporter", "update-station", "wayland", "wayland-logout", diff --git a/src/pkg_to_reinstall b/src/pkg_to_reinstall new file mode 100644 index 0000000..12a9869 --- /dev/null +++ b/src/pkg_to_reinstall @@ -0,0 +1,233 @@ +acerhdf-kmod +acpi_call +aquantia-atlantic-kmod +arduino +arduino-avrdude +arduino-bsd-mk +arduino-builder +arduino-core +arduino-ctags +arduino-irremote +arduino-mk +arduino-openglcd +arduino-sevseg +arduino-tools +arduino18 +arduinoOTA +bhyve-firmware +bhyve-rc +biosfont +bwi-firmware-kmod +bwn-firmware-kmod +cciss_vol_status +cx88 +dahdi-kmod26 +devctl-jail-kmod +drm-61-kmod +drm-515-kmod +drm-kmod +gpu-firmware-amd-kmod-aldebaran +gpu-firmware-amd-kmod-arcturus +gpu-firmware-amd-kmod-banks +gpu-firmware-amd-kmod-beige-goby +gpu-firmware-amd-kmod-bonaire +gpu-firmware-amd-kmod-carrizo +gpu-firmware-amd-kmod-cyan-skillfish2 +gpu-firmware-amd-kmod-dcn-3-1-4 +gpu-firmware-amd-kmod-dcn-3-1-5 +gpu-firmware-amd-kmod-dcn-3-1-6 +gpu-firmware-amd-kmod-dcn-3-2-0 +gpu-firmware-amd-kmod-dcn-3-2-1 +gpu-firmware-amd-kmod-dimgrey-cavefish +gpu-firmware-amd-kmod-fiji +gpu-firmware-amd-kmod-gc-10-3-6 +gpu-firmware-amd-kmod-gc-10-3-7 +gpu-firmware-amd-kmod-gc-11-0-0 +gpu-firmware-amd-kmod-gc-11-0-1 +gpu-firmware-amd-kmod-gc-11-0-2 +gpu-firmware-amd-kmod-gc-11-0-3 +gpu-firmware-amd-kmod-gc-11-0-4 +gpu-firmware-amd-kmod-green-sardine +gpu-firmware-amd-kmod-hainan +gpu-firmware-amd-kmod-hawaii +gpu-firmware-amd-kmod-kabini +gpu-firmware-amd-kmod-kaveri +gpu-firmware-amd-kmod-mullins +gpu-firmware-amd-kmod-navi10 +gpu-firmware-amd-kmod-navi12 +gpu-firmware-amd-kmod-navi14 +gpu-firmware-amd-kmod-navy-flounder +gpu-firmware-amd-kmod-oland +gpu-firmware-amd-kmod-picasso +gpu-firmware-amd-kmod-pitcairn +gpu-firmware-amd-kmod-polaris10 +gpu-firmware-amd-kmod-polaris11 +gpu-firmware-amd-kmod-polaris12 +gpu-firmware-amd-kmod-psp-13-0-0 +gpu-firmware-amd-kmod-psp-13-0-4 +gpu-firmware-amd-kmod-psp-13-0-5 +gpu-firmware-amd-kmod-psp-13-0-7 +gpu-firmware-amd-kmod-psp-13-0-8 +gpu-firmware-amd-kmod-psp-13-0-10 +gpu-firmware-amd-kmod-psp-13-0-11 +gpu-firmware-amd-kmod-raven +gpu-firmware-amd-kmod-raven2 +gpu-firmware-amd-kmod-renoir +gpu-firmware-amd-kmod-sdma-5-2-6 +gpu-firmware-amd-kmod-sdma-5-2-7 +gpu-firmware-amd-kmod-sdma-6-0-0 +gpu-firmware-amd-kmod-sdma-6-0-1 +gpu-firmware-amd-kmod-sdma-6-0-2 +gpu-firmware-amd-kmod-sdma-6-0-3 +gpu-firmware-amd-kmod-si58 +gpu-firmware-amd-kmod-sienna-cichlid +gpu-firmware-amd-kmod-smu-13-0-0 +gpu-firmware-amd-kmod-smu-13-0-7 +gpu-firmware-amd-kmod-smu-13-0-10 +gpu-firmware-amd-kmod-stoney +gpu-firmware-amd-kmod-tahiti +gpu-firmware-amd-kmod-tonga +gpu-firmware-amd-kmod-topaz +gpu-firmware-amd-kmod-vangogh +gpu-firmware-amd-kmod-vcn-3-1-2 +gpu-firmware-amd-kmod-vcn-4-0-0 +gpu-firmware-amd-kmod-vcn-4-0-2 +gpu-firmware-amd-kmod-vcn-4-0-4 +gpu-firmware-amd-kmod-vega10 +gpu-firmware-amd-kmod-vega12 +gpu-firmware-amd-kmod-vega20 +gpu-firmware-amd-kmod-vegam +gpu-firmware-amd-kmod-verde +gpu-firmware-amd-kmod-yellow-carp +gpu-firmware-intel-kmod-alderlake +gpu-firmware-intel-kmod-broxton +gpu-firmware-intel-kmod-cannonlake +gpu-firmware-intel-kmod-dg1 +gpu-firmware-intel-kmod-dg2 +gpu-firmware-intel-kmod-elkhartlake +gpu-firmware-intel-kmod-geminilake +gpu-firmware-intel-kmod-icelake +gpu-firmware-intel-kmod-kabylake +gpu-firmware-intel-kmod-rocketlake +gpu-firmware-intel-kmod-skylake +gpu-firmware-intel-kmod-tigerlake +gpu-firmware-kmod +gpu-firmware-radeon-kmod-aruba +gpu-firmware-radeon-kmod-barts +gpu-firmware-radeon-kmod-bonaire +gpu-firmware-radeon-kmod-btc +gpu-firmware-radeon-kmod-caicos +gpu-firmware-radeon-kmod-cayman +gpu-firmware-radeon-kmod-cedar +gpu-firmware-radeon-kmod-cypress +gpu-firmware-radeon-kmod-hainan +gpu-firmware-radeon-kmod-hawaii +gpu-firmware-radeon-kmod-juniper +gpu-firmware-radeon-kmod-kabini +gpu-firmware-radeon-kmod-kaveri +gpu-firmware-radeon-kmod-mullins +gpu-firmware-radeon-kmod-oland +gpu-firmware-radeon-kmod-palm +gpu-firmware-radeon-kmod-pitcairn +gpu-firmware-radeon-kmod-r100 +gpu-firmware-radeon-kmod-r200 +gpu-firmware-radeon-kmod-r300 +gpu-firmware-radeon-kmod-r420 +gpu-firmware-radeon-kmod-r520 +gpu-firmware-radeon-kmod-r600 +gpu-firmware-radeon-kmod-r700 +gpu-firmware-radeon-kmod-redwood +gpu-firmware-radeon-kmod-rs600 +gpu-firmware-radeon-kmod-rs690 +gpu-firmware-radeon-kmod-rs780 +gpu-firmware-radeon-kmod-rv610 +gpu-firmware-radeon-kmod-rv620 +gpu-firmware-radeon-kmod-rv630 +gpu-firmware-radeon-kmod-rv635 +gpu-firmware-radeon-kmod-rv670 +gpu-firmware-radeon-kmod-rv710 +gpu-firmware-radeon-kmod-rv730 +gpu-firmware-radeon-kmod-rv740 +gpu-firmware-radeon-kmod-rv770 +gpu-firmware-radeon-kmod-sumo +gpu-firmware-radeon-kmod-sumo2 +gpu-firmware-radeon-kmod-tahiti +gpu-firmware-radeon-kmod-turks +gpu-firmware-radeon-kmod-verde +hammer2 +hv_kvp_cmd +intel-ixl-kmod +isal-kmod +isboot-kmod +linux_dvbwrapper-kmod +mac_nonet-kmod +mac_rtprio-kmod +malo-firmware-kmod +mbgtools +mdb +ndproxy +ng_ipacct +ng_mikrotik_eoip +nvidia-driver +nvidia-driver-304 +nvidia-driver-340 +nvidia-driver-390 +nvidia-driver-470 +nvidia-secondary-driver +nvidia-secondary-driver-390 +nvidia-settings +nvidia-texture-tools +nvidia-xconfig +open-vm-tools +open-vm-tools-nox11 +opencbm-kmod +opennurbs +openzfs-kmod +oss +parallels-tools +pefs-kmod +plasma-kmod +ptx-kmod +pwcbsd +py311-libzfs +py311-python-dtrace +quantis-kmod +realtek-re-kmod +realtek-re-kmod198 +sysctlbyname-improved-kmod +sysctlinfo-kmod +sysutils/lsof +twa-kmod +uarduno +utouch-kmod +vether-kmod +virtualbox-ose +virtualbox-ose-additions +virtualbox-ose-additions-legacy +virtualbox-ose-additions-nox11 +virtualbox-ose-additions-nox11-legacy +virtualbox-ose-kmod +virtualbox-ose-kmod-legacy +virtualbox-ose-legacy +wifi-firmware-kmod +wifibox +wifibox-alpine +wifibox-alpine-ath10k +wifibox-alpine-ath11k +wifibox-alpine-ath12k +wifibox-alpine-brcm +wifibox-alpine-iwlwifi +wifibox-alpine-marvell +wifibox-alpine-mediatek +wifibox-ath10k +wifibox-ath11k +wifibox-ath12k +wifibox-brcm +wifibox-core +wifibox-iwlwifi +wifibox-marvell +wifibox-mediatek +wifibox-rt61 +wifibox-rtlwifi +wifibox-rtw88 +wifibox-rtw89 \ No newline at end of file diff --git a/src/update-station b/src/update-station deleted file mode 100755 index c58b774..0000000 --- a/src/update-station +++ /dev/null @@ -1,1318 +0,0 @@ -#!/usr/local/bin/python -"""This is the main file for the update-station application""" - -import bectl -import datetime -import getpass -import gi -import json -import os -import gettext -import re -import socket -import sys -import threading -import distro -gi.require_version('Gtk', '3.0') -gi.require_version('Notify', '0.7') -from gi.repository import Gtk, GLib, Notify -from subprocess import Popen, PIPE, run -from time import sleep -from update_data import Data -from updateHandler import ( - check_for_update, - get_pkg_upgrade_data, - network_stat, - repo_online, - look_update_station, - unlock_update_station, - updating, - repository_is_syncing, - is_major_upgrade_available, - get_abi_upgrade, - get_current_abi, - find_if_os_generic_exists, - remove_os_generic, - install_ghostbsd_pkgbase, - set_package_base_config_file, - restore_vital_files, - remove_package_config, - fetch_ghostbsd_pkgbase -) -gettext.bindtextdomain('update-station', '/usr/local/share/locale') -gettext.textdomain('update-station') -_ = gettext.gettext - -lib_path: str = f'{sys.prefix}/lib/update-station' - -username = os.environ.get('SUDO_USER') if 'SUDO_USER' in os.environ else getpass.getuser() -home = os.path.expanduser('~') - - -class UpdateWindow: - """ - Class that creates the main window to see update list and start the update process. - """ - def delete_event(self, widget: Gtk.Widget) -> None: - """ - Function that handles the delete event when the window is closed. - :param widget: The widget that triggered the delete event. - """ - if Data.close_session is True: - if updating(): - unlock_update_station() - Gtk.main_quit() - else: - self.window.destroy() - if Data.update_started is False: - Data.stop_pkg_refreshing = False - if updating(): - unlock_update_station() - tray.tray_icon().set_visible(True) - - def start_update(self, widget): - """ - Function that starts the update process. - :param widget: The widget that triggered the start update event. - """ - Data.update_started = True - InstallUpdate() - self.window.hide() - - def if_backup(self, widget): - """ - Function that handles the backup checkbox. - :param widget: The widget that triggered the checkbox event. - """ - Data.backup = widget.get_active() - - def create_bbox(self): - """ - Function that creates the button box. - :return: The button box. - """ - table = Gtk.Table( - n_rows=1, - n_columns=5, - homogeneous=False, - column_spacing=5 - ) - backup_checkbox = Gtk.CheckButton( - label=_("Create boot environment backup") - ) - table.attach(backup_checkbox, 0, 1, 0, 1) - backup_checkbox.connect("toggled", self.if_backup) - if bectl.is_file_system_zfs() and Data.second_update is False: - backup_checkbox.set_active(True) - backup_checkbox.set_sensitive(True) - Data.backup = True - else: - backup_checkbox.set_active(False) - backup_checkbox.set_sensitive(False) - Data.backup = False - img = Gtk.Image(icon_name='window-close') - close_button = Gtk.Button(label=_("Close")) - close_button.set_image(img) - table.attach(close_button, 3, 4, 0, 1) - close_button.connect("clicked", self.delete_event) - install_button = Gtk.Button(label=_("Install update")) - table.attach(install_button, 4, 5, 0, 1) - install_button.connect("clicked", self.start_update) - return table - - def __init__(self): - """ - The constructor for the UpdateWindow class. - """ - self.window = Gtk.Window() - self.window.connect("destroy", self.delete_event) - self.window.set_size_request(700, 400) - self.window.set_resizable(False) - self.window.set_title(_("Update Manager")) - self.window.set_border_width(0) - self.window.set_position(Gtk.WindowPosition.CENTER) - self.window.set_default_icon_name('system-software-update') - box1 = Gtk.VBox(homogeneous=False, spacing=0) - self.window.add(box1) - box1.show() - box2 = Gtk.VBox(homogeneous=False, spacing=0) - box2.set_border_width(20) - box1.pack_start(box2, True, True, 0) - box2.show() - # Title - title_text = _("Updates available!") - - update_title_label = Gtk.Label( - label=f"{title_text}" - ) - update_title_label.set_use_markup(True) - box2.pack_start(update_title_label, False, False, 0) - self.tree_store = Gtk.TreeStore(str, bool) - sw = Gtk.ScrolledWindow() - sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN) - sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) - self.view = Gtk.TreeView(model=self.store()) - self.renderer = Gtk.CellRendererText() - self.column0 = Gtk.TreeViewColumn("Name", self.renderer, text=0) - self.view.append_column(self.column0) - self.view.set_headers_visible(False) - sw.add(self.view) - sw.show() - box2.pack_start(sw, True, True, 10) - box2 = Gtk.HBox(homogeneous=False, spacing=10) - box2.set_border_width(5) - box1.pack_start(box2, False, False, 5) - box2.show() - # Add button - box2.pack_start(self.create_bbox(), True, True, 10) - self.window.show_all() - - def store(self): - """ - Function that creates the store for the list of package in the treeview. - :return: The store for the list of package to be updated. - """ - self.tree_store.clear() - r_num = 0 - u_num = 0 - i_num = 0 - ri_num = 0 - if bool(Data.packages_dictionary['remove']): - r_num = len(Data.packages_dictionary['remove']) - message = _('Installed packages to be REMOVED:') - message += f' {r_num}' - r_pinter = self.tree_store.append(None, (message, True)) - for line in Data.packages_dictionary['remove']: - self.tree_store.append(r_pinter, (line, True)) - if bool(Data.packages_dictionary['upgrade']): - u_num = len(Data.packages_dictionary['upgrade']) - message = _('Installed packages to be UPGRADED') - message += f' {u_num}' - u_pinter = self.tree_store.append(None, (message, True)) - for line in Data.packages_dictionary['upgrade']: - self.tree_store.append(u_pinter, (line, True)) - if bool(Data.packages_dictionary['install']): - i_num = len(Data.packages_dictionary['install']) - message = _('New packages to be INSTALLED:') - message += f' {i_num}' - i_pinter = self.tree_store.append(None, (message, True)) - for line in Data.packages_dictionary['install']: - self.tree_store.append(i_pinter, (line, True)) - if bool(Data.packages_dictionary['reinstall']): - ri_num = len(Data.packages_dictionary['reinstall']) - message = _('Installed packages to be REINSTALLED:') - message += f' {ri_num}' - ri_pinter = self.tree_store.append(None, (message, True)) - for line in Data.packages_dictionary['reinstall']: - self.tree_store.append(ri_pinter, (line, True)) - Data.total_packages = r_num + u_num + i_num + ri_num - return self.tree_store - - def display(self, model: Gtk.TreeStore) -> Gtk.TreeView: - """ - Function that creates the treeview. - - :param model: The store for the list of package to be updated. - :return: The treeview. - """ - self.view = Gtk.TreeView(model=model) - self.renderer = Gtk.CellRendererText() - self.column0 = Gtk.TreeViewColumn("Name", self.renderer, text=0) - self.view.append_column(self.column0) - self.view.set_headers_visible(False) - return self.view - - -class UpdateNotifier: - """ - Class that creates the notification for the update. - """ - - def __init__(self): - """ - The constructor for the UpdateNotifier class. - """ - self.notification = None - Notify.init('Test') - self.msg = _("Software updates are now available.") - self.timeout = 10000 # 10 seconds - - def notify(self): - """ - Function that creates the notification for the update. - """ - if Data.major_upgrade is True: - self.msg = _("Major system version upgrade is now available.") - elif Data.kernel_upgrade is True: - self.msg = _("System and software updates are now available.") - self.notification = Notify.Notification.new( - summary=_('Update Available'), - body=self.msg, - icon='system-software-update' - ) - self.notification.add_action('clicked', 'Start Upgrade', self.on_activated) - self.notification.show() - - def on_activated(self, notification, action_name): - """ - Function that starts the upgrade. - :param notification: The notification widget. - :param action_name: The name of the action. - """ - if Data.major_upgrade is True: - MajorUpgradeWindow() - else: - StartCheckUpdate() - notification.close() - GLib.idle_add(tray.tray_icon().set_visible, False) - - -class MajorUpgradeWindow(Gtk.Window): - """ - Class that creates the window for the major upgrade. - """ - - def __init__(self): - """ - The constructor for the MajorUpgradeWindow class. - """ - Gtk.Window.__init__(self, title=_("Major version upgrade")) - self.connect("destroy", Gtk.main_quit) - vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) - vbox.set_border_width(10) - self.add(vbox) - - label = Gtk.Label( - label=_(f"Would you like to upgrade from {Data.current_abi} to {Data.new_abi}?" - "\n\nIf you select No, the upgrade will be skipped until the next boot.") - ) - vbox.pack_start(label, True, True, 5) - hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) - vbox.pack_start(hbox, False, False, 0) - button1 = Gtk.Button(label="Yes") - button1.connect("clicked", self.on_clicked) - hbox.pack_end(button1, True, True, 0) - - button2 = Gtk.Button(label="No") - button2.connect("clicked", self.on_clicked) - hbox.pack_end(button2, True, True, 0) - self.show_all() - - def on_clicked(self, widget): - """ - Function that starts the upgrade. - :param widget: The widget that was clicked. - """ - if widget.get_label() == "Yes": - Data.major_upgrade = True - Data.do_not_upgrade = False - StartCheckUpdate() - else: - Data.major_upgrade = False - Data.do_not_upgrade = True - self.hide() - - -class TrayIcon: - """ - The class for the tray icon. - """ - - def tray_icon(self): - return self.status_icon - - def __init__(self): - """ - The constructor for the TrayIcon class. - """ - self.status_icon = Gtk.StatusIcon() - self.status_icon.set_tooltip_text('Update Available') - self.menu = Gtk.Menu() - self.menu.show_all() - self.status_icon.connect("activate", self.left_click) - self.status_icon.connect('popup-menu', self.icon_clicked) - self.status_icon.set_visible(False) - self.status_icon.set_from_icon_name('system-software-update') - - def nm_menu(self): - """ - Function that creates the menu for the tray icon. - :return: The menu. - """ - self.menu = Gtk.Menu() - open_update = Gtk.MenuItem(label=_("Open Update")) - open_update.connect("activate", self.left_click) - close_item = Gtk.MenuItem(label=_("Close")) - close_item.connect("activate", Gtk.main_quit) - self.menu.append(open_update) - self.menu.append(close_item) - self.menu.show_all() - return self.menu - - def left_click(self, status_icon: Gtk.StatusIcon): - """ - Function that is called when the user left-clicks on the tray icon. - :param status_icon: The status icon. - """ - if updating(): - UpdateStationOpen() - else: - Data.stop_pkg_refreshing = True - if Data.major_upgrade is True: - MajorUpgradeWindow() - else: - StartCheckUpdate() - status_icon.set_visible(False) - - def icon_clicked(self, status_icon, button, time): - """ - Function that is called when the user right-clicks on the tray icon. - :param status_icon: The status icon. - :param button: The button. - :param time: The time. - """ - position = Gtk.StatusIcon.position_menu - self.nm_menu().popup(None, None, position, status_icon, button, time) - - def update_tray(self): - """ - Function that updates the tray icon. - """ - if find_if_os_generic_exists(): - # Simple code avoid showing the Upgrade to PKGBase Window twice. - if Data.pkgbase_upgrade_shown is False: - Data.pkgbase_upgrade_shown = True - UpgradeToPKGBase() - elif check_for_update(): - GLib.idle_add(self.status_icon.set_visible, True) - notifier = UpdateNotifier() - notifier.notify() - elif is_major_upgrade_available() and Data.do_not_upgrade is False: - Data.major_upgrade = True - GLib.idle_add(self.status_icon.set_visible, True) - Data.current_abi = get_current_abi() - Data.new_abi = get_abi_upgrade() - notifier = UpdateNotifier() - notifier.notify() - else: - GLib.idle_add(self.status_icon.set_visible, False) - - def threading_update(self): - """ - Function that creates a thread that checks for updates. - """ - if updating(): - unlock_update_station() - thr = threading.Thread(target=self.check, daemon=True) - thr.start() - - def check(self): - """ - Function that checks for updates. - """ - while True: - sleep(120) - if not repository_is_syncing(): - if not Data.stop_pkg_refreshing: - if not updating(): - GLib.idle_add(self.update_tray) - else: - GLib.idle_add(self.status_icon.set_visible, False) - # Wait for an hour to look for update - sleep(600) - - -class UpgradeToPKGBase(Gtk.Window): - def __init__(self): - Gtk.Window.__init__(self, title=_("Upgrade to PKGBASE")) - self.connect("destroy", Gtk.main_quit) - vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) - vbox.set_border_width(10) - self.add(vbox) - label = Gtk.Label(label=_("To continue receiving system updates, you need to upgrade to PKGBASE.\n\n" - "This upgrade cannot be performed on a UFS installation. If you have \n" - "installed GhostBSD with UFS, please cancel and reinstall using ZFS.\n\n" - "This upgrade will be performed in a new boot environment and will\n" - "automatically reboot into it.")) - vbox.pack_start(label, True, True, 5) - - hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) - vbox.pack_end(hbox, False, False, 0) - upgrade_button = Gtk.Button(label=_("Upgrade Now")) - upgrade_button.connect("clicked", self.on_clicked) - hbox.pack_end(upgrade_button, False, False, 0) - cancel_button = Gtk.Button(label=_("Cancel")) - cancel_button.connect("clicked", Gtk.main_quit) - hbox.pack_end(cancel_button, False, False, 0) - - self.show_all() - - def on_clicked(self, widget): - self.hide() - UpgradePKGBASEProgress() - - -class UpgradePKGBASEProgress: - """ - The class for the window that is displayed the progress of the update. - """ - def close_application(self, widget, data): - if updating(): - unlock_update_station() - Gtk.main_quit() - - def __init__(self): - """ - The constructor for the InstallUpdate class. - """ - self.win = Gtk.Window() - self.win.connect("delete-event", self.close_application) - self.win.set_size_request(500, 75) - self.win.set_resizable(False) - self.win.set_title(_("Upgrading to PKGBASE")) - self.win.set_border_width(0) - self.win.set_position(Gtk.WindowPosition.CENTER) - self.win.set_default_icon_name('system-software-update') - box1 = Gtk.VBox(homogeneous=False, spacing=0) - self.win.add(box1) - box1.show() - box2 = Gtk.VBox(homogeneous=False, spacing=10) - box2.set_border_width(10) - box1.pack_start(box2, True, True, 0) - box2.show() - self.pbar = Gtk.ProgressBar() - self.pbar.set_show_text(True) - self.pbar.set_fraction(0.0) - # self.pbar.set_size_request(-1, 20) - box2.pack_start(self.pbar, False, False, 0) - self.win.show_all() - self.thr = threading.Thread( - target=self.read_output, - args=[self.pbar], - daemon=True - ) - self.thr.start() - - def update_progress(self, progress, fraction, text): - """ - Function that updates the progress bar. - :param progress: The progress bar. - :param fraction: The fraction to add. - :param text: The text to display. - """ - progress.set_fraction(fraction) - progress.set_text(text) - - def read_output(self, progress): - fraction = 1.0 / 20 - progress_text = _("Setting temporary packages configuration.") - GLib.idle_add(self.update_progress, progress, fraction, progress_text) - assert set_package_base_config_file().returncode == 0 - progress_text = _("Temporary packages configuration setting completed.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - progress_text = _("Creating boot environment pkgbase-upgrade.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - bectl.create_be('pkgbase-upgrade') - progress_text = _("Boot environment pkgbase-upgrade created.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - progress_text = _("Mounting boot environment pkgbase-upgrade.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - bectl.mount_be('pkgbase-upgrade', '/tmp/pkgbase-upgrade') - progress_text = _("Boot environment mounted to /tmp/pkgbase-upgrade.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - progress_text = _("Removing os-generic packages from boot environment.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - assert remove_os_generic('/tmp/pkgbase-upgrade').returncode == 0 - progress_text = _("os-generic packages are removed from boot environment.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - progress_text = _("Downloading PGKBASE packages. This will take a while.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - assert fetch_ghostbsd_pkgbase('/tmp/pkgbase-upgrade').returncode == 0 - progress_text = _("PGKBASE packages download completed.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - progress_text = _("Installing PGKBASE in the boot environment.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - assert install_ghostbsd_pkgbase('/tmp/pkgbase-upgrade').returncode == 0 - progress_text = _("PGKBASE installed in the boot environment.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - progress_text = _("Restoring vital files.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - restore_vital_files('/tmp/pkgbase-upgrade') - progress_text = _("Vital files restored.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - progress_text = _("Unmounting boot environment pkgbase-upgrade.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - bectl.umount_be('pkgbase-upgrade') - progress_text = _("Boot environment unmounted.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - progress_text = _("Activating pkgbase-upgrade boot environment.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - bectl.activate_be('pkgbase-upgrade') - progress_text = _("Activating pkgbase-upgrade boot environment completed.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - progress_text = _("Remove temporary package configuration.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - assert remove_package_config().returncode == 0 - progress_text = _("Temporary package configuration removed.") - GLib.idle_add(self.update_progress, progress, progress.get_fraction() + fraction, progress_text) - sleep(1) - self.stop_tread() - - def stop_tread(self): - self.win.hide() - RestartSystem() - - -class InstallUpdate: - """ - The class for the window that is displayed the progress of the update. - """ - def close_application(self, widget, data): - if updating(): - unlock_update_station() - Gtk.main_quit() - - def __init__(self): - """ - The constructor for the InstallUpdate class. - """ - self.win = Gtk.Window() - self.win.connect("delete-event", self.close_application) - self.win.set_size_request(500, 75) - self.win.set_resizable(False) - self.win.set_title(_("Installing Update")) - self.win.set_border_width(0) - self.win.set_position(Gtk.WindowPosition.CENTER) - self.win.set_default_icon_name('system-software-update') - box1 = Gtk.VBox(homogeneous=False, spacing=0) - self.win.add(box1) - box1.show() - box2 = Gtk.VBox(homogeneous=False, spacing=10) - box2.set_border_width(10) - box1.pack_start(box2, True, True, 0) - box2.show() - self.pbar = Gtk.ProgressBar() - self.pbar.set_show_text(True) - self.pbar.set_fraction(0.0) - # self.pbar.set_size_request(-1, 20) - box2.pack_start(self.pbar, False, False, 0) - self.win.show_all() - self.thr = threading.Thread(target=self.read_output, args=[self.pbar], daemon=True) - self.thr.start() - - def update_progress(self, progress, fraction, text): - """ - Function that updates the progress bar. - :param progress: The progress bar. - :param fraction: The fraction to add. - :param text: The text to display. - """ - progress.set_fraction(fraction) - progress.set_text(text) - - def read_output(self, progress): - """ - Function that reads the output of the update to update the progress bar. - :param progress: The progress bar. - """ - fail = False - update_pkg = False - option = '' - packages = '' - env = f'env ABI={Data.new_abi} ' if Data.major_upgrade else '' - need_reboot_packages = set(json.loads(open(f'{lib_path}/need_reboot.json').read())) - upgrade_packages = set(re.split(": | ", " ".join(Data.packages_dictionary['upgrade']))) - reboot = bool(need_reboot_packages.intersection(upgrade_packages)) - if len(Data.packages_dictionary['upgrade']) == 1 and 'pkg:' in Data.packages_dictionary['upgrade'][0]: - update_pkg = True - packages = ' pkg' - Data.second_update = True - else: - Data.second_update = False - if Data.kernel_upgrade: - option = 'f' - howmany = (Data.total_packages * 4) + 20 - fraction = 1.0 / howmany - if Data.backup: - today = datetime.datetime.now().strftime("%Y-%m-%d") - txt = _("Cleaning old boot environment") - GLib.idle_add(self.update_progress, progress, fraction, txt) - for be in bectl.get_be_list(): - if 'backup' in be and today not in be and 'NR' not in be: - bectl.destroy_be(be.split()[0]) - backup_name = datetime.datetime.now().strftime(f"{distro.version()}-backup-%Y-%m-%d-%H-%M") - txt = _("Creating boot environment") - txt += f" {backup_name}" - GLib.idle_add(self.update_progress, progress, fraction, txt) - bectl.create_be(new_be_name=backup_name) - sleep(1) - if Data.major_upgrade: - txt = _("Fetching package updates") - new_val = progress.get_fraction() + fraction - GLib.idle_add(self.update_progress, progress, new_val, txt) - fetch = Popen( - f'{env}env IGNORE_OSVERSION=yes ASSUME_ALWAYS_YES=yes pkg bootstrap -f', - shell=True, - stdout=PIPE, - stderr=PIPE, - close_fds=True, - universal_newlines=True - ) - fetch_text = "" - while True: - stdout_line = fetch.stdout.readline() - if fetch.poll() is not None: - break - fetch_text += stdout_line - new_val = progress.get_fraction() + fraction - GLib.idle_add(self.update_progress, progress, new_val, - stdout_line.strip()) - if fetch.returncode != 0: - stderr_line = fetch.stderr.read() - fetch_text += stderr_line - update_fail = open(f'{home}/update.failed', 'w') - update_fail.writelines(fetch_text) - update_fail.close() - fail = True - GLib.idle_add(self.win.destroy) - GLib.idle_add(self.stop_tread, fail, update_pkg, reboot) - return - txt = _("Fetching package updates") - new_val = progress.get_fraction() + fraction - GLib.idle_add(self.update_progress, progress, new_val, txt) - sleep(1) - fetch = Popen( - f'{env}pkg-static upgrade -Fy{option}{packages}', - shell=True, - stdout=PIPE, - stderr=PIPE, - close_fds=True, - universal_newlines=True - ) - fetch_text = "" - while True: - stdout_line = fetch.stdout.readline() - if fetch.poll() is not None: - break - fetch_text += stdout_line - new_val = progress.get_fraction() + fraction - GLib.idle_add(self.update_progress, progress, new_val, - stdout_line.strip()) - if fetch.returncode != 0: - stderr_line = fetch.stderr.read() - fetch_text += stderr_line - update_fail = open(f'{home}/update.failed', 'w') - update_fail.writelines(fetch_text) - update_fail.close() - fail = True - else: - new_val = progress.get_fraction() + fraction - txt = _("Package updates downloaded") - GLib.idle_add(self.update_progress, progress, new_val, txt) - sleep(1) - new_val = progress.get_fraction() + fraction - txt = _("Installing package updates") - GLib.idle_add(self.update_progress, progress, new_val, txt) - sleep(1) - while True: - install = Popen( - f'{env}pkg-static upgrade -y{option}{packages}', - shell=True, - stdout=PIPE, - stderr=PIPE, - close_fds=True, - universal_newlines=True - ) - install_text = "" - while True: - stdout_line = install.stdout.readline() - if install.poll() is not None: - break - install_text += stdout_line - new_val = progress.get_fraction() + fraction - GLib.idle_add(self.update_progress, progress, new_val, - stdout_line.strip()) - if install.returncode == 3: - stderr_line = install.stderr.readline() - if 'Fail to create temporary file' in stderr_line: - raw_line = install_text.splitlines()[-2] - failed_package = raw_line.split()[2].replace(':', '') - pkg_rquery = run( - f'{env}pkg-static rquery -x "%n" "{failed_package}"', - shell=True, - stdout=PIPE, - stderr=PIPE, - universal_newlines=True - ) - package_name = pkg_rquery.stdout.strip() - reinstall = Popen( - f'{env}pkg-static delete -y {package_name} ;' - f'{env}pkg-static install -y {package_name}', - shell=True, - stdout=PIPE, - stderr=PIPE, - close_fds=True, - universal_newlines=True - ) - reinstall_text = "" - while True: - stdout_line = reinstall.stdout.readline() - if reinstall.poll() is not None: - break - reinstall_text += stdout_line - new_val = progress.get_fraction() + fraction - GLib.idle_add(self.update_progress, progress, - new_val, stdout_line.strip()) - if reinstall.returncode != 0: - reinstall_text += reinstall.stderr.readline() - update_fail = open(f'{home}/update.failed', 'w') - update_fail.writelines(reinstall_text) - update_fail.close() - fail = True - break - else: - new_val = progress.get_fraction() + fraction - txt = _("Reinstalling") - txt += f" {failed_package} " - txt += _("completed") - GLib.idle_add(self.update_progress, progress, new_val, txt) - sleep(1) - elif install.returncode != 0: - stderr_line = install.stderr.readline() - install_text += stderr_line - update_fail = open(f'{home}/update.failed', 'w') - update_fail.writelines(install_text) - update_fail.close() - fail = True - break - else: - new_val = progress.get_fraction() + fraction - txt = _("Software packages upgrade completed") - GLib.idle_add(self.update_progress, progress, new_val, txt) - sleep(1) - break - GLib.idle_add(self.win.destroy) - GLib.idle_add(self.stop_tread, fail, update_pkg, reboot) - - def stop_tread(self, fail: bool, update_pkg: bool, reboot: bool): - """ - The function to stop the thread. - :param fail: True if update failed. - :param update_pkg: True if update pkg was updated first. - :param reboot: True if system needs to be rebooted after update completed. - """ - if updating(): - unlock_update_station() - if fail is True: - Data.update_started = False - Data.stop_pkg_refreshing = False - FailedUpdate() - else: - if update_pkg is True and check_for_update() is True: - Data.packages_dictionary = get_pkg_upgrade_data() - StartCheckUpdate() - else: - Data.update_started = False - Data.stop_pkg_refreshing = False - if reboot is True: - RestartSystem() - else: - UpdateCompleted() - - -class FailedUpdate: - """ - FailedUpdate class for failed update window. - """ - - def get_detail(self, widget): - Popen(f'sudo -u {username} xdg-open {home}/update.failed', shell=True) - - def on_close(self, widget): - """ - The function to close the window. - :param widget: The window widget. - """ - if Data.close_session is True: - Gtk.main_quit() - else: - self.window.destroy() - - def __init__(self): - """ - The constructor of the FailedUpdate class. - """ - self.window = Gtk.Window() - self.window.set_position(Gtk.WindowPosition.CENTER) - # self.window.set_border_width(8) - self.window.connect("destroy", self.on_close) - self.window.set_title(_("Update Failed")) - self.window.set_default_icon_name('system-software-update') - v_box = Gtk.VBox(homogeneous=False, spacing=0) - self.window.add(v_box) - v_box.show() - label = Gtk.Label() - failed_text = _("""Press "Detail" to get information about the failure. - Get help at https://forums.ghostbsd.org.""") - label.set_markup(failed_text) - v_box.set_border_width(5) - v_box.pack_start(label, False, False, 5) - hBox = Gtk.HBox(homogeneous=False, spacing=0) - # hBox.set_border_width(5) - v_box.pack_start(hBox, False, True, 5) - hBox.show() - restart = Gtk.Button(label=_("Detail")) - restart.connect("clicked", self.get_detail) - continue_button = Gtk.Button(label=_("Close")) - continue_button.connect("clicked", self.on_close) - hBox.pack_end(continue_button, False, False, 5) - hBox.pack_end(restart, False, False, 5) - self.window.show_all() - - -class RestartSystem: - """ - RestartSystem class for restarting system window. - """ - def on_reboot(self, widget): - """ - The function to reboot the system. - :param widget: The window widget. - """ - Popen('shutdown -r now', shell=True) - Gtk.main_quit() - - def on_close(self, widget): - """ - The function to close the window. - :param widget: The window widget. - """ - if Data.close_session is True: - Gtk.main_quit() - else: - self.window.destroy() - - def __init__(self): - """ - The constructor of the RestartSystem class. - """ - self.window = Gtk.Window() - self.window.set_position(Gtk.WindowPosition.CENTER) - # self.window.set_border_width(8) - self.window.connect("destroy", self.on_close) - self.window.set_title(_("Update Completed")) - self.window.set_default_icon_name('system-software-update') - vBox = Gtk.VBox(homogeneous=False, spacing=0) - self.window.add(vBox) - vBox.show() - reboot_text = _("The computer needs to restart to run on the updated software.") - label = Gtk.Label(label=reboot_text) - vBox.set_border_width(5) - vBox.pack_start(label, False, False, 5) - hBox = Gtk.HBox(homogeneous=False, spacing=0) - # hBox.set_border_width(5) - vBox.pack_start(hBox, False, True, 5) - hBox.show() - restart = Gtk.Button(label=_("Restart Now")) - restart.connect("clicked", self.on_reboot) - continue_button = Gtk.Button(label=_("Restart Later")) - continue_button.connect("clicked", self.on_close) - hBox.pack_end(restart, False, False, 5) - hBox.pack_end(continue_button, False, False, 5) - self.window.show_all() - - -class UpdateCompleted: - """ - Class for update completed window. - """ - - def on_close(self, widget: Gtk.Widget): - """ - The function to close the window. - :param widget: The window widget. - """ - if Data.close_session is True: - Gtk.main_quit() - else: - self.window.destroy() - - def __init__(self): - """ - The constructor of the UpdateCompleted class. - """ - self.window = Gtk.Window() - self.window.set_position(Gtk.WindowPosition.CENTER) - self.window.connect("destroy", self.on_close) - self.window.set_title(_("Update Completed")) - self.window.set_default_icon_name('system-software-update') - v_box = Gtk.VBox(homogeneous=False, spacing=0) - self.window.add(v_box) - v_box.show() - label = Gtk.Label(label=_("""All software on this system is up to date.""")) - v_box.set_border_width(5) - v_box.pack_start(label, False, False, 5) - hBox = Gtk.HBox(homogeneous=False, spacing=0) - # hBox.set_border_width(5) - v_box.pack_start(hBox, False, True, 5) - hBox.show() - close_button = Gtk.Button(label=_("Close")) - close_button.connect("clicked", self.on_close) - hBox.pack_end(close_button, False, False, 5) - self.window.show_all() - - -class NoUpdateAvailable(object): - """ - Class for no update available window. - """ - - def __init__(self): - """ - The constructor of the NoUpdateAvailable class. - """ - window = Gtk.Window() - window.set_position(Gtk.WindowPosition.CENTER) - window.set_border_width(8) - window.connect("destroy", Gtk.main_quit) - window.set_title(_("No Update Available")) - box1 = Gtk.VBox(homogeneous=False, spacing=0) - window.add(box1) - box1.show() - box2 = Gtk.VBox(homogeneous=False, spacing=10) - box2.set_border_width(10) - box1.pack_start(box2, True, True, 0) - box2.show() - label = Gtk.Label(label=_("No update available. This system is up " - "to date.")) - box2.pack_start(label, False, False, 0) - box2 = Gtk.HBox(homogeneous=False, spacing=10) - box2.set_border_width(5) - box1.pack_start(box2, False, True, 0) - box2.show() - ok_button = Gtk.Button(label=_("Close")) - ok_button.connect("clicked", Gtk.main_quit) - box2.pack_end(ok_button, False, False, 0) - window.show_all() - - -class StartCheckUpdate: - """ - Class for start check for update window. - """ - def close_application(self, widget: Gtk.Widget): - """ - The function to close the window. - :param widget: The window widget. - """ - if updating(): - unlock_update_station() - Gtk.main_quit() - - def __init__(self): - """ - The constructor of the StartCheckUpdate class. - """ - self.win = Gtk.Window() - self.win.connect("delete-event", self.close_application) - self.win.set_size_request(500, 75) - self.win.set_resizable(False) - self.win.set_title(_("Looking For Updates")) - self.win.set_border_width(0) - self.win.set_position(Gtk.WindowPosition.CENTER) - self.win.set_default_icon_name('system-software-update') - box1 = Gtk.VBox(homogeneous=False, spacing=0) - self.win.add(box1) - box1.show() - box2 = Gtk.VBox(homogeneous=False, spacing=10) - box2.set_border_width(10) - box1.pack_start(box2, True, True, 0) - box2.show() - self.pbar = Gtk.ProgressBar() - self.pbar.set_show_text(True) - self.pbar.set_fraction(0.0) - box2.pack_start(self.pbar, False, False, 0) - self.win.show_all() - self.thr = threading.Thread( - target=self.check_for_update, - args=[self.pbar], - daemon=True - ) - self.thr.start() - - def update_progress(self, progress: Gtk.ProgressBar, text: str): - """ - The function to update the progress bar. - :param progress: The progress bar. - :param text: the text to be displayed on the progress bar. - """ - progress.set_text(text) - fraction = progress.get_fraction() + 0.2 - progress.set_fraction(fraction) - - def check_for_update(self, progress: Gtk.ProgressBar): - """ - The function to check for update and update the progress bar. - :param progress: The progress bar. - """ - GLib.idle_add(self.update_progress, progress, - _('Checking if the repository is online')) - sleep(1) - if network_stat() == 'UP' and repo_online() is True: - GLib.idle_add(self.update_progress, progress, - _('The repository is online')) - sleep(1) - if repository_is_syncing() is True: - GLib.idle_add(self.update_progress, progress, - _('The mirror is Syncing')) - GLib.idle_add(self.stop_tread, MirrorSyncing) - else: - if updating(): - GLib.idle_add(self.update_progress, progress, - _('Updates are already running')) - GLib.idle_add(self.stop_tread, UpdateStationOpen) - else: - GLib.idle_add(self.update_progress, progress, - _('Checking for updates')) - update_available = check_for_update() - if update_available: - GLib.idle_add(self.update_progress, progress, - _('Getting the list of packages')) - Data.packages_dictionary = get_pkg_upgrade_data() - look_update_station() - GLib.idle_add(self.update_progress, progress, - _('Open the update window')) - GLib.idle_add(self.stop_tread, UpdateWindow) - elif not update_available and update_available is not None: - GLib.idle_add(self.update_progress, progress, - _('No update found')) - GLib.idle_add(self.stop_tread, NoUpdateAvailable) - else: - GLib.idle_add(self.stop_tread, SomethingIsWrong) - else: - GLib.idle_add(self.update_progress, progress, - _('The Mirror is unreachable')) - GLib.idle_add(self.stop_tread, ServerUnreachable) - - def stop_tread(self, start_window: object): - """ - The function to stop the thread. - :param start_window: The start window object. - """ - start_window() - self.win.hide() - - -class UpdateStationOpen(object): - """ - Class for update station already started window. - """ - def on_close(self, widget): - """ - The function to close the window. - :param widget: The window widget. - """ - if Data.close_session is True: - Gtk.main_quit() - else: - self.window.destroy() - - def __init__(self): - """ - The constructor of the UpdateStationOpen class. - """ - self.window = Gtk.Window() - self.window.set_position(Gtk.WindowPosition.CENTER) - self.window.set_border_width(8) - self.window.connect("destroy", self.on_close) - self.window.set_title(_("Update Station already started")) - box1 = Gtk.VBox(homogeneous=False, spacing=0) - self.window.add(box1) - box1.show() - box2 = Gtk.VBox(homogeneous=False, spacing=10) - box2.set_border_width(10) - box1.pack_start(box2, True, True, 0) - box2.show() - label = Gtk.Label(label=_("Update Station already open.")) - box2.pack_start(label, False, False, 0) - box2 = Gtk.HBox(homogeneous=False, spacing=10) - box2.set_border_width(5) - box1.pack_start(box2, False, True, 0) - box2.show() - ok_button = Gtk.Button(label=_("Close")) - ok_button.connect("clicked", self.on_close) - box2.pack_end(ok_button, False, False, 0) - self.window.show_all() - - -class MirrorSyncing(object): - """ - Class for the mirror is syncing warning window. - """ - - def __init__(self): - """ - The constructor of the MirrorSyncing class. - """ - window = Gtk.Window() - window.set_position(Gtk.WindowPosition.CENTER) - window.set_border_width(8) - window.connect("destroy", Gtk.main_quit) - window.set_title(_("Server Unreachable")) - box1 = Gtk.VBox(homogeneous=False, spacing=0) - window.add(box1) - box1.show() - box2 = Gtk.VBox(homogeneous=False, spacing=10) - box2.set_border_width(10) - box1.pack_start(box2, True, True, 0) - box2.show() - label = Gtk.Label(label=_("Packages mirrors are syncing with new " - "packages")) - box2.pack_start(label, False, False, 0) - box2 = Gtk.HBox(homogeneous=False, spacing=10) - box2.set_border_width(5) - box1.pack_start(box2, False, True, 0) - box2.show() - ok_button = Gtk.Button(label=_("Close")) - ok_button.connect("clicked", Gtk.main_quit) - box2.pack_end(ok_button, False, False, 0) - window.show_all() - - -class ServerUnreachable(object): - """ - Class for the server unreachable warning window. - """ - - def __init__(self): - """ - The constructor of the ServerUnreachable class. - """ - window = Gtk.Window() - window.set_position(Gtk.WindowPosition.CENTER) - window.set_border_width(8) - window.connect("destroy", Gtk.main_quit) - window.set_title(_("Server Unreachable")) - box1 = Gtk.VBox(homogeneous=False, spacing=0) - window.add(box1) - box1.show() - box2 = Gtk.VBox(homogeneous=False, spacing=10) - box2.set_border_width(10) - box1.pack_start(box2, True, True, 0) - box2.show() - label = Gtk.Label(label=_("The server is unreachable. Your internet " - "could\nbe down or software package server is down.")) - box2.pack_start(label, False, False, 0) - box2 = Gtk.HBox(homogeneous=False, spacing=10) - box2.set_border_width(5) - box1.pack_start(box2, False, True, 0) - box2.show() - ok_button = Gtk.Button(label=_("Close")) - ok_button.connect("clicked", Gtk.main_quit) - box2.pack_end(ok_button, False, False, 0) - window.show_all() - - -class SomethingIsWrong(object): - """ - Class for the something is wrong warning window. - """ - - def __init__(self): - """ - The constructor of the SomethingIsWrong class. - """ - window = Gtk.Window() - window.set_position(Gtk.WindowPosition.CENTER) - window.set_border_width(8) - window.connect("destroy", Gtk.main_quit) - window.set_title(_("Something Is Wrong")) - box1 = Gtk.VBox(homogeneous=False, spacing=0) - window.add(box1) - box1.show() - box2 = Gtk.VBox(homogeneous=False, spacing=10) - box2.set_border_width(10) - box1.pack_start(box2, True, True, 0) - box2.show() - label = Gtk.Label(label=_( - "If you see this message it means that " - "something is wrong.\n Please look at pkg upgrade " - "output.")) - box2.pack_start(label, False, False, 0) - box2 = Gtk.HBox(homogeneous=False, spacing=10) - box2.set_border_width(5) - box1.pack_start(box2, False, True, 0) - box2.show() - ok_button = Gtk.Button(label=_("Close")) - ok_button.connect("clicked", Gtk.main_quit) - box2.pack_end(ok_button, False, False, 0) - window.show_all() - - -class NotRoot(Gtk.Window): - """ - Class for the user is not root warning window. - """ - def __init__(self): - """ - The constructor of the NotRoot class. - """ - Gtk.Window.__init__(self) - self.set_title(_("Software Station")) - self.connect("delete-event", Gtk.main_quit) - self.set_size_request(200, 80) - box1 = Gtk.VBox(homogeneous=False, spacing=0) - self.add(box1) - box1.show() - label = Gtk.Label(label=_('You need to be root')) - box1.pack_start(label, True, True, 0) - hBox = Gtk.HBox(homogeneous=False, spacing=0) - hBox.show() - box1.pack_end(hBox, False, False, 5) - ok_button = Gtk.Button() - ok_button.set_label(_("OK")) - apply_img = Gtk.Image() - apply_img.set_from_icon_name('gtk-ok') - ok_button.set_image(apply_img) - ok_button.connect("clicked", Gtk.main_quit) - hBox.pack_end(ok_button, False, False, 5) - self.show_all() - - -arg = sys.argv -UsageMSG = f""" -Usage for {arg[0]}: - -Available Commands: - -check-now - Look for update now - -""" - - -if os.geteuid() == 0: - if len(arg) == 1: - if socket.gethostname() != 'livecd': - Data.close_session = False - tray = TrayIcon() - tray.threading_update() - else: - exit() - elif len(arg) == 2 and arg[1] == "check-now": - Data.close_session = True - StartCheckUpdate() - else: - print(UsageMSG) - sys.exit(0) -else: - NotRoot() -Gtk.main() diff --git a/update-station b/update-station new file mode 100755 index 0000000..c7c33a1 --- /dev/null +++ b/update-station @@ -0,0 +1,110 @@ +#!/usr/local/bin/python +"""This is the main file for the update-station application""" + +import os +import gettext +import gi +import socket +import sys +import threading +gi.require_version('Gtk', '3.0') +gi.require_version('Notify', '0.7') +from gi.repository import Gtk, GLib +from time import sleep +from update_station.notification import UpdateNotifier, TrayIcon +from update_station.frontend import StartCheckUpdate +from update_station.dialog import NotRoot +from update_station.data import Data +from update_station.backend import ( + check_for_update, + unlock_update_station, + updating, + repository_is_syncing, + is_major_upgrade_available, + get_abi_upgrade, + get_current_abi, +) + +gettext.bindtextdomain('update-station', '/usr/local/share/locale') +gettext.textdomain('update-station') +_ = gettext.gettext + + +def update_tray(): + """ + Function that updates the tray icon. + """ + if check_for_update(): + Data.system_tray.tray_icon().set_visible(True) + print('system_tray') + notifier = UpdateNotifier() + notifier.notify() + print('notifier') + elif is_major_upgrade_available() and Data.do_not_upgrade is False: + Data.major_upgrade = True + Data.system_tray.tray_icon().set_visible(True) + Data.current_abi = get_current_abi() + Data.new_abi = get_abi_upgrade() + notifier = UpdateNotifier() + notifier.notify() + else: + Data.system_tray.tray_icon().set_visible(False) + + +def threading_update(): + """ + Function that creates a thread that checks for updates. + """ + if updating(): + unlock_update_station() + thr = threading.Thread(target=check, daemon=True) + thr.start() + + +def check(): + """ + Function that checks for updates. + """ + while True: + sleep(20) + if not repository_is_syncing(): + if not Data.stop_pkg_refreshing: + if not updating(): + GLib.idle_add(update_tray) + else: + print('True') + GLib.idle_add(Data.system_tray.tray_icon().set_visible, False) + # Wait for an hour to look for update + sleep(600) + + +def check_now(): + pass + + +arg = sys.argv +UsageMSG = f""" +Usage for {arg[0]}: + +Available Commands: + +check-now - Look for update now + +""" +if os.geteuid() == 0: + if len(arg) == 1: + if socket.gethostname() != 'livecd': + Data.close_session = False + Data.system_tray = TrayIcon() + threading_update() + else: + exit() + elif len(arg) == 2 and arg[1] == "check-now": + Data.close_session = True + StartCheckUpdate() + else: + print(UsageMSG) + sys.exit(0) +else: + NotRoot() +Gtk.main() diff --git a/update_station/__init__.py b/update_station/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/updateHandler.py b/update_station/backend.py similarity index 88% rename from src/updateHandler.py rename to update_station/backend.py index 5de0c48..50e0fe9 100755 --- a/src/updateHandler.py +++ b/update_station/backend.py @@ -1,15 +1,53 @@ #!/usr/local/bin/python -"""All functions to handle various updates for GhostBSD.""" +"""All functions to handle various command for Update Station.""" import os +import sys import socket import requests -from update_data import Data +from gi.repository import Gtk +from update_station.data import Data from subprocess import Popen, PIPE, call, run, CompletedProcess -update_station_db = '/var/db/update-station' -pkg_lock_file = f'{update_station_db}/lock-pkgs' -updates_run = '/tmp/update-station' +lib_path: str = f'{sys.prefix}/lib/update-station' +update_station_db: str = '/var/db/update-station' +pkg_lock_file: str = f'{update_station_db}/lock-pkgs' +updates_run: str = '/tmp/update-station' + + +def read_file(file_path: str) -> str: + """ + Read a file and return the contents. + :param file_path: The file path. + :return: The file contents. + """ + with open(file_path, 'r') as file: + return file.read() + + +def on_reboot(*args) -> None: + """ + The function to reboot the system. + """ + Popen('shutdown -r now', shell=True) + Gtk.main_quit() + + +def get_detail() -> None: + """ + Get the details of the upgrade failure. + :return: + """ + Popen(f'sudo -u {Data.username} xdg-open {Data.home}/update.failed', shell=True) + + +def get_packages_to_reinstall() -> list: + """ + Get packages to reinstall on kernel upgrade. + :return: The list of packages to reinstall. + """ + packages = read_file(f'../src/pkg_to_reinstall').replace('\n', ' ') + return run_command(f'pkg query "%n" {packages}').stdout.splitlines() def run_command(command: str, check: bool = False) -> CompletedProcess: @@ -21,7 +59,7 @@ def run_command(command: str, check: bool = False) -> CompletedProcess: :return: The CompletedProcess object. """ - process = run(command, shell=True, stdout=PIPE, stderr=PIPE) + process = run(command, shell=True, stdout=PIPE, stderr=PIPE, universal_newlines=True) if check and process.returncode != 0: raise RuntimeError(f"Command failed: {command}\n{process.stderr}") return process diff --git a/update_station/common.py b/update_station/common.py new file mode 100644 index 0000000..0da2239 --- /dev/null +++ b/update_station/common.py @@ -0,0 +1,30 @@ +"""This module contains common functions to avoid code duplication.""" + +from gi.repository import Gtk +from update_station.data import Data + + +def update_progress(progress, fraction, text): + """ + Function that updates the progress bar. + :param progress: The progress bar. + :param fraction: The fraction to add. + :param text: The text to display. + """ + new_val = progress.get_fraction() + fraction + progress.set_fraction(new_val) + progress.set_text(text) + + +def on_close(*args): + """ + This function to close the window or quit the application if Data.close_session is True. + :param args: Additional arguments. The last argument must be the Gtk.Window object. + """ + window = args[-1] + # Ensure the last argument is a Gtk.Window + assert isinstance(window, Gtk.Window), "The last argument must be a Gtk.Window" + if Data.close_session is True: + Gtk.main_quit() + else: + window.destroy() diff --git a/src/update_data.py b/update_station/data.py similarity index 67% rename from src/update_data.py rename to update_station/data.py index 04beb20..69546ad 100755 --- a/src/update_data.py +++ b/update_station/data.py @@ -1,29 +1,40 @@ +import getpass +import os + + class Data: """ - Cla + Class that contains all the data that is used by the update-station. + Attributes: backup: Boolean that indicates if the update-station should back up the current boot environment. close_session: Boolean that indicates if the update-station should close the session. current_abi: String that indicates the current ABI of the system. do_not_upgrade: Boolean that indicates if the update-station should not upgrade the system. + home: String that indicates the home directory of the user that is running the update-station. + kernel_upgrade: Boolean that indicates if the update-station should upgrade the kernel. packages_dictionary: Dictionary that contains all the packages that are installed on the system. major_upgrade: Boolean that indicates if the update-station should do a major upgrade. new_abi: String that indicates the new ABI of the system. second_update: Boolean that indicates if the update-station should do 2 update. stop_pkg_refreshing: Boolean that indicates if the update-station should stop refreshing the packages. + system_tray: Object that contains the system tray of the update-station. total_packages: Integer that indicates the total number of packages that are that will be updated. update_started: Boolean that indicates if the application has started updating the system. + username: String that indicates the username of the user that is running the update-station. """ backup: bool = False close_session: bool = False - pkgbase_upgrade_shown: bool = False current_abi: str = '' do_not_upgrade: bool = False + home: str = os.path.expanduser('~') kernel_upgrade: bool = False major_upgrade: bool = False new_abi: str = '' packages_dictionary: dict = {} second_update: bool = False stop_pkg_refreshing: bool = False + system_tray = None total_packages: int = 0 - update_started: bool = False \ No newline at end of file + update_started: bool = False + username: str = os.environ.get('SUDO_USER') if 'SUDO_USER' in os.environ else getpass.getuser() diff --git a/update_station/dialog.py b/update_station/dialog.py new file mode 100644 index 0000000..7a3e9c0 --- /dev/null +++ b/update_station/dialog.py @@ -0,0 +1,316 @@ +#!/usr/local/bin/python + +import gi +import gettext +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +from update_station.common import on_close +from update_station.backend import ( + get_detail, + on_reboot +) + +gettext.bindtextdomain('update-station', '/usr/local/share/locale') +gettext.textdomain('update-station') +_ = gettext.gettext + + +class FailedUpdate: + """ + FailedUpdate class for failed update window. + """ + + def __init__(self): + """ + The constructor of the FailedUpdate class. + """ + window = Gtk.Window() + window.set_position(Gtk.WindowPosition.CENTER) + # self.window.set_border_width(8) + window.connect("delete-event", on_close, window) + window.set_title(_("Update Failed")) + window.set_default_icon_name('system-software-update') + v_box = Gtk.VBox(homogeneous=False, spacing=0) + window.add(v_box) + v_box.show() + label = Gtk.Label() + failed_text = _("""Press "Detail" to get information about the failure. + Get help at https://forums.ghostbsd.org or .""") + label.set_markup(failed_text) + v_box.set_border_width(5) + v_box.pack_start(label, False, False, 5) + h_box = Gtk.HBox(homogeneous=False, spacing=0) + # hBox.set_border_width(5) + v_box.pack_start(h_box, False, True, 5) + h_box.show() + restart = Gtk.Button(label=_("Detail")) + restart.connect("clicked", get_detail) + continue_button = Gtk.Button(label=_("Close")) + continue_button.connect("clicked", on_close, window) + h_box.pack_end(continue_button, False, False, 5) + h_box.pack_end(restart, False, False, 5) + window.show_all() + + +class RestartSystem: + """ + RestartSystem class for restarting system window. + """ + + def __init__(self): + """ + The constructor of the RestartSystem class. + """ + window = Gtk.Window() + window.set_position(Gtk.WindowPosition.CENTER) + # self.window.set_border_width(8) + window.connect("destroy", on_close, window) + window.set_title(_("Update Completed")) + window.set_default_icon_name('system-software-update') + v_box = Gtk.VBox(homogeneous=False, spacing=0) + window.add(v_box) + v_box.show() + reboot_text = _("The computer needs to restart to run on the updated software.") + label = Gtk.Label(label=reboot_text) + v_box.set_border_width(5) + v_box.pack_start(label, False, False, 5) + h_box = Gtk.HBox(homogeneous=False, spacing=0) + # h_box.set_border_width(5) + v_box.pack_start(h_box, False, True, 5) + h_box.show() + restart = Gtk.Button(label=_("Restart Now")) + restart.connect("clicked", on_reboot) + continue_button = Gtk.Button(label=_("Restart Later")) + continue_button.connect("clicked", on_close, window) + h_box.pack_end(restart, False, False, 5) + h_box.pack_end(continue_button, False, False, 5) + window.show_all() + + +class UpdateCompleted: + """ + Class for update completed window. + """ + + def __init__(self): + """ + The constructor of the UpdateCompleted class. + """ + window = Gtk.Window() + window.set_position(Gtk.WindowPosition.CENTER) + window.connect("destroy", on_close, window) + window.set_title(_("Update Completed")) + window.set_default_icon_name('system-software-update') + v_box = Gtk.VBox(homogeneous=False, spacing=0) + window.add(v_box) + v_box.show() + label = Gtk.Label(label=_("""All software on this system is up to date.""")) + v_box.set_border_width(5) + v_box.pack_start(label, False, False, 5) + h_box = Gtk.HBox(homogeneous=False, spacing=0) + # h_box.set_border_width(5) + v_box.pack_start(h_box, False, True, 5) + h_box.show() + close_button = Gtk.Button(label=_("Close")) + close_button.connect("clicked", on_close, window) + h_box.pack_end(close_button, False, False, 5) + window.show_all() + + +class NoUpdateAvailable(object): + """ + Class for no update available window. + """ + + def __init__(self): + """ + The constructor of the NoUpdateAvailable class. + """ + window = Gtk.Window() + window.set_position(Gtk.WindowPosition.CENTER) + window.set_border_width(8) + window.connect("destroy", on_close, window) + window.set_title(_("No Update Available")) + box1 = Gtk.VBox(homogeneous=False, spacing=0) + window.add(box1) + box1.show() + box2 = Gtk.VBox(homogeneous=False, spacing=10) + box2.set_border_width(10) + box1.pack_start(box2, True, True, 0) + box2.show() + label = Gtk.Label(label=_("No update available. This system is up " + "to date.")) + box2.pack_start(label, False, False, 0) + box2 = Gtk.HBox(homogeneous=False, spacing=10) + box2.set_border_width(5) + box1.pack_start(box2, False, True, 0) + box2.show() + ok_button = Gtk.Button(label=_("Close")) + ok_button.connect("clicked", on_close, window) + box2.pack_end(ok_button, False, False, 0) + window.show_all() + + +class UpdateStationOpen(object): + """ + Class for update station already started window. + """ + + def __init__(self): + """ + The constructor of the UpdateStationOpen class. + """ + window = Gtk.Window() + window.set_position(Gtk.WindowPosition.CENTER) + window.set_border_width(8) + window.connect("destroy", on_close, window) + window.set_title(_("Update Station already started")) + box1 = Gtk.VBox(homogeneous=False, spacing=0) + window.add(box1) + box1.show() + box2 = Gtk.VBox(homogeneous=False, spacing=10) + box2.set_border_width(10) + box1.pack_start(box2, True, True, 0) + box2.show() + label = Gtk.Label(label=_("Update Station already open.")) + box2.pack_start(label, False, False, 0) + box2 = Gtk.HBox(homogeneous=False, spacing=10) + box2.set_border_width(5) + box1.pack_start(box2, False, True, 0) + box2.show() + ok_button = Gtk.Button(label=_("Close")) + ok_button.connect("clicked", on_close, window) + box2.pack_end(ok_button, False, False, 0) + window.show_all() + + +class MirrorSyncing(object): + """ + Class for the mirror is syncing warning window. + """ + + def __init__(self): + """ + The constructor of the MirrorSyncing class. + """ + window = Gtk.Window() + window.set_position(Gtk.WindowPosition.CENTER) + window.set_border_width(8) + window.connect("destroy", on_close, window) + window.set_title(_("Server Unreachable")) + box1 = Gtk.VBox(homogeneous=False, spacing=0) + window.add(box1) + box1.show() + box2 = Gtk.VBox(homogeneous=False, spacing=10) + box2.set_border_width(10) + box1.pack_start(box2, True, True, 0) + box2.show() + label = Gtk.Label(label=_("Packages mirrors are syncing with new " + "packages")) + box2.pack_start(label, False, False, 0) + box2 = Gtk.HBox(homogeneous=False, spacing=10) + box2.set_border_width(5) + box1.pack_start(box2, False, True, 0) + box2.show() + ok_button = Gtk.Button(label=_("Close")) + ok_button.connect("clicked", on_close, window) + box2.pack_end(ok_button, False, False, 0) + window.show_all() + + +class ServerUnreachable(object): + """ + Class for the server unreachable warning window. + """ + + def __init__(self): + """ + The constructor of the ServerUnreachable class. + """ + window = Gtk.Window() + window.set_position(Gtk.WindowPosition.CENTER) + window.set_border_width(8) + window.connect("destroy", on_close, window) + window.set_title(_("Server Unreachable")) + box1 = Gtk.VBox(homogeneous=False, spacing=0) + window.add(box1) + box1.show() + box2 = Gtk.VBox(homogeneous=False, spacing=10) + box2.set_border_width(10) + box1.pack_start(box2, True, True, 0) + box2.show() + label = Gtk.Label(label=_("The server is unreachable. Your internet " + "could\nbe down or software package server is down.")) + box2.pack_start(label, False, False, 0) + box2 = Gtk.HBox(homogeneous=False, spacing=10) + box2.set_border_width(5) + box1.pack_start(box2, False, True, 0) + box2.show() + ok_button = Gtk.Button(label=_("Close")) + ok_button.connect("clicked", on_close, window) + box2.pack_end(ok_button, False, False, 0) + window.show_all() + + +class SomethingIsWrong(object): + """ + Class for the something is wrong warning window. + """ + + def __init__(self): + """ + The constructor of the SomethingIsWrong class. + """ + window = Gtk.Window() + window.set_position(Gtk.WindowPosition.CENTER) + window.set_border_width(8) + window.connect("destroy", Gtk.main_quit) + window.set_title(_("Something Is Wrong")) + box1 = Gtk.VBox(homogeneous=False, spacing=0) + window.add(box1) + box1.show() + box2 = Gtk.VBox(homogeneous=False, spacing=10) + box2.set_border_width(10) + box1.pack_start(box2, True, True, 0) + box2.show() + label = Gtk.Label(label=_( + "If you see this message it means that " + "something is wrong.\n Please look at pkg upgrade " + "output.")) + box2.pack_start(label, False, False, 0) + box2 = Gtk.HBox(homogeneous=False, spacing=10) + box2.set_border_width(5) + box1.pack_start(box2, False, True, 0) + box2.show() + ok_button = Gtk.Button(label=_("Close")) + ok_button.connect("clicked", Gtk.main_quit) + box2.pack_end(ok_button, False, False, 0) + window.show_all() + + +class NotRoot: + """ + Class for the user is not root warning window. + """ + + def __init__(self): + """ + The constructor of the NotRoot class. + """ + window = Gtk.Window() + window.set_title(_("Software Station")) + window.connect("destroy", on_close, window) + window.set_size_request(300, 80) + box1 = Gtk.VBox(homogeneous=False, spacing=0) + window.add(box1) + box1.show() + label = Gtk.Label(label=_('You need to be root')) + box1.pack_start(label, True, True, 0) + h_box = Gtk.HBox(homogeneous=False, spacing=0) + h_box.show() + box1.pack_end(h_box, False, False, 5) + ok_button = Gtk.Button() + ok_button.set_label(_("Close")) + ok_button.connect("clicked", on_close, window) + h_box.pack_end(ok_button, False, False, 5) + window.show_all() diff --git a/update_station/frontend.py b/update_station/frontend.py new file mode 100644 index 0000000..ea69ef6 --- /dev/null +++ b/update_station/frontend.py @@ -0,0 +1,624 @@ +import bectl +import datetime +import distro +import gettext +import json +import re +import sys +import threading +from gi.repository import Gtk, GLib +from subprocess import Popen, PIPE, run +from time import sleep +from update_station.common import update_progress +from update_station.data import Data +from update_station.dialog import FailedUpdate +from update_station.dialog import ( + MirrorSyncing, + UpdateStationOpen, + NoUpdateAvailable, + ServerUnreachable, + SomethingIsWrong, + UpdateCompleted, + RestartSystem +) +from update_station.backend import ( + check_for_update, + get_packages_to_reinstall, + get_pkg_upgrade_data, + unlock_update_station, + updating, + look_update_station, + repo_online, + repository_is_syncing, + network_stat +) + +gettext.bindtextdomain('update-station', '/usr/local/share/locale') +gettext.textdomain('update-station') +_ = gettext.gettext + +lib_path: str = f'{sys.prefix}/lib/update-station' + + +class UpdateWindow: + """ + Class that creates the main window to see update list and start the update process. + """ + def delete_event(self, widget: Gtk.Widget) -> None: + """ + Function that handles the delete event when the window is closed. + :param widget: The widget that triggered the delete event. + """ + if Data.close_session is True: + if updating(): + unlock_update_station() + Gtk.main_quit() + else: + self.window.destroy() + if Data.update_started is False: + Data.stop_pkg_refreshing = False + if updating(): + unlock_update_station() + Data.system_tray.tray_icon().set_visible(True) + + def start_update(self, widget): + """ + Function that starts the update process. + :param widget: The widget that triggered the start update event. + """ + Data.update_started = True + InstallUpdate() + self.window.hide() + + def if_backup(self, widget): + """ + Function that handles the backup checkbox. + :param widget: The widget that triggered the checkbox event. + """ + Data.backup = widget.get_active() + + def create_bbox(self): + """ + Function that creates the button box. + :return: The button box. + """ + table = Gtk.Table( + n_rows=1, + n_columns=5, + homogeneous=False, + column_spacing=5 + ) + backup_checkbox = Gtk.CheckButton( + label=_("Create boot environment backup") + ) + table.attach(backup_checkbox, 0, 1, 0, 1) + backup_checkbox.connect("toggled", self.if_backup) + if bectl.is_file_system_zfs() and Data.second_update is False: + backup_checkbox.set_active(True) + backup_checkbox.set_sensitive(True) + Data.backup = True + else: + backup_checkbox.set_active(False) + backup_checkbox.set_sensitive(False) + Data.backup = False + img = Gtk.Image(icon_name='window-close') + close_button = Gtk.Button(label=_("Close")) + close_button.set_image(img) + table.attach(close_button, 3, 4, 0, 1) + close_button.connect("clicked", self.delete_event) + install_button = Gtk.Button(label=_("Install update")) + table.attach(install_button, 4, 5, 0, 1) + install_button.connect("clicked", self.start_update) + return table + + def __init__(self): + """ + The constructor for the UpdateWindow class. + """ + self.window = Gtk.Window() + self.window.connect("destroy", self.delete_event) + self.window.set_size_request(700, 400) + self.window.set_resizable(False) + self.window.set_title(_("Update Manager")) + self.window.set_border_width(0) + self.window.set_position(Gtk.WindowPosition.CENTER) + self.window.set_default_icon_name('system-software-update') + box1 = Gtk.VBox(homogeneous=False, spacing=0) + self.window.add(box1) + box1.show() + box2 = Gtk.VBox(homogeneous=False, spacing=0) + box2.set_border_width(20) + box1.pack_start(box2, True, True, 0) + box2.show() + # Title + title_text = _("Updates available!") + + update_title_label = Gtk.Label( + label=f"{title_text}" + ) + update_title_label.set_use_markup(True) + box2.pack_start(update_title_label, False, False, 0) + self.tree_store = Gtk.TreeStore(str, bool) + sw = Gtk.ScrolledWindow() + sw.set_shadow_type(Gtk.ShadowType.ETCHED_IN) + sw.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) + self.view = Gtk.TreeView(model=self.store()) + self.renderer = Gtk.CellRendererText() + self.column0 = Gtk.TreeViewColumn("Name", self.renderer, text=0) + self.view.append_column(self.column0) + self.view.set_headers_visible(False) + sw.add(self.view) + sw.show() + box2.pack_start(sw, True, True, 10) + box2 = Gtk.HBox(homogeneous=False, spacing=10) + box2.set_border_width(5) + box1.pack_start(box2, False, False, 5) + box2.show() + # Add button + box2.pack_start(self.create_bbox(), True, True, 10) + self.window.show_all() + + def store(self): + """ + Function that creates the store for the list of package in the treeview. + :return: The store for the list of package to be updated. + """ + self.tree_store.clear() + r_num = 0 + u_num = 0 + i_num = 0 + ri_num = 0 + if bool(Data.packages_dictionary['remove']): + r_num = len(Data.packages_dictionary['remove']) + message = _('Installed packages to be REMOVED:') + message += f' {r_num}' + r_pinter = self.tree_store.append(None, (message, True)) + for line in Data.packages_dictionary['remove']: + self.tree_store.append(r_pinter, (line, True)) + if bool(Data.packages_dictionary['upgrade']): + u_num = len(Data.packages_dictionary['upgrade']) + message = _('Installed packages to be UPGRADED') + message += f' {u_num}' + u_pinter = self.tree_store.append(None, (message, True)) + for line in Data.packages_dictionary['upgrade']: + self.tree_store.append(u_pinter, (line, True)) + if bool(Data.packages_dictionary['install']): + i_num = len(Data.packages_dictionary['install']) + message = _('New packages to be INSTALLED:') + message += f' {i_num}' + i_pinter = self.tree_store.append(None, (message, True)) + for line in Data.packages_dictionary['install']: + self.tree_store.append(i_pinter, (line, True)) + if bool(Data.packages_dictionary['reinstall']): + ri_num = len(Data.packages_dictionary['reinstall']) + message = _('Installed packages to be REINSTALLED:') + message += f' {ri_num}' + ri_pinter = self.tree_store.append(None, (message, True)) + for line in Data.packages_dictionary['reinstall']: + self.tree_store.append(ri_pinter, (line, True)) + Data.total_packages = r_num + u_num + i_num + ri_num + return self.tree_store + + def display(self, model: Gtk.TreeStore) -> Gtk.TreeView: + """ + Function that creates the treeview. + + :param model: The store for the list of package to be updated. + :return: The treeview. + """ + self.view = Gtk.TreeView(model=model) + self.renderer = Gtk.CellRendererText() + self.column0 = Gtk.TreeViewColumn("Name", self.renderer, text=0) + self.view.append_column(self.column0) + self.view.set_headers_visible(False) + return self.view + + +class InstallUpdate: + """ + The class for the window that is displayed the progress of the update. + """ + def close_application(self, widget): + if updating(): + unlock_update_station() + Gtk.main_quit() + + def __init__(self): + """ + The constructor for the InstallUpdate class. + """ + self.win = Gtk.Window() + self.win.connect("delete-event", self.close_application) + self.win.set_size_request(500, 75) + self.win.set_resizable(False) + self.win.set_title(_("Installing Update")) + self.win.set_border_width(0) + self.win.set_position(Gtk.WindowPosition.CENTER) + self.win.set_default_icon_name('system-software-update') + box1 = Gtk.VBox(homogeneous=False, spacing=0) + self.win.add(box1) + box1.show() + box2 = Gtk.VBox(homogeneous=False, spacing=10) + box2.set_border_width(10) + box1.pack_start(box2, True, True, 0) + box2.show() + self.pbar = Gtk.ProgressBar() + self.pbar.set_show_text(True) + self.pbar.set_fraction(0.0) + # self.pbar.set_size_request(-1, 20) + box2.pack_start(self.pbar, False, False, 0) + self.win.show_all() + self.thr = threading.Thread(target=self.read_output, args=[self.pbar], daemon=True) + self.thr.start() + + def read_output(self, progress): + """ + Function that reads the output of the update to update the progress bar. + :param progress: The progress bar. + """ + fail = False + update_pkg = False + packages = '' + env = f'env ABI={Data.new_abi} ' if Data.major_upgrade else '' + need_reboot_packages = set(json.loads(open(f'{lib_path}/need_reboot.json').read())) + upgrade_packages = set(re.findall(r"(\S+):", " ".join(Data.packages_dictionary['upgrade']))) + reboot = bool(need_reboot_packages.intersection(upgrade_packages)) + if len(Data.packages_dictionary['upgrade']) == 1 and 'pkg:' in Data.packages_dictionary['upgrade'][0]: + update_pkg = True + packages = ' pkg' + Data.second_update = True + else: + Data.second_update = False + howmany = (Data.total_packages * 5) + 25 + fraction = 1.0 / howmany + + # TODO: make a function for this part. + if Data.backup: + today = datetime.datetime.now().strftime("%Y-%m-%d") + txt = _("Cleaning old boot environment") + GLib.idle_add(update_progress, progress, fraction, txt) + for be in bectl.get_be_list(): + if 'backup' in be and today not in be and 'NR' not in be: + bectl.destroy_be(be.split()[0]) + backup_name = datetime.datetime.now().strftime(f"{distro.version()}-backup-%Y-%m-%d-%H-%M") + txt = _("Creating boot environment") + txt += f" {backup_name}" + GLib.idle_add(update_progress, progress, fraction, txt) + bectl.create_be(new_be_name=backup_name) + sleep(1) + + if Data.major_upgrade: + txt = _("Setting env and bootstrap pkg to upgrade") + GLib.idle_add(update_progress, progress, fraction, txt) + fetch = Popen( + f'{env}env IGNORE_OSVERSION=yes ASSUME_ALWAYS_YES=yes pkg bootstrap -f', + shell=True, + stdout=PIPE, + stderr=PIPE, + close_fds=True, + universal_newlines=True + ) + fetch_text = "" + while True: + stdout_line = fetch.stdout.readline() + if fetch.poll() is not None: + break + fetch_text += stdout_line + GLib.idle_add(update_progress, progress, fraction, + stdout_line.strip()) + if fetch.returncode != 0: + stderr_line = fetch.stderr.read() + fetch_text += stderr_line + update_fail = open(f'{Data.home}/update.failed', 'w') + update_fail.writelines(fetch_text) + update_fail.close() + fail = True + GLib.idle_add(self.win.destroy) + GLib.idle_add(self.stop_tread, fail, update_pkg, reboot) + return + txt = _("Downloading packages to upgrade") + GLib.idle_add(update_progress, progress, fraction, txt) + sleep(1) + fetch = Popen( + f'{env}pkg-static upgrade -Fy{packages}', + shell=True, + stdout=PIPE, + stderr=PIPE, + close_fds=True, + universal_newlines=True + ) + fetch_text = "" + while True: + stdout_line = fetch.stdout.readline() + if fetch.poll() is not None: + break + fetch_text += stdout_line + GLib.idle_add(update_progress, progress, fraction, + stdout_line.strip()) + if fetch.returncode != 0: + stderr_line = fetch.stderr.read() + fetch_text += stderr_line + update_fail = open(f'{Data.home}/update.failed', 'w') + update_fail.writelines(fetch_text) + update_fail.close() + fail = True + else: + txt = _("Packages to upgrade downloaded") + GLib.idle_add(update_progress, progress, fraction, txt) + sleep(1) + txt = _("Upgrading packages") + GLib.idle_add(update_progress, progress, fraction, txt) + sleep(1) + while True: + install = Popen( + f'{env}pkg-static upgrade -y{packages}', + shell=True, + stdout=PIPE, + stderr=PIPE, + close_fds=True, + universal_newlines=True + ) + install_text = "" + while True: + stdout_line = install.stdout.readline() + if install.poll() is not None: + break + install_text += stdout_line + GLib.idle_add(update_progress, progress, fraction, + stdout_line.strip()) + if install.returncode == 3: + stderr_line = install.stderr.readline() + if 'Fail to create temporary file' in stderr_line: + raw_line = install_text.splitlines()[-2] + failed_package = raw_line.split()[2].replace(':', '') + pkg_rquery = run( + f'{env}pkg-static rquery -x "%n" "{failed_package}"', + shell=True, + stdout=PIPE, + stderr=PIPE, + universal_newlines=True + ) + package_name = pkg_rquery.stdout.strip() + reinstall = Popen( + f'{env}pkg-static delete -y {package_name} ;' + f'{env}pkg-static install -y {package_name}', + shell=True, + stdout=PIPE, + stderr=PIPE, + close_fds=True, + universal_newlines=True + ) + reinstall_text = "" + while True: + stdout_line = reinstall.stdout.readline() + if reinstall.poll() is not None: + break + reinstall_text += stdout_line + GLib.idle_add(update_progress, progress, + fraction, stdout_line.strip()) + if reinstall.returncode != 0: + reinstall_text += reinstall.stderr.readline() + update_fail = open(f'{Data.home}/update.failed', 'w') + update_fail.writelines(reinstall_text) + update_fail.close() + fail = True + break + else: + txt = _("Reinstalling") + txt += f" {failed_package} " + txt += _("completed") + GLib.idle_add(update_progress, progress, fraction, txt) + sleep(1) + elif install.returncode != 0: + stderr_line = install.stderr.readline() + install_text += stderr_line + update_fail = open(f'{Data.home}/update.failed', 'w') + update_fail.writelines(install_text) + update_fail.close() + fail = True + break + else: + txt = _("Packages upgraded") + GLib.idle_add(update_progress, progress, fraction, txt) + sleep(1) + break + if Data.kernel_upgrade: + all_packages = set(re.findall(r"(\S+):", " ".join(Data.packages_dictionary['reinstall']))) + all_packages.update(upgrade_packages) + packages_to_reinstall = set(get_packages_to_reinstall()) + packages = " ".join(list(packages_to_reinstall.difference(all_packages))) + txt = _("Downloading packages depending on kernel") + GLib.idle_add(update_progress, progress, fraction, txt) + sleep(1) + fetch = Popen( + f'{env}pkg-static upgrade -Fy {packages}', + shell=True, + stdout=PIPE, + stderr=PIPE, + close_fds=True, + universal_newlines=True + ) + fetch_text = "" + while True: + stdout_line = fetch.stdout.readline() + if fetch.poll() is not None: + break + fetch_text += stdout_line + GLib.idle_add(update_progress, progress, fraction, + stdout_line.strip()) + if fetch.returncode != 0: + stderr_line = fetch.stderr.read() + fetch_text += stderr_line + update_fail = open(f'{Data.home}/update.failed', 'w') + update_fail.writelines(fetch_text) + update_fail.close() + fail = True + else: + txt = _("Packages depending on kernel downloaded") + GLib.idle_add(update_progress, progress, fraction, txt) + sleep(1) + txt = _("Reinstalling packages depending on kernel") + GLib.idle_add(update_progress, progress, fraction, txt) + sleep(1) + install = Popen( + f'{env}pkg-static upgrade -fy {packages}', + shell=True, + stdout=PIPE, + stderr=PIPE, + close_fds=True, + universal_newlines=True + ) + install_text = "" + while True: + stdout_line = install.stdout.readline() + if install.poll() is not None: + break + install_text += stdout_line + GLib.idle_add(update_progress, progress, fraction, + stdout_line.strip()) + if install.returncode != 0: + stderr_line = install.stderr.readline() + install_text += stderr_line + update_fail = open(f'{Data.home}/update.failed', 'w') + update_fail.writelines(install_text) + update_fail.close() + fail = True + else: + txt = _("Packages depending on kernel reinstalled") + GLib.idle_add(update_progress, progress, fraction, txt) + sleep(1) + GLib.idle_add(self.win.destroy) + GLib.idle_add(self.stop_tread, fail, update_pkg, reboot) + + @classmethod + def stop_tread(cls, fail: bool, update_pkg: bool, reboot: bool): + """ + The function to stop the thread. + :param fail: True if update failed. + :param update_pkg: True if update pkg was updated first. + :param reboot: True if system needs to be rebooted after update completed. + """ + if updating(): + unlock_update_station() + if fail is True: + Data.update_started = False + Data.stop_pkg_refreshing = False + FailedUpdate() + else: + if update_pkg is True and check_for_update() is True: + Data.packages_dictionary = get_pkg_upgrade_data() + StartCheckUpdate() + else: + Data.update_started = False + Data.stop_pkg_refreshing = False + if reboot is True: + RestartSystem() + else: + UpdateCompleted() + + +class StartCheckUpdate: + """ + Class for start check for update window. + """ + def close_application(self, widget: Gtk.Widget): + """ + The function to close the window. + :param widget: The window widget. + """ + if updating(): + unlock_update_station() + Gtk.main_quit() + + def __init__(self): + """ + The constructor of the StartCheckUpdate class. + """ + self.win = Gtk.Window() + self.win.connect("delete-event", self.close_application) + self.win.set_size_request(500, 75) + self.win.set_resizable(False) + self.win.set_title(_("Looking For Updates")) + self.win.set_border_width(0) + self.win.set_position(Gtk.WindowPosition.CENTER) + self.win.set_default_icon_name('system-software-update') + box1 = Gtk.VBox(homogeneous=False, spacing=0) + self.win.add(box1) + box1.show() + box2 = Gtk.VBox(homogeneous=False, spacing=10) + box2.set_border_width(10) + box1.pack_start(box2, True, True, 0) + box2.show() + self.pbar = Gtk.ProgressBar() + self.pbar.set_show_text(True) + self.pbar.set_fraction(0.0) + box2.pack_start(self.pbar, False, False, 0) + self.win.show_all() + self.thr = threading.Thread( + target=self.check_for_update, + args=[self.pbar], + daemon=True + ) + self.thr.start() + + def update_progress(self, progress: Gtk.ProgressBar, text: str): + """ + The function to update the progress bar. + :param progress: The progress bar. + :param text: the text to be displayed on the progress bar. + """ + progress.set_text(text) + fraction = progress.get_fraction() + 0.2 + progress.set_fraction(fraction) + + def check_for_update(self, progress: Gtk.ProgressBar): + """ + The function to check for update and update the progress bar. + :param progress: The progress bar. + """ + GLib.idle_add(self.update_progress, progress, + _('Checking if the repository is online')) + sleep(1) + if network_stat() == 'UP' and repo_online() is True: + GLib.idle_add(self.update_progress, progress, + _('The repository is online')) + sleep(1) + if repository_is_syncing() is True: + GLib.idle_add(self.update_progress, progress, + _('The mirror is Syncing')) + GLib.idle_add(self.stop_tread, MirrorSyncing) + else: + if updating(): + GLib.idle_add(self.update_progress, progress, + _('Updates are already running')) + GLib.idle_add(self.stop_tread, UpdateStationOpen) + else: + GLib.idle_add(self.update_progress, progress, + _('Checking for updates')) + update_available = check_for_update() + if update_available: + GLib.idle_add(self.update_progress, progress, + _('Getting the list of packages')) + Data.packages_dictionary = get_pkg_upgrade_data() + look_update_station() + GLib.idle_add(self.update_progress, progress, + _('Open the update window')) + GLib.idle_add(self.stop_tread, UpdateWindow) + elif not update_available and update_available is not None: + GLib.idle_add(self.update_progress, progress, + _('No update found')) + GLib.idle_add(self.stop_tread, NoUpdateAvailable) + else: + GLib.idle_add(self.stop_tread, SomethingIsWrong) + else: + GLib.idle_add(self.update_progress, progress, + _('The Mirror is unreachable')) + GLib.idle_add(self.stop_tread, ServerUnreachable) + + def stop_tread(self, start_window: object): + """ + The function to stop the thread. + :param start_window: The start window object. + """ + start_window() + self.win.hide() diff --git a/update_station/notification.py b/update_station/notification.py new file mode 100644 index 0000000..13fff49 --- /dev/null +++ b/update_station/notification.py @@ -0,0 +1,164 @@ +import gettext + +from gi.repository import Gtk, GLib, Notify +from update_station.data import Data +from update_station.backend import updating +from update_station.frontend import StartCheckUpdate +from update_station.dialog import UpdateStationOpen + +gettext.bindtextdomain('update-station', '/usr/local/share/locale') +gettext.textdomain('update-station') +_ = gettext.gettext + + +class UpdateNotifier: + """ + Class that creates the notification for the update. + """ + + def __init__(self): + """ + The constructor for the UpdateNotifier class. + """ + self.notification = None + Notify.init('Test') + self.msg = _("Software updates are now available.") + self.timeout = 10000 # 10 seconds + + def notify(self): + """ + Function that creates the notification for the update. + """ + if Data.major_upgrade is True: + self.msg = _("Major system version upgrade is now available.") + elif Data.kernel_upgrade is True: + self.msg = _("System and software updates are now available.") + self.notification = Notify.Notification().new( + summary=_('Update Available'), + body=self.msg, + icon='system-software-update' + ) + self.notification.add_action('clicked', 'Start Upgrade', self.on_activated) + self.notification.show() + + def on_activated(self, notification, action_name): + """ + Function that starts the upgrade. + :param notification: The notification widget. + :param action_name: The name of the action. + """ + if Data.major_upgrade is True: + MajorUpgradeWindow() + else: + StartCheckUpdate() + notification.close() + GLib.idle_add(Data.system_tray.tray_icon().set_visible, False) + + +class TrayIcon: + """ + The class for the tray icon. + """ + + def tray_icon(self): + return self.status_icon + + def __init__(self): + """ + The constructor for the TrayIcon class. + """ + self.status_icon = Gtk.StatusIcon() + self.status_icon.set_tooltip_text('Update Available') + self.menu = Gtk.Menu() + self.menu.show_all() + self.status_icon.connect("activate", self.left_click) + self.status_icon.connect('popup-menu', self.icon_clicked) + self.status_icon.set_visible(False) + self.status_icon.set_from_icon_name('system-software-update') + + def nm_menu(self): + """ + Function that creates the menu for the tray icon. + :return: The menu. + """ + self.menu = Gtk.Menu() + open_update = Gtk.MenuItem(label=_("Open Update")) + open_update.connect("activate", self.left_click) + close_item = Gtk.MenuItem(label=_("Close")) + close_item.connect("activate", Gtk.main_quit) + self.menu.append(open_update) + self.menu.append(close_item) + self.menu.show_all() + return self.menu + + @classmethod + def left_click(cls, status_icon: Gtk.StatusIcon): + """ + Function that is called when the user left-clicks on the tray icon. + :param status_icon: The status icon. + """ + if updating(): + UpdateStationOpen() + else: + Data.stop_pkg_refreshing = True + if Data.major_upgrade is True: + MajorUpgradeWindow() + else: + StartCheckUpdate() + status_icon.set_visible(False) + + def icon_clicked(self, status_icon, button, time): + """ + Function that is called when the user right-clicks on the tray icon. + :param status_icon: The status icon. + :param button: The button. + :param time: The time. + """ + position = Gtk.StatusIcon.position_menu + self.nm_menu().popup(None, None, position, status_icon, button, time) + + +class MajorUpgradeWindow(Gtk.Window): + """ + Class that creates the window for the major upgrade. + """ + + def __init__(self): + """ + The constructor for the MajorUpgradeWindow class. + """ + Gtk.Window.__init__(self, title=_("Major version upgrade")) + self.connect("destroy", Gtk.main_quit) + vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=5) + vbox.set_border_width(10) + self.add(vbox) + + label = Gtk.Label( + label=_(f"Would you like to upgrade from {Data.current_abi} to {Data.new_abi}?" + "\n\nIf you select No, the upgrade will be skipped until the next boot.") + ) + vbox.pack_start(label, True, True, 5) + hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=5) + vbox.pack_start(hbox, False, False, 0) + button1 = Gtk.Button(label="Yes") + button1.connect("clicked", self.on_clicked) + hbox.pack_end(button1, True, True, 0) + + button2 = Gtk.Button(label="No") + button2.connect("clicked", self.on_clicked) + hbox.pack_end(button2, True, True, 0) + self.show_all() + + def on_clicked(self, widget): + """ + Function that starts the upgrade. + :param widget: The widget that was clicked. + """ + if widget.get_label() == "Yes": + Data.major_upgrade = True + Data.do_not_upgrade = False + StartCheckUpdate() + else: + Data.major_upgrade = False + Data.do_not_upgrade = True + self.hide() \ No newline at end of file