Aller au contenu
Sommaire

gup Un outil open source

Une commande pour mettre à jour toute sa machine.

Sur un poste de développeur, les logiciels arrivent par des dizaines de canaux, chacun avec sa commande de mise à jour. gup les interroge tous d’un coup, liste ce qui est en retard et lance au même endroit ce que je coche. Je l’ai conçu et je le publie en open source.

La vraie interface, fenêtre entière, sur la machine d’exemple du dépôt. Un clic l’agrandit.
Besoin
Mettre à jour en une fois tout ce qui est installé sur un poste de développeur, quel que soit l’outil qui l’a installé.
Ma part
J’ai conçu l’outil et écrit l’essentiel de son code, de son architecture à ses 153 sources, avec ses tests et sa documentation ; je relis les contributions et je publie les versions.
Rôle
Auteur et mainteneur
Période
Depuis mai 2026
Équipe
Projet personnel mené par son mainteneur, ouvert aux contributions ; deux pull requests extérieures fusionnées en mai 2026
Statut
Open source, sur npm

Discutons de ce projet

Une commande par outil, et aucune vue d’ensemble

Sur un poste de développeur, chaque outil a sa commande de mise à jour, son format de sortie et ses angles morts. La documentation de gup le résume ainsi : dix à quinze commandes enchaînées à la main, plusieurs fois par mois, sans savoir laquelle a oublié quoi.

gup ne remplace aucun de ces outils. Il les interroge tous en parallèle, liste ce qui est en retard et les fait travailler : c’est un chef d’orchestre, pas un gestionnaire de paquets de plus.

Six petites machines dont les fils emmêlés convergent vers une seule boîte éclairée par une lanterne. Six petites machines dont les fils emmêlés convergent vers une seule boîte éclairée par une lanterne.
  • Winget : winget upgrade --id Git.Git --exact
  • Chocolatey : choco upgrade all -y
  • Scoop : scoop update *
  • Homebrew : brew upgrade --formula
  • npm : npm install -g typescript@latest
  • pipx : pipx upgrade-all
  • uv : uv tool upgrade --all
  • Cargo : cargo install-update -a
  • Rustup : rustup update
  • VS Code : code --install-extension … --force
  • gcloud : gcloud components update --quiet
gup une commande, toutes les sources
Onze des commandes que gup lance à ma place, avec leurs arguments principaux (release 0.5.3).

Analyser, choisir, mettre à jour, garder une trace

Quatre écrans de la vraie application, en français, sur la machine d’exemple de son dépôt. Un clic agrandit chaque écran.

  1. Analyser

    gup interroge toutes les sources en parallèle. Une source en panne ne coûte que sa propre ligne : l’analyse continue.

    L’analyse en cours dans gup : neuf sources sur quatorze ont répondu ; Scoop a échoué, les autres annoncent leurs mises à jour.
    Scoop échoue ; les treize autres sources continuent.
  2. Choisir

    Je coche un paquet, une source entière ou tout. Avant de lancer, gup annonce les paquets qui demandent les droits administrateur.

    La confirmation avant la mise à jour : six paquets cochés, dont un qui demande les droits administrateur, avec une seule invite en fin de lot.
    Une seule demande de droits administrateur, en fin de lot.
  3. Mettre à jour

    Chaque installation se déroule dans l’application, dans un vrai terminal : la barre de progression et les questions de l’installateur restent visibles.

    La mise à jour en cours : trois paquets faits, PowerToys en téléchargement dans le terminal intégré, deux paquets en attente dont un réservé à l’étape administrateur.
    PowerToys se télécharge dans le terminal intégré.
  4. Garder une trace

    gup tient un journal de ce qu’il a fait et en tire un rapport. Il sait aussi programmer des paquets précis, sans rien laisser tourner en arrière-plan.

    Le journal de gup sur douze mois d’exemple : mises à jour réussies, échecs, scans, et une carte des mises à jour par jour.
    Douze mois d’activité, échecs compris.

