diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 00000000000..e7a976a7be4
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1,18 @@
+* @element-hq/element-web-reviewers
+/.github/workflows/** @element-hq/element-web-team
+/package.json @element-hq/element-web-team
+/yarn.lock @element-hq/element-web-team
+
+/src/SecurityManager.ts @element-hq/element-crypto-web-reviewers
+/test/SecurityManager-test.ts @element-hq/element-crypto-web-reviewers
+/src/async-components/views/dialogs/security/ @element-hq/element-crypto-web-reviewers
+/src/components/views/dialogs/security/ @element-hq/element-crypto-web-reviewers
+/test/components/views/dialogs/security/ @element-hq/element-crypto-web-reviewers
+/src/stores/SetupEncryptionStore.ts @element-hq/element-crypto-web-reviewers
+/test/stores/SetupEncryptionStore-test.ts @element-hq/element-crypto-web-reviewers
+
+# Ignore translations as those will be updated by GHA for Localazy download
+/src/i18n/strings
+# Ignore the synapse plugin as this is updated by GHA for docker image updating
+/playwright/plugins/homeserver/synapse/index.ts
+
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000000..afc29f01425
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+patreon: matrixdotorg
+liberapay: matrixdotorg
diff --git a/.github/ISSUE_TEMPLATE/bug-desktop.yml b/.github/ISSUE_TEMPLATE/bug-desktop.yml
new file mode 100644
index 00000000000..529c0a0ebcf
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-desktop.yml
@@ -0,0 +1,76 @@
+name: Bug report for the Element desktop app (not in a browser)
+description: File a bug report if you are using the desktop Element application.
+labels: [T-Defect]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+
+ Please report security issues by email to security@matrix.org
+ - type: textarea
+ id: reproduction-steps
+ attributes:
+ label: Steps to reproduce
+ description: Please attach screenshots, videos or logs if you can.
+ placeholder: Tell us what you see!
+ value: |
+ 1. Where are you starting? What can you see?
+ 2. What do you click?
+ 3. More steps…
+ validations:
+ required: true
+ - type: textarea
+ id: result
+ attributes:
+ label: Outcome
+ placeholder: Tell us what went wrong
+ value: |
+ #### What did you expect?
+
+ #### What happened instead?
+ validations:
+ required: true
+ - type: input
+ id: os
+ attributes:
+ label: Operating system
+ placeholder: Windows, macOS, Ubuntu, Arch Linux…
+ validations:
+ required: false
+ - type: input
+ id: version
+ attributes:
+ label: Application version
+ description: You can find the version information in Settings -> Help & About.
+ placeholder: e.g. Element version 1.7.34, olm version 3.2.3
+ validations:
+ required: false
+ - type: input
+ id: source
+ attributes:
+ label: How did you install the app?
+ description: Where did you install the app from? Please give a link or a description.
+ placeholder: e.g. From https://element.io/get-started
+ validations:
+ required: false
+ - type: input
+ id: homeserver
+ attributes:
+ label: Homeserver
+ description: |
+ Which server is your account registered on? If it is a local or non-public homeserver, please tell us what is the homeserver implementation (ex: Synapse/Dendrite/etc.) and the version.
+ placeholder: e.g. matrix.org or Synapse 1.50.0rc1
+ validations:
+ required: false
+ - type: dropdown
+ id: rageshake
+ attributes:
+ label: Will you send logs?
+ description: |
+ Did you know that you can send a /rageshake command from your application to submit logs for this issue? Trigger the defect, then type `/rageshake` into the message input area followed by a description of the problem and send the command. You will be able to add a link to this defect report and submit anonymous logs to the developers.
+ options:
+ - "Yes"
+ - "No"
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/bug-web.yml b/.github/ISSUE_TEMPLATE/bug-web.yml
new file mode 100644
index 00000000000..24ab78a153d
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug-web.yml
@@ -0,0 +1,84 @@
+name: Bug report for Element Web (in browser)
+description: File a bug report if you are using Element in a web browser like Firefox, Chrome, Edge, and so on.
+labels: [T-Defect]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+
+ Please report security issues by email to security@matrix.org
+ - type: textarea
+ id: reproduction-steps
+ attributes:
+ label: Steps to reproduce
+ description: Please attach screenshots, videos or logs if you can.
+ placeholder: Tell us what you see!
+ value: |
+ 1. Where are you starting? What can you see?
+ 2. What do you click?
+ 3. More steps…
+ validations:
+ required: true
+ - type: textarea
+ id: result
+ attributes:
+ label: Outcome
+ placeholder: Tell us what went wrong
+ value: |
+ #### What did you expect?
+
+ #### What happened instead?
+ validations:
+ required: true
+ - type: input
+ id: os
+ attributes:
+ label: Operating system
+ placeholder: Windows, macOS, Ubuntu, Arch Linux…
+ validations:
+ required: false
+ - type: input
+ id: browser
+ attributes:
+ label: Browser information
+ description: Which browser are you using? Which version?
+ placeholder: e.g. Chromium Version 92.0.4515.131
+ validations:
+ required: false
+ - type: input
+ id: webapp-url
+ attributes:
+ label: URL for webapp
+ description: Which URL are you using to access the webapp? If a private server, tell us what version of Element Web you are using.
+ placeholder: e.g. develop.element.io, app.element.io
+ validations:
+ required: false
+ - type: input
+ id: version
+ attributes:
+ label: Application version
+ description: You can find the version information in Settings -> Help & About.
+ placeholder: e.g. Element version 1.7.34, olm version 3.2.3
+ validations:
+ required: false
+ - type: input
+ id: homeserver
+ attributes:
+ label: Homeserver
+ description: |
+ Which server is your account registered on? If it is a local or non-public homeserver, please tell us what is the homeserver implementation (ex: Synapse/Dendrite/etc.) and the version.
+ placeholder: e.g. matrix.org or Synapse 1.50.0rc1
+ validations:
+ required: false
+ - type: dropdown
+ id: rageshake
+ attributes:
+ label: Will you send logs?
+ description: |
+ Did you know that you can send a /rageshake command from the web application to submit logs for this issue? Trigger the defect, then type `/rageshake` into the message input area followed by a description of the problem and send the command. You will be able to add a link to this defect report and submit anonymous logs to the developers.
+ options:
+ - "Yes"
+ - "No"
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 00000000000..b34e4493684
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,5 @@
+blank_issues_enabled: false
+contact_links:
+ - name: Questions & support
+ url: https://matrix.to/#/#element-web:matrix.org
+ about: Please ask and answer questions here.
diff --git a/.github/ISSUE_TEMPLATE/enhancement.yml b/.github/ISSUE_TEMPLATE/enhancement.yml
new file mode 100644
index 00000000000..7dd384e78ff
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/enhancement.yml
@@ -0,0 +1,36 @@
+name: Enhancement request
+description: Do you have a suggestion or feature request?
+labels: [T-Enhancement]
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for taking the time to propose an enhancement to an existing feature. If you would like to propose a new feature or a major cross-platform change, please [start a discussion here](https://github.com/element-hq/element-meta/discussions/new?category=ideas).
+ - type: textarea
+ id: usecase
+ attributes:
+ label: Your use case
+ description: What would you like to be able to do? Please feel welcome to include screenshots or mock ups.
+ placeholder: Tell us what you would like to do!
+ value: |
+ #### What would you like to do?
+
+ #### Why would you like to do it?
+
+ #### How would you like to achieve it?
+ validations:
+ required: true
+ - type: textarea
+ id: alternative
+ attributes:
+ label: Have you considered any alternatives?
+ placeholder: A clear and concise description of any alternative solutions or features you've considered.
+ validations:
+ required: false
+ - type: textarea
+ id: additional-context
+ attributes:
+ label: Additional context
+ placeholder: Is there anything else you'd like to add?
+ validations:
+ required: false
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000000..16036541f88
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,8 @@
+
+
+## Checklist
+
+- [ ] Tests written for new code (and old code if feasible).
+- [ ] New or updated `public`/`exported` symbols have accurate [TSDoc](https://tsdoc.org/) documentation.
+- [ ] Linter and other CI checks pass.
+- [ ] I have licensed the changes to Element by completing the [Contributor License Agreement (CLA)](https://cla-assistant.io/element-hq/element-web)
diff --git a/.github/actions/download-verify-element-tarball/action.yml b/.github/actions/download-verify-element-tarball/action.yml
new file mode 100644
index 00000000000..e61b96596bf
--- /dev/null
+++ b/.github/actions/download-verify-element-tarball/action.yml
@@ -0,0 +1,39 @@
+name: Upload release assets
+description: Uploads assets to an existing release and optionally signs them
+inputs:
+ tag:
+ description: GitHub release tag to fetch assets from.
+ required: true
+ out-file-path:
+ description: Path to where the webapp should be extracted to.
+ required: true
+runs:
+ using: composite
+ steps:
+ - name: Download release tarball
+ id: current_download
+ uses: robinraju/release-downloader@a96f54c1b5f5e09e47d9504526e96febd949d4c2 # v1
+ with:
+ tag: ${{ inputs.tag }}
+ fileName: element-*.tar.gz*
+ out-file-path: ${{ runner.temp }}/download-verify-element-tarball
+
+ - name: Verify tarball
+ shell: bash
+ run: gpg --verify element-*.tar.gz.asc element-*.tar.gz
+ working-directory: ${{ runner.temp }}/download-verify-element-tarball
+
+ - name: Extract tarball
+ shell: bash
+ run: |
+ mkdir webapp
+ tar xvzf element-*.tar.gz -C webapp --strip-components=1
+ working-directory: ${{ runner.temp }}/download-verify-element-tarball
+
+ - name: Move webapp to out-file-path
+ shell: bash
+ run: mv ${{ runner.temp }}/download-verify-element-tarball/webapp ${{ inputs.out-file-path }}
+
+ - name: Clean up temp directory
+ shell: bash
+ run: rm -R ${{ runner.temp }}/download-verify-element-tarball
diff --git a/.github/cfp_headers b/.github/cfp_headers
index 482c9ead0dd..497a5ff58d9 100644
--- a/.github/cfp_headers
+++ b/.github/cfp_headers
@@ -5,7 +5,6 @@
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
- permissions-policy: accelerometer=(), gyroscope=(), magnetometer=(), usb=(), interest-cohort=()
/version
Content-Type: text/plain
diff --git a/.github/labels.yml b/.github/labels.yml
new file mode 100644
index 00000000000..80c5408c1e3
--- /dev/null
+++ b/.github/labels.yml
@@ -0,0 +1,269 @@
+- name: "A-Aliases"
+ color: "bfd4f2"
+- name: "A-Authentication"
+ color: "bfd4f2"
+- name: "A-Autocomplete"
+ color: "bfd4f2"
+- name: "A-Breadcrumbs"
+ color: "bfd4f2"
+- name: "A-Bridge"
+ color: "bfd4f2"
+- name: "A-Broadcast"
+ description: "Broadcast-style voice messages"
+ color: "bfd4f2"
+- name: "A-Create-Room"
+ description: "Create room flow, user suggestions, etc."
+ color: "bfd4f2"
+- name: "A-DevTools"
+ description: "/devtools, show hidden events, etc."
+ color: "bfd4f2"
+- name: "A-Dialogs"
+ color: "bfd4f2"
+- name: "A-Disambiguation"
+ color: "bfd4f2"
+- name: "A-DM-Start"
+ description: "Creating a DM with another user"
+ color: "bfd4f2"
+- name: "A-E2EE-Dehydration"
+ color: "8CC59A"
+- name: "A-Electron"
+ color: "bfd4f2"
+- name: "A-Element-Call"
+ description: "Group calls via Element Call"
+ color: "bfd4f2"
+- name: "A-Element-R"
+ description: "Issues affecting the port of Element's crypto layer to Rust"
+ color: "bfd4f2"
+- name: "A-ELS"
+ description: "Event List Summary (and Membership ELS, MELS)"
+ color: "bfd4f2"
+- name: "A-Emotes"
+ color: "bfd4f2"
+- name: "A-EMS"
+ description: "Issues related to EMS"
+ color: "bfd4f2"
+- name: "A-Error-Message"
+ color: "bfd4f2"
+- name: "A-Federation"
+ color: "bfd4f2"
+- name: "A-Feedback-Reporting"
+ description: "Reporting process for bugs, debug logs (rageshakes), suggestions"
+ color: "bfd4f2"
+- name: "A-File-Download"
+ color: "bfd4f2"
+- name: "A-File-Panel"
+ color: "bfd4f2"
+- name: "A-Identity-Server"
+ color: "bfd4f2"
+- name: "A-Indexing"
+ description: "Indexing messages via Seshat"
+ color: "bfd4f2"
+- name: "A-IRC-Layout"
+ color: "bfd4f2"
+- name: "A-Jump-To-Date"
+ description: "Jump to date headers or slash command"
+ color: "bfd4f2"
+- name: "A-Lazy-Loading"
+ color: "bfd4f2"
+- name: "A-Light-Box"
+ description: "UI when viewing an image"
+ color: "bfd4f2"
+- name: "A-Location-Sharing"
+ color: "bfd4f2"
+- name: "A-Logout"
+ description: "Logout, sign out, etc."
+ color: "bfd4f2"
+- name: "A-Maths"
+ description: "Render LaTeX maths in messages"
+ color: "bfd4f2"
+- name: "A-Memory"
+ description: "Memory leaks, leak hunting tools"
+ color: "bfd4f2"
+- name: "A-Message-Forwarding"
+ color: "bfd4f2"
+- name: "A-Message-Pinning"
+ color: "bfd4f2"
+- name: "A-Message-Previews"
+ color: "bfd4f2"
+- name: "A-Message-Starring"
+ description: "Saving favourite messages for later"
+ color: "bfd4f2"
+- name: "A-Modules"
+ description: "Module system related"
+ color: "bfd4f2"
+- name: "A-New-Search-Experience"
+ description: "The new search dialog available in Labs"
+ color: "bfd4f2"
+- name: "A-Packaging"
+ description: "Packaging, signing, releasing"
+ color: "bfd4f2"
+- name: "A-Peeking"
+ color: "bfd4f2"
+- name: "A-Picture-in-Picture"
+ color: "bfd4f2"
+- name: "A-Power-Levels"
+ description: "The permissions that users have in rooms and spaces"
+ color: "bfd4f2"
+- name: "A-Replies"
+ description: "reply"
+ color: "bfd4f2"
+- name: "A-Session-Mgmt"
+ description: "Session / device names, management UI, etc."
+ color: "bfd4f2"
+- name: "A-Share"
+ color: "bfd4f2"
+- name: "A-Shortcuts"
+ description: "Keyboard shortcuts"
+ color: "bfd4f2"
+- name: "A-Sliding-Sync"
+ description: "Also known as Sync v3 - https://github.com/matrix-org/sliding-sync"
+ color: "bfd4f2"
+- name: "A-Soft-Logout"
+ description: "https://github.com/element-hq/element-web/issues/10224"
+ color: "bfd4f2"
+- name: "A-Spaces-Settings"
+ color: "bfd4f2"
+- name: "A-SSO"
+ color: "bfd4f2"
+- name: "A-Status-Bar"
+ description: "Unsent messages warning and 'Connectivity to the server has been lost'"
+ color: "bfd4f2"
+- name: "A-Storage"
+ description: "Storage layer of the app, including IndexedDB, local storage, etc."
+ color: "bfd4f2"
+- name: "A-Technical-Debt"
+ color: "bfd4f2"
+- name: "A-Testing"
+ description: "Testing, code coverage, etc."
+ color: "bfd4f2"
+- name: "A-Themes-Custom"
+ description: "Custom theme variables or support"
+ color: "bfd4f2"
+- name: "A-Themes-Official"
+ description: "Official themes (light, dark)"
+ color: "bfd4f2"
+- name: "A-Theming"
+ color: "bfd4f2"
+- name: "A-Timeline-Jumpy-Scroll"
+ description: "Stable timeline dream ✨"
+ color: "bfd4f2"
+- name: "A-Timesheet-1"
+ description: "Log any time spent on this into the A-Timesheet-1 project"
+ color: "5319E7"
+- name: "A-Toast"
+ color: "bfd4f2"
+- name: "A-Tooltips"
+ description: "Anything related to tooltips"
+ color: "bfd4f2"
+- name: "A-UI-Customisation"
+ description: "UIFeatures etc. for customising entire parts of the UI"
+ color: "bfd4f2"
+- name: "A-URL-Previews"
+ color: "bfd4f2"
+- name: "A-User-Menu"
+ description: "The top left main menu with the user's name and avatar"
+ color: "bfd4f2"
+- name: "A-User-Search"
+ description: "The start DM or invite to room dialogs (things dealing with `/user_directory/search`)"
+ color: "bfd4f2"
+- name: "A-Video-Rooms"
+ description: "Persistent group calls"
+ color: "bfd4f2"
+- name: "A-Voice-Messages"
+ color: "bfd4f2"
+- name: "A-Welcome-Page"
+ color: "bfd4f2"
+- name: "backport staging"
+ description: "Label to automatically backport PR to staging branch"
+ color: "B60205"
+- name: "Dependencies"
+ description: "Pull requests that update a dependency file"
+ color: "0366d6"
+- name: "Hacktoberfest"
+ description: "Issues which are suitable for Hacktoberfest PRs: https://hacktoberfest.digitalocean.com/"
+ color: "ff7518"
+- name: "P4"
+ description: "[OBSOLETE LABEL] Interesting — Not yet scheduled, will accept patches"
+ color: "d1e5f0"
+- name: "spam"
+ color: "B60205"
+- name: "Sponsored"
+ color: "ffc8f4"
+- name: "T-Deprecation"
+ description: "A pull request that makes something deprecated"
+ color: "98e6ae"
+- name: "T-Other"
+ description: "Questions, user support, anything else"
+ color: "98e6ae"
+- name: "Team: App"
+ color: "FFA500"
+- name: "X-Blocked"
+ color: "ff7979"
+- name: "X-Cannot-Reproduce"
+ color: "ff7979"
+- name: "X-Command"
+ description: "Created using the !github command"
+ color: "ff7979"
+- name: "X-Community-Supported-Platform"
+ description: "This issue occurs in a platform not directly supported by us, but by a community project elsewhere"
+ color: "ff7979"
+- name: "X-Upcoming-Release-Blocker"
+ description: "This does not affect the current release cycle but will affect the next one"
+ color: "e99695"
+- name: "Z-Actions"
+ color: "ededed"
+- name: "Z-Cache-Confusion"
+ description: "Related to internal cache (clearing helps / causes the issue)"
+ color: "ededed"
+- name: "Z-Community-PR"
+ description: "Issue is solved by a community member's PR"
+ color: "ededed"
+- name: "Z-Element-R-Blocker"
+ description: "A blocker for enabling Element R by default"
+ color: "ededed"
+- name: "Z-Experimental"
+ color: "ededed"
+- name: "Z-Fixed by Element Call"
+ description: "Issues which can be closed when we move to Element Call"
+ color: "ededed"
+- name: "Z-Fixed-By-OIDC"
+ description: "Issues which can be closed when we move to OIDC"
+ color: "ededed"
+- name: "Z-Flaky-Test"
+ description: "A test is raising false alarms"
+ color: "ededed"
+- name: "Z-Flaky-Jest-Test"
+ description: "A Jest test is raising false alarms"
+ color: "ededed"
+- name: "Z-FOSDEM"
+ description: "Issues in chat.fosdem.org"
+ color: "ededed"
+- name: "Z-Gitter"
+ description: "Issues relating to or coming out of the Gitter migration, feature parity, etc"
+ color: "ededed"
+- name: "Z-Legacy-Crypto"
+ description: "Issues affecting the legacy crypto stack"
+ color: "EEEEEE"
+- name: "Z-Maximised-Widgets"
+ color: "ededed"
+- name: "Z-Papercuts"
+ description: "Visible. Impactful. Predictable to action."
+ color: "ededed"
+- name: "Z-Power-Users"
+ color: "ededed"
+- name: "Z-Rageshake"
+ description: "Has attached rageshake (not for log submission process)"
+ color: "ededed"
+- name: "Z-RICE"
+ color: "ededed"
+- name: "Z-Soft-Crash"
+ description: "React soft crash caught by an error boundary"
+ color: "ededed"
+- name: "Z-Spec-Compliance"
+ description: "An area where Element doesn't correctly implement the spec"
+ color: "ededed"
+- name: "Z-t3chguy"
+ color: "ededed"
+- name: "Z-Flaky-Test-Disabled"
+ description: "The flaking test has been disabled"
+ color: "ededed"
diff --git a/.github/release-drafter.yml b/.github/release-drafter.yml
new file mode 100644
index 00000000000..5045f2bfc9e
--- /dev/null
+++ b/.github/release-drafter.yml
@@ -0,0 +1,3 @@
+_extends: matrix-org/matrix-js-sdk
+version-resolver:
+ default: patch
diff --git a/.github/renovate.json b/.github/renovate.json
new file mode 100644
index 00000000000..76320426d9e
--- /dev/null
+++ b/.github/renovate.json
@@ -0,0 +1,4 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": ["github>matrix-org/renovate-config-element-web"]
+}
diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml
new file mode 100644
index 00000000000..5a11ad5bbd5
--- /dev/null
+++ b/.github/workflows/backport.yml
@@ -0,0 +1,32 @@
+name: Backport
+on:
+ pull_request_target:
+ 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 }}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 19afbe5c32d..381755b6067 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build
on:
pull_request: {}
push:
- branches: [release, master]
+ branches: [develop, master]
merge_group:
types: [checks_requested]
# develop pushes and repository_dispatch handled in build_develop.yaml
@@ -20,8 +20,8 @@ jobs:
matrix:
image:
- ubuntu-24.04
- # - windows-2022
- # - macos-14
+ - windows-2022
+ - macos-14
isDevelop:
- ${{ github.event_name == 'push' && github.ref_name == 'develop' }}
# Skip the ubuntu-24.04 build for the develop branch as the dedicated CD build_develop workflow handles that
@@ -46,107 +46,5 @@ jobs:
- name: Install Dependencies
run: "./scripts/layered.sh"
- # - name: Build
- # run: "yarn build"
-
- # Build step from develop workflow
- - name: Build and Package
- run: "./scripts/ci_package.sh"
-
- - run: mv dist/elecord-*.tar.gz dist/elecord.tar.gz
-
- - name: Upload Artifact
- uses: actions/upload-artifact@v4
- with:
- name: webapp
- path: dist/elecord.tar.gz
-
- deploy:
- needs: build
- name: Deploy webapp
- runs-on: ubuntu-24.04
- permissions:
- pull-requests: write
- steps:
- # Download and extract the build folder
- - name: Download Artifact
- uses: actions/download-artifact@v4
- with:
- name: webapp
- path: .
-
- - name: Extract tarball
- run: tar -xvzf elecord.tar.gz
-
- - name: Move to dist
- run: mv elecord-*/ dist
-
- # Set the Cloudflare pages branch name
- # - PR to master : test
- # - Push to master : dev
- # - PR to release : preview
- # - Push to release : release (Production environment)
- - name: Set Pages branch
- run: |
- if [[ "${{ github.event_name }}" == "pull_request" ]]; then
- if [[ "${{ github.event.pull_request.base.ref }}" == "master" ]]; then
- echo "cf_branch=test" >> $GITHUB_ENV
- elif [[ "${{ github.event.pull_request.base.ref }}" == "release" ]]; then
- echo "cf_branch=preview" >> $GITHUB_ENV
- fi
- elif [[ "${{ github.event_name }}" == "push" ]]; then
- if [[ "${{ github.ref_name }}" == "master" ]]; then
- echo "cf_branch=dev" >> $GITHUB_ENV
- elif [[ "${{ github.ref_name }}" == "release" ]]; then
- echo "cf_branch=release" >> $GITHUB_ENV
- fi
- fi
-
- # Deploy to Cloudflare Pages (using wrangler)
- - name: Deploy to Cloudflare Pages
- id: cf
- uses: cloudflare/wrangler-action@v3
- with:
- apiToken: ${{ secrets.CLOUDFLARE_PAGES_API_TOKEN }}
- accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
- command: pages deploy dist --project-name=elecord-web --branch=${{ env.cf_branch }}
-
- # Update PR status comment with deployment URL
- - name: Update status comment (Failure)
- if: ${{ github.event_name == 'pull_request' && failure() }}
- uses: thollander/actions-comment-pull-request@v3
- with:
- message: |
- ## Cloudflare Pages deployment
-
- | **Latest commit** | ${{ github.event.pull_request.head.sha || github.sha }}
|
- |-------------------|:-:|
- | **Status** | ❌ Failure. Check workflow logs for details |
- | **Preview URL** | Not available |
- pr-number: ${{ github.event.pull_request.number }}
- comment-tag: CFPages-deployment
- reactions: -1
- mode: recreate
-
- # When deploying to the preview branch, add the preview URL
- - name: Add preview URL
- run: |
- if [[ "${{ env.cf_branch }}" == "preview" ]]; then
- echo -e "cf_preview=https://preview.elecord.app\n" >> $GITHUB_ENV
- fi
-
- - name: Update status comment (Success)
- if: ${{ github.event_name == 'pull_request' && success() }}
- uses: thollander/actions-comment-pull-request@v3
- with:
- message: |
- ## Cloudflare Pages deployment
-
- | **Latest commit** | ${{ github.event.pull_request.head.sha || github.sha }}
|
- |-------------------|:-:|
- | **Status** | ✅ Deployed! |
- | **URL** | ${{ env.cf_preview }}${{ steps.cf.outputs.deployment-url != '' && steps.cf.outputs.deployment-url || 'Not available' }} |
- pr-number: ${{ github.event.pull_request.number }}
- comment-tag: CFPages-deployment
- reactions: rocket
- mode: recreate
\ No newline at end of file
+ - name: Build
+ run: "yarn build"
diff --git a/.github/workflows/build_debian.yaml b/.github/workflows/build_debian.yaml
new file mode 100644
index 00000000000..f46678512a7
--- /dev/null
+++ b/.github/workflows/build_debian.yaml
@@ -0,0 +1,79 @@
+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@v4
+
+ - name: Download package
+ 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
+ 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
+ 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
+ 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 $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
+ 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@v4
+ with:
+ name: element-web.deb
+ path: 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
+ with:
+ file: 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 }}
diff --git a/.github/workflows/build_develop.yml b/.github/workflows/build_develop.yml
new file mode 100644
index 00000000000..8bbcfe726f5
--- /dev/null
+++ b/.github/workflows/build_develop.yml
@@ -0,0 +1,131 @@
+# 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
+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@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ node-version: "lts/*"
+
+ - name: Install Dependencies
+ run: "./scripts/layered.sh"
+
+ - name: Build, Package & Upload sourcemaps
+ 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
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: webapp
+ path: dist/develop.tar.gz
+ retention-days: 1
+
+ - name: Extract webapp
+ run: |
+ mkdir _deploy
+ tar xf dist/develop.tar.gz -C _deploy --strip-components=1
+
+ - name: Copy config
+ run: cp 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).)*$
+
+ # 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.
+ - name: Deploy to R2
+ run: |
+ aws s3 cp dist/develop.tar.gz s3://$R2_BUCKET/develop.tar.gz --endpoint-url $R2_URL --region=auto
+ aws s3 cp _deploy/ s3://$R2_BUCKET/ --recursive --endpoint-url $R2_URL --region=auto
+ 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
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 00000000000..ac6249d654a
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,91 @@
+# 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
+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@v4
+
+ - 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: |
+ echo "version=v$(curl -s https://$SITE/version)" >> $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: ${{ 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 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
+ 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 }}
diff --git a/.github/workflows/dockerhub.yaml b/.github/workflows/dockerhub.yaml
new file mode 100644
index 00000000000..7911cf794a9
--- /dev/null
+++ b/.github/workflows/dockerhub.yaml
@@ -0,0 +1,79 @@
+name: Dockerhub
+on:
+ workflow_dispatch: {}
+ push:
+ tags: [v*]
+ 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: dockerhub
+ permissions:
+ id-token: write # needed for signing the images with GitHub OIDC Token
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ fetch-depth: 0 # needed for docker-package to be able to calculate the version
+
+ - name: Install Cosign
+ uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3
+
+ - name: Set up QEMU
+ uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@c47758b77c9736f4b2ef4073d4d51994fabfe349 # v3
+ with:
+ install: true
+
+ - name: Login to Docker Hub
+ uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Docker meta
+ id: meta
+ uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5
+ with:
+ images: |
+ vectorim/element-web
+ tags: |
+ type=ref,event=branch
+ type=ref,event=tag
+ flavor: |
+ latest=${{ contains(github.ref_name, '-rc.') && 'false' || 'auto' }}
+
+ - name: Build and push
+ id: build-and-push
+ uses: docker/build-push-action@4f58ea79222b3b9dc2c8bbdd6debcef730109a75 # v6
+ with:
+ context: .
+ 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 }}
+ run: |
+ images=""
+ for tag in ${TAGS}; do
+ images+="${tag}@${DIGEST} "
+ done
+ cosign sign --yes ${images}
+
+ - name: Update repo description
+ uses: peter-evans/dockerhub-description@e98e4d1628a5f3be2be7c231e50981aee98723ae # v4
+ continue-on-error: true
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+ repository: vectorim/element-web
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
new file mode 100644
index 00000000000..a301b6daf6f
--- /dev/null
+++ b/.github/workflows/docs.yml
@@ -0,0 +1,107 @@
+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:
+ - name: Fetch element-desktop
+ uses: actions/checkout@v4
+ with:
+ repository: element-hq/element-desktop
+ path: element-desktop
+
+ - name: Fetch element-web
+ uses: actions/checkout@v4
+ with:
+ path: element-web
+
+ - name: Fetch matrix-js-sdk
+ uses: actions/checkout@v4
+ with:
+ repository: matrix-org/matrix-js-sdk
+ path: matrix-js-sdk
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ cache-dependency-path: element-web/yarn.lock
+ node-version: "lts/*"
+
+ - name: Generate automations docs
+ working-directory: element-web
+ run: |
+ yarn install --frozen-lockfile
+ yarn ts-node ./scripts/gen-workflow-mermaid.ts ../element-desktop ../element-web ../matrix-js-sdk > docs/automations.md
+ echo "- [Automations](automations.md)" >> docs/SUMMARY.md
+
+ - name: Setup mdBook
+ uses: peaceiris/actions-mdbook@v2
+ with:
+ mdbook-version: "0.4.10"
+
+ - name: Install mdbook extensions
+ run: cargo install mdbook-combiner mdbook-mermaid
+
+ - name: Prepare docs
+ run: |
+ mkdir docs
+
+ mv element-desktop/README.md element-desktop/docs/
+ mv element-desktop/docs "docs/Element Desktop"
+
+ mv element-web/README.md element-web/docs/
+ mv element-web/docs/lib docs/
+ mv element-web/docs "docs/Element Web"
+
+ mv matrix-js-sdk/README.md matrix-js-sdk/docs/
+ mv matrix-js-sdk/docs "docs/Matrix JS SDK"
+
+ sed -i -e 's/\.\.\/README.md/README.md/' docs/**/SUMMARY.md
+
+ mdbook-combiner -m docs
+ sed -i -E 's/^\t# (.+)$/- [\1]()/gm;t' SUMMARY.md
+ sed -i -E 's/^- \[(.+)]\(<>\)$/---\n# \1/gm;t' SUMMARY.md
+ sed -i -E 's/\t- \[Introduction]/- [Introduction]/gm;t' SUMMARY.md
+
+ cat < docs/SUMMARY.md
+ # Summary
+ - [Introduction]()
+
+ EOF
+ cat SUMMARY.md >> docs/SUMMARY.md
+
+ mv element-web/book.toml .
+
+ - name: Build docs
+ run: mdbook build
+
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./book
+
+ 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@v4
diff --git a/.github/workflows/end-to-end-tests-netlify.yaml b/.github/workflows/end-to-end-tests-netlify.yaml
new file mode 100644
index 00000000000..e25994ec9df
--- /dev/null
+++ b/.github/workflows/end-to-end-tests-netlify.yaml
@@ -0,0 +1,46 @@
+# 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:
+ workflow_run:
+ workflows: ["End to End 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 != '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@v4
+ 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@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-"
diff --git a/.github/workflows/end-to-end-tests.yaml b/.github/workflows/end-to-end-tests.yaml
new file mode 100644
index 00000000000..1a31f750656
--- /dev/null
+++ b/.github/workflows/end-to-end-tests.yaml
@@ -0,0 +1,191 @@
+# Produce a build of element-web with this version of react-sdk
+# and any matching branches of element-web and js-sdk, output it
+# as an artifact and run end-to-end tests.
+name: End to End Tests
+on:
+ pull_request: {}
+ merge_group:
+ types: [checks_requested]
+ push:
+ branches: [develop, master]
+ repository_dispatch:
+ types: [element-web-notify]
+
+ # 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 }}
+
+permissions: {} # No permissions required
+
+jobs:
+ build:
+ name: "Build Element-Web"
+ runs-on: ubuntu-24.04
+ if: inputs.skip != true
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ repository: element-hq/element-web
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ node-version: "lts/*"
+
+ - name: Fetch layered build
+ id: 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
+ JSSDK_SHA=$(git -C matrix-js-sdk rev-parse --short=12 HEAD)
+ VECTOR_SHA=$(git rev-parse --short=12 HEAD)
+ echo "VERSION=$VECTOR_SHA--js-$JSSDK_SHA" >> $GITHUB_OUTPUT
+
+ - name: Copy config
+ run: cp element.io/develop/config.json config.json
+
+ - name: Build
+ env:
+ CI_PACKAGE: true
+ VERSION: "${{ steps.layered_build.outputs.VERSION }}"
+ run: |
+ yarn build
+
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4
+ with:
+ name: webapp
+ path: webapp
+ retention-days: 1
+
+ playwright:
+ name: "Run Tests ${{ matrix.runner }}/${{ strategy.job-total }}"
+ needs: build
+ if: inputs.skip != true
+ runs-on: ubuntu-22.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: [1, 2, 3, 4, 5, 6]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ persist-credentials: false
+ repository: element-hq/element-web
+
+ - name: 📥 Download artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: webapp
+ path: webapp
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ cache-dependency-path: yarn.lock
+ node-version: "lts/*"
+
+ - name: Install dependencies
+ run: yarn install --frozen-lockfile
+
+ - name: Get installed Playwright version
+ id: playwright
+ run: echo "version=$(yarn list --pattern @playwright/test --depth=0 --json --non-interactive --no-progress | jq -r '.data.trees[].name')" >> $GITHUB_OUTPUT
+
+ - name: Cache playwright binaries
+ uses: actions/cache@v4
+ id: playwright-cache
+ with:
+ path: |
+ ~/.cache/ms-playwright
+ key: ${{ runner.os }}-playwright-${{ steps.playwright.outputs.version }}
+
+ - name: Install Playwright browsers
+ if: steps.playwright-cache.outputs.cache-hit != 'true'
+ run: yarn playwright install --with-deps
+
+ - name: Run Playwright tests
+ run: yarn playwright test --shard ${{ matrix.runner }}/${{ strategy.job-total }}
+
+ - name: Upload blob report to GitHub Actions Artifacts
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: all-blob-reports-${{ matrix.runner }}
+ path: blob-report
+ retention-days: 1
+
+ complete:
+ name: end-to-end-tests
+ needs: playwright
+ if: always()
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+ if: inputs.skip != true
+ with:
+ persist-credentials: false
+ repository: element-hq/element-web
+
+ - uses: actions/setup-node@v4
+ if: inputs.skip != true
+ with:
+ cache: "yarn"
+ node-version: "lts/*"
+
+ - name: Install dependencies
+ if: inputs.skip != true
+ run: yarn install --frozen-lockfile
+
+ - name: Download blob reports from GitHub Actions Artifacts
+ if: inputs.skip != true
+ uses: actions/download-artifact@v4
+ with:
+ pattern: all-blob-reports-*
+ path: all-blob-reports
+ merge-multiple: true
+
+ - name: Merge into HTML Report
+ if: inputs.skip != true
+ run: yarn playwright merge-reports --reporter=html,./playwright/flaky-reporter.ts,./playwright/stale-screenshot-reporter.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 || '' }}
+
+ # 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() && inputs.skip != true
+ uses: actions/upload-artifact@v4
+ with:
+ name: html-report
+ path: playwright-report
+ retention-days: 14
+
+ - if: needs.playwright.result != 'skipped' && needs.playwright.result != 'success'
+ run: exit 1
diff --git a/.github/workflows/issue_closed.yml b/.github/workflows/issue_closed.yml
new file mode 100644
index 00000000000..2cffae0011a
--- /dev/null
+++ b/.github/workflows/issue_closed.yml
@@ -0,0 +1,157 @@
+# 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@v7
+ 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@v7
+ 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,
+ });
diff --git a/.github/workflows/localazy_download.yaml b/.github/workflows/localazy_download.yaml
new file mode 100644
index 00000000000..435b8154ba5
--- /dev/null
+++ b/.github/workflows/localazy_download.yaml
@@ -0,0 +1,11 @@
+name: Localazy Download
+on:
+ workflow_dispatch: {}
+ schedule:
+ - cron: "0 6 * * 1,3,5" # Every Monday, Wednesday and Friday at 6am UTC
+permissions: {} # We use ELEMENT_BOT_TOKEN instead
+jobs:
+ download:
+ uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main
+ secrets:
+ ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
diff --git a/.github/workflows/localazy_upload.yaml b/.github/workflows/localazy_upload.yaml
new file mode 100644
index 00000000000..8cb77439680
--- /dev/null
+++ b/.github/workflows/localazy_upload.yaml
@@ -0,0 +1,12 @@
+name: Localazy Upload
+on:
+ push:
+ branches: [develop]
+ paths:
+ - "src/i18n/strings/en_EN.json"
+permissions: {} # No permissions needed
+jobs:
+ upload:
+ uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_upload.yaml@main
+ secrets:
+ LOCALAZY_WRITE_KEY: ${{ secrets.LOCALAZY_WRITE_KEY }}
diff --git a/.github/workflows/netlify.yaml b/.github/workflows/netlify.yaml
new file mode 100644
index 00000000000..63bac7d33f5
--- /dev/null
+++ b/.github/workflows/netlify.yaml
@@ -0,0 +1,51 @@
+# Triggers after the layered build has finished, taking the artifact
+# and uploading it to netlify
+name: Upload Preview Build to Netlify
+on:
+ workflow_run:
+ workflows: ["End to End Tests"]
+ 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@v4
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ run-id: ${{ github.event.workflow_run.id }}
+ name: webapp
+ path: webapp
+
+ - name: 📤 Deploy to Netlify
+ uses: matrix-org/netlify-pr-preview@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.
diff --git a/.github/workflows/pending-reviews.yaml b/.github/workflows/pending-reviews.yaml
new file mode 100644
index 00000000000..c96ed3f17e8
--- /dev/null
+++ b/.github/workflows/pending-reviews.yaml
@@ -0,0 +1,92 @@
+name: Pending reviews automation
+on:
+ # The bot exceeded its API rate limit. Disabling for now (adding workflow dispatch so the workflow file stays valid & we can test to see if it starts working again)
+ workflow_dispatch: {}
+ # We run it on a schedule instead of on pull_request_* events to not create confusing messaging in the PR
+ #schedule:
+ # - cron: "*/10 * * * *"
+concurrency: ${{ github.workflow }}
+permissions: {} # We use ELEMENT_BOT_TOKEN instead
+jobs:
+ bot:
+ name: Pending reviews bot
+ runs-on: ubuntu-24.04
+ environment: Matrix
+ env:
+ URL: "https://github.com/pulls?q=is%3Apr+is%3Aopen+repo%3Amatrix-org%2Fmatrix-js-sdk+repo%3Amatrix-org%2Fmatrix-react-sdk+repo%3Aelement-hq%2Felement-web+repo%3Aelement-hq%2Felement-desktop+review-requested%3A%40me+sort%3Aupdated-desc+"
+ RELEASE_BLOCKERS_URL: "https://github.com/pulls?q=is%3Aopen+repo%3Amatrix-org%2Fmatrix-js-sdk+repo%3Amatrix-org%2Fmatrix-react-sdk+repo%3Aelement-hq%2Felement-web+repo%3Aelement-hq%2Felement-desktop+sort%3Aupdated-desc+label%3AX-Release-Blocker+"
+ steps:
+ - uses: actions/github-script@v7
+ env:
+ HS_URL: ${{ secrets.BETABOT_HS_URL }}
+ ROOM_ID: ${{ secrets.ROOM_ID }}
+ TOKEN: ${{ secrets.BETABOT_ACCESS_TOKEN }}
+ 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 { HS_URL, ROOM_ID, TOKEN, URL, RELEASE_BLOCKERS_URL } = process.env;
+
+ async function updateCounter(counter, link, severity, title, value, clearOnZero) {
+ const apiUrl = `${HS_URL}/_matrix/client/v3/rooms/${ROOM_ID}/state/re.jki.counter/${counter}`;
+ const headers = {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${TOKEN}`,
+ };
+ const res = await fetch(apiUrl, {
+ method: "GET",
+ headers,
+ });
+
+ const data = await res.json();
+
+ if (data.value === issueCount) {
+ console.log("Pending review count already correct");
+ return;
+ }
+
+ let body = {};
+ if (issueCount || !clearOnZero) {
+ body = JSON.stringify({
+ link,
+ severity,
+ title,
+ value,
+ });
+ }
+
+ await fetch(apiUrl, {
+ method: "PUT",
+ body,
+ headers,
+ });
+ }
+
+ const repos = [
+ "element-hq/element-desktop",
+ "element-hq/element-web",
+ "matrix-org/matrix-js-sdk",
+ ];
+ const teams = [
+ "matrix-org/element-web-team",
+ "matrix-org/element-web-reviewers",
+ "element-hq/element-web-team",
+ "element-hq/element-web-reviewers",
+ ];
+
+ let issueCount = 0;
+ for (const team of teams) {
+ const org = team.split("/", 2)[0];
+ const reposInOrg = repos.filter(repo => repo.startsWith(org + "/"));
+ const { data } = await github.rest.search.issuesAndPullRequests({
+ q: `is:pr is:open review:required ${reposInOrg.map(r => `repo:${r}`).join(" ")} team-review-requested:${team}`,
+ });
+ issueCount += data.total_count;
+ }
+ await updateCounter("gh_reviews", URL, "warning", "Pending reviews", issueCount);
+
+ const { data } = await github.rest.search.issuesAndPullRequests({
+ q: `is:open ${repos.map(repo => `repo:${repo}`).join(" ")} label:X-Release-Blocker`,
+ });
+ const blockerCount = data.total_count;
+ await updateCounter("release_blockers", RELEASE_BLOCKERS_URL, "alert", "Release Blockers", blockerCount, true);
diff --git a/.github/workflows/playwright-image-updates.yaml b/.github/workflows/playwright-image-updates.yaml
new file mode 100644
index 00000000000..1613b42dfb7
--- /dev/null
+++ b/.github/workflows/playwright-image-updates.yaml
@@ -0,0 +1,48 @@
+name: Update Playwright docker images
+on:
+ workflow_dispatch: {}
+ schedule:
+ - cron: "0 6 * * *" # Every day at 6am UTC
+permissions: {}
+jobs:
+ update:
+ runs-on: ubuntu-24.04
+ permissions:
+ pull-requests: write
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Update synapse image
+ run: |
+ docker pull "$IMAGE"
+ INSPECT=$(docker inspect --format='{{index .RepoDigests 0}}' "$IMAGE")
+ DIGEST=${INSPECT#*@}
+ sed -i "s/const DOCKER_TAG.*/const DOCKER_TAG = \"develop@$DIGEST\";/" playwright/plugins/homeserver/synapse/index.ts
+ env:
+ IMAGE: ghcr.io/element-hq/synapse:develop
+
+ - name: Create Pull Request
+ id: cpr
+ uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7
+ with:
+ token: ${{ secrets.ELEMENT_BOT_TOKEN }}
+ branch: actions/playwright-image-updates
+ delete-branch: true
+ title: Playwright Docker image updates
+ labels: |
+ T-Task
+
+ - name: Enable automerge
+ run: gh pr merge --merge --auto "$PR_NUMBER"
+ if: steps.cpr.outputs.pull-request-operation == 'created'
+ env:
+ GH_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
+ PR_NUMBER: ${{ steps.cpr.outputs.pull-request-number }}
+
+ - name: Enable autoapprove
+ run: |
+ gh pr review --approve "$PR_NUMBER"
+ if: steps.cpr.outputs.pull-request-operation == 'created'
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ PR_NUMBER: ${{ steps.cpr.outputs.pull-request-number }}
diff --git a/.github/workflows/pull_request.yaml b/.github/workflows/pull_request.yaml
new file mode 100644
index 00000000000..2f97ccbbb41
--- /dev/null
+++ b/.github/workflows/pull_request.yaml
@@ -0,0 +1,14 @@
+name: Pull Request
+on:
+ pull_request_target:
+ 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
+ permissions:
+ pull-requests: read
+ secrets:
+ ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
diff --git a/.github/workflows/pull_request_base_branch.yaml b/.github/workflows/pull_request_base_branch.yaml
new file mode 100644
index 00000000000..6610ee48791
--- /dev/null
+++ b/.github/workflows/pull_request_base_branch.yaml
@@ -0,0 +1,17 @@
+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@v7
+ with:
+ script: |
+ const baseBranch = context.payload.pull_request.base.ref;
+ if (!['develop', 'staging'].includes(baseBranch) && !baseBranch.startsWith('feat/')) {
+ core.setFailed(`Invalid base branch: ${baseBranch}`);
+ }
diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml
new file mode 100644
index 00000000000..c4bf8e6ab30
--- /dev/null
+++ b/.github/workflows/release-drafter.yml
@@ -0,0 +1,12 @@
+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
diff --git a/.github/workflows/release-gitflow.yml b/.github/workflows/release-gitflow.yml
new file mode 100644
index 00000000000..128c6a1e05d
--- /dev/null
+++ b/.github/workflows/release-gitflow.yml
@@ -0,0 +1,15 @@
+# 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
+ secrets:
+ ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
+ with:
+ dependencies: |
+ matrix-js-sdk
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 00000000000..2ecc4a46626
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,68 @@
+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
+ permissions:
+ contents: write
+ issues: 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
+
+ notify-downstream:
+ name: Trigger release drafter downstream
+ needs: release
+ runs-on: ubuntu-24.04
+ steps:
+ - name: Notify element-desktop repo that element-web release has completed to re-trigger release-drafter
+ uses: benc-uk/workflow-dispatch@e2e5e9a103e331dad343f381a29e654aea3cf8fc # v1
+ with:
+ workflow: release-drafter.yml
+ repo: element-hq/element-desktop
+ ref: staging
+ # Required when using the `repo` option. Either a PAT or a token generated from the GitHub app or CLI
+ token: "${{ secrets.ELEMENT_BOT_TOKEN }}"
+
+ check:
+ name: Post release checks
+ needs: release
+ runs-on: ubuntu-24.04
+ permissions:
+ checks: read
+ steps:
+ - name: Wait for dockerhub
+ 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
diff --git a/.github/workflows/release_prepare.yml b/.github/workflows/release_prepare.yml
new file mode 100644
index 00000000000..b655bb42061
--- /dev/null
+++ b/.github/workflows/release_prepare.yml
@@ -0,0 +1,111 @@
+name: Cut branches
+on:
+ workflow_dispatch:
+ inputs:
+ element-desktop:
+ description: Prepare element-desktop
+ required: true
+ type: boolean
+ default: true
+ 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:
+ prepare:
+ runs-on: ubuntu-24.04
+ env:
+ # The order is specified bottom-up to avoid any races for allchange
+ REPOS: matrix-js-sdk element-web element-desktop
+ steps:
+ - name: Checkout Element Desktop
+ uses: actions/checkout@v4
+ if: inputs.element-desktop
+ with:
+ repository: element-hq/element-desktop
+ path: element-desktop
+ ref: staging
+ fetch-depth: 0
+ fetch-tags: true
+ token: ${{ secrets.ELEMENT_BOT_TOKEN }}
+ - name: Checkout Element Web
+ uses: actions/checkout@v4
+ 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 }}
+ - name: Checkout Matrix JS SDK
+ uses: actions/checkout@v4
+ 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 }}
+
+ - name: Prepare Git
+ run: |
+ git config --global user.email "releases@riot.im"
+ git config --global user.name "RiotRobot"
+
+ - name: Merge Element Desktop
+ if: inputs.element-desktop
+ run: |
+ git -C "element-desktop" merge origin/develop
+ - 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
+ 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
+ allowed-conclusions: success
+
+ - name: Wait for element-desktop draft
+ if: inputs.element-desktop
+ uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
+ with:
+ ref: staging
+ repo: element-hq/element-desktop
+ repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
+ wait-interval: 10
+ check-name: draft
+ allowed-conclusions: success
diff --git a/.github/workflows/sonarqube.yml b/.github/workflows/sonarqube.yml
new file mode 100644
index 00000000000..0ee457bac21
--- /dev/null
+++ b/.github/workflows/sonarqube.yml
@@ -0,0 +1,24 @@
+name: SonarQube
+on:
+ workflow_run:
+ 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
+ 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
diff --git a/.github/workflows/static_analysis.yaml b/.github/workflows/static_analysis.yaml
new file mode 100644
index 00000000000..b7c02c3f2e9
--- /dev/null
+++ b/.github/workflows/static_analysis.yaml
@@ -0,0 +1,140 @@
+name: Static Analysis
+on:
+ pull_request: {}
+ push:
+ branches: [develop, master]
+ merge_group:
+ types: [checks_requested]
+ repository_dispatch:
+ types: [element-web-notify]
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
+ cancel-in-progress: true
+
+env:
+ # These must be set for fetchdep.sh to get the right branch
+ REPOSITORY: ${{ github.repository }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+
+permissions: {} # No permissions required
+
+jobs:
+ ts_lint:
+ name: "Typescript Syntax Check"
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ node-version: "lts/*"
+
+ - name: Install Dependencies
+ run: "./scripts/layered.sh"
+
+ - name: Typecheck
+ run: "yarn run lint:types"
+
+ i18n_lint:
+ name: "i18n Check"
+ uses: matrix-org/matrix-web-i18n/.github/workflows/i18n_check.yml@main
+ permissions:
+ pull-requests: read
+ with:
+ hardcoded-words: "Element"
+ 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_to_element
+
+ rethemendex_lint:
+ name: "Rethemendex Check"
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+
+ - run: ./res/css/rethemendex.sh
+
+ - run: git diff --exit-code
+
+ js_lint:
+ name: "ESLint"
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ node-version: "lts/*"
+
+ # Does not need branch matching as only analyses this layer
+ - name: Install Deps
+ run: "yarn install --frozen-lockfile"
+
+ - name: Run Linter
+ run: "yarn run lint:js"
+
+ style_lint:
+ name: "Style Lint"
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ node-version: "lts/*"
+
+ # Does not need branch matching as only analyses this layer
+ - name: Install Deps
+ run: "yarn install"
+
+ - name: Run Linter
+ run: "yarn run lint:style"
+
+ workflow_lint:
+ name: "Workflow Lint"
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ node-version: "lts/*"
+
+ # Does not need branch matching as only analyses this layer
+ - name: Install Deps
+ run: "yarn install --frozen-lockfile"
+
+ - name: Run Linter
+ run: "yarn lint:workflows"
+
+ analyse_dead_code:
+ name: "Analyse Dead Code"
+ runs-on: ubuntu-24.04
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ node-version: "lts/*"
+
+ - name: Install Deps
+ run: "yarn install --frozen-lockfile"
+
+ - name: Run linter
+ run: "yarn run lint:knip"
+
+ - name: Install Deps
+ run: "scripts/layered.sh"
+
+ - name: Dead Code Analysis
+ run: "yarn run analyse:unused-exports"
diff --git a/.github/workflows/sync-labels.yml b/.github/workflows/sync-labels.yml
new file mode 100644
index 00000000000..fa1be485bb0
--- /dev/null
+++ b/.github/workflows/sync-labels.yml
@@ -0,0 +1,24 @@
+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@develop
+ with:
+ LABELS: |
+ element-hq/element-meta
+ .github/labels.yml
+ DELETE: true
+ WET: true
+ secrets:
+ ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 00000000000..14fd5ffd648
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,114 @@
+name: Tests
+on:
+ pull_request: {}
+ merge_group:
+ types: [checks_requested]
+ push:
+ branches: [develop, master]
+ repository_dispatch:
+ types: [element-web-notify]
+ 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 }}
+
+permissions: {}
+
+jobs:
+ jest:
+ name: Jest
+ 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@v4
+ with:
+ repository: ${{ inputs.matrix-js-sdk-sha && 'element-hq/element-web' || github.repository }}
+
+ - name: Yarn cache
+ uses: actions/setup-node@v4
+ with:
+ node-version: "lts/*"
+ cache: "yarn"
+
+ - name: Install Deps
+ run: "./scripts/layered.sh"
+ env:
+ JS_SDK_GITHUB_BASE_REF: ${{ inputs.matrix-js-sdk-sha }}
+
+ - name: Jest Cache
+ uses: actions/cache@v4
+ with:
+ path: /tmp/jest_cache
+ key: ${{ hashFiles('**/yarn.lock') }}
+
+ - name: Get number of CPU cores
+ id: cpu-cores
+ uses: SimenB/github-actions-cpu-cores@97ba232459a8e02ff6121db9362b09661c875ab8 # v2
+
+ - name: Run tests
+ run: |
+ yarn test \
+ --coverage=${{ env.ENABLE_COVERAGE }} \
+ --ci \
+ --max-workers ${{ steps.cpu-cores.outputs.count }} \
+ --shard ${{ matrix.runner }}/${{ strategy.job-total }} \
+ --cacheDirectory /tmp/jest_cache
+ env:
+ JEST_SONAR_UNIQUE_OUTPUT_NAME: true
+
+ # tell jest to use coloured output
+ FORCE_COLOR: true
+
+ - name: Move coverage files into place
+ if: env.ENABLE_COVERAGE == 'true'
+ run: mv coverage/lcov.info coverage/${{ steps.setupNode.outputs.node-version }}-${{ matrix.runner }}.lcov.info
+
+ - name: Upload Artifact
+ if: env.ENABLE_COVERAGE == 'true'
+ uses: actions/upload-artifact@v4
+ with:
+ name: coverage-${{ matrix.runner }}
+ path: |
+ coverage
+ !coverage/lcov-report
+
+ complete:
+ name: jest-tests
+ needs: jest
+ if: always()
+ runs-on: ubuntu-24.04
+ permissions:
+ statuses: write
+ steps:
+ - if: needs.jest.result != 'skipped' && needs.jest.result != 'success'
+ 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@1f26a0237cd1a57626fbb5a0eb2494c9b8797d07
+ 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 }}
diff --git a/.github/workflows/triage-assigned.yml b/.github/workflows/triage-assigned.yml
new file mode 100644
index 00000000000..e43eb946183
--- /dev/null
+++ b/.github/workflows/triage-assigned.yml
@@ -0,0 +1,20 @@
+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, 'andybalaam') ||
+ contains(github.event.issue.assignees.*.login, 'MidhunSureshR')
+ steps:
+ - uses: actions/add-to-project@main
+ with:
+ project-url: https://github.com/orgs/element-hq/projects/67
+ github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
diff --git a/.github/workflows/triage-incoming.yml b/.github/workflows/triage-incoming.yml
new file mode 100644
index 00000000000..b084b4d55eb
--- /dev/null
+++ b/.github/workflows/triage-incoming.yml
@@ -0,0 +1,16 @@
+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@main
+ with:
+ project-url: https://github.com/orgs/element-hq/projects/120
+ github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml
new file mode 100644
index 00000000000..2cb05a8bcff
--- /dev/null
+++ b/.github/workflows/triage-labelled.yml
@@ -0,0 +1,178 @@
+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@v7
+ 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@v7
+ 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@v1.0.2
+ 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@v1.0.2
+ 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@main
+ 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@main
+ 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@main
+ 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@main
+ with:
+ project-url: https://github.com/orgs/element-hq/projects/41
+ github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
+
+ verticals_feature:
+ name: Add labelled issues to Verticals Feature project
+ runs-on: ubuntu-24.04
+ if: >
+ contains(github.event.issue.labels.*.name, 'Team: Verticals Feature')
+ steps:
+ - uses: actions/add-to-project@main
+ with:
+ project-url: https://github.com/orgs/element-hq/projects/57
+ 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@main
+ with:
+ project-url: https://github.com/orgs/element-hq/projects/101
+ github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
diff --git a/.github/workflows/triage-move-review-requests.yml b/.github/workflows/triage-move-review-requests.yml
new file mode 100644
index 00000000000..d3bcda270bb
--- /dev/null
+++ b/.github/workflows/triage-move-review-requests.yml
@@ -0,0 +1,140 @@
+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@v2.x
+ 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@v2.x
+ 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@v2.x
+ 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@v2.x
+ 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 }}
diff --git a/.github/workflows/triage-stale-flaky-tests.yml b/.github/workflows/triage-stale-flaky-tests.yml
new file mode 100644
index 00000000000..90ba7c40f75
--- /dev/null
+++ b/.github/workflows/triage-stale-flaky-tests.yml
@@ -0,0 +1,19 @@
+name: Close stale flaky issues
+on:
+ schedule:
+ - cron: "30 1 * * *"
+permissions: {}
+jobs:
+ close:
+ runs-on: ubuntu-24.04
+ permissions:
+ actions: write
+ issues: write
+ steps:
+ - uses: actions/stale@v9
+ with:
+ only-labels: "Z-Flaky-Test"
+ days-before-stale: 14
+ days-before-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"
diff --git a/.github/workflows/triage-unlabelled.yml b/.github/workflows/triage-unlabelled.yml
new file mode 100644
index 00000000000..efbf80eea99
--- /dev/null
+++ b/.github/workflows/triage-unlabelled.yml
@@ -0,0 +1,73 @@
+name: Move unlabelled from needs info columns to triaged
+
+on:
+ issues:
+ types: [unlabeled]
+permissions: {}
+jobs:
+ Move_Unabeled_Issue_On_Project_Board:
+ name: Move no longer X-Needs-Info issues to Triaged
+ runs-on: ubuntu-24.04
+ permissions:
+ repository-projects: read
+ if: >
+ ${{
+ !contains(github.event.issue.labels.*.name, 'X-Needs-Info') }}
+ env:
+ BOARD_NAME: "Issue triage"
+ OWNER: ${{ github.repository_owner }}
+ REPO: ${{ github.event.repository.name }}
+ ISSUE: ${{ github.event.issue.number }}
+ steps:
+ - name: Check if issue is already in "${{ env.BOARD_NAME }}"
+ run: |
+ json=$(curl -s -H 'Content-Type: application/json' -H "Authorization: bearer ${{ secrets.GITHUB_TOKEN }}" -X POST -d '{"query": "query($issue: Int!, $owner: String!, $repo: String!) { repository(owner: $owner, name: $repo) { issue(number: $issue) { projectCards { nodes { project { name } isArchived } } } } } ", "variables" : "{ \"issue\": '${ISSUE}', \"owner\": \"'${OWNER}'\", \"repo\": \"'${REPO}'\" }" }' https://api.github.com/graphql)
+ if echo $json | jq '.data.repository.issue.projectCards.nodes | length'; then
+ if [[ $(echo $json | jq '.data.repository.issue.projectCards.nodes[0].project.name') =~ "${BOARD_NAME}" ]]; then
+ if [[ $(echo $json | jq '.data.repository.issue.projectCards.nodes[0].isArchived') == 'true' ]]; then
+ echo "Issue is already in Project '$BOARD_NAME', but is archived - skipping workflow";
+ echo "SKIP_ACTION=true" >> $GITHUB_ENV
+ else
+ echo "Issue is already in Project '$BOARD_NAME', proceeding";
+ echo "ALREADY_IN_BOARD=true" >> $GITHUB_ENV
+ fi
+ else
+ echo "Issue is not in project '$BOARD_NAME', cancelling this workflow"
+ echo "ALREADY_IN_BOARD=false" >> $GITHUB_ENV
+ fi
+ fi
+ - name: Move issue
+ uses: alex-page/github-project-automation-plus@303f24a24c67ce7adf565a07e96720faf126fe36
+ if: ${{ env.ALREADY_IN_BOARD == 'true' && env.SKIP_ACTION != 'true' }}
+ with:
+ project: Issue triage
+ column: Triaged
+ repo-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
+
+ 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@v7
+ with:
+ script: |
+ github.rest.issues.removeLabel({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: ['Z-Labs']
+ })
diff --git a/.github/workflows/update-jitsi.yml b/.github/workflows/update-jitsi.yml
new file mode 100644
index 00000000000..bf0414e73a4
--- /dev/null
+++ b/.github/workflows/update-jitsi.yml
@@ -0,0 +1,33 @@
+# 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@v4
+
+ - uses: actions/setup-node@v4
+ with:
+ cache: "yarn"
+ node-version: "lts/*"
+
+ - name: Install Deps
+ run: "yarn install --frozen-lockfile"
+
+ - name: Fetch Jitsi
+ run: "yarn update:jitsi"
+
+ - name: Create Pull Request
+ uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7
+ with:
+ token: ${{ secrets.ELEMENT_BOT_TOKEN }}
+ branch: actions/jitsi-update
+ delete-branch: true
+ title: Jitsi Update
+ labels: |
+ T-Task
diff --git a/.github/workflows/update-topics.yaml b/.github/workflows/update-topics.yaml
new file mode 100644
index 00000000000..cd6c2fc5533
--- /dev/null
+++ b/.github/workflows/update-topics.yaml
@@ -0,0 +1,103 @@
+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@v7
+ env:
+ HS_URL: ${{ secrets.BETABOT_HS_URL }}
+ LOBBY_ROOM_ID: ${{ secrets.ROOM_ID }}
+ PUBLIC_ROOM_ID: "!YTvKGNlinIzlkMTVRl:matrix.org"
+ ANNOUNCEMENT_ROOM_ID: "!bijaLdadorKgNGtHdA:matrix.org"
+ 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_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);
+
+ 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"].forEach(d => {
+ d.body = d.body.replace(regex, releaseTopic);
+ });
+ }
+
+ res = await fetch(apiUrl, {
+ method: "PUT",
+ body: JSON.stringify({
+ ...data,
+ topic,
+ }),
+ headers,
+ });
+
+ if (res.ok) {
+ console.log(roomId, "topic updated:", topic);
+ } else {
+ console.log(roomId, await res.text());
+ }
+ }
+
+ await updateReleaseInTopic(LOBBY_ROOM_ID);
+ await updateReleaseInTopic(PUBLIC_ROOM_ID);
+ await updateReleaseInTopic(ANNOUNCEMENT_ROOM_ID);
diff --git a/.gitignore b/.gitignore
index 5a140777f37..685a2cc3172 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,8 @@ package-lock.json
electron/dist
electron/pub
**/.idea
+/config.json
+/config.json.*
/config.local*.json
# Legacy skinning file that some people might still have
/src/component-index.js
diff --git a/README.md b/README.md
index 8ca08213ec1..fa4ac89ff99 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,313 @@
-[![Build](https://github.com/elecordapp/elecord-web/actions/workflows/build.yml/badge.svg)](https://github.com/elecordapp/elecord-web/actions/workflows/build.yml)
+[![Chat](https://img.shields.io/matrix/element-web:matrix.org?logo=matrix)](https://matrix.to/#/#element-web:matrix.org)
+![Tests](https://github.com/element-hq/element-web/actions/workflows/tests.yaml/badge.svg)
+![Static Analysis](https://github.com/element-hq/element-web/actions/workflows/static_analysis.yaml/badge.svg)
+[![Localazy](https://img.shields.io/endpoint?url=https%3A%2F%2Fconnect.localazy.com%2Fstatus%2Felement-web%2Fdata%3Fcontent%3Dall%26title%3Dlocalazy%26logo%3Dtrue)](https://localazy.com/p/element-web)
+[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=element-web&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=element-web)
+[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=element-web&metric=coverage)](https://sonarcloud.io/summary/new_code?id=element-web)
+[![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)
-# elecord-web
+# Element
-
+Element (formerly known as Vector and Riot) is a Matrix web client built using the [Matrix
+JS SDK](https://github.com/matrix-org/matrix-js-sdk).
-#### Privacy focused chat app for gamers
+# Supported Environments
-Elecord is a heavily personalised fork of Element, a Matrix web client.
+Element has several tiers of support for different environments:
-For detailed documentation, see [element-web](https://github.com/element-hq/element-web).
+- Supported
+ - Definition:
+ - Issues **actively triaged**, regressions **block** the release
+ - Last 2 major versions of Chrome, Firefox, and Edge on desktop OSes
+ - Last 2 versions of Safari
+ - Latest release of official Element Desktop app on desktop OSes
+ - Desktop OSes means macOS, Windows, and Linux versions for desktop devices
+ that are actively supported by the OS vendor and receive security updates
+- Best effort
+ - Definition:
+ - Issues **accepted**, regressions **do not block** the release
+ - The wider Element Products(including Element Call and the Enterprise Server Suite) do still not officially support these browsers.
+ - The element web project and its contributors should keep the client functioning and gracefully degrade where other sibling features (E.g. Element Call) may not function.
+ - Last major release of Firefox ESR and Chrome/Edge Extended Stable
+- Community Supported
+ - Definition:
+ - Issues **accepted**, regressions **do not block** the release
+ - Community contributions are welcome to support these issues
+ - Mobile web for current stable version of Chrome, Firefox, and Safari on Android, iOS, and iPadOS
+- Not supported
+ - Definition: Issues only affecting unsupported environments are **closed**
+ - Everything else
+
+The period of support for these tiers should last until the releases specified above, plus 1 app release cycle(2 weeks). In the case of Firefox ESR this is extended further to allow it land in Debian Stable.
+
+For accessing Element on an Android or iOS device, we currently recommend the
+native apps [element-android](https://github.com/element-hq/element-android)
+and [element-ios](https://github.com/element-hq/element-ios).
+
+# Getting Started
+
+The easiest way to test Element is to just use the hosted copy at .
+The `develop` branch is continuously deployed to
+for those who like living dangerously.
+
+To host your own instance of Element see [Installing Element Web](docs/install.md).
+
+To install Element as a desktop application, see [Running as a desktop app](#running-as-a-desktop-app) below.
+
+# Important Security Notes
+
+## Separate domains
+
+We do not recommend running Element from the same domain name as your Matrix
+homeserver. The reason is the risk of XSS (cross-site-scripting)
+vulnerabilities that could occur if someone caused Element to load and render
+malicious user generated content from a Matrix API which then had trusted
+access to Element (or other apps) due to sharing the same domain.
+
+We have put some coarse mitigations into place to try to protect against this
+situation, but it's still not good practice to do it in the first place. See
+ for more details.
+
+## Configuration best practices
+
+Unless you have special requirements, you will want to add the following to
+your web server configuration when hosting Element Web:
+
+- The `X-Frame-Options: SAMEORIGIN` header, to prevent Element Web from being
+ framed and protect from [clickjacking][owasp-clickjacking].
+- The `frame-ancestors 'self'` directive to your `Content-Security-Policy`
+ header, as the modern replacement for `X-Frame-Options` (though both should be
+ included since not all browsers support it yet, see
+ [this][owasp-clickjacking-csp]).
+- The `X-Content-Type-Options: nosniff` header, to [disable MIME
+ sniffing][mime-sniffing].
+- The `X-XSS-Protection: 1; mode=block;` header, for basic XSS protection in
+ legacy browsers.
+
+[mime-sniffing]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types#mime_sniffing
+[owasp-clickjacking-csp]: https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html#content-security-policy-frame-ancestors-examples
+[owasp-clickjacking]: https://cheatsheetseries.owasp.org/cheatsheets/Clickjacking_Defense_Cheat_Sheet.html
+
+If you are using nginx, this would look something like the following:
+
+```
+add_header X-Frame-Options SAMEORIGIN;
+add_header X-Content-Type-Options nosniff;
+add_header X-XSS-Protection "1; mode=block";
+add_header Content-Security-Policy "frame-ancestors 'self'";
+```
+
+For Apache, the configuration looks like:
+
+```
+Header set X-Frame-Options SAMEORIGIN
+Header set X-Content-Type-Options nosniff
+Header set X-XSS-Protection "1; mode=block"
+Header set Content-Security-Policy "frame-ancestors 'self'"
+```
+
+Note: In case you are already setting a `Content-Security-Policy` header
+elsewhere, you should modify it to include the `frame-ancestors` directive
+instead of adding that last line.
+
+# Building From Source
+
+Element is a modular webapp built with modern ES6 and uses a Node.js build system.
+Ensure you have the latest LTS version of Node.js installed.
+
+Using `yarn` instead of `npm` is recommended. Please see the Yarn [install
+guide](https://classic.yarnpkg.com/en/docs/install) if you do not have it already.
+
+1. Install or update `node.js` so that your `node` is at least the current recommended LTS.
+1. Install `yarn` if not present already.
+1. Clone the repo: `git clone https://github.com/element-hq/element-web.git`.
+1. Switch to the element-web directory: `cd element-web`.
+1. Install the prerequisites: `yarn install`.
+ - If you're using the `develop` branch, then it is recommended to set up a
+ proper development environment (see [Setting up a dev
+ environment](#setting-up-a-dev-environment) below). Alternatively, you
+ can use - the continuous integration release of
+ the develop branch.
+1. Configure the app by copying `config.sample.json` to `config.json` and
+ modifying it. See the [configuration docs](docs/config.md) for details.
+1. `yarn dist` to build a tarball to deploy. Untaring this file will give
+ a version-specific directory containing all the files that need to go on your
+ web server.
+
+Note that `yarn dist` is not supported on Windows, so Windows users can run `yarn build`,
+which will build all the necessary files into the `webapp` directory. The version of Element
+will not appear in Settings without using the dist script. You can then mount the
+`webapp` directory on your web server to actually serve up the app, which is
+entirely static content.
+
+# Running as a Desktop app
+
+Element can also be run as a desktop app, wrapped in Electron. You can download a
+pre-built version from or, if you prefer,
+build it yourself.
+
+To build it yourself, follow the instructions at .
+
+Many thanks to @aviraldg for the initial work on the Electron integration.
+
+The [configuration docs](docs/config.md#desktop-app-configuration) show how to override the desktop app's default settings if desired.
+
+# config.json
+
+Element supports a variety of settings to configure default servers, behaviour, themes, etc.
+See the [configuration docs](docs/config.md) for more details.
+
+# Labs Features
+
+Some features of Element may be enabled by flags in the `Labs` section of the settings.
+Some of these features are described in [labs.md](https://github.com/element-hq/element-web/blob/develop/docs/labs.md).
+
+# Caching requirements
+
+Element requires the following URLs not to be cached, when/if you are serving Element from your own webserver:
+
+```
+/config.*.json
+/i18n
+/home
+/sites
+/index.html
+```
+
+We also recommend that you force browsers to re-validate any cached copy of Element on page load by configuring your
+webserver to return `Cache-Control: no-cache` for `/`. This ensures the browser will fetch a new version of Element on
+the next page load after it's been deployed. Note that this is already configured for you in the nginx config of our
+Dockerfile.
+
+# Development
+
+Before attempting to develop on Element you **must** read the [developer guide
+for `matrix-react-sdk`](https://github.com/matrix-org/matrix-react-sdk#developer-guide), which
+also defines the design, architecture and style for Element too.
+
+Read the [Choosing an issue](docs/choosing-an-issue.md) page for some guidance
+about where to start. Before starting work on a feature, it's best to ensure
+your plan aligns well with our vision for Element. Please chat with the team in
+[#element-dev:matrix.org](https://matrix.to/#/#element-dev:matrix.org) before
+you start so we can ensure it's something we'd be willing to merge.
+
+You should also familiarise yourself with the ["Here be Dragons" guide
+](https://docs.google.com/document/d/12jYzvkidrp1h7liEuLIe6BMdU0NUjndUYI971O06ooM)
+to the tame & not-so-tame dragons (gotchas) which exist in the codebase.
+
+The idea of Element is to be a relatively lightweight "skin" of customisations on
+top of the underlying `matrix-react-sdk`. `matrix-react-sdk` provides both the
+higher and lower level React components useful for building Matrix communication
+apps using React.
+
+Please note that Element is intended to run correctly without access to the public
+internet. So please don't depend on resources (JS libs, CSS, images, fonts)
+hosted by external CDNs or servers but instead please package all dependencies
+into Element itself.
+
+# Setting up a dev environment
+
+Much of the functionality in Element is actually in the `matrix-js-sdk` module.
+It is possible to set these up in a way that makes it easy to track the `develop` branches
+in git and to make local changes without having to manually rebuild each time.
+
+First clone and build `matrix-js-sdk`:
+
+```bash
+git clone https://github.com/matrix-org/matrix-js-sdk.git
+pushd matrix-js-sdk
+yarn link
+yarn install
+popd
+```
+
+Clone the repo and switch to the `element-web` directory:
+
+```bash
+git clone https://github.com/element-hq/element-web.git
+cd element-web
+```
+
+Configure the app by copying `config.sample.json` to `config.json` and
+modifying it. See the [configuration docs](docs/config.md) for details.
+
+Finally, build and start Element itself:
+
+```bash
+yarn link matrix-js-sdk
+yarn install
+yarn start
+```
+
+Wait a few seconds for the initial build to finish; you should see something like:
+
+```
+[element-js] [webpack.Progress] 100%
+[element-js]
+[element-js] ℹ 「wdm」: 1840 modules
+[element-js] ℹ 「wdm」: Compiled successfully.
+```
+
+Remember, the command will not terminate since it runs the web server
+and rebuilds source files when they change. This development server also
+disables caching, so do NOT use it in production.
+
+Open in your browser to see your newly built Element.
+
+**Note**: The build script uses inotify by default on Linux to monitor directories
+for changes. If the inotify limits are too low your build will fail silently or with
+`Error: EMFILE: too many open files`. To avoid these issues, we recommend a watch limit
+of at least `128M` and instance limit around `512`.
+
+You may be interested in issues [#15750](https://github.com/element-hq/element-web/issues/15750) and
+[#15774](https://github.com/element-hq/element-web/issues/15774) for further details.
+
+To set a new inotify watch and instance limit, execute:
+
+```
+sudo sysctl fs.inotify.max_user_watches=131072
+sudo sysctl fs.inotify.max_user_instances=512
+sudo sysctl -p
+```
+
+If you wish, you can make the new limits permanent, by executing:
+
+```
+echo fs.inotify.max_user_watches=131072 | sudo tee -a /etc/sysctl.conf
+echo fs.inotify.max_user_instances=512 | sudo tee -a /etc/sysctl.conf
+sudo sysctl -p
+```
+
+---
+
+When you make changes to `matrix-js-sdk` they should be automatically picked up by webpack and built.
+
+If any of these steps error with, `file table overflow`, you are probably on a mac
+which has a very low limit on max open files. Run `ulimit -Sn 1024` and try again.
+You'll need to do this in each new terminal you open before building Element.
+
+## Running the tests
+
+There are a number of application-level tests in the `tests` directory; these
+are designed to run with Jest and JSDOM. To run them
+
+```
+yarn test
+```
+
+### End-to-End tests
+
+See [matrix-react-sdk](https://github.com/matrix-org/matrix-react-sdk/#end-to-end-tests) for how to run the end-to-end tests.
+
+# Translations
+
+To add a new translation, head to the [translating doc](docs/translating.md).
+
+For a developer guide, see the [translating dev doc](docs/translating-dev.md).
+
+# Triaging issues
+
+Issues are triaged by community members and the Web App Team, following the [triage process](https://github.com/element-hq/element-meta/wiki/Triage-process).
+
+We use [issue labels](https://github.com/element-hq/element-meta/wiki/Issue-labelling) to sort all incoming issues.
diff --git a/banner.webp b/banner.webp
deleted file mode 100644
index 6747255e133..00000000000
Binary files a/banner.webp and /dev/null differ
diff --git a/config.json b/config.json
deleted file mode 100644
index f26c0613f0b..00000000000
--- a/config.json
+++ /dev/null
@@ -1,53 +0,0 @@
-{
- "default_server_name": "matrix.org",
- "default_server_config": {
- "m.homeserver": {
- "base_url": "https://matrix-client.matrix.org"
- },
- "m.identity_server": {
- "base_url": "https://vector.im"
- }
- },
- "brand": "elecord",
- "integrations_ui_url": "https://scalar.vector.im/",
- "integrations_rest_url": "https://scalar.vector.im/api",
- "integrations_widgets_urls": [
- "https://scalar.vector.im/_matrix/integrations/v1",
- "https://scalar.vector.im/api",
- "https://scalar-staging.vector.im/_matrix/integrations/v1",
- "https://scalar-staging.vector.im/api",
- "https://scalar-staging.riot.im/scalar/api"
- ],
- "bug_report_endpoint_url": "https://element.io/bugreports/submit",
- "uisi_autorageshake_app": "element-auto-uisi",
- "show_labs_settings": false,
- "room_directory": {
- "servers": ["matrix.org", "gitter.im"]
- },
- "enable_presence_by_hs_url": {
- "https://matrix.org": false,
- "https://matrix-client.matrix.org": false
- },
- "terms_and_conditions_links": [
- {
- "url": "https://element.io/privacy",
- "text": "Privacy Policy"
- },
- {
- "url": "https://element.io/cookie-policy",
- "text": "Cookie Policy"
- }
- ],
- "privacy_policy_url": "https://element.io/cookie-policy",
- "setting_defaults": {
- "RustCrypto.staged_rollout_percent": 100
- },
- "features": {
- "feature_video_rooms": true,
- "feature_group_calls": true,
- "feature_element_call_video_rooms": true
- },
- "element_call": {
- "url": "https://call.element.io"
- }
-}
\ No newline at end of file
diff --git a/res/manifest.json b/res/manifest.json
index 69f8ecf86b4..f6f1e91bf47 100644
--- a/res/manifest.json
+++ b/res/manifest.json
@@ -1,8 +1,8 @@
{
- "name": "elecord",
- "short_name": "elecord",
+ "name": "Element",
+ "short_name": "Element",
"display": "standalone",
- "theme_color": "#3e9fed",
+ "theme_color": "#76CFA6",
"start_url": "index.html",
"icons": [
{
diff --git a/res/themes/element/img/backgrounds/lake.jpg b/res/themes/element/img/backgrounds/lake.jpg
index f824133faa1..eb3d19a7cc9 100644
Binary files a/res/themes/element/img/backgrounds/lake.jpg and b/res/themes/element/img/backgrounds/lake.jpg differ
diff --git a/res/themes/element/img/logos/element-logo.svg b/res/themes/element/img/logos/element-logo.svg
index 540a6081d7e..54a91b72f80 100644
--- a/res/themes/element/img/logos/element-logo.svg
+++ b/res/themes/element/img/logos/element-logo.svg
@@ -1,124 +1,7 @@
-
-
-
-
+
diff --git a/res/themes/element/img/logos/opengraph.png b/res/themes/element/img/logos/opengraph.png
index d818fdd7242..d1b9d70e081 100644
Binary files a/res/themes/element/img/logos/opengraph.png and b/res/themes/element/img/logos/opengraph.png differ
diff --git a/res/vector-icons/1024.png b/res/vector-icons/1024.png
index 3e301e8b288..f4f073930b3 100644
Binary files a/res/vector-icons/1024.png and b/res/vector-icons/1024.png differ
diff --git a/res/vector-icons/120.png b/res/vector-icons/120.png
index 067192ef63b..5a8544944e5 100644
Binary files a/res/vector-icons/120.png and b/res/vector-icons/120.png differ
diff --git a/res/vector-icons/1240x600.png b/res/vector-icons/1240x600.png
index c2fff2de00e..11a963db656 100644
Binary files a/res/vector-icons/1240x600.png and b/res/vector-icons/1240x600.png differ
diff --git a/res/vector-icons/150.png b/res/vector-icons/150.png
index 4a5755ff091..407668e679f 100644
Binary files a/res/vector-icons/150.png and b/res/vector-icons/150.png differ
diff --git a/res/vector-icons/152.png b/res/vector-icons/152.png
index 7002fb34f3b..aefe0df014d 100644
Binary files a/res/vector-icons/152.png and b/res/vector-icons/152.png differ
diff --git a/res/vector-icons/180.png b/res/vector-icons/180.png
index 1934189ef0e..bfb7a33243f 100644
Binary files a/res/vector-icons/180.png and b/res/vector-icons/180.png differ
diff --git a/res/vector-icons/24.png b/res/vector-icons/24.png
index a3bf9914bbb..b0ef723eb8f 100644
Binary files a/res/vector-icons/24.png and b/res/vector-icons/24.png differ
diff --git a/res/vector-icons/300.png b/res/vector-icons/300.png
index d28397dda1f..6cbb21a5689 100644
Binary files a/res/vector-icons/300.png and b/res/vector-icons/300.png differ
diff --git a/res/vector-icons/44.png b/res/vector-icons/44.png
index c4e7dd58775..171508dde03 100644
Binary files a/res/vector-icons/44.png and b/res/vector-icons/44.png differ
diff --git a/res/vector-icons/50.png b/res/vector-icons/50.png
index b2da2aa85fe..1d8de2bf06d 100644
Binary files a/res/vector-icons/50.png and b/res/vector-icons/50.png differ
diff --git a/res/vector-icons/620x300.png b/res/vector-icons/620x300.png
index 0b6c8ad08d0..f05c32b1c91 100644
Binary files a/res/vector-icons/620x300.png and b/res/vector-icons/620x300.png differ
diff --git a/res/vector-icons/76.png b/res/vector-icons/76.png
index d9a21d55dd3..05af3bb8dd2 100644
Binary files a/res/vector-icons/76.png and b/res/vector-icons/76.png differ
diff --git a/res/vector-icons/88.png b/res/vector-icons/88.png
index 3268223ff2a..dec953bcf58 100644
Binary files a/res/vector-icons/88.png and b/res/vector-icons/88.png differ
diff --git a/res/vector-icons/apple-touch-icon-114.png b/res/vector-icons/apple-touch-icon-114.png
index c1be161ee3b..1ae39a6debf 100644
Binary files a/res/vector-icons/apple-touch-icon-114.png and b/res/vector-icons/apple-touch-icon-114.png differ
diff --git a/res/vector-icons/apple-touch-icon-120.png b/res/vector-icons/apple-touch-icon-120.png
index 067192ef63b..749f941a1e1 100644
Binary files a/res/vector-icons/apple-touch-icon-120.png and b/res/vector-icons/apple-touch-icon-120.png differ
diff --git a/res/vector-icons/apple-touch-icon-144.png b/res/vector-icons/apple-touch-icon-144.png
index 0a5981f7a92..0dcc56a8027 100644
Binary files a/res/vector-icons/apple-touch-icon-144.png and b/res/vector-icons/apple-touch-icon-144.png differ
diff --git a/res/vector-icons/apple-touch-icon-152.png b/res/vector-icons/apple-touch-icon-152.png
index 7002fb34f3b..2ac8c6dce44 100644
Binary files a/res/vector-icons/apple-touch-icon-152.png and b/res/vector-icons/apple-touch-icon-152.png differ
diff --git a/res/vector-icons/apple-touch-icon-180.png b/res/vector-icons/apple-touch-icon-180.png
index 1934189ef0e..f87d72db939 100644
Binary files a/res/vector-icons/apple-touch-icon-180.png and b/res/vector-icons/apple-touch-icon-180.png differ
diff --git a/res/vector-icons/apple-touch-icon-57.png b/res/vector-icons/apple-touch-icon-57.png
index 6fef2668e52..d80f3859548 100644
Binary files a/res/vector-icons/apple-touch-icon-57.png and b/res/vector-icons/apple-touch-icon-57.png differ
diff --git a/res/vector-icons/apple-touch-icon-60.png b/res/vector-icons/apple-touch-icon-60.png
index c25a535613f..d5ec84b0382 100644
Binary files a/res/vector-icons/apple-touch-icon-60.png and b/res/vector-icons/apple-touch-icon-60.png differ
diff --git a/res/vector-icons/apple-touch-icon-72.png b/res/vector-icons/apple-touch-icon-72.png
index 25ab6435528..85dbe1b82f4 100644
Binary files a/res/vector-icons/apple-touch-icon-72.png and b/res/vector-icons/apple-touch-icon-72.png differ
diff --git a/res/vector-icons/apple-touch-icon-76.png b/res/vector-icons/apple-touch-icon-76.png
index d9a21d55dd3..f328d1bd612 100644
Binary files a/res/vector-icons/apple-touch-icon-76.png and b/res/vector-icons/apple-touch-icon-76.png differ
diff --git a/res/vector-icons/favicon.ico b/res/vector-icons/favicon.ico
index 72d09f14292..6ad5fb08651 100644
Binary files a/res/vector-icons/favicon.ico and b/res/vector-icons/favicon.ico differ
diff --git a/res/vector-icons/mstile-150.png b/res/vector-icons/mstile-150.png
index 4a5755ff091..6606d425f29 100644
Binary files a/res/vector-icons/mstile-150.png and b/res/vector-icons/mstile-150.png differ
diff --git a/res/vector-icons/mstile-310.png b/res/vector-icons/mstile-310.png
index d1ef0938ff2..0ce998223b3 100644
Binary files a/res/vector-icons/mstile-310.png and b/res/vector-icons/mstile-310.png differ
diff --git a/res/vector-icons/mstile-310x150.png b/res/vector-icons/mstile-310x150.png
index 16a53e2ae9d..42efd6a98e1 100644
Binary files a/res/vector-icons/mstile-310x150.png and b/res/vector-icons/mstile-310x150.png differ
diff --git a/res/vector-icons/mstile-70.png b/res/vector-icons/mstile-70.png
index ce677aec418..709056bc61a 100644
Binary files a/res/vector-icons/mstile-70.png and b/res/vector-icons/mstile-70.png differ
diff --git a/scripts/ci_package.sh b/scripts/ci_package.sh
index 87ccd4fe1f6..d5736420c35 100755
--- a/scripts/ci_package.sh
+++ b/scripts/ci_package.sh
@@ -4,7 +4,7 @@
set -ex
-rm dist/elecord-*.tar.gz || true # rm previous artifacts without failing if it doesn't exist
+rm dist/element-*.tar.gz || true # rm previous artifacts without failing if it doesn't exist
DIST_VERSION=`$(dirname $0)/get-version-from-git.sh`
diff --git a/scripts/package.sh b/scripts/package.sh
index 590771dadc0..9937dd20d3b 100755
--- a/scripts/package.sh
+++ b/scripts/package.sh
@@ -15,19 +15,16 @@ VERSION=$version yarn build
# `yarn build`, but it's just too painful.
cp config.sample.json webapp/
-# Copy any required files for deployment
-cp .github/cfp_headers webapp/_headers
-
mkdir -p dist
-cp -r webapp elecord-$version
+cp -r webapp element-$version
# Just in case you have a local config, remove it before packaging
-# rm elecord-$version/config.json || true
+rm element-$version/config.json || true
# GNU/BSD compatibility workaround
tar_perms=(--owner=0 --group=0) && [ "$(uname)" == "Darwin" ] && tar_perms=(--uid=0 --gid=0)
-tar "${tar_perms[@]}" -chvzf dist/elecord-$version.tar.gz elecord-$version
-rm -r elecord-$version
+tar "${tar_perms[@]}" -chvzf dist/element-$version.tar.gz element-$version
+rm -r element-$version
echo
-echo "Packaged dist/elecord-$version.tar.gz"
+echo "Packaged dist/element-$version.tar.gz"
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index f2ad5ce7ac2..66428300a08 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -645,7 +645,7 @@
"stop_voice_message": "Stop recording",
"voice_message_button": "Voice Message"
},
- "console_dev_note": "If you know what you're doing, elecord is open-source, be sure to check out our GitHub (https://github.com/elecordapp/elecord-web/) and contribute!",
+ "console_dev_note": "If you know what you're doing, Element is open-source, be sure to check out our GitHub (https://github.com/vector-im/element-web/) and contribute!",
"console_scam_warning": "If someone told you to copy/paste something here, there is a high likelihood you're being scammed!",
"console_wait": "Wait!",
"create_room": {
@@ -1039,11 +1039,11 @@
"hs_blocked": "This homeserver has been blocked by its administrator.",
"invalid_configuration_mixed_server": "Invalid configuration: a default_hs_url can't be specified along with default_server_name or default_server_config",
"invalid_configuration_no_server": "Invalid configuration: no default server specified.",
- "invalid_json": "Your elecord configuration contains invalid JSON. Please correct the problem and reload the page.",
+ "invalid_json": "Your Element configuration contains invalid JSON. Please correct the problem and reload the page.",
"invalid_json_detail": "The message from the parser is: %(message)s",
"invalid_json_generic": "Invalid JSON",
"mau": "This homeserver has hit its Monthly Active User limit.",
- "misconfigured": "Your elecord is misconfigured",
+ "misconfigured": "Your Element is misconfigured",
"mixed_content": "Can't connect to homeserver via HTTP when an HTTPS URL is in your browser bar. Either use HTTPS or enable unsafe scripts.",
"non_urgent_echo_failure_toast": "Your server isn't responding to some requests.",
"resource_limits": "This homeserver has exceeded one of its resource limits.",
@@ -3978,7 +3978,7 @@
"you_are_presenting": "You are presenting"
},
"web_default_device_name": "%(appName)s: %(browserName)s on %(osName)s",
- "welcome_to_element": "Welcome to elecord",
+ "welcome_to_element": "Welcome to Element",
"widget": {
"added_by": "Widget added by",
"capabilities_dialog": {
diff --git a/src/vector/index.html b/src/vector/index.html
index a2b80a61324..0c21a0791b6 100644
--- a/src/vector/index.html
+++ b/src/vector/index.html
@@ -2,7 +2,7 @@
- elecord
+ Element
@@ -16,13 +16,13 @@
-
-
+
+
-
-
+
+
-
+
<%