good updater experience (#25724)

* good updater experience

* set params on startup

* no fetch on first loop

* little type hinting

* little more

* update translations

* always set params with valid overlay

* wrap check

* use the param

* more wrapping

* vanish

* cleanup

* remove that
old-commit-hash: c4e63d14ab
taco
Adeeb Shihadeh 3 years ago committed by GitHub
parent 7cd138328e
commit 46cfb5c45b
  1. 10
      common/params.cc
  2. 2
      selfdrive/ui/.gitignore
  3. 3
      selfdrive/ui/SConscript
  4. 81
      selfdrive/ui/qt/offroad/settings.cc
  5. 13
      selfdrive/ui/qt/offroad/settings.h
  6. 156
      selfdrive/ui/qt/offroad/software_settings.cc
  7. 8
      selfdrive/ui/qt/widgets/controls.cc
  8. 14
      selfdrive/ui/qt/widgets/controls.h
  9. 2
      selfdrive/ui/qt/widgets/offroad_alerts.cc
  10. 8
      selfdrive/ui/qt/widgets/ssh_keys.cc
  11. 2
      selfdrive/ui/qt/widgets/ssh_keys.h
  12. 2
      selfdrive/ui/tests/cycle_offroad_alerts.py
  13. 91
      selfdrive/ui/translations/main_ja.ts
  14. 91
      selfdrive/ui/translations/main_ko.ts
  15. 91
      selfdrive/ui/translations/main_pt-BR.ts
  16. 91
      selfdrive/ui/translations/main_zh-CHS.ts
  17. 91
      selfdrive/ui/translations/main_zh-CHT.ts
  18. 306
      selfdrive/updated.py

@ -154,18 +154,24 @@ std::unordered_map<std::string, uint32_t> keys = {
{"PrimeType", PERSISTENT},
{"RecordFront", PERSISTENT},
{"RecordFrontLock", PERSISTENT}, // for the internal fleet
{"ReleaseNotes", PERSISTENT},
{"ReplayControlsState", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_ON},
{"ShouldDoUpdate", CLEAR_ON_MANAGER_START},
{"SnoozeUpdate", CLEAR_ON_MANAGER_START | CLEAR_ON_IGNITION_OFF},
{"SshEnabled", PERSISTENT},
{"SubscriberInfo", PERSISTENT},
{"SwitchToBranch", CLEAR_ON_MANAGER_START},
{"TermsVersion", PERSISTENT},
{"Timezone", PERSISTENT},
{"TrainingVersion", PERSISTENT},
{"UpdateAvailable", CLEAR_ON_MANAGER_START},
{"UpdateFailedCount", CLEAR_ON_MANAGER_START},
{"UpdaterState", CLEAR_ON_MANAGER_START},
{"UpdaterFetchAvailable", CLEAR_ON_MANAGER_START},
{"UpdaterTargetBranch", CLEAR_ON_MANAGER_START},
{"UpdaterAvailableBranches", CLEAR_ON_MANAGER_START},
{"UpdaterCurrentDescription", CLEAR_ON_MANAGER_START},
{"UpdaterCurrentReleaseNotes", CLEAR_ON_MANAGER_START},
{"UpdaterNewDescription", CLEAR_ON_MANAGER_START},
{"UpdaterNewReleaseNotes", CLEAR_ON_MANAGER_START},
{"Version", PERSISTENT},
{"VisionRadarToggle", PERSISTENT},
{"WideCameraOnly", PERSISTENT},

@ -1,6 +1,8 @@
moc_*
*.moc
translations/main_test_en.*
_mui
watch3
installer/installers/*

@ -56,7 +56,8 @@ qt_env.Program("qt/spinner", ["qt/spinner.cc"], LIBS=qt_libs)
# build main UI
qt_src = ["main.cc", "qt/sidebar.cc", "qt/onroad.cc", "qt/body.cc",
"qt/window.cc", "qt/home.cc", "qt/offroad/settings.cc",
"qt/offroad/onboarding.cc", "qt/offroad/driverview.cc"]
"qt/offroad/software_settings.cc", "qt/offroad/onboarding.cc",
"qt/offroad/driverview.cc"]
qt_env.Program("_ui", qt_src + [asset_obj], LIBS=qt_libs)
if GetOption('test'):
qt_src.remove("main.cc") # replaced by test_runner

@ -159,7 +159,7 @@ DevicePanel::DevicePanel(SettingsWindow *parent) : ListWidget(parent) {
addItem(dcamBtn);
auto resetCalibBtn = new ButtonControl(tr("Reset Calibration"), tr("RESET"), "");
connect(resetCalibBtn, &ButtonControl::showDescription, this, &DevicePanel::updateCalibDescription);
connect(resetCalibBtn, &ButtonControl::showDescriptionEvent, this, &DevicePanel::updateCalibDescription);
connect(resetCalibBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to reset calibration?"), this)) {
params.remove("CalibrationParams");
@ -282,85 +282,6 @@ void DevicePanel::poweroff() {
}
}
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
gitBranchLbl = new LabelControl(tr("Git Branch"));
gitCommitLbl = new LabelControl(tr("Git Commit"));
osVersionLbl = new LabelControl(tr("OS Version"));
versionLbl = new LabelControl(tr("Version"), "", QString::fromStdString(params.get("ReleaseNotes")).trimmed());
lastUpdateLbl = new LabelControl(tr("Last Update Check"), "", tr("The last time openpilot successfully checked for an update. The updater only runs while the car is off."));
updateBtn = new ButtonControl(tr("Check for Update"), "");
connect(updateBtn, &ButtonControl::clicked, [=]() {
if (params.getBool("IsOffroad")) {
fs_watch->addPath(QString::fromStdString(params.getParamPath("LastUpdateTime")));
fs_watch->addPath(QString::fromStdString(params.getParamPath("UpdateFailedCount")));
updateBtn->setText(tr("CHECKING"));
updateBtn->setEnabled(false);
}
std::system("pkill -1 -f selfdrive.updated");
});
connect(uiState(), &UIState::offroadTransition, updateBtn, &QPushButton::setEnabled);
branchSwitcherBtn = new ButtonControl(tr("Switch Branch"), tr("ENTER"), tr("The new branch will be pulled the next time the updater runs."));
connect(branchSwitcherBtn, &ButtonControl::clicked, [=]() {
QString branch = InputDialog::getText(tr("Enter branch name"), this, tr("The new branch will be pulled the next time the updater runs."),
false, -1, QString::fromStdString(params.get("SwitchToBranch")));
if (branch.isEmpty()) {
params.remove("SwitchToBranch");
} else {
params.put("SwitchToBranch", branch.toStdString());
}
std::system("pkill -1 -f selfdrive.updated");
});
connect(uiState(), &UIState::offroadTransition, branchSwitcherBtn, &QPushButton::setEnabled);
auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL"));
connect(uninstallBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to uninstall?"), this)) {
params.putBool("DoUninstall", true);
}
});
connect(uiState(), &UIState::offroadTransition, uninstallBtn, &QPushButton::setEnabled);
QWidget *widgets[] = {versionLbl, lastUpdateLbl, updateBtn, branchSwitcherBtn, gitBranchLbl, gitCommitLbl, osVersionLbl, uninstallBtn};
for (QWidget* w : widgets) {
if (w == branchSwitcherBtn && params.getBool("IsTestedBranch")) {
continue;
}
addItem(w);
}
fs_watch = new QFileSystemWatcher(this);
QObject::connect(fs_watch, &QFileSystemWatcher::fileChanged, [=](const QString path) {
if (path.contains("UpdateFailedCount") && std::atoi(params.get("UpdateFailedCount").c_str()) > 0) {
lastUpdateLbl->setText(tr("failed to fetch update"));
updateBtn->setText(tr("CHECK"));
updateBtn->setEnabled(true);
} else if (path.contains("LastUpdateTime")) {
updateLabels();
}
});
}
void SoftwarePanel::showEvent(QShowEvent *event) {
updateLabels();
}
void SoftwarePanel::updateLabels() {
QString lastUpdate = "";
auto tm = params.get("LastUpdateTime");
if (!tm.empty()) {
lastUpdate = timeAgo(QDateTime::fromString(QString::fromStdString(tm + "Z"), Qt::ISODate));
}
versionLbl->setText(getBrandVersion());
lastUpdateLbl->setText(lastUpdate);
updateBtn->setText(tr("CHECK"));
updateBtn->setEnabled(true);
gitBranchLbl->setText(QString::fromStdString(params.get("GitBranch")));
gitCommitLbl->setText(QString::fromStdString(params.get("GitCommit")).left(10));
osVersionLbl->setText(QString::fromStdString(Hardware::get_os_version()).trimmed());
}
void SettingsWindow::showEvent(QShowEvent *event) {
panel_widget->setCurrentIndex(0);
nav_btns->buttons()[0]->setChecked(true);

@ -71,14 +71,15 @@ public:
private:
void showEvent(QShowEvent *event) override;
void updateLabels();
void checkForUpdates();
LabelControl *gitBranchLbl;
LabelControl *gitCommitLbl;
LabelControl *osVersionLbl;
bool is_onroad = false;
QLabel *onroadLbl;
LabelControl *versionLbl;
LabelControl *lastUpdateLbl;
ButtonControl *updateBtn;
ButtonControl *branchSwitcherBtn;
ButtonControl *installBtn;
ButtonControl *downloadBtn;
ButtonControl *targetBranchBtn;
Params params;
QFileSystemWatcher *fs_watch;

@ -0,0 +1,156 @@
#include "selfdrive/ui/qt/offroad/settings.h"
#include <cassert>
#include <cmath>
#include <string>
#include <QDebug>
#include <QLabel>
#include "common/params.h"
#include "common/util.h"
#include "selfdrive/ui/ui.h"
#include "selfdrive/ui/qt/util.h"
#include "selfdrive/ui/qt/widgets/controls.h"
#include "selfdrive/ui/qt/widgets/input.h"
#include "system/hardware/hw.h"
void SoftwarePanel::checkForUpdates() {
std::system("pkill -SIGUSR1 -f selfdrive.updated");
}
SoftwarePanel::SoftwarePanel(QWidget* parent) : ListWidget(parent) {
onroadLbl = new QLabel(tr("Updates are only downloaded while the car is off."));
onroadLbl->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left; padding-top: 30px; padding-bottom: 30px;");
addItem(onroadLbl);
// current version
versionLbl = new LabelControl(tr("Current Version"), "");
addItem(versionLbl);
// download update btn
downloadBtn = new ButtonControl(tr("Download"), tr("CHECK"));
connect(downloadBtn, &ButtonControl::clicked, [=]() {
downloadBtn->setEnabled(false);
if (downloadBtn->text() == tr("CHECK")) {
checkForUpdates();
} else {
std::system("pkill -SIGHUP -f selfdrive.updated");
}
});
addItem(downloadBtn);
// install update btn
installBtn = new ButtonControl(tr("Install Update"), tr("INSTALL"));
connect(installBtn, &ButtonControl::clicked, [=]() {
installBtn->setEnabled(false);
params.putBool("DoShutdown", true);
});
addItem(installBtn);
// branch selecting
targetBranchBtn = new ButtonControl(tr("Target Branch"), tr("SELECT"));
connect(targetBranchBtn, &ButtonControl::clicked, [=]() {
auto current = params.get("GitBranch");
QStringList branches = QString::fromStdString(params.get("UpdaterAvailableBranches")).split(",");
for (QString b : {current.c_str(), "devel-staging", "devel", "master-ci", "master"}) {
auto i = branches.indexOf(b);
if (i >= 0) {
branches.removeAt(i);
branches.insert(0, b);
}
}
QString cur = QString::fromStdString(params.get("UpdaterTargetBranch"));
QString selection = MultiOptionDialog::getSelection(tr("Select a branch"), branches, cur, this);
if (!selection.isEmpty()) {
params.put("UpdaterTargetBranch", selection.toStdString());
targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch")));
checkForUpdates();
}
});
if (!params.getBool("IsTestedBranch")) {
addItem(targetBranchBtn);
}
// uninstall button
auto uninstallBtn = new ButtonControl(tr("Uninstall %1").arg(getBrand()), tr("UNINSTALL"));
connect(uninstallBtn, &ButtonControl::clicked, [&]() {
if (ConfirmationDialog::confirm(tr("Are you sure you want to uninstall?"), this)) {
params.putBool("DoUninstall", true);
}
});
addItem(uninstallBtn);
fs_watch = new QFileSystemWatcher(this);
QObject::connect(fs_watch, &QFileSystemWatcher::fileChanged, [=](const QString path) {
updateLabels();
});
connect(uiState(), &UIState::offroadTransition, [=](bool offroad) {
is_onroad = !offroad;
updateLabels();
});
updateLabels();
}
void SoftwarePanel::showEvent(QShowEvent *event) {
// nice for testing on PC
installBtn->setEnabled(true);
updateLabels();
}
void SoftwarePanel::updateLabels() {
// add these back in case the files got removed
fs_watch->addPath(QString::fromStdString(params.getParamPath("LastUpdateTime")));
fs_watch->addPath(QString::fromStdString(params.getParamPath("UpdateFailedCount")));
fs_watch->addPath(QString::fromStdString(params.getParamPath("UpdaterState")));
fs_watch->addPath(QString::fromStdString(params.getParamPath("UpdateAvailable")));
if (!isVisible()) {
return;
}
// updater only runs offroad
onroadLbl->setVisible(is_onroad);
downloadBtn->setVisible(!is_onroad);
// download update
QString updater_state = QString::fromStdString(params.get("UpdaterState"));
bool failed = std::atoi(params.get("UpdateFailedCount").c_str()) > 0;
if (updater_state != "idle") {
downloadBtn->setEnabled(false);
downloadBtn->setValue(updater_state);
} else {
if (failed) {
downloadBtn->setText("CHECK");
downloadBtn->setValue("failed to check for update");
} else if (params.getBool("UpdaterFetchAvailable")) {
downloadBtn->setText("DOWNLOAD");
downloadBtn->setValue("update available");
} else {
QString lastUpdate = "never";
auto tm = params.get("LastUpdateTime");
if (!tm.empty()) {
lastUpdate = timeAgo(QDateTime::fromString(QString::fromStdString(tm + "Z"), Qt::ISODate));
}
downloadBtn->setText("CHECK");
downloadBtn->setValue("up to date, last checked " + lastUpdate);
}
downloadBtn->setEnabled(true);
}
targetBranchBtn->setValue(QString::fromStdString(params.get("UpdaterTargetBranch")));
// current + new versions
versionLbl->setText(QString::fromStdString(params.get("UpdaterCurrentDescription")).left(40));
versionLbl->setDescription(QString::fromStdString(params.get("UpdaterCurrentReleaseNotes")));
installBtn->setVisible(!is_onroad && params.getBool("UpdateAvailable"));
installBtn->setValue(QString::fromStdString(params.get("UpdaterNewDescription")).left(35));
installBtn->setDescription(QString::fromStdString(params.get("UpdaterNewReleaseNotes")));
update();
}

@ -42,6 +42,12 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons
title_label->setStyleSheet("font-size: 50px; font-weight: 400; text-align: left");
hlayout->addWidget(title_label);
// value next to control button
value = new QLabel();
value->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
value->setStyleSheet("color: #aaaaaa");
hlayout->addWidget(value);
main_layout->addLayout(hlayout);
// description
@ -54,7 +60,7 @@ AbstractControl::AbstractControl(const QString &title, const QString &desc, cons
connect(title_label, &QPushButton::clicked, [=]() {
if (!description->isVisible()) {
emit showDescription();
emit showDescriptionEvent();
}
if (!description->text().isEmpty()) {

@ -45,8 +45,17 @@ public:
title_label->setText(title);
}
void setValue(const QString &val) {
value->setText(val);
}
public slots:
void showDescription() {
description->setVisible(true);
};
signals:
void showDescription();
void showDescriptionEvent();
protected:
AbstractControl(const QString &title, const QString &desc = "", const QString &icon = "", QWidget *parent = nullptr);
@ -54,6 +63,9 @@ protected:
QHBoxLayout *hlayout;
QPushButton *title_label;
private:
QLabel *value;
QLabel *description = nullptr;
};

@ -112,7 +112,7 @@ UpdateAlert::UpdateAlert(QWidget *parent) : AbstractAlert(true, parent) {
bool UpdateAlert::refresh() {
bool updateAvailable = params.getBool("UpdateAvailable");
if (updateAvailable) {
releaseNotes->setText(params.get("ReleaseNotes").c_str());
releaseNotes->setText(params.get("UpdaterNewReleaseNotes").c_str());
}
return updateAvailable;
}

@ -5,10 +5,6 @@
#include "selfdrive/ui/qt/widgets/input.h"
SshControl::SshControl() : ButtonControl(tr("SSH Keys"), "", tr("Warning: This grants SSH access to all public keys in your GitHub settings. Never enter a GitHub username other than your own. A comma employee will NEVER ask you to add their GitHub username.")) {
username_label.setAlignment(Qt::AlignRight | Qt::AlignVCenter);
username_label.setStyleSheet("color: #aaaaaa");
hlayout->insertWidget(1, &username_label);
QObject::connect(this, &ButtonControl::clicked, [=]() {
if (text() == tr("ADD")) {
QString username = InputDialog::getText(tr("Enter your GitHub username"), this);
@ -30,10 +26,10 @@ SshControl::SshControl() : ButtonControl(tr("SSH Keys"), "", tr("Warning: This g
void SshControl::refresh() {
QString param = QString::fromStdString(params.get("GithubSshKeys"));
if (param.length()) {
username_label.setText(QString::fromStdString(params.get("GithubUsername")));
setValue(QString::fromStdString(params.get("GithubUsername")));
setText(tr("REMOVE"));
} else {
username_label.setText("");
setValue("");
setText(tr("ADD"));
}
setEnabled(true);

@ -27,8 +27,6 @@ public:
private:
Params params;
QLabel username_label;
void refresh();
void getUserKeys(const QString &username);
};

@ -20,7 +20,7 @@ if __name__ == "__main__":
params.put_bool("UpdateAvailable", True)
r = open(os.path.join(BASEDIR, "RELEASES.md")).read()
r = r[:r.find('\n\n')] # Slice latest release notes
params.put("ReleaseNotes", r + "\n")
params.put("UpdaterNewReleaseNotes", r + "\n")
time.sleep(t)
params.put_bool("UpdateAvailable", False)

@ -193,7 +193,7 @@
<translation></translation>
</message>
<message>
<location line="+4"/>
<location line="+3"/>
<source>Select a language</source>
<translation></translation>
</message>
@ -418,7 +418,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
https://connect.comma.ai</translation>
</message>
<message>
<location line="+57"/>
<location line="+58"/>
<source>No home
location set</source>
<translation>
@ -432,7 +432,7 @@ location set</source>
</translation>
</message>
<message>
<location line="+113"/>
<location line="+120"/>
<source>no recent destinations</source>
<translation></translation>
</message>
@ -718,7 +718,7 @@ location set</source>
<context>
<name>SettingsWindow</name>
<message>
<location filename="../qt/offroad/settings.cc" line="+101"/>
<location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source>
<translation>×</translation>
</message>
@ -983,68 +983,47 @@ location set</source>
<context>
<name>SoftwarePanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-130"/>
<source>Git Branch</source>
<translation>Git </translation>
<location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Updates are only downloaded while the car is off.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Git Commit</source>
<translation>Git </translation>
</message>
<message>
<location line="+1"/>
<source>OS Version</source>
<translation>OS </translation>
<location line="+5"/>
<source>Current Version</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Version</source>
<translation></translation>
<location line="+4"/>
<source>Download</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Last Update Check</source>
<translation></translation>
<location line="+12"/>
<source>Install Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source>
<translation>openpilotが最後にアップデートの確認に成功してからの時間です</translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation></translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation></translation>
<source>INSTALL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+7"/>
<source>Switch Branch</source>
<translation></translation>
<location line="+8"/>
<source>Target Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>ENTER</source>
<translation></translation>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<location line="+2"/>
<source>The new branch will be pulled the next time the updater runs.</source>
<translation>updater </translation>
</message>
<message>
<location line="+0"/>
<source>Enter branch name</source>
<translation></translation>
<location line="+13"/>
<source>Select a branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+11"/>
<location line="+12"/>
<source>UNINSTALL</source>
<translation></translation>
</message>
@ -1059,13 +1038,8 @@ location set</source>
<translation></translation>
</message>
<message>
<location line="+17"/>
<source>failed to fetch update</source>
<translation></translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<location line="-47"/>
<location line="+3"/>
<source>CHECK</source>
<translation></translation>
</message>
@ -1083,7 +1057,7 @@ location set</source>
<translation>警告: これはGitHub SSH GitHub GitHub </translation>
</message>
<message>
<location line="+6"/>
<location line="+2"/>
<location line="+24"/>
<source>ADD</source>
<translation></translation>
@ -1153,7 +1127,7 @@ location set</source>
<context>
<name>TogglesPanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-324"/>
<location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source>
<translation>openpilot </translation>
</message>
@ -1290,12 +1264,11 @@ location set</source>
<name>WifiUI</name>
<message>
<location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source>
<translation>...</translation>
</message>
<message>
<location line="+26"/>
<location line="+80"/>
<source>CONNECTING...</source>
<translation>...</translation>
</message>

@ -193,7 +193,7 @@
<translation></translation>
</message>
<message>
<location line="+4"/>
<location line="+3"/>
<source>Select a language</source>
<translation> </translation>
</message>
@ -418,7 +418,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
https://connect.comma.ai</translation>
</message>
<message>
<location line="+57"/>
<location line="+58"/>
<source>No home
location set</source>
<translation>
@ -432,7 +432,7 @@ location set</source>
</translation>
</message>
<message>
<location line="+113"/>
<location line="+120"/>
<source>no recent destinations</source>
<translation> </translation>
</message>
@ -718,7 +718,7 @@ location set</source>
<context>
<name>SettingsWindow</name>
<message>
<location filename="../qt/offroad/settings.cc" line="+101"/>
<location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source>
<translation>×</translation>
</message>
@ -983,68 +983,47 @@ location set</source>
<context>
<name>SoftwarePanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-130"/>
<source>Git Branch</source>
<translation>Git </translation>
<location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Updates are only downloaded while the car is off.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Git Commit</source>
<translation>Git </translation>
</message>
<message>
<location line="+1"/>
<source>OS Version</source>
<translation>OS </translation>
<location line="+5"/>
<source>Current Version</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Version</source>
<translation></translation>
<location line="+4"/>
<source>Download</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Last Update Check</source>
<translation> </translation>
<location line="+12"/>
<source>Install Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source>
<translation> openpilot이 . .</translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation> </translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation></translation>
<source>INSTALL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+7"/>
<source>Switch Branch</source>
<translation> </translation>
<location line="+8"/>
<source>Target Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>ENTER</source>
<translation></translation>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<location line="+2"/>
<source>The new branch will be pulled the next time the updater runs.</source>
<translation> .</translation>
</message>
<message>
<location line="+0"/>
<source>Enter branch name</source>
<translation> </translation>
<location line="+13"/>
<source>Select a branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+11"/>
<location line="+12"/>
<source>UNINSTALL</source>
<translation></translation>
</message>
@ -1059,13 +1038,8 @@ location set</source>
<translation>?</translation>
</message>
<message>
<location line="+17"/>
<source>failed to fetch update</source>
<translation> </translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<location line="-47"/>
<location line="+3"/>
<source>CHECK</source>
<translation></translation>
</message>
@ -1083,7 +1057,7 @@ location set</source>
<translation>경고: 허용으로 GitHub SSH . GitHub ID . comma에서는 GitHub ID를 .</translation>
</message>
<message>
<location line="+6"/>
<location line="+2"/>
<location line="+24"/>
<source>ADD</source>
<translation></translation>
@ -1153,7 +1127,7 @@ location set</source>
<context>
<name>TogglesPanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-324"/>
<location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source>
<translation>openpilot </translation>
</message>
@ -1290,12 +1264,11 @@ location set</source>
<name>WifiUI</name>
<message>
<location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source>
<translation> ...</translation>
</message>
<message>
<location line="+26"/>
<location line="+80"/>
<source>CONNECTING...</source>
<translation>...</translation>
</message>

@ -193,7 +193,7 @@
<translation>ALTERAR</translation>
</message>
<message>
<location line="+4"/>
<location line="+3"/>
<source>Select a language</source>
<translation>Selecione o Idioma</translation>
</message>
@ -419,7 +419,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
uma assinatura prime Inscreva-se agora: https://connect.comma.ai</translation>
</message>
<message>
<location line="+57"/>
<location line="+58"/>
<source>No home
location set</source>
<translation>Sem local
@ -433,7 +433,7 @@ location set</source>
trabalho definido</translation>
</message>
<message>
<location line="+113"/>
<location line="+120"/>
<source>no recent destinations</source>
<translation>sem destinos recentes</translation>
</message>
@ -722,7 +722,7 @@ trabalho definido</translation>
<context>
<name>SettingsWindow</name>
<message>
<location filename="../qt/offroad/settings.cc" line="+101"/>
<location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source>
<translation>×</translation>
</message>
@ -987,68 +987,47 @@ trabalho definido</translation>
<context>
<name>SoftwarePanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-130"/>
<source>Git Branch</source>
<translation>Git Branch</translation>
</message>
<message>
<location line="+1"/>
<source>Git Commit</source>
<translation>Último Commit</translation>
<location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Updates are only downloaded while the car is off.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>OS Version</source>
<translation>Versão do Sistema</translation>
<location line="+5"/>
<source>Current Version</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Version</source>
<translation>Versão</translation>
<location line="+4"/>
<source>Download</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Last Update Check</source>
<translation>Verificação da última atualização</translation>
<location line="+12"/>
<source>Install Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source>
<translation>A última vez que o openpilot verificou com sucesso uma atualização. O atualizador funciona com o carro desligado.</translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation>Verifique atualizações</translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation>VERIFICANDO</translation>
</message>
<message>
<location line="+7"/>
<source>Switch Branch</source>
<translation>Alterar Branch</translation>
<source>INSTALL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>ENTER</source>
<translation>INSERIR</translation>
<location line="+8"/>
<source>Target Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<location line="+2"/>
<source>The new branch will be pulled the next time the updater runs.</source>
<translation>A nova branch será aplicada ao verificar atualizações.</translation>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>Enter branch name</source>
<translation>Inserir o nome da branch</translation>
<location line="+13"/>
<source>Select a branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+11"/>
<location line="+12"/>
<source>UNINSTALL</source>
<translation>DESINSTALAR</translation>
</message>
@ -1063,13 +1042,8 @@ trabalho definido</translation>
<translation>Tem certeza que quer desinstalar?</translation>
</message>
<message>
<location line="+17"/>
<source>failed to fetch update</source>
<translation>falha ao buscar atualização</translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<location line="-47"/>
<location line="+3"/>
<source>CHECK</source>
<translation>VERIFICAR</translation>
</message>
@ -1087,7 +1061,7 @@ trabalho definido</translation>
<translation>Aviso: isso concede acesso SSH a todas as chaves públicas nas configurações do GitHub. Nunca insira um nome de usuário do GitHub que não seja o seu. Um funcionário da comma NUNCA pedirá que você adicione seu nome de usuário do GitHub.</translation>
</message>
<message>
<location line="+6"/>
<location line="+2"/>
<location line="+24"/>
<source>ADD</source>
<translation>ADICIONAR</translation>
@ -1157,7 +1131,7 @@ trabalho definido</translation>
<context>
<name>TogglesPanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-324"/>
<location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source>
<translation>Ativar openpilot</translation>
</message>
@ -1294,12 +1268,11 @@ trabalho definido</translation>
<name>WifiUI</name>
<message>
<location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source>
<translation>Procurando redes...</translation>
</message>
<message>
<location line="+26"/>
<location line="+80"/>
<source>CONNECTING...</source>
<translation>CONECTANDO...</translation>
</message>

@ -193,7 +193,7 @@
<translation></translation>
</message>
<message>
<location line="+4"/>
<location line="+3"/>
<source>Select a language</source>
<translation></translation>
</message>
@ -418,7 +418,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
https://connect.comma.ai</translation>
</message>
<message>
<location line="+57"/>
<location line="+58"/>
<source>No home
location set</source>
<translation></translation>
@ -430,7 +430,7 @@ location set</source>
<translation></translation>
</message>
<message>
<location line="+113"/>
<location line="+120"/>
<source>no recent destinations</source>
<translation></translation>
</message>
@ -716,7 +716,7 @@ location set</source>
<context>
<name>SettingsWindow</name>
<message>
<location filename="../qt/offroad/settings.cc" line="+101"/>
<location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source>
<translation>×</translation>
</message>
@ -981,68 +981,47 @@ location set</source>
<context>
<name>SoftwarePanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-130"/>
<source>Git Branch</source>
<translation>Git Branch</translation>
</message>
<message>
<location line="+1"/>
<source>Git Commit</source>
<translation>Git Commit</translation>
<location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Updates are only downloaded while the car is off.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>OS Version</source>
<translation></translation>
<location line="+5"/>
<source>Current Version</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Version</source>
<translation></translation>
<location line="+4"/>
<source>Download</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Last Update Check</source>
<translation></translation>
<location line="+12"/>
<source>Install Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source>
<translation></translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation></translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation></translation>
</message>
<message>
<location line="+7"/>
<source>Switch Branch</source>
<translation></translation>
<source>INSTALL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>ENTER</source>
<translation></translation>
<location line="+8"/>
<source>Target Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<location line="+2"/>
<source>The new branch will be pulled the next time the updater runs.</source>
<translation></translation>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>Enter branch name</source>
<translation></translation>
<location line="+13"/>
<source>Select a branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+11"/>
<location line="+12"/>
<source>UNINSTALL</source>
<translation></translation>
</message>
@ -1057,13 +1036,8 @@ location set</source>
<translation></translation>
</message>
<message>
<location line="+17"/>
<source>failed to fetch update</source>
<translation></translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<location line="-47"/>
<location line="+3"/>
<source>CHECK</source>
<translation></translation>
</message>
@ -1081,7 +1055,7 @@ location set</source>
<translation>SSH访问权限给您GitHub设置中的所有公钥GitHub用户名comma员工永远不会要求您添加他们的GitHub用户名</translation>
</message>
<message>
<location line="+6"/>
<location line="+2"/>
<location line="+24"/>
<source>ADD</source>
<translation></translation>
@ -1151,7 +1125,7 @@ location set</source>
<context>
<name>TogglesPanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-324"/>
<location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source>
<translation>openpilot</translation>
</message>
@ -1288,12 +1262,11 @@ location set</source>
<name>WifiUI</name>
<message>
<location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source>
<translation></translation>
</message>
<message>
<location line="+26"/>
<location line="+80"/>
<source>CONNECTING...</source>
<translation></translation>
</message>

@ -193,7 +193,7 @@
<translation></translation>
</message>
<message>
<location line="+4"/>
<location line="+3"/>
<source>Select a language</source>
<translation></translation>
</message>
@ -418,7 +418,7 @@ prime subscription. Sign up now: https://connect.comma.ai</source>
https://connect.comma.ai</translation>
</message>
<message>
<location line="+57"/>
<location line="+58"/>
<source>No home
location set</source>
<translation>
@ -432,7 +432,7 @@ location set</source>
</translation>
</message>
<message>
<location line="+113"/>
<location line="+120"/>
<source>no recent destinations</source>
<translation></translation>
</message>
@ -718,7 +718,7 @@ location set</source>
<context>
<name>SettingsWindow</name>
<message>
<location filename="../qt/offroad/settings.cc" line="+101"/>
<location filename="../qt/offroad/settings.cc" line="+22"/>
<source>×</source>
<translation>×</translation>
</message>
@ -983,68 +983,47 @@ location set</source>
<context>
<name>SoftwarePanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-130"/>
<source>Git Branch</source>
<translation>Git </translation>
</message>
<message>
<location line="+1"/>
<source>Git Commit</source>
<translation>Git </translation>
<location filename="../qt/offroad/software_settings.cc" line="+24"/>
<source>Updates are only downloaded while the car is off.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>OS Version</source>
<translation></translation>
<location line="+5"/>
<source>Current Version</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Version</source>
<translation></translation>
<location line="+4"/>
<source>Download</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+1"/>
<source>Last Update Check</source>
<translation></translation>
<location line="+12"/>
<source>Install Update</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>The last time openpilot successfully checked for an update. The updater only runs while the car is off.</source>
<translation></translation>
</message>
<message>
<location line="+1"/>
<source>Check for Update</source>
<translation></translation>
</message>
<message>
<location line="+5"/>
<source>CHECKING</source>
<translation></translation>
</message>
<message>
<location line="+7"/>
<source>Switch Branch</source>
<translation></translation>
<source>INSTALL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>ENTER</source>
<translation></translation>
<location line="+8"/>
<source>Target Branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<location line="+2"/>
<source>The new branch will be pulled the next time the updater runs.</source>
<translation></translation>
<source>SELECT</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+0"/>
<source>Enter branch name</source>
<translation></translation>
<location line="+13"/>
<source>Select a branch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+11"/>
<location line="+12"/>
<source>UNINSTALL</source>
<translation></translation>
</message>
@ -1059,13 +1038,8 @@ location set</source>
<translation></translation>
</message>
<message>
<location line="+17"/>
<source>failed to fetch update</source>
<translation></translation>
</message>
<message>
<location line="+1"/>
<location line="+21"/>
<location line="-47"/>
<location line="+3"/>
<source>CHECK</source>
<translation></translation>
</message>
@ -1083,7 +1057,7 @@ location set</source>
<translation> GitHub SSH GitHub comma GitHub </translation>
</message>
<message>
<location line="+6"/>
<location line="+2"/>
<location line="+24"/>
<source>ADD</source>
<translation></translation>
@ -1153,7 +1127,7 @@ location set</source>
<context>
<name>TogglesPanel</name>
<message>
<location filename="../qt/offroad/settings.cc" line="-324"/>
<location filename="../qt/offroad/settings.cc" line="-303"/>
<source>Enable openpilot</source>
<translation> openpilot</translation>
</message>
@ -1290,12 +1264,11 @@ location set</source>
<name>WifiUI</name>
<message>
<location filename="../qt/offroad/networking.cc" line="+113"/>
<location line="+53"/>
<source>Scanning for networks...</source>
<translation>...</translation>
</message>
<message>
<location line="+26"/>
<location line="+80"/>
<source>CONNECTING...</source>
<translation>...</translation>
</message>

@ -23,6 +23,7 @@
# disable this service.
import os
import re
import datetime
import subprocess
import psutil
@ -31,8 +32,9 @@ import signal
import fcntl
import time
import threading
from collections import defaultdict
from pathlib import Path
from typing import List, Tuple, Optional
from typing import List, Union, Optional
from markdown_it import MarkdownIt
from common.basedir import BASEDIR
@ -54,20 +56,27 @@ DAYS_NO_CONNECTIVITY_MAX = 14 # do not allow to engage after this many days
DAYS_NO_CONNECTIVITY_PROMPT = 10 # send an offroad prompt after this many days
class WaitTimeHelper:
def __init__(self, proc):
self.proc = proc
def __init__(self):
self.ready_event = threading.Event()
self.only_check_for_update = False
signal.signal(signal.SIGHUP, self.update_now)
signal.signal(signal.SIGUSR1, self.check_now)
def update_now(self, signum: int, frame) -> None:
cloudlog.info("caught SIGHUP, running update check immediately")
cloudlog.info("caught SIGHUP, attempting to downloading update")
self.only_check_for_update = False
self.ready_event.set()
def check_now(self, signum: int, frame) -> None:
cloudlog.info("caught SIGUSR1, checking for updates")
self.only_check_for_update = True
self.ready_event.set()
def sleep(self, t: float) -> None:
self.ready_event.wait(timeout=t)
def run(cmd: List[str], cwd: Optional[str] = None):
def run(cmd: List[str], cwd: Optional[str] = None) -> str:
return subprocess.check_output(cmd, cwd=cwd, stderr=subprocess.STDOUT, encoding='utf8')
@ -80,59 +89,19 @@ def set_consistent_flag(consistent: bool) -> None:
consistent_file.unlink(missing_ok=True)
os.sync()
def set_params(new_version: bool, failed_count: int, exception: Optional[str]) -> None:
params = Params()
params.put("UpdateFailedCount", str(failed_count))
last_update = datetime.datetime.utcnow()
if failed_count == 0:
t = last_update.isoformat()
params.put("LastUpdateTime", t.encode('utf8'))
else:
try:
t = params.get("LastUpdateTime", encoding='utf8')
last_update = datetime.datetime.fromisoformat(t)
except (TypeError, ValueError):
pass
if exception is None:
params.remove("LastUpdateException")
else:
params.put("LastUpdateException", exception)
# Write out release notes for new versions
if new_version:
def parse_release_notes(basedir: str) -> bytes:
try:
with open(os.path.join(basedir, "RELEASES.md"), "rb") as f:
r = f.read().split(b'\n\n', 1)[0] # Slice latest release notes
try:
with open(os.path.join(FINALIZED, "RELEASES.md"), "rb") as f:
r = f.read().split(b'\n\n', 1)[0] # Slice latest release notes
try:
params.put("ReleaseNotes", MarkdownIt().render(r.decode("utf-8")))
except Exception:
params.put("ReleaseNotes", r + b"\n")
return bytes(MarkdownIt().render(r.decode("utf-8")), encoding="utf-8")
except Exception:
params.put("ReleaseNotes", "")
params.put_bool("UpdateAvailable", True)
# Handle user prompt
for alert in ("Offroad_UpdateFailed", "Offroad_ConnectivityNeeded", "Offroad_ConnectivityNeededPrompt"):
set_offroad_alert(alert, False)
now = datetime.datetime.utcnow()
dt = now - last_update
if failed_count > 15 and exception is not None:
if is_tested_branch():
extra_text = "Ensure the software is correctly installed"
else:
extra_text = exception
set_offroad_alert("Offroad_UpdateFailed", True, extra_text=extra_text)
elif dt.days > DAYS_NO_CONNECTIVITY_MAX and failed_count > 1:
set_offroad_alert("Offroad_ConnectivityNeeded", True)
elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
remaining = max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 1)
set_offroad_alert("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining} day{'' if remaining == 1 else 's'}.")
return r + b"\n"
except FileNotFoundError:
pass
except Exception:
cloudlog.exception("failed to parse release notes")
return b""
def setup_git_options(cwd: str) -> None:
# We sync FS object atimes (which NEOS doesn't use) and mtimes, but ctimes
@ -267,65 +236,161 @@ def handle_agnos_update() -> None:
set_offroad_alert("Offroad_NeosUpdate", False)
def check_git_fetch_result(fetch_txt: str) -> bool:
err_msg = "Failed to add the host to the list of known hosts (/data/data/com.termux/files/home/.ssh/known_hosts).\n"
return len(fetch_txt) > 0 and (fetch_txt != err_msg)
class Updater:
def __init__(self):
self.params = Params()
self.branches = defaultdict(lambda: None)
@property
def target_branch(self) -> str:
b: Union[str, None] = self.params.get("UpdaterTargetBranch", encoding='utf-8')
if b is None:
b = self.get_branch(BASEDIR)
self.params.put("UpdaterTargetBranch", b)
return b
@property
def update_ready(self) -> bool:
consistent_file = Path(os.path.join(FINALIZED, ".overlay_consistent"))
if consistent_file.is_file():
hash_mismatch = self.get_commit_hash(BASEDIR) != self.branches[self.target_branch]
branch_mismatch = self.get_branch(BASEDIR) != self.target_branch
on_target_branch = self.get_branch(FINALIZED) == self.target_branch
return ((hash_mismatch or branch_mismatch) and on_target_branch)
return False
@property
def update_available(self) -> bool:
if os.path.isdir(OVERLAY_MERGED):
hash_mismatch = self.get_commit_hash(OVERLAY_MERGED) != self.branches[self.target_branch]
branch_mismatch = self.get_branch(OVERLAY_MERGED) != self.target_branch
return hash_mismatch or branch_mismatch
return False
def get_branch(self, path: str) -> str:
return run(["git", "rev-parse", "--abbrev-ref", "HEAD"], path).rstrip()
def get_commit_hash(self, path: str = OVERLAY_MERGED) -> str:
return run(["git", "rev-parse", "HEAD"], path).rstrip()
def set_params(self, failed_count: int, exception: Optional[str]) -> None:
self.params.put("UpdateFailedCount", str(failed_count))
self.params.put_bool("UpdaterFetchAvailable", self.update_available)
self.params.put("UpdaterAvailableBranches", ','.join(self.branches.keys()))
last_update = datetime.datetime.utcnow()
if failed_count == 0:
t = last_update.isoformat()
self.params.put("LastUpdateTime", t.encode('utf8'))
else:
try:
t = self.params.get("LastUpdateTime", encoding='utf8')
last_update = datetime.datetime.fromisoformat(t)
except (TypeError, ValueError):
pass
def check_for_update() -> Tuple[bool, bool]:
setup_git_options(OVERLAY_MERGED)
try:
git_fetch_output = run(["git", "fetch", "--dry-run"], OVERLAY_MERGED)
return True, check_git_fetch_result(git_fetch_output)
except subprocess.CalledProcessError:
return False, False
if exception is None:
self.params.remove("LastUpdateException")
else:
self.params.put("LastUpdateException", exception)
def fetch_update() -> bool:
cloudlog.info("attempting git fetch inside staging overlay")
# Write out current and new version info
def get_description(basedir: str) -> str:
version = ""
branch = ""
commit = ""
try:
branch = self.get_branch(basedir)
commit = self.get_commit_hash(basedir)
with open(os.path.join(basedir, "common", "version.h")) as f:
version = f.read().split('"')[1]
except Exception:
pass
return f"{version} / {branch} / {commit[:7]}"
self.params.put("UpdaterCurrentDescription", get_description(BASEDIR))
self.params.put("UpdaterCurrentReleaseNotes", parse_release_notes(BASEDIR))
self.params.put("UpdaterNewDescription", get_description(FINALIZED))
self.params.put("UpdaterNewReleaseNotes", parse_release_notes(FINALIZED))
self.params.put_bool("UpdateAvailable", self.update_ready)
# Handle user prompt
for alert in ("Offroad_UpdateFailed", "Offroad_ConnectivityNeeded", "Offroad_ConnectivityNeededPrompt"):
set_offroad_alert(alert, False)
now = datetime.datetime.utcnow()
dt = now - last_update
if failed_count > 15 and exception is not None:
if is_tested_branch():
extra_text = "Ensure the software is correctly installed. Uninstall and re-install if this error persists."
else:
extra_text = exception
set_offroad_alert("Offroad_UpdateFailed", True, extra_text=extra_text)
elif dt.days > DAYS_NO_CONNECTIVITY_MAX and failed_count > 1:
set_offroad_alert("Offroad_ConnectivityNeeded", True)
elif dt.days > DAYS_NO_CONNECTIVITY_PROMPT:
remaining = max(DAYS_NO_CONNECTIVITY_MAX - dt.days, 1)
set_offroad_alert("Offroad_ConnectivityNeededPrompt", True, extra_text=f"{remaining} day{'' if remaining == 1 else 's'}.")
def check_for_update(self) -> None:
cloudlog.info("checking for updates")
excluded_branches = ('release2', 'release2-staging', 'dashcam', 'dashcam-staging')
setup_git_options(OVERLAY_MERGED)
output = run(["git", "ls-remote", "--heads"], OVERLAY_MERGED)
self.branches = defaultdict(lambda: None)
for line in output.split('\n'):
ls_remotes_re = r'(?P<commit_sha>\b[0-9a-f]{5,40}\b)(\s+)(refs\/heads\/)(?P<branch_name>.*$)'
x = re.fullmatch(ls_remotes_re, line.strip())
if x is not None and x.group('branch_name') not in excluded_branches:
self.branches[x.group('branch_name')] = x.group('commit_sha')
cur_branch = self.get_branch(OVERLAY_MERGED)
cur_commit = self.get_commit_hash(OVERLAY_MERGED)
new_branch = self.target_branch
new_commit = self.branches[new_branch]
if (cur_branch, cur_commit) != (new_branch, new_commit):
cloudlog.info(f"update available, {cur_branch} ({cur_commit[:7]}) -> {new_branch} ({new_commit[:7]})")
else:
cloudlog.info(f"up to date on {cur_branch} ({cur_commit[:7]})")
setup_git_options(OVERLAY_MERGED)
def fetch_update(self) -> None:
cloudlog.info("attempting git fetch inside staging overlay")
git_fetch_output = run(["git", "fetch"], OVERLAY_MERGED)
cloudlog.info("git fetch success: %s", git_fetch_output)
self.params.put("UpdaterState", "downloading...")
cur_hash = run(["git", "rev-parse", "HEAD"], OVERLAY_MERGED).rstrip()
upstream_hash = run(["git", "rev-parse", "@{u}"], OVERLAY_MERGED).rstrip()
new_version: bool = cur_hash != upstream_hash
git_fetch_result = check_git_fetch_result(git_fetch_output)
# TODO: cleanly interrupt this and invalidate old update
set_consistent_flag(False)
self.params.put_bool("UpdateAvailable", False)
new_branch = Params().get("SwitchToBranch", encoding='utf8')
if new_branch is not None:
new_version = True
setup_git_options(OVERLAY_MERGED)
cloudlog.info(f"comparing {cur_hash} to {upstream_hash}")
if new_version or git_fetch_result:
cloudlog.info("Running update")
branch = self.target_branch
git_fetch_output = run(["git", "fetch", "origin", branch], OVERLAY_MERGED)
cloudlog.info("git fetch success: %s", git_fetch_output)
if new_version:
cloudlog.info("git reset in progress")
cmds = [
["git", "reset", "--hard", "@{u}"],
["git", "clean", "-xdf"],
["git", "submodule", "init"],
["git", "submodule", "update"],
]
if new_branch is not None:
cloudlog.info(f"switching to branch {repr(new_branch)}")
cmds.insert(0, ["git", "checkout", "-f", new_branch])
r = [run(cmd, OVERLAY_MERGED) for cmd in cmds]
cloudlog.info("git reset success: %s", '\n'.join(r))
cloudlog.info("git reset in progress")
cmds = [
["git", "checkout", "--force", "--no-recurse-submodules", branch],
["git", "reset", "--hard", f"origin/{branch}"],
["git", "clean", "-xdf"],
["git", "submodule", "init"],
["git", "submodule", "update"],
]
r = [run(cmd, OVERLAY_MERGED) for cmd in cmds]
cloudlog.info("git reset success: %s", '\n'.join(r))
if AGNOS:
handle_agnos_update()
# TODO: show agnos download progress
if AGNOS:
handle_agnos_update()
# Create the finalized, ready-to-swap update
self.params.put("UpdaterState", "finalizing update...")
finalize_update()
cloudlog.info("openpilot update successful!")
else:
cloudlog.info("nothing new from git at this time")
return new_version
cloudlog.info("finalize success!")
def main() -> None:
@ -357,8 +422,12 @@ def main() -> None:
overlay_init = Path(os.path.join(BASEDIR, ".overlay_init"))
overlay_init.unlink(missing_ok=True)
updater = Updater()
update_failed_count = 0 # TODO: Load from param?
wait_helper = WaitTimeHelper(proc)
# no fetch on the first time
wait_helper = WaitTimeHelper()
wait_helper.only_check_for_update = True
# Run the update loop
while True:
@ -366,21 +435,24 @@ def main() -> None:
# Attempt an update
exception = None
new_version = False
update_failed_count += 1
try:
# TODO: reuse overlay from previous updated instance if it looks clean
init_overlay()
# TODO: still needed? skip this and just fetch?
# Lightweight internt check
internet_ok, update_available = check_for_update()
if internet_ok and not update_available:
update_failed_count = 0
# ensure we have some params written soon after startup
updater.set_params(update_failed_count, exception)
update_failed_count += 1
# check for update
params.put("UpdaterState", "checking...")
updater.check_for_update()
# Fetch update
if internet_ok:
new_version = fetch_update()
update_failed_count = 0
# download update
if wait_helper.only_check_for_update:
cloudlog.info("skipping fetch this cycle")
else:
updater.fetch_update()
update_failed_count = 0
except subprocess.CalledProcessError as e:
cloudlog.event(
"update process failed",
@ -396,12 +468,14 @@ def main() -> None:
overlay_init.unlink(missing_ok=True)
try:
set_params(new_version, update_failed_count, exception)
params.put("UpdaterState", "idle")
updater.set_params(update_failed_count, exception)
except Exception:
cloudlog.exception("uncaught updated exception while setting params, shouldn't happen")
# infrequent attempts if we successfully updated recently
wait_helper.sleep(5*60 if update_failed_count > 0 else 90*60)
wait_helper.only_check_for_update = False
wait_helper.sleep(5*60 if update_failed_count > 0 else 1.5*60*60)
if __name__ == "__main__":

Loading…
Cancel
Save