snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-16 00:29:55 +05:00
commit 240db4e2a7
15309 changed files with 2173406 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
---
title: "Configuration"
sidebar_position: 1
---
### Config module
The **configuration** module in the **Common** module is responsible for facilitating reading from and writing to external configuration sources. It also consolidates, verifies, and upgrades configuration where applicable.
#### Files
We have a few different configuration files in the Desktop App, but the main one is `config.json`. Most of the user's configuration from the Settings Window is stored there, as well as any user-configured servers.
The application supports different configuration versions and allows for them to be migrating to the version supported by the configuration module via the `upgradePreferences` module. When no configuration is found, the `defaultPreferences` object is copied over to the main configuration module.
We also support a build configuration in which the packager of the application can pre-define servers and a few other configuration items.
#### Registry
We support reading from the Windows registry to allow system administrators to define Group Policy that will pre-define servers and potentially disable user-defined servers and automatic updates as per administrator wishes.
Templates for these can be found under `resources\windows\gpo`.
### Server manager
The `ServerManager` is a singleton class that acts as a single source of truth for all server configuration, managing adding/modifying/removing servers and serving the server information to the rest of the application.
#### Initialization
We populate the `ServerManager` with all servers provided by the **configuration** module, marking them as pre-defined when applicable to not allow the user to modify them. Servers are given a unique UUID when the app initialized, and this UUID acts as the global way of identifying the server to the rest of the application.
An external call is responsible for populating information about the specific Mattermost server (eg. server version, plugins installed), but the data is stored within the `ServerManager`.
#### Modification
The `ServerManager` is the only place that allows the persistent server configuration to be modified. Changes cannot be made directly through the **configuration** module. Once a server is modified, the `ServerManager` will update the **configuration** module with the new changes.
When a server is added or updating, up to two events will be emitted:
- `SERVERS_UPDATE`: This event is emitted when the `ServerManager` has new changes. This could be a name, URL or an ordering change.
- `SERVERS_URL_MODIFIED`: This event is emitted specifically when a URL has changed, signifying that the application might need to fetch new remote server information or refresh any views associated with the server to reflect the new URL.
#### Lookup
We provide a server lookup call that allows for an arbitrary URL to be provided and potentially matched to a server. If found, the UUID will be provided. This function is useful for deep linking or for cross-server linking, when only a URL is available when the request is provided.

View File

@@ -0,0 +1,8 @@
---
title: "External views"
sidebar_position: 4
---
To provide access to different servers, we create a series of `BrowserView` objects that directly render on top of the Main Window and load the Mattermost Web App directly from the server they correspond to. We wrap these `BrowserView` objects into a `MattermostView` that manages the loading of the view and handles events such as navigation and notifications.
These views are also contained within and managed by the `viewManager` class. The class is responsible for adding and removing the views from the Main Window when the user needs them and handling IPC calls from the renderer processes and passing them to the child objects.

View File

