docs: Add syntax highlighting languages to code blocks.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
(cherry picked from commit b29b6f6526)
This commit is contained in:
Anders Kaseorg
2021-08-19 22:09:04 -07:00
parent dbb7bc824c
commit 90bf44bde0
62 changed files with 729 additions and 627 deletions

View File

@@ -60,7 +60,7 @@ Problems with Zulip's accessibility should be reported as
label. This label can be added by entering the following text in a separate label. This label can be added by entering the following text in a separate
comment on the issue: comment on the issue:
@zulipbot add "area: accessibility" > @zulipbot add "area: accessibility"
If you want to help make Zulip more accessible, here is a list of the If you want to help make Zulip more accessible, here is a list of the
[currently open accessibility issues][accessibility-issues]. [currently open accessibility issues][accessibility-issues].

View File

@@ -34,11 +34,15 @@ When in doubt, ask in [chat.zulip.org](https://chat.zulip.org).
You can run them all at once with You can run them all at once with
./tools/lint ```bash
./tools/lint
```
You can set this up as a local Git commit hook with You can set this up as a local Git commit hook with
tools/setup-git-repo ```bash
tools/setup-git-repo
```
The Vagrant setup process runs this for you. The Vagrant setup process runs this for you.
@@ -66,17 +70,21 @@ to read secrets from `/etc/zulip/secrets.conf`.
Look out for Django code like this: Look out for Django code like this:
bars = Bar.objects.filter(...) ```python
for bar in bars: bars = Bar.objects.filter(...)
foo = bar.foo for bar in bars:
# Make use of foo foo = bar.foo
# Make use of foo
```
...because it equates to: ...because it equates to:
bars = Bar.objects.filter(...) ```python
for bar in bars: bars = Bar.objects.filter(...)
foo = Foo.objects.get(id=bar.foo.id) for bar in bars:
# Make use of foo foo = Foo.objects.get(id=bar.foo.id)
# Make use of foo
```
...which makes a database query for every Bar. While this may be fast ...which makes a database query for every Bar. While this may be fast
locally in development, it may be quite slow in production! Instead, locally in development, it may be quite slow in production! Instead,
@@ -84,10 +92,12 @@ tell Django's [QuerySet
API](https://docs.djangoproject.com/en/dev/ref/models/querysets/) to API](https://docs.djangoproject.com/en/dev/ref/models/querysets/) to
_prefetch_ the data in the initial query: _prefetch_ the data in the initial query:
bars = Bar.objects.filter(...).select_related() ```python
for bar in bars: bars = Bar.objects.filter(...).select_related()
foo = bar.foo # This doesn't take another query, now! for bar in bars:
# Make use of foo foo = bar.foo # This doesn't take another query, now!
# Make use of foo
```
If you can't rewrite it as a single query, that's a sign that something If you can't rewrite it as a single query, that's a sign that something
is wrong with the database schema. So don't defer this optimization when is wrong with the database schema. So don't defer this optimization when
@@ -118,7 +128,7 @@ different database queries:
For example, the following will, surprisingly, fail: For example, the following will, surprisingly, fail:
``` ```python
# Bad example -- will raise! # Bad example -- will raise!
obj: UserProfile = get_user_profile_by_id(17) obj: UserProfile = get_user_profile_by_id(17)
some_objs = UserProfile.objects.get(id=17) some_objs = UserProfile.objects.get(id=17)
@@ -127,7 +137,7 @@ assert obj in set([some_objs])
You should work with the IDs instead: You should work with the IDs instead:
``` ```python
obj: UserProfile = get_user_profile_by_id(17) obj: UserProfile = get_user_profile_by_id(17)
some_objs = UserProfile.objects.get(id=17) some_objs = UserProfile.objects.get(id=17)
assert obj.id in set([o.id for i in some_objs]) assert obj.id in set([o.id for i in some_objs])
@@ -266,18 +276,24 @@ The best way to build complicated DOM elements is a Mustache template
like `static/templates/message_reactions.hbs`. For simpler things like `static/templates/message_reactions.hbs`. For simpler things
you can use jQuery DOM building APIs like so: you can use jQuery DOM building APIs like so:
var new_tr = $('<tr />').attr('id', object.id); ```js
var new_tr = $('<tr />').attr('id', object.id);
```
Passing a HTML string to jQuery is fine for simple hardcoded things Passing a HTML string to jQuery is fine for simple hardcoded things
that don't need internationalization: that don't need internationalization:
foo.append('<p id="selected">/</p>'); ```js
foo.append('<p id="selected">/</p>');
```
but avoid programmatically building complicated strings. but avoid programmatically building complicated strings.
We used to favor attaching behaviors in templates like so: We used to favor attaching behaviors in templates like so:
<p onclick="select_zerver({{id}})"> ```js
<p onclick="select_zerver({{id}})">
```
but there are some reasons to prefer attaching events using jQuery code: but there are some reasons to prefer attaching events using jQuery code:
@@ -328,8 +344,10 @@ type changes in the future.
reason to do otherwise. reason to do otherwise.
- Unpacking sequences doesn't require list brackets: - Unpacking sequences doesn't require list brackets:
[x, y] = xs # unnecessary ```python
x, y = xs # better [x, y] = xs # unnecessary
x, y = xs # better
```
- For string formatting, use `x % (y,)` rather than `x % y`, to avoid - For string formatting, use `x % (y,)` rather than `x % y`, to avoid
ambiguity if `y` happens to be a tuple. ambiguity if `y` happens to be a tuple.

View File

@@ -21,7 +21,7 @@ The best way to connect to your server is using the command line tool `ssh`.
Open *Terminal* or *Bash for Git*, and connect with the following: Open *Terminal* or *Bash for Git*, and connect with the following:
``` ```console
$ ssh username@host $ ssh username@host
``` ```
@@ -75,7 +75,7 @@ Once you have set up the development environment, you can start up the
development server with the following command in the directory where development server with the following command in the directory where
you cloned Zulip: you cloned Zulip:
``` ```bash
./tools/run-dev.py --interface='' ./tools/run-dev.py --interface=''
``` ```
@@ -98,7 +98,7 @@ such as a DigitalOcean Droplet or an AWS EC2 instance, you can set up
port-forwarding to access Zulip by running the following command in port-forwarding to access Zulip by running the following command in
your terminal: your terminal:
``` ```bash
ssh -L 3000:127.0.0.1:9991 <username>@<remote_server_ip> -N ssh -L 3000:127.0.0.1:9991 <username>@<remote_server_ip> -N
``` ```
@@ -202,7 +202,7 @@ developing locally.
1. Install the extension 1. Install the extension
[Remote VSCode](https://marketplace.visualstudio.com/items?itemName=rafaelmaiolla.remote-vscode). [Remote VSCode](https://marketplace.visualstudio.com/items?itemName=rafaelmaiolla.remote-vscode).
2. On your remote machine, run: 2. On your remote machine, run:
``` ```console
$ mkdir -p ~/bin $ mkdir -p ~/bin
$ curl -Lo ~/bin/rmate https://raw.githubusercontent.com/textmate/rmate/master/bin/rmate $ curl -Lo ~/bin/rmate https://raw.githubusercontent.com/textmate/rmate/master/bin/rmate
$ chmod a+x ~/bin/rmate $ chmod a+x ~/bin/rmate
@@ -210,11 +210,11 @@ developing locally.
3. Make sure the remote server is running in VS Code (you can 3. Make sure the remote server is running in VS Code (you can
force-start through the Command Palette). force-start through the Command Palette).
4. SSH to your remote machine using 4. SSH to your remote machine using
``` ```console
$ ssh -R 52698:localhost:52698 user@example.org $ ssh -R 52698:localhost:52698 user@example.org
``` ```
5. On your remote machine, run 5. On your remote machine, run
``` ```console
$ rmate [options] file $ rmate [options] file
``` ```
and the file should open up in VS Code. Any changes you make now will be saved remotely. and the file should open up in VS Code. Any changes you make now will be saved remotely.
@@ -292,7 +292,7 @@ different.
1. First, get an SSL certificate; you can use 1. First, get an SSL certificate; you can use
[our certbot wrapper script used for production](../production/ssl-certificates.html#certbot-recommended) [our certbot wrapper script used for production](../production/ssl-certificates.html#certbot-recommended)
by running the following commands as root: by running the following commands as root:
``` ```bash
# apt install -y crudini # apt install -y crudini
mkdir -p /var/lib/zulip/certbot-webroot/ mkdir -p /var/lib/zulip/certbot-webroot/
# if nginx running this will fail and you need to run `service nginx stop` # if nginx running this will fail and you need to run `service nginx stop`
@@ -303,7 +303,7 @@ different.
1. Install nginx configuration: 1. Install nginx configuration:
``` ```bash
apt install -y nginx-full apt install -y nginx-full
cp -a /home/zulipdev/zulip/tools/droplets/zulipdev /etc/nginx/sites-available/ cp -a /home/zulipdev/zulip/tools/droplets/zulipdev /etc/nginx/sites-available/
ln -nsf /etc/nginx/sites-available/zulipdev /etc/nginx/sites-enabled/ ln -nsf /etc/nginx/sites-available/zulipdev /etc/nginx/sites-enabled/
@@ -316,6 +316,6 @@ different.
will be HTTPS. will be HTTPS.
1. Start the Zulip development environment with the following command: 1. Start the Zulip development environment with the following command:
``` ```bash
env EXTERNAL_HOST="hostname.example.com" ./tools/run-dev.py --interface='' env EXTERNAL_HOST="hostname.example.com" ./tools/run-dev.py --interface=''
``` ```

View File

@@ -36,13 +36,13 @@ the
Start by [cloning your fork of the Zulip repository][zulip-rtd-git-cloning] Start by [cloning your fork of the Zulip repository][zulip-rtd-git-cloning]
and [connecting the Zulip upstream repository][zulip-rtd-git-connect]: and [connecting the Zulip upstream repository][zulip-rtd-git-connect]:
``` ```bash
git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git
cd zulip cd zulip
git remote add -f upstream https://github.com/zulip/zulip.git git remote add -f upstream https://github.com/zulip/zulip.git
``` ```
``` ```bash
# On CentOS/RHEL, you must first install epel-release, and then python36, # On CentOS/RHEL, you must first install epel-release, and then python36,
# and finally you must run `sudo ln -nsf /usr/bin/python36 /usr/bin/python3` # and finally you must run `sudo ln -nsf /usr/bin/python36 /usr/bin/python3`
# On Fedora, you must first install python3 # On Fedora, you must first install python3
@@ -71,20 +71,20 @@ installation method described here.
1. Launch the `Ubuntu 18.04` shell and run the following commands: 1. Launch the `Ubuntu 18.04` shell and run the following commands:
``` ```bash
sudo apt update && sudo apt upgrade sudo apt update && sudo apt upgrade
sudo apt install rabbitmq-server memcached redis-server postgresql sudo apt install rabbitmq-server memcached redis-server postgresql
``` ```
1. Open `/etc/rabbitmq/rabbitmq-env.conf` using e.g.: 1. Open `/etc/rabbitmq/rabbitmq-env.conf` using e.g.:
``` ```bash
sudo vim /etc/rabbitmq/rabbitmq-env.conf sudo vim /etc/rabbitmq/rabbitmq-env.conf
``` ```
Add the following lines at the end of your file and save: Add the following lines at the end of your file and save:
``` ```ini
NODE_IP_ADDRESS=127.0.0.1 NODE_IP_ADDRESS=127.0.0.1
NODE_PORT=5672 NODE_PORT=5672
``` ```
@@ -92,14 +92,14 @@ installation method described here.
1. Make sure you are inside the WSL disk and not in a Windows mounted disk. 1. Make sure you are inside the WSL disk and not in a Windows mounted disk.
You will run into permission issues if you run `provision` from `zulip` You will run into permission issues if you run `provision` from `zulip`
in a Windows mounted disk. in a Windows mounted disk.
``` ```bash
cd ~ # or cd /home/USERNAME cd ~ # or cd /home/USERNAME
``` ```
1. [Clone your fork of the Zulip repository][zulip-rtd-git-cloning] 1. [Clone your fork of the Zulip repository][zulip-rtd-git-cloning]
and [connecting the Zulip upstream repository][zulip-rtd-git-connect]: and [connecting the Zulip upstream repository][zulip-rtd-git-connect]:
``` ```bash
git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git ~/zulip git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git ~/zulip
cd zulip cd zulip
git remote add -f upstream https://github.com/zulip/zulip.git git remote add -f upstream https://github.com/zulip/zulip.git
@@ -109,7 +109,7 @@ installation method described here.
start it (click `Allow access` if you get popups for Windows Firewall start it (click `Allow access` if you get popups for Windows Firewall
blocking some services) blocking some services)
``` ```bash
# Start database, cache, and other services # Start database, cache, and other services
./tools/wsl/start_services ./tools/wsl/start_services
# Install/update the Zulip development environment # Install/update the Zulip development environment
@@ -154,7 +154,7 @@ expected.
1. Start by [cloning your fork of the Zulip repository][zulip-rtd-git-cloning] 1. Start by [cloning your fork of the Zulip repository][zulip-rtd-git-cloning]
and [connecting the Zulip upstream repository][zulip-rtd-git-connect]: and [connecting the Zulip upstream repository][zulip-rtd-git-connect]:
``` ```bash
git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git
cd zulip cd zulip
git remote add -f upstream https://github.com/zulip/zulip.git git remote add -f upstream https://github.com/zulip/zulip.git
@@ -169,7 +169,7 @@ expected.
You should get output like this: You should get output like this:
```text ```console
Bringing machine 'default' up with 'hyperv' provider... Bringing machine 'default' up with 'hyperv' provider...
==> default: Verifying Hyper-V is enabled... ==> default: Verifying Hyper-V is enabled...
==> default: Verifying Hyper-V is accessible... ==> default: Verifying Hyper-V is accessible...
@@ -203,14 +203,14 @@ expected.
1. Set the `EXTERNAL_HOST` environment variable. 1. Set the `EXTERNAL_HOST` environment variable.
```bash ```console
(zulip-py3-venv) vagrant@ubuntu-18:/srv/zulip$ export EXTERNAL_HOST="$(hostname -I | xargs):9991" (zulip-py3-venv) vagrant@ubuntu-18:/srv/zulip$ export EXTERNAL_HOST="$(hostname -I | xargs):9991"
(zulip-py3-venv) vagrant@ubuntu-18:/srv/zulip$ echo $EXTERNAL_HOST (zulip-py3-venv) vagrant@ubuntu-18:/srv/zulip$ echo $EXTERNAL_HOST
``` ```
The output will be like: The output will be like:
```text ```console
172.28.122.156:9991 172.28.122.156:9991
``` ```
@@ -226,13 +226,13 @@ expected.
1. You should now be able to start the Zulip development server. 1. You should now be able to start the Zulip development server.
```bash ```console
(zulip-py3-venv) vagrant@ubuntu-18:/srv/zulip$ ./tools/run-dev.py (zulip-py3-venv) vagrant@ubuntu-18:/srv/zulip$ ./tools/run-dev.py
``` ```
The output will look like: The output will look like:
```text ```console
Starting Zulip on: Starting Zulip on:
http://172.30.24.235:9991/ http://172.30.24.235:9991/

View File

@@ -94,14 +94,14 @@ Now you are ready for [Step 2: Get Zulip code](#step-2-get-zulip-code).
##### 1. Install Vagrant, Docker, and Git ##### 1. Install Vagrant, Docker, and Git
``` ```console
christie@ubuntu-desktop:~ christie@ubuntu-desktop:~
$ sudo apt install vagrant docker.io git $ sudo apt install vagrant docker.io git
``` ```
##### 2. Add yourself to the `docker` group: ##### 2. Add yourself to the `docker` group:
``` ```console
christie@ubuntu-desktop:~ christie@ubuntu-desktop:~
$ sudo adduser $USER docker $ sudo adduser $USER docker
Adding user `christie' to group `docker' ... Adding user `christie' to group `docker' ...
@@ -112,7 +112,7 @@ Done.
You will need to reboot for this change to take effect. If it worked, You will need to reboot for this change to take effect. If it worked,
you will see `docker` in your list of groups: you will see `docker` in your list of groups:
``` ```console
christie@ubuntu-desktop:~ christie@ubuntu-desktop:~
$ groups | grep docker $ groups | grep docker
christie adm cdrom sudo dip plugdev lpadmin sambashare docker christie adm cdrom sudo dip plugdev lpadmin sambashare docker
@@ -126,7 +126,7 @@ bug](https://bugs.launchpad.net/ubuntu/+source/docker.io/+bug/1844894)
may prevent Docker from being automatically enabled and started after may prevent Docker from being automatically enabled and started after
installation. You can check using the following: installation. You can check using the following:
``` ```console
$ systemctl status docker $ systemctl status docker
● docker.service - Docker Application Container Engine ● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled) Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
@@ -137,7 +137,7 @@ If the service is not running, you'll see `Active: inactive (dead)` on
the second line, and will need to enable and start the Docker service the second line, and will need to enable and start the Docker service
using the following: using the following:
``` ```bash
sudo systemctl unmask docker sudo systemctl unmask docker
sudo systemctl enable docker sudo systemctl enable docker
sudo systemctl start docker sudo systemctl start docker
@@ -189,13 +189,13 @@ In **Git for BASH**:
Open **Git BASH as an administrator** and run: Open **Git BASH as an administrator** and run:
``` ```console
$ git config --global core.symlinks true $ git config --global core.symlinks true
``` ```
Now confirm the setting: Now confirm the setting:
``` ```console
$ git config core.symlinks $ git config core.symlinks
true true
``` ```
@@ -210,7 +210,7 @@ In **Cygwin**:
Open a Cygwin window **as an administrator** and do this: Open a Cygwin window **as an administrator** and do this:
``` ```console
christie@win10 ~ christie@win10 ~
$ echo 'export "CYGWIN=$CYGWIN winsymlinks:native"' >> ~/.bash_profile $ echo 'export "CYGWIN=$CYGWIN winsymlinks:native"' >> ~/.bash_profile
``` ```
@@ -218,7 +218,7 @@ $ echo 'export "CYGWIN=$CYGWIN winsymlinks:native"' >> ~/.bash_profile
Next, close that Cygwin window and open another. If you `echo` $CYGWIN you Next, close that Cygwin window and open another. If you `echo` $CYGWIN you
should see: should see:
``` ```console
christie@win10 ~ christie@win10 ~
$ echo $CYGWIN $ echo $CYGWIN
winsymlinks:native winsymlinks:native
@@ -244,7 +244,7 @@ projects and to instead follow these instructions exactly.)
[clone your fork of the Zulip repository](../git/cloning.html#step-1b-clone-to-your-machine) and [clone your fork of the Zulip repository](../git/cloning.html#step-1b-clone-to-your-machine) and
[connect the Zulip upstream repository](../git/cloning.html#step-1c-connect-your-fork-to-zulip-upstream): [connect the Zulip upstream repository](../git/cloning.html#step-1c-connect-your-fork-to-zulip-upstream):
``` ```bash
git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git
cd zulip cd zulip
git remote add -f upstream https://github.com/zulip/zulip.git git remote add -f upstream https://github.com/zulip/zulip.git
@@ -255,7 +255,7 @@ This will create a 'zulip' directory and download the Zulip code into it.
Don't forget to replace YOURUSERNAME with your Git username. You will see Don't forget to replace YOURUSERNAME with your Git username. You will see
something like: something like:
``` ```console
christie@win10 ~ christie@win10 ~
$ git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git $ git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git
Cloning into 'zulip'... Cloning into 'zulip'...
@@ -276,7 +276,7 @@ environment](#step-3-start-the-development-environment).
Change into the zulip directory and tell vagrant to start the Zulip Change into the zulip directory and tell vagrant to start the Zulip
development environment with `vagrant up`: development environment with `vagrant up`:
``` ```bash
# On Windows or macOS: # On Windows or macOS:
cd zulip cd zulip
vagrant plugin install vagrant-vbguest vagrant plugin install vagrant-vbguest
@@ -320,14 +320,14 @@ specified.` several times. This is normal and is not a problem.
Once `vagrant up` has completed, connect to the development Once `vagrant up` has completed, connect to the development
environment with `vagrant ssh`: environment with `vagrant ssh`:
``` ```console
christie@win10 ~/zulip christie@win10 ~/zulip
$ vagrant ssh $ vagrant ssh
``` ```
You should see output that starts like this: You should see output that starts like this:
``` ```console
Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-54-generic x86_64) Welcome to Ubuntu 18.04.2 LTS (GNU/Linux 4.15.0-54-generic x86_64)
``` ```
@@ -340,14 +340,14 @@ provisioning failed and you should look at the
Next, start the Zulip server: Next, start the Zulip server:
``` ```console
(zulip-py3-venv) vagrant@ubuntu-bionic:/srv/zulip (zulip-py3-venv) vagrant@ubuntu-bionic:/srv/zulip
$ ./tools/run-dev.py $ ./tools/run-dev.py
``` ```
You will see several lines of output starting with something like: You will see several lines of output starting with something like:
``` ```console
2016-05-04 22:20:33,895 INFO: process_fts_updates starting 2016-05-04 22:20:33,895 INFO: process_fts_updates starting
Recompiling templates Recompiling templates
2016-05-04 18:20:34,804 INFO: Not in recovery; listening for FTS updates 2016-05-04 18:20:34,804 INFO: Not in recovery; listening for FTS updates
@@ -364,7 +364,7 @@ Performing system checks...
``` ```
And ending with something similar to: And ending with something similar to:
``` ```console
http://localhost:9994/webpack-dev-server/ http://localhost:9994/webpack-dev-server/
webpack result is served from http://localhost:9991/webpack/ webpack result is served from http://localhost:9991/webpack/
content is served from /srv/zulip content is served from /srv/zulip
@@ -385,7 +385,7 @@ The Zulip server will continue to run and send output to the terminal window.
When you navigate to Zulip in your browser, check your terminal and you When you navigate to Zulip in your browser, check your terminal and you
should see something like: should see something like:
``` ```console
2016-05-04 18:21:57,547 INFO 127.0.0.1 GET 302 582ms (+start: 417ms) / (unauth@zulip via ?) 2016-05-04 18:21:57,547 INFO 127.0.0.1 GET 302 582ms (+start: 417ms) / (unauth@zulip via ?)
[04/May/2016 18:21:57]"GET / HTTP/1.0" 302 0 [04/May/2016 18:21:57]"GET / HTTP/1.0" 302 0
2016-05-04 18:21:57,568 INFO 127.0.0.1 GET 301 4ms /login (unauth@zulip via ?) 2016-05-04 18:21:57,568 INFO 127.0.0.1 GET 301 4ms /login (unauth@zulip via ?)
@@ -484,7 +484,7 @@ can halt vagrant from another Terminal/Git BASH window.
From the window where run-dev.py is running: From the window where run-dev.py is running:
``` ```console
2016-05-04 18:33:13,330 INFO 127.0.0.1 GET 200 92ms /register/ (unauth@zulip via ?) 2016-05-04 18:33:13,330 INFO 127.0.0.1 GET 200 92ms /register/ (unauth@zulip via ?)
^C ^C
KeyboardInterrupt KeyboardInterrupt
@@ -495,7 +495,7 @@ christie@win10 ~/zulip
``` ```
Now you can suspend the development environment: Now you can suspend the development environment:
``` ```console
christie@win10 ~/zulip christie@win10 ~/zulip
$ vagrant suspend $ vagrant suspend
==> default: Saving VM state and suspending execution... ==> default: Saving VM state and suspending execution...
@@ -503,7 +503,7 @@ $ vagrant suspend
If `vagrant suspend` doesn't work, try `vagrant halt`: If `vagrant suspend` doesn't work, try `vagrant halt`:
``` ```console
christie@win10 ~/zulip christie@win10 ~/zulip
$ vagrant halt $ vagrant halt
==> default: Attempting graceful shutdown of VM... ==> default: Attempting graceful shutdown of VM...
@@ -520,7 +520,7 @@ pass the `--provider` option required above). You will also need to
connect to the virtual machine with `vagrant ssh` and re-start the connect to the virtual machine with `vagrant ssh` and re-start the
Zulip server: Zulip server:
``` ```console
christie@win10 ~/zulip christie@win10 ~/zulip
$ vagrant up $ vagrant up
$ vagrant ssh $ vagrant ssh
@@ -572,7 +572,7 @@ This is caused by provisioning failing to complete successfully. You
can see the errors in `var/log/provision.log`; it should end with can see the errors in `var/log/provision.log`; it should end with
something like this: something like this:
``` ```text
ESC[94mZulip development environment setup succeeded!ESC[0m ESC[94mZulip development environment setup succeeded!ESC[0m
``` ```
@@ -589,7 +589,7 @@ shell and run `vagrant ssh` again to get the virtualenv setup properly.
#### Vagrant was unable to mount VirtualBox shared folders #### Vagrant was unable to mount VirtualBox shared folders
For the following error: For the following error:
``` ```console
Vagrant was unable to mount VirtualBox shared folders. This is usually Vagrant was unable to mount VirtualBox shared folders. This is usually
because the filesystem "vboxsf" is not available. This filesystem is because the filesystem "vboxsf" is not available. This filesystem is
made available via the VirtualBox Guest Additions and kernel made available via the VirtualBox Guest Additions and kernel
@@ -603,7 +603,7 @@ was:
If this error starts happening unexpectedly, then just run: If this error starts happening unexpectedly, then just run:
``` ```bash
vagrant halt vagrant halt
vagrant up vagrant up
``` ```
@@ -615,7 +615,7 @@ to reboot the guest. After this, you can do `vagrant provision` and
If you receive the following error while running `vagrant up`: If you receive the following error while running `vagrant up`:
``` ```console
SSL read: error:00000000:lib(0):func(0):reason(0), errno 104 SSL read: error:00000000:lib(0):func(0):reason(0), errno 104
``` ```
@@ -627,14 +627,14 @@ better network connection).
When running `vagrant up` or `provision`, if you see the following error: When running `vagrant up` or `provision`, if you see the following error:
``` ```console
==> default: E:unmet dependencies. Try 'apt-get -f install' with no packages (or specify a solution). ==> default: E:unmet dependencies. Try 'apt-get -f install' with no packages (or specify a solution).
``` ```
It means that your local apt repository has been corrupted, which can It means that your local apt repository has been corrupted, which can
usually be resolved by executing the command: usually be resolved by executing the command:
``` ```bash
apt-get -f install apt-get -f install
``` ```
@@ -642,7 +642,7 @@ apt-get -f install
On running `vagrant ssh`, if you see the following error: On running `vagrant ssh`, if you see the following error:
``` ```console
ssh_exchange_identification: Connection closed by remote host ssh_exchange_identification: Connection closed by remote host
``` ```
@@ -655,7 +655,7 @@ for more details.
If you receive the following error while running `vagrant up`: If you receive the following error while running `vagrant up`:
``` ```console
==> default: Traceback (most recent call last): ==> default: Traceback (most recent call last):
==> default: File "./emoji_dump.py", line 75, in <module> ==> default: File "./emoji_dump.py", line 75, in <module>
==> default: ==> default:
@@ -697,7 +697,7 @@ Get the name of your virtual machine by running `vboxmanage list vms` and
then print out the custom settings for this virtual machine with then print out the custom settings for this virtual machine with
`vboxmanage getextradata YOURVMNAME enumerate`: `vboxmanage getextradata YOURVMNAME enumerate`:
``` ```console
christie@win10 ~/zulip christie@win10 ~/zulip
$ vboxmanage list vms $ vboxmanage list vms
"zulip_default_1462498139595_55484" {5a65199d-8afa-4265-b2f6-6b1f162f157d} "zulip_default_1462498139595_55484" {5a65199d-8afa-4265-b2f6-6b1f162f157d}
@@ -716,7 +716,7 @@ If `vboxmanage enumerate` prints nothing, or shows a value of 0 for
VBoxInternal2/SharedFoldersEnableSymlinksCreate/srv_zulip, then enable VBoxInternal2/SharedFoldersEnableSymlinksCreate/srv_zulip, then enable
symbolic links by running this command in Terminal/Git BASH/Cygwin: symbolic links by running this command in Terminal/Git BASH/Cygwin:
``` ```bash
vboxmanage setextradata YOURVMNAME VBoxInternal2/SharedFoldersEnableSymlinksCreate/srv_zulip 1 vboxmanage setextradata YOURVMNAME VBoxInternal2/SharedFoldersEnableSymlinksCreate/srv_zulip 1
``` ```
@@ -730,7 +730,7 @@ Windows is incorrectly attempting to use Hyper-V rather than
Virtualbox as the virtualization provider. You can fix this by Virtualbox as the virtualization provider. You can fix this by
explicitly passing the virtualbox provider to `vagrant up`: explicitly passing the virtualbox provider to `vagrant up`:
``` ```console
christie@win10 ~/zulip christie@win10 ~/zulip
$ vagrant up --provide=virtualbox $ vagrant up --provide=virtualbox
``` ```
@@ -739,7 +739,7 @@ $ vagrant up --provide=virtualbox
If you see the following error after running `vagrant up`: If you see the following error after running `vagrant up`:
``` ```console
default: SSH address: 127.0.0.1:2222 default: SSH address: 127.0.0.1:2222
default: SSH username: vagrant default: SSH username: vagrant
default: SSH auth method: private key default: SSH auth method: private key
@@ -762,7 +762,7 @@ this post](https://stackoverflow.com/questions/22575261/vagrant-stuck-connection
If you see the following error when you run `vagrant up`: If you see the following error when you run `vagrant up`:
``` ```console
Timed out while waiting for the machine to boot. This means that Timed out while waiting for the machine to boot. This means that
Vagrant was unable to communicate with the guest machine within Vagrant was unable to communicate with the guest machine within
the configured ("config.vm.boot_timeout" value) time period. the configured ("config.vm.boot_timeout" value) time period.
@@ -809,7 +809,7 @@ proxy to access the Internet and haven't [configured the development
environment to use it](#specifying-a-proxy). environment to use it](#specifying-a-proxy).
Once you've provisioned successfully, you'll get output like this: Once you've provisioned successfully, you'll get output like this:
``` ```console
Zulip development environment setup succeeded! Zulip development environment setup succeeded!
(zulip-py3-venv) vagrant@vagrant-base-trusty-amd64:~/zulip$ (zulip-py3-venv) vagrant@vagrant-base-trusty-amd64:~/zulip$
``` ```
@@ -836,7 +836,7 @@ the VM.
##### yarn install warnings ##### yarn install warnings
``` ```console
$ yarn install $ yarn install
yarn install v0.24.5 yarn install v0.24.5
[1/4] Resolving packages... [1/4] Resolving packages...
@@ -853,7 +853,7 @@ It is okay to proceed and start the Zulip server.
#### VBoxManage errors related to VT-x or WHvSetupPartition #### VBoxManage errors related to VT-x or WHvSetupPartition
``` ```console
There was an error while executing `VBoxManage`, a CLI used by Vagrant There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below. for controlling VirtualBox. The command and stderr is shown below.
@@ -866,7 +866,7 @@ VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component ConsoleWrap,
or or
``` ```console
Stderr: VBoxManage.exe: error: Call to WHvSetupPartition failed: ERROR_SUCCESS (Last=0xc000000d/87) (VERR_NEM_VM_CREATE_FAILED) Stderr: VBoxManage.exe: error: Call to WHvSetupPartition failed: ERROR_SUCCESS (Last=0xc000000d/87) (VERR_NEM_VM_CREATE_FAILED)
VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component ConsoleWrap, interface IConsole VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component ConsoleWrap, interface IConsole
``` ```
@@ -882,7 +882,7 @@ later, run `bcdedit /deletevalue hypervisorlaunchtype`, and reboot.
#### OSError: [Errno 26] Text file busy #### OSError: [Errno 26] Text file busy
``` ```console
default: Traceback (most recent call last): default: Traceback (most recent call last):
default: File "/srv/zulip-py3-venv/lib/python3.6/shutil.py", line 426, in _rmtree_safe_fd default: File "/srv/zulip-py3-venv/lib/python3.6/shutil.py", line 426, in _rmtree_safe_fd
@@ -896,7 +896,7 @@ the VirtualBox Guest Additions for Linux on Windows hosts. You can
check the running version of VirtualBox Guest Additions with this check the running version of VirtualBox Guest Additions with this
command: command:
``` ```bash
vagrant ssh -- 'modinfo -F version vboxsf' vagrant ssh -- 'modinfo -F version vboxsf'
``` ```
@@ -905,13 +905,13 @@ able to work around it by downgrading VirtualBox Guest Additions to
6.0.4. To do this, create a `~/.zulip-vagrant-config` file and add 6.0.4. To do this, create a `~/.zulip-vagrant-config` file and add
this line: this line:
``` ```text
VBOXADD_VERSION 6.0.4 VBOXADD_VERSION 6.0.4
``` ```
Then run these commands (yes, reload is needed twice): Then run these commands (yes, reload is needed twice):
``` ```bash
vagrant plugin install vagrant-vbguest vagrant plugin install vagrant-vbguest
vagrant reload vagrant reload
vagrant reload --provision vagrant reload --provision
@@ -927,7 +927,7 @@ a local mirror closer to your location. To do this, create
`~/.zulip-vagrant-config` and add a line like this, replacing the URL `~/.zulip-vagrant-config` and add a line like this, replacing the URL
as appropriate: as appropriate:
``` ```text
UBUNTU_MIRROR http://us.archive.ubuntu.com/ubuntu/ UBUNTU_MIRROR http://us.archive.ubuntu.com/ubuntu/
``` ```
@@ -937,14 +937,14 @@ If you need to use a proxy server to access the Internet, you will
need to specify the proxy settings before running `Vagrant up`. need to specify the proxy settings before running `Vagrant up`.
First, install the Vagrant plugin `vagrant-proxyconf`: First, install the Vagrant plugin `vagrant-proxyconf`:
``` ```bash
vagrant plugin install vagrant-proxyconf vagrant plugin install vagrant-proxyconf
``` ```
Then create `~/.zulip-vagrant-config` and add the following lines to Then create `~/.zulip-vagrant-config` and add the following lines to
it (with the appropriate values in it for your proxy): it (with the appropriate values in it for your proxy):
``` ```text
HTTP_PROXY http://proxy_host:port HTTP_PROXY http://proxy_host:port
HTTPS_PROXY http://proxy_host:port HTTPS_PROXY http://proxy_host:port
NO_PROXY localhost,127.0.0.1,.example.com,.zulipdev.com NO_PROXY localhost,127.0.0.1,.example.com,.zulipdev.com
@@ -953,7 +953,7 @@ NO_PROXY localhost,127.0.0.1,.example.com,.zulipdev.com
For proxies that require authentication, the config will be a bit more For proxies that require authentication, the config will be a bit more
complex, e.g.: complex, e.g.:
``` ```text
HTTP_PROXY http://userName:userPassword@192.168.1.1:8080 HTTP_PROXY http://userName:userPassword@192.168.1.1:8080
HTTPS_PROXY http://userName:userPassword@192.168.1.1:8080 HTTPS_PROXY http://userName:userPassword@192.168.1.1:8080
NO_PROXY localhost,127.0.0.1,.example.com,.zulipdev.com NO_PROXY localhost,127.0.0.1,.example.com,.zulipdev.com
@@ -978,7 +978,7 @@ then do a `vagrant reload`.
You can also change the port on the host machine that Vagrant uses by You can also change the port on the host machine that Vagrant uses by
adding to your `~/.zulip-vagrant-config` file. E.g. if you set: adding to your `~/.zulip-vagrant-config` file. E.g. if you set:
``` ```text
HOST_PORT 9971 HOST_PORT 9971
``` ```
@@ -989,7 +989,7 @@ If you'd like to be able to connect to your development environment from other
machines than the VM host, you can manually set the host IP address in the machines than the VM host, you can manually set the host IP address in the
'~/.zulip-vagrant-config' file as well. For example, if you set: '~/.zulip-vagrant-config' file as well. For example, if you set:
``` ```text
HOST_IP_ADDR 0.0.0.0 HOST_IP_ADDR 0.0.0.0
``` ```
@@ -1015,14 +1015,14 @@ more resources.
To do so, create a `~/.zulip-vagrant-config` file containing the To do so, create a `~/.zulip-vagrant-config` file containing the
following lines: following lines:
``` ```text
GUEST_CPUS <number of cpus> GUEST_CPUS <number of cpus>
GUEST_MEMORY_MB <system memory (in MB)> GUEST_MEMORY_MB <system memory (in MB)>
``` ```
For example: For example:
``` ```text
GUEST_CPUS 4 GUEST_CPUS 4
GUEST_MEMORY_MB 8192 GUEST_MEMORY_MB 8192
``` ```

View File

@@ -17,7 +17,7 @@ RAM, in order to accommodate the VMs and the steps which build the
release assets. release assets.
To begin, install the LXC toolchain: To begin, install the LXC toolchain:
``` ```bash
sudo apt-get install lxc lxc-utils sudo apt-get install lxc lxc-utils
``` ```
@@ -32,7 +32,7 @@ You only need to do this step once per time you work on a set of
changes, to refresh the package that the installer uses. The installer changes, to refresh the package that the installer uses. The installer
doesn't work cleanly out of a source checkout; it wants a release doesn't work cleanly out of a source checkout; it wants a release
checkout, so we build a tarball of one of those first: checkout, so we build a tarball of one of those first:
``` ```bash
./tools/build-release-tarball test-installer ./tools/build-release-tarball test-installer
``` ```
@@ -46,7 +46,7 @@ directory. The test installer needs the release directory to be named
`zulip-server`, so we rename it and move it appropriately. In the `zulip-server`, so we rename it and move it appropriately. In the
first line, you'll need to substitute the actual path that you got for first line, you'll need to substitute the actual path that you got for
the tarball, above: the tarball, above:
``` ```bash
tar xzf /tmp/tmp.fepqqNBWxp/zulip-server-test-installer.tar.gz tar xzf /tmp/tmp.fepqqNBWxp/zulip-server-test-installer.tar.gz
mkdir zulip-test-installer mkdir zulip-test-installer
mv zulip-server-test-installer zulip-test-installer/zulip-server mv zulip-server-test-installer zulip-test-installer/zulip-server
@@ -65,7 +65,7 @@ into the installer.
For example, to test an install onto Ubuntu 20.04 "Focal", we might For example, to test an install onto Ubuntu 20.04 "Focal", we might
call: call:
``` ```bash
sudo ./tools/test-install/install \ sudo ./tools/test-install/install \
-r focal \ -r focal \
./zulip-test-installer/ \ ./zulip-test-installer/ \
@@ -82,7 +82,7 @@ take a while.
Regardless of if the install succeeds or fails, it will stay running Regardless of if the install succeeds or fails, it will stay running
so you can inspect it. You can see all of the containers which are so you can inspect it. You can see all of the containers which are
running, and their randomly-generated names, by running: running, and their randomly-generated names, by running:
``` ```bash
sudo lxc-ls -f sudo lxc-ls -f
``` ```
@@ -90,7 +90,7 @@ sudo lxc-ls -f
After using `lxc-ls` to list containers, you can choose one of them After using `lxc-ls` to list containers, you can choose one of them
and connect to its terminal: and connect to its terminal:
``` ```bash
sudo lxc-attach --clear-env -n zulip-install-focal-PUvff sudo lxc-attach --clear-env -n zulip-install-focal-PUvff
``` ```
@@ -98,12 +98,12 @@ sudo lxc-attach --clear-env -n zulip-install-focal-PUvff
To destroy all containers (but leave the base containers, which speed To destroy all containers (but leave the base containers, which speed
up the initial install): up the initial install):
``` ```bash
sudo ./tools/test-install/destroy-all -f sudo ./tools/test-install/destroy-all -f
``` ```
To destroy just one container: To destroy just one container:
``` ```bash
sudo lxc-destroy -f -n zulip-install-focal-PUvff sudo lxc-destroy -f -n zulip-install-focal-PUvff
``` ```
@@ -115,7 +115,7 @@ Iterate on the installer by making changes to your source tree,
copying them into the release directory, and re-running the installer, copying them into the release directory, and re-running the installer,
which will start up a new container. Here, we update just the which will start up a new container. Here, we update just the
`scripts` and `puppet` directories of the release directory: `scripts` and `puppet` directories of the release directory:
``` ```bash
rsync -az scripts puppet zulip-test-installer/zulip-server/ rsync -az scripts puppet zulip-test-installer/zulip-server/
sudo ./tools/test-install/install \ sudo ./tools/test-install/install \
@@ -124,4 +124,3 @@ sudo ./tools/test-install/install \
--hostname=zulip.example.net \ --hostname=zulip.example.net \
--email=username@example.net --email=username@example.net
``` ```

View File

@@ -101,7 +101,7 @@ defined using a special Markdown extension
(`zerver/openapi/markdown_extension.py`). To use this extension, one (`zerver/openapi/markdown_extension.py`). To use this extension, one
writes a Markdown file block that looks something like this: writes a Markdown file block that looks something like this:
``` ```md
{start_tabs} {start_tabs}
{tab|python} {tab|python}
@@ -169,7 +169,7 @@ an API endpoint supports. You'll see this in files like
directive (implemented in directive (implemented in
`zerver/lib/markdown/api_arguments_table_generator.py`): `zerver/lib/markdown/api_arguments_table_generator.py`):
``` ```md
{generate_api_arguments_table|zulip.yaml|/messages/render:post} {generate_api_arguments_table|zulip.yaml|/messages/render:post}
``` ```
@@ -186,7 +186,7 @@ You can use the following Markdown directive to render the fixtures
defined in the OpenAPI `zulip.yaml` for a given endpoint and status defined in the OpenAPI `zulip.yaml` for a given endpoint and status
code: code:
``` ```md
{generate_code_example|/messages/render:post|fixture(200)} {generate_code_example|/messages/render:post|fixture(200)}
``` ```

View File

@@ -46,7 +46,7 @@ Usually, this involves a few steps:
If your new integration is an incoming webhook integration, you can generate If your new integration is an incoming webhook integration, you can generate
the screenshot using `tools/generate-integration-docs-screenshot`: the screenshot using `tools/generate-integration-docs-screenshot`:
```sh ```bash
./tools/generate-integration-docs-screenshot --integration integrationname ./tools/generate-integration-docs-screenshot --integration integrationname
``` ```
@@ -136,7 +136,7 @@ Here are a few common macros used to document Zulip's integrations:
* `{!webhook-url-with-bot-email.md!}` - Used in certain non-webhook integrations * `{!webhook-url-with-bot-email.md!}` - Used in certain non-webhook integrations
to generate URLs of the form: to generate URLs of the form:
``` ```text
https://bot_email:bot_api_key@yourZulipDomain.zulipchat.com/api/v1/external/beanstalk https://bot_email:bot_api_key@yourZulipDomain.zulipchat.com/api/v1/external/beanstalk
``` ```

View File

@@ -40,7 +40,7 @@ types of authentication, and configure other settings. Once defined,
information in this section rarely changes. information in this section rarely changes.
For example, the `swagger` and `info` objects look like this: For example, the `swagger` and `info` objects look like this:
``` ```yaml
# Basic Swagger UI info # Basic Swagger UI info
openapi: 3.0.1 openapi: 3.0.1
info: info:
@@ -79,7 +79,7 @@ expects a GET request with one
Basic authentication, and returns a JSON response containing `msg`, Basic authentication, and returns a JSON response containing `msg`,
`result`, and `presence` values. `result`, and `presence` values.
``` ```yaml
/users/{user}/presence: /users/{user}/presence:
get: get:
description: Get presence data for another user. description: Get presence data for another user.
@@ -119,7 +119,7 @@ contains schemas referenced by other objects. For example,
contains three required parameters. Two are strings, and one is an contains three required parameters. Two are strings, and one is an
integer. integer.
``` ```yaml
MessageResponse: MessageResponse:
type: object type: object
required: required:
@@ -183,7 +183,7 @@ correct.
### Examples: ### Examples:
``` ```yaml
Description: | Description: |
This description has multiple lines. This description has multiple lines.
Sometimes descriptions can go on for Sometimes descriptions can go on for

View File

@@ -43,7 +43,7 @@ your changes), the dependencies are automatically installed as part of
Zulip development environment provisioning, and you can build the Zulip development environment provisioning, and you can build the
documentation using: documentation using:
``` ```bash
./tools/build-docs ./tools/build-docs
``` ```

View File

@@ -210,7 +210,7 @@ instructions. For instance, it may address a common problem users may
encounter while following the instructions, or point to an option for power encounter while following the instructions, or point to an option for power
users. users.
``` ```md
!!! tip "" !!! tip ""
If you've forgotten your password, see the If you've forgotten your password, see the
[Change your password](/help/change-your-password) page for [Change your password](/help/change-your-password) page for
@@ -220,7 +220,7 @@ users.
A **warning** is a note on what happens when there is some kind of problem. A **warning** is a note on what happens when there is some kind of problem.
Tips are more common than warnings. Tips are more common than warnings.
``` ```md
!!! warn "" !!! warn ""
**Note:** If you attempt to input a nonexistent stream name, an error **Note:** If you attempt to input a nonexistent stream name, an error
message will appear. message will appear.
@@ -237,14 +237,16 @@ design to easily show the instructions for different
[platforms](https://zulip.com/help/logging-out) in user docs, [platforms](https://zulip.com/help/logging-out) in user docs,
languages in API docs, etc. To create a tab switcher, write: languages in API docs, etc. To create a tab switcher, write:
{start_tabs} ```md
{tab|desktop-web} {start_tabs}
# First tab's content {tab|desktop-web}
{tab|ios} # First tab's content
# Second tab's content {tab|ios}
{tab|android} # Second tab's content
# Third tab's content {tab|android}
{end_tabs} # Third tab's content
{end_tabs}
```
The tab identifiers (e.g. `desktop-web` above) and their mappings to The tab identifiers (e.g. `desktop-web` above) and their mappings to
the tabs' labels are declared in the tabs' labels are declared in

View File

@@ -20,7 +20,7 @@ the main server app, this is [zulip/zulip][github-zulip-zulip].
Next, clone your fork to your local machine: Next, clone your fork to your local machine:
``` ```console
$ git clone --config pull.rebase https://github.com/YOUR_USERNAME/zulip.git $ git clone --config pull.rebase https://github.com/YOUR_USERNAME/zulip.git
Cloning into 'zulip' Cloning into 'zulip'
remote: Counting objects: 86768, done. remote: Counting objects: 86768, done.
@@ -56,7 +56,7 @@ your fork.
First, show the currently configured remote repository: First, show the currently configured remote repository:
``` ```console
$ git remote -v $ git remote -v
origin git@github.com:YOUR_USERNAME/zulip.git (fetch) origin git@github.com:YOUR_USERNAME/zulip.git (fetch)
origin git@github.com:YOUR_USERNAME/zulip.git (push) origin git@github.com:YOUR_USERNAME/zulip.git (push)
@@ -68,7 +68,7 @@ have the upstream remote repository configured. For example, when you clone
the remote repository `zulip` and you see the following output from `git remote the remote repository `zulip` and you see the following output from `git remote
-v`: -v`:
``` ```console
origin git@github.com:YOUR_USERNAME/zulip.git (fetch) origin git@github.com:YOUR_USERNAME/zulip.git (fetch)
origin git@github.com:YOUR_USERNAME/zulip.git (push) origin git@github.com:YOUR_USERNAME/zulip.git (push)
zulip https://github.com/zulip/zulip.git (fetch) zulip https://github.com/zulip/zulip.git (fetch)
@@ -78,13 +78,13 @@ zulip https://github.com/zulip/zulip.git (push)
If your client hasn't automatically configured a remote for zulip/zulip, you'll If your client hasn't automatically configured a remote for zulip/zulip, you'll
need to with: need to with:
``` ```console
$ git remote add -f upstream https://github.com/zulip/zulip.git $ git remote add -f upstream https://github.com/zulip/zulip.git
``` ```
Finally, confirm that the new remote repository, upstream, has been configured: Finally, confirm that the new remote repository, upstream, has been configured:
``` ```console
$ git remote -v $ git remote -v
origin git@github.com:YOUR_USERNAME/zulip.git (fetch) origin git@github.com:YOUR_USERNAME/zulip.git (fetch)
origin git@github.com:YOUR_USERNAME/zulip.git (push) origin git@github.com:YOUR_USERNAME/zulip.git (push)

View File

@@ -6,7 +6,7 @@ What happens when you would like to collaborate with another contributor and
they have work-in-progress on their own fork of Zulip? No problem! Just add they have work-in-progress on their own fork of Zulip? No problem! Just add
their fork as a remote and pull their changes. their fork as a remote and pull their changes.
``` ```console
$ git remote add <username> https://github.com/<username>/zulip.git $ git remote add <username> https://github.com/<username>/zulip.git
$ git fetch <username> $ git fetch <username>
``` ```
@@ -15,12 +15,12 @@ Now you can check out their branch just like you would any other. You can name
the branch anything you want, but using both the username and branch name will the branch anything you want, but using both the username and branch name will
help you keep things organized. help you keep things organized.
``` ```console
$ git checkout -b <username>/<branchname> $ git checkout -b <username>/<branchname>
``` ```
You can choose to rename the branch if you prefer: You can choose to rename the branch if you prefer:
``` ```bash
git checkout -b <custombranchname> <username>/<branchname> git checkout -b <custombranchname> <username>/<branchname>
``` ```
@@ -34,13 +34,13 @@ specific to GitHub rather than Git.
First, fetch and create a branch for the pull request, replacing *ID* and First, fetch and create a branch for the pull request, replacing *ID* and
*BRANCHNAME* with the ID of the pull request and your desired branch name: *BRANCHNAME* with the ID of the pull request and your desired branch name:
``` ```console
$ git fetch upstream pull/ID/head:BRANCHNAME $ git fetch upstream pull/ID/head:BRANCHNAME
``` ```
Now switch to the branch: Now switch to the branch:
``` ```console
$ git checkout BRANCHNAME $ git checkout BRANCHNAME
``` ```
@@ -48,7 +48,7 @@ Now you work on this branch as you would any other.
Note: you can use the scripts provided in the tools/ directory to fetch pull Note: you can use the scripts provided in the tools/ directory to fetch pull
requests. You can read more about what they do [here][tools-PR]. requests. You can read more about what they do [here][tools-PR].
``` ```bash
tools/fetch-rebase-pull-request <PR-number> tools/fetch-rebase-pull-request <PR-number>
tools/fetch-pull-request <PR-number> tools/fetch-pull-request <PR-number>
``` ```

View File

@@ -46,7 +46,7 @@ fork up to date][keep-up-to-date] for details.
Here's an example (you would replace *issue-123* with the name of your feature branch): Here's an example (you would replace *issue-123* with the name of your feature branch):
``` ```console
$ git checkout issue-123 $ git checkout issue-123
Switched to branch 'issue-123' Switched to branch 'issue-123'
@@ -68,7 +68,7 @@ Applying: troubleshooting tip about provisioning
Once you've updated your local feature branch, push the changes to GitHub: Once you've updated your local feature branch, push the changes to GitHub:
``` ```console
$ git push origin issue-123 $ git push origin issue-123
Counting objects: 6, done. Counting objects: 6, done.
Delta compression using up to 4 threads. Delta compression using up to 4 threads.
@@ -83,7 +83,7 @@ To git@github.com:christi3k/zulip.git
If your push is rejected with error **failed to push some refs** then you need If your push is rejected with error **failed to push some refs** then you need
to prefix the name of your branch with a `+`: to prefix the name of your branch with a `+`:
``` ```console
$ git push origin +issue-123 $ git push origin +issue-123
Counting objects: 6, done. Counting objects: 6, done.
Delta compression using up to 4 threads. Delta compression using up to 4 threads.

View File

@@ -8,20 +8,20 @@ on reviewing changes by other contributors.
Display changes between index and working tree (what is not yet staged for commit): Display changes between index and working tree (what is not yet staged for commit):
``` ```console
$ git diff $ git diff
``` ```
Display changes between index and last commit (what you have staged for commit): Display changes between index and last commit (what you have staged for commit):
``` ```console
$ git diff --cached $ git diff --cached
``` ```
Display changes in working tree since last commit (changes that are staged as Display changes in working tree since last commit (changes that are staged as
well as ones that are not): well as ones that are not):
``` ```console
$ git diff HEAD $ git diff HEAD
``` ```
@@ -31,13 +31,13 @@ Use any git-ref to compare changes between two commits on the current branch.
Display changes between commit before last and last commit: Display changes between commit before last and last commit:
``` ```console
$ git diff HEAD^ HEAD $ git diff HEAD^ HEAD
``` ```
Display changes between two commits using their hashes: Display changes between two commits using their hashes:
``` ```console
$ git diff e2f404c 7977169 $ git diff e2f404c 7977169
``` ```
@@ -45,19 +45,19 @@ $ git diff e2f404c 7977169
Display changes between tip of topic branch and tip of master branch: Display changes between tip of topic branch and tip of master branch:
``` ```console
$ git diff topic master $ git diff topic master
``` ```
Display changes that have occurred on master branch since topic branch was created: Display changes that have occurred on master branch since topic branch was created:
``` ```console
$ git diff topic...master $ git diff topic...master
``` ```
Display changes you've committed so far since creating a branch from upstream/master: Display changes you've committed so far since creating a branch from upstream/master:
``` ```console
$ git diff upstream/master...HEAD $ git diff upstream/master...HEAD
``` ```

View File

@@ -26,7 +26,7 @@ A merge commit is usually created when you've run `git pull` or `git merge`.
You'll know you're creating a merge commit if you're prompted for a commit You'll know you're creating a merge commit if you're prompted for a commit
message and the default is something like this: message and the default is something like this:
``` ```text
Merge branch 'master' of https://github.com/zulip/zulip Merge branch 'master' of https://github.com/zulip/zulip
# Please enter a commit message to explain why this merge is necessary, # Please enter a commit message to explain why this merge is necessary,
@@ -38,7 +38,7 @@ Merge branch 'master' of https://github.com/zulip/zulip
And the first entry for `git log` will show something like: And the first entry for `git log` will show something like:
``` ```console
commit e5f8211a565a5a5448b93e98ed56415255546f94 commit e5f8211a565a5a5448b93e98ed56415255546f94
Merge: 13bea0e e0c10ed Merge: 13bea0e e0c10ed
Author: Christie Koehler <ck@christi3k.net> Author: Christie Koehler <ck@christi3k.net>
@@ -52,7 +52,7 @@ Some graphical Git clients may also create merge commits.
To undo a merge commit, first run `git reflog` to identify the commit you want To undo a merge commit, first run `git reflog` to identify the commit you want
to roll back to: to roll back to:
``` ```console
$ git reflog $ git reflog
e5f8211 HEAD@{0}: pull upstream master: Merge made by the 'recursive' strategy. e5f8211 HEAD@{0}: pull upstream master: Merge made by the 'recursive' strategy.
@@ -67,7 +67,7 @@ by `git pull` and `13bea0e HEAD@{1}:` is the last commit I made before running
Once you'd identified the ref you want to revert to, you can do so with [git Once you'd identified the ref you want to revert to, you can do so with [git
reset][gitbook-reset]: reset][gitbook-reset]:
``` ```console
$ git reset --hard 13bea0e $ git reset --hard 13bea0e
HEAD is now at 13bea0e test commit for docs. HEAD is now at 13bea0e test commit for docs.
``` ```
@@ -87,7 +87,7 @@ just keep in mind that this changes as you run git commands.
Now when you look at the output of `git reflog`, you should see that the tip of your branch points to your Now when you look at the output of `git reflog`, you should see that the tip of your branch points to your
last commit `13bea0e` before the merge: last commit `13bea0e` before the merge:
``` ```console
$ git reflog $ git reflog
13bea0e HEAD@{2}: reset: moving to HEAD@{1} 13bea0e HEAD@{2}: reset: moving to HEAD@{1}
@@ -97,7 +97,7 @@ e5f8211 HEAD@{3}: pull upstream master: Merge made by the 'recursive' strategy.
And the first entry `git log` shows is this: And the first entry `git log` shows is this:
``` ```console
commit 13bea0e40197b1670e927a9eb05aaf50df9e8277 commit 13bea0e40197b1670e927a9eb05aaf50df9e8277
Author: Christie Koehler <ck@christi3k.net> Author: Christie Koehler <ck@christi3k.net>
Date: Mon Oct 10 13:25:38 2016 -0700 Date: Mon Oct 10 13:25:38 2016 -0700
@@ -115,14 +115,14 @@ with `git cherry-pick` ([docs][gitbook-git-cherry-pick]).
For example, let's say you just committed "some work" and your `git log` looks For example, let's say you just committed "some work" and your `git log` looks
like this: like this:
``` ```console
* 67aea58 (HEAD -> master) some work * 67aea58 (HEAD -> master) some work
* 13bea0e test commit for docs. * 13bea0e test commit for docs.
``` ```
You then mistakenly run `git reset --hard 13bea0e`: You then mistakenly run `git reset --hard 13bea0e`:
``` ```console
$ git reset --hard 13bea0e $ git reset --hard 13bea0e
HEAD is now at 13bea0e test commit for docs. HEAD is now at 13bea0e test commit for docs.
@@ -134,7 +134,7 @@ And then realize you actually needed to keep commit 67aea58. First, use `git
reflog` to confirm that commit you want to restore and then run `git reflog` to confirm that commit you want to restore and then run `git
cherry-pick <commit>`: cherry-pick <commit>`:
``` ```console
$ git reflog $ git reflog
13bea0e HEAD@{0}: reset: moving to 13bea0e 13bea0e HEAD@{0}: reset: moving to 13bea0e
67aea58 HEAD@{1}: commit: some work 67aea58 HEAD@{1}: commit: some work
@@ -160,7 +160,7 @@ change to a part of the file I also want to change. When I try to bring my
branch up to date with `git fetch` and then `git rebase upstream/master`, I see branch up to date with `git fetch` and then `git rebase upstream/master`, I see
the following: the following:
``` ```console
First, rewinding head to replay your work on top of it... First, rewinding head to replay your work on top of it...
Applying: test change for docs Applying: test change for docs
Using index info to reconstruct a base tree... Using index info to reconstruct a base tree...
@@ -182,7 +182,7 @@ after bringing in the new commits from upstream/master.
Running `git status` also gives me some information: Running `git status` also gives me some information:
``` ```console
rebase in progress; onto 5ae56e6 rebase in progress; onto 5ae56e6
You are currently rebasing branch 'docs-test' on '5ae56e6'. You are currently rebasing branch 'docs-test' on '5ae56e6'.
(fix conflicts and then run "git rebase --continue") (fix conflicts and then run "git rebase --continue")
@@ -204,7 +204,7 @@ and `>>>>>>>`) markers to indicate where in files there are conflicts.
Tip: You can see recent changes made to a file by running the following Tip: You can see recent changes made to a file by running the following
commands: commands:
``` ```bash
git fetch upstream git fetch upstream
git log -p upstream/master -- /path/to/file git log -p upstream/master -- /path/to/file
``` ```
@@ -215,7 +215,7 @@ you are rebasing.
Once you've done that, save the file(s), stage them with `git add` and then Once you've done that, save the file(s), stage them with `git add` and then
continue the rebase with `git rebase --continue`: continue the rebase with `git rebase --continue`:
``` ```console
$ git add README.md $ git add README.md
$ git rebase --continue $ git rebase --continue
@@ -241,7 +241,7 @@ where you committed them.
So, before you stop working for the day, or before you switch computers, push So, before you stop working for the day, or before you switch computers, push
all of your commits to GitHub with `git push`: all of your commits to GitHub with `git push`:
``` ```console
$ git push origin <branchname> $ git push origin <branchname>
``` ```
@@ -254,7 +254,7 @@ But if you're switching to another computer on which you have already cloned
Zulip, you need to update your local Git database with new refs from your Zulip, you need to update your local Git database with new refs from your
GitHub fork. You do this with `git fetch`: GitHub fork. You do this with `git fetch`:
``` ```console
$ git fetch <usermame> $ git fetch <usermame>
``` ```
@@ -262,7 +262,7 @@ Ideally you should do this before you have made any commits on the same branch
on the second computer. Then you can `git merge` on whichever branch you need on the second computer. Then you can `git merge` on whichever branch you need
to update: to update:
``` ```console
$ git checkout <my-branch> $ git checkout <my-branch>
Switched to branch '<my-branch>' Switched to branch '<my-branch>'

View File

@@ -8,7 +8,7 @@ determine the currently checked out branch several ways.
One way is with [git status][gitbook-git-status]: One way is with [git status][gitbook-git-status]:
``` ```console
$ git status $ git status
On branch issue-demo On branch issue-demo
nothing to commit, working directory clean nothing to commit, working directory clean
@@ -17,7 +17,7 @@ nothing to commit, working directory clean
Another is with [git branch][gitbook-git-branch] which will display all local Another is with [git branch][gitbook-git-branch] which will display all local
branches, with a star next to the current branch: branches, with a star next to the current branch:
``` ```console
$ git branch $ git branch
* issue-demo * issue-demo
master master
@@ -26,7 +26,7 @@ $ git branch
To see even more information about your branches, including remote branches, To see even more information about your branches, including remote branches,
use `git branch -vva`: use `git branch -vva`:
``` ```console
$ git branch -vva $ git branch -vva
* issue-123 517468b troubleshooting tip about provisioning * issue-123 517468b troubleshooting tip about provisioning
master f0eaee6 [origin/master] bug: Fix traceback in get_missed_message_token_from_address(). master f0eaee6 [origin/master] bug: Fix traceback in get_missed_message_token_from_address().
@@ -53,14 +53,14 @@ and then `git rebase`.
First, [fetch][gitbook-fetch] changes from Zulip's upstream repository you First, [fetch][gitbook-fetch] changes from Zulip's upstream repository you
configured in the step above: configured in the step above:
``` ```console
$ git fetch upstream $ git fetch upstream
``` ```
Next, check out your `master` branch and [rebase][gitbook-git-rebase] it on top Next, check out your `master` branch and [rebase][gitbook-git-rebase] it on top
of `upstream/master`: of `upstream/master`:
``` ```console
$ git checkout master $ git checkout master
Switched to branch 'master' Switched to branch 'master'
@@ -74,7 +74,7 @@ history clean and readable.
When you're ready, [push your changes][github-help-push] to your remote fork. When you're ready, [push your changes][github-help-push] to your remote fork.
Make sure you're in branch `master` and then run `git push`: Make sure you're in branch `master` and then run `git push`:
``` ```console
$ git checkout master $ git checkout master
$ git push origin master $ git push origin master
``` ```
@@ -83,7 +83,7 @@ You can keep any branch up to date using this method. If you're working on a
feature branch (see next section), which we recommend, you would change the feature branch (see next section), which we recommend, you would change the
command slightly, using the name of your `feature-branch` rather than `master`: command slightly, using the name of your `feature-branch` rather than `master`:
``` ```console
$ git checkout feature-branch $ git checkout feature-branch
Switched to branch 'feature-branch' Switched to branch 'feature-branch'
@@ -105,7 +105,7 @@ how][zulip-git-guide-up-to-date]).
Next, from your master branch, create a new tracking branch, providing a Next, from your master branch, create a new tracking branch, providing a
descriptive name for your feature branch: descriptive name for your feature branch:
``` ```console
$ git checkout master $ git checkout master
Switched to branch 'master' Switched to branch 'master'
@@ -116,7 +116,7 @@ Switched to a new branch 'issue-1755-fail2ban'
Alternatively, you can create a new branch explicitly based off Alternatively, you can create a new branch explicitly based off
`upstream/master`: `upstream/master`:
``` ```console
$ git checkout -b issue-1755-fail2ban upstream/master $ git checkout -b issue-1755-fail2ban upstream/master
Switched to a new branch 'issue-1755-fail2ban' Switched to a new branch 'issue-1755-fail2ban'
``` ```
@@ -146,7 +146,7 @@ staged, use `git status`.
If you have no changes in the working directory, you'll see something like If you have no changes in the working directory, you'll see something like
this: this:
``` ```console
$ git status $ git status
On branch issue-123 On branch issue-123
nothing to commit, working directory clean nothing to commit, working directory clean
@@ -154,7 +154,7 @@ nothing to commit, working directory clean
If you have unstaged changes, you'll see something like this: If you have unstaged changes, you'll see something like this:
``` ```console
On branch issue-123 On branch issue-123
Untracked files: Untracked files:
(use "git add <file>..." to include in what will be committed) (use "git add <file>..." to include in what will be committed)
@@ -173,7 +173,7 @@ add` is all about staging the changes you want to commit, you use it to add
Continuing our example from above, after we run `git add newfile.py`, we'll see Continuing our example from above, after we run `git add newfile.py`, we'll see
the following from `git status`: the following from `git status`:
``` ```console
On branch issue-123 On branch issue-123
Changes to be committed: Changes to be committed:
(use "git reset HEAD <file>..." to unstage) (use "git reset HEAD <file>..." to unstage)
@@ -193,7 +193,7 @@ You can also stage changes using your graphical Git client.
If you stage a file, you can undo it with `git reset HEAD <filename>`. Here's If you stage a file, you can undo it with `git reset HEAD <filename>`. Here's
an example where we stage a file `test3.txt` and then unstage it: an example where we stage a file `test3.txt` and then unstage it:
``` ```console
$ git add test3.txt $ git add test3.txt
On branch issue-1234 On branch issue-1234
Changes to be committed: Changes to be committed:
@@ -222,7 +222,7 @@ stage the file for deletion and leave it in your working directory.
To stage a file for deletion and **remove** it from your working directory, use To stage a file for deletion and **remove** it from your working directory, use
`git rm <filename>`: `git rm <filename>`:
``` ```console
$ git rm test.txt $ git rm test.txt
rm 'test.txt' rm 'test.txt'
@@ -240,7 +240,7 @@ ls: No such file or directory
To stage a file for deletion and **keep** it in your working directory, use To stage a file for deletion and **keep** it in your working directory, use
`git rm --cached <filename>`: `git rm --cached <filename>`:
``` ```console
$ git rm --cached test2.txt $ git rm --cached test2.txt
rm 'test2.txt' rm 'test2.txt'
@@ -258,7 +258,7 @@ test2.txt
If you stage a file for deletion with the `--cached` option, and haven't yet If you stage a file for deletion with the `--cached` option, and haven't yet
run `git commit`, you can undo it with `git reset HEAD <filename>`: run `git commit`, you can undo it with `git reset HEAD <filename>`:
``` ```console
$ git reset HEAD test2.txt $ git reset HEAD test2.txt
``` ```
@@ -273,7 +273,7 @@ with `git commit -m "My commit message."` to include a commit message.
Here's an example of committing with the `-m` for a one-line commit message: Here's an example of committing with the `-m` for a one-line commit message:
``` ```console
$ git commit -m "Add a test commit for docs." $ git commit -m "Add a test commit for docs."
[issue-123 173e17a] Add a test commit for docs. [issue-123 173e17a] Add a test commit for docs.
1 file changed, 1 insertion(+) 1 file changed, 1 insertion(+)
@@ -295,7 +295,7 @@ messages][zulip-rtd-commit-messages] for details.
Here's an example of a longer commit message that will be used for a pull request: Here's an example of a longer commit message that will be used for a pull request:
``` ```text
Integrate Fail2Ban. Integrate Fail2Ban.
Updates Zulip logging to put an unambiguous entry into the logs such Updates Zulip logging to put an unambiguous entry into the logs such
@@ -337,7 +337,7 @@ machine and allows others to follow your progress. It also allows you to
Pushing to a feature branch is just like pushing to master: Pushing to a feature branch is just like pushing to master:
``` ```console
$ git push origin <branch-name> $ git push origin <branch-name>
Counting objects: 6, done. Counting objects: 6, done.
Delta compression using up to 4 threads. Delta compression using up to 4 threads.
@@ -367,7 +367,7 @@ your commit history be able to clearly understand your progression of work?
On the command line, you can use the `git log` command to display an easy to On the command line, you can use the `git log` command to display an easy to
read list of your commits: read list of your commits:
``` ```console
$ git log --all --graph --oneline --decorate $ git log --all --graph --oneline --decorate
* 4f8d75d (HEAD -> 1754-docs-add-git-workflow) docs: Add details about configuring Travis CI. * 4f8d75d (HEAD -> 1754-docs-add-git-workflow) docs: Add details about configuring Travis CI.
@@ -404,7 +404,7 @@ Any time you alter history for commits you have already pushed to GitHub,
you'll need to prefix the name of your branch with a `+`. Without this, your you'll need to prefix the name of your branch with a `+`. Without this, your
updates will be rejected with a message such as: updates will be rejected with a message such as:
``` ```console
$ git push origin 1754-docs-add-git-workflow $ git push origin 1754-docs-add-git-workflow
To git@github.com:christi3k/zulip.git To git@github.com:christi3k/zulip.git
! [rejected] 1754-docs-add-git-workflow -> 1754-docs-add-git-workflow (non-fast-forward) ! [rejected] 1754-docs-add-git-workflow -> 1754-docs-add-git-workflow (non-fast-forward)
@@ -418,7 +418,7 @@ hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Re-running the command with `+<branch>` allows the push to continue by Re-running the command with `+<branch>` allows the push to continue by
re-writing the history for the remote repository: re-writing the history for the remote repository:
``` ```console
$ git push origin +1754-docs-add-git-workflow $ git push origin +1754-docs-add-git-workflow
Counting objects: 12, done. Counting objects: 12, done.
Delta compression using up to 4 threads. Delta compression using up to 4 threads.

View File

@@ -16,7 +16,7 @@ notices or warnings it displays.
It's simple to use. Make sure you're in the clone of zulip and run the following: It's simple to use. Make sure you're in the clone of zulip and run the following:
``` ```console
$ ./tools/setup-git-repo $ ./tools/setup-git-repo
``` ```
@@ -24,7 +24,7 @@ The script doesn't produce any output if successful. To check that the hook has
been installed, print a directory listing for `.git/hooks` and you should see been installed, print a directory listing for `.git/hooks` and you should see
something similar to: something similar to:
``` ```console
$ ls -l .git/hooks $ ls -l .git/hooks
pre-commit -> ../../tools/pre-commit pre-commit -> ../../tools/pre-commit
``` ```
@@ -47,7 +47,7 @@ First, make sure you are working in a branch you want to move (in this
example, we'll use the local `master` branch). Then run the script example, we'll use the local `master` branch). Then run the script
with the ID number of the pull request as the first argument. with the ID number of the pull request as the first argument.
``` ```console
$ git checkout master $ git checkout master
Switched to branch 'master' Switched to branch 'master'
Your branch is up-to-date with 'origin/master'. Your branch is up-to-date with 'origin/master'.
@@ -74,7 +74,7 @@ changes from upstream/master with `git rebase`.
Run the script with the ID number of the pull request as the first argument. Run the script with the ID number of the pull request as the first argument.
``` ```console
$ tools/fetch-rebase-pull-request 1913 $ tools/fetch-rebase-pull-request 1913
+ request_id=1913 + request_id=1913
+ git fetch upstream pull/1913/head + git fetch upstream pull/1913/head
@@ -101,7 +101,7 @@ exactly the same repository state as the commit author had.
Run the script with the ID number of the pull request as the first argument. Run the script with the ID number of the pull request as the first argument.
``` ```console
$ tools/fetch-pull-request 5156 $ tools/fetch-pull-request 5156
+ git diff-index --quiet HEAD + git diff-index --quiet HEAD
+ request_id=5156 + request_id=5156
@@ -155,7 +155,7 @@ arguments for default behavior. Since removing review branches can inadvertently
feature branches whose names are like `review-*`, it is not done by default. To feature branches whose names are like `review-*`, it is not done by default. To
use it, run `tools/clean-branches --reviews`. use it, run `tools/clean-branches --reviews`.
``` ```console
$ tools/clean-branches --reviews $ tools/clean-branches --reviews
Deleting local branch review-original-5156 (was 5a1e982) Deleting local branch review-original-5156 (was 5a1e982)
``` ```
@@ -167,7 +167,7 @@ regenerate the file. *Important* don't delete the yarn.lock file. Check out the
latest one from origin/master so that yarn knows the previous asset versions. latest one from origin/master so that yarn knows the previous asset versions.
Run the following commands Run the following commands
``` ```bash
git checkout origin/master -- yarn.lock git checkout origin/master -- yarn.lock
yarn install yarn install
git add yarn.lock git add yarn.lock

View File

@@ -181,8 +181,10 @@ Redis is configured in `zulip/puppet/zulip/files/redis` and it's a
pretty standard configuration except for the last line, which turns off pretty standard configuration except for the last line, which turns off
persistence: persistence:
# Zulip-specific configuration: disable saving to disk. ```text
save "" # Zulip-specific configuration: disable saving to disk.
save ""
```
People often wonder if we could replace memcached with Redis (or People often wonder if we could replace memcached with Redis (or
replace RabbitMQ with Redis, with some loss of functionality). replace RabbitMQ with Redis, with some loss of functionality).

View File

@@ -418,7 +418,7 @@ up-to-date list of raw changes.
that will fix this bug. The new migration will fail if any such that will fix this bug. The new migration will fail if any such
duplicate accounts already exist; you can check whether this will duplicate accounts already exist; you can check whether this will
happen be running the following in a [management shell][manage-shell]: happen be running the following in a [management shell][manage-shell]:
``` ```python
from django.db.models.functions import Lower from django.db.models.functions import Lower
UserProfile.objects.all().annotate(email_lower=Lower("delivery_email")) UserProfile.objects.all().annotate(email_lower=Lower("delivery_email"))
.values('realm_id', 'email_lower').annotate(Count('id')).filter(id__count__gte=2) .values('realm_id', 'email_lower').annotate(Count('id')).filter(id__count__gte=2)

View File

@@ -98,7 +98,7 @@ In either configuration, you will need to do the following:
* Set `AUTH_LDAP_REVERSE_EMAIL_SEARCH` to a query that will find * Set `AUTH_LDAP_REVERSE_EMAIL_SEARCH` to a query that will find
an LDAP user given their email address (i.e. a search by an LDAP user given their email address (i.e. a search by
`LDAP_EMAIL_ATTR`). For example: `LDAP_EMAIL_ATTR`). For example:
``` ```python
AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE, "(mail=%(email)s)") ldap.SCOPE_SUBTREE, "(mail=%(email)s)")
``` ```
@@ -107,7 +107,7 @@ In either configuration, you will need to do the following:
You can quickly test whether your configuration works by running: You can quickly test whether your configuration works by running:
``` ```bash
/home/zulip/deployments/current/manage.py query_ldap username /home/zulip/deployments/current/manage.py query_ldap username
``` ```
@@ -119,7 +119,7 @@ email address, if it isn't the same as the "Zulip username").
of the following configurations: of the following configurations:
* To access by Active Directory username: * To access by Active Directory username:
``` ```python
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)") ldap.SCOPE_SUBTREE, "(sAMAccountName=%(user)s)")
AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
@@ -128,7 +128,7 @@ of the following configurations:
``` ```
* To access by Active Directory email address: * To access by Active Directory email address:
``` ```python
AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", AUTH_LDAP_USER_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
ldap.SCOPE_SUBTREE, "(mail=%(user)s)") ldap.SCOPE_SUBTREE, "(mail=%(user)s)")
AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com", AUTH_LDAP_REVERSE_EMAIL_SEARCH = LDAPSearch("ou=users,dc=example,dc=com",
@@ -157,7 +157,7 @@ Zulip can automatically synchronize data declared in
`AUTH_LDAP_USER_ATTR_MAP` from LDAP into Zulip, via the following `AUTH_LDAP_USER_ATTR_MAP` from LDAP into Zulip, via the following
management command: management command:
``` ```bash
/home/zulip/deployments/current/manage.py sync_ldap_user_data /home/zulip/deployments/current/manage.py sync_ldap_user_data
``` ```
@@ -270,7 +270,7 @@ the fields that would be useful to sync from your LDAP databases.
### Multiple LDAP searches ### Multiple LDAP searches
To do the union of multiple LDAP searches, use `LDAPSearchUnion`. For example: To do the union of multiple LDAP searches, use `LDAPSearchUnion`. For example:
``` ```python
AUTH_LDAP_USER_SEARCH = LDAPSearchUnion( AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(
LDAPSearch("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"), LDAPSearch("ou=users,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"),
LDAPSearch("ou=otherusers,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"), LDAPSearch("ou=otherusers,dc=example,dc=com", ldap.SCOPE_SUBTREE, "(uid=%(user)s)"),
@@ -300,7 +300,7 @@ For the root subdomain, `www` in the list will work, or any other of
For example, with `org_membership` set to `department`, a user with For example, with `org_membership` set to `department`, a user with
the following attributes will have access to the root and `engineering` subdomains: the following attributes will have access to the root and `engineering` subdomains:
``` ```text
... ...
department: engineering department: engineering
department: www department: www
@@ -428,7 +428,7 @@ it as follows:
trust, which consists of multiple certificates. trust, which consists of multiple certificates.
4. Set the proper permissions on these files and directories: 4. Set the proper permissions on these files and directories:
``` ```bash
chown -R zulip.zulip /etc/zulip/saml/ chown -R zulip.zulip /etc/zulip/saml/
find /etc/zulip/saml/ -type f -exec chmod 644 -- {} + find /etc/zulip/saml/ -type f -exec chmod 644 -- {} +
chmod 640 /etc/zulip/saml/zulip-private-key.key chmod 640 /etc/zulip/saml/zulip-private-key.key
@@ -492,7 +492,7 @@ For example, with `attr_org_membership` set to `member`, a user with
the following attribute in their `AttributeStatement` will have access the following attribute in their `AttributeStatement` will have access
to the root and `engineering` subdomains: to the root and `engineering` subdomains:
``` ```xml
<saml2:Attribute Name="member" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified"> <saml2:Attribute Name="member" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string"> <saml2:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">
www www
@@ -525,7 +525,7 @@ straightforward way to deploy that SSO solution with Zulip.
2. Edit `/etc/zulip/zulip.conf` and change the `puppet_classes` line to read: 2. Edit `/etc/zulip/zulip.conf` and change the `puppet_classes` line to read:
``` ```ini
puppet_classes = zulip::profile::standalone, zulip::apache_sso puppet_classes = zulip::profile::standalone, zulip::apache_sso
``` ```
@@ -543,7 +543,7 @@ straightforward way to deploy that SSO solution with Zulip.
using the `htpasswd` example configuration and demonstrate that using the `htpasswd` example configuration and demonstrate that
working end-to-end, before returning later to configure your SSO working end-to-end, before returning later to configure your SSO
solution. You can do that with the following steps: solution. You can do that with the following steps:
``` ```bash
/home/zulip/deployments/current/scripts/restart-server /home/zulip/deployments/current/scripts/restart-server
cd /etc/apache2/sites-available/ cd /etc/apache2/sites-available/
cp zulip-sso.example zulip-sso.conf cp zulip-sso.example zulip-sso.conf
@@ -637,7 +637,7 @@ domain for your server).
`/etc/zulip/apple-auth-key.p8`. Be sure to set `/etc/zulip/apple-auth-key.p8`. Be sure to set
permissions correctly: permissions correctly:
``` ```bash
chown zulip:zulip /etc/zulip/apple-auth-key.p8 chown zulip:zulip /etc/zulip/apple-auth-key.p8
chmod 640 /etc/zulip/apple-auth-key.p8 chmod 640 /etc/zulip/apple-auth-key.p8
``` ```

View File

@@ -11,7 +11,7 @@ something more complicated. This page documents the options for doing so.
To install a development version of Zulip from Git, just clone the Git To install a development version of Zulip from Git, just clone the Git
repository from GitHub: repository from GitHub:
``` ```bash
# First, install Git if you don't have it installed already # First, install Git if you don't have it installed already
sudo apt install git sudo apt install git
git clone https://github.com/zulip/zulip.git zulip-server-git git clone https://github.com/zulip/zulip.git zulip-server-git
@@ -86,7 +86,7 @@ configuration to be completely modular.
For example, to install a Zulip Redis server on a machine, you can run For example, to install a Zulip Redis server on a machine, you can run
the following after unpacking a Zulip production release tarball: the following after unpacking a Zulip production release tarball:
``` ```bash
env PUPPET_CLASSES=zulip::profile::redis ./scripts/setup/install env PUPPET_CLASSES=zulip::profile::redis ./scripts/setup/install
``` ```
@@ -119,7 +119,7 @@ Follow the [standard instructions](../production/install.md), with one
change. When running the installer, pass the `--no-init-db` change. When running the installer, pass the `--no-init-db`
flag, e.g.: flag, e.g.:
``` ```bash
sudo -s # If not already root sudo -s # If not already root
./zulip-server-*/scripts/setup/install --certbot \ ./zulip-server-*/scripts/setup/install --certbot \
--email=YOUR_EMAIL --hostname=YOUR_HOSTNAME \ --email=YOUR_EMAIL --hostname=YOUR_HOSTNAME \
@@ -130,7 +130,7 @@ The script also installs and starts PostgreSQL on the server by
default. We don't need it, so run the following command to default. We don't need it, so run the following command to
stop and disable the local PostgreSQL server. stop and disable the local PostgreSQL server.
``` ```bash
sudo service postgresql stop sudo service postgresql stop
sudo update-rc.d postgresql disable sudo update-rc.d postgresql disable
``` ```
@@ -167,13 +167,13 @@ If you're using password authentication, you should specify the
password of the `zulip` user in /etc/zulip/zulip-secrets.conf as password of the `zulip` user in /etc/zulip/zulip-secrets.conf as
follows: follows:
``` ```ini
postgres_password = abcd1234 postgres_password = abcd1234
``` ```
Now complete the installation by running the following commands. Now complete the installation by running the following commands.
``` ```bash
# Ask Zulip installer to initialize the PostgreSQL database. # Ask Zulip installer to initialize the PostgreSQL database.
su zulip -c '/home/zulip/deployments/current/scripts/setup/initialize-database' su zulip -c '/home/zulip/deployments/current/scripts/setup/initialize-database'
@@ -191,7 +191,7 @@ configure that as follows:
with `/home/zulip/deployments/current/scripts/restart-server`. with `/home/zulip/deployments/current/scripts/restart-server`.
1. Add the following block to `/etc/zulip/zulip.conf`: 1. Add the following block to `/etc/zulip/zulip.conf`:
``` ```ini
[application_server] [application_server]
nginx_listen_port = 12345 nginx_listen_port = 12345
``` ```
@@ -219,7 +219,7 @@ To use Smokescreen:
1. Add `, zulip::profile::smokescreen` to the list of `puppet_classes` 1. Add `, zulip::profile::smokescreen` to the list of `puppet_classes`
in `/etc/zulip/zulip.conf`. A typical value after this change is: in `/etc/zulip/zulip.conf`. A typical value after this change is:
``` ```ini
puppet_classes = zulip::profile::standalone, zulip::profile::smokescreen puppet_classes = zulip::profile::standalone, zulip::profile::smokescreen
``` ```
@@ -231,7 +231,7 @@ To use Smokescreen:
1. Add the following block to `/etc/zulip/zulip.conf`, substituting in 1. Add the following block to `/etc/zulip/zulip.conf`, substituting in
your proxy's hostname/IP and port: your proxy's hostname/IP and port:
``` ```ini
[http_proxy] [http_proxy]
host = 127.0.0.1 host = 127.0.0.1
port = 4750 port = 4750
@@ -280,7 +280,7 @@ HTTP as follows:
1. Add the following block to `/etc/zulip/zulip.conf`: 1. Add the following block to `/etc/zulip/zulip.conf`:
``` ```ini
[application_server] [application_server]
http_only = true http_only = true
``` ```
@@ -304,7 +304,7 @@ For `nginx` configuration, there's two things you need to set up:
`/etc/nginx/sites-available`) for the Zulip app. The following `/etc/nginx/sites-available`) for the Zulip app. The following
example is a good starting point: example is a good starting point:
``` ```nginx
server { server {
listen 443 ssl http2; listen 443 ssl http2;
listen [::]:443 ssl http2; listen [::]:443 ssl http2;
@@ -341,7 +341,7 @@ make the following changes in two configuration files.
1. Follow the instructions for [Configure Zulip to allow HTTP](#configuring-zulip-to-allow-http). 1. Follow the instructions for [Configure Zulip to allow HTTP](#configuring-zulip-to-allow-http).
2. Add the following to `/etc/zulip/settings.py`: 2. Add the following to `/etc/zulip/settings.py`:
``` ```python
EXTERNAL_HOST = 'zulip.example.com' EXTERNAL_HOST = 'zulip.example.com'
ALLOWED_HOSTS = ['zulip.example.com', '127.0.0.1'] ALLOWED_HOSTS = ['zulip.example.com', '127.0.0.1']
USE_X_FORWARDED_HOST = True USE_X_FORWARDED_HOST = True
@@ -357,7 +357,7 @@ make the following changes in two configuration files.
and then run `a2ensite zulip.example.com && systemctl reload and then run `a2ensite zulip.example.com && systemctl reload
apache2`): apache2`):
``` ```apache
<VirtualHost *:80> <VirtualHost *:80>
ServerName zulip.example.com ServerName zulip.example.com
RewriteEngine On RewriteEngine On
@@ -398,7 +398,7 @@ make the following changes in two configuration files.
If you want to use HAProxy with Zulip, this `backend` config is a good If you want to use HAProxy with Zulip, this `backend` config is a good
place to start. place to start.
``` ```text
backend zulip backend zulip
mode http mode http
balance leastconn balance leastconn

View File

@@ -57,7 +57,7 @@ using an [HTTP reverse proxy][reverse-proxy]).
configuring email for `emaildomain.example.com` to be processed by configuring email for `emaildomain.example.com` to be processed by
`hostname.example.com`. You can check your work using this command: `hostname.example.com`. You can check your work using this command:
``` ```console
$ dig +short emaildomain.example.com -t MX $ dig +short emaildomain.example.com -t MX
1 hostname.example.com 1 hostname.example.com
``` ```
@@ -66,7 +66,7 @@ using an [HTTP reverse proxy][reverse-proxy]).
1. Add `, zulip::postfix_localmail` to `puppet_classes` in 1. Add `, zulip::postfix_localmail` to `puppet_classes` in
`/etc/zulip/zulip.conf`. A typical value after this change is: `/etc/zulip/zulip.conf`. A typical value after this change is:
``` ```ini
puppet_classes = zulip::profile::standalone, zulip::postfix_localmail puppet_classes = zulip::profile::standalone, zulip::postfix_localmail
``` ```
@@ -74,7 +74,7 @@ using an [HTTP reverse proxy][reverse-proxy]).
`emaildomain.example.com`, add a section to `/etc/zulip/zulip.conf` `emaildomain.example.com`, add a section to `/etc/zulip/zulip.conf`
on your Zulip server like this: on your Zulip server like this:
``` ```ini
[postfix] [postfix]
mailname = emaildomain.example.com mailname = emaildomain.example.com
``` ```
@@ -117,7 +117,7 @@ Congratulations! The integration should be fully operational.
* Password in `/etc/zulip/zulip-secrets.conf` as `email_gateway_password`. * Password in `/etc/zulip/zulip-secrets.conf` as `email_gateway_password`.
1. Install a cron job to poll the inbox every minute for new messages: 1. Install a cron job to poll the inbox every minute for new messages:
``` ```bash
cd /home/zulip/deployments/current/ cd /home/zulip/deployments/current/
sudo cp puppet/zulip/files/cron.d/email-mirror /etc/cron.d/ sudo cp puppet/zulip/files/cron.d/email-mirror /etc/cron.d/
``` ```

View File

@@ -78,7 +78,7 @@ configuration on the system that forwards email sent locally into your
corporate email system), you will likely need to use something like corporate email system), you will likely need to use something like
these setting values: these setting values:
``` ```python
EMAIL_HOST = 'localhost' EMAIL_HOST = 'localhost'
EMAIL_PORT = 25 EMAIL_PORT = 25
EMAIL_USE_TLS = False EMAIL_USE_TLS = False
@@ -122,7 +122,7 @@ can log them to a file instead.
To do so, add these lines to `/etc/zulip/settings.py`: To do so, add these lines to `/etc/zulip/settings.py`:
``` ```python
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = '/var/log/zulip/emails' EMAIL_FILE_PATH = '/var/log/zulip/emails'
``` ```
@@ -137,7 +137,7 @@ later set up a real SMTP provider!
You can quickly test your outgoing email configuration using: You can quickly test your outgoing email configuration using:
``` ```bash
su zulip -c '/home/zulip/deployments/current/manage.py send_test_email user@example.com' su zulip -c '/home/zulip/deployments/current/manage.py send_test_email user@example.com'
``` ```

View File

@@ -19,42 +19,46 @@ can run them manually before starting the upgrade:
PostgreSQL database. PostgreSQL database.
3. In the PostgreSQL shell, run the following commands: 3. In the PostgreSQL shell, run the following commands:
CREATE INDEX CONCURRENTLY ```postgresql
zerver_usermessage_is_private_message_id CREATE INDEX CONCURRENTLY
ON zerver_usermessage (user_profile_id, message_id) zerver_usermessage_is_private_message_id
WHERE (flags & 2048) != 0; ON zerver_usermessage (user_profile_id, message_id)
WHERE (flags & 2048) != 0;
CREATE INDEX CONCURRENTLY CREATE INDEX CONCURRENTLY
zerver_usermessage_active_mobile_push_notification_id zerver_usermessage_active_mobile_push_notification_id
ON zerver_usermessage (user_profile_id, message_id) ON zerver_usermessage (user_profile_id, message_id)
WHERE (flags & 4096) != 0; WHERE (flags & 4096) != 0;
```
(These first migrations are the only new ones in Zulip 1.9). (These first migrations are the only new ones in Zulip 1.9).
CREATE INDEX CONCURRENTLY ```postgresql
zerver_usermessage_mentioned_message_id CREATE INDEX CONCURRENTLY
ON zerver_usermessage (user_profile_id, message_id) zerver_usermessage_mentioned_message_id
WHERE (flags & 8) != 0; ON zerver_usermessage (user_profile_id, message_id)
WHERE (flags & 8) != 0;
CREATE INDEX CONCURRENTLY CREATE INDEX CONCURRENTLY
zerver_usermessage_starred_message_id zerver_usermessage_starred_message_id
ON zerver_usermessage (user_profile_id, message_id) ON zerver_usermessage (user_profile_id, message_id)
WHERE (flags & 2) != 0; WHERE (flags & 2) != 0;
CREATE INDEX CONCURRENTLY CREATE INDEX CONCURRENTLY
zerver_usermessage_has_alert_word_message_id zerver_usermessage_has_alert_word_message_id
ON zerver_usermessage (user_profile_id, message_id) ON zerver_usermessage (user_profile_id, message_id)
WHERE (flags & 512) != 0; WHERE (flags & 512) != 0;
CREATE INDEX CONCURRENTLY CREATE INDEX CONCURRENTLY
zerver_usermessage_wildcard_mentioned_message_id zerver_usermessage_wildcard_mentioned_message_id
ON zerver_usermessage (user_profile_id, message_id) ON zerver_usermessage (user_profile_id, message_id)
WHERE (flags & 8) != 0 OR (flags & 16) != 0; WHERE (flags & 8) != 0 OR (flags & 16) != 0;
CREATE INDEX CONCURRENTLY CREATE INDEX CONCURRENTLY
zerver_usermessage_unread_message_id zerver_usermessage_unread_message_id
ON zerver_usermessage (user_profile_id, message_id) ON zerver_usermessage (user_profile_id, message_id)
WHERE (flags & 1) = 0; WHERE (flags & 1) = 0;
```
These will take some time to run, during which the server will These will take some time to run, during which the server will
continue to serve user traffic as usual with no disruption. Once they continue to serve user traffic as usual with no disruption. Once they

View File

@@ -57,7 +57,7 @@ service (or back):
The Zulip server has a built-in backup tool: The Zulip server has a built-in backup tool:
``` ```bash
# As the zulip user # As the zulip user
/home/zulip/deployments/current/manage.py backup /home/zulip/deployments/current/manage.py backup
# Or as root # Or as root
@@ -84,7 +84,7 @@ First, [install a new Zulip server through Step 3][install-server]
with the same version of both the base OS and Zulip from your previous with the same version of both the base OS and Zulip from your previous
installation. Then, run as root: installation. Then, run as root:
``` ```bash
/home/zulip/deployments/current/scripts/setup/restore-backup /path/to/backup /home/zulip/deployments/current/scripts/setup/restore-backup /path/to/backup
``` ```
@@ -113,7 +113,7 @@ tarball: `postgres-version`, `os-version`, and `zulip-version`. The
following command may be useful for viewing these files without following command may be useful for viewing these files without
extracting the entire archive. extracting the entire archive.
``` ```bash
tar -Oaxf /path/to/archive/zulip-backup-rest.tar.gz zulip-backup/zulip-version tar -Oaxf /path/to/archive/zulip-backup-rest.tar.gz zulip-backup/zulip-version
``` ```
@@ -167,7 +167,7 @@ daily incremental backups using
storing the backups, edit `/etc/zulip/zulip-secrets.conf` on the storing the backups, edit `/etc/zulip/zulip-secrets.conf` on the
PostgreSQL server to add: PostgreSQL server to add:
``` ```ini
s3_backups_key = # aws public key s3_backups_key = # aws public key
s3_backups_secret_key = # aws secret key s3_backups_secret_key = # aws secret key
s3_backups_bucket = # name of S3 backup s3_backups_bucket = # name of S3 backup
@@ -280,7 +280,7 @@ of the lines for the appropriate option.
Log in to a shell on your Zulip server as the `zulip` user. Run the Log in to a shell on your Zulip server as the `zulip` user. Run the
following commands: following commands:
``` ```bash
cd /home/zulip/deployments/current cd /home/zulip/deployments/current
# ./scripts/stop-server # ./scripts/stop-server
# export DEACTIVATE_FLAG="--deactivate" # Deactivates the organization # export DEACTIVATE_FLAG="--deactivate" # Deactivates the organization
@@ -307,7 +307,7 @@ archive of all the organization's uploaded files.
master][upgrade-zulip-from-git], since we run run master on master][upgrade-zulip-from-git], since we run run master on
Zulip Cloud: Zulip Cloud:
``` ```bash
/home/zulip/deployments/current/scripts/upgrade-zulip-from-git master /home/zulip/deployments/current/scripts/upgrade-zulip-from-git master
``` ```
@@ -364,7 +364,7 @@ about doing so:
following commands, replacing the filename with the path to your data following commands, replacing the filename with the path to your data
export tarball: export tarball:
``` ```bash
cd ~ cd ~
tar -xf /path/to/export/file/zulip-export-zcmpxfm6.tar.gz tar -xf /path/to/export/file/zulip-export-zcmpxfm6.tar.gz
cd /home/zulip/deployments/current cd /home/zulip/deployments/current
@@ -386,7 +386,7 @@ custom subdomain, e.g. if you already have an existing organization on the
root domain. Replace the last three lines above with the following, after replacing root domain. Replace the last three lines above with the following, after replacing
`<subdomain>` with the desired subdomain. `<subdomain>` with the desired subdomain.
``` ```bash
./manage.py import <subdomain> ~/zulip-export-zcmpxfm6 ./manage.py import <subdomain> ~/zulip-export-zcmpxfm6
./manage.py reactivate_realm -r <subdomain> # Reactivates the organization ./manage.py reactivate_realm -r <subdomain> # Reactivates the organization
``` ```
@@ -403,12 +403,12 @@ You can use the `./manage.py send_password_reset_email` command to
send password reset emails to your users. We send password reset emails to your users. We
recommend starting with sending one to yourself for testing: recommend starting with sending one to yourself for testing:
``` ```bash
./manage.py send_password_reset_email -u username@example.com ./manage.py send_password_reset_email -u username@example.com
``` ```
and then once you're ready, you can email them to everyone using e.g. and then once you're ready, you can email them to everyone using e.g.
``` ```bash
./manage.py send_password_reset_email -r '' --all-users ./manage.py send_password_reset_email -r '' --all-users
``` ```
@@ -426,7 +426,7 @@ organization using the following procedure:
with the subdomain if [you are hosting the organization on a with the subdomain if [you are hosting the organization on a
subdomain](../production/multiple-organizations.md): subdomain](../production/multiple-organizations.md):
``` ```python
realm = Realm.objects.get(string_id="") realm = Realm.objects.get(string_id="")
realm.delete() realm.delete()
``` ```
@@ -434,7 +434,7 @@ realm.delete()
The output contains details on the objects deleted from the database. The output contains details on the objects deleted from the database.
Now, exit the management shell and run this to clear Zulip's cache: Now, exit the management shell and run this to clear Zulip's cache:
``` ```bash
/home/zulip/deployments/current/scripts/setup/flush-memcached /home/zulip/deployments/current/scripts/setup/flush-memcached
``` ```
@@ -444,7 +444,7 @@ can additionally delete all file uploads, avatars, and custom emoji on
a Zulip server (across **all organizations**) with the following a Zulip server (across **all organizations**) with the following
command: command:
``` ```bash
rm -rf /home/zulip/uploads/*/* rm -rf /home/zulip/uploads/*/*
``` ```
@@ -454,7 +454,7 @@ in the management shell before deleting the organization from the
database (this will be `2` for the first organization created on a database (this will be `2` for the first organization created on a
Zulip server, shown in the example below), e.g.: Zulip server, shown in the example below), e.g.:
``` ```bash
rm -rf /home/zulip/uploads/*/2/ rm -rf /home/zulip/uploads/*/2/
``` ```

View File

@@ -26,7 +26,7 @@ zulip.com](https://zulip.com)).
Copy your existing nginx configuration to a backup and then merge the Copy your existing nginx configuration to a backup and then merge the
one created by Zulip into it: one created by Zulip into it:
```shell ```bash
sudo cp /etc/nginx/nginx.conf /etc/nginx.conf.before-zulip-install sudo cp /etc/nginx/nginx.conf /etc/nginx.conf.before-zulip-install
sudo wget -O /etc/nginx/nginx.conf.zulip \ sudo wget -O /etc/nginx/nginx.conf.zulip \
https://raw.githubusercontent.com/zulip/zulip/master/puppet/zulip/templates/nginx.conf.template.erb https://raw.githubusercontent.com/zulip/zulip/master/puppet/zulip/templates/nginx.conf.template.erb
@@ -43,7 +43,7 @@ installs.
After the Zulip installation completes, then you can overwrite (or After the Zulip installation completes, then you can overwrite (or
merge) your new nginx.conf with the installed one: merge) your new nginx.conf with the installed one:
```shell ```console
$ sudo meld /etc/nginx/nginx.conf.zulip /etc/nginx/nginx.conf # be sure to merge to the right $ sudo meld /etc/nginx/nginx.conf.zulip /etc/nginx/nginx.conf # be sure to merge to the right
$ sudo service nginx restart $ sudo service nginx restart
``` ```
@@ -58,13 +58,13 @@ If you have a Puppet server running on your server, you will get an
error message about not being able to connect to the client during the error message about not being able to connect to the client during the
install process: install process:
```shell ```console
puppet-agent[29873]: Could not request certificate: Failed to open TCP connection to puppet:8140 puppet-agent[29873]: Could not request certificate: Failed to open TCP connection to puppet:8140
``` ```
So you'll need to shut down any Puppet servers. So you'll need to shut down any Puppet servers.
```shell ```console
$ sudo service puppet-agent stop $ sudo service puppet-agent stop
$ sudo service puppet stop $ sudo service puppet stop
``` ```

View File

@@ -18,7 +18,7 @@ Download and unpack [the latest built server
tarball](https://www.zulip.org/dist/releases/zulip-server-latest.tar.gz) tarball](https://www.zulip.org/dist/releases/zulip-server-latest.tar.gz)
with the following commands: with the following commands:
``` ```bash
cd $(mktemp -d) cd $(mktemp -d)
wget https://www.zulip.org/dist/releases/zulip-server-latest.tar.gz wget https://www.zulip.org/dist/releases/zulip-server-latest.tar.gz
tar -xf zulip-server-latest.tar.gz tar -xf zulip-server-latest.tar.gz
@@ -35,7 +35,7 @@ using code from our [repository on GitHub](https://github.com/zulip/zulip/).
To set up Zulip with the most common configuration, you can run the To set up Zulip with the most common configuration, you can run the
installer as follows: installer as follows:
``` ```bash
sudo -s # If not already root sudo -s # If not already root
./zulip-server-*/scripts/setup/install --certbot \ ./zulip-server-*/scripts/setup/install --certbot \
--email=YOUR_EMAIL --hostname=YOUR_HOSTNAME --email=YOUR_EMAIL --hostname=YOUR_HOSTNAME

View File

@@ -10,7 +10,7 @@ framework][django-management].
Start by logging in as the `zulip` user on the Zulip server. Then run Start by logging in as the `zulip` user on the Zulip server. Then run
them as follows: them as follows:
``` ```bash
cd /home/zulip/deployments/current cd /home/zulip/deployments/current
# Start by reading the help # Start by reading the help
@@ -39,7 +39,7 @@ string ID (usually the subdomain).
You can see all the organizations on your Zulip server using You can see all the organizations on your Zulip server using
`./manage.py list_realms`. `./manage.py list_realms`.
``` ```console
zulip@zulip:~$ /home/zulip/deployments/current/manage.py list_realms zulip@zulip:~$ /home/zulip/deployments/current/manage.py list_realms
id string_id name id string_id name
-- --------- ---- -- --------- ----
@@ -56,7 +56,7 @@ Unless you are
your single Zulip organization on the root domain will have the empty your single Zulip organization on the root domain will have the empty
string (`''`) as its `string_id`. So you can run e.g.: string (`''`) as its `string_id`. So you can run e.g.:
``` ```console
zulip@zulip:~$ /home/zulip/deployments/current/manage.py show_admins -r '' zulip@zulip:~$ /home/zulip/deployments/current/manage.py show_admins -r ''
``` ```
@@ -73,7 +73,7 @@ You can get an IPython shell with full access to code within the Zulip
project using `manage.py shell`, e.g., you can do the following to project using `manage.py shell`, e.g., you can do the following to
change a user's email address: change a user's email address:
``` ```console
$ cd /home/zulip/deployments/current/ $ cd /home/zulip/deployments/current/
$ ./manage.py shell $ ./manage.py shell
In [1]: user_profile = get_user_profile_by_email("email@example.com") In [1]: user_profile = get_user_profile_by_email("email@example.com")

View File

@@ -34,7 +34,7 @@ You can enable this for your Zulip server as follows:
1. If you're running Zulip 1.8.1 or newer, you can run the 1. If you're running Zulip 1.8.1 or newer, you can run the
registration command: registration command:
``` ```bash
# As root: # As root:
su zulip -c '/home/zulip/deployments/current/manage.py register_server' su zulip -c '/home/zulip/deployments/current/manage.py register_server'
# Or as the zulip user, you can skip the `su zulip -c`: # Or as the zulip user, you can skip the `su zulip -c`:

View File

@@ -71,7 +71,7 @@ If you'd like to use hostnames that are not subdomains of each other,
you can set the `REALM_HOSTS` setting in `/etc/zulip/settings.py` to a you can set the `REALM_HOSTS` setting in `/etc/zulip/settings.py` to a
Python dictionary, like this: Python dictionary, like this:
``` ```python
REALM_HOSTS = { REALM_HOSTS = {
'mysubdomain': 'hostname.example.com', 'mysubdomain': 'hostname.example.com',
} }

View File

@@ -53,13 +53,13 @@ PostgreSQL documentation):
Then you should specify the password of the user zulip for the Then you should specify the password of the user zulip for the
database in /etc/zulip/zulip-secrets.conf: database in /etc/zulip/zulip-secrets.conf:
``` ```ini
postgres_password = xxxx postgres_password = xxxx
``` ```
Finally, you can stop your database on the Zulip server via: Finally, you can stop your database on the Zulip server via:
``` ```bash
sudo service postgresql stop sudo service postgresql stop
sudo update-rc.d postgresql disable sudo update-rc.d postgresql disable
``` ```
@@ -76,7 +76,7 @@ can give you some tips.
When debugging PostgreSQL issues, in addition to the standard `pg_top` When debugging PostgreSQL issues, in addition to the standard `pg_top`
tool, often it can be useful to use this query: tool, often it can be useful to use this query:
``` ```postgresql
SELECT procpid,waiting,query_start,current_query FROM pg_stat_activity ORDER BY procpid; SELECT procpid,waiting,query_start,current_query FROM pg_stat_activity ORDER BY procpid;
``` ```
@@ -97,7 +97,7 @@ and enter recovery mode.
To start or stop PostgreSQL manually, use the pg_ctlcluster command: To start or stop PostgreSQL manually, use the pg_ctlcluster command:
``` ```bash
pg_ctlcluster 9.1 [--force] main {start|stop|restart|reload} pg_ctlcluster 9.1 [--force] main {start|stop|restart|reload}
``` ```
@@ -128,7 +128,7 @@ database failed to start. It may tell you to check the logs, but you
won't find any information there. pg_ctlcluster runs the following won't find any information there. pg_ctlcluster runs the following
command underneath when it actually goes to start PostgreSQL: command underneath when it actually goes to start PostgreSQL:
``` ```bash
/usr/lib/postgresql/9.1/bin/pg_ctl start -D /var/lib/postgresql/9.1/main -s -o \ /usr/lib/postgresql/9.1/bin/pg_ctl start -D /var/lib/postgresql/9.1/main -s -o \
'-c config_file="/etc/postgresql/9.1/main/postgresql.conf"' '-c config_file="/etc/postgresql/9.1/main/postgresql.conf"'
``` ```

View File

@@ -38,7 +38,7 @@ If you're using Ubuntu, the
[Ubuntu universe repository][ubuntu-repositories] must be [Ubuntu universe repository][ubuntu-repositories] must be
[enabled][enable-universe], which is usually just: [enabled][enable-universe], which is usually just:
``` ```bash
sudo add-apt-repository universe sudo add-apt-repository universe
sudo apt update sudo apt update
``` ```

View File

@@ -15,7 +15,7 @@ This page discusses additional configuration that a system
administrator can do. To change any of the following settings, edit administrator can do. To change any of the following settings, edit
the `/etc/zulip/settings.py` file on your Zulip server, and then the `/etc/zulip/settings.py` file on your Zulip server, and then
restart the server with the following command: restart the server with the following command:
``` ```bash
su zulip -c '/home/zulip/deployments/current/scripts/restart-server' su zulip -c '/home/zulip/deployments/current/scripts/restart-server'
``` ```

View File

@@ -84,7 +84,7 @@ one as described in the section below after installing Zulip.
To enable the Certbot automation on an already-installed Zulip To enable the Certbot automation on an already-installed Zulip
server, run the following commands: server, run the following commands:
``` ```bash
sudo -s # If not already root sudo -s # If not already root
/home/zulip/deployments/current/scripts/setup/setup-certbot --email=EMAIL HOSTNAME [HOSTNAME2...] /home/zulip/deployments/current/scripts/setup/setup-certbot --email=EMAIL HOSTNAME [HOSTNAME2...]
``` ```
@@ -125,7 +125,7 @@ just pass the `--self-signed-cert` flag when
To generate a self-signed certificate for an already-installed Zulip To generate a self-signed certificate for an already-installed Zulip
server, run the following commands: server, run the following commands:
``` ```bash
sudo -s # If not already root sudo -s # If not already root
/home/zulip/deployments/current/scripts/setup/generate-self-signed-cert HOSTNAME /home/zulip/deployments/current/scripts/setup/generate-self-signed-cert HOSTNAME
``` ```
@@ -134,7 +134,7 @@ generated certificate.
After replacing the certificates, you need to reload `nginx` by After replacing the certificates, you need to reload `nginx` by
running the following as `root`: running the following as `root`:
``` ```bash
service nginx reload service nginx reload
``` ```

View File

@@ -37,13 +37,13 @@ and restart various services.
### Checking status with `supervisorctl status` ### Checking status with `supervisorctl status`
You can check if the Zulip application is running using: You can check if the Zulip application is running using:
``` ```bash
supervisorctl status supervisorctl status
``` ```
When everything is running as expected, you will see something like this: When everything is running as expected, you will see something like this:
``` ```console
process-fts-updates RUNNING pid 2194, uptime 1:13:11 process-fts-updates RUNNING pid 2194, uptime 1:13:11
zulip-django RUNNING pid 2192, uptime 1:13:11 zulip-django RUNNING pid 2192, uptime 1:13:11
zulip-tornado RUNNING pid 2193, uptime 1:13:11 zulip-tornado RUNNING pid 2193, uptime 1:13:11
@@ -75,7 +75,7 @@ After you change configuration in `/etc/zulip/settings.py` or fix a
misconfiguration, you will often want to restart the Zulip application. misconfiguration, you will often want to restart the Zulip application.
You can restart Zulip using: You can restart Zulip using:
``` ```bash
supervisorctl restart all supervisorctl restart all
``` ```
@@ -83,7 +83,7 @@ supervisorctl restart all
Similarly, you can stop Zulip using: Similarly, you can stop Zulip using:
``` ```bash
supervisorctl stop all supervisorctl stop all
``` ```
@@ -111,13 +111,13 @@ problems and how to resolve them:
nginx will fail to start if you configured SSL incorrectly or did nginx will fail to start if you configured SSL incorrectly or did
not provide SSL certificates. To fix this, configure them properly not provide SSL certificates. To fix this, configure them properly
and then run: and then run:
``` ```bash
service nginx restart service nginx restart
``` ```
* If your host is being port scanned by unauthorized users, you may see * If your host is being port scanned by unauthorized users, you may see
messages in `/var/log/zulip/server.log` like messages in `/var/log/zulip/server.log` like
``` ```text
2017-02-22 14:11:33,537 ERROR Invalid HTTP_HOST header: '10.2.3.4'. You may need to add u'10.2.3.4' to ALLOWED_HOSTS. 2017-02-22 14:11:33,537 ERROR Invalid HTTP_HOST header: '10.2.3.4'. You may need to add u'10.2.3.4' to ALLOWED_HOSTS.
``` ```
Django uses the hostnames configured in `ALLOWED_HOSTS` to identify Django uses the hostnames configured in `ALLOWED_HOSTS` to identify
@@ -128,7 +128,7 @@ problems and how to resolve them:
* An AMQPConnectionError traceback or error running rabbitmqctl * An AMQPConnectionError traceback or error running rabbitmqctl
usually means that RabbitMQ is not running; to fix this, try: usually means that RabbitMQ is not running; to fix this, try:
``` ```bash
service rabbitmq-server restart service rabbitmq-server restart
``` ```
If RabbitMQ fails to start, the problem is often that you are using If RabbitMQ fails to start, the problem is often that you are using
@@ -176,7 +176,7 @@ You can ensure that the `unattended-upgrades` package never upgrades
PostgreSQL, memcached, Redis, or RabbitMQ, by configuring in PostgreSQL, memcached, Redis, or RabbitMQ, by configuring in
`/etc/apt/apt.conf.d/50unattended-upgrades`: `/etc/apt/apt.conf.d/50unattended-upgrades`:
``` ```text
// Python regular expressions, matching packages to exclude from upgrading // Python regular expressions, matching packages to exclude from upgrading
Unattended-Upgrade::Package-Blacklist { Unattended-Upgrade::Package-Blacklist {
"libc\d+"; "libc\d+";

View File

@@ -27,7 +27,7 @@ to a new Zulip release:
<https://www.zulip.org/dist/releases/> You can download the latest <https://www.zulip.org/dist/releases/> You can download the latest
release with: release with:
``` ```bash
wget https://www.zulip.org/dist/releases/zulip-server-latest.tar.gz wget https://www.zulip.org/dist/releases/zulip-server-latest.tar.gz
``` ```
@@ -39,7 +39,7 @@ to a new Zulip release:
1. Log in to your Zulip and run as root: 1. Log in to your Zulip and run as root:
``` ```bash
/home/zulip/deployments/current/scripts/upgrade-zulip zulip-server-VERSION.tar.gz /home/zulip/deployments/current/scripts/upgrade-zulip zulip-server-VERSION.tar.gz
``` ```
@@ -71,7 +71,7 @@ Git repository, which is great for [running pre-release changes from
master](#applying-changes-from-master) or [maintaining a master](#applying-changes-from-master) or [maintaining a
fork](#making-changes). The process is simple: fork](#making-changes). The process is simple:
``` ```bash
# Upgrade to an official release # Upgrade to an official release
/home/zulip/deployments/current/scripts/upgrade-zulip-from-git 1.8.1 /home/zulip/deployments/current/scripts/upgrade-zulip-from-git 1.8.1
# Upgrade to a branch (or other Git ref) # Upgrade to a branch (or other Git ref)
@@ -95,7 +95,7 @@ By default, this uses the main upstream Zulip server repository, but
you can configure any other Git repository by adding a section like you can configure any other Git repository by adding a section like
this to `/etc/zulip/zulip.conf`: this to `/etc/zulip/zulip.conf`:
``` ```ini
[deployment] [deployment]
git_repo_url = https://github.com/zulip/zulip.git git_repo_url = https://github.com/zulip/zulip.git
``` ```
@@ -123,7 +123,7 @@ suggest using that updated template to update
do not have a recent [complete backup][backups]), and make a copy do not have a recent [complete backup][backups]), and make a copy
of the current template: of the current template:
``` ```bash
cp -a /etc/zulip/settings.py ~/zulip-settings-backup.py cp -a /etc/zulip/settings.py ~/zulip-settings-backup.py
cp -a /home/zulip/deployments/current/zproject/prod_settings_template.py /etc/zulip/settings-new.py cp -a /home/zulip/deployments/current/zproject/prod_settings_template.py /etc/zulip/settings-new.py
``` ```
@@ -137,7 +137,7 @@ suggest using that updated template to update
the template that your `/etc/zulip/settings.py` was installed the template that your `/etc/zulip/settings.py` was installed
using, and the differences that your file has from that: using, and the differences that your file has from that:
``` ```bash
/home/zulip/deployments/current/scripts/setup/compare-settings-to-template /home/zulip/deployments/current/scripts/setup/compare-settings-to-template
``` ```
@@ -149,7 +149,7 @@ suggest using that updated template to update
the server to pick up the new file; this should be a no-op, but it the server to pick up the new file; this should be a no-op, but it
is much better to discover immediately if it is not: is much better to discover immediately if it is not:
``` ```bash
cp -a /etc/zulip/settings-new.py /etc/zulip/settings.py cp -a /etc/zulip/settings-new.py /etc/zulip/settings.py
su zulip -c '/home/zulip/deployments/current/scripts/restart-server' su zulip -c '/home/zulip/deployments/current/scripts/restart-server'
``` ```
@@ -261,7 +261,7 @@ instructions for other supported platforms.
2. As the Zulip user, stop the Zulip server and run the following 2. As the Zulip user, stop the Zulip server and run the following
to back up the system: to back up the system:
``` ```bash
supervisorctl stop all supervisorctl stop all
/home/zulip/deployments/current/manage.py backup --output=/home/zulip/release-upgrade.backup.tar.gz /home/zulip/deployments/current/manage.py backup --output=/home/zulip/release-upgrade.backup.tar.gz
``` ```
@@ -271,7 +271,7 @@ instructions for other supported platforms.
`do-release-upgrade` and following the prompts until it completes `do-release-upgrade` and following the prompts until it completes
successfully: successfully:
``` ```bash
sudo -i # Or otherwise get a root shell sudo -i # Or otherwise get a root shell
do-release-upgrade -d do-release-upgrade -d
``` ```
@@ -288,7 +288,7 @@ instructions for other supported platforms.
4. As root, upgrade the database to the latest version of PostgreSQL: 4. As root, upgrade the database to the latest version of PostgreSQL:
``` ```bash
/home/zulip/deployments/current/scripts/setup/upgrade-postgresql /home/zulip/deployments/current/scripts/setup/upgrade-postgresql
``` ```
@@ -297,7 +297,7 @@ instructions for other supported platforms.
"collations"); this corrupts database indexes that rely on "collations"); this corrupts database indexes that rely on
collations. Regenerate the affected indexes by running: collations. Regenerate the affected indexes by running:
``` ```bash
/home/zulip/deployments/current/scripts/setup/reindex-textual-data --force /home/zulip/deployments/current/scripts/setup/reindex-textual-data --force
``` ```
@@ -307,7 +307,7 @@ instructions for other supported platforms.
full-text search indexes to work with the upgraded dictionary full-text search indexes to work with the upgraded dictionary
packages: packages:
``` ```bash
rm -rf /srv/zulip-venv-cache/* rm -rf /srv/zulip-venv-cache/*
/home/zulip/deployments/current/scripts/lib/upgrade-zulip-stage-2 \ /home/zulip/deployments/current/scripts/lib/upgrade-zulip-stage-2 \
/home/zulip/deployments/current/ --ignore-static-assets --audit-fts-indexes /home/zulip/deployments/current/ --ignore-static-assets --audit-fts-indexes
@@ -330,7 +330,7 @@ instructions for other supported platforms.
4. As root, upgrade the database installation and OS configuration to 4. As root, upgrade the database installation and OS configuration to
match the new OS version: match the new OS version:
``` ```bash
touch /usr/share/postgresql/10/pgroonga_setup.sql.applied touch /usr/share/postgresql/10/pgroonga_setup.sql.applied
/home/zulip/deployments/current/scripts/zulip-puppet-apply -f /home/zulip/deployments/current/scripts/zulip-puppet-apply -f
pg_dropcluster 10 main --stop pg_dropcluster 10 main --stop
@@ -346,7 +346,7 @@ instructions for other supported platforms.
among other things will recompile Zulip's Python module among other things will recompile Zulip's Python module
dependencies for your new version of Python: dependencies for your new version of Python:
``` ```bash
rm -rf /srv/zulip-venv-cache/* rm -rf /srv/zulip-venv-cache/*
/home/zulip/deployments/current/scripts/lib/upgrade-zulip-stage-2 \ /home/zulip/deployments/current/scripts/lib/upgrade-zulip-stage-2 \
/home/zulip/deployments/current/ --ignore-static-assets /home/zulip/deployments/current/ --ignore-static-assets
@@ -361,7 +361,7 @@ instructions for other supported platforms.
7. As root, finish by verifying the contents of the full-text indexes: 7. As root, finish by verifying the contents of the full-text indexes:
``` ```bash
/home/zulip/deployments/current/manage.py audit_fts_indexes /home/zulip/deployments/current/manage.py audit_fts_indexes
``` ```
@@ -378,7 +378,7 @@ instructions for other supported platforms.
4. As root, upgrade the database installation and OS configuration to 4. As root, upgrade the database installation and OS configuration to
match the new OS version: match the new OS version:
``` ```bash
apt remove upstart -y apt remove upstart -y
/home/zulip/deployments/current/scripts/zulip-puppet-apply -f /home/zulip/deployments/current/scripts/zulip-puppet-apply -f
pg_dropcluster 9.5 main --stop pg_dropcluster 9.5 main --stop
@@ -394,7 +394,7 @@ instructions for other supported platforms.
among other things will recompile Zulip's Python module among other things will recompile Zulip's Python module
dependencies for your new version of Python: dependencies for your new version of Python:
``` ```bash
rm -rf /srv/zulip-venv-cache/* rm -rf /srv/zulip-venv-cache/*
/home/zulip/deployments/current/scripts/lib/upgrade-zulip-stage-2 \ /home/zulip/deployments/current/scripts/lib/upgrade-zulip-stage-2 \
/home/zulip/deployments/current/ --ignore-static-assets /home/zulip/deployments/current/ --ignore-static-assets
@@ -429,7 +429,7 @@ instructions for other supported platforms.
4. As root, upgrade the database installation and OS configuration to 4. As root, upgrade the database installation and OS configuration to
match the new OS version: match the new OS version:
``` ```bash
apt remove upstart -y apt remove upstart -y
/home/zulip/deployments/current/scripts/zulip-puppet-apply -f /home/zulip/deployments/current/scripts/zulip-puppet-apply -f
pg_dropcluster 11 main --stop pg_dropcluster 11 main --stop
@@ -445,7 +445,7 @@ instructions for other supported platforms.
among other things will recompile Zulip's Python module among other things will recompile Zulip's Python module
dependencies for your new version of Python: dependencies for your new version of Python:
``` ```bash
rm -rf /srv/zulip-venv-cache/* rm -rf /srv/zulip-venv-cache/*
/home/zulip/deployments/current/scripts/lib/upgrade-zulip-stage-2 \ /home/zulip/deployments/current/scripts/lib/upgrade-zulip-stage-2 \
/home/zulip/deployments/current/ --ignore-static-assets /home/zulip/deployments/current/ --ignore-static-assets
@@ -463,13 +463,13 @@ instructions for other supported platforms.
"collations"); this corrupts database indexes that rely on "collations"); this corrupts database indexes that rely on
collations. Regenerate the affected indexes by running: collations. Regenerate the affected indexes by running:
``` ```bash
/home/zulip/deployments/current/scripts/setup/reindex-textual-data --force /home/zulip/deployments/current/scripts/setup/reindex-textual-data --force
``` ```
8. As root, finish by verifying the contents of the full-text indexes: 8. As root, finish by verifying the contents of the full-text indexes:
``` ```bash
/home/zulip/deployments/current/manage.py audit_fts_indexes /home/zulip/deployments/current/manage.py audit_fts_indexes
``` ```
@@ -570,7 +570,7 @@ Git guide][git-guide] if you need a primer):
[GitHub](https://github.com). [GitHub](https://github.com).
* Create a branch (named `acme-branch` below) containing your changes: * Create a branch (named `acme-branch` below) containing your changes:
``` ```bash
cd zulip cd zulip
git checkout -b acme-branch 2.0.4 git checkout -b acme-branch 2.0.4
``` ```
@@ -578,7 +578,7 @@ git checkout -b acme-branch 2.0.4
* Use your favorite code editor to modify Zulip. * Use your favorite code editor to modify Zulip.
* Commit your changes and push them to GitHub: * Commit your changes and push them to GitHub:
``` ```bash
git commit -a git commit -a
# Use `git diff` to verify your changes are what you expect # Use `git diff` to verify your changes are what you expect
@@ -614,7 +614,7 @@ Otherwise, you'll need to update your branch by rebasing your changes
repository). The example below assumes you have a branch off of 2.0.4 repository). The example below assumes you have a branch off of 2.0.4
and want to upgrade to 2.1.0. and want to upgrade to 2.1.0.
``` ```bash
cd zulip cd zulip
git fetch --tags upstream git fetch --tags upstream
git checkout acme-branch git checkout acme-branch
@@ -657,7 +657,7 @@ fixes on your local Zulip server without waiting for an official release.
Many bugs have small/simple fixes. In this case, you can use the Git Many bugs have small/simple fixes. In this case, you can use the Git
workflow [described above](#making-changes), using: workflow [described above](#making-changes), using:
``` ```bash
git fetch upstream git fetch upstream
git cherry-pick abcd1234 git cherry-pick abcd1234
``` ```

View File

@@ -50,7 +50,7 @@ as world-readable, whereas the "uploaded files" one is not.
With Zulip 1.9.0 and newer, you can do this automatically with the With Zulip 1.9.0 and newer, you can do this automatically with the
following commands run as root: following commands run as root:
``` ```bash
crudini --set /etc/zulip/zulip.conf application_server no_serve_uploads true crudini --set /etc/zulip/zulip.conf application_server no_serve_uploads true
/home/zulip/deployments/current/scripts/zulip-puppet-apply /home/zulip/deployments/current/scripts/zulip-puppet-apply
``` ```
@@ -83,7 +83,7 @@ each of the two buckets, you'll want to
[add an S3 bucket policy](https://awspolicygen.s3.amazonaws.com/policygen.html) [add an S3 bucket policy](https://awspolicygen.s3.amazonaws.com/policygen.html)
entry that looks something like this: entry that looks something like this:
``` ```json
{ {
"Version": "2012-10-17", "Version": "2012-10-17",
"Id": "Policy1468991802321", "Id": "Policy1468991802321",
@@ -117,7 +117,7 @@ entry that looks something like this:
The avatars bucket is intended to be world-readable, so you'll also The avatars bucket is intended to be world-readable, so you'll also
need a block like this: need a block like this:
``` ```json
{ {
"Sid": "Stmt1468991795389", "Sid": "Stmt1468991795389",
"Effect": "Allow", "Effect": "Allow",

View File

@@ -132,7 +132,7 @@ you configure some code to run every time Django does something (for
There's a handful of lines in `zerver/models.py` like these that There's a handful of lines in `zerver/models.py` like these that
configure this: configure this:
``` ```python
post_save.connect(flush_realm, sender=Realm) post_save.connect(flush_realm, sender=Realm)
post_save.connect(flush_user_profile, sender=UserProfile) post_save.connect(flush_user_profile, sender=UserProfile)
``` ```

View File

@@ -90,7 +90,7 @@ following keys in `zproject/dev-secrets.conf`
Here is an example of how `zproject/dev-secrets.conf` might look if Here is an example of how `zproject/dev-secrets.conf` might look if
you are using Gmail. you are using Gmail.
``` ```ini
email_host = smtp.gmail.com email_host = smtp.gmail.com
email_port = 587 email_port = 587
email_host_user = username@gmail.com email_host_user = username@gmail.com

View File

@@ -233,11 +233,13 @@ ready to write a test in `test_events.py`.
The actual code for a `test_events` test can be quite concise: The actual code for a `test_events` test can be quite concise:
def test_default_streams_events(self) -> None: ```python
stream = get_stream("Scotland", self.user_profile.realm) def test_default_streams_events(self) -> None:
events = self.verify_action(lambda: do_add_default_stream(stream)) stream = get_stream("Scotland", self.user_profile.realm)
check_default_streams("events[0]", events[0]) events = self.verify_action(lambda: do_add_default_stream(stream))
# (some details omitted) check_default_streams("events[0]", events[0])
# (some details omitted)
```
The real trick is debugging these tests. The real trick is debugging these tests.
@@ -292,23 +294,25 @@ only has one required parameter, which is the action function. We
typically express the action function as a lambda, so that we typically express the action function as a lambda, so that we
can pass in arguments: can pass in arguments:
events = self.verify_action(lambda: do_add_default_stream(stream)) ```python
events = self.verify_action(lambda: do_add_default_stream(stream))
```
There are some notable optional parameters for `verify_action`: There are some notable optional parameters for `verify_action`:
* `state_change_expected` must be set to `False` if your action * `state_change_expected` must be set to `False` if your action
doesn't actually require state changes for some reason; otherwise, doesn't actually require state changes for some reason; otherwise,
`verify_action` will complain that your test doesn't really `verify_action` will complain that your test doesn't really
exercise any `apply_events` logic. Typing notifications (which exercise any `apply_events` logic. Typing notifications (which
are ephemereal) are a common place where we use this. are ephemereal) are a common place where we use this.
* `num_events` will tell `verify_action` how many events the * `num_events` will tell `verify_action` how many events the
`hamlet` user will receive after the action (the default is 1). `hamlet` user will receive after the action (the default is 1).
* parameters such as `client_gravatar` and `slim_presence` get * parameters such as `client_gravatar` and `slim_presence` get
passed along to `fetch_initial_state_data` (and it's important passed along to `fetch_initial_state_data` (and it's important
to test both boolean values of these parameters for relevant to test both boolean values of these parameters for relevant
actions). actions).
For advanced use cases of `verify_action`, we highly recommend reading For advanced use cases of `verify_action`, we highly recommend reading
the code itself in `BaseAction` (in `test_events.py`). the code itself in `BaseAction` (in `test_events.py`).
@@ -327,9 +331,11 @@ The second is higher-detail check inside `test_events` that this
specific test generated the expected series of events. Let's look at specific test generated the expected series of events. Let's look at
the last line of our example test snippet: the last line of our example test snippet:
# ... ```python
events = self.verify_action(lambda: do_add_default_stream(stream)) # ...
check_default_streams("events[0]", events[0]) events = self.verify_action(lambda: do_add_default_stream(stream))
check_default_streams("events[0]", events[0])
```
We have discussed `verify_action` in some detail, and you will We have discussed `verify_action` in some detail, and you will
note that it returns the actual events generated by the action note that it returns the actual events generated by the action
@@ -346,13 +352,15 @@ If you are creating a new event format, then you will have to
write your own schema checker in `event_schema.py`. Here is write your own schema checker in `event_schema.py`. Here is
the example relevant to our example: the example relevant to our example:
default_streams_event = event_dict_type( ```python
required_keys=[ default_streams_event = event_dict_type(
("type", Equals("default_streams")), required_keys=[
("default_streams", ListType(DictType(basic_stream_fields))), ("type", Equals("default_streams")),
] ("default_streams", ListType(DictType(basic_stream_fields))),
) ]
check_default_streams = make_checker(default_streams_event) )
check_default_streams = make_checker(default_streams_event)
```
Note that `basic_stream_fields` is not shown in these docs. The Note that `basic_stream_fields` is not shown in these docs. The
best way to understand how to write schema checkers is to read best way to understand how to write schema checkers is to read

View File

@@ -41,33 +41,44 @@ All steps in this section should be run as the `root` user; on most installs, th
1. Alter the deployment setting: 1. Alter the deployment setting:
crudini --set /etc/zulip/zulip.conf machine pgroonga enabled ```bash
crudini --set /etc/zulip/zulip.conf machine pgroonga enabled
```
1. Update the deployment to respect that new setting: 1. Update the deployment to respect that new setting:
/home/zulip/deployments/current/scripts/zulip-puppet-apply ```bash
/home/zulip/deployments/current/scripts/zulip-puppet-apply
```
1. Edit `/etc/zulip/settings.py`, to add: 1. Edit `/etc/zulip/settings.py`, to add:
USING_PGROONGA = True ```python
USING_PGROONGA = True
```
1. Apply the PGroonga migrations: 1. Apply the PGroonga migrations:
su zulip -c '/home/zulip/deployments/current/manage.py migrate pgroonga' ```bash
su zulip -c '/home/zulip/deployments/current/manage.py migrate pgroonga'
```
Note that the migration may take a long time, and users will be Note that the migration may take a long time, and users will be
unable to send new messages until the migration finishes. unable to send new messages until the migration finishes.
1. Once the migrations are complete, restart Zulip: 1. Once the migrations are complete, restart Zulip:
su zulip -c '/home/zulip/deployments/current/scripts/restart-server' ```bash
su zulip -c '/home/zulip/deployments/current/scripts/restart-server'
```
### Disabling PGroonga ### Disabling PGroonga
1. Remove the PGroonga migration: 1. Remove the PGroonga migration:
su zulip -c '/home/zulip/deployments/current/manage.py migrate pgroonga zero' ```bash
su zulip -c '/home/zulip/deployments/current/manage.py migrate pgroonga zero'
```
If you intend to re-enable PGroonga later, you can skip this step, If you intend to re-enable PGroonga later, you can skip this step,
at the cost of your Message table being slightly larger than it would at the cost of your Message table being slightly larger than it would
@@ -75,12 +86,18 @@ All steps in this section should be run as the `root` user; on most installs, th
1. Edit `/etc/zulip/settings.py`, editing the line containing `USING_PGROONGA` to read: 1. Edit `/etc/zulip/settings.py`, editing the line containing `USING_PGROONGA` to read:
USING_PGROONGA = False ```python
USING_PGROONGA = False
```
1. Restart Zulip: 1. Restart Zulip:
su zulip -c '/home/zulip/deployments/current/scripts/restart-server' ```bash
su zulip -c '/home/zulip/deployments/current/scripts/restart-server'
```
1. Finally, remove the deployment setting: 1. Finally, remove the deployment setting:
crudini --del /etc/zulip/zulip.conf machine pgroonga ```bash
crudini --del /etc/zulip/zulip.conf machine pgroonga
```

View File

@@ -17,7 +17,7 @@ In `zerver/lib/hotspots.py`, add your content to the `ALL_HOTSPOTS` dictionary.
Each key-value pair in `ALL_HOTSPOTS` associates the name of the hotspot with the Each key-value pair in `ALL_HOTSPOTS` associates the name of the hotspot with the
content displayed to the user. content displayed to the user.
``` ```python
ALL_HOTSPOTS = { ALL_HOTSPOTS = {
... ...
'new_hotspot_name': { 'new_hotspot_name': {
@@ -67,8 +67,8 @@ a target element on a sidebar or overlay, the icon's z-index may need to
be increased to 101, 102, or 103. be increased to 101, 102, or 103.
This adjustment can be made at the bottom of `static/styles/hotspots.css`: This adjustment can be made at the bottom of `static/styles/hotspots.css`:
``` ```css
\#hotspot_new_hotspot_name_icon { #hotspot_new_hotspot_name_icon {
z-index: 103; z-index: 103;
} }
``` ```

View File

@@ -91,7 +91,7 @@ the default context available to all Jinja2 templates.
renders the template. For example, if you want to find the context renders the template. For example, if you want to find the context
passed to `index.html`, you can do: passed to `index.html`, you can do:
``` ```console
$ git grep zerver/app/index.html '*.py' $ git grep zerver/app/index.html '*.py'
zerver/views/home.py: response = render(request, 'zerver/app/index.html', zerver/views/home.py: response = render(request, 'zerver/app/index.html',
``` ```
@@ -101,7 +101,7 @@ The next line in the code being the context definition.
* `zproject/urls.py` for some fairly static pages that are rendered * `zproject/urls.py` for some fairly static pages that are rendered
using `TemplateView`, for example: using `TemplateView`, for example:
``` ```python
path('config-error/google', TemplateView.as_view( path('config-error/google', TemplateView.as_view(
template_name='zerver/config_error.html',), template_name='zerver/config_error.html',),
{'google_error': True},), {'google_error': True},),

View File

@@ -72,7 +72,7 @@ In production, one usually wants to look at `errors.log` for errors
since the main server log can be very verbose, but the main server log since the main server log can be very verbose, but the main server log
can be extremely valuable for investigating performance problems. can be extremely valuable for investigating performance problems.
``` ```text
2016-05-20 14:50:22.056 INFO [zr] 127.0.0.1 GET 302 528ms (db: 1ms/1q) (+start: 123ms) / (unauth@zulip via ?) 2016-05-20 14:50:22.056 INFO [zr] 127.0.0.1 GET 302 528ms (db: 1ms/1q) (+start: 123ms) / (unauth@zulip via ?)
[20/May/2016 14:50:22]"GET / HTTP/1.0" 302 0 [20/May/2016 14:50:22]"GET / HTTP/1.0" 302 0
2016-05-20 14:50:22.272 INFO [zr] 127.0.0.1 GET 200 124ms (db: 3ms/2q) /login/ (unauth@zulip via ?) 2016-05-20 14:50:22.272 INFO [zr] 127.0.0.1 GET 200 124ms (db: 3ms/2q) /login/ (unauth@zulip via ?)

View File

@@ -75,14 +75,14 @@ processor's code path, but it isn't always possible.
If you need to clear a queue (delete all the events in it), run If you need to clear a queue (delete all the events in it), run
`./manage.py purge_queue <queue_name>`, for example: `./manage.py purge_queue <queue_name>`, for example:
``` ```bash
./manage.py purge_queue user_activity ./manage.py purge_queue user_activity
``` ```
You can also use the amqp tools directly. Install `amqp-tools` from You can also use the amqp tools directly. Install `amqp-tools` from
apt and then run: apt and then run:
``` ```bash
amqp-delete-queue --username=zulip --password='...' --server=localhost \ amqp-delete-queue --username=zulip --password='...' --server=localhost \
--queue=user_presence --queue=user_presence
``` ```

View File

@@ -25,7 +25,7 @@ There are two main methods for creating realms.
#### Using unique link generator #### Using unique link generator
```bash ```bash
./manage.py generate_realm_creation_link ./manage.py generate_realm_creation_link
``` ```
The above command will output a URL which can be used for creating a The above command will output a URL which can be used for creating a
@@ -80,7 +80,7 @@ lookup should still work even if you disable proxy for
*.zulipdev.com. If it doesn't you can add zulipdev.com records in *.zulipdev.com. If it doesn't you can add zulipdev.com records in
`/etc/hosts` file. The file should look something like this. `/etc/hosts` file. The file should look something like this.
``` ```text
127.0.0.1 localhost 127.0.0.1 localhost
127.0.0.1 zulipdev.com 127.0.0.1 zulipdev.com

View File

@@ -93,7 +93,7 @@ migrations.
Another important note is that making changes to the data in a table Another important note is that making changes to the data in a table
via `RunPython` code and `ALTER TABLE` operations within a single, via `RunPython` code and `ALTER TABLE` operations within a single,
atomic migration don't mix well. If you encounter an error such as atomic migration don't mix well. If you encounter an error such as
``` ```text
django.db.utils.OperationalError: cannot ALTER TABLE "table_name" because it has pending trigger events django.db.utils.OperationalError: cannot ALTER TABLE "table_name" because it has pending trigger events
``` ```
when testing the migration, the reason is often that these operations when testing the migration, the reason is often that these operations

View File

@@ -30,7 +30,7 @@ means that the settings files are Python programs that set a lot of
variables with all-capital names like `EMAIL_GATEWAY_PATTERN`. You can variables with all-capital names like `EMAIL_GATEWAY_PATTERN`. You can
access these anywhere in the Zulip Django code using e.g.: access these anywhere in the Zulip Django code using e.g.:
``` ```python
from django.conf import settings from django.conf import settings
print(settings.EMAIL_GATEWAY_PATTERN) print(settings.EMAIL_GATEWAY_PATTERN)
``` ```
@@ -38,7 +38,7 @@ print(settings.EMAIL_GATEWAY_PATTERN)
Additionally, if you need to access a Django setting in a shell Additionally, if you need to access a Django setting in a shell
script (or just on the command line for debugging), you can use e.g.: script (or just on the command line for debugging), you can use e.g.:
``` ```console
$ ./scripts/get-django-setting EMAIL_GATEWAY_PATTERN $ ./scripts/get-django-setting EMAIL_GATEWAY_PATTERN
%s@localhost:9991 %s@localhost:9991
``` ```

View File

@@ -12,7 +12,7 @@ state grouped by relevant conversation keys. This data is included in the
`unread_msgs` key if both `update_message_flags` and `message` are required `unread_msgs` key if both `update_message_flags` and `message` are required
in the register call. in the register call.
``` ```json
{ {
"count": 4, "count": 4,
"huddles": [ "huddles": [

View File

@@ -37,9 +37,11 @@ and exempting legacy files from lint checks.
If you run `./tools/test-all`, it will automatically run the linters. If you run `./tools/test-all`, it will automatically run the linters.
You can also run them individually or pass specific files: You can also run them individually or pass specific files:
./tools/lint ```bash
./tools/lint static/js/compose.js ./tools/lint
./tools/lint static/js/ ./tools/lint static/js/compose.js
./tools/lint static/js/
```
`./tools/lint` has many useful options; you can read about them in its `./tools/lint` has many useful options; you can read about them in its
internal documentation using `./tools/lint --help`. Of particular internal documentation using `./tools/lint --help`. Of particular

View File

@@ -48,7 +48,9 @@ requirements/mypy.txt`.
To run mypy on Zulip's python code, you can run the command: To run mypy on Zulip's python code, you can run the command:
tools/run-mypy ```bash
tools/run-mypy
```
Mypy outputs errors in the same style as a compiler would. For Mypy outputs errors in the same style as a compiler would. For
example, if your code has a type error like this: example, if your code has a type error like this:
@@ -60,7 +62,7 @@ foo = '1'
you'll get an error like this: you'll get an error like this:
``` ```console
test.py: note: In function "test": test.py: note: In function "test":
test.py:200: error: Incompatible types in assignment (expression has type "str", variable has type "int") test.py:200: error: Incompatible types in assignment (expression has type "str", variable has type "int")
``` ```
@@ -507,7 +509,7 @@ have untracked files in your Zulip checkout safely). So if you get a
`mypy` error like this after adding a new file that is referenced by `mypy` error like this after adding a new file that is referenced by
the existing codebase: the existing codebase:
``` ```console
mypy | zerver/models.py:1234: note: Import of 'zerver.lib.markdown_wrappers' ignored mypy | zerver/models.py:1234: note: Import of 'zerver.lib.markdown_wrappers' ignored
mypy | zerver/models.py:1234: note: (Using --follow-imports=error, module not passed on command line) mypy | zerver/models.py:1234: note: (Using --follow-imports=error, module not passed on command line)
``` ```

