Skip to content

Browser-Erweiterung ​

Die Erweiterung setzt einen Run-Button dorthin, wo du schon bist: neben das Branch-Dropdown auf der Repository-Seite, in den Header eines Pull Requests und in jede Zeile der Branch-Liste.

Das tut sie auf github.com, gitlab.com, bitbucket.org und Azure DevOps (dev.azure.com und das ältere <org>.visualstudio.com).

Ein eigenes GitLab oder einen eigenen Azure DevOps Server trägst du in den Optionen der Erweiterung ein: die Adresse und welches der beiden es ist. Der Browser fragt dann einmal, ob QuickRun auf dieser Adresse laufen darf, und auf keiner anderen. Deshalb wird nicht schon bei der Installation gefragt: eine Erweiterung, die auf jeder Seite laufen darf, zeigt allen "kann alle Daten auf allen Websites lesen und ändern", nur wegen der wenigen mit eigenem Server. Bitbucket Server geht noch nicht. Dort fügst du die Clone-URL direkt in QuickRun ein.

Ein Pull Request auf Bitbucket Cloud hat keinen Ref, den git holen kann. Sein Button startet deshalb den Quell-Branch des Pull Requests, bei einem Fork aus diesem Fork. Auf den anderen Hosts startet der Button den Pull Request selbst.

Ohne Erweiterung bleibt das README-Badge: ein Link, den ein Repository-Besitzer in sein eigenes README setzt und der über eine https-Seite dasselbe lokale Fenster erreicht.

Warum sie den lokalen Listener braucht ​

Ein Browser lässt sich nicht fragen, ob für ein URL-Schema ein Handler registriert ist. Das ist Absicht — es wäre ein Fingerprinting-Vektor — und es gibt keine API dafür. Eine Erweiterung, deren einziger Kanal quickrun:// ist, kann also nie wissen, ob QuickRun installiert ist, und nie Fortschritt aus einem Lauf zurückbekommen, den sie ausgelöst hat.

Deshalb ist der localhost-Listener der Hauptkanal, und quickrun:// hat genau eine Aufgabe übrig: einen installierten, aber nicht laufenden Daemon zu starten.

Ping-ErgebnisDer Button zeigt
QuickRun antwortetRun this — Klick bereitet einen Lauf vor
keine Antwort, dann greift quickrun://Starting QuickRun…, danach Run this
überhaupt keine AntwortInstall QuickRun — verlinkt die Download-Seite

Button-Zustände ​

  • ready — QuickRun hat geantwortet. Klick bereitet einen Lauf vor.
  • running — aktuelle Phase und, wo es eine echte Zahl gibt, ein Prozentwert.
  • done — der Lauf ist fertig.
  • error — der Tooltip zeigt, warum.

Das Bestätigungsfenster ​

Ein Klick auf Run startet nichts. QuickRun checkt das Repository aus, baut den Plan, und die Erweiterung öffnet ein Fenster mit Repository, Ref, aufgelöstem Commit und den exakten Befehlen, die laufen werden. Erst der Button in diesem Fenster startet sie.

Ein Fenster pro Plan. Wird ein zweites Repository übergeben, während das erste noch auf dem Schirm ist, bekommt es sein eigenes Fenster statt das vorhandene zu überschreiben — ein Lauf, den du gestartet hast, mit Log und Stop, soll nicht durch einen späteren Klick verschwinden. Dieselbe Übergabe noch einmal holt das Fenster nach vorn, das sie schon hat, und bei fünf offenen Fenstern ist Schluss: darüber nimmt das älteste den neuen Plan, weil es am wenigsten wahrscheinlich noch gelesen wird.

Während des Auscheckens gibt es noch nichts zu zeigen, also zeigt das Fenster die Marke und was es gerade tut. Endet das in einem Grund statt in einem Plan — keine Config, ein Branch, den es nicht gibt —, bleibt dieser Grund stehen. Vorher war das ein leeres Fenster.