@@ -0,0 +1,35 @@
---
title: "Architecture"
sidebar_position: 1
---
#### Electron
The Desktop App, like all Electron apps, is broken into two pieces: the **main** process and the **renderer** process.
- The **main** process is a NodeJS process that has access to operating system functions, and governs the creation and management of several renderer processes.
- The **renderer** processes are Chromium instances that perform different functions. In our app, each Mattermost server is its own renderer process.
![Process diagram](process-diagram.png)
In order to facilitate communication between the two processes, there's a communication layer in which information can be sent between. We expose *ONLY* the communication API to the renderer process so that we don't allow any malicious server to wreak havoc on a user's computer.
You can read more about the Process Model [here](https://www.electronjs.org/docs/latest/tutorial/process-model).
#### Directory structure
The directory structure is broken down into a few pieces to best organize the code:
```
Mattermost Desktop
├── docs/ - Documentation for working on the Desktop App
├── e2e/ - E2E tests
│ ├── modules/ - Setup code for the E2E tests
│ └── specs/ - E2E tests themselves
├── resources/ - Assets such as images or sound files that the Desktop App uses
├── scripts/ - Automated scripts used for building or packaging the Desktop App
└── src/ - Application source code
├── assets/ - Assets such as images or sound files that the Desktop App uses
├── common/ - Common objects and utility functions that aren't specifically tied to Electron
├── main/ - The majority of the main process code, including setup for the Electron app
├── renderer/ - The web code for all of the main application wrapper, modals. and server dropdown views that are used by the renderer process
└── types/ - Common types for use between all of the individual modules
```

View File

@@ -0,0 +1,59 @@
---
title: "Internal views"
sidebar_position: 3
---
There are several renderer processes that make up the internal interface of the Desktop App. These are all represented by singleton objects that reside in the Main Module. These classes are in charge of holding the corresponding `BrowserWindow` or `BrowserView` object, initializing any handlers specific to that view, and exposing any special functionality that other modules may need to either read or affect the view.
As all of these views only load trusted scripts in the renderer process, all of these views are given full access to the `desktopAPI` module, allowing them to perform basically any action that we allow for in the Desktop App via the IPC layer.
### Windows
These are the internally-managed windows acting as the main user interface points for the user. Each of these views are represented by a `BrowserWindow` object.
#### Main window
![Main Window screenshot](/img/desktop-architecture/main-window.png)
This is the primary view that encapsulates the core of the Desktop App interface. Most `BrowserView` objects are rendered using this window as their parent, and are affected by the behavior of this window. Most other controls, including the tray icon and taskbar/dock icon, interact with this window, and most of their functionality is tied to it as well.
This window is managed by the `MainWindow` module located at [main/windows/mainWindow](https://github.com/mattermost/desktop/blob/master/src/main/windows/mainWindow.ts).
##### Hooks
- `init()`: Creates the `BrowserWindow` object for the Main Window and adds all appropriate listeners.
- `get()`: Returns the `BrowserWindow` object for the Main Window. This is directly exposed as there are many different functions affecting the behavior of the window, and thus the encapsulating module often needs to pass that control to other modules. If `true` is passed as an argument, `init()` will be called if the window does not exist, otherwise `undefined` is returned.
- `getBounds()`: Returns the current size and location of the `BrowserWindow`, used for resize functionality, and to ensure that child windows/views are positioned correctly.
- `focusThreeDotMenu()`: Sends a message to the Main process that focuses the view and highlights and focuses the 3-dot menu on Windows/Linux. This is used when the `ALT` key is pressed as a shortcut to focus the menu.
#### Settings window
![Settings Window screenshot](/img/desktop-architecture/settings-window.png)
This window is created when the user opens **Preferences** from the **File** menu. It contains an interface where the user can change settings specific to the Desktop App client that do not affect their Mattermost servers. This window is a child window of the Main Window and will close/hide when the Main Window is closed/hidden.
This window is managed by the `SettingsWindow` module located at [main/windows/settingsWindow](https://github.com/mattermost/desktop/blob/master/src/main/windows/settingsWindow.ts).
##### Hooks
- `show()`: Shows the Settings Window if it exists and will create it if does not. When the window is closed, the `BrowserWindow` object is dereferenced.
- `get()`: Retrieves the Settings Window `BrowserWindow` object if it exists and returns `undefined` if it does not.
### Views
These are the internally managed views that are rendered on top of existing windows, adding additional functionality. Each of these views are represented by a `BrowserView` object.
Most of these views exist as they act as augments to the existing interface and must be rendered over top of the external sandbox Mattermost `BrowserViews`.
#### Loading screen
![Loading Screen screenshot](/img/desktop-architecture/loading-screen.png)
This is a `BrowserView` that renders over top of external Mattermost views that are loading. It is a cosmetic view that avoids the user having a white screen while the application is loading. The view is ephemeral should only be visible while the current external Mattermost view is loading.
This view is managed by the `LoadingScreen` module located at [main/views/loadingScreen](https://github.com/mattermost/desktop/blob/master/src/main/views/loadingScreen.ts). Its parent is the Main Window.
##### Hooks
- `show()`: Displays the Loading Screen over top of any other `BrowserView` currently rendered in the Main Window and begins the animation.
- `fade()`: Starts the process of removing the Loading Screen. First a signal is sent to the renderer to fade the screen and stop the animation. When that finishes, the view is removed from the window.
- `setBounds()`: Calls when the Main Window resizes while the Loading Screen is still visible and the view needs to change its size as well.
- `setDarkMode()`: Calls when the application's dark mode flag is changed, to ensure a consistent color scheme.
- `isHidden()`: Helper method to check whether the view is hidden or not.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,34 @@
---
title: "Logging"
sidebar_position: 5
---
Our application uses the `electron-log` module to do most of our logging. It facilitates both file and console logging.
For file logging, you can find the location of the log files by going to **Help** > **View Logs** from within the application.
Our app supports the following log levels: `error`, `warn`, `info`, `verbose`, `debug`, and `silly`.
In addition to the library, we provide a **Logger** object that simplifies and streamlines setting up logging for an individual module.
To create a **Logger** object, simply create a new one:
```js
import {Logger} from 'common/log';
const log = new Logger('MyModuleName');
```
You can then use the resulting *log* object to call any of the provided `electron-log` functions, and each log entry with be automatically prefixed with your module name.
```js
// Will print out "[MyModuleName] a long entry"
log.debug('a log entry');
```
If you need to add additional prefixing, for example to log events on a specific object instance, we provide the `withPrefix()` method which allows you to add additional prefixes.
```js
// Will print out "[MyModuleName] [some-id] a long entry"
const myObjectId = 'some-id';
log.withPrefix(myObjectId).debug('a log entry');
```

View File

@@ -0,0 +1,71 @@
---
title: "Navigation"
sidebar_position: 2
---
The Desktop App exercises relatively strict control over the user's ability to navigate through the web. This is done for a few reasons:
- **Security:** Since we expose certain Electron (and therefore NodeJS) APIs to the front-end application, we want to be in control of what scripts are run in the front-end. We make a concerted effort to lock down the exposed APIs to only what is necessary; however, to avoid any privacy or security breaches, it's best to avoid allowing the user to navigate to any page that isn't explicitly trusted.
- **User Experience:** Our application is ONLY designed to work with the Mattermost Web App and thus allowing the user to navigate to other places that are not the Web App is not a supported use case, and could create some undesirable effects.
![Navigation diagram](/img/desktop-architecture/navigation-diagram.png)
### Internal navigation
The Mattermost Web App is self-contained, with the majority of links provided by `react-router` and thus most navigation is handled by that module. However, in the Desktop App, we have a major feature that allows users to navigate between distinct tabs bound to the same server. There are two ways that this style of navigation happens in the Web App:
- A user clicks on a link provided by the `react-router` `Link` component
- The application calls `browserHistory.push` directly within the Web App based on the user action
Both of these methods will make use of the `browserHistory` module within the Web App.
When one of the above methods is used, normally the Web App would update the browser's URL and change the state of the page. In the Desktop App, we instead send the arguments of the call to `browserHistory.push` up to the Electron Main Process. The information is received at the method `WindowManager.handleBrowserHistoryPush`, where we perform the following actions:
- **Clean the path name by removing any part of the server's subpath pathname.**
- When the arguments are sent up to the Desktop App, it includes the subpath of the server hosting it.
- As an example, if the server URL is `http://server-1.com/mattermost`, any path that is received will start with `/mattermost` and we will need to remove that component. The same would be true for any other path following the origin `http://server-1.com`.
- **Retrieve the view matching the path name**
- After removing the leading subpath (if applicable), we check to see if a portion of the path matches one of the other tabs, signally that we will need to switch to that tab.
- For server `http://server-1.com/mattermost`, if the pathname is `/mattermost/boards/board1`, we would get the *Boards* view matching the server.
- **Display the correct view and send the cleaned path to its renderer process**
- We then explicitly display the new view if it's not currently in focus. If it's closed, we open it and load the corresponding URL with the provided path.
- *Exception*: If we're redirecting to the root of the application and the user is not logged in, it will generate an unnecessary refresh. In this case, we do not send the path name down.
### External navigation
For the cases where a user wants to navigate away from the Web App to an external site, we generally want to direct the user outside of the Desktop App and have them open their default web browser and use the external site in that application.
In order to achieve this, we need to explicitly handle every other link and method of navigation that is available to an Electron renderer process. Fortunately, Electron provides a few listeners that help us with that:
- [**will-navigate**](https://www.electronjs.org/docs/latest/api/web-contents#event-will-navigate) is an event that fires when the URL is changed for a given renderer process. Attaching a listener for this event allows us to prevent the navigation if desired.
- NOTE: The event will not fire for in-page navigations or updating `window.location.hash`.
- [**did-start-navigation**](https://www.electronjs.org/docs/latest/api/web-contents#event-did-start-navigation) is another renderer process event that will fire once the page has started navigating. We can use this event to perform any actions when a certain URL is visited.
- [**new-window**](https://www.electronjs.org/docs/latest/breaking-changes#removed-webcontents-new-window-event) is an event that will fire when the user tries to open a new window or tab. This commonly will fire when the user clicks on a link marked `target=_blank`. We attach this listener using the `setWindowOpenHandler` and will allow us to `allow` or `deny` the opening as we desire.
In our application, we define all of these listeners in the `webContentEvents` module, and we attach them whenever a new [webContents](https://www.electronjs.org/docs/latest/api/web-contents) object is create to make sure that all renderer processes are correctly secured and set up correctly.
#### New window handling
Our new window handler will *deny* the opening of a new Electron window if any of the following cases are true:
- **Malformed URL:** Depending on the case, it will outright ignore it (if the URL could not be parsed), or it will open the user's default browser if it is somehow invalid in another way.
- **Untrusted Protocol:** If the URL does not match an allowed protocol (allowed protocols include `http`, `https`, and any other protocol that was explicitly allowed by the user).
- In this case, it will ask the user whether the protocol should be allowed, and if so will open the URL in the user's default application that corresponds to that protocol.
- **Unknown Site:** If the URL does not match the root of a configured server, it will always try to open the link in the user's default browser.
- If the URL DOES match the root of a configured server, we still will deny the window opening for a few cases:
- If the URL matches the public files route (`/api/v4/public/files/*`)
- If the URL matches the image proxy route (`/api/v4/image/*`)
- If the URL matches the help route (`/help/*`)
- For these cases, we will open the link in the user's browser.
- **Deep Link Case**: If the URL doesn't match any of the above routes, but is still a valid configured server, we will generally treat is as the deep link cause, and will instead attempt to show the correct tab as well as navigate to the corresponding URL within the app.
There are two cases where we do allow the application to open a new window:
- If the URL matches the `devtools:` protocol, so that we can open the Chrome Developer Tools.
- If the URL is a valid configured server URL that corresponds to the plugins route (`/plugins/*`). In these cases we allow a single popup per tab to be opened for certain plugins to do things like OAuth (e.g. GitHub or JIRA).
Any other case will be automatically denied for security reasons.
#### Links within the same window
By default, the Mattermost Web App marks any link external to its application as `target=_blank`, so that the application doesn't try to open it in the same window. Any other links should therefore be internal to the application.
We *deny* any sort of in-window navigation with the following exceptions: if the link is a `mailto:` link (which always opens the default mail program), OR if we are in the custom login flow.
#### Custom login flow
In order to facilitate logging into to the app using an external provider (e.g. Okta) in the same way that one would in the browser, we add an exception to the navigation flow that bypasses the `will-navigate` check.
When a user clicks on a login link that redirects them to a matching URL scheme (listed [here](https://github.com/mattermost/desktop/blob/master/src/common/utils/constants.ts#L48)), we will activate the custom login flow. The URL *MUST* still be internal to the application before we activate this flow, or any URL matching this pattern would allow the app to circumvent the navigation protection.
While the current window is in the custom login flow, all links that emit the `will-navigate` event will be allowed. Anything that opens a new window will still be restricted based on the rules for new windows. We leave the custom login flow once the app has navigated back to an URL internal to the application

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View File

@@ -0,0 +1,83 @@
---
title: "Build and CLI commands"
sidebar_position: 2
---
## Build
Here's a list of all the commands used by the Desktop App. These can all be found in `package.json`, and should be run using `npm`, using the following syntax: ```npm run <command>```.
#### Testing and Verification
* `check` - Runs ESLint, checks types, validates the build config and runs the unit tests
* `check-build-config` - Builds and validates the build config
* `check-types` - Runs the TypeScript compiler against the code to check the types for errors
* `lint:js` - Runs ESLint against the code and displays results
* `lint:js-quiet` - Same as above, but with the --quiet option
* `fix:js` - Save as above, but attempts to fix some of the issues
* `test` - Builds and runs all of the automated tests for the Desktop App
* `test:e2e` - Builds and runs the E2E tests for the Desktop App
* `test:e2e:no-rebuild` - Runs the E2E tests without rebuilding the entire app
* `test:e2e:run` - Runs the E2E tests without building them
* `test:e2e:send-report` - Uploads E2E results
* `test:unit` - Runs the unit tests for the main module
* `test:unit-coverage` - Runs the unit tests and displays a coverage breakdown
#### Building and Running
* `build` - An amalgam of the following build commands, used to build the Desktop App:
* `build:main` - Builds the source code used by the Electron Main process
* `build:renderer` - Builds the source code used by the Electron Renderer process
* `build:preload` - Builds the source code used by the preload scripts run in the preload context of the Electron Renderer process
* `build-prod` - Builds the app in production mode
* `build-prod-mas` - Builds the app in production mode for Mac App Store distribution
* `build-prod-upgrade` - Builds the app in production mode with auto-update functionality
* `build-test`- Builds the app for E2E testing
* `build-test:e2e` - Builds only the E2E tests and not the app
* `build-test:robotjs` - Builds the RobotJS test module for the current Electron version
* `start` - Runs the Desktop App using the current code built in the dist/ folder
* `restart` - Re-runs the build process and then starts the app (amalgam of build and start)
* `watch` - Runs the app, but watches for code changes and re-compiles on the fly when a file is changed
#### Packaging
* `package` - Builds and creates distributable packages for all OSes
* `package:windows` - Builds and creates distributable packages for Windows
* `package:windows-zip` - Builds and create distributable ZIP packages for Windows
* `package:windows-installers` - Builds and creates distributable MSI and EXE packages for Windows
* `package:mac` - Builds and creates distributable packages for macOS
* `package:mac-with-universal` - Same as above, but includes a universal binary
* `package:mas` - Builds and creates distributable packages for Mac App Store
* `package:mas-dev` - Same as above, but builds the development version for testing
* `package:linux` - Builds and creates distributable packages for Linux
* `package:linux-tar` - Builds and creates distributable .tar.gz packagesfor Linux
* `package:linux-pkg` - Builds and creates distributable .deb packages for Ubuntu/Debian and .rpm for Red Hat/Fedora
* `package:linux-appImage` - Builds and creates distributable .AppImage packages for Linux
#### Workspace Utility
* `clean` - Removes all installed Node modules and built code
* `clean-install` - Same as above, but then runs npm install to reinstall the Node modules
* `clean-dist` - Only removes the built code
* `prune` - Runs ts-prune to display unused code
* `i18n-extract` - Scrape the codebase and adds missing translations to the translation file
* `create-linux-dev-shortcut`: Creates a shortcut for Linux developers to ensure deep linking works
## CLI options
Some useful CLI options the desktop app uses are shown below. You can also display these options by running: `npm run start help`.
```
--version, -v: Prints the application version
--dataDir, -d: Set the path to where user data is stored
--disableDevMode, -p: Disable development mode to allow for testing as if it was Production
```
## Environment variables
Some common environment variables that are used include:
- `NODE_ENV`: Defines the Node environment
- `PRODUCTION`: Used for Production mode
- `DEVELOPMENT`: Development mode
- `TEST`: Used when running automated tests
- `MM_DEBUG_MODALS`: Used for debugging modals, set to `1` to show Developer Tools when a modal is opened

View File

@@ -0,0 +1,65 @@
---
title: "Debug the desktop app"
sidebar_position: 3
---
## Debug the main process
The simplest way to debug the main process is to simply insert logging statements wherever needed and have the application output logs of whatever is necessary.
For already built applications (or bugs that only appear in the packaged version of the application), you can view the Logs by going to Help > Show Logs in the 3-dot menu, which will open a file manager window showing the location of the log file.
If you'd like to make use of better debugging tools, you can use the Chrome Dev Tools or the debugger in VSCode by following the steps here: https://www.electronjs.org/docs/latest/tutorial/debugging-main-process
## Debug the renderer process
The renderer processes are controller by Chrome instances, so each of them will have their own Developer Tools instance.
You can access these instances by going to the **View > Developer Tools** menu (under the 3-dot menu on Windows/Linux, and in the top bar on macOS) and selecting:
- **Developer Tools for Application Wrapper** for anything involving the top bar.
- **Developer Tools for Current Tab** for anything involving the Mattermost view or the preload script.
<Note title="Note">
For this one, make sure you're currently on the tab where you want to load the Developer Tools. You can have instances open for tabs you aren't currently viewing, but to open them in the first place requires it to be opened.
</Note>
- **Developer Tools for Call Widget** if you are using Mattermost Calls and the calls widget is currently open.
There are other `BrowserViews` that are governed seperately from the main application wrapper, including:
- Dropdown Menu
- You can open this one by adding a line in the `main/teamDropdownView.ts` file. In the constructor, at the end, add:
```js
this.view.webContents.openDevTools({mode: 'detach'});
```
- Modals
- You can open these by setting an environment variable when running the Desktop App called `MM_DEBUG_MODALS`.
```
// macOS/Linux
export MM_DEBUG_MODALS=1
// Windows PowerShell
$env:MM_DEBUG_MODALS = 1
```
- URL View
- You can open this one by adding a line in the `main/viewManager.ts` file. In the function `showURLView`, at the end, add:
```js
urlView.webContents.openDevTools({mode: 'detach'});
```
<Note title="Note">
This view is ephemeral and based on whether a link is hovered with the mouse, so it might be best to use some logging instead here.
</Note>
## Debug the Mattermost Server/webapp
Some issues are only reproducible on the Desktop App, though the code that is causing the issue may not live in the Desktop App.
Here are some ways of determining whether this is true:
- Does the issue reproduce on the browser? Specifically Chrome?
- Does the issue surround a piece of code on the server/webapp that only applies to the Desktop App? You can check this by seeing if there is a call to `isDesktopApp` in the webapp.
If you have determined that the issue doesn't apply to the Desktop App code base directly, you can file a ticket in the appropriate repository, such as the [server and web app](https://github.com/mattermost/mattermost) repository.
If you are having trouble determining where the issue lies, feel free to post in the [Developers: Desktop App](https://community.mattermost.com/core/channels/desktop-app) on Mattermost Community, or you can file a ticket in the [server and web app](https://github.com/mattermost/mattermost) repository and it will be triaged and transferred to the appropriate location.

View File

@@ -0,0 +1,33 @@
---
title: "Dependencies"
sidebar_position: 3
---
The Desktop App uses `npm` to manage its dependencies.
We usually try to keep each major dependency version locked such that we don't accidentally introduce any bugs or breaking changes by upgrading.
All dependencies are locked using a `package-lock.json` file to ensure that we don't change the package versions used to build unless explicitly upgrading the package. Thus if a PR contains changes to `package-lock.json` without explicitly changing a dependency, we will usually ask the contributor to revert those changes.
## Electron
The most important dependency for the Desktop App is Electron, and is usually the library that can have the most impact on how the Desktop App works. The Electron dependency also contains the Chromium driver.
Generally we try to use the **latest possible version** of Electron where applicable to ensure we have the latest security fixes and are using the latest possible version of Chromium to maintain compatibility with the Web App.
#### Upgrading
For **patch** releases of the Desktop App, we will generally upgrade Electron to the latest **patch version**.
For **major and minor version** releases of the Desktop App, we will upgrade to the latest **major version**.
* This will usually require QA testing to ensure that nothing has broken between versions.
#### Bug fixes
Sometimes, it's necessary to upgrade the Electron version in order to resolve a bug in the app caused by the framework. If this is the case, please change the dependency according to the above guidelines, and the PR will be merged and released as per the same guidelines.
## Other dependencies
We try to keep the majority of dependencies up-to-date as much as possible, with a few exceptions:
- **React:** We generally keep the same version of `react` in the Desktop App for as long as possible, unless an upgrade or a new feature is required. Since the Desktop App doesn't rely too heavily on `react`, it's better for us to avoid introducing potential breaking changes unless something urgently needs to change.
- **Webpack:** Upgrading `webpack` requires us to change our configuration significantly, so we generally keep it the same unless we need to make a change.

View File

@@ -0,0 +1,103 @@
---
title: "Developer setup"
sidebar_position: 1
---
Set up your development environment for building, running, and testing the Mattermost Desktop App.
## Dependencies
<div class="tab">
<button class="tablinks active" onclick="openTab(event, 'mac')">macOS</button>
<button class="tablinks" onclick="openTab(event, 'windows')">Windows</button>
<button class="tablinks" onclick="openTab(event, 'ubuntu')">Ubuntu</button>
<button class="tablinks" onclick="openTab(event, 'archlinux')">Arch Linux</button>
<button class="tablinks" onclick="openTab(event, 'redhat')">Fedora/RedHat/CentOS</button>
</div>
<div id="mac" class="tabcontent" style={{display: 'block'}}>
<Note title="Section moved">
This section's content used to be transcluded from `contribute/more-info/desktop/developer-setup/macos.md` via Hugo's `{/* TODO: unconverted Hugo shortcode {{% content %}} (sources/mattermost-developer-documentation/site/content/contribute/more-info/desktop/developer-setup.md) */}` shortcode. In the new IA each include is its own page — see the sidebar.
</Note>
</div>
<div id="ubuntu" class="tabcontent">
<Note title="Section moved">
This section's content used to be transcluded from `contribute/more-info/desktop/developer-setup/ubuntu.md` via Hugo's `{/* TODO: unconverted Hugo shortcode {{% content %}} (sources/mattermost-developer-documentation/site/content/contribute/more-info/desktop/developer-setup.md) */}` shortcode. In the new IA each include is its own page — see the sidebar.
</Note>
</div>
<div id="windows" class="tabcontent">
<Note title="Section moved">
This section's content used to be transcluded from `contribute/more-info/desktop/developer-setup/windows.md` via Hugo's `{/* TODO: unconverted Hugo shortcode {{% content %}} (sources/mattermost-developer-documentation/site/content/contribute/more-info/desktop/developer-setup.md) */}` shortcode. In the new IA each include is its own page — see the sidebar.
</Note>
</div>
<div id="archlinux" class="tabcontent">
<Note title="Section moved">
This section's content used to be transcluded from `contribute/more-info/desktop/developer-setup/arch.md` via Hugo's `{/* TODO: unconverted Hugo shortcode {{% content %}} (sources/mattermost-developer-documentation/site/content/contribute/more-info/desktop/developer-setup.md) */}` shortcode. In the new IA each include is its own page — see the sidebar.
</Note>
</div>
<div id="redhat" class="tabcontent">
<Note title="Section moved">
This section's content used to be transcluded from `contribute/more-info/desktop/developer-setup/redhat.md` via Hugo's `{/* TODO: unconverted Hugo shortcode {{% content %}} (sources/mattermost-developer-documentation/site/content/contribute/more-info/desktop/developer-setup.md) */}` shortcode. In the new IA each include is its own page — see the sidebar.
</Note>
</div>
#### Mattermost Server
To develop with the Desktop App, we recommend that you set up a Mattermost server specifically for this purpose. This lets you customize it as needed in cases where there are specific integration requirements needed for testing.
You can find information on setting that up here:
[Developer Setup](/developers/contribute/developer-setup)
Alternatively, for some changes you may be able to test using an existing Mattermost instance, or one that has been deployed on platforms like Docker, Linux, Kubernetes, Heroku, or others. Please refer to the [Mattermost Deployment Guide](https://docs.mattermost.com/guides/deployment.html) for more info.
## Repo setup
1. Fork GitHub Repository: https://github.com/mattermost/desktop
2. Clone from your repo:
```sh
git clone https://github.com/<YOUR_GITHUB_USERNAME>/desktop.git
```
3. Open the desktop directory
```sh
cd desktop
```
4. Install Node Modules
```sh
npm i
```
5. Run the application
```sh
npm run watch
```

View File

@@ -0,0 +1,34 @@
---
---
**NOTE:** We don't officially support Arch Linux for use with the Mattermost Desktop App. The provided guide is unofficial.
1. Open a terminal
2. Install nvm via
1. [nvm-sh](https://github.com/nvm-sh/nvm#installing-and-updating):
```sh
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
```
OR
2. [AUR](https://aur.archlinux.org/) (possibly using [a helper](https://wiki.archlinux.org/title/AUR_helpers)):
```sh
yay -S nvm
```
4. Install NodeJS via
```sh
nvm install --lts
```
6. Install other dependencies:
Linux requires the X11 development libraries and `libpng` to build native Node modules.
Arch requires `libffi` since it's not installed by default.
```sh
sudo pacman -S npm git python3 gcc make libx11 libxtst libpng libffi
```
#### Notes
* To build RPMs, you need `rpmbuild`
```sh
sudo pacman -S rpm
```

View File

@@ -0,0 +1,17 @@
---
---
1. Install Homebrew: http://brew.sh
2. Open Terminal
3. Install dependencies
```sh
brew install git python3
```
4. Install [NVM](https://github.com/nvm-sh/nvm) by following [these instructions](https://github.com/nvm-sh/nvm#installing-and-updating).
After installing, follow the post-install steps shown by the installer to add the necessary lines to your shell profile (for example `~/.zshrc` or `~/.bash_profile`). Then open a new terminal and run:
```sh
nvm install --lts
```

View File

@@ -0,0 +1,26 @@
---
---
**NOTE:** We don't officially support Fedora/Red Hat/CentOS Linux for use with the Mattermost Desktop App. The provided guide is unofficial.
1. Open Terminal
2. Install NodeJS via [nvm](https://github.com/nvm-sh/nvm#installing-and-updating):
```sh
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
nvm install --lts
```
3. Install other dependencies:
Linux requires the X11 developement libraries and `libpng` to build native Node modules.
```sh
sudo yum install git python3 g++ libX11-devel libXtst-devel libpng-devel
```
#### Notes
* To build RPMs, you need `rpmbuild`:
```sh
sudo dnf install rpm-build
```

View File

@@ -0,0 +1,44 @@
---
---
1. Open Terminal
2. Install NodeJS from one of the following sources:
1. [nvm](https://github.com/nvm-sh/nvm#installing-and-updating):
```sh
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash
nvm install --lts
```
2. [NodeSource](https://github.com/nodesource/distributions#installation-instructions):
```sh
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_16.x nodistro main" | sudo tee /etc/apt/sources.list.d/nodesource.list
sudo apt-get update
sudo apt-get install -y nodejs
```
* You might need to install `curl` as well:
```sh
sudo apt install curl
```
3. Install other dependencies:
Linux requires the X11 developement libraries and `libpng` to build native Node modules.
```sh
sudo apt install git python3 make g++ libx11-dev libxtst-dev libpng-dev
```
#### Notes
* To build RPMs, you need `rpmbuild`:
```sh
sudo apt install rpm
```

View File

@@ -0,0 +1,15 @@
---
---
1. Install Chocolatey: https://chocolatey.org/install
2. Install Visual Studio Community: https://visualstudio.microsoft.com/vs/community/
- Include **Desktop development with C++** when installing
3. If you are on Windows 11, you may need to install `wmic` via the system settings > Optional Features.
4. Open PowerShell
5. Install dependencies
```sh
choco install nvm git python3
```
6. Restart PowerShell (to refresh the environment variables)
7. Run `nvm install lts` and `nvm use lts` to install and use the latest NodeJS LTS version.

View File

@@ -0,0 +1,23 @@
---
title: "Desktop app"
sidebar_position: 7
---
The Mattermost Desktop App is an [Electron](https://electronjs.org/) wrapper around the [web app](/developers/contribute/more-info/webapp) project. It lives in the [mattermost/desktop](https://github.com/mattermost/desktop) repository. The desktop app runs on Windows, Linux, and macOS.
## Desktop app contributor resources
- [GitHub Repository](https://github.com/mattermost/desktop) - Get the code, report issues, or submit PRs.
- [Help Wanted](https://mattermost.com/pl/help-wanted-desktop) - This is a good place to start if you're looking for a way to contribute code. Many issues are labeled by difficulty level to make it easier to find ways to get involved.
- [Developer Setup](/developers/contribute/more-info/desktop/developer-setup) - Setup your development environment to start work on the desktop app.
- [Build and CLI Commands](/developers/contribute/more-info/desktop/build-commands) - Useful commands to help build, debug, test, and modify the desktop app on your local machine.
- [Debugging](/developers/contribute/more-info/desktop/debugging) - Identify issues in the desktop app and debug the rendering process.
- [Dependencies](/developers/contribute/more-info/desktop/dependencies) - Information about including dependencies in the desktop app.
- [Style and Code Quality](/developers/contribute/more-info/desktop/style-and-code-quality) - Information about linting, type checking, and submitting great PRs
- [Unit and End-to-End (E2E) Tests](/developers/contribute/more-info/desktop/testing) - Find out how we incorporate unit and end-to-end testing into the desktop app development process.
- [Packaging for Release](/developers/contribute/more-info/desktop/packaging-and-releasing) - Build and package the app into a distributable version.
- [General Contributor Guidelines](/developers/contribute/more-info/getting-started) - Everything you need to know about contributing code to Mattermost.
## Where to get help
If you have any questions related to development of the Desktop App, you can ask us in the [Developers: Desktop App](https://community.mattermost.com/core/channels/desktop-app) channel on our [community Mattermost](https://docs.mattermost.com/guides/community-chat.html). If you need help deploying, administering, or using Mattermost, refer to our [Get Help guide](https://docs.mattermost.com/guides/get-help.html) to find all of the resources that are availalbe to support your journey.

View File

@@ -0,0 +1,79 @@
---
title: "Package and release"
sidebar_position: 4
---
## Build
You can build the Desktop App by running the following command:
```text
npm run build
```
You can build the Desktop App for development and watch for changes in the main process with this command:
```text
npm run watch
```
Our application uses `webpack` to bundle the scripts together for the main and renderer process.
There are bundles generated for each page used the renderer process, and one bundle for the main process.
A bundle is also generated for the E2E tests when needed.
You can predefine certain variables in the app before building, by editing the build config under `src/common/config/buildConfig.ts`. For example, you can predefine servers, or disable server management.
## Package
Our app uses `electron-builder` to package the app into a distributable format for release.
You can find the configuration for the builder in the `electron-builder.json` file in the root directory.
You can run the packager using this command:
```text
npm run package:<os>
```
where **\&lt;os\&gt;** is one of the following values: `windows, mac, mac-with-universal, mas, linux`
All of the above values will generate builds for `x64` and `arm64` architectures:
- `windows`: `exe`, `zip` and `msi` formats
- `mac`: `dmg` and `zip` formats
- `mac-with-universal`: universal binary for all architectures - `dmg`
- `mas`: universal Mac App Store-compliant build
- `linux`: `deb`, `rpm` and `tar.gz` formats
You can build for more specific targets using the commands [here](/developers/contribute/more-info/desktop/build-commands#packaging)
#### After pack script
We include an `afterPack` script to run functions after the application is built into a binary. This is a good place to inject code and make any modifications to the binary after build.
#### Code sign
In order to generate signed builds of the application for Windows and macOS, you'll need a certificate file for each of the operating systems.
These files are under control of Mattermost and aren't generally distributed, but you can obtain your own certificate and sign the app yourself if necessary.
For macOS, you'll need a valid `Mac Developer` or `Developer ID Application` certificate from the Apple Developer Program.
For Windows, you'll need a valid code signing certificate.
More information on Code Signing can be found here: https://www.electron.build/code-signing
## Release
Releasing a new version of the Desktop App can be done by running the shell script `release.sh` under the `scripts/` folder.
It will increment the version number in `package.json` for you, create a tag and generate a commit for you. It will also give you the `git` command to run to push all these changes to your repository.
It has the following options:
```
// generates a patch version release candidate, will increment x of v0.0.x (so v5.0.1 becomes v5.0.2-rc1)
$ ./scripts/release.sh patch
// generates a release candidate version, on top of a current release candidate (so v5.0.2-rc1 becomes v5.0.2-rc2)
$ ./scripts/release.sh rc
// generates a final version, on top of a current release candidate (so v5.0.2-rc2 becomes v5.0.2)
$ ./scripts/release.sh final
```

View File

@@ -0,0 +1,33 @@
---
title: "Style and code quality"
sidebar_position: 3
---
We run automated style and type-checking against every new PR that is created and the new code must pass before it can be merged.
In some rare cases you can override these, but this is strongly discouraged.
#### Linter
We make use of `eslint` to enforce good coding style in the Desktop App.
You can run the linter using the following command:
```text
npm run lint:js
```
Outside of the linter, we generally allow for a loose coding style, although the reviewer of the PR has the final say.
#### Type checker
We make use of TypeScript in our application to help reduce errors when coding.
You can run the type checker by running the following command:
```text
npm run check-types
```
#### Submitting great PRs
Jesse Hallam has written an excellent blog post entitled "Submitting Great PRs" that can be found [here](https://mattermost.com/blog/submitting-great-prs/)

View File

@@ -0,0 +1,33 @@
---
title: "Unit and End-to-End (E2E) Tests"
sidebar_position: 4
---
For most changes that happen to the desktop app, consider writing an automated test to ensure that the change or fix is maintained in the codebase. Depending on the nature of the change, you will write either a unit test or an E2E test.
### Unit tests
The [Jest](https://jestjs.io/en/) test runner is used to run unit tests in the desktop app. You can run the following command to run the tests: `npm run test:unit`. You can also run subsets of the tests by filtering using `testNamePattern` or `testPathPattern` on the `spec` files.
Unit tests are usually written for parts of the `common` and `main` modules, and usually cover individual functions or classes.
We should endeavor to write our code such that it allows for simple testing, and any new features or bug fixes should likely have an associated unit test if possible. Check out [\[MM-40146\]\[MM-40147\] Unit tests for authManager and certificateManager #1874](https://github.com/mattermost/desktop/pull/1874), which is an example of a unit test pull request (PR).
In order to ensure that most of the app is covered, we try to maintain 70% coverage of the `common` and `main` modules.
You can view a coverage map by running this command: `npm run test:coverage`.
### E2E tests
We use a combination of two technologies to facilitate E2E testing in the desktop app:
- **[Playwright](https://playwright.dev/):** A testing framework similar to Cypress or Selenium that acts as a Chromium driver for testing. It's used to simulate interactions with the various web environments that make up the Desktop App, including the top bar (servers and tabs) and the individual Mattermost views.
- **[RobotJS](https://robotjs.io/):** A multi-platform OS level automation framework written in NodeJS, used for simulating arbitrary keyboard and mouse inputs. It's generally used to mock actions involving keyboard shortcuts and the Electron menu, as those are not web environments.
To build the app and run the E2E tests, you can run the following command: `npm run test:e2e`. You can also run this command to build the tests without rebuilding the app: `npm run test:e2e:nobuild`. You can also run subsets of the tests by filtering using `grep`, for example: `npm run test:e2e:run -- --grep back_button`.
E2E tests are usually written to cover parts of the `renderer` module and should generally cover complete workflows, such as creating and editing a server. You will generally need a combination of both Playwright and RobotJS APIs to test most workflows.
An example of an E2E test PR is [ \[MM-39680\] E2E Test for Deep Linking #1843](https://github.com/mattermost/desktop/pull/1843).
#### Notes
There are many interactions (i.e. things that integrate with the operating system), such as notifications, that cannot be adequately tested using the automation frameworks we have. If this is the case, we will generally create a script to test in [Rainforest](https://handbook.mattermost.com/operations/research-and-development/quality/rainforest-process), our crowd-sourced QA platform to perform these tests manually.
Check out the page on [web app unit testing](/developers/contribute/more-info/webapp/unit-testing) to see more of Jest in action. For other CLI commands related to testing, go to [Build and CLI commands](/developers/contribute/more-info/desktop/build-commands).