snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-15 15:05:30 +05:00
commit 64898b2c79
8999 changed files with 1757358 additions and 0 deletions

1
docs/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/_build

19
docs/Makefile Normal file
View File

@@ -0,0 +1,19 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS = -j auto -W
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

6
docs/README.md Normal file
View File

@@ -0,0 +1,6 @@
# Zulip Markdown documentation hosted elsewhere
The Markdown files in this directory ( /zulip/docs ) are not intended
to be read on GitHub. Instead, visit our
[ReadTheDocs](https://zulip.readthedocs.io/en/latest/index.html) to
read the Zulip documentation.

1937
docs/THIRDPARTY Normal file

File diff suppressed because it is too large Load Diff

0
docs/__init__.py Normal file
View File

18
docs/_static/theme_overrides.css vendored Normal file
View File

@@ -0,0 +1,18 @@
/* override table width restrictions */
.wy-table-responsive table td,
.wy-table-responsive table th {
/* !important prevents the common CSS stylesheets from
overriding this as on RTD they are loaded after this stylesheet */
white-space: normal !important;
}
.wy-table-responsive {
overflow: visible !important;
}
/* Present tables with columns of equal size
when images are present. */
.rst-content table.docutils:has(img) {
table-layout: fixed;
width: 100%;
}

29
docs/_templates/layout.html vendored Normal file
View File

@@ -0,0 +1,29 @@
{% extends "!layout.html" %}
{% block document %}
{#
# This allows us to insert a warning that appears only on the development
# version e.g. to say that something is likely to have changed.
# For more info see: https://www.sphinx-doc.org/en/master/templating.html
#}
{% if pagename in ["production/docker"] and release.endswith('+git') %}
{#
# Pages added since the most recent stable release should be listed
# above so the development-version warning falls back to the production
# index instead of a not-yet-existent stable URL. Remove entries after
# each release.
#}
<div class="admonition warning">
<p class="first admonition-title">Warning</p>
<p class="last">You are reading a <strong>development version</strong> of the Zulip documentation. These instructions may contain changes that are not yet present in a supported Zulip Server release.
See <a class="reference external" href="https://zulip.readthedocs.io/en/stable/production/">documentation for the latest stable release</a>.</p>
</div>
{% elif pagename.split("/")[0] == "production" and release.endswith('+git') %}
<div class="admonition warning">
<p class="first admonition-title">Warning</p>
<p class="last">You are reading a <strong>development version</strong> of the Zulip documentation. These instructions may contain changes that are not yet present in a supported Zulip Server release.
See <a class="reference external" href="https://zulip.readthedocs.io/en/stable/{{ pagename }}.html">documentation for the latest stable release</a>.</p>
</div>
{% endif %}
{{ super() }}
{% endblock %}

1
docs/code-of-conduct.md Symbolic link
View File

@@ -0,0 +1 @@
../CODE_OF_CONDUCT.md

73
docs/conf.py Normal file
View File

@@ -0,0 +1,73 @@
# For documentation on Sphinx configuration options, see:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# https://myst-parser.readthedocs.io/en/latest/sphinx/reference.html
# https://sphinx-rtd-theme.readthedocs.io/en/stable/configuring.html
# https://sphinx-design.readthedocs.io/en/latest/tabs.html
import os
import sys
from typing import Any
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from version import LATEST_RELEASE_VERSION, ZULIP_VERSION
on_rtd = os.environ.get("READTHEDOCS") == "True"
# General configuration
extensions = [
"sphinx.ext.intersphinx",
"sphinx.ext.autosectionlabel",
"myst_parser",
"sphinx_rtd_theme",
"sphinx_design",
"sphinx_copybutton",
]
templates_path = ["_templates"]
project = "Zulip"
copyright = "2012–2015 Dropbox, Inc., 2015–2021 Kandra Labs, Inc., and contributors"
author = "The Zulip Team"
version = ZULIP_VERSION
release = ZULIP_VERSION
exclude_patterns = ["_build", "README.md"]
suppress_warnings = [
"myst.header",
"autosectionlabel.git/terminology", # HEAD vs head
]
pygments_style = "sphinx"
autosectionlabel_prefix_document = True
autosectionlabel_maxdepth = 2
intersphinx_mapping = {
"docker": ("https://zulip.readthedocs.io/projects/docker/en/latest/", None),
}
intersphinx_disabled_reftypes = ["*"]
# Options for Markdown parser
myst_enable_extensions = [
"colon_fence",
"substitution",
]
myst_heading_anchors = 6
myst_substitutions = {
"LATEST_RELEASE_VERSION": LATEST_RELEASE_VERSION,
}
# Options for HTML output
html_theme = "sphinx_rtd_theme"
html_theme_options = {
"collapse_navigation": not on_rtd, # makes local builds much faster
"logo_only": True,
}
html_logo = "images/zulip-logo.svg"
html_static_path = ["_static"]
# When copying code blocks, ignore line numbers and prompts
copybutton_exclude = ".linenos, .gp"
def setup(app: Any) -> None:
# overrides for wide tables in RTD theme
app.add_css_file("theme_overrides.css") # path relative to _static

View File

@@ -0,0 +1,51 @@
# Asking great questions
A well-formed question helps you learn, respects the person answering, and makes
efficient use of time for everyone involved. Asking the right question, to the
right person, in the right way, at the right time, is a skill that requires a
lifetime of fine-tuning. This page offers some guidelines and resources the
[Zulip community](https://zulip.com/development-community/) has found helpful in this pursuit.
## Where to ask your question
It is almost always best to ask questions and have a conversation in a public
channel, rather than in direct messages. You’ll get better and faster answers
since many people can help, and it makes it possible for others to benefit from
the discussion.
The [Zulip community
guide](https://zulip.com/development-community/#where-do-i-send-my-message)
offers guidelines on how the major public channels in the community are used.
Don’t stress too much about picking the right place if you’re not sure, as
moderators can [move your question thread to a different
channel](https://zulip.com/help/move-content-to-another-channel) if needed.
## How to ask a great question
Spending some extra time and effort to carefully formulate your question is well
worthwhile, as it makes it much more likely that you'll get the information you
need to move forward. There are a few wonderfully thoughtful blog posts that
explain what it means to ask a good question:
- [Try, Then Ask](https://www.mattringel.com/2013/09/30/you-must-try-and-then-you-must-ask/)
- [We Aren’t Just Making Code, We’re Making History](https://www.harihareswara.net/sumana/2016/10/12/0)
- [How to Ask Good Questions](https://jvns.ca/blog/good-questions/)
In brief, to formulate a great question, you should:
- Try to solve your own problem first, including reading through relevant
documentation and code.
- Identify the precise point on which you feel stuck.
- Formulate a clear question that includes an appropriate amount of context
and a specific request for help.
When your question is answered, follow through on the advice you receive, and (when
appropriate) summarize the resolution of your problem so that others can learn
from your experience.
## Follow the community guidelines
As always, be sure to follow the [Zulip community
norms](https://zulip.com/development-community/). In particular, check out [the
section on getting help](https://zulip.com/development-community/#getting-help)
before you post.

View File

@@ -0,0 +1,429 @@
# Reviewing Zulip code
Code review is a key part of how Zulip does development. It's an essential
aspect of our process to build a high-quality product with a maintainable
codebase. See the [pull request review process](../contributing/review-process.md)
guide for a detailed overview of Zulip's PR review process.
## Principles of code review
Zulip has an active contributor community, and just a small handful
of maintainers who can do the final rounds of code review. As such, we would
love for contributors to help each other with making pull requests that are not
only correct, but easy to review. Doing so ensures that PRs can be finalized and
merged more quickly, and accelerates the pace of progress for the entire
project.
If you're new to open source, this may be the first time you do a code review of
anyone's changes! We have therefore written this step-by-step guide to be
accessible to all Zulip contributors.
### Reviewing your own code
One of the best ways to improve the quality of your own work as a software
engineer is to do a code review of your own work before submitting it to others for
review. We thus strongly encourage you to get into the habit of reviewing your
own code. You can often find things you missed by taking a step back to look
over your work before asking others to do so, and this guide will walk you
through the process. Catching mistakes yourself will help your PRs be merged
faster, and folks will appreciate the quality and professionalism of your
work.
### Reviewing other contributors' code
Doing code reviews is a valuable contribution to the Zulip project.
It's also an important skill to develop for participating in
open-source projects and working in the industry in general. If
you're contributing to Zulip and have been working in our code for a
little while, we would love for you to start doing code reviews!
Anyone can do a code review -- you don't have to have a ton of experience, and
you don't have to have the power to ultimately merge the PR. The sections below
offer accessible, step-by-step guidance for how to go about reviewing Zulip PRs.
For students participating in Google Summer of Code or a similar
program, we expect you to spend a chunk of your time each week (after
the first couple of weeks as you're getting going) doing code reviews.
## How to review code
Whether you are reviewing your own code or somebody else's, this section
describes how to go about it.
If you are reviewing somebody else's code, you will likely need to first fetch
it so that you can play around with the new functionality. If you're working in
the Zulip server codebase, use our [Git tool][git-tool]
`tools/fetch-rebase-pull-request` to check out a pull request locally and rebase
it onto `main`.
### Code review checklist
The following review steps apply to the majority of PRs.
**Think about the issue:**
1. Start by **rereading the issue** the PR is intended to solve. Are you
confident that you **understand everything the issue is asking for**? If not,
try exploring the relevant parts of the Zulip app and reading any linked
discussions on the [development community server][czo] to see if the
additional context helps. If any part is still confusing, post a GitHub
comment or a message on the [development community server][czo] explaining
precisely what points you find confusing.
2. Now that you're confident that you understand the issue, **does the PR
address all the points described in the issue**? If not, is it easy to tell
without reading the code which points are not addressed and why? Here is a
handful of good ways for the author to communicate why the issue as written
might not be fully solved by the PR:
- The issue explicitly notes that it's fine for some parts to be completed
separately, and the PR clearly indicates which parts are solved.
- After discussion of initial prototypes (in GitHub comments or on the
[development community server][czo]), it was decided to change some part of
the specification, and the PR notes this.
- The author explains why the PR is a better way to solve the issue than what
was described.
- The solution changed because of changes in the project or application since
the issue was written, and the author explains the adjustments that were
made.
**Think about the code:**
1. Make sure the PR uses **clear function, argument, variable, and test names.**
Every new piece of Zulip code will be read many times by other developers, and
future developers will `grep` for relevant terms when researching a problem, so
it's important that variable names communicate clearly the purpose of each
piece of the codebase.
1. Make sure the PR **avoids duplicated code.** Code duplication is a huge
source of bugs in large projects and makes the codebase difficult to
understand, so we avoid significant code duplication wherever possible.
Sometimes avoiding code duplication involves some refactoring of existing
code; if so, that should usually be done as its own series of commits (not
squashed into other changes or left as a thing to do later). That series of
commits can be in the same pull request as the feature that they support, and
we recommend ordering the history of commits so that the refactoring comes
_before_ the feature. That way, it's easy to merge the refactoring (and
minimize risk of merge conflicts) if there are still user experience issues
under discussion for the feature itself.
1. **Good comments** help. It's often worth thinking about whether explanation
in a commit message or pull request discussion should be included in
a comment, `/docs`, or other documentation. But it's better yet if
verbose explanation isn't needed. We prefer writing code that is
readable without explanation over a heavily commented codebase using
lots of clever tricks.
1. Make sure the PR follows Zulip's **coding style**. See the Zulip [coding
style documentation][code-style] for details. Our goal is to have as much of
this as possible verified via the linters and tests, but there will always be
unusual forms of Python/JavaScript style that our tools don't check for.
1. If you can, step back and think about the **technical design**. There are a
lot of considerations here: security, migration paths/backwards compatibility,
cost of new dependencies, interactions with features, speed of performance,
API changes. Security is especially important and worth thinking about
carefully with any changes to security-sensitive code like views.
**Think about testing:**
1. **The CI build tests need to pass.** One can investigate
any failures and figure out what to fix by clicking on a red X next
to the commit hash or the Detail links on a pull request. (Example:
in [#17584](https://github.com/zulip/zulip/pull/17584),
click the red X before `49b10a3` to see the build jobs
for that commit. You can see that there are 7 build jobs in total.
All the 7 jobs run in GitHub Actions. You can see what caused
the job to fail by clicking on the failed job. This will open
up a page in the CI that has more details on why the job failed.
For example [this](https://github.com/zulip/zulip/actions/runs/15362225042/job/43230810881)
is the page of an "Ubuntu 22.04 (Python 3.10, backend + frontend)" job.
See our docs on [continuous integration](../testing/continuous-integration.md)
to learn more.
2. Make sure **the code is well-tested**; see below for details. The PR should
summarize any [manual testing](#manual-testing) that was done to validate
that the feature is working as expected.
**Think about the commits:**
1. Does the PR follow the principle that “**Each commit is a minimal coherent
idea**”? See the [commit discipline guide][commit-discipline] to learn more
about commit structure in Zulip.
2. Does each commit have a **clear commit message**? Check for content, format,
spelling and grammar. See the [Zulip commit discipline][commit-messages]
documentation for details on what we look for.
You should also go through any of the following checks that are applicable:
- _Error handling._ The code should always check for invalid user
input. User-facing error messages should be clear and when possible
be actionable (it should be obvious to the user what they need to do
in order to correct the problem).
- _Translation._ Make sure that the strings are marked for
[translation].
- _Completeness of refactoring._ When reviewing a refactor, verify that the changes are
complete. Usually, one can check that efficiently using `git grep`,
and it's worth it, as we very frequently find issues by doing so.
- _Documentation updates._ If this changes how something works, does it
update the documentation in a corresponding way? If it's a new
feature, is it documented, and documented in the right place?
- _mypy annotations in Python code._ New functions should be annotated using
[mypy] and existing annotations should be updated. Use of `Any`, `ignore`, and
unparameterized containers should be limited to cases where a more precise
type cannot be specified.
### Automated testing
- The tests should **validate that the feature works correctly**, and
specifically test for common error conditions, bad user input, and potential
bugs that are likely for the type of change being made. Tests that exclude
whole classes of potential bugs are preferred when possible (e.g., the common
test suite `test_markdown.py` between the Zulip server's [frontend and backend
Markdown processors](../subsystems/markdown.md), or the `GetEventsTest` test
for buggy race condition handling). See the [test writing][test-writing]
documentation to learn more.
- We are trying to maintain ~100% test coverage on the backend, so backend
changes should have negative tests for the various error conditions. See
[backend testing documentation][backend-testing] for details.
- If the feature involves frontend changes, there should be frontend tests. See
[frontend testing documentation][frontend-testing] for details.
### Manual testing
If the PR makes any user-facing changes, make sure to play with the part
of the app being changed to validate that things look and work as expected.
While not all of the situations below will apply, here are some ideas for things
that should be tested if they are applicable. Use the [development
environment][development-environment] to test any web app changes.
This might seem like a long process, but you can go through it quite quickly
once you get the hang of it. Trust us, it will save time and review round-trips
down the line!
Testing a PR is a great opportunity to discover pre-existing bugs or usability
issues. Please verify that the issue you observed is pre-existing by replicating
the behavior on `main`, and [report it](./reporting-bugs.md)!
Describe the manual testing you did in the PR description (for your own work),
or in a comment (as a reviewer), so that the next reviewer knows how thoroughly
the changes were exercised, and can catch any testing gaps.
**Visual appearance:**
- Open up the parts of the UI that were changed, and make sure they look as
you were expecting.
- Is the new UI consistent with similar UI elements? Think about fonts, colors,
sizes, etc. If a new or modified element has multiple states (e.g., "on" and
"off"), consider all of them.
- Is the new UI aligned correctly with the elements around it, both vertically and
horizontally?
- If the PR adds or modifies a clickable element, does it have a hover behavior
that's consistent with similar UI elements?
- If the PR adds or modifies an element (e.g., a button or checkbox) that is
sometimes disabled, is the disabled version of the UI consistent with similar
UI elements?
- Did the PR accidentally affect any other parts of the UI? E.g., if the PR
modifies some CSS, look for other elements that may have been altered
unintentionally. Are you using a shared function or component? Use `git grep`
to see if the code you modified is being used elsewhere. Compare carefully
with the state in `main` to make sure you haven't introduced any incidental
changes.
- Now check all of the above in the other theme (light/dark).
- Now check all of the above at small and large [font
sizes](https://zulip.com/help/font-size).
**Responsiveness and internationalization:**
- Check the new UI at different window sizes, including mobile sizes (you can
use Chrome DevTools if you like). Does it look good in both wide and narrow
windows?
- To simulate what will happen when the UI is translated to different languages,
try changing any new strings, or ones that are now displayed differently, to
make them 1.5x longer, and check if anything breaks. What would happen if the
strings were half as long as in English?
- Are any [plurals and
lists](../translating/internationalization.md#plurals-and-lists)
internationalized correctly?
**Strings (text):**
If the PR adds or modifies strings, check the following:
- Does the wording seem consistent with similar features (similar style, level
of detail, etc.)?
- Are strings appropriate in all situations (e.g., user vs. admin, other users
vs. yourself)?
**Tooltips:**
- Do elements that require tooltips have them? Check similar elements to see
whether a tooltip is needed, and what information it should contain.
**Functionality:**
We're finally getting to the part where you actually use the new/updated
feature. :) Test to see if it works as expected, trying a variety of scenarios.
If it works as described in the issue but seems awkward in some way, note this
on the PR.
If relevant, be sure to check that:
- Live updates are working as expected, in all the places that should be updated.
- Keyboard navigation, including tabbing to the interactive elements, is working
as expected.
Some scenarios to consider:
- Try clicking on any interactive elements, multiple times, in a variety of orders.
- If the feature affects the **message view**, try it out in different types of
narrows: topic, channel, Combined feed, direct messages.
- If the feature affects the **compose box** in the web app, try both ways of
[resizing the compose box](https://zulip.com/help/resize-the-compose-box).
Test both channel messages and direct messages.
- If the feature might require **elevated permissions**, check it out as a user who has
permissions to use it and one who does not.
- If the feature takes user input, check input edge cases (e.g., empty, invalid,
or very long).
- Think about how the feature might **interact with other features**, and try out
such scenarios. For example:
- If the PR adds a banner, is it possible that it would be shown at the same
time as other banners? Does something reasonable happen?
- If the feature has to do with topic editing, do you need to think
about what happens when a topic is resolved/unresolved?
- If it's a message view feature, would anything go wrong if the message was
collapsed or muted? If it was colored like an `@`-mention or a direct message?
**Logged out view:** When appropriate, test as a logged out user. For example,
was a new message menu item correctly added or skipped? If the item was skipped,
did your changes introduce an extraneous separator in the menu?
## Review process and communication
### Asking for a code review
The [pull request review process](../contributing/review-process.md) guide
provides a detailed overview of Zulip's PR review process. Your reviewers and
Zulip's maintainers will help shepherd your PR through the process. There are
also some additional ways to ask for a code review:
- Are there folks who have been working on similar things, or a loosely related
area? If so, they might be a good person to review your PR. `@`-mention them
with something like "`@person`, would you be up for reviewing this?" If
you're not sure whether they are familiar with how Zulip code reviews work, you
can also include a link to this guide.
- If you're not sure who to ask, you can post a message in
[#code-review](https://chat.zulip.org/#narrow/channel/91-code-review) on [the Zulip
development community server](https://zulip.com/development-community/) to reach
out to a wider group of potential reviewers.
Please be patient and mindful of the fact that it isn't always possible to
provide a quick reply. Going through the [review process](#how-to-review-code)
described above for your own PR will make your code easier and faster to review,
which makes it much more likely that it will be reviewed quickly and require
fewer review cycles.
### Reviewing someone else's code
#### Fast replies are key
For the author of a PR, getting feedback quickly is really important
for making progress quickly and staying productive. That means that
if you get @-mentioned on a PR with a request for you to review it,
it helps the author a lot if you reply promptly.
A reply doesn't even have to be a full review; if a PR is big or if
you're pressed for time, then just getting some kind of reply in
quickly -- initial thoughts, feedback on the general direction, or
just saying you're busy and when you'll have time to look harder -- is
still really valuable for the author and for anyone else who might
review the PR.
People in the Zulip project live and work in many time zones, and code
reviewers also need focused chunks of time to write code and do other
things, so an immediate reply isn't always possible. But a good
benchmark is to try to always reply **within one workday**, at least
with a short initial reply, if you're working regularly on Zulip. And
sooner is better.
#### Communication style
Any time you leave a code review, be sure to treat the author with respect.
Remember that they are generously spending their time on an effort to improve
the Zulip project. Thank them for their work, and express your appreciation for
anything the author did especially well, whether it's a nice commit message, a
prompt response to earlier feedback, or a well-written test.
Be as clear and direct as you can when describing requested changes. There is no
need to apologize when asking for a change; you're collaborating with the
author to make the PR as good as you can together.
Be sure to explain the motivation for the changes you're requesting if it's not
obvious, so that the author can learn for next time. It may be helpful to point
to developer documentation, such as the [commit discipline
guide][commit-discipline].
#### Fixing up the PR
If a pull request just needs a little fixing to make it mergeable,
feel free to do that in a new commit, then push your branch to GitHub
and mention the branch in a comment on the pull request. That'll save
the maintainer time and get the PR merged quicker.
### Responding to review feedback
Once you've received a review and resolved any feedback, it's critical
to update the GitHub thread to reflect that. Best practices are to:
- Make sure that CI passes and the PR is rebased onto recent `main`.
- Post comments on each feedback thread explaining at least how you
resolved the feedback, as well as any other useful information
(problems encountered, reasoning for why you picked one of several
options, a test you added to make sure the bug won't recur, etc.).
- Post a summary comment in the main feed for the PR, explaining that
this is ready for another review, and summarizing any changes from
the previous version, details on how you tested the changes, new
screenshots/etc. More detail is better than less, as long as you
take the time to write clearly.
If you resolve the feedback, but the PR has merge conflicts, CI
failures, or the most recent comment is the reviewer asking you to fix
something, it's very likely that a potential reviewer skimming your PR
will assume it isn't ready for review and move on to other work.
If you need help or think an open discussion topic requires more
feedback or a more complex discussion, move the discussion to a topic
in the [Zulip development community server][czo]. Be sure to provide links
from the GitHub PR to the conversation (and vice versa) so that it's
convenient to read both conversations together.
## Additional resources
We also recommend the following resources on code reviews.
- [The Gentle Art of Patch Review](https://sage.thesharps.us/2014/09/01/the-gentle-art-of-patch-review/)
article by Sarah Sharp
- [Zulip & Good Code Review](https://www.harihareswara.net/sumana/2016/05/17/0)
article by Sumana Harihareswara
- [Code Review - A consolidation of advice and stuff from the
internet](https://gist.github.com/porterjamesj/002fb27dd70df003646df46f15e898de)
article by James J. Porter
- [Zulip code of conduct](../code-of-conduct.md)
[code-style]: code-style.md
[commit-discipline]: commit-discipline.md
[commit-messages]: commit-discipline.md#commit-messages
[test-writing]: ../testing/testing.md
[backend-testing]: ../testing/testing-with-django.md
[frontend-testing]: ../testing/testing-with-node.md
[mypy]: ../testing/mypy.md
[git-tool]: ../git/zulip-tools.md#fetch-a-pull-request-and-rebase
[translation]: ../translating/translating.md
[czo]: https://zulip.com/development-community/
[development-environment]: ../development/overview.md

View File

@@ -0,0 +1,369 @@
# Code style and conventions
This page documents code style policies that every Zulip developer
should understand. We aim for this document to be short and focused
only on details that cannot be easily enforced in another way (e.g.,
through linters, automated tests, or subsystem design that makes classes
of mistakes unlikely). This approach minimizes the cognitive
load of ensuring a consistent coding style for both contributors and
maintainers.
One can summarize Zulip's coding philosophy as a relentless focus on
making the codebase easy to understand and difficult to make dangerous
mistakes in (see the sections on [dangerous constructs](#dangerous-constructs-in-django)
at the end of this page). The majority of work in any large software
development project is understanding the existing code so one can debug
or modify it, and investments in code readability usually end up paying
for themselves when someone inevitably needs to debug or improve the code.
When there's something subtle or complex to explain or ensure in the
implementation, we try hard to make it clear through a combination of
clean and intuitive interfaces, well-named variables and functions,
comments/docstrings, and commit messages (roughly in that order of
priority -- if you can make something clear with a good interface,
that's a lot better than writing a comment explaining how the bad
interface works).
After an introduction to our lint tools and test suites, this document
outlines some general
[conventions and practices](#follow-zulip-conventions-and-practices)
applicable to all languages used in the codebase, as well as specific
guidance on [Python](#python-specific-conventions-and-practices) and
[JavaScript and TypeScript](#javascript-and-typescript-conventions-and-practices).
([HTML and CSS](../subsystems/html-css.md) are outlined in their own
documentation.)
At the end of the document, you can read about
[dangerous constructs in Django](#dangerous-constructs-in-django) and
[JavaScript and TypeScript](#dangerous-constructs-in-javascript-and-typescript)
that you should absolutely avoid.
## Be consistent with existing code
Look at the surrounding code, or a similar part of the project, and try
to do the same thing. If you think the other code has actively bad
style, fix it (in a separate commit).
When in doubt, ask in
[#development help](https://chat.zulip.org/#narrow/channel/49-development-help).
### Use the linters
You can run all of the linters at once:
```bash
$ ./tools/lint
```
But, doing that will take a little time, because `./tools/lint`
runs many lint checks in parallel, including:
- JavaScript ([ESLint](https://eslint.org/),
[Prettier](https://prettier.io/))
- Python ([mypy](http://mypy-lang.org/),
[Ruff](https://docs.astral.sh/ruff/))
- templates
- Puppet configuration
- custom checks (e.g., trailing whitespace and spaces-not-tabs)
To speed things up, you can [pass specific files or directories
to the linter](../testing/linters.md):
```
$ ./tools/lint web/src/compose.ts
```
If you'd like, you can also set up a local Git commit hook that
will lint only your changed files each time you commit:
```bash
$ ./tools/setup-git-repo
```
### Use tests to verify your logic
Clear, readable code is important for [tests](../testing/testing.md);
familiarize yourself with our
[testing frameworks](../testing/testing.md#major-test-suites) and
[testing philosophy](../testing/philosophy.md) so that you can write
clean, readable tests. In-test comments about anything subtle that is
being verified are appreciated.
You can run all of the tests like this:
```
$ ./tools/test-all
```
But consult [our documentation on running tests](../testing/testing.md#running-tests),
which covers more targeted approaches to commanding the test-runners.
## Follow Zulip conventions and practices
What follows is language-neutral advice that is beyond the bounds of
linters and automated tests.
### Observe a reasonable line length
We have an absolute hard limit on line length only for some files, but
we should still avoid extremely long lines. A general guideline is:
refactor stuff to get it under 85 characters, unless that makes the
code a lot uglier, in which case it's fine to go up to 120 or so.
### Tag user-facing strings for translation
Remember to
[tag all user-facing strings for translation](../translating/translating.md),
whether the strings are in HTML templates or output by JavaScript/TypeScript
that injects or modifies HTML (e.g., error messages).
### Correctly prepare paths destined for state or log files
When writing out state or log files, always pass an absolute path
through `zulip_path` (found in `zproject/computed_settings.py`), which
will do the right thing in both development and production.
### Never include secrets inline with code
Please don't put any passwords, secret access keys, etc. inline in the
code. Instead, use the `get_secret` function or the `get_mandatory_secret`
function in `zproject/config.py` to read secrets from `/etc/zulip/secrets.conf`.
### Familiarize yourself with rules about third-party code
See [our docs on dependencies](../subsystems/dependencies.md) for discussion of
rules about integrating third-party projects.
## Python-specific conventions and practices
- Our Python code is formatted with
[Ruff](https://docs.astral.sh/ruff/). The [linter
tool](../testing/linters.md) enforces this by running Ruff in check
mode, or in write mode with
`tools/lint --only=ruff,ruff-format --fix`. You may find it helpful
to [integrate Ruff](https://docs.astral.sh/ruff/integrations/) with
your editor.
- Don't put a shebang line on a Python file unless it's meaningful to
run it as a script. (Some libraries can also be run as scripts, e.g.,
to run a test suite.)
- Scripts should be executed directly (`./script.py`), so that the
interpreter is implicitly found from the shebang line, rather than
explicitly overridden (`python script.py`).
- Put all imports together at the top of the file, absent a compelling
reason to do otherwise.
- Unpacking sequences doesn't require list brackets:
```python
[x, y] = xs # unnecessary
x, y = xs # better
```
- For string formatting, use `x % (y,)` rather than `x % y`, to avoid
ambiguity if `y` happens to be a tuple.
## JavaScript and TypeScript conventions and practices
Our JavaScript and TypeScript code is formatted with
[Prettier](https://prettier.io/). You can ask Prettier to reformat
all code via our [linter tool](../testing/linters.md) with
`tools/lint --only=prettier --fix`. You can also [integrate it with your
editor](https://prettier.io/docs/en/editors.html).
### Build DOM elements in Handlebars
The best way to build complicated DOM elements is a Handlebars template
like `web/templates/message_reactions.hbs`. For simpler things you can
use jQuery DOM-building APIs like this:
```js
const $new_tr = $('<tr />').attr('id', object.id);
```
### Attach behaviors to event listeners
Attach callback functions to events using jQuery code. For example:
```js
$("body").on("click", ".move_message_button", function (e) {
// message-moving UI logic
}
```
That approach has multiple benefits:
- Potential huge performance gains by using delegated events where
possible
- When calling a function from an `onclick` attribute, `this` is not
bound to the element like you might think
- jQuery does event normalization
Do not use `onclick` attributes in the HTML.
### Declare variables using `const` and `let`
Always declare JavaScript variables using `const` or `let` rather than
`var`.
### Manipulate objects and arrays with modern methods
For functions that operate on arrays or JavaScript objects, you should
generally use modern
[ECMAScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Language_Resources)
primitives such as [`for … of`
loops](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of),
[`Array.prototype.{entries, every, filter, find, indexOf, map, some}`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array),
[`Object.{assign, entries, keys, values}`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object),
[spread
syntax](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax),
and so on. Our Babel configuration automatically transpiles and
polyfills these using [`core-js`](https://github.com/zloirock/core-js)
when necessary. We used to use the
[Underscore](https://underscorejs.org/) library, but that should be
avoided in new code.
## HTML and CSS
See the documentation on [HTML and CSS](../subsystems/html-css.md)
for guidance on conventions in those languages.
## Dangerous constructs in Django
### Avoid excessive database queries
Look out for Django code like this:
```python
bars = Bar.objects.filter(...)
for bar in bars:
foo = bar.foo
# Make use of foo
```
...because it equates to:
```python
bars = Bar.objects.filter(...)
for bar in bars:
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
locally in development, it may be quite slow in production! Instead,
tell Django's [QuerySet
API](https://docs.djangoproject.com/en/5.0/ref/models/querysets/) to
_prefetch_ the data in the initial query:
```python
bars = Bar.objects.filter(...).select_related()
for bar in bars:
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
is wrong with the database schema. So don't defer this optimization when
performing schema changes, or else you may later find that it's
impossible.
### Never do direct database queries (`UserProfile.objects.get()`, `Client.objects.get()`, etc.)
In our Django code, never do direct `UserProfile.objects.get(email=foo)`
database queries. Instead always use `get_user_profile_by_{email,id}`.
There are 3 reasons for this:
1. It's guaranteed to correctly do a case-insensitive lookup
2. It fetches the user object from remote cache, which is faster
3. It always fetches a UserProfile object which has been queried
using `.select_related()` ([see above](#avoid-excessive-database-queries)!),
and thus will perform well when one later accesses related models
like the Realm.
Similarly we have `get_client` and `access_stream_by_id` /
`access_stream_by_name` functions to fetch those commonly accessed
objects via remote cache.
### Don't use Django model objects as keys in sets/dicts
Don't use Django model objects as keys in sets/dictionaries -- you will
get unexpected behavior when dealing with objects obtained from
different database queries:
For example, the following will, surprisingly, fail:
```python
# Bad example -- will raise!
obj: UserProfile = get_user_profile_by_id(17)
some_objs = UserProfile.objects.get(id=17)
assert obj in set([some_objs])
```
You should work with the IDs instead:
```python
obj: UserProfile = get_user_profile_by_id(17)
some_objs = UserProfile.objects.get(id=17)
assert obj.id in set([o.id for o in some_objs])
```
### Don't call user_profile.save() without `update_fields`
You should always pass the `update_fields` keyword argument to `.save()`
when modifying an existing Django model object. By default, `.save()`
updates all of the model object fields in the database, which results
in lots of race conditions where unrelated changes made by one thread can be
accidentally overwritten by another thread that fetched its `UserProfile`
object before the first thread wrote out its change.
### Don't update important model objects with raw saves
In most cases, we already have a function in `zerver.actions` with
a name like `do_activate_user` that will correctly handle lookups,
caching, and notifying running browsers via the event system about your
change. So please check whether such a function exists before writing
new code to modify a model object, since your new code has a good chance
of getting at least one of these things wrong.
### Don't use naive datetime objects
Python allows datetime objects to not have an associated time zone, which can
cause time-related bugs that are hard to catch with a test suite, or bugs
that only show up during daylight saving time.
Good ways to make time-zone-aware datetimes are below. We import time zone
libraries as `from datetime import datetime, timezone` and
`from django.utils.timezone import now as timezone_now`.
Use:
- `timezone_now()` to get a datetime when Django is available, such as
in `zerver/`.
- `datetime.now(tz=timezone.utc)` when Django is not available, such as
for bots and scripts.
- `datetime.fromtimestamp(timestamp, tz=timezone.utc)` if creating a
datetime from a timestamp. This is also available as
`zerver.lib.timestamp.timestamp_to_datetime`.
- `datetime.strptime(date_string, format).replace(tzinfo=timezone.utc)` if
creating a datetime from a formatted string that is in UTC.
Idioms that result in time-zone-naive datetimes, and should be avoided, are
`datetime.now()` and `datetime.fromtimestamp(timestamp)` without a `tz`
parameter, `datetime.utcnow()` and `datetime.utcfromtimestamp()`, and
`datetime.strptime(date_string, format)` without replacing the `tzinfo` at
the end.
Additional notes:
- Especially in scripts and puppet configuration where Django is not
available, using `time.time()` to get timestamps can be cleaner than
dealing with datetimes.
- All datetimes on the backend should be in UTC, unless there is a good
reason to do otherwise.
## Dangerous constructs in JavaScript and TypeScript
### Do not use `for...in` statements to traverse arrays
That construct pulls in properties inherited from the prototype chain.
Don't use it:
[[1]](https://stackoverflow.com/questions/500504/javascript-for-in-with-arrays),
[[2]](https://google.github.io/styleguide/javascriptguide.xml#for-in_loop),
[[3]](https://www.jslint.com/help.html#forin)

View File

@@ -0,0 +1,345 @@
# Commit discipline
We follow the Git project's own commit discipline practice of "Each
commit is a minimal coherent idea". This discipline takes a bit of work,
but it makes it much easier for code reviewers to spot bugs, and
makes the commit history a much more useful resource for developers
trying to understand why the code works the way it does, which also
helps a lot in preventing bugs.
Use `git rebase -i` as much as you need to shape your commit structure. See the
[Git guide](../git/overview.md) for useful resources on mastering Git.
**Tip**: Check out how to use [`git log -p` to study the commit
history](../git/reading-history.md) of the project.
## Each commit must be coherent
- It should pass tests (so test updates needed by a change should be
in the same commit as the original change, not a separate "fix the
tests that were broken by the last commit" commit).
- It should not make Zulip worse. For example, it is fine to add backend
capabilities without adding a frontend to access them. It's not fine to add a
frontend component with no backend to make it work.
- It should be safe to deploy individually, or explain in detail in
the commit message as to why it isn't (maybe with a [manual] tag).
So implementing a new API endpoint in one commit and then adding the
security checks in a future commit should be avoided -- the security
checks should be there from the beginning.
- Error handling should generally be included along with the code that
might trigger the error.
- TODO comments should be in the commit that introduces the issue or
the functionality with further work required.
## Commits should generally be minimal
Whenever possible, find chunks of complexity that you can separate from the
rest of the project.
- If you need to refactor code, add tests for existing functionality,
rename variables or functions, or make other changes that do not
change the functionality of the product, make those changes into a
series of preparatory commits that can be merged independently of
building the feature itself.
- Moving code from one file to another should be done in separate
commits from functional changes or even refactoring within a file.
- 2 different refactorings should be done in different commits.
- 2 different features should be done in different commits.
- If you find yourself writing a commit message that reads like a list
of somewhat dissimilar things that you did, you probably should have
just done multiple commits.
### When not to be overly minimal
- For completely new features, you don't necessarily need to split out
new commits for each little subfeature of the new feature. E.g., if
you're writing a new tool from scratch, it's fine to have the
initial tool have plenty of options/features without doing separate
commits for each one. That said, reviewing a 2000-line giant blob of
new code isn't fun, so please be thoughtful about submitting things
in reviewable units.
- Don't bother to split backend commits from frontend commits, even
though the backend can often be coherent on its own.
## Write a clean commit history
- Overly fine commits are easy to squash later, but not vice versa.
So err toward small commits, and the code reviewer can advise on
squashing.
- If a commit you write doesn't pass tests, you should usually fix
that by amending the commit to fix the bug, not writing a new "fix
tests" commit on top of it.
Zulip expects you to structure the commits in your pull requests to form
a clean history before we will merge them. It's best to write your
commits following these guidelines in the first place, but if you don't,
you can always fix your history using `git rebase -i` (more on that
[here](../git/fixing-commits.md)).
Never mix multiple changes together in a single commit, but it's great
to include several related changes, each in their own commit, in a
single pull request. If you notice an issue that is only somewhat
related to what you were working on, but you feel that it's too minor
to create a dedicated pull request, feel free to append it as an
additional commit in the pull request for your main project (that
commit should have a clear explanation of the bug in its commit
message). This way, the bug gets fixed, but this independent change
is highlighted for reviewers. Or just create a dedicated pull request
for it. Whatever you do, don't squash unrelated changes together in a
single commit; the reviewer will ask you to split the changes out into
their own commits.
It can take some practice to get used to writing your commits with a
clean history so that you don't spend much time doing interactive
rebases. For example, often you'll start adding a feature, and discover
you need to do a refactoring partway through writing the feature. When that
happens, we recommend you stash your partial feature, do the refactoring,
commit it, and then unstash and finish implementing your feature.
For additional guidance on how to structure your commits (and why it matters!),
check out GitHub's excellent [blog post](https://github.blog/2022-06-30-write-better-commits-build-better-projects).
## Commit messages
Commit messages have two parts:
1. A **summary**, which is a brief one-line overview of the changes.
2. A **description**, which provides further details on the changes,
the motivation behind them, and why they improve the project.
In Zulip, commit summaries have a two-part structure:
1. A one or two word description of the part of the codebase changed
by the commit.
2. A short sentence summarizing your changes.
Here is an
[example](https://github.com/zulip/zulip/commit/084dd216f017c32e15c1b13469bcbc928cd0bce9)
of a good commit message:
> tests: Remove ignored realm_str parameter from message send test.
>
> In commit
> [8181ec4](https://github.com/zulip/zulip/commit/8181ec4b56abf598223112e7bc65ce20f3a6236b),
> we removed the `realm_str` as a parameter for `send_message_backend`. This
> removes a missed test that included this as a parameter for that
> endpoint/function.
The commit message is a key piece of how you communicate with reviewers and
future contributors, and is no less important than the code you write. This
section provides detailed guidance on how to write an excellent commit message.
**Tip:** You can set up [Zulip's Git pre-commit hook][commit-hook] to
automatically catch common commit message mistakes.
[commit-hook]: ../git/zulip-tools.md#set-up-git-repo-script
### Commit summary, part 1
The first part of the commit summary should only be 1-2 **lower-case**
words, followed by a `:`, describing what the part of the product the
commit changes. These prefixes are essential for maintainers to
efficiently skim commits when doing release management or
investigating regressions.
Common examples include: settings, message feed, compose, left
sidebar, right sidebar, recent (for **Recent conversations**), search,
markdown, tooltips, popovers, drafts, integrations, email, docs, help,
and api docs.
When it's possible to do so concisely, it's helpful to be a little more
specific, e.g., emoji, spoilers, polls. However, a simple `settings:` is better
than a lengthy description of a specific setting.
If your commit doesn't cleanly map to a part of the product, you might
use something like "css" for CSS-only changes, or the name of the file
or technical subsystem principally being modified (not the full path,
so `realm_icon`, not `zerver/lib/realm_icon.py`).
There is no need to be creative here! If one of the examples above
fits your commit, use it. Consistency makes it easier for others to
scan commit messages to find what they need.
Additional tips:
- Use lowercase (e.g., "settings", not "Settings").
- If it's hard to find a 1-2 word description of the part of the codebase
affected by your commit, consider again whether you have structured your
commits well.
- Never use a generic term like "bug", "fix", or "refactor".
### Commit summary, part 2
This is a **complete sentence** that briefly summarizes your changes. There are
a few rules to keep in mind:
- Start the sentence with an
[imperative](https://en.wikipedia.org/wiki/Imperative_mood) verb, e.g.,
"fix", "add", "change", "rename", etc.
- Use proper capitalization and punctuation.
- Avoid abbreviations and acronyms.
- Be concise, and don't include unnecessary details. For example, "Change X and
update tests/docs," would be better written as just, "Change X," since (as
discussed above) _every_ commit is expected to update tests and documentation
as needed.
- Make it readable to someone who is familiar with Zulip's codebase, but hasn't
been involved with the effort you're working on.
- Use no more than 72 characters for the entire commit summary (parts 1 and 2).
### Examples of good commit summaries
- `provision: Improve performance of installing npm.`
- `channel: Discard all HTTP responses while reloading.`
- `integrations: Add GitLab integration.`
- `typeahead: Rename compare_by_popularity() for clarity.`
- `typeahead: Convert to ES6 module.`
- `tests: Compile Handlebars templates with source maps.`
- `blueslip: Add feature to time common operations.`
- `gather_subscriptions: Fix exception handling bad input.`
- `channel_settings: Fix save/discard widget on narrow screens.`
#### Detailed example
- **Good summary**: "gather_subscriptions: Fix exception handling bad input."
- **Not so good alternatives**:
- "gather_subscriptions was broken": This doesn't explain how it was broken, and
doesn't follow the format guidelines for commit summaries.
- "Fix exception when given bad input": It's impossible to tell what part of the
codebase was changed.
- Not using the imperative:
- "gather_subscriptions: Fixing exception when given bad input."
- "gather_subscriptions: Fixed exception when given bad input."
### Commit description
The body of the commit message should explain why and how the change
was made. Like a good code comment, it should provide context and
motivation that will help both a reviewer now, and a developer looking
at your changes a year later, understand the motivation behind your
decisions.
Many decisions may be documented in multiple places (for example, both
in a commit message and a code comment). The general rules of thumb are:
- Use the commit message for information that's relevant for someone
trying to understand the change this commit is making, or the difference
between the old version of the code and the new version. In particular,
this includes information about why the new version of the code is better than,
or not worse than, the old version.
- Use code comments, or the code itself, for information that's relevant
for someone trying to read and understand the new version of the code
in the future, without comparing it to the old version.
- If the information is helpful for reviewing your work (for example,
an alternative approach that you rejected or are considering,
something you noticed that seemed weird, or an error you aren't sure
you resolved correctly), include it in the PR description /
discussion.
As an example, if you have a question that you expect to be resolved
during the review process, put it in a PR comment attached to a
relevant part of the changes. When the question is resolved, remember
to update code comments and/or the commit description to document the
reasoning behind the decisions.
There are some cases when the best approach is improving the code or commit
structure, not writing up details in a comment or a commit message. For example:
- If the information is the description of a calculation or function,
consider the abstractions you're using. Often, a better name for a
variable or function is a better path to readable code than writing
a prose explanation.
- If the information describes an additional change that you made while working
on the commit, consider whether it is separable from the rest of the changes.
If it is, it should probably be moved to its own commit, with its own commit
message explaining it. Reviewing and integrating a series of several
well-written commits is far easier than reviewing those same changes in a
single commit.
When you fix a GitHub issue, [mark that you have fixed the issue in
your commit
message](https://help.github.com/en/articles/closing-issues-via-commit-messages)
so that the issue is automatically closed when your code is merged,
and the commit has a permanent reference to the issue(s) that it
resolves. Zulip's preferred style for this is to have the final
paragraph of the commit message read, e.g., `Fixes #123.`.
**Note:** Avoid using a phrase like `Partially fixes #1234.`, as
GitHub's regular expressions ignore the "partially" and close the
issue. `Fixes part of #1234.` is a good alternative.
#### The purpose of the commit description
The commit summary and description should, taken together, explain to another
Zulip developer (who may not be deeply familiar with the specific
files/subsystems you're changing) why this commit improves the project. This
means explaining both what it accomplishes, and why it won't break things one
might worry about it breaking.
- Include any important investigation/reasoning that another developer
would need to understand in order to verify the correctness of your
change. For example, if you're removing a parameter from a function,
the commit message might say, "It's safe to remove this parameter
because it was always False," or, "This behavior needs to be removed
because ...". A reviewer will likely check that indeed it was always
`False` as part of checking your work -- what you're doing is
providing them a chain of reasoning that they can verify.
- Provide background context. A good pattern in a commit message
description is, "Previously, when X happened, this caused Y to
happen, which resulted in ...", followed by a description of the
negative outcome.
- Don't include details that are obvious from looking at the diff for
the commit, such as lists of the names of the files or functions
that were changed, or the fact that you updated the tests.
- Avoid unnecessary personal narrative about the process through which
you developed this commit or pull request, like "First I tried X" or
"I changed Y".
#### Mentioning other contributors
You can
[credit](https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors)
co-authors on a commit by adding a `Co-authored-by:` line after a blank line at
the end of your commit message:
Co-authored-by: Greg Price <greg@zulip.com>
You should always give credit where credit is due. See our [guide on continuing
unfinished work](../contributing/continuing-unfinished-work.md) for step-by-step
guidance on continuing work someone else has started.
You can also add other notes, such as `Reported-by:`, `Debugged-by:`, or
`Suggested-by:`, but we don't typically do so.
@-mentions don't belong in commit messages (and won't notify the user you
mentioned). If you want to send someone a notification about a change, @-mention
them in the PR thread.
#### Formatting guidelines
There are a few specific formatting guidelines to keep in mind:
- The commit description should be separated from the commit summary
by a blank line. Most tools, including GitHub, will misrender commit
messages that don't do this.
- Use full sentences and paragraphs, with proper punctuation and
capitalization. Paragraphs should be separated with a single blank
line.
- Be sure to check your description for typos, spelling, and grammar
mistakes; commit messages are important technical writing and
English mistakes will distract reviewers from your ideas.
- Your commit message should be line-wrapped to about 68 characters
per line, but no more than 70, so that your commit message will be
easy to read in `git log` in a normal terminal. (It's OK for links
to be longer -- ignore `gitlint` when it complains about them.)
**Tip:** You may find it helpful to configure Git to use your preferred editor
using the `EDITOR` environment variable or `git config --global core.editor`,
and configure the editor to automatically wrap text to 70 or fewer columns per
line (all text editors support this).
### Examples of good commit messages
- [A backend testing
commit](https://github.com/zulip/zulip/commit/4869e1b0b2bc6d56fcf44b7d0e36ca20f45d0521)
- [A development environment provisioning
commit](https://github.com/zulip/zulip/commit/cd5b38f5d8bdcc1771ad794f37262a61843c56c0)

View File

@@ -0,0 +1,154 @@
# Continuing unfinished work
Sometimes, work is started on an issue or PR, but not brought to completion.
This may happen for a variety of reasons — the contributor working on the
project gets busy, maintainers cannot prioritize reviewing the work, a
contributor doesn't have the skills required to complete the project, there is
an unexpected technical challenge or blocker, etc.
Completing work that someone else has started is a great way to contribute! Here
are the steps required:
1. [Find work to be completed.](#find-work-to-be-completed)
1. [Review existing work and feedback.](#review-existing-work-and-feedback)
1. [Decide how to use prior work.](#decide-how-to-use-prior-work)
1. [Credit prior work in your commit history.](#credit-prior-work-in-your-commit-history)
1. [Present your pull request.](#present-your-pull-request)
## Find work to be completed
In Zulip's server and web app [repository](https://github.com/zulip/zulip), pull
requests that have significant work towards something valuable are often tagged
with a [completion candidate][completion-candidate] label. You can review
this label for unfinished work that you find interesting and have the skills to
complete.
Note that it's common to see one or more pull requests linked to an issue
you're interested in. The guidelines below apply regardless of whether you
intentionally set out to find work to complete or simply find yourself
building on someone else's work.
## Review existing work and feedback
Any time there are pull requests linked to the issue you are working on, start
by reviewing the existing work. Read the code, and pay close attention to any
feedback the pull request received. This will help you avoid any pitfalls other
contributors encountered.
## Decide how to use prior work
Consider how to use prior work on the issue. In your best judgment, is the
existing PR on the right track? If there's reviewer feedback, it should help you
figure this out.
If prior work looks like a good start:
1. Pull down the existing pull request.
1. Rebase it on the current version of the `main` branch.
1. Carefully address any open feedback from reviewers.
1. Make any other changes you think are needed, including completing any parts
of the work that had not been finished.
1. Make sure the work of others is [properly credited](#credit-prior-work-in-your-commit-history).
1. [Self-review](../contributing/code-reviewing.md), test, and revise the work,
including potentially [splitting out](../contributing/commit-discipline.md)
preparatory commits to make it easier to read. You should be proud of the
resulting series of commits, and be prepared to argue that it is the best
work you can produce to complete the issue.
Otherwise, you can:
1. Make your own changes from scratch.
1. Go through reviewer feedback on prior work. Would any of it apply to the
changes you're proposing? Be sure to address it if so.
## Credit prior work in your commit history
When you use or build upon someone else's unmerged work, it is both
professionally and ethically necessary to [properly
credit][coauthor-git-guide] their contributions in the commit history
of work that you submit. Git, used properly, does a good job of
preserving the original authorship of commits.
However, it's normal to find yourself making changes to commits
originally authored by other contributors, whether resolving merge
conflicts when doing `git rebase` or fixing bugs to create an
atomically correct commit compliant with Zulip's [commit
guidelines](../contributing/commit-discipline.md).
When you do that, it's your responsibility to ensure the resulting
commit series correctly credits the work of everyone who materially
contributed to it. The most direct way to credit the work of someone
beyond the commit's author maintained in the Git metadata is
`Co-authored-by:` line after a blank line at the end of your commit
message:
Co-authored-by: Greg Price <greg@zulip.com>
Be careful to type it precisely, because software parses these commit
message records in generating statistics. You should add such a line
in two scenarios:
- If your own work was squashed into a commit originally authored by
another contributor, add such a line crediting yourself.
- If you used another contributor's work in generating your own
commit, add such a line crediting the other contributor(s).
Sometimes, you make a mistake when rebasing and accidentally squash
commits in a way that messes up Git's authorship records. Often,
undoing the rebase change via `git reflog` is the best way to correct
such mistakes, but there are two other Git commands that can be used
to correct Git's primary authorship information after the fact:
- `git commit --amend --reset-author` will replace the Git commit
metadata (date, author, etc.) of the currently checked out commit
with yourself. This is useful to correct a commit that incorrectly
shows someone else as the author of your work.
- `git commit --amend -C <commit_id>` will replace the commit metadata
(date, author, etc.) on a commit with that of the provided commit
ID. This is useful if you accidentally made someone else's commit
show yourself as the author, or lost a useful commit message via
accidental squashing. (You can usually find the right commit ID to
use with `git reflog` or from GitHub).
As an aside, maintainers who modify commits before merging them are
credited via Git's "Committer" records (visible with `git show
--pretty=fuller`, for example). As a result, they may not bother with
adding a separate `Co-authored-by` record on commits that they revise
as part of merging a pull request.
## Present your pull request
In addition to the usual [guidance](../contributing/reviewable-prs.md) for
putting together your pull request, there are a few key points to keep in mind.
- **Take responsibility for the work.** Any time you propose changes to the
Zulip project, you are accountable for those changes. Do your very best to verify that they are correct.
- Don't submit code you don't understand — dig in to figure out what it's
doing, even if you didn't write it. This is a great way to catch bugs and
make improvements.
- Test the work carefully, even if others have tested it before. There may be
problems that the reviewers missed, or that were introduced by rebasing across other changes.
- **Give credit where credit is due.** Reviewers should be able to examine your
commit history and see that you have [properly credited](#credit-prior-work-in-your-commit-history)
the work of others.
- **Explain the relationship between your PR and prior work** in the description
for your pull request. This is required for your PR to be reviewed, as
reviewing a new PR when there is an existing one is a good use of time only if
the motivation for doing so is clear.
- If you started from an existing PR, explain what changes you made, and how
you addressed each point of reviewer feedback that hadn't been addressed previously.
- If you started from scratch, explain _why_ you decided to do so, and how
your approach differs from prior work. For example:
- "I didn't use the work in PR #12345, because the surrounding code has
changed too much since it was written."
- "I didn't use the work in PR #23154, because [this reviewer
comment](#present-your-pull-request) asked to solve this issue using CSS,
rather than the JavaScript changes made in #23154."
- "I didn't use the work in PRs #12345 and #23154, because both didn't work
properly when a user opened their own profile."
[completion-candidate]: https://github.com/zulip/zulip/pulls?q=is%3Aopen+is%3Apr+label%3A%22completion+candidate%22
[coauthor-git-guide]: https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors

View File

@@ -0,0 +1 @@
../../CONTRIBUTING.md

View File

@@ -0,0 +1,98 @@
# Counting contributions
The [Zulip team page](https://zulip.com/team/) displays commit counts for
contributors to Zulip projects. We display these statistics prominently because
they are easy to compute, and can be fun and motivating for some of our
contributors.
We do not consider commit count to be a good way to measure someone's
contributions to a software project. Many invaluable contributions may not
result in the contributor authoring any code at all, including design, feedback,
translations, bug reports, helping new contributors, and other types of
participation in our [development community][dev-community]. These non-code
contributions are essential to making the Zulip project successful.
Thus, Zulip's policy is to always express appreciation for non-code
contributions to the project whenever we discuss code contribution
statistics, especially in our highest-visibility contexts like release
blog posts.
## How the contribution stats are calculated
The data for all contributors to the Zulip project is aggregated by
querying [GitHub's API endpoint for listing a repository's
contributors][github-list-contrib-endpoint]. The numbers Zulip gets
from this endpoint differs slightly from the data GitHub displays in a
repository's [contributors page][github-contrib-page]. This
discrepancy is due to the following reasons:
- A repository's contributors page excludes commits with author email
addresses that do not have an associated GitHub account. However,
the GitHub API counts such emails in its contributor stats.
- A repository's contributors page does not count commits authored by
email addresses that have been deleted from a GitHub profile by the
user. However, the GitHub API treats these stale emails as
contributors as well.
- Some commits have multiple authors when that is declared via
[Co-Authored-By][co-authored-by] in a commit message.
## Old email addresses
If you remove an email address from your GitHub profile, the commits
you contributed with that email as the `GIT_AUTHOR_EMAIL` will
disappear from your GitHub profile. If you made contributions to Zulip
with an author email address that is no longer associated with your
GitHub profile, here are some important points to keep in mind:
- Zulip's team page will still display your contributions. These
contributions will be grouped under the email that was used for the
commits, but will not link to your GitHub profile.
- If you still have access to the email used for the contributions, you
can link the contributions to your GitHub profile by
[adding the email to your GitHub account][github-add-email].
- If you do not have access to the email in question, you can ask for
help on chat.zulip.org, or submit a pull request editing the
`.mailmap` file at the root of the repository where your
contributed.
The comments at the top of our `.mailmap` files document how to map
your old email address to one currently associated with your GitHub
account.
## Relevant source code
To dig deeper into how the contributor stats are calculated, please check
out the following files in the [Zulip server repository][server-repo]:
- `tools/fetch-contributor-data` - The script that fetches contributor
data from GitHub's API.
- `static/js/portico/team.js` - The JavaScript code that processes and
renders the data received from GitHub.
## Attribution for non-code contributions
As noted above, Zulip's policy is to express appreciation for non-code
contributions to the project whenever we discuss code contribution
statistics.
We do not specifically attribute non-code contributions by name, because the
logistics of giving individual attributions in a consistent and fair way across
50+ features in a release are far more than we have the capacity to manage.
For context, a significant feature usually involves a half-dozen people or more
helping with different parts of the work (suggesting ideas, providing technical
and non-technical feedback, etc.). Contributions can range from multiple rounds
of PR reviews to a 👍 reaction on someone else's suggestion. Even if one could
review everything that happened (in itself a daunting task), it may not be clear
which contributions should "count". At the same time, seeing that your name is
missing from a list of attributions where it belongs can feel really hurtful.
So, while we cannot thank everyone by name, please know that your contributions
are deeply appreciated!
[github-list-contrib-endpoint]: https://docs.github.com/en/rest/reference/repos#list-repository-contributors
[github-contrib-page]: https://docs.github.com/en/repositories/viewing-activity-and-data-for-your-repository/viewing-a-projects-contributors
[dev-community]: https://zulip.com/development-community/
[server-repo]: https://github.com/zulip/zulip
[github-add-email]: https://docs.github.com/en/account-and-profile/setting-up-and-managing-your-github-user-account/managing-email-preferences/adding-an-email-address-to-your-github-account
[co-authored-by]: https://docs.github.com/en/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors

View File

@@ -0,0 +1,296 @@
# Design discussions
We discuss ideas for improving Zulip's user experience, interface, and visual
design in the [Zulip development
community](https://zulip.com/development-community/). The purpose of these
design discussions is to help us make smart, well-informed decisions about
design changes to the Zulip product. We want Zulip to work great for a diverse
array of users and organizations, and discussions in the development community
are an incredibly valuable source of insight and ideas. We welcome all
perspectives, respectfully shared.
Most design discussions take place in the [#design][design channel] channel in the
development community. Discussions about mobile app design happen in
[#mobile-design][mobile-design channel],
and design of the terminal app is discussed in
[#zulip-terminal][zulip-terminal channel].
## Guidelines for all participants
Everyone is encouraged to participate in design discussions! Your participation
greatly helps improve the product, especially when you focus your contributions
on supporting the productivity of the design team. The more we are able to
incorporate a variety of ideas, experiences, and perspectives into the
discussion, the better decisions we'll be able to make.
Please start by reviewing the guide to [how we
communicate](how-we-communicate.md) in the Zulip community. Also, when sharing
your ideas:
- Think about corner cases and interactions with existing features that the
design will need to handle, and bring up problems with them, especially if they
are not obvious. (E.g., “This component also appears with a darker background
in the Drafts UI,” with a screenshot).
- Present technical considerations _where appropriate_. “X requires
some refactoring that would take me another hour,” is probably not
worth bringing up if X would produce a better user
experience. “Adding X might require removing feature Y,” or “X is
incompatible with Zulip's security model,” is important to present
early.
Note that [#design][design channel] is a high-traffic channel, and thoughtful
participation takes time. Don’t let it prevent you from doing your own work. It
can be helpful to pick particular conversations to follow, where you feel that
you have insight to share.
## Participant roles
At this point, it will be helpful to define a few key roles in design
discussions:
- [Code contributor](#guidelines-for-code-contributors): Anyone working on a PR
that includes some frontend changes.
- [Community moderator](#guidelines-for-community-moderators): Any core
contributor or other experienced community member who is helping guide the
discussion (with or without "moderator" permissions in the organization).
- **Design team**: Anyone working actively on the design of the feature at hand
and/or overall design for the Zulip product.
- [Decision makers](#guidelines-for-decision-makers): Project maintainers
responsible for design decisions, including design leaders, product leaders,
and overall project leadership.
## Guidelines for code contributors
When you are working on a PR that includes frontend changes, you may find it helpful
to get interactive feedback on the design. The best way to do so is by posting a
message in the appropriate design channel in the Zulip development
community: [#design][design channel] for the web/desktop app,
[#mobile-design][mobile-design channel] for the mobile app, or
[#zulip-terminal][zulip-terminal channel] for the terminal app.
### When to post
- The issue or a comment on your PR specifically asks you to get feedback in
[#design][design channel] or another design channel.
- The issue you’re working on is not specific about some design point, and you
would like advice.
- You’ve implemented an issue as described, but the UI doesn’t look good or
seems awkward to use.
- You’re prototyping an idea that’s not fully fleshed out.
### Guidelines for requesting design feedback
You will get the most helpful feedback by sharing enough context for community
participants to understand what you're trying to do, and clearly stating the
questions you are looking for feedback on. Some advice:
- Start a new topic, or use an existing one if there is a topic linked from the
issue you’re working on. If you’re starting a new topic, appending the issue
or PR number (e.g., `#1234`) to the topic name will turn it into a handy link.
- Summarize the feature you’re working on. You should provide enough
context for readers to understand your question, and include links
to any relevant issues or in-progress PRs for additional background.
- Post screenshots, and perhaps a short video if there is an interaction that
screenshots fail to show.
- Use the [guide on presenting visual
changes](../contributing/presenting-visual-changes.md) for guidance on what to
show, and how to capture precise Before/After screenshots when appropriate.
- You may want to post a few screenshots of different options you’re
considering.
- Screenshots should show enough of the app to evaluate how the new feature
looks in its context, but not so much that it’s hard to see the feature.
- Screen captures should demonstrate the feature with a minimal amount of
extraneous content.
- Post a clear question or set of questions that you need help with. What
specifically are you looking for feedback on?
- Since you’ve been working on this issue, you have likely gained some expertise
in this area. Educate others by sharing any tradeoffs and relevant
considerations you’re aware of.
Keep in mind that the Zulip community is distributed around the world, and you
should not expect to get realtime feedback. However, feel free to bump the
thread if you don’t see a response after a couple of business days.
## Guidelines for community moderators
Any experienced community participant can guide design discussions, and help
make sure that we use everyone's time productively towards making the best
decisions we can.
### Improving the quality of discussions
Here are some suggestions for how you can help the community have a productive
design discussion:
- If a design discussion seems to have been derailed by a tangent or argument,
consider moving the tangent to another topic so that the conversation can
refocus on the questions at hand.
- If the direction of the discussion seems unproductive, you can explicitly
suggest circling back to a topic where additional discussion seems valuable.
- If someone is repeating the same points in a way that’s unhelpful, you can let
them know that you understand what they are saying and appreciate their
feedback, but at this point would find it helpful to hear feedback from other
participants. People may sometimes repeat themselves because they are not feeling
heard.
- That said, sometimes the best way to deal with questions or feedback that
don’t move the discussion forward is to let them go by without comment, rather
than potentially getting into a protracted back-and-forth that derails the
thread. Examples of such feedback include unmotivated personal opinions,
proposals that ignore counterarguments that have already been discussed, etc.
- It’s totally fine to let the conversation slow down or die, especially if it
seems to be going off-track. If the decision makers feel that they do not have
enough feedback yet, they can revive the conversation as needed, and the pause
can serve as a good reset.
If a conversation is going off-track and you are not sure how to fix it, please
ping someone on the core team to intervene and help get the conversation into a
better state.
### Moving threads to the most appropriate channel
Sometimes it helps to move (part of) a thread to a different channel, so that
it's seen by the appropriate audience.
- We generally aim to discuss raw user feedback on the product’s design in
[#feedback](https://chat.zulip.org/#narrow/channel/137-feedback) (when it
applies to the web and desktop apps, or to all Zulip clients). The
[#design][design channel] should be reserved for design aspects that we’re
actively (considering) working on. This lets the design team focus on
discussions that are expected to result in actionable decisions.
- If a discussion that started in another channel has shifted into the design
phase, moving the discussion to [#design][design channel] helps the design team
follow the conversation.
- Discussion of implementation-related decisions should ideally happen in
non-design channels:
[#frontend](https://chat.zulip.org/#narrow/channel/6-frontend) for the web
app, and see our [guide to chat.zulip.org
channels](https://zulip.com/development-community/#channels-for-code-contributors)
for other Zulip codebases. The line can sometimes blur (and that’s OK), but we
should aim to move (parts of) the thread if there is an extensive conversation
that belongs in the other channel.
- Similarly, for changes to the mobile app:
- We aim to discuss raw user feedback in
[#mobile](https://chat.zulip.org/#narrow/channel/48-mobile).
The [#mobile-design][mobile-design channel] channel is reserved for design
aspects that we're actively (considering) working on.
- A discussion that has shifted into the design phase should be moved to
[#mobile-design][mobile-design channel], to help the design team closely
follow the discussion that calls for their input.
- Discussion of implementation details should move to
[#mobile-team](https://chat.zulip.org/#narrow/channel/243-mobile-team) or to
[#mobile-dev-help](https://chat.zulip.org/#narrow/channel/516-mobile-dev-help).
- Discussions of the mobile app's design should move to
[#mobile-design][mobile-design channel] (instead of #design), which the
mobile team follows closely.
Similarly, discussions of the design of the terminal app should move to
[#zulip-terminal][zulip-terminal channel].
## Guidelines for decision makers
The main purpose of design discussions is to help us make the best design
decisions we can. Decision makers should guide the conversation to elicit the
ideas, feedback and advice they need from the community.
Ideally, design discussions should also help us learn as a community. Community
members who follow the conversation should get a better understanding of the
considerations behind the decisions being discussed, and thus be better able to
contribute to the next conversation.
### Managing the discussion
Decision makers should actively manage the discussion to make sure we're making
good use of everyone's time and attention, and getting useful feedback.
- Decision makers should aim to follow design threads closely and provide input
early and often, so that conversations don’t get blocked waiting for their
opinion.
- Decision makers should actively manage discussion threads when needed in order
to seek the types of inputs that will help them. This may include outlining a
set of alternatives to consider, posing questions to dig into someone’s
feedback, asking for ideas to solve a specific design challenge, etc.
- Decision makers should explain the reasoning behind their proposed decisions,
so that it’s possible to identify any gaps in their thinking, and in order to
build a shared understanding in the community.
- That said, decision makers are not required to respond to every comment being
made regarding a proposal, or to answer every question.
### From discussion to decision
There are a number of factors that affect when it’s time to move a thread from
discussion to a decision. In part, this depends on how significant a commitment
we are making with the decision at hand:
- We want to be very thoughtful about decisions that will take a lot of work to
implement, and/or will be difficult to undo.
- We should try to come up with good designs for the features we're building,
but sometimes it's difficult to foresee how an interaction will feel until we
try it. Prototyping a UI we are not sure about is a normal part of the design
process.
- When the decision results in filing a non-urgent issue, it’s fine to write up
the conclusions on GitHub relatively quickly, and update the issue if more
ideas come in later on.
- We should accept that sometimes an idea we decided on is just not working out,
and be willing to go back to the drawing board or iterate further until we get
to a state we're happy with.
With those considerations in mind, here are rough guidelines for when to move on
to a decision:
- For very small decisions, it may be enough to get a sanity-check from one or
two well-informed community participants.
- For more significant decisions, one should generally allow at least 1-2 business
days for discussion, to give core team members time to share their perspective
if they have something to contribute.
- Beyond that minimum, the decision makers can move to the decision phase
whenever they have enough input to make a well-informed decision. Here are
some situations that would indicate that it’s time to move on:
- There is general consensus on how to proceed. Or, there is consensus
between the well-informed participants in the discussion.
- For a relatively small decision, there is enough useful feedback to
generate a solid proposal.
- If the discussion is primarily rehashing old points, and doesn’t seem to
be generating additional insights, it’s time to redirect the conversation
or move on to a decision.
- If the thread has died down, and the decision makers feel that they have
enough information to go on. (If they don’t, the thread can be bumped.)
[design channel]: https://chat.zulip.org/#narrow/channel/101-design
[mobile-design channel]: https://chat.zulip.org/#narrow/channel/530-mobile-design
[zulip-terminal channel]: https://chat.zulip.org/#narrow/channel/206-zulip-terminal

View File

@@ -0,0 +1,116 @@
# How we communicate
The primary communication forum for the Zulip community is the Zulip server
hosted at [chat.zulip.org](https://chat.zulip.org/). If you are not familiar
with it, start by reading the [Zulip development community
guide](https://zulip.com/development-community/). The guidelines here also apply
to how we communicate on GitHub issues and pull requests, but other pages in
this section go into greater detail about expectations that are specific to pull
requests.
We are deeply committed to maintaining a respectful, collaborative atmosphere
across all interactions in the Zulip community. To get a feel for what that
means to us, please review the [code of conduct](../code-of-conduct.md) for our
community.
Beyond that, the following guidelines should help you communicate effectively to
express your perspective, and support and encourage other participants in the
community. By incorporating these patterns of behavior, we'll be able to reach
better decisions as a group, and learn and have fun along the way.
## Providing suggestions and feedback
- Aim to present your feedback precisely, with reasoning, and in as objective a
fashion as you can manage. E.g., “This button really jumps out at me in a way
that’s distracting; maybe it’s because of the color has a higher contrast than
the surrounding components?” is better than, “Can we make that color less
dark?”.
- Clarify your feedback if there are follow-up questions or points of confusion.
However, avoid simply repeating the same points, as it does not move us closer
to making the best decisions we can.
- When relevant, highlight information you have beyond your personal opinion.
E.g., “I moderate a community, and often have to answer questions about how
this works,” is more helpful than, “This is confusing.”
- In addition to offering constructive feedback, call out specific things that
someone is doing well. This helps folks feel good about their work, and also
helps them learn. E.g., “I particularly like the test you added, because...”
- When asking someone to do something, explain the reasoning behind your
request. This is more motivating, and educates folks on what to do next time.
- If you are not certain about a suggestion you're making, it often works well
to frame it as a question. E.g., “Would this be clearer if we...?” or “Could
we...?”.
- Invite others to disagree or contribute additional thoughts, especially if you
might be considered an authority in the area being discussed. When more folks
feel comfortable speaking up, we are better able to identify problems and
fine-tune solutions.
## Handling disagreements
- Always treat other participants in the discussion with respect, regardless of
whether you agree with their ideas. Ad hominem attacks are never appropriate.
- Approach discussions with a perspective of curiosity. If someone has expressed
an opinion you find odd or surprising, ask them to explain in more detail
where they are coming from.
- If you think someone is factually mistaken, consider how they might have reached
their conclusion, and aim to get to a shared understanding. For example:
- “I wasn't able to replicate this -- is it possible you are on an old Zulip
server?”, rather than “This bug report is wrong.”
- “Using this function won't work here, because...” instead of “That's the
wrong function.”
- If you disagree with someone on the core team, the best way to make progress
is usually to state your opinions and reasoning clearly and respectfully, and
then let the other core team members catch up on the conversation. Other
project members may find your argument persuasive, and may have ideas that
address your concerns.
## Expressing your appreciation
There are many ways to contribute to the Zulip community, and it's important to
express our appreciation for all the different ways in which folks jump in to
help. It helps motivate others, builds consensus towards decisions, and creates
a more positive atmosphere in the community.
For many community participants, it takes stepping out their comfort zone to try
something for the first time, such as submitting a pull request, answering a
question in the development community, or suggesting a new feature. It's
especially important to thank and encourage folks who are stretching themselves
to try something new.
- Remember to say “thanks” when responding to a question or suggestion. For
example:
- “Thanks for the report! ... ” when someone reports a bug.
- “Thanks for reviewing my PR! ... ”
- Express your appreciation for the work that went into a pull request, even
when it turns out that the approach taken in the PR is not successful. E.g.,
“Thanks for working on this! ... ”
- Try especially hard to express your appreciation for others' contributions,
effort and ideas when you are also providing negative (constructive) feedback
on their work.
- You can use a variety of channels to express your appreciation. A comment
directly in a Zulip thread or on a pull request is often best, but in some
cases you may also want to send a friendly direct message. For example:
- “I've noticed that you've been answering lots of questions in #**development
help** lately. Thanks so much for doing that!”
- “Thanks for moving those messages into a new topic -- the discussion was
getting really tangled!”
- Sometimes a quick emoji reaction can help someone feel that their work is
appreciated. 🎉
- Look over the message you just sent. Did you forget to say “thanks”? It's easy
to edit your message, or send a quick follow-up.

View File

@@ -0,0 +1,26 @@
# Contributing to Zulip
```{toctree}
---
maxdepth: 3
---
contributing
../code-of-conduct
how-we-communicate
asking-great-questions
design-discussions
commit-discipline
code-style
presenting-visual-changes
code-reviewing
reviewable-prs
review-process
continuing-unfinished-work
zulipbot-usage
reporting-bugs
reporting-security-vulnerabilities
suggesting-features
counting-contributions
licensing
```

View File

@@ -0,0 +1,40 @@
# Licensing
Zulip is distributed under the [Apache 2.0
license](https://github.com/zulip/zulip/blob/main/LICENSE). This means that all
contributions to Zulip — code, images, sounds, etc. — must be compatible with
this license.
## Contributing your own work
If the work you are contributing is 100% your own, Zulip doesn't require you to
sign a copyright assignment or a contributor license agreement for your
contribution. As noted [in the GitHub Terms of
Service](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service#6-contributions-under-repository-license):
> “Whenever you add Content to a repository containing notice of a license, you
> license that Content under the same terms, and you agree that you have the
> right to license that Content under those terms... This is widely accepted as
> the norm in the open-source community; ...”
## Contributing someone else's work
If any part of your contribution is from someone else (code snippets, images,
sounds, or any other copyrightable work, modified or unmodified), you need to
check whether it was distributed under an open-source license that is
[compatible][wiki-compatibility] with the [Apache 2.0
license](https://github.com/zulip/zulip/blob/main/LICENSE). If it was not, this
work cannot be contributed to Zulip, with or without modification.
[wiki-compatibility]: https://en.wikipedia.org/wiki/License_compatibility#Compatibility_of_FOSS_licenses
If you have verified that the work is OK to use, you will need to provide
appropriate attribution in the [`docs/THIRDPARTY`
file](https://github.com/zulip/zulip/blob/main/docs/THIRDPARTY) as part of your
PR. Please refer to the [`debian/copyright` file
documentation](https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/)
for a detailed description (including examples) of how to correctly describe the
copyright and licensing information in your PR.
If you are not sure about the licensing or attribution for your work, please
include the full details of your open questions in the description for your PR.

View File

@@ -0,0 +1,390 @@
# Presenting visual changes
If your pull request makes visual changes, or includes refactoring that might
cause visual changes, you must include screenshots demonstrating the impact of
your changes in the description for your pull request.
**Always make sure the screenshots in the PR description capture the _current_
state of the PR.** To avoid confusion, delete outdated screenshots from the
description. If reviewers need to see multiple variants of the UI to make some
decision, include those variants in the comments.
## What to capture
Always include screenshots of _all_ the UI components and states affected by
your PR. For example, a PR that adds a new dropdown setting would need
screenshots to demonstrate:
- What the setting looks like in the settings UI (with some surrounding context).
- The open dropdown.
- The disabled version of the setting with its accompanying tooltip that would
be shown to users who aren't allowed to modify the setting.
- Visual impact of using each value of the setting (if any).
- Updated help center documentation (if any); see
[note](#presenting-documentation-changes) below.
If you're using standard components, there's no need to go overboard with
screenshots:
- Show the Before state if it provides helpful context (e.g., if you're changing
text on a banner), but otherwise you can skip them (e.g., if you're adding a
setting).
- There's usually no need to show light and dark theme screenshots, or
screenshots at different font sizes.
- There's usually no need to show the keyboard selection UI.
However, if your pull request makes any CSS changes, you'll need to thoroughly
demonstrate its effects. You will need to **capture precise screenshots**
demonstrating the differences between the Before and After states, as described
[below](#capturing-precise-screenshots).
Precise screenshots are necessary not only to document your intended
changes, but also to demonstrate that there are **no unintended side-effects**
or visual regressions. In fact, PRs that refactor templates or CSS often need to
include screenshots to explicitly demonstrate that there are no visual changes.
:::{important}
Wherever possible, use still screenshots rather than videos.
:::
Static screenshots are much easier to review than screencast videos. Use a video
only when necessary to demonstrate an interaction, and only to supplement still
screenshots. Always include screenshots for any aspects of your changes which
can be seen on a still screenshot. See
[below](#capturing-and-presenting-short-videos) for how to capture reviewable videos.
For updates or changes to CSS class rules, it's a good practice
to include the results of a [`git grep`][git-grep] search for
the class name(s) to confirm that you've tested and captured screenshots
of all the impacted areas of the UI or Zulip documentation.
```console
$ git grep '.example-class-name' web/templates/ templates/
templates/corporate/...
templates/zerver/...
web/templates/...
```
Never include screenshots of your code or the GitHub UI, which reviewers can
easily see directly.
## What are precise Before/After screenshots?
Precise Before/After screenshots have two essential qualities:
1. **The state of the UI should be identical between screenshots.**
For example, a screenshot of the message feed should have messages scrolled to the
same spot, the focus ring should be around the same message, etc. The goal is to
highlight the differences your PR introduces, and not include any unintended noise in
the screenshots from things that just happen to be different.
1. **The screenshots must have the exact same pixel dimensions.** If the Before
screenshot is 800px by 600px, the After screenshot should be too. This is
to make sure that there's a pixel-to-pixel correspondence of everything
you've captured onscreen.
## Why bother with precise Before/After screenshots?
Capturing precise screenshots serves two primary purposes:
1. For you as a contributor, **checking your own screenshots** will help you to
catch any regressions on your own, prior to submitting your PR. It'll also
help you point out changes or shifts for reviewers to consider, which may
require some design discussion.
2. For reviewers, checking screenshots is a quick way to **verify** that a PR
does what's expected, and provide **feedback** on visual changes without
pulling down the PR.
## Capturing precise screenshots
This section describes different techniques for producing pixel-precise
screen captures. To master these techniques, you'll likely need to spend some time
working with your capture tools of choice, whether they're part of your browser or
operating system, or a third-party screen capture tool or browser plugin.
### Using Git to get required states
Git is, of course, your friend for setting up the UI state for Before and After
screenshots: the Before shot will typically be of the `main` branch updated from
upstream, while the After shot will be of your PR's working branch. Take the time
to rebase your working branch over the latest `main`, too, before capturing your
screenshots.
Sometimes you may be asked to present alternative looks to your PR in tandem
with a discussion [#design](https://chat.zulip.org/#topics/channel/101-design)
channel in the Zulip development community, in which case you'll need to walk
through different commits on your branch—or make judicious use of `git stash`
and `git stash pop`.
### Capturing precise screenshots with your browser
The easiest cross-platform way of capturing screenshots of your PR's changes
is to use your browser. Both Chrome and Firefox have built-in screenshot capabilities,
so it's worth your time to explore how those work. Firefox's capabilities are neatly
tied to its responsive design view, which means you can very precisely size the viewport
to highlight the changes in your PR. (And if you're not used to developing in Firefox,
this can be a convenient way to check your work in a different browser, too.)
For example, it's easy to capture a mobile-scale viewport in Firefox like this:
| In responsive mode for capture | Resulting screenshot |
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| ![Firefox in responsive design mode](/images/firefox-rwd-capture-mobile.png) | ![Mobile-scale capture from Firefox](/images/firefox-mobile-scale-350x600.png) |
For another example, if you're making changes to the left sidebar, you'll want to open
the viewport wider, until the left sidebar is showing:
![Firefox in opened wider to show sidebar](/images/firefox-rwd-capture-medium.png)
The great thing about capturing screenshots in your browser is that it is very easy to ensure that you meet both criteria for precise screenshots: a UI in the same state for
the Before and After shots, and screenshots that are the same exact dimensions—easily verified by the values you set on the width and height of the viewport.
Additionally, if you need to demonstrate states like `:hover` or `:active` on an element,
you can use your browser's developer tools to emulate that state for you, without your
needing to fiddle with the keyboard or mouse.
### Capturing precise screenshots with native OS tools
Most of the time, browser-captured screenshots are ideal. Screens are generally
high-enough resolution that your reviewers (and you) can quickly recognize any
changes just by comparing the full-size screenshots.
However, if you really do need to illustrate a change in closer detail, it can be helpful
to use your operating system's native screen-capture tools. See if your OS allows
you to specify a box of fixed dimensions that you can use to repeatedly capture the same
area of the screen.
For example, perhaps there's something very subtle about the channel list that a PR needs
to highlight:
![Fixed region capture using OS tools](/images/os-fixed-capture-box.png)
Note that [a fixed, reusable capture box is different from a freehand capture](#freehand-versus-fixed-screenshots),
because the OS enables you to call up the same fixed-size box for repeated screenshots
of the same region of the screen, helping you to maintain pixel precision in the
Before and After shots. Just be careful not to move your browser window around between
shots.
### Capturing precise screenshots with other software
If you find that your operating system or browser's tools are insufficient for capturing
the screenshots that you need, you might want to explore [third-party software](#screenshot-and-gif-software). Just keep
in mind the two qualities your screenshots need to maintain: identical UI state, and
pixel-precise dimensions between Before and After shots.
### Freehand versus fixed screenshots
It may be tempting to freehand your screenshots, perhaps by using your operating
system's screen-capture utility that allows freehand dragging of a capture area.
The two freehand-captured images below represent Before and After states. Can you
spot any changes to the UI? How about if you open each image in a separate tab in
your browser? (Typically a reviewer will open up the full-size images in separate
tabs in order to more easily compare them.)
| Before | After |
| --------------------------------------------------------------- | ------------------------------------------------------------- |
| ![Freehand screenshot, before](/images/freehand-cap-before.png) | ![Freehand screenshot, after](/images/freehand-cap-after.png) |
Here's an animated GIF approximating a comparison of the full-size images:
![Comparing freehand screenshots](/images/freehand-capture-compare.gif)
Compare those to this set of screenshots. These are the same Before and After
states as captured in the freehand shots above, but these have been captured using
a fixed box offered by an operating system's native screen-capture utility:
| Before | After |
| --------------------------------------------------------- | ------------------------------------------------------- |
| ![Fixed screenshot, before](/images/fixed-cap-before.png) | ![Fixed screenshot, after](/images/fixed-cap-after.png) |
Now see if you can catch the change between the Before and After images in this GIF:
![Comparing fixed screenshots](/images/fixed-capture-compare.gif)
Those screenshots make it obvious that the space between the Zulip logo and the top
of the message area has been reduced. Without precise screenshots, that change
would've been nearly impossible to detect.
### Providing sufficient UI context
The images above might have been intended to show close details of the Zulip logo
and message area. But the contributor could have also just used their browser
(Firefox, in this case) to capture more of the viewport:
| Before | After |
| ----------------------------------------------------------------- | --------------------------------------------------------------- |
| ![Browser screenshot, before](/images/browser-capture-before.png) | ![Browser screenshot, after](/images/browser-capture-after.png) |
Even if your work is targeted at a specific element, detailed screenshots are not
always the best choice. CSS adjustments are notorious for having unintended consequences,
so it's often a smart choice to show as much of the UI as you can.
As with the fixed-box screenshots above, having pixel-precise screenshots
where the UI is in an identical state and there isn't a bunch of unintended movement
or other noise, the browser captures above make catching the space shift much easier.
## Capturing and presenting short videos
Still screenshots are almost always superior to videos. They are easier to compare, and
enable reviewers to focus more precisely on the changes your PR introduces.
If you absolutely must show off a brief interaction on your PR, see whether your operating
system will allow you to again set a fixed box for capturing video of a region of your
screen.
Be sure to edit your video down to the shortest length possible. And rather
than post the video—whose format may not be suitable for the web or accessible to all
reviewers—use a video-to-GIF service like [ezgif.com](https://ezgif.com/video-to-gif)
to create a looping animation showing off the interaction.
Here, for an example, is a short looping animation from [an actual PR](https://github.com/zulip/zulip/pull/35969)
that modified some interactions on the compose box:
![Animation showing compose box](/images/general-chat-after.gif)
It's worth noting here that [the full PR](https://github.com/zulip/zulip/pull/35969)
included plenty of static screenshots in addition to the GIFs showing off interactivity.
Finally, make sure a person watching your video can see what you're typing, or where on
the screen you're tapping or clicking. Use the "show touches" or "include the mouse
pointer" feature if your screen-recording software supports it. To show keyboard
interactions, e.g., using a [keyboard shortcut](https://zulip.com/help/keyboard-shortcuts),
you can use a [keypress visualizer](#keypress-visualizers-by-platform) while recording.
## Presenting screenshots on your pull request
Now that you've got all these screenshots, how do you present them in the
description for your PR?
### Using tables for Before/After comparisons
For presenting Before and After states, use GitHub's table syntax to render
them side-by-side. Reviewers can open the full-size image from GitHub for quick
and easy comparison:
```
### Descriptive header for images:
| Before | After |
| --- | --- |
| ![image-before](uploaded-file-information) | ![image-after](uploaded-file-information)
```
To quickly insert these tables, consider
[creating a GitHub Saved Reply](https://docs.github.com/en/get-started/writing-on-github/working-with-saved-replies/creating-a-saved-reply).
### Making screenshots collapsible
When including numerous screenshots or screencasts, consider grouping them
in details/summary tags to reduce visual clutter and the scroll length of
pull-request descriptions and comments. This is especially useful when you
have several screenshots or screencasts to include in your PR description
or follow-up comments, as you can put groups of related images in separate
details/summary tags.
Do think about your reviewers' comfort when using more than one set of
details/summary tags, as it is generally much easier to scroll past a
bunch of screenshots than it is to open numerous collapsed details/summary
tags.
```
<details>
<summary>Descriptive summary of image group</summary>
| Before | After |
| --- | --- |
| ![image-before](uploaded-file-information) | ![image-after](uploaded-file-information) |
| ![image-2-before](uploaded-file-information) | ![image-2-after](uploaded-file-information) |
</details>
```
### Presenting documentation changes
If you've updated existing documentation in your pull request,
include a link to the current documentation above the screenshot
of the updates. That way a reviewer can quickly compare the current
documentation while reviewing your changes.
```
[Current documentation](link-to-current-documentation-page)
![image-after](uploaded-file-information)
```
[git-grep]: https://git-scm.com/docs/git-grep
[screenshots-gifs]: ../contributing/presenting-visual-changes.md#screenshot-and-gif-software
[zulip-dev-community]: https://chat.zulip.org
[link-to-message]: https://zulip.com/help/link-to-a-message-or-conversation#get-a-link-to-a-specific-message
[dev-community-linkifiers]: https://zulip.com/development-community/#linking-to-github-issues-and-pull-requests
## Screenshot and GIF software
You can check out the following tools for capturing screenshots and videos/GIFs.
### Screenshot tools by platform
#### Browser
- [Firefox screenshots](https://support.mozilla.org/en-US/kb/take-screenshots-firefox) without any plugins
- [Chrome screenshots](https://developer.chrome.com/docs/devtools/device-mode#screenshot) without any plugins
- [LightShot Screenshot](https://app.prntscr.com/en/index.html) (Chrome, Firefox, IE & Opera)
- [Chrome Capture](https://chrome.google.com/webstore/detail/chrome-capture-screenshot/ggaabchcecdbomdcnbahdfddfikjmphe?hl=en)
#### macOS
- [Take a screenshot on Mac](https://support.apple.com/en-us/102646)
- [LightShot Screenshot](https://app.prntscr.com/en/index.html)
- [Gyazo](https://gyazo.com/en)
#### Windows
- [Use Snipping Tool to capture screenshots](https://support.microsoft.com/en-us/windows/use-snipping-tool-to-capture-screenshots-00246869-1843-655f-f220-97299b865f6b)
- [LightShot Screenshot](https://app.prntscr.com/en/index.html)
- [Gyazo](https://gyazo.com/en)
- [ScreenToGif](https://www.screentogif.com/)
#### Linux
- [GNOME Screenshots and screencasts](https://help.gnome.org/gnome-help/screen-shot-record.html)
- [Spectacle by KDE](https://apps.kde.org/spectacle/)
### GIF tools by platform
#### Browser
- [GIPHY](https://giphy.com)
- [Chrome Capture](https://chrome.google.com/webstore/detail/chrome-capture/ggaabchcecdbomdcnbahdfddfikjmphe?hl=en)
(Tip: Use `Alt`+`I` to interact with the page while recording)
#### macOS
- [QuickTime](https://support.apple.com/en-in/HT201066)
- [GIPHY](https://giphy.com/apps/giphycapture)
- [Zight](https://zight.com)
- [Kap](https://getkap.co)
- [Gifski](https://sindresorhus.com/gifski)
- [Gyazo GIF](https://gyazo.com/en)
#### Windows
- [ScreenToGif](https://www.screentogif.com)
- [Gyazo GIF](https://gyazo.com/en)
#### Linux
- [Peek](https://github.com/phw/peek)
- [SilentCast](https://github.com/colinkeenan/silentcast)
### Keypress visualizers by platform
#### macOS
- [KeyCastr](https://github.com/keycastr/keycastr)
#### Windows
- [YetAnotherKeyDisplayer(YAKD)](https://github.com/Jagailo/YetAnotherKeyDisplayer)
#### Linux
- [hibiki](https://github.com/linuxmobile/hibiki), for Wayland.
- [screenkey](https://gitlab.com/screenkey/screenkey), for X11.

View File

@@ -0,0 +1,157 @@
# Reporting bugs
There are several ways to report bugs (or possible bugs) you encounter in Zulip:
- If you have a concrete bug report with steps to reproduce the behavior, [file an
issue](#filing-a-github-issue) in the appropriate GitHub repository.
- If you are not sure whether the issue you encountered is a bug, or how to
reproduce it, [start a
conversation](#starting-a-conversation-about-a-possible-bug) in the Zulip
development community.
- To report a possible security issue, contact Zulip's security team at
[security@zulip.com](mailto:security@zulip.com). _Do not_ report security issues
publicly (in GitHub or in the Zulip development community). We create a CVE for
every security issue in our released software.
- If reporting a bug requires sharing private details about your
organization, email [support@zulip.com](mailto:support@zulip.com).
No matter where you report the bug, please follow the instructions below for
what to include in a bug report.
## What to include in a bug report
1. **Describe** what you were expecting to see, what you saw instead, and steps
that may help others reproduce the surprising behavior you experienced.
Include screenshots and/or screen captures (see
[recommended tools](../contributing/presenting-visual-changes.md#screenshot-and-gif-software))
if they help communicate what you are describing, but avoid posting long videos.
1. **Indicate the [version](https://zulip.com/help/view-zulip-version)** of the
Zulip app where you encountered the bug. It may also be helpful to note your
operating system, whether you are using the web app or the desktop app, and
your browser if using the web app.
## Filing a GitHub issue
Filing a GitHub issue works best when:
- You are confident that the behavior you encountered is a bug, not some quirk
of how a feature works that may turn out to be intentional.
- You can describe clearly what you were expecting to see, and what you saw instead.
- You can provide steps for someone else to reproduce the issue you encountered.
This is important for developers to be able to fix the bug, and test that
their fix worked.
If all of the above accurately describe your situation, please file an issue!
Otherwise, we recommend [starting a
conversation](#starting-a-conversation-about-a-possible-bug) in the Zulip
development community so that the problem you encountered can be discussed
interactively.
Steps and best practices for filing a GitHub issue:
1. Report the issue in the **appropriate [Zulip
repository](https://github.com/zulip)**. The most commonly used repositories
are:
- [zulip/zulip](https://github.com/zulip/zulip/issues) for issues with the
Zulip web app or server. A good default if you aren't sure which repository
to use.
- [zulip/zulip-flutter](https://github.com/zulip/zulip-flutter/issues) for
issues with the mobile apps.
- [zulip/zulip-desktop](https://github.com/zulip/zulip-desktop/issues) for
issues that are specific to the Zulip desktop app, and therefore _do not_
occur in the web app.
- [zulip/zulip-terminal](https://github.com/zulip/zulip-terminal/issues) for
issues with the terminal app.
2. Do a **quick search** of the repository to see if your issue has already
been filed. If it has, you can add a comment if that seems helpful.
3. If you are aware of a related discussion in the Zulip development community,
please **cross-link** between the issue and the discussion thread. [Link to a
specific
message](https://zulip.com/help/link-to-a-message-or-conversation#get-a-link-to-a-specific-message)
in the discussion thread, as message links will still work even if the topic is
renamed or resolved.
To encourage prompt attention and discussion for a bug report you have filed,
you can send a message in the Zulip development community with the key points
from your report. Be sure to [link to the GitHub
issue](https://zulip.com/development-community/#linking-to-github-issues-and-pull-requests).
See the following section for advice on where and how to start the conversation.
## Starting a conversation about a possible bug
If you are not sure whether the issue you encountered is a bug, or how to
reproduce it, we highly recommend reporting it in the [Zulip development
community](https://zulip.com/development-community/). It's the best place to
interactively discuss your problem.
Steps and best practices for starting a conversation:
1. [**Join** the Zulip development
community](https://zulip.com/development-community/) if you don't already
have an account.
2. Pick an **appropriate channel** to report your issue:
- [#issues](https://chat.zulip.org/#narrow/channel/9-issues) for issues with
the Zulip web app or server. Use this channel if you aren't sure which
channel is most appropriate.
- [#mobile](https://chat.zulip.org/#narrow/channel/48-mobile) for issues with
the mobile apps.
- [#desktop](https://chat.zulip.org/#narrow/channel/16-desktop) for issues
that are specific to the Zulip desktop app, and therefore _do not_
occur in the web app.
- [#zulip-terminal](https://chat.zulip.org/#narrow/channel/206-zulip-terminal)
for issues with the terminal app.
- [#production
help](https://chat.zulip.org/#narrow/channel/31-production-help) for issues
related to self-hosting Zulip. See the [troubleshooting
guide](../production/troubleshooting.md) for additional details.
3. **[Start a new topic](https://zulip.com/help/introduction-to-topics#how-to-start-a-new-topic)**
for discussing your issue, using a brief summary of the issue as the name of
the topic.
If you aren't sure where to post or how to name your topic, don't worry!
Moderators can always rename the topic, or move the thread to another channel.
Once a possible bug is reported, members of the development community will jump
in to discuss whether the report constitutes a bug, how to reproduce it, and how
it can be resolved. The initial reporter can help by monitoring the discussion
and replying to any follow-up questions. If the report is determined to be a
reproducible bug, a GitHub issue will be filed to keep track of it (see below).
## Managing bug reports
This section describes our process for managing bugs. All community members are
encouraged to help make sure this process runs smoothly, whether or not they
originally reported the bug.
Whenever a bug is tracked in GitHub and also discussed in the development
community, be sure to cross-link between the issue and the conversation. [Link
to a specific
message](https://zulip.com/help/link-to-a-message-or-conversation#get-a-link-to-a-specific-message)
in the discussion thread, as message links will still work even if the topic is
renamed or resolved.
- If you encounter a definite bug with a clear reproducer and significant user
impact, it is best to both file a GitHub issue and immediately start a
discussion in the development community. This helps us address important
issues as quickly as possible.
- For minor bugs (e.g., a visual glitch in a settings menu for very long channel
names), filing a GitHub issue is sufficient.
- If a potential bug discussed in the development community is confirmed to be
an actual, reproducible bug, anyone can help out by filing a GitHub issue to
track it:
- In some cases, especially if we're planning to fix the issue right away, the
GitHub issue description can simply quote and link to a message from the
discussion in the development community -- no need to stress over making it
perfect.
- [Use Zulipbot](../contributing/zulipbot-usage.md) to add the appropriate
labels, including “bug” and at least one area label; leave a comment on
the issue if you don't know what area labels to use.
- You can add the “help wanted” label (and claim the issue if you like) if
that is appropriate based on the discussion. Note that sometimes we won't
mark a reproducible bug as “help wanted” for various reasons. For example,
we might want a core contributor to take it on, or the fix might be planned
as part of a larger project.
- Don't forget to cross-link between the issue and the discussion.
- If a bug report in GitHub is not sufficiently clear, Zulip maintainers will
often encourage the reporter to discuss it interactively in the development
community.

View File

@@ -0,0 +1 @@
../../SECURITY.md

View File

@@ -0,0 +1,135 @@
# Pull request review process
Pull requests submitted to Zulip go through a rigorous review process, which is
designed to ensure that we are building a high-quality product with a
maintainable codebase. This page describes the stages of review your pull
request may go through, and offers guidance on how you can help keep your pull
request moving along.
## Labels for managing the stages of pull request review
In the Zulip server/web app repository
([`zulip/zulip`](https://github.com/zulip/zulip/)), we use GitHub labels to help
everyone understand where a pull request is in the review process. These labels
are noted below, alongside their corresponding pull-request stage. Each label is
removed by the reviewer for that stage when they have no more feedback on the PR
and consider it ready for the next stage of review.
Sometimes, a label may also be removed because significant changes by
the contributor are required before the PR ready to be reviewed again. In that
case, the contributor should post a comment mentioning the reviewer when the
changes have been completed, unless the reviewer requested some other action.
## Stages of a pull request review
This section describes the stages of the pull request review process. Each stage
may require several rounds of iteration. Don't feel daunted! Not every PR will
require all the stages described, and the process often goes quite quickly for
smaller changes that are clearly explained.
1. **Product review.** Oftentimes, seeing an initial implementation will make it
clear that the product design for a feature needs to be revised, or that
additional changes are needed. The reviewer may therefore ask you to amend or
change the implementation. Some changes may be blockers for getting the PR
merged, while others may be improvements that can happen afterwards. Feel
free to ask if it's unclear which type of feedback you're getting.
(Follow-ups can be a great next issue to work on!)
Your PR might be assigned the [product
review](https://github.com/zulip/zulip/pulls?q=is%3Aopen+is%3Apr+label%3A%22product+review%22)
label at this stage, or later in the review process as questions come up. You
can also add this label yourself if appropriate. If doing so, be sure to
clearly outline the product questions that need to be addressed.
2. **QA.** If your PR makes user-facing changes, it may get a round of testing
without reference to the code. You will get feedback on any user-facing bugs
in the implementation. To minimize the number of review round-trips, make
sure to [thoroughly test](../contributing/code-reviewing.md#manual-testing)
your own PR prior to asking for review.
Your PR might be assigned the [QA
needed](https://github.com/zulip/zulip/pulls?q=is%3Aopen+is%3Apr+label%3A%22QA+needed%22)
label at this stage, or later on if re-testing is required.
3. **Initial code review.** All PRs will go through one or more rounds of code
review. Your code may initially be [reviewed by other
contributors](../contributing/code-reviewing.md). This helps us make good use
of project maintainers' time, and helps you make progress on the PR by
getting quick feedback. A project maintainer may leave a comment asking
someone with expertise in the area you're working on to review your work.
4. **Maintainer code review.** In this phase, a Zulip maintainer will do a
thorough review of your proposed code changes. Your PR may be assigned the
[maintainer
review](https://github.com/zulip/zulip/pulls?q=is%3Aopen+is%3Apr+label%3A%22maintainer+review%22)
label at this stage.
5. **Documentation review.** If your PR includes documentation changes, those
changes will require review. This generally happens fairly late in the review
process, once the UI and the code are unlikely to undergo major changes.
Maintainers may indicate that a PR is ready for documentation review by
adding a [help center
review](https://github.com/zulip/zulip/pulls?q=is%3Aopen+is%3Apr+label%3A%22help+center+review%22)
and/or [api docs
review](https://github.com/zulip/zulip/pulls?q=is%3Aopen+is%3Apr+label%3A%22api+docs+review%22)
label, and mentioning a documentation maintainer in the comments.
6. **Integration review**. This is the final round of the review process,
generally done by `@timabbott` for server and web app PRs. A maintainer will
usually assign the [integration
review](https://github.com/zulip/zulip/pulls?q=is%3Aopen+is%3Apr+label%3A%22integration+review%22)
label when the PR is ready for this phase.
## How to help move the review process forward
If there are no comments on your PR for a week after you submit it, you can
check again to make sure that it's ready for review, and then post a quick
comment to remind Zulip's maintainers to take a look at your work. Consider also
[asking for a
review](../contributing/code-reviewing.md#asking-for-a-code-review) in the Zulip
development community.
After that, the key to keeping your review moving through the review process is to:
- Address _all_ the feedback to the best of your ability.
- Make it clear when the requested changes have been made
and you believe it's time for another look.
- Make it as easy as possible to review the changes you made.
In order to do this, when you believe you have addressed the previous round of
feedback on your PR as best you can, post a comment asking reviewers to take
another look. Your comment should make it easy to understand what has been done
and what remains by:
- Summarizing the changes made since the last review you received. Be clear
about which issues you've resolved (and how!).
- Highlighting remaining questions or decisions, with links to any relevant
threads in the [Zulip development
community](https://zulip.com/development-community/). It should be clear which
feedback you _haven't_ addressed yet.
- Providing updated screenshots and information on manual testing if
appropriate.
The easier it is to review your work, the more likely you are to receive quick
feedback.
## Follow-ups
Sometimes reviewers will comment on "follow-ups" to your pull request. A
follow-up is something that can be done separately from the changes in the
current pull request, because it's tangential to the current changes, or would
be the next task to do once the current changes are completed.
You should prioritize completing the current pull request over working on
follow-ups. However, you are expected to manage follow-ups to your work:
1. If possible, it's best to complete follow-ups as soon as you are done with
the current pull request. That way, both you and reviewers have the context
for the changes fresh in mind.
1. If you don't expect to have time to complete a follow-up soon (or aren't sure
if you will), please file an issue to track it. Include a description of the
required changes, and a link to the comment where the follow-up was
discussed. You can copy-paste the issue description from the prior
discussion, but be sure that what you write makes sense without needing to
refer to the original thread.

View File

@@ -0,0 +1,168 @@
# Submitting a pull request
A pull request (PR) is a presentation of your proposed changes to Zulip. Your aim
should be to explain your changes as clearly as possible. This will help
reviewers evaluate whether the proposed changes are correct, and address any
open questions. Clear communication helps the whole Zulip project move more
quickly by saving maintainers time when they review your code. It will also make
a big difference for getting your work integrated without delay.
You will go through the following steps to prepare your work for review. Each
step is described in detail below, with links to additional resources:
1. Write your code [with clarity in mind](#write-clear-code).
1. [Organize your proposed changes](#organize-your-proposed-changes) into a
series of commits that tell the story of how the codebase will change.
1. [Explain your changes](#explain-your-changes) in the description for your
pull request, including
[screenshots](../contributing/presenting-visual-changes.md) for visual changes.
1. Carefully [review your own work](#review-your-own-work).
1. [Submit your pull request](#submit-your-pull-request-for-review) for review.
To learn more about putting together pull requests maintainers will love to
review, check out [this FOSDEM
talk](https://fosdem.org/2026/schedule/event/L7ERNP-prs-maintainers-will-love/)
by a Zulip maintainer.
See the [pull request review process](../contributing/review-process.md) guide
for a detailed overview of what happens once your pull request is submitted.
## Write clear code
When you write code, you should make sure that you understand _why it works_ as
intended. This is the foundation for being able to explain your proposed changes
to others.
Zulip’s coding philosophy is to focus relentlessly on making the codebase easy
to understand and difficult to make dangerous mistakes. Our linters, tests, code
style guidelines, [testing philosophy](../testing/philosophy.md), [commit
discipline](../contributing/commit-discipline.md), this documentation, and our
attention to detail in [code review](../contributing/review-process.md) are all
important elements of this strategy. Following these guidelines is essential if
you'd like your work to be merged into the project.
If any part of your contribution is from someone else (code snippets, images,
sounds, or any other copyrightable work, modified or unmodified), be sure to
review the instructions on how to [properly attribute](./licensing.md) the work.
## Organize your proposed changes
The changes you submit will be organized into a series of commits. A PR might
contain a single commit, or a dozen or more, depending on the changes being
made.
Commits help you tell the story of how each change you are proposing is
necessary or helpful. If you were presenting your changes, a commit might be a
slide in your presentation. As a rough guideline, a good commit usually has less
than 100 lines of code changes. If you can see a way to split a commit into
different pieces of meaning, you should split it.
Keep in mind that you are presenting your final work product, _not_ the path you
took to get there. You should never have a commit that can be described as
fixing a mistake in an earlier commit in the same PR; use `git rebase -i` to fix
the mistake in the original commit.
See the [commit discipline guide](../contributing/commit-discipline.md) for more
details on how to structure your commits, and guidelines on how to write good
commit messages. Your pull request can only be reviewed once you've followed
these guidelines to the best of your ability. This makes it much easier for
reviewers to understand your work and identify any problems.
Ideally, when reviewing a pull request for a complex project, Zulip's
maintainers should be able to verify and merge the first few commits, and leave
comments on the rest. It is by far the most efficient way to do collaborative
development, since one is constantly making progress, we keep branches small,
and reviewers don't end up repeatedly going over the earlier parts of a pull
request.
## Explain your changes
By the time you are submitting your pull request, you should already have put a
lot of thought into how to organize and present your proposed changes. In the
description for your pull request, you will:
- Provide an overview of your changes.
- Note any differences from prior plans (e.g., from the description of the issue you
are solving).
- Call out any open questions, concerns, or decisions you are uncertain about.
The review process will go a lot more smoothly if points of uncertainty are
explicitly laid out.
- Include screenshots for all visual changes, so that they can be reviewed
without running your code. Be sure to follow our
[guide on presenting visual changes](../contributing/presenting-visual-changes.md).
If you have a question about a specific part of your code that you expect to be
resolved during the review process, put it in a PR comment attached to a
relevant part of the changes.
Take advantage of [GitHub's formatting][github-syntax] to make your pull request
description and comments easy to read.
[github-syntax]: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax
### Discussions in the development community
Any questions for which broader feedback or visibility is helpful are discussed
in the [Zulip development community](https://zulip.com/development-community/).
If there has been a conversation in the [Zulip development
community][zulip-dev-community] about the changes you've made or the issue your
pull request addresses, please cross-link between your pull request and those
conversations in both directions. This provides helpful context for maintainers
and reviewers. Specifically, it's best to link from your pull request [to a
specific message][link-to-message], as these links will still work even if the
topic of the conversation is renamed, moved or resolved.
Once you've created a pull request on GitHub, you can use one of the [custom
linkifiers][dev-community-linkifiers] in the development community to easily
link to your pull request from the relevant conversation.
[zulip-dev-community]: https://chat.zulip.org
[link-to-message]: https://zulip.com/help/link-to-a-message-or-conversation#get-a-link-to-a-specific-message
[dev-community-linkifiers]: https://zulip.com/development-community/#linking-to-github-issues-and-pull-requests
## Review your own work
Before requesting a review for your pull request, follow our [review
guide](./code-reviewing.md#reviewing-your-own-code) to carefully review and test
your own work. You can often find things you missed by taking a step back to
look over your work before asking others to do so. Catching mistakes yourself
will help your PRs be merged faster, and reviewers will appreciate the quality
and professionalism of your work.
The pull request template in the `zulip/zulip` repository has a checklist of
reminders for points you need to cover in your review. Make sure that all the
relevant items on the self-review checklist have been addressed, and check them
off on the list.
## Submit your pull request for review
If you are new to Git, see our guide on [making a pull
request](../git/pull-requests.md) for detailed technical instructions on how to
submit a pull request.
When submitting your PR, you will need to make sure that the pull request passes
all CI tests. You can sometimes request initial feedback if there are open
questions that will impact how you update the tests. But in general,
maintainers will wait for your PR to pass tests before reviewing your work.
If your PR was not ready for review when you first posted it (e.g., because it
was failing tests, or you weren't done working through the self-review
checklist), notify maintainers when you'd like them to take a look by posting a
clear comment on the main GitHub thread for your PR with details on any changes
from the original version; this is very helpful for any maintainers who already
read the draft PR.
## Draft pull requests
If it helps your workflow, you can submit your pull request marked as
a [draft][github-help-draft-pr] while you're still working on it. When ready for
review:
1. Make sure your PR is no longer marked as a [draft][github-help-draft-pr], and
doesn't have "WIP" in the title.
1. Post a quick "Ready for review!" comment on the main GitHub thread for your
PR.
[github-help-draft-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests

View File

@@ -0,0 +1,111 @@
# Suggesting features and improvements
If you have ideas for how to make Zulip better, we'd love to hear from you! Many
improvements start with a user's suggestion. The best way to suggest a feature
or an improvement is by starting a conversation in the [Zulip development
community](https://zulip.com/development-community/). It's a great way to engage
interactively with members of the community, and explore how best to improve
Zulip for you and other users.
Usually, if the discussion leads to a concrete proposal, Zulip's product team
will follow up by filing a GitHub issue to track the idea. Many conversations do
not immediately lead to a GitHub issue, and that's OK! Sometimes the time is not
right to pin down a plan, or more ideas need to come in before a great proposal
emerges. Regardless of whether a discussion results in immediate action, we
appreciate the time everyone takes to make suggestions and brainstorm ideas.
## What to include in your proposal
- Describe your idea. It's absolutely fine to bring up a problem without
suggesting a concrete solution.
- Provide context on how the change you are proposing would help you or your
organization. It is often helpful to describe how you are using Zulip (e.g.,
"I work at a small startup", or "I'm teaching a class"). This lets us combine
our Zulip expertise with your requirements to come up with a great design for
a feature.
- If you are aware of a related issue in GitHub or a prior conversation in the
development community, please include a link.
## Starting a conversation about a suggested feature or improvement
Steps and best practices for starting a conversation:
1. [**Join** the Zulip development
community](https://zulip.com/development-community/) if you don't already
have an account.
2. Pick an **appropriate channel** to discuss your idea:
- [#feedback](https://chat.zulip.org/#narrow/channel/137-feedback) for suggestions for
the Zulip web app or server. Use this channel if you aren't sure which channel is
most appropriate.
- [#mobile](https://chat.zulip.org/#narrow/channel/48-mobile) for suggestions
for the mobile apps.
- [#desktop](https://chat.zulip.org/#narrow/channel/16-desktop) for suggestions
that are specific to the Zulip desktop app.
- [#zulip-terminal](https://chat.zulip.org/#narrow/channel/206-zulip-terminal)
for suggestions for the terminal app.
- [#production
help](https://chat.zulip.org/#narrow/channel/31-production-help) for suggestions
related to self-hosting Zulip.
3. **[Start a new topic](https://zulip.com/help/introduction-to-topics#how-to-start-a-new-topic)**
for discussing your suggestions, using a brief summary of your idea as the
name of the topic.
If you aren't sure where to post or how to name your topic, don't worry!
Moderators can always rename the topic, or move the thread to another channel.
Members of the development community will jump in to discuss your idea. You can
help by participating in the discussion, including replying to any follow-up
questions, and helping develop the proposal. The discussion may conclude with an
issue being filed in GitHub to track the plan that was developed.
## Filing a GitHub issue
Filing a GitHub issue can be effective when you have a very specific proposal
for a feature or improvement. Steps and best practices for filing an issue:
1. File the issue in the **appropriate [Zulip
repository](https://github.com/zulip)**. The most commonly used repositories
are:
- [zulip/zulip](https://github.com/zulip/zulip/issues) for suggestions for the
Zulip web app or server. A good default if you aren't sure which repository
to use.
- [zulip/zulip-flutter](https://github.com/zulip/zulip-flutter/issues) for
suggestions for the mobile apps.
- [zulip/zulip-desktop](https://github.com/zulip/zulip-desktop/issues) for
suggestions that are specific to the Zulip desktop app.
- [zulip/zulip-terminal](https://github.com/zulip/zulip-terminal/issues) for
suggestions for the terminal app.
2. Do a **quick search** of the repository to see if there is already a similar
request. If there is, add a comment explaining why you would also appreciate
the proposed change, and make any additional suggestions you might have. Do
not file a new issue.
3. If you are aware of a related discussion in the Zulip development community,
please **cross-link** between the issue and the discussion thread. [Link to a
specific
message](https://zulip.com/help/link-to-a-message-or-conversation#get-a-link-to-a-specific-message)
in the discussion thread, as message links will still work even if the topic is
renamed or resolved.
## Evaluation and onboarding feedback
If you have been involved in the decision to use (or not use!) Zulip, or are
currently considering it, we'd love to hear about it. You can contact us
publicly in the [#feedback](https://chat.zulip.org/#narrow/channel/137-feedback)
channel in the [Zulip development
community](https://zulip.com/development-community/), or privately at
[feedback@zulip.com](mailto:feedback@zulip.com).
Here are some questions we're especially interested in:
- **Who you are**: Tell us a bit about your organization. What does it do? How many
people are involved?
- **Evaluation**: What is the process by which your organization chose or will
choose a team chat product?
- **Pros and cons**: What are the pros and cons of Zulip for your organization,
and the pros and cons of other products you are evaluating?
- **Features**: What are the features that are most important for your
organization? In the best-case scenario, what would your chat solution do
for you?
- **Onboarding**: If you remember, what were your impressions during your first
few minutes of using Zulip? What did you notice, and how did you feel? Was
there anything that stood out to you as confusing, or unexpected, or great?

View File

@@ -0,0 +1,94 @@
# Using zulipbot
Zulip uses [@zulipbot](https://github.com/zulipbot), a GitHub workflow bot
deployed on all Zulip repositories, to handle issues and pull requests in our
repositories in order to create a better workflow for Zulip contributors.
Its purpose is to work around various limitations in GitHub's
permissions and notifications systems to make it possible to have a
much more democratic workflow for our contributors. It allows anyone
to self-assign or label an issue, not just the core contributors
trusted with full write access to the repository (which is the only
model GitHub supports).
## Usage
- **Claim an issue** — Comment `@zulipbot claim` on the issue you want
to claim; **@zulipbot** will assign you to the issue and label the issue as
**in progress**.
- If you're a new contributor, **@zulipbot** will give you read-only
collaborator access to the repository and leave a welcome message on the
issue you claimed.
- You can also claim an issue that you've opened by including
`@zulipbot claim` in the body of your issue.
- If you accidentally claim an issue you didn't want to claim, comment
`@zulipbot abandon` to abandon an issue.
- **Label your issues** — Add appropriate labels to issues that you opened by
including `@zulipbot add` in an issue comment or the body of your issue
followed by the desired labels enclosed within double quotes (`""`).
- For example, to add the **bug** and **help wanted** labels to your
issue, comment or include `@zulipbot add "bug" "help wanted"` in the
issue body.
- You'll receive an error message if you try to add any labels to your issue
that don't exist in your repository.
- If you accidentally added the wrong labels, you can remove them by commenting
`@zulipbot remove` followed by the desired labels enclosed with double quotes
(`""`).
- **Find unclaimed issues** — Use the [GitHub search
feature](https://help.github.com/en/articles/using-search-to-filter-issues-and-pull-requests)
to find unclaimed issues by adding one of the following filters to your search:
- `-label: "in progress"` (excludes issues labeled with the **in progress** label)
- `no:assignee` (shows issues without assignees)
Issues labeled with the **in progress** label and/or assigned to other users have
already been claimed.
- **Collaborate in area label teams** — Receive notifications on
issues and pull requests within your fields of expertise on the
[Zulip server repository](https://github.com/zulip/zulip) by joining
the Zulip server
[area label teams](https://github.com/orgs/zulip/teams?utf8=✓&query=Server)
(Note: this link only works for members of the Zulip organization;
we'll happily add you if you're interested). These teams correspond
to the repository's
[area labels](https://github.com/zulip/zulip/labels), although some
teams are associated with multiple labels; for example, the **area:
message-editing** and **area: message view** labels are both related
to the
[Server message view](https://github.com/orgs/zulip/teams/server-message-view)
team. Feel free to join as many area label teams as you'd like!
After your request to join an area label team is approved, you'll receive
notifications for any issues labeled with the team's corresponding area
label as well as any pull requests that reference issues labeled with your
team's area label.
- **Track inactive claimed issues** — If a claimed issue has not been updated
for a week, **@zulipbot** will post a comment on the inactive issue to ask the
assignee(s) if they are still working on the issue.
If you see this comment on an issue you claimed, you should post a comment
on the issue to notify **@zulipbot** that you're still working on it.
If **@zulipbot** does not receive a response from the assignee within 3 days
of an inactive issue prompt, **@zulipbot** will automatically remove the
issue's current assignee(s) and the "in progress" label to allow others to
work on an inactive issue.
### Contributing
If you wish to help develop and contribute to **@zulipbot**, check out the
[zulip/zulipbot](https://github.com/zulip/zulipbot) repository on GitHub and read
the project's [contributing
guidelines](https://github.com/zulip/zulipbot/blob/main/.github/CONTRIBUTING.md#contributing) for
more information.

View File

@@ -0,0 +1,249 @@
# Authentication in the development environment
This page documents special notes that are useful for configuring
Zulip's various [authentication
methods](../production/authentication-methods.md) for testing in a
development environment.
Many of these authentication methods involve a complex interaction
between Zulip, an external service, and the user's browser. Because
browsers can (rightly!) be picky about the identity of sites you
interact with, the preferred way to set up authentication methods in a
development environment is provide secret keys so that you can go
through the real flow.
The steps to do this are a variation of the steps discussed in the
production documentation, including the comments in
`zproject/prod_settings_template.py`. The differences here are driven
by the fact that `dev_settings.py` is in Git, so it is inconvenient
for local [settings configuration](../subsystems/settings.md). As a
result, in the development environment, we allow setting certain
settings in the untracked file `zproject/dev-secrets.conf` (which is
also serves as `/etc/zulip/zulip-secrets.conf`).
Below, we document the procedure for each of the major authentication
methods supported by Zulip.
### Email and password
Zulip's default EmailAuthBackend authenticates users by verifying
control over their email address, and then allowing them to set a
password for their account. There are two development environment
details worth understanding:
- All of our authentication flows in the development environment have
special links to the `/emails` page (advertised in `/devtools`),
which shows all emails that the Zulip server has "sent" (emails are
not actually sent by the development environment), to make it
convenient to click through the UI of signup, password reset, etc.
- There's a management command,
`manage.py print_initial_password username@example.com`, that prints
out **default** passwords for the development environment users.
Note that if you change a user's password in the development
environment, those passwords will no longer work. It also prints
out the user's **current** API key.
### Google
- Visit [the Google developer
console](https://console.developers.google.com) and navigate to "APIs
& services" > "Credentials". Create a "Project", which will correspond
to your dev environment.
- Navigate to "APIs & services" > "Library", and find the "Identity
Toolkit API". Choose "Enable".
- Return to "Credentials", and select "Create credentials". Choose
"OAuth client ID", and follow prompts to create a consent screen, etc.
For "Authorized redirect URIs", fill in
`http://auth.zulipdev.com:9991/complete/google/` .
- You should get a client ID and a client secret. Copy them. In
`dev-secrets.conf`, set `social_auth_google_key` to the client ID
and `social_auth_google_secret` to the client secret.
### GitHub
- Register an OAuth2 application with GitHub at one of
<https://github.com/settings/developers> or
`https://github.com/organizations/<your-org>/settings/applications`.
Specify `http://auth.zulipdev.com:9991/complete/github/` as the callback URL.
- You should get a page with settings for your new application,
showing a client ID and a client secret. In `dev-secrets.conf`, set
`social_auth_github_key` to the client ID and `social_auth_github_secret`
to the client secret.
### GitLab
- Register an OAuth application with GitLab at
<https://gitlab.com/oauth/applications>.
Specify `http://auth.zulipdev.com:9991/complete/gitlab/` as the callback URL.
- You should get a page containing the Application ID and Secret for
your new application. In `dev-secrets.conf`, enter the Application
ID as `social_auth_gitlab_key` and the Secret as
`social_auth_gitlab_secret`.
### Apple
- Visit <https://developer.apple.com/account/resources/>,
Enable App ID and Create a Services ID with the instructions in
<https://help.apple.com/developer-account/?lang=en#/dev1c0e25352> .
When prompted for a "Return URL", enter
`http://auth.zulipdev.com:9991/complete/apple/` .
- [Create a Sign in with Apple private key](https://help.apple.com/developer-account/?lang=en#/dev77c875b7e)
- In `dev-secrets.conf`, set
- `social_auth_apple_services_id` to your
"Services ID" (eg. com.application.your).
- `social_auth_apple_app_id` to "App ID" or "Bundle ID".
This is only required if you are testing Apple auth on iOS.
- `social_auth_apple_key` to your "Key ID".
- `social_auth_apple_team` to your "Team ID".
- Put the private key file you got from apple at the path
`zproject/dev_apple.key`.
### Discord
- Register an OAuth application with Discord at
<https://discord.com/developers/applications>.
Specify `http://auth.zulipdev.com:9991/complete/discord/` as the callback URL.
- You should get a page containing the Client ID and Secret for
your new application. In `dev-secrets.conf`, enter the Client
ID as `social_auth_discord_key` and the Secret as
`social_auth_discord_secret`.
### SAML
- Sign up for a [developer Okta account](https://developer.okta.com/).
- Set up SAML authentication by following
[Okta's documentation](https://developer.okta.com/docs/guides/saml-application-setup/overview/).
Specify:
- `http://localhost:9991/complete/saml/` for the "Single sign on URL"`. If you are using a dev droplet, this will
be `http://auth.{your_droplet_name}.zulipdev.org:9991/complete/saml/`
- `http://localhost:9991` for the "Audience URI (SP Entity ID)". If you are using a dev droplet, this will be
`http://zulip.{your_droplet_name}.zulipdev.org:9991/`
- Skip "Default RelayState".
- Skip "Name ID format".
- Set 'Email` for "Application username format".
- Provide "Attribute statements" of `email` to `user.email`,
`first_name` to `user.firstName`, and `last_name` to `user.lastName`.
- Assign at least one account in the "Assignments" tab. You'll use it for
signing up / logging in to Zulip.
- Visit the big "Setup instructions" button on the "Sign on" tab.
- Edit `zproject/dev-secrets.conf` to add the two values provided:
- Set `saml_url = http...` from "Identity Provider Single Sign-On
URL".
- Set `saml_entity_id = http://...` from "Identity Provider Issuer".
- Download the certificate and put it at the path `zproject/dev_saml.cert`.
- Now you should have working SAML authentication!
- You can sign up to the target realm with the account that you've "assigned"
in the previous steps (if the account's email address is allowed in the realm,
so you may have to change the realm settings to allow the appropriate email domain)
and then you'll be able to log in freely. Alternatively, you can create an account
with the email in any other way, and then just use SAML to log in.
### When SSL is required
Some OAuth providers (such as Facebook) require HTTPS on the callback
URL they post back to, which isn't supported directly by the Zulip
development environment. If you run a
[remote Zulip development server](remote.md), we have
instructions for
[an nginx reverse proxy with SSL](remote.md#using-an-nginx-reverse-proxy)
that you can use for your development efforts.
## Testing LDAP in development
Before Zulip 2.0, one of the more common classes of bug reports with
Zulip's authentication was users having trouble getting [LDAP
authentication](../production/authentication-methods.md#ldap-including-active-directory)
working. The root cause was because setting up a local LDAP server
for development was difficult, which meant most developers were unable
to work on fixing even simple issues with it.
We solved this problem for our unit tests long ago by using the
popular [fakeldap](https://github.com/zulip/fakeldap) library. And in
2018, we added convenient support for using fakeldap in the Zulip
development environment as well, so that you can go through all the
actual flows for LDAP configuration.
- To enable fakeldap, set `FAKE_LDAP_MODE` in
`zproject/dev_settings.py` to one of the following options. For more
information on these modes, refer to
[our production docs](../production/authentication-methods.md#ldap-including-active-directory):
- `a`: If users' email addresses are in LDAP and used as username.
- `b`: If LDAP only has usernames but email addresses are of the form
username@example.com
- `c`: If LDAP usernames are completely unrelated to email addresses.
- To disable fakeldap, set `FAKE_LDAP_MODE` back to `None`.
- In all fakeldap configurations, users' fake LDAP passwords are equal
to their usernames (e.g., for `ldapuser1@zulip.com`, the password is
`ldapuser1`).
- `FAKE_LDAP_NUM_USERS` in `zproject/dev_settings.py` can be used to
specify the number of LDAP users to be added. The default value for
the number of LDAP users is 8.
### Testing avatar and custom profile field synchronization
The fakeldap LDAP directories we use in the development environment
are generated by the code in `zerver/lib/dev_ldap_directory.py`, and
contain data one might want to sync, including avatars and custom
profile fields.
We also have configured `AUTH_LDAP_USER_ATTR_MAP` in
`zproject/dev_settings.py` to sync several of those fields. For
example:
- Modes `a` and `b` will set the user's avatar on account creation and
update it when `manage.py sync_ldap_user_data` is run.
- Mode `b` is configured to automatically have the `birthday` and
`Phone number` custom profile fields populated/synced.
- Mode `a` is configured to deactivate/reactivate users whose accounts
are disabled in LDAP when `manage.py sync_ldap_user_data` is run.
(Note that you'll likely need to edit
`zerver/lib/dev_ldap_directory.py` to ensure there are some accounts
configured to be disabled).
### Automated testing
For our automated tests, we generally configure custom LDAP data for
each individual test, because that generally means one can understand
exactly what data is being used in the test without looking at other
resources. It also gives us more freedom to edit the development
environment directory without worrying about tests.
## Two factor authentication
Zulip uses [django-two-factor-auth][0] as a beta 2FA integration.
To enable 2FA, set `TWO_FACTOR_AUTHENTICATION_ENABLED` in settings to
`True`, then log in to Zulip and add an OTP device from the settings
page. Once the device is added, password based authentication will ask
for a one-time-password. In the development environment, this
one-time-password will be printed to the console when you try to
log in. Just copy-paste it into the form field to continue.
Direct development logins don't prompt for 2FA one-time-passwords, so
to test 2FA in development, make sure that you log in using a
password. You can get the passwords for the default test users using
`./manage.py print_initial_password`.
## Password form implementation
By default, Zulip uses `autocomplete=off` for password fields where we
enter the current password, and `autocomplete="new-password"` for
password fields where we create a new account or change the existing
password. This prevents the browser from auto-filling the existing
password.
Visit <https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete> for more details.
[0]: https://github.com/Bouke/django-two-factor-auth

15
docs/development/index.md Normal file
View File

@@ -0,0 +1,15 @@
# Development environment
```{toctree}
---
maxdepth: 3
---
Development environment installation <overview>
Recommended setup <setup-recommended>
Advanced setup <setup-advanced>
Using the development environment <using>
Developing remotely <remote>
Authentication in the development environment <authentication>
Testing the installer <test-install>
```

View File

@@ -0,0 +1,87 @@
# Development environment installation
## Requirements
The Zulip development environment can be installed on **macOS,
Windows, and Linux** (Debian or Ubuntu recommended). You'll need at least **2GB
of available RAM**.
Installing the Zulip development environment requires downloading several hundred
megabytes of dependencies, so you will need an **active, reasonably fast,
internet connection throughout the entire installation process.** You can
[configure a proxy][configure-proxy] if you need one.
## Recommended setup
**For first-time contributors, we recommend using the
[Vagrant development environment][install-vagrant]** on
macOS and Linux based OS and [WSL 2 setup][install-via-wsl] on Windows.
## Vagrant setup
[Vagrant setup][install-vagrant] creates a virtual machine (for Windows and macOS) or a
Linux container (otherwise) inside which the Zulip server and all
related services will run. Vagrant adds a bit of overhead to using the
Zulip development server, but provides an isolated environment that is
easy to install, update, and uninstall. It has been well-tested and
performs well.
## Advanced setup
Zulip also supports a wide range of ways to install the Zulip
development environment:
- On Linux platforms, you can **[install directly][install-direct]**.
- On Windows, you can **[install directly][install-via-wsl]** via WSL 2.
## Slow internet connections
If you have a very slow network connection, however, you may want to
avoid using Vagrant (which involves downloading an Ubuntu virtual
machine or Linux container) and [install directly][install-direct] on
a Linux system.
An alternative option if you have poor network connectivity is to rent
a cloud server and install the Zulip development environment for
remote use. See the [next section][self-install-remote] for details.
## Installing remotely
The Zulip development environment works well on remote virtual
machines. This can be a good alternative for those with poor network
connectivity or who have limited storage/memory on their local
machines.
We recommend giving the Zulip development environment its **own
virtual machine**, running one of
[the supported platforms for direct installation][install-direct],
with at least **2GB of memory**.
If the Zulip development environment will be the only thing running on
the remote virtual machine, we recommend installing
[directly][install-direct]. Otherwise, we recommend the
[Vagrant][install-vagrant] method so you can easily uninstall if you
need to.
## Next steps
Once you've installed the Zulip development environment, you'll want
to read these documents to learn how to use it:
- [Using the development environment][using-dev-env]
- [Testing][testing] (and [Configuring CI][ci])
And if you've set up the Zulip development environment on a remote
machine, take a look at our tips for
[developing remotely][dev-remote].
[dev-remote]: remote.md
[install-direct]: setup-advanced.md#installing-directly-on-ubuntu-debian-centos-or-fedora
[install-vagrant]: setup-recommended.md
[self-install-remote]: #installing-remotely
[self-slow-internet]: #slow-internet-connections
[configure-proxy]: setup-recommended.md#specifying-a-proxy
[using-dev-env]: using.md
[testing]: ../testing/testing.md
[ci]: ../git/cloning.md#step-3-configure-continuous-integration-for-your-fork
[install-via-wsl]: setup-recommended.md

320
docs/development/remote.md Normal file
View File

@@ -0,0 +1,320 @@
# Developing on a remote machine
The Zulip developer environment works well on remote virtual machines. This can
be a good alternative for those with poor network connectivity or who have
limited storage/memory on their local machines.
We recommend giving the Zulip development environment its own virtual
machine with at least 2GB of memory. If the Zulip development
environment will be the only thing running on the remote virtual
machine, we recommend installing
[directly][install-direct]. Otherwise, we recommend the
[Vagrant][install-vagrant] method so you can easily uninstall if you
need to.
## Connecting to the remote environment
The best way to connect to your server is using the command line tool `ssh`.
- On macOS and Linux/UNIX, `ssh` is a part of Terminal.
- On Windows, `ssh` comes with [Bash for Git][git-bash].
Open _Terminal_ or _Bash for Git_, and connect with the following:
```console
$ ssh username@host
```
If you have poor internet connectivity, we recommend using
[Mosh](https://mosh.org/) as it is more reliable over slow or unreliable
networks.
## Setting up user accounts
You will need a non-root user account with sudo privileges to set up
the Zulip development environment. If you have one already, continue
to the next section.
You can create a new user with sudo privileges by running the
following commands as root:
- You can create a `zulipdev` user by running the command
`adduser zulipdev`. Run through the prompts to assign a password and
user information. (You can pick any username you like for this user
account.)
- You can add the user to the sudo group by running the command
`usermod -aG sudo zulipdev`.
- Finally, you can switch to the user by running the command
`su - zulipdev` (or just log in to that user using `ssh`).
## Setting up the development environment
After you have connected to your remote server, you need to install the
development environment.
If the Zulip development environment will be the only thing running on
the remote virtual machine, we recommend installing
[directly][install-direct]. Otherwise, we recommend the
[Vagrant][install-vagrant] method so you can easily uninstall if you
need to.
The main difference from the standard instructions is that for a
remote development environment, and you're not using our Digital Ocean
Droplet infrastructure (which handles `EXTERNAL_HOST` for you), you'll
need to run `export EXTERNAL_HOST=<REMOTE_IP>:9991` in a shell before
running `run-dev` (and see also the `--interface=''` option
documented below).
If your server has a static IP address, we recommend putting this
command in `~/.bashrc`, so you don't need to remember to run it every
time. This allows you to access Zulip running in your development
environment using a browser on another host.
## Running the development server
Once you have set up the development environment, you can start up the
development server with the following command in the directory where
you cloned Zulip:
```bash
./tools/run-dev --interface=''
```
This will start up the Zulip server on port 9991. You can then
navigate to `http://<REMOTE_IP>:9991/devlogin` and you should see something like
this screenshot of the Zulip development environment:
![Image of Zulip development environment](../images/zulip-devlogin.png)
The `--interface=''` option makes the Zulip development environment
accessible from any IP address (in contrast with the much more secure
default of only being accessible from localhost, which is great for
developing on your laptop).
To properly secure your remote development environment, you can
[port forward](https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding)
using ssh instead of running the development environment on an exposed
interface. For example, if you're running Zulip on a remote server
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
your terminal:
```bash
ssh -L 3000:127.0.0.1:9991 <username>@<remote_server_ip> -N
```
Now you can access Zulip by navigating to `http://127.0.0.1:3000` in
your local computer's browser.
For more information, see [Using the development
environment][rtd-using-dev-env].
## Making changes to code on your remote development server
To see changes on your remote development server, you need to do one of the following:
- [Edit locally](#editing-locally): Clone Zulip code to your computer and
then use your favorite editor to make changes. When you want to see changes
on your remote Zulip development instance, sync with Git.
- [Edit remotely](#editing-remotely): Edit code directly on your remote
Zulip development instance using a [Web-based IDE](#web-based-ide) (recommended for
beginners) or a [command line editor](#command-line-editors), or a
[desktop IDE](#desktop-gui-editors) using a plugin to sync your
changes to the server when you save.
#### Editing locally
If you want to edit code locally install your favorite text editor. If you
don't have a favorite, here are some suggestions:
- [atom](https://atom.io/)
- [emacs](https://www.gnu.org/software/emacs/)
- [vim](https://www.vim.org/)
- [spacemacs](https://github.com/syl20bnr/spacemacs)
- [sublime](https://www.sublimetext.com/)
- [PyCharm](https://www.jetbrains.com/pycharm/)
Next, follow our [Git and GitHub guide](../git/index.md) to clone and configure
your fork of zulip on your local computer.
Once you have cloned your code locally, you can get to work.
##### Syncing changes
The easiest way to see your changes on your remote development server
is to **push them to GitHub** and then **fetch and merge** them from
the remote server.
For more detailed instructions about how to do this, see our [Git & GitHub
guide][rtd-git-guide]. In brief, the steps are as follows.
On your **local computer**:
1. Open _Terminal_ (macOS/Linux) or _Git for BASH_.
2. Change directory to where you cloned Zulip (e.g., `cd zulip`).
3. Use `git add` and `git commit` to stage and commit your changes (if you
haven't already).
4. Push your commits to GitHub with `git push origin branchname`.
Be sure to replace `branchname` with the name of your actual feature branch.
Once `git push` has completed successfully, you are ready to fetch the commits
from your remote development instance:
1. In _Terminal_ or _Git BASH_, connect to your remote development
instance with `ssh user@host`.
2. Change to the zulip directory (e.g., `cd zulip`).
3. Fetch new commits from GitHub with `git fetch origin`.
4. Change to the branch you want to work on with `git checkout branchname`.
5. Merge the new commits into your branch with `git merge origin/branchname`.
#### Editing remotely
There are a few good ways to edit code in your remote development
environment:
- With a command-line editor like vim or emacs run over SSH.
- With a desktop GUI editor like VS Code or Atom and a plugin for
syncing your changes to the remote server.
- With a web-based IDE like CodeAnywhere.
We document these options below; we recommend using whatever editor
you prefer for development in general.
##### Desktop GUI editors
If you use [TextMate](https://macromates.com), Atom, VS Code, or a
similar GUI editor, tools like
[Visual Studio Code Remote - SSH](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh) and
[rmate](https://github.com/textmate/rmate) that are designed to
integrate that editor with remote development over SSH allow you to
develop remotely from the comfort of your local machine.
Similar packages/extensions exist for other popular code editors as
well; contributions of precise documentation for them are welcome!
- [VSCode Remote - SSH][vscode-remote-ssh]: Lets you use Visual Studio
Code against a remote repository with a similar user experience to
developing locally.
[vscode-remote-ssh]: https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh
- [rmate](https://github.com/textmate/rmate) for TextMate + VS Code:
1. Install the extension
[Remote VSCode](https://marketplace.visualstudio.com/items?itemName=rafaelmaiolla.remote-vscode).
2. On your remote machine, run:
```console
$ mkdir -p ~/bin
$ curl -fL -o ~/bin/rmate https://raw.githubusercontent.com/textmate/rmate/master/bin/rmate
$ chmod a+x ~/bin/rmate
```
3. Make sure the remote server is running in VS Code (you can
force-start through the Command Palette).
4. SSH to your remote machine using:
```console
$ ssh -R 52698:localhost:52698 user@example.org
```
5. On your remote machine, run:
```console
$ rmate [options] file
```
and the file should open up in VS Code. Any changes you make now will be saved remotely.
##### Command line editors
Another way to edit directly on the remote development server is with
a command line text editor on the remote machine.
Two editors often available by default on Linux systems are:
- **Nano**: A very simple, beginner-friendly editor. However, it lacks a lot of
features useful for programming, such as syntax highlighting, so we only
recommended it for quick edits to things like configuration files. Launch by
running command `nano <filename>`. Exit by pressing _Ctrl-X_.
- **[Vim](https://www.vim.org/)**: A very powerful editor that can take a while
to learn. Launch by running `vim <filename>`. Quit Vim by pressing _Esc_,
typing `:q`, and then pressing _Enter_. Vim comes with a program to learn it
called `vimtutor` (just run that command to start it).
Other options include:
- [emacs](https://www.gnu.org/software/emacs/)
- [spacemacs](https://github.com/syl20bnr/spacemacs)
##### Web-based IDE
If you are relatively new to working on the command line, or just want to get
started working quickly, we recommend web-based IDE
[Codeanywhere][codeanywhere].
To set up Codeanywhere for Zulip:
1. Create a [Codeanywhere][codeanywhere] account and log in.
2. Create a new **SFTP-SSH** project. Use _Public key_ for authentication.
3. Click **GET YOUR PUBLIC KEY** to get the new public key that
Codeanywhere generates when you create a new project. Add this public key to
`~/.ssh/authorized_keys` on your remote development instance.
4. Once you've added the new public key to your remote development instance, click
_CONNECT_.
Now your workspace should look similar this:
![Codeanywhere workspace][img-ca-workspace]
#### Next steps
Next, read the following to learn more about developing for Zulip:
- [Git & GitHub guide][rtd-git-guide]
- [Using the development environment][rtd-using-dev-env]
- [Testing][rtd-testing]
[install-direct]: setup-advanced.md#installing-directly-on-ubuntu-debian-centos-or-fedora
[install-vagrant]: setup-recommended.md
[rtd-git-guide]: ../git/index.md
[rtd-using-dev-env]: using.md
[rtd-testing]: ../testing/testing.md
[git-bash]: https://git-for-windows.github.io/
[codeanywhere]: https://codeanywhere.com/
[img-ca-settings]: ../images/codeanywhere-settings.png
[img-ca-workspace]: ../images/codeanywhere-workspace.png
## Using an nginx reverse proxy
For some applications (e.g., developing an OAuth2 integration for
Facebook), you may need your Zulip development to have a valid SSL
certificate. While `run-dev` doesn't support that, you can do this
with an `nginx` reverse proxy sitting in front of `run-dev`.
The following instructions assume you have a Zulip Droplet working and
that the user is `zulipdev`; edit accordingly if the situation is
different.
1. First, get an SSL certificate; you can use
[our certbot wrapper script used for production](../production/ssl-certificates.md#certbot-recommended)
by running the following commands as root:
```bash
# apt install -y crudini
mkdir -p /var/lib/zulip/certbot-webroot/
# if nginx running this will fail and you need to run `service nginx stop`
/home/zulipdev/zulip/scripts/setup/setup-certbot \
hostname.example.com \
--email=username@example.com --method=standalone
```
1. Install nginx configuration:
```bash
apt install -y nginx-full
cp -a /home/zulipdev/zulip/tools/droplets/zulipdev /etc/nginx/sites-available/
ln -nsf /etc/nginx/sites-available/zulipdev /etc/nginx/sites-enabled/
nginx -t # Verifies your nginx configuration
service nginx reload # Actually enabled your nginx configuration
```
1. Start the Zulip development environment in HTTPS mode with the following command:
```bash
env EXTERNAL_HOST="hostname.example.com" ./tools/run-dev --behind-https-proxy --interface=''
```

View File

@@ -0,0 +1,86 @@
---
orphan: true
---
# How to request a remote Zulip development instance
Under specific circumstances, typically during sprints, hackathons, and
Google Code-in, Zulip can provide you with a virtual machine with the
development environment already set up.
The machines (droplets) are being generously provided by
[DigitalOcean](https://www.digitalocean.com/). Thank you DigitalOcean!
## Step 1: Join GitHub and create SSH keys
To contribute to Zulip and to use a remote Zulip developer instance, you'll
need a GitHub account. If you don't already have one, sign up
[here][github-join].
You'll also need to [create SSH keys and add them to your GitHub
account][github-help-add-ssh-key].
## Step 2: Create a fork of zulip/zulip
Zulip uses a **forked-repo** and **[rebase][gitbook-rebase]-oriented
workflow**. This means that all contributors create a fork of the [Zulip
repository][github-zulip-zulip] they want to contribute to and then submit pull
requests to the upstream repository to have their contributions reviewed and
accepted.
When we create your Zulip dev instance, we'll connect it to your fork of Zulip,
so that needs to exist before you make your request.
While you're logged in to GitHub, navigate to [zulip/zulip][github-zulip-zulip]
and click the **Fork** button. (See [GitHub's help article][github-help-fork]
for further details).
## Step 3: Make request via chat.zulip.org
Now that you have a GitHub account, have added your SSH keys, and forked
zulip/zulip, you are ready to request your Zulip developer instance.
If you haven't already, create an account on https://chat.zulip.org/.
Next, join the [development
help](https://chat.zulip.org/#narrow/channel/49-development-help) channel. Create a
new **channel message** with your GitHub username as the **topic** and request
your remote dev instance. **Please make sure you have completed steps 1 and 2
before doing so**. A core developer should reply letting you know they're
working on creating it as soon as they are available to help.
Once requested, it will only take a few minutes to create your instance. You
will be contacted when it is complete and available.
## Next steps
Once your remote dev instance is ready:
- Connect to your server by running
`ssh zulipdev@<username>.zulipdev.org` on the command line
(Terminal for macOS and Linux, Bash for Git on Windows).
- There is no password; your account is configured to use your SSH keys.
- Once you log in, you should see `(zulip-server) ~$`.
- To start the dev server, `cd zulip` and then run `./tools/run-dev`.
- While the dev server is running, you can see the Zulip server in your browser
at http://zulip.username.zulipdev.org:9991.
- The development server actually runs on all subdomains of
`username.zulipdev.org`; this is important for testing Zulip's
support for multiple organizations in your development server.
Once you've confirmed you can connect to your remote server, take a look at:
- [developing remotely](remote.md) for tips on using the remote dev
instance, and
- our [Git & GitHub guide](../git/index.md) to learn how to use Git with Zulip.
Next, read the following to learn more about developing for Zulip:
- [Using the development environment](using.md)
- [Testing](../testing/testing.md)
[github-join]: https://github.com/join
[github-help-add-ssh-key]: https://help.github.com/en/articles/adding-a-new-ssh-key-to-your-github-account
[github-zulip-zulip]: https://github.com/zulip/zulip/
[github-help-fork]: https://help.github.com/en/articles/fork-a-repo
[gitbook-rebase]: https://git-scm.com/book/en/v2/Git-Branching-Rebasing

View File

@@ -0,0 +1,295 @@
# Advanced setup
Contents:
- [Installing directly on Ubuntu, Debian, CentOS, or Fedora](#installing-directly-on-ubuntu-debian-centos-or-fedora)
- [Installing using Vagrant with VirtualBox on Windows 10](#installing-using-vagrant-with-virtualbox-on-windows-10)
- [Using the Vagrant Hyper-V provider on Windows](#using-the-vagrant-hyper-v-provider-on-windows-beta)
- [Newer versions of supported platforms](#newer-versions-of-supported-platforms)
## Installing directly on Ubuntu, Debian, CentOS, or Fedora
:::{warning}
There is no supported uninstallation process with the direct-install
method. If you want that, use [the Vagrant environment](setup-recommended.md),
where you can just do `vagrant destroy` to clean up the development environment.
:::
One can install the Zulip development environment directly on a Linux
host by following these instructions. Currently supported platforms
are:
- Ubuntu 22.04, 24.04, 26.04
- Debian 12, 13
- CentOS 7 (beta)
- Fedora 43, 44 (beta)
- RHEL 7 (beta)
**Note**: You should not use the `root` user to run the installation.
If you are using a [remote server](remote.md), see
the
[section on creating appropriate user accounts](remote.md#setting-up-user-accounts).
Start by [cloning your fork of the Zulip repository][zulip-rtd-git-cloning]
and [connecting the Zulip upstream repository][zulip-rtd-git-connect]:
```bash
git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git
cd zulip
git remote add -f upstream https://github.com/zulip/zulip.git
```
CentOS, Fedora, and RHEL users should ensure that python3 is installed on their
systems (Debian and Ubuntu distributions already include it):
```bash
# On CentOS/Fedora/RHEL, you must first install python3.
# For example, this command installs python3 with yum:
yum install python
```
With python3 installed, change into the directory where you have cloned
Zulip and run the following commands:
```bash
# From inside a clone of zulip.git:
./tools/provision
source .venv/bin/activate
./tools/run-dev # starts the development server
```
Once you've done the above setup, you can pick up the [documentation
on using the Zulip development
environment](setup-recommended.md#step-4-developing),
ignoring the parts about `vagrant` (since you're not using it).
## Installing using Vagrant with VirtualBox on Windows 10
:::{note}
We recommend using [WSL 2 for Windows development](setup-recommended.md)
because it is easier to set up and provides a substantially better experience.
:::
1. Install [Git for Windows][git-bash], which installs _Git BASH_.
2. Install [VirtualBox][vbox-dl] (latest).
3. Install [Vagrant][vagrant-dl] (latest).
(Note: While _Git BASH_ is recommended, you may also use [Cygwin][cygwin-dl].
If you do, make sure to **install default required packages** along with
**git**, **curl**, **openssh**, and **rsync** binaries.)
Also, you must have hardware virtualization enabled (VT-x or AMD-V) in your
computer's BIOS.
#### Running Git BASH as an administrator
It is important that you **always run Git BASH with administrator
privileges** when working on Zulip code, as not doing so will cause
errors in the development environment (such as symlink creation). You
might wish to configure your Git BASH shortcut to always run with
these privileges enabled (see this [guide][bash-admin-setup] for how
to set this up).
##### Enable native symlinks
The Zulip code includes symbolic links (symlinks). By default, native Windows
symlinks are not enabled in either Git BASH or Cygwin, so you need to do a bit
of configuration. **You must do this before you clone the Zulip code.**
In **Git for BASH**:
Open **Git BASH as an administrator** and run:
```console
$ git config --global core.symlinks true
```
Now confirm the setting:
```console
$ git config core.symlinks
true
```
If you see `true`, you are ready for [Step 2: Get Zulip code](setup-recommended.md#step-2-get-zulip-code).
Otherwise, if the above command prints `false` or nothing at all, then symlinks
have not been enabled.
In **Cygwin**:
Open a Cygwin window **as an administrator** and do this:
```console
christie@win10 ~
$ echo 'export "CYGWIN=$CYGWIN winsymlinks:native"' >> ~/.bash_profile
```
Next, close that Cygwin window and open another. If you `echo` $CYGWIN you
should see:
```console
christie@win10 ~
$ echo $CYGWIN
winsymlinks:native
```
Now you are ready for [Step 2: Get Zulip code](setup-recommended.md#step-2-get-zulip-code).
(Note: The **GitHub Desktop client** for Windows has a bug where it
will automatically set `git config core.symlink false` on a repository
if you use it to clone a repository, which will break the Zulip
development environment, because we use symbolic links. For that
reason, we recommend avoiding using GitHub Desktop client to clone
projects and to instead follow these instructions exactly.)
[cygwin-dl]: https://cygwin.com
[git-bash]: https://git-for-windows.github.io
[vbox-dl]: https://www.virtualbox.org/wiki/Downloads
[vagrant-dl]: https://www.vagrantup.com/downloads.html
[bash-admin-setup]: https://superuser.com/questions/1002262/run-applications-as-administrator-by-default-in-windows-10
## Using the Vagrant Hyper-V provider on Windows (beta)
You should have [Vagrant](https://www.vagrantup.com/downloads) and
[Hyper-V][hyper-v] installed on your system. Ensure they both work as
expected.
[hyper-v]: https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/quick-start/enable-hyper-v
**NOTE**: Hyper-V is available only on Windows Enterprise, Pro, or Education.
1. Start by [cloning your fork of the Zulip repository][zulip-rtd-git-cloning]
and [connecting the Zulip upstream repository][zulip-rtd-git-connect]:
```bash
git clone --config pull.rebase git@github.com:YOURUSERNAME/zulip.git
cd zulip
git remote add -f upstream https://github.com/zulip/zulip.git
```
1. You will have to open up powershell with administrator rights in
order to use Hyper-V. Then provision the development environment:
```bash
vagrant up --provider=hyperv
```
You should get output like this:
```console
Bringing machine 'default' up with 'hyperv' provider...
==> default: Verifying Hyper-V is enabled...
==> default: Verifying Hyper-V is accessible...
<other stuff>...
==> default: Waiting for the machine to report its IP address...
default: Timeout: 120 seconds
default: IP: 172.28.119.70
==> default: Waiting for machine to boot. This may take a few minutes...
default: SSH address: 172.28.122.156
==> default: Machine booted and ready!
==> default: Preparing SMB shared folders...
Vagrant requires administrator access for pruning SMB shares and
may request access to complete removal of stale shares.
==> default: Starting the machine...
<other stuff>...
default: Username (user[@domain]): <your-machine-username>
default: Password (will be hidden):
```
At this point, you will be prompted for your Windows administrator
username and password (not your Microsoft account credentials).
1. SSH into your newly created virtual machine
```bash
vagrant ssh
```
This will ssh you into the bash shell of the Zulip development environment
where you can execute bash commands.
1. Set the `EXTERNAL_HOST` environment variable.
```console
(zulip-server) vagrant@ubuntu-18:/srv/zulip$ export EXTERNAL_HOST="$(hostname -I | xargs):9991"
(zulip-server) vagrant@ubuntu-18:/srv/zulip$ echo $EXTERNAL_HOST
```
The output will be like:
```console
172.28.122.156:9991
```
Make sure you note down this down. This is where your zulip development web
server can be accessed.
:::{important}
The output of the above command changes every time you restart the Vagrant
development machine. Thus, it will have to be run every time you bring one up.
This quirk is one reason this method is marked experimental.
:::
1. You should now be able to start the Zulip development server.
```console
(zulip-server) vagrant@ubuntu-18:/srv/zulip$ ./tools/run-dev
```
The output will look like:
```console
Starting Zulip on:
http://172.30.24.235:9991/
Internal ports:
9991: Development server proxy (connect here)
9992: Django
9993: Tornado
9994: webpack
```
Visit the indicated URL in your web browser.
1. You can stop the development environment using `vagrant halt`, and restart it
using `vagrant up` and then going through steps **3** and **4** again.
### Problems you may encounter
1. If you get the error `Hyper-V could not initialize memory`, this is
likely because your system has insufficient free memory to start
the virtual machine. You can generally work around this error by
closing all other running programs and running
`vagrant up --provider=hyperv` again. You can reopen the other
programs after the provisioning is completed. If it still isn't
enough, try restarting your system and running the command again.
2. Be patient the first time you run `./tools/run-dev`.
As with other installation methods, please visit [#provision
help][provision-help] in the [Zulip development community
server](https://zulip.com/development-community/) if you need help.
[provision-help]: https://chat.zulip.org/#narrow/channel/21-provision-help
## Newer versions of supported platforms
You can use
[our provisioning tool](#installing-directly-on-ubuntu-debian-centos-or-fedora)
to set up the Zulip development environment on current versions of
these platforms reliably and easily, so we no longer maintain manual
installation instructions for these platforms.
If `tools/provision` doesn't yet support a newer release of Debian or
Ubuntu that you're using, we'd love to add support for it. It's
likely only a few lines of changes to `tools/lib/provision.py` and
`scripts/lib/setup-apt-repo` if you'd like to do it yourself and
submit a pull request, or you can ask for help in
[#development help](https://chat.zulip.org/#narrow/channel/49-development-help)
in [the Zulip development community](https://zulip.com/development-community/),
and a core team member can help guide you through adding support for the platform.
[zulip-rtd-git-cloning]: ../git/cloning.md#step-1b-clone-to-your-machine
[zulip-rtd-git-connect]: ../git/cloning.md#step-1c-connect-your-fork-to-zulip-upstream

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
##### 2. Create the `docker` group and add yourself to it:
```console
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
```
You will need to log out and log back in (or reboot) for this change to take effect.
If it worked, you will see `docker` in your list of groups:
```console
$ groups | grep docker
YOURUSERNAME adm cdrom sudo dip plugdev lpadmin sambashare docker
```
##### 3. Make sure the Docker daemon is running
Docker runs as a background service (daemon), which must be running for Docker commands to work.
You can check using the following:
```console
$ systemctl status docker
● docker.service - Docker Application Container Engine
Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2019-07-15 23:20:46 IST; 18min ago
```
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
using the following:
```console
$ sudo systemctl unmask docker
$ sudo systemctl enable docker
$ sudo systemctl start docker
```

View File

@@ -0,0 +1,121 @@
#### Vagrant guest doesn't show (zulip-server) at start of prompt
This is caused by provisioning failing to complete successfully. You
can see the errors in `var/log/provision.log`; it should end with
something like this:
```text
ESC[94mZulip development environment setup succeeded!ESC[0m
```
The `ESC` stuff are the terminal color codes that make it show as a nice
blue in the terminal, which unfortunately looks ugly in the logs.
If you encounter an incomplete `/var/log/provision.log file`, you need to
update your environment. Re-provision your Vagrant machine; if the problem
persists, please come chat with us (see instructions above) for help.
After you provision successfully, you'll need to exit your `vagrant ssh`
shell and run `vagrant ssh` again to get the virtualenv setup properly.
#### ssl read error
If you receive the following error while running `vagrant up`:
```console
SSL read: error:00000000:lib(0):func(0):reason(0), errno 104
```
It means that either your network connection is unstable and/or very
slow. To resolve it, run `vagrant up` until it works (possibly on a
better network connection).
#### ssh connection closed by remote host
On running `vagrant ssh`, if you see the following error:
```console
ssh_exchange_identification: Connection closed by remote host
```
It usually means the Vagrant guest is not running, which is usually
solved by rebooting the Vagrant guest via `vagrant halt; vagrant up`. See
[Vagrant was unable to communicate with the guest machine](/development/setup-recommended.md#vagrant-was-unable-to-communicate-with-the-guest-machine)
for more details.
#### Vagrant was unable to communicate with the guest machine
If you see the following error when you run `vagrant up`:
```console
Timed out while waiting for the machine to boot. This means that
Vagrant was unable to communicate with the guest machine within
the configured ("config.vm.boot_timeout" value) time period.
If you look above, you should be able to see the error(s) that
Vagrant had when attempting to connect to the machine. These errors
are usually good hints as to what may be wrong.
If you're using a custom box, make sure that networking is properly
working and you're able to connect to the machine. It is a common
problem that networking isn't setup properly in these boxes.
Verify that authentication configurations are also setup properly,
as well.
If the box appears to be booting properly, you may want to increase
the timeout ("config.vm.boot_timeout") value.
```
This has a range of possible causes, that usually amount to a bug in
Virtualbox or Vagrant. If you see this error, you usually can fix it
by rebooting the guest via `vagrant halt; vagrant up`.
#### Vagrant up fails with subprocess.CalledProcessError
The `vagrant up` command basically does the following:
- Downloads an Ubuntu image and starts it using a Vagrant provider.
- Uses `vagrant ssh` to connect to that Ubuntu guest, and then runs
`tools/provision`, which has a lot of subcommands that are
executed via Python's `subprocess` module. These errors mean that
one of those subcommands failed.
To debug such errors, you can log in to the Vagrant guest machine by
running `vagrant ssh`, which should present you with a standard shell
prompt. You can debug interactively by using, for example,
`cd zulip && ./tools/provision`, and then running the individual
subcommands that failed. Once you've resolved the problem, you can
rerun `tools/provision` to proceed; the provisioning system is
designed to recover well from failures.
The Zulip provisioning system is generally highly reliable; the most common
cause of issues here is a poor network connection (or one where you need a
proxy to access the Internet and haven't [configured the development
environment to use it](/development/setup-recommended.md#specifying-a-proxy)).
Once you've provisioned successfully, you'll get output like this:
```console
Zulip development environment setup succeeded!
(zulip-server) vagrant@vagrant:/srv/zulip$
```
If the `(zulip-server)` part is missing, this is because your
installation failed the first time before the Zulip virtualenv was
created. You can fix this by just closing the shell and running
`vagrant ssh` again, or using `source .venv/bin/activate`.
Finally, if you encounter any issues that weren't caused by your
Internet connection, please report them! We try hard to keep Zulip
development environment provisioning free of bugs.
##### `pip install` fails during `vagrant up` on Linux
Likely causes are:
1. Networking issues
2. Insufficient RAM. Check whether you've allotted at least two
gigabytes of RAM, which is the minimum Zulip
[requires](/development/setup-recommended.md#requirements). If
not, go to your VM settings and increase the RAM, then restart
the VM.

View File

@@ -0,0 +1,14 @@
#### Unmet dependencies 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).
```
It means that your local apt repository has been corrupted, which can
usually be resolved by executing the command:
```console
$ apt-get -f install
```

View File

@@ -0,0 +1,36 @@
To shut down but preserve the development environment so you can use
it again later use `vagrant halt` or `vagrant suspend`.
You can do this from the same Terminal/Git BASH window that is running
run-dev by pressing ^C to halt the server and then typing `exit`. Or you
can halt Vagrant from another Terminal/Git BASH window.
From the window where run-dev is running:
```console
2016-05-04 18:33:13,330 INFO 127.0.0.1 GET 200 92ms /register/ (unauth@zulip via ?)
^C
KeyboardInterrupt
(zulip-server) vagrant@vagrant:/srv/zulip$ exit
logout
Connection to 127.0.0.1 closed.
$
```
Now you can suspend the development environment:
```console
$ vagrant suspend
==> default: Saving VM state and suspending execution...
```
If `vagrant suspend` doesn't work, try `vagrant halt`:
```console
$ vagrant halt
==> default: Attempting graceful shutdown of VM...
```
Check out the Vagrant documentation to learn more about
[suspend](https://www.vagrantup.com/docs/cli/suspend.html) and
[halt](https://www.vagrantup.com/docs/cli/halt.html).

View File

@@ -0,0 +1,15 @@
If you ever want to recreate your development environment again from
scratch (e.g., to test a change you've made to the provisioning
process, or because you think something is broken), you can do so
using `vagrant destroy` and then `vagrant up`. This will usually be
much faster than the original `vagrant up` since the base image is
already cached on your machine (it takes about 5 minutes to run with a
fast Internet connection).
Any additional programs (e.g., Zsh, emacs, etc.) or configuration that
you may have installed in the development environment will be lost
when you recreate it. To address this, you can create a script called
`tools/custom_provision` in your Zulip Git checkout; and place any
extra setup commands there. Vagrant will run `tools/custom_provision`
every time you run `vagrant provision` (or create a Vagrant guest via
`vagrant up`).

View File

@@ -0,0 +1,11 @@
When you're ready to work on Zulip again, run `vagrant up` (no need to
pass the `--provider` option required above). You will also need to
connect to the virtual machine with `vagrant ssh` and re-start the
Zulip server:
```console
$ vagrant up
$ vagrant ssh
(zulip-server) vagrant@vagrant:/srv/zulip$ ./tools/run-dev
```

View File

@@ -0,0 +1,64 @@
Once `vagrant up` has completed, connect to the development environment
with `vagrant ssh`:
```console
$ vagrant ssh
```
You should see output that starts like this:
```console
Welcome to Ubuntu 22.04.3 LTS (GNU/Linux 5.15.0-92-generic x86_64)
```
Congrats, you're now inside the Zulip development environment!
You can confirm this by looking at the command prompt, which starts
with `(zulip-server) vagrant@`. If it just starts with `vagrant@`, your
provisioning failed and you should look at the
[troubleshooting section](/development/setup-recommended.md#troubleshooting-and-common-errors).
Next, start the Zulip server:
```console
(zulip-server) vagrant@vagrant:/srv/zulip$ ./tools/run-dev
```
You will see something like:
```console
Starting Zulip on:
http://localhost:9991/
Internal ports:
9991: Development server proxy (connect here)
9992: Django
9993: Tornado
9994: webpack
Tornado server (re)started on port 9993
2023-12-15 20:57:14.206 INFO [process_queue] 13 queue worker threads were launched
frontend:
frontend (webpack 5.89.0) compiled successfully in 8054 ms
```
Now the Zulip server should be running and accessible. Verify this by
navigating to <http://localhost:9991/devlogin> in the browser on your main machine.
You should see something like this:
![Image of Zulip devlogin](/images/zulip-devlogin.png)
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
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 ?)
[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 ?)
[04/May/2016 18:21:57]"GET /login HTTP/1.0" 301 0
2016-05-04 18:21:57,819 INFO 127.0.0.1 GET 200 209ms (db: 7ms/2q) /login/ (unauth@zulip via ?)
```

View File

@@ -0,0 +1,25 @@
The first time you run this command it will take some time because Vagrant
does the following:
- downloads the base Ubuntu 22.04 virtual machine/Docker image
- configures this virtual machine/container for use with Zulip,
- creates a shared directory mapping your clone of the Zulip code inside the
virtual machine/container at `~/zulip`
- runs the `./tools/provision` script inside the virtual machine/container, which
downloads all required dependencies, sets up the Python environment for
the Zulip development server, and initializes a default test
database. We call this process "provisioning", and it is documented
in some detail in our [dependencies documentation](/subsystems/dependencies.md).
You will need an active internet connection during the entire
process. (See [Specifying a proxy](/development/setup-recommended.md#specifying-a-proxy) if you need a
proxy to access the internet.) `vagrant up` can fail while
provisioning if your Internet connection is unreliable. To retry, you
can use `vagrant provision` (`vagrant up` will just boot the guest
without provisioning after the first time). Other common issues are
documented in the
[Troubleshooting and common errors](/development/setup-recommended.md#troubleshooting-and-common-errors)
section. If that doesn't help, please visit
[#provision help](https://chat.zulip.org/#narrow/channel/21-provision-help)
in the [Zulip development community server](https://zulip.com/development-community/) for
real-time help.

View File

@@ -0,0 +1,7 @@
Change into the zulip directory and tell Vagrant to start the Zulip
development environment with `vagrant up`:
```console
$ cd zulip
$ vagrant up --provider=docker
```

View File

@@ -0,0 +1,18 @@
If after rebasing onto a new version of the Zulip server, you receive
new errors while starting the Zulip server or running tests, this is
probably not because Zulip's `main` branch is broken. Instead, this
is likely because we've recently merged changes to the development
environment provisioning process that you need to apply to your
development environment. To update your environment, you'll need to
re-provision your Vagrant machine using `vagrant provision` (this just
runs `tools/provision` from your Zulip checkout inside the Vagrant
guest); this should complete in about a minute.
After provisioning, you'll want to
[(re)start the Zulip development server](/development/setup-recommended.md#step-3-start-the-development-environment).
If you run into any trouble, [#provision
help](https://chat.zulip.org/#narrow/channel/21-provision-help) in the
[Zulip development community
server](https://zulip.com/development-community/) is a great place to ask for
help.

View File

@@ -0,0 +1,34 @@
If your preferred editor is Visual Studio Code, the [Visual Studio
Code Remote - SSH](https://code.visualstudio.com/docs/remote/ssh)
extension is recommended for editing files when developing with
Vagrant. When you have it installed, you can run:
```console
$ code .
```
to open VS Code connected to your Vagrant environment. See the
[Remote development over SSH](https://code.visualstudio.com/docs/remote/ssh-tutorial) tutorial for more information.
When using this plugin with Vagrant, you will want to run the command
`vagrant ssh-config` from your `zulip` folder:
```console
$ vagrant ssh-config
Host default
HostName 127.0.0.1
User vagrant
Port 2222
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
PasswordAuthentication no
IdentityFile /PATH/TO/zulip/.vagrant/machines/default/docker/private_key
IdentitiesOnly yes
LogLevel FATAL
PubkeyAcceptedKeyTypes +ssh-rsa
HostKeyAlgorithms +ssh-rsa
```
Then copy that config into your `~/.ssh/config` file. You may want to change
the host name from `default` to something more descriptive, like `zulip`.
Finally, refresh the known remotes in Visual Studio Code's Remote Explorer.

View File

@@ -0,0 +1,196 @@
#### Vagrant was unable to mount VirtualBox shared folders
For the following error:
```console
Vagrant was unable to mount VirtualBox shared folders. This is usually
because the filesystem "vboxsf" is not available. This filesystem is
made available via the VirtualBox Guest Additions and kernel
module. Please verify that these guest additions are properly
installed in the guest. This is not a bug in Vagrant and is usually
caused by a faulty Vagrant box. For context, the command attempted
was:
mount -t vboxsf -o uid=1000,gid=1000 keys /keys
```
If this error starts happening unexpectedly, then just run:
```console
$ vagrant halt
$ vagrant up
```
to reboot the guest. After this, you can do `vagrant provision` and
`vagrant ssh`.
#### os.symlink error
If you receive the following error while running `vagrant up`:
```console
==> default: Traceback (most recent call last):
==> default: File "./emoji_dump.py", line 75, in <module>
==> default:
==> default: os.symlink('unicode/{}.png'.format(code_point), 'out/{}.png'.format(name))
==> default: OSError
==> default: :
==> default: [Errno 71] Protocol error
```
Then Vagrant was not able to create a symbolic link.
First, if you are using Windows, **make sure you have run Git BASH (or
Cygwin) as an administrator**. By default, only administrators can
create symbolic links on Windows. Additionally [UAC][windows-uac], a
Windows feature intended to limit the impact of malware, can prevent
even administrator accounts from creating symlinks. [Turning off
UAC][disable-uac] will allow you to create symlinks. You can also try
some of the solutions mentioned
[here](https://superuser.com/questions/124679/how-do-i-create-a-link-in-windows-7-home-premium-as-a-regular-user).
[windows-uac]: https://docs.microsoft.com/en-us/windows/security/identity-protection/user-account-control/how-user-account-control-works
[disable-uac]: https://stackoverflow.com/questions/15320550/why-is-secreatesymboliclinkprivilege-ignored-on-windows-8
If you ran Git BASH as administrator but you already had VirtualBox
running, you might still get this error because VirtualBox is not
running as administrator. In that case: close the Zulip VM with
`vagrant halt`; close any other VirtualBox VMs that may be running;
exit VirtualBox; and try again with `vagrant up --provision` from a
Git BASH running as administrator.
Second, VirtualBox does not enable symbolic links by default. Vagrant
starting with version 1.6.0 enables symbolic links for VirtualBox shared
folder.
You can check to see that this is enabled for your virtual machine with
`vboxmanage` command.
Get the name of your virtual machine by running `vboxmanage list vms` and
then print out the custom settings for this virtual machine with
`vboxmanage getextradata YOURVMNAME enumerate`:
```console
$ vboxmanage list vms
"zulip_default_1462498139595_55484" {5a65199d-8afa-4265-b2f6-6b1f162f157d}
$ vboxmanage getextradata zulip_default_1462498139595_55484 enumerate
Key: VBoxInternal2/SharedFoldersEnableSymlinksCreate/srv_zulip, Value: 1
Key: supported, Value: false
```
If you see "command not found" when you try to run VBoxManage, you need to
add the VirtualBox directory to your path. On Windows this is mostly likely
`C:\Program Files\Oracle\VirtualBox\`.
If `vboxmanage enumerate` prints nothing, or shows a value of 0 for
VBoxInternal2/SharedFoldersEnableSymlinksCreate/srv_zulip, then enable
symbolic links by running this command in Terminal/Git BASH/Cygwin:
```console
$ vboxmanage setextradata YOURVMNAME VBoxInternal2/SharedFoldersEnableSymlinksCreate/srv_zulip 1
```
The virtual machine needs to be shut down when you run this command.
#### Hyper-V error messages
If you get an error message on Windows about lack of Windows Home
support for Hyper-V when running `vagrant up`, the problem is that
Windows is incorrectly attempting to use Hyper-V rather than
Virtualbox as the virtualization provider. You can fix this by
explicitly passing the virtualbox provider to `vagrant up`:
```console
$ vagrant up --provide=virtualbox
```
#### Connection timeout on `vagrant up`
If you see the following error after running `vagrant up`:
```console
default: SSH address: 127.0.0.1:2222
default: SSH username: vagrant
default: SSH auth method: private key
default: Error: Connection timeout. Retrying...
default: Error: Connection timeout. Retrying...
default: Error: Connection timeout. Retrying...
```
A likely cause is that hardware virtualization is not enabled for your
computer. This must be done via your computer's BIOS settings. Look for a
setting called VT-x (Intel) or (AMD-V).
If this is already enabled in your BIOS, double-check that you are running a
64-bit operating system.
For further information about troubleshooting Vagrant timeout errors [see
this post](https://stackoverflow.com/questions/22575261/vagrant-stuck-connection-timeout-retrying#22575302).
#### VBoxManage errors related to VT-x or WHvSetupPartition
```console
There was an error while executing `VBoxManage`, a CLI used by Vagrant
for controlling VirtualBox. The command and stderr is shown below.
Command: ["startvm", "8924a681-b4e4-4b7a-96f2-4cb11619f123", "--type", "headless"]
Stderr: VBoxManage.exe: error: (VERR_NEM_MISSING_KERNEL_API).
VBoxManage.exe: error: VT-x is not available (VERR_VMX_NO_VMX)
VBoxManage.exe: error: Details: code E_FAIL (0x80004005), component ConsoleWrap, interface IConsole
```
or
```console
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
```
First, ensure that hardware virtualization support (VT-x or AMD-V) is
enabled in your BIOS.
If the error persists, you may have run into an incompatibility
between VirtualBox and Hyper-V on Windows. To disable Hyper-V, open
command prompt as administrator, run
`bcdedit /set hypervisorlaunchtype off`, and reboot. If you need to
enable it later, run `bcdedit /deletevalue hypervisorlaunchtype`, and
reboot.
#### OSError: [Errno 26] Text file busy
```console
default: Traceback (most recent call last):
…
default: File "/srv/zulip-py3-venv/lib/python3.6/shutil.py", line 426, in _rmtree_safe_fd
default: os.rmdir(name, dir_fd=topfd)
default: OSError: [Errno 26] Text file busy: 'baremetrics'
```
This error is caused by a
[bug](https://www.virtualbox.org/ticket/19004) in recent versions of
the VirtualBox Guest Additions for Linux on Windows hosts. You can
check the running version of VirtualBox Guest Additions with this
command:
```console
$ vagrant ssh -- 'sudo modinfo -F version vboxsf'
```
The bug has not been fixed upstream as of this writing, but you may be
able to work around it by downgrading VirtualBox Guest Additions to
5.2.44. To do this, create a `~/.zulip-vagrant-config` file and add
this line:
```text
VBOXADD_VERSION 5.2.44
```
Then run these commands (yes, reload is needed twice):
```console
$ vagrant plugin install vagrant-vbguest
$ vagrant reload
$ vagrant reload --provision
```

View File

@@ -0,0 +1,42 @@
If you ever want to recreate your development environment again from
scratch (e.g., to test a change you've made to the provisioning
process, or because you think something is broken), you can do so
using the following steps:
1. To find the distribution name to unregister (delete), open Command
Prompt or PowerShell and use the following command:
```console
$ wsl --list --verbose
```
If you are unsure about which distribution to unregister, you can log
into the WSL distributions to ensure you are deleting the one
containing your development environment using the command:
```console
wsl -d <Distribution Name>
```
2. To uninstall your WSL distribution, enter the command:
```console
$ wsl --unregister <Distribution Name>
```
For more information, checkout the [official documentation for WSL
commands](https://learn.microsoft.com/en-us/windows/wsl/basic-commands#unregister-or-uninstall-a-linux-distribution)
3. **Next, follow the setup instructions**, starting from [[Step 1:
Install
prerequisites]](/development/setup-recommended.md#step-1-install-prerequisites)
If you just want to rebuild the development database, the following is
much faster:
```console
$ ./tools/rebuild-dev-database
```
For more details, see the [schema migration
documentation](/subsystems/schema-migrations.md#local-schema-rebuilds).

View File

@@ -0,0 +1,75 @@
WSL2 users often encounter issues where services fail to start or remain inactive. Follow the steps below to diagnose and resolve such problems.
#### 1. Check the Status of the Service
To verify if a service is running, use the following command:
```console
$ systemctl status <service_name>
```
If the service is inactive, you can attempt to start it with:
```console
$ systemctl start <service_name>
```
#### 2. Diagnose Port Conflicts
Services like `postgresql` may fail to start due to port conflicts. These conflicts can be caused by:
- Other services running in Windows.
- Services running in another WSL2 instance.
#### Resolving Port Conflicts with Other WSL Instances
To resolve port conflicts with another WSL2 instance, stop the conflicting instance using the following command:
```console
> wsl -t <WSL_Instance_Name>
```
After stopping the conflicting instance, restart your WSL instance with:
```console
> wsl -d <Your_Zulip_Instance_Name>
```
#### Resolving Port Conflicts with Services Running on Windows
To resolve conflicts caused by Windows processes:
1. Identify the process using the conflicting port by running:
```console
> Get-Process -Id (Get-NetTCPConnection -LocalPort <your_port_number>).OwningProcess
```
2. If a process is found, terminate it using:
```console
> taskkill /PID <pid> /F
```
3. Restart the Service or Enable Auto-Start
After resolving port conflicts, try restarting the service using:
```console
$ systemctl start <service_name>
```
To ensure the service always starts on boot, enable it with:
```console
$ systemctl enable <service_name>
```
---
#### Additional Tips
- Use `wsl --list` to view all running WSL2 instances and their states.
- Avoid overlapping port usage between WSL2 instances and Windows processes.
- Keep a record of services and their associated port numbers to prevent conflicts in the future.
- Ensure that you use a fresh WSL instance to setup the Zulip development environment to avoid dependency conflicts.

View File

@@ -0,0 +1,133 @@
# Testing the installer
Zulip's install process is tested as part of [its continuous
integrations suite][ci], but that only tests the most common
configurations; when making changes to more complicated [installation
options][installer-docs], Zulip provides tooling to repeatedly test
the installation process in a clean environment each time.
[ci]: https://github.com/zulip/zulip/actions/workflows/production-suite.yml?query=branch%3Amain
[installer-docs]: ../production/install.md
## Configuring
Using the test installer framework requires a Linux operating system;
it will not work on WSL, for instance. It requires at least 3G of
RAM, in order to accommodate the VMs and the steps which build the
release assets.
To begin, install the LXC toolchain:
```bash
sudo apt-get install lxc lxc-utils
```
All LXC commands (and hence many parts of the test installer) will
need to be run as root.
## Running a test install
### Build and unpack a release tarball
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
doesn't work cleanly out of a source checkout; it wants a release
checkout, so we build a tarball of one of those first:
```bash
./tools/build-release-tarball test-installer
```
This will produce a file in /tmp, which it will print out the path to
as the last step; for example,
`/tmp/tmp.fepqqNBWxp/zulip-server-test-installer.tar.gz`
Next, unpack that file into a local directory; we will make any
changes we want in our source checkout and copy them into this
directory. The test installer needs the release directory to be named
`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
the tarball, above:
```bash
tar xzf /tmp/tmp.fepqqNBWxp/zulip-server-test-installer.tar.gz
mkdir zulip-test-installer
mv zulip-server-test-installer zulip-test-installer/zulip-server
```
You should delete and re-create this `zulip-test-installer` directory
(using these steps) if you are working on a different installer
branch, or a significant time has passed since you last used it.
### Test an install
The `test-install` tooling takes a distribution release name
(e.g., "jammy"), the path to an unpacked release directory
or tarball, and then any of the normal options you want to pass down
into the installer.
For example, to test an install onto Ubuntu 22.04 "Jammy", we might
call:
```bash
sudo ./tools/test-install/install \
-r jammy \
./zulip-test-installer/ \
--hostname=zulip.example.net \
--email=username@example.net
```
The first time you run this command for a given distribution, it will
build a "base" image for that to use on subsequent times; this will
take a while.
### See running containers after installation
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
running, and their randomly-generated names, by running:
```bash
sudo lxc-ls -f
```
### Connect to a running container
After using `lxc-ls` to list containers, you can choose one of them
and connect to its terminal:
```bash
sudo lxc-attach --clear-env -n zulip-install-jammy-PUvff
```
### Stopping and destroying containers
To destroy all containers (but leave the base containers, which speed
up the initial install):
```bash
sudo ./tools/test-install/destroy-all -f
```
To destroy just one container:
```bash
sudo lxc-destroy -f -n zulip-install-jammy-PUvff
```
### Iterating on the installer
Iterate on the installer by making changes to your source tree,
copying them into the release directory, and re-running the installer,
which will start up a new container. Here, we update just the
`scripts` and `puppet` directories of the release directory:
```bash
rsync -az scripts puppet zulip-test-installer/zulip-server/
sudo ./tools/test-install/install \
-r jammy \
./zulip-test-installer/ \
--hostname=zulip.example.net \
--email=username@example.net
```

103
docs/development/using.md Normal file
View File

@@ -0,0 +1,103 @@
# Using the development environment
This page describes the basic edit/refresh workflows for working with
the Zulip development environment. Generally, the development
environment will automatically update as soon as you save changes
using your editor. Details for work on the [server](#server),
[web app](#web), and [mobile apps](#mobile) are below.
If you're working on authentication methods or need to use the [Zulip
REST API][rest-api], which requires an API key, see [authentication in
the development environment][authentication-dev-server].
## Common
- Zulip's `main` branch moves quickly, and you should rebase
constantly with, for example,
`git fetch upstream; git rebase upstream/main` to avoid developing
on an old version of the Zulip codebase (leading to unnecessary
merge conflicts).
- Remember to run `tools/provision` to update your development
environment after switching branches; it will run in under a second
if no changes are required.
- After making changes, you'll often want to run the
[linters](../testing/linters.md) and relevant [test
suites](../testing/testing.md). Consider using our [Git pre-commit
hook](../git/zulip-tools.md#set-up-git-repo-script) to
automatically lint whenever you make a commit.
- All of our test suites are designed to support quickly testing just
a single file or test case, which you should take advantage of to
save time.
- Many useful development tools, including tools for rebuilding the
database with different test data, are documented in-app at
`https://localhost:9991/devtools`.
- If you want to restore your development environment's database to a
pristine state, you can use `./tools/rebuild-dev-database`.
## Server
- For changes that don't affect the database model, the Zulip
development environment will automatically detect changes and
restart:
- The main Django/Tornado server processes are run on top of
Django's [manage.py runserver][django-runserver], which will
automatically restart them when you save changes to Python code
they use. You can watch this happen in the `run-dev` console
to make sure the backend has reloaded.
- The Python queue workers will also automatically restart when you
save changes, as long as they haven't crashed (which can happen if
they reloaded into a version with a syntax error).
- If you change the database schema (`zerver/models/*.py`), you'll need
to use the [Django migrations
process](../subsystems/schema-migrations.md); see also the [new
feature tutorial][new-feature-tutorial] for an example.
- While testing server changes, it's helpful to watch the `run-dev`
console output, which will show tracebacks for any 500 errors your
Zulip development server encounters (which are probably caused by
bugs in your code).
- To manually query Zulip's database interactively, use
`./manage.py shell` or `manage.py dbshell`.
- The database(s) used for the automated tests are independent from
the one you use for manual testing in the UI, so changes you make to
the database manually will never affect the automated tests.
## Web
- Once the development server (`run-dev`) is running, you can visit
<http://localhost:9991/> in your browser.
- By default, the development server homepage just shows a list of the
users that exist on the server and you can log in as any of them by
just clicking on a user.
- This setup saves time for the common case where you want to test
something other than the login process.
- You can test the login or registration process by clicking the
links for the normal login page.
- Most changes will take effect automatically. Details:
- If you change CSS files, your changes will appear immediately via
webpack hot module replacement.
- If you change JavaScript code (`web/src`) or Handlebars
templates (`web/templates`), the browser window will be
reloaded automatically.
- For Jinja2 backend templates (`templates/*`), you'll need to reload
the browser window to see your changes.
- Any JavaScript exceptions encountered while using the web app in a
development environment will be displayed as a large notice, so you
don't need to watch the JavaScript console for exceptions.
- Both Chrome and Firefox have great debuggers, inspectors, and
profilers in their built-in developer tools.
- `debug.js` has some occasionally useful JavaScript profiling code.
## Mobile
See the mobile project's documentation on [getting set up to develop
and contribute to the mobile app][mobile-development-guide], including
how to [run the app against your development
server][mobile-dev-server-guide].
[rest-api]: https://zulip.com/api/rest
[authentication-dev-server]: authentication.md
[django-runserver]: https://docs.djangoproject.com/en/5.0/ref/django-admin/#runserver
[new-feature-tutorial]: ../tutorials/new-feature-tutorial.md
[testing-docs]: ../testing/testing.md
[mobile-development-guide]: https://github.com/zulip/zulip-flutter/blob/main/docs/setup.md
[mobile-dev-server-guide]: https://github.com/zulip/zulip-flutter/blob/main/docs/howto/dev-server.md

631
docs/documentation/api.md Normal file
View File

@@ -0,0 +1,631 @@
# Documenting REST API endpoints
This document explains the system for documenting [Zulip's REST
API](https://zulip.com/api/rest). See also the [API design
documentation](../processes/api-design.md) which covers how to safely
make changes to the Zulip API.
Zulip's API documentation is an essential resource both for users and
for the developers of Zulip's mobile and terminal apps. Our vision is
for the documentation to be sufficiently good that developers of
Zulip's apps should never need to look at the server's implementation
to answer questions about the API's semantics.
To achieve these goals, Zulip leverages the popular OpenAPI format as
the data source to ensure that Zulip's API documentation is correct
and remains so as Zulip's API evolves.
In particular, the top goal for this system is that all mistakes in
verifiable content (i.e. not the English explanations) should cause
the Zulip test suite to fail. This is incredibly important, because
once you notice one error in API documentation, you no longer trust it
to be correct, which ends up wasting the time of its users.
Since it's very difficult to not make little mistakes when writing any
untested code, the only good solution to this is a way to test
the documentation. We found dozens of errors in the process of adding
the validation Zulip has today.
Our API documentation is defined by a few sets of files:
- The primary source of our API documentation is the Zulip server's
[OpenAPI description](openapi.md) at
`zerver/openapi/zulip.yaml`.
- The documentation is written the same Markdown framework that powers
our [integration docs](integrations.md), with some special
extensions for rendering nice code blocks and example
responses. Most API endpoints share a common template,
`api_docs/api-doc-template.md`, which renders the
OpenAPI description of the API endpoint. A handful of endpoints that
require special content, as well as pages that document general API
details rather than specific endpoints, live at `api_docs/*.md`.
- We have an extensive set of tests designed to validate that the data
in the OpenAPI file matching the implementation. Specifically,
`zerver/tests/test_openapi.py` compares every endpoint's accepted
parameters in `views` code with those declared in `zulip.yaml`. And
the [backend test suite](../testing/testing-with-django.md) checks
that every API response served during our extensive backend test
suite matches one the declared OpenAPI schema for that endpoint.
- The text for the Python examples comes from a test suite for the
Python API documentation (`zerver/openapi/python_examples.py`; run via
`tools/test-api`). The `generate_code_example` macro will magically
read content from that test suite and render it as the code example.
This structure ensures that Zulip's API documentation is robust to a
wide range of possible typos and other bugs in the API
documentation.
- The JavaScript examples are similarly generated and tested using
`zerver/openapi/javascript_examples.js`.
- The cURL examples are generated and tested using
`zerver/openapi/curl_param_value_generators.py`.
- The REST API index
(`api_docs/include/rest-endpoints.md`) in the broader
/api left sidebar (`api_docs/sidebar_index.md`).
This first section is focused on explaining how the API documentation
system is put together; when actually documenting an endpoint, you'll
want to also read the [Step by step guide](#step-by-step-guide).
## How it works
Let's use the existing documentation for one of our REST API endpoints
to show how the documentation system works:
[POST /messages/render](https://zulip.com/api/render-message).
We highly recommend looking at these resources while reading the above
documentation page:
- `api_docs/api-doc-template.md`
- `zerver/openapi/zulip.yaml`, specifically the section with
`operationId: render-message`
- `zerver/openapi/python_examples.py`
If you look at the documentation for existing endpoints, you'll notice
that a typical endpoint's documentation is divided into four sections:
- **Title and description**
- **Usage examples**
- **Parameters**
- **Response with examples**
The rest of this guide describes how each of these sections works.
### Title and description
The first line of `api-doc-template.md` generates a lot of key
information for our API endpoint documentation:
```
{generate_api_header(API_ENDPOINT_NAME)}
```
At the top of the endpoint documentation page is the title, and it
comes from the `summary` parameter in the OpenAPI data,
`zerver/openapi/zulip.yaml`.
The endpoint `description` in the OpenAPI data explains what the
endpoint does in clear English. It should include details on how to
use the endpoint correctly or what it's good or bad for, with links
to any alternative endpoints the user might want to consider.
The description should often contain a link to the documentation of
the relevant feature in the [help center](helpcenter.md), and should
include **Changes** notes for all feature level updates documented
in the [API changelog](https://zulip.com/api/changelog), see
`api_docs/changelog.md`, that reference the endpoint.
The description itself should always describe the behavior of
current servers, so that a reader never needs to consult a
**Changes** note to understand it. The **Changes** notes exist for
understanding the behavior of older servers, starting from that of
current servers. So when documenting a change to existing behavior,
write the note as a diff going backward in time, giving only the
details of the old behavior: for example, "Before Zulip 12.0
(feature level 483), the server did not automatically remove legacy
registrations", rather than "As of Zulip 12.0 (feature level 483),
the server now automatically removes...". The forward framing
misleadingly suggests that the note carries all the details of the
new behavior, when those belong in the main description.
Endpoints that only administrators can use should be tagged with the
custom `x-requires-administrator` field in the OpenAPI definition.
All of this information is rendered via a Markdown preprocessor,
specifically the `APIHeaderPreprocessor` class defined in
`zerver/openapi/markdown_extension.py`.
### Usage examples
We display usage examples in three languages: Python, JavaScript and
curl; we may add more in the future. Every endpoint should have
Python and curl documentation; JavaScript is optional as we don't
consider that API library to be fully supported.
The examples are defined using a special Markdown extension, see
`zerver/openapi/markdown_extension.py`. Here's the Markdown file
block that uses this in `api-doc-template.md`:
```md
{start_tabs}
{generate_code_example(python)|API_ENDPOINT_NAME|example}
{generate_code_example(javascript)|API_ENDPOINT_NAME|example}
{tab|curl}
{generate_code_example(curl)|API_ENDPOINT_NAME|example}
{end_tabs}
```
In some cases, one wants to configure specific parameters to be
included or excluded from the example curl requests for readability
reasons. One can do that using the `x-curl-examples-parameters`
parameter in the OpenAPI data.
#### Writing Python examples
For the Python examples, you'll write the example in
`zerver/openapi/python_examples.py`, and it'll be run and verified
automatically in Zulip's automated test suite. The code for our
example API endpoint looks like this:
```python
@openapi_test_function('/messages/render:post')
def render_message(client: Client) -> None:
# {code_example|start}
# Render a message
request = {
'content': '**foo**'
}
result = client.render_message(request)
# {code_example|end}
validate_against_openapi_schema(result, '/messages/render', 'post', '200')
```
This is an actual Python function which will be run as part of the
`tools/test-api` test suite. The `validate_against_openapi_schema`
function will verify that the result of that request is as defined in
the examples in `zerver/openapi/zulip.yaml`.
To run as part of the test suite, the `render_message` function needs
to be called from `test_messages` (or one of the other functions at
the bottom of the file). The final function, `test_the_api`, is what
actually runs the tests. Tests with the `openapi_test_function`
decorator that are not called will fail tests, as will new endpoints
that are not covered by an `openapi_test_function`-decorated test.
You will still want to manually test the example using Zulip's Python
API client by copy-pasting from the website; it's easy to make typos
and other mistakes where variables are defined outside the tested
block, and the tests are not foolproof.
The code that renders API documentation pages will extract the block
between the `# {code_example|start}` and `# {code_example|end}` comments,
and substitute it in place of
`{generate_code_example(python)|/messages/render:post|example}`. Note
that here the `API_ENDPOINT_NAME` has been filled in with our example
endpoint's information.
Additional Python imports can be added using the custom
`x-python-examples-extra-imports` field in the OpenAPI definition.
### Parameters
We have a separate Markdown extension to document the parameters that
an API endpoint supports. Implemented in
`zerver/lib/markdown/api_arguments_table_generator.py`, you can see
this in `api-doc-template.md` after the **Parameters** header:
```
{generate_api_arguments_table|zulip.yaml|API_ENDPOINT_NAME}
```
This generates the information from the endpoint's parameter
definition in the OpenAPI data.
Additional content that you'd like to appear in the parameter
description area can be declared using the custom
`x-parameter-description` field in the OpenAPI definition.
### Response with examples
Similar to the parameters section above, there is a separate Markdown
extension to document the endpoint's return values and generate the
example response(s) from the OpenAPI data. Implemented in
`zerver/lib/markdown/api_return_values_table_generator.py`, you can
see this in after the **Response** header in `api-doc-template.md`:
```
{generate_return_values_table|zulip.yaml|API_ENDPOINT_NAME}
```
To generate the example responses from the OpenAPI data, we again
use the special Markdown extension from the **Usage examples**
discussed above, except with the `fixture` argument instead of the
`example` argument:
```
{generate_code_example|API_ENDPOINT_NAME|fixture}
```
Additional content that you'd like to appear in the responses part of
the page can be added using the custom `x-response-description` field
in the OpenAPI definition.
## Step by step guide
This section offers a step-by-step process for adding documentation
for a new API endpoint. It assumes you've read and understood the
above.
1. Start by adding [OpenAPI format](openapi.md)
data to `zerver/openapi/zulip.yaml` for the endpoint. If you
copy-paste (which is helpful to get the indentation structure
right), be sure to update all the content that you copied to
correctly describe your endpoint!
In order to do this, you need to figure out how the endpoint in
question works by reading the code! To understand how arguments
are specified in Zulip backend endpoints, read our [REST API
tutorial][rest-api-tutorial], paying special attention to the
details of `typed_endpoint`.
Once you understand that, the best way to determine the supported
arguments for an API endpoint is to find the corresponding URL
pattern in `zproject/urls.py`, look up the backend function for
that endpoint in `zerver/views/`, and inspect its keyword-only
arguments.
You can check your formatting using these helpful tools.
- `tools/check-openapi.ts` will verify the syntax of `zerver/openapi/zulip.yaml`.
- `tools/test-backend zerver/tests/test_openapi.py`; this test compares
your documentation against the code and can find many common
mistakes in how arguments are declared.
- `test-backend`: The full Zulip backend test suite will fail if
any actual API responses generated by the tests don't match your
defined OpenAPI schema. Use `test-backend --rerun` for a fast
edit/refresh cycle when debugging.
[rest-api-tutorial]: ../tutorials/writing-views.md#writing-api-rest-endpoints
1. Add a function for the endpoint you'd like to document to
`zerver/openapi/python_examples.py`, decorated with
`@openapi_test_function`. `render_message` is a good example to
follow. There are generally two key pieces to your test: (1) doing
an API query and (2) verifying its result has the expected format
using `validate_against_openapi_schema`.
1. Make the desired API call inside the function. If our Python
bindings don't have a dedicated method for a specific API call,
you may either use `client.call_endpoint` or add a dedicated
function to the [zulip PyPI
package](https://github.com/zulip/python-zulip-api/tree/main/zulip).
Ultimately, the goal is for every endpoint to be documented the
latter way, but it's useful to be able to write working
documentation for an endpoint that isn't supported by
`python-zulip-api` yet.
1. Add the function to one of the `test_*` functions at the end of
`zerver/openapi/python_examples.py`; this will ensure your
function will be called when running `test-api`.
1. Capture the JSON response returned by the API call (the test
"fixture"). The easiest way to do this is add an appropriate print
statement (usually `json.dumps(result, indent=4, sort_keys=True)`),
and then run `tools/test-api`. You can also use
<https://jsonformatter.curiousconcept.com/> to format the JSON
fixtures. Add the fixture to the `example` subsection of the
`responses` section for the endpoint in
`zerver/openapi/zulip.yaml`.
1. Run `./tools/test-api` to make sure your new test function is being
run and the tests pass.
1. Now, inside the function, isolate the lines of code that call the API and could
be displayed as a code example. Wrap the relevant lines in
`# {code_example|start} ... relevant lines go here ... # {code_example|end}`
comments. The lines inside these comments are what will be displayed as the
code example on our `/api` page.
1. Finally, if the API docs page of the endpoint doesn't follow the
common API docs template in
`api_docs/api-doc-template.md`, then add its custom
Markdown file under `api_docs/`. However, it is a goal
to minimize the number of files that diverse from the common
template, so only do this if there's a good reason.
1. Add the endpoint to the index in
`api_docs/include/rest-endpoints.md`. The URL should
match the `operationId` for the endpoint, and the link text should
match the title of the endpoint from the OpenAPI `summary` field.
1. Test your endpoint, pretending to be a new user in a hurry, by
visiting it via the links on `http://localhost:9991/api/` (the API
docs are rendered from the Markdown source files on page load, so
just reload to see an updated version as you edit). You should
make sure that copy-pasting the code in your examples works, and
post an example of the output in the pull request.
1. Run `./tools/create-api-changelog` which will create a new empty
markdown file in `api_docs/unmerged.d/` directory
(e.g., `api_docs/unmerged.d/ZF-1f4a39.md`). Open this
file and document the API changes, formatted as an unordered list
(`*` bullets). The raw content of this file will be merged into
`api_docs/changelog.md` when your commit is merged into the `main`
branch. Therefore, keep the formatting and line wrapping consistent
with the content in `api_docs/changelog.md`.
1. Add a `**Changes**` note in the description of all updates and
additions to the API documentation (`zerver/openapi/zulip.yaml`),
and mention the name of the file generated in the previous step
(without the `.md` extension) in place of the API feature level,
for example:
```yaml
**Changes**: New in Zulip 11.0 (feature level ZF-1f4a39).
```
For a change to existing behavior, make sure the main description
describes the behavior of current servers, and write the note as
a diff going backward in time, describing only the old behavior
(see [above](#title-and-description)):
```yaml
**Changes**: Before Zulip 11.0 (feature level ZF-1f4a39),
this parameter was ignored for direct messages.
```
1. Proofread your new documentation in its rendered HTML, including
all links! Unmerged changelog entries are conveniently previewed on
`/api/changelog`.
## Redirecting an existing article
From time to time, we might want to rename an article in the REST API
documentation. This change will break incoming links, including links
in published Zulip blog posts, links in other branches of the
repository that haven't been rebased, and more importantly links from
previous versions of Zulip.
To fix these broken links, you can easily add a URL redirect in:
`zerver/lib/url_redirects.py`.
For REST API documentation, you will either need to rename the file,
or you will need to update the endpoint's `operationId` in
`zerver/openapi/zulip.yaml`. Then, you need to add a new `URLRedirect`
to the `API_DOCUMENTATION_REDIRECTS` list in `url_redirects.py`:
```python
API_DOCUMENTATION_REDIRECTS: List[URLRedirect] = [
# Add URL redirects for REST API documentation here:
URLRedirect("/api/delete-stream", "/api/archive-stream"),
...
```
You should still check for references to the old URL in your branch
and replace those with the new URL (e.g., `git grep "/api/foo"`).
One exception to this are links with the old URL that were included
in the content of `zulip_update_announcements`, which can be found
in `zerver/lib/zulip_update_announcements.py`. It's preferable to
have the source code accurately reflect what was sent to users in
those [Zulip update announcements][zulip-updates], so these should
not be replaced with the new URL.
If you have the Zulip development environment set up, you can manually
test your changes by loading the old URL in your browser (e.g.,
`http://localhost:9991/api/foo`), and confirming that it redirects to
the new url (e.g., `http://localhost:9991/api`/bar`).
There is also an automated test in `zerver/tests/test_urls.py` that
checks all the URL redirects, which you can run from the command line:
```console
./tools/test-backend zerver.tests.test_urls.URLRedirectTest
```
[zulip-updates]: https://zulip.com/help/configure-automated-notices#zulip-update-announcements
## Why a custom system?
Given that our documentation is written in large part using the
OpenAPI format, why maintain a custom Markdown system for displaying
it? There's several major benefits to this system:
- It is extremely common for API documentation to become out of date
as an API evolves; this automated testing system helps make it
possible for Zulip to maintain accurate documentation without a lot
of manual management.
- Every Zulip server can host correct API documentation for its
version, with the key variables (like the Zulip server URL) already
pre-substituted for the user.
- We're able to share implementation language and visual styling with
our help center, which is especially useful for the extensive
non-REST API documentation pages (e.g., our bot framework).
Using the standard OpenAPI format gives us flexibility, though; if we
later choose to migrate to third-party tools, we don't need to redo
the actual documentation work in order to migrate tools.
## Debugging schema validation errors
A common function used to validate and test Zulip's REST API is
`validate_against_openapi_schema`. It is used to verify that every
successful API response returned in the backend and documentation test
suites are a documented possibility in the API documentation.
Therefore, when you add a new feature or setting to Zulip, you will most
likely need to update the API documentation (`zerver/openapi/zulip.yaml`)
in order to pass existing tests that use this function. Additionally, if
you're writing documentation for a new or undocumented REST API endpoint,
you'll want to use this function to validate and test your changes in
`zerver/openapi/python_examples.py`.
Below are some examples to help you when debugging the schema validation
errors produced by `validate_against_openapi_schema`. Before reading
through the examples, we recommend reviewing the
[OpenAPI configuration](openapi.md) documentation if you're unfamiliar
with the format.
If you use Visual Studio Code, an OpenAPI extension can be very helpful in
navigating Zulip's large and detailed OpenAPI file; see
`.vscode/extensions.json`.
### Deconstructing the error output
To start with a clear example, let's imagine that we are writing the
documentation for the REST API endpoint for uploading a file,
[POST /api/v1/user_uploads](https://zulip.com/api/upload-file).
There are no parameters for this endpoint, and only one return value
specific to this endpoint, `uri`, which is the URL of the uploaded file.
If we comment out that return value and example from the existing API
documentation in `zerver/openapi/zulip.yaml`, for example:
```yaml
/user_uploads:
post:
operationId: upload-file
...
responses:
"200":
description: Success.
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/JsonSuccessBase"
- additionalProperties: false
properties:
result: {}
msg: {}
# uri:
# type: string
# description: |
# The URI of the uploaded file.
example:
{
"msg": "",
"result": "success",
# "uri": "/user_uploads/1/4e/m2A3MSqFnWRLUf9SaPzQ0Up_/zulip.txt",
}
```
We will now get an error when we run the API documentation test suite
in the development environment (`tools/test-api`):
```console
Running API tests...
2022-12-19 15:05:42.347 WARN [django.server] "POST /api/v1/users HTTP/1.1" 400 88
Waiting for children to stop...
Traceback (most recent call last):
File "tools/test-api", line 93, in <module>
test_the_api(client, nonadmin_client, owner_client)
File "/srv/zulip/zerver/openapi/python_examples.py", line 1636, in test_the_api
test_users(client, owner_client)
File "/srv/zulip/zerver/openapi/python_examples.py", line 1555, in test_users
upload_file(client)
File "/srv/zulip/zerver/openapi/python_examples.py", line 52, in _record_calls_wrapper
return test_func(*args, **kwargs)
File "/srv/zulip/zerver/openapi/python_examples.py", line 1284, in upload_file
validate_against_openapi_schema(result, "/user_uploads", "post", "200")
File "/srv/zulip/zerver/openapi/openapi.py", line 489, in validate_against_openapi_schema
raise SchemaError(message) from None
zerver.openapi.openapi.SchemaError: 1 response validation error(s) at post /api/v1/user_uploads (200):
ValidationError: Additional properties are not allowed ('uri' was unexpected)
Failed validating 'additionalProperties' in schema['allOf'][2]:
{'additionalProperties': False,
'example': {'msg': '',
'result': 'success',
'properties': {'msg': {}, 'result': {}}}
On instance:
{'msg': '',
'result': 'success',
'uri': '/user_uploads/2/85/XoqF0K7XEOLVGylgdpof80RB/img.jpg'}
```
We can see in the traceback that a `SchemaError` was raised in
`validate_against_openapi_schema`:
```console
File "/srv/zulip/zerver/openapi/openapi.py", line 478, in validate_against_openapi_schema
raise SchemaError(message) from None
```
The next line in the output, lets us know how many errors were found
and for what endpoint.
```console
zerver.openapi.openapi.SchemaError: 1 response validation error(s) at post /api/v1/user_uploads (200):
```
As expected from commenting out the code above, there was one validation
error for the `POST /api/v1/user_uploads` endpoint. The next line gives
more information about that error.
```console
ValidationError: Additional properties are not allowed ('uri' was unexpected)
```
We see that there was a `uri` value returned by the endpoint that hasn't
been documented. The next few lines of output, show us what return values
are documented (again due to our changes) for this endpoint.
```console
Failed validating 'additionalProperties' in schema['allOf'][2]:
{'additionalProperties': False,
'example': {'msg': '',
'result': 'success',
'properties': {'msg': {}, 'result': {}}}
```
And finally, we see the test instance that did not match our current
documentation, which includes the `uri` return value.
```console
On instance:
{'msg': '',
'result': 'success',
'uri': '/user_uploads/2/85/XoqF0K7XEOLVGylgdpof80RB/img.jpg'}
```
This is a useful example because the endpoint's documentation is short
and straightforward, helping to easily identify the parts of the
error output that are useful in debugging these errors when testing the
API documentation.
### Adding a realm setting
Building on [the new feature tutorial](../tutorials/new-feature-tutorial.md)
example, if the realm setting for `mandatory_topics` was not documented
in the `POST /api/v1/register` endpoint, running `tools/test-api` in the
development environment would result in this error:
```console
...
zerver.openapi.openapi.SchemaError: 1 response validation error(s) at post /api/v1/register (200):
ValidationError: Additional properties are not allowed ('realm_mandatory_topics' was unexpected)
Failed validating 'additionalProperties' in schema['allOf'][2]:
'OpenAPI schema omitted due to length of output.'
On instance:
'Error instance omitted due to length of output.'
```
Because this endpoint is very long and descriptive, we do not print the
entire documentation schema (or test instance, in this case) to the
console. Doing so would print thousands of lines of output that are not
useful for debugging what is missing from the API documentation.
The key information for debugging this endpoint is in the line beginning
with `ValidationError`. There we can see that the documentation does not
include the new `realm_mandatory_topics` boolean that we added in the
example feature tutorial, and we can look at other similar realm settings
to add the documentation for that new feature.

View File

@@ -0,0 +1,653 @@
# Writing help center articles
Our goal is for Zulip to have complete, high-quality
documentation about Zulip's features and key workflows, such
as setting up an organization.
There are two types of help center documents: articles about specific features,
and a handful of longer guides. We aim to have comprehensive onboarding guides,
plus a select handful of guides for administrators and power users. Our goal is
to document all the features of the app and associated key workflows.
The feature articles that make up the bulk of help center content serve many
purposes:
- Feature and workflow explanations for Zulip users and admins, including
specific instructions and tips on when the feature would be useful. For
complex settings (e.g., permissions), detailed explanations of how the feature
works.
- Feature discovery, for someone browsing the help center, and looking at
the set of articles and guides.
- Public documentation of our feature set for search engine and LLM users.
- Quick responses to support questions; if someone emails a Zulip admin
asking "How do I change my name?", they can reply with a link to the doc.
- A reference to link to any time we mention the feature (e.g., in a Zulip
update, on the Zulip website, in a blog post, etc.), for anyone who'd like
more detail.
- Reference links from the Zulip app (e.g., for complex settings).
It's important to keep the docs up to date. We should update the help center as
needed whenever a feature is added or changed.
## Getting started
There are over 100 feature articles and longer guides in the
[Zulip help center](https://zulip.com/help/), so make the most of
the current documentation as a resource and guide as you begin.
- Use the left sidebar in the help center documentation to find the
section of the docs (e.g., Preferences, Sending messages, Reading
messages, etc.) that relates to the new feature you're documenting.
- Read through the existing articles in that section and pay attention
to the [writing style](#writing-style) used, as well as any
[features and components](#mdx-features-and-custom-zulip-components)
used to enhance the readability of the documentation.
If you aren't sure how something should be documented, the
[#documentation](https://chat.zulip.org/#narrow/channel/19-documentation)
channel in the [Zulip development
community](https://zulip.com/development-community/) is a great place to get
help.
## Structure of a feature article
The general structure of a feature article is:
1. [Introduction](#introduction)
1. One or more [sections with instructions](#instructions-section)
1. [Related articles](#related-articles)
### Introduction
This is the part of the article that requires the most thought and understanding
of context. It should generally (often in roughly this order):
1. Explain briefly **what the feature does** for the user (but not how to activate it).
1. Give some **guidance** on when someone might want to use this feature, perhaps
with specific examples.
1. **Explain more precisely** what the feature does, if there are important
points to note. Are there any details that the user may feel uncertainty
about that should be cleared up?
1. Note any **limitations**, like organization permissions settings that could disable this feature.
1. Briefly describe other features that are good **alternatives** for related workflows.
For example, the intro to the [article on moving messages to another
topic](https://github.com/zulip/zulip/blob/8ddc7bee008f7e8d13cbe0b5ec4d1e1eb2333a9d/starlight_help/src/content/docs/move-content-to-another-topic.mdx?plain=1)
follows this structure:
1. **What the feature does**:
> Zulip makes it possible to move messages between topics.
1. **Guidance**:
> This is useful for keeping messages organized when there is a digression,
> or the discussion shifts from the original topic.
1. **Alternatives**:
> You can also [rename a topic](https://zulip.com/help/rename-a-topic).
1. **Explain more precisely**:
> When messages are moved, Zulip's [permanent links to messages in
> context](https://zulip.com/help/link-to-a-message-or-conversation#get-a-link-to-a-specific-message)
> will automatically redirect to the new location of the message. [Muted
> topics](https://zulip.com/help/mute-a-topic) are automatically migrated when
> an entire topic is moved.
1. **Limitations**:
> Organization administrators can
> [configure](https://zulip.com/help/restrict-moving-messages) who is allowed to move
> messages.
You can see that the structure has some flexibility, but all the key points are
succinctly covered.
To write a good introduction, you may need to reread the issue that was fixed
and any user comments on the issue, and to skim any discussion around the
feature for relevant context.
### Instructions section
If the context for the feature is fully covered in the intro, a simple
subsection heading plus instructions works well. As described in detail at the
bottom of this article, be sure to use the standard help center
components/macros. Refer to other parts of the article you're working on, or
related articles (e.g., another setting in the same settings section).
Be sure to include a **Mobile** instructions tab if the feature is supported on
mobile. You can use a stub **Mobile** tab if we have an issue for it, but it's
not yet implemented.
When there are multiple ways to access a feature that are worth noting, label
tabs by the broad-level location of where the feature is accessed from (e.g.,
"Left sidebar"). We always document how to get to a setting via the settings UI,
even if there's a handy alternative. The most convenient method should be in the
first tab.
For a single set of desktop/web app instructions, use the "Desktop/Web" tab
label. We generally add this tab label for user features even if there are no
other tabs to add, but don't do so for admin features. The vast majority
of settings aren't available on mobile, so admins can easily infer that we're
referring to the desktop/web app.
In longer articles, the introduction might not cover everything. In that case,
we might add an introduction to an instructions section, with a similar
structure to article intros. Some caveats and alternatives can appear after the
instructions, but the most important content should precede them.
Be sure to document any keyboard shortcuts (generally inside the instructions
block).
### Related articles
What articles is the reader most likely to be interested in?
- Articles already linked to above are good candidates, but often not all of
them will feel sufficiently relevant. For example, you don't need to link to
articles that were linked to earlier just as a reference on some view or part
of the UI that was mentioned.
- Articles about features that support related workflows.
- For a permission setting, the article that explains the feature (and conversely).
- Articles that go deeper into the same part of the app.
- For onboarding features, articles about features you might use around the same time.
Consider the reader -- in the context of this article, are they an end user or
an admin? Admin articles will primarily link to other admin articles, and
end-user articles to other end-user articles.
## What the help center _isn't_ for
An anti-pattern is trying to make up for bad UX by adding help center
documentation. When writing documentation, try to pay attention to whether
something feels hard to explain. It's a sign that there may be a product UX
problem to fix here. Are there ways to change UI strings or workflows that would
make the feature easier to document? That could be a good product direction to
explore.
It's worth remembering that for most articles, almost 100% of
the users of the feature will never read the article. Instructions for
filling out forms, interacting with UI widgets (e.g., typeaheads), interacting
with modals, etc. should never go in the help center documentation.
In such cases, you may be able to fix the problem by adding text in-app,
where the user will see it as they are interacting with the feature.
## Adding and updating articles
Should the feature you're documenting be added or merged into an existing
article?
Real estate in the left sidebar is somewhat precious.
Minor features should rarely get their own article, and should instead
be merged into the existing help center documentation where appropriate.
If the new feature you're documenting is a refinement on,
or related to, a feature that already has a dedicated help center
article, the information will be more useful and discoverable for
users as an addition to the existing article.
Permissions settings have a different target audience (administrators) than the
feature they control, and thus often get their own article (with cross-links).
### Updating an existing article
Here are some things to keep in mind when expanding and updating
existing help center articles:
- If you're repeating content, consider moving it to an /include file in a prep
commit, for maintainability.
- While you're there, check for other updates: any new features the article
should link to? outdated writing patterns? icons we missed updating? mobile
instructions that need to be added? related articles you looked at that were
missing reference links to this one? etc.
If your updates to the existing article will change the name of the
markdown file, then see section below on [redirecting an existing
article](#redirecting-an-existing-article). We're generally OK with the URL
being a bit off from the current article title, as long as it still makes sense
given the current content.
### Adding a new article
It's often easiest to get started by choosing an existing article to use as a
template for your new article (usually a similar feature in the same sidebar
section). You can match its format, wording, style, etc. If
you decide _not_ to follow its pattern, consider whether the article you're
using as a template should itself be updated.
Consider what articles (feature articles and guides) might need to link to the
article you're adding (see [related articles tips](#related-articles)). For
major new features, take a look at `/features` and other parts of the website
(e.g, `/for/business`) for potential updates to make.
If the feature exists in other team chat products, consider checking their
documentation for inspiration (but each app has its own documentation style).
## Writing style
Aim for a clear, specific, and succinct writing style. Remember that a wide
variety of users may be reading the article, including non-native English
speakers.
Avoid technical vocabulary and jargon wherever possible. It might be fine and
unavoidable in, e.g., explanations of linkifier details. But strongly avoid it
in, e.g., an introduction to a general user article. Remember that some terms
that feel familiar to you as perhaps an engineer, and someone who
specifically develops Zulip at that, may not be in common use.
### User interface
When you refer to the features in the Zulip UI, you should **bold** the
feature's name followed by the feature itself (e.g., **Settings** page,
**Change password** button, **Email** field). No quotation marks should be
used. Use **bold** for channel names, and quotation marks for topic names.
It's important to use consistent terminology for each UI element — doing
otherwise can be quite confusing to the reader. If you aren't quite sure what to
call something, check by finding another article that you expect to mention the
same part of the UI, or an analogous one.
Keep in mind that the UI may change — don’t describe it in more detail than
is needed. Beyond requiring more frequent updates, overly specific descriptions
are hard to maintain because nobody is likely to realize that the help center
needs to be updated when purely visual changes are made. In particular:
- Do not specify what the default configuration is. This might change in the
future, or may even be different for different types of organizations.
- Do not list out the options the user is choosing from. Once the user finds the
right menu in the UI, they'll be able to see the options. In some cases, we
may describe the options in more detail outside of the instructions block.
- Never identify or refer to a button by its color. You _can_ describe its
location.
- Use screenshots only when it's very difficult to get your point across without
them.
We don't describe the UI just of the sake of describing it --- UI elements are
mentioned in the service of helping the user figure out how to take the actions
they need to take.
### Voice
Do not use `we` to refer to Zulip or its creators; for example, "Zulip also
allows ...", rather than "we also allow ...". On the other hand, `you` is ok
and used liberally.
### Keyboard shortcuts
Surround each keyboard key in the shortcut with `<kbd>` HTML element start and
end tags (e.g., `<kbd>Enter</kbd>` or `<kbd>R</kbd>`).
For shortcuts with more than one key, add a plus sign (+) surrounded by spaces
in between the keys (e.g., `<kbd>Ctrl</kbd> + <kbd>K</kbd>`). Any shortcut for
an arrow key (↑, ↓, ←, →) will also need the `"arrow-key"` CSS class included
in the `<kbd>` start tag (e.g., ` <kbd class="arrow-key">↑</kbd>`).
Use the labels one sees on the actual keyboard rather than the letter they
produce when pressed (e.g., `R` and `Shift` + `R` rather than `r` and `R`).
For symbols, such as `?` or `@`, that are produced through key combinations that
change depending on the user's keyboard layout, you should use the symbol as it
appears on a keyboard instead of any specific combination of keys.
Use non-Mac keyboard keys; for example `Enter`, instead of `Return`. Zulip will
automatically translate non-Mac keys to the Mac versions for users with a Mac
user agent. If you want to confirm that your documentation is rendering Mac keys
correctly when writing documentation in Windows or Linux, you can temporarily
change `has_mac_keyboard` in `/web/src/common.ts` to always return `True`.
Then when you view your documentation changes in the development environment,
the keyboard shortcuts should be rendered with Mac keys where appropriate.
If you're adding a tip to an article about a keyboard shortcut, you should
use the more specific [keyboard tip](#asides) component. In general, all
keyboard shortcuts should be documented on the [keyboard
shortcuts](https://zulip.com/help/keyboard-shortcuts) help center
article.
### Images
Images and screenshots should be included in help center documentation
_only_ if they will help guide the user in how to do something (e.g., if
the image will make it much clearer which element on the page the user
should interact with). For instance, an image of an element should
not be included if the element the user needs to interact with is the
only thing on the page, but images can be included to show the end
result of an interaction with the UI.
Using too many screenshots creates maintainability problems (we have
to update them every time the UI is changed) and also can make the
instructions for something simple look long and complicated.
When taking screenshots, the image should never include the whole
Zulip browser window in a screenshot; instead, it should only show
relevant parts of the app. In addition, the screenshot should always
come _after_ the text that describes it, never before.
Images used in the help center can be found at `starlight_help/src/images`.
## Viewing and updating help center articles
The help center is built with [@astro/starlight](https://starlight.astro.build/).
Starlight is a full-featured documentation theme built on top of the
[Astro](https://astro.build/) framework. Astro is a web framework designed
for content driven websites. The content for the help center articles are
[MDX](https://mdxjs.com/) files, which live at `starlight_help/src/content/docs`
in the [main Zulip server repository](https://github.com/zulip/zulip).
Images are usually linked from `starlight_help/src/images`.
Zulip help center documentation is available under `/help/` on any Zulip server;
(e.g., <https://zulip.com/help/> or `http://localhost:9991/help/` in
the Zulip development environment). The help center documentation is not hosted
on ReadTheDocs, since Zulip supports running a server completely disconnected
from the Internet, and we'd like the documentation to be available in that
environment.
This means that you can contribute to the Zulip help center documentation by
just adding to or editing the collection of MDX files under
`starlight_help/src/content/docs`. To add a help center article, create a new
file in `starlight_help/src/content/docs/`, and add a sidebar link to it in
`starlight_help/astro.config.mjs`.
If you have the Zulip development environment set up, you simply need to reload
your browser on `http://localhost:9991/help/foo` to see the latest version of
`foo.mdx` rendered.
This system is designed to make writing and maintaining such documentation
highly efficient.
## MDX features and custom Zulip components
MDX supports standard markdown syntax. Some useful markdown features to
remember when writing help center documentation are:
- Since raw HTML is supported in Markdown, you can include arbitrary
HTML/CSS in your documentation as needed.
- Code blocks allow you to highlight syntax, similar to
[Zulip's own Markdown](https://zulip.com/help/format-your-message-using-markdown).
- Anchor tags can be used to link to headers in other documents.
Additionally, there are some useful MDX components implemented and used
throughout the help center documentation:
- [Icon](#icons) components allow documentation to use the exact icons
for a button or link that is used in the Zulip UI.
- [Include files](#include-files) allow us to reuse repeated content
in the documentation.
- Our custom [Aside](#asides) components create special highlighted
information and warning blocks for tips, keyboard shortcuts, and the
like.
- Instructions can be formatted with [Tab](#tabs),
[Steps/FlattenSteps](#steps-and-flattenedsteps) and
[NavigationSteps](#navigationsteps) components.
### Icons
See [icons documentation](../subsystems/icons.md). Icons should always be
referred to with their in-app tooltip or a brief action name, _not_ the
name of the icon in the code.
When using these icons in an MDX file, they act as any other component:
```
import SquarePlusIcon from "~icons/zulip-icon/square-plus"
Click the **new topic** (<SquarePlusIcon />) button next to the name of the channel.
```
For the import statement, the icon component should be named as the camel
case of the icon name, with any dashes removed, followed by `Icon`, e.g.,
in the example above `square-plus` is imported as `SquarePlusIcon`.
### Include files
You can include any file inside another MDX file as a regular import, which
helps to eliminate repeated content in our documentation.
All our include files live at `starlight_help/src/content/docs/include`,
and can be imported and used as a regular component:
```
import AdminOnly from "./include/_AdminOnly.mdx";
<AdminOnly />
```
If you're adding a new include file, make sure to have an underscore at the
beginning of the file name as that ensures the file won't be considered a
standalone article in the help center.
A lot of our include files are list macros, i.e., they are partial lists
that are part of bigger lists. When the partial list is an ordered list,
it needs to be wrapped in a [FlattenedSteps](#steps-and-flattenedsteps)
component.
We recommend avoiding having any h2 or h3 headers (`##`, `###`) in an
include file because, when the file is imported into a help center article,
those headings will not be rendered in the "On this page" outline in the
right sidebar.
If it is necessary to have headers in the include file content, then the
workaround for having them rendered in the right sidebar is to insert the
same headers into any file where you are importing and using that include
file. In the example below, we add the two h3 headers from the include file so
that they are rendered in the right sidebar:
```
import AutomaticallyFollowTopics from "./include/_AutomaticallyFollowTopics.mdx";
<AutomaticallyFollowTopics>
### Follow topics you start or participate in
### Follow topics where you are mentioned
</AutomaticallyFollowTopics>
```
### Asides
We have customized aside components that are used for tips, warnings and
keyboard tips in the help center.
A **tip** is any suggestion for the user that is not part of the main set
of instructions. For instance, it may address a common problem users may
encounter while following the instructions, or point to an option for power
users.
```
import ZulipTip from "../../components/ZulipTip.astro";
<ZulipTip>
The app will update automatically to future versions.
</ZulipTip>
```
A **keyboard tip** is a note for users to let them know that the same action
can also be accomplished via a [keyboard shortcut](#keyboard-shortcuts).
```
import KeyboardTip from "../../components/KeyboardTip.astro";
<KeyboardTip>
You can also use <kbd>?</kbd> to open the keyboard shortcuts reference.
</KeyboardTip>
```
A **warning** is a note on what happens when there is some kind of problem.
Tips are more common than warnings.
```
import ZulipNote from "../../components/ZulipNote.astro";
<ZulipNote>
This feature is only available to organization owners and administrators.
</ZulipNote>
```
There should be only one tip/warning inside each component. They usually
should be formatted as a continuation of a numbered step, if they are in
an ordered list.
You can find the code for these custom aside components at
`starlight_help/src/components`.
### Tabs
There are built-in [starlight/astro `Tab` and `TabItem`
components](https://starlight.astro.build/components/tabs/) for creating
tabbed instructions:
```
import {Steps, TabItem, Tabs} from "@astrojs/starlight/components";
import FlattenedSteps from "../../components/FlattenedSteps.astro";
import MobileSwitchAccount from "./include/_MobileSwitchAccount.mdx";
<Tabs>
<TabItem label="Mobile">
<FlattenedSteps>
<MobileSwitchAccount />
1. Tap **Add new account**.
1. Enter the Zulip URL of the organization, and tap **Continue**.
1. Follow the on-screen instructions.
</FlattenedSteps>
</TabItem>
<TabItem label="Web">
<Steps>
1. Go to the Zulip URL of the organization.
1. Follow the on-screen instructions.
</Steps>
</TabItem>
</Tabs>
```
The above example has instructions for logging in for the mobile app
and the web app. Make sure you add a label for each `TabItem`.
#### Steps and FlattenedSteps
If you don't need multiple tabs, or a tabbed label, for your instructions,
then you can just wrap the ordered list of instructions in a [`Steps`
component](https://starlight.astro.build/components/steps/):
```
import {Steps} from "@astrojs/starlight/components";
<Steps>
1. Go to the Zulip URL of the organization.
1. Follow the on-screen instructions.
</Steps>
```
If you have an ordered list of instructions with a portion of the list in
an [include file](#include-files), then you need to wrap the instructions
in a `FlattenedSteps` component, so that the instructions are numbered
correctly when rendered (e.g., `1.`, `2.`, `3.`, `4.`, etc.):
```
import FlattenedSteps from "../../components/FlattenedSteps.astro";
import MobileSwitchAccount from "./include/_MobileSwitchAccount.mdx";
<FlattenedSteps>
<MobileSwitchAccount />
1. Tap **Add new account**.
1. Enter the Zulip URL of the organization, and tap **Continue**.
1. Follow the on-screen instructions.
</FlattenedSteps>
```
#### NavigationSteps
Many instructions begin with the same set of steps, some of which can have
relative links that directly go to the logged in session for the Zulip
organization of the user reading the documentation. For these partial
instruction lists, we use a custom `NavigationSteps` component, which needs
to be wrapped in a `FlattenedSteps` component ([see above](#steps-and-flattenedsteps)).
```
import NavigationSteps from "../../components/NavigationSteps.astro";
<FlattenedSteps>
<NavigationSteps target="settings/organization-permissions" />
1. Under **Joining the organization**, toggle **Invitations are required for
joining this organization**.
<SaveChanges />
</FlattenedSteps>
```
Each `NavigationStep` has a `target`, which can be one of two forms:
- Settings: `settings/{setting_key}`
- Relative: `relative/{link_type}/{key}`
The settings targets are for instructions that navigate to a section in
either the personal or organization settings overlay in the web app.
The relative targets are for instructions that navigate from the gear or
help menus to a particular location in the web app.
See `starlight_help/src/components/NavigationSteps.astro` for what values
for settings and relative targets are currently implemented.
You can find the code for any custom components used in the help center at:
`starlight_help/src/components`.
## Redirecting an existing article
From time to time, we might want to rename an article in the help
center. This change will break incoming links, including links in
published Zulip blog posts, links in other branches of the repository
that haven't been rebased, and more importantly links from previous
versions of Zulip.
To fix these broken links, you can easily add a URL redirect in:
`starlight_help/astro.config.mjs`.
For a help center article, once you've renamed the file in your
branch (e.g., `git mv path/to/foo.mdx path/to/bar.mdx`), go to
`astro.config.mjs` and add a new line to the `redirects` property:
```
// Redirects in astro are just directories with index.html inside
// them doing the redirect we define in the value. The base of
// /help/ will apply to the keys in the list below but we will
// have to prepend /help/ in the redirect URL.
redirects: {
"pm-mention-alert-notifications": "/help/dm-mention-alert-notifications",
...
```
Note that you will also need to add redirects when you're deleting
a help center article and adding its content to an existing article
as a section. In that case, the new URL will include the new section
header:
```
"add-an-alert-word": "/help/dm-mention-alert-notifications#alert-words"
```
You should still check for references to the old URL in your branch
and replace those with the new URL (e.g., `git grep "/help/foo"`).
One exception to this are links with the old URL that were included
in the content of `zulip_update_announcements`, which can be found
in `zerver/lib/zulip_update_announcements.py`. It's preferable to
have the source code accurately reflect what was sent to users in
those [Zulip update announcements][help-zulip-updates], so these
should not be replaced with the new URL.
Updating section headers in existing help center articles does not
require adding a URL redirect, but you will need to update any
existing links to that article's section in your branch.
You can manually test your changes in the dev environment by loading the old URL
in your browser (e.g., `http://localhost:9991/help/foo`), and confirming that it
redirects to the new URL (e.g., `http://localhost:9991/help/bar`).
[help-zulip-updates]: https://zulip.com/help/configure-automated-notices#zulip-update-announcements

View File

@@ -0,0 +1,13 @@
# Writing documentation
```{toctree}
---
maxdepth: 3
---
overview
helpcenter
integrations
api
openapi
```

View File

@@ -0,0 +1,363 @@
# Documenting an integration
In order for a [Zulip
integration](https://zulip.com/api/integrations-overview) to be useful
to users, it must be documented. Zulip's common system for documenting
integrations involves writing Markdown files, either at
`zerver/webhooks/{webhook_name}/doc.md` (for webhook integrations) or
`templates/zerver/integrations/{integration_name}.md` (for other
integrations).
The [integrations][api-integrations] in
[zulip/python-zulip-api][api-repo] have their corresponding Markdown files
at `zulip/integrations/{integration_name}/doc.md`, which are imported into
[zulip/zulip][zulip-repo] at
`static/generated/integrations/{integration_name}/doc.md` using the
`tools/setup/generate_bots_integrations_static_files.py` script.
[api-repo]: https://github.com/zulip/python-zulip-api/
[api-integrations]: https://github.com/zulip/python-zulip-api/tree/main/zulip/integrations
[zulip-repo]: https://github.com/zulip/zulip
Typically, the documentation process involves the following steps:
- Add text explaining all of the steps required to set up the
integration, including what URLs to use, etc. See
[Writing guidelines](#writing-guidelines) for detailed writing guidelines.
Zulip's pre-defined Markdown macros can be used for some of these steps.
See [Markdown macros](#markdown-macros) for further details.
- Make sure you've added your integration to `zerver/lib/integrations.py` in
the relevant `*_INTEGRATIONS` list, such as the
`INCOMING_WEBHOOK_INTEGRATIONS` list for incoming webhook integrations.
These lists get aggregated into the `INTEGRATIONS` registry which
configures your integration to appear on the `/integrations` page, and
makes it possible to automatically generate the screenshot of an example
message, which is important so that these screenshots can be easily
updated as Zulip's design changes.
- You'll need to add an SVG graphic of your integration's logo under the
`static/images/integrations/logos/<name>.svg`, where `<name>` is the
name of the integration, all in lower case; you can usually find them in the
product branding or press page. Make sure to optimize the SVG graphic by
running `tools/setup/optimize-svg`. This will also run
`tools/setup/generate_integration_bots_avatars.py` automatically to generate
a smaller version of the image you just added and optimized. This smaller
image will be used as the bot avatar in the documentation screenshot that
will be generated in the next step.
If you cannot find an SVG graphic of the logo, please find and include a PNG
image of the logo instead.
- Finally, you will need to generate a message sent by the integration, and
generate a screenshot of the message to provide an example message in the
integration's documentation.
If your new integration is not an incoming webhook and does not have fixtures, add a
message template and topic to `zerver/webhooks/fixtureless_integrations.py`.
Then, add your integration's name to `FIXTURELESS_INTEGRATIONS_WITH_SCREENSHOTS`
in `zerver/lib/integrations.py`. See [this commit][example-commit] for an
example.
Otherwise, you should have already added your integration along with its
screenshot config to `INCOMING_WEBHOOK_INTEGRATIONS`.
Generate the screenshot using `tools/screenshots/generate-integration-docs-screenshot`,
where `integrationname` is the name of the integration:
```bash
./tools/screenshots/generate-integration-docs-screenshot --integration integrationname
```
If you have trouble using this tool, you can also manually generate the
screenshot using `manage.py send_webhook_fixture_message`. When generating the
screenshot of a sample message using this method, give your test bot a nice
name like "GitHub Bot", use the project's logo as the bot's avatar, and take
the screenshot showing the channel/topic bar for the message, not just the
message body.
[example-commit]: https://github.com/zulip/zulip/commit/f6e847d026013097ecdc484ec6675680ce76aa84
## Markdown macros
**Macros** are elements in the format of `{!macro.md!}` that insert common
phrases and steps at the location of the macros. Macros help eliminate
repeated content in our documentation.
The source for macros is the Markdown files under
`templates/zerver/integrations/include/` in the
[main Zulip server repository](https://github.com/zulip/zulip). If you find
multiple instances of particular content in the documentation, you can
always create a new macro by adding a new file to that folder.
Here are a few common macros used to document Zulip's integrations:
- `{!create-channel.md!}` macro - Recommends that users create a dedicated
channel for a given integration. Usually the first step is setting up an
integration or incoming webhook. For an example rendering, see **Step 1** of
[the docs for Zulip's Zendesk integration][zendesk].
- `{!create-an-incoming-webhook.md!}` macro - Instructs users to create a bot
for a given integration and select **Incoming webhook** as the **Bot type**.
This macro is usually used right after `{!create-channel.md!}`. For an example
rendering, see **Step 2** of [the docs for Zulip's Zendesk integration][zendesk].
- `{!create-a-generic-bot.md!}` macro - Instructs users to create a bot
for a given integration and select **Generic bot** as the **Bot type**. For an
example rendering, see [the docs for Zulip's Matrix integration][matrix].
- `{!generate-webhook-url-basic.md!}` - Instructs user how to get the URL for a
bot for a given integration. Note that this macro should not be used with the
`{!create-channel.md!}` macro. For an example rendering, see **Step 2** of
[the docs for Zulip's GitHub integration][github-integration].
- `{!generate-integration-url.md!}` - Instructs user how to get the URL for a
bot for a given integration. An example URL is generated automatically for
every incoming webhook by using attributes in the `IncomingWebhookIntegration` class
in [zerver/lib/integrations.py][integrations-file].
**Note:** If special configuration is required to set up the URL and you can't
use these macros, be sure to use the `{{ api_url }}` template variable, so
that your integration documentation will provide the correct URL for whatever
server it is deployed on.
- `{!congrats.md!}` macro - Inserts congratulatory lines signifying the
successful setup of a given integration. This macro is usually used at
the end of the documentation, right before the sample message screenshot.
For an example rendering, see the end of
[the docs for Zulip's GitHub integration][github-integration].
- `{!event-filtering-additional-feature.md!}` macro - If a webhook integration
supports event filtering, then this adds a section with the specific
events that can be filtered for the integration. Should be included in
the documentation if `all_event_types` is set in the webhook integration
view. For an example see, the **Filtering incoming events** section in
[Zulip's GitLab integration][gitlab].
- `{!download-python-bindings.md!}` macro - Links to Zulip's
[API page](https://zulip.com/api/) to download and install Zulip's
API bindings. This macro is usually used in non-webhook integration docs under
`templates/zerver/integrations/<integration_name>.md`. For an example
rendering, see **Step 3** of
[the docs for Zulip's Codebase integration][codebase].
- `{!change-zulip-config-file.md!}` macro - Instructs users to create a bot and
specify said bot's credentials in the config file for a given non-webhook
integration. This macro is usually used in non-webhook integration docs under
`templates/zerver/integrations/<integration_name>.md`. For an example
rendering, see **Step 4** of
[the docs for Zulip's Codebase integration][codebase].
- `{!webhook-url-with-bot-email.md!}` - Used in certain non-webhook integrations
to generate URLs of the form:
```text
https://bot_email:bot_api_key@your-org.zulipchat.com/api/v1/external/beanstalk
```
For an example rendering, see
[Zulip's Beanstalk integration](https://zulip.com/integrations/beanstalk).
[github-integration]: https://zulip.com/integrations/github
[zendesk]: https://zulip.com/integrations/zendesk
[matrix]: https://zulip.com/integrations/matrix#configure-the-bridge
[codebase]: https://zulip.com/integrations/codebase
[beanstalk]: https://zulip.com/integrations/beanstalk
[front]: https://zulip.com/integrations/front
[gitlab]: https://zulip.com/integrations/gitlab
[integrations-file]: https://github.com/zulip/zulip/blob/main/zerver/lib/integrations.py
## Writing guidelines
For the vast majority of integrations, you should just copy the docs for a
similar integration and edit it. [Basecamp][basecamp] is a good one to copy.
[basecamp]: https://zulip.com/integrations/basecamp
### General writing guidelines
At a high level, the goals are for the instructions to feel simple, be easy to
follow, and be easy to maintain. Easier said than done, but here are a few
concrete guidelines.
##### Feel simple
- Use simple language.
- Use the imperative tense.
- Only describe things as much as necessary. For example: "Select **Project settings**."
is better than "Select **Project settings** from the dropdown."
- Cut unnecessary words. For example, do not start steps with transition words
like "Next", "First", "Now", etc. Each step should be structurally
independent.
##### Be easy to follow
- Actions should appear in order, including implicit ones. So "Under
**Webhooks**, click **Add Webhook**." not "Click **Add Webhook** in the
**Webhooks** section." "Under **Webhooks**" is an action, since it’s basically
the same as "Find the Webhooks section".
- UI elements in third-party websites should be **bolded**.
- Trailing punctuation can be stripped from bolded elements. For example,
"**Enable this checkbox**" instead of "**Enable this checkbox?**".
Starting punctuation such as the "**+**" in "**+ New Notification**" should
be preserved.
- You can use a screenshot if a step is particularly complicated
(see [Screenshots](#screenshots) below).
##### Be easy to maintain
- Follow the organization and wording of existing docs as much as possible.
### Guidelines for specific steps
Most doc files should start with a generic sentence about the
integration, for example, "Get `webhook name` notifications in Zulip!"
A typical doc will then have the following steps.
##### "Create the channel" step
- Use the `create-channel` macro. This step should be omitted if the
integration only supports notifications via direct messages.
##### "Create the bot" step
- Typically, use the `create-an-incoming-webhook` and
`generate-integration-url` macros.
- [Existing macros](#markdown-macros) should be used for this if they exist,
but if the macro defaults don’t work, it may make sense to write something
custom for the integration in question. This step is mandatory for all
integrations.
##### "Navigate to this screen" step
- In general, this should be one step, even if it takes multiple clicks.
- Begin by mentioning the third-party service being documented with language
such as "Go to your Taiga project", "Go to your GitHub repository",
"On your X project", etc. Assume the user is already logged in to their
account.
- If a UI element is difficult to spot, you can use additional cues like
"Click on **Settings** in the top-right corner" or "On the left, click
**Webhooks**.". Only do this if the element is actually difficult to spot.
- If this step includes more than 5 sentences, it may need to be split up
into multiple steps.
##### "Fill out form and save" step
- Filling out a form and clicking **Save** should generally be one step, even if
there are multiple fields to fill out.
- It’s fine to say things like "Follow the on-screen instructions to create an
app" if they have a sequence of steps they guide you through that are pretty
clear.
Lastly, end with the `congrats.md` macro and a screenshot of a sample message
within Zulip.
### Screenshots
Screenshots are hard to maintain, so we generally err on the side of not
including screenshots. That being said, screenshots may be used to aid the
process if the third-party UI is confusing or a specific UI element is hard
to find. A few things to keep in mind:
- Screenshots should be small and should mainly include the third-party UI that
the text is referring to that is difficult to locate.
- Screenshots should never include the entirety of a third-party website’s page.
- Each screenshot should come after the step that refers to it.
## Markdown features
Zulip's Markdown processor allows you to include several special features in
your documentation to help improve its readability:
- Since raw HTML is supported in Markdown, you can include arbitrary
HTML/CSS in your documentation as needed.
- Code blocks allow you to highlight syntax, similar to
[Zulip's own Markdown](https://zulip.com/help/format-your-message-using-markdown).
- Anchor tags can be used to link to headers in other documents.
- Inline [icons](#icons) are used to refer to features in the Zulip UI.
- Utilize [macros](#markdown-macros) to limit repeated content in the documentation.
- Create special highlight warning blocks using
[tips and warnings](#tips-and-warnings).
- Format instructions with tabs using
[Markdown tab switcher](#tab-switcher).
### Icons
See [icons documentation](../subsystems/icons.md). Icons should always be
referred to with their in-app tooltip or a brief action name, _not_ the
name of the icon in the code.
### Tips and warnings
A **tip** is any suggestion for the user that is not part of the main set of
instructions. For instance, it may address a common problem users may
encounter while following the instructions, or point to an option for power
users.
```md
!!! tip ""
If you want notifications for issues, as well as events, you can
scroll down to **Webhooks** on the same page, and toggle the
**issue** checkbox.
```
A **keyboard tip** is a note for users to let them know that the same action
can also be accomplished via a [keyboard shortcut](https://zulip.com/help/keyboard-shortcuts).
```md
!!! keyboard_tip ""
Use <kbd>D</kbd> to bring up your list of drafts.
```
A **warning** is a note on what happens when there is some kind of problem.
Tips are more common than warnings.
```md
!!! warn ""
**Note**: Zulip also supports configuring this integration as a
webhook in Sentry.
```
All tips/warnings should appear inside tip/warning blocks. There
should be only one tip/warning inside each block, and they usually
should be formatted as a continuation of a numbered step.
### Tab switcher
Our Markdown processor supports easily creating a tab switcher widget
design to easily show the instructions for different languages in API
docs, etc. To create a tab switcher, write:
```md
{start_tabs}
{tab|python}
# First tab's content
{tab|js}
# Second tab's content
{tab|curl}
# Third tab's content
{end_tabs}
```
The tab identifiers (e.g., `python` above) and their mappings to
the tabs' labels are declared in
[zerver/lib/markdown/tabbed_sections.py][tabbed-sections-code].
This widget can also be used just to create a nice box around a set of
instructions
([example](https://zulip.com/help/deactivate-your-account)) by
only declaring a single tab, which is often used for the main set of
instructions for setting up an integration.
[tabbed-sections-code]: https://github.com/zulip/zulip/blob/main/zerver/lib/markdown/tabbed_sections.py

View File

@@ -0,0 +1,195 @@
# OpenAPI configuration
[OpenAPI][openapi-spec] is a popular format for describing an API. An
OpenAPI file can be used by various tools to generate documentation
for the API or even basic client-side bindings for dozens of
programming languages.
Zulip's API is described in `zerver/openapi/zulip.yaml`. Our aim is
for that file to fully describe every endpoint in the Zulip API, and
for the Zulip test suite to fail should the API every change without a
corresponding adjustment to the documentation. In particular,
essentially all content in Zulip's [REST API
documentation](api.md) is generated from our OpenAPI
file.
In an OpenAPI Swagger file, every configuration section is an object.
Objects may contain other objects, or reference objects defined
elsewhere. Larger API specifications may be split into multiple
files. See the [OpenAPI specification][openapi-spec].
[openapi-spec]: https://swagger.io/docs/specification/about/
This library isn't in production use yet, but it is our current plan
for how Zulip's API documentation will work.
## Working with the `zulip.yaml` file
An OpenAPI specification file has three general parts: information and
configuration, endpoint definitions, and object schemas referenced by
other objects (as an alternative to defining everything inline.)
References can either specify an individual object, using `$ref:`, or
compose a larger definition from individual objects with `allOf:`
(which may itself contain a `$ref`.)
### Configuration
These objects, at the top of `zulip.yaml`, identify the API, define
the backend host for the working examples, list supported schemes and
types of authentication, and configure other settings. Once defined,
information in this section rarely changes.
For example, the `swagger` and `info` objects look like this:
```yaml
# Basic Swagger UI info
openapi: 3.0.1
info:
version: 1.0.0
title: Zulip REST API
description: Powerful open source group chat.
contact:
url: https://zulip.com
license:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
```
### Endpoint definitions
The [Paths Object](https://swagger.io/specification/#pathsObject)
contains
[Path Item Objects](https://swagger.io/specification/#pathItemObject)
for each endpoint. It describes in detail the methods and parameters
the endpoint accepts and responses it returns.
There is one Path Item Object for each supported method, containing a
[Parameters Definition Object](https://swagger.io/specification/#parametersDefinitionObject)
describing the required and optional inputs. A
[Response Object](https://swagger.io/specification/#responseObject)
similarly specifies the content of the response. They may reference
schemas from a global Definitions Object (see [Schemas](#schemas),
below.)
Example:
The `/users/{user}/presence` endpoint (defined in a
[Path Item Object](https://swagger.io/specification/#pathItemObject))
expects a GET request with one
[parameter](https://swagger.io/specification/#parameterObject), HTTP
Basic authentication, and returns a JSON response containing `msg`,
`result`, and `presence` values.
```yaml
/users/{user}/presence:
get:
description: Get presence data for another user.
operationId: getPresence
parameters:
- name: user
in: path
description: Enter email address
required: true
type: string
security:
- basicAuth: []
responses:
'200':
description: The response from a successful call
schema:
type: object
required:
- msg
- result
- presence
properties:
msg:
type: string
result:
type: string
presence:
type: array
```
### Schemas
The
[Definitions Object](https://swagger.io/specification/#definitionsObject)
contains schemas referenced by other objects. For example,
`MessageResponse`, the response from the `/messages` endpoint,
contains three required parameters. Two are strings, and one is an
integer.
```yaml
MessageResponse:
type: object
required:
- msg
- result
- id
properties:
msg:
type: string
result:
type: string
id:
type: integer
format: int64
```
You can find more examples, including GET requests and nested objects, in
`zerver/openapi/zulip.yaml`.
## Zulip Swagger YAML style:
We're collecting decisions we've made on how our Swagger YAML files
should be organized here:
- Use shared definitions and YAML anchors to avoid duplicating content
where possible.
## Tips for working with YAML:
You can edit YAML files in any text editor. Indentation defines
blocks, so whitespace is important (as it is in Python.) TAB
characters are not permitted. If your editor has an option to replace
tabs with spaces, this is helpful.
You can also use the
[Swagger Editor](https://swagger.io/swagger-editor), which validates
YAML and understands the Swagger specification. Download and run it
locally, or use the online version. If you aren't using a YAML-aware
editor, make small changes and check your additions often.
Note: if you are working with
[Swagger UI](https://swagger.io/swagger-ui/) in a local development
environment, it uses an online validator that must be able to access
your file. You may see a red "ERROR" button at the bottom of your API
docs page instead of the green "VALID" one even if your file is
correct.
### Formatting help:
- Comments begin with a # character.
- Descriptions do not need to be in quotes, and may use common
Markdown format options like inline code \` (backtick) and `#`
headings.
- A single `|` (pipe) character begins a multi-line description on the
next line. Single spaced lines (one newline at the end of each) are
joined. Use an extra blank line for a paragraph break. We prefer
to use this format for all descriptions because it doesn't require
extra effort to expand.
### Examples:
```yaml
Description: |
This description has multiple lines.
Sometimes descriptions can go on for
several sentences.
A description might have multiple paragraphs
as well.
```

View File

@@ -0,0 +1,166 @@
# Documentation systems
Zulip has three major documentation systems:
- **Developer and sysadmin documentation**: Documentation for people
actually interacting with the Zulip codebase (either by developing
it or installing it), and written in Markdown.
- **Core website documentation**: Complete webpages for complex topics,
written in HTML, JavaScript, and CSS (using the Django templating
system). These roughly correspond to the documentation someone
might look at when deciding whether to use Zulip. We don't expect
to ever have more than about 10 pages written using this system.
- **User-facing documentation**: Zulip uses a scalable system for
documenting Zulip's integrations and REST API, without a lot of
overhead or duplicated code/syntax, written in Markdown. Zulip's
help center uses [@astro/starlight](https://starlight.astro.build/),
with most of the content written in MDX.
- [Help center documentation](#help-center-documentation)
(with a target audience of individual Zulip users)
- [Integrations documentation](#integrations-documentation)
(with a target audience of IT folks setting up integrations)
- [API documentation](#api-documentation) (with a target audience
of developers writing code to extend Zulip)
These three systems are documented in detail.
## Developer and sysadmin documentation
What you are reading right now is part of the collection of
documentation targeted at developers and people running their own
Zulip servers. These docs are written in
[CommonMark Markdown](https://commonmark.org/).
We've chosen Markdown because it is
[easy to write](https://commonmark.org/help/). The source for Zulip's
developer documentation is at `docs/` in the Zulip Git repository, and
they are served in production at
[zulip.readthedocs.io](https://zulip.readthedocs.io/en/latest/).
This documentation is hosted by the excellent [ReadTheDocs
service](https://readthedocs.org/). ReadTheDocs automatically [builds
a preview](https://docs.readthedocs.io/en/stable/pull-requests.html)
for every pull request, accessible from a "Details" link in the
"Checks" section of the pull request page. It's nonetheless valuable
to submit a screenshot with any pull request modifying documentation
to help make reviews efficient.
If you want to build the developer documentation locally (e.g., to test
your changes), the dependencies are automatically installed as part of
Zulip development environment provisioning, and you can build the
documentation using:
```bash
./tools/build-docs
```
and then opening `http://127.0.0.1:9991/docs/index.html` in your
browser. The raw files are available at
`file:///path/to/zulip/docs/_build/html/index.html` in your browser
(so you can also use, for example, `firefox docs/_build/html/index.html`
from the root of your Zulip checkout).
If you are adding a new page to the table of contents, you will want
to modify `docs/index.md` and run `make clean` before `make html`, so
that other docs besides your new one also get the new entry in the
table of contents.
You can also usually test your changes by pushing a branch to GitHub
and looking at the content on the GitHub web UI, since GitHub renders
Markdown, though that won't be as faithful as the `make html`
approach or the preview build.
We manage Python requirements for the documentation build in the `docs` uv
[group](https://docs.astral.sh/uv/concepts/projects/dependencies/#dependency-groups),
which is used by our ReadTheDocs build configuration in
[`.readthedocs.yaml`](https://docs.readthedocs.com/platform/stable/config-file/v2.html).
## Core website documentation
Zulip has around 10 HTML documentation pages under `templates/zerver`
for specific major topics, like the features list, client apps,
integrations, hotkeys, API bindings, etc. These documents often have
somewhat complex HTML and JavaScript, without a great deal of common
patterns between them other than inheriting from the `portico.html`
template. We generally avoid adding new pages to this collection
unless there's a good reason, but we don't intend to migrate them,
either, since this system gives us the flexibility to express these
important elements of the product clearly.
## User-facing documentation
Zulip's API and integrations documentation use a common Markdown-based
framework with various extensions for macros and variable interpolation,
(`render_markdown_path` in the code), designed to make it convenient
to do the things one does a lot in each type of documentation.
Zulip's help center is built with [@astro/starlight](https://starlight.astro.build/).
Starlight is a full-featured documentation theme built on top of the
[Astro](https://astro.build/) framework. Astro is a web framework designed
for content driven websites.
### Help center documentation
Zulip's [help center](https://zulip.com/help/) documentation is
designed to explain how the product works to end users. We aim for
this to be clear, concise, correct, and readable to nontechnical
audiences where possible.
See our guide on [writing help center articles](helpcenter.md).
### Integrations documentation
Zulip's [integrations documentation](https://zulip.com/integrations/)
is user-facing documentation explaining to end users how to set up each
of Zulip's more than 100 integrations. There is a detailed [guide on
documenting integrations](integrations.md), including style guidelines
to ensure that the documentation is high quality and consistent.
See also our broader [integrations developer
guide](https://zulip.com/api/integrations-overview).
### API documentation
Zulip's [API documentation](https://zulip.com/api/) is intended to make
it easy for a technical user to write automation tools that interact
with Zulip. This documentation also serves as our main mechanism for
Zulip server developers to communicate with client developers about
how the Zulip API works.
See the [API documentation tutorial](api.md) for
details on how to contribute to this documentation.
## Automated testing
Zulip has several automated test suites that we run in CI and
recommend running locally when making significant edits:
- `tools/lint` catches a number of common mistakes, and we highly
recommend
[using our linter pre-commit hook](../git/zulip-tools.md#set-up-git-repo-script).
See the [main linter doc](../testing/linters.md) for more details.
- The ReadTheDocs docs are built and the links tested by
`tools/test-documentation`, which runs `build-docs` and then checks
all the links.
There's an exclude list for the link testing at this horrible path:
`tools/documentation_crawler/documentation_crawler/spiders/common/spiders.py`,
which is relevant for flaky links.
- The API docs are tested by `tools/test-api`, which does some basic
payload verification. Note that this test does not check for broken
links (those are checked by `test-help-documentation`).
- `tools/test-help-documentation` checks `/help/`, `/api/`,
`/integrations/`, and the core website ("portico") documentation for
broken links. Note that the "portico" documentation check has a
manually maintained whitelist of pages, so if you add a new page to
this site, you will need to edit `PorticoDocumentationSpider` to add it.
- `tools/test-backend test_docs.py` tests various internal details of
the variable substitution logic, as well as rendering. It's
essential when editing the documentation framework, but not
something you'll usually need to interact with when editing
documentation.

118
docs/git/cheat-sheet.md Normal file
View File

@@ -0,0 +1,118 @@
# Git cheat sheet
See also [fixing commits][fix-commit]
## Common commands
- add
- `git add foo.py`
- checkout
- `git checkout -b new-branch-name`
- `git checkout main`
- `git checkout old-branch-name`
- commit
- `git commit -m "topic: Commit message title."`
- `git commit --amend`: Modify the previous commit.
- config
- `git config --global core.editor nano`
- `git config --global core.symlinks true`
- diff
- `git diff`
- `git diff --cached`
- `git diff HEAD~2..`
- fetch
- `git fetch origin`
- `git fetch upstream`
- grep
- `git grep update_unread_counts`
- log
- `git log`
- pull
- `git pull --rebase`: **Use this**. Zulip uses a [rebase oriented workflow][git-overview].
- `git pull` (with no options): Will either create a merge commit
(which you don't want) or do the same thing as `git pull --rebase`,
depending on [whether you've configured Git properly][git-config-clone]
- push
- `git push origin +branch-name`
- rebase
- `git rebase -i HEAD~3`
- `git rebase -i main`
- `git rebase upstream/main`
- reflog
- `git reflog | head -10`
- remote
- `git remote -v`
- reset
- `git reset HEAD~2`
- rm
- `git rm oops.txt`
- show
- `git show HEAD`
- `git show HEAD~~~`
- `git show main`
- status
- `git status`
## Detailed cheat sheet
- add
- `git add foo.py`: add `foo.py` to the staging area
- `git add foo.py bar.py`: add `foo.py` AND `bar.py` to the staging area
- `git add -u`: Adds all tracked files to the staging area.
- checkout
- `git checkout -b new-branch-name`: create branch `new-branch-name` and switch to/check out that new branch
- `git checkout main`: switch to your `main` branch
- `git checkout old-branch-name`: switch to an existing branch `old-branch-name`
- commit
- `git commit -m "commit message"`: It is recommended to type a
multiline commit message, however.
- `git commit`: Opens your default text editor to write a commit message.
- `git commit --amend`: changing the last commit message. Read more [here][fix-commit]
- config
- `git config --global core.editor nano`: set core editor to `nano` (you can set this to `vim` or others)
- `git config --global core.symlinks true`: allow symbolic links
- diff
- `git diff`: display the changes you have made to all files
- `git diff --cached`: display the changes you have made to staged files
- `git diff HEAD~2..`: display the 2 most recent changes you have made to files
- fetch
- `git fetch origin`: fetch origin repository
- `git fetch upstream`: fetch upstream repository
- grep
- `git grep update_unread_counts web/src`: Search our JS for references to update_unread_counts.
- log
- `git log`: show commit logs
- `git log --oneline | head`: To quickly see the latest ten commits on a branch.
- `git log -p`: show commit logs with changes; see [the "secret" to reading it's output][git-log-secret]
- pull
- `git pull --rebase`: rebase your changes on top of `main`.
- `git pull` (with no options): Will either create a merge commit
(which you don't want) or do the same thing as `git pull --rebase`,
depending on [whether you've configured Git properly][git-config-clone]
- push
- `git push origin branch-name`: push you commits to the origin repository _only if_ there are no conflicts.
Use this when collaborating with others to prevent overwriting their work.
- `git push origin +branch-name`: force push your commits to your origin repository.
- rebase
- `git rebase -i HEAD~3`: interactive rebasing current branch with first three items on HEAD
- `git rebase -i main`: interactive rebasing current branch with `main` branch
- `git rebase upstream/main`: rebasing current branch with `main` branch from upstream repository
- reflog
- `git reflog | head -10`: manage reference logs for the past 10 commits
- remote
- `git remote -v`: display your origin and upstream repositories
- reset
- `git reset HEAD~2`: reset two most recent commits
- rm
- `git rm oops.txt`: remove `oops.txt`
- show
- `git show HEAD`: display most recent commit
- `git show HEAD~~~`: display third most recent commit
- `git show main`: display most recent commit on `main`
- status
- `git status`: show the working tree status, unstaged and staged files
[fix-commit]: fixing-commits.md
[git-log-secret]: reading-history.md#the-secret-to-using-git-log--p
[git-config-clone]: cloning.md#step-1b-clone-to-your-machine
[git-overview]: overview.md

140
docs/git/cloning.md Normal file
View File

@@ -0,0 +1,140 @@
# Get Zulip code
Zulip uses a **forked-repo** and **[rebase][gitbook-rebase]-oriented
workflow**. This means that all contributors create a fork of the [Zulip
repository][github-zulip] they want to contribute to and then submit pull
requests to the upstream repository to have their contributions reviewed and
accepted. We also recommend you work on feature branches.
## Step 1a: Create your fork
The following steps you'll only need to do the first time you set up a machine
for contributing to a given Zulip project. You'll need to repeat the steps for
any additional Zulip projects ([list][github-zulip]) that you work on.
The first thing you'll want to do to contribute to Zulip is fork ([see
how][github-help-fork]) the appropriate [Zulip repository][github-zulip]. For
the main server app, this is [zulip/zulip][github-zulip-zulip].
## Step 1b: Clone to your machine
Next, clone your fork to your local machine:
```console
$ git clone --config pull.rebase https://github.com/YOUR_USERNAME/zulip.git
Cloning into 'zulip'
remote: Counting objects: 86768, done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 86768 (delta 5), reused 1 (delta 1), pack-reused 86752
Receiving objects: 100% (86768/86768), 112.96 MiB | 523.00 KiB/s, done.
Resolving deltas: 100% (61106/61106), done.
Checking connectivity... done.
```
(The `--config pull.rebase` option configures Git so that `git pull`
will behave like `git pull --rebase` by default. Using
`git pull --rebase` to update your changes to resolve merge conflicts
is expected by essentially all of open source projects, including
Zulip. You can also set that option after cloning using
`git config --add pull.rebase true`, or just be careful to always run
`git pull --rebase`, never `git pull`).
Note: If you receive an error while cloning, you may not have [added your ssh
key to GitHub][github-help-add-ssh-key].
Once the repository is cloned, we recommend running
[setup-git-repo][zulip-rtd-tools-setup] to install Zulip's pre-commit
hook which runs the Zulip linters on the changed files when you
commit.
## Step 1c: Connect your fork to Zulip upstream
Next you'll want to [configure an upstream remote
repository][github-help-conf-remote] for your fork of Zulip. This will allow
you to [sync changes][github-help-sync-fork] from the main project back into
your fork.
First, show the currently configured remote repository:
```console
$ git remote -v
origin git@github.com:YOUR_USERNAME/zulip.git (fetch)
origin git@github.com:YOUR_USERNAME/zulip.git (push)
```
Note: If you've cloned the repository using a graphical client, you may already
have the upstream remote repository configured. For example, when you clone
[zulip/zulip][github-zulip-zulip] with the GitHub desktop client it configures
the remote repository `zulip` and you see the following output from
`git remote -v`:
```console
origin git@github.com:YOUR_USERNAME/zulip.git (fetch)
origin git@github.com:YOUR_USERNAME/zulip.git (push)
zulip https://github.com/zulip/zulip.git (fetch)
zulip https://github.com/zulip/zulip.git (push)
```
If your client hasn't automatically configured a remote for zulip/zulip, you'll
need to with:
```console
$ git remote add -f upstream https://github.com/zulip/zulip.git
```
Finally, confirm that the new remote repository, upstream, has been configured:
```console
$ git remote -v
origin git@github.com:YOUR_USERNAME/zulip.git (fetch)
origin git@github.com:YOUR_USERNAME/zulip.git (push)
upstream https://github.com/zulip/zulip.git (fetch)
upstream https://github.com/zulip/zulip.git (push)
```
## Step 2: Set up the Zulip development environment
If you haven't already, now is a good time to install the Zulip development environment
([overview][zulip-rtd-dev-overview]). If you're new to working on Zulip or open
source projects in general, we recommend following our [detailed guide for
first-time contributors][zulip-rtd-dev-first-time].
## Step 3: Configure continuous integration for your fork
This step is optional, but recommended.
The Zulip Server project is configured to use [GitHub Actions][github-actions]
to test and create builds upon each new commit and pull request.
GitHub Actions is the primary CI that runs frontend and backend
tests across a wide range of Ubuntu distributions.
GitHub Actions is free for open source projects and it's easy to
configure for your own fork of Zulip. After doing so, GitHub Actions
will run tests for new refs you push to GitHub and email you the outcome
(you can also view the results in the web interface).
Running CI against your fork can help save both your and the
Zulip maintainers time by making it easy to test a change fully before
submitting a pull request. We generally recommend a workflow where as
you make changes, you use a fast edit-refresh cycle running individual
tests locally until your changes work. But then once you've gotten
the tests you'd expect to be relevant to your changes working, push a
branch to run the full test suite in GitHub Actions before
you create a pull request. While you wait for GitHub Actions jobs
to run, you can start working on your next task. When the tests finish,
you can create a pull request that you already know passes the tests.
GitHub Actions will run all the jobs by default on your forked repository.
You can check the `Actions` tab of your repository to see the builds.
[gitbook-rebase]: https://git-scm.com/book/en/v2/Git-Branching-Rebasing
[github-help-add-ssh-key]: https://help.github.com/en/articles/adding-a-new-ssh-key-to-your-github-account
[github-help-conf-remote]: https://help.github.com/en/articles/configuring-a-remote-for-a-fork
[github-help-fork]: https://help.github.com/en/articles/fork-a-repo
[github-help-sync-fork]: https://help.github.com/en/articles/syncing-a-fork
[github-zulip]: https://github.com/zulip/
[github-zulip-zulip]: https://github.com/zulip/zulip/
[github-actions]: https://docs.github.com/en/actions
[zulip-rtd-dev-first-time]: ../development/setup-recommended.md
[zulip-rtd-dev-overview]: ../development/overview.md
[zulip-rtd-tools-setup]: zulip-tools.md#set-up-git-repo-script

59
docs/git/collaborate.md Normal file
View File

@@ -0,0 +1,59 @@
# Collaborate
## Fetch another contributor's branch
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
their fork as a remote and pull their changes.
```console
$ git remote add <username> https://github.com/<username>/zulip.git
$ git fetch <username>
```
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
help you keep things organized.
```console
$ git checkout -b <username>/<branchname>
```
You can choose to rename the branch if you prefer:
```bash
git checkout -b <custombranchname> <username>/<branchname>
```
## Check out a pull request locally
Just as you can check out any user's branch locally, you can also check out any
pull request locally. GitHub provides a special syntax
([details][github-help-co-pr-locally]) for this since pull requests are
specific to GitHub rather than Git.
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:
```console
$ git fetch upstream pull/ID/head:BRANCHNAME
```
Now switch to the branch:
```console
$ git checkout BRANCHNAME
```
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
requests. You can read more about what they do [here][tools-pr].
```bash
tools/fetch-rebase-pull-request <PR-number>
tools/fetch-pull-request <PR-number>
```
[github-help-co-pr-locally]: https://help.github.com/en/articles/checking-out-pull-requests-locally
[tools-pr]: zulip-tools.md#fetch-a-pull-request-and-rebase

View File

@@ -0,0 +1,45 @@
# Fixing commits
This is mostly from
[here](https://help.github.com/en/articles/changing-a-commit-message#rewriting-the-most-recent-commit-message).
## Fixing the last commit
### Changing the last commit message
1. `git commit --amend -m "New message"`
### Changing the last commit
1. Make your changes to the files
2. Run `git add <filename>` to add one file or `git add <filename1> <filename2> ...` to add multiple files
3. `git commit --amend`
## Fixing older commits
### Changing commit messages
1. `git rebase -i HEAD~5` (if, for example, you are editing some of the last five commits)
2. For each commit that you want to change the message, change `pick` to `reword`, and save
3. Change the commit messages
### Deleting old commits
1. `git rebase -i HEAD~n` where `n` is the number of commits you are looking at
2. For each commit that you want to delete, change `pick` to `drop`, and save
## Squashing commits
Sometimes, you want to make one commit out of a bunch of commits. To do this,
1. `git rebase -i HEAD~n` where `n` is the number of commits you are interested in
2. Change `pick` to `squash` on the lines containing the commits you want to squash and save
## Reordering commits
1. `git rebase -i HEAD~n` where `n` is the number of commits you are interested in
2. Reorder the lines containing the commits and save
## Pushing commits after tidying them
1. `git push origin +my-feature-branch` (Note the `+` there and substitute your actual branch name.)

23
docs/git/index.md Normal file
View File

@@ -0,0 +1,23 @@
# Git guide
```{toctree}
---
maxdepth: 3
---
Quick start <overview>
Set up Git <setup>
Zulip-specific tools <zulip-tools>
How Git is different <the-git-difference>
Important Git terms <terminology>
Get Zulip code <cloning>
Working copies <working-copies>
Using Git as you work <using>
Pull requests <pull-requests>
Collaborate <collaborate>
Fixing commits <fixing-commits>
Reviewing changes <reviewing>
Reading history <reading-history>
Get and stay out of trouble <troubleshooting>
Git cheat sheet <cheat-sheet>
```

73
docs/git/overview.md Normal file
View File

@@ -0,0 +1,73 @@
# Quick start: how Zulip uses Git and GitHub
This quick start provides a brief overview of how Zulip uses Git and GitHub.
Those who are familiar with Git and GitHub should be able to start contributing
with these details in mind:
- We use **GitHub for source control and code review.** To contribute, fork
[zulip/zulip][github-zulip-zulip] (or the appropriate
[repository][github-zulip], if you are working on something else besides
Zulip server) to your own account and then create feature/issue branches.
When you're ready to get feedback, submit a [draft][github-help-draft-pr]
pull request. _We encourage you to submit draft pull requests early and
often._
- We use a **[rebase][gitbook-rebase]-oriented workflow.** We do not use merge
commits. This means you should use `git fetch` followed by `git rebase`
rather than `git pull` (or you can use `git pull --rebase`). Also, to prevent
pull requests from becoming out of date with the main line of development,
you should rebase your feature branch prior to submitting a pull request, and
as needed thereafter. If you're unfamiliar with how to rebase a pull request,
[read this excellent guide][edx-howto-rebase-pr].
We use this strategy in order to avoid the extra commits that appear
when another branch is merged, that clutter the commit history (it's
popular with other large projects such as Django). This makes
Zulip's commit history more readable, but a side effect is that many
pull requests we merge will be reported by GitHub's UI as _closed_
instead of _merged_, since GitHub has poor support for
rebase-oriented workflows.
- We have a **[code style guide][zulip-rtd-code-style]**, a **[commit message
guide][zulip-rtd-commit-messages]**, and strive for each commit to be _a
minimal coherent idea_ (see **[commit
discipline][zulip-rtd-commit-discipline]** for details).
- We provide **many tools to help you submit quality code.** These include
[linters][zulip-rtd-lint-tools], [tests][zulip-rtd-testing], [continuous
integration][continuous-integration] and [mypy][zulip-rtd-mypy].
- We use [zulipbot][zulip-rtd-zulipbot-usage] to manage our issues and
pull requests to create a better GitHub workflow for contributors.
- We provide some handy **[Zulip-specific Git scripts][zulip-rtd-zulip-tools]**
for developers to easily do tasks like fetching and rebasing a pull
request, cleaning unimportant branches, etc. These reduce the common
tasks of testing other contributors' pull requests to single commands.
Finally, install the [Zulip developer environment][zulip-rtd-dev-overview], and then
[configure continuous integration for your fork][zulip-git-guide-fork-ci].
---
The following sections will help you be awesome with Zulip and Git/GitHub in a
rebased-based workflow. Read through it if you're new to Git, to a rebase-based
Git workflow, or if you'd like a Git refresher.
[github-help-draft-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests
[gitbook-rebase]: https://git-scm.com/book/en/v2/Git-Branching-Rebasing
[edx-howto-rebase-pr]: https://web.archive.org/web/20240204225038/https://github.com/openedx/edx-platform/wiki/How-to-Rebase-a-Pull-Request
[github-zulip]: https://github.com/zulip/
[github-zulip-zulip]: https://github.com/zulip/zulip/
[continuous-integration]: ../testing/continuous-integration.md
[zulip-git-guide-fork-ci]: cloning.md#step-3-configure-continuous-integration-for-your-fork
[zulip-rtd-code-style]: ../contributing/code-style.md
[zulip-rtd-commit-discipline]: ../contributing/commit-discipline.md
[zulip-rtd-commit-messages]: ../contributing/commit-discipline.md
[zulip-rtd-dev-overview]: ../development/overview.md
[zulip-rtd-lint-tools]: ../contributing/code-style.md#use-the-linters
[zulip-rtd-mypy]: ../testing/mypy.md
[zulip-rtd-testing]: ../testing/testing.md
[zulip-rtd-zulip-tools]: zulip-tools.md
[zulip-rtd-zulipbot-usage]: ../contributing/zulipbot-usage.md

163
docs/git/pull-requests.md Normal file
View File

@@ -0,0 +1,163 @@
# Pull requests
When you're ready for feedback, submit a pull request. Pull requests
are a feature specific to GitHub. They provide a simple, web-based way
to submit your work (often called "patches") to a project. It's called
a _pull request_ because you're asking the project to _pull changes_
from your fork.
If you're unfamiliar with how to create a pull request, you can check
out GitHub's documentation on
[creating a pull request from a fork][github-help-create-pr-fork]. You
might also find GitHub's article
[about pull requests][github-help-about-pr] helpful. That all said,
the tutorial below will walk you through the process.
## Draft pull requests
In the Zulip project, we encourage submitting [draft pull
requests][github-help-draft-pr] early and often. This allows you to
share your code to make it easier to get feedback and help with your
changes, even if you don't think your pull request is ready to be
merged (e.g., it might not work or pass tests). This sets expectations
correctly for any feedback from other developers, and prevents your
work from being merged before you're confident in it.
## Create a pull request
### Step 0: Make sure you're on a feature branch (not `main`)
It is important to [work on a feature
branch](using.md#work-on-a-feature-branch) when creating a pull
request. Your new pull request will be inextricably linked with your
branch while it is open, so you will need to reserve your branch only
for changes related to your issue, and avoid introducing extraneous
changes for other issues or from upstream.
If you are working on a branch named `main`, you need to create and
switch to a feature branch before proceeding.
### Step 1: Update your branch with git rebase
The best way to update your branch is with `git fetch` and `git rebase`. Do not
use `git pull` or `git merge` as this will create merge commits. See [keep your
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):
```console
$ git checkout issue-123
Switched to branch 'issue-123'
$ git fetch upstream
remote: Counting objects: 69, done.
remote: Compressing objects: 100% (23/23), done.
remote: Total 69 (delta 49), reused 39 (delta 39), pack-reused 7
Unpacking objects: 100% (69/69), done.
From https://github.com/zulip/zulip
69fa600..43e21f6 main -> upstream/main
$ git rebase upstream/main
First, rewinding head to replay your work on top of it...
Applying: troubleshooting tip about provisioning
```
### Step 2: Push your updated branch to your remote fork
Once you've updated your local feature branch, push the changes to GitHub:
```console
$ git push origin issue-123
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 658 bytes | 0 bytes/s, done.
Total 6 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 1 local objects.
To git@github.com:christi3k/zulip.git
+ 2d49e2d...bfb2433 issue-123 -> issue-123
```
If your push is rejected with error **failed to push some refs** then you need
to prefix the name of your branch with a `+`:
```console
$ git push origin +issue-123
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 658 bytes | 0 bytes/s, done.
Total 6 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 1 local objects.
To git@github.com:christi3k/zulip.git
+ 2d49e2d...bfb2433 issue-123 -> issue-123 (forced update)
```
This is perfectly okay to do on your own feature branches, especially if you're
the only one making changes to the branch. If others are working along with
you, they might run into complications when they retrieve your changes because
anyone who has based their changes off a branch you rebase will have to do a
complicated rebase.
### Step 3: Open the pull request
If you've never created a pull request or need a refresher, take a look at
GitHub's article on [creating a pull request from a
fork][github-help-create-pr-fork]. We'll briefly review the process here.
First, sign in to GitHub on your web browser and navigate to your fork of Zulip.
Next, navigate to the branch you've been working on. Do this by clicking on the
**Branch** button and selecting the relevant branch. Finally, click the **New
pull request** button. Alternatively, if you've recently pushed the relevant
branch to your fork, you will see a **Compare & pull request** button.
A pull request template will open with some information pre-filled in.
Provide (or update) the title for your pull request and write a first comment.
If your pull request makes UI changes, always include one or more still
screenshots to demonstrate your changes. If it seems helpful, add a brief
video of the new functionality as well. Take time to read our
[comprehensive guide to capturing screenshots](../contributing/presenting-visual-changes.md),
which includes [a list of screenshot tools][screenshots-gifs] you can use.
See the documentation for creating [reviewable pull requests][reviewable-prs]
for more guidance and tips when writing pull request comments. If the repository
has a self-review checklist in the pull request template, make sure that all the
relevant points have been addressed before submitting it.
When ready, click the **Create pull request** button to submit the pull request.
Remember to mark your pull request as a [draft][github-help-draft-pr] if it is a
work-in-progress.
Note: **Pull request titles are different from commit messages.** Commit
messages can be edited with `git commit --amend`, `git rebase -i`, etc., while
the title of a pull request can only be edited via GitHub.
## Update a pull request
As you get make progress on your feature or bugfix, your pull request, once
submitted, will be updated each time you [push commits][self-push-commits] to
your remote branch. This means you can keep your pull request open as long as
you need, rather than closing and opening new ones for the same feature or
bugfix.
It's a good idea to keep your pull request mergeable with Zulip upstream by
frequently fetching, rebasing, and pushing changes. See [keep your fork up to
date][keep-up-to-date] for details. You might also find this excellent
article [How to Rebase a Pull Request][edx-howto-rebase-pr] helpful.
And, as you address review comments others have made, we recommend posting a
follow-up comment in which you: a) ask for any clarifications you need, b)
explain to the reviewer how you solved any problems they mentioned, and c) ask
for another review.
[edx-howto-rebase-pr]: https://web.archive.org/web/20240204225038/https://github.com/openedx/edx-platform/wiki/How-to-Rebase-a-Pull-Request
[github-help-about-pr]: https://help.github.com/en/articles/about-pull-requests
[github-help-create-pr-fork]: https://help.github.com/en/articles/creating-a-pull-request-from-a-fork
[github-help-draft-pr]: https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests
[keep-up-to-date]: using.md#keep-your-fork-up-to-date
[self-push-commits]: using.md#push-your-commits-to-github
[screenshots-gifs]: ../contributing/presenting-visual-changes.md#screenshot-and-gif-software
[reviewable-prs]: ../contributing/reviewable-prs.md

View File

@@ -0,0 +1,88 @@
# Reading history
One of the things that makes Git so valuable is its facilities for
studying history -- both
- very recent history (e.g., rereading your own branch before you
send a PR), and
- the distant past (e.g., tracking down when and why some piece
of code became the way it is.)
This guide, written by Greg Price, will help you take advantage of
these powerful abilities.
## Use a graphical client
One great way to read history: [use a graphical Git client](setup.md#get-a-graphical-client)!
Especially helpful for
- reading through the recent commits, or
- clarifying how branches are related to each other.
## The "secret" to using `git log -p`
Use `git log -p`, with this important secret:
- In the pager that `git log` puts you into, hit `/` to search, then enter
the pattern `^c` -- that is, caret, then `c`. Then hit `n`/`N` for
next/previous match.
- This finds lines that begin with `c`. Because of thoughtful design in
the default format of `git log`, these are exactly the first line of the
log entry for each commit. So with this search pattern, you effectively
get **keybindings for "next/previous commit"**. This makes a huge
difference in skimming quickly past boring commits without missing
anything.
- For a further upgrade, use `git log --stat -p` (you might alias this as
e.g., `git lsp`.) Now every time you hit `n` or `N`, you see the commit
message and complete list of affected files for the new commit -- a good
summary to help decide whether to read in more detail or hit `n`/`N`
again to move on.
## Filter `git log` down to relevant commits
Use some of `git log`'s many features to filter down to commits you
care about. For example:
- Filter to a range of commits with `git log A..B`. E.g., to reread your
current branch relative to upstream main, you might say
`git log --stat -p upstream/main..`, or `git log --stat -p @{u}..`.
(Greg has the latter aliased as `git usp`, and types it constantly.)
- Filter to changes touching certain files or directories:
`git log PATHS`.
- Filter to changes touching lines that mention some pattern:
`git log -G PATTERN`.
- Filter to changes adding or removing mentions of some pattern:
`git log -S PATTERN`. This feature is traditionally called the
"pickaxe", presumably in honor of its power to mine just the right bit
of historical/explanatory gold.
- Many more. Do take a few minutes to skim through the documentation in
`git help log` (or [this web version](https://git-scm.com/docs/git-log))
to get an idea of what's available; and perhaps an hour now and then to
read and try things in more detail.
## Filter in your graphical client
Try all those `git log` filtering features in your graphical client -- it
may even support the very same command-line options to do it. For
example, `gitk upstream/main..` shows basically the same information as
`git log --stat -p upstream/main..`, but graphically.
## Git a summary, with `git log --oneline`
Try `git log --graph --oneline --decorate --boundary`. (Quite a mouthful;
Greg has this aliased as `git k`, in homage to `gitk`.) It can be a
lightweight alternative to your graphical client for simple, routine
situations, giving a compact list of commits each on just one line.
- To list the commits local to your current branch: `git k @{u}..`.
(Greg has an alias for this, and types it constantly.)
- To list all your own local commits on all branches:
`git k --branches @ --not --remotes=origin --remotes=upstream`.
(For explanation, see `git help log`.)

64
docs/git/reviewing.md Normal file
View File

@@ -0,0 +1,64 @@
# Review changes
**Note** - The following section covers reviewing changes on your local
clone. Please read the section on [code reviews][zulip-rtd-review] for a guide
on reviewing changes by other contributors.
## Changes on (local) working tree
Display changes between index and working tree (what is not yet staged for commit):
```console
$ git diff
```
Display changes between index and last commit (what you have staged for commit):
```console
$ git diff --cached
```
Display changes in working tree since last commit (changes that are staged as
well as ones that are not):
```console
$ git diff HEAD
```
## Changes within branches
Use any git-ref to compare changes between two commits on the current branch.
Display changes between commit before last and last commit:
```console
$ git diff HEAD^ HEAD
```
Display changes between two commits using their hashes:
```console
$ git diff e2f404c 7977169
```
## Changes between branches
Display changes between tip of `topic` branch and tip of `main` branch:
```console
$ git diff topic main
```
Display changes that have occurred on `main` branch since `topic` branch was created:
```console
$ git diff topic...main
```
Display changes you've committed so far since creating a branch from `upstream/main`:
```console
$ git diff upstream/main...HEAD
```
[zulip-rtd-review]: ../contributing/code-reviewing.md

72
docs/git/setup.md Normal file
View File

@@ -0,0 +1,72 @@
# Set up Git
If you're already using Git, have a client you like, and a GitHub account, you
can skip this section. Otherwise, read on!
## Install and configure Git, join GitHub
If you're not already using Git, you might need to [install][gitbook-install]
and [configure][gitbook-setup] it.
**If you are using Windows 10 without [WSL
2](https://docs.microsoft.com/en-us/windows/wsl/wsl2-about), make sure you
[are running Git BASH as an administrator][git-bash-admin] at all times.**
You'll also need a GitHub account, which you can sign up for
[here][github-join].
We highly recommend you create an SSH key if you don't already have
one and [add it to your GitHub account][github-help-add-ssh-key]. If
you don't, you'll have to type your GitHub username and password every
time you interact with GitHub, which is usually several times a day.
We also highly recommend the following:
- [Configure Git][gitbook-config] with your name and email and
[aliases][gitbook-aliases] for commands you'll use often. We
recommend using your full name (not just your first name), since
that's what we'll use to give credit to your work in places like the
Zulip release notes.
- Install the command auto-completion and/or git-prompt plugins available for
[Bash][gitbook-other-envs-bash] and [Zsh][gitbook-other-envs-zsh].
If you are installing the Zulip development environment, now you are ready to
continue with [Step 1: Install prerequisites](../development/setup-recommended.md#step-1-install-prerequisites).
## Get a graphical client
Even if you're comfortable using Git on the command line, having a graphical
client can be useful for viewing your repository. This is especially true
when doing complicated rebases and similar operations because you can check
the state of your repository after each command to see what changed. If
something goes wrong, this helps you figure out when and why.
If you don't already have one installed, here are some suggestions:
- macOS: [GitX][gitgui-gitx] (previously [GitX-dev][gitgui-gitxdev])
- Ubuntu/Linux: [git-cola][gitgui-gitcola], [gitg][gitgui-gitg], [gitk][gitgui-gitk]
- Windows: [SourceTree][gitgui-sourcetree]
If you like working on the command line, but want better visualization and
navigation of your Git repo, try [Tig][tig], a cross-platform ncurses-based
text-mode interface to Git.
And, if none of the above are to your liking, try [one of these][gitbook-guis].
[git-bash-admin]: ../development/setup-advanced.md#running-git-bash-as-an-administrator
[gitbook-aliases]: https://git-scm.com/book/en/v2/Git-Basics-Git-Aliases
[gitbook-config]: https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration
[gitbook-guis]: https://git-scm.com/downloads/guis
[gitbook-install]: https://git-scm.com/book/en/v2/Getting-Started-Installing-Git
[github-join]: https://github.com/join
[gitbook-setup]: https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup
[gitbook-other-envs-bash]: https://git-scm.com/book/en/v2/Appendix-A%3A-Git-in-Other-Environments-Git-in-Bash
[gitbook-other-envs-zsh]: https://git-scm.com/book/en/v2/Appendix-A%3A-Git-in-Other-Environments-Git-in-Zsh
[gitgui-gitcola]: https://git-cola.github.io/
[gitgui-gitg]: https://wiki.gnome.org/Apps/Gitg
[gitgui-gitk]: https://git-scm.com/docs/gitk
[gitgui-gitx]: https://github.com/gitx/gitx/
[gitgui-gitxdev]: https://rowanj.github.io/gitx/
[gitgui-sourcetree]: https://www.sourcetreeapp.com/
[github-help-add-ssh-key]: https://help.github.com/en/articles/adding-a-new-ssh-key-to-your-github-account
[tig]: https://jonas.github.io/tig/

96
docs/git/terminology.md Normal file
View File

@@ -0,0 +1,96 @@
# Important Git terms
When you install Git, it adds a manual entry for `gitglossary`. You can view
this glossary by running `man gitglossary`. Below we've included the Git terms
you'll encounter most often along with their definitions from _gitglossary_.
## branch
A "branch" is an active line of development. The most recent commit
on a branch is referred to as the tip of that branch. The tip of
the branch is referenced by a branch head, which moves forward as
additional development is done on the branch. A single Git
repository can track an arbitrary number of branches, but your
working tree is associated with just one of them (the "current" or
"checked out" branch), and HEAD points to that branch.
## cache
Obsolete for: index
## checkout
The action of updating all or part of the working tree with a tree
object or blob from the object database, and updating the index and
HEAD if the whole working tree has been pointed at a new branch.
## commit
As a noun: A single point in the Git history; the entire history of
a project is represented as a set of interrelated commits. The word
"commit" is often used by Git in the same places other revision
control systems use the words "revision" or "version". Also used as
a short hand for commit object.
As a verb: The action of storing a new snapshot of the project's
state in the Git history, by creating a new commit representing the
current state of the index and advancing HEAD to point at the new
## fast-forward
A fast-forward is a special type of merge where you have a revision
and you are "merging" another branch's changes that happen to be a
descendant of what you have. In such these cases, you do not make a
new merge commit but instead just update to their revision. This will
happen frequently on a remote-tracking branch of a remote
repository.
## fetch
Fetching a branch means to get the branch's head ref from a remote
repository, to find out which objects are missing from the local
object database, and to get them, too. See also [git-fetch(1)](https://git-scm.com/docs/git-fetch)
## hash
In Git's context, synonym for object name.
## head
A named reference to the commit at the tip of a branch. Heads are
stored in a file in $GIT_DIR/refs/heads/ directory, except when
using packed refs. See also [git-pack-refs(1)](https://git-scm.com/docs/git-pack-refs).
## HEAD
The current branch. In more detail: Your working tree is normally
derived from the state of the tree referred to by HEAD. HEAD is a
reference to one of the heads in your repository, except when using
a detached HEAD, in which case it directly references an arbitrary
commit.
## index
A collection of files with stat information, whose contents are
stored as objects. The index is a stored version of your working
tree. Truth be told, it can also contain a second, and even a third
version of a working tree, which are used when merging.
## pull
Pulling a branch means to fetch it and merge it. See also [git-pull(1)](https://git-scm.com/docs/git-pull)
## push
Pushing a branch means to get the branch's head ref from a remote
repository, find out if it is a direct ancestor to the branch's
local head ref, and in that case, putting all objects, which are
reachable from the local head ref, and which are missing from the
remote repository, into the remote object database, and updating
the remote head ref. If the remote head is not an ancestor to the
local head, the push fails.
## rebase
To reapply a series of changes from a branch to a different base,
and reset the head of that branch to the result.

View File

@@ -0,0 +1,64 @@
# How Git is different
Whether you're new to Git or have experience with another version control
system (VCS), it's a good idea to learn a bit about how Git works. We recommend
this excellent presentation _[Understanding Git][understanding-git]_ from
Nelson Elhage and Anders Kaseorg and the [Git Basics][gitbook-basics] chapter
from _Pro Git_ by Scott Chacon and Ben Straub.
Here are the top things to know:
- **Git works on snapshots.** Unlike other version control systems (e.g.,
Subversion, Perforce, Bazaar), which track files and changes to those files
made over time, Git tracks _snapshots_ of your project. Each time you commit
or otherwise make a change to your repository, Git takes a snapshot of your
project and stores a reference to that snapshot. If a file hasn't changed,
Git creates a link to the identical file rather than storing it again.
- **Most Git operations are local.** Git is a distributed version control
system, so once you've cloned a repository, you have a complete copy of that
repository's _entire history_. Staging, committing, branching, and browsing
history are all things you can do locally without network access and without
immediately affecting any remote repositories. To make or receive changes
from remote repositories, you need to `git fetch`, `git pull`, or `git push`.
- **Nearly all Git actions add information to the Git database**, rather than
removing it. As such, it's hard to make Git perform actions that you can't
undo. However, Git can't undo what it doesn't know about, so it's a good
practice to frequently commit your changes and frequently push your commits to
your remote repository.
- **Git is designed for lightweight branching and merging.** Branches are
simply references to snapshots. It's okay and expected to make a lot of
branches, even throwaway and experimental ones.
- **Git stores all data as objects, of which there are four types:** blob
(file), tree (directory), commit (revision), and tag. Each of these objects
is named by a unique hash, the SHA-1 hash of its contents. Most of the time
you'll refer to objects by their truncated hash or more human-readable
reference like `HEAD` (the current branch). Blobs and trees represent files
and directories. Tags are named references to other objects. A commit object
includes: tree id, zero or more parents as commit ids, an author (name,
email, date), a committer (name, email, date), and a log message. A Git
repository is a collection of mutable pointers to these objects called
**refs**.
- **Cloning a repository creates a working copy.** Every working copy has a
`.git` subdirectory, which contains its own Git repository. The `.git`
subdirectory also tracks the _index_, a staging area for changes that will
become part of the next commit. All files outside of `.git` are the _working
tree_.
- **Files tracked with Git have three possible states: committed, modified, and
staged.** Committed files are those safely stored in your local `.git`
repository/database. Staged files have changes and have been marked for
inclusion in the next commit; they are part of the index. Modified files have
changes but have not yet been marked for inclusion in the next commit; they
have not been added to the index.
- **Git commit workflow is as follows.** Edit files in your _working tree_. Add
to the _index_ (that is _stage_) with `git add`. _Commit_ to the HEAD of the
current branch with `git commit`.
[gitbook-basics]: https://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository
[understanding-git]: https://web.mit.edu/nelhage/Public/git-slides-2009.pdf

284
docs/git/troubleshooting.md Normal file
View File

@@ -0,0 +1,284 @@
# Get and stay out of trouble
Git is a powerful yet complex version control system. Even for contributors
experienced at using version control, it can be confusing. The good news is
that nearly all Git actions add information to the Git database, rather than
removing it. As such, it's hard to make Git perform actions that you can't
undo. However, Git can't undo what it doesn't know about, so it's a good
practice to frequently commit your changes and frequently push your commits to
your remote repository.
## Undo a merge commit
A merge commit is a special type of commit that has two parent commits. It's
created by Git when you merge one branch into another and the last commit on
your current branch is not a direct ancestor of the branch you are trying to
merge in. This happens quite often in a busy project like Zulip where there are
many contributors because upstream/zulip will have new commits while you're
working on a feature or bugfix. In order for Git to merge your changes and the
changes that have occurred on zulip/upstream since you first started your work,
it must perform a three-way merge and create a merge commit.
Merge commits aren't bad, however, Zulip doesn't use them. Instead Zulip uses a
forked-repo, rebase-oriented workflow.
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
message and the default is something like this:
```text
Merge branch 'main' of https://github.com/zulip/zulip
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.
```
And the first entry for `git log` will show something like:
```console
commit e5f8211a565a5a5448b93e98ed56415255546f94
Merge: 13bea0e e0c10ed
Author: Christie Koehler <ck@christi3k.net>
Date: Mon Oct 10 13:25:51 2016 -0700
Merge branch 'main' of https://github.com/zulip/zulip
```
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 roll back to:
```console
$ git reflog
e5f8211 HEAD@{0}: pull upstream main: Merge made by the 'recursive' strategy.
13bea0e HEAD@{1}: commit: test commit for docs.
```
Reflog output will be long. The most recent Git refs will be listed at the top.
In the example above `e5f8211 HEAD@{0}:` is the merge commit made automatically
by `git pull` and `13bea0e HEAD@{1}:` is the last commit I made before running
`git pull`, the commit that I want to rollback to.
Once you'd identified the ref you want to revert to, you can do so with [git
reset][gitbook-reset]:
```console
$ git reset --hard 13bea0e
HEAD is now at 13bea0e test commit for docs.
```
:::{important}
`git reset --hard <commit>` will discard all changes in your
working directory and index since the commit you're resetting to with
`<commit>`. _This is the main way you can lose work in Git_. If you need
to keep any changes that are in your working directory or that you have
committed, use `git reset --merge <commit>` instead.
:::
You can also use the relative reflog `HEAD@{1}` instead of the commit hash,
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
last commit `13bea0e` before the merge:
```console
$ git reflog
13bea0e HEAD@{2}: reset: moving to HEAD@{1}
e5f8211 HEAD@{3}: pull upstream main: Merge made by the 'recursive' strategy.
13bea0e HEAD@{4}: commit: test commit for docs.
```
And the first entry `git log` shows is this:
```console
commit 13bea0e40197b1670e927a9eb05aaf50df9e8277
Author: Christie Koehler <ck@christi3k.net>
Date: Mon Oct 10 13:25:38 2016 -0700
test commit for docs.
```
## Restore a lost commit
We've mentioned you can use `git reset --hard` to rollback to a previous
commit. What if you run `git reset --hard` and then realize you actually need
one or more of the commits you just discarded? No problem, you can restore them
with `git cherry-pick` ([docs][gitbook-git-cherry-pick]).
For example, let's say you just committed "some work" and your `git log` looks
like this:
```console
* 67aea58 (HEAD -> main) some work
* 13bea0e test commit for docs.
```
You then mistakenly run `git reset --hard 13bea0e`:
```console
$ git reset --hard 13bea0e
HEAD is now at 13bea0e test commit for docs.
$ git log
* 13bea0e (HEAD -> main) test commit for docs.
```
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 cherry-pick <commit>`:
```console
$ git reflog
13bea0e HEAD@{0}: reset: moving to 13bea0e
67aea58 HEAD@{1}: commit: some work
$ git cherry-pick 67aea58
[main 67aea58] some work
Date: Thu Oct 13 11:51:19 2016 -0700
1 file changed, 1 insertion(+)
create mode 100644 test4.txt
```
## Recover from a `git rebase` failure
One situation in which `git rebase` will fail and require you to intervene is
when your change, which Git will try to re-apply on top of new commits from
which ever branch you are rebasing on top of, is to code that has been changed
by those new commits.
For example, while I'm working on a file, another contributor makes a change to
that file, submits a pull request and has their code merged into `main`.
Usually this is not a problem, but in this case the other contributor made a
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/main`, I see
the following:
```console
First, rewinding head to replay your work on top of it...
Applying: test change for docs
Using index info to reconstruct a base tree...
M README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
error: Failed to merge in the changes.
Patch failed at 0001 test change for docs
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
```
This message tells me that Git was not able to apply my changes to README.md
after bringing in the new commits from upstream/main.
Running `git status` also gives me some information:
```console
rebase in progress; onto 5ae56e6
You are currently rebasing branch 'docs-test' on '5ae56e6'.
(fix conflicts and then run "git rebase --continue")
(use "git rebase --skip" to skip this patch)
(use "git rebase --abort" to check out the original branch)
Unmerged paths:
(use "git reset HEAD <file>..." to unstage)
(use "git add <file>..." to mark resolution)
both modified: README.md
no changes added to commit (use "git add" and/or "git commit -a")
```
To fix, open all the files with conflicts in your editor and decide which edits
should be applied. Git uses standard conflict-resolution (`<<<<<<<`, `=======`,
and `>>>>>>>`) markers to indicate where in files there are conflicts.
Tip: You can see recent changes made to a file by running the following
commands:
```bash
git fetch upstream
git log -p upstream/main -- /path/to/file
```
You can use this to compare the changes that you have made to a file with the
ones in upstream, helping you avoid undoing changes from a previous commit when
you are rebasing.
Once you've done that, save the file(s), stage them with `git add` and then
continue the rebase with `git rebase --continue`:
```console
$ git add README.md
$ git rebase --continue
Applying: test change for docs
```
For help resolving merge conflicts, see [basic merge
conflicts][gitbook-basic-merge-conflicts], [advanced
merging][gitbook-advanced-merging], and/or GitHub's help on [how to resolve a
merge conflict][github-help-resolve-merge-conflict].
## Working from multiple computers
Working from multiple computers with Zulip and Git is fine, but you'll need to
pay attention and do a bit of work to ensure all of your work is readily
available.
Recall that most Git operations are local. When you commit your changes with
`git commit` they are safely stored in your _local_ Git database only. That is,
until you _push_ the commits to GitHub, they are only available on the computer
where you committed them.
So, before you stop working for the day, or before you switch computers, push
all of your commits to GitHub with `git push`:
```console
$ git push origin <branchname>
```
When you first start working on a new computer, you'll [clone the Zulip
repository][clone-to-your-machine] and [connect it to Zulip
upstream][connect-upstream]. A clone retrieves all current commits,
including the ones you pushed to GitHub from your other computer.
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
GitHub fork. You do this with `git fetch`:
```console
$ git fetch <username>
```
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
to update:
```console
$ git checkout <my-branch>
Switched to branch '<my-branch>'
$ git merge origin/main
```
**If you have already made commits on the second computer that you need to
keep,** you'll need to use `git log FETCH_HEAD` to identify that hashes of the
commits you want to keep and then `git cherry-pick <commit>` those commits into
whichever branch you need to update.
[clone-to-your-machine]: cloning.md#step-1b-clone-to-your-machine
[connect-upstream]: cloning.md#step-1c-connect-your-fork-to-zulip-upstream
[gitbook-advanced-merging]: https://git-scm.com/book/en/v2/Git-Tools-Advanced-Merging#_advanced_merging
[gitbook-basic-merge-conflicts]: https://git-scm.com/book/en/v2/Git-Branching-Basic-Branching-and-Merging#Basic-Merge-Conflicts
[gitbook-git-cherry-pick]: https://git-scm.com/docs/git-cherry-pick
[gitbook-reset]: https://git-scm.com/docs/git-reset
[github-help-resolve-merge-conflict]: https://help.github.com/en/articles/resolving-a-merge-conflict-using-the-command-line

457
docs/git/using.md Normal file
View File

@@ -0,0 +1,457 @@
# Using Git as you work
## Know what branch you're working on
When using Git, it's important to know which branch you currently have checked
out because most Git commands implicitly operate on the current branch. You can
determine the currently checked out branch several ways.
One way is with [git status][gitbook-git-status]:
```console
$ git status
On branch issue-demo
nothing to commit, working directory clean
```
Another is with [git branch][gitbook-git-branch] which will display all local
branches, with a star next to the current branch:
```console
$ git branch
* issue-demo
main
```
To see even more information about your branches, including remote branches,
use `git branch -vva`:
```console
$ git branch -vva
* issue-123 517468b troubleshooting tip about provisioning
main f0eaee6 [origin/main] bug: Fix traceback in get_missed_message_token_from_address().
remotes/origin/HEAD -> origin/main
remotes/origin/issue-1234 4aeccb7 Another test commit, with longer message.
remotes/origin/main f0eaee6 bug: Fix traceback in get_missed_message_token_from_address().
remotes/upstream/main dbeab6a Optimize checks of test database state by moving into Python.
```
You can also configure [Bash][gitbook-other-envs-bash] and
[Zsh][gitbook-other-envs-zsh] to display the current branch in your prompt.
## Keep your fork up to date
You'll want to [keep your fork][github-help-sync-fork] up-to-date with changes
from Zulip's main repositories.
**Note about `git pull`**: You might be used to using `git pull` on other
projects. With Zulip, because we don't use merge commits, you'll want to avoid
it. Rather than using `git pull`, which by default is a shortcut for
`git fetch && git merge FETCH_HEAD` ([docs][gitbook-git-pull]), you
should use `git fetch` and then `git rebase`.
First, [fetch][gitbook-fetch] changes from Zulip's upstream repository you
configured in the step above:
```console
$ git fetch upstream
```
Next, check out your `main` branch and [rebase][gitbook-git-rebase] it on top
of `upstream/main`:
```console
$ git checkout main
Switched to branch 'main'
$ git rebase upstream/main
```
This will rollback any changes you've made to `main`, update it from
`upstream/main`, and then re-apply your changes. Rebasing keeps the commit
history clean and readable.
When you're ready, [push your changes][github-help-push] to your remote fork.
Make sure you're in branch `main` and then run `git push`:
```console
$ git checkout main
$ git push origin main
```
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
command slightly, using the name of your `feature-branch` rather than `main`:
```console
$ git checkout feature-branch
Switched to branch 'feature-branch'
$ git rebase upstream/main
$ git push origin feature-branch
```
## Work on a feature branch
One way to keep your work organized is to create a branch for each issue or
feature. Recall from [how Git is different][how-git-is-different] that
**Git is designed for lightweight branching and merging.** You can and should
create as many branches as you'd like.
First, make sure your `main` branch is up-to-date with Zulip upstream ([see
how][zulip-git-guide-up-to-date]).
Next, from your `main` branch, create a new tracking branch, providing a
descriptive name for your feature branch:
```console
$ git checkout main
Switched to branch 'main'
$ git checkout -b issue-1755-fail2ban
Switched to a new branch 'issue-1755-fail2ban'
```
Alternatively, you can create a new branch explicitly based off
`upstream/main`:
```console
$ git checkout -b issue-1755-fail2ban upstream/main
Switched to a new branch 'issue-1755-fail2ban'
```
Now you're ready to work on the issue or feature.
## Run linters and tests locally
In addition to having GitHub Actions run tests and linters each time you
push a new commit, you can also run them locally. See
[testing](../testing/testing.md) for details.
## Stage changes
Recall that files tracked with Git have three possible states:
committed, modified, and staged.
To prepare a commit, first add the files with changes that you want
to include in your commit to your staging area. You _add_ both new files and
existing ones. You can also remove files from staging when necessary.
### Get status of working directory
To see what files in the working directory have changes that have not been
staged, use `git status`.
If you have no changes in the working directory, you'll see something like
this:
```console
$ git status
On branch issue-123
nothing to commit, working directory clean
```
If you have unstaged changes, you'll see something like this:
```console
On branch issue-123
Untracked files:
(use "git add <file>..." to include in what will be committed)
newfile.py
nothing added to commit but untracked files present (use "git add" to track)
```
### Stage additions with `git add`
To add changes to your staging area, use `git add <filename>`. Because
`git add` is all about staging the changes you want to commit, you use
it to add _new files_ as well as _files with changes_ to your staging
area.
Continuing our example from above, after we run `git add newfile.py`, we'll see
the following from `git status`:
```console
On branch issue-123
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: newfile.py
```
You can view the changes in files you have staged with `git diff --cached`. To
view changes to files you haven't yet staged, just use `git diff`.
If you want to add all changes in the working directory, use `git add -A`
([documentation][gitbook-add]).
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
an example where we stage a file `test3.txt` and then unstage it:
```console
$ git add test3.txt
On branch issue-1234
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: test3.txt
$ git reset HEAD test3.txt
$ git status
On branch issue-1234
Untracked files:
(use "git add <file>..." to include in what will be committed)
test3.txt
nothing added to commit but untracked files present (use "git add" to track)
```
### Stage deletions with `git rm`
To remove existing files from your repository, use `git rm`
([documentation][gitbook-rm]). This command can either stage the file for
removal from your repository AND delete it from your working directory or just
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
`git rm <filename>`:
```console
$ git rm test.txt
rm 'test.txt'
$ git status
On branch issue-1234
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: test.txt
$ ls test.txt
ls: No such file or directory
```
To stage a file for deletion and **keep** it in your working directory, use
`git rm --cached <filename>`:
```console
$ git rm --cached test2.txt
rm 'test2.txt'
$ git status
On branch issue-1234
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: test2.txt
$ ls test2.txt
test2.txt
```
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>`:
```console
$ git reset HEAD test2.txt
```
Unfortunately, you can't restore a file deleted with `git rm` if you didn't use
the `--cache` option. However, `git rm` only deletes files it knows about.
Files you have never added to Git won't be deleted.
## Commit changes
When you've staged all your changes, you're ready to commit. You can do this
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:
```console
$ git commit -m "Add a test commit for docs."
[issue-123 173e17a] Add a test commit for docs.
1 file changed, 1 insertion(+)
create mode 100644 newfile.py
```
You can also use `git commit` without the `-m` option and your editor to open,
allowing you to easily draft a multi-line commit message.
How long your commit message should be depends on where you are in your work.
Using short, one-line messages for commits related to in-progress work makes
sense. For a commit that you intend to be final or that encompasses a
significant amount or complex work, you should include a longer message.
Keep in mind that your commit should contain a 'minimal coherent idea' and have
a quality commit message. See Zulip docs [Commit
Discipline][zulip-rtd-commit-discipline] and [Commit
messages][zulip-rtd-commit-messages] for details.
Here's an example of a longer commit message that will be used for a pull request:
```text
Integrate Fail2Ban.
Updates Zulip logging to put an unambiguous entry into the logs such
that fail2ban can be configured to look for these entries.
Tested on my local Ubuntu development server, but would appreciate
someone testing on a production install with more users.
Fixes #1755.
```
The first line is the summary. It's a complete sentence, ending in a period. It
uses a present-tense action verb, "Integrate", rather than "Integrates" or
"Integrating".
The following paragraphs are full prose and explain why and how the change was
made. It explains what testing was done and asks specifically for further
testing in a more production-like environment.
The final paragraph indicates that this commit addresses and fixes issue #1755.
When you submit your pull request, GitHub will detect and link this reference
to the appropriate issue. Once your commit is merged into `upstream/main`, GitHub
will automatically close the referenced issue. See [Closing issues via commit
messages][github-help-closing-issues] for details.
Note in particular that GitHub's regular expressions for this feature
are sloppy, so phrases like `Partially fixes #1234` will automatically
close the issue. Phrases like `Fixes part of #1234` are a good
alternative.
Make as many commits as you need to address the issue or implement your feature.
## Push your commits to GitHub
As you're working, it's a good idea to frequently push your changes to GitHub.
This ensures your work is backed up should something happen to your local
machine and allows others to follow your progress. It also allows you to
[work from multiple computers][self-multiple-computers] without losing work.
Pushing to a feature branch is just like pushing to `main`:
```console
$ git push origin <branch-name>
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (6/6), 658 bytes | 0 bytes/s, done.
Total 6 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 1 local objects.
To git@github.com:christi3k/zulip.git
* [new branch] issue-demo -> issue-demo
```
If you want to see what Git will do without actually performing the push, add
the `-n` (dry-run) option: `git push -n origin <branch-name>`. If everything
looks good, re-run the push command without `-n`.
If the feature branch does not already exist on GitHub, it will be created when
you push and you'll see `* [new branch]` in the command output.
## Examine and tidy your commit history
Examining your commit history prior to submitting your pull request is a good
idea. Is it tidy such that each commit represents a minimally coherent idea
(see [commit discipline][zulip-rtd-commit-discipline])? Do your commit messages
follow [Zulip's style][zulip-rtd-commit-messages]? Will the person reviewing
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
read list of your commits:
```console
$ git log --all --graph --oneline --decorate
* 4f8d75d (HEAD -> 1754-docs-add-git-workflow) docs: Add details about configuring Travis CI.
* bfb2433 (origin/1754-docs-add-git-workflow) docs: Add section for keeping fork up-to-date to Git Guide.
* 4fe10f8 docs: Add sections for creating and configuring fork to Git Guide.
* 985116b docs: Add graphic client recs to Git Guide.
* 3c40103 docs: Add stubs for remaining Git Guide sections.
* fc2c01e docs: Add git guide quickstart.
| * f0eaee6 (upstream/main) bug: Fix traceback in get_missed_message_token_from_address().
```
Alternatively, use your graphical client to view the history for your feature branch.
If you need to update any of your commits, you can do so with an interactive
[rebase][github-help-rebase]. Common reasons to use an interactive rebase
include:
- squashing several commits into fewer commits
- splitting a single commit into two or more
- rewriting one or more commit messages
There is ample documentation on how to rebase, so we won't go into details
here. We recommend starting with GitHub's help article on
[rebasing][github-help-rebase] and then consulting Git's documentation for
[git-rebase][gitbook-git-rebase] if you need more details.
If all you need to do is edit the commit message for your last commit, you can
do that with `git commit --amend`. See [Git Basics - Undoing
Things][gitbook-basics-undoing] for details on this and other useful commands.
## Force-push changes to GitHub after you've altered your history
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
updates will be rejected with a message such as:
```console
$ git push origin 1754-docs-add-git-workflow
To git@github.com:christi3k/zulip.git
! [rejected] 1754-docs-add-git-workflow -> 1754-docs-add-git-workflow (non-fast-forward)
error: failed to push some refs to 'git@github.com:christi3k/zulip.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.,
hint: 'git pull ...') before pushing again.
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-writing the history for the remote repository:
```console
$ git push origin +1754-docs-add-git-workflow
Counting objects: 12, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (12/12), done.
Writing objects: 100% (12/12), 3.71 KiB | 0 bytes/s, done.
Total 12 (delta 8), reused 0 (delta 0)
remote: Resolving deltas: 100% (8/8), completed with 2 local objects.
To git@github.com:christi3k/zulip.git
+ 2d49e2d...bfb2433 1754-docs-add-git-workflow -> 1754-docs-add-git-workflow (forced update)
```
This is perfectly okay to do on your own feature branches, especially if you're
the only one making changes to the branch. If others are working along with
you, they might run into complications when they retrieve your changes because
anyone who has based their changes off a branch you rebase will have to do a
complicated rebase.
[gitbook-add]: https://git-scm.com/docs/git-add
[gitbook-basics-undoing]: https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things
[gitbook-fetch]: https://git-scm.com/docs/git-fetch
[gitbook-git-branch]: https://git-scm.com/docs/git-branch
[gitbook-git-pull]: https://git-scm.com/docs/git-pull
[gitbook-git-rebase]: https://git-scm.com/docs/git-rebase
[gitbook-git-status]: https://git-scm.com/docs/git-status
[gitbook-other-envs-bash]: https://git-scm.com/book/en/v2/Appendix-A%3A-Git-in-Other-Environments-Git-in-Bash
[gitbook-other-envs-zsh]: https://git-scm.com/book/en/v2/Appendix-A%3A-Git-in-Other-Environments-Git-in-Zsh
[gitbook-rm]: https://git-scm.com/docs/git-rm
[github-help-closing-issues]: https://help.github.com/en/articles/closing-issues-via-commit-messages
[github-help-push]: https://help.github.com/en/articles/pushing-to-a-remote
[github-help-rebase]: https://help.github.com/en/articles/using-git-rebase
[github-help-sync-fork]: https://help.github.com/en/articles/syncing-a-fork
[how-git-is-different]: the-git-difference.md
[self-multiple-computers]: troubleshooting.md#working-from-multiple-computers
[zulip-git-guide-up-to-date]: #keep-your-fork-up-to-date
[zulip-rtd-commit-discipline]: ../contributing/commit-discipline.md
[zulip-rtd-commit-messages]: ../contributing/commit-discipline.md

View File

@@ -0,0 +1,50 @@
# Working copies
When you work on Zulip code, there are three copies of the Zulip Git
repository that you are generally concerned with:
- The `upstream` remote. This is the [official Zulip
repository](https://github.com/zulip/zulip) on GitHub. You probably
don't have write access to this repository.
- The **origin** remote: Your personal remote repository on GitHub.
You'll use this to share your code and create [pull requests](pull-requests.md).
- local copy: This lives on your laptop or your remote dev instance,
and is what you'll use to make changes and create commits.
When you work on Zulip code, you will end up moving code between
the various working copies.
## Workflows
Sometimes you need to get commits. Here are some scenarios:
- You may fork the official Zulip repository to your GitHub fork.
- You may fetch commits from the official Zulip repository to your local copy.
- You occasionally may fetch commits from your forked copy.
Sometimes you want to publish commits. Here are some scenarios:
- You push code from your local copy to your GitHub fork. (You usually
want to put the commit on a feature branch.)
- You submit a PR to the official Zulip repo.
Finally, the Zulip core team will occasionally want your changes!
- The Zulip core team can accept your changes and add them to
the official repo, usually on the `main` branch.
## Relevant Git commands
The following commands are useful for moving commits between
working copies:
- `git fetch`: This grabs code from another repository to your local
copy. (Defaults to fetching from your default remote, `origin`).
- `git fetch upstream`: This grabs code from the upstream repository to your local copy.
- `git push`: This pushes code from your local repository to one of the remotes.
- `git remote`: This helps you configure short names for remotes.
- `git pull`: This pulls code, but by default creates a merge commit
(which you definitely don't want). However, if you've followed our
[cloning documentation](cloning.md), this will do
`git pull --rebase` instead, which is the only mode you'll want to
use when working on Zulip.

180
docs/git/zulip-tools.md Normal file
View File

@@ -0,0 +1,180 @@
# Zulip-specific tools
This article documents several useful tools that can save you a lot of
time when working with Git on the Zulip project.
## Set up Git repo script
**Extremely useful**. In the `tools` directory of
[zulip/zulip][github-zulip-zulip] you'll find a bash script
`setup-git-repo`. This script installs a pre-commit hook, which will
run each time you `git commit` to automatically run
[Zulip's linter suite](../testing/linters.md) on just the files that
the commit modifies (which is really fast!). The hook passes no matter
the result of the linter, but you should still pay attention to any
notices or warnings it displays.
It's simple to use. Make sure you're in the clone of zulip and run the following:
```console
$ ./tools/setup-git-repo
```
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
something similar to:
```console
$ ls -l .git/hooks
pre-commit -> ../../tools/pre-commit
```
## Configure continuous integration for your Zulip fork
You might also wish to [configure continuous integration for your fork][zulip-git-guide-ci].
## Reset to pull request
`tools/reset-to-pull-request` is a short-cut for [checking out a pull request
locally][zulip-git-guide-fetch-pr]. It works slightly differently from the method
described above in that it does not create a branch for the pull request
checkout.
**This tool checks for uncommitted changes, but it will move the
current branch using `git reset --hard`. Use with caution.**
First, make sure you are working in a branch you want to move (in this
example, we'll use the local `main` branch). Then run the script
with the ID number of the pull request as the first argument.
```console
$ git checkout main
Switched to branch 'main'
Your branch is up-to-date with 'origin/main'.
$ ./tools/reset-to-pull-request 1900
+ request_id=1900
+ git fetch upstream pull/1900/head
remote: Counting objects: 159, done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 159 (delta 94), reused 91 (delta 91), pack-reused 51
Receiving objects: 100% (159/159), 55.57 KiB | 0 bytes/s, done.
Resolving deltas: 100% (113/113), completed with 54 local objects.
From https://github.com/zulip/zulip
* branch refs/pull/1900/head -> FETCH_HEAD
+ git reset --hard FETCH_HEAD
HEAD is now at 2bcd1d8 troubleshooting tip about provisioning
```
## Fetch a pull request and rebase
`tools/fetch-rebase-pull-request` is a short-cut for [checking out a pull
request locally][zulip-git-guide-fetch-pr] in its own branch and then updating it with any
changes from `upstream/main` with `git rebase`.
Run the script with the ID number of the pull request as the first argument.
```console
$ tools/fetch-rebase-pull-request 1913
+ request_id=1913
+ git fetch upstream pull/1913/head
remote: Counting objects: 4, done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (4/4), done.
From https://github.com/zulip/zulip
* branch refs/pull/1913/head -> FETCH_HEAD
+ git checkout upstream/main -b review-1913
Branch review-1913 set up to track remote branch main from upstream.
Switched to a new branch 'review-1913'
+ git reset --hard FETCH_HEAD
HEAD is now at 99aa2bf Add provision.py fails issue in common errors
+ git pull --rebase
Current branch review-1913 is up to date.
```
## Fetch a pull request without rebasing
`tools/fetch-pull-request` is a similar to `tools/fetch-rebase-pull-request`, but
it does not rebase the pull request against `upstream/main`, thereby getting
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.
```console
$ tools/fetch-pull-request 5156
+ git diff-index --quiet HEAD
+ request_id=5156
+ remote=upstream
+ git fetch upstream pull/5156/head
From https://github.com/zulip/zulip
* branch refs/pull/5156/head -> FETCH_HEAD
+ git checkout -B review-original-5156
Switched to a new branch 'review-original-5156'
+ git reset --hard FETCH_HEAD
HEAD is now at 5a1e982 tools: Update clean-branches to clean review branches.
```
## Push to a pull request
`tools/push-to-pull-request` is primarily useful for maintainers who
are merging other users' commits into a Zulip repository. After doing
`reset-to-pull-request` or `fetch-pull-request` and making some
changes, you can push a branch back to a pull request with, for example,
`tools/push-to-pull-request 1234`. This is useful for a few things:
- Getting CI to run and enabling you to use the GitHub "Merge" buttons
to merge a PR after you make some corrections to a PR, without
waiting for an extra round trip with the PR author.
- For commits that aren't ready to merge yet, communicating clearly
any changes you'd like to see happen that are easier for you to
explain by just editing the code than in words.
- Saving a contributor from needing to duplicate any rebase work that
you did as part of integrating parts of the PR.
You'll likely want to comment on the PR after doing so, to ensure that
the original contributor knows to pull your changes rather than
accidentally overwriting them with a force push when they make their
next batch of changes.
Note that in order to do this you need permission to do such a push,
which GitHub offers by default to users with write access to the
repository. For multiple developers collaborating on a PR, you can
achieve this by granting other users permission to write to your fork.
## Delete unimportant branches
`tools/clean-branches` is a shell script that removes branches that are either:
1. Local branches that are ancestors of `origin/main`.
2. Branches in origin that are ancestors of `origin/main` and named like `$USER-*`.
3. Review branches created by `tools/fetch-rebase-pull-request` and `tools/fetch-pull-request`.
First, make sure you are working in branch `main`. Then run the script without any
arguments for default behavior. Since removing review branches can inadvertently remove any
feature branches whose names are like `review-*`, it is not done by default. To
use it, run `tools/clean-branches --reviews`.
```console
$ tools/clean-branches --reviews
Deleting local branch review-original-5156 (was 5a1e982)
```
## Merge conflict on `pnpm-lock.yaml` file
If there is a merge conflict on `pnpm-lock.yaml`, pnpm should be run to
regenerate the file. _Important:_ don't delete the `pnpm-lock.yaml` file. Check out the
latest one from `origin/main` so that pnpm knows the previous asset versions.
Run the following commands
```bash
git checkout origin/main -- pnpm-lock.yaml
pnpm install
git add pnpm-lock.yaml
git rebase --continue
```
[github-zulip-zulip]: https://github.com/zulip/zulip/
[zulip-git-guide-fetch-pr]: collaborate.md#check-out-a-pull-request-locally
[zulip-git-guide-ci]: cloning.md#step-3-configure-continuous-integration-for-your-fork

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 512 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
docs/images/streams-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 726 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

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