Außerdem kommt es beim Öffnen nach vorn. Windows lässt ein Hintergrundprogramm nicht in den Vordergrund — es blinkt stattdessen der Taskleisten-Eintrag, den man leicht übersieht —, also legt sich das Fenster einen Moment lang über die anderen und verhält sich danach wie jedes andere. Der Schalter Keep a plan's window above other windows in den Einstellungen macht daraus einen Dauerzustand, für alle, die gar nicht erst suchen wollen.

Und es schließt sich, wenn nichts mehr darin ist: Cancel beantwortet den Plan mit nein, Remove nimmt den fertigen Lauf weg — und für genau diesen einen Lauf war dieses Fenster da. Leer stehenzubleiben ist schlechter als beides.

Das Fenster zeigt außerdem die description aus der Config, wenn es eine gibt, das Verzeichnis, in das ausgecheckt wurde, und — sobald ein Task eine meldet — die Adresse, auf der es läuft, als Link.

Deklariert die Config Inputs, fragt das Fenster sie ab, bevor es überhaupt etwas zu bestätigen gibt: Label, Beschreibung, Default, ein Dropdown für select, eine Checkbox für bool, ein maskiertes Feld für password. Ein geänderter Wert wird einen Moment später von selbst übernommen und die Befehlsliste daraus neu gebaut — auf dem Schirm steht also immer die Liste, die diese Werte ergeben: du bestätigst genau das, was läuft, und es bleibt bei einem Klick auf Run. Continue steht nur auf dem Button, solange nötige Werte fehlen, denn bis dahin gibt es keine Befehlsliste zu bestätigen. Werte mit env gehen als diese Umgebungsvariable in den Lauf, und ein Secret wird nie ans Fenster zurückgeschickt.

Sobald der Lauf geht, tritt das Formular ab — aber die Antworten bleiben stehen: unter Answered zeigt das Fenster, mit welchen Werten dieser Lauf gestartet ist, und die Karte im QuickRun-Fenster tut dasselbe. Eine Auswahl steht dort mit ihrem Options-Text und nicht mit ihrem Wert, weil der Wert oft ein Flag ist und der Text der Satz, den du gelesen hast. Ein password steht als hidden da, niemals als Wert — den schickt der Dienst gar nicht mit.

Solange ein Lauf dieses Branches läuft, ist der Button der Weg dorthin und nicht der Weg in einen zweiten Lauf. Ein Klick öffnet die Aktionen:

  • Show log — holt das Log-Fenster zurück, oder öffnet ein neues, das sich an den Lauf hängt. Das Fenster zu schließen hat nie etwas gestoppt, also muss man auch zurückkommen können
  • Open … — die Adresse, die der Lauf gemeldet hat, sobald er eine gemeldet hat
  • Stop — stoppt den Lauf, und beendet auch das, was er laufen ließ, wenn ein Task sich beendet und einen Server hinterlassen hat

Nach einem Reload findet der Button seinen Lauf wieder: der Tab vergisst ihn, der Daemon nicht. Ein Branch, der schon läuft, lässt sich damit nicht versehentlich zweimal starten.

Nach der Bestätigung bleibt das Fenster offen und wird zum Log des Laufs: der Checkout mit den echten Fortschrittszählern, jeder Setup-Schritt und alles, was die Befehle des Repositories ausgeben. Der Button auf der Seite zeigt nur Prozent und eine grobe Phase — ein Toolbar-Button ist kein Ort für hundert Zeilen Build-Ausgabe.

Die Zeilen finden, auf die es ankommt ​

