From 741b82b0263ff79c98ac48a0f6ea9dbd7e8b2e95 Mon Sep 17 00:00:00 2001 From: Timo K Date: Tue, 21 Apr 2026 16:06:51 +0200 Subject: [PATCH] update linking realted logic - remove post-commit hook - remove .links.cjs enable/disable (instead just add/rm .pnpmfile.cjs) - rename pnpm links:enable -> pnpm links:on - rename pnpm links:disable -> pnpm links:off - unify doc filenames `-` -> `_` - add linking_concept_reasoning.md to provide background why the linking is how it is. --- .githooks/post-commit | 11 ---- .githooks/pre-commit | 14 ++--- README.md | 6 +- docs/README.md | 6 +- ...d-standalone.md => embedded_standalone.md} | 6 +- docs/linking.md | 45 +++++++++----- docs/linking_concept_reasoning.md | 27 ++++++++ docs/{self-hosting.md => self_hosting.md} | 0 docs/{url-params.md => url_params.md} | 14 ++--- package.json | 4 +- pnpm-lock.yaml | 8 +-- scripts/.pnpmfile.cjs | 62 +++++++++++++++++++ scripts/setup-linking.sh | 26 ++++++++ 13 files changed, 174 insertions(+), 55 deletions(-) delete mode 100755 .githooks/post-commit rename docs/{embedded-standalone.md => embedded_standalone.md} (97%) create mode 100644 docs/linking_concept_reasoning.md rename docs/{self-hosting.md => self_hosting.md} (100%) rename docs/{url-params.md => url_params.md} (97%) create mode 100644 scripts/.pnpmfile.cjs create mode 100755 scripts/setup-linking.sh diff --git a/.githooks/post-commit b/.githooks/post-commit deleted file mode 100755 index 243b4f51..00000000 --- a/.githooks/post-commit +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/sh - -FILE=.links.cjs -FILE_DIS=.links.temp-disabled.cjs -if test -f "$FILE_DIS"; then - # Only do the post-commit hook if the file was temp-disabled by the pre-commit hook. - # Otherwise linking was actively (`pnpm links:disable`) disabled and this hook should noop. - mv $FILE_DIS $FILE - echo "[pnpm-linker] The post-commit hook has re-enabled $FILE" - exit 1 -fi diff --git a/.githooks/pre-commit b/.githooks/pre-commit index f8ecfe4d..2656c9b9 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -1,11 +1,9 @@ -#!/usr/bin/sh +#!/usr/bin/env bash -FILE=.links.cjs -FILE_DIS=.links.temp-disabled.cjs -if test -f "$FILE"; then - mv $FILE .links.temp-disabled.cjs - x=$(pnpm install) - y=$(git add pnpm-lock.yaml) - echo "[pnpm-linker] The pre-commit hook has disabled $FILE and MODIFIED the pnpm-lock.yaml file. Review the staged changes (the hook added pnpm-lock.yaml, was this desired?) and run \`git commit \` again if they look okay. The post-commit hook will re-enable your links." +# Checks if there currently is linking configured. Informs the user to disable linking before committing. + +PNPMFILE=.pnpmfile.cjs +if test -f "$PNPMFILE"; then + echo "[pnpm-linker] The pre-commit hook detected $PNPMFILE which implies you have linked packages in your pnpm-lock.yaml. Run pnpm links:off and commit again. See also linking.md." exit 1 fi diff --git a/README.md b/README.md index 3685e523..0c82e4b0 100644 --- a/README.md +++ b/README.md @@ -108,17 +108,17 @@ recommended method for embedding Element Call.

For more details on the packages, see the -[Embedded vs. Standalone Guide](./docs/embedded-standalone.md). +[Embedded vs. Standalone Guide](./docs/embedded_standalone.md). ## 🛠️ Self-Hosting For operating and deploying Element Call on your own server, refer to the -[**Self-Hosting Guide**](./docs/self-hosting.md). +[**Self-Hosting Guide**](./docs/self_hosting.md). ## 🧭 MatrixRTC Backend Discovery and Selection For proper Element Call operation each site deployment needs a MatrixRTC backend -setup as outlined in the [Self-Hosting](#self-hosting). A typical federated site +setup as outlined in the [Self-Hosting](#self_hosting). A typical federated site deployment for three different sites A, B and C is depicted below.