View File

@@ -30,14 +30,18 @@ on a fast machine. When you are in iterative mode, you can run
individual tests or individual modules, following the dotted.test.name individual tests or individual modules, following the dotted.test.name
convention below: convention below:
cd /srv/zulip ```bash
./tools/test-backend zerver.tests.test_queue_worker.WorkerTest cd /srv/zulip
./tools/test-backend zerver.tests.test_queue_worker.WorkerTest
```
There are many command line options for running Zulip tests, such There are many command line options for running Zulip tests, such
as a `--verbose` option. The as a `--verbose` option. The
best way to learn the options is to use the online help: best way to learn the options is to use the online help:
./tools/test-backend --help ```bash
./tools/test-backend --help
```
We also have ways to instrument our tests for finding code coverage, We also have ways to instrument our tests for finding code coverage,
URL coverage, and slow tests. Use the `-h` option to discover these URL coverage, and slow tests. Use the `-h` option to discover these
@@ -172,24 +176,28 @@ analyzed.
Say you have a module `greetings` defining the following functions: Say you have a module `greetings` defining the following functions:
def fetch_database(key: str) -> str: ```python
# ... def fetch_database(key: str) -> str:
# Do some look-ups in a database # ...
return data # Do some look-ups in a database
return data
def greet(name_key: str) -> str: def greet(name_key: str) -> str:
name = fetch_database(name_key) name = fetch_database(name_key)
return "Hello" + name return "Hello" + name
```
* You want to test `greet()`. * You want to test `greet()`.
* In your test, you want to call `greet("Mario")` and verify that it returns the correct greeting: * In your test, you want to call `greet("Mario")` and verify that it returns the correct greeting:
from greetings import greet ```python
from greetings import greet
def test_greet() -> str: def test_greet() -> str:
greeting = greet("Mario") greeting = greet("Mario")
assert greeting == "Hello Mr. Mario Mario" assert greeting == "Hello Mr. Mario Mario"
```
-> **You have a problem**: `greet()` calls `fetch_database()`. `fetch_database()` does some look-ups in -> **You have a problem**: `greet()` calls `fetch_database()`. `fetch_database()` does some look-ups in
a database. *You haven't created that database for your tests, so your test would fail, even though a database. *You haven't created that database for your tests, so your test would fail, even though
@@ -202,15 +210,17 @@ Say you have a module `greetings` defining the following functions:
-> **Solution**: You mock `fetch_database()`. This is also referred to as "mocking out" `fetch_database()`. -> **Solution**: You mock `fetch_database()`. This is also referred to as "mocking out" `fetch_database()`.
from unittest.mock import patch ```python
from unittest.mock import patch
def test_greet() -> None: def test_greet() -> None:
# Mock `fetch_database()` with an object that acts like a shell: It still accepts calls like `fetch_database()`, # Mock `fetch_database()` with an object that acts like a shell: It still accepts calls like `fetch_database()`,
# but doesn't do any database lookup. We "fill" the shell with a return value; This value will be returned on every # but doesn't do any database lookup. We "fill" the shell with a return value; This value will be returned on every
# call to `fetch_database()`. # call to `fetch_database()`.
with patch("greetings.fetch_database", return_value="Mr. Mario Mario"): with patch("greetings.fetch_database", return_value="Mr. Mario Mario"):
greeting = greetings.greet("Mario") greeting = greetings.greet("Mario")
assert greeting == "Hello Mr. Mario Mario" assert greeting == "Hello Mr. Mario Mario"
```
That's all. Note that **this mock is suitable for testing `greet()`, but not for testing `fetch_database()`**. That's all. Note that **this mock is suitable for testing `greet()`, but not for testing `fetch_database()`**.
More generally, you should only mock those functions you explicitly don't want to test. More generally, you should only mock those functions you explicitly don't want to test.
@@ -226,17 +236,21 @@ those are the ones starting with with a dunder `__`). From the docs:
`Mock` itself is a class that principally accepts and records any and all calls. A piece of code like `Mock` itself is a class that principally accepts and records any and all calls. A piece of code like
from unittest import mock ```python
from unittest import mock
foo = mock.Mock() foo = mock.Mock()
foo.bar('quux') foo.bar('quux')
foo.baz foo.baz
foo.qux = 42 foo.qux = 42
```
is *not* going to throw any errors. Our mock silently accepts all these calls and records them. is *not* going to throw any errors. Our mock silently accepts all these calls and records them.
`Mock` also implements methods for us to access and assert its records, e.g. `Mock` also implements methods for us to access and assert its records, e.g.
foo.bar.assert_called_with('quux') ```python
foo.bar.assert_called_with('quux')
```
Finally, `unittest.mock` also provides a method to mock objects only within a scope: `patch()`. We can use `patch()` either Finally, `unittest.mock` also provides a method to mock objects only within a scope: `patch()`. We can use `patch()` either
as a decorator or as a context manager. In both cases, the mock created by `patch()` will apply for the scope of the decorator / as a decorator or as a context manager. In both cases, the mock created by `patch()` will apply for the scope of the decorator /
@@ -244,13 +258,15 @@ context manager. `patch()` takes only one required argument `target`. `target` i
the name of the object you want to mock*. It will then assign a `MagicMock()` to that object. the name of the object you want to mock*. It will then assign a `MagicMock()` to that object.
As an example, look at the following code: As an example, look at the following code:
from unittest import mock ```python
from os import urandom from unittest import mock
from os import urandom
with mock.patch('__main__.urandom', return_value=42): with mock.patch('__main__.urandom', return_value=42):
print(urandom(1)) print(urandom(1))
print(urandom(1)) # No matter what value we plug in for urandom, it will always return 42. print(urandom(1)) # No matter what value we plug in for urandom, it will always return 42.
print(urandom(1)) # We exited the context manager, so the mock doesn't apply anymore. Will return a random byte. print(urandom(1)) # We exited the context manager, so the mock doesn't apply anymore. Will return a random byte.
```
*Note that calling `mock.patch('os.urandom', return_value=42)` wouldn't work here*: `os.urandom` would be the name of our patched *Note that calling `mock.patch('os.urandom', return_value=42)` wouldn't work here*: `os.urandom` would be the name of our patched
object. However, we imported `urandom` with `from os import urandom`; hence, we bound the `urandom` name to our current module object. However, we imported `urandom` with `from os import urandom`; hence, we bound the `urandom` name to our current module
@@ -262,27 +278,35 @@ On the other hand, if we had used `import os.urandom`, we would need to call `mo
* Including the Python mocking library: * Including the Python mocking library:
from unittest import mock ```python
from unittest import mock
```
* Mocking a class with a context manager: * Mocking a class with a context manager:
with mock.patch('module.ClassName', foo=42, return_value='I am a mock') as my_mock: ```python
# In here, 'module.ClassName' is mocked with a MagicMock() object my_mock. with mock.patch('module.ClassName', foo=42, return_value='I am a mock') as my_mock:
# my_mock has an attribute named foo with the value 42. # In here, 'module.ClassName' is mocked with a MagicMock() object my_mock.
# var = module.ClassName() will assign 'I am a mock' to var. # my_mock has an attribute named foo with the value 42.
# var = module.ClassName() will assign 'I am a mock' to var.
```
* Mocking a class with a decorator: * Mocking a class with a decorator:
@mock.patch('module.ClassName', foo=42, return_value='I am a mock') ```python
def my_function(my_mock): @mock.patch('module.ClassName', foo=42, return_value='I am a mock')
# ... def my_function(my_mock):
# In here, 'module.ClassName' will behave as in the previous example. # ...
# In here, 'module.ClassName' will behave as in the previous example.
```
* Mocking a class attribute: * Mocking a class attribute:
with mock.patch.object(module.ClassName, 'class_method', return_value=42) ```python
# In here, 'module.ClassName' has the same properties as before, except for 'class_method' with mock.patch.object(module.ClassName, 'class_method', return_value=42)
# Calling module.ClassName.class_method() will now return 42. # In here, 'module.ClassName' has the same properties as before, except for 'class_method'
# Calling module.ClassName.class_method() will now return 42.
```
Note the missing quotes around module.ClassName in the patch.object() call. Note the missing quotes around module.ClassName in the patch.object() call.
@@ -292,11 +316,13 @@ For mocking we generally use the "mock" library and use `mock.patch` as
a context manager or decorator. We also take advantage of some context managers a context manager or decorator. We also take advantage of some context managers
from Django as well as our own custom helpers. Here is an example: from Django as well as our own custom helpers. Here is an example:
with self.settings(RATE_LIMITING=True): ```python
with mock.patch('zerver.decorator.rate_limit_user') as rate_limit_mock: with self.settings(RATE_LIMITING=True):
api_result = my_webhook(request) with mock.patch('zerver.decorator.rate_limit_user') as rate_limit_mock:
api_result = my_webhook(request)
self.assertTrue(rate_limit_mock.called) self.assertTrue(rate_limit_mock.called)
```
Follow [this link](../subsystems/settings.html#testing-non-default-settings) for more Follow [this link](../subsystems/settings.html#testing-non-default-settings) for more
information on the "settings" context manager. information on the "settings" context manager.

View File

@@ -7,8 +7,8 @@ system since it is much (>100x) faster and also easier to do correctly
than the Puppeteer system. than the Puppeteer system.
You can run this test suite as follows: You can run this test suite as follows:
``` ```bash
tools/test-js-with-node tools/test-js-with-node
``` ```
See `test-js-with-node --help` for useful options; even though the See `test-js-with-node --help` for useful options; even though the
@@ -19,7 +19,7 @@ The JS unit tests are written to work with node. You can find them
in `frontend_tests/node_tests`. Here is an example test from in `frontend_tests/node_tests`. Here is an example test from
`frontend_tests/node_tests/stream_data.js`: `frontend_tests/node_tests/stream_data.js`:
``` ```js
(function test_get_by_id() { (function test_get_by_id() {
stream_data.clear_subscriptions(); stream_data.clear_subscriptions();
var id = 42; var id = 42;
@@ -109,19 +109,23 @@ different types of declarations depending on whether we want to:
For all the modules where you want to run actual code, add statements For all the modules where you want to run actual code, add statements
like the following toward the top of your test file: like the following toward the top of your test file:
> zrequire('util'); ```js
> zrequire('stream_data'); zrequire('util');
> zrequire('Filter', 'js/filter'); zrequire('stream_data');
zrequire('Filter', 'js/filter');
```
For modules that you want to completely stub out, use a pattern like For modules that you want to completely stub out, use a pattern like
this: this:
> const reminder = mock_esm("../../static/js/reminder", { ```js
> is_deferred_delivery: noop, const reminder = mock_esm("../../static/js/reminder", {
> }); is_deferred_delivery: noop,
> });
> // then maybe further down
> reminder.is_deferred_delivery = () => true; // then maybe further down
reminder.is_deferred_delivery = () => true;
```
One can similarly stub out functions in a module's exported interface One can similarly stub out functions in a module's exported interface
with either `noop` functions or actual code. with either `noop` functions or actual code.
@@ -132,13 +136,15 @@ this is a pretty strong code smell that the other module might be
lacking in cohesion, but sometimes it's not worth going down the lacking in cohesion, but sometimes it's not worth going down the
rabbit hole of trying to improve that. The pattern here is this: rabbit hole of trying to improve that. The pattern here is this:
> // Import real code. ```js
> zrequire('narrow_state'); // Import real code.
> zrequire('narrow_state');
> // And later...
> narrow_state.stream = function () { // And later...
> return 'office'; narrow_state.stream = function () {
> }; return 'office';
};
```
## Creating new test modules ## Creating new test modules
@@ -151,8 +157,8 @@ in that directory to create a new test.
You can automatically generate coverage reports for the JavaScript unit You can automatically generate coverage reports for the JavaScript unit
tests like this: tests like this:
``` ```bash
tools/test-js-with-node --coverage tools/test-js-with-node --coverage
``` ```
If tests pass, you will get instructions to view coverage reports If tests pass, you will get instructions to view coverage reports

View File

@@ -10,8 +10,8 @@ keyboard shortcuts, etc.).
## Running tests ## Running tests
You can run this test suite as follows: You can run this test suite as follows:
``` ```bash
tools/test-js-with-puppeteer tools/test-js-with-puppeteer
``` ```
See `tools/test-js-with-puppeteer --help` for useful options, See `tools/test-js-with-puppeteer --help` for useful options,
@@ -34,7 +34,7 @@ appears/disappears", or "Click on this HTML element".
For example, this function might test the `x` keyboard shortcut to For example, this function might test the `x` keyboard shortcut to
open the compose box for a new private message: open the compose box for a new private message:
``` ```js
async function test_private_message_compose_shortcut(page) { async function test_private_message_compose_shortcut(page) {
await page.keyboard.press("KeyX"); await page.keyboard.press("KeyX");
await page.waitForSelector("#private_message_recipient", {visible: true}); await page.waitForSelector("#private_message_recipient", {visible: true});

View File

@@ -23,7 +23,7 @@ you're using Vagrant, you may need to enter it with `vagrant ssh`.
You can run all of the test suites (similar to our continuous integration) You can run all of the test suites (similar to our continuous integration)
as follows: as follows:
``` ```bash
./tools/test-all ./tools/test-all
``` ```
@@ -31,7 +31,7 @@ However, you will rarely want to do this while actively developing,
because it takes a long time. Instead, your edit/refresh cycle will because it takes a long time. Instead, your edit/refresh cycle will
typically involve running subsets of the tests with commands like these: typically involve running subsets of the tests with commands like these:
``` ```bash
./tools/lint zerver/lib/actions.py # Lint the file you just changed ./tools/lint zerver/lib/actions.py # Lint the file you just changed
./tools/test-backend zerver.tests.test_markdown.MarkdownTest.test_inline_youtube ./tools/test-backend zerver.tests.test_markdown.MarkdownTest.test_inline_youtube
./tools/test-backend MarkdownTest # Run `test-backend --help` for more options ./tools/test-backend MarkdownTest # Run `test-backend --help` for more options
@@ -140,7 +140,7 @@ depending on Internet access.
This enforcement code results in the following exception: This enforcement code results in the following exception:
``` ```pytb
File "tools/test-backend", line 120, in internet_guard File "tools/test-backend", line 120, in internet_guard
raise Exception("Outgoing network requests are not allowed in the Zulip tests." raise Exception("Outgoing network requests are not allowed in the Zulip tests."
Exception: Outgoing network requests are not allowed in the Zulip tests. Exception: Outgoing network requests are not allowed in the Zulip tests.

View File

@@ -139,7 +139,7 @@ template so that it can be translated.
To mark a string for translation in a Jinja2 template, you To mark a string for translation in a Jinja2 template, you
can use the `_()` function in the templates like this: can use the `_()` function in the templates like this:
``` ```jinja
{{ _("English text") }} {{ _("English text") }}
``` ```
@@ -149,14 +149,14 @@ help translators to translate an entire sentence. To translate a
block, Jinja2 uses the [trans][] tag. So rather than writing block, Jinja2 uses the [trans][] tag. So rather than writing
something ugly and confusing for translators like this: something ugly and confusing for translators like this:
``` ```jinja
# Don't do this! # Don't do this!
{{ _("This string will have") }} {{ value }} {{ _("inside") }} {{ _("This string will have") }} {{ value }} {{ _("inside") }}
``` ```
You can instead use: You can instead use:
``` ```jinja
{% trans %}This string will have {{ value }} inside.{% endtrans %} {% trans %}This string will have {{ value }} inside.{% endtrans %}
``` ```
@@ -165,7 +165,7 @@ You can instead use:
A string in Python can be marked for translation using the `_()` function, A string in Python can be marked for translation using the `_()` function,
which can be imported as follows: which can be imported as follows:
``` ```python
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
``` ```
@@ -174,7 +174,7 @@ ensure this, the error message passed to `json_error` and
`JsonableError` should always be a literal string enclosed by `_()` `JsonableError` should always be a literal string enclosed by `_()`
function, e.g.: function, e.g.:
``` ```python
json_error(_('English text')) json_error(_('English text'))
JsonableError(_('English text')) JsonableError(_('English text'))
``` ```
@@ -257,13 +257,13 @@ $t_html(
For translations in Handlebars templates we also use FormatJS, through two For translations in Handlebars templates we also use FormatJS, through two
Handlebars [helpers][] that Zulip registers. The syntax for simple strings is: Handlebars [helpers][] that Zulip registers. The syntax for simple strings is:
``` ```html+handlebars
{{t 'English text' }} {{t 'English text' }}
``` ```
If you are passing a translated string to a Handlebars partial, you can use: If you are passing a translated string to a Handlebars partial, you can use:
``` ```html+handlebars
{{> template_name {{> template_name
variable_name=(t 'English text') variable_name=(t 'English text')
}} }}
@@ -271,7 +271,8 @@ If you are passing a translated string to a Handlebars partial, you can use:
The syntax for block strings or strings containing variables is: The syntax for block strings or strings containing variables is:
``` <!-- The html+handlebars lexer fails to lex the single braces. -->
```text
{{#tr}} {{#tr}}
Block of English text. Block of English text.
{{/tr}} {{/tr}}
@@ -298,7 +299,7 @@ Restrictions on including HTML tags in translated strings are the same
as in JavaScript. You can insert more complex markup using a local as in JavaScript. You can insert more complex markup using a local
custom HTML tag like this: custom HTML tag like this:
``` ```html+handlebars
{{#tr}} {{#tr}}
<b>HTML</b> linking to the <z-link>login page</z-link> <b>HTML</b> linking to the <z-link>login page</z-link>
{{#*inline "z-link"}}<a href="/login/">{{> @partial-block}}</a>{{/inline}} {{#*inline "z-link"}}<a href="/login/">{{> @partial-block}}</a>{{/inline}}
@@ -319,7 +320,7 @@ file, located at `~/.transifexrc`.
You can find details on how to set it up [here][transifexrc], but it should You can find details on how to set it up [here][transifexrc], but it should
look similar to this (with your credentials): look similar to this (with your credentials):
``` ```ini
[https://www.transifex.com] [https://www.transifex.com]
username = user username = user
token = token =

View File

@@ -143,7 +143,7 @@ There are a few ways to see your translations in the Zulip UI:
you can pass the `Accept-Language` header; here is some sample code to you can pass the `Accept-Language` header; here is some sample code to
test `Accept-Language` header using Python and `requests`: test `Accept-Language` header using Python and `requests`:
``` ```python
import requests import requests
headers = {"Accept-Language": "de"} headers = {"Accept-Language": "de"}
response = requests.get("http://localhost:9991/login/", headers=headers) response = requests.get("http://localhost:9991/login/", headers=headers)

View File

@@ -29,7 +29,7 @@ File not found errors (404) are served using a Django URL, so that we
can use configuration variables (like whether the user is logged in) can use configuration variables (like whether the user is logged in)
in the 404 error page. in the 404 error page.
``` ```nginx
location /static/ { location /static/ {
alias /home/zulip/prod-static/; alias /home/zulip/prod-static/;
# Set a nonexistent path, so we just serve the nice Django 404 page. # Set a nonexistent path, so we just serve the nice Django 404 page.
@@ -61,10 +61,7 @@ in
[the directory structure doc](../overview/directory-structure.md). [the directory structure doc](../overview/directory-structure.md).
The main Zulip Django app is `zerver`. The routes are found in The main Zulip Django app is `zerver`. The routes are found in
``` `zproject/urls.py` and `zproject/legacy_urls.py`.
zproject/urls.py
zproject/legacy_urls.py
```
There are HTML-serving, REST API, legacy, and webhook url patterns. We There are HTML-serving, REST API, legacy, and webhook url patterns. We
will look at how each of these types of requests are handled, and focus will look at how each of these types of requests are handled, and focus
@@ -140,9 +137,11 @@ yields a response with this HTTP header:
We can see this reflected in [zproject/urls.py](https://github.com/zulip/zulip/blob/master/zproject/urls.py): We can see this reflected in [zproject/urls.py](https://github.com/zulip/zulip/blob/master/zproject/urls.py):
rest_path('users', ```python
GET=get_members_backend, rest_path('users',
PUT=create_user_backend), GET=get_members_backend,
PUT=create_user_backend),
```
In this way, the API is partially self-documenting. In this way, the API is partially self-documenting.
@@ -175,7 +174,7 @@ the request, and then figure out which view to show from that.
In our example, In our example,
``` ```python
GET=get_members_backend, GET=get_members_backend,
PUT=create_user_backend PUT=create_user_backend
``` ```
@@ -195,7 +194,9 @@ This is covered in good detail in the [writing views doc](writing-views.md).
Our API works on JSON requests and responses. Every API endpoint should Our API works on JSON requests and responses. Every API endpoint should
return `json_error` in the case of an error, which gives a JSON string: return `json_error` in the case of an error, which gives a JSON string:
`{'result': 'error', 'msg': <some error message>}` ```json
{"result": "error", "msg": "<some error message>"}
```
in a in a
[HTTP response](https://docs.djangoproject.com/en/1.8/ref/request-response/) [HTTP response](https://docs.djangoproject.com/en/1.8/ref/request-response/)
@@ -203,11 +204,13 @@ with a content type of 'application/json'.
To pass back data from the server to the calling client, in the event of To pass back data from the server to the calling client, in the event of
a successfully handled request, we use a successfully handled request, we use
`json_success(data=<some python object which can be converted to a JSON string>`. `json_success(data=<some python object which can be converted to a JSON string>)`.
This will result in a JSON string: This will result in a JSON string:
`{'result': 'success', 'msg': '', 'data'='{'var_name1': 'var_value1', 'var_name2': 'var_value2'...}` ```json
{"result": "success", "msg": "", "data": {"var_name1": "var_value1", "var_name2": "var_value2"}}
```
with a HTTP 200 status and a content type of 'application/json'. with a HTTP 200 status and a content type of 'application/json'.

View File

@@ -68,7 +68,7 @@ organization in Zulip). The following files are involved in the process:
**Create and run the migration:** To create and apply a migration, run the **Create and run the migration:** To create and apply a migration, run the
following commands: following commands:
``` ```bash
./manage.py makemigrations ./manage.py makemigrations
./manage.py migrate ./manage.py migrate
``` ```
@@ -168,14 +168,13 @@ boolean field, `mandatory_topics`, to the Realm model in
`zerver/models.py`. `zerver/models.py`.
``` diff ``` diff
# zerver/models.py
# zerver/models.py class Realm(models.Model):
# ...
class Realm(models.Model): emails_restricted_to_domains: bool = models.BooleanField(default=True)
# ... invite_required: bool = models.BooleanField(default=False)
emails_restricted_to_domains: bool = models.BooleanField(default=True) + mandatory_topics: bool = models.BooleanField(default=False)
invite_required: bool = models.BooleanField(default=False)
+ mandatory_topics: bool = models.BooleanField(default=False)
``` ```
The Realm model also contains an attribute, `property_types`, which The Realm model also contains an attribute, `property_types`, which
@@ -186,18 +185,17 @@ is the field's type. Add the new field to the `property_types`
dictionary. dictionary.
``` diff ``` diff
# zerver/models.py
# zerver/models.py class Realm(models.Model)
# ...
class Realm(models.Model) # Define the types of the various automatically managed properties
# ... property_types = dict(
# Define the types of the various automatically managed properties add_emoji_by_admins_only=bool,
property_types = dict( allow_edit_history=bool,
add_emoji_by_admins_only=bool, # ...
allow_edit_history=bool, + mandatory_topics=bool,
# ... # ...
+ mandatory_topics=bool,
# ...
``` ```
**The majority of realm settings can be included in **The majority of realm settings can be included in
@@ -231,7 +229,7 @@ is helpful.
Apply the migration using Django's `migrate` command: `./manage.py migrate`. Apply the migration using Django's `migrate` command: `./manage.py migrate`.
Output: Output:
``` ```console
shell $ ./manage.py migrate shell $ ./manage.py migrate
Operations to perform: Operations to perform:
Synchronize unmigrated apps: staticfiles, analytics, pipeline Synchronize unmigrated apps: staticfiles, analytics, pipeline
@@ -291,50 +289,54 @@ argument can be a single user (if the setting is a personal one, like
time display format), members in a particular stream only or all time display format), members in a particular stream only or all
active users in a realm. active users in a realm.
# zerver/lib/actions.py ```python
# zerver/lib/actions.py
def do_set_realm_property( def do_set_realm_property(
realm: Realm, name: str, value: Any, *, acting_user: Optional[UserProfile] realm: Realm, name: str, value: Any, *, acting_user: Optional[UserProfile]
) -> None: ) -> None:
"""Takes in a realm object, the name of an attribute to update, the """Takes in a realm object, the name of an attribute to update, the
value to update and and the user who initiated the update. value to update and and the user who initiated the update.
""" """
property_type = Realm.property_types[name] property_type = Realm.property_types[name]
assert isinstance(value, property_type), ( assert isinstance(value, property_type), (
'Cannot update %s: %s is not an instance of %s' % ( 'Cannot update %s: %s is not an instance of %s' % (
name, value, property_type,)) name, value, property_type,))
setattr(realm, name, value) setattr(realm, name, value)
realm.save(update_fields=[name]) realm.save(update_fields=[name])
event = dict( event = dict(
type='realm', type='realm',
op='update', op='update',
property=name, property=name,
value=value, value=value,
) )
send_event(realm, event, active_user_ids(realm)) send_event(realm, event, active_user_ids(realm))
```
If the new realm property being added does not fit into the If the new realm property being added does not fit into the
`property_types` framework (such as the `authentication_methods` `property_types` framework (such as the `authentication_methods`
field), you'll need to create a new function to explicitly update this field), you'll need to create a new function to explicitly update this
field and send an event. For example: field and send an event. For example:
# zerver/lib/actions.py ```python
# zerver/lib/actions.py
def do_set_realm_authentication_methods( def do_set_realm_authentication_methods(
realm: Realm, authentication_methods: Dict[str, bool], *, acting_user: Optional[UserProfile] realm: Realm, authentication_methods: Dict[str, bool], *, acting_user: Optional[UserProfile]
) -> None: ) -> None:
for key, value in list(authentication_methods.items()): for key, value in list(authentication_methods.items()):
index = getattr(realm.authentication_methods, key).number index = getattr(realm.authentication_methods, key).number
realm.authentication_methods.set_bit(index, int(value)) realm.authentication_methods.set_bit(index, int(value))
realm.save(update_fields=['authentication_methods']) realm.save(update_fields=['authentication_methods'])
event = dict( event = dict(
type="realm", type="realm",
op="update_dict", op="update_dict",
property='default', property='default',
data=dict(authentication_methods=realm.authentication_methods_dict()) data=dict(authentication_methods=realm.authentication_methods_dict())
) )
send_event(realm, event, active_user_ids(realm)) send_event(realm, event, active_user_ids(realm))
```
### Update application state ### Update application state
@@ -351,27 +353,29 @@ apps). The `apply_event` function in `zerver/lib/events.py` is important for
making sure the `state` is always correct, even in the event of rare making sure the `state` is always correct, even in the event of rare
race conditions. race conditions.
# zerver/lib/events.py ```python
# zerver/lib/events.py
def fetch_initial_state_data(user_profile, event_types, queue_id, include_subscribers=True): def fetch_initial_state_data(user_profile, event_types, queue_id, include_subscribers=True):
# ... # ...
if want('realm'): if want('realm'):
for property_name in Realm.property_types: for property_name in Realm.property_types:
state['realm_' + property_name] = getattr(user_profile.realm, property_name) state['realm_' + property_name] = getattr(user_profile.realm, property_name)
state['realm_authentication_methods'] = user_profile.realm.authentication_methods_dict() state['realm_authentication_methods'] = user_profile.realm.authentication_methods_dict()
state['realm_allow_message_editing'] = user_profile.realm.allow_message_editing state['realm_allow_message_editing'] = user_profile.realm.allow_message_editing
# ... # ...
def apply_event def apply_event
user_profile: UserProfile, user_profile: UserProfile,
# ... # ...
) -> None: ) -> None:
for event in events: for event in events:
# ... # ...
elif event['type'] == 'realm': elif event['type'] == 'realm':
field = 'realm_' + event['property'] field = 'realm_' + event['property']
state[field] = event['value'] state[field] = event['value']
# ... # ...
```
If your new realm property fits the `property_types` If your new realm property fits the `property_types`
framework, you don't need to change `fetch_initial_state_data` or framework, you don't need to change `fetch_initial_state_data` or
@@ -380,14 +384,16 @@ property that is handled separately, you will need to explicitly add
the property to the `state` dictionary in the `fetch_initial_state_data` the property to the `state` dictionary in the `fetch_initial_state_data`
function. E.g., for `authentication_methods`: function. E.g., for `authentication_methods`:
# zerver/lib/events.py ```python
# zerver/lib/events.py
def fetch_initial_state_data(user_profile, event_types, queue_id, include_subscribers=True): def fetch_initial_state_data(user_profile, event_types, queue_id, include_subscribers=True):
# ... # ...
if want('realm'): if want('realm'):
# ... # ...
state['realm_authentication_methods'] = user_profile.realm.authentication_methods_dict() state['realm_authentication_methods'] = user_profile.realm.authentication_methods_dict()
# ... # ...
```
For this setting, one won't need to change `apply_event` since its For this setting, one won't need to change `apply_event` since its
default code for `realm` event types handles this case correctly, but default code for `realm` event types handles this case correctly, but
@@ -409,8 +415,7 @@ function in `zerver/views/realm.py` (and add the appropriate mypy type
annotation). annotation).
``` diff ``` diff
# zerver/views/realm.py
# zerver/views/realm.py
def update_realm( def update_realm(
request: HttpRequest, request: HttpRequest,
@@ -430,15 +435,17 @@ to `zerver/views/realm.py`.
Text fields or other realm properties that need additional validation Text fields or other realm properties that need additional validation
can be handled at the beginning of `update_realm`. can be handled at the beginning of `update_realm`.
# zerver/views/realm.py ```python
# zerver/views/realm.py
# Additional validation/error checking beyond types go here, so # Additional validation/error checking beyond types go here, so
# the entire request can succeed or fail atomically. # the entire request can succeed or fail atomically.
if default_language is not None and default_language not in get_available_language_codes(): if default_language is not None and default_language not in get_available_language_codes():
raise JsonableError(_("Invalid language '%s'" % (default_language,))) raise JsonableError(_("Invalid language '%s'" % (default_language,)))
if description is not None and len(description) > 100: if description is not None and len(description) > 100:
return json_error(_("Realm description cannot exceed 100 characters.")) return json_error(_("Realm description cannot exceed 100 characters."))
# ... # ...
```
The code in `update_realm` loops through the `property_types` dictionary The code in `update_realm` loops through the `property_types` dictionary
and calls `do_set_realm_property` on any property to be updated from and calls `do_set_realm_property` on any property to be updated from
@@ -449,20 +456,22 @@ to call the function you wrote in `actions.py` that updates the database
with the new value. E.g., for `authentication_methods`, we created with the new value. E.g., for `authentication_methods`, we created
`do_set_realm_authentication_methods`, which we will call here: `do_set_realm_authentication_methods`, which we will call here:
# zerver/views/realm.py ```python
# zerver/views/realm.py
# import do_set_realm_authentication_methods from actions.py # import do_set_realm_authentication_methods from actions.py
from zerver.lib.actions import ( from zerver.lib.actions import (
do_set_realm_message_editing, do_set_realm_message_editing,
do_set_realm_authentication_methods, do_set_realm_authentication_methods,
# ...
)
# ...
# ...
if authentication_methods is not None and realm.authentication_methods_dict() != authentication_methods:
do_set_realm_authentication_methods(realm, authentication_methods, acting_user=user_profile)
data['authentication_methods'] = authentication_methods
# ... # ...
)
# ...
# ...
if authentication_methods is not None and realm.authentication_methods_dict() != authentication_methods:
do_set_realm_authentication_methods(realm, authentication_methods, acting_user=user_profile)
data['authentication_methods'] = authentication_methods
# ...
```
This completes the backend implementation. A great next step is to This completes the backend implementation. A great next step is to
write automated backend tests for your new feature. write automated backend tests for your new feature.
@@ -508,18 +517,17 @@ template.
Then add the new form control in `static/js/admin.js`. Then add the new form control in `static/js/admin.js`.
``` diff ``` diff
// static/js/admin.js
// static/js/admin.js function _setup_page() {
var options = {
function _setup_page() { realm_name: page_params.realm_name,
var options = { realm_description: page_params.realm_description,
realm_name: page_params.realm_name, realm_emails_restricted_to_domains: page_params.realm_emails_restricted_to_domains,
realm_description: page_params.realm_description, realm_invite_required: page_params.realm_invite_required,
realm_emails_restricted_to_domains: page_params.realm_emails_restricted_to_domains, // ...
realm_invite_required: page_params.realm_invite_required, + realm_mandatory_topics: page_params.mandatory_topics,
// ... // ...
+ realm_mandatory_topics: page_params.mandatory_topics,
// ...
``` ```
The JavaScript code for organization settings and permissions can be found in The JavaScript code for organization settings and permissions can be found in
@@ -582,20 +590,19 @@ setting has changed, your function should be referenced in the
`settings_emoji.update_custom_emoji_ui`. `settings_emoji.update_custom_emoji_ui`.
``` diff ``` diff
// static/js/server_events_dispatch.js
// static/js/server_events_dispatch.js function dispatch_normal_event(event) {
switch (event.type) {
function dispatch_normal_event(event) { // ...
switch (event.type) { case 'realm':
// ... var realm_settings = {
case 'realm': add_emoji_by_admins_only: settings_emoji.update_custom_emoji_ui,
var realm_settings = { allow_edit_history: noop,
add_emoji_by_admins_only: settings_emoji.update_custom_emoji_ui, // ...
allow_edit_history: noop, + mandatory_topics: noop,
// ... // ...
+ mandatory_topics: noop, };
// ...
};
``` ```
Checkboxes and other common input elements handle the UI updates Checkboxes and other common input elements handle the UI updates
@@ -638,12 +645,14 @@ At the minimum, if you created a new function to update UI in
`frontend_tests/node_tests/dispatch.js`. Add the name of the UI `frontend_tests/node_tests/dispatch.js`. Add the name of the UI
function you created to the following object with `noop` as the value: function you created to the following object with `noop` as the value:
# frontend_tests/node_tests/dispatch.js ```js
// frontend_tests/node_tests/dispatch.js
set_global('settings_org', { set_global('settings_org', {
update_email_change_display: noop, update_email_change_display: noop,
update_name_change_display: noop, update_name_change_display: noop,
}); });
```
Beyond that, you should add any applicable tests that verify the Beyond that, you should add any applicable tests that verify the
behavior of the setting you just created. behavior of the setting you just created.

View File

@@ -44,7 +44,7 @@ abbreviation for your home directory (`/home/YOUR_USERNAME` most of the times).
That's why the following is exactly the same, if the user running it is That's why the following is exactly the same, if the user running it is
`john`: `john`:
``` ```console
$ cd ~ $ cd ~
$ cd /home/john $ cd /home/john
``` ```
@@ -58,7 +58,7 @@ directory, instead of writing the whole path.
Imagine you have a file called `ideas.txt` inside `/home/john/notes/`, and Imagine you have a file called `ideas.txt` inside `/home/john/notes/`, and
you want to edit it using `nano`. You could use: you want to edit it using `nano`. You could use:
``` ```console
$ nano /home/john/notes/ideas.txt $ nano /home/john/notes/ideas.txt
``` ```
@@ -69,7 +69,7 @@ That's why it's very useful to change the path where you are currently
located (usually known as **working directory**). To do that, you use `cd` located (usually known as **working directory**). To do that, you use `cd`
(**c**hange **d**irectory): (**c**hange **d**irectory):
``` ```console
$ cd /home/john/notes/ $ cd /home/john/notes/
~/notes$ nano ideas.txt ~/notes$ nano ideas.txt
``` ```
@@ -99,7 +99,7 @@ In case you were wondering, the name `sudo` comes from **s**uper **u**ser
Some characters cannot be used directly in the shell, because they have a Some characters cannot be used directly in the shell, because they have a
special meaning. Consider the following example: special meaning. Consider the following example:
``` ```console
$ echo "He said hello" $ echo "He said hello"
He said hello He said hello
``` ```
@@ -118,7 +118,7 @@ before it.
Returning to our example: Returning to our example:
``` ```console
$ echo "He said \"hello\"" $ echo "He said \"hello\""
He said "hello" He said "hello"
``` ```
@@ -138,7 +138,7 @@ the shell provides two different separators:
- **Semicolon `;`**: runs a command, and once it has finished, runs the next - **Semicolon `;`**: runs a command, and once it has finished, runs the next
one: one:
``` ```console
$ echo "Hello"; echo "World!" $ echo "Hello"; echo "World!"
Hello Hello
World! World!
@@ -147,7 +147,7 @@ the shell provides two different separators:
- **Double ampersand `&&`**: runs a command, and **only if** it finished - **Double ampersand `&&`**: runs a command, and **only if** it finished
without errors, it proceeds with the next one: without errors, it proceeds with the next one:
``` ```console
$ qwfvijwe && echo "Hello" $ qwfvijwe && echo "Hello"
qwfvijwe: command not found qwfvijwe: command not found
``` ```
@@ -158,7 +158,7 @@ the shell provides two different separators:
When using an incorrect command with a semicolon, the `Hello` will still When using an incorrect command with a semicolon, the `Hello` will still
be printed: be printed:
``` ```console
$ qwfvijwe; echo "Hello" $ qwfvijwe; echo "Hello"
qwfvijwe: command not found qwfvijwe: command not found
Hello Hello
@@ -176,7 +176,7 @@ shell "wait, there's more on the next line".
This is an example, taken from the docs on how to install the Zulip development This is an example, taken from the docs on how to install the Zulip development
environment: environment:
``` ```bash
sudo apt-get -y purge vagrant && \ sudo apt-get -y purge vagrant && \
wget https://releases.hashicorp.com/vagrant/2.0.2/vagrant_2.0.2_x86_64.deb && \ wget https://releases.hashicorp.com/vagrant/2.0.2/vagrant_2.0.2_x86_64.deb && \
sudo dpkg -i vagrant*.deb && \ sudo dpkg -i vagrant*.deb && \
@@ -204,7 +204,7 @@ Most commands need additional data to work, like a path or a file. That extra
information is called an **argument**, and it's specified after the name of the information is called an **argument**, and it's specified after the name of the
command, like this: command, like this:
``` ```console
$ cd /home/john/notes $ cd /home/john/notes
``` ```
@@ -221,7 +221,7 @@ different meanings.
Sometimes, a command can accept arguments indicated with dashes. Here's another Sometimes, a command can accept arguments indicated with dashes. Here's another
example of arguments usage: example of arguments usage:
``` ```console
$ nano -C /home/john/backups --mouse todo.txt $ nano -C /home/john/backups --mouse todo.txt
``` ```
@@ -245,7 +245,7 @@ Note that the `todo.txt` is the file we want to open! It has nothing to do with
the previous argument. This will probably clarify it (taken from `nano`'s the previous argument. This will probably clarify it (taken from `nano`'s
help): help):
``` ```console
Usage: nano [OPTIONS] [FILE]... Usage: nano [OPTIONS] [FILE]...
``` ```
@@ -263,13 +263,13 @@ them.
That's why you may have seen cases, in the Zulip codebase or That's why you may have seen cases, in the Zulip codebase or
elsewhere, when some Python scripts are called with `python`: elsewhere, when some Python scripts are called with `python`:
``` ```console
$ python my_program.py $ python my_program.py
``` ```
While other times, `python` isn't used: While other times, `python` isn't used:
``` ```console
$ ./my_program.py $ ./my_program.py
``` ```
@@ -281,7 +281,7 @@ The note telling the OS how to interpret the file goes on the file's
very first line, and it's called a **shebang**. In our Python scripts, very first line, and it's called a **shebang**. In our Python scripts,
it looks like this: it looks like this:
``` ```python
#!/usr/bin/env python3 #!/usr/bin/env python3
``` ```
@@ -294,7 +294,7 @@ added as a command-line argument. So, returning to our example with
`my_program.py`, when you run `./my_program.py`, what happens under `my_program.py`, when you run `./my_program.py`, what happens under
the hood is equivalent to: the hood is equivalent to:
``` ```console
$ /usr/bin/env python3 ./my_program.py $ /usr/bin/env python3 ./my_program.py
``` ```

View File

@@ -98,6 +98,9 @@ markdown_whitespace_rules: List["Rule"] = [
"pattern": "^#+[A-Za-z0-9]", "pattern": "^#+[A-Za-z0-9]",
"strip": "\n", "strip": "\n",
"description": "Missing space after # in heading", "description": "Missing space after # in heading",
"exclude_line": {
("docs/subsystems/hotspots.md", "#hotspot_new_hotspot_name_icon {"),
},
"good_lines": ["### some heading", "# another heading"], "good_lines": ["### some heading", "# another heading"],
"bad_lines": ["###some heading", "#another heading"], "bad_lines": ["###some heading", "#another heading"],
}, },