QuickRun liest jede Zeile beim Eintreffen und stuft sie ein, und das Fenster zeigt das: Fehler rot, Warnungen gelb, alles andere normal. Wenn alles bunt ist, ist nichts bunt — eine Zeile zählt also nur, wenn sie sagt, dass etwas schiefging, und die Zeilen, die das Wort bloß enthalten, zählen nicht: 0 Error(s) am Ende eines Builds, found 0 vulnerabilities, compiling ErrorPage.tsx und das Echo des Befehls selbst, in dem steht, was der Config-Autor geschrieben hat.

Über dem Log:

  • Everything / N error lines / N warnings — filtert das Log auf diese Art. Die Chips erscheinen nur, wenn es davon überhaupt etwas gibt.
  • Show them — alle Fehlerzeilen des Laufs in einem Dialog, zum Kopieren. Auch die, die aus dem Log-Block schon herausgerollt sind: er hält die letzten paar hundert Zeilen, weil ein Restore Tausende ausgibt — und gesucht ist meist der Fehler aus Minute eins.

Im Fenster der Erweiterung macht die Zahl im Banner dasselbe: 3 error lines ist jetzt ein Knopf, und ein Klick beantwortet die Frage, die diese Zahl aufwirft.

Standardfehler gilt dabei nicht als Fehler: docker, git, npm und jeder Fortschrittsbalken schreiben ihre normale Ausgabe dorthin, und alles davon zu zählen ist der Grund, warum über einem perfekt gelaufenen Lauf „17 error lines" stand. Eine unbekannte Zeile von dort ist eine Warnung — genau das, was der Stream ehrlich sagt: nicht die normale Ausgabe, und nicht zwangsläufig ein Problem.

Sobald der Lauf läuft, bekommt jeder Task eine eigene Zeile: was er tut — starting, ready, exited — die Adresse, die er gemeldet hat, als Link, und die Prozess-ID dessen, was er gestartet hat. Bei einer Desktop-Anwendung ist diese PID der einzige Griff, den man an ihr hat — und genau darauf wartet ein Task mit readyWhen: {window: true}. „Running" für den ganzen Lauf sagt nichts darüber, welcher von fünf Diensten oben ist.

Stop stoppt den Lauf und sagt es: der Button wechselt beim Klick sofort auf Stopping… mit Spinner, das Banner auf Stopped, sobald die Prozesse weg sind, und das Fenster schließt sich kurz danach selbst. „Sobald die Prozesse weg sind" ist wörtlich gemeint: hat der Lauf etwas im Hintergrund laufen lassen, sagt das Fenster wie viele und stoppt erneut, statt es stopped zu nennen — du wolltest stoppen, nicht ein Fenster aufräumen. Klickbar ist es nur, solange wirklich etwas läuft: ein Lauf, dessen Prozesse alle beendet sind, hat nichts mehr zu stoppen.

Dieses Fenster ist eine Extension-Seite und nicht Teil der GitHub-Seite — und das mit Absicht: eine Webseite kann über ihren eigenen Inhalt ein überzeugendes gefälschtes Panel zeichnen, und niemand soll je eine Befehlsliste bestätigen, während eine andere ausgeführt wird.

Warum keine Webseite es fernsteuern kann ​

Es gibt nichts zu koppeln. Der Browser hängt an jede seitenübergreifende Anfrage einen Origin-Header, den eine Seite nicht ändern kann — QuickRun lehnt daher alles ab, was nicht von einer Browser-Erweiterung kommt. https://github.com steht nicht auf dieser Liste: ein Skript auf GitHub selbst kann keinen Lauf starten.

Ein Programm auf deinem eigenen Rechner — curl, QuickRuns eigene CLI — sendet keinen Origin und ist erlaubt. Es läuft ohnehin mit deinen Rechten, der Daemon gibt ihm also nichts dazu.

Optionen ​

EinstellungStandard
Port9876
quickrun:// versuchen, wenn QuickRun nicht antwortetan
Wo der Button erscheintbei jedem Repository

Aus QuickRun heraus installieren ​