Cocher, lancer, regarder passer les paquets

La même liste, à manipuler dans la page. Une source a échoué à l’analyse : elle reste à sa ligne, sans bloquer les autres.

Le paquet marqué « admin » passe en dernier, avec les autres paquets qui demandent des droits, derrière une seule demande.

Le vrai gup s’essaie aussi sur son site, dans une démonstration de terminal.

Le site de gup

Données d’exemple du dépôt de gup : rien n’est installé.

Les paquets à mettre à jour, par source

Scoop analyse en échec : scoop status failed (code 1). Les autres sources continuent.

Aucun paquet coché.

Sans JavaScript, la démonstration reste figée : les quatre écrans ci-dessus montrent le vrai déroulé.

Apprendre les règles de 153 outils

Le vrai travail est là : chaque outil a ses exceptions, et gup les absorbe une par une.

  • winget répond dans la langue du système : gup lit aussi sa sortie en français.

    Le commit du 19/05/2026
  • winget peut s’arrêter sur une question (le dossier d’installation) et retenir tout le lot : gup lui interdit d’en poser, et donne la commande à lancer à la main.

    Le commit du 06/10/2026
  • Pour Chocolatey, les codes 3010 et 1641 ne sont pas des erreurs : la mise à jour a réussi, un redémarrage est requis.

    La règle dans le code
  • Sur macOS, le Ruby d’Apple est figé par le système : gup l’écarte au lieu d’afficher une quarantaine de mises à jour impossibles.

    La règle dans le code
  • Un logiciel listé par deux sources n’apparaît qu’une fois, chez celle qui sait le mettre à jour.

    La règle dans le code
  • Sous Windows, chercher les outils dans le PATH figeait le terminal 3,3 à 3,8 s. Cela prend maintenant 9 ms.

    Les notes de la 0.5.0

Les 153 sources de gup, par famille

Système d’exploitation

Sous Windows : 139 sources possibles, 14 grisées et jamais lancées. Sous macOS : 132 sources possibles, 21 grisées et jamais lancées. Sous Linux : 126 sources possibles, 27 grisées et jamais lancées.

  • Systèmes d’exploitation 6 / 15 9 / 15 4 / 15
  • WSL 7 / 7 0 / 7 0 / 7
  • Node.js 9 / 10 9 / 10 9 / 10
  • Python 8 / 9 8 / 9 8 / 9
  • Gestionnaires de versions 5 / 6 6 / 6 6 / 6
  • Autres langages 13 / 14 14 / 14 14 / 14
  • .NET et PHP 7 / 7 7 / 7 7 / 7
  • JVM 2 / 2 2 / 2 2 / 2
  • Rust 2 / 2 2 / 2 2 / 2
  • Shell et terminal 5 / 5 4 / 5 4 / 5
  • Cloud 12 / 12 12 / 12 12 / 12
  • Infrastructure as code 10 / 10 10 / 10 10 / 10
  • Kubernetes 13 / 13 13 / 13 13 / 13
  • Conteneurs 6 / 6 3 / 6 3 / 6
  • Sécurité 10 / 10 10 / 10 10 / 10
  • Outils en ligne de commande 8 / 8 7 / 8 7 / 8
  • Embarqué et mobile 5 / 6 6 / 6 5 / 6
  • IDE 6 / 6 5 / 6 5 / 6
  • Extensions d’éditeurs 4 / 4 4 / 4 4 / 4
  • Les gestionnaires eux-mêmes 1 / 1 1 / 1 1 / 1

Registre de gup, release 0.5.3, relevé le 13/10/2026. Famille : le dossier de la source dans le code.

Tenu comme un produit

Des versions publiées, testées sur trois systèmes et documentées, pour que d’autres puissent s’en servir et y contribuer.