diff --git a/docs/README.md b/docs/README.md index d97e8d56..e5a5d08a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,8 +2,8 @@ This folder contains documentation for setup, usage, and development of Element Call. -- [Embedded vs standalone mode](./embedded-standalone.md) -- [Url format and parameters](./url-params.md) +- [Embedded vs standalone mode](./embedded_standalone.md) +- [Url format and parameters](./url_params.md) - [Global JS controls](./controls.md) -- [Self-Hosting](./self-hosting.md) +- [Self-Hosting](./self_hosting.md) - [Developing with linked packages](./linking.md) diff --git a/docs/embedded-standalone.md b/docs/embedded_standalone.md similarity index 97% rename from docs/embedded-standalone.md rename to docs/embedded_standalone.md index 440dfac0..24ad2a7d 100644 --- a/docs/embedded-standalone.md +++ b/docs/embedded_standalone.md @@ -14,7 +14,7 @@ The table below provides a comparison of the two packages: | **Release artifacts** | Docker Image, Tarball | Tarball, NPM for Web, Android AAR, SwiftPM for iOS | | **Recommended for** | Standalone/guest access usage | Embedding within messenger apps | | **Responsibility for regulatory compliance** | The administrator that is deploying the app is responsible for compliance with any applicable regulations (e.g. privacy) | The developer of the messenger app is responsible for compliance | -| **Analytics consent** | Element Call will show a consent UI. | Element Call will not show a consent UI. The messenger app should only provide the embedded Element Call with the [analytics URL parameters](./url-params.md#embedded-only-parameters) if consent has been granted. | +| **Analytics consent** | Element Call will show a consent UI. | Element Call will not show a consent UI. The messenger app should only provide the embedded Element Call with the [analytics URL parameters](./url_params.md#embedded-only-parameters) if consent has been granted. | | **Analytics data** | Element Call will send data to the Posthog, Sentry and Open Telemetry targets specified by the administrator in the `config.json` | Element Call will send data to the Posthog and Sentry targets specified in the URL parameters by the messenger app | ### Using the embedded package within a messenger app @@ -26,7 +26,7 @@ The basics are: 1. Add the appropriate platform dependency as given for a [release](https://github.com/element-hq/element-call/releases), or use the embedded tarball. e.g. `npm install @element-hq/element-call-embedded@0.9.0` 2. Include the assets from the platform dependency in the build process. e.g. copy the assets during a [Webpack](https://github.com/element-hq/element-web/blob/247cd8d56d832d006d7dfb919d1042529d712b59/webpack.config.js#L677-L682) build. 3. Use the `index.html` entrypointof the imported assets when you are constructing the WebView or iframe. e.g. using a [relative path in a webapp](https://github.com/element-hq/element-web/blob/247cd8d56d832d006d7dfb919d1042529d712b59/src/models/Call.ts#L680), or on the the Android [WebViewAssetLoader](https://github.com/element-hq/element-x-android/blob/fe5aab6588ecdcf9354a3bfbd9e97c1b31175a8f/features/call/impl/src/main/kotlin/io/element/android/features/call/impl/utils/DefaultCallWidgetProvider.kt#L20) -4. Set any of the [embedded-only URL parameters](./url-params.md#embedded-only-parameters) that you need. +4. Set any of the [embedded-only URL parameters](./url_params.md#embedded-only-parameters) that you need. ## Widget vs standalone mode @@ -36,4 +36,4 @@ As a widget, the app only uses the core calling (MatrixRTC) parts. The rest (aut Element Call and the hosting client are connected via the widget API. Element Call detects that it is run as a widget if a widgetId is defined in the url parameters. If `widgetId` is present then Element Call will try to connect to the client via the widget postMessage API using the parameters provided in [Url Format and parameters -](./url-params.md). +](./url_params.md). diff --git a/docs/linking.md b/docs/linking.md index 4ac6abf7..b9c62edf 100644 --- a/docs/linking.md +++ b/docs/linking.md @@ -1,8 +1,25 @@ +## Quickstart guide +run +```bash +./scripts/setup-linking.sh +``` +Read the script output: +``` +Setup complete. +Update: .links.cjs to your liking +Run: 'pnpm links:on' to test your .links.cjs +Run: 'git commit' with links enabled to test the git pre-commit hook. +Run: 'pnpm links:off' to be able to commit again +Run: 'git config --local core.hooksPath ""' to allow committing with linking (not recommended) +Run: 'rm links.cjs' & 'git config --local core.hooksPath ""' to fully revert what this script did +``` + + # Developing with linked packages -If you want to make changes to a package that Element Call depends on and see those changes applied in real time, you can create a link to a local copy of the package. Pnpm has a command for this (`pnpm link`), but it's not recommended to use it as it ends up modifying package.json with details specific to your development environment. +If you want to make changes to a package that Element Call depends on and see those changes applied in real time, you can create a link to a local copy of the package. `pnpm` has a command for this (`pnpm link`), but it's not recommended to use it as it ends up modifying package.json with details specific to your development environment. -Instead, you can use our little 'linker' plugin. Create a file named `.links.cjs` in the Element Call project directory, listing the names and paths of any dependencies you want to link. For example: +Instead, create a file named `.links.cjs` in the Element Call project directory (or run `./scripts/setup-linking.sh` to create a template), listing the names and paths of any dependencies you want to link. For example: ```cjs // Packages to link to local checkouts @@ -12,32 +29,32 @@ module.exports = { }; ``` -Then run `pnpm install`. +Then run `pnpm links:on`. (this will activate the pnpm file + run `pnpm install` to setup the linking) ## Hooks -Changes in `.links.yaml` will also update `pnpm-lock.yaml` when `pnpm` is executed. The lockfile will then contain the local +Changes in `.links.cjs` will also update `pnpm-lock.yaml` when `pnpm install` is executed. The lockfile will then contain the local version of the package which would not work on others dev setups or the github CI. -One always needs to run: +One always needs to remove the pnpm `readPackage` script (the `.pnpmfile.cjs`) and run: ```bash -mv .links.cjs .links.disabled.cjs pnpm install ``` before committing a change. -To make it more convenient to work with this linking system we added git hooks. -A `pre-commit` hook will run `mv .links.yaml .links.disabled.yaml`, `pnpm install` and `git add yarn.lock` if it detects -a `.links.yaml` file and abort the commit. -You will than need to check if the resulting changes are appropriate and commit again. +To make this less of a foot gun we added a git hook. +A `pre-commit` hook will check if linking is currently used. If it detects +a `.pnpmfile.cjs` file it will abort the commit with an explanatory message. +You will than need to run `pnpm links:off` and commit again. -A `post-commit` hook will setup the linking as it was -before if a `.links.disabled.cjs` is present. It runs `mv .links.disabled.cjs .links.cjs` and `pnpm install`. - -To activate the hooks automatically configure git with +To activate the hooks configure git with (when using the setup script (`./scripts/setup-linking.sh`) this is already done): ```bash git config --local core.hooksPath .githooks ``` +This will add the hook path for this repository only to .gihooks. which is a tracked (by git) folder containing the pre-commit hook. + +## Background +Information, why this approach is used can be found in the [linking concept reasoning](./linking_concept_reasoning.md) document. diff --git a/docs/linking_concept_reasoning.md b/docs/linking_concept_reasoning.md new file mode 100644 index 00000000..37221344 --- /dev/null +++ b/docs/linking_concept_reasoning.md @@ -0,0 +1,27 @@ + +### Why do we not enable .pnpmfile.cjs by default +Background: The presence of the `.pnpmfile.cjs` adds a field to the `pnpm-lock.yaml` called: `pnpmfileChecksum`. This field is a checksum of the content of the `.pnpmfile.cjs` file. +`pnpm install --frozen-lockfile` **fails** if there is a `.pnpmfile.cjs` but no `pnpmfileChecksum` or vice versa (or on mismatch). + +_TLDR: running with `--ignore-pnpmfile` will fail if `pnpmfileChecksum` is present._ + +#### `pnpmfileChecksum` + renovate bot +When the renovate bot creates a PR it runs `pnpm install --ignore-pnpmfile`. This means that the `pnpmfileChecksum` in the lockfile will be **empty**. +This breaks builds that **don't** ignore the `.pnpmfile.cjs`-file. (CI that runs on the renovate PR) +From here we have two possible paths: + - ignore `.pnpmfile.cjs` in all CI builds CI will also fail if we accidently add it locally. + - fixup the `pnpm-lock.yaml` in the renovate PR to contain the correct `pnpmfileChecksum`. + + Ignoring in all CI builds means that CI will always fail if we enable the linking system. + This is annoying but can be worked around with the git hook we provide that at least lets us know that we are + commiting with enabled linking. + Only if we remember setting it back/disbale linking (or let ourselves remember by the git hook) the CI will work. + + #### Summary + - We will always run into conflicts with the `pnpmfileChecksum` because in renovate prs it will be empty (`--ignore-pnpmfile`) + - To keep it simple we set `--ignore-pnpmfile` in all of ours CI to see issues immediately. + - The only solution is to never have a `.pnpmfile.cjs` in the repository when pushing. + - This way there will never be a commit with `pnpmfileChecksum` in the lockfile. + - renovate (which uses `--ignore-pnpmfile` which we cannot disable) and other CI will work + - We are able to use the linking system locally if we `cp` this file from the scripts folder into `./` on demand. + - `pnpm links:on` and `pnpm links:off` + `./scripts/setup-linking.sh` will help us with this. diff --git a/docs/self-hosting.md b/docs/self_hosting.md similarity index 100% rename from docs/self-hosting.md rename to docs/self_hosting.md diff --git a/docs/url-params.md b/docs/url_params.md similarity index 97% rename from docs/url-params.md rename to docs/url_params.md index a1e4793d..e88e7095 100644 --- a/docs/url-params.md +++ b/docs/url_params.md @@ -4,7 +4,7 @@ There are two formats for Element Call URLs. ## Link for sharing -Requires Element Call to be deployed in [standalone](./embedded-standalone.md) mode. +Requires Element Call to be deployed in [standalone](./embedded_standalone.md) mode. ```text https://element_call.domain/room/# @@ -36,15 +36,15 @@ possible to support encryption. | Package | Deployment | URL | | ------------------------------------ | ----------------------------- | ----------------------------------------------------------------------------- | -| [Full](./embedded-standalone.md) | All | `https://element_call.domain/room` | -| [Embedded](./embedded-standalone.md) | Remote URL | `https://element_call.domain/` n.b. no `/room` part | -| [Embedded](./embedded-standalone.md) | Embedded within messenger app | Platform dependent, but you load the `index.html` file without a `/room` part | +| [Full](./embedded_standalone.md) | All | `https://element_call.domain/room` | +| [Embedded](./embedded_standalone.md) | Remote URL | `https://element_call.domain/` n.b. no `/room` part | +| [Embedded](./embedded_standalone.md) | Embedded within messenger app | Platform dependent, but you load the `index.html` file without a `/room` part | ## Parameters ### Common Parameters -These parameters are relevant to both [widget](./embedded-standalone.md) and [standalone](./embedded-standalone.md) modes: +These parameters are relevant to both [widget](./embedded_standalone.md) and [standalone](./embedded_standalone.md) modes: | Name | Values | Required for widget | Required for SPA | Description | | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -74,7 +74,7 @@ These parameters are relevant to both [widget](./embedded-standalone.md) and [st ### Widget-only parameters -These parameters are only supported in [widget](./embedded-standalone.md) mode. +These parameters are only supported in [widget](./embedded_standalone.md) mode. | Name | Values | Required | Description | | --------------- | ----------------------------------------------------------------------------------------- | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | @@ -89,7 +89,7 @@ These parameters are only supported in [widget](./embedded-standalone.md) mode. ### Embedded-only parameters -These parameters are only supported in the [embedded](./embedded-standalone.md) package of Element Call and will be ignored in the [full](./embedded-standalone.md) package. +These parameters are only supported in the [embedded](./embedded_standalone.md) package of Element Call and will be ignored in the [full](./embedded_standalone.md) package. | Name | Values | Required | Description | | -------------------- | -------------------------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------- | diff --git a/package.json b/package.json index 867822c9..da6b7a2d 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,8 @@ "backend-playwright": "docker-compose -f playwright-backend-docker-compose.yml -f playwright-backend-docker-compose.override.yml up", "test:playwright": "playwright test", "test:playwright:open": "pnpm test:playwright --ui", - "links:enable": "mv .links.disabled.cjs .links.cjs & touch .links.cjs", - "links:disable": "mv .links.cjs .links.disabled.cjs", + "links:on": "cp scripts/.pnpmfile.cjs .pnpmfile.cjs & pnpm install", + "links:off": "rm .pnpmfile.cjs & pnpm install", "storybook": "storybook dev -p 6006", "build-storybook": "storybook build" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f75fdaa1..c9d752ab 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -236,7 +236,7 @@ importers: version: 1.9.2 matrix-js-sdk: specifier: matrix-org/matrix-js-sdk#develop - version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4ee3e591bf6498eecc1a92aad92b2ff3fc604fb8 + version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/d7d771fadb983bf40bed2c97aa31c398d8eec875 matrix-widget-api: specifier: ^1.16.1 version: 1.17.0 @@ -4974,8 +4974,8 @@ packages: matrix-events-sdk@0.0.1: resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4ee3e591bf6498eecc1a92aad92b2ff3fc604fb8: - resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4ee3e591bf6498eecc1a92aad92b2ff3fc604fb8} + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/d7d771fadb983bf40bed2c97aa31c398d8eec875: + resolution: {tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/d7d771fadb983bf40bed2c97aa31c398d8eec875} version: 41.3.0 engines: {node: '>=22.0.0'} @@ -11749,7 +11749,7 @@ snapshots: matrix-events-sdk@0.0.1: {} - matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/4ee3e591bf6498eecc1a92aad92b2ff3fc604fb8: + matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/d7d771fadb983bf40bed2c97aa31c398d8eec875: dependencies: '@babel/runtime': 7.29.2 '@matrix-org/matrix-sdk-crypto-wasm': 18.1.0 diff --git a/scripts/.pnpmfile.cjs b/scripts/.pnpmfile.cjs new file mode 100644 index 00000000..23b0759f --- /dev/null +++ b/scripts/.pnpmfile.cjs @@ -0,0 +1,62 @@ +/* +Copyright 2026 Element Creations Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ +// DONT RUN THIS FILE MANUALLY +// This file is intended to be used with `pnpm links:on` and `pnpm links:off` which will copy this file to the project root. +// See docs/linking.md for details. +// +// +// Created based on https://github.com/element-hq/element-call/blob/60fae70a60e3697eb41210ccf1e400cab37df7c8/.yarn/plugins/linker.cjs +// and the following prompt history: +// - Can you convert this yarn plugin into a pnpm plugin. +// - The goal is to not have modifications to the package.json and lock files so that we do not track links on gh. +// This seems to modify the package.json file. +// What can we do with pnpm to have the link inforamtion in a seperate file +// - why do you cache the loaded links. When does this file get executed? +// Do we need this optimization. +// How do we guarantee, that we aleays use the most recent content from the links file? +// +// Manual transition to cjs. Claude proposed manual yaml parsing. + +const fs = require("fs"); +const path = require("path"); + +function loadLinks() { + try { + return require(path.join(__dirname, ".links.cjs")); + } catch (e) { + return null; + } +} + +function readPackage(pkg, context) { + const links = loadLinks(); + if (!links) return pkg; + + const manifest = JSON.parse( + fs.readFileSync(path.join(__dirname, "package.json"), "utf8"), + ); + if (pkg.name !== manifest.name) return pkg; + + for (const [name, linkPath] of Object.entries(links)) { + const resolved = `link:${path.resolve(__dirname, linkPath)}`; + if (pkg.dependencies && pkg.dependencies[name]) { + context.log(`Linking ${name} -> ${resolved}`); + pkg.dependencies[name] = resolved; + } else if (pkg.devDependencies && pkg.devDependencies[name]) { + context.log(`Linking ${name} -> ${resolved}`); + pkg.devDependencies[name] = resolved; + } + } + + return pkg; +} + +module.exports = { + hooks: { + readPackage, + }, +}; diff --git a/scripts/setup-linking.sh b/scripts/setup-linking.sh new file mode 100755 index 00000000..b20228d6 --- /dev/null +++ b/scripts/setup-linking.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +# Checks if there currently is linking configured. Informs the user to disable linking before committing. + +LINKSFILE=.links.cjs +echo "Checking for existing linking configuration in $LINKSFILE..." +if test -f "$LINKSFILE"; then +echo "Linking configuration found in $LINKSFILE." +else + echo "No $LINKSFILE -> Creating $LINKSFILE with default values. Please edit this file to point to your local checkouts of the dependencies you want to link." + echo '''// Packages to link to local checkouts +module.exports = { + "matrix-js-sdk": "../your/path/matrix-js-sdk", + "matrix-widget-api": "../your/path/matrix-widget-api", +};''' > $LINKSFILE +fi +echo "updating local git hookPath to .githooks" +git config --local core.hooksPath .githooks +echo "" +echo "Setup complete." +echo "Update: .links.cjs to your liking" +echo "Run: 'pnpm links:on' to test your .links.cjs" +echo "Run: 'git commit' with links enabled to test the git pre-commit hook." +echo "Run: 'pnpm links:off' to be able to commit again" +echo "Run: 'git config --local core.hooksPath \"\"' to allow committing with linking on (not recommended)" +echo "Run: 'rm links.cjs' & 'git config --local core.hooksPath \"\"' to fully revert what this script did"