[IMP] script todo: ne pas retélécharger un IDE déjà installé
Rejouer une installation est le cas normal — une qui est morte, un outil ajouté après coup — et le téléchargement en est la partie longue : environ cinq minutes pour PyCharm, autant pour Android Studio, à chaque fois. Les deux étapes vérifient donc /opt avant de sortir curl. Mesuré sur erplibre-ubuntu-2604-gnome, IDE déjà posés : les deux étapes passent de dix minutes à 0,094 s au total. Le reste rejoue quand même — lanceur, alias, raccourci de bureau — il est idempotent et bon marché. Un test vérifie que la garde n'avale pas l'échec du cas où il faut bel et bien télécharger. --- EN --- Replaying an install is the normal case — one that died, a tool added later — and the download is the long part: about five minutes for PyCharm, as much for Android Studio, every time. Both steps now check /opt before reaching for curl. Measured on erplibre-ubuntu-2604-gnome with both IDEs already in place: the two steps drop from ten minutes to 0.094 s total. The rest still replays — launcher, alias, desktop entry — it is idempotent and cheap. A test checks the guard does not swallow the failure of the case where downloading is actually needed. Assisted-by: Claude Opus 5
This commit is contained in:
parent
b4310665fc
commit
72b19568af
3 changed files with 98 additions and 2 deletions
|
|
@ -5606,6 +5606,15 @@ class TODO:
|
|||
'if [ -z "$jb" ]; then '
|
||||
f'echo " {t("no JetBrains build for")} $(uname -m)"; false; '
|
||||
"else "
|
||||
# Déjà posé ? On ne retélécharge pas. Rejouer une
|
||||
# installation est le cas NORMAL — une qui est morte, un outil
|
||||
# ajouté après coup — et le téléchargement en est la partie
|
||||
# longue : mesuré, ~5 min pour Android Studio, autant pour
|
||||
# PyCharm. Le reste de l'étape (lanceur, alias, raccourci)
|
||||
# rejoue de toute façon, lui est idempotent et bon marché.
|
||||
"if [ -x /opt/pycharm/bin/pycharm.sh ]; then "
|
||||
f'echo " {t("already there, download skipped")}"; '
|
||||
"else "
|
||||
# /var/tmp et non /tmp : sur Fedora et dérivés /tmp est un tmpfs, en
|
||||
# RAM — 1,2 Go d'archive y tueraient une VM de 3 Go.
|
||||
# Le flux dit quelle archive Community prendre pour cette
|
||||
|
|
@ -5635,7 +5644,7 @@ class TODO:
|
|||
'curl -fsSL "$url" -o "$tmp" && '
|
||||
"sudo mkdir -p /opt/pycharm && "
|
||||
'sudo tar -xzf "$tmp" -C /opt/pycharm --strip-components=1; '
|
||||
'rc=$?; rm -f "$tmp"; [ $rc -eq 0 ]; fi; } && { '
|
||||
'rc=$?; rm -f "$tmp"; [ $rc -eq 0 ]; fi; fi; } && { '
|
||||
+ self._qemu_jetbrains_launcher_cmd("/opt/pycharm", "pycharm")
|
||||
+ self._qemu_desktop_entry_cmd(
|
||||
"pycharm",
|
||||
|
|
@ -5825,6 +5834,15 @@ class TODO:
|
|||
f'echo " {t("Android Studio: Google publishes x86_64 only")}"; '
|
||||
"false; "
|
||||
"else "
|
||||
# Déjà posé ? On ne retélécharge pas. Rejouer une
|
||||
# installation est le cas NORMAL — une qui est morte, un outil
|
||||
# ajouté après coup — et le téléchargement en est la partie
|
||||
# longue : mesuré, ~5 min pour Android Studio, autant pour
|
||||
# PyCharm. Le reste de l'étape (lanceur, alias, raccourci)
|
||||
# rejoue de toute façon, lui est idempotent et bon marché.
|
||||
"if [ -x /opt/android-studio/bin/studio ]; then "
|
||||
f'echo " {t("already there, download skipped")}"; '
|
||||
"else "
|
||||
# La page officielle porte l'URL en clair ; le repli garde une
|
||||
# version connue qui répond, pour le jour où sa forme change.
|
||||
f"url=$(curl -fsSL --max-time 30 {self._QEMU_ANDROID_PAGE} "
|
||||
|
|
@ -5836,7 +5854,7 @@ class TODO:
|
|||
"sudo mkdir -p /opt/android-studio && "
|
||||
'sudo tar -xzf "$tmp" -C /opt/android-studio '
|
||||
"--strip-components=1; "
|
||||
'rc=$?; rm -f "$tmp"; [ $rc -eq 0 ]; fi; } && { '
|
||||
'rc=$?; rm -f "$tmp"; [ $rc -eq 0 ]; fi; fi; } && { '
|
||||
+ self._qemu_jetbrains_launcher_cmd(
|
||||
"/opt/android-studio", "studio", alias="android-studio"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2260,6 +2260,10 @@ TRANSLATIONS = {
|
|||
"line": {"fr": "ligne", "en": "line"},
|
||||
"free space": {"fr": "libre", "en": "free"},
|
||||
"silent": {"fr": "silence", "en": "silent"},
|
||||
"already there, download skipped": {
|
||||
"fr": "déjà présent, téléchargement évité",
|
||||
"en": "already there, download skipped",
|
||||
},
|
||||
"inotify watches raised for the IDE": {
|
||||
"fr": "watches inotify relevées pour l'IDE",
|
||||
"en": "inotify watches raised for the IDE",
|
||||
|
|
|
|||
|
|
@ -773,6 +773,80 @@ class TestPycharmFirstOpen(unittest.TestCase):
|
|||
)
|
||||
|
||||
|
||||
class TestIdeInstallIsReplayable(unittest.TestCase):
|
||||
"""Rejouer une installation ne doit pas retélécharger 2 Go.
|
||||
|
||||
C'est le cas NORMAL : une installation morte qu'on relance, un outil ajouté
|
||||
après coup. Mesuré sur la VM, les deux étapes passent de ~5 min chacune à
|
||||
0,094 s au total quand /opt porte déjà l'IDE — le reste (lanceur, alias,
|
||||
raccourci) rejoue quand même, il est idempotent et bon marché.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.todo = TODO.__new__(TODO)
|
||||
self.py = self.todo._qemu_pycharm_remote_cmd()
|
||||
self.st = self.todo._qemu_android_studio_remote_cmd()
|
||||
|
||||
def test_pycharm_checks_before_downloading(self):
|
||||
self.assertIn("[ -x /opt/pycharm/bin/pycharm.sh ]", self.py)
|
||||
self.assertLess(
|
||||
self.py.index("/opt/pycharm/bin/pycharm.sh"),
|
||||
self.py.index("curl"),
|
||||
)
|
||||
|
||||
def test_android_studio_checks_before_downloading(self):
|
||||
self.assertIn("[ -x /opt/android-studio/bin/studio ]", self.st)
|
||||
self.assertLess(
|
||||
self.st.index("/opt/android-studio/bin/studio"),
|
||||
self.st.index("curl"),
|
||||
)
|
||||
|
||||
def test_the_launcher_still_runs_when_the_download_is_skipped(self):
|
||||
"""Sauter le téléchargement ne doit pas sauter l'alias : c'est lui qui
|
||||
rend « pycharm » et « android-studio » appelables."""
|
||||
# rindex : le chemin du lanceur apparaît aussi dans la garde, tout au
|
||||
# début. C'est la DERNIÈRE occurrence — l'installation du lanceur — qui
|
||||
# doit suivre le bloc de téléchargement.
|
||||
for cmd, marker in (
|
||||
(self.py, "/usr/local/bin"),
|
||||
(self.st, "/usr/local/bin"),
|
||||
):
|
||||
self.assertGreater(cmd.rindex(marker), cmd.index("curl"), marker)
|
||||
|
||||
def test_a_real_download_failure_still_fails(self):
|
||||
"""La garde ne doit pas avaler l'échec du cas où il faut télécharger.
|
||||
On force l'absence d'IDE et un curl qui échoue."""
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
bin_dir = pathlib.Path(tmp) / "bin"
|
||||
bin_dir.mkdir()
|
||||
(bin_dir / "curl").write_text("#!/bin/bash\nexit 22\n")
|
||||
(bin_dir / "sudo").write_text("#!/bin/bash\nexit 0\n")
|
||||
(bin_dir / "python3").write_text("#!/bin/bash\ncat >/dev/null\n")
|
||||
for n in ("curl", "sudo", "python3"):
|
||||
(bin_dir / n).chmod(0o755)
|
||||
res = subprocess.run(
|
||||
["bash", "-c", self.st],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
env=dict(os.environ, PATH=f"{bin_dir}:/usr/bin:/bin"),
|
||||
timeout=60,
|
||||
)
|
||||
out = res.stdout + res.stderr
|
||||
# Sur cette machine /opt/android-studio n'existe pas : la garde
|
||||
# laisse donc passer, et l'échec du curl doit se voir.
|
||||
self.assertIn("⚠", out, out[-300:])
|
||||
|
||||
def test_both_steps_are_valid_shell(self):
|
||||
for cmd in (self.py, self.st):
|
||||
res = subprocess.run(
|
||||
["bash", "-n"], input=cmd, capture_output=True, text=True
|
||||
)
|
||||
self.assertEqual(0, res.returncode, res.stderr)
|
||||
|
||||
|
||||
class TestPycharmNetIsNarrow(unittest.TestCase):
|
||||
"""Le filet de fermeture ne doit JAMAIS viser le ssh qui porte l'install.
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue