Merge branch 'develop' into rav/tsdoc_in_pr_template

This commit is contained in:
Richard van der Hoff 2024-02-28 14:03:38 +00:00 committed by GitHub
commit 85f1b5c672
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
83 changed files with 7182 additions and 7562 deletions

View File

@ -2,11 +2,11 @@ module.exports = {
plugins: ["matrix-org"], plugins: ["matrix-org"],
extends: [".eslintrc.js"], extends: [".eslintrc.js"],
parserOptions: { parserOptions: {
project: ["test/tsconfig.json"], project: ["playwright/tsconfig.json"],
}, },
overrides: [ overrides: [
{ {
files: ["test/**/*.ts"], files: ["playwright/**/*.ts"],
extends: ["plugin:matrix-org/typescript"], extends: ["plugin:matrix-org/typescript"],
rules: { rules: {
// Things we do that break the ideal style // Things we do that break the ideal style

9
.github/CODEOWNERS vendored
View File

@ -1,4 +1,5 @@
* @vector-im/element-web * @element-hq/element-web-reviewers
/.github/workflows/** @vector-im/element-web-app-team /.github/workflows/** @element-hq/element-web-team
/package.json @vector-im/element-web-app-team /package.json @element-hq/element-web-team
/yarn.lock @vector-im/element-web-app-team /yarn.lock @element-hq/element-web-team
/src/i18n/strings

View File

@ -6,9 +6,3 @@
- [ ] New or updated `public`/`exported` symbols have accurate [TSDoc](https://tsdoc.org/) documentation. - [ ] New or updated `public`/`exported` symbols have accurate [TSDoc](https://tsdoc.org/) documentation.
- [ ] Linter and other CI checks pass. - [ ] Linter and other CI checks pass.
- [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/vector-im/element-desktop/blob/develop/CONTRIBUTING.md)). - [ ] Sign-off given on the changes (see [CONTRIBUTING.md](https://github.com/vector-im/element-desktop/blob/develop/CONTRIBUTING.md)).
<!--
If you would like to specify text for the changelog entry other than your PR title, add the following:
Notes: Add super cool feature
-->

31
.github/labels.yml vendored Normal file
View File

@ -0,0 +1,31 @@
- name: "A-Install"
color: "72A447"
- name: "A-Seshat"
color: "8262BE"
- name: "A-Update"
color: "17BE67"
- name: "Story"
description: "A change to the product that generates user value on its own. Unit of delivery."
color: "0BAC47"
- name: "X-Breaking-Change"
color: "ff7979"
- name: "Z-Arch"
color: "D601BE"
- name: "Z-ARM"
color: "5DEC5B"
- name: "Z-Flatpak"
color: "0CA856"
- name: "Z-Linux"
color: "7B4A9C"
- name: "Z-macOS"
color: "500605"
- name: "Z-Official"
color: "1D2B20"
- name: "Z-Snap"
color: "29CD95"
- name: "Z-Suse"
color: "79D07B"
- name: "Z-Wayland"
color: "94C519"
- name: "Z-Windows"
color: "0632DE"

1
.github/release-drafter.yml vendored Normal file
View File

@ -0,0 +1 @@
_extends: element-hq/element-web

View File

@ -48,6 +48,7 @@ jobs:
config: element.io/${{ inputs.mode || (github.event_name == 'release' && 'release') || 'nightly' }} config: element.io/${{ inputs.mode || (github.event_name == 'release' && 'release') || 'nightly' }}
version: ${{ (inputs.mode != 'release' && github.event_name != 'release') && 'develop' || '' }} version: ${{ (inputs.mode != 'release' && github.event_name != 'release') && 'develop' || '' }}
nightly: ${{ inputs.mode != 'release' && github.event_name != 'release' }} nightly: ${{ inputs.mode != 'release' && github.event_name != 'release' }}
deploy: ${{ inputs.deploy || (github.event_name != 'workflow_dispatch' && github.event.release.prerelease != true) }}
secrets: secrets:
CF_R2_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }} CF_R2_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
CF_R2_TOKEN: ${{ secrets.CF_R2_TOKEN }} CF_R2_TOKEN: ${{ secrets.CF_R2_TOKEN }}
@ -58,12 +59,11 @@ jobs:
name: Windows ${{ matrix.arch }} name: Windows ${{ matrix.arch }}
strategy: strategy:
matrix: matrix:
arch: [x86, x64] arch: [ia32, x64]
uses: ./.github/workflows/build_windows.yaml uses: ./.github/workflows/build_windows.yaml
secrets: inherit secrets: inherit
with: with:
sign: true sign: true
deploy-mode: true
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
version: ${{ needs.prepare.outputs.nightly-version }} version: ${{ needs.prepare.outputs.nightly-version }}
@ -75,67 +75,120 @@ jobs:
secrets: inherit secrets: inherit
with: with:
sign: true sign: true
deploy-mode: true
base-url: https://packages.element.io/${{ needs.prepare.outputs.packages-dir }} base-url: https://packages.element.io/${{ needs.prepare.outputs.packages-dir }}
version: ${{ needs.prepare.outputs.nightly-version }} version: ${{ needs.prepare.outputs.nightly-version }}
# We do not put these calls into deploy-mode as we do not want it to add to the packages.element.io artifact
# We ship this build via reprepro only
linux: linux:
if: github.event_name != 'workflow_dispatch' || inputs.linux if: github.event_name != 'workflow_dispatch' || inputs.linux
needs: prepare needs: prepare
name: Linux ${{ matrix.arch }} (sqlcipher system) name: Linux ${{ matrix.arch }} (sqlcipher ${{ matrix.sqlcipher }})
strategy: strategy:
matrix: matrix:
arch: [amd64, arm64] arch: [amd64, arm64]
sqlcipher: [system, static]
exclude:
- arch: arm64
sqlcipher: static
uses: ./.github/workflows/build_linux.yaml uses: ./.github/workflows/build_linux.yaml
with: with:
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
config: ${{ needs.prepare.outputs.config }} config: ${{ needs.prepare.outputs.config }}
sqlcipher: system sqlcipher: ${{ matrix.sqlcipher }}
version: ${{ needs.prepare.outputs.nightly-version }} version: ${{ needs.prepare.outputs.nightly-version }}
# We ship the static build via static tarball only
linux_static:
if: github.event_name != 'workflow_dispatch' || inputs.linux
needs: prepare
name: Linux (sqlcipher static)
uses: ./.github/workflows/build_linux.yaml
with:
arch: amd64
deploy-mode: true
config: ${{ needs.prepare.outputs.config }}
sqlcipher: static
version: ${{ needs.prepare.outputs.nightly-version }}
# This deploy job only handles Windows, macOS & linux_static as those are stateless and static.
# Linux will be deployed via reprepro after it, but we list it as a dependency to abort if it fails.
deploy: deploy:
needs: needs:
- prepare - prepare
- macos - macos
- linux - linux
- linux_static
- windows - windows
runs-on: ubuntu-latest runs-on: ubuntu-latest
name: Deploy name: ${{ needs.prepare.outputs.deploy == 'true' && 'Deploy' || 'Deploy (dry-run)' }}
if: | if: always() && !failure() && !cancelled()
( environment: ${{ needs.prepare.outputs.deploy == 'true' && 'packages.element.io' || '' }}
github.event_name != 'workflow_dispatch' &&
github.event.release.prerelease != true
) || (
always() && !failure() && !cancelled() && inputs.deploy &&
(inputs.macos || inputs.windows || inputs.linux)
)
environment: packages.element.io
steps: steps:
- name: Download artifacts - name: Download artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v4
- name: Prepare artifacts for deployment
run: |
# Windows
for arch in x64 ia32 arm64
do
if [ -d "win-$arch" ]; then
mkdir -p packages.element.io/{install,update}/win32/$arch
mv win-$arch/squirrel-windows*/*.exe "packages.element.io/install/win32/$arch/"
mv win-$arch/squirrel-windows*/*.nupkg "packages.element.io/update/win32/$arch/"
mv win-$arch/squirrel-windows*/RELEASES "packages.element.io/update/win32/$arch/"
fi
done
# macOS
if [ -d macos ]; then
mkdir -p packages.element.io/{install,update}/macos
mv macos/*.dmg packages.element.io/install/macos/
mv macos/*-mac.zip packages.element.io/update/macos/
mv macos/*.json packages.element.io/update/macos/
fi
# Linux
if [ -d linux-amd64-sqlcipher-static ]; then
mkdir -p packages.element.io/install/linux/glibc-x86-64
mv linux-amd64-sqlcipher-static/*.tar.gz packages.element.io/install/linux/glibc-x86-64
fi
# We don't wish to store the installer for every nightly ever, so we only keep the latest
- name: "[Nightly] Strip version from installer file"
if: needs.prepare.outputs.nightly-version != ''
run: |
# Windows
for arch in x64 ia32 arm64
do
[ -d "win-$arch" ] && mv packages.element.io/install/win32/$arch/{*,"Element Nightly Setup"}.exe
done
# macOS
[ -d macos ] && mv packages.element.io/install/macos/{*,"Element Nightly"}.dmg
# Linux
[ -d linux-amd64-sqlcipher-static ] && mv packages.element.io/install/linux/glibc-x86-64/{*,element-desktop-nightly}.tar.gz
- name: "[Release] Prepare release latest symlink"
if: needs.prepare.outputs.nightly-version == ''
run: |
# Windows
for arch in x64 ia32 arm64
do
if [ -d "win-$arch" ]; then
pushd packages.element.io/install/win32/$arch
ln -s "$(find . -type f -iname "*.exe" | xargs -0 -n1 -- basename)" "Element Setup.exe"
popd
fi
done
# macOS
if [ -d macos ]; then
pushd packages.element.io/install/macos
ln -s "$(find . -type f -iname "*.dmg" | xargs -0 -n1 -- basename)" "Element.dmg"
popd
fi
# Linux
if [ -d linux-amd64-sqlcipher-static ]; then
pushd packages.element.io/install/linux/glibc-x86-64
ln -s "$(find . -type f -iname "*.tar.gz" | xargs -0 -n1 -- basename)" "element-desktop.tar.gz"
popd
fi
- name: Stash packages.element.io
if: needs.prepare.outputs.deploy == 'false'
uses: actions/upload-artifact@v4
with: with:
name: packages.element.io name: packages.element.io
path: packages.element.io path: packages.element.io
- name: Deploy artifacts - name: Deploy artifacts
if: needs.prepare.outputs.deploy == 'true'
run: | run: |
aws s3 cp --recursive packages.element.io/ s3://$R2_BUCKET/$DEPLOYMENT_DIR --endpoint-url $R2_URL --region auto aws s3 cp --recursive packages.element.io/ s3://$R2_BUCKET/$DEPLOYMENT_DIR --endpoint-url $R2_URL --region auto
env: env:
@ -145,29 +198,47 @@ jobs:
DEPLOYMENT_DIR: ${{ needs.prepare.outputs.packages-dir }} DEPLOYMENT_DIR: ${{ needs.prepare.outputs.packages-dir }}
- name: Notify packages.element.io of new files - name: Notify packages.element.io of new files
uses: peter-evans/repository-dispatch@bf47d102fdb849e755b0b0023ea3e81a44b6f570 # v2 if: needs.prepare.outputs.deploy == 'true'
uses: peter-evans/repository-dispatch@ff45666b9427631e3450c54a1bcbee4d9ff4d7c0 # v3
with: with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }} token: ${{ secrets.ELEMENT_BOT_TOKEN }}
repository: vector-im/packages.element.io repository: element-hq/packages.element.io
event-type: packages-index event-type: packages-index
reprepro: - name: Find debs
needs: id: deb
- linux if: needs.linux.result == 'success'
# We queue this after the other deploy stage as we want to abort if that fails run: |
- deploy for arch in amd64 arm64
name: Run reprepro ${{ matrix.arch }} do
strategy: echo "$arch=$(ls linux-$arch-sqlcipher-system/*.deb | tail -n1)" >> $GITHUB_OUTPUT
matrix: done
arch: [amd64, arm64]
if: | - name: Stash debs
( if: needs.prepare.outputs.deploy == 'false' && needs.linux.result == 'success'
github.event_name != 'workflow_dispatch' && uses: actions/upload-artifact@v4
github.event.release.prerelease != true with:
) || ( name: debs
always() && !failure() && !cancelled() && inputs.deploy && inputs.linux path: |
) ${{ steps.deb.outputs.amd64 }}
uses: ./.github/workflows/reprepro.yaml ${{ steps.deb.outputs.arm64 }}
secrets: inherit
with: - name: Publish amd64 deb to packages.element.io
artifact-name: linux-${{ matrix.arch }}-sqlcipher-system uses: element-hq/packages.element.io@master
if: needs.prepare.outputs.deploy == 'true' && needs.linux.result == 'success'
with:
file: ${{ steps.deb.outputs.amd64 }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
bucket-api: ${{ vars.CF_R2_S3_API }}
bucket-key-id: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
bucket-access-key: ${{ secrets.CF_R2_TOKEN }}
- name: Publish arm64 deb to packages.element.io
uses: element-hq/packages.element.io@master
if: needs.prepare.outputs.deploy == 'true' && needs.linux.result == 'success'
with:
file: ${{ steps.deb.outputs.arm64 }}
github-token: ${{ secrets.ELEMENT_BOT_TOKEN }}
bucket-api: ${{ vars.CF_R2_S3_API }}
bucket-key-id: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
bucket-access-key: ${{ secrets.CF_R2_TOKEN }}

View File

@ -19,7 +19,7 @@ jobs:
uses: ./.github/workflows/build_windows.yaml uses: ./.github/workflows/build_windows.yaml
strategy: strategy:
matrix: matrix:
arch: [x64, x86] arch: [x64, ia32]
with: with:
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
@ -37,17 +37,17 @@ jobs:
REGISTRY: ghcr.io REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}-dockerbuild-pr IMAGE_NAME: ${{ github.repository }}-dockerbuild-pr
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: "Get modified files" - name: "Get modified files"
id: changed_files id: changed_files
uses: tj-actions/changed-files@41960309398d165631f08c5df47a11147e14712b # v39 uses: tj-actions/changed-files@ae82ed4ae04587b665efad2f206578aa6f0e8539 # v42
with: with:
files: | files: |
dockerbuild/* dockerbuild/**
- name: Log in to the Container registry - name: Log in to the Container registry
if: steps.changed_files.outputs.any_modified == 'true' if: steps.changed_files.outputs.any_modified == 'true'
uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6 uses: docker/login-action@83a00bc1ab5ded6580f31df1c49e6aaa932d840d
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@ -63,7 +63,7 @@ jobs:
- name: Build and push Docker image - name: Build and push Docker image
if: steps.changed_files.outputs.any_modified == 'true' if: steps.changed_files.outputs.any_modified == 'true'
uses: docker/build-push-action@4c1b68d83ad20cc1a09620ca477d5bbbb5fa14d0 uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
with: with:
context: dockerbuild context: dockerbuild
push: true push: true
@ -106,21 +106,26 @@ jobs:
- name: macOS Universal - name: macOS Universal
os: macos os: macos
artifact: macos artifact: macos
executable: "/Volumes/Element/Element.app/Contents/MacOS/Element" executable: "/Users/runner/Applications/Element.app/Contents/MacOS/Element"
prepare_cmd: "hdiutil attach ./dist/*.dmg -mountpoint /Volumes/Element" # We need to mount the DMG and copy the app to the Applications folder as a mounted DMG is
# read-only and thus would not allow us to override the fuses as is required for Playwright.
prepare_cmd: |
hdiutil attach ./dist/*.dmg -mountpoint /Volumes/Element &&
rsync -a /Volumes/Element/Element.app ~/Applications/ &&
hdiutil detach /Volumes/Element
- name: "Linux (amd64) (sqlcipher: system)" - name: "Linux (amd64) (sqlcipher: system)"
os: ubuntu os: ubuntu
artifact: linux-amd64-sqlcipher-system artifact: linux-amd64-sqlcipher-system
executable: "element-desktop" executable: "/opt/Element/element-desktop"
prepare_cmd: "sudo apt install ./dist/*.deb" prepare_cmd: "sudo apt install ./dist/*.deb"
- name: "Linux (amd64) (sqlcipher: static)" - name: "Linux (amd64) (sqlcipher: static)"
os: ubuntu os: ubuntu
artifact: linux-amd64-sqlcipher-static artifact: linux-amd64-sqlcipher-static
executable: "element-desktop" executable: "/opt/Element/element-desktop"
prepare_cmd: "sudo apt install ./dist/*.deb" prepare_cmd: "sudo apt install ./dist/*.deb"
- name: Windows (x86) - name: Windows (x86)
os: windows os: windows
artifact: win-x86 artifact: win-ia32
executable: "./dist/win-ia32-unpacked/Element.exe" executable: "./dist/win-ia32-unpacked/Element.exe"
- name: Windows (x64) - name: Windows (x64)
os: windows os: windows
@ -129,16 +134,17 @@ jobs:
name: Test ${{ matrix.name }} name: Test ${{ matrix.name }}
runs-on: ${{ matrix.os }}-latest runs-on: ${{ matrix.os }}-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version-file: package.json
cache: "yarn" cache: "yarn"
- name: Install Deps - name: Install Deps
run: "yarn install --frozen-lockfile" run: "yarn install --frozen-lockfile"
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
name: ${{ matrix.artifact }} name: ${{ matrix.artifact }}
path: dist path: dist
@ -147,18 +153,26 @@ jobs:
run: ${{ matrix.prepare_cmd }} run: ${{ matrix.prepare_cmd }}
if: matrix.prepare_cmd if: matrix.prepare_cmd
# We previously disabled the `EnableNodeCliInspectArguments` fuse, but Playwright requires
# it to be enabled to test Electron apps, so turn it back on.
- name: Set EnableNodeCliInspectArguments fuse enabled
run: $RUN_AS npx @electron/fuses write --app ${{ matrix.executable }} EnableNodeCliInspectArguments=on
shell: bash
env:
# We need sudo on Linux as it is installed in /opt/
RUN_AS: ${{ runner.os == 'Linux' && 'sudo' || '' }}
- name: Run tests - name: Run tests
uses: coactions/setup-xvfb@b6b4fcfb9f5a895edadc3bc76318fae0ac17c8b3 # v1 uses: coactions/setup-xvfb@6b00cf1889f4e1d5a48635647013c0508128ee1a
timeout-minutes: 5 timeout-minutes: 5
with: with:
run: "yarn test" run: "yarn test ${{ runner.os != 'Linux' && '--ignore-snapshots' || '' }}"
env: env:
ELEMENT_DESKTOP_EXECUTABLE: ${{ matrix.executable }} ELEMENT_DESKTOP_EXECUTABLE: ${{ matrix.executable }}
- name: Upload Artifacts - name: Upload HTML report
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
if: always()
with: with:
name: ${{ matrix.artifact }} name: ${{ matrix.artifact }}-test
path: test_artifacts path: playwright-report
retention-days: 1 retention-days: 14

View File

@ -20,24 +20,20 @@ on:
type: string type: string
required: true required: true
description: "How to link sqlcipher, one of 'system' | 'static'" description: "How to link sqlcipher, one of 'system' | 'static'"
deploy-mode:
type: boolean
required: false
description: "Whether to arrange artifacts in the arrangement needed for deployment, skipping unrelated ones"
docker-image: docker-image:
type: string type: string
required: false required: false
description: "The docker image to use for the build, defaults to ghcr.io/vector-im/element-desktop-dockerbuild" description: "The docker image to use for the build, defaults to ghcr.io/element-hq/element-desktop-dockerbuild"
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: container:
image: ${{ inputs.docker-image || format('ghcr.io/vector-im/element-desktop-dockerbuild:{0}', github.ref_name == 'master' && 'master' || 'develop') }} image: ${{ inputs.docker-image || format('ghcr.io/element-hq/element-desktop-dockerbuild:{0}', github.ref_name == 'master' && 'master' || 'develop') }}
defaults: defaults:
run: run:
shell: bash shell: bash
steps: steps:
- uses: kanga333/variable-mapper@master - uses: nbucic/variable-mapper@0673f6891a0619ba7c002ecfed0f9f4f39017b6f
id: config id: config
with: with:
key: "${{ inputs.arch }}" key: "${{ inputs.arch }}"
@ -55,22 +51,23 @@ jobs:
} }
} }
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
name: webapp name: webapp
- name: Cache .hak - name: Cache .hak
id: cache id: cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
key: ${{ runner.os }}-${{ inputs.docker-image || github.ref_name }}-${{ inputs.sqlcipher }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion') }} key: ${{ runner.os }}-${{ inputs.docker-image || github.ref_name }}-${{ inputs.sqlcipher }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion') }}
path: | path: |
./.hak ./.hak
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version-file: package.json
cache: "yarn" cache: "yarn"
env: env:
# Workaround for https://github.com/actions/setup-node/issues/317 # Workaround for https://github.com/actions/setup-node/issues/317
@ -91,12 +88,9 @@ jobs:
if: steps.cache.outputs.cache-hit != 'true' && inputs.arch == 'arm64' if: steps.cache.outputs.cache-hit != 'true' && inputs.arch == 'arm64'
run: | run: |
set -x set -x
sed -i 's/deb http/deb [arch=amd64] http/g' /etc/apt/sources.list
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ bionic main multiverse restricted universe" | tee -a /etc/apt/sources.list
echo "deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports/ bionic-updates main multiverse restricted universe" | tee -a /etc/apt/sources.list
dpkg --add-architecture arm64 dpkg --add-architecture arm64
apt-get -qq update apt-get -qq update
apt-get -qq install --no-install-recommends crossbuild-essential-arm64 libsqlcipher-dev:arm64 libssl-dev:arm64 libsecret-1-dev:arm64 libgnome-keyring-dev:arm64 apt-get -qq install --no-install-recommends crossbuild-essential-arm64 libsqlcipher-dev:arm64 libssl-dev:arm64 libsecret-1-dev:arm64
rustup target add aarch64-unknown-linux-gnu rustup target add aarch64-unknown-linux-gnu
mv dockerbuild/aarch64/.cargo . mv dockerbuild/aarch64/.cargo .
cat dockerbuild/aarch64/.env >> $GITHUB_ENV cat dockerbuild/aarch64/.env >> $GITHUB_ENV
@ -106,24 +100,19 @@ jobs:
run: "yarn build:native --target ${{ steps.config.outputs.target }}" run: "yarn build:native --target ${{ steps.config.outputs.target }}"
- name: "[Nightly] Resolve version" - name: "[Nightly] Resolve version"
id: nightly
if: inputs.version != '' if: inputs.version != ''
run: | run: |
echo "config-args=--nightly '${{ inputs.version }}'" >> $GITHUB_OUTPUT echo "ED_NIGHTLY=${{ inputs.version }}" >> $GITHUB_ENV
- name: Generate debian files and arguments - name: Generate debian files and arguments
id: debian
run: | run: |
if [ -f changelog.Debian ]; then if [ -f changelog.Debian ]; then
echo "config-args=--deb-changelog changelog.Debian" >> $GITHUB_OUTPUT echo "ED_DEBIAN_CHANGELOG=changelog.Debian" >> $GITHUB_ENV
fi fi
- name: Build App - name: Build App
run: | run: |
npx ts-node scripts/generate-builder-config.ts \ yarn build --publish never -l ${{ steps.config.outputs.build-args }}
${{ steps.nightly.outputs.config-args }} \
${{ steps.debian.outputs.config-args }}
yarn build --publish never -l --config electron-builder.json ${{ steps.config.outputs.build-args }}
- name: Check native libraries - name: Check native libraries
run: | run: |
@ -151,39 +140,11 @@ jobs:
env: env:
ARCH: ${{ steps.config.outputs.arch }} ARCH: ${{ steps.config.outputs.arch }}
- name: Stash deb package
if: inputs.deploy-mode
uses: actions/upload-artifact@v3
with:
name: linux-sqlcipher-${{ inputs.sqlcipher }}-deb
path: dist/*.deb
retention-days: 1
- name: Prepare artifacts for deployment
if: inputs.deploy-mode
run: |
mv dist _dist
mkdir -p "dist/install/linux/glibc-x86-64/"
mv _dist/*.tar.gz "dist/install/linux/glibc-x86-64"
# We don't wish to store the tarball for every nightly ever, so we only keep the latest
- name: "[Nightly] Strip version from tarball"
if: inputs.deploy-mode && inputs.version != ''
run: |
mv dist/install/linux/glibc-x86-64/*.tar.gz "dist/install/linux/glibc-x86-64/element-desktop-nightly.tar.gz"
- name: "[Release] Prepare release latest symlink"
if: inputs.deploy-mode && inputs.version == ''
shell: bash
run: |
ln -s "$(find . -type f -iname "*.tar.gz" | xargs -0 -n1 -- basename)" "element-desktop.tar.gz"
working-directory: "dist/install/linux/glibc-x86-64"
# We exclude *-unpacked as it loses permissions and the tarball contains it with correct permissions # We exclude *-unpacked as it loses permissions and the tarball contains it with correct permissions
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.deploy-mode && 'packages.element.io' || format('linux-{0}-sqlcipher-{1}', inputs.arch, inputs.sqlcipher) }} name: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
path: | path: |
dist dist
!dist/*-unpacked/** !dist/*-unpacked/**

View File

@ -23,28 +23,24 @@ on:
type: string type: string
required: false required: false
description: "Whether to sign & notarise the build, requires 'packages.element.io' environment" description: "Whether to sign & notarise the build, requires 'packages.element.io' environment"
deploy-mode:
type: boolean
required: false
description: "Whether to arrange artifacts in the arrangement needed for deployment, skipping unrelated ones"
base-url: base-url:
type: string type: string
required: false required: false
description: "The URL to which the output will be deployed, required if deploy-mode is enabled." description: "The URL to which the output will be deployed."
jobs: jobs:
build: build:
runs-on: macos-latest runs-on: macos-14 # M1
environment: ${{ inputs.sign && 'packages.element.io' || '' }} environment: ${{ inputs.sign && 'packages.element.io' || '' }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
name: webapp name: webapp
- name: Cache .hak - name: Cache .hak
id: cache id: cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
key: ${{ runner.os }}-${{ hashFiles('hakHash', 'electronVersion') }} key: ${{ runner.os }}-${{ hashFiles('hakHash', 'electronVersion') }}
path: | path: |
@ -56,9 +52,16 @@ jobs:
rustup toolchain install stable --profile minimal --no-self-update rustup toolchain install stable --profile minimal --no-self-update
rustup default stable rustup default stable
rustup target add aarch64-apple-darwin rustup target add aarch64-apple-darwin
rustup target add x86_64-apple-darwin
- uses: actions/setup-node@v3 # M1 macos-14 comes without Python preinstalled
- uses: actions/setup-python@v5
with: with:
python-version: "3.12"
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: "yarn" cache: "yarn"
# Does not need branch matching as only analyses this layer # Does not need branch matching as only analyses this layer
@ -67,21 +70,23 @@ jobs:
- name: Build Natives - name: Build Natives
if: steps.cache.outputs.cache-hit != 'true' if: steps.cache.outputs.cache-hit != 'true'
run: "yarn build:native:universal" run: |
# Python 3.12 drops distutils which keytar relies on
pip3 install setuptools
yarn build:native:universal
- name: "[Nightly] Resolve version" - name: "[Nightly] Resolve version"
id: nightly
if: inputs.version != '' if: inputs.version != ''
run: | run: |
echo "config-args=--nightly '${{ inputs.version }}'" >> $GITHUB_OUTPUT echo "ED_NIGHTLY=${{ inputs.version }}" >> $GITHUB_ENV
# We split these because electron-builder gets upset if we set CSC_LINK even to an empty string # We split these because electron-builder gets upset if we set CSC_LINK even to an empty string
- name: "[Signed] Build App" - name: "[Signed] Build App"
if: inputs.sign != '' if: inputs.sign != ''
run: | run: |
scripts/generate-builder-config.ts ${{ steps.nightly.outputs.config-args }} --notarytool-team-id='${{ secrets.APPLE_TEAM_ID }}' yarn build:universal --publish never
yarn build:universal --publish never --config electron-builder.json
env: env:
ED_NOTARYTOOL_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CSC_KEY_PASSWORD }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CSC_KEY_PASSWORD }}
@ -90,27 +95,21 @@ jobs:
- name: Check app was signed & notarised successfully - name: Check app was signed & notarised successfully
if: inputs.sign != '' if: inputs.sign != ''
run: | run: |
hdiutil attach dist/*.dmg hdiutil attach dist/*.dmg -mountpoint /Volumes/Element
codesign -dv --verbose=4 /Volumes/Element*/*.app codesign -dv --verbose=4 /Volumes/Element/*.app
spctl -a -vvv -t install /Volumes/Element*/*.app spctl -a -vvv -t install /Volumes/Element/*.app
hdiutil detach /Volumes/Element* hdiutil detach /Volumes/Element
- name: "[Unsigned] Build App" - name: "[Unsigned] Build App"
if: inputs.sign == '' if: inputs.sign == ''
run: | run: |
scripts/generate-builder-config.ts ${{ steps.nightly.outputs.config-args }} yarn build:universal --publish never
yarn build:universal --publish never --config electron-builder.json
env: env:
CSC_IDENTITY_AUTO_DISCOVERY: false CSC_IDENTITY_AUTO_DISCOVERY: false
- name: Prepare artifacts for deployment - name: Generate releases.json
if: inputs.deploy-mode if: inputs.base-url
run: | run: |
mv dist _dist
mkdir -p dist/install/macos dist/update/macos
mv _dist/*-mac.zip dist/update/macos/
mv _dist/*.dmg dist/install/macos/
PKG_JSON_VERSION=$(cat package.json | jq -r .version) PKG_JSON_VERSION=$(cat package.json | jq -r .version)
LATEST=$(find dist -type f -iname "*-mac.zip" | xargs -0 -n1 -- basename) LATEST=$(find dist -type f -iname "*-mac.zip" | xargs -0 -n1 -- basename)
# Encode spaces in the URL as Squirrel.Mac complains about bad JSON otherwise # Encode spaces in the URL as Squirrel.Mac complains about bad JSON otherwise
@ -127,30 +126,18 @@ jobs:
}, },
}], }],
} }
' > dist/update/macos/releases.json ' > dist/releases.json
jq -n --arg url "$URL" ' jq -n --arg url "$URL" '
{ url: $url } { url: $url }
' > dist/update/macos/releases-legacy.json ' > dist/releases-legacy.json
env: env:
VERSION: ${{ inputs.version }} VERSION: ${{ inputs.version }}
# We don't wish to store the installer for every nightly ever, so we only keep the latest # We exclude mac-universal as the unpacked app takes forever to upload and zip and dmg already contains it
- name: "[Nightly] Strip version from installer file"
if: inputs.deploy-mode && inputs.version != ''
run: |
mv dist/install/macos/*.dmg "dist/install/macos/Element Nightly.dmg"
- name: "[Release] Prepare release latest symlink"
if: inputs.deploy-mode && inputs.version == ''
run: |
ln -s "$(find . -type f -iname "*.dmg" | xargs -0 -n1 -- basename)" "Element.dmg"
working-directory: "dist/install/macos"
# We exclude mac-universal as the unpacked app takes forever to upload and zip and dmg already contain it
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.deploy-mode && 'packages.element.io' || 'macos' }} name: macos
path: | path: |
dist dist
!dist/mac-universal/** !dist/mac-universal/**

View File

@ -15,6 +15,11 @@ on:
required: false required: false
default: false default: false
description: "Whether the build is a Nightly and to calculate the version strings new builds should use" description: "Whether the build is a Nightly and to calculate the version strings new builds should use"
deploy:
type: boolean
required: false
default: false
description: "Whether the build should be deployed to production"
secrets: secrets:
# Required if `nightly` is set # Required if `nightly` is set
CF_R2_ACCESS_KEY_ID: CF_R2_ACCESS_KEY_ID:
@ -29,10 +34,13 @@ on:
packages-dir: packages-dir:
description: "The directory non-deb packages for this run should live in within packages.element.io" description: "The directory non-deb packages for this run should live in within packages.element.io"
value: ${{ inputs.nightly && 'nightly' || 'desktop' }} value: ${{ inputs.nightly && 'nightly' || 'desktop' }}
# This is just a simple pass-through of the input to simplify reuse of complex inline conditions # These are just simple pass-throughs of the input to simplify reuse of complex inline conditions
config: config:
description: "The relative path to the config file for this run" description: "The relative path to the config file for this run"
value: ${{ inputs.config }} value: ${{ inputs.config }}
deploy:
description: "The relative path to the config file for this run"
value: ${{ inputs.deploy }}
jobs: jobs:
prepare: prepare:
name: Prepare name: Prepare
@ -41,10 +49,11 @@ jobs:
outputs: outputs:
nightly-version: ${{ steps.versions.outputs.nightly }} nightly-version: ${{ steps.versions.outputs.nightly }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version-file: package.json
cache: "yarn" cache: "yarn"
- name: Install Deps - name: Install Deps
@ -74,6 +83,7 @@ jobs:
# Pick the greatest one # Pick the greatest one
VERSION=$(cat VERSIONS | sort -uf | tail -n1) VERSION=$(cat VERSIONS | sort -uf | tail -n1)
echo "Found latest nightly version $VERSION"
# Increment it # Increment it
echo "nightly=$(scripts/generate-nightly-version.ts --latest $VERSION)" >> $GITHUB_OUTPUT echo "nightly=$(scripts/generate-nightly-version.ts --latest $VERSION)" >> $GITHUB_OUTPUT
env: env:
@ -124,11 +134,11 @@ jobs:
echo "| Component | Version |" >> $GITHUB_STEP_SUMMARY echo "| Component | Version |" >> $GITHUB_STEP_SUMMARY
echo "| ----------- | ------- |" >> $GITHUB_STEP_SUMMARY echo "| ----------- | ------- |" >> $GITHUB_STEP_SUMMARY
echo "| Bundle Hash | $BUNDLE_HASH |" >> $GITHUB_STEP_SUMMARY echo "| Bundle Hash | $BUNDLE_HASH |" >> $GITHUB_STEP_SUMMARY
echo "| Element Web | [$WEB_VERSION](https://github.com/vector-im/element-web/commit/$WEB_VERSION) |" >> $GITHUB_STEP_SUMMARY echo "| Element Web | [$WEB_VERSION](https://github.com/element-hq/element-web/commit/$WEB_VERSION) |" >> $GITHUB_STEP_SUMMARY
echo "| React SDK | [$REACT_VERSION](https://github.com/matrix-org/matrix-react-sdk/commit/$REACT_VERSION) |" >> $GITHUB_STEP_SUMMARY echo "| React SDK | [$REACT_VERSION](https://github.com/matrix-org/matrix-react-sdk/commit/$REACT_VERSION) |" >> $GITHUB_STEP_SUMMARY
echo "| JS SDK | [$JS_VERSION](https://github.com/matrix-org/matrix-js-sdk/commit/$JS_VERSION) |" >> $GITHUB_STEP_SUMMARY echo "| JS SDK | [$JS_VERSION](https://github.com/matrix-org/matrix-js-sdk/commit/$JS_VERSION) |" >> $GITHUB_STEP_SUMMARY
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v4
with: with:
name: webapp name: webapp
retention-days: 1 retention-days: 1

View File

@ -1,6 +1,11 @@
# This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build # This workflow relies on actions/cache to store the hak dependency artifacts as they take a long time to build
# Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure # Due to this extra care must be taken to only ever run all build_* scripts against the same branch to ensure
# the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch. # the correct cache scoping, and additional care must be taken to not run untrusted actions on the develop branch.
# window-latest by default uses the pwsh shell which breaks codeSigningCert in the workflow
defaults:
run:
shell: powershell
on: on:
workflow_call: workflow_call:
secrets: secrets:
@ -14,7 +19,7 @@ on:
arch: arch:
type: string type: string
required: true required: true
description: "The architecture to build for, one of 'x64' | 'x86' | 'arm64'" description: "The architecture to build for, one of 'x64' | 'ia32' | 'arm64'"
version: version:
type: string type: string
required: false required: false
@ -23,10 +28,6 @@ on:
type: string type: string
required: false required: false
description: "Whether to sign & notarise the build, requires 'packages.element.io' environment" description: "Whether to sign & notarise the build, requires 'packages.element.io' environment"
deploy-mode:
type: boolean
required: false
description: "Whether to arrange artifacts in the arrangement needed for deployment, skipping unrelated ones"
jobs: jobs:
build: build:
runs-on: windows-latest runs-on: windows-latest
@ -34,7 +35,7 @@ jobs:
env: env:
SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.22000.0/x86/signtool.exe" SIGNTOOL_PATH: "C:/Program Files (x86)/Windows Kits/10/bin/10.0.22000.0/x86/signtool.exe"
steps: steps:
- uses: kanga333/variable-mapper@3681b75f5c6c00162721168fb91ab74925eaebcb - uses: nbucic/variable-mapper@0673f6891a0619ba7c002ecfed0f9f4f39017b6f
id: config id: config
with: with:
key: "${{ inputs.arch }}" key: "${{ inputs.arch }}"
@ -42,38 +43,36 @@ jobs:
map: | map: |
{ {
"x64": { "x64": {
"target": "x86_64-pc-windows-msvc", "target": "x86_64-pc-windows-msvc"
"dir": "x64"
}, },
"arm64": { "arm64": {
"target": "aarch64-pc-windows-msvc", "target": "aarch64-pc-windows-msvc",
"build-args": "--arm64", "build-args": "--arm64",
"arch": "amd64_arm64", "arch": "amd64_arm64"
"dir": "arm64"
}, },
"x86": { "ia32": {
"target": "i686-pc-windows-msvc", "target": "i686-pc-windows-msvc",
"build-args": "--ia32", "build-args": "--ia32",
"dir": "ia32" "arch": "x86"
} }
} }
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/download-artifact@v3 - uses: actions/download-artifact@v4
with: with:
name: webapp name: webapp
- name: Cache .hak - name: Cache .hak
id: cache id: cache
uses: actions/cache@v3 uses: actions/cache@v4
with: with:
key: ${{ runner.os }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion') }} key: ${{ runner.os }}-${{ inputs.arch }}-${{ hashFiles('hakHash', 'electronVersion') }}
path: | path: |
./.hak ./.hak
- name: Set up build tools - name: Set up build tools
uses: ilammy/msvc-dev-cmd@cec98b9d092141f74527d0afa6feb2af698cfe89 uses: ilammy/msvc-dev-cmd@0b201ec74fa43914dc39ae48a89fd1d8cb592756 # v1.13.0
with: with:
arch: ${{ steps.config.outputs.arch || inputs.arch }} arch: ${{ steps.config.outputs.arch || inputs.arch }}
@ -100,8 +99,9 @@ jobs:
rustup default stable rustup default stable
rustup target add ${{ steps.config.outputs.target }} rustup target add ${{ steps.config.outputs.target }}
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version-file: package.json
cache: "yarn" cache: "yarn"
# Does not need branch matching as only analyses this layer # Does not need branch matching as only analyses this layer
@ -115,13 +115,14 @@ jobs:
yarn build:native --target ${{ steps.config.outputs.target }} yarn build:native --target ${{ steps.config.outputs.target }}
- name: Install and configure eSigner CKA - name: Install and configure eSigner CKA
id: esigner
if: inputs.sign if: inputs.sign
run: | run: |
Set-StrictMode -Version 'Latest' Set-StrictMode -Version 'Latest'
# Download # Download, extract, and rename
Invoke-WebRequest -OutFile eSigner_CKA.exe "https://packages.element.io/tools/SSL.COM%20eSigner%20CKA_1.0.4-build-20230221_signed.exe" Invoke-WebRequest -OutFile eSigner_CKA.zip "$env:ESIGNER_URL"
Expand-Archive -Path eSigner_CKA.zip -DestinationPath .
Get-ChildItem -Path * -Include "*_build_*.exe" | Rename-Item -NewName eSigner_CKA.exe
# Install # Install
New-Item -ItemType Directory -Force -Path "$env:INSTALL_DIR" New-Item -ItemType Directory -Force -Path "$env:INSTALL_DIR"
@ -144,61 +145,37 @@ jobs:
# Extract thumbprint and subject name # Extract thumbprint and subject name
$Thumbprint = $CodeSigningCert.Thumbprint $Thumbprint = $CodeSigningCert.Thumbprint
$SubjectName = ($CodeSigningCert.Subject -replace ", ?", "`n" | ConvertFrom-StringData).CN $SubjectName = ($CodeSigningCert.Subject -replace ", ?", "`n" | ConvertFrom-StringData).CN
echo "config-args=--signtool-thumbprint '$Thumbprint' --signtool-subject-name '$SubjectName'" >> $env:GITHUB_OUTPUT
echo "ED_SIGNTOOL_THUMBPRINT=$Thumbprint" >> $env:GITHUB_ENV
echo "ED_SIGNTOOL_SUBJECT_NAME=$SubjectName" >> $env:GITHUB_ENV
env: env:
ESIGNER_URL: https://github.com/SSLcom/eSignerCKA/releases/download/v1.0.6/SSL.COM-eSigner-CKA_1.0.6.zip
INSTALL_DIR: C:\Users\runneradmin\eSignerCKA INSTALL_DIR: C:\Users\runneradmin\eSignerCKA
MASTER_KEY_FILE: C:\Users\runneradmin\eSignerCKA\master.key MASTER_KEY_FILE: C:\Users\runneradmin\eSignerCKA\master.key
- name: "[Nightly] Resolve version" - name: "[Nightly] Resolve version"
id: nightly
if: inputs.version != '' if: inputs.version != ''
shell: bash shell: bash
run: | run: |
echo "config-args=--nightly '${{ inputs.version }}'" >> $GITHUB_OUTPUT echo "ED_NIGHTLY=${{ inputs.version }}" >> $GITHUB_ENV
# XXX: For whatever reason if we use `yarn build ...` it freezes, but splitting it into parts it is fine
- run: yarn run build:ts
- run: yarn run build:res
- name: Build App - name: Build App
run: | run: |
yarn ts-node scripts/generate-builder-config.ts ${{ steps.nightly.outputs.config-args }} ${{ steps.esigner.outputs.config-args }} yarn electron-builder --publish never -w ${{ steps.config.outputs.build-args }}
yarn build --publish never -w --config electron-builder.json ${{ steps.config.outputs.build-args }}
- name: Check app was signed successfully - name: Check app was signed successfully
if: inputs.sign != '' if: inputs.sign != ''
run: | run: |
. "$env:SIGNTOOL_PATH" verify /pa (get-item ./dist/squirrel-windows*/*.exe) . "$env:SIGNTOOL_PATH" verify /pa (get-item ./dist/squirrel-windows*/*.exe)
- name: Prepare artifacts for deployment
if: inputs.deploy-mode
shell: bash
run: |
mv dist _dist
mkdir -p "dist/install/win32/$DIR/msi" "dist/update/win32/$DIR"
mv _dist/squirrel-windows*/*.exe "dist/install/win32/$DIR"
mv _dist/squirrel-windows*/*.nupkg "dist/update/win32/$DIR/"
mv _dist/squirrel-windows*/RELEASES "dist/update/win32/$DIR/"
# mv _dist/*.msi "dist/install/win32/$DIR/msi/"
env:
DIR: ${{ steps.config.outputs.dir }}
# We don't wish to store the installer for every nightly ever, so we only keep the latest
- name: "[Nightly] Strip version from installer file"
if: inputs.deploy-mode && inputs.version != ''
shell: bash
run: |
mv dist/install/win32/$DIR/*.exe "dist/install/win32/$DIR/Element Nightly Setup.exe"
# mv dist/install/win32/$DIR/msi/*.msi "dist/install/win32/$DIR/msi/Element Nightly Setup.msi"
env:
DIR: ${{ steps.config.outputs.dir }}
- name: "[Release] Prepare release latest symlink"
if: inputs.deploy-mode && inputs.version == ''
shell: bash
run: |
ln -s "$(find . -type f -iname "*.exe" | xargs -0 -n1 -- basename)" "Element Setup.exe"
working-directory: "dist/install/win32/${{ steps.config.outputs.dir }}"
- name: Upload Artifacts - name: Upload Artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v4
with: with:
name: ${{ inputs.deploy-mode && 'packages.element.io' || format('win-{0}', inputs.arch) }} name: win-${{ inputs.arch }}
path: dist path: |
dist
retention-days: 1 retention-days: 1

View File

@ -17,10 +17,10 @@ jobs:
contents: read contents: read
packages: write packages: write
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@b4bedf8053341df3b5a9f9e0f2cf4e79e27360c6 uses: docker/login-action@83a00bc1ab5ded6580f31df1c49e6aaa932d840d
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@ -28,14 +28,14 @@ jobs:
- name: Extract metadata for Docker - name: Extract metadata for Docker
id: meta id: meta
uses: docker/metadata-action@879dcbb708d40f8b8679d4f7941b938a086e23a7 uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5
with: with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: | tags: |
type=ref,event=branch type=ref,event=branch
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@4c1b68d83ad20cc1a09620ca477d5bbbb5fa14d0 uses: docker/build-push-action@4a13e500e55cf31b7a5d59a38ab2040ab0f42f56 # v5
with: with:
context: dockerbuild context: dockerbuild
push: true push: true

View File

@ -1,6 +1,8 @@
name: Localazy Download name: Localazy Download
on: on:
workflow_dispatch: {} workflow_dispatch: {}
schedule:
- cron: "0 6 * * 1,3,5" # Every Monday, Wednesday and Friday at 6am UTC
jobs: jobs:
download: download:
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_download.yaml@main

View File

@ -3,7 +3,7 @@ on:
push: push:
branches: [develop] branches: [develop]
paths: paths:
- "src/strings/i18n/en_EN.json" - "src/i18n/strings/en_EN.json"
jobs: jobs:
upload: upload:
uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_upload.yaml@main uses: matrix-org/matrix-web-i18n/.github/workflows/localazy_upload.yaml@main

11
.github/workflows/release-drafter.yml vendored Normal file
View File

@ -0,0 +1,11 @@
name: Release Drafter
on:
push:
branches: [staging]
workflow_dispatch: {}
concurrency: ${{ github.workflow }}
jobs:
draft:
uses: matrix-org/matrix-js-sdk/.github/workflows/release-drafter-workflow.yml@develop
with:
include-changes: element-hq/element-web@$VERSION

11
.github/workflows/release-gitflow.yml vendored Normal file
View File

@ -0,0 +1,11 @@
# Gitflow merge-back master->develop
name: Merge master -> develop
on:
push:
branches: [master]
concurrency: ${{ github.repository }}-${{ github.workflow }}
jobs:
merge:
uses: matrix-org/matrix-js-sdk/.github/workflows/release-gitflow.yml@develop
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

39
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,39 @@
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 }}
jobs:
release:
uses: matrix-org/matrix-js-sdk/.github/workflows/release-make.yml@develop
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' }}
include-changes: element-hq/element-web@$VERSION
gpg-fingerprint: ${{ vars.GPG_FINGERPRINT }}
expected-asset-count: 1
check:
name: Post release checks
needs: release
runs-on: ubuntu-latest
steps:
- name: Wait for desktop packaging
uses: t3chguy/wait-on-check-action@18541021811b56544d90e0f073401c2b99e249d6 # fork
with:
ref: master
repo-token: ${{ secrets.GITHUB_TOKEN }}
wait-interval: 10
check-name: Deploy
allowed-conclusions: success

View File

@ -1,49 +0,0 @@
on:
workflow_call:
inputs:
artifact-name:
type: string
required: true
description: "The name of the artifact containing the deb to include"
secrets:
ELEMENT_BOT_TOKEN:
required: true
CF_R2_ACCESS_KEY_ID:
required: true
CF_R2_TOKEN:
required: true
# Protect reprepro database using concurrency
concurrency: reprepro
jobs:
reprepro:
name: Deploy debian package
environment: packages.element.io
runs-on: ubuntu-latest
env:
R2_INCOMING_BUCKET: ${{ vars.R2_INCOMING_BUCKET }}
R2_URL: ${{ vars.CF_R2_S3_API }}
steps:
- name: Download artifact
uses: actions/download-artifact@v3
with:
name: ${{ inputs.artifact-name }}
path: dist
- name: Upload incoming deb
id: upload
run: |
deb="$(ls *.deb | tail -n1)"
echo "incoming=$deb" >> $GITHUB_OUTPUT
aws s3 cp "$deb" "s3://$R2_INCOMING_BUCKET" --endpoint-url "$R2_URL" --region auto
working-directory: dist
env:
AWS_ACCESS_KEY_ID: ${{ secrets.CF_R2_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.CF_R2_TOKEN }}
- name: Notify packages.element.io of incoming deb
uses: peter-evans/repository-dispatch@bf47d102fdb849e755b0b0023ea3e81a44b6f570 # v2
with:
token: ${{ secrets.ELEMENT_BOT_TOKEN }}
repository: vector-im/packages.element.io
event-type: reprepro-incoming
client-payload: '{"incoming": "${{ steps.upload.outputs.incoming }}"}'

View File

@ -8,10 +8,11 @@ jobs:
name: "Typescript Syntax Check" name: "Typescript Syntax Check"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version-file: package.json
cache: "yarn" cache: "yarn"
# Does not need branch matching as only analyses this layer # Does not need branch matching as only analyses this layer
@ -29,10 +30,11 @@ jobs:
name: "ESLint" name: "ESLint"
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v4
- uses: actions/setup-node@v3 - uses: actions/setup-node@v4
with: with:
node-version-file: package.json
cache: "yarn" cache: "yarn"
# Does not need branch matching as only analyses this layer # Does not need branch matching as only analyses this layer
@ -41,3 +43,38 @@ jobs:
- name: Run Linter - name: Run Linter
run: "yarn run lint:js" run: "yarn run lint:js"
workflow_lint:
name: "Workflow Lint"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: "yarn"
# 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-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: package.json
cache: "yarn"
- name: Install Deps
run: "yarn install --frozen-lockfile"
- name: Run linter
run: "yarn run lint:knip"

21
.github/workflows/sync-labels.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Sync labels
on:
workflow_dispatch: {}
schedule:
- cron: "0 2 * * *" # 2am every day
push:
branches:
- develop
paths:
- .github/labels.yml
jobs:
sync-labels:
uses: element-hq/element-meta/.github/workflows/sync-labels.yml@develop
with:
LABELS: |
element-hq/element-web
.github/labels.yml
DELETE: true
WET: true
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

10
.github/workflows/triage-labelled.yml vendored Normal file
View File

@ -0,0 +1,10 @@
name: Move labelled issues to correct projects
on:
issues:
types: [labeled]
jobs:
call-triage-labelled:
uses: element-hq/element-web/.github/workflows/triage-labelled.yml@develop
secrets: inherit

View File

@ -1,8 +0,0 @@
name: Upgrade Dependencies
on:
workflow_dispatch: {}
jobs:
upgrade:
uses: matrix-org/matrix-js-sdk/.github/workflows/upgrade_dependencies.yml@develop
secrets:
ELEMENT_BOT_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

View File

@ -8,6 +8,8 @@
/CHANGELOG.md /CHANGELOG.md
/package-lock.json /package-lock.json
/yarn.lock /yarn.lock
/playwright/html-report
/playwright/test-results
**/.idea **/.idea
.vscode .vscode

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
# Docker image to facilitate building Element Desktop with native bits using a glibc version with broader compatibility # Docker image to facilitate building Element Desktop with native bits using a glibc version with broader compatibility
FROM buildpack-deps:bionic-curl FROM buildpack-deps:buster-curl
ENV DEBIAN_FRONTEND noninteractive ENV DEBIAN_FRONTEND noninteractive
@ -11,9 +11,9 @@ RUN apt-get -qq update && apt-get -qq dist-upgrade && \
# python for node-gyp # python for node-gyp
# rpm is required for FPM to build rpm package # rpm is required for FPM to build rpm package
# tclsh is required for building SQLite as part of SQLCipher # tclsh is required for building SQLite as part of SQLCipher
# libsecret-1-dev and libgnome-keyring-dev are required even for prebuild keytar # libsecret-1-dev is required even for prebuild keytar
apt-get -qq install --no-install-recommends qtbase5-dev bsdtar build-essential autoconf libssl-dev gcc-multilib g++-multilib lzip rpm python libcurl4 git git-lfs ssh unzip tcl \ apt-get -qq install --no-install-recommends qtbase5-dev bsdtar build-essential autoconf libssl-dev gcc-multilib g++-multilib lzip rpm python libcurl4 git git-lfs ssh unzip tcl \
libsecret-1-dev libgnome-keyring-dev \ libsecret-1-dev \
libopenjp2-tools \ libopenjp2-tools \
# Used by github actions \ # Used by github actions \
jq grep file \ jq grep file \
@ -34,13 +34,11 @@ ENV LC_ALL C.UTF-8
ENV DEBUG_COLORS true ENV DEBUG_COLORS true
ENV FORCE_COLOR true ENV FORCE_COLOR true
ENV NODE_VERSION 16.18.1 ENV NODE_VERSION 18.19.0
# this package is used for snapcraft and we should not clear apt list - to avoid apt-get update during snap build # this package is used for snapcraft and we should not clear apt list - to avoid apt-get update during snap build
RUN curl --proto "=https" -L https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz | tar xz -C /usr/local --strip-components=1 && \ RUN curl --proto "=https" -L https://nodejs.org/dist/v$NODE_VERSION/node-v$NODE_VERSION-linux-x64.tar.gz | tar xz -C /usr/local --strip-components=1 && \
unlink /usr/local/CHANGELOG.md && unlink /usr/local/LICENSE && unlink /usr/local/README.md && \ unlink /usr/local/CHANGELOG.md && unlink /usr/local/LICENSE && unlink /usr/local/README.md
# https://github.com/npm/npm/issues/4531
npm config set unsafe-perm true
ENV RUSTUP_HOME=/usr/local/rustup \ ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \ CARGO_HOME=/usr/local/cargo \

17
docs/SUMMARY.md Normal file
View File

@ -0,0 +1,17 @@
# Summary
- [Introduction](../README.md)
# Build
- [Native Node modules](native-node-modules.md)
- [Windows requirements](windows-requirements.md)
# Distribution
- [Updates](updates.md)
- [Packaging](packaging.md)
# Setup
- [Config](config.md)

15
docs/config.md Normal file
View File

@ -0,0 +1,15 @@
# Configuration
All Element Web options documented [here](https://github.com/vector-im/element-web/blob/develop/docs/config.md) can be used as well as the following:
---
The app contains a configuration file specified at build time using [these instructions](https://github.com/vector-im/element-desktop/#config).
This config can be overwritten by the end using by creating a `config.json` file at the paths described [here](https://github.com/vector-im/element-desktop/#user-specified-configjson).
After changing the config, the app will need to be exited fully (including via the task tray) and re-started.
---
1. `update_base_url`: Specifies the URL of the update server, see [document](https://github.com/vector-im/element-desktop/blob/develop/docs/updates.md).
2. `web_base_url`: Specifies the Element Web URL when performing actions such as popout widget. Defaults to `https://app.element.io/`.

55
docs/packaging.md Normal file
View File

@ -0,0 +1,55 @@
## Packaging nightlies
Element Desktop nightly builds are build automatically by the [Github Actions workflow](https://github.com/vector-im/element-desktop/blob/develop/.github/workflows/build_and_deploy.yaml).
The schedule is currently set for once a day at 9am UTC. It will deploy to packages.element.io upon completion.
## Triggering a manual nightly build
Simply go to https://github.com/vector-im/element-desktop/actions/workflows/build_and_deploy.yaml
1. Click `Run workflow`
1. Feel free to make changes to the checkboxes depending on the circumstances
1. Click the green `Run workflow`
## Packaging releases
**Don't do this for RCs! We don't build Element Desktop for RCs.**
For releasing Element Desktop, we assume the following prerequisites:
- a tag of `element-desktop` repo with the Element Desktop version to be released set in `package.json`.
- an Element Web tarball published to GitHub with a matching version number.
**Both of these are done automatically when you run the release automation.**
The packaging is kicked off automagically for you when a Github Release for Element Desktop is published.
### More detail on the github actions
We moved to Github Actions for the following reasons:
1. Removing single point of failure
2. Improving reliability
3. Unblocking the packaging on a single individual
4. Improving parallelism
The Windows builds are signed by SSL.com using their Cloud Key Adapter for eSigner.
This allows us to use Microsoft's signtool to interface with eSigner and send them a hash of the exe along with
credentials in exchange for a signed certificate which we attach onto all the relevant files.
The Apple builds are signed using standard code signing means and then notarised to appease GateKeeper.
The Linux builds are distributed via a signed reprepro repository.
The packages.element.io site is a public Cloudflare R2 bucket which is deployed to solely from Github Actions.
The main bucket in R2 is `packages-element-io` which is a direct mapping of packages.element.io,
we have a workflow which generates the index.html files there to imitate a public index which Cloudflare does not currently support.
The reprepro database lives in `packages-element-io-db`.
There is an additional pair of buckets of same name but appended with `-test` which can be used for testing,
these land on https://packages-element-io-test.element.io/.
### Debian/Ubuntu Distributions
We used to add a new distribution to match each Debian and Ubuntu release. As of April 2020, we have created a `default` distribution that everyone can use (since the packages have never differed by distribution anyway).
The distribution configuration lives in https://github.com/vector-im/packages.element.io/blob/master/debian/conf/distributions as a canonical source.

View File

@ -1,6 +1,5 @@
# Windows # Windows
## Requirements to build native modules ## Requirements to build native modules
We rely on Github Actions `windows-latest` plus a few extra utilities as per [the workflow](https://github.com/vector-im/element-desktop/blob/develop/.github/workflows/build_windows.yaml). We rely on Github Actions `windows-latest` plus a few extra utilities as per [the workflow](https://github.com/vector-im/element-desktop/blob/develop/.github/workflows/build_windows.yaml).

215
electron-builder.js Normal file
View File

@ -0,0 +1,215 @@
const os = require("os");
const fs = require("fs");
const path = require("path");
const Arch = require("electron-builder").Arch;
const { flipFuses, FuseVersion, FuseV1Options } = require("@electron/fuses");
// Typescript conversion blocked on https://github.com/electron-userland/electron-builder/issues/7775
/**
* This script has different outputs depending on your os platform.
*
* On Windows:
* Prefixes the nightly version with `0.0.1-nightly.` as it breaks if it is not semver
* Passes $ED_SIGNTOOL_THUMBPRINT and $ED_SIGNTOOL_SUBJECT_NAME to
* build.win.signingHashAlgorithms and build.win.certificateSubjectName respectively if specified.
*
* On macOS:
* Passes $ED_NOTARYTOOL_TEAM_ID to build.mac.notarize.notarize if specified
*
* On Linux:
* Replaces spaces in the product name with dashes as spaces in paths can cause issues
* Removes libsqlcipher0 recommended dependency if env SQLCIPHER_BUNDLED is asserted.
* Passes $ED_DEBIAN_CHANGELOG to build.deb.fpm if specified
*/
const NIGHTLY_APP_ID = "im.riot.nightly";
const NIGHTLY_DEB_NAME = "element-nightly";
const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
/**
* @type {import('electron-builder').Configuration}
* @see https://www.electron.build/configuration/configuration
*/
const config = {
appId: "im.riot.app",
asarUnpack: "**/*.node",
afterPack: async (context) => {
if (context.electronPlatformName !== "darwin" || context.arch === Arch.universal) {
// Burn in electron fuses for proactive security hardening.
// On macOS, we only do this for the universal package, as the constituent arm64 and amd64 packages are embedded within.
const ext = {
darwin: ".app",
win32: ".exe",
linux: "",
}[context.electronPlatformName];
let executableName = context.packager.appInfo.productFilename;
if (context.electronPlatformName === "linux") {
// Linux uses the package name as the executable name
executableName = context.packager.appInfo.name;
}
const electronBinaryPath = path.join(context.appOutDir, `${executableName}${ext}`);
console.log(`Flipping fuses for: ${electronBinaryPath}`);
await flipFuses(electronBinaryPath, {
version: FuseVersion.V1,
resetAdHocDarwinSignature: context.electronPlatformName === "darwin" && context.arch === Arch.universal,
[FuseV1Options.EnableCookieEncryption]: true,
[FuseV1Options.OnlyLoadAppFromAsar]: true,
[FuseV1Options.RunAsNode]: false,
[FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
[FuseV1Options.EnableNodeCliInspectArguments]: false,
// Mac app crashes on arm for us when `LoadBrowserProcessSpecificV8Snapshot` is enabled
[FuseV1Options.LoadBrowserProcessSpecificV8Snapshot]: false,
// https://github.com/electron/fuses/issues/7
[FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: false,
});
}
},
files: [
"package.json",
{
from: ".hak/hakModules",
to: "node_modules",
},
"lib/**",
],
extraResources: [
{
from: "res/img",
to: "img",
},
"webapp.asar",
],
extraMetadata: {
name: pkg.name,
productName: pkg.productName,
description: pkg.description,
},
linux: {
target: ["tar.gz", "deb"],
category: "Network;InstantMessaging;Chat",
maintainer: "support@element.io",
icon: "build/icons",
desktop: {
MimeType: "x-scheme-handler/element",
},
},
deb: {
packageCategory: "net",
depends: [
"libgtk-3-0",
"libnotify4",
"libnss3",
"libxss1",
"libxtst6",
"xdg-utils",
"libatspi2.0-0",
"libuuid1",
"libsecret-1-0",
"libasound2",
"libgbm1",
],
recommends: ["libsqlcipher0", "element-io-archive-keyring"],
fpm: [
"--deb-field",
"Replaces: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)",
"--deb-field",
"Breaks: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)",
],
},
mac: {
category: "public.app-category.social-networking",
darkModeSupport: true,
hardenedRuntime: true,
gatekeeperAssess: true,
entitlements: "./build/entitlements.mac.plist",
icon: "build/icons/icon.icns",
},
win: {
target: ["squirrel"],
signingHashAlgorithms: ["sha256"],
icon: "build/icons/icon.ico",
},
directories: {
output: "dist",
},
protocols: [
{
name: "element",
schemes: ["element"],
},
],
};
/**
* Allow specifying windows signing cert via env vars
* @param {string} process.env.ED_SIGNTOOL_SUBJECT_NAME
* @param {string} process.env.ED_SIGNTOOL_THUMBPRINT
*/
if (process.env.ED_SIGNTOOL_SUBJECT_NAME && process.env.ED_SIGNTOOL_THUMBPRINT) {
config.win.certificateSubjectName = process.env.ED_SIGNTOOL_SUBJECT_NAME;
config.win.certificateSha1 = process.env.ED_SIGNTOOL_THUMBPRINT;
}
/**
* Allow specifying macOS notary team id via env var
* @param {string} process.env.ED_NOTARYTOOL_TEAM_ID
*/
if (process.env.ED_NOTARYTOOL_TEAM_ID) {
config.mac.notarize = {
teamId: process.env.ED_NOTARYTOOL_TEAM_ID,
};
}
/**
* Allow specifying nightly version via env var
* @param {string} process.env.ED_NIGHTLY
*/
if (process.env.ED_NIGHTLY) {
config.deb.fpm = []; // Clear the fpm as the breaks deb fields don't apply to nightly
config.appId = NIGHTLY_APP_ID;
config.extraMetadata.productName += " Nightly";
config.extraMetadata.name += "-nightly";
config.extraMetadata.description += " (nightly unstable build)";
config.deb.fpm.push("--name", NIGHTLY_DEB_NAME);
let version = process.env.ED_NIGHTLY;
if (os.platform() === "win32") {
// The windows packager relies on parsing this as semver, so we have to make it look like one.
// This will give our update packages really stupid names, but we probably can't change that either
// because squirrel windows parses them for the version too. We don't really care: nobody sees them.
// We just give the installer a static name, so you'll just see this in the 'about' dialog.
// Turns out if you use 0.0.0 here it makes Squirrel windows crash, so we use 0.0.1.
version = "0.0.1-nightly." + version;
}
config.extraMetadata.version = version;
}
if (os.platform() === "linux") {
// Electron crashes on debian if there's a space in the path.
// https://github.com/vector-im/element-web/issues/13171
config.extraMetadata.productName = config.extraMetadata.productName.replace(/ /g, "-");
/**
* Allow specifying deb changelog via env var
* @param {string} process.env.ED_DEB_CHANGELOG
*/
if (process.env.ED_DEBIAN_CHANGELOG) {
config.deb.fpm.push(`--deb-changelog=${process.env.ED_DEBIAN_CHANGELOG}`);
}
if (process.env.SQLCIPHER_BUNDLED) {
// Remove sqlcipher dependency when using bundled
config.deb.recommends = config.deb.recommends?.filter((d) => d !== "libsqlcipher0");
}
}
exports.default = config;

View File

@ -50,10 +50,11 @@
"privacy_policy_url": "https://element.io/cookie-policy", "privacy_policy_url": "https://element.io/cookie-policy",
"features": { "features": {
"feature_spotlight": true, "feature_spotlight": true,
"feature_video_rooms": true "feature_video_rooms": true,
"feature_element_call_video_rooms": true
}, },
"element_call": { "element_call": {
"url": "https://element-call-livekit.netlify.app" "url": "https://call.element.dev"
}, },
"map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx" "map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"
} }

View File

@ -28,16 +28,12 @@ export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Prom
console.log("Running yarn install"); console.log("Running yarn install");
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn( const proc = childProcess.spawn("yarn" + (hakEnv.isWin() ? ".cmd" : ""), ["install"], {
"yarn" + (hakEnv.isWin() ? ".cmd" : ""), cwd: moduleInfo.moduleBuildDir,
["install"], env,
{ shell: true,
cwd: moduleInfo.moduleBuildDir, stdio: "inherit",
env, });
shell: true,
stdio: "inherit",
},
);
proc.on("exit", (code) => { proc.on("exit", (code) => {
code ? reject(code) : resolve(); code ? reject(code) : resolve();
}); });
@ -47,16 +43,12 @@ export default async function (hakEnv: HakEnv, moduleInfo: DependencyInfo): Prom
console.log("Running yarn build"); console.log("Running yarn build");
await new Promise<void>((resolve, reject) => { await new Promise<void>((resolve, reject) => {
const proc = childProcess.spawn( const proc = childProcess.spawn("yarn" + (hakEnv.isWin() ? ".cmd" : ""), ["run", buildTarget], {
"yarn" + (hakEnv.isWin() ? ".cmd" : ""), cwd: moduleInfo.moduleBuildDir,
["run", buildTarget], env,
{ shell: true,
cwd: moduleInfo.moduleBuildDir, stdio: "inherit",
env, });
shell: true,
stdio: "inherit",
},
);
proc.on("exit", (code) => { proc.on("exit", (code) => {
code ? reject(code) : resolve(); code ? reject(code) : resolve();
}); });

View File

@ -5,10 +5,10 @@
"target": "es2016", "target": "es2016",
"sourceMap": false, "sourceMap": false,
"strict": true, "strict": true,
"lib": ["es2020"] "lib": ["es2020"],
}, },
"include": ["../scripts/@types/*.d.ts", "./**/*.ts"], "include": ["../scripts/@types/*.d.ts", "./**/*.ts"],
"ts-node": { "ts-node": {
"transpileOnly": true "transpileOnly": true,
} },
} }

16
knip.ts Normal file
View File

@ -0,0 +1,16 @@
import { KnipConfig } from "knip";
export default {
entry: ["src/electron-main.ts", "src/preload.ts", "electron-builder.js", ".eslintrc-*.js", "scripts/**", "hak/**"],
project: ["**/*.{js,ts}"],
ignoreDependencies: [
// Brought in via hak scripts
"keytar",
"matrix-seshat",
// Needed by `electron-builder`
"electron-builder-squirrel-windows",
// Required for `action-validator`
"@action-validator/*",
],
ignoreBinaries: ["jq", "scripts/in-docker.sh"],
} satisfies KnipConfig;

View File

@ -2,7 +2,7 @@
"name": "element-desktop", "name": "element-desktop",
"productName": "Element", "productName": "Element",
"main": "lib/electron-main.js", "main": "lib/electron-main.js",
"version": "1.11.45", "version": "1.11.59",
"description": "A feature-rich client for Matrix.org", "description": "A feature-rich client for Matrix.org",
"author": "Element", "author": "Element",
"homepage": "https://element.io", "homepage": "https://element.io",
@ -13,33 +13,35 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"files": [], "files": [],
"engines": { "engines": {
"node": ">=16.0.0" "node": ">=18.0.0"
}, },
"scripts": { "scripts": {
"i18n": "matrix-gen-i18n && yarn i18n:sort && yarn i18n:lint", "i18n": "matrix-gen-i18n && yarn i18n:sort && yarn i18n:lint",
"i18n:sort": "jq --sort-keys '.' src/i18n/strings/en_EN.json > src/i18n/strings/en_EN.json.tmp && mv src/i18n/strings/en_EN.json.tmp src/i18n/strings/en_EN.json", "i18n:sort": "jq --sort-keys '.' src/i18n/strings/en_EN.json > src/i18n/strings/en_EN.json.tmp && mv src/i18n/strings/en_EN.json.tmp src/i18n/strings/en_EN.json",
"i18n:lint": "prettier --write src/i18n/strings/ --ignore-path /dev/null", "i18n:lint": "prettier --log-level=silent --write src/i18n/strings/ --ignore-path /dev/null",
"i18n:diff": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && yarn i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json", "i18n:diff": "cp src/i18n/strings/en_EN.json src/i18n/strings/en_EN_orig.json && yarn i18n && matrix-compare-i18n-files src/i18n/strings/en_EN_orig.json src/i18n/strings/en_EN.json",
"mkdirs": "mkdirp packages deploys", "mkdirs": "mkdirp packages deploys",
"fetch": "yarn run mkdirs && ts-node scripts/fetch-package.ts", "fetch": "yarn run mkdirs && ts-node scripts/fetch-package.ts",
"asar-webapp": "asar p webapp webapp.asar", "asar-webapp": "asar p webapp webapp.asar",
"start": "yarn run build:ts && yarn run build:res && electron .", "start": "yarn run build:ts && yarn run build:res && electron .",
"lint": "yarn lint:types && yarn lint:js", "lint": "yarn lint:types && yarn lint:js && yarn lint:workflows",
"lint:js": "yarn lint:js:src && yarn lint:js:test && yarn lint:js:scripts && yarn lint:js:hak", "lint:js": "yarn lint:js:src && yarn lint:js:test && yarn lint:js:scripts && yarn lint:js:hak && prettier --check .",
"lint:js:src": "eslint --max-warnings 0 src", "lint:js:src": "eslint --max-warnings 0 src",
"lint:js:test": "eslint --max-warnings 0 --config .eslintrc-test.js test", "lint:js:test": "eslint --max-warnings 0 --config .eslintrc-test.js playwright",
"lint:js:scripts": "eslint --max-warnings 0 --config .eslintrc-scripts.js scripts", "lint:js:scripts": "eslint --max-warnings 0 --config .eslintrc-scripts.js scripts",
"lint:js:hak": "eslint --max-warnings 0 --config .eslintrc-hak.js hak", "lint:js:hak": "eslint --max-warnings 0 --config .eslintrc-hak.js hak",
"lint:js-fix": "yarn lint:js-fix:src &&yarn lint:js-fix:test && yarn lint:js-fix:scripts && yarn lint:js-fix:hak", "lint:js-fix": "yarn lint:js-fix:src &&yarn lint:js-fix:test && yarn lint:js-fix:scripts && yarn lint:js-fix:hak && prettier --log-level=warn --write .",
"lint:js-fix:src": "eslint --fix --max-warnings 0 src", "lint:js-fix:src": "eslint --fix --max-warnings 0 src",
"lint:js-fix:test": "eslint --fix --max-warnings 0 --config .eslintrc-test.js test", "lint:js-fix:test": "eslint --fix --max-warnings 0 --config .eslintrc-test.js playwright",
"lint:js-fix:scripts": "eslint --fix --max-warnings 0 --config .eslintrc-scripts.js scripts", "lint:js-fix:scripts": "eslint --fix --max-warnings 0 --config .eslintrc-scripts.js scripts",
"lint:js-fix:hak": "eslint --fix --max-warnings 0 --config .eslintrc-hak.js hak", "lint:js-fix:hak": "eslint --fix --max-warnings 0 --config .eslintrc-hak.js hak",
"lint:types": "yarn lint:types:src && yarn lint:types:test && yarn lint:types:scripts && yarn lint:types:hak", "lint:types": "yarn lint:types:src && yarn lint:types:test && yarn lint:types:scripts && yarn lint:types:hak",
"lint:types:src": "tsc --noEmit", "lint:types:src": "tsc --noEmit",
"lint:types:test": "tsc --noEmit -p test/tsconfig.json", "lint:types:test": "tsc --noEmit -p playwright/tsconfig.json",
"lint:types:scripts": "tsc --noEmit -p scripts/tsconfig.json", "lint:types:scripts": "tsc --noEmit -p scripts/tsconfig.json",
"lint:types:hak": "tsc --noEmit -p hak/tsconfig.json", "lint:types:hak": "tsc --noEmit -p hak/tsconfig.json",
"lint:workflows": "find .github/workflows -type f \\( -iname '*.yaml' -o -iname '*.yml' \\) | xargs -I {} sh -c 'echo \"Linting {}\"; action-validator \"{}\"'",
"lint:knip": "knip",
"build:native": "yarn run hak", "build:native": "yarn run hak",
"build:native:universal": "yarn run hak --target x86_64-apple-darwin fetchandbuild && yarn run hak --target aarch64-apple-darwin fetchandbuild && yarn run hak --target x86_64-apple-darwin --target aarch64-apple-darwin copyandlink", "build:native:universal": "yarn run hak --target x86_64-apple-darwin fetchandbuild && yarn run hak --target aarch64-apple-darwin fetchandbuild && yarn run hak --target x86_64-apple-darwin --target aarch64-apple-darwin copyandlink",
"build:32": "yarn run build:ts && yarn run build:res && electron-builder --ia32", "build:32": "yarn run build:ts && yarn run build:res && electron-builder --ia32",
@ -48,13 +50,16 @@
"build": "yarn run build:ts && yarn run build:res && electron-builder", "build": "yarn run build:ts && yarn run build:res && electron-builder",
"build:ts": "tsc", "build:ts": "tsc",
"build:res": "ts-node scripts/copy-res.ts", "build:res": "ts-node scripts/copy-res.ts",
"docker:setup": "docker build -t element-desktop-dockerbuild dockerbuild", "docker:setup": "docker build --platform linux/amd64 -t element-desktop-dockerbuild dockerbuild",
"docker:build:native": "scripts/in-docker.sh yarn run hak", "docker:build:native": "scripts/in-docker.sh yarn run hak",
"docker:build": "scripts/in-docker.sh yarn run build", "docker:build": "scripts/in-docker.sh yarn run build",
"docker:install": "scripts/in-docker.sh yarn install", "docker:install": "scripts/in-docker.sh yarn install",
"clean": "rimraf webapp.asar dist packages deploys lib", "clean": "rimraf webapp.asar dist packages deploys lib",
"hak": "ts-node scripts/hak/index.ts", "hak": "ts-node scripts/hak/index.ts",
"test": "jest" "test": "playwright test",
"test:open": "yarn test --ui",
"test:screenshots:build": "docker build playwright -t element-desktop-playwright --platform linux/amd64",
"test:screenshots:run": "docker run --rm --network host -v $(pwd):/work/element-desktop -v /var/run/docker.sock:/var/run/docker.sock --platform linux/amd64 -it element-desktop-playwright"
}, },
"dependencies": { "dependencies": {
"@sentry/electron": "^4.3.0", "@sentry/electron": "^4.3.0",
@ -69,145 +74,55 @@
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"@action-validator/cli": "^0.5.3",
"@action-validator/core": "^0.5.3",
"@babel/core": "^7.18.10", "@babel/core": "^7.18.10",
"@babel/preset-env": "^7.18.10", "@babel/preset-env": "^7.18.10",
"@babel/preset-typescript": "^7.18.6", "@babel/preset-typescript": "^7.18.6",
"@electron/asar": "^3.2.3", "@electron/asar": "^3.2.3",
"@electron/notarize": "^2.0.0", "@electron/fuses": "^1.7.0",
"@playwright/test": "1.41.2",
"@types/auto-launch": "^5.0.1", "@types/auto-launch": "^5.0.1",
"@types/counterpart": "^0.18.1", "@types/counterpart": "^0.18.1",
"@types/detect-libc": "^1.0.0",
"@types/jest": "^29.0.0",
"@types/minimist": "^1.2.1", "@types/minimist": "^1.2.1",
"@types/mkdirp": "^1.0.2", "@types/mkdirp": "^1.0.2",
"@types/node": "16.18.52", "@types/node": "18.19.8",
"@types/pacote": "^11.1.1", "@types/pacote": "^11.1.1",
"@types/tar": "^6.1.3", "@types/tar": "^6.1.3",
"@types/uuid": "^9.0.2", "@types/uuid": "^9.0.2",
"@typescript-eslint/eslint-plugin": "^5.42.0", "@typescript-eslint/eslint-plugin": "^7.0.0",
"@typescript-eslint/parser": "^5.42.0", "@typescript-eslint/parser": "^7.0.0",
"allchange": "^1.0.6", "app-builder-lib": "24.13.1",
"app-builder-lib": "24.6.5",
"babel-jest": "^29.0.0",
"chokidar": "^3.5.2", "chokidar": "^3.5.2",
"detect-libc": "^1.0.3", "detect-libc": "^2.0.0",
"electron": "^26.2.1", "electron": "^28.0.0",
"electron-builder": "24.6.4", "electron-builder": "24.12.0",
"electron-builder-squirrel-windows": "24.6.5", "electron-builder-squirrel-windows": "24.13.1",
"electron-devtools-installer": "^3.2.0", "electron-devtools-installer": "^3.2.0",
"eslint": "^8.26.0", "eslint": "^8.26.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^9.0.0", "eslint-config-prettier": "^9.0.0",
"eslint-plugin-import": "^2.25.4", "eslint-plugin-import": "^2.25.4",
"eslint-plugin-matrix-org": "^1.0.0", "eslint-plugin-matrix-org": "^1.0.0",
"eslint-plugin-unicorn": "^48.0.0", "eslint-plugin-unicorn": "^51.0.0",
"expect-playwright": "^0.8.0",
"find-npm-prefix": "^1.0.2", "find-npm-prefix": "^1.0.2",
"fs-extra": "^11.0.0",
"glob": "^10.0.0", "glob": "^10.0.0",
"jest": "^29.0.0", "knip": "^5.0.0",
"matrix-web-i18n": "^3.1.1", "matrix-web-i18n": "^3.1.3",
"mkdirp": "^3.0.0", "mkdirp": "^3.0.0",
"node-pre-gyp": "^0.17.0", "node-pre-gyp": "^0.17.0",
"pacote": "^17.0.0", "pacote": "^17.0.0",
"playwright": "^1.25.0", "prettier": "^3.0.0",
"prettier": "^2.8.1",
"rimraf": "^5.0.0", "rimraf": "^5.0.0",
"tar": "^6.1.2", "tar": "^6.1.2",
"ts-jest": "^29.0.0",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "5.1.6" "typescript": "5.3.3"
}, },
"hakDependencies": { "hakDependencies": {
"matrix-seshat": "^3.0.1", "matrix-seshat": "^3.0.1",
"keytar": "^7.9.0" "keytar": "^7.9.0"
}, },
"resolutions": { "resolutions": {
"@types/node": "16.18.52" "@types/node": "18.19.8"
},
"build": {
"appId": "im.riot.app",
"asarUnpack": "**/*.node",
"files": [
"package.json",
{
"from": ".hak/hakModules",
"to": "node_modules"
},
"lib/**"
],
"extraResources": [
{
"from": "res/img",
"to": "img"
},
"webapp.asar"
],
"linux": {
"target": [
"tar.gz",
"deb"
],
"category": "Network;InstantMessaging;Chat",
"maintainer": "support@element.io",
"icon": "build/icons"
},
"deb": {
"packageCategory": "net",
"depends": [
"libgtk-3-0",
"libnotify4",
"libnss3",
"libxss1",
"libxtst6",
"xdg-utils",
"libatspi2.0-0",
"libuuid1",
"libsecret-1-0",
"libasound2",
"libgbm1"
],
"recommends": [
"libsqlcipher0",
"element-io-archive-keyring"
]
},
"mac": {
"category": "public.app-category.social-networking",
"darkModeSupport": true,
"hardenedRuntime": true,
"gatekeeperAssess": true,
"entitlements": "./build/entitlements.mac.plist",
"icon": "build/icons/icon.icns"
},
"win": {
"target": [
"squirrel"
],
"signingHashAlgorithms": [
"sha256"
],
"icon": "build/icons/icon.ico"
},
"directories": {
"output": "dist"
},
"protocols": [
{
"name": "element",
"schemes": [
"element"
]
}
]
},
"jest": {
"testEnvironment": "node",
"testMatch": [
"<rootDir>/test/**/*-test.[jt]s?(x)"
],
"setupFilesAfterEnv": [
"expect-playwright"
]
} }
} }

33
playwright.config.ts Normal file
View File

@ -0,0 +1,33 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { defineConfig } from "@playwright/test";
export default defineConfig({
use: {
viewport: { width: 1280, height: 720 },
video: "retain-on-failure",
trace: "on-first-retry",
},
testDir: "playwright/e2e",
outputDir: "playwright/test-results",
workers: 1,
retries: process.env.CI ? 2 : 0,
reporter: [["html", { outputFolder: "playwright/html-report" }]],
snapshotDir: "playwright/snapshots",
snapshotPathTemplate: "{snapshotDir}/{testFilePath}/{arg}-{platform}{ext}",
timeout: 10 * 1000,
});

5
playwright/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/test-results/
/html-report/
# Only commit snapshots from Linux
/snapshots/**/*.png
!/snapshots/**/*-linux.png

10
playwright/Dockerfile Normal file
View File

@ -0,0 +1,10 @@
FROM mcr.microsoft.com/playwright:v1.41.2-jammy
WORKDIR /work/element-desktop
RUN apt-get update && apt-get -y install xvfb
USER 1000:1000
COPY docker-entrypoint.sh /opt/docker-entrypoint.sh
ENTRYPOINT ["bash", "/opt/docker-entrypoint.sh"]

View File

@ -0,0 +1,11 @@
#!/bin/bash
set -e
echo "Starting Xvfb"
Xvfb :99 -ac &
sleep 2
export DISPLAY=:99
npx playwright test --update-snapshots --reporter line $1

View File

@ -0,0 +1,26 @@
/*
Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { test, expect } from "../../element-desktop-test";
test.describe("App launch", () => {
test("should launch and render the welcome view successfully", async ({ page }) => {
await page.locator("#matrixchat").waitFor();
await page.locator(".mx_Welcome").waitFor();
await expect(page).toHaveURL("vector://vector/webapp/#/welcome");
await expect(page).toHaveScreenshot();
});
});

View File

@ -0,0 +1,57 @@
/*
Copyright 2023 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { _electron as electron, test as base, expect as baseExpect, type ElectronApplication } from "@playwright/test";
import fs from "node:fs/promises";
import path from "node:path";
import os from "node:os";
export const test = base.extend<{ app: ElectronApplication; tmpDir: string }>({
// eslint-disable-next-line no-empty-pattern
tmpDir: async ({}, use) => {
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "element-desktop-tests-"));
console.log("Using temp profile directory: ", tmpDir);
await use(tmpDir);
await fs.rm(tmpDir, { recursive: true });
},
app: async ({ tmpDir }, use) => {
const args = ["--profile-dir", tmpDir];
const executablePath = process.env["ELEMENT_DESKTOP_EXECUTABLE"];
if (!executablePath) {
// Unpackaged mode testing
args.unshift("./lib/electron-main.js");
}
const app = await electron.launch({
env: process.env,
executablePath,
args,
});
await app.firstWindow();
await use(app);
},
page: async ({ app }, use) => {
const window = await app.firstWindow();
await use(window);
await app.close().catch((e) => {
console.error(e);
});
},
});
export const expect = baseExpect;

11
playwright/tsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"resolveJsonModule": true,
"moduleResolution": "node",
"esModuleInterop": true,
"target": "es2017",
"module": "es2022",
"lib": ["ESNext", "es2021", "dom"],
},
"include": ["**/*.ts"],
}

View File

@ -1,9 +0,0 @@
#!/bin/bash
#
# Script to perform a release of element-desktop.
set -e
cd "$(dirname "$0")"
./node_modules/matrix-js-sdk/release.sh "$@"

View File

@ -12,7 +12,7 @@ import riotDesktopPackageJson from "../package.json";
import { setPackageVersion } from "./set-version"; import { setPackageVersion } from "./set-version";
const PUB_KEY_URL = "https://packages.riot.im/element-release-key.asc"; const PUB_KEY_URL = "https://packages.riot.im/element-release-key.asc";
const PACKAGE_URL_PREFIX = "https://github.com/vector-im/element-web/releases/download/"; const PACKAGE_URL_PREFIX = "https://github.com/element-hq/element-web/releases/download/";
const DEVELOP_TGZ_URL = "https://develop.element.io/develop.tar.gz"; const DEVELOP_TGZ_URL = "https://develop.element.io/develop.tar.gz";
const ASAR_PATH = "webapp.asar"; const ASAR_PATH = "webapp.asar";

View File

@ -1,129 +0,0 @@
#!/usr/bin/env -S npx ts-node
/**
* Script to generate electron-builder.json config files for builds which don't match package.json, e.g. nightlies
* This script has different outputs depending on your os platform.
*
* On Windows:
* Prefixes the nightly version with `0.0.1-nightly.` as it breaks if it is not semver
*
* On macOS:
* Passes --notarytool-team-id to build.mac.notarize.notarize if specified
*
* On Linux:
* Replaces spaces in the product name with dashes as spaces in paths can cause issues
* Passes --deb-custom-control to build.deb.fpm if specified
* Removes libsqlcipher0 recommended dependency if env SQLCIPHER_BUNDLED is asserted.
*/
import parseArgs from "minimist";
import fsProm from "fs/promises";
import * as os from "os";
import { Configuration } from "app-builder-lib";
const ELECTRON_BUILDER_CFG_FILE = "electron-builder.json";
const NIGHTLY_APP_ID = "im.riot.nightly";
const NIGHTLY_APP_NAME = "element-desktop-nightly";
const NIGHTLY_DEB_NAME = "element-nightly";
const argv = parseArgs<{
"nightly"?: string;
"signtool-thumbprint"?: string;
"signtool-subject-name"?: string;
"notarytool-team-id"?: string;
"deb-changelog"?: string;
}>(process.argv.slice(2), {
string: ["nightly", "deb-changelog", "signtool-thumbprint", "signtool-subject-name", "notarytool-team-id"],
});
type DeepWriteable<T> = { -readonly [P in keyof T]: DeepWriteable<T[P]> };
interface PackageBuild extends DeepWriteable<Omit<Configuration, "extraMetadata">> {
extraMetadata?: {
productName?: string;
name?: string;
version?: string;
description?: string;
};
}
interface Package {
build: PackageBuild;
productName: string;
description: string;
}
async function main(): Promise<number | void> {
// Electron builder doesn't overlay with the config in package.json, so load it here
const pkg: Package = JSON.parse(await fsProm.readFile("package.json", "utf8"));
const cfg: PackageBuild = {
...pkg.build,
extraMetadata: {
productName: pkg.productName,
description: pkg.description,
},
};
if (!cfg.deb!.fpm) cfg.deb!.fpm = [];
if (argv.nightly) {
cfg.appId = NIGHTLY_APP_ID;
cfg.extraMetadata!.productName += " Nightly";
cfg.extraMetadata!.name = NIGHTLY_APP_NAME;
cfg.extraMetadata!.description += " (nightly unstable build)";
cfg.deb!.fpm!.push("--name", NIGHTLY_DEB_NAME);
let version = argv.nightly;
if (os.platform() === "win32") {
// The windows packager relies on parsing this as semver, so we have to make it look like one.
// This will give our update packages really stupid names, but we probably can't change that either
// because squirrel windows parses them for the version too. We don't really care: nobody sees them.
// We just give the installer a static name, so you'll just see this in the 'about' dialog.
// Turns out if you use 0.0.0 here it makes Squirrel windows crash, so we use 0.0.1.
version = "0.0.1-nightly." + version;
}
cfg.extraMetadata!.version = version;
} else {
cfg.deb!.fpm!.push("--deb-field", "Replaces: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)");
cfg.deb!.fpm!.push("--deb-field", "Breaks: riot-desktop (<< 1.7.0), riot-web (<< 1.7.0)");
}
if (argv["signtool-thumbprint"] && argv["signtool-subject-name"]) {
cfg.win!.certificateSubjectName = argv["signtool-subject-name"];
cfg.win!.certificateSha1 = argv["signtool-thumbprint"];
}
if (argv["notarytool-team-id"]) {
cfg.mac!.notarize = {
teamId: argv["notarytool-team-id"],
};
}
if (os.platform() === "linux") {
// Electron crashes on debian if there's a space in the path.
// https://github.com/vector-im/element-web/issues/13171
cfg.extraMetadata!.productName = cfg.extraMetadata!.productName!.replace(/ /g, "-");
if (argv["deb-changelog"]) {
cfg.deb!.fpm!.push(`--deb-changelog=${argv["deb-changelog"]}`);
}
if (process.env.SQLCIPHER_BUNDLED) {
// Remove sqlcipher dependency when using bundled
cfg.deb!.recommends = cfg.deb!.recommends?.filter((d) => d !== "libsqlcipher0");
}
}
await fsProm.writeFile(ELECTRON_BUILDER_CFG_FILE, JSON.stringify(cfg, null, 4));
}
main()
.then((ret) => {
process.exit(ret!);
})
.catch((e) => {
console.error(e);
process.exit(1);
});

View File

@ -41,7 +41,10 @@ export default class HakEnv {
public runtimeVersion?: string; public runtimeVersion?: string;
public dotHakDir: string; public dotHakDir: string;
public constructor(public readonly projectRoot: string, targetId: TargetId | null) { public constructor(
public readonly projectRoot: string,
targetId: TargetId | null,
) {
const target = targetId ? TARGETS[targetId] : getHost(); const target = targetId ? TARGETS[targetId] : getHost();
if (!target) { if (!target) {

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import { GLIBC, MUSL, family as processLibC } from "detect-libc"; import { GLIBC, MUSL, familySync as processLibC } from "detect-libc";
// We borrow Rust's target naming scheme as a way of expressing all target // We borrow Rust's target naming scheme as a way of expressing all target
// details in a single string. // details in a single string.
@ -61,7 +61,7 @@ export type WindowsTarget = Target & {
export type LinuxTarget = Target & { export type LinuxTarget = Target & {
platform: "linux"; platform: "linux";
libC: typeof processLibC; libC: typeof GLIBC | typeof MUSL;
}; };
export type UniversalTarget = Target & { export type UniversalTarget = Target & {
@ -212,7 +212,7 @@ export function getHost(): Target | undefined {
(target) => (target) =>
target.platform === process.platform && target.platform === process.platform &&
target.arch === process.arch && target.arch === process.arch &&
(process.platform !== "linux" || (target as LinuxTarget).libC === processLibC), (process.platform !== "linux" || (target as LinuxTarget).libC === processLibC()),
); );
} }

View File

@ -11,6 +11,7 @@ fi
# Taken from https://www.electron.build/multi-platform-build#docker # Taken from https://www.electron.build/multi-platform-build#docker
# Pass through any vars prefixed with INDOCKER_, removing the prefix # Pass through any vars prefixed with INDOCKER_, removing the prefix
docker run --rm -ti \ docker run --rm -ti \
--platform linux/amd64 \
--env-file <(env | grep -E '^INDOCKER_' | sed -e 's/^INDOCKER_//') \ --env-file <(env | grep -E '^INDOCKER_' | sed -e 's/^INDOCKER_//') \
--env ELECTRON_CACHE="/root/.cache/electron" \ --env ELECTRON_CACHE="/root/.cache/electron" \
--env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder" \ --env ELECTRON_BUILDER_CACHE="/root/.cache/electron-builder" \

View File

@ -7,10 +7,10 @@
"module": "commonjs", "module": "commonjs",
"sourceMap": false, "sourceMap": false,
"strict": true, "strict": true,
"lib": ["es2020", "dom"] "lib": ["es2020", "dom"],
}, },
"include": ["../src/@types", "./**/*.ts"], "include": ["../src/@types", "./**/*.ts"],
"ts-node": { "ts-node": {
"transpileOnly": true "transpileOnly": true,
} },
} }

View File

@ -41,6 +41,7 @@ import { getProfileFromDeeplink, protocolInit } from "./protocol";
import { _t, AppLocalization } from "./language-helper"; import { _t, AppLocalization } from "./language-helper";
import { setDisplayMediaCallback } from "./displayMediaCallback"; import { setDisplayMediaCallback } from "./displayMediaCallback";
import { setupMacosTitleBar } from "./macos-titlebar"; import { setupMacosTitleBar } from "./macos-titlebar";
import { loadJsonFile } from "./utils";
const argv = minimist(process.argv, { const argv = minimist(process.argv, {
alias: { help: "h" }, alias: { help: "h" },
@ -143,8 +144,7 @@ async function loadConfig(): Promise<void> {
const asarPath = await getAsarPath(); const asarPath = await getAsarPath();
try { try {
// eslint-disable-next-line @typescript-eslint/no-var-requires global.vectorConfig = loadJsonFile(asarPath, "config.json");
global.vectorConfig = require(asarPath + "config.json");
} catch (e) { } catch (e) {
// it would be nice to check the error code here and bail if the config // it would be nice to check the error code here and bail if the config
// is unparsable, but we get MODULE_NOT_FOUND in the case of a missing // is unparsable, but we get MODULE_NOT_FOUND in the case of a missing
@ -155,8 +155,7 @@ async function loadConfig(): Promise<void> {
try { try {
// Load local config and use it to override values from the one baked with the build // Load local config and use it to override values from the one baked with the build
// eslint-disable-next-line @typescript-eslint/no-var-requires const localConfig = loadJsonFile(app.getPath("userData"), "config.json");
const localConfig = require(path.join(app.getPath("userData"), "config.json"));
// If the local config has a homeserver defined, don't use the homeserver from the build // If the local config has a homeserver defined, don't use the homeserver from the build
// config. This is to avoid a problem where Riot thinks there are multiple homeservers // config. This is to avoid a problem where Riot thinks there are multiple homeservers
@ -165,10 +164,13 @@ async function loadConfig(): Promise<void> {
// Rip out all the homeserver options from the vector config // Rip out all the homeserver options from the vector config
global.vectorConfig = Object.keys(global.vectorConfig) global.vectorConfig = Object.keys(global.vectorConfig)
.filter((k) => !homeserverProps.includes(<any>k)) .filter((k) => !homeserverProps.includes(<any>k))
.reduce((obj, key) => { .reduce(
obj[key] = global.vectorConfig[key]; (obj, key) => {
return obj; obj[key] = global.vectorConfig[key];
}, {} as Omit<Partial<(typeof global)["vectorConfig"]>, keyof typeof homeserverProps>); return obj;
},
{} as Omit<Partial<(typeof global)["vectorConfig"]>, keyof typeof homeserverProps>,
);
} }
global.vectorConfig = Object.assign(global.vectorConfig, localConfig); global.vectorConfig = Object.assign(global.vectorConfig, localConfig);

View File

@ -1,61 +0,0 @@
{
"action": {
"cancel": "Отказ",
"close": "Затвори",
"copy": "Копирай",
"cut": "Изрежи",
"delete": "Изтрий",
"edit": "Редактирай",
"minimise": "Минимизирай",
"paste": "Постави",
"paste_match_style": "Постави и Използвай текущия стил",
"quit": "Напусни",
"redo": "Върни",
"select_all": "Избери Всичко",
"show_hide": "Покажи/Скрий",
"undo": "Отмени",
"zoom_in": "Увеличи",
"zoom_out": "Намали"
},
"common": {
"about": "Относно",
"help": "Помощ",
"preferences": "Предпочитания"
},
"confirm_quit": "Сигурен ли си че искаш да напуснеш?",
"edit_menu": {
"speech": "Говор",
"speech_start_speaking": "Започни да говориш",
"speech_stop_speaking": "Спри да говориш"
},
"file_menu": {
"label": "Файл"
},
"menu": {
"hide": "Скрий",
"hide_others": "Скрий Останалите",
"services": "Услуги",
"unhide": "Покажи"
},
"right_click_menu": {
"add_to_dictionary": "Добави към речника",
"copy_email": "Копирай имейл адрес",
"copy_image": "Копирай изображение",
"copy_image_url": "Копирай адреса на изображението",
"copy_link_url": "Копирай линка",
"save_image_as": "Запази изображението като...",
"save_image_as_error_description": "Изображението не успя да се запази",
"save_image_as_error_title": "Неуспешно запазване на изображението"
},
"view_menu": {
"actual_size": "Действителен Размер",
"toggle_developer_tools": "Превключи инструментите за разработчици",
"toggle_full_screen": "Превключи на Цял екран",
"view": "Преглед"
},
"window_menu": {
"bring_all_to_front": "Покажи всички най-отгоре",
"label": "Прозорец",
"zoom": "Мащабирай"
}
}

View File

@ -1,12 +1,12 @@
{ {
"action": { "action": {
"cancel": "Zrušit", "cancel": "Storno",
"close": "Zavřít", "close": "Zavřít",
"close_brand": "Zavřít %(brand)s", "close_brand": "Zavřít %(brand)s",
"copy": "Kopírovat", "copy": "Zkopírovat",
"cut": "Vyjmout", "cut": "Vyjmout",
"delete": "Smazat", "delete": "Smazat",
"edit": "Úpravy", "edit": "Upravit",
"minimise": "Minimalizovat", "minimise": "Minimalizovat",
"paste": "Vložit", "paste": "Vložit",
"paste_match_style": "Vložit a přizpůsobit styl", "paste_match_style": "Vložit a přizpůsobit styl",
@ -19,7 +19,7 @@
"zoom_out": "Oddálit" "zoom_out": "Oddálit"
}, },
"common": { "common": {
"about": "O aplikaci", "about": "O",
"brand_help": "%(brand)s nápověda", "brand_help": "%(brand)s nápověda",
"help": "Nápověda", "help": "Nápověda",
"preferences": "Předvolby" "preferences": "Předvolby"

View File

@ -57,6 +57,7 @@
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "Alles in den Vordergrund", "bring_all_to_front": "Alles in den Vordergrund",
"label": "Fenster" "label": "Fenster",
"zoom": "Zoomen"
} }
} }

View File

@ -3,7 +3,7 @@
"cancel": "Nuligi", "cancel": "Nuligi",
"close": "Fermi", "close": "Fermi",
"close_brand": "Fermu %(brand)s", "close_brand": "Fermu %(brand)s",
"copy": "Kopiu", "copy": "Kopii",
"cut": "Tranĉi", "cut": "Tranĉi",
"delete": "Forigi", "delete": "Forigi",
"edit": "Redakti", "edit": "Redakti",
@ -15,9 +15,9 @@
"undo": "Malfari" "undo": "Malfari"
}, },
"common": { "common": {
"about": "Informilo", "about": "Prio",
"help": "Helpo", "help": "Helpo",
"preferences": "Preferoj" "preferences": "Agordoj"
}, },
"confirm_quit": "Ĉu vi certas, ke vi volas ĉesi?", "confirm_quit": "Ĉu vi certas, ke vi volas ĉesi?",
"edit_menu": { "edit_menu": {

View File

@ -5,7 +5,7 @@
"close_brand": "Cerrar %(brand)s", "close_brand": "Cerrar %(brand)s",
"copy": "Copiar", "copy": "Copiar",
"cut": "Cortar", "cut": "Cortar",
"delete": "Eliminar", "delete": "Borrar",
"edit": "Editar", "edit": "Editar",
"minimise": "Minimizar", "minimise": "Minimizar",
"paste": "Pegar", "paste": "Pegar",

View File

@ -1,6 +1,6 @@
{ {
"action": { "action": {
"cancel": "Tühista", "cancel": "Loobu",
"close": "Sulge", "close": "Sulge",
"close_brand": "Sulge %(brand)s", "close_brand": "Sulge %(brand)s",
"copy": "Kopeeri", "copy": "Kopeeri",
@ -22,7 +22,7 @@
"about": "Rakenduse teave", "about": "Rakenduse teave",
"brand_help": "%(brand)s abiteave", "brand_help": "%(brand)s abiteave",
"help": "Abiteave", "help": "Abiteave",
"preferences": "Seadistused" "preferences": "Eelistused"
}, },
"confirm_quit": "Kas sa kindlasti soovid rakendusest väljuda?", "confirm_quit": "Kas sa kindlasti soovid rakendusest väljuda?",
"edit_menu": { "edit_menu": {
@ -53,7 +53,7 @@
"actual_size": "Näita tavasuuruses", "actual_size": "Näita tavasuuruses",
"toggle_developer_tools": "Arendaja töövahendid sisse/välja", "toggle_developer_tools": "Arendaja töövahendid sisse/välja",
"toggle_full_screen": "Täisekraanivaade sisse/välja", "toggle_full_screen": "Täisekraanivaade sisse/välja",
"view": "Vaata" "view": "Näita"
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "Too kõik esiplaanile", "bring_all_to_front": "Too kõik esiplaanile",

View File

@ -5,7 +5,7 @@
"close_brand": "بستن %(brand)s", "close_brand": "بستن %(brand)s",
"copy": "رونوشت", "copy": "رونوشت",
"cut": "برش", "cut": "برش",
"delete": "حذف", "delete": "پاک‌کردن",
"edit": "ویرایش", "edit": "ویرایش",
"minimise": "کمینه", "minimise": "کمینه",
"paste": "جای‌گذاری", "paste": "جای‌گذاری",
@ -20,6 +20,7 @@
}, },
"common": { "common": {
"about": "درباره", "about": "درباره",
"brand_help": "کمک %(brand)s",
"help": "راهنما", "help": "راهنما",
"preferences": "ترجیحات" "preferences": "ترجیحات"
}, },
@ -52,7 +53,7 @@
"actual_size": "اندازهٔ واقعی", "actual_size": "اندازهٔ واقعی",
"toggle_developer_tools": "تغییر وضعیت ابزارهای توسعه‌دهنده", "toggle_developer_tools": "تغییر وضعیت ابزارهای توسعه‌دهنده",
"toggle_full_screen": "تغییر وضعیت تمام‌صفحه", "toggle_full_screen": "تغییر وضعیت تمام‌صفحه",
"view": "دیدن" "view": "مشاهده"
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "همه را به جلو بیاورید", "bring_all_to_front": "همه را به جلو بیاورید",

View File

@ -19,9 +19,9 @@
"zoom_out": "Pienennä" "zoom_out": "Pienennä"
}, },
"common": { "common": {
"about": "Tietoja", "about": "Tietoa",
"help": "Apua", "help": "Ohje",
"preferences": "Asetukset" "preferences": "Valinnat"
}, },
"confirm_quit": "Haluatko varmasti poistua?", "confirm_quit": "Haluatko varmasti poistua?",
"edit_menu": { "edit_menu": {

View File

@ -36,6 +36,7 @@
"menu": { "menu": {
"hide": "Masquer", "hide": "Masquer",
"hide_others": "Masquer les autres", "hide_others": "Masquer les autres",
"services": "Services",
"unhide": "Dé-masquer" "unhide": "Dé-masquer"
}, },
"right_click_menu": { "right_click_menu": {
@ -56,6 +57,7 @@
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "Tout amener au premier plan", "bring_all_to_front": "Tout amener au premier plan",
"label": "Fenêtre" "label": "Fenêtre",
"zoom": "Zoom"
} }
} }

View File

@ -50,7 +50,7 @@
"actual_size": "Tamaño real", "actual_size": "Tamaño real",
"toggle_developer_tools": "Activar ferramentas de desenvolvemento", "toggle_developer_tools": "Activar ferramentas de desenvolvemento",
"toggle_full_screen": "Activar pantalla completa", "toggle_full_screen": "Activar pantalla completa",
"view": "Ver" "view": "Vista"
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "Traer todo á fronte", "bring_all_to_front": "Traer todo á fronte",

View File

@ -5,7 +5,7 @@
"copy": "העתק", "copy": "העתק",
"cut": "גזור", "cut": "גזור",
"delete": "מחק", "delete": "מחק",
"edit": "עריכה", "edit": "ערוך",
"minimise": "מזער", "minimise": "מזער",
"paste": "הדבק", "paste": "הדבק",
"paste_match_style": "הדבק והתאם סגנון", "paste_match_style": "הדבק והתאם סגנון",

View File

@ -1,26 +1,27 @@
{ {
"action": { "action": {
"cancel": "Mégsem", "cancel": "Mégse",
"close": "Bezár", "close": "Bezárás",
"close_brand": "%(brand)s bezárása", "close_brand": "%(brand)s bezárása",
"copy": "Másol", "copy": "Másolás",
"cut": "Kivág", "cut": "Kivágás",
"delete": "Töröl", "delete": "Törlés",
"edit": "Szerkeszt", "edit": "Szerkesztés",
"minimise": "Lecsukás", "minimise": "Lecsukás",
"paste": "Beillesztés", "paste": "Beillesztés",
"paste_match_style": "Beillesztés formázással", "paste_match_style": "Beillesztés formázással",
"quit": "Kilép", "quit": "Kilépés",
"redo": "Újra", "redo": "Újra",
"select_all": "Összes kijelölése", "select_all": "Összes kijelölése",
"show_hide": "Megmutat/Elrejt", "show_hide": "Megjelenítés/elrejtés",
"undo": "Visszavon", "undo": "Visszavonás",
"zoom_in": "Nagyít", "zoom_in": "Nagyítás",
"zoom_out": "Kicsinyít" "zoom_out": "Kicsinyítés"
}, },
"common": { "common": {
"about": "Névjegy", "about": "Névjegy",
"help": "Segítség", "brand_help": "%(brand)s Súgó",
"help": "Súgó",
"preferences": "Beállítások" "preferences": "Beállítások"
}, },
"confirm_quit": "Biztos, hogy kilép?", "confirm_quit": "Biztos, hogy kilép?",
@ -33,18 +34,18 @@
"label": "Fájl" "label": "Fájl"
}, },
"menu": { "menu": {
"hide": "Eltakar", "hide": "Elrejtés",
"hide_others": "Minden mást eltakar", "hide_others": "Mások elrejtése",
"services": "Szolgáltatás", "services": "Szolgáltatás",
"unhide": "Felfed" "unhide": "Felfedés"
}, },
"right_click_menu": { "right_click_menu": {
"add_to_dictionary": "Hozzáadás a szótárhoz", "add_to_dictionary": "Hozzáadás a szótárhoz",
"copy_email": "E-mail cím másolása", "copy_email": "E-mail-cím másolása",
"copy_image": "Kép másolása", "copy_image": "Kép másolása",
"copy_image_url": "Kép címének másolása", "copy_image_url": "Kép címének másolása",
"copy_link_url": "Hivatkozás másolása", "copy_link_url": "Hivatkozás másolása",
"save_image_as": "Kép mentése másként...", "save_image_as": "Kép mentése másként",
"save_image_as_error_description": "A kép mentése sikertelen", "save_image_as_error_description": "A kép mentése sikertelen",
"save_image_as_error_title": "Kép mentése sikertelen" "save_image_as_error_title": "Kép mentése sikertelen"
}, },
@ -52,7 +53,7 @@
"actual_size": "Jelenlegi méret", "actual_size": "Jelenlegi méret",
"toggle_developer_tools": "Fejlesztői eszközök", "toggle_developer_tools": "Fejlesztői eszközök",
"toggle_full_screen": "Teljes képernyő", "toggle_full_screen": "Teljes képernyő",
"view": "Nézet" "view": "Megtekintés"
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "Mindent előtérbe hoz", "bring_all_to_front": "Mindent előtérbe hoz",

View File

@ -1,11 +1,12 @@
{ {
"action": { "action": {
"cancel": "Batal", "cancel": "Batalkan",
"close": "Tutup", "close": "Tutup",
"close_brand": "Tutuo %(brand)s", "close_brand": "Tutuo %(brand)s",
"copy": "Salin", "copy": "Salin",
"cut": "Potong", "cut": "Potong",
"delete": "Hapus", "delete": "Hapus",
"edit": "Sunting",
"minimise": "Minimalkan", "minimise": "Minimalkan",
"paste": "Tempel", "paste": "Tempel",
"paste_match_style": "Tempel dan Cocokkan Gaya", "paste_match_style": "Tempel dan Cocokkan Gaya",
@ -21,7 +22,7 @@
"about": "Tentang", "about": "Tentang",
"brand_help": "Bantuan %(brand)s", "brand_help": "Bantuan %(brand)s",
"help": "Bantuan", "help": "Bantuan",
"preferences": "Pengaturan" "preferences": "Preferensi"
}, },
"confirm_quit": "Apakah Anda yakin ingin keluar?", "confirm_quit": "Apakah Anda yakin ingin keluar?",
"edit_menu": { "edit_menu": {
@ -29,6 +30,9 @@
"speech_start_speaking": "Mulai Berbicara", "speech_start_speaking": "Mulai Berbicara",
"speech_stop_speaking": "Berhenti Berbicara" "speech_stop_speaking": "Berhenti Berbicara"
}, },
"file_menu": {
"label": "Berkas"
},
"menu": { "menu": {
"hide": "Sembunyikan", "hide": "Sembunyikan",
"hide_others": "Sembunyikan yang Lain", "hide_others": "Sembunyikan yang Lain",

View File

@ -30,6 +30,9 @@
"speech_start_speaking": "Inizia a parlare", "speech_start_speaking": "Inizia a parlare",
"speech_stop_speaking": "Smetti di parlare" "speech_stop_speaking": "Smetti di parlare"
}, },
"file_menu": {
"label": "File"
},
"menu": { "menu": {
"hide": "Nascondi", "hide": "Nascondi",
"hide_others": "Nascondi gli altri", "hide_others": "Nascondi gli altri",
@ -54,6 +57,7 @@
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "Porta tutto in primo piano", "bring_all_to_front": "Porta tutto in primo piano",
"label": "Finestra" "label": "Finestra",
"zoom": "Ingrandisci"
} }
} }

View File

@ -6,7 +6,7 @@
"copy": "Kopijuoti", "copy": "Kopijuoti",
"cut": "Iškirpti", "cut": "Iškirpti",
"delete": "Ištrinti", "delete": "Ištrinti",
"edit": "Redaguoti", "edit": "Koreguoti",
"minimise": "Sumažinti", "minimise": "Sumažinti",
"paste": "Įklijuoti", "paste": "Įklijuoti",
"paste_match_style": "Įklijuoti ir suderinti stilių", "paste_match_style": "Įklijuoti ir suderinti stilių",
@ -52,7 +52,7 @@
"actual_size": "Tikrasis dydis", "actual_size": "Tikrasis dydis",
"toggle_developer_tools": "Perjungti kūrėjo įrankius", "toggle_developer_tools": "Perjungti kūrėjo įrankius",
"toggle_full_screen": "Perjungti viso ekrano režimą", "toggle_full_screen": "Perjungti viso ekrano režimą",
"view": "Peržiūrėti" "view": "Žiūrėti"
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "Viską iškelti į priekį", "bring_all_to_front": "Viską iškelti į priekį",

View File

@ -19,7 +19,7 @@
"zoom_out": "Pomniejsz" "zoom_out": "Pomniejsz"
}, },
"common": { "common": {
"about": "O nas", "about": "Informacje",
"brand_help": "Pomoc %(brand)s", "brand_help": "Pomoc %(brand)s",
"help": "Pomoc", "help": "Pomoc",
"preferences": "Preferencje" "preferences": "Preferencje"
@ -53,7 +53,7 @@
"actual_size": "Rozmiar rzeczywisty", "actual_size": "Rozmiar rzeczywisty",
"toggle_developer_tools": "Przełącz na narzędzia deweloperskie", "toggle_developer_tools": "Przełącz na narzędzia deweloperskie",
"toggle_full_screen": "Przełącz na pełny ekran", "toggle_full_screen": "Przełącz na pełny ekran",
"view": "Pokaż" "view": "Wyświetl"
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "Wyciągnij wszystko do przodu", "bring_all_to_front": "Wyciągnij wszystko do przodu",

View File

@ -5,7 +5,7 @@
"close_brand": "Fechar %(brand)s", "close_brand": "Fechar %(brand)s",
"copy": "Copiar", "copy": "Copiar",
"cut": "Cortar", "cut": "Cortar",
"delete": "Deletar", "delete": "Excluir",
"edit": "Editar", "edit": "Editar",
"minimise": "Minimizar", "minimise": "Minimizar",
"paste": "Colar", "paste": "Colar",
@ -52,7 +52,7 @@
"actual_size": "Tamanho de Verdade", "actual_size": "Tamanho de Verdade",
"toggle_developer_tools": "Ativar/Desativar Ferramentas de Desenvolvimento", "toggle_developer_tools": "Ativar/Desativar Ferramentas de Desenvolvimento",
"toggle_full_screen": "Pôr em/Tirar de Tela Cheia", "toggle_full_screen": "Pôr em/Tirar de Tela Cheia",
"view": "Visualizar" "view": "Ver"
}, },
"window_menu": { "window_menu": {
"bring_all_to_front": "Trazer Todas Para Frente", "bring_all_to_front": "Trazer Todas Para Frente",

View File

@ -6,7 +6,7 @@
"copy": "Kopírovať", "copy": "Kopírovať",
"cut": "Vystrihnúť", "cut": "Vystrihnúť",
"delete": "Odstrániť", "delete": "Odstrániť",
"edit": "Úpravy", "edit": "Upraviť",
"minimise": "Minimalizovať", "minimise": "Minimalizovať",
"paste": "Vložiť", "paste": "Vložiť",
"paste_match_style": "Vložiť a prispôsobiť štýl", "paste_match_style": "Vložiť a prispôsobiť štýl",
@ -19,10 +19,10 @@
"zoom_out": "Oddialiť" "zoom_out": "Oddialiť"
}, },
"common": { "common": {
"about": "O aplikácii", "about": "Informácie",
"brand_help": "%(brand)s Pomoc", "brand_help": "%(brand)s Pomoc",
"help": "Pomocník", "help": "Pomocník",
"preferences": "Vlastnosti" "preferences": "Predvoľby"
}, },
"confirm_quit": "Naozaj chcete zavrieť aplikáciu?", "confirm_quit": "Naozaj chcete zavrieť aplikáciu?",
"edit_menu": { "edit_menu": {

View File

@ -6,7 +6,7 @@
"copy": "Kopiera", "copy": "Kopiera",
"cut": "Klipp ut", "cut": "Klipp ut",
"delete": "Radera", "delete": "Radera",
"edit": "Redigera", "edit": "Ändra",
"minimise": "Minimera", "minimise": "Minimera",
"paste": "Klistra in", "paste": "Klistra in",
"paste_match_style": "Klistra in och matcha stilen", "paste_match_style": "Klistra in och matcha stilen",

View File

@ -1,61 +0,0 @@
{
"action": {
"cancel": "İptal",
"close": "Kapat",
"copy": "Kopyala",
"cut": "Kes",
"delete": "Sil",
"edit": "Düzenle",
"minimise": "Küçült",
"paste": "Yapıştır",
"paste_match_style": "Biçimiyle bir yapıştır",
"quit": ık",
"redo": "Yinele",
"select_all": "Tümünü seç",
"show_hide": "Göster/Gizle",
"undo": "Geri al",
"zoom_in": "Yaklaştır",
"zoom_out": "Uzaklaştır"
},
"common": {
"about": "Hakkında",
"help": "Yardım",
"preferences": "Tercihler"
},
"confirm_quit": ıkmak istediğinize emin misiniz?",
"edit_menu": {
"speech": "Konuşma",
"speech_start_speaking": "Konuşmaya başla",
"speech_stop_speaking": "Konuşmayı durdur"
},
"file_menu": {
"label": "Dosya"
},
"menu": {
"hide": "Gizle",
"hide_others": "Diğerlerini gizle",
"services": "Hizmetler",
"unhide": "Gizlemeyi bırak"
},
"right_click_menu": {
"add_to_dictionary": "Sözlüğe ekle",
"copy_email": "E-posta adresini kopyala",
"copy_image": "Resmi kopyala",
"copy_image_url": "Görsel adresini kopyala",
"copy_link_url": "Bağlantılı adresi kopyala",
"save_image_as": "Resmi ... olarak farklı kaydet",
"save_image_as_error_description": "Bu resim kaydedilemedi",
"save_image_as_error_title": "Resim kaydedilemedi"
},
"view_menu": {
"actual_size": "Gerçek boyut",
"toggle_developer_tools": "Geliştirici araçları",
"toggle_full_screen": "Tam ekran",
"view": "Görünüm"
},
"window_menu": {
"bring_all_to_front": "Hepsini öne getir",
"label": "Pencere",
"zoom": "Yaklaştır"
}
}

View File

@ -3,7 +3,7 @@
"cancel": "Скасувати", "cancel": "Скасувати",
"close": "Закрити", "close": "Закрити",
"close_brand": "Закрити %(brand)s", "close_brand": "Закрити %(brand)s",
"copy": "Копіювати", "copy": "Скопіювати",
"cut": "Вирізати", "cut": "Вирізати",
"delete": "Видалити", "delete": "Видалити",
"edit": "Змінити", "edit": "Змінити",

View File

@ -1,12 +1,12 @@
{ {
"action": { "action": {
"cancel": "Hủy bỏ", "cancel": "Huỷ bỏ",
"close": "Đóng", "close": "Đóng",
"close_brand": "Đóng %(brand)s", "close_brand": "Đóng %(brand)s",
"copy": "Sao chép", "copy": "Sao chép",
"cut": "Cắt", "cut": "Cắt",
"delete": "Xóa", "delete": "X",
"edit": "Chỉnh sửa", "edit": "Sửa",
"minimise": "Thu nhỏ", "minimise": "Thu nhỏ",
"paste": "Dán", "paste": "Dán",
"paste_match_style": "Dán và khớp kiểu", "paste_match_style": "Dán và khớp kiểu",

View File

@ -19,15 +19,12 @@ import { TranslationKey as TKey } from "matrix-web-i18n";
import type Store from "electron-store"; import type Store from "electron-store";
import type EN from "./i18n/strings/en_EN.json"; import type EN from "./i18n/strings/en_EN.json";
import { loadJsonFile } from "./utils";
const FALLBACK_LOCALE = "en"; const FALLBACK_LOCALE = "en";
type TranslationKey = TKey<typeof EN>; type TranslationKey = TKey<typeof EN>;
export function _td(text: TranslationKey): TranslationKey {
return text;
}
type SubstitutionValue = number | string; type SubstitutionValue = number | string;
interface Variables { interface Variables {
@ -109,7 +106,7 @@ export class AppLocalization {
public fetchTranslationJson(locale: string): Record<string, string> { public fetchTranslationJson(locale: string): Record<string, string> {
try { try {
console.log("Fetching translation json for locale: " + locale); console.log("Fetching translation json for locale: " + locale);
return require(`./i18n/strings/${this.denormalize(locale)}.json`); return loadJsonFile(__dirname, "i18n", "strings", `${this.denormalize(locale)}.json`);
} catch (e) { } catch (e) {
console.log(`Could not fetch translation json for locale: '${locale}'`, e); console.log(`Could not fetch translation json for locale: '${locale}'`, e);
return {}; return {};

View File

@ -65,15 +65,6 @@ export function setupMacosTitleBar(window: BrowserWindow): void {
.mx_AuthPage .mx_AuthFooter > * { .mx_AuthPage .mx_AuthFooter > * {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
} }
/* Mark the header as a drag handle */
.mx_LeftPanel .mx_LeftPanel_filterContainer {
-webkit-app-region: drag;
}
/* Exclude header interactive elements from being drag handles */
.mx_LeftPanel .mx_LeftPanel_filterContainer .mx_AccessibleButton {
-webkit-app-region: no-drag;
}
/* Mark the home page background as a drag handle */ /* Mark the home page background as a drag handle */
.mx_HomePage { .mx_HomePage {
@ -86,23 +77,10 @@ export function setupMacosTitleBar(window: BrowserWindow): void {
} }
/* Mark the header as a drag handle */ /* Mark the header as a drag handle */
.mx_LegacyRoomHeader,
.mx_RoomHeader {
-webkit-app-region: drag;
-webkit-user-select: none;
}
.mx_ImageView_panel { .mx_ImageView_panel {
-webkit-app-region: drag; -webkit-app-region: drag;
} }
/* Exclude header interactive elements from being drag handles */ /* Exclude header interactive elements from being drag handles */
.mx_RoomHeader .mx_BaseAvatar,
.mx_RoomHeader_heading,
.mx_RoomHeader button,
.mx_RoomHeader .mx_FacePile,
.mx_LegacyRoomHeader .mx_LegacyRoomHeader_avatar,
.mx_LegacyRoomHeader .mx_E2EIcon,
.mx_LegacyRoomHeader .mx_RoomTopic,
.mx_LegacyRoomHeader .mx_AccessibleButton,
.mx_ImageView_panel > .mx_ImageView_info_wrapper, .mx_ImageView_panel > .mx_ImageView_info_wrapper,
.mx_ImageView_panel > .mx_ImageView_title, .mx_ImageView_panel > .mx_ImageView_title,
.mx_ImageView_panel > .mx_ImageView_toolbar > * { .mx_ImageView_panel > .mx_ImageView_toolbar > * {
@ -110,7 +88,8 @@ export function setupMacosTitleBar(window: BrowserWindow): void {
} }
/* Mark the background as a drag handle only if no modal is open */ /* Mark the background as a drag handle only if no modal is open */
.mx_MatrixChat_wrapper[aria-hidden="false"] .mx_RoomView_wrapper { .mx_MatrixChat_wrapper[aria-hidden="false"] .mx_RoomView_wrapper,
.mx_MatrixChat_wrapper[aria-hidden="false"] .mx_HomePage {
-webkit-app-region: drag; -webkit-app-region: drag;
} }
/* Exclude content elements from being drag handles */ /* Exclude content elements from being drag handles */
@ -119,7 +98,11 @@ export function setupMacosTitleBar(window: BrowserWindow): void {
.mx_RoomView_body, .mx_RoomView_body,
.mx_AutoHideScrollbar, .mx_AutoHideScrollbar,
.mx_RightPanel_ResizeWrapper, .mx_RightPanel_ResizeWrapper,
.mx_RoomPreviewCard { .mx_RoomPreviewCard,
.mx_LeftPanel,
.mx_RoomView,
.mx_SpaceRoomView,
.mx_AccessibleButton {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
} }
/* Exclude context menus and their backgrounds */ /* Exclude context menus and their backgrounds */
@ -130,6 +113,38 @@ export function setupMacosTitleBar(window: BrowserWindow): void {
iframe { iframe {
-webkit-app-region: no-drag; -webkit-app-region: no-drag;
} }
/* Add a bar above room header + left panel */
.mx_LeftPanel {
flex-direction: column;
}
.mx_LeftPanel::before {
content: "";
height: 20px;
-webkit-app-region: drag;
}
.mx_RoomView::before,
.mx_SpaceRoomView::before {
content: "";
-webkit-app-region: drag;
}
.mx_SpaceRoomView::before {
display: block;
height: 24px;
}
.mx_RoomView[data-room-header="new"]::before {
height: 13px;
}
.mx_RoomView[data-room-header="legacy"]::before {
height: 27px;
}
`); `);
} }

View File

@ -86,8 +86,13 @@ export function getProfileFromDeeplink(args: string[]): string | undefined {
if (deeplinkUrl?.includes(SEARCH_PARAM)) { if (deeplinkUrl?.includes(SEARCH_PARAM)) {
const parsedUrl = new URL(deeplinkUrl); const parsedUrl = new URL(deeplinkUrl);
if (parsedUrl.protocol === PROTOCOL) { if (parsedUrl.protocol === PROTOCOL) {
const ssoID = parsedUrl.searchParams.get(SEARCH_PARAM)!;
const store = readStore(); const store = readStore();
let ssoID = parsedUrl.searchParams.get(SEARCH_PARAM);
if (!ssoID) {
// In OIDC, we must shuttle the value in the `state` param rather than `element-desktop-ssoid`
// We encode it as a suffix like `:element-desktop-ssoid:XXYYZZ`
ssoID = parsedUrl.searchParams.get("state")!.split(`:${SEARCH_PARAM}:`)[1];
}
console.log("Forwarding to profile: ", store[ssoID]); console.log("Forwarding to profile: ", store[ssoID]);
return store[ssoID]; return store[ssoID];
} }

View File

@ -72,7 +72,9 @@ export function create(config: IConfig): void {
guid = uuidv5(`${app.getName()}-${app.getPath("userData")}`, getUuid()); guid = uuidv5(`${app.getName()}-${app.getPath("userData")}`, getUuid());
} }
trayIcon = new Tray(defaultIcon, guid); // Passing guid=undefined on Windows will cause it to throw `Error: Invalid GUID format`
// The type here is wrong, the param must be omitted, never undefined.
trayIcon = guid ? new Tray(defaultIcon, guid) : new Tray(defaultIcon);
trayIcon.setToolTip(config.brand); trayIcon.setToolTip(config.brand);
initApplicationMenu(); initApplicationMenu();
trayIcon.on("click", toggleWin); trayIcon.on("click", toggleWin);

View File

@ -15,6 +15,8 @@ limitations under the License.
*/ */
import crypto from "crypto"; import crypto from "crypto";
import fs from "node:fs";
import path from "node:path";
export async function randomArray(size: number): Promise<string> { export async function randomArray(size: number): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -27,3 +29,20 @@ export async function randomArray(size: number): Promise<string> {
}); });
}); });
} }
type JsonValue = null | string | number;
type JsonArray = Array<JsonValue | JsonObject | JsonArray>;
interface JsonObject {
[key: string]: JsonObject | JsonArray | JsonValue;
}
type Json = JsonArray | JsonObject;
/**
* Synchronously load a JSON file from the local filesystem.
* Unlike `require`, will never execute any javascript in a loaded file.
* @param paths - An array of path segments which will be joined using the system's path delimiter.
*/
export function loadJsonFile<T extends Json>(...paths: string[]): T {
const file = fs.readFileSync(path.join(...paths), { encoding: "utf-8" });
return JSON.parse(file);
}

View File

@ -85,8 +85,9 @@ function onLinkContextMenu(ev: Event, params: ContextMenuParams, webContents: We
if (url.startsWith("vector://vector/webapp")) { if (url.startsWith("vector://vector/webapp")) {
// Avoid showing a context menu for app icons // Avoid showing a context menu for app icons
if (params.hasImageContents) return; if (params.hasImageContents) return;
// Rewrite URL so that it can be used outside of the app const baseUrl = vectorConfig.web_base_url ?? "https://app.element.io/";
url = "https://app.element.io/" + url.substring(23); // Rewrite URL so that it can be used outside the app
url = baseUrl + url.substring(23);
} }
const popupMenu = new Menu(); const popupMenu = new Menu();

View File

@ -1,66 +0,0 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import * as os from "os";
import * as fs from "fs";
import * as path from "path";
import "expect-playwright";
import { _electron as electron } from "playwright";
import { ElectronApplication, Page } from "playwright-core";
describe("App launch", () => {
const artifactsPath = path.join(process.cwd(), "test_artifacts");
if (!fs.existsSync(artifactsPath)) fs.mkdirSync(artifactsPath);
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "element-desktop-tests"));
console.log("Using temp profile directory: ", tmpDir);
let app: ElectronApplication;
let window: Page;
beforeAll(async () => {
const args = ["--profile-dir", tmpDir];
const executablePath = process.env["ELEMENT_DESKTOP_EXECUTABLE"];
if (!executablePath) {
// Unpackaged mode testing
args.unshift("./lib/electron-main.js");
}
app = await electron.launch({
executablePath,
args,
recordVideo: {
dir: artifactsPath,
},
});
window = await app.firstWindow();
}, 60000);
afterAll(async () => {
await app?.close().catch((e) => {
console.error(e);
});
fs.rmSync(tmpDir, { recursive: true });
}, 60000);
it("should launch and render the welcome view successfully", async () => {
await window.locator("#matrixchat").waitFor();
await window.locator(".mx_Welcome").waitFor();
await expect(window).toMatchURL("vector://vector/webapp/#/welcome");
await window.screenshot({ path: path.join(artifactsPath, "welcome.png") });
}, 60000);
});

View File

@ -1,16 +0,0 @@
{
"compilerOptions": {
"resolveJsonModule": true,
"moduleResolution": "node",
"esModuleInterop": true,
"target": "es2017",
"module": "commonjs",
"sourceMap": false,
"strict": true,
"lib": ["es2020", "dom"]
},
"include": ["./**/*.ts"],
"ts-node": {
"transpileOnly": true
}
}

View File

@ -12,7 +12,7 @@
"typeRoots": ["src/@types", "node_modules/@types"], "typeRoots": ["src/@types", "node_modules/@types"],
"lib": ["es2020", "dom"], "lib": ["es2020", "dom"],
"types": ["node"], "types": ["node"],
"strict": true "strict": true,
}, },
"include": ["./src/**/*.ts"] "include": ["./src/**/*.ts"],
} }

4897
yarn.lock

File diff suppressed because it is too large Load Diff