diff --git a/app/helpers/installation_helper.rb b/app/helpers/installation_helper.rb
index 022fd0e5..1f4f36ad 100644
--- a/app/helpers/installation_helper.rb
+++ b/app/helpers/installation_helper.rb
@@ -1,8 +1,25 @@
# frozen_string_literal: true
module InstallationHelper
+ # The block, plus a way out for the readers whose bootloader cannot run it.
+ #
+ # guarded_flash joins the transfer to the erase and the write with `&&` so a
+ # failed transfer cannot reach the erase -- see the comment on that method for
+ # the camera that cost. But `&&` is a hush feature, and stock vendor
+ # bootloaders are what people are running when they follow this page for the
+ # first time. At least one does not have it: the reporter in
+ # OpenIPC/firmware#2299 got "the help entry for the first command" back and
+ # had to enter each part by hand.
+ #
+ # That failure is safe -- nothing is transferred, nothing is erased -- but it
+ # is silent about why, and the line is not one a reader can take apart
+ # unaided. So say it, next to the block it applies to, and only there.
def list_of_commands(text)
- content_tag 'pre', text.join('
').html_safe, class: 'bg-light p-4'
+ block = content_tag 'pre', text.join('
').html_safe, class: 'bg-light p-4'
+ return block unless text.any? { |line| line.to_s.include?('&&') }
+
+ safe_join([block, content_tag('p', t('firmware.installation.compound_caveat_html'),
+ class: 'small text-muted')])
end
def do_not_copy_paste
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 988c6af3..708e87ab 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -74,6 +74,7 @@ de:
subtitle: Sichern Sie Ihre Standard-Firmware. Überspringen Sie diesen Teil des Installationsprozesses nicht!
title: Speichern Sie die Original-Firmware
backup_32: AUFMERKSAMKEIT! Stellen Sie vor dem Sichern von 32 MB Flash-ROM sicher, dass die Kamera über mindestens 64 MB RAM verfügt.
+ compound_caveat_html: Wenn der Bootloader einen Hinweis zur Verwendung ausgibt, statt die Zeile auszuführen, versteht er && nicht. Geben Sie die Teile einzeln ein und führen Sie jeden erst aus, nachdem der vorhergehende Erfolg gemeldet hat — genau diese Prüfung nimmt Ihnen && ab.
flashing:
alternative: Alternativ können Sie die OpenIPC-Firmware einzeln flashen
flashing_footfs:
diff --git a/config/locales/en.yml b/config/locales/en.yml
index f665157f..b3b2414a 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -74,6 +74,7 @@ en:
subtitle: Back up your stock firmware, don't skip this part of the installation process!
title: Save the original firmware
backup_32: ATTENTION! Before backing up 32M flash ROM, make sure that the camera has at least 64M of RAM.
+ compound_caveat_html: If your bootloader answers with a usage message instead of running the line, it does not understand &&. Enter the parts one at a time, and run each one only after the part before it has reported success — that check is what && does for you.
flashing:
alternative: Alternatively, flash OpenIPC Firmware by its parts
flashing_footfs:
diff --git a/config/locales/es.yml b/config/locales/es.yml
index a0cd5c05..72c23ae7 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -74,6 +74,7 @@ es:
subtitle: Haga una copia de seguridad de su firmware original, ¡no se salte esta parte del proceso de instalación!
title: Guarde el firmware original
backup_32: "¡ATENCIÓN! Antes de realizar una copia de seguridad de la ROM flash de 32 M, asegúrese de que la cámara tenga al menos 64 M de RAM."
+ compound_caveat_html: Si el gestor de arranque responde con un mensaje de uso en lugar de ejecutar la línea, no entiende &&. Introduzca las partes de una en una y ejecute cada una solo después de que la anterior haya indicado que ha tenido éxito — esa comprobación es lo que && hace por usted.
flashing:
alternative: Alternativamente, actualice el firmware OpenIPC por sus partes
flashing_footfs:
diff --git a/config/locales/fa.yml b/config/locales/fa.yml
index 785806cf..9232fcb5 100644
--- a/config/locales/fa.yml
+++ b/config/locales/fa.yml
@@ -74,6 +74,7 @@ fa:
subtitle: از سیستم عامل استوک خود نسخه پشتیبان تهیه کنید، این قسمت از فرآیند نصب را نادیده نگیرید!
title: سیستم عامل اصلی را ذخیره کنید
backup_32: توجه! قبل از پشتیبان گیری از رام فلش 32M، مطمئن شوید که دوربین حداقل 64M رم دارد.
+ compound_caveat_html: اگر بوتلودر بهجای اجرای این خط، راهنمای استفادهٔ فرمان را نمایش داد، یعنی && را نمیشناسد. بخشها را یکییکی وارد کنید و هر بخش را تنها پس از آنکه بخش پیشین موفقیت را گزارش کرد اجرا کنید — همین بررسی همان کاری است که && برای شما انجام میدهد.
flashing:
alternative: یا، فریمور OpenIPC را با قطعات آن فلش کنید
flashing_footfs:
diff --git a/config/locales/fr.yml b/config/locales/fr.yml
index 9870ef1c..5b4e9191 100644
--- a/config/locales/fr.yml
+++ b/config/locales/fr.yml
@@ -74,6 +74,7 @@ fr:
subtitle: Sauvegardez votre firmware d'origine, ne sautez pas cette partie du processus d'installation !
title: Enregistrez le firmware d'origine
backup_32: ATTENTION! Avant de sauvegarder une ROM flash de 32 Mo, assurez-vous que l'appareil photo dispose d'au moins 64 Mo de RAM.
+ compound_caveat_html: Si le chargeur d'amorçage affiche un message d'utilisation au lieu d'exécuter la ligne, c'est qu'il ne comprend pas &&. Saisissez les parties une par une et n'exécutez chacune qu'après que la précédente a signalé une réussite — c'est exactement la vérification que && effectue pour vous.
flashing:
alternative: Vous pouvez également flasher le micrologiciel OpenIPC par ses pièces.
flashing_footfs:
diff --git a/config/locales/it.yml b/config/locales/it.yml
index 63f4cb10..6038beb8 100644
--- a/config/locales/it.yml
+++ b/config/locales/it.yml
@@ -74,6 +74,7 @@ it:
subtitle: Esegui il backup del firmware di serie, non saltare questa parte del processo di installazione!
title: Salvare il firmware originale
backup_32: ATTENZIONE! Prima di eseguire il backup della ROM flash da 32 MB, assicurarsi che la fotocamera disponga di almeno 64 MB di RAM.
+ compound_caveat_html: Se il bootloader risponde con un messaggio d'uso invece di eseguire la riga, non riconosce &&. Inserire le parti una alla volta ed eseguire ciascuna solo dopo che la precedente ha riportato esito positivo — è proprio questo il controllo che && svolge per voi.
flashing:
alternative: In alternativa, eseguire il flashing del firmware OpenIPC dalle sue parti
flashing_footfs:
diff --git a/config/locales/pl.yml b/config/locales/pl.yml
index ca9d90e6..291c15e9 100644
--- a/config/locales/pl.yml
+++ b/config/locales/pl.yml
@@ -74,6 +74,7 @@ pl:
subtitle: Utwórz kopię zapasową oprogramowania sprzętowego, nie pomijaj tej części procesu instalacji!
title: Zapisz oryginalne oprogramowanie sprzętowe
backup_32: UWAGA! Przed utworzeniem kopii zapasowej pamięci flash ROM o pojemności 32 MB upewnij się, że aparat ma co najmniej 64 MB pamięci RAM.
+ compound_caveat_html: Jeśli bootloader zamiast wykonać wiersz wypisuje sposób użycia polecenia, nie rozumie &&. Wprowadzaj części pojedynczo i uruchamiaj każdą dopiero wtedy, gdy poprzednia zgłosi powodzenie — to właśnie tę kontrolę wykonuje za ciebie &&.
flashing:
alternative: Alternatywnie, flashuj oprogramowanie sprzętowe OpenIPC według jego części
flashing_footfs:
diff --git a/config/locales/pt.yml b/config/locales/pt.yml
index c8062b44..91e77650 100644
--- a/config/locales/pt.yml
+++ b/config/locales/pt.yml
@@ -74,6 +74,7 @@ pt:
subtitle: Faça backup do seu firmware padrão, não pule esta parte do processo de instalação!
title: Salve o firmware original
backup_32: ATENÇÃO! Antes de fazer backup de ROM flash de 32M, certifique-se de que a câmera tenha pelo menos 64M de RAM.
+ compound_caveat_html: Se o bootloader responder com uma mensagem de utilização em vez de executar a linha, não reconhece &&. Introduza as partes uma de cada vez e execute cada uma apenas depois de a anterior indicar sucesso — é essa verificação que && faz por si.
flashing:
alternative: Como alternativa, atualize o firmware OpenIPC por suas partes
flashing_footfs:
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index d2a75f2f..84b7fde8 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -74,6 +74,7 @@ ru:
subtitle: Сделайте резервную копию стоковой прошивки, не пропускайте эту часть процесса установки!
title: Сохраните оригинальную прошивку
backup_32: ВНИМАНИЕ! Перед резервным копированием 32-мегабайтной флэш-памяти убедитесь, что камера имеет не менее 64 МБ оперативной памяти.
+ compound_caveat_html: Если загрузчик в ответ печатает подсказку по использованию команды вместо того, чтобы выполнить строку, значит он не понимает &&. Вводите части по одной и запускайте каждую следующую только после того, как предыдущая сообщит об успехе — именно эту проверку и делает за вас &&.
flashing:
alternative: В качестве альтернативы прошивайте прошивку OpenIPC по частям.
flashing_footfs:
diff --git a/config/locales/zh.yml b/config/locales/zh.yml
index beda5d2c..94a4d62d 100644
--- a/config/locales/zh.yml
+++ b/config/locales/zh.yml
@@ -74,6 +74,7 @@ zh:
subtitle: 备份您的原始固件,请不要跳过这个步骤!
title: 保存原始固件
backup_32: 请注意!在备份 32M 闪存之前,请确保摄像机至少有 64M 的 RAM。
+ compound_caveat_html: 如果引导程序输出的是命令用法说明而不是执行该行,说明它不支持 &&。请逐条输入各个部分,并且每一条都要等前一条报告成功之后再执行——这正是 && 替你做的检查。
flashing:
alternative: 或者,通过其烧录部分 OpenIPC 固件
flashing_footfs:
diff --git a/test/controllers/socs_controller_test.rb b/test/controllers/socs_controller_test.rb
index 9a91969d..105ff349 100644
--- a/test/controllers/socs_controller_test.rb
+++ b/test/controllers/socs_controller_test.rb
@@ -599,6 +599,35 @@ def submit_in_russian(soc, flash_type, firmware_version)
end
end
+ # --- the way out when the bootloader has no `&&` ---
+
+ # guarded_flash joins the transfer to the erase with `&&` so a failed transfer
+ # cannot reach the erase. `&&` is a hush feature, and the bootloader people
+ # are running when they first follow this page is the stock vendor one -- the
+ # reporter in OpenIPC/firmware#2299 got "the help entry for the first command"
+ # back from theirs. Nothing is erased in that case, but nothing explains it
+ # either, so the block that uses `&&` says what to do instead.
+ test 'a block built with && carries the note about bootloaders that lack it' do
+ soc = instructable_soc('TS3516EVD00')
+
+ with_release_index(*every_edition_for(soc)) do
+ submit(soc, 'nor8m')
+
+ assert_match '&& sf erase', response.body
+ assert_match 'it does not understand &&', response.body
+ end
+ end
+
+ test 'the note is translated, not another hardcoded English string' do
+ soc = instructable_soc('TS3516EVD10')
+
+ with_release_index(*every_edition_for(soc)) do
+ I18n.with_locale(:ru) { submit(soc, 'nor8m') }
+
+ assert_match 'не понимает &&', response.body
+ end
+ end
+
# --- the permanent link ---
def permalink_for(**attrs)
diff --git a/test/helpers/installation_helper_test.rb b/test/helpers/installation_helper_test.rb
new file mode 100644
index 00000000..bf7c7426
--- /dev/null
+++ b/test/helpers/installation_helper_test.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class InstallationHelperTest < ActionView::TestCase
+ include InstallationHelper
+
+ # The note belongs to the blocks that chain with `&&` and to no others. A
+ # page carries several blocks and only some of them chain, so this is the
+ # level where the rule is visible; the controller test covers it end to end.
+ test 'a block that chains with && is followed by the note' do
+ html = list_of_commands(['tftpboot 0x82000000 f && sf erase 0x0 0x800000'])
+
+ assert_includes html, 'it does not understand &&'
+ end
+
+ # guarded_flash is `transfer && erase && write` -- two gates, not one. The
+ # first wording said to run the erase and the write once the transfer had
+ # succeeded, which rebuilds only the first gate: a reader whose erase failed
+ # would have gone on to write into flash that was never cleared. The rule has
+ # to be sequential to cover both, so pin that rather than the sentence.
+ test 'the note gates every part on the one before it, not just on the transfer' do
+ html = list_of_commands(['tftpboot 0x82000000 f && sf erase 0x0 0x800000 && sf write 0x82000000 0x0 0x1000'])
+
+ assert_includes html, 'only after the part before it has reported success'
+ assert_not_includes html, 'after the transfer has reported success'
+ end
+
+ # `run setnor8m`, `run uknand; run urnand`, the mw.b lines -- none of these
+ # chain, so the note would be answering a question the block has not raised.
+ test 'a block that does not chain is left alone' do
+ html = list_of_commands(['run setnor8m'])
+
+ assert_equal '
run setnor8m', html + end + + test 'the note is html, not escaped markup' do + html = list_of_commands(['tftp 0x82000000 f && sf erase 0x0 0x800000']) + + assert_includes html, '
&&'
+ assert_not_includes html, '<code>'
+ end
+end