Author SHA1 Message Date
Thore Cimbal 1d5a136dff Titelbild ausgetauscht: Alpengluehen statt Elements See
alpenglow.jpg (John Towner, Unsplash) ersetzt lake.jpg als Hintergrund der Login-Seite. Die Unsplash License erlaubt kommerzielle Nutzung und Aenderung ohne Genehmigung und verlangt keine Namensnennung - wir nennen ihn trotzdem, das ist die faire Form.

Die Danksagung ist dabei bewusst nicht uebersetzt: der Schluessel credits|default_cover_photo liegt in 32 Sprachdateien, 31 nennen Elements Fotografen namentlich. Nur en/de anzupassen haette in 29 Sprachen eine falsche Attribution stehen lassen, und diese 29 pflegen wir nicht - sie kommen aus Elements Uebersetzungsdienst.

lake.jpg ist entfernt, weil es durch diese Aenderung unerreichbar wird - 610 KB, die sonst in jedem Image mitfahren.
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 580827f7ca desktop: branding-Block, damit der Client nicht Elements Logo zeigt
Die heute im Web gesetzten branding-Werte fehlten der Desktop-Config. Ohne sie
faellt AuthHeaderLogo.tsx auf themes/element/img/logos/element-logo.svg zurueck -
die Anmeldemaske des Desktop-Clients haette also weiter Elements gruenes Logo
gezeigt, waehrend der Web-Client bereits die ThreadNet-Marke traegt.

vector-icons/512.png liegt im webapp-Bundle des Desktops (geprueft), der Pfad
loest also auch dort auf.

Chirurgisch eingefuegt, nicht neu serialisiert - die 17 Themes und der
Gedankenstrich in der Beschreibung bleiben unangetastet.

Danach gilt: Web-Konfiguration ist eine echte Teilmenge der Desktop-Konfiguration,
ohne einen einzigen Wertkonflikt (verglichen ueber alle verschachtelten
Schluessel).

