mirror of
https://github.com/vector-im/element-web.git
synced 2026-04-15 18:41:37 +02:00
Improve workflows for better reusability (#2608)
This commit is contained in:
parent
5dd1984896
commit
1c55aa780f
67
.github/workflows/build_linux.yaml
vendored
67
.github/workflows/build_linux.yaml
vendored
@ -39,15 +39,39 @@ on:
|
||||
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
|
||||
for example icons in the `build/` directory to override the app icons.
|
||||
default: "webapp"
|
||||
test:
|
||||
type: boolean
|
||||
required: false
|
||||
default: true
|
||||
description: "Whether to run the test stage after building"
|
||||
test-args:
|
||||
type: string
|
||||
required: false
|
||||
description: "Additional arguments to pass to playwright"
|
||||
runs-on:
|
||||
type: string
|
||||
required: false
|
||||
description: "The runner image to use, normally set for you, may be needed for running in private repos."
|
||||
artifact-prefix:
|
||||
type: string
|
||||
required: false
|
||||
description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
|
||||
default: ""
|
||||
targets:
|
||||
type: string
|
||||
required: false
|
||||
description: "List of targets to build"
|
||||
default: "tar.gz deb"
|
||||
env:
|
||||
SQLCIPHER_BUNDLED: ${{ inputs.sqlcipher == 'static' && '1' || '' }}
|
||||
MAX_GLIBC: 2.31 # bullseye-era glibc, used by glibc-check.sh
|
||||
permissions: {} # No permissions required
|
||||
jobs:
|
||||
build:
|
||||
name: Build Linux ${{ inputs.arch }} SQLCipher ${{ inputs.sqlcipher }}
|
||||
# We build on native infrastructure as matrix-seshat fails to cross-compile properly
|
||||
# https://github.com/matrix-org/seshat/issues/135
|
||||
runs-on: ${{ inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04' }}
|
||||
runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }}
|
||||
env:
|
||||
HAK_DOCKER_IMAGE: ghcr.io/element-hq/element-desktop-dockerbuild
|
||||
steps:
|
||||
@ -109,7 +133,7 @@ jobs:
|
||||
|
||||
- name: "Get modified files"
|
||||
id: changed_files
|
||||
if: steps.cache.outputs.cache-hit != 'true' && github.event_name == 'pull_request'
|
||||
if: steps.cache.outputs.cache-hit != 'true' && github.event_name == 'pull_request' && github.repository == 'element-hq/element-desktop'
|
||||
uses: tj-actions/changed-files@823fcebdb31bb35fdf2229d9f769b400309430d0 # v46
|
||||
with:
|
||||
files: |
|
||||
@ -159,13 +183,11 @@ jobs:
|
||||
echo "USE_SYSTEM_FPM=true" >> $GITHUB_ENV
|
||||
|
||||
- name: Build App
|
||||
run: yarn build --publish never -l ${{ steps.config.outputs.build-args }}
|
||||
run: yarn build --publish never ${{ steps.config.outputs.build-args }} -l ${{ inputs.targets }}
|
||||
env:
|
||||
VARIANT_PATH: variant.json
|
||||
# Only set for Nightly builds
|
||||
VERSION: ${{ inputs.version }}
|
||||
FPM_DEBUG: true # TODO
|
||||
DEBUG: electron-builder # TODO
|
||||
# Workaround for https://github.com/electron-userland/electron-builder/issues/5721
|
||||
USE_HARD_LINKS: false
|
||||
|
||||
@ -199,39 +221,46 @@ jobs:
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
|
||||
name: ${{ inputs.artifact-prefix }}linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
|
||||
path: |
|
||||
dist
|
||||
!dist/*-unpacked/**
|
||||
retention-days: 1
|
||||
|
||||
- name: Assert all required files are present
|
||||
- name: Assert deb is present and valid
|
||||
if: contains(inputs.targets, 'deb')
|
||||
run: |
|
||||
test -f ./dist/element-desktop*$ARCH.deb
|
||||
test -f ./dist/element-desktop*.tar.gz
|
||||
env:
|
||||
ARCH: ${{ inputs.arch }}
|
||||
|
||||
- name: Assert no hardlinks are found
|
||||
run: |
|
||||
TAR_GZ_LISTING=$(tar -tvf ./dist/element-desktop*.tar.gz)
|
||||
echo "tar.gz listing: "
|
||||
echo "$TAR_GZ_LISTING"
|
||||
! echo "$TAR_GZ_LISTING" | grep '^h'
|
||||
|
||||
DEB_LISTING=$(dpkg-deb --fsys-tarfile ./dist/element-desktop*.deb | tar -tv)
|
||||
echo "deb listing: "
|
||||
echo "$DEB_LISTING"
|
||||
! echo "$DEB_LISTING" | grep '^h'
|
||||
env:
|
||||
ARCH: ${{ inputs.arch }}
|
||||
|
||||
- name: Assert tar.gz is present
|
||||
if: contains(inputs.targets, 'tar.gz')
|
||||
run: |
|
||||
test -f ./dist/element-desktop*.tar.gz
|
||||
|
||||
TAR_GZ_LISTING=$(tar -tvf ./dist/element-desktop*.tar.gz)
|
||||
echo "tar.gz listing: "
|
||||
echo "$TAR_GZ_LISTING"
|
||||
! echo "$TAR_GZ_LISTING" | grep '^h'
|
||||
|
||||
test:
|
||||
name: Test Linux ${{ inputs.arch }} SQLCipher ${{ inputs.sqlcipher }}
|
||||
needs: build
|
||||
if: inputs.test && contains(inputs.targets, 'deb')
|
||||
uses: ./.github/workflows/build_test.yaml
|
||||
with:
|
||||
artifact: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
|
||||
runs-on: ${{ inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04' }}
|
||||
project: linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
|
||||
artifact: ${{ inputs.artifact-prefix }}linux-${{ inputs.arch }}-sqlcipher-${{ inputs.sqlcipher }}
|
||||
runs-on: ${{ inputs.runs-on || (inputs.arch == 'arm64' && 'ubuntu-22.04-arm' || 'ubuntu-22.04') }}
|
||||
executable: /opt/Element*/element-desktop*
|
||||
prepare_cmd: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt install ./dist/*.deb
|
||||
blob_report: ${{ inputs.blob_report }}
|
||||
args: ${{ inputs.test-args }}
|
||||
|
||||
41
.github/workflows/build_macos.yaml
vendored
41
.github/workflows/build_macos.yaml
vendored
@ -49,9 +49,29 @@ on:
|
||||
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
|
||||
for example icons in the `build/` directory to override the app icons.
|
||||
default: "webapp"
|
||||
test:
|
||||
type: boolean
|
||||
required: false
|
||||
default: true
|
||||
description: "Whether to run the test stage after building"
|
||||
test-args:
|
||||
type: string
|
||||
required: false
|
||||
description: "Additional arguments to pass to playwright"
|
||||
artifact-prefix:
|
||||
type: string
|
||||
required: false
|
||||
description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
|
||||
default: ""
|
||||
targets:
|
||||
type: string
|
||||
required: false
|
||||
description: "List of targets to build"
|
||||
default: "dmg zip"
|
||||
permissions: {} # No permissions required
|
||||
jobs:
|
||||
build:
|
||||
name: Build macOS Universal
|
||||
runs-on: macos-14 # M1
|
||||
environment: ${{ inputs.sign && 'packages.element.io' || '' }}
|
||||
steps:
|
||||
@ -105,7 +125,7 @@ jobs:
|
||||
- name: "[Signed] Build App"
|
||||
if: inputs.sign != ''
|
||||
run: |
|
||||
yarn build:universal --publish never
|
||||
yarn build:universal --publish never -m ${{ inputs.targets }}
|
||||
env:
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
@ -127,7 +147,7 @@ jobs:
|
||||
- name: "[Unsigned] Build App"
|
||||
if: inputs.sign == ''
|
||||
run: |
|
||||
yarn build:universal --publish never
|
||||
yarn build:universal --publish never -m ${{ inputs.targets }}
|
||||
env:
|
||||
CSC_IDENTITY_AUTO_DISCOVERY: false
|
||||
VARIANT_PATH: variant.json
|
||||
@ -162,22 +182,30 @@ jobs:
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: macos
|
||||
name: ${{ inputs.artifact-prefix }}macos
|
||||
path: |
|
||||
dist
|
||||
!dist/mac-universal/**
|
||||
retention-days: 1
|
||||
|
||||
- name: Assert all required files are present
|
||||
- name: Assert zip is present
|
||||
if: contains(inputs.targets, 'zip')
|
||||
run: |
|
||||
test -f ./dist/Element*.dmg
|
||||
test -f ./dist/Element*-mac.zip
|
||||
|
||||
- name: Assert dmg is present
|
||||
if: contains(inputs.targets, 'dmg')
|
||||
run: |
|
||||
test -f ./dist/Element*.dmg
|
||||
|
||||
test:
|
||||
name: Test macOS Universal
|
||||
needs: build
|
||||
if: inputs.test && contains(inputs.targets, 'dmg')
|
||||
uses: ./.github/workflows/build_test.yaml
|
||||
with:
|
||||
artifact: macos
|
||||
project: macos
|
||||
artifact: ${{ inputs.artifact-prefix }}macos
|
||||
runs-on: macos-14
|
||||
executable: /Users/runner/Applications/Element*.app/Contents/MacOS/Element*
|
||||
# We need to mount the DMG and copy the app to the Applications folder as a mounted DMG is
|
||||
@ -187,3 +215,4 @@ jobs:
|
||||
rsync -a /Volumes/Element/Element*.app ~/Applications/ &&
|
||||
hdiutil detach /Volumes/Element
|
||||
blob_report: ${{ inputs.blob_report }}
|
||||
args: ${{ inputs.test-args }}
|
||||
|
||||
13
.github/workflows/build_test.yaml
vendored
13
.github/workflows/build_test.yaml
vendored
@ -10,6 +10,10 @@ on:
|
||||
type: string
|
||||
required: true
|
||||
description: "The name of the artifact to download"
|
||||
project:
|
||||
type: string
|
||||
required: true
|
||||
description: "The Playwright project to use for testing"
|
||||
executable:
|
||||
type: string
|
||||
required: true
|
||||
@ -22,12 +26,19 @@ on:
|
||||
type: boolean
|
||||
default: false
|
||||
description: "Whether to upload a blob report instead of the HTML report"
|
||||
args:
|
||||
type: string
|
||||
required: false
|
||||
description: "Additional arguments to pass to playwright, for e.g. skipping specific tests"
|
||||
permissions: {}
|
||||
jobs:
|
||||
test:
|
||||
name: Test ${{ inputs.project }}
|
||||
runs-on: ${{ inputs.runs-on }}
|
||||
steps:
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
|
||||
with:
|
||||
repository: ${{ github.repository == 'element-hq/element-web-pro' && 'element-hq/element-desktop' || github.repository }}
|
||||
|
||||
- uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5
|
||||
with:
|
||||
@ -69,7 +80,7 @@ jobs:
|
||||
uses: coactions/setup-xvfb@6b00cf1889f4e1d5a48635647013c0508128ee1a
|
||||
timeout-minutes: 20
|
||||
with:
|
||||
run: yarn test --project=${{ inputs.artifact }} ${{ runner.os != 'Linux' && '--ignore-snapshots' || '' }} ${{ inputs.blob_report == false && '--reporter=html' || '' }}
|
||||
run: yarn test --project=${{ inputs.project }} ${{ runner.os != 'Linux' && '--ignore-snapshots' || '' }} ${{ inputs.blob_report == false && '--reporter=html' || '' }} ${{ inputs.args }}
|
||||
env:
|
||||
ELEMENT_DESKTOP_EXECUTABLE: ${{ steps.executable.outputs.path }}
|
||||
|
||||
|
||||
46
.github/workflows/build_windows.yaml
vendored
46
.github/workflows/build_windows.yaml
vendored
@ -53,9 +53,33 @@ on:
|
||||
The artifact can also contain any additional files which will be applied as overrides to the checkout root before building,
|
||||
for example icons in the `build/` directory to override the app icons.
|
||||
default: "webapp"
|
||||
test:
|
||||
type: boolean
|
||||
required: false
|
||||
default: true
|
||||
description: "Whether to run the test stage after building"
|
||||
test-runs-on:
|
||||
type: string
|
||||
required: false
|
||||
description: "The runner image to use for testing, normally set for you, may be needed for running in private repos."
|
||||
test-args:
|
||||
type: string
|
||||
required: false
|
||||
description: "Additional arguments to pass to playwright"
|
||||
artifact-prefix:
|
||||
type: string
|
||||
required: false
|
||||
description: "An optional prefix to add to the artifact name, useful for distinguishing builds in private repos."
|
||||
default: ""
|
||||
targets:
|
||||
type: string
|
||||
required: false
|
||||
description: "List of targets to build"
|
||||
default: "squirrel msi"
|
||||
permissions: {} # No permissions required
|
||||
jobs:
|
||||
build:
|
||||
name: Build Windows ${{ inputs.arch }}
|
||||
runs-on: windows-2025
|
||||
environment: ${{ inputs.sign && 'packages.element.io' || '' }}
|
||||
env:
|
||||
@ -208,7 +232,7 @@ jobs:
|
||||
MASTER_KEY_FILE: C:\Users\runneradmin\eSignerCKA\master.key
|
||||
|
||||
- name: Build App
|
||||
run: yarn build --publish never -w ${{ steps.config.outputs.build-args }}
|
||||
run: yarn build --publish never ${{ steps.config.outputs.build-args }} -w ${{ inputs.targets }}
|
||||
env:
|
||||
VARIANT_PATH: variant.json
|
||||
# Only set for Nightly builds
|
||||
@ -236,24 +260,36 @@ jobs:
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: win-${{ inputs.arch }}
|
||||
name: ${{ inputs.artifact-prefix }}win-${{ inputs.arch }}
|
||||
path: |
|
||||
dist
|
||||
retention-days: 1
|
||||
|
||||
- name: Assert all required files are present
|
||||
- name: Assert executable is present
|
||||
run: |
|
||||
Test-Path './dist/win-*unpacked/Element*.exe'
|
||||
|
||||
- name: Assert all Squirrel files are present
|
||||
if: contains(inputs.targets, 'squirrel')
|
||||
run: |
|
||||
Test-Path './dist/squirrel-windows*/Element Setup*.exe'
|
||||
Test-Path './dist/squirrel-windows*/element-desktop-*-full.nupkg'
|
||||
Test-Path './dist/squirrel-windows*/RELEASES'
|
||||
|
||||
- name: Assert MSI is present
|
||||
if: contains(inputs.targets, 'msi')
|
||||
run: |
|
||||
Test-Path './dist/Element*.msi'
|
||||
|
||||
test:
|
||||
name: Test Windows ${{ inputs.arch }}
|
||||
needs: build
|
||||
if: inputs.test
|
||||
uses: ./.github/workflows/build_test.yaml
|
||||
with:
|
||||
artifact: win-${{ inputs.arch }}
|
||||
runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2022' }}
|
||||
project: win-${{ inputs.arch }}
|
||||
artifact: ${{ inputs.artifact-prefix }}win-${{ inputs.arch }}
|
||||
runs-on: ${{ inputs.test-runs-on || (inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-2022') }}
|
||||
executable: ./dist/win*-unpacked/Element*.exe
|
||||
blob_report: ${{ inputs.blob_report }}
|
||||
args: ${{ inputs.test-args }}
|
||||
|
||||
@ -151,6 +151,7 @@ const config: Omit<Writable<Configuration>, "electronFuses"> & {
|
||||
fpm: ["--deb-pre-depends", "libc6 (>= 2.31)"],
|
||||
},
|
||||
mac: {
|
||||
target: ["dmg", "zip"],
|
||||
category: "public.app-category.social-networking",
|
||||
darkModeSupport: true,
|
||||
hardenedRuntime: true,
|
||||
|
||||
@ -169,59 +169,74 @@ function loadLocalConfigFile(): Json {
|
||||
}
|
||||
}
|
||||
|
||||
let loadConfigPromise: Promise<void> | undefined;
|
||||
// Loads the config from asar, and applies a config.json from userData atop if one exists
|
||||
// Writes config to `global.vectorConfig`. Does nothing if `global.vectorConfig` is already set.
|
||||
async function loadConfig(): Promise<void> {
|
||||
if (global.vectorConfig) return;
|
||||
// Writes config to `global.vectorConfig`. Idempotent, returns the same promise on subsequent calls.
|
||||
function loadConfig(): Promise<void> {
|
||||
if (loadConfigPromise) return loadConfigPromise;
|
||||
|
||||
const asarPath = await getAsarPath();
|
||||
async function actuallyLoadConfig(): Promise<void> {
|
||||
const asarPath = await getAsarPath();
|
||||
|
||||
try {
|
||||
console.log(`Loading app config: ${path.join(asarPath, LocalConfigFilename)}`);
|
||||
global.vectorConfig = loadJsonFile(asarPath, LocalConfigFilename);
|
||||
} catch {
|
||||
// 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
|
||||
// file or invalid json, so node is just very unhelpful.
|
||||
// Continue with the defaults (ie. an empty config)
|
||||
global.vectorConfig = {};
|
||||
}
|
||||
|
||||
try {
|
||||
// Load local config and use it to override values from the one baked with the build
|
||||
const localConfig = loadLocalConfigFile();
|
||||
|
||||
// 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
|
||||
// defined, and panics as a result.
|
||||
if (Object.keys(localConfig).find((k) => homeserverProps.includes(<any>k))) {
|
||||
// Rip out all the homeserver options from the vector config
|
||||
global.vectorConfig = Object.keys(global.vectorConfig)
|
||||
.filter((k) => !homeserverProps.includes(<any>k))
|
||||
.reduce(
|
||||
(obj, key) => {
|
||||
obj[key] = global.vectorConfig[key];
|
||||
return obj;
|
||||
},
|
||||
{} as Omit<Partial<(typeof global)["vectorConfig"]>, keyof typeof homeserverProps>,
|
||||
);
|
||||
try {
|
||||
console.log(`Loading app config: ${path.join(asarPath, LocalConfigFilename)}`);
|
||||
global.vectorConfig = loadJsonFile(asarPath, LocalConfigFilename);
|
||||
} catch {
|
||||
// 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
|
||||
// file or invalid json, so node is just very unhelpful.
|
||||
// Continue with the defaults (ie. an empty config)
|
||||
global.vectorConfig = {};
|
||||
}
|
||||
|
||||
global.vectorConfig = Object.assign(global.vectorConfig, localConfig);
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
void dialog.showMessageBox({
|
||||
type: "error",
|
||||
title: `Your ${global.vectorConfig.brand || "Element"} is misconfigured`,
|
||||
message:
|
||||
`Your custom ${global.vectorConfig.brand || "Element"} configuration contains invalid JSON. ` +
|
||||
`Please correct the problem and reopen ${global.vectorConfig.brand || "Element"}.`,
|
||||
detail: e.message || "",
|
||||
try {
|
||||
// Load local config and use it to override values from the one baked with the build
|
||||
const localConfig = loadLocalConfigFile();
|
||||
|
||||
// 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
|
||||
// defined, and panics as a result.
|
||||
if (Object.keys(localConfig).find((k) => homeserverProps.includes(<any>k))) {
|
||||
// Rip out all the homeserver options from the vector config
|
||||
global.vectorConfig = Object.keys(global.vectorConfig)
|
||||
.filter((k) => !homeserverProps.includes(<any>k))
|
||||
.reduce(
|
||||
(obj, key) => {
|
||||
obj[key] = global.vectorConfig[key];
|
||||
return obj;
|
||||
},
|
||||
{} as Omit<Partial<(typeof global)["vectorConfig"]>, keyof typeof homeserverProps>,
|
||||
);
|
||||
}
|
||||
|
||||
global.vectorConfig = Object.assign(global.vectorConfig, localConfig);
|
||||
} catch (e) {
|
||||
if (e instanceof SyntaxError) {
|
||||
void dialog.showMessageBox({
|
||||
type: "error",
|
||||
title: `Your ${global.vectorConfig.brand || "Element"} is misconfigured`,
|
||||
message:
|
||||
`Your custom ${global.vectorConfig.brand || "Element"} configuration contains invalid JSON. ` +
|
||||
`Please correct the problem and reopen ${global.vectorConfig.brand || "Element"}.`,
|
||||
detail: e.message || "",
|
||||
});
|
||||
}
|
||||
|
||||
// Could not load local config, this is expected in most cases.
|
||||
}
|
||||
|
||||
// Tweak modules paths as they assume the root is at the same level as webapp, but for `vector://vector/webapp` it is not.
|
||||
if (Array.isArray(global.vectorConfig.modules)) {
|
||||
global.vectorConfig.modules = global.vectorConfig.modules.map((m) => {
|
||||
if (m.startsWith("/")) {
|
||||
return "/webapp" + m;
|
||||
}
|
||||
return m;
|
||||
});
|
||||
}
|
||||
|
||||
// Could not load local config, this is expected in most cases.
|
||||
}
|
||||
loadConfigPromise = actuallyLoadConfig();
|
||||
return loadConfigPromise;
|
||||
}
|
||||
|
||||
// Configure Electron Sentry and crashReporter using sentry.dsn in config.json if one is present.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user