mirror of
https://github.com/zulip/zulip.git
synced 2025-10-30 19:43:47 +00:00
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:
@@ -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].
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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=''
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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/
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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)}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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>
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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>'
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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).
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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/
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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/
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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`:
|
||||||
|
|||||||
@@ -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',
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"'
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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+";
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
```
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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},),
|
||||||
|
|||||||
@@ -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 ?)
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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": [
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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});
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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 =
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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'.
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -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"],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user