feat(docs): move docs-experimental content into monorepo docs/ (#37330)

* feat(docs): move docs-experimental content into monorepo docs/

Co-authored-by: Cursor <cursoragent@cursor.com>

* Remove plan related files and comments

* Cleanup comments, remove generated files

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Eva Sarafianou
2026-07-02 18:42:57 +03:00
committed by GitHub
parent 6d2f843ede
commit 1d3bbc638b
1410 changed files with 155267 additions and 0 deletions
+5
View File
@@ -39,3 +39,8 @@ insert_final_newline = false
[*.yml,*.yaml]
indent_style = space
indent_size = 2
[*.mdx]
indent_style = space
indent_size = 2
trim_trailing_whitespace = false
+8
View File
@@ -179,3 +179,11 @@ docker-compose.override.yaml
server/prev-report.xml
server/prev-gotestsum.json
server/shard-*.txt
# Docusaurus docs site (docs/)
docs/site/build/
docs/site/.docusaurus/
docs/site/node_modules/
docs/api/reference/
docs/pdf/build/
docs/pdf/node_modules/
+20
View File
@@ -0,0 +1,20 @@
# Vale configuration for Mattermost docs.
# See PLAN.md §7 for the full style enforcement strategy.
#
# Run locally: vale docs/ develop/ api/
# CI: vale --output=line --no-exit docs/ develop/ api/
StylesPath = styles
# Vale's prebuilt vocabularies. We don't load any of the wholesale
# packages (write-good, proselint) by default — they generate too much
# noise on technical content. Add them per-rule if needed.
MinAlertLevel = suggestion
# Lint MDX, MD, RST. .docusaurus, build, and node_modules are excluded.
[*.{md,mdx}]
BasedOnStyles = Mattermost
TokenIgnores = (\$\$.*?\$\$), \\\(.*?\\\)
[*.rst]
BasedOnStyles = Mattermost
+84
View File
@@ -0,0 +1,84 @@
---
id: examples
title: Quick-start with curl
description: Copy-pasteable curl quick-start for the most common Mattermost API operations.
sidebar_position: 2
sidebar_label: Examples
---
<Eyebrow>Examples</Eyebrow>
Copy-pasteable requests for the most common operations. Replace `your-mattermost-server.com` with your server URL and `<TOKEN>` with a valid personal access token.
## Log in and get a session token
```bash
curl -i -X POST https://your-mattermost-server.com/api/v4/users/login \
-H 'Content-Type: application/json' \
-d '{
"login_id": "user@example.com",
"password": "yourpassword"
}'
```
The session token is returned in the `Token` response header.
## Post a message to a channel
```bash
curl -X POST https://your-mattermost-server.com/api/v4/posts \
-H 'Authorization: Bearer <TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"channel_id": "<CHANNEL_ID>",
"message": "Status update from API"
}'
```
## Create a channel
```bash
curl -X POST https://your-mattermost-server.com/api/v4/channels \
-H 'Authorization: Bearer <TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"team_id": "<TEAM_ID>",
"name": "ops-incident-2026-05",
"display_name": "Ops Incident — 2026-05",
"type": "O"
}'
```
## Add a user to a channel
```bash
curl -X POST https://your-mattermost-server.com/api/v4/channels/<CHANNEL_ID>/members \
-H 'Authorization: Bearer <TOKEN>' \
-H 'Content-Type: application/json' \
-d '{ "user_id": "<USER_ID>" }'
```
## Upload a file
```bash
curl -X POST https://your-mattermost-server.com/api/v4/files \
-H 'Authorization: Bearer <TOKEN>' \
-F 'channel_id=<CHANNEL_ID>' \
-F 'files=@./report.pdf'
```
## Trigger a slash command
```bash
curl -X POST https://your-mattermost-server.com/api/v4/commands/execute \
-H 'Authorization: Bearer <TOKEN>' \
-H 'Content-Type: application/json' \
-d '{
"channel_id": "<CHANNEL_ID>",
"command": "/echo Hello from the API"
}'
```
<Tip>
Every endpoint page in the **[Reference](/api/reference/login)** sidebar includes the equivalent samples in **curl, PowerShell, Python, Node, and Go** — selectable via the language tab strip on the right side of the page.
</Tip>
+107
View File
@@ -0,0 +1,107 @@
---
slug: /
title: Mattermost API v4
description: REST API v4 — first-class per-endpoint pages generated from the canonical OpenAPI specification.
sidebar_position: 1
sidebar_label: Overview
---
<Eyebrow>API Reference</Eyebrow>
Every endpoint is a first-class, deep-linkable page generated directly from the canonical OpenAPI spec. Per-endpoint code samples in **curl, PowerShell, Python, Node, and Go**.
<StatStrip stats={[
{value: '549', label: 'Endpoints'},
{value: '38', label: 'Resource groups'},
{value: '5', label: 'Languages', hint: 'curl · PowerShell · Python · Node · Go'},
{value: 'v4', label: 'API version', hint: 'Stable, semver-tracked'}
]} />
<MethodLegend />
## Start here
<CardGrid columns={3} cards={[
{
title: 'Examples',
icon: 'channels',
to: '/api/examples',
description: 'Copy-pasteable curl quick-start for the most common operations: log in, post a message, create a channel, upload a file.',
meta: '6 quick recipes'
},
{
title: 'Authentication',
icon: 'security',
to: '/api/reference/login',
description: 'Log in flows, personal access tokens, session tokens, MFA, SSO. Required for almost every endpoint.',
meta: 'Login + tokens'
},
{
title: 'Reference',
icon: 'boards',
to: '/api/reference/login',
description: 'Every endpoint, grouped by resource: Users, Channels, Posts, Teams, Files, Webhooks, and more.',
meta: '38 groups · 549 endpoints'
}
]} />
## Common workflows
<CardGrid columns={2} cards={[
{
title: 'Channels',
icon: 'channels',
to: '/api/reference/channels',
description: 'Create, list, archive, and manage channels. Add and remove members, change channel type, set headers and purposes.'
},
{
title: 'Posts',
icon: 'channels',
to: '/api/reference/posts',
description: 'Post, edit, delete, pin, react. Search, fetch threads, retrieve flagged posts.'
},
{
title: 'Users',
icon: 'channels',
to: '/api/reference/users',
description: 'Search, fetch, create, deactivate users. Manage roles, sessions, MFA, profile images.'
},
{
title: 'Webhooks',
icon: 'channels',
to: '/api/reference/webhooks',
description: 'Incoming and outgoing webhooks for integrations and ChatOps.'
}
]} />
## How it's generated
The canonical source is the OpenAPI specification in `mattermost/mattermost/api/v4/source/` (56 YAML fragments, one per resource). The build pipeline:
```bash
node docs-site/scripts/build-openapi.mjs # bundle 56 fragments → one YAML, 435 paths, 550 ops
npx docusaurus gen-api-docs all # emit ~589 MDX files (one per endpoint + tag pages)
```
When the upstream spec changes, this regenerates automatically. AI never authors API reference content — it's deterministic from the spec.
## Looking for something else?
<CardGrid columns={2} cards={[
{
title: 'Developers',
icon: 'channels',
to: '/developers',
description: 'Building plugins, integrations, slash commands, OAuth apps? Start here.'
},
{
title: 'Documentation',
icon: 'channels',
to: '/',
description: 'Operating, configuring, or using Mattermost? Admin and end-user guides.'
}
]} />
<Note>
The legacy ReDoc-based reference at `developers.mattermost.com/api-documentation` is being decommissioned as part of the cutover (see PLAN.md §11.1 step 10).
</Note>
@@ -0,0 +1,133 @@
---
title: "Docker Services"
sidebar_position: 1
---
By default, only a small number of required Docker services are started to support basic development:
```
ENABLED_DOCKER_SERVICES="postgres mysql inbucket"
```
But there are many additional services ready to work with your local environment. Note that some services will require a Mattermost Enterprise license.
```
ENABLED_DOCKER_SERVICES="postgres mysql inbucket minio openldap dejavu keycloak elasticsearch grafana prometheus promtail loki"
```
To customize which services are started, either export the above environment variable or copy [`config.mk`](https://github.com/mattermost/mattermost/blob/master/server/config.mk) as `config.override.mk` to tune appropriately.
## postgres
From https://www.postgresql.org/:
> The official site for PostgreSQL, the world's most advanced open source database.
This is the default and recommended database to use with Mattermost. No additional configuration should be required, but the following settings apply to a Mattermost instance using Postgres:
```
MM_SQLSETTINGS_DRIVERNAME=postgres
MM_SQLSETTINGS_DATASOURCE=postgres://mmuser:mostest@localhost:5432/mattermost_test?sslmode=disable\u0026connect_timeout=10
```
## mysql
From https://dev.mysql.com/doc/refman/8.3/en/introduction.html:
> The MySQL software delivers a very fast, multithreaded, multi-user, and robust SQL (Structured Query Language) database server.
This is an alternate database supported by Mattermost, but not recommended for new deployments.
To use with Mattermost, be sure to configure the following settings:
```
MM_SQLSETTINGS_DRIVERNAME=mysql
MM_SQLSETTINGS_DATASOURCE=mmuser:mostest@tcp(localhost:3306)/mattermost_test?charset=utf8mb4,utf8\u0026readTimeout=30s\u0026writeTimeout=30s
```
## inbucket
From https://inbucket.org/about/:
> Inbucket is an email testing application; it will accept messages for any email address and make them available to view via a web interface. If you've ever used mailinator.com, you already have a good idea of what Inbucket does. The benefit of Inbucket is that it is an application instead of a hosted service; you may run it on your own private network, or even your desktop.
Use this during development to "receive" email confirmations, password resets, or message notifications.
To use with Mattermost, be sure to configure the following settings:
```
MM_EMAILSETTINGS_ENABLESMTPAUTH=false
MM_EMAILSETTINGS_SMTPUSERNAME=
MM_EMAILSETTINGS_SMTPPASSWORD=
MM_EMAILSETTINGS_SMTPSERVER=localhost
MM_EMAILSETTINGS_SMTPPORT=10025
```
When running, access the web interface at [http://localhost:9001/](http://localhost:9001/).
![inbucket](/img/docker/inbucket.png)
## grafana
From https://grafana.com/docs/
> Collect, correlate, and visualize data with beautiful dashboards using our open source data visualization and monitoring solution.
Grafana is where all the metrics and logs collected by Prometheus, Loki and promtail come together. Panels visualize the data and are grouped into dashboards. The home dashboard links out to various performance dashboards, lists which Docker services are currently online, has quick links to various filtered log views, and panels showing the most recent Mattermost and Docker container logs.
When running, access the web interface at [http://localhost:3000](http://localhost:3000).
![grafana](/img/docker/grafana.png)
## prometheus
From https://prometheus.io/docs/introduction/overview/:
> Prometheus collects and stores its metrics as time series data, i.e. metrics information is stored with the timestamp at which it was recorded, alongside optional key-value pairs called labels.
Mattermost exposes metrics at [http://localhost:8067/metrics](http://localhost:8067/metrics) which are scraped periodically by Prometheus to form a time series database. While you can access Prometheus directly to view and graph this collected data, typically this is used in tandem with Grafana for a rich dashboard experience.
To use with Mattermost, be sure to install a Mattermost enterprise license and configure the following settings:
```
MM_METRICSSETTINGS_ENABLE=true
```
When running, access the web interface at [http://localhost:9090/](http://localhost:9090).
![prometheus](/img/docker/prometheus.png)
## promtail
From https://grafana.com/docs/loki/latest/send-data/promtail/:
> Promtail is an agent which ships the contents of local logs to a private Grafana Loki instance or Grafana Cloud. It is usually deployed to every machine that runs applications which need to be monitored.
To use with Mattermost, be sure to enable file logs, with the containing directory automatically mounted as a volume for promtail to scrape and relay to Loki. Promtail is automatically configured to scrape all Docker container logs for use with Loki and Grafana.
```
MM_LOGSETTINGS_ENABLEFILE=true
MM_LOGSETTINGS_FILELEVEL=debug
MM_LOGSETTINGS_FILEJSON=true
MM_LOGSETTINGS_FILELOCATION=logs
```
## loki
From https://grafana.com/oss/loki/:
> Loki is a log aggregation system designed to store and query logs from all your applications and infrastructure.
Just as Prometheus is for metrics, think of Loki being for logs. Combined with promtail scraping the logs from Mattermost and all these supporting Docker containers, and Grafana for the frontend, Loki effectively provides a powerful user interface for slicing and dicing your developer logs.
## keycloak
From https://www.keycloak.org/documentation:
> Keycloak is an open source identity and access management solution.
Keycloak can be used as a SAML identity provider with your local setup. See the setup instructions [here](https://github.com/mattermost/mattermost/blob/master/server/build/docker/keycloak/README.md).
# Other Docker services
Other Docker services supported by the development environment include:
* minio
* openldap
* dejavu
* elasticsearch
@@ -0,0 +1,234 @@
---
title: "Developer setup"
sidebar_position: 1
---
Set up your development environment for building, running, and testing Mattermost.
<Note title="Note">
- If you're migrating from before the monorepo see the [migration notes](/developers/contribute/monorepo-migration-notes).
- If you're developing plugins, see the plugin [developer setup](/developers/integrate/plugins/developer-setup) documentation.
- If you are forking Mattermost to create a derivative version, you must comply with the AGPLv2 license in both source code and compiled versions and replace the Mattermost name and logo from the system, among other requirements, per the [Mattermost trademark policy](https://mattermost.com/trademark-standards-of-use/).
</Note>
# Prerequisites for Windows
If you're using Windows, we recommend using the Windows Subsystem for Linux (WSL) for Mattermost development. Go and Node must be run from within WSL, so you'll need to install them in WSL even if you already have the Windows versions of them installed.
1. [Install WSL](https://learn.microsoft.com/en-us/windows/wsl/install) by running the following command as an administrator in PowerShell: `wsl --install`
2. [Install Docker Desktop for Windows](https://learn.microsoft.com/en-us/windows/wsl/tutorials/wsl-containers#install-docker-desktop) on your Windows machine. Alternatively, you can also [install](https://docs.docker.com/engine/install/) docker engine directly on your linux distribution.
3. Perform the rest of the operations (except Docker installation) within the WSL environment and not in Windows.
# Setup the Mattermost Server
<Note title="Note">
The web app isn't exposed directly, it's exposed via the server. So if both server and web app are running, you can open `localhost:8065`, the server's port to access the web app.
</Note>
1. Install `make`.
- On Ubuntu, you can install `build essential` tools which will also take care of installing the `make`:
```sh
sudo apt install build-essential
```
1. Install and run [Docker](https://www.docker.com/). If you don't want to use Docker, you can follow [this guide](#develop-mattermost-without-docker).
- When running `docker` commands under WSL2, if you receive the error `The command 'docker' could not be found in this WSL 2 distro.` you may need to toggle the `Use the WSL 2 based engine` off and on within Docker Settings after installation.
- Make sure that Docker has virtual file share access to the directory that you will clone the repository in
1. Install [Go](https://go.dev/).
- Version 1.21 or higher is required.
1. Increase the number of available file descriptors. Update your shell's initialization script (e.g. `.bashrc` or `.zshrc`), and add the following:
```sh
ulimit -n 8096
```
1. If you don't have it already, install libpng with your preferred package manager.
- If you are on ARM based Mac, you'll need to install [Rosetta](https://support.apple.com/en-in/HT211861) to make `libpng` work. Rosetta can be installed by the following command-
```sh
softwareupdate --install-rosetta
```
1. Fork https://github.com/mattermost/mattermost.
1. Clone the Mattermost source code from your fork:
```sh
git clone https://github.com/YOUR_GITHUB_USERNAME/mattermost.git
```
1. Install NVM and use it to install the required version of Node.js:
1. Install [NVM](https://github.com/nvm-sh/nvm) by following [these instructions](https://github.com/nvm-sh/nvm#installing-and-updating).
1. Then, use NVM to install the correct version of Node.js for the Mattermost web app (this should be run within the `webapp` directory):
```sh
nvm install
```
1. Start the server:
```sh
cd server
make run-server
```
1. Test your environment to ensure that the server is running:
```sh
curl http://localhost:8065/api/v4/system/ping
```
If successful, the `curl` step will return a JSON object:
```json
{"AndroidLatestVersion":"","AndroidMinVersion":"","DesktopLatestVersion":"","DesktopMinVersion":"","IosLatestVersion":"","IosMinVersion":"","status":"OK"}
```
1. Set up up your admin user using mmctl:
```sh
bin/mmctl user create --local --email ADMIN_EMAIL --username ADMIN_USERNAME --password ADMIN_PASSWORD --system-admin
```
- Optionally, you can also populate the database with random sample data as well:
```sh
bin/mmctl sampledata
```
1. Start the web app:
```sh
cd webapp
make run
```
1. Open the web app by going to http://localhost:8065 in your browser or by adding it to the Mattermost desktop app.
1. Stop the server:
```sh
make stop-server
```
The `stop-server` make target does not stop all the docker containers started by `run-server`. To stop the running docker containers:
```sh
make stop-docker
```
1. Set your options:
Some behaviors can be customized such as running the server in the foreground as described in the `config.mk` file in the server directory. See that file for details.
# Build the Mattermost Server
The `make package` command will package the application and place it under the `./dist` directory. You can distribute the .tar.gz file if you wish the run the application elsewhere. Note that you would need to run `make build` before this to build the binaries.
# Develop Mattermost without Docker
1. Install `make`.
- On Ubuntu, you can install `build essential` tools which will also take care of installing the `make`:
```sh
sudo apt install build-essential
```
1. Copy the file `server/config.mk` as `server/config.override.mk` and set `MM_NO_DOCKER` to `true` in the copy.
1. Install [PostgreSQL](https://www.postgresql.org/download/)
1. Run `psql postgres`. Then create `mmuser` by running `CREATE ROLE mmuser WITH LOGIN PASSWORD 'mostest';`
1. Modify the role to give rights to create a database by running `ALTER ROLE mmuser CREATEDB;`
1. Confirm the role rights by running `\du`
1. Before creating the database, exit by running `\q`
1. Login again via `mmuser` by running `psql postgres -U mmuser`
1. Create the database by running `CREATE DATABASE mattermost_test;` and exit again with `\q`
1. Login again with `psql postgres` and run `GRANT ALL PRIVILEGES ON DATABASE mattermost_test TO mmuser;` to give all rights to `mmuser`
1. Install [Go](https://go.dev/).
1. Increase the number of available file descriptors. Update your shell's initialization script (e.g. `.bashrc` or `.zshrc`), and add the following:
```sh
ulimit -n 8096
```
1. If you don't have it already, install libpng with your preferred package manager.
- If you are on ARM based Mac, you'll need to install [Rosetta](https://support.apple.com/en-in/HT211861) to make `libpng` work. Rosetta can be installed by the following command-
```sh
softwareupdate --install-rosetta
```
1. Fork https://github.com/mattermost/mattermost.
1. Clone the Mattermost source code from your fork:
```sh
git clone https://github.com/YOUR_GITHUB_USERNAME/mattermost.git
cd mattermost
```
1. Install NVM and use it to install the required version of Node.js:
- First, install [NVM](https://github.com/nvm-sh/nvm) by following [these instructions](https://github.com/nvm-sh/nvm#installing-and-updating).
- Then, use NVM to install the correct version of Node.js for the Mattermost web app (this should be run within the `webapp` directory):
```sh
cd webapp
nvm install
cd ..
```
- NOTE: If you get `zsh: command not found: nvm` when running `nvm install`, you will need to add the following to your ~/.zshrc file:
```zsh
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
```
1. Start the server:
```sh
cd server
make run-server
```
1. Test your environment to ensure that the server is running by running the following in a different terminal session:
```sh
curl -s http://localhost:8065/api/v4/system/ping | jq .
```
If successful, the `curl` step will return a JSON object:
```json
{"AndroidLatestVersion":"","AndroidMinVersion":"","DesktopLatestVersion":"","DesktopMinVersion":"","IosLatestVersion":"","IosMinVersion":"","status":"OK"}
```
Alternately, you can enter `http://localhost:8065/api/v4/system/ping` in a web browser.
1. Set up up your admin user using mmctl:
```sh
bin/mmctl user create --local --email ADMIN_EMAIL --username ADMIN_USERNAME --password ADMIN_PASSWORD --system-admin
```
- Note: `ADMIN_PASSWORD` must be 8 characters or more.
- Optionally, you can also populate the database with random sample data as well:
```sh
bin/mmctl --local sampledata
```
1. Start the web app (in another terminal window):
```sh
cd PATH_TO_MATTERMOST_REPO/webapp
make run
```
1. Open the web app by going to http://localhost:8065 in your browser or by adding it to the Mattermost desktop app.
1. Stop the server:
```sh
cd PATH_TO_MATTERMOST_REPO/server
make stop-server
```
1. Set your options:
Some behaviors can be customized such as running the server in the foreground as described in the `config.mk` file in the server directory. See that file for details.
@@ -0,0 +1,83 @@
---
title: "Contributor expectations"
sidebar_position: 3
---
To contribute to Mattermost, you must sign the [Contributor License Agreement](https://mattermost.com/mattermost-contributor-agreement/). Doing so adds you to our list of [Mattermost Approved Contributors](https://docs.google.com/spreadsheets/d/1NTCeG-iL_VS9bFqtmHSfwETo5f-8MQ7oMDE5IUYJi_Y/pubhtml?gid=0&single=true).
Please also read our [community expectations](/developers/contribute/good-decisions/) and note that we all abide by the [Mattermost Code of Conduct (CoC)](https://handbook.mattermost.com/contributors/contributors/guidelines/contribution-guidelines), and by joining our contributor community, you agree to abide by it as well.
<Note title="Tip">
Love swag? If you choose to provide us with your mailing address in the signed agreement, you'll receive a [Limited Edition Mattermost Mug](https://forum.mattermost.com/t/limited-edition-mattermost-mugs/143) as a thank you gift after your first pull request is merged.
</Note>
## Before contributing
There are many ways to contribute to Mattermost beyond a core Mattermost repository:
- You can create lightweight external applications that dont require customizations to the Mattermost user experience by using [incoming](/developers/integrate/webhooks/incoming) and [outgoing](/developers/integrate/webhooks/outgoing) webhooks, or by using [the Mattermost API](https://api.mattermost.com/).
- You can activate external functionality within Mattermost by creating custom [slash commands](/developers/integrate/slash-commands/).
- You can extend, modify, and deeply integrate with the Mattermost server and its UI/UX by using [plugins](/developers/integrate/plugins/). However, please note that plugin development comes with the highest level of overhead and must be written in Go and React.
- You can use Mattermost from other applications, by [embedding and launching](/developers/integrate/customization/embedding/) Mattermost within other applications and mobile apps.
To get started:
1. Identify which repository you need to work in (see point below), then review the README located within the root of the repository to learn more about getting started with your contribution and any processes that may be unique to that repository.
These are the Mattermost Core repositories you can contribute to:
- [Server](/developers/contribute/more-info/server/): Highly-scalable Mattermost server written in Go.
- [Web App](/developers/contribute/more-info/webapp/): JavaScript client app built on React and Redux.
- [Mobile Apps](/developers/contribute/more-info/mobile/): JavaScript client apps for Android and iOS built on React Native.
- [Desktop App](/developers/contribute/more-info/desktop/): An Electron wrapper around the web app project that runs on Windows, Linux, and macOS.
- [Core Plugins](/developers/contribute/more-info/plugins/): A core set of officially-maintained plugins that provide a variety of improvements to Mattermost.
- [Boards](/developers/contribute/more-info/focalboard/) and [Playbooks](https://github.com/mattermost/mattermost-plugin-playbooks) core integrations.
2. To contribute to documentation, you should be able to edit any page and get to the source file in the documentation repository by selecting the **Edit on GitHub** button in the top right of its respective published page. You can read more about this process on the [why and how to contribute page](/developers/contribute/why-contribute/#you-want-to-help-with-content). You can contribute to the following Mattermost documentation sites:
- [Product documentation](https://github.com/mattermost/docs)
- [Developer documentation](https://github.com/mattermost/mattermost-developer-documentation)
- [API reference documentation](https://github.com/mattermost/mattermost-api-reference)
- [Handbook documentation](https://github.com/mattermost/mattermost-handbook)
## During the contribution process
1. Check in regularly with your Pull Request (PR) to review and respond to feedback.
2. Thoroughly document what youre doing in your PR. This way, future contributors can pick up on your work (including you!). This is especially helpful if you need to step back from a PR.
3. Each PR should represent a single project, both in code and in content. Keep unrelated tasks in separate PRs.
4. Make your PR titles and commit messages descriptive! Briefly describing the project in the PR title and in your commit messages often results in faster responses, less clarifying questions, and better feedback.
<Note title="Tip">
If you need to take a break from an assigned issue during, for example, the Hacktoberfest project, please commit any completed work to date in a PR, and note that you're stepping away in the issue itself. These two steps help ensure that your contributions are counted and outstanding work on a given ticket can be made available to other contributors.
</Note>
## Writing code
Thoroughly test your contributions! We recommend the following testing best practices for your contribution:
1. Detail exactly what you expect to happen in the product when others test your contributions.
2. Identify updates to existing [product](https://docs.mattermost.com/), [developer](https://developers.mattermost.com/), and/or [API](https://api.mattermost.com/) documentation based on your contributions, and identify documentation gaps for new features or functionality.
<Note title="Note">
Contributors and reviewers are strongly encouraged to work with the Mattermost Technical Writing team via the [Documentation Working Group channel](https://community.mattermost.com/core/channels/dwg-documentation-working-group) on the Mattermost Community Server before approving community contributions. See the Mattermost Handbook for additional details on [engaging the Mattermost Technical Writing team](https://handbook.mattermost.com/operations/research-and-development/product/technical-writing-team-handbook/work-with-us#how-to-engage-with-us), and for [submitting documentation with your PR](https://handbook.mattermost.com/operations/research-and-development/product/technical-writing-team-handbook/writing-community-documentation#submit-documentation-with-your-pr).
</Note>
3. If your PR adds a new plugin API method or hook, please add an example to the [Plugin Starter Template](https://github.com/mattermost/mattermost-plugin-starter-template).
4. If your code adds a new user interface string, include it in the proper localization file, either for [the server](https://github.com/mattermost/mattermost/blob/master/server/i18n/en.json), [the webapp](https://github.com/mattermost/mattermost/blob/master/webapp/channels/src/i18n/en.json), or [mobile](https://github.com/mattermost/mattermost-mobile/blob/master/assets/base/i18n/en.json).
<Note title="Note">
When working within the webapp repository, additionally run `make i18n-extract` from a terminal to update the list of product strings with your changes.
</Note>
# Writing content
Always consider who will consume your content, and write directly to your target audience.
Write clearly and be concise. Write informally, in the present tense, and address the reader directly. See our [voice, tone, and writing style guidelines](https://handbook.mattermost.com/operations/operations/company-processes/publishing/publishing-guidelines/voice-tone-and-writing-style-guidelines), and the [Mattermost Documentation Style Guide](https://handbook.mattermost.com/operations/operations/company-processes/publishing/publishing-guidelines/voice-tone-and-writing-style-guidelines) for details on general writing principles, syntax used to format content, and common terms used to describe product functionality.
@@ -0,0 +1,38 @@
---
title: "When a merged PR results in a bug"
sidebar_position: 2
---
This page describes the process to follow when someone notices a mistake in a merged pull request (PR).
1. A contributor (either staff or community member) submits a PR, it is reviewed and merged into the codebase.
2. Sometime later, the community notices a mistake with the PR.
Question is, what should we, as a community, do? That depends on the scope of the changes in the PR that was merged.
## Low impact issues
A low impact PR might mean that it affected:
- Some non-critical functionality.
- It doesn't affect users in a substantial way.
If this is the case, do the following:
1. Capture details in an issue.
2. Mark it according to its priority.
3. Would be best to assign it to the person who introduced the issue in the first place.
## High impact issues
A high impact PR represents something that has or will result in a customer incident.
If this is the case, there are two scenarios:
1. The feature introduced in the PR is handled by a feature flag.
2. The feature introduced in the PR is **not** handled by a feature flag.
For scenario 1, if it's not affecting other functionality, turn that feature flag off to disable the feature.
For scenario 2:
1. Revert the changes introduced in the original PR.
2. Notify the person who worked on the PR so they can work on a proper fix for their PR.
3. Reintroduce the change through the regular PR cycle.
@@ -0,0 +1,37 @@
---
title: "Community expectations"
sidebar_position: 2
---
We are a welcoming and open community and were excited to have you join us!
To be an open, safe, and welcoming community, we strive to be inclusive, collaborative, considerate, and respectful. We all abide by the [Mattermost Code of Conduct (CoC)](https://handbook.mattermost.com/contributors/contributors/guidelines/contribution-guidelines), and by joining our contributor community, **you agree to abide by it as well**.
Learn more about our [company values](https://handbook.mattermost.com/company/about-mattermost#leadership-principles), and how to become a successful member of the Mattermost community by learning and following our standard operational guidelines below. Please read these sections below carefully and let us know if you have any questions or concerns.
## We are inclusive
We welcome all people, **but not all behavior**.
We are a diverse community who celebrate both our differences and the things that connect us. We treat each other with respect, and aim to treat others better than they wish to be treated.
We try our best to be clear and respectful. We remember that others may not communicate in the same language with the same fluency. We recognize that communication can be challenging, especially among a diverse group of people communicating in many different languages and coming from many different cultures and backgrounds.
We all try to be mindful of our differences when we communicate and collaborate. Were aware that misunderstandings can happen. We try to resolve them by being respectful, understanding, and by using clear and simple language.
## We are collaborative
- We ask questions and consult others.
- We work together and help each other.
- We aim for clarity.
## We are considerate
- We have patience with each other.
- We understand that no one has all the answers, nor are they expected to.
## We are respectful
- We offer thanks and were grateful.
- We may occasionally disagree, but we resolve these disagreements in respectful ways, take breaks if things get heated, reassess, and consult others where appropriate.
- We aim to be self-aware and we take responsibility for our impact through our words and actions.
- We understand and acknowledge that [intent doesnt equal impact](https://www.betterup.com/blog/intent-vs-impact). We can have the best of intentions, but still cause negative impact to others by our words and actions. This can happen to all of us, so we practice openness and grace.
### Attribution
This document was heavily inspired by and adapted from the work of the [Drupal Community and its Code of Conduct](https://www.drupal.org/dcoc) and its [Values and Principles](https://www.drupal.org/about/values-and-principles) documents.
+19
View File
@@ -0,0 +1,19 @@
---
title: "Contributor guide"
sidebar_position: 1
---
This guide is your reference for all Mattermost contributions. The following is a brief summary to help you find what you're looking for:
- **[Why and how to contribute](/why-contribute)**:
How to get started contributing to Mattermost projects.
- **[Community expectations](/good-decisions)**:
How to communicate and interact effectively, respectfully, and inclusively with other members of the Mattermost community.
- **[Contributor expectations](/expectations)**:
What is expected of you throughout the whole contribution process, including guidelines to follow when producing new code and content.
- **[Where to find more information](/more-info)**:
Where to find more information on the contribution process beyond the guidance offered in this guide.
Please consult this guide as your official reference when contributing at Mattermost.
@@ -0,0 +1,28 @@
---
title: "Monorepo migration notes"
sidebar_position: 2
---
If you are transitioning from the non-monorepo ``mattermost-server`` to the monorepo, the easiest way to do so is to move the old mattermost server folder to something like ``mattermost-server-old`` then re-clone mattermost-server.
Then:
1. Copy over your old config
```sh
cd server
cp ../../mattermost-server-old/config/config.json ./config/
```
1. Copy over your developer config override
```sh
cd server
cp ../../mattermost-server-old/config.override.mk ./
```
1. Update your development Docker containers for the new location of the server folder:
```sh
cd server
make update-docker
```
@@ -0,0 +1,55 @@
---
title: "Containers"
---
Mattermost uses the [Docker Registry](https://hub.docker.com/u/mattermost) to publish the official images for the Mattermost Server and also for other supporting images that are used for internal/public development and testing.
This page lists all the Docker repositories currently in use.
## Mattermost official docker images
- [mattermost/mattermost-enterprise-edition](https://hub.docker.com/r/mattermost/mattermost-enterprise-edition) - **Official Mattermost Server** image for the **Enterprise Edition version**. To find the Dockerfile please refer to the [GitHub repo](https://github.com/mattermost/mattermost/tree/master/server/build).
- [mattermost/mattermost-team-edition](https://hub.docker.com/r/mattermost/mattermost-team-edition) - **Official Mattermost Server** image for the **Team Edition version**. To find the Dockerfile please refer to the [GitHub repo](https://github.com/mattermost/mattermost/tree/master/server/build).
- [mattermost/mattermost-push-proxy](https://hub.docker.com/r/mattermost/mattermost-push-proxy) - Mattermost Push Proxy. [Documentation](/developers/contribute/more-info/mobile/push-notifications/service). [GitHub repo](https://github.com/mattermost/mattermost-push-proxy).
- [mattermost/mattermost-loadtest](https://hub.docker.com/r/mattermost/mattermost-loadtest) - Image for the Load Test application. Tools for profiling Mattermost under heavy load. [GitHub repo](https://github.com/mattermost/mattermost-load-test).
- [mattermost/mattermost-operator](https://hub.docker.com/r/mattermost/mattermost-operator) - Official image for Mattermost Operator for Kubernetes. For more information please refer to the [GitHub repo](https://github.com/mattermost/mattermost-operator).
- [mattermost/mattermost-cloud](https://hub.docker.com/r/mattermost/mattermost-cloud) - Mattermost Private Cloud is a SaaS offering meant to smooth and accelerate the customer journey from trial to full adoption. For more information please refer to the [GitHub repo](https://github.com/mattermost/mattermost-cloud).
- [mattermost/mattermost-preview](https://hub.docker.com/r/mattermost/mattermost-preview) - This is a Docker image to install Mattermost in Preview Mode for exploring product functionality on a single machine using Docker. [Documentation](http://bit.ly/1W76riY). [GitHub repo](https://github.com/mattermost/mattermost-docker-preview).
- [mattermost/platform](https://hub.docker.com/r/mattermost/platform) - Mirror of **mattermost/mattermost-preview**. This is a Docker image to install Mattermost in Preview Mode for exploring product functionality on a single machine using Docker. Preview image (mirror). [Documentation](http://bit.ly/1W76riY). [GitHub repo](https://github.com/mattermost/mattermost-docker-preview).
## Community-maintained Docker images
- [mattermost/mattermost-prod-app](https://hub.docker.com/r/mattermost/mattermost-prod-app) - Community driven image for Mattermost Server. **This Docker repository will be deprecated in Mattermost 6.0**. For more information and to check the Dockerfile please refer to the [GitHub repo](https://github.com/mattermost/mattermost-docker).
- [mattermost/mattermost-prod-db](https://hub.docker.com/r/mattermost/mattermost-prod-db) - Community driven image for Database to run together with **mattermost/mattermost-prod-app**. **This Docker repository will be deprecated in Mattermost 6.0**. For more information and to check the Dockerfile please refer to the [GitHub repo](https://github.com/mattermost/mattermost-docker).
- [mattermost/mattermost-prod-web](https://hub.docker.com/r/mattermost/mattermost-prod-web) - Community driven image for WebServer to run together with **mattermost/mattermost-prod-app**. **This Docker repository will be deprecated in Mattermost 6.0**. For more information and to check the Dockerfile please refer to the [GitHub repo](https://github.com/mattermost/mattermost-docker).
## Mattermost internal Docker images
- [mattermost/mattermost-test-enterprise](https://hub.docker.com/r/mattermost/mattermost-test-enterprise) - Repository where all testing images are published and available for any type of testing. These images are built from the CircleCI Pipelines from the [mattermost-server](https://github.com/mattermost/mattermost) and [mattermost-webapp](https://github.com/mattermost/mattermost-webapp).
- [mattermost/mattermost-test-team](https://hub.docker.com/r/mattermost/mattermost-test-team) - Repository where all testing images are published and available for any type of testing. These images are built from the CircleCI Pipelines from the [mattermost-server](https://github.com/mattermost/mattermost) and [mattermost-webapp](https://github.com/mattermost/mattermost-webapp).
- [mattermost/mattermost-elasticsearch-docker](https://hub.docker.com/r/mattermost/mattermost-elasticsearch-docker) - Used in in CI and for local development. Please refer to the [GitHub repo](https://github.com/mattermost/mattermost/blob/master/docker-compose.yaml) for more information.
- [mattermost/mattermost-build-server](https://hub.docker.com/r/mattermost/mattermost-build-server) - Image used to build Mattermost used in CI. To check the Docker file refer to the [GitHub repo](https://github.com/mattermost/mattermost/blob/master/server/build/Dockerfile.buildenv).
- [mattermost/mattermost-wait-for-dep](https://hub.docker.com/r/mattermost/mattermost-wait-for-dep) - Image used to wait for the other containers to start. Used in in CI and for local development. Please refer to the [GitHub repo](https://github.com/mattermost/mattermost/blob/master/docker-compose.yaml) for more information.
- [mattermost/sync-helpwanted-tickets](https://hub.docker.com/r/mattermost/sync-helpwanted-tickets) - For internal use. This image runs the sync with Jira tickets and GitHub Issues. To check the code please refer to the [GitHub repo](https://github.com/mattermost/mattermost-utilities/tree/master/github_jira_tools).
- [mattermost/podman](https://hub.docker.com/repository/docker/mattermost/podman) - For internal use. Contains Podman to build/tag/push container images.
- [mattermost/chewbacca](https://hub.docker.com/repository/docker/mattermost/chewbacca-bot) - For internal use. A GitHub Bot for administrative tasks. Please refer to the [GitHub repo](https://github.com/mattermost/chewbacca) for more information.
- [mattermost/matterwick](https://hub.docker.com/repository/docker/mattermost/matterwick) - For internal use. A GitHub Bot to spin test servers for pull requests. Please refer to the [GitHub repo](https://github.com/mattermost/matterwick) for more information.
- [mattermost/webrtc](https://hub.docker.com/repository/docker/mattermost/webrtc) - DEPRECATED. Preview docker image of Mattermost WebRTC.
@@ -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.
@@ -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.
@@ -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
```
@@ -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](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](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](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: 328 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

@@ -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');
```
@@ -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](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

@@ -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
@@ -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.
@@ -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.
@@ -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
```
@@ -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
```
@@ -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
```
@@ -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
```
@@ -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
```
@@ -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.
@@ -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.
@@ -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
```
@@ -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/)
@@ -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).
@@ -0,0 +1,32 @@
---
title: "Focalboard"
sidebar_position: 9
---
The [Focalboard](https://www.focalboard.com) project is written in [TypeScript](https://www.typescriptlang.org/) and [Go](https://go.dev/).
Here's the process for contributing to Focalboard:
1. Fork the [Focalboard repository](https://github.com/mattermost/focalboard), clone it locally, and follow the steps in the [Personal Server Setup Guide](personal-server-setup-guide) to build it. You can read the [CHANGELOG](https://github.com/mattermost/focalboard/blob/main/CHANGELOG.md) to learn about recent updates.
2. Find [help wanted tickets that are up for grabs in GitHub](https://github.com/mattermost/focalboard/issues?q=is%3Aopen+is%3Aissue+label%3A%22Up+for+grabs%22+label%3A%22Help+Wanted%22). Comment to let everyone know youre working on it and let a core contributor assign the issue to you. If theres no ticket for what you want to work on, read about [contributions without a ticket](/developers/contribute/more-info/getting-started/contributions-without-ticket).
3. When your changes are checked in to your fork, follow the steps on our [contribution checklist](/developers/contribute/more-info/getting-started/contribution-checklist). If this will be your first contribution, there is a standard [CLA](https://www.mattermost.org/mattermost-contributor-agreement/) that you will need to sign as part of this checklist.
4. Submit your pull request for a [code review](/developers/contribute/more-info/getting-started/code-review#if-you-are-a-community-member-seeking-a-review) and [wait](/developers/contribute/more-info/getting-started/code-review#if-you-are-awaiting-a-review) for a [Focalboard core committer](https://github.com/mattermost/focalboard/blob/main/CONTRIBUTING.md#contributors) to review it. When in doubt, ask for help in the [Focalboard channel](https://community.mattermost.com/core/channels/focalboard) on our community server. If you are still stuck, please message Chen Lim ([@chenilim](https://github.com/chenilim) on GitHub).
5. After a noteable bug fix or improvement is merged, submit a pull request to the [CHANGELOG](https://github.com/mattermost/focalboard/blob/main/CHANGELOG.md) under the next release section.
We're glad ❤️ you're here! Good luck and have fun!
## Repository
https://github.com/mattermost/focalboard
## Community
You can join the [public Focalboard channel](https://community.mattermost.com/core/channels/focalboard) on our Mattermost community server. You can also [file a bug](https://github.com/mattermost/focalboard/issues/new/choose) for an issue or [start a discussion](https://github.com/mattermost/focalboard/discussions) on the repository.
## Help wanted
You can find help wanted tickets [here](https://github.com/mattermost/focalboard/issues?q=is%3Aopen+is%3Aissue+label%3A%22Up+for+grabs%22+label%3A%22Help+Wanted%22).
@@ -0,0 +1,58 @@
---
title: "Mattermost Boards plugin guide"
sidebar_position: 2
---
<Note title="Important">
From Mattermost v7.11, Mattermost Boards is a core part of the product that cannot be disabled or built separately. Developers should read the updated [Developer Guide](/developers/contribute/developer-setup) for details.
</Note>
In Mattermost v7.10 and earlier releases, **[Mattermost Boards](https://mattermost.com/boards/)** is the Mattermost plugin version of Focalboard that combines project management tools with messaging and collaboration for teams of all sizes. It is installed and enabled by default in Mattermost v6.0 and later. For working with Focalboard as a standalone application, please refer to the [Personal Server Setup Guide](/developers/contribute/more-info/focalboard/personal-server-setup-guide).
## Build the plugin
1. Fork the [Focalboard repository](https://github.com/mattermost/focalboard) and clone it locally. Clone [Mattermost](https://github.com/mattermost/mattermost) in a sibling directory.
2. Define an environment variable ``EXCLUDE_ENTERPRISE`` with a value of ``1``.
3. To install the dependencies:
```
cd mattermost-plugin/webapp
npm install --no-optional
cd ../..
make prebuild
```
4. To build the plugin:
```
make webapp
cd mattermost-plugin
make dist
```
Refer to the [dev-release.yml](https://github.com/mattermost/focalboard/blob/main/.github/workflows/dev-release.yml#L168) workflow for the up-to-date commands that are run as part of CI.
## Upload and install the plugin
1. Enable [custom plugins](/developers/integrate/plugins/using-and-managing-plugins#custom-plugins) by setting `PluginSettings.EnableUploads` to `true` and set `FileSettings.MaxFileSize` to a number larger than the size of the packed`.tar.gz` plugin file in bytes (e.g., `524288000`) in the Mattermost `config.json` file.
2. Navigate to **System Console > Plugins > Management** and upload the packed `.tar.gz` file from your `mattermost-plugin/dist` directory.
3. Enable the plugin.
## Deploy the plugin to a local Mattermost server
Instead of following the steps above, you can also set up a `mattermost-server` in local mode and automatically deploy `mattermost-plugin` via `make deploy`.
* Follow the steps in the [`mattermost-webapp` developer setup guide](/developers/contribute/developer-setup) and then:
* Open a new terminal window. In this terminal window, add an environmental variable to your bash via `MM_SERVICESETTINGS_SITEURL='http://localhost:8065'` ([docs](https://developers.mattermost.com/blog/subpath/#using-subpaths-in-development))
* Build the web app via `make build`
* Follow the steps in the [`mattermost-server` developer setup guide](/developers/contribute/developer-setup) and then:
* Make sure Docker is running.
* Run `make config-reset` to generate the `config/config.json` file:
* Edit `config/config.json`:
* Set `ServiceSettings > SiteURL` to `http://localhost:8065` ([docs](https://docs.mattermost.com/configure/configuration-settings.html#site-url))
* Set `ServiceSettings > EnableLocalMode` to `true` ([docs](https://docs.mattermost.com/configure/configuration-settings.html#enable-local-mode))
* Set `PluginSettings > EnableUploads` to `true` ([docs](/developers/integrate/plugins/using-and-managing-plugins#custom-plugins))
* In this terminal window, add an environmental variable to your bash via `MM_SERVICESETTINGS_SITEURL='http://localhost:8065'` ([docs](https://developers.mattermost.com/blog/subpath/#using-subpaths-in-development))
* Build and run the server via `make run-server`
* Follow the [steps above](#build-the-plugin) to install the dependencies.
* Run `make deploy` in the `mattermost-plugin` folder to automatically deploy your plugin to your local Mattermost server.
@@ -0,0 +1,137 @@
---
title: "Personal server setup guide"
sidebar_position: 1
---
This guide will help you configure your developer environment for the Focalboard **Personal Server**. For most features, this is the easiest way to get started working against code that ships across editions. For working with **Mattermost Boards** (Focalboard as a plugin), please refer to the [Mattermost Boards Plugin Guide](/developers/contribute/more-info/focalboard/mattermost-boards-setup-guide).
## Install prerequisites
### All
* [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) (if using Windows, see below)
* [Go](https://golang.org/doc/install)
* [Node.js](https://nodejs.org/en/download/) (v10+)
* [npm](https://www.npmjs.com/get-npm)
### Windows
* Install [MinGW-w64](https://community.chocolatey.org/packages/mingw) via [Chocolatey](https://chocolatey.org/)
* Install [Git for Windows](https://gitforwindows.org/) and use the `git-bash` terminal shell
### Mac
* Install [Xcode](https://apps.apple.com/us/app/xcode/id497799835?mt=12) (v12+)
* Install the Xcode Command Line Tools via `xcode-select --install`
### Linux
* `sudo apt-get install libgtk-3-dev`
* `sudo apt-get install libwebkit2gtk-4.0-dev`
* `sudo apt-get install autoconf dh-autoreconf`
## Fork the project repositories
Fork the [Focalboard GitHub repository](https://github.com/mattermost/focalboard) and [Mattermost GitHub repository](https://github.com/mattermost/mattermost). Clone both repositories locally in sibling directories.
## Build via the terminal
To build the server:
```
make prebuild
make
```
To run the server:
```
./bin/focalboard-server
```
Then navigate your browser to [`http://localhost:8000`](http://localhost:8000) to access your Focalboard server. The port is configured in `config.json`.
Once the server is running, you can rebuild just the web app via `make webapp` in a separate terminal window. Reload your browser to see the changes.
## Build and run standalone desktop apps
You can build standalone apps that package the server to run locally against [SQLite](https://www.sqlite.org/index.html):
* **Windows**:
* *Requires Windows 10, [Windows 10 SDK](https://developer.microsoft.com/en-us/windows/downloads/sdk-archive/) 10.0.19041.0, and .NET 4.8 developer pack*
* Open a `git-bash` prompt.
* Run `make prebuild`
* The above prebuild step needs to be run only when you make changes to or want to install your npm dependencies, etc.
* Once the prebuild is completed, you can keep repeating the below steps to build the app & see the changes.
* Run `make win-wpf-app`
* Run `cd win-wpf/msix && focalboard.exe`
* **Mac**:
* *Requires macOS 11.3+ and Xcode 13.2.1+*
* Run `make prebuild`
* The above prebuild step needs to be run only when you make changes to or want to install your npm dependencies, etc.
* Once the prebuild is completed, you can keep repeating the below steps to build the app & see the changes.
* Run `make mac-app`
* Run `open mac/dist/Focalboard.app`
* **Linux**:
* *Tested on Ubuntu 18.04*
* Install `webgtk` dependencies
* Run `sudo apt-get install libgtk-3-dev`
* Run `sudo apt-get install libwebkit2gtk-4.0-dev`
* Run `make prebuild`
* The above prebuild step needs to be run only when you make changes to or want to install your npm dependencies, etc.
* Once the prebuild is completed, you can keep repeating the below steps to build the app & see the changes.
* Run `make linux-app`
* Uncompress `linux/dist/focalboard-linux.tar.gz` to a directory of your choice
* Run `focalboard-app` from the directory you have chosen
* **Docker**:
* To run it locally from offical image:
* `docker run -it -p 80:8000 mattermost/focalboard`
* To build it for your current architecture:
* `docker build -f docker/Dockerfile .`
* To build it for a custom architecture (experimental):
* `docker build -f docker/Dockerfile --platform linux/arm64 .`
Cross-compilation currently isn't fully supported, so please build on the appropriate platform. Refer to the GitHub Actions workflows (`build-mac.yml`, `build-win.yml`, `build-ubuntu.yml`) for the detailed list of steps on each platform.
## Set up VS Code
* Open a [VS Code](https://code.visualstudio.com/) terminal window in the project folder.
* Run `make prebuild` to install packages. *Do this whenever dependencies change in `webapp/package.json`.*
* Run `cd webapp && npm run watchdev` to automatically rebuild the web app when files are changed. It also includes source maps from JavaScript to TypeScript.
* Install the [Go](https://marketplace.visualstudio.com/items?itemName=golang.Go) and [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) VS Code extensions (if you haven't already).
* Launch the server:
* **Windows**: <kbd><kbd>Ctrl</kbd>+<kbd>P</kbd></kbd>, type `debug`, press the <kbd>Space</kbd> key, and select `Go: Launch Server`.
* **Mac**: <kbd><kbd>Cmd</kbd>+<kbd>P</kbd></kbd>, type `debug`, press the <kbd>Space</kbd> key, and select `Go: Launch Server`.
* *If you do not see `Go: Launch Server` as an option, check your `./.vscode/launch.json` file and make sure you are not using a VS Code workspace.*
* Navigate a browser to `http://localhost:8000`
You can now edit the web app code and refresh the browser to see your changes efficiently.
**Debugging the web app**: As a starting point, add a breakpoint to the `render()` function in `BoardPage.tsx` and refresh the browser to walk through page rendering.
**Debugging the server**: As a starting point, add a breakpoint to `handleGetBlocks()` in `server/api/api.go` and refresh the browser to see how data is retrieved.
## Rebuild translations
We use `i18n` to localize the web app. Localized string generally use `intl.formatMessage`. When adding or modifying localized strings, run `npm run i18n-extract` in `webapp` to rebuild `webapp/i18n/en.json`.
Translated strings are stored in other json files under `webapp/i18n`, (e.g. `es.json` for Spanish).
## Access the database
By default, data is stored in a sqlite database `focalboard.db`. You can view and edit this directly using `sqlite3 focalboard.db`.
## Unit tests
Run `make ci`, which is similar to the `.gitlab-ci.yml` workflow and includes:
* **Server unit tests**: `make server-test`
* **Web app ESLint**: `cd webapp; npm run check`
* **Web app unit tests**: `cd webapp; npm run test`
* **Web app UI tests**: `cd webapp; npm run cypress:ci`
Unit tests for Focalboard are similar to the [web app and server testing](/developers/contribute/more-info/getting-started/test-guideline) requirements.
## Staying informed
Are you interested in influencing the future of the Focalboard open source project? Please read the [Focalboard Contribution Guide](/developers/contribute/more-info/focalboard/). We welcome everyone and appreciate any feedback. ❤️ There are several ways you can get involved:
* **Changes**: See the [CHANGELOG](https://github.com/mattermost/focalboard/blob/main/CHANGELOG.md) for the latest updates
* **GitHub Discussions**: Join the [Developer Discussion](https://github.com/mattermost/focalboard/discussions) board
* **Bug Reports**: [ title=](https://github.com/mattermost/focalboard/issues/new?assignees=&labels=bug&template=bug_report.md&title=)
* **Chat**: Join the [Focalboard community channel](https://community.mattermost.com/core/channels/focalboard)
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@@ -0,0 +1,64 @@
---
title: "Mattermost cherry-pick process"
sidebar_position: 20
---
The self-managed releases are cut based off of the Mattermost Cloud release tags (e.g Mattermost Server v6.3 release was based off of ``cloud-2021-12-08-1`` Cloud release tag) in the server, webapp, enterprise, and api-reference repos. See [the Handbook release process](https://handbook.mattermost.com/operations/research-and-development/product/release-process/release-overview#cloud-release-branch-processes) for more details.
The Mobile and Desktop app release branches are based off of ``master`` branch.
## Developer process
When your PR is required on a release branch (e.g. for a dot release or to fix a regression for an upcoming release), you will follow the cherry-picking process.
1. Make a PR to 'master' like normal.
2. Add the appropriate milestone and the `CherryPick/Approved` label.
3. When your PR is approved, it will be assigned back to you to perform the merge and any cherry picking if necessary.
4. Merge the PR.
5. An automated cherry-pick process will try to cherry-pick the PR. If the automatic process succeeds, a new PR pointing to the correct release branch will open with all the appropriate labels. If there are no additional changes from the original PR for the cherry-pick, it can be merged without further review.
6. If the automated cherry-pick fails, the developer will need to cherry-pick the PR manually. Cherry-pick the master commit back to the appropriate releases. If the release branches have not been cut yet, leave the labels as-is and cherry-pick once the branch has been cut. The release manager will remind you to finish your cherry-pick.
7. Set the `CherryPick/Done` label when completed.
* If the cherry-pick fails, the developer needs to apply the cherry-pick manually.
* Cherry-pick the commit from `master` to the affected releases. See the steps below:
8. Run the checks for lint and tests.
9. Push your changes directly to the remote branch if the check style and tests passed.
10. No new pull request is required unless there are substantial merge conflicts.
11. Remove the `CherryPick/Approved` label and apply the `CherryPick/Done` label.
<Note title="Note">
If the PR needs to go to other release branches, you can run the command `/cherry-pick release-x.yz` in the PR comments and it will try to cherry-pick it to the branch you specified.
</Note>
### Manual cherry-pick
If conflicts appear between your pull request (PR) and the cherry-pick target branch, the automated cherry-pick process will fail and will let you know that you need to do a manual cherry-pick. Here are the steps to do so:
1. Fetch the latest updates from origin:
```sh
git fetch origin
```
2. Create a new branch starting at the release branch on origin.
```sh
git checkout -b manual-cherry-pick-pr-[PR_NUMBER] origin/release-[VERSION]
```
3. Find the SHA of the pull request merge commit, and cherry-pick this commit in your new branch:
```sh
git log origin/master
git cherry-pick [SHA]
```
4. You're likely to face the conflict that prevented the automated cherry-pick now. Fix the conflict, and then run the following:
```sh
git add [path/to/conflicted/files]
git cherry-pick --continue
```
5. Finally, push your new branch as usual and create a PR. Make sure you select `release-[VERSION]` as the base branch, and not the default (master).
```sh
git push -u origin manual-cherry-pick-pr-[PR_NUMBER]
```
## Reviewer process
If you are the second reviewer reviewing a PR that needs to be cherry-picked, do not merge the PR. If the submitter is a core team member, you should set the `Reviews Complete` label and assign it to the submitter to cherry-pick. If the submitter is a community member who is not available to cherry-pick their PR or can not do it themselves, you should follow the cherry-pick process above.
@@ -0,0 +1,140 @@
---
title: "Code review"
sidebar_position: 5
---
All changes to the product should be reviewed. Every team will have its own workflow, but in general:
* User experience changes should be reviewed by a product manager or designer.
* Code changes should be reviewed by at least two core committers.
* Documentation changes should be reviewed by a product manager or technical writer.
Staff should consult the internal [organization chart](https://docs.google.com/spreadsheets/d/1lH8QIjQGEoGospDUdVs_LQ_i2b82I1ce6W7z18vhPTQ/edit#gid=1730823498) as needed when finding the right reviewer.
If you are a community member seeking a review
----------------------------------------------
1. Submit your pull request.
* Follow the [contribution checklist](/developers/contribute/more-info/getting-started/contribution-checklist).
2. Wait for a reviewer to be assigned.
* Product managers are on the lookout for new pull requests and usually handle this for you automatically.
* If you have been working alongside a core committer, feel free to message them for help.
* When in doubt, ask for help in the [Developers](https://community.mattermost.com/core/channels/developers) channel on our community server.
* If you are still stuck, message Jason Blais ([@jasonblais](https://github.com/jasonblais) on GitHub) or Jason Frerich ([@jfrerich](https://github.com/jfrerich) on GitHub).
3. [Wait for a review](#if-you-are-awaiting-a-review).
* Expect some interaction with at least one reviewer within 5 business days. (Business days are typically Monday to Friday, excluding [statutory holidays](https://handbook.mattermost.com/operations/workplace/people/working-at-mattermost/paid-time-off#typical-public-holidays-in-canada-germany-uk-us).)
* Keep in mind that core committers are geographically distributed around the world and likely in a different time zone than your own.
* If no interaction has occurred after 5 business days, at-mention a reviewer with a comment on your PR.
4. Make any necessary changes.
* If a reviewer requests changes, your pull request will disappear from their queue of reviews.
* Once you've addressed the concerns, re-request a review from any reviewer requesting changes.
* [Avoid force pushing](https://mattermost.com/blog/submitting-great-prs/#4-avoid-force-pushing).
5. Wait for your code to be merged.
* Larger pull requests may require more time to review.
* Once all reviewers have approved your changes, they will handle merging your code.
If you are a core committer seeking a review
--------------------------------------------
1. Submit your pull request.
* Follow the [contribution checklist](/developers/contribute/more-info/getting-started/contribution-checklist).
2. Immediately add the `1: UX Review`, `2: Dev Review`, and `3: QA Review` labels.
* Your pull request should not be merged until these labels are later removed in the review process.
3. Apply additional labels as necessary:
* `CherryPick/Approved`: Apply this if the pull request is meant for a quality or patch release.
* `Do Not Merge/Awaiting PR`: Apply this if the pull request depends on another (e.g. server changes)
* `Setup Test Server`: Apply this to create a test server with your changes for review.
* See [labels](/developers/contribute/more-info/getting-started/labels) for additional documentation.
4. Assign a milestone as necessary.
* Most pull requests do not require a milestone, but will simply ship once merged.
* Some reviewers may prioritize reviews for known upcoming milestones.
* The milestone is mandatory for bug fixes that must be cherry-picked.
5. Request a review from a Product Manager and/or a Designer.
* The choice of Product Manager or Designer is up to you.
- In most cases, choose the individual embedded with your team.
- If your change primarily touches another team's codebase, assign the individual from that team.
- If in doubt, assign any Product Manager or Designer and comment about the uncertainty. They may reassign as appropriate.
* [Wait](#if-you-are-awaiting-a-review) for their review to complete before continuing so as to avoid churn if changes are requested.
* Remove the `1: UX Review` label only when these reviews are done and they accept the changes.
- Product Managers and Designers ensure the changes meet [user experience guidelines](https://docs.mattermost.com/developer/fx-guidelines.html).
* If your changes do not affect the user experience, you may remove `1: UX Review` immediately.
6. After UX review, request a review from two core committers.
* The choice of core committers is up to you.
- When picking your first core committer, consider someone with domain expertise relative to your changes. Sometimes GitHub will recommend a recent editor of the code, but often you must rely on your own intuition from past interactions.
- When picking your second core committer, consider someone who may have expertise in the language you're using or the problem you're solving, even if they aren't intricately familiar with the codebase. This can provide a fresh set of eyes on the code to reveal blindspots that are not biased by hitting deadlines, and helps expose the team to new parts of the code to help spread out domain knowledge. This may not make sense for every pull request but is a practice to keep in mind. If you don't have someone specific in mind, consider using the [`@core-reviewers`](https://github.com/orgs/mattermost/teams/core-reviewers) group to assign an available reviewer automatically.
- Don't be afraid to pick someone who gives "hard" reviews. Code review feedback is never a personal attack: it should "sharpen" the skills of both the author and the reviewers, not to mention improving the quality of the product.
- Try to avoid assigning the same person to all of your reviews unless they are related.
- When in doubt, ask for recommendations on our community server.
* [Wait](#if-you-are-awaiting-a-review) for their review to complete before continuing so as to avoid churn if changes are requested.
* Remove the `2: Dev Review` label only when these reviews are done and they accept the changes.
7. After Dev review, assign a QA tester.
* Ensure that your PR includes test steps or expected results for QA reference if the QA Test Steps in the Jira ticket have not already been filled in.
* The choice of QA tester is up to you.
- In most cases, choose the individual embedded with your team.
- If your change primarily touches another team's codebase, assign the individual from that team.
- If in doubt, assign any QA tester and comment about the uncertainty. They may reassign as appropriate.
* [Wait](#if-you-are-awaiting-a-review) for their review to complete before continuing.
- It is the QA tester's responsibility to determine the scope of required testing, if any.
* Remove the `3: QA Review` label only when their review is done and they accept the changes.
* In the rare event your changes do not require QA review, you may remove `3: QA Review` immediately.
- Comment on the pull request clearly explaining the rationale.
8. Merge the pull request.
* Do not merge until the labels `1: UX Review`, `2: Dev Review` and `3: QA Review` labels have been removed.
* Add the `4: Reviews Complete` label if the last reviewer did not already add it.
* Do not merge if there are outstanding changes requested.
* Merge your pull request and delete the branch if not from a fork.
- Note that any core committer is free to merge on your behalf.
- If your pull request depends on other pull requests, consider assigning the `Do Not Merge/Awaiting PR` label to avoid merging prematurely.
9. Handle any cherry-picks.
* There is an automated cherry-pick process.
* As the author of the pull request, you should make sure the cherry-pick succeeds.
* [Check here](/developers/contribute/more-info/getting-started/branching#cherry-pick-process---developer) for details.
10. After a pull request is merged (and cherry-picked where needed), update the Jira ticket.
* Resolve the ticket for QA from "Ready for QA" button with QA test steps (or "No Testing Required" if no QA testing is needed).
If you are awaiting a review
----------------------------
1. Wait patiently for reviews to complete.
* Expect some interaction with each of your reviewers within 2 business days.
* There is no need to explicitly mention them on the pull request or to explicitly reach out on our community server.
* Core committers and QA testers are expected to have the GitHub plugin installed to automate notifications and to trigger a daily review of their outstanding requested reviews.
2. Make any necessary changes.
* If a reviewer requests changes, your pull request will disappear from their queue of reviews.
* Once you've addressed the concerns, assign them as a reviewer again to put your pull request back in their queue.
If you are a core committer asked to give a review
--------------------------------------------------
1. Respond promptly to requested reviews.
* Assume the requested review is urgent and blocking unless explicitly stated otherwise.
* Try to interact with the author within 2 business days.
* Configure the GitHub plugin to automate notifications.
* Review your outstanding requested reviews daily to avoid blocking authors.
* Prioritize earlier milestones when reviewing to help with the release process.
* Responding quickly doesn't necessarily mean reviewing quickly! Just don't leave the author hanging.
2. Feel free to clarify expectations with the author.
* If the PR adds a substantial feature, check that a feature flag is included. Please see [criteria here](/developers/contribute/more-info/server/feature-flags#when-to-use).
* If the code is experimental, they may need only a cursory glance and thumbs up to proceed with productizing their changes.
* If the review is large or complex, additional time may be required to complete your review. Be upfront with the author.
* If you are not comfortable reviewing the code, avoid "rubber stamping" the review. Be honest with the author and ask them to consider another core committer.
3. Never rush a review.
* Take the time necessary to review the code thoroughly.
* Don't be afraid to ask for changes repeatedly until all concerns are addressed.
* Feel free to challenge assumptions and timelines. Rushing a change into a patch release may cause more harm than good.
4. Avoid leaving a review hanging.
* Try to accept or reject the review instead of just leaving comments.
* If you are the last developer to approve the changes, consider requesting a review from the appropriate QA tester to speed up the process.
6. Run the E2E tests against the PR code
* After the PR has been reviewed, the reviewer should trigger an E2E test run on it by using [one of the available mechanisms](https://mattermost.atlassian.net/wiki/spaces/CLOUD/pages/2627371043/E2E+Tests). The E2E Test result will be posted back to the PR as a comment.
* It's the PR author's responsibility to address the E2E test failures, with the assistance of the reviewers.
* Core committers can refer to [this Confluence page](https://mattermost.atlassian.net/wiki/spaces/CLOUD/pages/2627371043/E2E+Tests) for additional informations on E2E testing and related processes.
7. Merge the pull request.
* Do not merge until the labels `1: UX Review`, `2: Dev Review` and `3: QA Review` labels have been removed.
* Add the `4: Reviews Complete` label if the last reviewer did not already add it.
* Do not merge if there are outstanding changes requested.
* Do not merge if there are any `Do Not Merge` labels applied.
- When in doubt, leave the merging of the pull request to the author.
* Merge the pull request, and delete the branch if not from a fork.
8. Handle any cherry-picks.
* There is an automated cherry-pick process and the author of the pull request should make sure the cherry-pick succeeds. Assume this is the case unless you are explicitly asked to help cherry-pick.
@@ -0,0 +1,35 @@
---
title: "Contribution checklist"
sidebar_position: 2
---
Thanks for your interest in contributing to Mattermost! Come join our [Contributors community channel](https://community.mattermost.com/core/channels/tickets) on the community server, where you can discuss questions with community members and the Mattermost core team.
To help with translations, [see the localization process](https://docs.mattermost.com/developer/localization.html).
Follow this checklist for submitting a pull request (PR):
1. You've signed the [Contributor License Agreement](https://mattermost.com/mattermost-contributor-agreement/), so you can be added to the Mattermost [Approved Contributor List](https://docs.google.com/spreadsheets/d/1NTCeG-iL_VS9bFqtmHSfwETo5f-8MQ7oMDE5IUYJi_Y/pubhtml?gid=0&single=true).
- If you've included your mailing address in the signed [Contributor License Agreement](https://mattermost.com/mattermost-contributor-agreement/), you may receive a [Limited Edition Mattermost Mug](https://forum.mattermost.com/t/limited-edition-mattermost-mugs/143) as a thank you gift after your first pull request is merged.
2. You have claimed the ticket that you wish to work on by asking for an assignment from the Mattermost team.
- Tickets are assigned on a first-come-first-serve basis.
3. Your ticket is a Help Wanted GitHub issue for the Mattermost project you're contributing to.
- If not, follow the process [here](/developers/contribute/more-info/getting-started/contributions-without-ticket).
4. Your code is thoroughly tested, including appropriate [unit, end-to-end, and integration tests for webapp](/developers/contribute/more-info/getting-started/test-guideline).
5. If applicable, user interface strings are included in localization files:
- [mattermost/server/en.json](https://github.com/mattermost/mattermost/blob/master/server/i18n/en.json)
- [mattermost/webapp/channels/src/i18n/en.json](https://github.com/mattermost/mattermost/blob/master/webapp/channels/src/i18n/en.json)
- [mattermost-mobile/assets/base/i18n/en.json](https://github.com/mattermost/mattermost-mobile/blob/master/assets/base/i18n/en.json)
5.1. In the webapp/channels repository run `npm run i18n-extract` to generate the new/updated strings.
6. The PR is submitted against the Mattermost `master` branch from your fork.
7. The PR title begins with the Jira or GitHub ticket ID (e.g. `[MM-394]` or `[GH-394]`) and summary template is filled out.
8. If your PR adds or changes a RESTful API endpoint, please update the [API documentation](https://github.com/mattermost/mattermost/tree/master/api).
9. If your PR adds a new plugin API method or hook, please add an example to the [Plugin Starter Template](https://github.com/mattermost/mattermost-plugin-starter-template).
10. If QA review is applicable, your PR includes test steps or expected results.
11. If the PR adds a substantial feature, a feature flag is included. Please see [criteria here](/developers/contribute/more-info/server/feature-flags#when-to-use).
12. Your PR includes basic documentation about the change/addition you're submitting. View our [guidelines](https://handbook.mattermost.com/operations/research-and-development/product/technical-writing-team-handbook#submit-documentation-with-your-pr-community) for more information about submitting documentation and the review process.
Once submitted, the automated build process must pass in order for the PR to be accepted. Any errors or failures need to be addressed in order for the PR to be accepted. Next, the PR goes through [code review](/developers/contribute/more-info/getting-started/code-review). To learn about the review process for each project, read the `CONTRIBUTING.md` file of that GitHub repository.
That's all! If you have any feedback about this checklist, let us know in the [Contributors channel](https://community.mattermost.com/core/channels/tickets).
@@ -0,0 +1,29 @@
---
title: "Contributions without ticket"
sidebar_position: 1
---
Contributions for minor corrections and improvements without a corresponding `Help Wanted` ticket are welcome. For example, a pull request for a bug or incremental improvement, with less than 20 lines of code change, is usually accepted if the change to existing behaviour is minor.
All pull requests submitted without a corresponding ticket will first be reviewed by a core team product manager. Some examples of minor corrections and improvements include:
- [Fix a formatting error in help text](https://github.com/mattermost/mattermost/pull/5640)
- [Fix success typo in Makefile](https://github.com/mattermost/mattermost/pull/5809)
- [Fix broken Cancel button in Edit Webhooks screen](https://github.com/mattermost/mattermost/pull/5612)
- [Fix Android app crashing when saving user notification settings](https://github.com/mattermost/mattermost-mobile/pull/364)
- [Fix recent mentions search not working](https://github.com/mattermost/mattermost/pull/5878)
<Note title="Note">
For pull requests greater than 20 lines of code, a `Help Wanted` ticket should be opened by the core team. This helps ensure that everything going into the project aligns with a unified vision. Core committers who review the PR are entitled to reject it if there isn't a `Help Wanted` ticket and feel it significantly changes behavior or user expectations.
</Note>
<Note title="Note">
Please use [our translation server](https://translate.mattermost.com) to correct errors in translation.
</Note>
The best way to discuss opening a `Help Wanted` ticket with the core team is by [starting a conversation in the feature idea forum](https://mattermost.uservoice.com/forums/306457-general) or [opening an issue in the GitHub repository](https://github.com/mattermost/mattermost/issues/new). Alternatively, don't hesitate to come chat about it in the [Contributors](https://community.mattermost.com/core/channels/tickets) or [Developers](https://community.mattermost.com/core/channels/developers) channels.
@@ -0,0 +1,6 @@
---
title: "Core committers"
sidebar_position: 4
---
A core committer is a maintainer on the Mattermost project that has merge access to Mattermost repositories. They are responsible for reviewing pull requests, cultivating the Mattermost developer community, and guiding the technical vision of Mattermost.
@@ -0,0 +1,41 @@
---
title: "Engineering guilds"
sidebar_position: 50
---
Our Engineering Guilds are a forum for sharing knowledge, discussing and agreeing on plans, and disseminating information related to a particular subject or technical topic. Many (although not all) Engineering Guilds are affiliated to a particular "platform" that forms a part of Mattermost, and are therefore associated with a particular Platform Team. However, the participants in each Guild span many engineering teams at Mattermost in order to get people together to share knowledge and disseminate decisions across the Engineering organization.
The main activity of Guilds is a regular meeting. These meetings are public, and anyone within Mattermost and the wider community is welcome to attend. Details are provided below on each of the Guilds, including how to participate.
## Web platform guild
The Web Platform Guild covers all areas related to front-end web development and Electron desktop app development at Mattermost.
- **Guild Lead:** Harrison Healey
- **Guild channel:** [~Developers: WebApp](https://community.mattermost.com/core/channels/webapp)
- **Guild meeting time:** *(see header of Guild channel for meeting invite)*
## Mobile guild
The Mobile Guild covers all areas related to mobile application development and React Native.
- **Guild Lead:** Elias Nahum
- **Guild channel:** [~Developers: Mobile](https://community.mattermost.com/core/channels/native-mobile-apps)
- **Guild meeting time:** *(see header of Guild channel for meeting invite)*
## Server guild
The Server Guild covers all areas related to our server codebase and general Go development at Mattermost.
- **Guild Lead:** Agniva De Sarker
- **Guild channel:** [~Developers: Server](https://community.mattermost.com/core/channels/developers-server)
- **Guild meeting time:** *(see header of Guild channel for meeting invite)*
## QA guild
The QA Guild covers all areas related to Quality Assurance and automated testing infrastructure at Mattermost.
- **Guild Lead:** Saturnino Abril
- **Guild channel:** [~QA: Weekly Meetings](https://community.mattermost.com/core/channels/qa-weekly-meetings)
- **Guild meeting time:** *(see header of Guild channel for meeting invite)*
@@ -0,0 +1,26 @@
---
title: "Inactive contributions"
sidebar_position: 3
---
This process describes how inactive contributions are managed at Mattermost, inspired by the [Kubernetes project](https://github.com/kubernetes/kubernetes):
1. After 10 days of inactivity, a contribution becomes stale and a bot will add the `lifecycle/1:stale` label to the contribution.
- If action is required from submitter, Community Coordinator asks if the team can help clarify previous feedback or provide guidance on next steps.
- If action is required from reviewers, Community Coordinator asks reviewers to share feedback or help answer questions. The Coordinator will follow up with reviewers until a response is received.
2. After 20 days of inactivity, a contribution becomes inactive.
- Community Coordinator asks the submitter if the team can help with questions. They acknowledge that after another 30 days of inactivity the contribution will be closed. They also add a `lifecycle/2:inactive` label to the contribution.
<Note title="Note">
Contributions should never become orphaned because of reviewers. The Coordinator will be responsible for receiving a response from the reviewers during the stale period, which may be that the maintainers aren't able to accept the contribution in its current form.
</Note>
3. After 30 days of inactivity, a contribution becomes orphaned.
- Community Coordinator notes that the contribution has been inactive for 60 days, thanks for the contribution and closes the contribution. They also add an `lifecycle/3:orphaned` label to the contribution, and adds an `Up For Grabs` label to the associated help wanted ticket, if appropriate.
Exceptions:
1. If the contribution is inactive but shouldn't be closed, the Coordinator adds a `lifecycle/frozen` label to the contribution. An example of this is when a design decision is being discussed but no decision has been arrived at yet.
2. Once the contribution reaches the `lifecycle/2:inactive` state, it is eligible to be assumed by another community member interested in working on the ticket.
3. Invalid PRs may be closed immediately without advancing through this lifecycle, especially if the contributor is unresponsive.
@@ -0,0 +1,31 @@
---
title: "Contribute code"
sidebar_position: 1
---
This site is for developers who want to contribute code to the core Mattermost project. If youre looking for other ways to contribute, [head over to our website](https://mattermost.com/contribute/). Before getting started, its a good idea to review our guide on [integrating and extending Mattermost](/developers/integrate/getting-started) because you might be able to build the improvements you want without needing to contribute them upstream.
## Technical overview
The Mattermost core repositories include:
* [Server](/developers/contribute/more-info/server) - Highly-scalable Mattermost installation written in Go
* [Web App](/developers/contribute/more-info/webapp) - JavaScript client app built on React and Redux
* [Mobile Apps](/developers/contribute/more-info/mobile) - JavaScript client apps for Android and iOS built on React Native
* [Desktop App](/developers/contribute/more-info/desktop) - An Electron wrapper around the web app project that runs on Windows, Linux, and macOS
* [Core Plugins](/developers/contribute/more-info/plugins) - A core set of officially-maintained plugins that provide a variety of improvements to Mattermost.
* Core Integrations - Major Mattermost features including [Focalboard](/developers/contribute/more-info/focalboard) and [Playbooks](https://github.com/mattermost/mattermost-plugin-playbooks).
Improvements to Mattermost may require you to contribute to multiple projects; if youre unsure where to start, the server repository is generally the best way to get introduced to the codebase.
## How to contribute code
If youre looking for an existing issue to help with, check out the [help wanted tickets on GitHub](https://mattermost.com/pl/help-wanted). If you see any that youre interested in working on, comment on it to let everyone know youre working on it. If theres no ticket for what you want to contribute, see our guide on [contributing without a ticket.](/contributions-without-ticket)
Once youve created some code that you want to contribute, follow our [pull request checklist](/contribution-checklist) to submit your contribution for [review](/code-review), and one of our [core committers](/developers/contribute/more-info/getting-started/core-committers) will reach out with any feedback, questions, or requests they have.
## How to get help with your contribution
Our contributor community is segmented into [guilds](/developers/contribute/more-info/getting-started/guilds) that focus on specific components within the Mattermost ecosystem. Each guild has a leader and a channel on our [community chat server](https://docs.mattermost.com/guides/community-chat.html) where you can ask questions about your contribution.
@@ -0,0 +1,61 @@
---
title: "Labels"
sidebar_position: 7
---
We leverage [GitHub labels](https://help.github.com/en/articles/about-labels) to track the details and lifecycle of issues and pull requests.
# Issue labels
* `Area/<name>`: Involves changes to the named area (APIv4, E2E Tests, Localization, Plugins, etc.)
* `Bug Report/Open`: Bug report unresolved, awaiting for more information or in development backlog.
* `Bug Report/Scheduled for Release`: Bug report resolved and scheduled for an upcoming release. Milestone indicates scheduled release version.
* `Difficulty/1:easy`: Easy ticket.
* `Difficulty/2:medium`: Medium ticket.
* `Difficulty/3:hard`: Hard ticket.
* `Good First Issue`: Suitable for first-time contributors.
* `Help Wanted`: Community help wanted.
* `Move to Feature Ideas forum`: Marked for relocation to the feature ideas forum.
* `Move to Troubleshooting`: Marked for relocation to the troubleshooting section of the documentation.
* `PR Submitted`: A pull request has been opened for this issue.
* `Tech/<name>`: Requires using the named technology (Go, JavaScript, ReactJS, Redux, etc.)
* `Up for Grabs`: Ready for help from the community. Removed when someone volunteers.
# Pull request labels
* `1: PM Review`: Requires review by a [product manager](https://handbook.mattermost.com/contributors/contributors/core-committers#product-managers).
* `1: UX Review`: Requires review by a UX designer.
* `1: SME Review`: Requires review by a subject matter expert (used in the Handbook).
* `2: Dev Review`: Requires review by a [core committer](https://handbook.mattermost.com/contributors/contributors/core-committers#core-committers).
* `2: Editor Review`: Requires review by a [technical writer](https://handbook.mattermost.com/contributors/contributors/core-committers#technical-writers).
* `3: QA Review`: Requires review by a [QA tester](https://handbook.mattermost.com/contributors/contributors/core-committers#qa-testers). May occur at the same time as Dev Review.
* `4: Reviews Complete`: All reviewers have approved the pull request.
* `Awaiting Submitter Action`: Blocked on the author.
* `AutoMerge`: If all checks and approvals pass and the user adds this label, it will be in the queue to get merge automatically without a human intervention.
* `Changelog/Done`: Required changelog entry has been written.
* `Changelog/Not Needed`: Does not require a changelog entry.
* `CherryPick/Approved`: Meant for the quality or patch release tracked in the milestone.
* `CherryPick/Candidate`: A candidate for a quality or patch release, but not yet approved.
* `CherryPick/Done`: Successfully cherry-picked to the quality or patch release tracked in the milestone.
* `Demo Plugin Changes/Needed`: Requires changes to the demo plugin.
* `Demo Plugin Changes/Done`: Required changes to the demo plugin have been submitted.
* `Do Not Merge/Awaiting Loadtest`: Must be loadtested before it can be merged.
* `Do Not Merge/Awaiting Next Release`: To be merged with the next release (e.g. API documentation updates).
* `Do Not Merge/Awaiting PR`: Awaiting another pull request before merging (e.g. server changes).
* `Do Not Merge`: Should not be merged until this label is removed.
* `Docs/Done`: Required documentation has been written.
* `Docs/Needed`: Requires documentation.
* `Docs/Not Needed`: Does not require documentation.
* `Hackfest`: Related to a Mattermost hackathon.
* `Hacktoberfest`: Related to [Hacktoberfest](https://hacktoberfest.digitalocean.com/).
* `Lifecycle/<state>`: An [inactive contribution](/developers/contribute/more-info/getting-started/inactive-contributions).
* `Loadtest`: Triggers an automatic load test.
* `Major Change`: The pull request is a major feature or affects large areas of the code base (e.g. [moving channel store and actions to Redux](https://github.com/mattermost/platform/pull/6235)).
* `QA Deferred`: Testing of this PR is expected to be completed after merge, likely when it is available on Community. Apply this in lieu of asking for `3: QA Review`.
* `Setup Cloud Test Server`: Triggers the creation of a Enterprise Edition test server.
* `Setup HA Cloud Test Server`: Triggers the creation of a test server that has high availability.
* `Setup Cloud + CWS Test Server`: Triggers the creation of a test server that connects to our test Customer Web Server.
* `Setup Upgrade Test Server`: Triggers the creation a test server and performs an upgrade.
* `Tests/Done`: Required tests have been written.
* `Tests/Not Needed`: Does not require tests.
* `Work in Progress`: Not yet ready for review.
@@ -0,0 +1,13 @@
---
title: "Slash commands"
sidebar_position: 30
---
There are a couple of slash-commands available on GitHub which are implemented via [Mattermod](https://github.com/mattermost/mattermost-mattermod). They only work on PRs.
The commands are:
- `/cherry-pick $BRANCH_NAME`, e.g. `/cherry-pick release-5.10`: Opens a PR to cherry-pick a change into the branch `$BRANCH_NAME`. This command only works for the submitter of the PR and members of the Mattermost organization.
- `/check-cla`: Checks if the PR contributor has signed the CLA.
- `/autoassign`: Automatically assigns reviewers to a PR.
- `/update-branch`: Updates the pull request branch with the latest upstream changes by merging HEAD from the base branch into the pull request branch. This command only works for members of the Mattermost organization.
@@ -0,0 +1,72 @@
---
title: "Test guidelines"
sidebar_position: 6
---
At Mattermost, we write tests because we want to be confident that our product works as expected for our users. As developers, we write tests as a gift to our future selves or to be confident that changes won't cause regressions or unintended behaviors. We value contributors who write tests as much as any others, and we want the process to be integrated in our core development workflow rather than being an afterthought or follow-up action.
This page stresses the importance of tests, including for every pull request being submitted. It is the foundation of our test guidelines, and serves as a reference on why we do not merge code without tests. This is not to meet higher code coverage but rather to write effective and well-planned use cases depending on the changes being made. But of course, there's always an exception. If, for some reason, it isn't possible to write a test, let the reviewers know by writing a description to start a discussion and to fully understand the situation you are facing.
Test categories
---------------
Not all test types are required in a single pull request. Only write whichever test types are most effective and appropriate.
1. __Unit tests__ - Unit tests verify that individual, isolated parts work as expected.
2. __Integration tests__ - Integration tests verify that several units work together in harmony.
3. __End-to-End (E2E) tests__ - End-to-End tests exercise most of the parts of a large application.
<Note title="Note">
Wisdom and definitions mostly taken from [Martin Fowler's Software Testing Guide](https://martinfowler.com/testing/) and [Kent C. Dodds's personal site](https://kentcdodds.com/).
</Note>
In general, when are tests necessary?
-------------------------------------
- For all files written in the main language(s) of the repository; this could be JavaScript/Typescript and JSX/TSX files, Go files, or both which are exported such as functions, modules, or components used in various places.
- Un-exported functions or methods, which have low or no test coverage from the parent exported function/method, that affect critical functionality or behavior of the application.
- New features and bug fixes, especially those originating from customer and community bugs.
When is it fine not to have tests?
--------------------------------------------
- For implementation details of standard libraries or external packages that are implicitly covered by the standard library or external package itself.
- Where the situation may require external services running to effectively test the functionality, such as dependencies on feature flags via [Split](https://split.io), OAuth with third-party providers such as Google, etc.
- Tests should be made at the most effective and lowest possible level, but if it requires too much effort or complicated setup to accomplish at a unit test level, it would be best to skip and assess feasibility on the next level such as integration or end-to-end testing.
- Mocks and test helpers.
- Types only.
- Interfaces only or interfaces to other repositories, such as with private Enterprise via “interfaces”.
- End-to-end tests codebase.
- Automatically generated code for database migrations, store layers, etc.
- External dependencies, modules, imports, or vendors.
How to run and write tests
------------------
### Server
For writing and running **unit tests** in general, see the [Server workflow](/developers/contribute/more-info/server/developer-workflow) page. If you have written a new endpoint or changed an endpoint for the Mattermost REST API, check out the [REST API](/developers/contribute/more-info/server/rest-api) page.
### Web App
For writing and running **unit tests** in general, see the [Unit tests](/developers/contribute/more-info/webapp/unit-testing) page. For writing and running **E2E tests** in general, see the [End-to-End testing](/developers/contribute/more-info/webapp/e2e-testing) section, and the [End-to-End cheatsheets](/developers/contribute/more-info/webapp/e2e-cheatsheets) section.
For writing and running **E2E (and unit) tests for Redux** components, see the [Redux Unit and E2E Testing](/developers/contribute/more-info/webapp/redux/testing) page.
### Mobile Apps
For writing and running **E2E tests** for both Android and iOS systems, take a look at the [Mobile End-to-End (E2E) Tests](/developers/contribute/more-info/mobile/e2e) page.
### Desktop App
For writing and running **unit and E2E tests** for the desktop app, check out the [Unit and End-to-End (E2E) Tests in the desktop app](/developers/contribute/more-info/desktop/testing) page.
# How to Contribute E2E Tests
If you're looking to improve your development skills or improve your familiarity with the Mattermost code base, issues for E2E tests that are marked Help Wanted are a great place to start.
* Look for [issues in the mattermost](https://github.com/mattermost/mattermost/issues?q=is%3Aissue+is%3Aopen+e2e) repository that have the `Help Wanted` label and either the `Area/E2E Tests` label or something related to E2E in the issue title.
* Once you find an issue you would like to work on, comment on the issue to claim it.
* Each issue is filled with specific test steps and verifications that need to be accomplished as a minimum requirement. Additional steps and assertions for robust test implementation are very welcome. The contents of an E2E issue follow this general format:
* **Steps**: What the code in the test should do and/or emulate.
* **Expected**: What the results of the test should be.
* **Test Folder**: Where the file that holds the test code should be located.
* **Test code arrangement**: Starter code for the test.
* **Notes**: Comments on what to add and not to add to the test file, plus resources for contributions, asking questions, etc.
If you'd like to see an example of an ideal E2E test contribution, please view these issues and their associated PRs:
* [Write Webapp E2E with Cypress: "MM-T642 Attachment does not collapse"](https://github.com/mattermost/mattermost/issues/18184)
* [Cypress test: "CTRL/CMD+K - Open private channel using arrow keys and Enter"](https://github.com/mattermost/mattermost/issues/14078)
@@ -0,0 +1,31 @@
---
title: "Test servers"
sidebar_position: 8
---
As part of the pull request review process, reviewers may need to test and verify proposed changes. Leveraging our Cloud infrastructure, we can spin up full environments on demand to test code submitted in PRs.
Core committers and staff can trigger test server creation on a PR by adding one of the following labels to the PR:
* `Setup Cloud Test Server`: Triggers the creation of a standard test server using the latest commit on the PR and a PostgreSQL database.
* `Setup HA Cloud Test Server`: Triggers the creation of a test server that has high availability.
* `Setup Cloud + CWS Test Server`: Triggers the creation of a test server that connects to our test Customer Web Server.
After adding these labels, a bot will comment on the PR notifying you that a test server is being created. It should take approximately 3-5 minutes for the server to create it. Once it's ready, the bot will comment on the PR again with a link and credentials to the test server for both an admin and a regular user.
If the bot comments that an error has occurred, try removing the label and then re-adding it again. If that still fails, please ask for help in [~Developers: Cloud](https://community.mattermost.com/core/channels/cloud). If you need urgent help, please mention `@sresupport` in your message.
Once testing is complete, remove the label and the test server will be destroyed.
Test servers are available on any repositories that have the labels.
# Tips and tricks
* Avoid adding and removing the labels quickly in succession - this can confuse the bot and result in issues. Please be patient. :)
* Pushing new commits to PRs will trigger the bot to automatically update the test server.
* When submitting a new PR or pushing an update to a PR, the docker build/push CI step must complete before a test server can be created/updated.
* Please make sure to remove labels when testing is complete since there is a limited capacity for test servers.
* If you want a test server to have the changes from two different PRs across the webapp and server repositories, ensure:
* Both server and webapp branches are named the same and are on the main repos and not from forks.
* The server build completes before the webapp build runs (you can re-trigger the webapp build if it didn't).
* The test server was created after the webapp build is complete (that included the server build) and present on the PR for the web app.
@@ -0,0 +1,112 @@
---
title: "Using Gitpod"
sidebar_position: 6
---
### What is Gitpod?
[Gitpod](https://www.gitpod.io/) is a cloud development environment. The following instructions have been adapted from comments on this issue: [Document general usage of Gitpod #18 ](https://github.com/mattermost/mattermost-gitpod-config/issues/18). You can also check out these videos to learn how to work with Gitpod (and write an E2E test): [How to set up a developer environment for Mattermost with Gitpod](https://www.youtube.com/watch?v=LgQ2Z_GelYQ) and [Writing your first E2E test for Mattermost](https://www.youtube.com/watch?v=mLbzKSGZv4A).
#### :cyclone: Spinning up an environment
1. Create a new workspace for a ticket/issue that you've claimed by going to the [mattermost-gitpod-config](https://github.com/mattermost/mattermost-gitpod-config/tree/master) repository and clicking the "Open in Gitpod" badge.
* You can also use the [Gitpod browser extension](https://www.gitpod.io/docs/configure/user-settings/browser-extension) to open up a repository in Gitpod, or manually prefix GitHub repository URLs with `gitpod.io/#`. What the extension does is add a green Gitpod button to repository pages on GitHub, and clicking it spins up a new environment for the repository on Gitpod.
![mattermost-gitpod-config-repo](https://user-images.githubusercontent.com/43153413/194467192-675a6b15-bb3b-4a4d-be05-f1df0fbdd524.jpeg)
2. You may need to sign in (through GitHub) to access the workspace on Gitpod. Once Gitpod has done loading, the user interface presented is that of VSCode.
![mattermost-gitpod-intro](https://user-images.githubusercontent.com/43153413/194467255-98b5a9be-85a5-4da8-b519-279011882384.jpeg)
#### :pencil2: Working on an issue/ticket
3. Make changes to one of the projects. For example, you could be working on writing an End-to-End (E2E) test, like this issue [Write Webapp E2E with Cypress: "MM-T642 Attachment does not collapse" #18184](https://github.com/mattermost/mattermost/issues/18184). The development process is very similar to how you would work locally.
#### :herb: Making branches and forks
4. You most likely won't have direct write access to the repository you are working on. Thus, you will need to bring the changes you've made over from the `master` or `main` branch to a new branch on your own fork of the repository.
5. Click the "Source Control" icon on the left sidebar. On the panel next to the side bar, you will see a list of the repositories in the workspace, and under each repository a list of the files that have changed (if any). Next to the label for `mattermost-webapp`, click the button with the branch name and the source control icon.
6. A dropdown will appear via the command palette. Click the option that says: "+ Create new branch..."
![mattermost-gitpod-new-branch](https://user-images.githubusercontent.com/43153413/194467696-498917fe-14a3-4cbc-ac35-1b201ce3c730.jpeg)
7. In the box that appears, name your new branch. A good name idea is to name it after the code that refers to your issue - in this case, this is the "Test Key", so a suitable name for the branch is `MM-T642`. Then, hit `Enter` on your keyboard.
![mattermost-gitpod-branch-name](https://user-images.githubusercontent.com/43153413/194467742-e7312d5d-dbb3-4bcc-a5af-b67941bbf4f2.jpeg)
8. Now that the branch has changed, you can commit your changes to it. Back in the source control panel, you can make a commit message in the text box above the "commit button" in the section for the `mattermost-webapp` repository. Then, press the "commit button". A modal may appear asking you to first stage your changes.
![mattermost-gitpod-commit-message](https://user-images.githubusercontent.com/43153413/194467789-0f588a7c-ff8b-4bc9-adaf-5d224726b2a5.jpeg)
9. You can now publish your branch to the remote. However, as you will not have access to the main repository itself, you will be prompted to first create a fork. Click on the "publish branch button" on the source control panel. A popup will appear, asking if you would like to make a fork and push to that instead. On the popup, select the "create fork" button.
![mattermost-gitpod-make-fork](https://user-images.githubusercontent.com/43153413/194468420-14564230-fb63-442c-b0e6-815aeb799da5.jpeg)
10. During the process of fork creation, you may be prompted to grant GitHub access to Gitpod's GitHub extension (you should allow this).
![mattermost-gitpod-sign-into-GitHub](https://user-images.githubusercontent.com/43153413/194468466-79cf2804-5393-4a1c-994f-9c087ff42b1d.jpeg)
11. You may also be asked to update the permissions you give Gitpod to access GitHub through a popup. If this happens, you will have to restart the fork creation process (publish branch -> say yes to creating a fork). Click the "open access control" button on the popup.
![mattermost-gitpod-need-perms](https://user-images.githubusercontent.com/43153413/194468523-f7c7ce87-3586-48bf-b779-38253de0059e.jpeg)
12. You will get taken to the Integration section of Gitpod's settings. In the list of Git Providers on the page, find GitHub, click the three dots next to the listing, and select "Edit Permissions" from the dropdown menu.
![mattermost-gitpod-open-perms](https://user-images.githubusercontent.com/43153413/194468548-7b31a634-dd4a-49bb-a697-a8b3e5aa39db.jpeg)
13. A popup will appear with a list of permission checkboxes. It's a good idea to check all of them off so you don't need to go through this process again. When you're done checking off the permissions, click the "Update Permissions" button.
![mattermost-gitpod-select-perms](https://user-images.githubusercontent.com/43153413/194468567-fba1fc58-f4db-4787-8dcd-d594472d7692.jpeg)
14. Another tab might also appear where on GitHub's end you accept the additional permissions Gitpod is requesting. Click on the "authorize gitpod-io" button.
![mattermost-gitpod-confirm-perms](https://user-images.githubusercontent.com/43153413/194468598-1866b681-ae3b-42a0-b94c-c02735776e02.jpeg)
15. Once the forking process is done, there will be a couple of popups that appear on Gitpod: one asking if you'd like to periodically run `git fetch` (which will periodically download content from the remote), another one asking if you'd like to create a pull request for the branch you're on right inside Gitpod/the VSCode editor, and finally one informing that the fork was successfully created. On the successful fork creation popup, click the `open on GitHub` option.
![mattermost-gitpod-open-fork-on-GitHub](https://user-images.githubusercontent.com/43153413/194468614-a89701c4-8b09-4b80-b84e-a0beaca8431c.jpeg)
#### :mag: Creating a Pull Request (PR)
16. You'll get taken to GitHub, to the fork of the repository you've worked on in your account instead of the main one in the Mattermost organization. Navigate to the branch you've made on your fork if you're not there already.
* Near the top of the page will be a bar mentioning how many commits ahead/behind your branch is from the `master`/`main` branch of the main repository. There will also be two buttons: "contribute" and "sync fork". Click on the "contribute" button, and in the dropdown that appears, click the "open pull request" button.
![mattermost-gitpod-create-PR-on-GitHub](https://user-images.githubusercontent.com/43153413/194468644-8c625663-17d4-4187-92be-171b683d298f.jpeg)
17. You'll be taken to another page on GitHub called "Open a pull request".
![mattermost-gitpod-PR-creation-page](https://user-images.githubusercontent.com/43153413/194468667-aace5ac5-4bbe-4e93-9902-e210964e00c8.jpeg)
```text
* The first section of the page compares whether the branches (your branch on your fork - the "head repository" vs. the master/main branch on the main repository - the "base repository") can be merged automatically.
* The second section of the page will show other PRs that are based on your same branch, if any.
* The third section is where you create a write-up for your pull request - giving it a title, and filling out the template. At the bottom of this section is a "Create pull request" button. This button will be faded out until you make a title for the PR.
* If you haven't already, give Mattermost's [Contribution Checklist](https://developers.mattermost.com/contribute/more-info/getting-started/contribution-checklist/) a read. An important takeaway is that you will need to sign the [Contributor License Agreement](https://mattermost.com/mattermost-contributor-agreement/) - this will be another check on the pull request and if you haven't signed it, this will also block merging.
* Also check out this blog post about [Submitting Great PRs](https://developers.mattermost.com/blog/2019-01-24-submitting-great-prs), and other repository specific information [here](https://developers.mattermost.com/contribute).
* **Parts of a PR body**:
* _Title_: a good title will refer back to the issue; and it should begin with the related Jira or GitHub ticket ID (e.g. [MM-394] or [GH-394]). In the context of the E2E issue example: `MM-T642: Attachment does not collapse - Cypress Webapp E2E Test`.
* _Summary_: description of what the PR does, as well as QA test steps (if applicable and if not already added to the Jira ticket). For example: `Verifies that attachments on posts do not collapse after entering the slash command collapse`.
* _Ticket Link_: Either link the relevant Jira ticket or if you picked up an issue/ticket with a `Helped Wanted` label, link to the GitHub issue. For example: [Write Webapp E2E with Cypress: "MM-T642 Attachment does not collapse" #18184](https://github.com/mattermost/mattermost/issues/18184).
* _Related Pull Requests_: Link other PRs here if they are related to this PR.
* _Screenshots_: Illustrate what your changes have done.
* _Release Note_: There are certain conditions that require release notes:
* Config changes (additions, deletions, updates).
* API additions—new endpoints, new response fields, or newly accepted request parameters.
* Database changes (any).
* Schema migration changes. Use the [Schema Migration Template](https://docs.google.com/document/d/18lD7N32oyMtYjFrJKwsNv8yn6Fe5QtF-eMm8nn0O8tk/edit?usp=sharing) as a starting point to capture these details as release notes.
* Websocket additions or changes.
* Anything noteworthy to a Mattermost instance administrator (err on the side of over-communicating).
* New features and improvements, including behavioral changes, UI changes, and CLI changes.
* Bug fixes and fixes of previous known issues.
* Deprecation warnings, breaking changes, or compatibility notes.
<br/><br/>
If no release notes are required, write NONE. Use past-tense. For E2E tests, having `NONE` as a release note suffices. If you do not end up writing a release note at all, you'll get a warning on your PR like this: `Adding the "do-not-merge/release-note-label-needed" label [to the PR] because no release-note block was detected, please follow our release note process to remove it.` If this happens, you can just edit the body of the PR, and add it back in.
* The last section details the code changes on your branch, including information on the commits on the branch, the files changed, and the contributors.
```
18. Once you've created your pull request, you'll get taken to its page, like this one: [MM-T642: Attachment does not collapse - Cypress Webapp E2E Test #11231](https://github.com/mattermost/mattermost/pull/11231). Below your initial body text of the PR will be a list of commits and other comments. At the end of this list is a checklist which notes the status of reviews required for the pull request, and the checks that the pull request must pass, plus a place to write your own additional comments.
![mattermost-gitpod-real-PR-1](https://user-images.githubusercontent.com/43153413/194468726-afddf66f-eaf1-4dab-a6bf-7ddf39db78bf.jpeg)
19. if you need to make any changes to your PR, you can return to Gitpod and stage and commit your changes from there onto your branch; and this will be reflected in GitHub.
### :white_check_mark: Code Review
Information from this section comes from: [Code review at Mattermost](https://developers.mattermost.com/contribute/more-info/getting-started/code-review/).
20. Wait for a reviewer to be assigned - normally this is handled automatically, but if you need help feel free to ask for help in the [Developers channel](https://community.mattermost.com/core/channels/developers) of the Mattermost community server.
21. Wait for a review - if a reviewer requests changes, your PR will disappear from their queue of reviews. Once you've addressed the concerns, re-request a review from any person requesting changes. Avoid force pushing, which is the act of overwriting the commit history on the remote with what you have on local.
22. Once all reviewers approve your pull request, they will handle the merging of your code.
@@ -0,0 +1,28 @@
---
title: "Where to find more information?"
sidebar_position: 4
---
Here are a few links to Mattermost content that you might find helpful depending on the specific area where youll be contributing.
## Mattermost resources
1. Specific localization guides (coming soon!)
2. [Mattermost contributor agreement](https://mattermost.com/mattermost-contributor-agreement/)
3. [Approved contributor list](https://docs.google.com/spreadsheets/d/1NTCeG-iL_VS9bFqtmHSfwETo5f-8MQ7oMDE5IUYJi_Y/pubhtml?gid=0&single=true)
4. [The API](https://api.mattermost.com)
5. [Incoming](/developers/integrate/webhooks/incoming) and [outgoing](/developers/integrate/webhooks/outgoing) webhooks
6. [Plugins](/developers/integrate/plugins), also [Plugins](/developers/contribute/more-info/plugins)
7. [Making slash commands](/developers/integrate/slash-commands)
8. [Embedding Mattermost in other places](/developers/integrate/customization/embedding)
9. [Server project](/developers/contribute/more-info/server)
10. [Webapp project](/developers/contribute/more-info/webapp)
11. [Mobile apps](/developers/contribute/more-info/mobile)
12. [Desktop app](/developers/contribute/more-info/desktop)
13. [Focalboard](/developers/contribute/more-info/focalboard)
14. [Playbooks](https://github.com/mattermost/mattermost-plugin-playbooks)
## External resources
1. [How ICU syntax works](https://formatjs.io/docs/core-concepts/icu-syntax/)
2. [Use gender-neutral language in communications and in content](https://apastyle.apa.org/style-grammar-guidelines/grammar/singular-they)
@@ -0,0 +1,136 @@
---
title: "Build the Android mobile app"
sidebar_position: 1
---
At times, you may want to build your own Mattermost mobile app. The most common use cases are:
* To white label the Mattermost mobile app.
* To use your own deployment of the Mattermost Push Notification Service (always required if you are building your own version of the mobile app).
# Build preparations
### 1. Package name and source files
* Ensure the package ID of the mobile app remains the same as the one in the original [mattermost-mobile GitHub repository](https://github.com/mattermost/mattermost-mobile) in `com.mattermost.rnbeta`.
* Source files for the main package remain under the `android/app/src/main/java/com/mattermost/rnbeta` folder.
### 2. Generate a signing key
As Android requires all apps to be digitally signed with a certificate before they can be installed building the Android app for distribution requires the release APK to be signed.
To generate the signed key, use **keytool** which comes with the JDK required to develop the Android app. (see [Developer Setup](/developers/contribute/more-info/mobile/developer-setup#additional-setup-for-android)).
```sh
$ keytool -genkey -v -keystore <my-release-key>.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000
```
The above command prompts you for passwords for the keystore and key (make sure you use the same password for both), and asks you to provide the Distinguished Name fields for your key. It then generates the keystore as a file called `<my-release-key>.keystore`.
The keystore contains a single key, valid for 10000 days. The alias is a name that you will use later when signing your app, so remember to take a note of the alias.
---
<Note title="Note">
* Replace `<my-release-key>` with the filename you want to specify.
* Remember to keep your keystore file private and never commit it to version control.
</Note>
---
### 3. Create a new app in Google Play
Create a new application using the [Google Play console](https://play.google.com/console/developers). If you already have an app registered in the Google Play console you can skip this step.
### 4. Set up Gradle variables
Now that we have created the keystore file we can tell the build process to use that file:
- Copy or move the `my-release-key.keystore` file under a directory that you can access. It can be in your home directory or anywhere in the file system.
- Edit the `gradle.properties` file in your `$HOME` directory (e.g. `$HOME/.gradle/gradle.properties`), or create it if one does not exist, and add the following:
```sh
MATTERMOST_RELEASE_STORE_FILE=/full/path/to/directory/containing/my-release-key.keystore
MATTERMOST_RELEASE_KEY_ALIAS=my-key-alias
MATTERMOST_RELEASE_PASSWORD=*****
```
---
<Note title="Note">
* Replace `/full/path/to/directory/containing/my-release-key.keystore` with the full path to the actual keystore file and `********` with the actual keystore password.
* Back up your keystore and don't forget the password.
</Note>
---
---
**Important:**
Once you publish the app on the Play Store, the app needs to be signed with the same key every time you want to distribute a new build. If you lose this key, you will need to republish your app under a different package id (losing all downloads and ratings).
---
### 5. Configure environment variables
To make it easier to customize your build, we've defined a few environment variables that are going to be used by Fastlane during the build process.
| Variable | Description | Default | Required |
|----------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------------|----------|
| `COMMIT_CHANGES_TO_GIT` | Should the Fastlane script ensure that there are no changes to Git before building the app and that every change made during the build is committed back to Git. <br/><br/>Valid values are: `true`, `false` | `false` | No |
| `BRANCH_TO_BUILD` | Defines the Git branch that is going to be used for generating the build. <br/><br/>**Make sure that, if this value is set, the branch it is set to exists**. | `$GIT_BRANCH` | No |
| `GIT_LOCAL_BRANCH` | Defines the local branch to be created from `BRANCH_TO_BUILD` to ensure the base branch does not get any new commits on it. <br/><br/>**Make sure a branch with this name does not yet exist in your local Git repository**. | build | No |
| `RESET_GIT_BRANCH` | Defines if, once the build is done, the branch should be reset to the initial state before building and whether to also delete the branch created to build the app. <br/><br/>Valid values are: `true`, `false` | `false` | No |
| `VERSION_NUMBER ` | Set the version of the app at build time to a specific value, rather than using the one set in the project. | | No |
| `INCREMENT_VERSION_NUMBER_MESSAGE` | Set the commit message when changing the app version number. | Bump app version number to | No |
| `INCREMENT_BUILD_NUMBER` | Defines if the app build number should be incremented. <br/><br/>Valid values are: `true`, `false` | `false` | No |
| `BUILD_NUMBER` | Set the build number of the app at build time to a specific value, rather than incrementing the last build number. | | No |
| `INCREMENT_BUILD_NUMBER_MESSAGE` | Set the commit message when changing the app build number. | Bump app build number to | No |
| `ANDROID_BUILD_TASK` | The build tasks for Android. This is a comma-separated list of tasks that can have two values: 'assemble' and 'bundle'. <br/><br/>`assemble` is used for building `APK` file and `bundle` is used for building `AAB` file. | assemble | No |
| `APP_NAME` | The name of the app as it is going to be shown on the device home screen. | Mattermost Beta | Yes |
| `APP_SCHEME` | The URL naming scheme for the app as used in direct deep links to app content from outside the app. | mattermost | No |
| `REPLACE_ASSETS` | Override the assets as described in [White Labeling](/developers/contribute/more-info/mobile/build-your-own/white-label). <br/><br/>Valid values are: `true`, `false` | `false` | No |
| `MAIN_APP_IDENTIFIER` | The package identifier for the app. | | Yes |
| `BUILD_FOR_RELEASE` | Defines if the app should be built in release mode. <br/><br/>Valid values are: `true`, `false` <br/><br/>**Make sure you set this value to true if you plan to submit this app Google Play or distribute it in any other way**. | `false` | Yes |
| `SEPARATE_APKS` | Build one APK per achitecture (armeabi-v7a, x86, arm64-v8a and x86_64) as well as a universal APK. The advantage is the size of the APK is reduced by about 4MB. <br/><br/>People will download the correct APK from the Play Store based on the CPU architecture of their device. | `false` | Yes |
| `SUBMIT_ANDROID_TO_GOOGLE_PLAY` | Should the app be submitted to the Play Store once it finishes building, use along with `SUPPLY_TRACK`.<br/><br/>Valid values are: `true`, `false` | `false` | Yes |
| `SUPPLY_TRACK` | The track of the application to use when submitting the app to Google Play Store. Valid values are: `alpha`, `beta`, `production` <br/><br/>**RIt is not recommended to submit the app to production. First try any of the other tracks and then promote your app using the Google Play console**. | `alpha` | Yes |
| `SUPPLY_PACKAGE_NAME` | The package Id of your application, make sure it matches `MAIN_APP_IDENTIFIER`. | | Yes |
| `SUPPLY_JSON_KEY` | The path to the service account `json` file used to authenticate with Google.<br/><br/>See the [Supply documentation]( https://docs.fastlane.tools/actions/supply/#setup) to learn more. | | Yes |
---
<Note title="Note">
To configure your variables create the file `./mattermost-mobile/fastlane/.env` where `.env` is the filename. You can find the sample file `env_vars_example` [here](https://github.com/mattermost/mattermost-mobile/blob/master/fastlane/env_vars_example).
</Note>
---
### 6. Google services
Replace the `google-services.json` file as instructed in the [Android Push Notification Guide](/developers/contribute/more-info/mobile/push-notifications/android) before you build the app.
## Build the mobile app
Once all the previous steps are done, execute the following command from within the project's directory:
```sh
$ npm run build:android
```
This will start the build process following the environment variables you've set. Once it finishes, it will create the `.apk` file(s) with the `APP_NAME` as the filename in the project's root directory. If you have not set Fastlane to submit the app to the Play Store, you can use this file to manually publish and distribute the app.
## Frequently Asked Questions
### How do I update the lock file?
We use lockfiles to lock dependencies and make sure the builds are reproductible. If we want to update the lockfile to update all dependencies to the latest, we can run these commands:
```
cd android
./gradlew app:dependencies --update-locks "*:*"
```
In case we want to regenerate the lockfile from the scratch, we can delete the `android/buildscript-gradle.lockfile` and then run the following commands:
```
cd android
./gradlew app:dependencies --write-locks
```
@@ -0,0 +1,27 @@
---
title: "Build your own mobile app"
sidebar_position: 2
---
You can build the app from source and distribute it within your team or company either using the App Stores, Enterprise App Stores or EMM providers, or another way of your choosing.
At Mattermost, we build and deploy the Apps using a CI pipeline. The pipeline has different jobs and steps that run on specific contexts based on what we want to accomplish. You can check it out [here](https://github.com/mattermost/mattermost-mobile/blob/main/.github/workflows).
As an alternative we've also created a set of **scripts** to help automate build tasks. Learn more about the scripts by reviewing the [package.json](https://github.com/mattermost/mattermost-mobile/blob/master/package.json) file.
<Note title="Note">
By using the **scripts**, [Fastlane](https://docs.fastlane.tools/#choose-your-installation-method) and other dependencies will be installed in your system.
</Note>
- [Build the Android app](/developers/contribute/more-info/mobile/build-your-own/android)
- [Build the iOS app](/developers/contribute/more-info/mobile/build-your-own/ios)
### Push notifications with your own mobile app
When building your own Mattermost mobile app, you will also need to host the [Mattermost Push Notification Service](https://github.com/mattermost/mattermost-push-proxy) in order to receive push notifications.
See [Setup Push Notifications](/developers/contribute/more-info/mobile/push-notifications) for more details.
@@ -0,0 +1,91 @@
---
title: "Build the iOS mobile app"
sidebar_position: 2
---
At times, you may want to build your own Mattermost mobile app. The most common use cases are:
* To white label the Mattermost mobile app.
* To use your own deployment of the Mattermost Push Notification Service (always required if you are building your own version of the mobile app).
# Build preparations
### 1. Prerequisites
The Mattermost mobile app for iOS needs to be built on a macOS computer with Xcode and the Xcode command line tools installed.
```sh
$ xcode-select --install
```
### 2. Bundle ID and entitlements
* Follow the steps 1, 2, and 3 for [Run on iOS Devices](/developers/contribute/more-info/mobile/developer-setup/run#run-on-ios-devices) in the Developer Setup.
* After configuring the app in the previous step, **ensure** the bundle ID for each target of the mobile app remains the same as the one in the original [mattermost-mobile GitHub repository](https://github.com/mattermost/mattermost-mobile) (`com.mattermost.rnbeta`, `com.mattermost.rnbeta.MattermostShare`, and `com.mattermost.rnbeta.NotificationService`).
### 3. Code sign
Apple requires all apps to be digitally signed with a certificate before they can be installed.
The build script will make use of [Match](https://docs.fastlane.tools/actions/match/) to sync your provisioning profiles (the profiles will be created for you if needed). The provisioning profiles will be created based on the environment variables.
### 4. Configure environment variables
To make it easier to customize your build, we've defined a few environment variables that are going to be used by Fastlane during the build process.
| Variable | Description | Default | Required |
|-----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------|----------|
| `COMMIT_CHANGES_TO_GIT` | Should the fastlane script ensure that there are no changes to Git before building the app and that every change made during the build is committed back to Git.<br/><br/>Valid values are: `true`, `false` | `false` | No |
| `BRANCH_TO_BUILD` | Defines the Git branch that is going to be used for generating the build. <br/><br/>**Make sure that, if this value is set, the branch it is set to exists**. | `$GIT_BRANCH` | No |
| `GIT_LOCAL_BRANCH` | Defines the local branch to be created from BRANCH\_TO\_BUILD to ensure the base branch does not get any new commits on it.<br/><br/>**Make sure a branch with this name does not yet exist in your local git**. | build | No |
| `RESET_GIT_BRANCH` | Defines if, once the build is done, the branch should be reset to the initial state before building and whether to also delete the branch created to build the app. <br/><br/>Valid values are: `true`, `false` | `false` | No |
| `VERSION_NUMBER` | Set the version of the app at build time to a specific value, rather than using the one set in the project. | | No |
| `INCREMENT_VERSION_NUMBER_MESSAGE` | Set the commit message when changing the app version number. | Bump app version number to | No |
| `INCREMENT_BUILD_NUMBER` | Defines if the app build number should be incremented.<br/><br/>Valid values are: `true`, `false` | `false` | No |
| `BUILD_NUMBER` | Set the build number of the app at build time to a specific value, rather than incrementing the last build number. | | No |
| `INCREMENT_BUILD_NUMBER_MESSAGE` | Set the commit message when changing the app build number. | Bump app build number to | No |
| `APP_NAME` | The name of the app as it is going to be shown in the device home screen. | Mattermost Beta | Yes |
| `APP_SCHEME` | The URL naming scheme for the app as used in direct deep links to app content from outside the app. | mattermost | No |
| `REPLACE_ASSETS` | Override the assets as described in [White Labeling](/developers/contribute/more-info/mobile/build-your-own/white-label).<br/><br/>Valid values are: `true`, `false` | `false` | No |
| `MAIN_APP_IDENTIFIER` | The bundle identifier for the app. | | Yes |
| `BUILD_FOR_RELEASE` | Defines if the app should be built in release mode. <br/><br/>Valid values are: `true`, `false` <br/><br/>**Make sure you set this value to true if you plan to submit this app to TestFlight, the Apple App Store or distribute it in any other way**. | `false` | Yes |
| `NOTIFICATION_SERVICE_IDENTIFIER` | The bundle identifier for the notification service extension. | | Yes |
| `EXTENSION_APP_IDENTIFIER` | The bundle identifier for the share extension. | | Yes |
| `FASTLANE_TEAM_ID` | The ID of your Apple Developer Portal Team. | | Yes |
| `IOS_ICLOUD_CONTAINER` | The iOS iCloud container identifier used to support iCloud storage. | | Yes |
| `IOS_APP_GROUP` | The iOS App Group identifier used to share data between the app and the share extension. | | Yes |
| `SYNC_PROVISIONING_PROFILES` | Should we run **match** to sync the provisioning profiles. **Note**: Not syncing the provisioning profiles, will cause the singing to fail. Valid values are: `true`, `false` | `false` | Yes |
| `MATCH_USERNAME` | Your Apple ID Username. | | Yes |
| `MATCH_PASSWORD` | Your Apple ID Password. | | Yes |
| `MATCH_KEYCHAIN_PASSWORD` | Your Mac user password used to install the certificates in the build computer KeyChain. | | No |
| `MATCH_GIT_URL` | URL to the Git repo containing all the certificates. <br/><br/> **Make sure this Git repo is set to private. Remember this repo will be used to sync the provisioning profiles and other certificates**. | | Yes |
| `MATCH_APP_IDENTIFIER` | The Bundle Identifiers for the app (comma-separated).<br/><br/>**List the identifiers for each target of the app**. | for example:<br/>`com.mattermost.rnbeta`, <br/>`com.mattermost.rnbeta.MattermostShare`, <br/>`com.mattermost.rnbeta.NotificationService` | Yes |
| `MATCH_TYPE` | Define the provisioning profile type to sync. Valid values are: `appstore`, `adhoc`, `development`, `enterprise` <br/><br/>**Make sure you set this value to the same type as the `IOS_BUILD_EXPORT_METHOD` as you want to have the same provisioning profiles installed in the machine so they are found when signing the app**. | `adhoc` | Yes |
| `SUBMIT_IOS_TO_TESTFLIGHT` | Submit the app to TestFlight once the build finishes. Valid values are: `true`, `false` | `false` | No |
| `PILOT_USERNAME` | Your Apple ID Username used to deploy the app to TestFlight. | | No |
| `PILOT_SKIP_WAITING_FOR_BUILD_PROCESSING` | Do not wait until TestFlight finishes processing the app.<br/><br/>Valid values are: `true`, `false` | `true` | No |
---
<Note title="Note">
To configure your variables create the file `./mattermost-mobile/fastlane/.env` where `.env` is the filename. You can find the sample file `env_vars_example` [here](https://github.com/mattermost/mattermost-mobile/blob/master/fastlane/env_vars_example).
</Note>
---
## Build the mobile app
Once all the previous steps are complete, execute the following command from within the project's directory:
```sh
$ npm run build:ios
```
This will start the build process following the environment variables you've set. Once it finishes, it will create an `.ipa` file with the `APP_NAME` as the filename in the project's root directory. If you have not set Fastlane to submit the app to TestFlight, you can use this file to manually publish and distribute the app.
# Troubleshooting
## I keep receiving `Invalid username and password combination.` but the user and password are correct
Apple IDs must be lowercase. A username like "Example@icloud.com" may not work properly, while "example@icloud.com" will. Also ensure you have recently changed your Apple ID password. "Old" passwords may be blocked by Apple when not connecting through a browser, so Apple may block Fastlane. Resetting your password may solve this issue.
@@ -0,0 +1,57 @@
---
title: "Enable SSL Pinning certificates"
sidebar_position: 2
---
##### What is SSL Pinning?
SSL (Secure Sockets Layer) pinning is a technique used in mobile app development to ensure that the app communicates only with a server that has a specific certificate. This is done by embedding the servers certificate in the app itself and then validating the servers certificate against this embedded certificate during communication. If the server's certificate does not match the pinned certificate, the connection is rejected.
##### Advantages of SSL Pinning
1. **Increased Security**: Protects against man-in-the-middle (MITM) attacks by ensuring that the app only communicates with trusted servers.
2. **Trustworthiness**: Guarantees that the data sent and received is from the expected server.
3. **Prevention of Certificate Spoofing**: Ensures that the servers certificate is exactly what is expected, preventing spoofing attempts.
##### Disadvantages of SSL Pinning
1. **Certificate Management**: Requires regular updates to the app when the servers certificate is renewed or changed.
2. **Deployment Complexity**: Coordination between development and deployment teams is necessary to avoid disruptions during certificate rotations.
3. **Maintenance Overhead**: Adds additional steps and complexity to the apps maintenance process.
<Note title="Important Note">
SSL pinning requires that both development and deployment teams understand and follow best practices for cryptographic key management, certificate rotation, and incident response.
- Coordinating the timing of certificate updates and app updates is crucial to minimize impact on end-users. When the server's SSL certificate is renewed or rotated, the hardcoded public key in the app no longer matches the server's new certificate, leading to connection failures until the app is updated with the new key.
- Both development and deployment teams need to align on a deployment window that considers factors like user downtime, mobile app store review timelines, and peak usage times, as well as a coordinated rollback plan.
</Note>
### Steps to Enable SSL Pinning in Your Mobile App
#### 1. Obtain the Certificate from the Server
Use `openssl` to retrieve the certificate from your server and save it to a file. This can be done with the following command:
```sh
openssl s_client -connect yourserver.com:443 -showcerts < /dev/null | openssl x509 -outform DER -out yourserver.cer
```
Alternatively, to save it in PEM format:
```sh
openssl s_client -connect yourserver.com:443 -showcerts < /dev/null | openssl x509 -outform PEM -out yourserver.crt
```
#### 2. Naming the Certificate Files
Name the certificate files using the domain name as the filename with either `.cer` or `.crt` as the extension. For example, if your servers domain is `example.com`, your files should be named `example.com.cer` and/or `example.com.crt`.
Optionally you can have both file types (`.cer` and `.crt`) to match the server trust and to ensure continued app functionality during certificate rotations. **Coordinate certificate rotations with the deployment teams** to avoid disruptions between the app and the server.
#### 3. Copy the Certificate Files to the Assets Folder
Place the certificate files in the `assets/certs` folder of your project. This is necessary for the app to access and use the certificates during runtime.
#### 4. Build Your App
Follow the instructions in the [Build the iOS app](/developers/contribute/more-info/mobile/build-your-own/ios) or [Build the Android app](/developers/contribute/more-info/mobile/build-your-own/android) sections to build your app with SSL pinning enabled.
@@ -0,0 +1,69 @@
---
title: "White label"
sidebar_position: 3
---
We've made it easy to white label the mobile app and to replace and override the assets used, however, you have to [Build Your Own App](/developers/contribute/more-info/mobile/build-your-own) from source.
If you look at the [Project Folder Structure](/developers/contribute/more-info/mobile/developer-setup/structure), you'll see that there is an assets folder containing a base folder with assets provided by Mattermost. These include localization files and images as well as a release folder that optionally contains the icons and the splash screen of the app when building in release mode.
To replace these with your own assets, create a sub-directory called `override` in the `assets` folder. The assets that you add using the same directory structure and file names as in the `base` directory, will be used instead of the original ones.
### Localization strings
To replace these with your own assets, create a sub-directory called `override` in the `assets` folder. Using the same directory structure and file names as in the `base` directory, you can add assets to the override folder to be used instead.
For example, to override `assets/base/images/logo.png` you would replace your own `logo.png` file in `assets/override/images/logo.png`.
### Images
To replace an image, copy the image to `assets/override/images/` with the same location and file name as in the `base` folder.
---
<Note title="Note">
Make sure the images have the same height, width, and DPI as the images that you are overriding.
</Note>
---
### App splash screen and launch icons
In the `assets` directory you will find a folder named `assets/base/release` which contains an `icons` folder and a `splash_screen` folder under each platform directory.
Copy the full `release` directory under `assets/override/release` and then replace each image with the same name. Make sure you replace all the icon images for the platform you are building the app - the same applies to the splash screen.
The splash screen's background color is white by default and the image is centered. If you need to change the color or the layout to improve the experience of your new splash screen make sure that you also override the file `launch_screen.xml` for Android and `LaunchScreen.storyboard` for iOS. Both can be found under `assets/base/release/splash_screen/<platform>/`.
Splash screen and launch icons assets are replaced at build time when the environment variable `REPLACE_ASSETS` is set to true (default is false).
---
<Note title="Note">
Make sure the images have the same height, width, and DPI as the images that you are overriding.
</Note>
---
### Configuration
The `config.json` file handles custom configuration for the app for settings that cannot be controlled by the Mattermost server. Like with localization strings, create a `config.json` file under `assets/override` and just include the keys and values that you wish to change that are present in `assets/base/config.json`.
For example, if you want the app to automatically provide a server URL and skip the screen to input it, you would add the following to `assets/override/config.json`:
```json
{
"DefaultServerUrl": "http://192.168.0.13:8065",
"AutoSelectServerUrl": true
}
```
---
<Note title="Note">
The above key/value pairs are taken from the original `config.json` file. Since we dont need to change anything else, we only included these two settings.
</Note>
---
@@ -0,0 +1,31 @@
---
title: "Add new dependencies"
sidebar_position: 4
---
If you need to add a new dependency to the project, it is important to add them in the right way. Instructions for adding different types of dependencies are described below.
#### JavaScript only
If you need to add a new JavaScript dependency that is not related to React Native, **use npm, not yarn**. Be sure to save the exact version number to avoid conflicts in the future.
```sh
$ npm i --save-exact <package-name>
```
If the dependency is only for development
```sh
$ npm i --save-exact --save-dev <package-name>
```
#### React Native
As with [JavaScript only](#javascript-only), **use npm** to add your dependency and include an exact version.
If the library contains iOS native code, make sure to run:
```sh
$ npm run pod-install
```
Most of the time linking the library to React Native is done automatically, but at times some libraries need to be manually linked. In this case follow the library's documentation.
@@ -0,0 +1,259 @@
---
title: "Developer setup"
sidebar_position: 1
---
The following instructions apply to the mobile apps for iOS and Android built in React Native.
Download the iOS version [here](https://apps.apple.com/us/app/mattermost/id1257222717) and the Android version [here](https://play.google.com/store/apps/details?id=com.mattermost.rn).
Source code can be found at the [GitHub Mattermost Mobile app repository](https://github.com/mattermost/mattermost-mobile).
If you run into any issues getting your environment set up, check the [Troubleshooting](https://docs.mattermost.com/deploy/mobile-troubleshoot.html) section of the product docs for common solutions.
<Note title="iOS mobile app">
A macOS computer is required to build the Mattermost iOS mobile app.
</Note>
<Note title="Android mobile app">
Version 17 of the Java SE Development Kit (JDK) is required to develop the Mattermost Android mobile app. You can download the latest OpenJDK release of Java from Oracle, for free, under an open source license.
</Note>
## Environment setup
The following instructions apply to both iOS and Android mobile apps.
On macOS, we recommend using [Homebrew](https://brew.sh) as a package manager.
### Install NodeJS and NPM
We recommend using NodeJS v22 and npm v10. Many of our team use [nvm](https://github.com/nvm-sh/nvm) to manage npm and NodeJS versions.
<Tabs>
<TabItem value="node-npm-mac" label="macOS">
To install [NVM](https://github.com/nvm-sh/nvm), follow [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
```
</TabItem>
<TabItem value="node-npm-linux" label="Linux">
There are three available options for installing NodeJS on Linux:
- Using NVM by following the instructions [here](https://github.com/nvm-sh/nvm#install-script).
- Install using your distribution's package manager.
- Download and install the package from the [NodeJS website](https://nodejs.org/en).
<Note>
The version of NodeJS that your distribution's package manager supports may not be the recommended version to build Mattermost mobile apps.
Please make sure that NodeJS installed by the package manager is at the recommended version.
</Note>
</TabItem>
</Tabs>
### Install Watchman
[Watchman](https://facebook.github.io/watchman) is a file watching program.
When a file changes, Watchman triggers an action, such as re-running a build command if a source file has changed.
The minimum required version of Watchman is 4.9.0.
<Tabs>
<TabItem value="watchman-mac" label="macOS">
To install Watchman using Homebrew, open a terminal and execute:
```sh
brew install watchman
```
</TabItem>
<TabItem value="watchman-linux" label="Linux">
Download the latest package from [here](https://github.com/facebook/watchman/releases).
<Note title="Inotify limits">
Note that you need to increase your `inotify` limits for Watchman to work properly.
</Note>
</TabItem>
</Tabs>
### Install `react-native-cli` tools
```sh
npm -g install react-native-cli
```
### Install Git
<Tabs>
<TabItem value="git-mac" label="macOS">
To install Git using Homebrew, open a terminal and execute:
```sh
brew install git
```
</TabItem>
<TabItem value="git-linux" label="Linux">
Some distributions come with Git preinstalled but you'll most likely have to install it yourself. For most distributions the package is simply called `git`.
</TabItem>
</Tabs>
## Additional setup for iOS (macOS)
### Install XCode
Install [Xcode](https://apps.apple.com/us/app/xcode/id497799835?ls=1&mt=12) to build and run the app on iOS. The minimum required version is 11.0.
### Install Ruby
A version of Ruby is automatically installed on macOS, but Mattermost React Native app development requires Ruby 3.2.0. You can check the current version of Ruby by running the following command.
```sh
ruby --version
```
If it isn't, we recommend using [Ruby Version Manager](https://rvm.io) or your preferred package manager to install the required version. The steps below are for using RVM.
1. Install the GPG keys for RVM using the command found [here](https://rvm.io/rvm/install#install-gpg-keys).
1. If you don't have the `gpg` command, you can install it using Homebrew by running.
```sh
brew install gnupg
```
2. Install the stable version of RVM using the following command.
```sh
\curl -sSL https://get.rvm.io | bash -s stable --ruby
```
3. To load RVM, either open a new terminal or run the following command.
```sh
source ~/.rvm/scripts/rvm
```
4. Install the required version of Ruby
```sh
rvm install 3.2.0
```
5. (Optional) If you don't need to use a different version of Ruby for anything else, you'll want to change the default version of Ruby. Without this, you'll need to run `rvm use 3.2.0` any time you want to work on the mobile app.
```sh
rvm alias create default 3.2.0
```
## Additional setup for Android
### Download and install Android Studio or Android SDK CLI tools
Download and install the [Android Studio app or the Android SDK command line tools](https://developer.android.com/studio/index.html#downloads)
<Note title="Default paths">
This documentation assumes you chose the default path for your Android SDK installation. If you chose a different path, adjust the environment variables below accordingly.
</Note>
#### Environment variables
Make sure you have the following environment variables configured for your platform:
<Tabs>
<TabItem value="droid-common" label="All platforms">
- Set `ANDROID_HOME` to where Android SDK is located (likely `/Users/<username>/Library/Android/sdk` or `/home/<username>/Android/Sdk`)
- Make sure your `PATH` includes `ANDROID_HOME/tools` and `ANDROID_HOME/platform-tools`
</TabItem>
<TabItem value="droid-mac" label="macOS">
On Mac, this usually requires adding the following lines to your `~/.bash_profile` file:
```sh
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$ANDROID_HOME/emulator:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH
```
Then reload your bash configuration:
```sh
source ~/.bash_profile
```
<Note>
Depending on the shell you're using, this might need to be put into a different file such as `~/.zshrc`. Adjust this accordingly.
</Note>
</TabItem>
<TabItem value="droid-linux" label="Linux">
On Linux the home folder is located under `/home/<username>` which results in a slightly different path:
```sh
export ANDROID_HOME=/home/<username>/Android/Sdk
export PATH=$ANDROID_HOME/platform-tools:$PATH
export PATH=$ANDROID_HOME/tools:$PATH
```
Then reload your configuration
```sh
source ~/.bash_profile
```
<Note>
Depending on the shell you're using, this might need to be put into a different file such as `~/.zshrc`. Adjust this accordingly.
</Note>
</TabItem>
</Tabs>
### Install the SDKs and SDK tools
In the SDK Manager using Android Studio or the [Android SDK command line tool](https://developer.android.com/studio/command-line/sdkmanager.html), ensure the following are installed:
- SDK Tools (you may have to select **Show Package Details** to expand packages):
- Android SDK Build-Tools 31
- Android Emulator
- Android SDK Platform-Tools
- Android SDK Tools
- Google Play services
- Intel x86 Emulator Accelerator (HAXM installer)
- Support Repository
- Android Support Repository
- Google Repository
![image](sdk_tools.png)
- SDK Platforms (you may have to select **Show Package Details** to expand packages)
- Android 12 or above
- Google APIs
- SDK Platform
- Android SDK Platform 31 or above
- Intel or Google Play Intel x86 Atom\_64 System Image
- Any other API version that you want to test
![image](sdk_platforms.png)
## Obtain the source code
In order to develop and build the Mattermost mobile apps, you'll need to get a copy of the source code. Forking the `mattermost-mobile` repository will also make it easy to contribute your work back to the project in the future.
1. Fork the [mattermost-mobile](https://github.com/mattermost/mattermost-mobile) repository on GitHub.
2. Clone your fork locally:
a. Open a terminal
b. Change to a directory you want to hold your local copy
c. Run `git clone https://github.com/<username>/mattermost-mobile.git` if you want to use HTTPS, or `git clone git@github.com:<username>/mattermost-mobile.git` if you want to use SSH
<Note>
`<username>` refers to the username or organization in GitHub that forked the repository
</Note>
3. Change the directory to `mattermost-mobile`.
```sh
cd mattermost-mobile
```
4. Install the project dependencies with `npm install`
@@ -0,0 +1,123 @@
---
title: "Run the mobile app"
sidebar_position: 2
---
We provide a set of scripts to help you run the app for the different platforms that are executed with `npm`:
* **npm start**: Start the React Native packager. The packager has to be running in order to build the JavaScript code that powers the app.
* **npm run android**: Compile and run the mobile app on Android.
* **npm run ios**: Compile and run the mobile app on iOS.
<Note>
To speed up development, only compile and run the apps in the following cases:
- You have not deployed the app to a device or simulator with the `npm run <platform>` command.
- There have been changes in the native code.
- A new library has been added or updated that has native code.
If none of the above cases apply, you could just simply start the React Native packager with `npm start` and launch the app you have already deployed to the device or simulator.
</Note>
The above commands are shortcuts for the `react-native` CLI. You can append `-- --help` to the above commands to see available options, for example:
```sh
npm run android -- --help
```
Make sure you are adding `--` before the options you want to include or run the `react-native` CLI directly:
```sh
npx react-native run-android --help
```
## Run on a device
By default, running the app will launch an Android emulator (if you created one) or an iOS simulator.
If you want to test the performance of the app or if you want to make a contribution it is always a good idea to run the app on an actual device.
This will let you ensure that the app is working correctly and in a performant way before submitting a pull request.
<Tabs>
<TabItem value="mobile-android" label="Android">
To be able to run the app on an Android device you'll need to follow these steps:
1. **Enable debugging over USB**
Most Android devices can only install and run apps downloaded from Google Play by default. In order to be able to install the Mattermost Mobile app in the device during development you will need to enable USB Debugging on your device in the **Developer options** menu by going to **Settings > About phone** and then tap the Build number row at the bottom seven times, then go back to **Settings > Developer options** and enable **USB debugging**.
2. **Plug in your device via USB**
Plug in your Android device in any available USB port in your development machine (try to avoid hubs and plug it directly into your computer) and check that your device is properly connecting to ADB (Android Debug Bridge) by running `adb devices`.
```
$ adb devices
List of devices attached
42006fb3e4fb25b8 device
```
If you see **device** in the right column that means that the device is connected. You can have multiple devices attached and the app will be deployed to **all of them**.
3. **Compile and run**
With your device connected to the USB port execute the following in your command prompt to install and launch the app on the device:
```sh
npm run android
```
<Note title="Note">
If you don't see a bar at the top loading the JavaScript code then it's possible that the device is not connected to the development server. See [Using adb reverse](http://reactnative.dev/docs/running-on-device.html#method-1-using-adb-reverse-recommended).
</Note>
</TabItem>
<TabItem value="mobile-ios" label="iOS">
To be able to run the app on an iOS device you'll need to have [Xcode](https://developer.apple.com/xcode/) installed on a Mac computer and follow this steps:
1. **Get an Apple Developer account**
The apps that run on an iOS device must be signed. To sign it, you'll need a set of provisioning profiles. If you already have an Apple Developer account enrolled in the Apple Developer program you can skip this step. If you don't have an account yet you'll need to [create one](https://appleid.apple.com/account?appId=632&returnUrl=https%3A%2F%2Fdeveloper.apple.com%2Faccount%2F#!&page=create) and enroll in the [Apple Developer Program](https://developer.apple.com/programs/).
2. **Open the project in Xcode**
Navigate to the `ios` folder in your `mattermost-mobile` project, then open the file `Mattermost.xcworkspace` in Xcode.
3. **Configure code signing and capabilities**
Select the **Mattermost** project in the Xcode Project Navigator, then select the **Mattermost** target. Look for the **Signing & Capabilities** tab.
- Go to the **Signing** section and make sure your Apple developer account or team is selected under the Team dropdown and change the [Bundle Identifier](https://developer.apple.com/documentation/appstoreconnectapi/bundle_ids).
Xcode will register your provisioning profiles in your account for the Bundle Identifier you've entered if it doesn't exist.
- Go to the **App Groups** section and change the [App Groups](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_application-groups?language=objc).
Xcode will register your AppGroupId and update the provision profile.
- Go to the **iCloud** section and change the [Containers](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_icloud-container-identifiers?language=objc).
Xcode will register your iCloud container and update the provision profile.
- Go to the **Keychain Sharing** section and change the [Keychain Groups](https://developer.apple.com/documentation/bundleresources/entitlements/keychain-access-groups?language=objc).
Xcode will register your Keychain access groups and update the provision profile.
<Note title="Important">
Repeat the steps for the `MattermostShare` and `NotificationService` targets. Each target must use a **different** *Bundle Identifier*.
</Note>
4. **Compile and run**
Plug in your iOS device in any available USB port in your development computer.
If everything is set up correctly, your device will be listed as the build target in the Xcode toolbar, and it will also appear in the Devices Pane (<kbd><kbd>⇧</kbd><kbd>⌘</kbd><kbd>2</kbd></kbd>). You can press the **Build and run** button (<kbd><kbd>⌘</kbd><kbd>R</kbd></kbd>) or select **Run** from the Product menu to run the app.
![image](running_ios.png)
As an alternative you can select the targeted device by opening the **Product** menu in Xcode menu bar, then go to **Destination** and look for your device to select from the list.
<Note>
If you run into any issues, please take a look at Apple's [Launching Your App on a Device](https://developer.apple.com/library/content/documentation/IDEs/Conceptual/AppDistributionGuide/LaunchingYourApponDevices/LaunchingYourApponDevices.html#//apple_ref/doc/uid/TP40012582-CH27-SW4) documentation.
If the app fails to build, go to the **Product** menu and select **Clean Build Folder** before trying to build the app again.
Also, be sure that your iOS device is trusted so app deployments can proceed.
</Note>
</TabItem>
</Tabs>
Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

@@ -0,0 +1,56 @@
---
title: "Folder structure"
sidebar_position: 1
---
The following is an overview of the Mobile app repository file structure:
```goat
|
+-- .circleci # Circle CI workflow to build the apps
+-- .github # GitHub actions
+-- .husky
+-- android # Android specific code
+-- app # React Native code
| |
| +-- actions
| +-- client
| +-- components
| +-- constants
| +-- context
| +-- database
| +-- helpers
| +-- hooks
| +-- i18n
| +-- init
| +-- managers
| +-- notifications
| +-- products
| +-- queries
| +-- screens
| +-- store
| +-- utils
|
+-- assets
| |
| +-- base
| | |
| | +-- i18n
| | +-- images
| | +-- release
| +-- fonts
|
+-- build
| |
| +-- notice-file
|
+-- detox
+-- docs
+-- eslint
+-- fastlane # Fastlane scripts to build the app
+-- ios # iOS specific code
+-- patches # Patches for various dependencies
+-- scripts
+-- share_extension # Android's share extension app
+-- test
+-- types
```
@@ -0,0 +1,148 @@
---
title: "Mobile End-to-End (E2E) Tests"
sidebar_position: 5
---
This page describes how to write and run End-to-End (E2E) testing for Mobile Apps for both iOS and Android. Mobile products use [Detox](https://github.com/wix/Detox), which is a "gray box end-to-end testing and automation library for mobile apps." See its [documentation](https://github.com/wix/Detox/tree/master/docs) to learn more.
### File Structure
The folder structure is as follows:
```
|-- detox
|-- e2e
|-- support
|-- test
|-- config.json
|-- environment.js
|-- init.js
|-- .babelrc
|-- .detoxrc.json
|-- package-lock.json
|-- package.json
```
* `/detox/e2e/support` is the support folder, which is a place to put reusable behavior such as Server API and UI commands, or global overrides that should be available to all test files.
* `/detox/e2e/test`: To start writing tests, create a new file (e.g. `login.e2e.js`) in the `/detox/e2e/test` folder.
- The subfolder naming convention depends on the test grouping, which is usually based on the general functional area (e.g. `/detox/e2e/test/messaging/` for "Messaging").
- Test cases that require an Enterprise license should fall under `/detox/e2e/test/enterprise/`. This is to easily identify license requirements, both during local development and production testing for Enterprise features.
* `/detox/.detoxrc.json`: for Detox configuration.
* `/detox/package.json` : for all dependencies related to Detox end-to-end testing.
### Writing an E2E Test
This process has many similarities to [writing an E2E test for the mattermost-webapp project](/developers/contribute/more-info/webapp/e2e-testing).
Before writing a script, ensure that it has a corresponding test case in Zephyr. All test cases may be found in this [link](https://mattermost.atlassian.net/projects/MM?selectedItem=com.atlassian.plugins.atlassian-connect-plugin%3Acom.kanoah.test-manager__main-project-page#!/design?projectId=10302). If test case is not available, feel free to prompt the QA team who will either search from an existing Zephyr entry or if it's a new one, it will be created for you.
1. Create a test file based on the file structure aforementioned above.
2. Include Zephyr identification (ID) and title in the test description, following the format of `it('[Zephyr_id] [title]')` or `it('[Zephyr_id]_[step] [title]')` if the test case has multiple steps. For test case "[MM-T109 RN apps: User can't send the same message repeatedly](https://mattermost.atlassian.net/projects/MM?selectedItem=com.atlassian.plugins.atlassian-connect-plugin%3Acom.kanoah.test-manager__main-project-page#!/testCase/MM-T109)", it should be:
```javascript
describe('Messaging', () => {
it('MM-T109 User can\'t send the same message repeatedly', () => {
// Test steps and assertion here
}
}
```
4. Target an element using available matchers. For best results, it is recommended to match elements by unique identifiers using `testID`. The identifier should follow the following format to avoid duplication, `<location>.<modifier>.<element>.<identifier>`, where:
<Note title="NOTE">
Not all fields are required. When assigning a `testID`, carefully inspect the actual render structure and pick up the minimum fields combination to create a unique value. Some examples include: `send.button` and `post.<post-id>`.
</Note>
```text
* `location` - can be a parent component, a main section, or a UI screen.
* `modifier` - adds meaning to the `element`.
* `element` - common terms like `button`, `text_input`, `image`, and the like.
* `identifier` - could be unique ID of a post, channel, team or user, or a number to represent order.
```
5. Prefix each comment line with appropriate indicator. Each line in a multi-line comment should be prefixed accordingly. Separate and group test step comments and assertion comments for better readability.
- `#` indicates a test step (e.g. `// # Go to a screen`)
- `*` indicates an assertion (e.g. `// * Check the title`)
6. Simulate user interaction using available actions, and verify user interface (UI) expectations using `expect`. When using `action`, `match`, or another API specific to a particular platform, verify that the equivalent logic is applied so that the API does not impact other platforms. Always run tests in applicable platforms.
### Running E2E Tests
#### Testing Android Locally
##### Local setup
1. Install the latest Android SDK:
```
sdkmanager "system-images;android-30;google_apis;x86"
sdkmanager --licenses
```
2. Create the emulator using `npm run e2e:android-create-emulator` from the `/detox` folder. Android testing requires an emulator named `detox_pixel_4_xl_api_30` and the script helps to create it automatically.
##### Complete a test run in debug mode
This is the typical flow for local development and test writing:
1. Open a terminal window and run react-native packager by `npm install && npm start` from the root folder.
2. Open a second terminal window and:
* Change directory to `/detox` folder.
* Install npm packages by `npm install`.
* Build the app together with the android test using `npm run e2e:android-build`.
* Run the test using `npm run e2e:android-test`.
* For running a single test, follow this example command: `npm run e2e:android-test -- connect_to_server.e2e.ts`.
##### Complete a test run in release mode
This is the typical flow for CI test run:
1. Build a release app by running `npm install && npm run e2e:android-build-release` from the `/detox` folder.
2. Run a test using `npm run e2e:android-test-release` from the `/detox` folder.
#### Testing iOS Locally
##### Local setup
1. Install [applesimutils](https://github.com/wix/AppleSimulatorUtils):
```
brew tap wix/brew
brew install applesimutils
```
2. Set XCode's build location so that the built app, especially debug, is expected at the project's location instead of the Library's folder which is unique/hashed.
3. Open XCode, then go to **XCode > Settings > Locations**.
4. Under **Derived Data**, click **Advanced...**.
5. Select **Custom > Relative to Workspace**, then set **Products** as **Build/Products**.
6. Click **Done** to save the changes.
##### Complete a test run in debug mode
1. In one terminal window, from the root folder run `npm run start` and on another `npm run ios` from the root folder.
2. Once the build is complete and installed, there will be a message informing of the path where the app is installed. Copy that path. Sample output:
```log
info Building (using "xcodebuild -workspace Mattermost.xcworkspace -configuration Debug -scheme Mattermost -destination id=00008110-00040CA10263801E")
info Installing "/Users/myuser/Proyectos/mattermost-mobile/ios/Build/Products/Debug-iphonesimulator/Mattermost.app
info Launching "com.mattermost.rnbeta"
success Successfully launched the app
```
3. Edit `/detox/.detoxrc` and
- In **apps > ios.debug**, substitute `binaryPath` by that value.
- In **devices > ios.simulator > device**, substitute `type` and `os` with the values corresponding to the ones being used by the simulator. If unsure wich ones, either open the simulator or go to Xcode to see where it is being built.
4. Export the values for `ADMIN_USERNAME` and `ADMIN_PASSWORD` with the appropiate values for your test server. Check the Environment Variables section below to learn more about default values and other variables available.
```sh
export SITE_1_URL="http://localhost:8065"
export ADMIN_USERNAME="sysadmin"
export ADMIN_PASSWORD="Sys@dmin-sample1"
```
5. In another terminal window, run `npm i` then `npm run e2e:ios-test` from the `/detox` folder.
* For running a single test, follow this example command: `npm run e2e:ios-test -- connect_to_server.e2e.ts`.
```sh
cd detox
npm i
npm run e2e:ios-test
```
##### Complete a test run in release mode
1. Build the release app by running `npm run build:ios-sim` from the root folder or `npm run e2e:ios-build-release` from within the `/detox` folder.
2. Run the test using `npm run e2e:ios-test-release` from the `/detox` folder.
#### Environment variables
Test configurations are [defined at test_config.js](https://github.com/mattermost/mattermost-mobile/blob/master/detox/e2e/support/test_config.js) and environment variables are used to override default values. In most cases you don't need to change the values, because it makes use of the default local developer setup. If you do need to make changes, you may override by exporting, e.g. `export SITE_URL=<site_url>`.
| Variable | Description |
|----------------|---------------------------------------------------------------------------------------------------------------------|
| SITE_URL | Host of test server.<br/><br/>*Default*: `http://localhost:8065` for iOS or `http://10.0.2.2:8065` for Android. |
| ADMIN_USERNAME | Admin's username for the test server.<br/><br/>*Default*: `sysadmin` when server is seeded by `make test-data`. |
| ADMIN_PASSWORD | Admin's password for the test server.<br/><br/>*Default*: `Sys@dmin-sample1` when server is seeded by `make test-data`. |
| LDAP_SERVER | Host of LDAP server.<br/><br/>*Default*: `localhost` |
| LDAP_PORT | Port of LDAP server.<br/><br/>*Default*: `389` |
@@ -0,0 +1,25 @@
---
title: "Mobile apps"
sidebar_position: 4
---
The Mattermost mobile apps are written in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) using [React Native](https://facebook.github.io/react-native/).
## Repository
Further explanation of the file structure can be found in [the Developer Setup](/developers/contribute/more-info/mobile/developer-setup/structure). For more information about E2E testing on the Android and iOS apps, check out [Mobile End-to-End (E2E) tests](/developers/contribute/more-info/mobile/e2e)
## Mobile app contributor resources
- [GitHub Repository](https://github.com/mattermost/mattermost-mobile) - Get the code, report issues, or submit PRs.
- [Running the app](/developers/contribute/more-info/mobile/developer-setup/run) - Compile recommendations.
- [Channel](https://community.mattermost.com/core/channels/native-mobile-apps) - Use the channel to interact with other Mattermost Mobile developers.
- [Help Wanted](https://mattermost.com/pl/help-wanted-mattermost-mobile) - Find help wanted tickets here.
### Android specific documentation
- [Build guide](/developers/contribute/more-info/mobile/build-your-own/android)
- [Push Notifications](/developers/contribute/more-info/mobile/push-notifications/android)
- [Sign Unsigned Builds](/developers/contribute/more-info/mobile/unsigned/android)
### iOS specific documentation
- [Build guide](/developers/contribute/more-info/mobile/build-your-own/ios)
- [Push Notifications](/developers/contribute/more-info/mobile/push-notifications/ios)
- [Sign Unsigned Builds](/developers/contribute/more-info/mobile/unsigned/ios)
@@ -0,0 +1,42 @@
---
title: "Android push notifications"
sidebar_position: 1
---
Push notifications on Android are managed and dispatched using [Firebase Cloud Messaging (FCM)](http://firebase.google.com/docs/cloud-messaging/)
- Create a Firebase project within the [Firebase Console](https://console.firebase.google.com).
- Click **Add Project**
![image](/img/mobile/firebase_console.png)
- Enter the project name, project ID and Country
- Click **CREATE PROJECT**
![image](/img/mobile/firebase_project.png)
Once the project is created you'll be redirected to the Firebase project
dashboard
![image](/img/mobile/firebase_dashboard.png)
- Click **Add Firebase to your Android App**
- Enter the package ID of your custom Mattermost app as the **Android package name**.
- Enter an **App nickname** so you can identify it with ease
- Click **REGISTER APP**
- Once the app has been registered, download the **google-services.json** file which will be used later
- Click **CONTINUE** and then **FINISH**
![image](/img/mobile/firebase_register_app.png)
![image](/img/mobile/firebase_google_services.png)
![image](/img/mobile/firebase_sdk.png)
Now that you have created the Firebase project and the app and
downloaded the *google-services.json* file, you need to make some
changes in the project.
- Replace `android/app/google-services.json` with the one you downloaded earlier
At this point, you can build the Mattermost app for Android and setup the [Mattermost Push Notification Service](/developers/contribute/more-info/mobile/push-notifications/service).
@@ -0,0 +1,33 @@
---
title: "Corporate proxy"
sidebar_position: 4
---
When your IT policy requires a corporate proxy to scan and audit all outbound traffic the following options are available:
###### 1. Deploy Mattermost with connection restricted post-proxy relay in DMZ or a trusted cloud environment
Some legacy corporate proxy configurations may be incompatible with the requirements of modern mobile architectures, such as the requirement of HTTP/2 requests from Apple to send push notifications to iOS devices.
In this case, a **post-proxy relay** (which accepts network traffic from a corporate proxy such as NGINX, and transmits it to the final destination) can be deployed to take messages from the Mattermost server passing through your corporate IT proxy in the incompatible format, e.g. HTTP/1.1, transform it to HTTP/2 and relay it to its final destination, either to the [Apple Push Notification Service (APNS)](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) and [Google Fire Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging) services.
The **post-proxy relay** [can be configured using the Mattermost Push Proxy installation guide](/developers/contribute/more-info/mobile/push-notifications/service) with connection restrictions to meet your custom security and compliance requirements.
You can also host in a trusted cloud environment such as AWS or Azure in place of a DMZ (this option may depend on your organization's internal policies).
![image](/img/mobile/post-proxy-relay.png)
###### 2. Whitelist Mattermost push notification proxy to bypass your corporate proxy server
Depending on your internal IT policy and approved waivers/exceptions, you may choose to deploy the [Mattermost Push Proxy](/developers/contribute/more-info/mobile/push-notifications/service) to connect directly to [Apple Push Notification Service (APNS)](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) without your corporate proxy.
You will need to [whitelist one subdomain and one port from Apple](https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW1) for this option:
- Development server: `api.development.push.apple.com:443`
- Production server: `api.push.apple.com:443`
###### 3. Run App Store versions of the Mattermost mobile apps
You can use the mobile applications hosted by Mattermost in the [Apple App Store](https://apps.apple.com/ca/app/mattermost/id1257222717) or [Google Play Store](https://play.google.com/store/apps/details?id=com.mattermost.rn) and connect with [Mattermost Hosted Push Notification Service (HPNS)](https://docs.mattermost.com/deploy/mobile-hpns.html) through your corporate proxy.
The use of hosted applications by Mattermost [can be deployed with Enterprise Mobility Management solutions via AppConfig](https://docs.mattermost.com/deploy/mobile-appconfig.html). Wrapping is not supported with this option.
@@ -0,0 +1,13 @@
---
title: "Set up push notifications"
sidebar_position: 3
---
When building a custom version of the Mattermost mobile app, you will also need to host your own [Mattermost Push Notification Service](https://github.com/mattermost/mattermost-push-proxy/releases) and make a few modifications to your Mattermost mobile app to be able to get push notifications.
1. Setup the custom mobile apps to receive push notifications
- [Android](/developers/contribute/more-info/mobile/push-notifications/android)
- [iOS](/developers/contribute/more-info/mobile/push-notifications/ios)
2. [Setup the Mattermost push notification service](/developers/contribute/more-info/mobile/push-notifications/service)
If the use of a proxy server is required by your IT policy, see the [corporate proxy](/corporate-proxy) page.
@@ -0,0 +1,50 @@
---
title: "iOS push notifications"
sidebar_position: 2
---
## Generate APNs Auth Key
To deliver push notifications on iOS, you need to authenticate with **Apple Push Notification service (APNs)**.
Mattermost recommends using **token-based authentication** with an APNs Auth Key (`.p8`) instead of certificates.
---
### Prerequisites
- Apple Developer Program account
- Registered iOS app Bundle ID with **Push Notifications** capability enabled
---
### 1. Create an APNs Auth Key
1. Sign in to [Apple Developer: Keys](https://developer.apple.com/account/resources/authkeys/list).
2. Click **+** to register a new key.
![Apple Developer register new key](/img/mobile/ios-register-key.png)
3. **Enter a Key Name** to easily identify it later (e.g., *Mattermost Push Proxy*).
![Enter key name](/img/mobile/ios-key-name.png)
4. **Enable APNs** by checking the **Apple Push Notifications service (APNs)** box and click **Configure** to configure the key.
![Enable APNs](/img/mobile/ios-enable-apns.png)
5. On the **Configure Key** screen:
- Select an **Environment**: *Sandbox*, *Production*, or *Sandbox & Production*.
- Choose a **Key Restriction**: *Team Scoped (All Topics)* or *Topic Specific*.
![Configure APNs key](/img/mobile/ios-configure-apns.png)
- If you select *Topic Specific*, add the topics (App IDs) you want to associate.
![Add topics](/img/mobile/ios-add-topics.png)
6. Click **Save**, then **Continue**.
7. Review the Key details and click **Register**.
8. Download the generated file `AuthKey_XXXXXXXXXX.p8` and store it securely.
> You can only download the file once.
9. Note the following values:
- **Key ID** (from the Keys list)
- **Team ID** (from your Apple Developer Membership)
- **Bundle ID** (your app identifier, used as the APNs topic)
![Apple Developer key list](/img/mobile/ios-key-list.png)
---
### 2. Next Steps
Once youve generated your APNs Auth Key and collected the Key ID, Team ID, and Bundle ID, continue to the [Push Notification Service setup](/developers/contribute/more-info/mobile/push-notifications/service) page to configure the Mattermost Push Notification Service (MPNS).
@@ -0,0 +1,174 @@
---
title: "Push notification service"
sidebar_position: 3
---
Now that the app can receive push notifications, we need to make sure that the Mattermost Push Notification Service is able to send the notification to the device. This guide will focus on installing and configuring the push notification service.
### Requirements
- A Linux or FreeBSD box server with at least 1GB of memory.
- A copy of the [Mattermost Push Notification Service](https://github.com/mattermost/mattermost-push-proxy/releases).
- [Custom Android and/or iOS](/developers/contribute/more-info/mobile/build-your-own) Mattermost mobile apps.
- An APNs Auth Key (`.p8`) obtained by following the [iOS Push Notifications guide](/developers/contribute/more-info/mobile/push-notifications/ios).
- A Firebase Cloud Messaging Server key obtained by following the [Android Push Notifications guide](/developers/contribute/more-info/mobile/push-notifications/android).
## Install and upgrade
For the sake of making this guide simple we located the files at `/home/ubuntu/mattermost-push-proxy`. We've also elected to run the Push Notification Service as the `ubuntu` account for simplicity. We **recommend** setting up and running the service under a `mattermost-push-proxy` user account with limited permissions.
1. Download the latest Mattermost Push Notification Service:
`wget https://github.com/mattermost/mattermost-push-proxy/releases/download/vX.X.X/mattermost-push-proxy-linux-amd64.tar.gz`
or
`wget https://github.com/mattermost/mattermost-push-proxy/releases/download/vX.X.X/mattermost-push-proxy-freebsd-amd64.tar.gz`
```text
In this command, `vX.X.X` refers to the release version you want to download. See [Mattermost Push Notification Service releases](https://github.com/mattermost/mattermost-push-proxy/releases).
```
2. If you're upgrading a previous version of the Mattermost Push Notification Service make sure to back up your `mattermost-push-proxy.json` file before continuing.
3. Unzip the downloaded Mattermost Push Notification Service using:
`tar -xvzf mattermost-push-proxy-linux-amd64.tar.gz`
or
`tar -xvzf mattermost-push-proxy-freebsd-amd64.tar.gz`
4. Configure the Mattermost Push Notification service by editing the `mattermost-push-proxy.json` file at `/home/ubuntu/mattermost-push-proxy/config`. Follow the steps in the [Android](#set-up-mattermost-push-notification-service-to-send-android-push-notifications)
and [iOS](#set-up-mattermost-push-notification-service-to-send-ios-push-notifications) sections to replace the values in the config file.
5. Create a systemd unit file to manage the Mattermost Push Notification Services with systemd and log all output of the service to `/var/log/syslog` by running this command as root.
```bash
echo "[Unit]
Description=Mattermost Push Notification Service
[Service]
Type=oneshot
ExecStart=/bin/sh -c '/home/ubuntu/mattermost-push-proxy/bin/mattermost-push-proxy | logger'
WorkingDirectory=/home/ubuntu/mattermost-push-proxy
[Install]
WantedBy=multi-user.target" >> /etc/systemd/system/mattermost-push-proxy.service
```
To route the traffic through a separate proxy server, add `Environment="HTTP_PROXY=<http://server>"` under the `[Service]` section of the file. If you have an HTTPS server, then use `HTTPS_PROXY`. If you set both then `HTTPS_PROXY` will take higher priority than `HTTP_PROXY`.
6. Start the service with `sudo systemctl start mattermost-push-proxy` or restart with `sudo systemctl restart mattermost-push-proxy`. Use `sudo systemctl enable mattermost-push-proxy` to have systemd start the service on boot.
### Set up Mattermost push notification service to send Android push notifications
- Go to the [Firebase Console](https://console.firebase.google.com) and select the project you've created. Once in the dashboard, go to the project settings and select **Service Accounts**.
![image](/img/mobile/firebase_settings.png)
![image](/img/mobile/firebase_cloud_messaging.png)
- Click on **Generate new private key** and store the downloaded file.
![image](/img/mobile/firebase_server_key.png)
- Open the **mattermost-push-proxy.json** file in the `mattermost-push-proxy/config` directory and look for the "ServiceFileLocation" entry under "AndroidPushSettings". Paste the location of the file as its value.
```
"ServiceFileLocation": "/path/to/downloaded_file"
```
### Set up Mattermost push notification service to send iOS push notifications
Instead of certificates, we now recommend using an **APNs Auth Key (`.p8`)** to authenticate with Apple Push Notification service (APNs).
If you havent generated your key yet, see [Generate an APNs Auth Key](/developers/contribute/more-info/mobile/push-notifications/ios).
- Open the **mattermost-push-proxy.json** file under the `mattermost-push-proxy/config` directory and configure it with your key details:
```json
"ApplePushSettings":[
{
"Type":"apple_rn",
"ApplePushUseDevelopment":true,
"ApplePushTopic":"your.bundle.id",
"AppleAuthKeyFile":"./config/beta/YourAuthKeyFile.p8",
"AppleAuthKeyID":"YourAuthKeyID",
"AppleTeamID":"YourAppleTeamID"
}
],
```
- **ApplePushTopic**: Your apps bundle ID (APNs topic).
- **AppleAuthKeyFile**: Path to the `.p8` file.
- **AppleAuthKeyID**: Key ID from Apple Developer portal.
- **AppleTeamID**: Team ID from Apple Developer Membership.
- **ApplePushUseDevelopment**: `true` for sandbox APNs, `false` for production.
<Note>
If you are migrating from certificate-based authentication, you can remove the `ApplePushCertPrivate` field and replace it with the new `AppleAuthKeyFile`, `AppleAuthKeyID`, and `AppleTeamID` values.
</Note>
### Configure the Mattermost Server to use the Mattermost push notification service
- In your Mattermost instance, enable mobile push notifications.
* Go to **System Console > Notifications > Mobile Push**.
* Under **Send Push Notifications**, select **Manually enter Push Notification Service location**.
* Enter the location of your Mattermost Push Notification Service in the **Push Notification Server** field.
![image](/img/mobile/manual_mpns.png)
- (Optional) Customize mobile push notification contents.
* Go to **System Console > Notifications > Mobile Push**.
* Select an option for **Push Notification Contents** to specify what type of information to include in the push notifications.
* Most deployments choose to include the full message snippet in push notifications unless they have policies against it to protect confidential information.
![image](/img/mobile/push_contents.png)
- Finally, start your Mattermost Push Notification Service, and your app should start receiving push notifications.
### Test the Mattermost push notification service
* Verify that the server is functioning normally and test the push notification using curl:
`curl http://127.0.0.1:8066/api/v1/send_push -X POST -H "Content-Type: application/json" -d '{"type": "message", "message": "test", "badge": 1, "platform": "PLATFORM", "server_id": "MATTERMOST_DIAG_ID", "device_id": "DEVICE_ID", "channel_id": "CHANNEL_ID"}'`
* Replace `MATTERMOST_DIAG_ID` with the value found by running the SQL query:
```sql
SELECT * FROM Systems WHERE Name = 'DiagnosticId';
```
* Replace `DEVICE_ID` with your device ID, which can be found using (where `your_email@example.com` is the email address of the user you are logged in as):
```sql
SELECT
Email, DeviceId
FROM
Sessions,
Users
WHERE
Sessions.UserId = Users.Id
AND DeviceId != ''
AND Email = 'your_email@example.com';
```
* Replace `CHANNEL_ID` with the Town Square channel ID, which can be found using:
```sql
SELECT Id FROM Channels WHERE DisplayName = 'Town Square';
```
<Note title="Migration">
Remove the `apple:`, `apple_rn`, `android:` or `android_rn:` prefix from your device ID before replacing `DEVICE_ID`. Use that prefix as the `PLATFORM` (make sure to remove the ":").
</Note>
* You can also verify push notifications are working by opening your Mattermost site and mentioning a user who has push notifications enabled in **Settings > Notifications > Mobile Push Notifications**.
To view the log file, use:
```bash
$ sudo tail -n 1000 /var/log/upstart/mattermost-push-proxy.log
```
<Note title="Note">
Note that device IDs can change somewhat frequently, as they are tied to a device session. If you're having trouble, double-check the latest device IDs by re-running the above queries.
</Note>
### Troubleshooting
##### DeviceTokenNotForTopic
**For iOS / Apple Push Notifications**: If the logs are reflecting DeviceTokenNotForTopic (error 400) this may be because you're using an older / previous Device ID. Re-run the queries you need to get device IDs and test.
This could also be because you generated a key for the wrong bundle ID. The bundle ID used in `mattermost-push-proxy.json` should be the same one as the app, and should be for the same app it was generated for.
### Reporting issues
For issues with repro steps, please report to https://github.com/mattermost/mattermost/issues
@@ -0,0 +1,19 @@
---
title: "Storybook"
sidebar_position: 6
---
Storybook has been added to the `mobile` repository to help prototype components. To use Storybook:
1. In the root of the repository, run `npm run storybook`. This step automatically scans and loads all stories, then opens a new browser tab with the Storybook interface.
**Note**: When using a real device, you may need to configure the Storybook *Host URL* by updating the `.env` file in the root of the repository. When running in an emulator, the code tries to use the default network values.
2. Run the usual `npm run android` (or `npm run ios`) and `npm start` commands.
3. Storybook has been integrated into the react-native dev menu.
- On Mac OS, press CMD+D to open the dev menu when your app is running in an iOS Simulator, or press CMD+M when running in an Android emulator.
- On Windows and Linux, press CTRL+M to open the dev menu, then select the "Storybook" option.
- If running on a real device, shaking the device brings up the react-native dev menu. You can also press `d` in the terminal window where you ran `npm start`.
4. The Storybook interface opens in the mobile app. The stories can be controlled either through the desktop browser Storybook UI or the mobile browser Storybook UI. Both will render the component on the device.
>**Caveat**: Promises are currently broken in Storybook for react native. Components using promises will not work correctly. There is a temporary hacky fix to work around this issue: [storybookjs/react-native#57](https://github.com/storybookjs/react-native/issues/57#issuecomment-737931284).
@@ -0,0 +1,77 @@
---
title: "Sign unsigned Android builds"
sidebar_position: 1
---
With every Mattermost mobile app release, we publish the Android unsigned apk in in the [GitHub Releases](https://github.com/mattermost/mattermost-mobile/releases) page. This guide describes the steps needed to modify and sign the app, so it can be distributed and installed on Android devices.
#### Prerequisites
1. [Apktool](https://ibotpeaches.github.io/Apktool/) is a tool for reverse engineering Android apk files.
2. [XMLStarlet](http://xmlstar.sourceforge.net/doc/UG/xmlstarlet-ug.html) is a set of command line utilities (tools) which can be used to transform, query, validate, and edit XML documents and files using a simple set of shell commands in the same way it is done for plain text files using UNIX `grep`, `sed`, `awk`, `diff`, `patch`, `join`, etc., commands.
3. [JQ](https://stedolan.github.io/jq/) is like `sed` for JSON data - you can use it to slice, filter, map, and transform structured data with the same ease that `sed`, `awk`, and `grep` let you work with text.
4. Android SDK as described in the [Developer Setup](/developers/contribute/more-info/mobile/developer-setup#additional-setup-for-android).
5. Set up keys and Google Services as described in steps 2, 3, 4, and 6 of the [Build your own App guide](/developers/contribute/more-info/mobile/build-your-own/android#build-preparations).
6. [sign-android](/scripts/sign-android) script to sign the Android app.
#### Sign tool
```bash
Usage: sign-android <unsigned apk file>
[-e|--extract path]
[-p|--package-id packageID]
[-g|--google-services path]
[-d|--display-name displayName]
outputApk
Usage: sign-android -h|--help
Options:
-e, --extract path (Optional) Path to extract the unsigned APK file.
By default the path of the unsigned APK is used.
-p, --package-id packageID (Optional) Specify the unique Android application ID.
-g, --google-services path (Optional) Path to the google-services.json file.
Will setup the Firebase to receive Push Notifications.
Warning: will apply only if packageID is set.
-d, --display-name displayName (Optional) Specify new application display name.
By default "Mattermost" is used.
-h, --help Display help message.
```
#### Sign the Mattermost Android app
Now that all requirements are met, it's time to sign the Mattermost app for Android. Most of the options of the signing tool are optional but you should use your own `package identifier`, `google services settings`, and change the `display name`.
* Create a folder that will serve as your working directory to store all the needed files.
* Download the [sign-android](/scripts/sign-android) script and save it in your working directory.
* Download the [Android unsigned build](https://github.com/mattermost/mattermost-mobile/releases) and save it in your working directory.
* Open a terminal to your working directory and make sure the `sign-android` script is executable.
```
$ ls -la
total 49756
drwxr-xr-x 4 user staff 128 Oct 2 08:12 .
drwx------@ 59 user staff 1888 Oct 1 14:12 ..
-rw-r--r-- 1 user staff 50685064 Sep 29 10:58 Mattermost-unsigned.apk
-rw-r--r-- 1 user staff 2597 Oct 2 08:19 google-services.json
-rwxr-xr-x 1 user staff 7005 Sep 30 12:47 sign-android
```
* Sign the app
```bash
$ ./sign-android Mattermost-unsigned.apk -p com.example.test -g google-services.json -d "My App" MyApp-signed.apk
```
Once the code sign is complete you should have a signed APK in the working directory with the name **MyApp-signed.apk**.
---
<Note title="Note">
The app name can be anything but be sure to use double quotes if the name includes white spaces. If you are using a `Google Services` JSON file, you need to specify a `package identifier` that has a corresponding client in the JSON configuration file.
</Note>
---
@@ -0,0 +1,22 @@
---
title: "Sign unsigned builds"
sidebar_position: 4
---
Mattermost publishes an unsigned build of the mobile app in the [GitHub Releases](https://github.com/mattermost/mattermost-mobile/releases) page with every version that gets released.
These unsigned builds cannot be distributed nor installed directly on devices until they are properly signed.
---
<Note title="Note">
Android and Apple require all apps to be digitally signed with a certificate before they can be installed.
</Note>
---
To avoid rebuilding the apps from scratch, you could just **sign** the unsigned builds published by Mattermost with your certificates and keys.
- [Sign Unsigned Android](/developers/contribute/more-info/mobile/unsigned/android)
- [Sign Unsigned iOS](/developers/contribute/more-info/mobile/unsigned/ios)
@@ -0,0 +1,92 @@
---
title: "Sign unsigned iOS builds"
sidebar_position: 2
---
With every Mattermost mobile app release, we publish the iOS unsigned ipa in in the [GitHub Releases](https://github.com/mattermost/mattermost-mobile/releases) page, this guide describes the steps needed to modify and sign the app, so it can be distributed and installed on iOS devices.
#### Requisites
1. macOS with [Xcode](https://itunes.apple.com/us/app/xcode/id497799835?ls=1&mt=12) installed. The minimum required version is **11.0**.
2. Install the Xcode command line tools:
```bash
$ xcode-select --install
```
3. Set up your Certificate and Provisioning profiles as described in steps 1 and 2 for [Run on iOS Devices](/developers/contribute/more-info/mobile/developer-setup/run#run-on-ios-devices) in the Developer Setup.
4. [sign-ios](/scripts/sign-ios) script to sign the iOS app.
#### Sign Tool
```bash
Usage: sign-ios <unsigned ipa file>
[-a|--app provisioning]
[-n|--notification provisioning]
[-s|--share provisioning]
[-c|--certificate certificateName]
[-g|--app-group-id appGroupId]
[-d|--display-name displayName]
outputIpa
Usage: sign-ios -h|--help
Options:
-a, --app provisioning Provisioning profile for the main application.
-a xxx.mobileprovision
-n, --notification provisioning Provisioning profile for the notification extension.
-n xxx.mobileprovision
-s, --share provisioning Provisioning profile for the share extension.
-s xxx.mobileprovision
-d, --display-name displayName (Optional) Specify new application display name.
By default "Mattermost" is used.
Warning: will apply for all nested apps and extensions.
-g, --app-group-id appGroupId Specify the app group identifier to use (AppGroupId).
Warning: will apply for all nested apps and extensions.
-v, --verbose Verbose output.
-h, --help Display help message.
```
#### Sign the Mattermost iOS app
Now that all requisites are met, it's time to sign the Mattermost app for iOS. Most of the options of the signing tool are mandatory
and you should be using your own `provisioning profiles`, `certificate`, also you could change the app `display name`.
* Create a folder that will serve as your working directory to store all the needed files.
* Download your **Apple Distribution certificate** from the [Apple Developer portal](https://developer.apple.com/account/resources/certificates/list) and save it in your working directory.
* Install the previously downloaded certificate into your macOS Keychain. [Learn more](https://developer.apple.com/support/certificates).
* Download your **Provisioning profiles** from the [Apple Developer portal](https://developer.apple.com/account/resources/profiles/list) and save it in your working directory.
* Download the [sign-ios](/scripts/sign-ios) script and save it in your working directory.
* Download the [iOS unsigned build](https://github.com/mattermost/mattermost-mobile/releases) and save it in your working directory.
* Open a terminal to your working directory and make sure the `sign-ios` script is executable.
```
$ ls -la
total 81472
drwxr-xr-x 7 user staff 224 Oct 11 10:54 .
drwxr-xr-x 8 user staff 256 Oct 11 10:49 ..
-rw-r--r--@ 1 user staff 75261811 Oct 2 12:44 Mattermost-unsigned.ipa
-rw-r--r--@ 1 user staff 10746 Oct 2 10:30 app.mobileprovision
-rw-r--r--@ 1 user staff 9963 Oct 2 10:30 noti.mobileprovision
-rw-r--r--@ 1 user staff 10763 Oct 2 10:30 share.mobileprovision
-rwxr-xr-x 1 user staff 38581 Oct 11 10:54 sign-ios
```
* Sign the app
```bash
$ ./sign-ios Mattermost-unsigned.ipa -c "Apple Distribution: XXXXXX. (XXXXXXXXXX)" -a app.mobileprovision -n noti.mobileprovision -s share.mobileprovision -g group.com.mattermost -d "My App Display Name" MyApp-signed.ipa
```
Once the code sign is complete you should have a signed IPA in the working directory with the name **MyApp-signed.ipa**.
---
<Note title="Note">
The app name can be anything but be sure to use double quotes if the name includes white spaces. The name of the `certificate` should match the name in the macOS Keychain.
</Note>
---
@@ -0,0 +1,121 @@
---
title: "MVP"
---
Hundreds of developers around the world contribute to Mattermost open source projects. Our Hall of Fame honors the best of the best.
![MVP Award](/img/mvp_award.png)
The title of “Most Valued Professional” is awarded to an outstanding contributor for a key Mattermost release.
| Version | Release date | MVP |
|---------|--------------------|-----------------------------------------------------------------------------------|
| 11.2 | December 16, 2025 | [Angel Mendez](https://github.com/hereje)
| 11.1 | November 14, 2025 | [Vicktor](https://github.com/Victor-Nyagudi)
| 11.0 | October 16, 2025 | [Kaya Zeren](https://github.com/kayazeren)
| 10.12 | September 16, 2025 | [Lucas Reis](https://github.com/panoramix360)
| 10.11 | August 15, 2025 | [Alan Lew](https://github.com/neflyte)
| 10.10 | July 16, 2025 | [Harsh Aulakh](https://github.com/AulakhHarsh)
| 10.9 | June 16, 2025 | [Pineoak](https://translate.mattermost.com/user/pineoak-audio/)
| 10.8 | May 16, 2025 | [Lucas Reis](https://github.com/panoramix360)
| 10.7 | April 16, 2025 | [Clément Collin](https://github.com/cinlloc)
| 10.6 | March 14, 2025 | [Vicktor](https://github.com/Victor-Nyagudi)
| 10.5 | February 19, 2025 | [TheInvincible](https://github.com/TheInvincibleRalph)
| 10.4 | January 16, 2025 | [Rohan Sharma](https://github.com/RS-labhub)
| 10.3 | December 16, 2024 | [Nicolas Le Cam](https://github.com/KuSh)
| 10.2 | November 15, 2024 | [Tanmay Thole](https://github.com/tanmaythole) and [Rutam Prita Mishra](https://github.com/Rutam21)
| 10.1 | October 16, 2024 | [Ivy Gesare](https://github.com/Gesare5)
| 10.0 | September 16, 2024 | [Rita Anene](https://github.com/Camillarhi)
| 9.11 | August 16, 2024 | [Arya Khochare](https://github.com/Aryakoste)
| 9.10 | July 16, 2024 | [Frank Paul Silye](https://translate.mattermost.com/user/frankps)
| 9.9 | June 14, 2024 | [Anna Os](https://github.com/annaos) and [Ezekiel](https://github.com/ezekielchow)
| 9.8 | May 16, 2024 | [Varghese Jose](https://github.com/varghesejose2020)
| 9.7 | April 16, 2024 | [Tanmay Thole](https://github.com/tanmaythole)
| 9.6 | March 15, 2024 | [Syed Ali Abbas Zaidi](https://github.com/Syed-Ali-Abbas-Zaidi)
| 9.5 | February 16, 2024 | [Tom De Moor](https://github.com/ctlaltdieliet) |
| 9.4 | January 16, 2024 | [Paul Stern](https://github.com/Paul-Stern)
| 9.3 | December 15, 2023 | [Rutam Prita Mishra](https://github.com/Rutam21)
| 9.2 | November 16, 2023 | [Yusuke Nemoto](https://github.com/kaakaa)
| 9.1 | October 16, 2023 | [KyeongSoo Kim](https://github.com/kyeongsoosoo)
| 9.0 | September 15, 2023 | [Kaya Zeren](https://github.com/kayazeren)
| 8.1 | August 24, 2023 | [Ben Bodenmiller](https://github.com/bbodenmiller)
| 8.0 | July 14, 2023 | [Roy Orbitson](https://github.com/Roy-Orbison)
| 7.10 | April 14, 2023 | [Matthew Dorner](https://github.com/matthewdorner) and [Alexander Griesser](https://github.com/anx-ag/) |
| 7.9 | March 16, 2023 | [Matthew Dalton](https://github.com/matthew-src) |
| 7.8 | February 16, 2023 | [Sinan Sonmez](https://github.com/sinansonmez) |
| 7.7 | January 16, 2023 | [Julien Fabre](https://github.com/jufab) |
| 7.5 | November 16, 2022 | [Ayroti Dey Sarkar](https://github.com/ayrotideysarkar)
| 7.4 | October 16, 2022 | [Sridhar](https://github.com/sridhar02) |
| 7.3 | September 16, 2022 | [Vishakha Poonia](https://github.com/VishakhaPoonia) |
| 7.2 | August 16, 2022 | [Alexander Griesser](https://github.com/anx-ag/) |
| 7.1 | July 16, 2022 | [kamre](https://github.com/kamre) |
| 7.0 | June 16, 2022 | [Tom De Moor](https://github.com/ctlaltdieliet) |
| 6.7 | May 16, 2022 | [Vishakha Poonia](https://github.com/VishakhaPoonia) |
| 6.6 | April 16, 2022 | [Sayanta Banerjee](https://github.com/Sayanta66) |
| 6.5 | March 16, 2022 | [Sinan Sonmez](https://github.com/sinansonmez) |
| 6.4 | February 16, 2022 | [Suneet Srivastava](https://github.com/codedsun) |
| 6.3 | January 16, 2022 | [Ujjwal Sharma](https://github.com/shadowshot-x) |
| 6.2 | December 16, 2021 | [Julien Fabre](https://github.com/jufab) |
| 6.1 | November 16, 2021 | [Penthaa Patel](https://github.com/penthaapatel) |
| 6.0 | October 13, 2021 | [Johannes Marbach](https://github.com/Johennes) |
| 5.39 | September 16, 2021 | [Rutam Prita Mishra](https://github.com/Rutam21) |
| 5.38 | August 16, 2021 | [kamre](https://github.com/kamre) |
| 5.37 | July 16, 2021 | [Sera Geyran](https://github.com/srgyrn) |
| 5.36 | June 16, 2021 | [Ben Bodenmiller](https://github.com/bbodenmiller) |
| 5.35 | May 16, 2021 | [Alexander Brenchev](https://github.com/TheDarkestDay) |
| 5.34 | April 16, 2021 | [darkLord19](https://github.com/darkLord19) |
| 5.33 | March 16, 2021 | [Mahmudul Haque](https://github.com/mahmud2011) |
| 5.32 | February 16, 2021 | [Yusuke Nemoto](https://github.com/kaakaa) |
| 5.31 | January 16, 2021 | [Haardik Dharma](https://github.com/haardikdharma10) |
| 5.30 | December 16, 2020 | [XxLilBoPeepsxX](https://github.com/XxLilBoPeepsxX) |
| 5.29 | November 16, 2020 | [Tom De Moor](https://github.com/ctlaltdieliet) |
| 5.28 | October 16, 2020 | [Soo Hwan Kim](https://github.com/josephk96) |
| 5.27 | September 16, 2020 | [Mohan Prasath](https://github.com/openmohan) |
| 5.26 | August 16, 2020 | [Abdu Assabri](https://github.com/abdusabri) |
| 5.25 | July 16, 2020 | [Rodrigo Villablanca](https://github.com/rvillablanca) |
| 5.24 | June 16, 2020 | [Rakesh Peela](https://github.com/rakhi2104) |
| 5.23 | May 16, 2020 | [Vladimir Lebedev](https://github.com/nadalfederer) |
| 5.22 | April 16, 2020 | [Tim Estermann](https://github.com/der-test) |
| 5.21 | March 16, 2020 | [Allan Guwatudde](https://github.com/AGMETEOR) |
| 5.20 | February 16, 2020 | [Md Zubair Ahmed](https://github.com/M-ZubairAhmed) |
| 5.19 | January 16, 2020 | [Allen Lai](https://github.com/allenlai18) |
| 5.18 | December 16, 2019 | [larkox](https://github.com/larkox) |
| 5.17 | November 16, 2019 | [Andre Vasconcelos](https://github.com/avasconcelos114) |
| 5.16 | October 16, 2019 | [Paulo Bittencourt](https://github.com/pbitty) |
| 5.15 | September 16, 2019 | [Siyuan Liu](https://github.com/liusy182) |
| 5.14 | August 16, 2019 | [Rodrigo Villablanca Vásquez](https://github.com/rvillablanca) |
| 5.13 | July 16, 2019 | [Adrian Mönnich](https://github.com/thiefmaster) |
| 5.12 | June 16, 2019 | [Scott Davis](https://github.com/scottleedavis) |
| 5.11 | May 16, 2019 | [Maneschi Romain](https://github.com/manland) |
| 5.10 | April 16, 2019 | [Grzegorz Kosik](https://github.com/kosgrz) |
| 5.9 | March 16, 2019 | [JtheBAB](https://github.com/JtheBAB) |
| 5.8 | February 16, 2019 | [Kyâne Pichou](https://github.com/pichouk) |
| 5.7 | January 16, 2019 | [sadb](https://github.com/sadb) |
| 5.6 | December 16, 2018 | [Pradeep Murugesan](https://github.com/pradeepmurugesan) |
| 5.5 | November 16, 2018 | [Mukul Rawat](https://github.com/mukulrawat1986) |
| 5.4 | October 16, 2018 | [Hanzei](https://github.com/Hanzei) |
| 5.3 | September 16, 2018 | [SmartHoneybee](https://github.com/SmartHoneybee) |
| 5.2 | August 16, 2018 | [Daniel Schalla](https://github.com/DSchalla) |
| 5.1 | July 16, 2018 | [Hyeseong Kim](https://github.com/cometkim) |
| 5.0 | June 16, 2018 | [William Gathoye](https://github.com/wget) |
| 4.10 | May 16, 2018 | [Siyuan Liu](https://github.com/liusy182) |
| 4.9 | April 16, 2018 | [Christian Hoff](https://github.com/chumbalum) |
| 4.8 | March 16, 2018 | [Pierre de La Morinerie of Codeurs en Liberté](https://github.com/kemenaran) |
| 4.7 | February 16, 2018 | [Kher Yee Ting](https://github.com/tkbky) |
| 4.6 | January 16, 2018 | [Yusuke Nemoto](https://github.com/kaakaa) |
| 4.5 | December 16, 2017 | [Ryan Wang](https://github.com/r-wang97) |
| 4.4 | November 16, 2017 | [Sudheer Timmaraju](https://github.com/sudheerDev) |
| 4.3 | October 16, 2017 | [Jesús Espino](https://github.com/jespino) |
| 4.2 | September 16, 2017 | Uber Development Team |
| 4.1 | August 16, 2017 | [Nazar Laba](https://github.com/n1aba) |
| 4.0 | July 16, 2017 | [prixone](https://github.com/prixone) and [sousapro](https://github.com/sousapro) |
| 3.10 | June 16, 2017 | [Galois, Inc.](https://github.com/matterhorn-chat) |
| 3.9 | May 16, 2017 | [Carlos Tadeu Panato Junior](https://github.com/cpanato) |
| 3.8 | April 16, 2017 | [VeraLyu](https://github.com/veralyu) |
| 3.7 | March 16, 2017 | [Saturnino Abril](https://github.com/saturninoabril) |
| 3.6 | January 16, 2017 | [Carlos Tadeu Panato Junior](https://github.com/cpanato) |
| 3.5 | November 16, 2016 | [Harshavardhana](https://github.com/harshavardhana) |
| 3.4 | September 16, 2016 | [Wim van Wemmel](https://github.com/42wim) |
| 3.3 | August 16, 2016 | [Ivan Naydonov](https://github.com/samogot) |
| 3.2 | July 16, 2016 | [Christian Arnold](https://github.com/meilon) |
| 3.1 | June 16, 2016 | [Thomas Balthazar](https://github.com/tbalthazar) |
@@ -0,0 +1,19 @@
---
title: "Plugins"
sidebar_position: 10
---
Mattermost plugins are isolated pieces of code written in Go and/or React. They're separate from the main repositories and are used to extend the functionality of the Mattermost server and webapp.
- The Go portions run directly on the Mattermost server, and are managed by the server at runtime.
- The React portions run in each user's browser, allowing developers to modify the user interface in [several ways](/developers/integrate/plugins/components/webapp/best-practices).
The plugin Help Wanted tickets are located in each plugin's respective GitHub repository. In order to browse all of the open tickets, see the plugin [Help Wanted tickets](https://mattermost.com/pl/help-wanted-plugins/) page with links to specific plugin repositories, as well as queries for Help Wanted tickets in all repositories. The [All Plugins Up for Grabs](https://github.com/issues?utf8=%E2%9C%93&q=repo%3Amattermost%2Fmattermost-plugin-agenda+repo%3Amattermost%2Fmattermost-plugin-antivirus+repo%3Amattermost%2Fmattermost-plugin-autolink+repo%3Amattermost%2Fmattermost-plugin-aws-SNS+repo%3Amattermost%2Fmattermost-plugin-custom-attributes+repo%3Amattermost%2Fmattermost-oembed-plugin+repo%3Amattermost%2Fmattermost-plugin-giphy+repo%3Amattermost%2Fmattermost-plugin-github+repo%3Amattermost%2Fmattermost-plugin-gitlab+repo%3Amattermost%2Fmattermost-plugin-google-calendar+repo%3Amattermost%2Fmattermost-plugin-jenkins+repo%3Amattermost%2Fmattermost-plugin-jira+repo%3Amattermost%2Fmattermost-plugin-msoffice+repo%3Amattermost%2Fmattermost-plugin-solar-lottery+repo%3Amattermost%2Fmattermost-plugin-suggestions+repo%3Amattermost%2Fmattermost-plugin-todo+repo%3Amattermost%2Fmattermost-plugin-webex+repo%3Amattermost%2Fmattermost-plugin-welcomebot+repo%3Amattermost%2Fmattermost-plugin-zoom+repo%3Amattermost%2Fmattermost-plugin-msteams-meetings+is%3Aopen+is%3Aissue+archived%3Afalse+label%3A%22help%20wanted%22%20label%3A%22up%20for%20grabs%22%20) link is useful to browse all repositories at once.
The plugin [developer setup](/developers/integrate/plugins/developer-setup) and [developer workflow](/developers/integrate/plugins/developer-workflow) pages are useful to learn about the plugin development environment. You can find more information about plugins in general [here](/developers/integrate/plugins).
<Note title="Note">
The `make` commands listed in the [developer workflow](/developers/integrate/plugins/developer-workflow#common-make-commands-for-working-with-plugins) page (specifically `make test` and `make check-style`) should be used locally to run certain tests before submitting a PR. This makes the PR review process much more streamlined overall.
</Note>
Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

@@ -0,0 +1,100 @@
---
title: "CLI commands"
sidebar_position: 5
---
As of 6.0, Mattermost CLI has been replaced by [mmctl](https://github.com/mattermost/mmctl). `mmctl` is built to enable access to Mattermost server from the command line. The tool leverages the public API so that administrator and user tasks can be performed.
Since `mmctl` uses the public API, an authorization mechanism is required. Which means the access rights are managed on the server side. There is a pre-run check to read credentials and use it in the client. In addition to authentication via credentials, `mmctl` can communicate to a local server without any authentication. This must be enabled via server configuration and both `mmctl` and `mattermost/server` needs to be running in the same machine.
In addition to provide more functionality towards testing and development, `db` subcommand has been added Mattermost server binary.
The CLI interface is written using [Cobra](https://github.com/spf13/cobra), a
powerful and modern CLI creation library. If you have never used Cobra before, it is
well documented in its [GitHub Repository](https://github.com/spf13/cobra).
The source code used to build our CLI interface is written in the `commands` directory of the [mmctl](https://github.com/mattermost/mmctl) repository.
Each "command" of the CLI is stored in a different file of the
`commands` directory. Within each file, you can find
multiple "subcommands".
## Add a new subcommand
If you want to add a new subcommand in an existing mattermost command, first find the relevant file. For example, if you want to add a `show` command to
the `channel` command, go to `commands/channel.go` and add your subcommand there.
To add the subcommand, start by creating a new `Command` instance, for example:
```go
var ChannelShowCmd = &cobra.Command{
Use: "show",
Short: "Show channel info",
Long: "Show channel information, including the name, header, purpose and the number of members.",
Example: " channel show --team myteam --channel mychannel"
RunE: showChannelCmdF,
}
```
Then implement the subcommand function, in this example `showChannelCmdF`.
```go
func showChannelCmdF(c client.Client, cmd *cobra.Command, args []string) error {
// Your code implementing the command itself
newChannel, _, err := c.ShowChannel(channel)
if err != nil {
return err
}
return nil
}
```
Now, you set the flags of your subcommand and register it in the command. In our case we register our new `ChannelShowCmd` flag in `ChannelCmd`.
```go
func init() {
...
ChannelShowCmd.Flags().String("team", "", "Team name or ID")
ChannelShowCmd.Flags().String("channel", "", "Channel name or ID")
...
ChannelCmd.AddCommand(
...
ChannelShowCmd,
)
...
}
```
Finally, implement unit tests in `commands/channel_test.go` and end-to-end tests to commands/channel_e2e_test.go`.
## Add a new command
If you want to add a new command to `mmctl`, first create a file for the command.
For example, if you want to add a new `emoji` command to manage emojis in
Mattermost from the CLI, create `commands/emoji.go`
and add your command and your subcommands there.
A command is exactly the same as a subcommand, so you can follow the same
steps of the previous section. However, you must also register the new command in the
"Root" command as follows:
```go
var EmojiCmd = &cobra.Command{
Use: "emoji",
Short: "Emoji management",
Long: "Lists, creates and deletes custom emoji",
}
func init() {
...
RootCmd.AddCommand(EmojiCmd)
...
}
```
Usually, you would then add several subcommands to perform various tasks.
## Submit your pull request
Please submit a pull request against the [mattermost/mmctl](https://github.com/mattermost/mmctl) repository by [following these instructions](/developers/contribute/more-info/server/developer-workflow).
@@ -0,0 +1,31 @@
---
title: "Dependencies"
sidebar_position: 5
---
The Mattermost server uses [Go modules](https://github.com/golang/go/wiki/Modules) to manage dependencies.
## Add or update a new dependency
Adding a dependency is easy. All you have to do is import the dependency in the code and recompile. The dependency will be automatically added for you. Updating uses the same procedure.
Before committing the code with your new dependency added, be sure to run `go mod tidy` to maintain a consistent format and `go mod vendor` to synchronize the vendor directory.
If you want to add or update to a specific version of a dependency you can use a command of the form:
```bash
go get -u github.com/pkg/errors@v0.8.1
go mod tidy
go mod vendor
```
If you just want whatever the latest version is, you can leave off the `@version` tag.
## Remove a dependency
Be sure you have enabled go modules support. After removing all references to the dependency in the code, you run:
```bash
go mod tidy
go mod vendor
```
to remove it from the `go.mod` file and the `vendor` directory.
@@ -0,0 +1,113 @@
---
title: "Server workflow"
sidebar_position: 3
---
If you haven't [set up your developer environment](/developers/contribute/developer-setup), please do so before continuing with this section.
Join the [Developers community channel](https://community.mattermost.com/core/channels/developers) to ask questions from community members and the Mattermost core team.
### Workflow
Here's a general workflow for a Mattermost developer working on the [mattermost](https://github.com/mattermost/mattermost) repository:
#### Making code changes
1. Review the repository structure to familiarize yourself with the project:
* [./server/channels/api4/](https://github.com/mattermost/mattermost/tree/master/server/channels/api4) holds all API and application related code.
* [./server/public/model/](https://github.com/mattermost/mattermost/tree/master/server/public/model) holds all data model definitions and the Go driver.
* [./server/channels/store/](https://github.com/mattermost/mattermost/tree/master/server/channels/store) holds all database querying code.
* [./server/channels/utils/](https://github.com/mattermost/mattermost/tree/master/server/channels/utils) holds all utilities, such as the mail utility.
* [./server/i18n/](https://github.com/mattermost/mattermost/tree/master/server/i18n) holds all localization files for the server.
2. On your fork, create a feature branch for your changes. Name it `MM-$NUMBER_$DESCRIPTION` where `$NUMBER` is the [Jira](https://mattermost.atlassian.net) ticket number you are working on and `$DESCRIPTION` is a short description of your changes. Example branch names are `MM-18150_plugin-panic-log` and `MM-22037_uppercase-email`.
3. Make the code changes required to complete your ticket.
#### Running and writing tests
4. Ensure that unit tests are written or modified where appropriate. For the server repository in general, Mattermost follows the opinionated way of testing in Go. You can learn more about this process in [DigitalOcean's How To Write Unit Tests in Go tutorial](https://www.digitalocean.com/community/tutorials/how-to-write-unit-tests-in-go-using-go-test-and-the-testing-package). Test files must always end with `_test.go`, and should be located in the same folder where the code they are checking lives. For example, check out [download.go](https://github.com/mattermost/mattermost/blob/master/server/channels/app/download.go) and [download_test.go](https://github.com/mattermost/mattermost/blob/master/server/channels/app/download_test.go), which are both located in the `app` folder. Please also use [testify](https://github.com/stretchr/testify) for new tests.
5. If you made changes to the store, run `make store-mocks` and `make store-layers` to update test mocks and timing layer.
6. To test your changes, run `make run-server` from the root directory of the server repository. This will start up the server at `http://localhost:8065`. To get changes to the server it must be restarted with `make restart-server`. If you want to test with the web app, you may also run `make run` which will start the server and a watcher for changes to the web app.
7. Once everything works to meet the ticket requirements, stop Mattermost by running `make stop` in the server repository, then run `make check-style` to check your syntax.
8. Run the tests using one or more of the following options:
* Run `make test` to run all the tests in the project. This may take a long time and provides very little feedback while it's running.
* Run individual tests by name executing `go test -run "TestName" ./<directory>`.
* Run all the tests in a package where changes were made executing `go test app`.
* Create a draft PR with your changes and let our CI servers run the tests for you.
9. Running every single unit test takes a lot of time while making changes, so you can run a subset of the server-side unit tests by using the following:
```
go test -v -run='<test name or regex>' ./<package containing test>
```
For example, if you want to run `TestUpdatePost` in `app/post_test.go`, you would execute the following:
```
go test -v -run='TestUpdatePost' ./app
```
10. If you added or changed any localization strings you will need to run `make i18n-extract` to generate the new/updated strings.
#### Testing email notifications
11. When Docker starts, the SMTP server is available on port 2500. A username and password are not required. You can access the Inbucket webmail on port 9000. For additional information on configuring an SMTP email server, including troubleshooting steps, see the [SMTP email setup page in the Mattermost user documentation](https://docs.mattermost.com/install/smtp-email-setup.html).
#### Testing with GitLab Omnibus
12. To test a locally compiled version of Mattermost with GitLab Omnibus, replace the following GitLab files:
* The compiled `mattermost` binary in `/opt/gitlab/embedded/bin/mattermost`.
* The assets (templates, i18n, fonts, webapp) in `/opt/gitlab/embedded/service/mattermost`.
#### Creating a pull request (PR)
13. Commit your changes, push your branch, and [create a pull request](/developers/contribute/more-info/getting-started/contribution-checklist).
14. Once a PR is submitted it's best practice to avoid rebasing on the base branch or force-pushing. Jesse, a developer at Mattermost, mentions this in his blog article [Submitting Great PRs](https://mattermost.com/blog/submitting-great-prs/). When the PR is merged, all the PR's commits are automatically squashed into one commit, so you don't need to worry about having multiple commits on the PR.
15. That's it! Rejoice that you've helped make Mattermost better.
### Useful Server makefile commands
Some useful `make` commands include:
* `make run` runs the server, creates a symlink for your mattermost-webapp folder, and starts a watcher for the web app.
* `make stop` stops the server and the web app watcher.
* `make run-server` runs only the server and not the client.
* `make debug-server` will run the server in the `delve` debugger.
* `make stop-server` stops only the server.
* `make update-docker` stops and updates your Docker images. This is needed if any changes are made to `docker-compose.yaml`.
* `make clean-docker` stops and removes your Docker images and is a good way to wipe your database.
* `make clean` cleans your local environment of temporary files.
* `make config-reset` resets the `config/config.json` file to the default.
* `make nuke` wipes your local environment back to a completely fresh start.
* `make package` creates packages for distributing your builds and puts them in the `./dist` directory. You will first need to run `make build` and `make build-client`.
If you would like to run the development environment without Docker you can set the `MM_NO_DOCKER` environment variable. If you do this, you will need to set up your own database and any of the other services needed to run Mattermost.
### Useful Mattermost and mmctl commands
During development you may want to reset the database and generate random data for testing your changes. For this purpose, Mattermost has the following commands in the Mattermost CLI:
1. First, install the server with `go install ./cmd/mattermost` in the server repository.
2. You can reset your database to the initial state using:
```
mattermost db reset
```
3. The following commands need to be run via the [mmctl](https://docs.mattermost.com/manage/mmctl-command-line-tool.html) tool.
* You can generate random data to populate the Mattermost database using:
```
mmctl sampledata
```
* Create an account using the following command:
```
mmctl user create --email user@example.com --username test1 --password mypassword
```
* Optionally, you can assign that account System Admin rights with the following command:
```
mmctl user create --email user@example.com --username test1 --password mypassword --system-admin
```
### Customize your workflow
#### Makefile variables
You can customize variables of the Makefile by creating a `config.override.mk` file or setting environment variables. To get started, you can copy the `config.mk` file to `config.override.mk` and change the values in your newly copied file.
#### Docker-compose configurations
If you create a `docker-compose.override.yaml` file at the root of the project, it will be automatically loaded by all the `Makefile` tasks using `docker-compose`, allowing you to define your own services or change the configuration of the ones Mattermost provides.
@@ -0,0 +1,122 @@
---
title: "Feature flags"
sidebar_position: 3
---
# What are feature flags
Feature flag is a software development technique that turns functionality on and off without deploying new code. Feature flags allow us to be more confident in shipping features continuously to Mattermost Cloud. Feature flags also allow us to control which features are enabled on a cluster level.
# How to use feature flags
## When to use
There are no hard rules on when a feature flag should be used. It is left up to the best judgement of the responsible engineers to determine if a feature flag is required. The following are guidelines designed to help the determination:
- Any "substantial" feature should have a flag
- Features that are probably substantial:
- Features with new UI or changes to existing UI
- Features with a risk of regression
- Features that are probably not substantial:
- Small bug fixes
- Refactoring
- Changes that are not user facing and can be completely verified by unit and E2E testing.
In all cases, ask yourself: Why do I need to add a feature flag? If I don't add one, what options do I have to control the impact on user experience (e.g. a config setting or System Console setting)?
## Add the feature flag in code
1. Add the new flag to the feature flag struct located in `model/feature_flags.go`.
2. Set a default value in the `SetDefaults` function in the same file.
3. Use the feature flag in code as you would use a regular configuration setting. In tests, manipulate the configuration value to test value changes, such as activation and deactivation of the feature flag.
4. Code may be merged regardless of setup in the management system. In this case it will always take the default value supplied in the `SetDefaults` function.
5. Create a removal ticket for the feature flag. All feature flags should be removed as soon as they have been verified by Cloud. The ticket should encompass removal of the supporting code and archiving in the management system.
### Feature flag code guidelines
- A ticket should be created when a feature flag is added to remove the feature flag as soon as it isn't required anymore.
- Tests should be written to verify the feature flag works as expected. Note that in cases where there may be a migration or new data, off to on and on to off should both be tested.
- Log messages by the feature should include the feature flag tag, with the feature flag name as a value, in order to ease debugging.
# Changing Feature Flag Values
## Self Hosted (and local development)
Feature flag values can be changed via environment variables. The environment variable set follows the pattern `MM_FEATUREFLAGS_<name>` where `<name>` is the uppercase key of the feature flag you added to model/feature_flags.go
## Cloud
Feature flag adjustments (ie, turning something on or off) in the Mattermost Cloud environment are owned and controlled by the Cloud team. To change the value for a feature flag, please open a ticket.
## Timelines for rollouts
Typically feature flag will initially disable the feature. It's a good idea to test the feature during a safe time or on a subset of instances. Each team can decide what's best and there's no need to request the flag value changes from the Cloud team. If you think there might be a performance impact there's no harm in communicating your plan beforehand.
<Note title="Note">
The steps below are an initial guideline and will be iterated on over time.
- 1st week after feature is merged (T-30): 10% rollout; only to test servers, no rollout to customers.
- 2nd week (T-22): 50% rollout; rollout to some customers (excluding big customers and newly signed-up customers); no major bugs in test servers.
- 3rd week (T-15): 100% rollout; no major bugs from customers or test servers.
- End of 3rd week (T-8): Remove flag. Feature is production ready and not experimental.
</Note>
For smaller, non-risky features, the above process can be more fast tracked as needed, such as starting with a 10% rollout to test servers, then 100%.
Features have to soak on Cloud for at least two weeks for testing. Focus is on severity and number of bugs found; if there are major bugs found at any stage, the feature flag can be turned off to roll back the feature.
When the feature is rolled out to customers, logs will show if there are crashes, and normally users will report feedback on the feature (e.g. bugs).
## Self-hosted releases
For self-hosted releases, typically a flagged feature will be released in an enabled state. That said, you can release a feature to self-hosted disabled, [it's not unprecedented](https://github.com/mattermost/mattermost/blob/master/server/public/model/feature_flags.go#L75).
## Tests
Tests should be written to verify all states of the feature flag. Tests should cover any migrations that may take place in both directions (i.e., from "off" to "on" and from "on" to "off"). Ideally E2E tests should be written before the feature is merged, or at least before the feature flag is removed.
## Examples of feature flags
Some [examples are here](https://github.com/mattermost/mattermost/blob/master/server/public/model/feature_flags.go#L75).
## FAQ
1. What are the expected values for boolean feature flags?
- Normally ``true`` or ``false``, but this may not always equate to enabled/disabled. A feature flag that introduces three new sorting algorithms can also be written:
- "selection" (default, the existing strategy in production)
- "bubble"
- "quick"
2. Is it possible to use a plugin feature flag such as `PluginIncidentManagement` to "prepackage" a plugin only on Cloud by only setting a plugin version to that flag on Cloud? Can self-hosted customers manually set that flag to install the said plugin?
- Yes. If you leave the default "" then nothing will happen for self-hosted installations.
3. How do feature flags work on webapp?
- To add a feature flag that affects frontend, the following is needed:
1. PR to server code to add the new feature flag.
2. PR to redux to update the types.
3. PR to webapp to actually use the feature flag.
4. How do feature flags work on mobile?
- To add a feature flag that affects mobile, the following is needed:
1. PR to server code to add the new feature flag.
2. PR to mobile to update the types and to actually use the feature flag.
5. What is the environment variable to set a feature flag?
- It is `MM_FEATUREFLAGS_<myflag>`.
6. Can plugins use feature flags to enable small features aside of the version forcing feature flag?
- Yes. You can create feature flags as if they were added for the core product, and they'll get included in the plugin through the config.
7. Do feature flag changes require the server to be restarted?
- Feature flags dont require a server restart unless the feature being flagged requires a restart itself.
8. For features that are requested by self-hosted customers, why do we have to deploy to Cloud first, rather than having the customer who has the test case test it?
- Cloud is the way to validate the stability of the feature before it goes to self-hosted customers. In exceptional cases we can let the self-hosted customer know that they can use environment variables to enable the feature flag (but specify that the feature is experimental).
9. How does the current process take into account bugs that may arise on self-hosted specifically?
- The process hasnt changed much from the old release process: Features can still be tested on self-hosted servers once they have been rolled out to Cloud. The primary goal is that bugs are first identified on Cloud servers.
10. How can self-hosted installations set feature flags?
- Self-hosted installations can set environment variables to set feature flag values. However, users should recognize that the feature is still considered "experimental" and should not be enabled on production servers.
@@ -0,0 +1,28 @@
---
title: "Server"
sidebar_position: 2
---
The server is the highly scalable backbone of the Mattermost project. Written in Go, it compiles to a single, standalone binary. It's generally stateless except for the WebSocket connections and some in-memory caches.
Communication with Mattermost clients and integrations mainly occurs through the RESTful JSON web API and WebSocket connections primarily used for event delivery.
Data storage is done with MySQL or PostgreSQL for non-binary data. Files are stored locally, on network drives or in a service such as S3 or Minio.
## Repository
https://github.com/mattermost/mattermost
## Server packages
The server consists of several different Go packages:
* `api4` - Version 4 of the RESTful JSON Web Service
* `app` - Logic layer for getting, modifying, and interacting with models
* `cmd` - Command line interface
* `einterfaces` - Interfaces for Enterprise Edition features
* `jobs` - Job server and scheduling
* `model` - Definitions and helper functions for data models
* `store` - Storage layer for interacting with caches and databases
* `utils` - Utility functions for various tasks
* `web` - Serves static pages
Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

@@ -0,0 +1,36 @@
---
title: "Plugins"
sidebar_position: 5
---
Mattermost supports plugins that offer powerful features for extending and deeply integrating with both the server and web/desktop apps.
This document covers the plugin infrastructure and how to contribute to it.
## Build plugins
Looking to build a plugin? [Then you want the plugin author documentation.](/developers/integrate/plugins)
## Overview
Plugins are generally made of at least two parts: a manifest and a server binary and/or a JavaScript bundle.
The manifest tells Mattermost what the plugin is and provides a set of metadata used by the server to install and run the plugin. Please see the [manifest reference](/developers/integrate/plugins/manifest-reference) for more information. Manifests may be defined in JSON or YAML.
The server binary is a compiled Go program that extends the [MattermostPlugin](https://godoc.org/github.com/mattermost/mattermost/server/public/plugin#MattermostPlugin) struct of the [plugin](https://godoc.org/github.com/mattermost/mattermost/server/public/plugin) package. When enabled, the plugin's server binary is started as a process by the Mattermost server. Plugin developers then have access to interact with the Mattermost server over RPC through the plugin [API](/developers/integrate/reference/server/server-reference#API) and [Hooks](/developers/integrate/reference/server/server-reference#Hooks). The server-side of plugins is built using the [go-plugin](https://github.com/hashicorp/go-plugin) library from Hashicorp. More information is available in the [server side of the plugin author documentation](/developers/integrate/plugins/components/server).
The JavaScript bundle is a webpack-built collection of JavaScript code that will be run on the Mattermost web/desktop apps. When a plugin is enabled, the client is notified and it makes a request to add the JS bundle to the document. The plugin's client code then registers itself and its components with the Mattermost client through the client's [plugin registry](/developers/integrate/reference/webapp/webapp-reference#registry). The registry contains many methods for registering different components and callbacks. These are all stored as part of the app's [plugin reducer](https://github.com/mattermost/mattermost/blob/master/webapp/channels/src/reducers/plugins/index.ts). The [Pluggable](https://github.com/mattermost/mattermost/tree/master/webapp/channels/src/plugins/pluggable) component is then inserted into various places in the app, allowing plugins to insert components into these locations in the UI. In some special cases, the Pluggable component is not used and we instead implement the plugs manually. More information is available in the [webapp side of the plugin author documentation](/developers/integrate/plugins/components/webapp).
All these different components of a plugin are compressed into a .tar.gz bundle. Installing a plugin is the process of uploading this bundle to the Mattermost server (via the UI, REST API or CLI). The server then unpacks the bundle, performs some validation and extracts it into the configured directory for storing installed plugins. Installed plugins are not yet running. To start a plugin it must be enabled (again via the UI, REST API or CLI). Once it is enabled, the server will then start the server process and prepare the web app bundle for serving to the client. Plugin settings, configuration and enabled/disabled status are managed by the Mattermost `config.json` using a [PluginSettings](https://godoc.org/github.com/mattermost/mattermost/server/public/model#PluginSettings) struct.
Check out the [`plugin` package](https://github.com/mattermost/mattermost/tree/master/server/public/plugin) and the [plugin_* files in the `app` package](https://github.com/mattermost/mattermost/tree/master/server/channels/app) for the code, and [mattermost-plugin-demo](https://github.com/mattermost/mattermost-plugin-demo) for an example plugin. To start developing your own plugin, please follow the instructions [here](/developers/integrate/plugins/developer-setup).
## Add an API
To add a plugin API you need to add the signature of your new method to the [API interface](https://github.com/mattermost/mattermost/blob/master/server/public/plugin/api.go). You then need to implement the API in the [plugin_api.go](https://github.com/mattermost/mattermost/blob/master/server/channels/app/plugin_api.go) of the `app` package. Finally, you need to run `make pluginapi` to generate the RPC glue code needed for your new API and `make plugin-mocks` to generate the mocks used for plugin testing.
That's it! Submit your pull request.
## Questions?
If you have any questions, feel free to ask in the [Toolkit channel](https://community.mattermost.com/core/channels/developer-toolkit) of our Mattermost community instance.
@@ -0,0 +1,101 @@
---
title: "REST API"
sidebar_position: 4
---
The REST API is a JSON web service that facilitates communication between Mattermost clients, as well as integrations, and the server. The server is currently on API version 4.
### Reference
Looking for the API reference? You can find it here: [https://api.mattermost.com](https://api.mattermost.com).
### Add an endpoint
To add an endpoint to API version 4, all of the following must be completed:
- [Reference](#reference)
- [Add an endpoint](#add-an-endpoint)
- [Document the endpoint](#document-the-endpoint)
- [Implement the API handler](#implement-the-api-handler)
- [Update the Golang driver](#update-the-golang-driver)
- [Write a unit test](#write-a-unit-test)
- [Submit your pull request (PR)](#submit-your-pull-request-pr)
- [Legacy Notes](#legacy-notes)
#### Document the endpoint
At Mattermost, the [OpenAPI specification](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md) is used for API documentation. The API documentation lives in the main Mattermost repository alongside the server: [api](https://github.com/mattermost/mattermost/tree/master/api).
To document an endpoint, follow these steps:
1. Find the `.yaml` file in the [api/v4/source](https://github.com/mattermost/mattermost/tree/master/api/v4/source) directory that fits your endpoint.
- For example, if you were adding the `GET /users/{user_id}` endpoint you would be looking for the [users.yaml](https://github.com/mattermost/mattermost/blob/master/api/v4/source/users.yaml) file.
- If the file doesn't exist yet, you may need to create it and then update the [Makefile](https://github.com/mattermost/mattermost/tree/master/api/Makefile) to include the file.
2. Copy an existing endpoint from the same or a different file.
3. Update the documentation you copied with the correct information for your endpoint, including:
- `Tag` - the resource type
- `Summary` - a summary of few words
- `Description` - a brief 1-2 sentence description
- `Permissions` - the permission(s) required
- `Parameters` - the URL and body parameters
- `Responses` - the success and error responses
4. Confirm you don't have any syntax errors by running `make build` within the [api](https://github.com/mattermost/mattermost/tree/master/api/) directory.
5. Continue with the implementation of your API handler, updating this documentation as needed.
#### Implement the API handler
To implement the API handler, you'll first need to [setup your developer environment](/developers/contribute/developer-setup), and then follow these steps:
1. Add the declaration for your endpoint. For an example, check out the [/api4/user.go](https://github.com/mattermost/mattermost/blob/master/server/channels/api4/user.go) file.
2. Implement the handler for your endpoint. Follow this general pattern for handlers:
```Go
func handlerName(c *Context, w http.ResponseWriter, r *http.Request) {
// 1. Parse the request URL and body.
// 2. Do a permissions check if required.
// 3. Invoke handler logic through the app package.
// 4. (Optional) Check the Etag.
// 5. Format the response and write the response.
}
```
For examples, see the [createUser()](https://github.com/mattermost/mattermost/blob/d693f880431741e3e1482503c4e80d6148b0f1bf/server/channels/api4/user.go#L111) and the [getUser()](https://github.com/mattermost/mattermost/blob/d693f880431741e3e1482503c4e80d6148b0f1bf/server/channels/api4/user.go#L177) handlers.
3. Run the server by runing `make run-server` within the [server](https://github.com/mattermost/mattermost/tree/master/server/) directory.
4. Use `curl` or [Postman](https://www.getpostman.com/) to test the basics of your endpoint.
#### Update the Golang driver
The Go driver for APIv4 is in [/model/client4.go](https://github.com/mattermost/mattermost/blob/master/server/public/model/client4.go). To add a function to support your new endpoint:
1. Copy over an existing driver function, such as [CreateUser](https://github.com/mattermost/mattermost/blob/master/server/public/model/client4.go#L827).
2. Paste the function into the section for your endpoint. For example, `POST /teams` would go in the Teams section.
3. Modify the function to correctly hit your endpoint. Make sure to update the request method to match your endpoint's HTTP method.
#### Write a unit test
The most important part of this process is to make sure the new endpoint works correctly. Follow these steps to write a unit test:
1. Open the test Go file related to your endpoint, or create one if necessary. For example, if you put your handler in [/api4/user.go](https://github.com/mattermost/mattermost/blob/master/server/channels/api4/user.go), your test will go in [/api4/user\_test.go](https://github.com/mattermost/mattermost/blob/master/server/channels/api4/user_test.go).
2. Write your test based on the other tests in your file (or folder). There are several helper functions in [/api4/apitestlib.go](https://github.com/mattermost/mattermost/blob/master/server/channels/api4/apitestlib.go) that you may use.
3. Ensure that your test covers the following:
- All combinations of correct inputs to your endpoint.
- Etags for your endpoint, if applicable.
- Incorrect URL or body parameters return a **400 Bad Request** status code.
- Requests without a token return a **401 Unauthorized** status code (for endpoints requiring a session).
- Requests with insufficient permissions return a **403 Forbidden** status code (for endpoints requiring permission).
- Requests to non-existent resources or URLs return a **404 Not Found** status code.
Returning the correct error code might require investigation in the [app](https://github.com/mattermost/mattermost/tree/master/server/channels/app) or [store](https://github.com/mattermost/mattermost/tree/master/server/channels/store) to find the source of errors. Status codes on errors should be set at the creation of the error.
#### Submit your pull request (PR)
Submit your pull request against the [mattermost/mattermost](https://github.com/mattermost/mattermost) repository by [following these instructions](/developers/contribute/more-info/server/developer-workflow).
### Legacy Notes
The Mattermost API used to be defined in https://github.com/mattermost/mattermost-api-reference, but the source has since been moved to the [mattermost/mattermost](https://github.com/mattermost/mattermost) to streamline making code and documentation changes at the same time.
@@ -0,0 +1,301 @@
---
title: "DB migration guide"
sidebar_position: 3
---
## Overview
This document aims to do an analysis of the types of schema migrations we do in Mattermost and ways to make them non-blocking so as to improve the Mattermost upgrade experience.
Historically, we have never put a lot of thought to the migration process. Developers would simply add a DDL statement and call it a day. But that causes a significant impact to large customers for whom downtime is not an option. This causes them to push back on upgrading their Mattermost version for a long time (sometimes for several years). This in turn, has some cascading effects like customers not able to get new features, performance improvements etc.
We want to improve the situation and make upgrades a worry-free experience for our customers. It definitely comes at a cost of writing more code and delaying some features to avoid breaking changes. This document will aim to uncover all those cases and provide best practices to follow so that we can hit the right balance.
## Goals
We have two overarching goals and a third auxiliary goal.
1. Schema migrations should **ALWAYS** be backwards compatible until the last ESR.
2. Schema migrations should **NEVER** lock the entire table.
3. Reduce migration time where possible.
We want to strictly follow this as much as possible (even at the cost of slower feature development).
### Background
Schema changes are always made synchronously when Mattermost starts up. This means the application won't be ready to serve requests until all schema changes are applied. In most cases, the new application won't be able to work until those schema changes are in place.
In a high availability environment, multiple instances will try to run migrations. To prevent this, a lock table is used in the migration system. Until migrations are completed, none of the instances will start. Once the lock is released by a node, another instance will obtain the lock, and check the migrations table. Since the previous node already applied the migrations, the remaining nodes won't re-apply the migrations.
From Mattermost release v6.4, we have started using a schema-based migration system. We are now creating SQL statement files to run migrations. A developer must create migration files for each database driver. Since we want our migrations to be reversible, the developer must create one `up` script along with a `down` script. For instance, a single migration would have the following files:
- `000066_upgrade_posts_v6.0.down.sql`
- `000066_upgrade_posts_v6.0.up.sql`
A file naming convention is used to determine the order in which the migrations should be applied that appends `up|down.sql` suffix to the migration name. We were using a database version before the new migration system which is why the versions exist in the migration file name in the example. Going forward, using version identifiers for future next migration files is not mandatory. A developer can add any information to the name if they think it's going to be helpful.
We are using [morph](https://github.com/go-morph/morph) for the migration engine. The tool has a library and also a CLI. Mattermost server imports the library to have programmatic access to morph functions. A developer can use the morph CLI tool to test whether their migrations are working properly. Please follow instructions in the morph documentation to use the morph CLI tool.
### Analysis
A rough analysis of our past schema migrations shows the following (some very early migrations were skipped which would be considered as base Mattermost):
```
CREATE INDEX - 489
ALTER TABLE - 195
ADD COLUMN - 113
ALTER COLUMN - 51
DROP COLUMN - 25
ADD CONSTRAINT - 6
DROP INDEX - 124
CREATE TABLE - 60
UPDATE - 19
DELETE - 2
```
![Migrations distribution](/contribute/more-info/server/migrations_pie.png)
We will go through each of these migration types and discuss how we can make it non-blocking. This is a lengthy document, so for those wanting to directly look at the executive summary, we present it right now. And then expand on each section in detail later.
### Conclusions
| Operation | Table rewrite | Concurrent DML allowed |
| ----------- | ------------- | ---------------------- |
| CREATE INDEX | NO | YES |
| DROP INDEX | NO | YES |
| ADD COLUMN | NO | YES<sup>1</sup> |
| ALTER COLUMN | YES | NO |
| DROP COLUMN | YES | YES<sup>1</sup> |
| ADD FK CONSTRAINT| NO | YES (only selects)<sup>2</sup> |
| ADD UNIQUE CONSTRAINT | NO | YES |
<Note title="Note">
1. Technically it takes an ACCESS EXCLUSIVE LOCK, however it is only to add/remove the metadata. The command returns instantly.
2. Adding FK constraint takes a SHARE ROW EXCLUSIVE [lock](https://www.postgresql.org/docs/11/sql-altertable.html).
</Note>
### Recommendations
- **Try to avoid FK constraints**.
- **Strongly avoid trying to alter column types**.
However, if you MUST do it, take a look into the following sections.
### Details
1. CREATE INDEX
CREATE INDEX CONCURRENTLY does not take any locks.
2. ALTER TABLE ADD COLUMN
Adding nullable columns happens in constant time from version 10. And from version 11 onwards, adding non-null columns with a default value also happens in constant time.
The catch here is to be able to handle denormalization optimizations which typically adds a new column but needs to backfill that with data before using the column. Take a look at the next section on how to achieve that.
3. ALTER TABLE ALTER COLUMN
This takes an exclusive lock. We strongly recommend you avoid doing this.
To give some context, we have this particular migration `ALTER TABLE posts ALTER COLUMN props TYPE jsonb USING props::jsonb;` which has caused us more pain than it was worth. Several large customers have faced problems with this migration where in some cases, it has been observed to take 8+ hrs. Therefore, we strongly suggest to avoid making any `ALTER COLUMN` changes until absolutely unavoidable (for example, security issues).
However, if you MUST do this, then see the example later.
4. ALTER TABLE DROP COLUMN
Only a metadata lock is taken. No table rewrite takes place. The space is just marked as unused and later taken up by future DB writes.
5. ALTER TABLE ADD CONSTRAINT
Relatively rare, but out of those 6 cases, 2 are adding unique constraints. For example:
```sql
ALTER TABLE oauthaccessdata ADD CONSTRAINT oauthaccessdata_clientid_userid_key UNIQUE (clientid, userid);
```
This can be improved by first adding the index concurrently, and then attaching the index to the constraint. See example later.
Adding a foreign key in PostgreSQL takes a share row exclusive lock, which means only SELECT queries are allowed. It is possible to bypass the table scanning by adding a “NOT VALID” suffix, but then it defeats the purpose of having a foreign key. We recommend against doing it.
6. DROP INDEX
DROP INDEX CONCURRENTLY does not take any locks.
7. CREATE TABLE
Does not lock any existing data so no issues.
8. UPDATE
An analysis shows that UPDATE statements roughly fall into one of these three categories:
- **Data migrations**:
```sql
UPDATE channelmembers SET MentionCountRoot = ..
UPDATE Channels SET TotalMsgCountRoot = ..
UPDATE ChannelMembers CM SET MsgCountRoot ..
```
In these cases, rather than operating on the entire table, we need to operate on batches at a time. See the example later on how to achieve that.
- **Changing NULL columns to NON-NULL**
```sql
UPDATE Channels SET LastRootPostAt=0 WHERE LastRootPostAt IS NULL;
UPDATE OAuthApps SET MattermostAppID = '' WHERE MattermostAppID IS NULL;
```
This is possible to handle from the code itself using a `COALESCE` function. It makes the code more complicated, but its a cost we have to pay to reduce migration overhead.
- **Denormalization optimizations**:
```sql
UPDATE threads SET threaddeleteat = posts.deleteat FROM posts WHERE threads.threaddeleteat IS NULL AND posts.id = threads.postid;
UPDATE reactions SET channelid = COALESCE((select channelid from posts where posts.id = reactions.postid), '') WHERE channelid='';
UPDATE threads SET threadteamid = channels.teamid FROM channels WHERE threads.threadteamid IS NULL AND channels.id = threads.channelid;
UPDATE fileinfo SET channelid = posts.channelid FROM posts WHERE fileinfo.channelid IS NULL AND fileinfo.postid = posts.id;
```
We can take the same approach as in data migrations.
9. DELETE
So far, there have been only a handful of DELETE statements in schema migrations. And mostly they have been for security issues. The general recommendation is to avoid running a full-blown DELETE statement that operates on the entire table, but rather operate on batches so as to avoid taking a lock on the entire table. This could either be done in a job since there is no new code waiting for this to be executed. (See above)
## Examples
### How do I change a column type if I MUST
Follow this long-winded procedure:
- Create a new column.
- Migrate existing data.
- From next ESR, start using the new column.
- Next ESR, drop the old column.
For example, lets say the next upcoming version is 8.4, and the next ESR is 8.6. So step 1 and 2, goes in 8.4. And in 8.7 onwards, we add the code to start using the new column, which will eventually be part of 8.12 (ESR after that). And then from 8.13 onwards, we can drop the column.
The following diagram should explain things better:
![ESR migrations](/contribute/more-info/server/ESR-diagram.png)
The reasoning behind this is some customers will only upgrade from ESR to ESR. So we need to ensure backwards compatibility with the previous version.
Following shows an example where we are adding a channel_count column to the status table. This is not exactly altering a column, but the idea remains the same, and you can extend this to fit your use-case.
1. ALTER TABLE status ADD COLUMN channel_count integer;
2. Our next objective is to migrate existing data. We do this in a 2-phase approach where we set up triggers to migrate all new data and in the background migrate old data in batches.
```sql
CREATE OR REPLACE FUNCTION public.update_status_channel_count()
RETURNS trigger
LANGUAGE plpgsql
AS $function$
DECLARE
member_count integer;
BEGIN
select count(*) into member_count from channelmembers where userid=NEW.userid;
NEW.channel_count := member_count;
RETURN NEW;
END
$function$
CREATE TRIGGER tr_update_status_channel_count
BEFORE INSERT OR UPDATE ON status
FOR EACH ROW EXECUTE PROCEDURE update_status_channel_count();
```
After this is taken care of, we need to create a job, which will migrate existing data in batches.
```sql
UPDATE status s SET channel_count=(SELECT count(*) FROM channelmembers cm WHERE cm.userid=s.userid) WHERE channel_count IS NOT NULL AND s.userid in (SELECT userid FROM status WHERE userid > '' ORDER BY userid ASC limit 10);
```
Then store the user id offset in the job metadata.
```sql
UPDATE status s SET channel_count=(SELECT count(*) FROM channelmembers cm WHERE cm.userid=s.userid) WHERE channel_count IS NOT NULL AND s.userid in (SELECT userid FROM status WHERE userid > <offset> ORDER BY userid ASC limit 10);
```
At this point, when the job finishes, the new column would be ready to use. And the triggers would take care of always keeping the data up to date.
3. Now we can start using the new column from the next ESR version. But we cannot yet drop the existing column because of backwards compatibility guarantees. The old column would still be in use by older app nodes in the cluster during upgrade. We also would want to drop the trigger since its use is finished.
```sql
DROP TRIGGER tr_update_status_channel_count on status
```
4. And in the ESR after that, now we can finally drop the old column.
This deliberately skips renaming the column for simplicity. Depending on your use-case, you can do that if you want to. It is a fast operation that does not rebuild the table, so there are no issues.
### How do I add a unique constraint to a table
```sql
CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS oauthaccessdata_clientid_userid_key on oauthaccessdata(clientid, userid);
ALTER TABLE oauthaccessdata ADD UNIQUE USING INDEX oauthaccessdata_clientid_userid_key; -- This is instantaneous
```
### How do I run UPDATE statements in data migrations/denormalizations
The idea would be to run the UPDATE statements in batches so as to avoid taking a large lock. This is similar to the second part in the column change example.
Following is an example where I update the channels table in batches setting a new column.
```sql
CREATE OR REPLACE FUNCTION public.update_in_batches()
RETURNS INTEGER
LANGUAGE plpgsql
AS $function$
DECLARE
id_offset text := '';
rows_updated integer;
BEGIN
LOOP
WITH table_holder AS (
SELECT id FROM channels
WHERE id > id_offset
ORDER BY id ASC limit 100
)
UPDATE channels c SET new='improved' WHERE c.id in (SELECT id FROM table_holder); -- change this query to whatever your requirement is
GET DIAGNOSTICS rows_updated = ROW_COUNT;
-- We have to run the select query again
-- becaue "select into" isn't allowed inside a CTE
-- and without CTE, we have to use a temp table (because you can't select into a table)
-- and with a temp table, you run into max_locks_inside_transaction limit.
-- Probably there is a better way but keeping things simple for now.
select id into id_offset from (select id from channels where id > id_offset ORDER BY id ASC limit 100) as temp order by id desc limit 1;
EXIT WHEN rows_updated = 0;
END LOOP;
return 1;
END
$function$;
```
## FAQ
### I need to make a schema change. What do I do?
1. Add the appropriate SQL script file containing the statements you want to run into the migrations directory. This directory is located in `{project_dir}/db/migrations/{driver_name}/`.
2. Run `make migrations-extract` to add your new migrations to the `db/migrations/migrations.list` file. This will ensure that there will be merge conflicts in case there is a conflict on migration sequence numbers with the master branch. Since we don't want to have a collision on version numbers of the migration files, the developer should merge the upstream branch to the feature branch just before merging so that we can be sure that there are no versioning issues. In case of a version number collision, the build process will fail and main branch will be broken until it gets fixed.
3. When you run the mattermost/server binary, the tool will automatically apply the migration if it's required. The migration name will be saved in the `db_migrations` table.
4. Lastly, please also measure the time taken for the migration with an eye towards resource usage. Please use the DB dumps from the ~developers-performance channel in our Community server. You will find the links in the channel header.
5. In your PR, make sure to add release notes following the [Developer Schema Migration Template](https://docs.google.com/document/d/18lD7N32oyMtYjFrJKwsNv8yn6Fe5QtF-eMm8nn0O8tk/edit?tab=t.0).
### My migration has failed. What do I do?
1. If you think your migration is applied, and you want to revert changes, you can run the down script to roll back in a clean way. You can use morph CLI to apply down migrations.
- Before rolling down the script, check the `db_migrations` table whether the migration is applied or not.
- If it's applied you can revert it using morph CLI command. An example command would look like `morph apply down --driver {your-driver} --dsn "{your-dsn}" --path {path-to-your-driver-specific-migration-files} --number 1`
2. If the migration has been shipped in a release and you want to apply fixes, instead of changing the existing script, you should add a new one so that `db_migrations` will stay consistent. You can edit the existing migration to be a no-op for future releases in this case.
## GLOSSARY
- **DDL** - short form of Data Definition Language, which deals with database schema changes. For example: `CREATE TABLE`, `ALTER TABLE` etc.
- **DML** - short form of Data Manipulation Language, which deals with SQL queries that read/update/delete tables. For example: `SELECT`, `UPDATE`, `INSERT` etc.
- **FK** - Foreign Key
@@ -0,0 +1,344 @@
---
title: "Golang style guide"
sidebar_position: 3
---
Golang ("go") is a more opinionated language than many others when it comes to coding style. The compiler enforces some basic stylistic elements, such as the removal of unused variables and imports. Many others are enforced by the `gofmt` tool, such as usage of white-space, semicolons, indentation, and alignment. The `gofmt` tool is run over all code in the Mattermost Server CI pipeline. Any code which is not consistent with the formatting enforced by `gofmt` will not be accepted into the repository.
Despite this, there are still many areas of coding style which are not dictated by these tools. Rather than reinventing the wheel, we are adopting [Effective Go](https://golang.org/doc/effective_go.html) as a basis for our style guide. On top of that, we also follow the guidelines laid out by the Go project at [CodeReviewComments](https://go.dev/wiki/CodeReviewComments).
However, at present, some of the guidelines from these sources come into conflict with existing patterns that are present in our codebase which cannot immediately be corrected due to the need to maintain backward compatibility.
This document, which should be read in conjunction with [Effective Go](https://golang.org/doc/effective_go.html) and [CodeReviewComments](https://go.dev/wiki/CodeReviewComments), outlines the small number of exceptions we make to maintain backward compatibility, as well as a number of additional stylistic rules we have adopted on top of those external recommendations.
### Application of guidelines
The following guidelines should be applied to both new and existing code. However, this does not mean that a developer is *required* to fix any surrounding code that contravenes the rules in the style guide. It's encouraged to keep fixing things as you go, but it's not compulsory to do so. Reviewers should refrain from asking for stylistic changes in surrounding code if the submitter has not included them in their pull request.
## Guidelines
### Project layout
When creating a new Go module, please follow the [standardized guidelines](https://go.dev/doc/modules/layout) of the Go team.
[This blog article](https://go.dev/blog/package-names) provides additional guidance on package names.
Following are some of the anti-patterns to keep in mind:
- Don't use the `pkg` pattern. This is a common standard used by many projects. But it goes against the Go philosophy of naming packages that signify what they contain. From https://blog.golang.org/package-names:
> A package's name provides context for its contents, making it easier for clients to understand what the package is for and how to use it.
The `pkg` directory was used long ago in the Go project when there weren't any well-defined standards. But it was later removed to just have normal packages.
- Same for `util` or `misc` packages. Don't use them. Instead break out related `util` functionalities into its own package or if it's not used by a lot of packages, make it part of the original package itself.
- Don't have too many small packages with only one file. It's usually a sign of splitting packages without giving thought into them. Look at the API boundaries and group the packages into bigger chunks.
### Functional
#### Default to sync instead of async
Always prefer synchronous functions by default. Async calls are hard to get right. They have no control over goroutine lifetimes and introduce data races. If you think something needs to be asynchronous, measure it and prove it. Ask these questions:
- Does it improve performance? If so, by how much?
- Whats the tradeoff of the happy path vs. slow path?
- How do I propagate errors?
- What about back-pressure?
- What should be my concurrency model?
Do not create one-off goroutines without knowing when/how they exit. They cause problems that are hard to debug, and can often cause performance degradation rather than an improvement. Have a look at:
- https://go.dev/wiki/CodeReviewComments#goroutine-lifetimes
- https://go.dev/wiki/CodeReviewComments#synchronous-functions
#### Pointers to slices
Do not use pointers to slices. Slices are already reference types which point to an underlying array. If you want a function to modify a slice, then return that slice from the function, rather than passing a pointer.
#### Avoid creating more ToJSON methods
Do not create new `ToJSON` methods for model structs. Instead, just use `json.Marshal` at the call site. This has two major benefits:
- It avoids bugs due to the suppression of the JSON error which happens with `ToJSON` methods (we've had a number of bugs caused by this).
- It's a common pattern to pass the output to something (like a network call) which accepts a byte-slice, leading to a double conversion from byte-slice to string to a byte-slice again if `ToJSON` methods are used.
#### [Interfaces](https://go.dev/wiki/CodeReviewComments#interfaces)
- Return structs, accept interfaces.
- Interface names should end with “-er”. This is not a strict rule. Just a guideline which indicates the fact that interface functionalities are designed around the concept of “doing” something.
- Try not to define interfaces on the implementer side of an API "for mocking"; instead, design the API so that it can be tested using the public API of the real implementation.
As an example, if you're trying to integrate with a third-party service, it's tempting to create an interface and use that in the code so that it can be easily mocked in the test. This is an anti-pattern and masks real bugs. Instead, you should try to use the real implementation via a docker container or if that's not feasible, mock the network response coming from the external process.
Another common pattern is to preemptively declare the interface in the source package itself, so that the consumer can just directly import the interface. Instead, try to declare the interface in the package which is going to consume the functionality. Often, different packages have non-overlapping set of functionalities to consume. If you do find several consumers of the package, remember that interfaces can be composed. So define small chunks of functionalities in different interfaces, and let consumers compose them as needed. Take a look at the set of interfaces in the [io](https://golang.org/pkg/io/) package.
These are just guidelines and not strict rules. Understand your use case and apply them appropriately.
### Stylistic
#### [CamelCase variables/constants](https://go.dev/wiki/CodeReviewComments#mixed-caps)
We use CamelCase names like WebsocketEventPostEdited, not WEBSOCKET_EVENT_POST_EDITED.
#### Empty string check
Use `foo == ""` to check for empty strings, not `len(foo) == 0`.
#### [Reduce indentation](https://go.dev/wiki/CodeReviewComments#indent-error-flow)
If there are multiple return statements in an if-else statement, remove the else block and outdent it.
This is an example from `mlog/human/parser.go`:
```go
// Look for an initial "{"
if token, err := dec.Token(); err != nil {
return result, err
} else {
d, ok := token.(json.Delim)
if !ok || d != '{' {
return result, fmt.Errorf("input is not a JSON object, found: %v", token)
}
}
```
This can be simplified to:
```go
// Look for an initial "{"
if token, err := dec.Token(); err != nil {
return result, err
}
d, ok := token.(json.Delim)
if !ok || d != '{' {
return result, fmt.Errorf("input is not a JSON object, found: %v", token)
}
```
#### [Initialisms](https://go.dev/wiki/CodeReviewComments#initialisms)
Use `userID` rather than `userId`. Same for abbreviations; `HTTP` is preferred over `Http` or `http`.
#### [Receiver names](https://go.dev/wiki/CodeReviewComments#receiver-names)
The name of a method's receiver should be a reflection of its identity; often a one or two letter abbreviation of its type suffices (such as "c" or "cl" for "Client"). Don't use generic names such as "me", "this", or "self" identifiers typical of object-oriented languages that give the variable a special meaning.
#### Error variable names
The name of any `error` variable must be `err` or prefixed with `err`. The name of any `*model.AppError` variable must be `appErr` or prefixed with `appErr`. This allows us to avoid confusion about how to handle different kind of errors inside Mattermost. If you are storing the error value from a function that returns an `error` type in its signature, it's considered an `error`, regardless of whether the function, internally, is returning a `*model.AppError` instance.
For example, when the function signature returns an `error`, we use the `err` variable name:
```go
func MyFunction() error {
return model.NewAppError(...)
}
func OtherFunction() {
...
err := MyFunction()
...
}
```
When the function signature returns an `*model.AppError`, we use the `appErr` variable name:
```go
func MyFunction() *model.AppError {
return model.NewAppError(...)
}
func OtherFunction() {
...
appErr := MyFunction()
...
}
```
### Errors
Always add proper context to errors.
#### Include the user input for validation errors.
For example:
`return fmt.Errorf("invalid export type, must be one of: csv, actiance, globalrelay")` does not include what was the input value entered by the user. A better way might be:
```diff
- return fmt.Errorf("invalid export type, must be one of: csv, actiance, globalrelay")
+ return fmt.Errorf("invalid export type: %s, must be one of: csv, actiance, globalrelay", exportType)
```
Another example:
```diff
if r.Start > r.End {
- return fmt.Errorf("report timestamps are erroneous")
+ return fmt.Errorf("report timestamps are erroneous: start_timestamp %f is greater than end_timestamp %f", r.Start, r.End)
}
```
#### Return errors instead of boolean for ID validation errors
A validation function can check various properties of an object. But if we simply return true or false, and then log that object is invalid, the user will have no idea why it is invalid or how to fix it.
For example:
```diff
- func IsValidId(value string) bool {
+ func IsValidId(value string) error {
if len(value) != 26 {
- return false
+ return fmt.Errorf("Invalid length. Found: %d; expected: %d", len(value), 26)
}
for _, r := range value {
if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
- return false
+ return fmt.Errorf("Rune %c in %s is not an unicode letter or number", r, value)
}
}
- return true
+ return nil
}
```
### Logging
Log messages should be annotated with contextual information in the form of key-value pairs to make it easier to identify the context they originated from. The keys should use snake_case. Refer to the corresponding JSON struct tags for key names.
```go
func (a *App) SendNotifications(...) {
..
_, err := a.sendOutOfChannelMentions(c, sender, post, channel, ...)
if err != nil {
c.Logger().Error(
"Failed to send warning for out of channel mentions",
mlog.String("user_id", sender.Id),
mlog.String("post_id", post.Id),
mlog.Err(err),
)
}
..
}
```
#### Avoid double-logging
Double-logging is when you immediately log something after an error, and also return an error at the same time. This creates two log lines with the same error and is confusing to the admin. The best practice is to always pass the error upwards adding context and log it in the upper-most layer correct. Logging the error at the lowest layer does not give any additional context as to from where it was called, and what path did the code take to reach there.
For example:
```diff
if err != nil {
- mlog.Error("Failed to generate SQL query",
- mlog.String("user_id", userID),
- mlog.Int("timestamp", int(syncTime)),
- mlog.Err(err),
- )
- return errors.Wrap(err, "failed to generate SQL query")
+ return errors.Wrapf(err, "failed to generate SQL query: user_id: %s, timestamp: %d", userID, int(syncTime))
}
```
#### Log levels
The purpose of logging is to provide observability - it enables the application communicate back to the administrator about what is happening. To communicate effectively logs should be meaningful and concise. To achieve this, log lines should conform to one of the definitions below:
**Critical:** This log-level represents the most severe situations when the service is entirely unable to continue operating. After emitting a _critical_ log line, it is expected that the service will terminate.
For example, the code block below demonstrates a _critical_ situation where the server startup routine fails, meaning the service is unable to start and must terminate.
```go
func runServer(..) {
..
server, err := app.NewServer(options...)
if err != nil {
mlog.Critical(err.Error())
return
}
..
}
```
**Error:** This log-level is used when something unexpected has happened to the service, but it does not result in a total loss of service. Log lines using the _error_ level must be actionable, so that the system administrator can investigate and resolve the incident. The _error_ log level may indicate a loss of service for an individual user or request or it may indicate a total failure of a non-critical subsystem within the service.
For example, the _error_ log level is used in the code snippet below as it represents a partial failure of one non-critical subsystem of the service. Administrator intervention is required to resolve this situation, but the rest of the service is able to continue operating in the meantime.
```go
func (a *App) SyncPlugins(..) {
..
reader, appErr := a.FileReader(plugin.path)
if appErr != nil {
mlog.Error("Failed to open plugin bundle from file store.", mlog.String("bundle", plugin.path), mlog.Err(appErr))
return
}
..
}
```
**Warn:** This log level is used to indicate that something unexpected has happened, but the server is able to continue operating and it has not suffered any loss of functionality as a consequence of this failure. System administrators may wish to investigate the cause of log lines at this level, but the need is typically less pressing than for those at _error_ level. System administrators may also wish to monitor the rate of occurrence of individual log-lines at this level as this may be indicative of a wider problem. Log lines at the _warning_ level should be as detailed as possible, since these are often the least clear-cut category of message.
For example, the _warning_ log level may be used to indicate that something went wrong but the overall operation was still able to complete successfully.
```go
func (a *App) UpdateUserRoles(..) {
..
if result := <-schan; result.NErr != nil {
// soft error since the user roles were still updated
mlog.Warn("Error during updating user roles", mlog.Err(result.NErr))
}
a.InvalidateCacheForUser(userId)
..
}
```
**Info:** This log level should be used to record normal, expected application behavior, even if it results in an error for the end user. They are not actionable individually, but the significant changes in the frequency of occurrence of individual log lines at this level may be indicative of a possible problem.
For example, the _info_ log level may be used to communicate to administrators that certain subsystems within the service have been started or stopped.
```go
func (s *Schedulers) Start(..) {
s.startOnce.Do(func() {
mlog.Info("Starting schedulers.")
..
})
..
}
```
**Debug:** This log-level is used for diagnostic information which may be used to debug issues but is not necessary for normal production system logging, nor actionable by system administrators.
```go
func (worker *Worker) Run() {
mlog.Debug("Worker started", mlog.String("worker", worker.name))
..
```
### Performance sensitive areas
Any PR that can potentially have a performance impact on the `mattermost/server` codebase is encouraged to have a performance review. For more information, please see this [link](https://docs.google.com/document/d/1Uzt3XHyKhDKipkuCmESkHoPio7vz7VYS4N_5_9ffgNU/edit). The following is a brief list of indicators that should to undergo a performance review:
- New features that might require benchmarks and/or are missing load-test coverage.
- PRs touching performance of critical parts of the codebase (e.g. `Hub`/`WebConn`).
- PRs adding or updating SQL queries.
- Creating goroutines.
- Doing potentially expensive allocations: `bytes.Buffer` and `[]byte`:
- Use of `Buffer.Grow`, `Buffer.ReadFrom`, `ioutil.ReadAll`.
- Creating big slices and maps without capacity when size is known in advance.
- Recursion, unbounded, and deeply nested `for` loops.
- Use of locks and/or other synchronization primitives.
- Regular expressions, especially when creating `regexp.MustCompile` dynamically every time.
- Use of the `reflect` package.
## Propose a new rule
To propose a new rule, follow the process below:
- Add it to the agenda in the [Server](https://community.mattermost.com/core/channels/developers-server) Guild meeting, and propose it.
- If it gets accepted, create a go-vet rule (if possible), or a golangci-lint rule to prevent new regressions from creeping in.
- Fix all existing issues.
- Add it to this guide.
@@ -0,0 +1,21 @@
---
title: "System Console"
sidebar_position: 5
---
## Add fields to the configuration
In order to add fields to the configuration, you need to modify `model/config.go` in the server by adding the desired field to one of the structs such as `ServiceSettings` and setting its default value in the corresponding `SetDefaults` method.
Note that some of the configuration values are collected as telemetries. The telemetry definitions are defined in the `services/telemetry` package. Once a configuration is added, it should be added to the telemetry package. If the configuration value is not going to be collected as a telemetry, a `// telemetry: none` comment must be added to prevent the [configtelemetry](https://github.com/mattermost/mattermost-govet#included-analyzers) check from failing.
Also we use struct tags to identify access level for configuration values. If the value requires a restriction, please use this tag accordingly.
### Expose settings in the System Console
To expose the newly-added field in the System Console, you need to add that same setting to the `AdminDefinition` JS object in `webapp/channels/src/components/admin_console/admin_definition.jsx`. This object defines most of the settings in the System Console.
### Make settings available for non-admin users
To make the newly added setting accessible to non-admin users in the apps, you'll need to add it to the `GenerateClientConfig` method in `config/client.go` in the server. Note that this always encodes the setting as a string, so anywhere that you would want to use this value in the client, you have to look for a string.
@@ -0,0 +1,122 @@
---
title: "Tests"
sidebar_position: 2
---
## Handling Flaky Tests
A flaky test is one that exhibits both passing and failing results when run multiple times without any code changes. When our automation detects a flaky test on your PR:
1. **Check if the Test is Newly Introduced**
- Review your PR changes to determine if the flaky test was introduced by your changes
- If the test is new, fix the flakiness in your PR before merging
2. **For Existing Flaky Tests**
- Create a JIRA ticket titled "Flaky Test: \{TestName\}", e.g. "Flaky Test: TestGetMattermostLog"
- Copy the test failure message into the JIRA ticket description
- Add the `flaky-test` and `triage-global` labels
- Create a PR to skip the test by adding:
```go
t.Skip("https://mattermost.atlassian.net/browse/MM-XXXXX")
```
where MM-XXXXX is your JIRA ticket number
- Link the JIRA ticket in the skip message for tracking
This process helps us track and systematically address flaky tests while preventing them from blocking development work.
## Writing Parallel Tests
Leveraging parallel tests can drastically reduce execution time for entire test packages, such as [`api4`](https://github.com/mattermost/mattermost/tree/master/server/channels/api4) and [`app`](https://github.com/mattermost/mattermost/tree/master/server/channels/app), which are notably heavy with hundreds of tests. However, careful implementation is essential to ensure reliability and prevent flakiness. Follow these guidelines when writing parallel tests:
### Enabling Parallel Tests
In [`api4`](https://github.com/mattermost/mattermost/tree/master/server/channels/api4), [`app`](https://github.com/mattermost/mattermost/tree/master/server/channels/app), [`platform`](https://github.com/mattermost/mattermost/tree/master/server/channels/app/platform), [`email`](https://github.com/mattermost/mattermost/tree/master/server/channels/app/email), [`jobs`](https://github.com/mattermost/mattermost/tree/master/server/channels/jobs) packages:
```go
func TestExample(t *testing.T) {
mainHelper.Parallel(t)
...
}
// OR
func TestExample(t *testing.T) {
th := Setup(t)
th.Parallel(t)
...
}
// OR
func TestExample(t *testing.T) {
if mainHelper.Options.RunParallel {
t.Parallel()
}
...
}
```
If [`sqlstore`](https://github.com/mattermost/mattermost/tree/master/server/channels/store/sqlstore) package:
```go
func TestExample(t *testing.T) {
if enableFullyParallelTests {
t.Parallel()
}
...
}
```
To enable parallel execution, you should set the `ENABLE_FULLY_PARALLEL_TESTS` environment variable. Example:
```bash
ENABLE_FULLY_PARALLEL_TESTS=true go test -v ./api4/...
```
### When to Use Parallel Tests
- **Generally Safe**: Tests with dedicated setup functions that ensure independence from other tests.
- **Subtests**: Only safe if each subtest features its own setup function, ensuring they are decoupled and independent of execution order.
- **Unsafe**: When a subtest depends on state changes made by another subtest, thus coupling their execution order.
### Common Issues That Break Parallel Safety
#### Global State
Avoid reliance on global variables and registrations such as:
- `LicenseValidator`
- `platform.RegisterMetricsInterface`
- `platform.PurgeLinkCache`
- `model.BuildEnterpriseReady`
- `jobs.DefaultWatcherPollingInterval`
#### Filesystem Operations
Avoid using `os.Chdir` (or `t.Chdir`) and relative paths tied to the test executable, as they may introduce inconsistencies when tests run in parallel. When possible, rely on temporary directories such as `th.tempWorkspace` which are dedicated to the test.
#### Environment Variables
Using `os.Setenv` for feature flags and other settings can cause interference between parallel tests. Instead, use the configuration API:
```go
// UNSAFE for parallel tests:
os.Setenv("MM_FEATUREFLAGS_CUSTOMFEATURE", "true")
defer os.Unsetenv("MM_FEATUREFLAGS_CUSTOMFEATURE")
// SAFE for parallel tests:
th.App.UpdateConfig(func(cfg *model.Config) {
cfg.FeatureFlags.CustomFeature = true
})
```
#### Process-Level Methods
Be cautious with methods affecting the entire process, such as `pprof.StartCPUProfile`, which can introduce contention between tests.
@@ -0,0 +1,23 @@
---
title: "Tools"
sidebar_position: 10
---
## Mattermost Server
In the [mattermost repository](https://github.com/mattermost/mattermost), we are using [Docker](https://www.docker.com/) images and [Docker Compose](https://docs.docker.com/compose/) to set up the development enviroment. The following are required images:
- [MySQL](https://www.mysql.com/)
- [PostgreSQL](https://www.postgresql.org/)
- [MinIO](https://min.io/)
- [Inbucket](https://www.inbucket.org/)
- [OpenLDAP](https://www.openldap.org/)
- [Elasticsearch](https://www.elastic.co)
We also have added optional tools to help with your development:
### Dejavu
[Dejavu](https://opensource.appbase.io/dejavu/) is a user interface for Elasticsearch when no UI is provided to visualize or modify the data you're storing inside Elasticsearch.
To use Dejavu, execute `docker-compose up -d dejavu`. It will run at `http://localhost:1358`.
@@ -0,0 +1,163 @@
---
title: "Build a component"
sidebar_position: 4
---
This page describes how to build a new React component in the Mattermost web app. A new component must meet the following requirements:
1. Is pure, meaning that all information required to render is passed in by props.
2. Has no direct store interaction. Use `connect` to wrap the component if needed.
3. Has component tests.
4. Is generic and re-usable when possible.
5. Has documented props.
If none of those make any sense to you or you're new to React and Redux, then check out these links:
- https://react.dev/learn/
- http://redux.js.org/
These requirements are discussed in more detail in the following sections.
## Design the component
The most important part of designing your component is deciding on what the props will be. Props are very much the API for your component. Think of them as a contract between your component and the users.
Props are read-only variables that get passed down to your component either directly from a parent component or from an `index.ts` container that connects the component to the Redux store.
How do you decide what props your component should have? Think about what your component is trying to display to the user. Any data you need to accomplish that should be part of the props.
As an example, let's imagine we're building an `ItemList` component with the purpose of displaying a list of items. The props for such a component might look like:
```typescript
type Props = {
/**
* The title of the list
*/
title?: string;
/**
* An array of items to display
*/
items: ListItem[];
}
```
The `title` prop is a string that's displayed as the title of the list, and `items` is an array of objects that make up the contents of the list. Note that `items` is required while `title` is optional.
Make sure you add brief but clear comments to each prop type as shown in the example.
Our ItemList component would live in a file named `item_list.tsx`.
## Use a Redux container component
The next question to ask yourself is whether you're going to need a container component. This is the `index.ts` file mentioned above. If your component needs either of the following, then you'll need a container:
1. Needs some data injected into its props that the parent component doesn't have access to
2. Needs to be able to perform some sort of action that affects the state of the store
Continuing the `ItemList` example above, maybe our parent component doesn't care about our list of items and doesn't have access to them. Let's also imagine that we want to let the user remove items from the list by clicking on them. This means our component now needs a container for both criteria above and our props will change slightly:
```typescript
type Props = {
/**
* The title of the list
*/
title?: string;
/**
* An array of item components to display
*/
items: ListItem[];
actions: {
/**
* An action to remove an item from the list
*/
removeItem: (item: ListItem) => void;
};
}
```
Note that the type definition for actions passed from Redux won't include the any reference to Redux's `dispatch` or Redux Thunk's `getState`. This is intentional as `connect` hides those details from the component.
The container will then handle getting data from the Redux state in `mapStateToProps` and passing Redux actions to the component using `mapDispatchToProps`. Note that either of these are optional if only one is needed.
```typescript
import {connect} from 'react-redux';
import {bindActionCreators, Dispatch} from 'redux';
import {removeItem} from 'mattermost-redux/actions/items';
import {getItems} from 'mattermost-redux/selectors/entities/items';
import {GlobalState} from 'types/store';
import ItemList from './item_list';
function mapStateToProps(state: GlobalState) {
return {
items: getItems(state),
};
}
function mapDispatchToProps(dispatch: Dispatch) {
return {
actions: bindActionCreators({
removeItem,
}, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ItemList);
```
If the selectors and/or actions you need don't yet exist in Redux then you should go add those first by following the [guide to adding actions and selectors](/developers/contribute/more-info/webapp/redux/actions).
Your `index.ts` and `item_list.ts` files will live together in an `item_list/` directory.
## Implement the component
With the props defined and, if necessary, the container built, you're ready to implement the rest of your component. For the most part, implementing a component for the web app is no different than building any other React component. While older code tends to use class components which extend `React.PureComponent`, most newer code should use functional components.
Our `ItemList` example might look something like this:
```tsx
type Props = {
/**
* The title of the list
*/
title?: string;
/**
* An array of item components to display
*/
items: ListItem[];
actions: {
/**
* An action to remove an item from the list
*/
removeItem: (item: ListItem) => void;
};
}
export default function ItemList(props: Props) {
const title = this.props.title ? <h1>{this.props.title}</h1> : null;
const items = this.props.items.map((item: ListItem) => (
<Item
key={item.id}
item={item}
removeItem={this.props.actions.removeItem}
/>
));
return (
<div className='item-list'>
{title}
{items}
</div>
);
}
```
---
To test your component, [follow the guide here](/developers/contribute/more-info/webapp/unit-testing).
@@ -0,0 +1,40 @@
---
title: "Web app workflow"
sidebar_position: 3
---
This page contains most of the information required for a developer to work with the Mattermost web app. Note that everything in this document will refer to working in the `webapp` directory of [the main Mattermost repository](https://github.com/mattermost/mattermost) unless otherwise stated.
### Workflow
1. If you haven't done so already, [set up your developer environment](/developers/contribute/developer-setup).
2. On your fork, create a feature branch for your changes. Name it `MM-$NUMBER_$DESCRIPTION` where `$NUMBER` is the [Jira](https://mattermost.atlassian.net) ticket number you are working on and `$DESCRIPTION` is a short description of your changes. Example branch names are `MM-18150_plugin-panic-log` and `MM-22037_uppercase-email`. You can also use the name `GH-$NUMBER_$DESCRIPTION` for tickets come from [GitHub Issues](https://github.com/mattermost/mattermost/issues).
3. Make the code changes required to complete your ticket, making sure to write or modify unit tests where appropriate. Use `make test` to run the unit tests.
4. To run your changes locally, you'll need to run both the client and server. The server and client can either be run together or separately as follows:
* You can run both together by using `make run` from the server directory. Both server and web app will be run together and can be stopped by using `make stop`. If you run into problems getting the server running this way, you may want to consider running them separately in case the output from one is hiding errors from the other.
* You can run the server independently by running `make run-server` from its directory and, using another terminal, you can run the web app by running `make run` from the web app directory. Each can be stopped by running `make stop-server` or `make stop` from their respective directories.
Once you've done either of those, your server will be available at `http://localhost:8065` by default. Changes to the web app will be built automatically, but changes to the server will only be applied if you restart the server by running `make restart-server` from the server directory.
5. If you added or changed any translatable text, you will need to update the English translation files to make them available to translators for other languages. You can do that by navigating to `channels` and running `make i18n-extract` to update `src/i18n/en.json`.
* Remember to double check that any newly added strings have the correct values in case they weren't detected correctly.
* Generally, only `en.json` should be modified directly from this repository. Other languages' translation files are updated using [Weblate](https://translate.mattermost.com).
6. Before submitting a PR, make sure to check your coding style and run the automated tests on your changes. These are checked automatically by CI, but they should be run manually before submitting changes to ensure the review process goes smoothly.
* To check the code style and run the linter, run `make check-style`. If any problems are encountered, they may be able to be automatically fixed by using `make fix-style`.
* To run the type checker, use `make check-types`.
* To run the unit tests, run `make test`.
7. Commit your changes, push your branch and [create a pull request](https://developers.mattermost.com/blog/submitting-great-prs/).
8. Respond to feedback on your pull request and make changes as necessary by committing to your branch and pushing it. Your branch should be kept roughly up to date by [merging](https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging#_basic_merging) master into it periodically. This can either be done using [`git merge`](https://git-scm.com/docs/git-merge) or, as long as there are no conflicts, by commenting `/update-branch` on the PR.
9. That's it! Rejoice that you've helped make Mattermost better.
### Useful Mattermost commands
During development you may want to reset the database and generate random data for testing your changes. See [the corresponding section of the server developer workflow](/developers/contribute/more-info/server/developer-workflow#useful-mattermost-commands) for how to do that.
@@ -0,0 +1,161 @@
---
title: "End-to-End (E2E) cheatsheets"
sidebar_position: 7
---
This page compiles all Cypress custom commands based on specific sections of the web app, as well as other general examples. The examples provided showcase the best and conventions on how to write great automated test scripts.
We encourage everyone to read this information, ask questions if something is not clear, and challenge the mentioned practices so that we can continuously refine and improve.
If you need to add more custom commands, add them to `/e2e-tests/cypress/tests/support`, and check out the [Cypress custom commands](https://docs.cypress.io/api/cypress-api/custom-commands.html) documentation. For ease of use, in-code documentation functionality, and making custom commands more discoverable, add type definitions. See this example of a [declaration file](https://github.com/mattermost/mattermost/blob/master/e2e-tests/cypress/tests/support/api/user.d.ts) for reference on how to include and make type definitions.
_____
### General Queries with the Testing Library
The [Testing Library](https://testing-library.com/) is used through the package `@testing-library/cypress`, and it provides simple and complete custom Cypress commands and utilities that encourage such good testing practices. To decide on the queries from the Testing Library you should be using while writing Cypress tests, check out this [article](https://testing-library.com/docs/guide-which-query/) to learn more. For instance, you can select something with test ID using: `cy.findByTestId`.
If you need more help, check out the [online Testing Playground](https://testing-playground.com/)—no install required, always up-to-date, and it teaches you the exact Testing-Library queries you should be using as you click through the DOM. And if youd rather stay in a VSCode editor, check out the [VS Code Testing Playground](https://marketplace.visualstudio.com/items?itemName=aganglada.vscode-testing-playground) extension, which brings the same interactive query suggestions and best-practice guidance right into your IDE.
The following is a short summary of the recommended order of priority for queries:
#### :white_check_mark: Queries Accessible to Everyone
These reflect the experience of visual/mouse users as well as those that use assistive technology. Examples include: `cy.findByRole`, `cy.findByLabelText`, `cy.findByPlaceholderText`, `cy.findByText`, and `cy.findByDisplayValue`.
#### :white_check_mark: Semantic Queries
These use HTML5 and ARIAcompliant selectors. Note that the user experience of interacting with these attributes varies greatly across browsers and assistive technology. Some examples include: `cy.findByAltText` and `cy.findByTitle`.
#### :warning: Base Queries
These are considered part of implementation details and are discouraged to be used. You will still find base queries in the codebase but they will be replaced soon. Therefore, please refrain from reusing the existing base query patterns. However, you may want to use them only to limit the scope of selection. Examples include: `cy.get('#elementId')` and `cy.get('.class-name')`. Below is an acceptable use case of base queries:
```javascript
// limit the scope but chained with recommended query
cy.get('#elementId').should('be.visible').findByRole('button', {name: 'Save'}).click();
// limit the scope then use the recommended queries within the scope
cy.get('.class-name').should('be.visible').within(() => {
cy.findByRole('input', {name: 'Position'}).type('Software Developer');
cy.findByRole('button', {name: 'Save'}).click();
});
```
#### :white_check_mark: Query Variants
Note that `cy.findBy*` are shown but other variants are `cy.findAllBy*`, `cy.queryBy*`, and `cy.queryAllBy*`. See the [Queries](https://testing-library.com/docs/dom-testing-library/api-queries) section from `testing-library`.
#### :x: Off-limits Queries
Please do not use any `Xpath` selectors such as the descendant selector. Do not use the `ul > li` and order selectors either, like `ul > li:nth-child(2)`. If an element can only be queried with this approach, then you may modify the application codebase, improve it, and make it "accessible to everyone".
_____
### Settings Modal
![settings modal image](../../../../img/e2e/settings-modal.png)
#### Opening the settings modal
The function `cy.uiOpenSettingsModal(section)` opens the settings modal when viewing a channel. `section` is of the
< <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type">string</a> > type. Possible values for `section` are: `'Notifications'`, `'Display'`, `'Sidebar'`, and `'Advanced'`.
* **Open 'Settings' modal and view the default 'General Settings'**: `cy.uiOpenSettingsModal();`
* **Open the Settings modal and view a specific section (like the 'Advanced' section)**: `cy.uiOpenSettingsModal('Advanced');`
* **Open the Settings modal, view a specific section, and change a setting**:
```javascript
// # Open 'Advanced' section of 'Settings' modal
cy.uiOpenSettingsModal('Advanced').within(() => {
// # Open 'Enable Join/Leave Messages' and turn it off
cy.findByRole('heading', {name: 'Enable Join/Leave Messages'}).click();
cy.findByRole('radio', {name: 'Off'}).click();
// # Save and close the modal
cy.uiSave();
cy.uiClose();
});
```
#### Selecting a section's button within a modal
Use the function `cy.findByRoleExtended('button', {name})`. `name` is of the < <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type">string</a> > type. Possible values for `name` are: `'Notifications'`, `'Display'`, `'Sidebar'`, and `'Advanced'`.
* **Clicking a button within the Settings modal**:
```javascript
// # Open 'Advanced' section of 'Settings' modal
cy.uiOpenSettingsModal().within(() => {
// # Click 'Notifications' button
cy.findByRoleExtended('button', {name: 'Notifications'}).should('be.visible').click();
});
```
#### Select a section's setting within a modal via the name of the section
Use the function `cy.findByRole('heading', {name})`. `name` is of the < <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type">string</a> > type. Possible values for `name` are: `'Full Name'`, `'Username'`, and others depending on the sections in the modal.
* **Open a section within the Settings modal**:
```javascript
// # Open 'Notifications' of 'Settings' modal
cy.uiOpenSettingsModal('Notifications').within(() => {
// # Open 'Words That Trigger Mentions' setting
cy.findByRole('heading', {name: 'Words That Trigger Mentions'}).should('be.visible').click();
});
```
#### Select a section's setting within a modal via role
Use the function `cy.findByRole(role, {name})`. `role` is of the < <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type">string</a> > type. Possible values for `role` are: `'textbox'`, `'radio'`, `'checkbox'` and other <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles">roles</a>. `name` is of the < <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type">string</a> > type. Possible values for `name` are: `'On'`, `'Off'`, and others depending on a section's settings.
* **Change value of a section's setting in the Settings modal**:
```javascript
// # Open 'Notifications' of 'Settings' modal
cy.uiOpenSettingsModal('Notifications').within(() => {
// # Open 'Words That Trigger Mentions' setting
cy.findByRole('heading', {name: 'Words That Trigger Mentions'}).should('be.visible').click();
// # Check channel-wide mentions
cy.findByRole('checkbox', {name: 'Channel-wide mentions "@channel", "@all", "@here"'}).click();
});
```
#### Saving and closing a modal
`cy.uiSave` and `cy.uiClose` are common functions that can be used to save things and close modals.
* **Saving and closing in the Settings modal**:
```javascript
// # Open 'Notifications' of 'Settings' modal
cy.uiOpenSettingsModal('Notifications').within(() => {
// # Open 'Words That Trigger Mentions' setting
cy.findByRole('heading', {name: 'Words That Trigger Mentions'}).should('be.visible').click();
// # Check channel-wide mentions
cy.findByRole('checkbox', {name: 'Channel-wide mentions "@channel", "@all", "@here"'}).click();
// # Save then close the modal
cy.uiSave();
cy.uiClose();
});
```
_____
### Channel Menu
![channel menu image](../../../../img/e2e/channel-menu.png)
#### Opening the channel menu
Use the function `cy.uiOpenChannelMenu(item)`. This will open the channel menu by clicking the channel header title or dropdown icon when viewing a channel. `item` is of the type < <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type">string</a> >. Possible values for `item` are: `'View Info'`, `'Move to...'`,`'Notification Preferences'`, `'Mute Channel'`, `'Add Members'`, `'Manage Members'`,`'Edit Channel Header'`, `'Edit Channel Purpose'`, `'Rename Channel'`, and `'Convert to Private Channel'`, `'Archive Channel'`, and `'Leave Channel'`.
* **Open the channel menu normally**:
```javascript
// # Open 'Channel Menu'
cy.uiOpenChannelMenu();
```
* **Open the channel menu and click on a specific item**:
```javascript
// # Open 'Advanced' section of 'Settings' modal
cy.uiOpenChannelMenu('View Info');
```
#### Closing the channel menu
Use the function `cy.uiCloseChannelMenu()`. This will close the channel menu by clicking the channel header title or dropdown icon again at the center channel view, given that the menu is already open.
#### Get the DOM elements of the channel menu
Use the function `cy.uiGetChannelMenu()`.
_____
### Product Menu
![product menu image](../../../../img/e2e/product-menu.png)
#### Opening the product menu
Use the function `cy.uiOpenProductMenu(item)`. `item` is of the type < <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type">string</a> >. Possible values for `item` are: `'Channels'`, `'Boards'`, `'Playbooks'`, `'System Console'`, `'Integrations'`, `'Marketplace'`, `'Download Apps'`, and `'About Mattermost'`.
* **Open the product menu normally**:
```javascript
// # Open 'Product menu'
cy.uiOpenProductMenu();
```
* **Open the product menu and click on a specific item**:
```javascript
// # Open 'Integrations' section of 'Product Menu' modal
cy.uiOpenProductMenu('Integrations');
```
#### Get the DOM elements of the product menu
Use the function `cy.uiGetProductMenu()`.
_____
@@ -0,0 +1,300 @@
---
title: "End-to-End (E2E) tests"
sidebar_position: 6
---
End-to-end tests for the Mattermost web app in general use [Cypress](https://www.cypress.io/) and [Playwright](https://playwright.dev/). If you're not familiar with Cypress, check out the Cypress [Developer Guide](https://docs.cypress.io/guides/overview/why-cypress.html#In-a-nutshell) and [API Reference](https://docs.cypress.io/api/api/table-of-contents.html). Feel free to also join us on the Mattermost Community server if you'd like to ask questions and collaborate with us!
<Note title="NOTE">
Playwright is a new framework getting added to the Mattermost web app for test automation (and is currently being used for visual tests). Documentation about Playwright in the web app is in development, so all other content about E2E testing will be related to Cypress.
If you're looking for information related to E2E tests and Redux, please check out [Redux Unit and E2E Testing](/developers/contribute/more-info/webapp/redux/testing).
</Note>
### What requires an E2E test?
* Test cases that are defined in [help-wanted E2E issues](https://github.com/mattermost/mattermost/issues?q=label%3A%22Area%2FE2E+Tests%22+label%3A%22Help+Wanted%22+is%3Aopen+is%3Aissue+).
* New features and stories - For example, check out [MM-19922 Add E2E tests for Mark as Unread #4243](https://github.com/mattermost/mattermost-webapp/pull/4243) which contains E2E tests for the `Mark As Unread` feature.
* Bug fixes - For example, see [MM-26751: Fix highlighting of at-mentions of self #5908](https://github.com/mattermost/mattermost-webapp/pull/5908), which fixes a highlighting issue and adds a related test.
* Test cases from [Zephyr](https://support.smartbear.com/zephyr-scale-cloud/docs/) - For example, see [Added Cypress tests MM-T1410, MM-T1415 and MM-T1419 #5850](https://github.com/mattermost/mattermost-webapp/pull/5850) which adds automated tests for `Guest Accounts`.
### File Structure for E2E Testing
E2E tests are located at the root of the repository in [the `e2e-tests` folder](https://github.com/mattermost/mattermost/tree/master/e2e-tests). The file structure is mostly based on the [Cypress scaffold](https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests#Folder-Structure). Here is an overview of some important folders and files:
```
|-- e2e-tests
|-- cypress
|-- tests
|-- fixtures
|-- integration
|-- plugins
|-- support
|-- utils
|-- cypress.config.ts
|-- package.json
```
* `/e2e-tests/cypress/tests/fixtures` or [Fixture Files](https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Fixture-Files):
- Fixtures are used as external pieces of static data that can be used by tests.
- Typically used with the `cy.fixture()` command and most often when stubbing network requests.
* `/e2e-tests/cypress/tests/integration` or [Test Files](https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Test-files):
- Subfolder naming convention depends on test grouping, which is usually based on the general functional area (e.g. `/e2e/cypress/tests/integration/messaging/` for "Messaging").
* `/e2e-tests/cypress/tests/plugins` or [Plugin Files](https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Plugins-file):
- A convenience mechanism that automatically includes plugins before running every single `spec` file.
* `/e2e-tests/cypress/tests/support` or [Support Files](https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Support-file):
- A support file is a place for reusable behaviour such as custom commands or global overrides that are available and can be applied to all `spec` files.
* `/e2e-tests/cypress/tests/utils`: this folder contains common utility functions.
* `/e2e-tests/cypress/cypress.config.ts`: this file is for Cypress [configuration](https://docs.cypress.io/guides/references/configuration.html#Options).
* `/e2e-tests/cypress/package.json`: this file is for all the dependencies related to Cypress end-to-end testing.
### Writing End-to-End Tests
#### Where should a new test go?
You will need to either add the new test to an existing `spec` file, or create a new file. Sometimes, you will be informed (for example through issue descriptions) of the specific folder the test file should go in, or the actual test file being amended. As aforementioned, the `e2e-tests/cypress/tests/integration` folder is where all of the tests live, with subdirectories that roughly divide the tests by functional areas. Cypress is configured to look for and run tests that match the pattern of `*_spec.ts`, so a good new test file name for an issue like [Write Web App E2E with Cypress: "MM-T642 Attachment does not collapse" #18184](https://github.com/mattermost/mattermost/issues/18184) would be `attachment_does_not_collapse_spec.ts`, to ensure that it gets picked up.
> *Note*: There may be some JavaScript `spec` files, but new tests should be written in TypeScript. If you are adding a test to an existing `spec` file, convert that file to TypeScript if necessary.
If you don't know where a test should go, first check the names of the subdirectories, and select a folder that describes the functional area of the test best. From there, look to see if there is already a `spec` file that may be similar to what you are testing; if there is one, it would be possible to add the test to the pre-existing file.
#### Test metadata on spec files
Test metadata is used to identify each `spec` file before it is forwarded for a Cypress run, and the metadata is located at the start of a `spec` file. Currently, supported test metadata fields include the following:
* **Stage** - Indicates the environment for testing; valid values for this include `@prod`, `@smoke`, `@pull_request`. "Stage" metadata in `spec` files are owned and controlled by the Quality Assurance (QA) team who carefully analyze the stability of tests and promote/demote them into certain stages. This is not required when submitting a `spec` file and it should be removed when modifying an existing `spec` file.
* **Group** - Indicates test group or category, which is primarily based on functional areas and existing release testing groups. Valid values for this include: `@settings` for Settings, `@playbooks` for Playbooks, etc. This is required when submitting a `spec` file.
* **Skip** - This is a way to skip running a `spec` file depending on the capabilities of the test environment. This is required when submitting a `spec` file if there is a test that has certain limitations or requirements. Forms of capabilities include:
- **Platform-related**: valid values include - `@darwin` for Mac, `@linux` for Linux flavors like Ubuntu, `@win32` for Windows, etc.
- **Browser-related**: valid values include - `@electron`, `@chrome`, `@firefox`, `@edge`, etc.
- **User interface-related**: valid values include `@headless` or `@headed`.
A `spec` file can have zero or more metadata values separated by spaces (for example, `// Stage: @prod @smoke`). A more full example of what metadata would look like at the start of a `spec` file (for example, `attachment_does_not_collapse_spec.ts`) would be:
```
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// ***************************************************************
// - [#] indicates a test step (e.g. # Go to a page)
// - [*] indicates an assertion (e.g. * Check the title)
// - Use element ID when selecting an element. Create one if none.
// ***************************************************************
// Stage: @prod
// Group: @incoming_webhook
```
The metadata is part of a comment block that also includes information on copyright and license, and a section to explain how to tag comments in your code appropriately.
#### Setting up test code
Underneath the comment header, we can add the starter code as defined from the "Test code arrangement" part of the issue. Each test (no matter the situation you're writing a test for) should have a corresponding test case in Zephyr. Therefore, the `describe` block encompassing the test code should correspond to folder name in Zephyr (e.g. "Incoming webhook"), and the `it` block should contain `Zephyr test case number` as `Test Key`, and then the test title. For [Write Web App E2E with Cypress: "MM-T642 Attachment does not collapse" #18184](https://github.com/mattermost/mattermost/issues/18184), in the spec file made for it (`attachment_does_not_collapse_spec.ts`), the starter code would be:
```javascript
describe('Integrations/Incoming Webhook', () => {
it('MM-T642 Attachment does not collapse', () => {
// Put test steps and assertions here
});
});
```
For those writing E2E from Help Wanted tickets with `Area/E2E Tests` label, the `Test Key` is available in the Github issue itself. The `Test Key` is used for mapping test cases per Release Testing specification. It will be used to measure coverage between manual and automated tests. In case the `Test Key` is not available, feel free to prompt the QA team who will either search for an existing Zephyr entry or if it's a new one, it will be created for you.
#### Using Cypress Hooks
Before writing the main body of the test in the `it` block, it can help to write some setup code for test isolation using [hooks](https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests#Hooks). In a `before()` hook, you can run tests in isolation using the custom command `cy.apiInitSetup()`. This command creates a new team, channel, and user which can only be used by the spec file itself. Make use of the `cy.apiInitSetup()` function as much as possible, as it is recommended to log in as a new user and visit the generated team and/or channel. Avoid the use of `sysadmin` user or default `ad-1` team if possible.
For `attachment_does_not_collapse_spec.ts` for example:
```javascript
let incomingWebhook;
let testChannel;
before(() => {
// # Create and visit new channel and create incoming webhook
cy.apiInitSetup().then(({team, channel}) => {
testChannel = channel;
const newIncomingHook = {
channel_id: channel.id,
channel_locked: true,
description: 'Incoming webhook - attachment does not collapse',
display_name: 'attachment-does-not-collapse',
};
cy.apiCreateWebhook(newIncomingHook).then((hook) => {
incomingWebhook = hook;
});
cy.visit(`/${team.name}/channels/${channel.name}`);
});
});
```
The `before()` hook is also a good place to add checks if a test requires a certain kind of server license. If test(s) require a certain licensed feature, use the function `cy.apiRequireLicenseForFeature('<feature name>')`. To check if the server has a license in general, use `cy.apiRequireLicense()`. You can also add hard requirements in the `before()` hook, such as: `cy.shouldNotRunOnCloudEdition()`, `cy.shouldRunOnTeamEdition()`, `cy.shouldHavePluginUploadEnabled()`, `cy.shouldHaveElasticsearchDisabled()`, and `cy.requireWebhookServer()`. For more information on custom commands and how to select elements, check out the [End-to-End (E2E) cheatsheets](/developers/contribute/more-info/webapp/e2e-cheatsheets).
Putting what you've gone through so far all together, you should have code that looks similar to this template:
```javascript
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
// **********************************************************************
// - Use [#] in comment to indicate a test step (e.g. # Go to a page)
// - Use [*] in comment to indicate an assertion (e.g. * Check the title)
// - Query an element with @testing-library/cypress as much as possible
// **********************************************************************
// Group: @change_group
describe('Change to Functional Group', () => {
before(() => {
// Add hard requirement(s) to immediately fail and throw a descriptive error if not met
// cy.shouldNotRunOnCloudEdition();
// Add license requirement(s)
// cy.apiRequireLicense();
// Init basic setup for test isolation
cy.apiInitSetup({loginAfter: true}).then(({team, channel, user}) => {
// Assign return values to variable/s
// # Visit a channel
// Do other setup per test data preconditions
});
});
// Add a title of "[Zephyr_id] - [Zephyr title]" for test case with single step,
// or "[Zephyr_id]_[step_number] - [Zephyr title]" for test case with multiple steps
it('[Zephyr_id] - [Zephyr title]', () => {
// Put test steps and assertions here
});
});
```
#### Main body of the test
<Note title="NOTE">
Use `camelCase` when assigning to `data-testid` or element ID. Also, watch out for potential breaking changes in the snapshot from [unit testing](/developers/contribute/more-info/webapp/unit-testing). Run `make test` to see if all unit tests are passing, and run `npm run updatesnapshot` or `npm run test -- -u` if necessary to update snapshot tests.
</Note>
Now, inside the body of the `it` block , we will write in code the "Steps" part of the E2E issue. The following steps and code are from [Write Web App E2E with Cypress: "MM-T642 Attachment does not collapse" #18184](https://github.com/mattermost/mattermost/issues/18184). Check out the complete file at: [`attachment_does_not_collapse_spec.ts`](https://github.com/mattermost/mattermost-webapp/pull/11231/files).
* **Create an incoming webhook and send it through POST with attachment**:
```javascript
// # Post the incoming webhook with a text attachment
const content = '[very long lorem ipsum test text]';
const payload = {
channel: testChannel.name,
attachments: [{fallback: 'testing attachment does not collapse', pretext: 'testing attachment does not collapse', text: content}],
};
cy.postIncomingWebhook({url: incomingWebhook.url, data: payload, waitFor: 'attachment-pretext'});
```
* **View the webhook post that has the attachment**: you are already in the channel that has the attachment post, as specified by the line `cy.visit('/${team.name}/channels/${channel.name}')`; from the setup section of the code.
* **Type /collapse and press Enter**:
```javascript
// * Check "show more" button is visible and click
cy.getLastPostId().then((postId) => {
const postMessageId = `#${postId}_message`;
cy.get(postMessageId).within(() => {
cy.get('#showMoreButton').scrollIntoView().should('be.visible').and('have.text', 'Show more').click();
});
});
// # Type /collapse and press Enter
const collapseCommand = 'collapse';
cy.uiGetPostTextBox().type(`/${collapseCommand} {enter}`);
```
* **Observe the integration post with the Message Attachment**: where you ascertain what is expected of the test.
```javascript
cy.getNthPostId(-2).then((postId) => {
const postMessageId = `#${postId}_message`;
cy.get(postMessageId).within(() => {
// * Verify "show more" button says "Show less"
cy.get('#showMoreButton').scrollIntoView().should('be.visible').and('have.text', 'Show less');
// * Verify gradient
cy.get('#collapseGradient').should('not.be.visible');
});
```
### Running E2E Tests
#### On your local development machine / Gitpod
1. If the server is not running, launch it by running `make run` in the `server` directory. Then, confirm that the Mattermost instance has started successfully. You can also run `make test-data` in the `server` directory to preload your server instance with initial seed data (you may need to restart the server again).
- Each test case should handle the required system or user settings, but if you encounter an unexpected error while testing, you may want to reset the configuration of the server to the default by going to the `server` directory and running `make config-reset`.
2. Change the directory to `e2e-tests/cypress`, and install dependencies by running `npm i`.
3. You can then run tests in a variety of different ways by using the following commands in the `e2e-tests/cypress` directory:
- **Running all E2E tests**: `npm run cypress:run`. This does not include the `spec` files in the `/e2e-tests/cypress/tests/integration/enterprise` folder because they need an Enterprise license to run successfully.
- **Running tests selectively based on `spec` metadata**: For example, if you want to run all the tests in a specific group, such as those in "accessibility", the command would be: `node run_tests.js --group='@accessibility'`.
- **Using the Cypress desktop app**: `npm run cypress:open`. This will start up the Cypress desktop app, where you will be able to do partial testing depending on the `spec` selected in the app. If you are using Gitpod, the Cypress app will open up in the VNC desktop, which is accessible at port `6080`.
4. Don't forget to check your coding styles! See the [Web app workflow](/developers/contribute/more-info/webapp/developer-workflow) page for helpful commands to run.
#### In a Continuous Integration (CI) pipeline
All tests are run by Mattermost in a CI pipeline, and they are grouped according to test stability.
1. __Daily production tests against development branch (master)__: Initiated on the master branch by using the command `node run_tests.js --stage='@prod'`. These production tests are selected and also labeled with `@prod` in the test metadata. See <a target="_blank" href="https://community.mattermost.com/core/pl/g6wx1d84ibdf7r5frjap4rb55a">link</a> for an example test run posted in our community channel.
2. __Daily production tests against release branch__: Same as above except the test is initiated against the release branch. See <a target="_blank" href="https://community.mattermost.com/core/pl/8r4f17fkutbxxcwumk5mzwpp5c">link</a> for an example test run.
3. __Daily unstable tests against development branch (master)__: Initiated on the master branch by using the command `node run_tests.js --stage='@prod' --invert` to run all tests except production tests. These are called "unstable tests" as they either consistently or intermittently fail due to automation bugs, and not because of product bugs.
#### Environment variables
Several environment variables (env variables) are used when testing with Cypress in order to easily change things when running tests in CI and to cater to different values across developer machines.
Environment variables are [defined in cypress.config.ts](https://github.com/mattermost/mattermost/blob/master/e2e-tests/cypress/cypress.config.ts) under the `env` key. In most cases you don't need to change the values, because it makes use of the default local developer setup. If you do need to make changes, the easiest method is to override by exporting `CYPRESS_*`, where `*` is the key of the variable, for example: `CYPRESS_adminUsername`. See the [Cypress documentation on environment variables](https://docs.cypress.io/guides/guides/environment-variables.html#Setting) for details.
| Variable | Description |
|---------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| CYPRESS\_adminUsername | Admin's username for the test server.<br/><br/>*Default*: `sysadmin` when server is seeded by `make test-data`. |
| CYPRESS\_adminPassword | Admin's password for the test server.<br/><br/>*Default*: `Sys@dmin-sample1` when server is seeded by `make test-data`. |
| CYPRESS\_dbClient | The database of the test server. It should match the server config `SqlSettings.DriverName`.<br/><br/>*Default*: `postgres` <br/>*Valid values*: `postgres` or `mysql` |
| CYPRESS\_dbConnection | The database connection string of the test server. It should match the server config `SqlSettings.DataSource`.<br/><br/> *Default*: `"postgres://mmuser:mostest@localhost/mattermost_test?sslmode=disable\u0026connect_timeout=10"` |
| CYPRESS\_enableVisualTest | Use for visual regression testing.<br/><br/>*Default*: `false`<br/>*Valid values*: `true` or `false` |
| CYPRESS\_ldapServer | Host of the Lightweight Directory Access Protocol (LDAP) server.<br/><br/>*Default*: `localhost` |
| CYPRESS\_ldapPort | Port of the LDAP server.<br/><br/>*Default*: `389` |
| CYPRESS\_runLDAPSync | Option to run LDAP sync.<br/><br/>*Default*: `true`<br/>*Valid values*: `true` or `false` |
| CYPRESS\_resetBeforeTest | When set to `true`, it deletes all teams and their channels where `sysadmin` is a member except `eligendi` team and its channels.<br/><br/>*Default*: `false`<br/>*Valid values*: `true` or `false` |
| CYPRESS\_webhookBaseUrl | A server used for testing webhook integrations.<br/><br/>*Default*: `http://localhost:3000` when initiated with the command `npm run start:webhook` in the `e2e-tests/cypress` directory. |
### Submitting your pull request (PR)
Review the [Test Guidelines](/developers/contribute/more-info/getting-started/test-guideline) for details on how to submit your PR
### Troubleshooting
#### Test(s) failing due to a known issue
If test(s) are failing due to another known issue, follow these steps to amend your test:
1. Append the Jira issue key in the test title, following the format of ` -- KNOWN ISSUE: [Jira_key]`. For example:
```javascript
describe('Upload Files', () => {
it('MM-T2261 Upload SVG and post -- KNOWN ISSUE: MM-38982', () => {
// Test steps and assertion here
});
});
```
2. Move the test case into a separate `spec` file following the format of `<existing_spec_file_name_[1-9].js>`. For example:
`accessibility_account_settings_spec_1.js` and demote the spec file (i.e. remove `// Stage: @prod` from the spec file)
3. If all the test cases are failing in a spec file, update each title as mentioned above and demote the spec file.
4. Link the failed test case(s) to the Jira issue (the known issue). In the Jira bug, select the **Zephyr Scale** tab. Select the **add an existing one** link, then select test case(s), and finally select **Add**.
5. Conversely, remove the Jira issue key if the issue has been resolved and the test is passing.
#### Cypress failed to start after running `npm run cypress:run`
In this problem, either the command line exits immediately without running any test or it logs out like the following with the error message:
```sh
✖ Verifying Cypress can run /Users/user/Library/Caches/Cypress/3.1.3/Cypress.app
→ Cypress Version: 3.1.3
Cypress failed to start.
This is usually caused by a missing library or dependency.
```
The solution to this problem is to clear node options by initiating `unset NODE_OPTIONS` in the command line. Running `npm run cypress:run` should then proceed with Cypress testing.
#### Running any Cypress spec gives `ENOSPC`
This error may occur in Ubuntu when running any Cypress spec:
```
code: 'ENOSPC',
errno: 'ENOSPC',
```
The solution to this problem is to run the following command: `echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p`.
@@ -0,0 +1,47 @@
---
title: "Web app"
sidebar_position: 1
---
The Mattermost web app is written in JavaScript using [React](https://react.dev/) and [Redux](https://redux.js.org/).
## Repository
It is located in the `webapp` directory of the [main Mattermost repository](https://github.com/mattermost/mattermost).
https://github.com/mattermost/mattermost/tree/master/webapp
## Help Wanted
[Find help wanted tickets here](https://mattermost.com/pl/help-wanted-mattermost-webapp/).
## Package structure
The web app is set up as a monorepo which has the code broken up into multiple packages. The main packages in the web app are:
* `channels` - The main web app which contains Channels, the System Console, login/signup pages, and most of the core infrastructure for the app.
* `src/`. Key folders include:
* `actions` - Contains Redux actions which make up much of the view logic for the web app
* `components` - Contains UI components and views written using React
* `i18n` - Contains the localization files for the web app
* `packages/mattermost-redux` - Contains most of the Redux logic used for handling data from the server
* `plugins` - Contains the plugin framework, utility functions and components
* `reducers` - Contains Redux reducers used for view state
* `selectors` - Contains Redux selectors used for view state
* `tests` - Contains setup code and mocks used for unit testing
* `utils` - Contains many widely-used utility functions
* `platform` - Packages used by the web app and related projects
* `client` - The JavaScript client for Mattermost's REST API, available on NPM as [@mattermost/client](https://www.npmjs.com/package/@mattermost/client)
* `components` - A work-in-progress package containing UI components designed to be used by different parts of Mattermost
* `types` - The TypeScript types used by Mattermost, available on NPM as [@mattermost/types](https://www.npmjs.com/package/@mattermost/types)
### Important libraries and technologies
- [React](https://reactjs.org/) - React is a user interface library used for React apps. Its key feature is that it uses a variation of JavaScript called JSX to declaratively define interfaces using HTML-like syntax.
- [Redux](https://redux.js.org/) - Redux is a state management library used for JavaScript apps. Its key features are a centralized data store for the entire app and a pattern for predictably modifying and displaying that application state. Notably, we're not using Redux Toolkit since a large portion of our Redux code predates its existence.
- [Redux Thunk](https://github.com/reduxjs/redux-thunk) - Redux Thunk is a middleware for Redux that's used to write async actions and logic that interacts more closely with the Redux store.
- [React Redux](https://react-redux.js.org/) - React Redux is the library used to connect React components to a Redux store.
## Legacy Notes
Note that the webapp was previously located at https://github.com/mattermost/mattermost-webapp/. You may find additional history in this repository that was not migrated back to https://github.com/mattermost/mattermost when forming the monorepo.

Some files were not shown because too many files have changed in this diff Show More