QuickRuns eigenes Fenster hat einen Reiter Browser-Erweiterung, der die auf diesem Rechner gefundenen Browser auflistet und pro Browser sagt, ob die Erweiterung dort liegt. Ein entpackter Build zählt mit: Er wird an dem Ordner erkannt, aus dem er geladen wurde — und den notiert der Browser selbst.

Der Installieren-Knopf geht so weit, wie ein Browser es zulässt, und keinen Schritt weiter. Wo es eine Store-Listung gibt, öffnet er sie in genau diesem Browser, und der letzte Klick ist der Hinzufügen-Knopf des Browsers. Wo es noch keine gibt, lädt er die gepackte Erweiterung aus dem neuesten Release, entpackt sie, öffnet die Erweiterungsseite des Browsers und daneben den Ordner — übrig bleibt „Entpackte Erweiterung laden, diesen Ordner wählen".

Dass er einen Klick vorher aufhört, ist Absicht. Chrome hat die Installation aus einer Seite heraus 2018 abgeschafft, und der einzige verbliebene Weg, ein Programm eine Erweiterung in Chrome oder Edge setzen zu lassen, ist eine Unternehmensrichtlinie, die sie erzwingt und dir das Entfernen nimmt. Das tut Schadsoftware, und QuickRun tut es nicht. Firefox ist die Ausnahme in die andere Richtung: Über about:debugging lässt sich ein temporäres Add-on laden, das Firefox beim Schließen wieder vergisst.

Wo der Button erscheint ​

QuickRun startet fast jedes Repository: Gibt es keine Konfiguration, liest es die Dateien und baut sich selbst einen Plan. Dieser Plan ist eine begründete Vermutung — eine gute, und die genauen Befehle siehst du vor jedem Lauf — aber vielleicht willst du den Button nur dort, wo das Repository selbst gesagt hat, wie es gestartet werden möchte.

  • Bei jedem Repository. Auch bei denen, die QuickRun sich selbst erschließen müsste.
  • Wo es Anweisungen gibt. Eine quickrun.yml oder Skripte, die für Pinokio geschrieben wurden.
  • Nur mit quickrun.yml. Das Repository hat eine QuickRun-Konfiguration eingecheckt.

Die letzten beiden fragen QuickRun auf deinem Rechner, ob es diese Dateien gibt — einmal pro Repository, eine halbe Stunde lang gemerkt. Die Erweiterung fragt GitHub nicht selbst: dafür bräuchte sie eine Berechtigung für raw.githubusercontent.com, und der Daemon kommt ohne aus. Läuft QuickRun nicht, lässt sich die Prüfung nicht durchführen, und der Button erscheint trotzdem — zu verschwinden, weil eine Prüfung fehlschlug, wäre von einer kaputten Erweiterung nicht zu unterscheiden.

Das Bestätigungsfenster nennt bei jedem Lauf die Quelle, damit klar ist, welcher der vier Fälle vorliegt: die quickrun.yml des Repositories, eine Konfiguration, die du selbst dafür gespeichert hast, die Skripte eines anderen Launchers, oder QuickRuns eigene Lesart der Dateien.

Dazu nennt es die Datei, aus der der Plan kommt, mit vollem Pfad — die quickrun.yml im Checkout, deine eigene Konfiguration für dieses Repository, QuickRuns Kopie aus der Sammlung auf deiner Platte, oder die Adresse, von der eine veröffentlichte gelesen wurde — und daneben einen View-Knopf, der die Datei zeigt. Welche der Quellen es war, sagt nicht, welche Datei es war, und alle vier erzeugen Pläne, die sich gleich lesen: die Befehle beantworten was läuft, die Datei beantwortet wer das geschrieben hat. In QuickRuns eigenem Fenster hat dieser Dialog außerdem Open in the config builder — damit landet die Konfiguration im Builder dieses Fensters, Repository und Ref schon eingetragen, und gespeichert wird nichts, bis du speicherst.

