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
1
docs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/_build
|
||||
19
docs/Makefile
Normal 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
@@ -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
0
docs/__init__.py
Normal file
18
docs/_static/theme_overrides.css
vendored
Normal 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
@@ -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
@@ -0,0 +1 @@
|
||||
../CODE_OF_CONDUCT.md
|
||||
73
docs/conf.py
Normal 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
|
||||
51
docs/contributing/asking-great-questions.md
Normal 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.
|
||||
429
docs/contributing/code-reviewing.md
Normal 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
|
||||
369
docs/contributing/code-style.md
Normal 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)
|
||||
345
docs/contributing/commit-discipline.md
Normal 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)
|
||||
154
docs/contributing/continuing-unfinished-work.md
Normal 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
|
||||
1
docs/contributing/contributing.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../CONTRIBUTING.md
|
||||
98
docs/contributing/counting-contributions.md
Normal 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
|
||||
296
docs/contributing/design-discussions.md
Normal 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
|
||||
116
docs/contributing/how-we-communicate.md
Normal 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.
|
||||
26
docs/contributing/index.md
Normal 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
|
||||
```
|
||||
40
docs/contributing/licensing.md
Normal 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.
|
||||
390
docs/contributing/presenting-visual-changes.md
Normal 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 |
|
||||
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
|
||||
|  |  |
|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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 |
|
||||
| --------------------------------------------------------------- | ------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|
||||
Here's an animated GIF approximating a comparison of the full-size images:
|
||||
|
||||

|
||||
|
||||
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 |
|
||||
| --------------------------------------------------------- | ------------------------------------------------------- |
|
||||
|  |  |
|
||||
|
||||
Now see if you can catch the change between the Before and After images in this 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 |
|
||||
| ----------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||
|  |  |
|
||||
|
||||
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:
|
||||
|
||||

|
||||
|
||||
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 |
|
||||
| --- | --- |
|
||||
|  | 
|
||||
```
|
||||
|
||||
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 |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
</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)
|
||||

|
||||
```
|
||||
|
||||
[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.
|
||||
157
docs/contributing/reporting-bugs.md
Normal 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.
|
||||
1
docs/contributing/reporting-security-vulnerabilities.md
Symbolic link
@@ -0,0 +1 @@
|
||||
../../SECURITY.md
|
||||
135
docs/contributing/review-process.md
Normal 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.
|
||||
168
docs/contributing/reviewable-prs.md
Normal 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
|
||||
111
docs/contributing/suggesting-features.md
Normal 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?
|
||||
94
docs/contributing/zulipbot-usage.md
Normal 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.
|
||||
249
docs/development/authentication.md
Normal 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
@@ -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>
|
||||
```
|
||||
87
docs/development/overview.md
Normal 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
@@ -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:
|
||||
|
||||

|
||||
|
||||
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=''
|
||||
```
|
||||
86
docs/development/request-remote.md
Normal 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
|
||||
295
docs/development/setup-advanced.md
Normal 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
|
||||
1056
docs/development/setup-recommended.md
Normal file
36
docs/development/setup/install-docker.md
Normal 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
|
||||
```
|
||||
121
docs/development/setup/shared-vagrant-errors.md
Normal 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.
|
||||
14
docs/development/setup/unix-troubleshoot.md
Normal 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
|
||||
```
|
||||
36
docs/development/setup/vagrant-halt.md
Normal 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).
|
||||
15
docs/development/setup/vagrant-rebuild.md
Normal 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`).
|
||||
11
docs/development/setup/vagrant-resume.md
Normal 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
|
||||
```
|
||||
64
docs/development/setup/vagrant-ssh.md
Normal 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:
|
||||
|
||||

|
||||
|
||||
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 ?)
|
||||
```
|
||||
25
docs/development/setup/vagrant-up-details.md
Normal 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.
|
||||
7
docs/development/setup/vagrant-up.md
Normal 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
|
||||
```
|
||||
18
docs/development/setup/vagrant-update.md
Normal 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.
|
||||
34
docs/development/setup/vscode-vagrant.md
Normal 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.
|
||||
196
docs/development/setup/winvm-troubleshoot.md
Normal 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
|
||||
```
|
||||
42
docs/development/setup/wsl-rebuild.md
Normal 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).
|
||||
75
docs/development/setup/wsl-troubleshoot.md
Normal 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.
|
||||
133
docs/development/test-install.md
Normal 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
@@ -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
@@ -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.
|
||||
653
docs/documentation/helpcenter.md
Normal 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
|
||||
13
docs/documentation/index.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Writing documentation
|
||||
|
||||
```{toctree}
|
||||
---
|
||||
maxdepth: 3
|
||||
---
|
||||
|
||||
overview
|
||||
helpcenter
|
||||
integrations
|
||||
api
|
||||
openapi
|
||||
```
|
||||
363
docs/documentation/integrations.md
Normal 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
|
||||
195
docs/documentation/openapi.md
Normal 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.
|
||||
```
|
||||
166
docs/documentation/overview.md
Normal 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
@@ -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
@@ -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
@@ -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
|
||||
45
docs/git/fixing-commits.md
Normal 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
@@ -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
@@ -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
@@ -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
|
||||
88
docs/git/reading-history.md
Normal 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
@@ -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
@@ -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
@@ -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.
|
||||
64
docs/git/the-git-difference.md
Normal 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
@@ -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
@@ -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
|
||||
50
docs/git/working-copies.md
Normal 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
@@ -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
|
||||
BIN
docs/images/architecture_simple.png
Normal file
|
After Width: | Height: | Size: 171 KiB |
1011
docs/images/architecture_simple.svg
Normal file
|
After Width: | Height: | Size: 43 KiB |
BIN
docs/images/browser-capture-after.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
docs/images/browser-capture-before.png
Normal file
|
After Width: | Height: | Size: 239 KiB |
BIN
docs/images/codeanywhere-settings.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
docs/images/codeanywhere-workspace.png
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
docs/images/firefox-mobile-scale-350x600.png
Normal file
|
After Width: | Height: | Size: 114 KiB |
BIN
docs/images/firefox-rwd-capture-medium.png
Normal file
|
After Width: | Height: | Size: 351 KiB |
BIN
docs/images/firefox-rwd-capture-mobile.png
Normal file
|
After Width: | Height: | Size: 263 KiB |
BIN
docs/images/fixed-cap-after.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
docs/images/fixed-cap-before.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
docs/images/fixed-capture-compare.gif
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
docs/images/freehand-cap-after.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
docs/images/freehand-cap-before.png
Normal file
|
After Width: | Height: | Size: 70 KiB |
BIN
docs/images/freehand-capture-compare.gif
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
docs/images/general-chat-after.gif
Normal file
|
After Width: | Height: | Size: 512 KiB |
BIN
docs/images/icon-prep-evenodd-broken-conversion.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
docs/images/icon-prep-evenodd-correct-preview.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
docs/images/icon-prep-stroke-broken-conversion.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/images/icon-prep-stroke-correct-preview.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/images/icon-prep-stroke-to-path-completed.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
docs/images/icon-prep-stroke-to-path-initial.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
docs/images/os-fixed-capture-box.png
Normal file
|
After Width: | Height: | Size: 216 KiB |
BIN
docs/images/shell-screenshot.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
docs/images/streams-1.png
Normal file
|
After Width: | Height: | Size: 8.2 KiB |
BIN
docs/images/zulip-admin-settings.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/images/zulip-circleci.gif
Normal file
|
After Width: | Height: | Size: 726 KiB |
BIN
docs/images/zulip-confirm-create-user.png
Normal file
|
After Width: | Height: | Size: 31 KiB |