Refs ThreadNet-Web#1

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 6f33c7277a desktop: macOS baut ohne volles Xcode (BUILD-01, management#22)
Upstream setzt mac.icon auf build/icon.icon - das Icon-Composer-Bundle von
macOS 26. Dessen Verarbeitung ruft actool auf, das es nur mit dem vollen Xcode
gibt (~10 GB, App-Store-Login). Mit blossen CommandLineTools scheitert damit
JEDER macOS-Build:

  ⨯ Failed to check actool version. Is Xcode 26 or higher installed?

Die bisherige Diagnose in management#22 war falsch: Dort stand, nur das
DMG-Target brauche actool und das ZIP baue problemlos. Tatsaechlich trifft es
alle macOS-Targets, weil mac.icon fuer alle gilt - das ZIP scheitert genauso.
Der dort empfohlene Ausweg (DMG per hdiutil bauen) haette ein Problem geloest,
das gar nicht am DMG lag.

Loesung: mac.icon und dmg.badgeIcon auf das klassische build/icon.icns. Damit
baut electron-builder ZIP UND DMG selbst, inklusive Blockmap - verifiziert am
2026-08-06, beide Artefakte in Release v0.4.1.

Umgesetzt als Variantenoption statt als Aenderung an Upstreams Defaults: Die
Werte stehen in unserem axion1337/build.json, electron-builder.ts wird nur
additiv um zwei optionale Schluessel erweitert - dieselbe Machart wie das
bereits vorhandene linux.deb.name. Ein Upstream-Merge erzeugt an der
mac.icon-Zeile damit keinen Konflikt.

Preis: kein macOS-26-Icon-Rendering. Ohne Xcode gaebe es ohnehin keinen Build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 6f1dfedde2 web: Tab-Titel, Favicon, Fehlerseite und Desktop-Hinweis auf ThreadNet
Nachtrag zu v0.4.0. Meine Icon-Arbeit deckte nur vector-icons/ ab; an vier
weiteren Stellen stand weiterhin Element - sichtbar, nicht im Verborgenen.

<title>Element</title> stand statisch in der index.html. Im Browser-Tab stand
also 'Element', unabhaengig vom Icon daneben. Ebenso application-name und
apple-mobile-web-app-title, die beim Ablegen als PWA greifen.

favicon.ico neu: Browser fragen diesen Pfad automatisch ab, unabhaengig von den
<link rel=icon>-Tags. Bisher kam dort ein 404 mit text/html zurueck - derselbe
Fehler, den wir am 2026-08-02 im Docusaurus-Wiki hatten. Sieben Groessen von 16
bis 256, aus derselben zentrierten Quelle wie alle anderen Icons.

Fehlerseite (ErrorView): zeigte Elements element-app-logo.png mit alt='Element'.
Ausgerechnet die Seite, die man sieht, wenn sonst nichts funktioniert.

Desktop-Hinweis (SdkConfig defaults): trug Elements Logo und verlinkte auf
element.io/get-started - also auf fremde Downloads, obwohl wir eigene Builds
ausliefern. Zeigt jetzt auf unsere Releases.

Die drei Bildverweise gehen alle auf vector-icons/512.png statt auf neue Dateien:
webpack kopiert aus res/ nur themes/** und vector-icons/**, und eine zweite
Logo-Datei koennte von den uebrigen abdriften.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 c0774b5f58 desktop: Icons zentriert + About-Attribution im Web-Client (ThreadNet-Web#10)
Zwei Haelften desselben Issues, bewusst in einem Commit, weil sie zusammen
gebaut und ausgeliefert werden.

Desktop-Icons: build/icon.png war byte-identisch mit dem alten, unzentrierten
Web-Icon - das Motiv klebte an der Oberkante (Rand oben 3 %, unten 40 %). macOS
und Windows zeigten also dasselbe schiefe Bild wie der Browser-Tab. Alle vier
Artefakte aus der jetzt zentrierten Quelle neu erzeugt: icon.png, icon.ico
(sieben Groessen von 16 bis 256), das Layer-Asset des macOS-Icon-Composers und
icon.icns ueber iconutil (10 Einzelbilder, 16-512 plus @2x). Alle vier gehen auf
dieselbe Datei zurueck, sie koennen also nicht mehr auseinanderlaufen.

About-Attribution: 'ThreadNet - powered by Element' steht jetzt in Einstellungen
-> Hilfe & Info direkt unter der Client-Version, mit Link auf element.io. Das
war der eigentliche Zweck des Issues - bisher stand die Zeile nur in der
Build-Beschreibung des Desktop-Pakets und war im Client nirgends sichtbar.

Zwei bewusste Entscheidungen dabei, beide im Code kommentiert:

- NICHT in getVersionTextToCopy aufgenommen. Der Text dort landet in
  Fehlerberichten; die Herkunft des Forks ist da nur Rauschen.
- Ohne _t(). Ein Markenhinweis wird nicht uebersetzt, und jeder zusaetzliche
  i18n-Schluessel ist Reibung beim naechsten Upstream-Merge - genau das, was das
  Issue mit 'chirurgisch halten' meint.

Das Stylesheet nutzt nur Variablen, die im Projekt bereits verbreitet sind
(--cpd-space-2x in 43, --cpd-color-text-secondary in 28,
--cpd-font-body-sm-regular in 22 Dateien) - kein Blindflug mit erfundenen Tokens.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 4fdad89fa6 web: Icons zentriert, theme_color auf die Markenfarbe (ThreadNet-Web#10)
Die Web-Icons trugen bereits die ThreadNet-Marke - anders als im Issue vermerkt.
Sie waren aber falsch zugeschnitten: Rand oben 3 %, unten 40 %, das Motiv klebte
an der Oberkante. In runden und quadratischen Icon-Slots sitzt es dadurch
sichtbar zu hoch.

Alle sieben Groessen aus dem 1024er neu erzeugt: auf die Motiv-Bounding-Box
beschnitten und bei gleicher Groesse (81 % der Kante) mittig gesetzt - jetzt
21 % Rand oben wie unten. resize() statt thumbnail(), sonst waere nur verkleinert
worden.

theme_color stand auf #76CFA6, Elements Mintgruen. Die Marke ist #ed4f4c
(dominante Farbe des Motivs gemessen, nicht geschaetzt) - das Gruen war ein
Upstream-Rest und faerbte die Browser-/PWA-Leiste falsch ein.

config.sample.json: brand von Element auf aXion1337.Chat, damit die Vorlage
zeigt, was Prod tatsaechlich setzt.

Nicht angefasst: der Favicon-Snapshot-Test - er prueft Canvas-Operationen
(clearRect, Dimensionen), nicht den Bildinhalt, und bleibt gruen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-06 12:00:00 +00:00
Thore CimbalandClaude Fable 5 80fcf6c227 desktop: Themes mit dem Web-Stand gleichgezogen
Die zehn theme-factory-Themes sind jetzt in beiden Configs identisch (gitops
c47b0a3 + Nachfolger). Ohne das haetten Desktop-Clients weiter meine alte
Interpretation der Paletten gezeigt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 f4995e5882 rebrand: auch 'name' auf threadnet-desktop - Linux-Pakete hiessen sonst weiter Element
electron-builder leitet unterschiedliche Dinge aus zwei Feldern ab: productName
bestimmt den App-Namen (macOS .app, Windows Setup.exe), 'name' dagegen den
Linux-Paketnamen, das Binary und den /opt-Pfad. Nach dem Rebrand hiess die App
ueberall ThreadNet - ausser bei .deb und .tar.gz.

Nebenwirkung bewusst in Kauf genommen: apt sieht ein neues Paket, ein
Upgrade-Pfad von element-desktop existiert nicht. Bei einem Rebrand ist das
erwartbar; die appId bleibt unveraendert, damit lokale Daten erhalten bleiben.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 e21a895390 ci: VARIANT_PATH in beiden Desktop-Jobs - Pakete hiessen weiter element-desktop
Der Rebrand-Commit legte apps/desktop/axion1337/build.json an, aber electron-builder
nutzt eine Variante nur, wenn VARIANT_PATH gesetzt ist. Lokal hatte ich sie per
Umgebungsvariable gesetzt, in der CI fehlte sie - deshalb trugen die Linux- und
Windows-Pakete aus Pipeline 147 weiter den Namen 'element-desktop' bzw.
'Element Setup', obwohl die Icons bereits neu waren.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 c51b68194a rebrand: alle Icons ersetzt - Windows-.ico und Web-Icons fehlten noch
Gestern wurden nur icon.png und icon.icns getauscht; Windows (.ico) und der
Web-Client (res/vector-icons/, manifest.json) trugen weiter das Element-Icon.

Dazu ein echter Fehler behoben: die Icons wurden mit PIL.thumbnail() erzeugt -
das skaliert NUR nach unten. Das 277px-Motiv wurde in 1024er-Icons daher nie
vergroessert und fuellte ueberall bloss 54% x 38% der Flaeche. Jetzt mit resize()
aus dem 1024er-Original: 81% Breite statt 54%.

Ersetzt: web/res/vector-icons/{24,120,144,152,180,512,1024}.png, manifest.json
(name/short_name -> ThreadNet), desktop/build/icon.{png,ico,icns}.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 6b0261d66c rebrand: Desktop-Client heisst ThreadNet und traegt die eigene Marke
Erster Schritt aus ThreadNet-Web#10 - der Client hiess bisher 'Element' und
zeigte dessen Icon.

- apps/desktop/axion1337/build.json: eigene Build-Variante (VARIANT_PATH), damit
  element.io/release/build.json unangetastet bleibt und Upstream-Merges nicht
  reiben. productName 'ThreadNet', Beschreibung 'ThreadNet - powered by Element'
  als faire Attribution (Element Web ist AGPL, dies ist ein Fork davon).
- appId bleibt bewusst 'im.riot.app': bestehende Installationen behalten ihre
  lokalen Daten. Ein appId-Wechsel waere ein Datenschnitt und gehoert in die
  Rebranding-Runde, nicht in einen Icon-Commit.
- build/icon.png + icon.icns: ThreadNet-Bildmarke, transparenter Rand entfernt
  (das Motiv fuellte im Original nur 54% x 38% - direkt skaliert verschwindet es).

Verifiziert im Build: CFBundleName/CFBundleDisplayName = ThreadNet, eigenes
Icon, alle 17 Themes im webapp.asar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 6e021ee853 desktop: dieselben 17 Themes wie im Web-Client
Die Desktop-Apps laden ihre eigene config.json und haetten die elf neuen Themes
sonst nicht (aXion1337 Light + zehn Paletten). Aus der bereits deployten
Web-Konfiguration uebernommen, damit beide Clients identisch sind.

Chirurgisch eingefuegt (Lehre vom 2026-08-01, als ein Re-Dump dieser Datei 441
Zeilen umformatierte): 0 entfernte / 427 neue Zeilen, JSON validiert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-02 12:00:00 +00:00
Thore CimbalandClaude Fable 5 5a8c42b5e9 desktop: Rageshake-Entfernung auf Minimal-Diff korrigiert (Formatierung des Vorgaengers wiederhergestellt)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-01 12:00:00 +00:00
Thore CimbalandClaude Fable 5 5505b9cfc1 desktop: Rageshake-Endpoint entfernt - keine Bug-Reports+Logs an element.io
Entscheidung sorb 2026-08-01: Bug-Melden-Funktion entfaellt vorerst;
eigener Rageshake oder Alternative (z.B. Zammad) als [LOW]-Issue notiert.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-01 12:00:00 +00:00
Thore CimbalandClaude Fable 5 a2bfaaec8f ci: woechentlicher Trivy-CVE-Scan des Prod-Web-Images (gitops#31)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-08-01 12:00:00 +00:00
Thore CimbalandClaude Fable 5 b656eefdc7 ci: Cache-Layout fuer app-builder-Artefakte korrigiert (gedoppelter Schluessel)
Aus artifactDownloader.go + binDownload.js verifiziert: Schluessel ist
<release>-<release>, Unterverzeichnis = Namensteil vor dem ersten
Bindestrich (nsis-resources daher unter nsis\). Job 431 traf winCodeSign
im Cache, scheiterte aber am nsis-Miss mit einfachem Namen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-07-31 12:00:00 +00:00
Thore CimbalandClaude Fable 5 a55adfdd65 ci: alle app-builder-GitHub-Downloads resumefaehig vorladen
winCodeSign starb an denselben CDN-Resets wie zuvor die Electron-Binary
(Job 424); Prefetch-Skript konsolidiert Electron-Zip + winCodeSign +
NSIS-Pakete mit curl -C - in die persistenten Caches (ELECTRON_CACHE,
ELECTRON_BUILDER_CACHE).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-07-31 12:00:00 +00:00
Thore CimbalandClaude Fable 5 cf66ff018d ci: Electron-Binary resumefaehig vorladen (ELECTRON_CACHE)
GitHub-CDN brach den ~120-MB-Download zweimal mitten im Transfer ab
(Jobs 415/416, wsarecv: connection forcibly closed); app-builder kann
nicht fortsetzen. curl -C - in Schleife + persistenter Cache im Gast.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-07-31 12:00:00 +00:00
Thore CimbalandClaude Fable 5 97e18bbf2f ci: Windows-Provisionierung um Rust/Perl/Python/NASM-PATH ergaenzt
hak/matrix-seshat/check.ts verlangt auf Windows rustc, perl, patch, nasm,
nmake und python - Job 399 scheiterte an fehlendem rustc. Rust landet
maschinenweit unter C:\Rust (SYSTEM-Dienst sieht kein Benutzerprofil),
Strawberry Perl deckt perl+patch ab, NASM-PATH wie im Upstream-Workflow
explizit nachgezogen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PKhFj1S3UdD6xL2fbWPeYj
2026-07-31 12:00:00 +00:00
Thore Cimbal c401f54e4d ci: windows_provision Dienst-Neustart via Scheduled Task (Race mit Job-Cleanup, siehe #5) 2026-07-31 12:00:00 +00:00
Thore Cimbal 1eb4968968 ci: desktop_linux nutzt das Build-Image aus der Lab-Registry (OVERMIND-01 abgeschlossen) 2026-07-31 12:00:00 +00:00
Thore CimbalandClaude Fable 5 a8767583da ci: verbliebene GitHub-Workflow-Dateien entfernt (CFGMON-11)
Vollstaendig durch .gitlab-ci.yml ersetzt (Linux verifiziert gruen, Windows
in Arbeit); Gitea Actions ist fuer dieses Repo deaktiviert, auf GitHub lag
das Repo nie. Die Dateien bleiben als Uebersetzungsreferenz in der
Git-Historie erhalten.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:00:00 +00:00
Thore Cimbal 0d0c1659c6 ci: desktop_image in die Lab-Registry (OVERMIND-01, CI_JOB_TOKEN statt rohana-Secret) 2026-07-31 12:00:00 +00:00
Thore Cimbal 624274f5f0 ci: windows_provision - core.longpaths + LongPathsEnabled (MAX_PATH-Fix fuer Checkout) 2026-07-31 12:00:00 +00:00
Thore CimbalandClaude Fable 5 9e2f49b3dd ci: schlankes Trigger-Schema - Pushes pruefen, Tags releasen
web laeuft nur noch bei build-relevanten Pfadaenderungen (Doku-Commits
kosten nichts) und immer bei v*-Tags. docker_web und desktop_linux bauen
nur noch bei Release-Tags (Image-Tag = Git-Tag statt sha), desktop_linux
bleibt auf main manuell triggerbar. Windows-/VM-Jobs sind auf main und
Tags manuell verfuegbar. Hintergrund: Deploy ist ohnehin ein bewusster
Tag-Bump im gitops-Repo - Artefakte pro Doku-Commit waren reine
Runner-Verschwendung.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:00:00 +00:00
Thore Cimbal a414bb8585 ci: windows_provision ohne Checkout (GIT_STRATEGY none) - Henne-Ei mit fehlendem git 2026-07-31 12:00:00 +00:00
Thore Cimbal 8465457a48 ci: windows_provision-Job - idempotente Gast-Werkzeuge via Runner (Runbook-Fix: choco einzeln statt Mehrfachpaket mit --version) 2026-07-31 12:00:00 +00:00
Thore Cimbal 252b16ca71 docs: Repo-Topologie - git.lab kanonisch, rohana als Push-Mirror 2026-07-31 12:00:00 +00:00
Thore CimbalandClaude Fable 5 0d0255cb7f ci: Windows-Strecke - VM-Start/Stop-Jobs + desktop_windows (Issue #5)
start/stop_windows_vm steuern die On-Demand-Build-VM vom Linux-Runner aus
(Docker-Socket). desktop_windows (tags: windows, manuell, unsigniert/nsis)
uebersetzt den x64-Pfad des Upstream-Workflows: frozen-lockfile, webapp +
axion-config vor dem asar, VS-DevShell + build:native mit MSVC-Target.
Wartet in der Queue bis der Windows-Runner (lab-windows-1) online ist.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:00:00 +00:00
Thore Cimbal bc0769809d ci: desktop_linux automatisch auf main nach erstem gruenen Lauf 2026-07-31 12:00:00 +00:00
Thore CimbalandClaude Fable 5 e21e101004 ci: GitLab pipeline (web build, rohana image push, desktop linux)
First CI for the lab GitLab (git.lab): web job builds the webapp with the
lessons from the Gitea attempts baked in (frozen-lockfile instead of
layered.sh, 6GB node heap), docker_web pushes the canonical
apps/web/Dockerfile image to the rohana registry that Flux pulls from,
and two manual jobs cover the desktop path - a dockerbuild build-image
and the Electron Linux build replicating the manually verified flow.
Also tracks the production client config (apps/desktop/axion1337/) that
previously only existed inside the one-off manual desktop build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-31 12:00:00 +00:00
Thore CimbalandClaude Sonnet 5 8d46fb33d2 ci: use lockfile install instead of layered.sh, fix always-green gate
layered.sh clones a branch-matching upstream matrix-js-sdk - on this fork
that would override the deliberately pinned js-sdk from pnpm-lock.yaml
(issue #12) with upstream develop, and it also needs jq which the node:20
job container lacks (the actual CI failure). Plain frozen-lockfile install
matches the production docker build.

Also replace the contains(needs.*.result, ...) gate check with explicit
per-job checks: the object-filter syntax evaluated wrongly on Gitea/act,
letting build-complete report success while build_ew had failed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 12:00:00 +00:00
Thore CimbalandClaude Sonnet 5 71ac7c4346 ci: map runs-on to actual runner labels, trim jobs the runner can't serve
Root cause found for jobs never starting: runs were created all along
(visible in the Actions UI), but every job requested ubuntu-24.04 while
builder-1 only offers ubuntu-latest, linux-build and win-wine - a label
mismatch means jobs queue forever, invisible via the /actions/tasks API
which only lists picked-up jobs.

Mapped build/prepare/gate jobs to ubuntu-latest and the Linux desktop
build to linux-build (electronuserland/builder image, amd64/static only -
the only variant buildable on the single x86 runner). Removed jobs the
runner cannot serve instead of letting them starve the queue: web+desktop
playwright tests (need Docker-in-job and display setup, tracked as
follow-up in issue #2) and the native-Windows desktop build (workflow
assumes signtool/MSVC/PowerShell; the win-wine label awaits a dedicated
wine-based workflow).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 12:00:00 +00:00
Thore Cimbal 8faade6c7b ci: test push after git-hook resync 2026-07-30 12:00:00 +00:00
Thore CimbalandClaude Sonnet 5 f2abae85dd ci: remove unused workflow_call trigger (Gitea Actions bug go-gitea/gitea#33238)
push + workflow_call in the same on: block doesn't reliably fire push
events on Gitea Actions 1.27 (reproducible upstream, closed as "not
planned" - https://github.com/go-gitea/gitea/issues/33238). Nothing in
this repo invokes build-and-test.yaml via workflow_call anymore after the
workflow cleanup, so removing the trigger is a safe, targeted fix rather
than a workaround.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 12:00:00 +00:00
Thore Cimbal 0f1d31021c ci: add trigger-test note to force a real push event 2026-07-30 12:00:00 +00:00
Thore Cimbal bd8298db39 ci: trigger first Actions run now that Actions is enabled for this repo 2026-07-30 12:00:00 +00:00
Thore CimbalandClaude Sonnet 5 77ffeb4514 ci: reduce workflows to fork-relevant CI, fix upstream checkouts (Issue #2)
Removed ~37 workflow files that are unmodified upstream tooling this fork
doesn't use (Netlify, SonarCloud, Localazy, release-drafter/backport,
GitHub issue-triage bots, npm-publish, Cloudflare Pages deploy, Docker
push to ghcr.io/element-hq). Enabling Actions for this repo would have
activated all of them simultaneously against a single-capacity shared
runner - most would just fail loudly and starve the one job slot shared
with other repos.

Kept build-and-test.yaml (the actual comprehensive build+test+desktop
workflow) and its build_desktop_*.yaml sub-workflows. Fixed: push trigger
now targets `main` (was `staging`/`master`, neither of which exist here -
push events never fired at all before), 4 checkout steps now check out
this fork instead of element-hq/element-web, and removed the macOS
desktop build job since the registered runner (builder-1) has no macOS
label and would queue forever instead of failing cleanly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 12:00:00 +00:00
Thore Cimbal 45fb329577 docs: add axion1337-fork.md documenting fork-specific changes vs upstream 2026-07-29 12:00:00 +00:00
73 changed files with 1156 additions and 4278 deletions
-34
View File
@@ -1,34 +0,0 @@
name: Backport
on:
# Privilege escalation necessary to enable backporting PRs from forks
# 🚨 We must not execute any checked out code here.
pull_request_target: # zizmor: ignore[dangerous-triggers]
types:
- closed
- labeled
branches:
- develop
permissions: {} # We use ELEMENT_BOT_TOKEN instead
jobs:
backport:
name: Backport
runs-on: ubuntu-24.04
# Only react to merged PRs for security reasons.
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target.
if: >
github.event.pull_request.merged
&& (
github.event.action == 'closed'
|| (
github.event.action == 'labeled'
&& contains(github.event.label.name, 'backport')
)
)
steps:
- uses: tibdex/backport@9565281eda0731b1d20c4025c43339fb0a23812e # v2
with:
labels_template: "<%= JSON.stringify([...labels, 'X-Release-Blocker']) %>"
# We can't use GITHUB_TOKEN here or CI won't run on the new PR
github_token: ${{ secrets.ELEMENT_BOT_TOKEN }}
@@ -1,48 +0,0 @@
# Triggers after the playwright tests have finished,
# taking the artifact and uploading it to Netlify for easier viewing
name: Upload End to End Test report to Netlify
on:
# Privilege escalation necessary to publish to Netlify
# 🚨 We must not execute any checked out code here.
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows: ["Build & Test"]
types:
- completed
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.run_id }}
cancel-in-progress: ${{ github.event.workflow_run.event == 'pull_request' }}
permissions: {}
jobs:
report:
if: github.event.workflow_run.conclusion != 'cancelled'
name: Report results
runs-on: ubuntu-24.04
environment: Netlify
permissions:
statuses: write
deployments: write
actions: read
steps:
- name: Download HTML report
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
name: html-report
path: playwright-report
- name: 📤 Deploy to Netlify
uses: matrix-org/netlify-pr-preview@9805cd123fc9a7e421e35340a05e1ebc5dee46b5 # v3
with:
path: playwright-report
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
branch: ${{ github.event.workflow_run.head_branch }}
revision: ${{ github.event.workflow_run.head_sha }}
token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
site_id: ${{ vars.NETLIFY_SITE_ID }}
desc: Playwright Report
deployment_env: EndToEndTests
prefix: "e2e-"
-311
View File
@@ -1,311 +0,0 @@
# builds Element Web
# runs Playwright tests against the built Element Web
# builds Element Desktop using the built Element Web
#
# Tries to use a matching js-sdk branch for the build.
#
# Produces a `webapp` artifact
# Produces multiple Desktop artifacts
# Produces multiple Playwright report artifacts
name: Build & Test
on:
# CRON to run all Projects at 6am UTC
schedule:
- cron: "0 6 * * *"
pull_request: {}
merge_group:
types: [checks_requested]
push:
# We do not build on push to develop as the merge_group check handles that
branches: [staging, master]
# support triggering from other workflows
workflow_call:
inputs:
skip:
type: boolean
required: false
default: false
description: "A boolean to skip the playwright check itself while still creating the passing check. Useful when only running in Merge Queues."
matrix-js-sdk-sha:
type: string
required: false
description: "The Git SHA of matrix-js-sdk to build against. By default, will use a matching branch name if it exists, or develop."
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
env:
# fetchdep.sh needs to know our PR number
PR_NUMBER: ${{ github.event.pull_request.number }}
# Use 4 runners in the default case, but only 1 when running on a schedule where we run all 5 projects
NUM_RUNNERS: ${{ github.event_name == 'schedule' && 1 || 4 }}
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
permissions: {} # No permissions required
jobs:
build_ew:
name: "Build Element Web"
runs-on: ubuntu-24.04
if: inputs.skip != true
outputs:
num-runners: ${{ env.NUM_RUNNERS }}
runners-matrix: ${{ steps.runner-vars.outputs.matrix }}
# Skip pull_request runs on renovate PRs to speed up CI time, delegating to the full run in merge queue
skip: ${{ inputs.skip || (github.event_name == 'pull_request' && startsWith(github.head_ref, 'renovate/')) }}
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
repository: element-hq/element-web
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
cache: "pnpm"
node-version: "lts/*"
- name: Fetch layered build
env:
# tell layered.sh to check out the right sha of the JS-SDK & EW, if they were given one
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
run: scripts/layered.sh
- name: Copy config
working-directory: apps/web
run: cp element.io/develop/config.json config.json
- name: Build
env:
CI_PACKAGE: true
working-directory: apps/web
run: VERSION=$(scripts/get-version-from-git.sh) pnpm run build
- name: Upload Artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: webapp
path: apps/web/webapp
retention-days: 1
- name: Calculate runner variables
id: runner-vars
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const numRunners = parseInt(process.env.NUM_RUNNERS, 10);
const matrix = Array.from({ length: numRunners }, (_, i) => i + 1);
core.setOutput("matrix", JSON.stringify(matrix));
playwright_ew:
name: "Run Tests [${{ matrix.project }}] ${{ matrix.runner }}/${{ needs.build_ew.outputs.num-runners }}"
needs: build_ew
if: needs.build_ew.outputs.skip == 'false'
runs-on: ubuntu-24.04
permissions:
actions: read
issues: read
pull-requests: read
strategy:
fail-fast: false
matrix:
# Run multiple instances in parallel to speed up the tests
runner: ${{ fromJSON(needs.build_ew.outputs.runners-matrix) }}
project:
- Chrome
- Firefox
- WebKit
- Dendrite
- Pinecone
runAllTests:
- ${{ github.event_name == 'schedule' || contains(github.event.pull_request.labels.*.name, 'X-Run-All-Tests') }}
# Skip the Firefox & Safari runs unless this was a cron trigger or PR has X-Run-All-Tests label
exclude:
- runAllTests: false
project: Firefox
- runAllTests: false
project: WebKit
- runAllTests: false
project: Dendrite
- runAllTests: false
project: Pinecone
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
repository: element-hq/element-web
- name: 📥 Download artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: webapp
path: apps/web/webapp
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
cache: "pnpm"
cache-dependency-path: pnpm-lock.yaml
node-version: "lts/*"
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Setup playwright
uses: ./.github/actions/setup-playwright
with:
needs-webkit: ${{ matrix.project == 'WebKit' }}
write-cache: ${{ github.event_name != 'merge_group' }}
# We skip tests tagged with @mergequeue when running on PRs, but run them in MQ and everywhere else
- name: Run Playwright tests
working-directory: apps/web
run: |
pnpm test:playwright \
--shard "$SHARD" \
--project="${{ matrix.project }}" \
${{ (github.event_name == 'pull_request' && matrix.runAllTests == false ) && '--grep-invert @mergequeue' || '' }}
env:
SHARD: ${{ format('{0}/{1}', matrix.runner, needs.build_ew.outputs.num-runners) }}
- name: Upload blob report to GitHub Actions Artifacts
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: blob-report-${{ matrix.project }}-${{ matrix.runner }}
path: apps/web/blob-report
retention-days: 1
if-no-files-found: error
downstream-modules:
name: Downstream Playwright tests [element-modules]
needs: build_ew
if: needs.build_ew.outputs.skip == 'false' && github.event_name == 'merge_group'
uses: element-hq/element-modules/.github/workflows/reusable-playwright-tests.yml@main # zizmor: ignore[unpinned-uses]
with:
webapp-artifact: webapp
reporter: blob
prepare_ed:
name: "Prepare Element Desktop"
uses: ./.github/workflows/build_desktop_prepare.yaml
needs: build_ew
if: needs.build_ew.outputs.skip == 'false'
permissions:
contents: read
with:
config: ${{ (github.event.pull_request.base.ref || github.ref_name) == 'develop' && 'element.io/nightly' || 'element.io/release' }}
version: ${{ case((github.event.pull_request.base.ref || github.ref_name) == 'develop' || github.event_name == 'merge_group', 'develop', '') }}
webapp-artifact: webapp
build_ed_windows:
needs: prepare_ed
name: "Desktop Windows"
uses: ./.github/workflows/build_desktop_windows.yaml
# Skip Windows builds on PRs, as the Linux amd64 build is enough of a smoke test and includes the screenshot tests
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'X-Run-All-Tests')
strategy:
matrix:
arch: [x64, ia32, arm64]
with:
arch: ${{ matrix.arch }}
blob_report: true
build_ed_linux:
needs: prepare_ed
name: "Desktop Linux"
uses: ./.github/workflows/build_desktop_linux.yaml
strategy:
matrix:
sqlcipher: [system, static]
arch: [amd64, arm64]
runAllTests:
- ${{ github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'X-Run-All-Tests') }}
exclude:
# We ship static sqlcipher builds, so delegate testing the system builds to the merge queue
- runAllTests: false
sqlcipher: system
# Additionally skip arm64 system builds on PRs, as the amd64 test is enough for a smoke test and includes the screenshot tests
- runAllTests: false
arch: arm64
with:
sqlcipher: ${{ matrix.sqlcipher }}
arch: ${{ matrix.arch }}
blob_report: true
build_ed_macos:
needs: prepare_ed
name: "Desktop macOS"
uses: ./.github/workflows/build_desktop_macos.yaml
# Skip macOS builds on PRs, as the Linux amd64 build is enough of a smoke test and includes the screenshot tests
# and we have a very low limit of concurrent macos runners (5) across the Github org.
if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'X-Run-All-Tests')
with:
blob_report: true
complete:
name: end-to-end-tests
needs:
- build_ew
- playwright_ew
- downstream-modules
- prepare_ed
- build_ed_windows
- build_ed_linux
- build_ed_macos
if: always()
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
if: needs.build_ew.outputs.skip == 'false'
with:
persist-credentials: false
repository: element-hq/element-web
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
if: needs.build_ew.outputs.skip == 'false'
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
if: needs.build_ew.outputs.skip == 'false'
with:
cache: "pnpm"
node-version: "lts/*"
- name: Install dependencies
if: needs.build_ew.outputs.skip == 'false'
run: pnpm install --frozen-lockfile
- name: Download blob reports from GitHub Actions Artifacts
if: needs.build_ew.outputs.skip == 'false'
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: blob-report-*
path: all-blob-reports
merge-multiple: true
- name: Merge into HTML Report
if: needs.build_ew.outputs.skip == 'false'
run: |
pnpm playwright merge-reports \
--config=playwright-merge.config.ts \
./all-blob-reports
env:
# Only pass creds to the flaky-reporter on main branch runs
GITHUB_TOKEN: ${{ github.ref_name == 'develop' && secrets.ELEMENT_BOT_TOKEN || '' }}
PLAYWRIGHT_HTML_TITLE: ${{ case(github.event_name == 'pull_request', format('Playwright Report PR-{0}', env.PR_NUMBER), 'Playwright Report') }}
# Upload the HTML report even if one of our reporters fails, this can happen when stale screenshots are detected
- name: Upload HTML report
if: always() && needs.build_ew.outputs.skip == 'false'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: html-report
path: playwright-report
retention-days: 14
if-no-files-found: error
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1
-76
View File
@@ -1,76 +0,0 @@
name: Build
on:
pull_request: {}
push:
branches: [develop, master]
merge_group:
types: [checks_requested]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
# develop pushes and repository_dispatch handled in build_develop.yaml
env:
# This must be set for fetchdep.sh to get the right branch
PR_NUMBER: ${{ github.event.pull_request.number }}
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
permissions: {} # No permissions required
jobs:
build:
name: "Build on ${{ matrix.image }}"
# We build on all 3 platforms to ensure we don't have any OS-specific build incompatibilities
strategy:
fail-fast: false
matrix:
image:
- ubuntu-24.04
- windows-2022
- macos-14
isDevelop:
- ${{ github.event_name == 'push' && github.ref_name == 'develop' }}
isPullRequest:
- ${{ github.event_name == 'pull_request' }}
# Skip the ubuntu-24.04 build for the develop branch as the dedicated CD build_develop workflow handles that
# Skip the non-linux builds for pull requests as Windows is awfully slow, so run in merge queue only
exclude:
- isDevelop: true
image: ubuntu-24.04
- isPullRequest: true
image: windows-2022
- isPullRequest: true
image: macos-14
runs-on: ${{ matrix.image }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
# Disable cache on Windows as it is slower than not caching
# https://github.com/actions/setup-node/issues/975
cache: ${{ runner.os != 'Windows' && 'pnpm' || '' }}
node-version: "lts/*"
- name: Fetch layered build
run: ./scripts/layered.sh
- name: Copy config
working-directory: apps/web
run: cp element.io/develop/config.json config.json
- name: Build
working-directory: apps/web
env:
CI_PACKAGE: true
run: VERSION=$(scripts/get-version-from-git.sh) pnpm run build
- name: Upload Artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: webapp-${{ matrix.image }}
path: apps/web/webapp
retention-days: 1
-86
View File
@@ -1,86 +0,0 @@
name: Build Debian package
on:
release:
types: [published]
concurrency: ${{ github.workflow }}
permissions: {} # We use ELEMENT_BOT_TOKEN instead
jobs:
build:
name: Build package
environment: packages.element.io
runs-on: ubuntu-24.04
env:
R2_INCOMING_BUCKET: ${{ vars.R2_INCOMING_BUCKET }}
R2_URL: ${{ vars.CF_R2_S3_API }}
VERSION: ${{ github.ref_name }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Download package
working-directory: apps/web
run: |
wget "https://github.com/element-hq/element-web/releases/download/$VERSION/element-$VERSION.tar.gz"
wget "https://github.com/element-hq/element-web/releases/download/$VERSION/element-$VERSION.tar.gz.asc"
- name: Check GPG signature
working-directory: apps/web
run: |
wget "https://packages.element.io/element-release-key.gpg"
gpg --import element-release-key.gpg
gpg --fingerprint "$FINGERPRINT"
gpg --verify "element-$VERSION.tar.gz.asc" "element-$VERSION.tar.gz"
env:
FINGERPRINT: ${{ vars.GPG_FINGERPRINT }}
- name: Prepare
working-directory: apps/web
run: |
mkdir -p debian/tmp/DEBIAN
find debian -maxdepth 1 -type f -exec cp "{}" debian/tmp/DEBIAN/ \;
mkdir -p debian/tmp/usr/share/element-web/ debian/tmp/etc/element-web/
tar -xf "element-$VERSION.tar.gz" -C debian/tmp/usr/share/element-web --strip-components=1 --no-same-owner --no-same-permissions
mv debian/tmp/usr/share/element-web/config.sample.json debian/tmp/etc/element-web/config.json
ln -s /etc/element-web/config.json debian/tmp/usr/share/element-web/config.json
- name: Write changelog
working-directory: apps/web
run: |
VERSION=$(cat package.json | jq -r .version)
TIME=$(date -d "$PUBLISHED_AT" -R)
{
echo "element-web ($VERSION) default; urgency=medium"
echo "$BODY" | sed 's/^##/\n */g;s/^\*/ */g' | perl -pe 's/\[.+?]\((.+?)\)/\1/g'
echo ""
echo " -- $ACTOR <support@element.io> $TIME"
} > debian/tmp/DEBIAN/changelog
env:
ACTOR: ${{ github.actor }}
VERSION: ${{ github.event.release.tag_name }}
BODY: ${{ github.event.release.body }}
PUBLISHED_AT: ${{ github.event.release.published_at }}
- name: Build deb package
working-directory: apps/web
run: |
VERSION=$(cat package.json | jq -r .version)
dpkg-gencontrol -v"$VERSION" -ldebian/tmp/DEBIAN/changelog
dpkg-deb -Zxz --root-owner-group --build debian/tmp element-web.deb
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: element-web.deb
path: apps/web/element-web.deb
retention-days: 14
- name: Publish to packages.element.io
if: github.event.release.prerelease == false
uses: element-hq/packages.element.io@master # zizmor: ignore[unpinned-uses]
with:
file: apps/web/element-web.deb
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
bucket-api: ${{ vars.CF_R2_S3_API }}
bucket-key-id: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
bucket-access-key: ${{ secrets.CF_R2_TOKEN }}
@@ -1,313 +0,0 @@
name: Build and Deploy
on:
# Nightly build
schedule:
- cron: "0 9 * * *"
# Release build
release:
types: [published]
# Manual nightly & release
workflow_dispatch:
inputs:
mode:
description: What type of build to trigger. Release builds MUST be ran from the `master` branch.
required: true
default: nightly
type: choice
options:
- nightly
- release
macos:
description: Build macOS
required: true
type: boolean
default: true
windows:
description: Build Windows
required: true
type: boolean
default: true
linux:
description: Build Linux
required: true
type: boolean
default: true
deploy:
description: Deploy artifacts
required: true
type: boolean
default: true
run-name: Element ${{ inputs.mode != 'release' && github.event_name != 'release' && 'Nightly' || 'Desktop' }}
concurrency: ${{ github.workflow }}
env:
R2_BUCKET: ${{ vars.R2_BUCKET }}
permissions: {} # Uses ELEMENT_BOT_TOKEN
jobs:
prepare:
uses: ./.github/workflows/build_desktop_prepare.yaml
permissions:
contents: read
with:
config: element.io/${{ inputs.mode || (github.event_name == 'release' && 'release') || 'nightly' }}
version: ${{ (inputs.mode != 'release' && github.event_name != 'release') && 'develop' || '' }}
nightly: ${{ inputs.mode != 'release' && github.event_name != 'release' }}
deploy: ${{ inputs.deploy || (github.event_name != 'workflow_dispatch' && github.event.release.prerelease != true) }}
secrets:
CF_R2_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
CF_R2_TOKEN: ${{ secrets.CF_R2_TOKEN }}
trigger-pro-pipeline:
name: Trigger Pro pipeline
needs: prepare
runs-on: ubuntu-24.04
steps:
- uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4
with:
repository: element-hq/element-web-pro
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
event-type: trigger-pipeline
client-payload: |-
{
"base-ref": "${{ github.ref_name }}"
}
windows:
if: github.event_name != 'workflow_dispatch' || inputs.windows
needs: prepare
name: Windows ${{ matrix.arch }}
strategy:
matrix:
arch: [x64, arm64]
uses: ./.github/workflows/build_desktop_windows.yaml
secrets: inherit # zizmor: ignore[secrets-inherit]
with:
sign: true
arch: ${{ matrix.arch }}
version: ${{ needs.prepare.outputs.nightly-version }}
macos:
if: github.event_name != 'workflow_dispatch' || inputs.macos
needs: prepare
name: macOS
uses: ./.github/workflows/build_desktop_macos.yaml
secrets: inherit # zizmor: ignore[secrets-inherit]
with:
sign: true
base-url: https://packages.element.io/${{ needs.prepare.outputs.packages-dir }}
version: ${{ needs.prepare.outputs.nightly-version }}
linux:
if: github.event_name != 'workflow_dispatch' || inputs.linux
needs: prepare
name: Linux ${{ matrix.arch }} (sqlcipher ${{ matrix.sqlcipher }})
strategy:
matrix:
arch: [amd64, arm64]
sqlcipher: [static]
uses: ./.github/workflows/build_desktop_linux.yaml
with:
arch: ${{ matrix.arch }}
sqlcipher: ${{ matrix.sqlcipher }}
version: ${{ needs.prepare.outputs.nightly-version }}
deploy:
needs:
- prepare
- macos
- linux
- windows
runs-on: ubuntu-24.04
name: ${{ needs.prepare.outputs.deploy == 'true' && 'Deploy' || 'Deploy (dry-run)' }}
if: always() && !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled')
environment: ${{ needs.prepare.outputs.deploy == 'true' && 'packages.element.io' || '' }}
steps:
- name: Download artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
- name: Prepare artifacts for deployment
run: |
set -ex
# Windows
for arch in x64 arm64
do
if [ -d "win-$arch" ]; then
mkdir -p packages.element.io/{install,update}/win32/$arch
mv win-$arch/squirrel-windows*/*.exe "packages.element.io/install/win32/$arch/"
mv win-$arch/squirrel-windows*/*.nupkg "packages.element.io/update/win32/$arch/"
mv win-$arch/squirrel-windows*/RELEASES "packages.element.io/update/win32/$arch/"
fi
done
# macOS
if [ -d macos ]; then
mkdir -p packages.element.io/{install,update}/macos
mv macos/*.dmg packages.element.io/install/macos/
mv macos/*-mac.zip packages.element.io/update/macos/
mv macos/*.json packages.element.io/update/macos/
fi
# Linux
if [ -d linux-amd64-sqlcipher-static ]; then
mkdir -p packages.element.io/install/linux/glibc-x86-64
mv linux-amd64-sqlcipher-static/*.tar.gz packages.element.io/install/linux/glibc-x86-64
fi
if [ -d linux-arm64-sqlcipher-static ]; then
mkdir -p packages.element.io/install/linux/glibc-aarch64
mv linux-arm64-sqlcipher-static/*.tar.gz packages.element.io/install/linux/glibc-aarch64
fi
# We don't wish to store the installer for every nightly ever, so we only keep the latest
- name: "[Nightly] Strip version from installer file"
if: needs.prepare.outputs.nightly-version != ''
run: |
set -ex
# Windows
for arch in x64 arm64
do
if [ -d "win-$arch" ]; then mv packages.element.io/install/win32/$arch/{*,"Element Nightly Setup"}.exe; fi
done
# macOS
if [ -d macos ]; then mv packages.element.io/install/macos/{*,"Element Nightly"}.dmg; fi
# Linux
if [ -d linux-amd64-sqlcipher-static ]; then mv packages.element.io/install/linux/glibc-x86-64/{*,element-desktop-nightly}.tar.gz; fi
if [ -d linux-arm64-sqlcipher-static ]; then mv packages.element.io/install/linux/glibc-aarch64/{*,element-desktop-nightly}.tar.gz; fi
- name: "[Release] Prepare release latest symlink"
if: needs.prepare.outputs.nightly-version == ''
run: |
set -ex
# Windows
for arch in x64 arm64
do
if [ -d "win-$arch" ]; then
pushd packages.element.io/install/win32/$arch
ln -s "$(find . -type f -iname "*.exe" | xargs -0 -n1 -- basename)" "Element Setup.exe"
popd
fi
done
# macOS
if [ -d macos ]; then
pushd packages.element.io/install/macos
ln -s "$(find . -type f -iname "*.dmg" | xargs -0 -n1 -- basename)" "Element.dmg"
popd
fi
# Linux
if [ -d linux-amd64-sqlcipher-static ]; then
pushd packages.element.io/install/linux/glibc-x86-64
ln -s "$(find . -type f -iname "*.tar.gz" | xargs -0 -n1 -- basename)" "element-desktop.tar.gz"
popd
fi
if [ -d linux-arm64-sqlcipher-static ]; then
pushd packages.element.io/install/linux/glibc-aarch64
ln -s "$(find . -type f -iname "*.tar.gz" | xargs -0 -n1 -- basename)" "element-desktop.tar.gz"
popd
fi
- name: Stash packages.element.io
if: needs.prepare.outputs.deploy == 'false'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: packages.element.io
path: packages.element.io
# Checksum algorithm specified as per https://developers.cloudflare.com/r2/examples/aws/aws-cli/
- name: Deploy artifacts
if: needs.prepare.outputs.deploy == 'true'
run: |
set -x
aws s3 cp --recursive packages.element.io/ s3://$R2_BUCKET/$DEPLOYMENT_DIR --endpoint-url $R2_URL --region auto --checksum-algorithm CRC32
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }}
R2_URL: ${{ vars.CF_R2_S3_API }}
DEPLOYMENT_DIR: ${{ needs.prepare.outputs.packages-dir }}
- name: Notify packages.element.io of new files
if: needs.prepare.outputs.deploy == 'true'
uses: peter-evans/repository-dispatch@28959ce8df70de7be546dd1250a005dd32156697 # v4
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
repository: element-hq/packages.element.io
event-type: packages-index
- name: Find debs
id: deb
if: needs.linux.result == 'success'
run: |
set -x
for arch in amd64 arm64
do
echo "$arch=$(ls linux-$arch-sqlcipher-static/*.deb | tail -n1)" >> $GITHUB_OUTPUT
done
- name: Stash debs
if: needs.prepare.outputs.deploy == 'false' && needs.linux.result == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: debs
path: |
${{ steps.deb.outputs.amd64 }}
${{ steps.deb.outputs.arm64 }}
- name: Publish amd64 deb to packages.element.io
uses: element-hq/packages.element.io@master # zizmor: ignore[unpinned-uses]
if: needs.prepare.outputs.deploy == 'true' && needs.linux.result == 'success'
with:
file: ${{ steps.deb.outputs.amd64 }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
bucket-api: ${{ vars.CF_R2_S3_API }}
bucket-key-id: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
bucket-access-key: ${{ secrets.CF_R2_TOKEN }}
- name: Publish arm64 deb to packages.element.io
uses: element-hq/packages.element.io@master # zizmor: ignore[unpinned-uses]
if: needs.prepare.outputs.deploy == 'true' && needs.linux.result == 'success'
with:
file: ${{ steps.deb.outputs.arm64 }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
bucket-api: ${{ vars.CF_R2_S3_API }}
bucket-key-id: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
bucket-access-key: ${{ secrets.CF_R2_TOKEN }}
deploy-ess:
needs: deploy
runs-on: ubuntu-24.04
name: Deploy builds to ESS
if: needs.prepare.outputs.deploy == 'true' && github.event_name == 'release'
env:
BUCKET_NAME: "element-desktop-msi.onprem.element.io"
AWS_REGION: "eu-central-1"
permissions:
id-token: write # This is required for requesting the JWT
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6
with:
role-to-assume: arn:aws:iam::264135176173:role/Push-ElementDesktop-MSI
role-session-name: githubaction-run-${{ github.run_id }}
aws-region: ${{ env.AWS_REGION }}
- name: Download artifacts
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
pattern: win-*
- name: Copy files to S3
run: |
set -x
PREFIX="${VERSION%.*}"
for file in win-*/*.msi; do
filename=$(basename "$file")
aws s3 cp "$file" "s3://${{ env.BUCKET_NAME }}/$PREFIX/$filename"
done
env:
VERSION: ${{ github.event.release.tag_name }}
-265
View File
@@ -1,265 +0,0 @@
# This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build
# Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure
# the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch.
on:
workflow_call:
inputs:
ref:
type: string
required: false
description: "The git ref to checkout, defaults to the default branch"
arch:
type: string
required: true
description: "The architecture to build for, one of 'amd64' | 'arm64'"
version:
type: string
required: false
description: "Version string to override the one in package.json, used for non-release builds"
sqlcipher:
type: string
required: true
description: "How to link sqlcipher, one of 'system' | 'static'"
blob_report:
type: boolean
required: false
description: "Whether to run the blob report"
prepare-artifact-name:
type: string
required: false
description: |
The name of the prepare artifact to use, defaults to 'desktop-prepare'.
The artifact must contain the following:
+ webapp.asar - the asar archive of the webapp to embed in the desktop app
+ electronVersion - the version of electron to use for cache keying
+ hakHash - the hash of the .hak directory to use for cache keying
+ changelog.Debian - the changelog file to embed in the Debian package
+ variant.json - the variant configuration to use for the build
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
for example icons in the `build/` directory to override the app icons.
default: "desktop-prepare"
test:
type: boolean
required: false
default: true
description: "Whether to run the test stage after building"
test-args:
type: string
required: false
description: "Additional arguments to pass to playwright"
runs-on:
type: string
required: false
description: "The runner image to use, normally set for you, may be needed for running in private repos."
artifact-prefix:
type: string
required: false
description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
default: ""
targets:
type: string
required: false
description: "List of targets to build"
default: "tar.gz deb"
env:
SQLCIPHER_BUNDLED: ${{ inputs.sqlcipher == 'static' && '1' || '' }}
MAX_GLIBC: 2.31 # bullseye-era glibc, used by glibc-check.sh
permissions: {} # No permissions required
jobs:
build:
name: Build Linux ${{ inputs.arch }} SQLCipher ${{ inputs.sqlcipher }}
# We build on native infrastructure as matrix-seshat fails to cross-compile properly
# https://github.com/matrix-org/seshat/issues/135
runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }}
env:
HAK_DOCKER_IMAGE: ghcr.io/element-hq/element-web/desktop-build-env:${{ case(github.event_name == 'push', inputs.ref || github.ref_name, github.event_name == 'release', 'staging', 'develop') }}
steps:
- uses: nbucic/variable-mapper@0673f6891a0619ba7c002ecfed0f9f4f39017b6f
id: config
with:
key: "${{ inputs.arch }}"
export_to: output
map: |
{
"amd64": {
"arch": "x86-64"
},
"arm64": {
"arch": "aarch64",
"build-args": "--arm64"
}
}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
repository: element-hq/element-web
ref: ${{ inputs.ref }}
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: ${{ inputs.prepare-artifact-name }}
path: apps/desktop
- name: Cache .hak
id: cache
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
key: ${{ runner.os }}-${{ github.ref_name }}-${{ inputs.sqlcipher }}-${{ inputs.arch }}-${{ hashFiles('apps/desktop/hakHash', 'apps/desktop/electronVersion', 'apps/desktop/dockerbuild/*') }}
path: |
apps/desktop/.hak
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: apps/desktop/.node-version
cache: "pnpm"
env:
# Workaround for https://github.com/actions/setup-node/issues/317
FORCE_COLOR: 0
- name: Install Deps
working-directory: apps/desktop
run: "pnpm install --frozen-lockfile --filter element-desktop"
- name: "Get modified files"
id: changed_files
if: steps.cache.outputs.cache-hit != 'true' && github.event_name == 'pull_request' && github.repository == 'element-hq/element-web'
uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47
with:
files: |
apps/desktop/dockerbuild/**
# This allows contributors to test changes to the dockerbuild image within a pull request
- name: Build docker image
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
if: steps.changed_files.outputs.any_modified == 'true'
with:
file: apps/desktop/dockerbuild/Dockerfile
context: apps/desktop
load: true
platforms: linux/${{ inputs.arch }}
tags: ${{ env.HAK_DOCKER_IMAGE }}
- name: Build Natives
if: steps.cache.outputs.cache-hit != 'true'
run: |
docker run \
-v ${{ github.workspace }}:/work -w /work \
-e SQLCIPHER_BUNDLED \
-e CI=1 \
$HAK_DOCKER_IMAGE \
pnpm -C apps/desktop run build:native
- name: Fix permissions
run: |
# For .hak
sudo chown -R $USER:$USER apps/desktop/.hak
# For node_modules pnpm strict security
sudo chmod +x node_modules/7zip-bin/linux/*/7za
- name: Check native libraries in hak dependencies
working-directory: apps/desktop
run: |
shopt -s globstar
for filename in ./.hak/hakModules/**/*.node; do
./scripts/glibc-check.sh $filename
done
- name: Generate debian files and arguments
working-directory: apps/desktop
run: |
if [ -f changelog.Debian ]; then
echo "ED_DEBIAN_CHANGELOG=changelog.Debian" >> $GITHUB_ENV
fi
- name: Build App
working-directory: apps/desktop
run: pnpm run build --publish never $BUILD_ARGS -l $TARGETS
env:
VARIANT_PATH: variant.json
# Only set for Nightly builds
VERSION: ${{ inputs.version }}
# Workaround for https://github.com/electron-userland/electron-builder/issues/5721
USE_HARD_LINKS: false
BUILD_ARGS: ${{ steps.config.outputs.build-args }}
TARGETS: ${{ inputs.targets }}
- name: Check native libraries
working-directory: apps/desktop
run: |
set -x
shopt -s globstar
FILES=$(file dist/**/*.node)
echo $FILES
! echo "$FILES" | grep -v "$ARCH"
LIBS=$(readelf -d dist/**/*.node | grep NEEDED)
echo "$LIBS"
set +x
assert_contains_string() { [[ "$1" == *"$2"* ]]; }
! assert_contains_string "$LIBS" "libcrypto.so.1.1"
if [ "$SQLCIPHER_BUNDLED" == "1" ]; then
! assert_contains_string "$LIBS" "libsqlcipher.so.0"
else
assert_contains_string "$LIBS" "libsqlcipher.so.0"
fi
./scripts/glibc-check.sh dist/linux-*unpacked/element-desktop*
env:
ARCH: ${{ steps.config.outputs.arch }}
# We exclude *-unpacked as it loses permissions and the tarball contains it with correct permissions
- name: Upload Artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ${{ inputs.artifact-prefix }}linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
path: |
apps/desktop/dist
!apps/desktop/dist/*-unpacked/**
retention-days: 1
- name: Assert deb is present and valid
if: contains(inputs.targets, 'deb')
working-directory: apps/desktop
run: |
test -f ./dist/element-desktop*$ARCH.deb
DEB_LISTING=$(dpkg-deb --fsys-tarfile ./dist/element-desktop*.deb | tar -tv)
echo "deb listing: "
echo "$DEB_LISTING"
! echo "$DEB_LISTING" | grep '^h'
env:
ARCH: ${{ inputs.arch }}
- name: Assert tar.gz is present
if: contains(inputs.targets, 'tar.gz')
working-directory: apps/desktop
run: |
test -f ./dist/element-desktop*.tar.gz
TAR_GZ_LISTING=$(tar -tvf ./dist/element-desktop*.tar.gz)
echo "tar.gz listing: "
echo "$TAR_GZ_LISTING"
! echo "$TAR_GZ_LISTING" | grep '^h'
test:
name: Test Linux ${{ inputs.arch }} SQLCipher ${{ inputs.sqlcipher }}
needs: build
if: inputs.test && contains(inputs.targets, 'deb')
uses: ./.github/workflows/build_desktop_test.yaml
with:
project: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
artifact: ${{ inputs.artifact-prefix }}linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }}
executable: /opt/Element*/element-desktop*
prepare_cmd: |
sudo apt-get -qq update
sudo apt install ./dist/*.deb
blob_report: ${{ inputs.blob_report }}
args: ${{ inputs.test-args }}
-234
View File
@@ -1,234 +0,0 @@
# This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build
# Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure
# the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch.
on:
workflow_call:
secrets:
APPLE_ID:
required: false
APPLE_ID_PASSWORD:
required: false
APPLE_CSC_KEY_PASSWORD:
required: false
APPLE_CSC_LINK:
required: false
inputs:
ref:
type: string
required: false
description: "The git ref to checkout, defaults to the default branch"
version:
type: string
required: false
description: "Version string to override the one in package.json, used for non-release builds"
sign:
type: string
required: false
description: "Whether to sign & notarise the build, requires 'Desktop Apple' environment"
base-url:
type: string
required: false
description: "The URL to which the output will be deployed."
blob_report:
type: boolean
required: false
description: "Whether to run the blob report"
prepare-artifact-name:
type: string
required: false
description: |
The name of the prepare artifact to use, defaults to 'desktop-prepare'.
The artifact must contain the following:
+ webapp.asar - the asar archive of the webapp to embed in the desktop app
+ electronVersion - the version of electron to use for cache keying
+ hakHash - the hash of the .hak directory to use for cache keying
+ variant.json - the variant configuration to use for the build
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
for example icons in the `build/` directory to override the app icons.
default: "desktop-prepare"
test:
type: boolean
required: false
default: true
description: "Whether to run the test stage after building"
test-args:
type: string
required: false
description: "Additional arguments to pass to playwright"
artifact-prefix:
type: string
required: false
description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
default: ""
targets:
type: string
required: false
description: "List of targets to build"
default: "dmg zip"
permissions: {} # No permissions required
jobs:
build:
name: Build macOS Universal
runs-on: macos-15 # M1
environment: ${{ inputs.sign && 'Desktop Apple' || '' }}
steps:
- uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1
with:
xcode-version: latest-stable
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
repository: element-hq/element-web
ref: ${{ inputs.ref }}
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: ${{ inputs.prepare-artifact-name }}
path: apps/desktop
- name: Cache .hak
id: cache
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
key: ${{ runner.os }}-${{ hashFiles('apps/desktop/hakHash', 'apps/desktop/electronVersion') }}
path: |
apps/desktop/.hak
- name: Install Rust
if: steps.cache.outputs.cache-hit != 'true'
run: |
rustup toolchain install stable --profile minimal --no-self-update
rustup default stable
rustup target add aarch64-apple-darwin
rustup target add x86_64-apple-darwin
# M1 macos-14 comes without Python preinstalled
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with:
python-version: "3.13"
# Install Quartz for DMG badges
# https://github.com/electron-userland/electron-builder/issues/9511#issuecomment-3774092888
- run: sudo pip3 install pyobjc-framework-Quartz
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: apps/desktop/.node-version
cache: "pnpm"
- name: Install Deps
working-directory: apps/desktop
run: "pnpm install --frozen-lockfile --filter element-desktop"
- name: Build Natives
if: steps.cache.outputs.cache-hit != 'true'
working-directory: apps/desktop
run: pnpm run build:native:universal
# We split these because electron-builder gets upset if we set CSC_LINK even to an empty string
- name: "[Signed] Build App"
if: inputs.sign != ''
working-directory: apps/desktop
run: |
pnpm run build:universal --publish never -m ${TARGETS}
env:
APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CSC_KEY_PASSWORD }}
CSC_LINK: ${{ secrets.APPLE_CSC_LINK }}
VARIANT_PATH: variant.json
# Only set for Nightly builds
VERSION: ${{ inputs.version }}
TARGETS: ${{ inputs.targets }}
- name: Check app was signed & notarised successfully
if: inputs.sign != ''
working-directory: apps/desktop
run: |
hdiutil attach dist/*.dmg -mountpoint /Volumes/Element
codesign -dv --verbose=4 /Volumes/Element/*.app
spctl -a -vvv -t install /Volumes/Element/*.app
hdiutil detach /Volumes/Element
- name: "[Unsigned] Build App"
if: inputs.sign == ''
working-directory: apps/desktop
run: |
pnpm run build:universal --publish never -m ${TARGETS}
env:
CSC_IDENTITY_AUTO_DISCOVERY: false
VARIANT_PATH: variant.json
TARGETS: ${{ inputs.targets }}
- name: Generate releases.json
if: inputs.base-url
working-directory: apps/desktop
run: |
PKG_JSON_VERSION=$(cat package.json | jq -r .version)
LATEST=$(find dist -type f -iname "*-mac.zip" | xargs -0 -n1 -- basename)
# Encode spaces in the URL as Squirrel.Mac complains about bad JSON otherwise
URL="${BASE_URL}/update/macos/${LATEST// /%20}"
jq -n --arg version "${VERSION:-$PKG_JSON_VERSION}" --arg url "$URL" '
{
currentRelease: $version,
releases: [{
version: $version,
updateTo: {
version: $version,
url: $url,
},
}],
}
' > dist/releases.json
jq -n --arg url "$URL" '
{ url: $url }
' > dist/releases-legacy.json
env:
VERSION: ${{ inputs.version }}
BASE_URL: ${{ inputs.base-url }}
# We exclude mac-universal as the unpacked app takes forever to upload and zip and dmg already contains it
- name: Upload Artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ${{ inputs.artifact-prefix }}macos
path: |
apps/desktop/dist
!apps/desktop/dist/mac-universal/**
retention-days: 1
- name: Assert zip is present
if: contains(inputs.targets, 'zip')
working-directory: apps/desktop
run: |
test -f ./dist/Element*-mac.zip
- name: Assert dmg is present
if: contains(inputs.targets, 'dmg')
working-directory: apps/desktop
run: |
test -f ./dist/Element*.dmg
test:
name: Test macOS Universal
needs: build
if: inputs.test && contains(inputs.targets, 'dmg')
uses: ./.github/workflows/build_desktop_test.yaml
with:
project: macos
artifact: ${{ inputs.artifact-prefix }}macos
runs-on: macos-14
executable: /Users/runner/Applications/Element*.app/Contents/MacOS/Element*
# We need to mount the DMG and copy the app to the Applications folder as a mounted DMG is
# read-only and thus would not allow us to override the fuses as is required for Playwright.
prepare_cmd: |
hdiutil attach ./dist/*.dmg -mountpoint /Volumes/Element &&
rsync -a /Volumes/Element/Element*.app ~/Applications/ &&
hdiutil detach /Volumes/Element
blob_report: ${{ inputs.blob_report }}
args: ${{ inputs.test-args }}
@@ -1,197 +0,0 @@
# This action helps perform common actions before the build_* actions are started in parallel.
on:
workflow_call:
inputs:
config:
type: string
required: true
description: "The config directory to use"
version:
type: string
required: false
description: "The version tag to fetch, or 'develop', will pick automatically if not passed"
nightly:
type: boolean
required: false
default: false
description: "Whether the build is a Nightly and to calculate the version strings new builds should use"
deploy:
type: boolean
required: false
default: false
description: "Whether the build should be deployed to production"
webapp-artifact:
type: string
required: false
description: "Name of the webapp artifact that should be used, will fetch a relevant build if omitted"
secrets:
# Required if `nightly` is set
CF_R2_ACCESS_KEY_ID:
required: false
# Required if `nightly` is set
CF_R2_TOKEN:
required: false
outputs:
nightly-version:
description: "The version string the next Nightly should use, only output for nightly"
value: ${{ jobs.prepare.outputs.nightly-version }}
packages-dir:
description: "The directory non-deb packages for this run should live in within packages.element.io"
value: ${{ inputs.nightly && 'nightly' || 'desktop' }}
# This is just a simple pass-through of the input to simplify reuse of complex inline conditions
deploy:
description: "Whether the build should be deployed to production"
value: ${{ inputs.deploy }}
permissions: {}
jobs:
prepare:
name: Prepare
environment: ${{ inputs.nightly && 'packages.element.io' || '' }}
runs-on: ubuntu-24.04
permissions:
contents: read
outputs:
nightly-version: ${{ steps.versions.outputs.nightly }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
repository: element-hq/element-web
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: apps/desktop/.node-version
cache: "pnpm"
- name: Install Deps
working-directory: apps/desktop
run: "pnpm install --frozen-lockfile --filter element-desktop"
- name: Fetch Element Web (from artifact)
if: inputs.webapp-artifact != ''
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: ${{ inputs.webapp-artifact }}
path: apps/desktop/webapp
- name: Build webapp.asar (from artifact)
if: inputs.webapp-artifact != ''
working-directory: apps/desktop
run: |
cp -f "$CONFIG_DIR/config.json" webapp/config.json
pnpm run asar-webapp
env:
CONFIG_DIR: ${{ inputs.config }}
- name: Fetch Element Web (${{ inputs.version }})
if: inputs.webapp-artifact == ''
working-directory: apps/desktop
run: pnpm run fetch --noverify -d ${CONFIG} ${VERSION}
env:
CONFIG: ${{ inputs.config }}
VERSION: ${{ inputs.version }}
- name: Copy variant config
working-directory: apps/desktop
run: cp "$CONFIG_DIR/build.json" variant.json
env:
CONFIG_DIR: ${{ inputs.config }}
# We split this out to save the build_* scripts having to do it to make use of `hashFiles` in the cache action
- name: Generate cache hash files
working-directory: apps/desktop
run: |
set -ex
# Add --no-sandbox as otherwise it fails because the helper isn't setuid root. It's only getting the version.
pnpm --silent electron --no-sandbox --version > electronVersion
cat package.json | jq -c .hakDependencies | sha1sum > hakHash
find hak -type f -print0 | xargs -0 sha1sum >> hakHash
find scripts/hak -type f -print0 | xargs -0 sha1sum >> hakHash
- name: "[Nightly] Calculate version"
id: versions
if: inputs.nightly
working-directory: apps/desktop
run: |
set -e
# Find all latest Nightly versions
aws s3 cp s3://$R2_BUCKET/nightly/update/macos/releases.json - --endpoint-url $R2_URL --region auto | jq -r .currentRelease >> VERSIONS
aws s3 cp s3://$R2_BUCKET/debian/dists/default/main/binary-amd64/Packages - --endpoint-url $R2_URL --region auto | grep "Package: element-nightly" -A 50 | grep Version -m1 | sed -n 's/Version: //p' >> VERSIONS
aws s3 cp s3://$R2_BUCKET/debian/dists/default/main/binary-arm64/Packages - --endpoint-url $R2_URL --region auto | grep "Package: element-nightly" -A 50 | grep Version -m1 | sed -n 's/Version: //p' >> VERSIONS
aws s3 cp s3://$R2_BUCKET/nightly/update/win32/x64/RELEASES - --endpoint-url $R2_URL --region auto | awk '{print $2}' | cut -d "-" -f 5 | cut -c 8- >> VERSIONS
aws s3 cp s3://$R2_BUCKET/nightly/update/win32/arm64/RELEASES - --endpoint-url $R2_URL --region auto | awk '{print $2}' | cut -d "-" -f 5 | cut -c 8- >> VERSIONS
# Pick the greatest one
VERSION=$(cat VERSIONS | sort -uf | tail -n1)
echo "Found latest nightly version $VERSION"
# Increment it
echo "nightly=$(scripts/generate-nightly-version.ts --latest $VERSION)" >> $GITHUB_OUTPUT
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }}
R2_BUCKET: ${{ vars.R2_BUCKET }}
R2_URL: ${{ vars.CF_R2_S3_API }}
- name: Check version
id: package
working-directory: apps/desktop
run: |
echo "version=$(cat package.json | jq -r .version)" >> $GITHUB_OUTPUT
- name: "[Release] Fetch release"
id: release
if: ${{ !inputs.nightly && inputs.version != 'develop' }}
uses: cardinalby/git-get-release-action@5172c3a026600b1d459b117738c605fabc9e4e44 # 1.2.5
env:
GITHUB_TOKEN: ${{ github.token }}
with:
tag: v${{ steps.package.outputs.version }}
- name: "[Release] Write changelog"
if: ${{ !inputs.nightly && inputs.version != 'develop' }}
working-directory: apps/desktop
run: |
TIME=$(date -d "$PUBLISHED_AT" -R)
echo "element-desktop ($VERSION) default; urgency=medium" >> changelog.Debian
echo "$BODY" | sed 's/^##/\n */g;s/^\*/ */g' | perl -pe 's/\[.+?]\((.+?)\)/\1/g' >> changelog.Debian
echo "" >> changelog.Debian
echo " -- $ACTOR <support@element.io> $TIME" >> changelog.Debian
env:
ACTOR: ${{ github.actor }}
VERSION: v${{ steps.package.outputs.version }}
BODY: ${{ steps.release.outputs.body }}
PUBLISHED_AT: ${{ steps.release.outputs.published_at }}
- name: "[Nightly] Write summary"
if: inputs.nightly
working-directory: apps/desktop
run: |
set -e
BUNDLE_HASH=$(npx asar l webapp.asar | grep /bundles/ | head -n 1 | sed 's|.*/||')
WEBAPP_VERSION=$(./scripts/get-version.ts)
WEB_VERSION=${WEBAPP_VERSION:0:12}
JS_VERSION=${WEBAPP_VERSION:16:12}
echo "### Nightly build ${NIGHTLY_VERSION}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Component | Version |" >> $GITHUB_STEP_SUMMARY
echo "| ----------- | ------- |" >> $GITHUB_STEP_SUMMARY
echo "| Bundle Hash | $BUNDLE_HASH |" >> $GITHUB_STEP_SUMMARY
echo "| Element Web | [$WEB_VERSION](https://github.com/element-hq/element-web/commit/$WEB_VERSION) |" >> $GITHUB_STEP_SUMMARY
echo "| JS SDK | [$JS_VERSION](https://github.com/matrix-org/matrix-js-sdk/commit/$JS_VERSION) |" >> $GITHUB_STEP_SUMMARY
env:
NIGHTLY_VERSION: ${{ steps.versions.outputs.nightly }}
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: desktop-prepare
retention-days: 1
path: |
apps/desktop/webapp.asar
apps/desktop/electronVersion
apps/desktop/hakHash
apps/desktop/changelog.Debian
apps/desktop/variant.json
-118
View File
@@ -1,118 +0,0 @@
# This action helps run Playwright tests within one of the build_* stages.
on:
workflow_call:
inputs:
runs-on:
type: string
required: true
description: "The runner image to use"
artifact:
type: string
required: true
description: "The name of the artifact to download"
project:
type: string
required: true
description: "The Playwright project to use for testing"
executable:
type: string
required: true
description: "Path to the executable to test"
prepare_cmd:
type: string
required: false
description: "Command to run to prepare the executable or environment for testing"
blob_report:
type: boolean
default: false
description: "Whether to upload a blob report instead of the HTML report"
args:
type: string
required: false
description: "Additional arguments to pass to playwright, for e.g. skipping specific tests"
permissions: {}
jobs:
test:
name: Test ${{ inputs.project }}
runs-on: ${{ inputs.runs-on }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
repository: element-hq/element-web
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: apps/desktop/.node-version
cache: "pnpm"
- name: Install Deps
run: "pnpm install --frozen-lockfile --filter element-desktop"
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: ${{ inputs.artifact }}
path: apps/desktop/dist
- name: Prepare for tests
working-directory: apps/desktop
# This is set by the caller of the reusable workflow, they have the ability to run the command they specify
# directly without our help so this is fine.
run: ${{ inputs.prepare_cmd }} # zizmor: ignore[template-injection]
if: inputs.prepare_cmd
- name: Expand executable path
id: executable
working-directory: apps/desktop
shell: bash
env:
EXECUTABLE: ${{ inputs.executable }}
run: |
FILES=($EXECUTABLE)
echo "path=${FILES[0]}" >> $GITHUB_OUTPUT
# We previously disabled the `EnableNodeCliInspectArguments` fuse, but Playwright requires
# it to be enabled to test Electron apps, so turn it back on.
- name: Set EnableNodeCliInspectArguments fuse enabled
run: $RUN_AS npx @electron/fuses write --app "$EXECUTABLE" EnableNodeCliInspectArguments=on
working-directory: apps/desktop
shell: bash
env:
# We need sudo on Linux as it is installed in /opt/
RUN_AS: ${{ runner.os == 'Linux' && 'sudo' || '' }}
EXECUTABLE: ${{ steps.executable.outputs.path }}
- name: Run tests
timeout-minutes: 20
shell: bash
working-directory: apps/desktop
run: |
$PREFIX pnpm playwright test \
${{ runner.os != 'Linux' && '--ignore-snapshots' || '' }} \
${{ inputs.blob_report == false && '--reporter=html' || '' }} \
$ARGS
env:
PREFIX: ${{ runner.os == 'Linux' && 'xvfb-run' || '' }}
PW_TAG: ${{ inputs.project }}
ELEMENT_DESKTOP_EXECUTABLE: ${{ steps.executable.outputs.path }}
ARGS: ${{ inputs.args }}
DEBUG: pw:browser
- name: Upload blob report
if: always() && inputs.blob_report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: blob-report-${{ inputs.artifact }}
path: apps/desktop/blob-report
retention-days: 1
if-no-files-found: error
- name: Upload HTML report
if: always() && inputs.blob_report == false
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ${{ inputs.artifact }}-test
path: apps/desktop/playwright-report
retention-days: 14
if-no-files-found: error
@@ -1,314 +0,0 @@
# This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build
# Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure
# the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch.
# Windows GHA runner by default uses the pwsh shell which breaks codeSigningCert in the workflow
# We always sign using eSignerCKA to ensure it keeps working, but aside from release & nightlies we use demo credentials
# which do not yield trusted signatures.
defaults:
run:
shell: powershell
on:
workflow_call:
secrets:
ESIGNER_USER_NAME:
required: false
ESIGNER_USER_PASSWORD:
required: false
ESIGNER_USER_TOTP:
required: false
inputs:
ref:
type: string
required: false
description: "The git ref to checkout, defaults to the default branch"
arch:
type: string
required: true
description: "The architecture to build for, one of 'x64' | 'ia32' | 'arm64'"
version:
type: string
required: false
description: "Version string to override the one in package.json, used for non-release builds"
sign:
type: string
required: false
description: "Whether to sign & notarise the build, requires 'Desktop eSigner' environment"
blob_report:
type: boolean
required: false
description: "Whether to run the blob report"
prepare-artifact-name:
type: string
required: false
description: |
The name of the prepare artifact to use, defaults to 'desktop-prepare'.
The artifact must contain the following:
+ webapp.asar - the asar archive of the webapp to embed in the desktop app
+ electronVersion - the version of electron to use for cache keying
+ hakHash - the hash of the .hak directory to use for cache keying
+ variant.json - the variant configuration to use for the build
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
for example icons in the `build/` directory to override the app icons.
default: "desktop-prepare"
test:
type: boolean
required: false
default: true
description: "Whether to run the test stage after building"
test-runs-on:
type: string
required: false
description: "The runner image to use for testing, normally set for you, may be needed for running in private repos."
test-args:
type: string
required: false
description: "Additional arguments to pass to playwright"
artifact-prefix:
type: string
required: false
description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
default: ""
targets:
type: string
required: false
description: "List of targets to build"
default: "squirrel msi"
permissions: {} # No permissions required
jobs:
build:
name: Build Windows ${{ inputs.arch }}
runs-on: windows-2025
environment: ${{ inputs.sign && 'Desktop eSigner' || '' }}
env:
SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0/x86/signtool.exe"
steps:
- uses: nbucic/variable-mapper@0673f6891a0619ba7c002ecfed0f9f4f39017b6f
id: config
with:
key: "${{ inputs.arch }}"
export_to: output
map: |
{
"x64": {
"target": "x86_64-pc-windows-msvc"
},
"arm64": {
"target": "aarch64-pc-windows-msvc",
"build-args": "--arm64",
"arch": "amd64_arm64"
},
"ia32": {
"target": "i686-pc-windows-msvc",
"build-args": "--ia32",
"arch": "x86",
"extra_config": "{\"user_notice\": {\"title\": \"Your desktop environment is unsupported.\",\"description\": \"Support for 32-bit Windows installations has ended. Transition to the web or mobile app for continued access.\"}}"
}
}
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
repository: element-hq/element-web
ref: ${{ inputs.ref }}
persist-credentials: false
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: ${{ inputs.prepare-artifact-name }}
path: apps/desktop/
- name: Cache .hak
id: cache
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
key: ${{ runner.os }}-${{ inputs.arch }}-${{ hashFiles('apps/desktop/hakHash', 'apps/desktop/electronVersion') }}
path: |
apps/desktop/.hak
# ActiveTCL package on choco is from 2015,
# this one is newer but includes more than we need
- name: Choco install tclsh
if: steps.cache.outputs.cache-hit != 'true'
shell: pwsh
run: |
choco install -y magicsplat-tcl-tk --no-progress
echo "${HOME}/AppData/Local/Apps/Tcl86/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Choco install NetWide Assembler
if: steps.cache.outputs.cache-hit != 'true'
shell: pwsh
run: |
choco install -y nasm --no-progress
echo "C:/Program Files/NASM" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install Rust
if: steps.cache.outputs.cache-hit != 'true'
run: |
rustup toolchain install stable --profile minimal --no-self-update
rustup default stable
rustup target add $env:TARGET
env:
TARGET: ${{ steps.config.outputs.target }}
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: apps/desktop/.node-version
cache: "pnpm"
- name: Install Deps
working-directory: apps/desktop
run: "pnpm install --frozen-lockfile --filter element-desktop"
- name: Insert config snippet
if: steps.config.outputs.extra_config != ''
working-directory: apps/desktop
shell: bash
run: |
mkdir config-edit
pnpm asar extract webapp.asar config-edit
cd config-edit
mv config.json old-config.json
echo '${{ steps.config.outputs.extra_config }}' | jq -s '.[0] * .[1]' old-config.json - > config.json
rm old-config.json
cd ..
rm webapp.asar
pnpm asar pack config-edit/ webapp.asar
- name: Set up sqlcipher macros
if: steps.cache.outputs.cache-hit != 'true' && contains(inputs.arch, 'arm')
shell: pwsh
run: |
echo "NCC=${{ github.workspace }}\scripts\cl.bat" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Set up build tools
if: steps.cache.outputs.cache-hit != 'true'
uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0
with:
arch: ${{ steps.config.outputs.arch || inputs.arch }}
- name: Build Natives
if: steps.cache.outputs.cache-hit != 'true'
working-directory: apps/desktop
run: |
refreshenv
pnpm run build:native --target $env:TARGET
env:
TARGET: ${{ steps.config.outputs.target }}
- name: Install and configure eSigner CKA
run: |
Set-StrictMode -Version 'Latest'
# Download, extract, and rename
Invoke-WebRequest -OutFile eSigner_CKA.zip "$env:ESIGNER_URL"
Expand-Archive -Path eSigner_CKA.zip -DestinationPath .
Get-ChildItem -Path * -Include "*_build_*.exe" | Rename-Item -NewName eSigner_CKA.exe
# Install
New-Item -ItemType Directory -Force -Path "$env:INSTALL_DIR"
./eSigner_CKA.exe /CURRENTUSER /VERYSILENT /SUPPRESSMSGBOXES /DIR="${{ env.INSTALL_DIR }}" | Out-Null
# Disable logger
$LogConfig = Get-Content -Path ${{ env.INSTALL_DIR }}/log4net.config
$LogConfig[0] = '<log4net threshold="OFF">'
$LogConfig | Set-Content -Path ${{ env.INSTALL_DIR }}/log4net.config
# Configure - default credentials from https://www.ssl.com/guide/esigner-demo-credentials-and-certificates/
${{ env.INSTALL_DIR }}/eSignerCKATool.exe config `
-mode "$env:ESIGNER_MODE" `
-user "${{ secrets.ESIGNER_USER_NAME || 'esigner_demo' }}" `
-pass "${{ secrets.ESIGNER_USER_PASSWORD || 'esignerDemo#1' }}" `
-totp "${{ secrets.ESIGNER_USER_TOTP || 'RDXYgV9qju+6/7GnMf1vCbKexXVJmUVr+86Wq/8aIGg=' }}" `
-key "${{ env.MASTER_KEY_FILE }}" -r
${{ env.INSTALL_DIR }}/eSignerCKATool.exe unload
${{ env.INSTALL_DIR }}/eSignerCKATool.exe load
# Find certificate
$CodeSigningCert = Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert | Select-Object -First 1
echo Certificate: $CodeSigningCert
# Extract thumbprint and subject name
$Thumbprint = $CodeSigningCert.Thumbprint
$SubjectName = ($CodeSigningCert.Subject -replace ", ?", "`n" | ConvertFrom-StringData).CN
echo "ED_SIGNTOOL_THUMBPRINT=$Thumbprint" >> $env:GITHUB_ENV
echo "ED_SIGNTOOL_SUBJECT_NAME=$SubjectName" >> $env:GITHUB_ENV
env:
ESIGNER_MODE: ${{ vars.ESIGNER_MODE || 'sandbox' }}
ESIGNER_URL: https://github.com/SSLcom/eSignerCKA/releases/download/v1.0.6/SSL.COM-eSigner-CKA_1.0.6.zip
INSTALL_DIR: C:\Users\runneradmin\eSignerCKA
MASTER_KEY_FILE: C:\Users\runneradmin\eSignerCKA\master.key
- name: Build App
working-directory: apps/desktop
run: pnpm run build --publish never $BUILD_ARGS -w $TARGETS
shell: bash
env:
VARIANT_PATH: variant.json
# Only set for Nightly builds
# The windows packager relies on parsing this as semver, so we have to make it look like one.
# This will give our update packages really stupid names, but we probably can't change that either
# because squirrel windows parses them for the version too. We don't really care: nobody sees them.
# We just give the installer a static name, so you'll just see this in the 'about' dialog.
# Turns out if you use 0.0.0 here it makes Squirrel windows crash, so we use 0.0.1.
VERSION: ${{ inputs.version && format('0.0.1-nightly.{0}', inputs.version) || '' }}
BUILD_ARGS: ${{ steps.config.outputs.build-args }}
TARGETS: ${{ inputs.targets }}
- name: Trust eSigner sandbox cert
if: inputs.sign == ''
run: |
Set-StrictMode -Version 'Latest'
Import-Certificate -CertStoreLocation Cert:\LocalMachine\Root -FilePath .github/SSLcom-sandbox.crt
- name: Check app was signed successfully
working-directory: apps/desktop
run: |
Set-StrictMode -Version 'Latest'
Get-ChildItem `
-Recurse dist `
-Include *.exe, *.msi `
| ForEach-Object -Process {. $env:SIGNTOOL_PATH verify /pa $_.FullName; if(!$?) { throw }}
- name: Upload Artifacts
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: ${{ inputs.artifact-prefix }}win-${{ inputs.arch }}
path: |
apps/desktop/dist
retention-days: 1
- name: Assert executable is present
working-directory: apps/desktop
run: |
Test-Path './dist/win-*unpacked/Element*.exe'
- name: Assert all Squirrel files are present
if: contains(inputs.targets, 'squirrel')
working-directory: apps/desktop
run: |
Test-Path './dist/squirrel-windows*/Element Setup*.exe'
Test-Path './dist/squirrel-windows*/element-desktop-*-full.nupkg'
Test-Path './dist/squirrel-windows*/RELEASES'
- name: Assert MSI is present
if: contains(inputs.targets, 'msi')
working-directory: apps/desktop
run: |
Test-Path './dist/Element*.msi'
test:
name: Test Windows ${{ inputs.arch }}
needs: build
if: inputs.test
uses: ./.github/workflows/build_desktop_test.yaml
with:
project: win-${{ inputs.arch }}
artifact: ${{ inputs.artifact-prefix }}win-${{ inputs.arch }}
runs-on: ${{ inputs.test-runs-on || (inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2022') }}
executable: ./dist/win*-unpacked/Element*.exe
blob_report: ${{ inputs.blob_report }}
args: ${{ inputs.test-args }}
-141
View File
@@ -1,141 +0,0 @@
# Separate to the main build workflow for access to develop
# environment secrets, largely similar to build.yaml.
name: Build and Deploy develop
on:
push:
branches: [develop]
repository_dispatch:
types: [element-web-notify]
concurrency:
group: ${{ github.repository_owner }}-${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: true
env:
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
permissions: {}
jobs:
build:
name: "Build & Deploy develop.element.io"
# Only respect triggers from our develop branch, ignore that of forks
if: github.repository == 'element-hq/element-web'
runs-on: ubuntu-24.04
environment: develop
permissions:
checks: read
pages: write
deployments: write
env:
R2_BUCKET: "element-web-develop"
R2_URL: ${{ vars.CF_R2_S3_API }}
R2_PUBLIC_URL: "https://element-web-develop.element.io"
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
cache: "pnpm"
node-version: "lts/*"
- name: Install Dependencies
run: "./scripts/layered.sh"
- name: Build, Package & Upload sourcemaps
working-directory: apps/web
run: "./scripts/ci_package.sh"
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
SENTRY_URL: ${{ secrets.SENTRY_URL }}
SENTRY_ORG: element
SENTRY_PROJECT: riot-web
# We only deploy the latest bundles to Cloudflare Pages and use _redirects to fallback to R2 for
# older ones. This redirect means that 'self' is insufficient in the CSP,
# and we have to add the R2 URL.
# Once Cloudflare redirects support proxying mode we will be able to ditch this.
# See Proxying in support table at https://developers.cloudflare.com/pages/platform/redirects
CSP_EXTRA_SOURCE: ${{ env.R2_PUBLIC_URL }}
- run: mv dist/element-*.tar.gz dist/develop.tar.gz
working-directory: apps/web
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: webapp
path: apps/web/dist/develop.tar.gz
retention-days: 1
- name: Extract webapp
run: |
mkdir _deploy
tar xf apps/web/dist/develop.tar.gz -C _deploy --strip-components=1
- name: Copy config
run: cp apps/web/element.io/develop/config.json _deploy/config.json
- name: Populate 404.html
run: echo "404 Not Found" > _deploy/404.html
- name: Populate _headers
run: cp .github/cfp_headers _deploy/_headers
# Redirect requests for the develop tarball and the historical bundles to R2
# We find the latest 100 bundle.css files and add their bundles to the redirects file
# S3 has no sane way to get the age of a directory as they don't really exist
- name: Populate _redirects
run: |
{
echo "/develop.tar.gz $R2_PUBLIC_URL/develop.tar.gz 301"
aws s3api --region auto --endpoint-url $R2_URL list-objects-v2 --bucket $R2_BUCKET \
--query "sort_by(Contents[?ends_with(Key, '/bundle.css')], &LastModified)[-100:].Key" \
--prefix "bundles/" | jq -r '.[]' | grep -oE '[^\"].*\/\s*' | while read -r path ; do
echo "/${path}* $R2_PUBLIC_URL/${path}:splat 301"
done
} | tee _deploy/_redirects
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }}
# We may be trying to deploy the same webapp bundles again, we need to ensure that the live bundles
# are not present in the _redirects file and instead accessed directly from Cloudflare Pages.
- name: Trim _redirects
working-directory: _deploy
run: |
find bundles -type d -mindepth 1 -maxdepth 1 -exec sed -i "\:{}:d" _redirects \;
- name: Wait for other steps to succeed
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
with:
ref: ${{ github.sha }}
running-workflow-name: "Build & Deploy develop.element.io"
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release|prepare|GitHub Pages|Upload|Netlify|Report).)*$
# We keep the latest develop.tar.gz on R2 instead of relying on the github artifact uploaded earlier
# as the expires after 24h and requires auth to download.
# Element Desktop's fetch script uses this tarball to fetch latest develop to build Nightlies.
# Checksum algorithm specified as per https://developers.cloudflare.com/r2/examples/aws/aws-cli/
- name: Deploy to R2
run: |
aws s3 cp apps/web/dist/develop.tar.gz s3://$R2_BUCKET/develop.tar.gz --endpoint-url $R2_URL --region=auto --checksum-algorithm CRC32
aws s3 cp _deploy/ s3://$R2_BUCKET/ --recursive --endpoint-url $R2_URL --region=auto --checksum-algorithm CRC32
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }}
- name: Deploy to Cloudflare Pages
id: cfp
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1
with:
apiToken: ${{ secrets.CF_PAGES_TOKEN }}
accountId: ${{ secrets.CF_PAGES_ACCOUNT_ID }}
projectName: element-web-develop
directory: _deploy
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
- run: |
echo "Deployed to ${STEPS_CFP_OUTPUTS_URL}" >> $GITHUB_STEP_SUMMARY
env:
STEPS_CFP_OUTPUTS_URL: ${{ steps.cfp.outputs.url }}
-75
View File
@@ -1,75 +0,0 @@
name: CD # Continuous Delivery
on:
push:
branches: [master, staging, develop]
paths:
- "**/Dockerfile"
- "**/dockerbuild"
- "**/docker"
- "**/docker-*"
- "pnpm-lock.yaml"
concurrency: ${{ github.workflow }}-${{ github.ref_name }}
permissions: {}
env:
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
jobs:
docker:
name: Docker Bake
runs-on: ubuntu-24.04
permissions:
id-token: write # needed for signing the images with GitHub OIDC Token
packages: write # needed for publishing packages to GHCR
# Needed for nx-set-shas
contents: read
actions: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Install Cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
- name: Set up Docker Buildx
id: builder
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version-file: package.json
cache: "pnpm"
- name: Install Deps
run: "pnpm install --frozen-lockfile"
- name: Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- run: pnpm nx run-many --nxBail -t docker:build
id: build
env:
INPUT_PUSH: true
INPUT_LOAD: false
INPUT_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INPUT_BUILDER: ${{ steps.builder.outputs.name }}
- name: Sign the images with GitHub OIDC token
run: |
shopt -s globstar
for FILE in ./node_modules/.cache/nx-container/**/metadata; do
TARGET=$(jq -r '(.["image.name"] | split(",") | last) + "@" + .["containerimage.digest"]' "$FILE")
echo "Signing $TARGET..."
cosign sign --yes "$TARGET"
done
-101
View File
@@ -1,101 +0,0 @@
# Manual deploy workflow for deploying to app.element.io & staging.element.io
# Runs automatically for staging.element.io when an RC or Release is published
# Note: Does *NOT* run automatically for app.element.io so that it gets tested on staging.element.io beforehand
name: Deploy release
run-name: Deploy ${{ github.ref_name }} to ${{ inputs.site || 'staging.element.io' }}
on:
release:
types: [published]
workflow_dispatch:
inputs:
site:
description: Which site to deploy to
required: true
default: staging.element.io
type: choice
options:
- staging.element.io
- app.element.io
skip-checks:
description: Skip CI on the tagged commit
required: true
default: false
type: boolean
concurrency: ${{ inputs.site || 'staging.element.io' }}
permissions: {}
jobs:
deploy:
name: "Deploy to Cloudflare Pages"
runs-on: ubuntu-24.04
environment: ${{ inputs.site || 'staging.element.io' }}
permissions:
checks: read
deployments: write
env:
SITE: ${{ inputs.site || 'staging.element.io' }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Load GPG key
run: |
curl https://packages.element.io/element-release-key.gpg | gpg --import
gpg -k "$GPG_FINGERPRINT"
env:
GPG_FINGERPRINT: ${{ vars.GPG_FINGERPRINT }}
- name: Check current version on deployment
id: current_version
run: |
version=$(curl -s https://$SITE/version)
echo "version=${version#v}" >> $GITHUB_OUTPUT
# The current version bundle melding dance is skipped if the version we're deploying is the same
# as then we're just doing a re-deploy of the same version with potentially different configs.
- name: Download current version for its old bundles
id: current_download
if: steps.current_version.outputs.version != github.ref_name
uses: ./.github/actions/download-verify-element-tarball
with:
tag: v${{ steps.current_version.outputs.version }}
out-file-path: _current_version
- name: Download target version
uses: ./.github/actions/download-verify-element-tarball
with:
tag: ${{ github.ref_name }}
out-file-path: _deploy
- name: Merge current bundles into target
if: steps.current_download.outcome == 'success'
run: cp -vnpr _current_version/bundles/* _deploy/bundles/
- name: Copy config
run: cp apps/web/element.io/app/config.json _deploy/config.json
- name: Populate 404.html
run: echo "404 Not Found" > _deploy/404.html
- name: Populate _headers
run: cp .github/cfp_headers _deploy/_headers
- name: Wait for other steps to succeed
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
if: inputs.skip-checks != true
with:
ref: ${{ github.sha }}
running-workflow-name: "Deploy to Cloudflare Pages"
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-regexp: ^((?!SonarCloud|SonarQube|issue|board|label|Release|prepare|GitHub Pages).)*$
- name: Deploy to Cloudflare Pages
uses: cloudflare/pages-action@f0a1cd58cd66095dee69bfa18fa5efd1dde93bca # v1
with:
apiToken: ${{ secrets.CF_PAGES_TOKEN }}
accountId: ${{ secrets.CF_PAGES_ACCOUNT_ID }}
projectName: ${{ env.SITE == 'staging.element.io' && 'element-web-staging' || 'element-web' }}
directory: _deploy
gitHubToken: ${{ secrets.GITHUB_TOKEN }}
branch: main
-181
View File
@@ -1,181 +0,0 @@
name: Docker
on:
workflow_dispatch: {}
push:
tags: [v*]
pull_request: {}
schedule:
# This job can take a while, and we have usage limits, so just publish develop only twice a day
- cron: "0 7/12 * * *"
concurrency: ${{ github.workflow }}-${{ github.ref_name }}
permissions: {}
jobs:
buildx:
name: Docker Buildx
runs-on: ubuntu-24.04
environment: ${{ github.event_name != 'pull_request' && 'dockerhub' || '' }}
permissions:
id-token: write # needed for signing the images with GitHub OIDC Token
packages: write # needed for publishing packages to GHCR
env:
TEST_TAG: vectorim/element-web:test
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0 # needed for docker-package to be able to calculate the version
persist-credentials: false
- name: Install Cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
if: github.event_name != 'pull_request'
- name: Set up QEMU
uses: docker/setup-qemu-action@ce360397dd3f832beb865e1373c09c0e9f86d70a # v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
with:
install: true
- name: Build and load
id: test-build
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
with:
context: .
file: apps/web/Dockerfile
load: true
- name: Test the image
env:
IMAGEID: ${{ steps.test-build.outputs.imageid }}
timeout-minutes: 2
run: |
set -x
# Make a fake module to test the image
MODULE_PATH="modules/module_name/index.js"
mkdir -p $(dirname $MODULE_PATH)
echo 'alert("Testing");' > $MODULE_PATH
# Spin up a container of the image
ELEMENT_WEB_PORT=8181
CONTAINER_ID=$(
docker run \
--rm \
-e "ELEMENT_WEB_PORT=$ELEMENT_WEB_PORT" \
-dp "$ELEMENT_WEB_PORT:$ELEMENT_WEB_PORT" \
-v $(pwd)/modules:/modules \
"$IMAGEID" \
)
# Run some smoke tests
wget --retry-connrefused --tries=5 -q --wait=3 --spider "http://localhost:$ELEMENT_WEB_PORT/modules/module_name/index.js"
MODULE_0=$(curl "http://localhost:$ELEMENT_WEB_PORT/config.json" | jq -r .modules[0])
test "$MODULE_0" = "/${MODULE_PATH}"
# Check healthcheck
until test "$(docker inspect -f {{.State.Health.Status}} $CONTAINER_ID)" == "healthy"; do
sleep 1
done
# Clean up
docker stop "$CONTAINER_ID"
- name: Docker meta
id: meta
uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf # v6
if: github.event_name != 'pull_request'
with:
images: |
vectorim/element-web
ghcr.io/element-hq/element-web
oci-push.vpn.infra.element.io/element-web
tags: |
type=ref,event=branch
type=ref,event=tag
flavor: |
latest=${{ contains(github.ref_name, '-rc.') && 'false' || 'auto' }}
- name: Login to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
if: github.event_name != 'pull_request'
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
if: github.event_name != 'pull_request'
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Connect to Tailscale
uses: tailscale/github-action@306e68a486fd2350f2bfc3b19fcd143891a4a2d8 # v4
if: github.event_name != 'pull_request'
with:
oauth-client-id: ${{ secrets.TS_OAUTH_CLIENT_ID }}
audience: ${{ secrets.TS_AUDIENCE }}
tags: tag:github-actions
- name: Compute vault jwt role name
id: vault-jwt-role
if: github.event_name != 'pull_request'
run: |
echo "role_name=github_service_management_$( echo "${{ github.repository }}" | sed -r 's|[/-]|_|g')" | tee -a "$GITHUB_OUTPUT"
- name: Get team registry token
id: import-secrets
uses: hashicorp/vault-action@4c06c5ccf5c0761b6029f56cfb1dcf5565918a3b # v3
if: github.event_name != 'pull_request'
with:
url: https://vault.infra.ci.i.element.dev
role: ${{ steps.vault-jwt-role.outputs.role_name }}
path: service-management/github-actions
jwtGithubAudience: https://vault.infra.ci.i.element.dev
method: jwt
secrets: |
services/web-repositories/secret/data/oci.element.io username | OCI_USERNAME ;
services/web-repositories/secret/data/oci.element.io password | OCI_PASSWORD ;
- name: Login to oci.element.io Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4
if: github.event_name != 'pull_request'
with:
registry: oci-push.vpn.infra.element.io
username: ${{ steps.import-secrets.outputs.OCI_USERNAME }}
password: ${{ steps.import-secrets.outputs.OCI_PASSWORD }}
- name: Build and push
id: build-and-push
uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f # v7
if: github.event_name != 'pull_request'
with:
context: .
file: apps/web/Dockerfile
push: true
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
- name: Sign the images with GitHub OIDC Token
env:
DIGEST: ${{ steps.build-and-push.outputs.digest }}
TAGS: ${{ steps.meta.outputs.tags }}
if: github.event_name != 'pull_request'
run: |
images=""
for tag in ${TAGS}; do
images+="${tag}@${DIGEST} "
done
cosign sign --yes ${images}
- name: Update repo description
uses: peter-evans/dockerhub-description@1b9a80c056b620d92cedb9d9b5a223409c68ddfa # v5
if: github.event_name != 'pull_request'
continue-on-error: true
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
repository: vectorim/element-web
-55
View File
@@ -1,55 +0,0 @@
name: Deploy documentation
on:
push:
branches: [develop]
workflow_dispatch: {}
permissions: {}
concurrency:
group: "pages"
cancel-in-progress: false
jobs:
build:
name: GitHub Pages
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
with:
package_json_file: package.json
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
cache: "pnpm"
cache-dependency-path: pnpm-lock.yaml
node-version: "lts/*"
- name: Fetch layered build
run: ./scripts/layered.sh
- name: Build docs
run: pnpm run docs:build
- name: Upload artifact
uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
with:
path: ./docs/.vitepress/dist
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-24.04
permissions:
pages: write
id-token: write
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
-157
View File
@@ -1,157 +0,0 @@
# For duplicate issues, ensure the close type is right (not planned), update it if not
# For all closed (completed) issues, cascade the closure onto any referenced rageshakes
# For all closed (not planned) issues, comment on rageshakes to move them into the canonical issue if one exists
on:
issues:
types: [closed]
permissions: {} # We use ELEMENT_BOT_TOKEN instead
jobs:
tidy:
name: Tidy closed issues
runs-on: ubuntu-24.04
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
id: main
with:
# PAT needed as the GITHUB_TOKEN won't be able to see cross-references from other orgs (matrix-org)
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
script: |
const variables = {
owner: context.repo.owner,
name: context.repo.repo,
number: context.issue.number,
};
const query = `query($owner:String!, $name:String!, $number:Int!) {
repository(owner: $owner, name: $name) {
issue(number: $number) {
stateReason,
timelineItems(first: 100, itemTypes: [MARKED_AS_DUPLICATE_EVENT, UNMARKED_AS_DUPLICATE_EVENT, CROSS_REFERENCED_EVENT]) {
edges {
node {
__typename
... on MarkedAsDuplicateEvent {
canonical {
... on Issue {
repository {
nameWithOwner
}
number
}
... on PullRequest {
repository {
nameWithOwner
}
number
}
}
}
... on UnmarkedAsDuplicateEvent {
canonical {
... on Issue {
repository {
nameWithOwner
}
number
}
... on PullRequest {
repository {
nameWithOwner
}
number
}
}
}
... on CrossReferencedEvent {
source {
... on Issue {
repository {
nameWithOwner
}
number
}
... on PullRequest {
repository {
nameWithOwner
}
number
}
}
}
}
}
}
}
}
}`;
const result = await github.graphql(query, variables);
const { stateReason, timelineItems: { edges } } = result.repository.issue;
const RAGESHAKE_OWNER = "matrix-org";
const RAGESHAKE_REPO = "element-web-rageshakes";
const rageshakes = new Set();
const duplicateOf = new Set();
console.log("Edges: ", JSON.stringify(edges));
for (const { node } of edges) {
switch(node.__typename) {
case "MarkedAsDuplicateEvent":
duplicateOf.add(node.canonical.repository.nameWithOwner + "#" + node.canonical.number);
break;
case "UnmarkedAsDuplicateEvent":
duplicateOf.remove(node.canonical.repository.nameWithOwner + "#" + node.canonical.number);
break;
case "CrossReferencedEvent":
if (node.source.repository.nameWithOwner === (RAGESHAKE_OWNER + "/" + RAGESHAKE_REPO)) {
rageshakes.add(node.source.number);
}
break;
}
}
console.log("Duplicate of: ", duplicateOf);
console.log("Found rageshakes: ", rageshakes);
if (duplicateOf.size) {
const body = Array.from(duplicateOf).join("\n");
// Comment on all rageshakes to create relationship to the issue this was closed as duplicate of
for (const rageshake of rageshakes) {
github.rest.issues.createComment({
owner: RAGESHAKE_OWNER,
repo: RAGESHAKE_REPO,
issue_number: rageshake,
body,
});
}
// Duplicate was closed with wrong reason, fix it
if (stateReason === "COMPLETED") {
core.setOutput("closeAsNotPlanned", "true");
}
} else {
// This issue was closed, close all related rageshakes
for (const rageshake of rageshakes) {
github.rest.issues.update({
owner: RAGESHAKE_OWNER,
repo: RAGESHAKE_REPO,
issue_number: rageshake,
state: "closed",
});
}
}
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
name: Close duplicate as Not Planned
if: steps.main.outputs.closeAsNotPlanned
with:
# We do this step separately, and with the default token so as to not re-trigger this workflow when re-closing
script: |
await github.graphql(`mutation($id:ID!) {
closeIssue(input: { issueId:$id, stateReason:NOT_PLANNED }) {
clientMutationId
}
}`, {
id: context.payload.issue.node_id,
});
-14
View File
@@ -1,14 +0,0 @@
name: Localazy Download
on:
workflow_dispatch: {}
schedule:
- cron: "0 6 * * 1,3,5" # Every Monday, Wednesday and Friday at 6am UTC
permissions:
pull-requests: write # needed to auto-approve PRs
jobs:
download:
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@6eda3835118f3bc3fb658a1a3c20b7da9d16ae42
with:
packageManager: pnpm
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
-14
View File
@@ -1,14 +0,0 @@
name: Localazy Upload
on:
workflow_dispatch: {}
push:
branches: [develop]
paths:
- "apps/web/src/i18n/strings/en_EN.json"
- "packages/shared-components/src/i18n/strings/en_EN.json"
permissions: {} # No permissions needed
jobs:
upload:
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_upload.yaml@6eda3835118f3bc3fb658a1a3c20b7da9d16ae42
secrets:
LOCALAZY_WRITE_KEY: ${{ secrets.LOCALAZY_WRITE_KEY }}
-29
View File
@@ -1,29 +0,0 @@
# Tweaks the behaviour of Merge Queue to skip certain checks
name: Merge Queue tweaks
on:
merge_group:
types: [checks_requested]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
permissions: {}
jobs:
run:
runs-on: ubuntu-24.04
permissions:
statuses: write
steps:
# This is only needed as license/cla at time of writing seems to be extraordinarily flaky
# and Github doesn't support conditional checks between PR & merge queue.
# This is fine to do as a PR won't make it to merge queue until it has license/cla passing.
- name: Skip license/cla on merge queues
uses: guibranco/github-status-action-v2@9bfa8773cdbdc6c185747fd43cd7faa9d7c32f09
with:
authToken: ${{ secrets.GITHUB_TOKEN }}
state: success
context: license/cla
sha: ${{ github.sha }}
target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
-53
View File
@@ -1,53 +0,0 @@
# Triggers after the layered build has finished, taking the artifact
# and uploading it to netlify
name: Upload Preview Build to Netlify
on:
# Privilege escalation necessary to publish to Netlify
# 🚨 We must not execute any checked out code here.
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows: ["Build"]
types:
- completed
jobs:
deploy:
if: github.event.workflow_run.conclusion != 'cancelled' && github.event.workflow_run.event == 'pull_request'
runs-on: ubuntu-24.04
environment: Netlify
permissions:
actions: read
deployments: write
steps:
- name: 📝 Create Deployment
uses: bobheadxi/deployments@648679e8e4915b27893bd7dbc35cb504dc915bc8 # v1
id: deployment
with:
step: start
token: ${{ secrets.GITHUB_TOKEN }}
env: Netlify
ref: ${{ github.event.workflow_run.head_sha }}
desc: |
Do you trust the author of this PR? Maybe this build will steal your keys or give you malware.
Exercise caution. Use test accounts.
- name: 📥 Download artifact
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
name: webapp-ubuntu-24.04
path: webapp
- name: 📤 Deploy to Netlify
uses: matrix-org/netlify-pr-preview@9805cd123fc9a7e421e35340a05e1ebc5dee46b5 # v3
with:
path: webapp
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
branch: ${{ github.event.workflow_run.head_branch }}
revision: ${{ github.event.workflow_run.head_sha }}
token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
site_id: ${{ vars.NETLIFY_SITE_ID }}
deployment_env: ${{ steps.deployment.outputs.env }}
deployment_id: ${{ steps.deployment.outputs.deployment_id }}
desc: |
Do you trust the author of this PR? Maybe this build will steal your keys or give you malware.
Exercise caution. Use test accounts.
-47
View File
@@ -1,47 +0,0 @@
name: Publish npm package
run-name: Publish ${{ inputs.package }}
on:
workflow_dispatch:
inputs:
package:
description: Which package to release
required: true
type: choice
options:
- playwright-common
- shared-components
- module-api
concurrency: release
jobs:
publish:
name: "Publish"
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: 🧮 Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- name: 🔧 Set up node environment
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
cache: "pnpm"
node-version-file: ".node-version"
registry-url: "https://registry.npmjs.org"
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
- name: 🛠️ Install dependencies
run: pnpm install --frozen-lockfile
- name: 🚀 Publish to npm
working-directory: packages/${{ inputs.package }}
run: npm publish --access public --provenance
-16
View File
@@ -1,16 +0,0 @@
name: Pull Request
on:
# Privilege escalation necessary access members of the review teams
# 🚨 We must not execute any checked out code here, and be careful around use of user-controlled inputs.
pull_request_target: # zizmor: ignore[dangerous-triggers]
types: [opened, edited, labeled, unlabeled, synchronize]
merge_group:
types: [checks_requested]
permissions: {}
jobs:
action:
uses: matrix-org/matrix-js-sdk/.github/workflows/pull_request.yaml@develop # zizmor: ignore[unpinned-uses]
permissions:
pull-requests: write
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
@@ -1,17 +0,0 @@
name: Pull Request Base Branch
on:
pull_request:
types: [opened, edited, synchronize]
permissions: {} # No permissions required
jobs:
check_base_branch:
name: Check PR base branch
runs-on: ubuntu-24.04
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
const baseBranch = context.payload.pull_request.base.ref;
if (!['develop', 'staging'].includes(baseBranch) && !baseBranch.startsWith('feat/')) {
core.setFailed(`Invalid base branch: ${baseBranch}`);
}
-12
View File
@@ -1,12 +0,0 @@
name: Release Drafter
on:
push:
branches: [staging]
workflow_dispatch: {}
concurrency: ${{ github.workflow }}
permissions: {}
jobs:
draft:
permissions:
contents: write
uses: matrix-org/matrix-js-sdk/.github/workflows/release-drafter-workflow.yml@develop # zizmor: ignore[unpinned-uses]
-17
View File
@@ -1,17 +0,0 @@
# Gitflow merge-back master->develop
name: Merge master -> develop
on:
push:
branches: [master]
concurrency: ${{ github.repository }}-${{ github.workflow }}
permissions: {} # We use ELEMENT_BOT_TOKEN instead
jobs:
merge:
uses: matrix-org/matrix-js-sdk/.github/workflows/release-gitflow.yml@develop # zizmor: ignore[unpinned-uses]
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
with:
# This relates to the directory in which to reset dependencies, only web needs this
dir: apps/web
dependencies: |
matrix-js-sdk
-68
View File
@@ -1,68 +0,0 @@
name: Release Process
on:
workflow_dispatch:
inputs:
mode:
description: What type of release
required: true
default: rc
type: choice
options:
- rc
- final
concurrency: ${{ github.workflow }}
permissions: {}
jobs:
release:
uses: matrix-org/matrix-js-sdk/.github/workflows/release-make.yml@develop # zizmor: ignore[unpinned-uses]
permissions:
contents: write
issues: write
pull-requests: read
id-token: write
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
with:
final: ${{ inputs.mode == 'final' }}
gpg-fingerprint: ${{ vars.GPG_FINGERPRINT }}
asset-path: dist/*.tar.gz
expected-asset-count: 3
# Desktop has no dist script so we only target web here
dist-dir: apps/web
version-dirs: apps/web apps/desktop
check:
name: Post release checks
needs: release
runs-on: ubuntu-24.04
permissions:
checks: read
steps:
- name: Wait for docker build
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
with:
ref: master
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-name: "Docker Buildx"
allowed-conclusions: success
- name: Wait for debian package
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
with:
ref: master
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-name: Build package
allowed-conclusions: success
- name: Wait for desktop packaging
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
with:
ref: master
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-regexp: Prepare|Linux|macOS|Windows|Deploy|deploy
allowed-conclusions: success
-97
View File
@@ -1,97 +0,0 @@
name: Cut branches
on:
workflow_dispatch:
inputs:
element-web:
description: Prepare element-web
required: true
type: boolean
default: true
matrix-js-sdk:
description: Prepare matrix-js-sdk
required: true
type: boolean
default: true
permissions: {} # Uses ELEMENT_BOT_TOKEN instead
jobs:
checks:
name: Sanity checks
strategy:
matrix:
repo:
- matrix-org/matrix-js-sdk
- element-hq/element-web
uses: matrix-org/matrix-js-sdk/.github/workflows/release-checks.yml@develop # zizmor: ignore[unpinned-uses]
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
with:
repository: ${{ matrix.repo }}
prepare:
runs-on: ubuntu-24.04
needs: checks
env:
# The order is specified bottom-up to avoid any races for allchange
REPOS: matrix-js-sdk element-web
steps:
- name: Checkout Element Web
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
if: inputs.element-web
with:
repository: element-hq/element-web
path: element-web
ref: staging
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
persist-credentials: true
- name: Checkout Matrix JS SDK
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
if: inputs.matrix-js-sdk
with:
repository: matrix-org/matrix-js-sdk
path: matrix-js-sdk
ref: staging
fetch-depth: 0
fetch-tags: true
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
persist-credentials: true
- name: Prepare Git
run: |
git config --global user.email "releases@riot.im"
git config --global user.name "RiotRobot"
- name: Merge Element Web
if: inputs.element-web
run: |
git -C "element-web" merge origin/develop
- name: Merge JS SDK
if: inputs.matrix-js-sdk
run: |
git -C "matrix-js-sdk" merge origin/develop
- name: Push staging
run: for REPO in $REPOS; do [ -d "$REPO" ] && git -C "$REPO" push origin staging; done
- name: Wait for matrix-js-sdk draft
if: inputs.matrix-js-sdk
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
with:
ref: staging
repo: matrix-org/matrix-js-sdk
repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
wait-interval: 10
check-name: "draft / draft"
allowed-conclusions: success
- name: Wait for element-web draft
if: inputs.element-web
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
with:
ref: staging
repo: element-hq/element-web
repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
wait-interval: 10
check-name: "draft / draft"
allowed-conclusions: success
@@ -1,38 +0,0 @@
name: Build shared component storybook
on:
merge_group: {}
pull_request: {}
workflow_call: {}
permissions: {}
jobs:
doc:
name: Build storybook
runs-on: ubuntu-latest
steps:
- name: 🧮 Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- name: 🔧 Pnpm cache
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
cache: "pnpm"
node-version-file: package.json
- name: 🔨 Install dependencies
working-directory: packages/shared-components
run: "pnpm install --frozen-lockfile"
- name: 📖 Build Storybook
working-directory: packages/shared-components
run: pnpm build:storybook
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: shared-components-storybook
path: packages/shared-components/storybook-static
retention-days: 1
@@ -1,33 +0,0 @@
name: Publish shared component storybook
on:
workflow_dispatch: {}
push:
branches:
- "develop"
paths:
- "packages/shared-components/**/*"
permissions: {}
jobs:
build:
name: Build storybook
uses: ./.github/workflows/shared-component-storybook-build.yml
publish:
name: Publish storybook
runs-on: ubuntu-latest
needs: build
environment: SharedComponents
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
name: shared-components-storybook
path: storybook-static
- name: 🚀 Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3
with:
apiToken: ${{ secrets.CF_PAGES_TOKEN }}
accountId: ${{ secrets.CF_PAGES_ACCOUNT_ID }}
command: pages deploy storybook-static --project-name=shared-components-storybook
@@ -1,50 +0,0 @@
# Triggers after the shared component tests have finished,
# It uploads the received images and diffs to netlify, printing the URLs to the console
name: Upload Shared Component Visual Test Diffs
on:
# Privilege escalation necessary to deploy to Netlify
# 🚨 We must not execute any checked out code here.
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows: ["Shared Component Visual Tests"]
types:
- completed
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch || github.run_id }}
cancel-in-progress: ${{ github.event.workflow_run.event == 'pull_request' }}
permissions: {}
jobs:
report:
if: github.event.workflow_run.conclusion == 'failure'
name: Upload Diffs
runs-on: ubuntu-24.04
environment: Netlify
permissions:
actions: read
deployments: write
steps:
- name: Download Diffs
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
run-id: ${{ github.event.workflow_run.id }}
name: received-images
path: received-images
- name: Generate Index
run: "cd received-images && tree -L 1 --noreport -H '' -o index.html ."
- name: 📤 Deploy to Netlify
uses: matrix-org/netlify-pr-preview@9805cd123fc9a7e421e35340a05e1ebc5dee46b5 # v3
with:
path: received-images
owner: ${{ github.event.workflow_run.head_repository.owner.login }}
branch: ${{ github.event.workflow_run.head_branch }}
revision: ${{ github.event.workflow_run.head_sha }}
token: ${{ secrets.NETLIFY_AUTH_TOKEN }}
site_id: ${{ vars.NETLIFY_SITE_ID }}
desc: Shared Component Visual Diffs
deployment_env: SharedComponentDiffs
prefix: "diffs-"
@@ -1,60 +0,0 @@
name: Shared Component Visual Tests
on:
pull_request: {}
merge_group:
types: [checks_requested]
push:
branches: [develop, master]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
permissions: {} # No permissions required
jobs:
testStorybook:
name: "Run Visual Tests"
runs-on: ubuntu-24.04
permissions:
actions: read
issues: read
pull-requests: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
repository: element-hq/element-web
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
cache: "pnpm"
node-version: "lts/*"
- name: Install dependencies
working-directory: packages/shared-components
run: pnpm install --frozen-lockfile
- name: Setup playwright
uses: ./.github/actions/setup-playwright
with:
write-cache: ${{ github.event_name != 'merge_group' }}
- name: Run Visual tests
working-directory: packages/shared-components
run: "pnpm test:storybook --run"
- name: Detect stale screenshots
run: |
if diff -rq __baselines__ __results__ | grep "^Only in __baselines__"; then
exit 1
fi
working-directory: packages/shared-components/__vis__/linux
- name: Upload received images & diffs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: received-images
path: packages/shared-components/__vis__/linux
-27
View File
@@ -1,27 +0,0 @@
name: SonarQube
on:
# Privilege escalation necessary to call upon SonarCloud
# 🚨 We must not execute any checked out code here.
workflow_run: # zizmor: ignore[dangerous-triggers]
workflows: ["Tests"]
types:
- completed
concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: true
permissions: {}
jobs:
sonarqube:
name: 🩻 SonarQube
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event != 'merge_group'
uses: matrix-org/matrix-js-sdk/.github/workflows/sonarcloud.yml@develop # zizmor: ignore[unpinned-uses]
permissions:
actions: read
statuses: write
id-token: write # sonar
secrets:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
with:
sharded: true
version-pkg-json-dir: ./apps/web
-133
View File
@@ -1,133 +0,0 @@
name: Static Analysis
on:
pull_request: {}
push:
branches: [develop, master]
merge_group:
types: [checks_requested]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
env:
# This must be set for fetchdep.sh to get the right branch
PR_NUMBER: ${{ github.event.pull_request.number }}
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
permissions: {} # No permissions required
jobs:
lint:
strategy:
fail-fast: false
matrix:
include:
- name: Typescript Syntax Check
install: layered
command: "lint:types"
- name: Prettier
install: normal
command: "lint:prettier"
- name: ESLint
install: normal
command: "lint:js"
- name: Style Lint
install: normal
command: "lint:style"
- name: Workflow Lint
install: normal
command: "lint:workflows"
- name: Analyse Dead Code
install: normal
command: "lint:knip"
- name: Rethemendex Check
command: "rethemendex"
assert-diff: true
- name: Docs
install: layered
command: "docs:build"
name: ${{ matrix.name }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
if: matrix.install != ''
with:
cache: "pnpm"
node-version: "lts/*"
- name: Install Dependencies (layered)
if: matrix.install == 'layered'
run: "./scripts/layered.sh"
- name: Install Dependencies (normal)
if: matrix.install == 'normal'
run: "pnpm install --frozen-lockfile"
- name: Run ${{ matrix.command }}
run: pnpm --if-present run "$CMD" && pnpm -r --if-present run "$CMD"
env:
CMD: ${{ matrix.command }}
- name: Assert no changes
run: git diff --exit-code
if: matrix.assert-diff
zizmor:
name: Zizmor Github Actions lint
runs-on: ubuntu-24.04
permissions:
security-events: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- name: Run zizmor
uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3
i18n:
strategy:
fail-fast: false
matrix:
include:
- name: Element Web
path: "apps/web"
allowed-hardcoded-keys: |
console_dev_note
labs|element_call_video_rooms
labs|feature_disable_call_per_sender_encryption
voip|element_call
error|invalid_json
error|misconfigured
welcome|title_element
devtools|settings|elementCallUrl
labs|sliding_sync_description
settings|voip|noise_suppression_description
settings|voip|echo_cancellation_description
- name: Element Desktop
path: "apps/desktop"
- name: Shared Components
path: "packages/shared-components"
name: "i18n Check (${{ matrix.name }})"
uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@6eda3835118f3bc3fb658a1a3c20b7da9d16ae42
permissions:
pull-requests: read
with:
hardcoded-words: "Element"
packageManager: pnpm
path: ${{ matrix.path }}
allowed-hardcoded-keys: ${{ matrix.allowed-hardcoded-keys }}
# Dummy job to simplify branch protections
ci:
name: Static Analysis
needs: [lint, i18n, zizmor]
if: always()
runs-on: ubuntu-24.04
steps:
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1
-24
View File
@@ -1,24 +0,0 @@
name: Sync labels
on:
workflow_dispatch: {}
schedule:
- cron: "0 1 * * *" # 1am every day
push:
branches:
- develop
paths:
- .github/labels.yml
permissions: {} # We use ELEMENT_BOT_TOKEN instead
jobs:
sync-labels:
uses: element-hq/element-meta/.github/workflows/sync-labels.yml@7f2f93fb9b52ece7a0998f60e64862aa203c1746
with:
LABELS: |
element-hq/element-meta
.github/labels.yml
DELETE: true
WET: true
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
-188
View File
@@ -1,188 +0,0 @@
name: Tests
on:
pull_request: {}
merge_group:
types: [checks_requested]
push:
branches: [develop, master]
workflow_call:
inputs:
disable_coverage:
type: boolean
required: false
description: "Specify true to skip generating and uploading coverage for tests"
matrix-js-sdk-sha:
type: string
required: false
description: "The matrix-js-sdk SHA to use"
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: true
env:
ENABLE_COVERAGE: ${{ github.event_name != 'merge_group' && inputs.disable_coverage != 'true' }}
# fetchdep.sh needs to know our PR number
PR_NUMBER: ${{ github.event.pull_request.number }}
NX_DEFAULT_OUTPUT_STYLE: stream-without-prefixes
permissions: {}
jobs:
jest_ew:
name: Jest (Element Web)
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
# Run multiple instances in parallel to speed up the tests
runner: [1, 2]
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- name: pnpm cache
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: "lts/*"
cache: "pnpm"
- name: Install Deps
run: "./scripts/layered.sh"
env:
JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
- name: Jest Cache
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: /tmp/jest_cache
key: ${{ hashFiles('**/pnpm-lock.yaml') }}
- name: Get number of CPU cores
id: cpu-cores
uses: SimenB/github-actions-cpu-cores@97330871fe1b7d3529392ea000e3d2c4b357e403 # v3
- name: Run tests
working-directory: apps/web
run: |
pnpm test \
--coverage=$ENABLE_COVERAGE \
--ci \
--max-workers $MAX_WORKERS \
--shard "$SHARD" \
--cacheDirectory /tmp/jest_cache
env:
JEST_SONAR_UNIQUE_OUTPUT_NAME: true
# tell jest to use coloured output
FORCE_COLOR: true
MAX_WORKERS: ${{ steps.cpu-cores.outputs.count }}
SHARD: ${{ format('{0}/{1}', matrix.runner, strategy.job-total) }}
- name: Move coverage files into place
if: env.ENABLE_COVERAGE == 'true'
working-directory: apps/web
run: mv coverage/lcov.info coverage/$NODE_VERSION-${{ matrix.runner }}.lcov.info
env:
NODE_VERSION: ${{ steps.setupNode.outputs.node-version }}
- name: Upload Artifact
if: env.ENABLE_COVERAGE == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: coverage-jest-${{ matrix.runner }}
path: |
apps/web/coverage
!apps/web/coverage/lcov-report
complete:
name: jest-tests
needs: [jest_ew, vitest]
if: always()
runs-on: ubuntu-24.04
permissions:
statuses: write
steps:
- if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
run: exit 1
- name: Skip SonarCloud in merge queue
if: github.event_name == 'merge_group' || inputs.disable_coverage == 'true'
uses: guibranco/github-status-action-v2@9bfa8773cdbdc6c185747fd43cd7faa9d7c32f09
with:
authToken: ${{ secrets.GITHUB_TOKEN }}
state: success
description: SonarCloud skipped
context: SonarCloud Code Analysis
sha: ${{ github.sha }}
target_url: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
vitest:
name: Vitest
strategy:
matrix:
path:
- apps/desktop
- packages/shared-components
- packages/module-api
runs-on: ubuntu-24.04
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- name: pnpm cache
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
node-version: "lts/*"
cache: "pnpm"
- name: Install Deps
run: "pnpm install"
- name: Cache storybook & vitest
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5
with:
path: |
${{ matrix.path }}/node_modules/.cache
${{ matrix.path }}/node_modules/.vite/vitest
key: ${{ matrix.path }}-${{ hashFiles('pnpm-lock.yaml') }}
- name: Setup playwright
uses: ./.github/actions/setup-playwright
if: matrix.path == 'packages/shared-components'
with:
write-cache: ${{ github.event_name != 'merge_group' }}
- name: Run tests
working-directory: ${{ matrix.path }}
run: pnpm test:unit --coverage=$ENABLE_COVERAGE
# Dump the disk usage on failure, because this job seems to fail with disk fills sometimes
- name: df
run: df -h && df -i
if: ${{ failure() }}
- name: Calculate artifact name
if: env.ENABLE_COVERAGE == 'true'
id: artifact
run: |
NAME=$(basename "$MATRIX_PATH")
echo "name=$NAME" >> $GITHUB_OUTPUT
env:
MATRIX_PATH: ${{ matrix.path }}
- name: Upload Artifact
if: env.ENABLE_COVERAGE == 'true'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: coverage-${{ steps.artifact.outputs.name }}
path: |
${{ matrix.path }}/coverage
!${{ matrix.path }}/coverage/lcov-report
-21
View File
@@ -1,21 +0,0 @@
name: Move issued assigned to specific team members to their boards
on:
issues:
types: [assigned]
permissions: {} # We use ELEMENT_BOT_TOKEN instead
jobs:
web-app-team:
runs-on: ubuntu-24.04
if: |
contains(github.event.issue.assignees.*.login, 't3chguy') ||
contains(github.event.issue.assignees.*.login, 'florianduros') ||
contains(github.event.issue.assignees.*.login, 'dbkr') ||
contains(github.event.issue.assignees.*.login, 'MidhunSureshR')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: https://github.com/orgs/element-hq/projects/67
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
-16
View File
@@ -1,16 +0,0 @@
name: Move new issues into Issue triage board
on:
issues:
types: [opened]
permissions: {} # We use ELEMENT_BOT_TOKEN instead
jobs:
automate-project-columns:
runs-on: ubuntu-24.04
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: https://github.com/orgs/element-hq/projects/120
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
-178
View File
@@ -1,178 +0,0 @@
name: Move labelled issues to correct projects
on:
issues:
types: [labeled]
workflow_call:
secrets:
ELEMENT_BOT_TOKEN:
required: true
permissions: {} # We use ELEMENT_BOT_TOKEN instead
jobs:
apply_Z-Labs_label:
name: Add Z-Labs label for features behind labs flags
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'A-Maths') ||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
contains(github.event.issue.labels.*.name, 'A-Jump-To-Date ') ||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
contains(github.event.issue.labels.*.name, 'A-Tags') ||
contains(github.event.issue.labels.*.name, 'A-Video-Rooms') ||
contains(github.event.issue.labels.*.name, 'A-Message-Starring') ||
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor') ||
contains(github.event.issue.labels.*.name, 'A-Element-Call')
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['Z-Labs']
})
apply_Help-Wanted_label:
name: Add "Help Wanted" label to all "good first issue" and Hacktoberfest
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'good first issue') ||
contains(github.event.issue.labels.*.name, 'Hacktoberfest')
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
github.rest.issues.addLabels({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
labels: ['Help Wanted']
})
move_needs_info_issues:
name: X-Needs-Info issues to Need info column on triage board
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'X-Needs-Info')
steps:
- id: add_to_project
uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: ${{ env.PROJECT_URL }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
- id: set_fields
uses: titoportas/update-project-fields@421a54430b3cdc9eefd8f14f9ce0142ab7678751 # v0.1.0
with:
project-url: ${{ env.PROJECT_URL }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
item-id: ${{ steps.add_to_project.outputs.itemId }} # Use the item-id output of the previous step
field-keys: Status
field-values: "Needs info"
env:
PROJECT_URL: https://github.com/orgs/element-hq/projects/120
move_flakey_test_issues:
name: Z-Flaky-Test issues to Sized for maintainer column on triage board
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'Z-Flaky-Test')
steps:
- id: add_to_project
uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: ${{ env.PROJECT_URL }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
- id: set_fields
uses: titoportas/update-project-fields@421a54430b3cdc9eefd8f14f9ce0142ab7678751 # v0.1.0
with:
project-url: ${{ env.PROJECT_URL }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
item-id: ${{ steps.add_to_project.outputs.itemId }} # Use the item-id output of the previous step
field-keys: Status
field-values: "Sized for maintainer"
env:
PROJECT_URL: https://github.com/orgs/element-hq/projects/120
add_priority_design_issues_to_project:
name: P1 X-Needs-Design to Design project board
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
(contains(github.event.issue.labels.*.name, 'S-Critical') &&
(contains(github.event.issue.labels.*.name, 'O-Frequent') ||
contains(github.event.issue.labels.*.name, 'O-Occasional')) ||
contains(github.event.issue.labels.*.name, 'S-Major') &&
contains(github.event.issue.labels.*.name, 'O-Frequent') ||
contains(github.event.issue.labels.*.name, 'A11y'))
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: https://github.com/orgs/element-hq/projects/18
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
add_product_issues:
name: X-Needs-Product to product project board
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'X-Needs-Product')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: https://github.com/orgs/element-hq/projects/28
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
Search_issues_to_board:
name: Search issues to project board
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'A-New-Search-Experience')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: https://github.com/orgs/element-hq/projects/48
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
voip:
name: Add labelled issues to VoIP project board
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'Team: VoIP')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: https://github.com/orgs/element-hq/projects/41
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
crypto:
name: Add labelled issues to Crypto project
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'Team: Crypto')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: https://github.com/orgs/element-hq/projects/76
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
tech_debt:
name: Add labelled issues to tech debt project
runs-on: ubuntu-24.04
if: >
contains(github.event.issue.labels.*.name, 'A-Developer-Experience') ||
contains(github.event.issue.labels.*.name, 'A-Documentation') ||
contains(github.event.issue.labels.*.name, 'A-Packaging') ||
contains(github.event.issue.labels.*.name, 'A-Technical-Debt') ||
contains(github.event.issue.labels.*.name, 'A-Testing') ||
contains(github.event.issue.labels.*.name, 'Z-Flaky-Test')
steps:
- uses: actions/add-to-project@5afcf98fcd03f1c2f92c3c83f58ae24323cc57fd # v2.0.0
with:
project-url: https://github.com/orgs/element-hq/projects/101
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
@@ -1,140 +0,0 @@
name: Move pull requests asking for review to the relevant project
on:
pull_request_target:
types: [review_requested]
permissions: {} # Uses ELEMENT_BOT_TOKEN instead
jobs:
add_design_pr_to_project:
name: Move PRs asking for design review to the design board
runs-on: ubuntu-24.04
steps:
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: find_team_members
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
query find_team_members($team: String!) {
organization(login: "element-hq") {
team(slug: $team) {
members {
nodes {
login
}
}
}
}
}
team: ${{ env.TEAM }}
env:
TEAM: "design"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
- id: any_matching_reviewers
run: |
# Fetch requested reviewers, and people who are on the team
echo '${{ tojson(fromjson(steps.find_team_members.outputs.data).organization.team.members.nodes[*].login) }}' | tee /tmp/team_members.json
echo '${{ tojson(github.event.pull_request.requested_reviewers[*].login) }}' | tee /tmp/reviewers.json
jq --raw-output .[] < /tmp/team_members.json | sort | tee /tmp/team_members.txt
jq --raw-output .[] < /tmp/reviewers.json | sort | tee /tmp/reviewers.txt
# Fetch requested team reviewers, and the name of the team
echo '${{ tojson(github.event.pull_request.requested_teams[*].slug) }}' | tee /tmp/team_reviewers.json
jq --raw-output .[] < /tmp/team_reviewers.json | sort | tee /tmp/team_reviewers.txt
echo '${{ env.TEAM }}' | tee /tmp/team.txt
# If either a reviewer matches a team member, or a team matches our team, say "true"
if [ $(join /tmp/team_members.txt /tmp/reviewers.txt | wc -l) != 0 ]; then
echo "match=true" >> $GITHUB_OUTPUT
elif [ $(join /tmp/team.txt /tmp/team_reviewers.txt | wc -l) != 0 ]; then
echo "match=true" >> $GITHUB_OUTPUT
else
echo "match=false" >> $GITHUB_OUTPUT
fi
env:
TEAM: "design"
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: add_to_project
if: steps.any_matching_reviewers.outputs.match == 'true'
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:ID!, $contentid:ID!) {
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
item {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.pull_request.node_id }}
env:
PROJECT_ID: "PVT_kwDOAM0swc0sUA"
TEAM: "design"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
add_product_pr_to_project:
name: Move PRs asking for design review to the design board
runs-on: ubuntu-24.04
steps:
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: find_team_members
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
query find_team_members($team: String!) {
organization(login: "element-hq") {
team(slug: $team) {
members {
nodes {
login
}
}
}
}
}
team: ${{ env.TEAM }}
env:
TEAM: "product"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
- id: any_matching_reviewers
run: |
# Fetch requested reviewers, and people who are on the team
echo '${{ tojson(fromjson(steps.find_team_members.outputs.data).organization.team.members.nodes[*].login) }}' | tee /tmp/team_members.json
echo '${{ tojson(github.event.pull_request.requested_reviewers[*].login) }}' | tee /tmp/reviewers.json
jq --raw-output .[] < /tmp/team_members.json | sort | tee /tmp/team_members.txt
jq --raw-output .[] < /tmp/reviewers.json | sort | tee /tmp/reviewers.txt
# Fetch requested team reviewers, and the name of the team
echo '${{ tojson(github.event.pull_request.requested_teams[*].slug) }}' | tee /tmp/team_reviewers.json
jq --raw-output .[] < /tmp/team_reviewers.json | sort | tee /tmp/team_reviewers.txt
echo '${{ env.TEAM }}' | tee /tmp/team.txt
# If either a reviewer matches a team member, or a team matches our team, say "true"
if [ $(join /tmp/team_members.txt /tmp/reviewers.txt | wc -l) != 0 ]; then
echo "match=true" >> $GITHUB_OUTPUT
elif [ $(join /tmp/team.txt /tmp/team_reviewers.txt | wc -l) != 0 ]; then
echo "match=true" >> $GITHUB_OUTPUT
else
echo "match=false" >> $GITHUB_OUTPUT
fi
env:
TEAM: "product"
- uses: octokit/graphql-action@ddde8ebb2493e79f390e6449c725c21663a67505 # v3.0.2
id: add_to_project
if: steps.any_matching_reviewers.outputs.match == 'true'
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:ID!, $contentid:ID!) {
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
item {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.pull_request.node_id }}
env:
PROJECT_ID: "PVT_kwDOAM0swc4AAg6N"
TEAM: "product"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
-29
View File
@@ -1,29 +0,0 @@
name: Close stale issues & PRs
on:
workflow_dispatch: {}
schedule:
- cron: "30 1 * * *"
permissions: {}
jobs:
close:
runs-on: ubuntu-24.04
permissions:
actions: write
issues: write
pull-requests: write
steps:
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10
with:
operations-per-run: 100
# Flaky test issue closing
any-of-issue-labels: "Z-Flaky-Test-Chrome,Z-Flaky-Test-Firefox,Z-Flaky-Test-Webkit"
days-before-issue-stale: 14
days-before-issue-close: 0
close-issue-message: "This flaky test issue has not been updated in 14 days. It is being closed as presumed resolved."
exempt-issue-labels: "Z-Flaky-Test-Disabled"
# Stale PR closing
days-before-pr-stale: 180
days-before-pr-close: 0
close-pr-message: "This PR has been automatically closed because it has been stale for 180 days. If you wish to continue working on this PR, please ping a maintainer to reopen it."
-54
View File
@@ -1,54 +0,0 @@
name: Move unlabelled from needs info columns to triaged
on:
issues:
types: [unlabeled]
permissions: {}
jobs:
move_no_longer_needs_info_issues:
name: Move no longer X-Needs-Info issues to Triaged
runs-on: ubuntu-24.04
if: >
!contains(github.event.issue.labels.*.name, 'X-Needs-Info')
steps:
- id: set_fields
uses: nipe0324/update-project-v2-item-field@c4af58452d1c5a788c1ea4f20e073fa722ec4a6b #v2.0.2
with:
project-url: ${{ env.PROJECT_URL }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
skip-update-script: |
const isIssue = item.type === 'ISSUE'
const status = item.fieldValues['Status']
return !isIssue || status !== 'Needs info'
field-name: Status
field-value: "Triaged"
env:
PROJECT_URL: https://github.com/orgs/element-hq/projects/120
remove_Z-Labs_label:
name: Remove Z-Labs label when features behind labs flags are removed
runs-on: ubuntu-24.04
if: >
!(contains(github.event.issue.labels.*.name, 'A-Maths') ||
contains(github.event.issue.labels.*.name, 'A-Message-Pinning') ||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
contains(github.event.issue.labels.*.name, 'Z-IA') ||
contains(github.event.issue.labels.*.name, 'A-Jump-To-Date') ||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
contains(github.event.issue.labels.*.name, 'A-Tags') ||
contains(github.event.issue.labels.*.name, 'A-Video-Rooms') ||
contains(github.event.issue.labels.*.name, 'A-Message-Starring') ||
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor') ||
contains(github.event.issue.labels.*.name, 'A-Element-Call')) &&
contains(github.event.issue.labels.*.name, 'Z-Labs')
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
with:
script: |
github.rest.issues.removeLabel({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
name: ['Z-Labs']
})
-37
View File
@@ -1,37 +0,0 @@
# Re-fetches the Jitsi SDK and opens a PR to update it if it's different from what's in the repository
name: Update Jitsi
on:
workflow_dispatch: {}
schedule:
- cron: "0 3 * * 0" # 3am every Sunday
permissions: {} # We use ELEMENT_BOT_TOKEN instead
jobs:
update:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
persist-credentials: false
- uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v5
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with:
cache: "pnpm"
node-version: "lts/*"
- name: Install Deps
run: "pnpm install --frozen-lockfile"
- name: Fetch Jitsi
working-directory: apps/web
run: "pnpm vendor:jitsi"
- name: Create Pull Request
uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
branch: actions/jitsi-update
delete-branch: true
title: Jitsi Update
labels: |
T-Task
-119
View File
@@ -1,119 +0,0 @@
name: Update release topics
on:
workflow_dispatch:
inputs:
expected_status:
description: What type of release is the next expected release
required: true
default: RC
type: choice
options:
- RC
- Release
expected_date:
description: Expected release date e.g. July 11th
required: true
type: string
concurrency: ${{ github.workflow }}
permissions: {} # No permissions required
jobs:
bot:
name: Release topic update
runs-on: ubuntu-24.04
environment: Matrix
steps:
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
HS_URL: ${{ secrets.BETABOT_HS_URL }}
LOBBY_ROOM_ID: ${{ secrets.ROOM_ID }}
PUBLIC_DISCUSSION_ROOM_ID: "!xUW4PpAe1CmThA3r2wI8IrgwwsK006-zqWdJCljpd10"
ANNOUNCEMENT_ROOM_ID: "!ars5ndgI6IIYZXECiJ-u8YljHNzShJn3nHdB-3rYI2M"
TOKEN: ${{ secrets.BETABOT_ACCESS_TOKEN }}
RELEASE_STATUS: "Release status: ${{ inputs.expected_status }} expected ${{ inputs.expected_date }}"
with:
script: |
const { HS_URL, TOKEN, RELEASE_STATUS, LOBBY_ROOM_ID, PUBLIC_DISCUSSION_ROOM_ID, ANNOUNCEMENT_ROOM_ID } = process.env;
const repo = context.repo;
const { data } = await github.rest.repos.getLatestRelease({
owner: repo.owner,
repo: repo.repo,
});
console.log("Found latest version: " + data.tag_name);
const releaseTopic = `Stable: ${data.tag_name} | ${RELEASE_STATUS}`;
console.log("Release topic: " + releaseTopic);
const regex = /Stable: v(.+) \| Release status: (\w+) expected (\w+ \d+\w\w)/gm;
async function updateReleaseInTopic(roomId) {
const apiUrl = `${HS_URL}/_matrix/client/v3/rooms/${roomId}/state/m.room.topic/`;
const headers = {
"Content-Type": "application/json",
"Authorization": `Bearer ${TOKEN}`,
};
await fetch(`${HS_URL}/_matrix/client/v3/rooms/${roomId}/join`, {
method: "POST",
headers,
body: "{}",
});
let res = await fetch(apiUrl, {
method: "GET",
headers,
});
if (!res.ok) {
console.log(roomId, "failed to fetch", await res.text());
return;
}
const data = await res.json();
console.log(roomId, "got event", data);
if (!regex.test(data.topic)) {
core.setFailed("Topic format is incorrect for room " + roomId);
return;
}
const topic = data.topic.replace(regex, releaseTopic);
if (topic === data.topic) {
console.log(roomId, "nothing to do");
return;
}
if (data["org.matrix.msc3765.topic"]) {
data["org.matrix.msc3765.topic"]?.["m.text"].forEach(d => {
d.body = d.body.replace(regex, releaseTopic);
});
}
if (data["m.topic"]) {
data["m.topic"]?.["m.text"].forEach(d => {
d.body = d.body.replace(regex, releaseTopic);
});
}
res = await fetch(apiUrl, {
method: "PUT",
body: JSON.stringify({
...data,
topic,
}),
headers,
});
if (res.ok) {
const resJson = res.json();
if (resJson.errcode) {
core.setFailed(`Error updating ${roomId}: ${resJson.error}`);
} else {
console.log(roomId, "topic updated:", topic);
}
} else {
const errText = await res.text();
core.setFailed(`Error updating ${roomId}: ${errText}`);
}
}
await updateReleaseInTopic(LOBBY_ROOM_ID);
await updateReleaseInTopic(PUBLIC_DISCUSSION_ROOM_ID);
await updateReleaseInTopic(ANNOUNCEMENT_ROOM_ID);
+274
View File
@@ -0,0 +1,274 @@
# GitLab-CI fuer den ThreadNet-Web-Fork (Lab-GitLab: git.lab).
# Ersetzt die frueheren GitHub/Gitea-Actions-Workflows - Hintergrund: docs/axion1337-fork.md.
#
# Erkenntnisse aus den Gitea-CI-Versuchen (2026-07-30), hier eingeflossen:
# - kein scripts/layered.sh: wuerde den gepinnten matrix-js-sdk-Stand aus pnpm-lock.yaml
# mit Upstream-develop ueberschreiben (und braucht jq) -> frozen-lockfile-Install
# - webpack braucht ~4 GB Heap -> NODE_OPTIONS
# - Desktop-Build laeuft im dockerbuild-Image (rust:bullseye + node, glibc-2.31-Ziel)
stages:
- build
- package
# --- Windows-Runner-VM-Steuerung (Issue #5) ---------------------------------
# Die Windows-Build-VM (dockur/windows-Fork, siehe git.lab/axion1337.chat/vendor/windows,
# Runbook: docs/axion-runner.md dort) laeuft on-demand auf Overmind. Diese beiden Jobs
# starten/stoppen sie vom Linux-Runner aus ueber den Docker-Socket - der
# desktop_windows-Job wartet danach einfach in der Queue, bis der Runner online ist.
start_windows_vm:
stage: build
image: docker:27-cli
rules:
- if: $CI_COMMIT_TAG =~ /^v/
when: manual
allow_failure: true
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
script:
- docker start windows-runner
stop_windows_vm:
stage: package
image: docker:27-cli
rules:
- if: $CI_COMMIT_TAG =~ /^v/
when: manual
allow_failure: true
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
script:
- docker stop windows-runner
# CVE-Scan des eigenen Prod-Images (gitops#31): woechentlich per Schedule und
# manuell triggerbar. Bricht bewusst nicht hart ab (allow_failure) - Funde landen
# als Artifact + Job-Log, die Bewertung bleibt menschlich. Lab-interne Images
# (desktop-build, windows-vm) brauchen erst CA-Trust im Trivy-Container - notiert
# in gitops#31 als Ausbaustufe.
trivy_scan:
stage: build
image:
name: aquasec/trivy:0.58.2
entrypoint: [""]
rules:
- if: $CI_PIPELINE_SOURCE == "schedule"
allow_failure: true
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
variables:
GIT_STRATEGY: none
TRIVY_NO_PROGRESS: "true"
script:
- trivy image --severity HIGH,CRITICAL --format table --output trivy-web.txt rohana.axion1337.de/sorb/threadnet-web:latest-ci
- grep -E "Total|CRITICAL|HIGH" trivy-web.txt | head -20 || true
artifacts:
paths:
- trivy-web.txt
expire_in: 30 days
when: always
web:
stage: build
image: node:24-bullseye
rules:
# Release-Tags bauen immer (docker_web/desktop_* brauchen web als needs)
- if: $CI_COMMIT_TAG =~ /^v/
# Scheduled Pipelines gehoeren dem trivy_scan - kein wochentlicher Leerbuild
- if: $CI_PIPELINE_SOURCE == "schedule"
when: never
# Pushes nur, wenn build-relevante Pfade betroffen sind - Doku-Commits kosten nichts
- if: $CI_COMMIT_BRANCH
changes:
- apps/**/*
- packages/**/*
- patches/**/*
- scripts/**/*
- pnpm-lock.yaml
- pnpm-workspace.yaml
- .gitlab-ci.yml
variables:
NODE_OPTIONS: "--max-old-space-size=6144"
CI_PACKAGE: "true"
before_script:
- corepack enable
script:
- pnpm install --frozen-lockfile
- cp apps/web/element.io/develop/config.json apps/web/config.json
- VERSION=$(scripts/get-version-from-git.sh) pnpm --dir apps/web build
artifacts:
paths:
- apps/web/webapp
expire_in: 1 day
# Baut das kanonische Web-Image (apps/web/Dockerfile, Kontext = Monorepo-Root) und pusht
# es in die rohana-Registry, aus der Flux/k8s zieht. Deploy bleibt ein manueller Tag-Bump
# im gitops-Repo. Bewusster Doppel-Build (webpack laeuft im web-Job UND im Dockerfile) -
# kanonisch/reproduzierbar vor schnell; Optimierung als Folgearbeit in Issue #2.
docker_web:
stage: package
image: docker:27-cli
needs:
- job: web
artifacts: false
rules:
# Nur bei Release-Tags (v*) - "releasen" ist ein bewusster Akt:
# Tag pushen -> Image entsteht -> Tag-Bump im gitops-Repo deployt es
- if: $CI_COMMIT_TAG =~ /^v/
variables:
IMAGE: rohana.axion1337.de/sorb/threadnet-web
DOCKER_BUILDKIT: "1"
script:
- echo "$REGISTRY_PASSWORD" | docker login rohana.axion1337.de -u "$REGISTRY_USER" --password-stdin
- docker build -f apps/web/Dockerfile -t "$IMAGE:$CI_COMMIT_TAG" -t "$IMAGE:latest-ci" .
- docker push "$IMAGE:$CI_COMMIT_TAG"
- docker push "$IMAGE:latest-ci"
# Einmalig/selten: Build-Image fuer den Desktop-Build (rust:bullseye + node + tcl/sqlcipher,
# aus apps/desktop/dockerbuild). Manuell ausloesen, wenn sich .node-version oder das
# Dockerfile aendert.
desktop_image:
stage: package
image: docker:27-cli
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
variables:
# Lab-Registry statt rohana (OVERMIND-01): Konsument ist nur die Lab-CI selbst
IMAGE: $CI_REGISTRY_IMAGE/desktop-build
script:
- echo "$CI_JOB_TOKEN" | docker login "$CI_REGISTRY" -u gitlab-ci-token --password-stdin
- docker build -f apps/desktop/dockerbuild/Dockerfile -t "$IMAGE:bullseye" apps/desktop
- docker push "$IMAGE:bullseye"
# Electron-Linux-Build (amd64, static sqlcipher) - repliziert den am 2026-07-29 manuell
# verifizierten Build-Weg. Automatisch bei Release-Tags, auf main manuell triggerbar.
desktop_linux:
stage: package
image: registry.git.lab/axion1337.chat/threadnet-web/desktop-build:bullseye
needs:
- job: web
artifacts: true
rules:
- if: $CI_COMMIT_TAG =~ /^v/
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
variables:
MAX_GLIBC: "2.31"
USE_HARD_LINKS: "false"
SQLCIPHER_BUNDLED: "1"
# Eigene Build-Variante: Produktname ThreadNet statt Element. Ohne das nutzt
# electron-builder element.io/release/build.json und die Pakete heissen weiter
# "element-desktop" (real passiert 2026-08-02).
VARIANT_PATH: axion1337/build.json
script:
- pnpm install --frozen-lockfile --filter element-desktop
- cp -r apps/web/webapp apps/desktop/webapp
# Produktions-Client-Config (getrackt seit diesem Commit, vorher nur im manuellen Build)
- cp apps/desktop/axion1337/config.json apps/desktop/webapp/config.json
- cd apps/desktop
- pnpm run asar-webapp
- pnpm run build:native
# pnpm/npm setzen beim Install kein Executable-Bit auf 7za - bekannter Fix,
# gleicher Schritt wie in der Upstream-CI ("Fix permissions")
- chmod +x ../../node_modules/7zip-bin/linux/*/7za || true
- pnpm run build --publish never -l tar.gz -l deb
artifacts:
paths:
- apps/desktop/dist/*.deb
- apps/desktop/dist/*.tar.gz
expire_in: 1 week
# Windows-Desktop-Build (x64, unsigniert - Signing siehe Issue #5). Laeuft NUR auf dem
# Windows-Runner (tags), manuell: vorher start_windows_vm ausloesen. Uebersetzt aus dem
# Upstream-Workflow build_desktop_windows.yaml (x64-Pfad; arm-only-Schritte entfallen).
desktop_windows:
stage: package
tags:
- windows
needs:
- job: web
artifacts: true
rules:
- if: $CI_COMMIT_TAG =~ /^v/
when: manual
allow_failure: true
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
variables:
SQLCIPHER_BUNDLED: "1"
ELECTRON_CACHE: C:\electron-cache
ELECTRON_BUILDER_CACHE: C:\eb-cache
# Eigene Build-Variante (Produktname ThreadNet) - siehe desktop_linux
VARIANT_PATH: axion1337/build.json
script:
- pnpm install --frozen-lockfile --filter element-desktop
# GitHub-CDN-Downloads (Electron-Binary, winCodeSign, NSIS) reissen im Gast
# reproduzierbar ab und app-builder kann nicht fortsetzen (Jobs 415/416/424) -
# das Skript laedt alles resumefaehig in die persistenten Caches vor.
- powershell -ExecutionPolicy Bypass -File apps\desktop\axion1337\prefetch-buildcache.ps1
- Copy-Item -Recurse apps/web/webapp apps/desktop/webapp
- Copy-Item apps/desktop/axion1337/config.json apps/desktop/webapp/config.json -Force
- cd apps/desktop
- pnpm run asar-webapp
- '& "C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\Common7\Tools\Launch-VsDevShell.ps1" -Arch amd64 -SkipAutomaticLocation'
- pnpm run build:native --target x86_64-pc-windows-msvc
- pnpm run build --publish never -w nsis
artifacts:
paths:
- apps/desktop/dist/*.exe
expire_in: 1 week
# Idempotente Werkzeug-Provisionierung im Windows-Gast (laeuft als SYSTEM ueber den
# Runner selbst - kein noVNC/RDP noetig). Hintergrund: choco akzeptiert --version nicht
# bei Mehrfach-Paketen, der urspruengliche Runbook-Einzeiler hat still nichts installiert.
# Startet am Ende den Runner-Dienst verzoegert neu, damit der neue PATH fuer
# Folge-Jobs greift (der Job selbst wird davon nicht mehr unterbrochen).
windows_provision:
stage: build
tags:
- windows
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
allow_failure: true
variables:
# Kein Checkout - der braeuchte git, das dieser Job erst installiert (Henne-Ei)
GIT_STRATEGY: none
script:
- choco install -y git
- choco install -y nasm
- choco install -y magicsplat-tcl-tk
- choco install -y nodejs --version=24.15.0
# Fuer den seshat-hak-Build (hak/matrix-seshat/check.ts): perl+patch (Strawberry
# liefert beides, patch.exe in C:\Strawberry\c\bin), python (node-gyp), rustc.
- choco install -y strawberryperl
- choco install -y python3
# Rust maschinenweit statt Benutzerprofil: der Runner-Dienst laeuft als SYSTEM und
# sieht nur die Maschinen-Umgebung - ein Default-rustup unter %USERPROFILE% waere
# fuer Jobs unsichtbar.
- $env:RUSTUP_HOME='C:\Rust\rustup'; $env:CARGO_HOME='C:\Rust\cargo'; Invoke-WebRequest -UseBasicParsing https://win.rustup.rs/x86_64 -OutFile "$env:TEMP\rustup-init.exe"
- '& "$env:TEMP\rustup-init.exe" -y --profile minimal --default-toolchain stable --default-host x86_64-pc-windows-msvc'
- '[Environment]::SetEnvironmentVariable(''RUSTUP_HOME'',''C:\Rust\rustup'',''Machine'')'
- '[Environment]::SetEnvironmentVariable(''CARGO_HOME'',''C:\Rust\cargo'',''Machine'')'
# choco-nasm traegt sich nicht selbst in den PATH ein (Upstream macht das auch
# explizit via GITHUB_PATH) - zusammen mit cargo\bin hier maschinenweit nachziehen
- $mp=[Environment]::GetEnvironmentVariable('Path','Machine'); if ($mp -notlike '*C:\Rust\cargo\bin*') { [Environment]::SetEnvironmentVariable('Path', "$mp;C:\Rust\cargo\bin;C:\Program Files\NASM", 'Machine') }
- refreshenv; corepack enable
- '& "C:\Program Files\Git\cmd\git.exe" --version'
- '& "C:\Program Files\nodejs\node.exe" --version'
- '& C:\Rust\cargo\bin\rustc.exe --version'
# Windows-260-Zeichen-Pfadlimit: die Visual-Baseline-Pfade des Monorepos sprengen
# MAX_PATH - beide Schalter noetig (git-seitig + OS-Policy)
- '& "C:\Program Files\Git\cmd\git.exe" config --system core.longpaths true'
- Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem' -Name LongPathsEnabled -Value 1 -Type DWord
# Dienst-Neustart als Scheduled Task: ueberlebt das Prozessbaum-Cleanup des Runners
# beim Job-Ende (der fruehere Start-Process-Trick wurde dabei gekillt und liess den
# Dienst gestoppt zurueck - Vorfall 2026-07-31, siehe ThreadNet-Web#5).
- schtasks /create /f /tn RunnerRestart /ru SYSTEM /sc once /st ((Get-Date).AddMinutes(1).ToString('HH:mm')) /tr "powershell -Command Restart-Service gitlab-runner -Force"
+6
View File
@@ -7,6 +7,12 @@
[![Vulnerabilities](https://sonarcloud.io/api/project_badges/measure?project=element-web&metric=vulnerabilities)](https://sonarcloud.io/summary/new_code?id=element-web)
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=element-web&metric=bugs)](https://sonarcloud.io/summary/new_code?id=element-web)
> **ThreadNet-Web**: this is a fork of Element Web, customized for the self-hosted
> [axion1337.chat](https://axion1337.chat) Matrix homeserver. See
> [docs/axion1337-fork.md](docs/axion1337-fork.md) for everything this fork changes versus
> upstream (Discord-style room list, client-side ClamAV content scanning, build fixes). The
> rest of this README describes upstream Element Web and is intentionally left as-is.
# Element
Element (formerly known as Vector and Riot) is a Matrix web & desktop client built using the [Matrix
+9
View File
@@ -0,0 +1,9 @@
{
"appId": "im.riot.app",
"name": "threadnet-desktop",
"productName": "ThreadNet",
"description": "ThreadNet — powered by Element",
"protocols": ["io.element.desktop", "element"],
"mac.icon": "build/icon.icns",
"dmg.badgeIcon": "build/icon.icns"
}
+653
View File
@@ -0,0 +1,653 @@
{
"brand": "aXion1337.Chat",
"branding": {
"auth_header_logo_url": "vector-icons/512.png",
"logo_link_url": "https://axion1337.chat"
},
"default_server_config": {
"m.homeserver": {
"base_url": "https://matrix.axion1337.chat",
"server_name": "axion1337.chat"
}
},
"default_theme": "aXion1337 Dark",
"element_call": {
"use_exclusively": true
},
"embedded_pages": {
"login_for_welcome": true
},
"features": {
"feature_element_call_video_rooms": true,
"feature_group_calls": true,
"feature_new_room_decoration_ui": true,
"feature_new_room_list": true,
"feature_qr_code_login": true,
"feature_video_rooms": true
},
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx",
"mobile_guide_app_variant": "element",
"setting_defaults": {
"UIFeature.deactivate": false,
"UIFeature.passwordReset": false,
"UIFeature.registration": false,
"custom_themes": [
{
"name": "aXion1337 Dark true",
"is_dark": true,
"colors": {
"accent-color": "#ffaf0f",
"primary-color": "#ffaf0f",
"secondary-color": "#ffaf0f"
}
},
{
"name": "Deep Purple",
"is_dark": true,
"colors": {
"accent-color": "#6503b3",
"primary-color": "#368bd6",
"warning-color": "#b30356",
"sidebar-color": "#15171B",
"roomlist-background-color": "#22262E",
"roomlist-text-color": "#A1B2D1",
"roomlist-text-secondary-color": "#EDF3FF",
"roomlist-highlights-color": "#343A46",
"roomlist-separator-color": "#a1b2d1",
"timeline-background-color": "#181b21",
"timeline-text-color": "#EDF3FF",
"timeline-text-secondary-color": "#A1B2D1",
"timeline-highlights-color": "#22262E"
}
},
{
"name": "Discord Dark",
"is_dark": true,
"colors": {
"accent-color": "#747ff4",
"accent": "#747ff4",
"primary-color": "#00aff4",
"warning-color": "#faa81ad9",
"alert": "#faa81ad9",
"sidebar-color": "#202225",
"roomlist-background-color": "#2f3136",
"roomlist-text-color": "#dcddde",
"roomlist-text-secondary-color": "#8e9297",
"roomlist-highlights-color": "#4f545c52",
"roomlist-separator-color": "#40444b",
"timeline-background-color": "#36393f",
"timeline-text-color": "#dcddde",
"secondary-content": "#dcddde",
"tertiary-content": "#dcddde",
"timeline-text-secondary-color": "#b9bbbe",
"timeline-highlights-color": "#04040512",
"reaction-row-button-selected-bg-color": "#4752c4",
"menu-selected-color": "#4752c4",
"focus-bg-color": "#4752c4",
"room-highlight-color": "#4752c4",
"other-user-pill-bg-color": "#4752c4",
"togglesw-off-color": "#72767d"
},
"compound": {
"--cpd-color-theme-bg": "#0019ff",
"--cpd-color-bg-canvas-default": "#2f3136",
"--cpd-color-bg-subtle-secondary": "#2f3136",
"--cpd-color-bg-subtle-primary": "#4f545c52",
"--cpd-color-bg-action-primary-rest": "#dcddde",
"--cpd-color-bg-action-secondary-rest": "#2f3136",
"--cpd-color-bg-critical-primary": "#fd3f3c",
"--cpd-color-bg-critical-subtle": "#745862",
"--cpd-color-bg-critical-hovered": "#fd3f3c",
"--cpd-color-bg-accent-rest": "#4cb387",
"--cpd-color-text-primary": "#dcddde",
"--cpd-color-text-secondary": "#b9bbbe",
"--cpd-color-text-action-accent": "#b9bbbe",
"--cpd-color-text-critical-primary": "#fd3f3c",
"--cpd-color-text-success-primary": "#4cb387",
"--cpd-color-icon-primary": "#dcddde",
"--cpd-color-icon-secondary": "#dcddde",
"--cpd-color-icon-tertiary": "#a7a0a7",
"--cpd-color-icon-accent-tertiary": "#4cb387",
"--cpd-color-border-interactive-primary": "#5d6064",
"--cpd-color-border-interactive-secondary": "#5d6064",
"--cpd-color-border-critical-primary": "#fd3f3c",
"--cpd-color-border-success-subtle": "#4cb387"
}
},
{
"name": "Electric Blue",
"is_dark": false,
"colors": {
"accent-color": "#3596fc",
"primary-color": "#368bd6",
"warning-color": "#ff4b55",
"sidebar-color": "#27303a",
"roomlist-background-color": "#f3f8fd",
"roomlist-text-color": "#2e2f32",
"roomlist-text-secondary-color": "#61708b",
"roomlist-highlights-color": "#ffffff",
"roomlist-separator-color": "#e3e8f0",
"timeline-background-color": "#ffffff",
"timeline-text-color": "#2e2f32",
"timeline-text-secondary-color": "#61708b",
"timeline-highlights-color": "#f3f8fd",
"username-colors": [
"#ff0000",
"#ff7f00",
"#ffff00",
"#00ff00",
"#0000ff",
"#4b0082",
"#9400d3",
"#ff1493"
],
"avatar-background-colors": [
"#cc0000",
"#cc6600",
"#cccc00",
"#00cc00",
"#0000cc",
"#3b0066",
"#7a00b3",
"#cc1077"
]
},
"compound": {
"--cpd-color-icon-accent-tertiary": "var(--cpd-color-blue-800)",
"--cpd-color-text-action-accent": "var(--cpd-color-blue-900)"
}
},
{
"name": "Everforest dark hard",
"is_dark": true,
"colors": {
"accent-color": "#a7c080",
"primary-color": "#a7c080",
"warning-color": "#e67e80",
"sidebar-color": "#323d43",
"roomlist-background-color": "#2f383e",
"roomlist-text-color": "#d3c6aa",
"roomlist-text-secondary-color": "#d3c6aa",
"roomlist-highlights-color": "#4b565c",
"roomlist-separator-color": "#4b565c",
"timeline-background-color": "#2b3339",
"timeline-text-color": "#d3c6aa",
"secondary-content": "#d3c6aa",
"tertiary-content": "#d3c6aa",
"timeline-text-secondary-color": "#a7c080",
"timeline-highlights-color": "#4b565c",
"reaction-row-button-selected-bg-color": "#4b565c"
}
},
{
"name": "aXion1337 Dark",
"is_dark": true,
"colors": {
"accent-color": "#bd93f9",
"primary-color": "#fe8019",
"warning-color": "#fb4934",
"sidebar-color": "#282828",
"roomlist-background-color": "#1d2021",
"roomlist-text-color": "#a89984",
"roomlist-text-secondary-color": "#00ff00",
"roomlist-highlights-color": "#00000030",
"roomlist-separator-color": "#4d4d4d90",
"timeline-background-color": "#282828",
"timeline-text-color": "#ebdbb2",
"secondary-content": "#928374",
"tertiary-content": "#928374",
"quinary-content": "#504945",
"timeline-text-secondary-color": "#a89984",
"timeline-highlights-color": "#00000030",
"reaction-row-button-selected-bg-color": "#689d6a",
"menu-selected-color": "#504945",
"icon-button-color": "#928374",
"accent": "#689d6a",
"alert": "#cc241d",
"username-colors": [
"#cc241d",
"#98971a",
"#d79921",
"#458588",
"#b16286",
"#689d6a",
"#a89984",
"#d65d0e"
]
}
},
{
"name": "aXion1337 Light",
"is_dark": false,
"colors": {
"accent-color": "#8f3f71",
"primary-color": "#af3a03",
"warning-color": "#9d0006",
"sidebar-color": "#ebdbb2",
"roomlist-background-color": "#f2e5bc",
"roomlist-text-color": "#665c54",
"roomlist-text-secondary-color": "#427b58",
"roomlist-highlights-color": "#00000012",
"roomlist-separator-color": "#bdae9330",
"timeline-background-color": "#fbf1c7",
"timeline-text-color": "#3c3836",
"secondary-content": "#7c6f64",
"tertiary-content": "#7c6f64",
"quinary-content": "#d5c4a1",
"timeline-text-secondary-color": "#665c54",
"timeline-highlights-color": "#00000012",
"reaction-row-button-selected-bg-color": "#8ec07c",
"menu-selected-color": "#d5c4a1",
"icon-button-color": "#7c6f64",
"accent": "#427b58",
"alert": "#9d0006",
"username-colors": [
"#9d0006",
"#79740e",
"#b57614",
"#076678",
"#8f3f71",
"#427b58",
"#665c54",
"#af3a03"
]
}
},
{
"name": "Ocean Depths",
"is_dark": false,
"colors": {
"accent-color": "#2d8b8b",
"accent": "#2d8b8b",
"primary-color": "#a8dadc",
"warning-color": "#457b9d",
"alert": "#70555a",
"sidebar-color": "#e3ebe0",
"roomlist-background-color": "#eaf2e7",
"roomlist-text-color": "#1a2332",
"roomlist-text-secondary-color": "#656e74",
"roomlist-highlights-color": "#00000010",
"roomlist-separator-color": "#0000001f",
"timeline-background-color": "#f1faee",
"timeline-text-color": "#1a2332",
"secondary-content": "#656e74",
"tertiary-content": "#656e74",
"timeline-text-secondary-color": "#656e74",
"timeline-highlights-color": "#00000010",
"reaction-row-button-selected-bg-color": "#99c8c1",
"menu-selected-color": "#b6d9d0",
"focus-bg-color": "#b6d9d0",
"room-highlight-color": "#b6d9d0",
"other-user-pill-bg-color": "#d4ede7",
"icon-button-color": "#656e74",
"username-colors": [
"#2d8b8b",
"#a8dadc",
"#457b9d",
"#276c70",
"#7da3a9",
"#365c78",
"#6ab2b4",
"#76aabc"
]
}
},
{
"name": "Sunset Boulevard",
"is_dark": true,
"colors": {
"accent-color": "#e76f51",
"accent": "#e76f51",
"primary-color": "#f4a261",
"warning-color": "#e9c46a",
"alert": "#d6453a",
"sidebar-color": "#1b323c",
"roomlist-background-color": "#213c47",
"roomlist-text-color": "#f6efe6",
"roomlist-text-secondary-color": "#adb4b3",
"roomlist-highlights-color": "#ffffff14",
"roomlist-separator-color": "#ffffff26",
"timeline-background-color": "#264653",
"timeline-text-color": "#f6efe6",
"secondary-content": "#adb4b3",
"tertiary-content": "#adb4b3",
"timeline-text-secondary-color": "#adb4b3",
"timeline-highlights-color": "#ffffff14",
"reaction-row-button-selected-bg-color": "#7d5852",
"menu-selected-color": "#605252",
"focus-bg-color": "#605252",
"room-highlight-color": "#605252",
"other-user-pill-bg-color": "#786b59",
"icon-button-color": "#adb4b3",
"username-colors": [
"#e76f51",
"#f4a261",
"#e9c46a",
"#ec957e",
"#f5b989",
"#eed395",
"#ee8859",
"#eeb366"
]
}
},
{
"name": "Forest Canopy",
"is_dark": false,
"colors": {
"accent-color": "#2d4a2b",
"accent": "#2d4a2b",
"primary-color": "#7d8471",
"warning-color": "#a4ac86",
"alert": "#703126",
"sidebar-color": "#ebeae7",
"roomlist-background-color": "#f2f2ef",
"roomlist-text-color": "#22301f",
"roomlist-text-secondary-color": "#6e766a",
"roomlist-highlights-color": "#00000010",
"roomlist-separator-color": "#0000001f",
"timeline-background-color": "#faf9f6",
"timeline-text-color": "#22301f",
"secondary-content": "#6e766a",
"tertiary-content": "#6e766a",
"timeline-text-secondary-color": "#6e766a",
"timeline-highlights-color": "#00000010",
"reaction-row-button-selected-bg-color": "#9eaa9b",
"menu-selected-color": "#bcc4b9",
"focus-bg-color": "#bcc4b9",
"room-highlight-color": "#bcc4b9",
"other-user-pill-bg-color": "#c8cac1",
"icon-button-color": "#6e766a",
"username-colors": [
"#2d4a2b",
"#7d8471",
"#a4ac86",
"#2a4227",
"#626b58",
"#768162",
"#55674e",
"#90987c"
]
}
},
{
"name": "Modern Minimalist",
"is_dark": false,
"colors": {
"accent-color": "#36454f",
"accent": "#36454f",
"primary-color": "#708090",
"warning-color": "#d3d3d3",
"alert": "#752e39",
"sidebar-color": "#f0f0f0",
"roomlist-background-color": "#f7f7f7",
"roomlist-text-color": "#1c252b",
"roomlist-text-secondary-color": "#6b7175",
"roomlist-highlights-color": "#00000010",
"roomlist-separator-color": "#0000001f",
"timeline-background-color": "#ffffff",
"timeline-text-color": "#1c252b",
"secondary-content": "#6b7175",
"tertiary-content": "#6b7175",
"timeline-text-secondary-color": "#6b7175",
"timeline-highlights-color": "#00000010",
"reaction-row-button-selected-bg-color": "#a5abb0",
"menu-selected-color": "#c3c7ca",
"focus-bg-color": "#c3c7ca",
"room-highlight-color": "#c3c7ca",
"other-user-pill-bg-color": "#c6ccd3",
"icon-button-color": "#6b7175",
"username-colors": [
"#36454f",
"#708090",
"#d3d3d3",
"#2e3b44",
"#576572",
"#939698",
"#536270",
"#a2aab2"
]
}
},
{
"name": "Golden Hour",
"is_dark": true,
"colors": {
"accent-color": "#f4a900",
"accent": "#f4a900",
"primary-color": "#c1666b",
"warning-color": "#d4b896",
"alert": "#dd650e",
"sidebar-color": "#352e2a",
"roomlist-background-color": "#403732",
"roomlist-text-color": "#f7f0e6",
"roomlist-text-secondary-color": "#bab2aa",
"roomlist-highlights-color": "#ffffff14",
"roomlist-separator-color": "#ffffff26",
"timeline-background-color": "#4a403a",
"timeline-text-color": "#f7f0e6",
"secondary-content": "#bab2aa",
"tertiary-content": "#bab2aa",
"timeline-text-secondary-color": "#bab2aa",
"timeline-highlights-color": "#ffffff14",
"reaction-row-button-selected-bg-color": "#966f20",
"menu-selected-color": "#7d6029",
"focus-bg-color": "#7d6029",
"room-highlight-color": "#7d6029",
"other-user-pill-bg-color": "#7a4f4e",
"icon-button-color": "#bab2aa",
"username-colors": [
"#f4a900",
"#c1666b",
"#d4b896",
"#f5be45",
"#d18f90",
"#e0ccb2",
"#da8836",
"#ca8f80"
]
}
},
{
"name": "Arctic Frost",
"is_dark": false,
"colors": {
"accent-color": "#4a6fa5",
"accent": "#4a6fa5",
"primary-color": "#d4e4f7",
"warning-color": "#c0c0c0",
"alert": "#804569",
"sidebar-color": "#ebebeb",
"roomlist-background-color": "#f2f2f2",
"roomlist-text-color": "#1e2c40",
"roomlist-text-secondary-color": "#6b7481",
"roomlist-highlights-color": "#00000010",
"roomlist-separator-color": "#0000001f",
"timeline-background-color": "#fafafa",
"timeline-text-color": "#1e2c40",
"secondary-content": "#6b7481",
"tertiary-content": "#6b7481",
"timeline-text-secondary-color": "#6b7481",
"timeline-highlights-color": "#00000010",
"reaction-row-button-selected-bg-color": "#abbbd4",
"menu-selected-color": "#c5d0e0",
"focus-bg-color": "#c5d0e0",
"room-highlight-color": "#c5d0e0",
"other-user-pill-bg-color": "#ebf1f9",
"icon-button-color": "#6b7481",
"username-colors": [
"#4a6fa5",
"#d4e4f7",
"#c0c0c0",
"#3d5b87",
"#9dadc0",
"#878c93",
"#8faace",
"#cad2dc"
]
}
},
{
"name": "Desert Rose",
"is_dark": true,
"colors": {
"accent-color": "#d4a5a5",
"accent": "#d4a5a5",
"primary-color": "#b87d6d",
"warning-color": "#e8d5c4",
"alert": "#cb6369",
"sidebar-color": "#432132",
"roomlist-background-color": "#50283c",
"roomlist-text-color": "#f6ece4",
"roomlist-text-secondary-color": "#c0aaad",
"roomlist-highlights-color": "#ffffff14",
"roomlist-separator-color": "#ffffff26",
"timeline-background-color": "#5d2e46",
"timeline-text-color": "#f6ece4",
"secondary-content": "#c0aaad",
"tertiary-content": "#c0aaad",
"timeline-text-secondary-color": "#c0aaad",
"timeline-highlights-color": "#ffffff14",
"reaction-row-button-selected-bg-color": "#936471",
"menu-selected-color": "#815262",
"focus-bg-color": "#815262",
"room-highlight-color": "#815262",
"other-user-pill-bg-color": "#814e56",
"icon-button-color": "#c0aaad",
"username-colors": [
"#d4a5a5",
"#b87d6d",
"#e8d5c4",
"#debab8",
"#cb9e91",
"#edddcf",
"#c69189",
"#d0a998"
]
}
},
{
"name": "Tech Innovation",
"is_dark": false,
"colors": {
"accent-color": "#0066ff",
"accent": "#0066ff",
"primary-color": "#00ffff",
"warning-color": "#1e1e1e",
"alert": "#57409a",
"sidebar-color": "#f0f0f0",
"roomlist-background-color": "#f7f7f7",
"roomlist-text-color": "#1e1e1e",
"roomlist-text-secondary-color": "#6d6d6d",
"roomlist-highlights-color": "#00000010",
"roomlist-separator-color": "#0000001f",
"timeline-background-color": "#ffffff",
"timeline-text-color": "#1e1e1e",
"secondary-content": "#6d6d6d",
"tertiary-content": "#6d6d6d",
"timeline-text-secondary-color": "#6d6d6d",
"timeline-highlights-color": "#00000010",
"reaction-row-button-selected-bg-color": "#8cbaff",
"menu-selected-color": "#b2d1ff",
"focus-bg-color": "#b2d1ff",
"room-highlight-color": "#b2d1ff",
"other-user-pill-bg-color": "#99ffff",
"icon-button-color": "#6d6d6d",
"username-colors": [
"#0066ff",
"#00ffff",
"#1e1e1e",
"#0950bc",
"#09bcbc",
"#1e1e1e",
"#00b2ff",
"#0f8e8e"
]
}
},
{
"name": "Botanical Garden",
"is_dark": false,
"colors": {
"accent-color": "#4a7c59",
"accent": "#4a7c59",
"primary-color": "#f9a620",
"warning-color": "#b7472a",
"alert": "#804c3f",
"sidebar-color": "#e6e4df",
"roomlist-background-color": "#eeece6",
"roomlist-text-color": "#22331f",
"roomlist-text-secondary-color": "#6c7667",
"roomlist-highlights-color": "#00000010",
"roomlist-separator-color": "#0000001f",
"timeline-background-color": "#f5f3ed",
"timeline-text-color": "#22331f",
"secondary-content": "#6c7667",
"tertiary-content": "#6c7667",
"timeline-text-secondary-color": "#6c7667",
"timeline-highlights-color": "#00000010",
"reaction-row-button-selected-bg-color": "#a8bdaa",
"menu-selected-color": "#c2cfc1",
"focus-bg-color": "#c2cfc1",
"room-highlight-color": "#c2cfc1",
"other-user-pill-bg-color": "#f7d49b",
"icon-button-color": "#6c7667",
"username-colors": [
"#4a7c59",
"#f9a620",
"#b7472a",
"#3e6648",
"#b88420",
"#834026",
"#a2913c",
"#d87625"
]
}
},
{
"name": "Midnight Galaxy",
"is_dark": false,
"colors": {
"accent-color": "#2b1e3e",
"accent": "#2b1e3e",
"primary-color": "#4a4e8f",
"warning-color": "#a490c2",
"alert": "#6e1930",
"sidebar-color": "#d8d8eb",
"roomlist-background-color": "#dfdff2",
"roomlist-text-color": "#241a34",
"roomlist-text-secondary-color": "#686179",
"roomlist-highlights-color": "#00000010",
"roomlist-separator-color": "#0000001f",
"timeline-background-color": "#e6e6fa",
"timeline-text-color": "#241a34",
"secondary-content": "#686179",
"tertiary-content": "#686179",
"timeline-text-secondary-color": "#686179",
"timeline-highlights-color": "#00000010",
"reaction-row-button-selected-bg-color": "#928ca5",
"menu-selected-color": "#aeaac2",
"focus-bg-color": "#aeaac2",
"room-highlight-color": "#aeaac2",
"other-user-pill-bg-color": "#a8a9cf",
"icon-button-color": "#686179",
"username-colors": [
"#2b1e3e",
"#4a4e8f",
"#a490c2",
"#291d3b",
"#3f3e74",
"#776790",
"#3a3666",
"#776fa8"
]
}
}
],
"feature_group_calls": true
},
"show_labs_settings": true,
"sso_redirect_options": {
"immediate": false
}
}
@@ -0,0 +1,52 @@
# Laedt die Build-Artefakte resumefaehig vor, die app-builder sonst selbst (ohne
# Resume) von GitHub laedt: das CDN (185.199.109.x) reisst im Lab-Gast groessere
# Transfers reproduzierbar mitten im Stream ab (Jobs 415/416/424, wsarecv-Reset).
# curl -C - setzt nach jedem Abriss am letzten Byte fort; einmal geladen liegen
# die Artefakte in den persistenten Caches (ELECTRON_CACHE/ELECTRON_BUILDER_CACHE)
# und werden nie erneut geholt.
$ErrorActionPreference = 'Stop'
function Get-Resumable([string]$Url, [string]$Out) {
foreach ($i in 1..8) {
& curl.exe -sSL -C - -o $Out $Url
if ($LASTEXITCODE -eq 0) { return }
Start-Sleep 5
}
throw "Download nach 8 Versuchen gescheitert: $Url"
}
# --- 1. Electron-Binary -> ELECTRON_CACHE ------------------------------------
$pkg = Join-Path $PSScriptRoot '..\package.json'
$ev = (& node -p "require(process.argv[1]).devDependencies.electron" $pkg) -replace '[\^~]', ''
New-Item -ItemType Directory -Force $env:ELECTRON_CACHE | Out-Null
$zip = Join-Path $env:ELECTRON_CACHE "electron-v$ev-win32-x64.zip"
if (-not (Test-Path $zip)) {
Get-Resumable "https://github.com/electron/electron/releases/download/v$ev/electron-v$ev-win32-x64.zip" "$zip.part"
Move-Item -Force "$zip.part" $zip
}
# --- 2. electron-builder-Werkzeuge -> ELECTRON_BUILDER_CACHE -----------------
# Zielstruktur laut app-builder-Quellcode (artifactDownloader.go) + electron-builder
# 26.9.1 (binDownload.js): der Cache-Schluessel ist "<release>-<dateiname-ohne-ext>",
# also GEDOPPELT (z.B. nsis-3.0.4.1-nsis-3.0.4.1), und liegt unter dem Teil des
# Namens VOR dem ersten Bindestrich als Unterverzeichnis - nsis-resources landet
# deshalb ebenfalls unter nsis\. Job 431 bewies den Miss mit einfachem Namen.
$sevenZa = Join-Path $PSScriptRoot '..\..\..\node_modules\7zip-bin\win\x64\7za.exe'
function Get-EbArtifact([string]$Release, [string]$SubDir) {
$dest = Join-Path $env:ELECTRON_BUILDER_CACHE (Join-Path $SubDir "$Release-$Release")
if (Test-Path $dest) { return }
$tmp = Join-Path $env:TEMP "$Release.7z"
Get-Resumable "https://github.com/electron-userland/electron-builder-binaries/releases/download/$Release/$Release.7z" $tmp
New-Item -ItemType Directory -Force $dest | Out-Null
& $sevenZa x -y "-o$dest" $tmp | Out-Null
if ($LASTEXITCODE -ne 0) { Remove-Item -Recurse -Force $dest; throw "Entpacken gescheitert: $tmp" }
}
# Versionen passend zu electron-builder 26.9.1 (winCodeSign aus dem Job-424-Trace,
# nsis/nsis-resources samt Checksummen aus nsisUtil.js). Bei einem kuenftigen
# Mismatch laedt app-builder selbst und der Fehler nennt die richtige Version.
Get-EbArtifact 'winCodeSign-2.6.0' 'winCodeSign'
Get-EbArtifact 'nsis-3.0.4.1' 'nsis'
Get-EbArtifact 'nsis-resources-3.4.1' 'nsis'
Write-Output "prefetch ok: electron v$ev + winCodeSign/nsis in den Caches"
Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 395 KiB

+27
View File
@@ -48,6 +48,8 @@ interface Variant extends Metadata {
"appId": string;
"linux.executableName"?: string;
"linux.deb.name"?: string;
"mac.icon"?: string;
"dmg.badgeIcon"?: string;
"protocols": string[];
}
@@ -202,6 +204,31 @@ if (variant["linux.deb.name"]) {
config.deb.fpm.push("--name", variant["linux.deb.name"]);
}
/**
* ThreadNet-Fork: Icon-Pfade fuer macOS ueberschreibbar machen.
*
* Upstream setzt `mac.icon` auf das Icon-Composer-Bundle `build/icon.icon`
* (macOS 26). Dessen Verarbeitung ruft `actool` auf, das es nur mit dem vollen
* Xcode gibt (~10 GB, App-Store-Login) - mit blossen CommandLineTools scheitert
* damit JEDER macOS-Build, nicht nur das DMG:
*
* Failed to check actool version. Is Xcode 26 or higher installed?
*
* Ueber die Variante laesst sich stattdessen das klassische `.icns` waehlen.
* Bewusst hier als Variantenoption statt als Aenderung an den Defaults oben:
* So bleibt Upstreams Wert unberuehrt und ein Upstream-Merge erzeugt keinen
* Konflikt an dieser Zeile.
*
* Preis: kein macOS-26-Icon-Rendering. Ohne Xcode gaebe es ohnehin keinen Build.
*/
if (variant["mac.icon"]) {
config.mac.icon = variant["mac.icon"];
}
if (variant["dmg.badgeIcon"]) {
config.dmg.badgeIcon = variant["dmg.badgeIcon"];
}
/**
* Allow specifying windows signing cert via env vars
* @param {string} process.env.ED_SIGNTOOL_SUBJECT_NAME
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "element-desktop",
"productName": "Element",
"productName": "ThreadNet",
"main": "lib/electron-main.js",
"exports": "./lib/electron-main.js",
"version": "1.12.17",
+1 -1
View File
@@ -13,7 +13,7 @@
"disable_login_language_selector": false,
"disable_3pid_login": false,
"force_verification": false,
"brand": "Element",
"brand": "aXion1337.Chat",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
@@ -10,3 +10,12 @@ Please see LICENSE files in the repository root for full details.
.mx_HelpUserSettingsTab_accessTokenDetails {
width: fit-content;
}
/* ThreadNet-Fork: Attribution unter den Versionsangaben. Zurueckhaltend
gesetzt - sie soll auffindbar sein, nicht um Aufmerksamkeit konkurrieren
mit der Version darueber, die Nutzer hier tatsaechlich suchen. */
.mx_HelpUserSettingsTab_attribution {
margin-top: var(--cpd-space-2x);
color: var(--cpd-color-text-secondary);
font: var(--cpd-font-body-sm-regular);
}
+3 -3
View File
@@ -1,8 +1,8 @@
{
"name": "Element",
"short_name": "Element",
"name": "ThreadNet",
"short_name": "ThreadNet",
"display": "standalone",
"theme_color": "#76CFA6",
"theme_color": "#ed4f4c",
"start_url": "index.html",
"icons": [
{
Binary file not shown.

After

Width:  |  Height:  |  Size: 590 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 829 B

After

Width:  |  Height:  |  Size: 901 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

+9 -4
View File
@@ -20,7 +20,11 @@ export const DEFAULTS: DeepReadonly<IConfigOptions> = {
branding: {
logo_link_url: "https://element.io",
auth_header_logo_url: "themes/element/img/logos/element-logo.svg",
welcome_background_url: "themes/element/img/backgrounds/lake.jpg",
// ThreadNet-Fork: eigenes Titelbild statt Elements lake.jpg. Steht hier im
// Default und nicht in element-values.yaml, weil die Bilddatei ohnehin nur
// ueber einen Build in den Container kommt - eine zusaetzliche Config-Zeile
// waere eine zweite Stelle, die mitwandern muesste.
welcome_background_url: "themes/element/img/backgrounds/alpenglow.jpg",
},
help_url: "https://element.io/help",
help_encryption_url: "https://element.io/help#encryption",
@@ -44,9 +48,10 @@ export const DEFAULTS: DeepReadonly<IConfigOptions> = {
// be preferred over their config.
desktopBuilds: {
available: true,
// eslint-disable-next-line @typescript-eslint/no-require-imports
logo: require("../res/img/element-desktop-logo.svg").default,
url: "https://element.io/get-started",
// ThreadNet-Fork: eigene Marke und eigener Download-Pfad statt
// element.io - wir liefern eigene Desktop-Builds aus.
logo: "vector-icons/512.png",
url: "https://git.lab/axion1337.chat/ThreadNet-Web/-/releases",
},
feedback: {
@@ -32,8 +32,8 @@ export const ErrorView: React.FC<IProps> = ({ title, messages, footer, children
<img
className="mx_ErrorView_logo"
height="160"
src="themes/element/img/logos/element-app-logo.png"
alt="Element"
src="vector-icons/512.png"
alt="ThreadNet"
/>
<div className="mx_ErrorView_container">
<Heading size="md" weight="semibold">
@@ -106,34 +106,42 @@ export default class HelpUserSettingsTab extends React.Component<EmptyObject, IS
<SettingsSubsection heading={_t("common|credits")}>
<SettingsSubsectionText>
<ul>
{/* ThreadNet-Fork: eigenes Titelbild, deshalb eigene Danksagung.
Bewusst OHNE _t(): der Schluessel credits|default_cover_photo
steckt in 32 Sprachdateien, 31 davon nennen Elements Fotografen
namentlich. Wuerden wir nur en/de anpassen, stuende in 29
Sprachen eine falsche Attribution und die uebrigen 29 koennen
wir nicht pflegen, sie kommen aus Elements Uebersetzungsdienst.
Ein Fotografenname und eine Lizenzbezeichnung werden ohnehin
nicht uebersetzt. Der ungenutzte Schluessel bleibt in den
Sprachdateien stehen ihn aus 32 Dateien zu entfernen waere
Laerm im naechsten Upstream-Merge. */}
<li>
{_t(
"credits|default_cover_photo",
{},
{
photo: (sub) => (
<ExternalLink
href="themes/element/img/backgrounds/lake.jpg"
rel="noreferrer noopener"
target="_blank"
>
{sub}
</ExternalLink>
),
author: (sub) => (
<ExternalLink href="https://www.flickr.com/golan">{sub}</ExternalLink>
),
terms: (sub) => (
<ExternalLink
href="https://creativecommons.org/licenses/by-sa/4.0/"
rel="noreferrer noopener"
target="_blank"
>
{sub}
</ExternalLink>
),
},
)}
The{" "}
<ExternalLink
href="themes/element/img/backgrounds/alpenglow.jpg"
rel="noreferrer noopener"
target="_blank"
>
default cover photo
</ExternalLink>{" "}
is by <ExternalLink href="https://unsplash.com/@heytowner">John Towner</ExternalLink> on{" "}
<ExternalLink
href="https://unsplash.com/photos/JgOeRuGD_Y4"
rel="noreferrer noopener"
target="_blank"
>
Unsplash
</ExternalLink>
, used under the{" "}
<ExternalLink
href="https://unsplash.com/license"
rel="noreferrer noopener"
target="_blank"
>
Unsplash License
</ExternalLink>
.
</li>
<li>
{_t(
@@ -259,6 +267,15 @@ export default class HelpUserSettingsTab extends React.Component<EmptyObject, IS
{cryptoVersion}
<br />
</CopyableText>
{/* ThreadNet-Fork: Attribution an Element. Bewusst NICHT in
getVersionTextToCopy der Text dort geht in Fehlerberichte,
dort ist die Herkunft des Forks nur Rauschen. Ebenso bewusst
ohne _t(): ein Markenhinweis wird nicht übersetzt, und jeder
zusätzliche i18n-Schlüssel ist Reibung beim Upstream-Merge. */}
<div className="mx_HelpUserSettingsTab_attribution">
ThreadNet powered by{" "}
<ExternalLink href="https://element.io">Element</ExternalLink>
</div>
{updateButton}
</SettingsSubsectionText>
</SettingsSubsection>
+4 -3
View File
@@ -2,19 +2,20 @@
<html lang="en" style="height: 100%;">
<head>
<meta charset="utf-8">
<title>Element</title>
<title>ThreadNet</title>
<link rel="apple-touch-icon" sizes="120x120" href="<%= require('../../res/vector-icons/120.png') %>">
<link rel="apple-touch-icon" sizes="144x144" href="<%= require('../../res/vector-icons/144.png') %>">
<link rel="apple-touch-icon" sizes="152x152" href="<%= require('../../res/vector-icons/152.png') %>">
<link rel="apple-touch-icon" sizes="180x180" href="<%= require('../../res/vector-icons/180.png') %>">
<link rel="icon" href="vector-icons/favicon.ico" sizes="any">
<link rel="manifest" href="manifest.json">
<meta name="referrer" content="no-referrer">
<link rel="icon" type="image/png" sizes="24x24" href="<%= require('../../res/vector-icons/24.png') %>">
<link rel="icon" type="image/png" sizes="144x144" href="<%= require('../../res/vector-icons/144.png') %>">
<link rel="icon" type="image/png" sizes="512x512" href="<%= require('../../res/vector-icons/512.png') %>">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-title" content="Element">
<meta name="application-name" content="Element">
<meta name="apple-mobile-web-app-title" content="ThreadNet">
<meta name="application-name" content="ThreadNet">
<meta name="theme-color" content="#ffffff">
<meta property="og:image" content="<%= og_image_url %>" />
<meta http-equiv="Content-Security-Policy" content="
+62
View File
@@ -0,0 +1,62 @@
# ThreadNet-Web: Fork-Anpassungen für axion1337.chat
Dieses Dokument beschreibt alles, was dieser Fork gegenüber Upstream Element Web ändert. Der
Rest der `docs/`-Ordner-Dateien und das Root-`README.md` sind unverändertes Upstream-Material
und beschreiben absichtlich **nicht** diese Anpassungen - diese Datei ist der zentrale
Anlaufpunkt dafür.
## 1. Discord-Style Room-List (Call-Teilnehmer in der Raumliste)
Zeigt aktive Call-Teilnehmer direkt in der Raumliste an (ähnlich Discords Voice-Channel-UI),
statt nur einen generischen "Call läuft"-Indikator. Betroffene Komponenten:
`apps/web/src/room-list/RoomListItemView/RoomListItemView.tsx` und
`RoomListItemView.module.css` (vertikale Ausrichtung der Teilnehmer-Avatare).
## 2. Client-seitiges ClamAV-Content-Scanning (Issue #19-Erweiterung)
Der Server-seitige Content-Scanner (Synapse-Modul, siehe gitops-Repo
[`docs/deployment-guides/06-moderation-content-scanning.md`](https://rohana.axion1337.de/sorb/axion1337.chat-gitops/src/branch/main/docs/deployment-guides/06-moderation-content-scanning.md))
sieht bei Ende-zu-Ende-verschlüsselten Räumen nur Ciphertext - eine strukturelle Grenze, kein
Bug. Da dieser Client bereits geforkt wird, scannt er stattdessen selbst, auf beiden Seiten:
- **Senden** (`apps/web/src/ContentMessages.ts`, `uploadFile()`): liest die Datei als
ArrayBuffer, scannt sie über `scanContent()` **bevor** verschlüsselt/hochgeladen wird - der
Upload wird bei Treffer gar nicht erst gestartet.
- **Empfangen** (`apps/web/src/utils/DecryptFile.ts`, `decryptFile()`): scannt die
entschlüsselten Bytes direkt nach dem Entschlüsseln, bevor das Blob an die UI zurückgegeben
wird - schützt auch vor Dateien von unveränderten/fremden Matrix-Clients, die diesen Patch
nicht haben.
- Gemeinsame Scan-Logik: `apps/web/src/utils/ContentScanner.ts` - ruft den (im gitops-Repo
deployten) `clamav-http-scanner`-Dienst per `fetch("/_scan", ...)` auf, mit dem eigenen
Matrix-Access-Token als Bearer-Auth. Fail-open bei Netzwerk-/Scanner-Fehlern (blockiert
Uploads/Downloads nicht bei einem Ausfall des Scanners).
- Neuer Fehlertyp `ContentScanRejectedError`, verdrahtet durch die bestehenden
Fehler-Rendering-Pfade in `MImageBody.tsx`, `MAudioBody.tsx`, `VideoBodyViewModel.ts`,
`FileBodyViewModel.ts`.
- Live getestet inkl. Hostile-Sender-Simulation (Datei per rohem API-Call ohne diesen Patch
gesendet, Empfangs-Hook hat trotzdem geblockt).
### ⚠️ Bekannte Lücke: Electron/Desktop (Issue #2)
Dieser Fix ist bestätigt nur für den **Web-Client-Build** wirksam. Der Electron-Desktop-Client
bekommt ihn aktuell **nicht automatisch** - die CI-Workflow-Kette (`build_ew`-Job) checkt
weiterhin `element-hq/element-web` (Upstream) statt diesen Fork aus, und es ist kein GitHub-
Actions-Runner für dieses Repo registriert. Der aktuell veröffentlichte Desktop-Build
(`desktop-v1.12.17-clientscan` Release) wurde manuell gebaut, nicht automatisiert. Details und
Fix-Plan: [Issue #2](https://rohana.axion1337.de/sorb/ThreadNet-Web/issues/2).
## 3. Build-Fixes (historisch, Issue #12)
Zwei Bugs blockierten einen vollständigen `docker build` von Grund auf (mussten für den
Client-Scanning-Rebuild oben behoben werden): (1) mehrere Shell-Skripte waren mit Modus 644
statt 755 committet (nicht ausführbar); (2) der `matrix-js-sdk#develop`-Git-Ref-Pin in
`pnpm-lock.yaml` war veraltet (fehlte `src/oidc/authorize.ts`, das `apps/web` importiert).
Beide behoben.
## Repo-Topologie (seit 2026-07-31)
**Kanonisch ist `git.lab/axion1337.chat/ThreadNet-Web`** (Homelab-GitLab, nur im Lab
auflösbar) — dort laufen Entwicklung und CI (`.gitlab-ci.yml`). Die Kopie auf
`rohana.axion1337.de/sorb/ThreadNet-Web` ist ein **Push-Mirror** (automatisch, GitLab →
Gitea) und dient als Lesekopie plus Standort für Issues, Container-Registry und Releases.
**Niemals direkt nach rohana pushen** — der Mirror überschreibt divergente Stände.