Einen Pull Request zu starten heißt, den Branch zu starten, aus dem er kommt — geholt als refs/pull/<n>/head. Das funktioniert auch bei Pull Requests aus Forks, und genau das macht der Button auf einer PR-Seite.

Eine GitHub-Adresse kann den Plan selbst anfordern:

https://github.com/acme/app?executeQuickRun
https://github.com/acme/app/tree/preview?executeQuickRun=true
https://github.com/acme/app?executeQuickRun=ci/demo.yml

Die Erweiterung macht dann genau das, was ein Klick auf den Button macht: Sie bereitet den Lauf vor und öffnet das Bestätigungsfenster mit der Befehlsliste. Gestartet wird nichts. Das ist Absicht — ein Link, der von sich aus Befehle ausführt, wäre ein Weg, mit einem Klick über den Rechner eines anderen zu verfügen. Gelesen und bestätigt wird weiterhin von dem, dem der Rechner gehört.

Mit einem Dateinamen wird diese Config genutzt statt der, die das Repository sonst gewählt hätte — praktisch, wo ein Repository mehrere mitbringt, eine pro Demo oder pro Umgebung. Der Name muss eine .yml- oder .yaml-Datei im Repository sein, relativ zu dessen Wurzel: ci/demo.yml ist eine Config, ../secrets.yml, /etc/irgendwas.yml und eine URL sind keine und werden abgelehnt. Die Erweiterung prüft das, und QuickRun prüft es erneut, bevor es irgendetwas öffnet — ein Wert aus einer Webadresse ist auf beiden Seiten der Zeichenkette eines Fremden.

Es funktioniert überall, wo die Seite einen eigenen Button hat, und bereitet genau das vor, was dieser Button vorbereiten würde:

AdresseWas vorbereitet wird
/acme/app?executeQuickRunder Standard-Branch des Repositories
/acme/app/tree/preview?executeQuickRunder Branch preview
/acme/app/tree/feature/ein/name?executeQuickRunder Branch feature/ein/name
/acme/app/pull/42?executeQuickRunPull Request 42, geholt als refs/pull/42/head
/acme/app/branches?executeQuickRunnichts — siehe unten

Die Branch-Liste ist die eine Ausnahme. Dort bekommt jede Zeile ihren eigenen Button, es gibt also keinen einzelnen Lauf, den die Adresse meinen könnte — und „der Branch, den die Liste zufällig oben zeigt" ist keine Antwort, auf die man handeln sollte. Wer auf den Branch selbst verlinkt (/tree/<branch>), bekommt genau ihn.

?executeQuickRun=false ist dasselbe wie gar keine Angabe, ein Link kann den Parameter also abgeschaltet enthalten. Es greift einmal pro Adresse: GitHub rendert über pushState, und wer sich danach durch das Repository klickt, bereitet keinen zweiten Lauf vor.

Selbst bauen ​

bash
cd extension
sh build.sh

Ein Quellbaum, drei Builds: dist/chromium für Chrome, Edge und Opera, dist/firefox und dist/safari. Sie unterscheiden sich nur in ihren Manifesten.

Safari ​

Safari kennt kein „entpackt laden", das einen Neustart übersteht. Eine Erweiterung wird dort in einer nativen macOS-App ausgeliefert, und diese App zu bauen braucht einen Mac mit Xcode — der Safari-Build ist also der eine, den man unter Windows oder Linux nur bis zum Ordner selbst bringen kann.

Ausprobieren, auf einem Mac, ohne Xcode und ohne Apple-Konto. Safari lädt den gebauten Ordner direkt:

  1. cd extension && sh build.sh
  2. Safari → Einstellungen → Erweitert → Funktionen für Webentwickler anzeigen
  3. Einstellungen → Entwickler → Unsignierte Erweiterungen erlauben ankreuzen
  4. Einstellungen → Entwickler → Temporäre Erweiterung hinzufügen… → extension/dist/safari wählen