12 versions publiées sur npm, de mai à octobre 2026
  1. 0.1.0 · 0.1.1
  2. 0.2.0
  3. 0.2.1
  4. 0.2.2
  5. 0.3.0 · 0.3.1 · 0.3.2
  6. 0.5.0 · 0.5.1 · 0.5.2 · 0.5.3

Registre npm, relevé le 13/10/2026. La 0.4.0, préparée dans le dépôt, n’a jamais été publiée : tout son contenu est sorti avec la 0.5.0.

  • Trois systèmes à chaque pull request

    L’intégration continue rejoue les tests sous Windows, macOS et Linux.

    La configuration
  • Une note par version

    Chaque version a ses notes : ce qui change, ce qui casse, comment migrer.

    Les notes de version
  • L’architecture écrite

    Couches, contrats des sources, pipeline de mise à jour et décisions motivées.

    L’architecture
  • Une gouvernance écrite

    Le projet est mené par son mainteneur ; les décisions se prennent en public, sur les issues et les pull requests.

    La gouvernance
  • Anglais et français

    L’interface parle les deux langues ; les captures de cette page sont en français.

    La configuration

Une source manque ? En général, il suffit d’un fichier, d’une ligne dans le registre et de ses tests.

Le guide de contribution

Sous le capot

Quatre choix d’architecture qui ne se voient pas à l’écran, puis deux garanties ; chacun se déplie sur son extrait du code public.

  1. Chaque source dans son fichier, derrière un contrat code
    Contrainte
    153 outils, dont certains plantent, bloquent ou répondent n’importe quoi.
    Choix
    Chaque source dans son propre fichier, derrière un contrat commun, sans dépendre des autres.
    Effet
    Une source en panne ne perd que sa propre ligne.
    L’architecture
    src/core/types.ts · TypeScript
    export interface Provider {
      readonly id: string;
      readonly displayName: string;
      readonly platforms?: PlatformSet;
      // …
      isAvailable(): Promise<boolean>;
      listOutdated(): Promise<OutdatedPackage[]>;
      update(packageId: string, options?: UpdateOptions): Promise<UpdateOutcome>;
      // …
    }
  2. Aucun programme lancé par un shell code
    Contrainte
    gup lance des programmes avec des noms de paquets venus de l’extérieur.
    Choix
    Tous les programmes partent d’un seul point du code, arguments en liste, sans shell. Seul Scoop, qui l’exige, passe par une liste blanche contrôlée ; un test refuse tout nouvel appel à un shell.
    Effet
    Un nom de paquet reste un argument, jamais une commande.
    Le test de sécurité
    tests/security/shell-usage.test.ts · TypeScript
    it("no source file uses child_process directly — runner.ts is the single chokepoint", async () => {
      const files = await walk(SRC_ROOT);
      const offenders: string[] = [];
      for (const file of files) {
        const rel = toPosixRel(file);
        if (rel === "src/core/runner.ts") continue;
        const content = await readFile(file, "utf8");
        if (/from\s+["']node:child_process["']/.test(content)) offenders.push(rel);
        if (/require\(["']child_process["']\)/.test(content)) offenders.push(rel);
      }
      expect(offenders).toEqual([]);
    });
  3. Un paquet par appel au gestionnaire code
    Contrainte
    Une installation peut échouer ou dépasser son délai au milieu d’un lot.
    Choix
    Un paquet par appel au gestionnaire, même quand l’outil sait tout mettre à jour d’un coup, au prix de quelques lancements de plus.
    Effet
    Un échec ne fait tomber qu’une installation, jamais le lot entier.
    Les décisions notables
    src/core/update/apply-update.ts · TypeScript
    async function settledUpdate(
      provider: Provider,
      packageId: string,
      update: UpdateOptions | undefined,
    ): Promise<UpdateOutcome> {
      try {
        return update ? await provider.update(packageId, update) : await provider.update(packageId);
      } catch (error) {
        return { id: packageId, success: false, message: rejectionMessage(error) };
      }
    }
  4. Un gup éphémère toutes les 15 minutes code
    Contrainte
    Programmer des mises à jour sans laisser un programme tourner en permanence.
    Choix
    Le planificateur du système lance un gup éphémère toutes les 15 minutes, seulement si une programmation existe.
    Effet
    Rien ne tourne en arrière-plan.
    La planification
    src/core/scheduler/scheduler-timing.ts · TypeScript
    /**
     *     MAX_RUN_MINUTES + cap  <  TICK_WATCHDOG_MINUTES  <  EXECUTION_TIME_LIMIT_MINUTES
     *          120 + 30          <          170           <             180
     */
    
    /** How often the OS trigger starts a tick: latency against wake-ups. */
    export const TICK_INTERVAL_MINUTES = 15;
  5. Chaque système voit ses outils code

    Une source d’un autre système est grisée et n’est jamais lancée, même si un exécutable du même nom traîne dans le PATH.

    Le filtre par système
    src/core/platform/is-supported-on.ts · TypeScript
    export function isSupportedOn(
      subject: PlatformScoped,
      platform: NodeJS.Platform = process.platform,
    ): boolean {
      return subject.platforms === undefined || subject.platforms.includes(platform);
    }
  6. Lisible partout, couleurs personnalisées comprises code

    27 thèmes, et chaque texte atteint le contraste WCAG AA, couleurs personnalisées comprises.

    La garantie de contraste
    src/ui/theme/contrast-rules.ts · TypeScript
    /** Minimum ratios: text (WCAG 1.4.3 / 1.4.6) and UI elements (1.4.11, which has no AAA). */
    export const CONTRAST_TARGETS: Readonly<Record<ContrastLevel, { text: number; ui: number }>> = {
      AA: { text: 4.5, ui: 3 },
      AAA: { text: 7, ui: 3 },
    };

En une phrase

Un seul travail, tenu sur 153 sources et trois systèmes : mettre à jour ce qui est déjà installé sur une machine, quel que soit l’outil qui l’a installé.

Sources et méthode : chiffres, dates, méthode et crédits

Sources. Les 153 sont comptées à l’exécution dans le registre de gup, à la release 0.5.3 (commit 0c0e85a du 06/10/2026), le 13/10/2026. La famille est le dossier de la source dans le code ; le système, l’ensemble déclaré par la source dans le catalogue : 14 sources grisées sous Windows, 21 sous macOS, 27 sous Linux.

Versions. Douze versions publiées sur npm, de la 0.1.0 (19/05/2026) à la 0.5.3 (06/10/2026), d’après les dates du registre npm relevées le 13/10/2026. La 0.4.0 n’a jamais été publiée. Les téléchargements npm ne sont pas affichés : ils se concentrent les jours de publication et ne mesurent pas un usage.

Écrans et démonstration. Les quatre écrans et celui de l’en-tête sont produits par le générateur de captures du dépôt, à la release 0.5.3, interface en français, sur sa machine d’exemple : des versions inventées, aucune machine réelle, aucun gestionnaire lancé. La démonstration reprend les mêmes paquets. Les commandes de la première figure sont lues dans les fichiers des sources, à la même révision.

Le problème. « Dix à quinze commandes, plusieurs fois par mois » traduit la page de cadrage du projet. Les décisions d’architecture sont décrites dans ses décisions notables. La règle « sans shell » a une exception écrite : Scoop, lancé par un shell sur une liste blanche (architecture, ligne 484). La phrase de clôture traduit la première ligne de la page de cadrage, avec les relevés de l’en-tête.

Contributions. La règle des codes 3010 et 1641 de Chocolatey et le lot administrateur unique viennent d’une contribution extérieure de mai 2026 : les pull requests n° 22 et n° 23, fusionnées le 20/05/2026. Le reste de l’historique, à la release 0.5.3 : 856 commits de l’auteur et 29 de Dependabot.

Sous le capot. Extraits du dépôt public, à la release 0.5.3 (0c0e85a), relus le 15/10/2026.