Schritt 4 nimmt auch ein Zip statt eines Ordners — quickrun-extension-safari.zip aus jedem Release funktioniert also, ohne etwas zu bauen.

Safari wirft eine temporäre Erweiterung beim Beenden weg, und Unsignierte Erweiterungen erlauben setzt sich mit zurück — das ist also für einen Blick gedacht, nicht für den Alltag.

Die App bauen. Auf einem Mac mit Xcode:

bash
sh scripts/build-safari.sh

Das führt den Extension-Build aus, übergibt extension/dist/safari an Apples xcrun safari-web-extension-packager und baut das erzeugte Xcode-Projekt zu safari/build/Release/QuickRun.app. Diese App einmal starten, dann steht die Erweiterung in Safaris Erweiterungsliste. Unsigniert braucht sie weiterhin Unsignierte Erweiterungen erlauben; mit einer Developer-ID signiert und notarisiert nicht mehr, ab Safari 18.4.

Das Skript verweigert den Dienst überall außer auf macOS, weil der Packager nur in Xcode existiert.

Was Safari anders macht ​

  • Du musst der Erweiterung Zugriff auf github.com geben. Safari führt ein Content-Script auf einer Seite erst aus, wenn du es erlaubst — über den Button der Erweiterung in der Symbolleiste. Vorher gibt es keinen Run-Button auf der Seite. 127.0.0.1 braucht dieselbe Freigabe, unter Einstellungen → Erweiterungen → QuickRun.
  • Safari 16.4 oder neuer. Das Manifest sagt es. Ältere Versionen haben kein storage.session, und dort wartet ein vorbereiteter Lauf, während das Bestätigungsfenster aufgeht.
  • Nur macOS. Das Bestätigungsfenster ist der ganze Sinn von QuickRun, und Safari auf iOS kann überhaupt kein Erweiterungsfenster öffnen — ein iOS-Build hätte also einen Run-Button, der nichts bestätigen kann.
  • Ein schon offenes Fenster kommt nach vorn, hüpft aber nicht im Dock. Safari ignoriert drawAttention.
  • Die Background-Seite ist eine Event-Page und kein Service Worker, weil Safari einem Service Worker den Loopback-Zugriff nicht gibt, den das Manifest verlangt. Dasselbe background.js, genau wie bei Firefox.

Veröffentlichen ​

Dieser Teil braucht eine Mitgliedschaft im Apple Developer Program (99 USD pro Jahr); einen Mac braucht er nicht zwingend.

  • Ohne Mac: In App Store Connect gibt es unter dem Tab Xcode Cloud einen Safari Web Extension Packager. App-Datensatz anlegen, die Extension-Dateien hochladen — quickrun-safari-<version>.zip aus extension/dist ist genau das — und er baut, signiert und reicht die App ein, auf Kosten der Xcode-Cloud-Minuten der Mitgliedschaft.
  • Mit Mac: das Projekt aus scripts/build-safari.sh in Xcode archivieren und hochladen, oder es mit einer Developer-ID signieren und notarisieren, um die App außerhalb des App Store zu verteilen.

In beiden Fällen prüft ein Mensch die App-Store-Einreichung, und es gibt keine Upload-API, mit der sich das automatisieren ließe, wie Chrome, Edge und Firefox sie haben. Der Release-Workflow baut und signiert die Safari-App deshalb als CI-Artefakt und hört dort auf; das Einreichen ist ein bewusst manueller Schritt.

Wenn GitHub sein DOM ändert ​

Der Button hängt an data-testid-Attributen und ARIA-Labels, wo GitHub sie anbietet, und jede Suche scheitert still: ein fehlender Button ist akzeptabel, eine kaputte GitHub-Seite nicht. GitHub baut diese Seiten um — ein fehlender Button bedeutet also meist, dass die Erweiterung ein Update braucht, und nicht, dass an deinem Setup etwas falsch ist.