Skip to content

Commit

Permalink
run/docs: Move the default configuration file.
Browse files Browse the repository at this point in the history
Switched from using '~/zuliprc' to
'XDG_CONFIG/zulip/.zuliprc'.
Default value of XDG_CONFIG is .config.
  • Loading branch information
Niloth-p committed May 19, 2024
1 parent 440a3fa commit 47c34fd
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 49 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,13 +160,13 @@ though our commit log should be very readable.

## Running for the first time

Upon first running `zulip-term` it looks for a `zuliprc` file, by default in
your home directory, which contains the details to log into a Zulip server.
Upon first running `zulip-term` it looks for a `.zuliprc` file, by default in
your configuration directory `.config/zulip/.zuliprc`, which contains the details to log into a Zulip server.

If it doesn't find this file, you have two options:

1. `zulip-term` will prompt you for your server, email and password, and create
a `zuliprc` file for you in that location
a `.zuliprc` file for you in that location

**NOTE:** If you use Google, Github or another external authentication to
access your Zulip organization then you likely won't have a password set and
Expand All @@ -179,8 +179,8 @@ If it doesn't find this file, you have two options:
your account (eg: https://chat.zulip.org/accounts/password/reset/).

2. Each time you run `zulip-term`, you can specify the path to an alternative
`zuliprc` file using the `-c` or `--config-file` options, eg. `$ zulip-term -c
/path/to/zuliprc`
`.zuliprc` file using the `-c` or `--config-file` options, eg. `$ zulip-term -c
/path/to/.zuliprc`

A `.zuliprc` file corresponding to your account on a particular Zulip server
can be downloaded via Web or Desktop applications connected to that server.
Expand All @@ -196,7 +196,7 @@ If it doesn't find this file, you have two options:
bots you have set up, though with correspondingly limited permissions.

**NOTE:** If your server uses self-signed certificates or an insecure
connection, you will need to add extra options to the `zuliprc` file manually -
connection, you will need to add extra options to the `.zuliprc` file manually -
see the documentation for the [Zulip python module](https://pypi.org/project/zulip/).

We suggest running `zulip-term` using the `-e` or `--explore` option (in
Expand All @@ -207,7 +207,7 @@ to get the hang of things.

## Configuration

The `zuliprc` file contains two sections:
The `.zuliprc` file contains two sections:
- an `[api]` section with information required to connect to your Zulip server
- a `[zterm]` section with configuration specific to `zulip-term`

Expand Down
8 changes: 4 additions & 4 deletions docs/FAQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ Yes. There are five supported themes:
You can specify one of them on the command-line using the command-line option
`--theme <theme>` or `-t <theme>` (where _theme_ is the name of the theme, or
its alias).
You can also specify it in the `zuliprc` file like this:
You can also specify it in the `.zuliprc` file like this:
```
[zterm]
theme=<theme_name>
Expand Down Expand Up @@ -195,7 +195,7 @@ This behavior was introduced in version 0.7.0, to avoid a message with many
links having a very long footlinks list below it.

This can be controlled by giving a different value to the new
`maximum-footlinks` setting in the `zuliprc` file, which defaults to 3.
`maximum-footlinks` setting in the `.zuliprc` file, which defaults to 3.

### Why can I not see the full text of a footlink?

Expand Down Expand Up @@ -273,8 +273,8 @@ when you intend to return to look at them properly later.

One session of Zulip Terminal represents a connection of one user to one Zulip
server.
Each session refers to a zuliprc file to identify how to connect to the server,
so by running Zulip Terminal and specifying a different zuliprc file (using
Each session refers to a .zuliprc file to identify how to connect to the server,
so by running Zulip Terminal and specifying a different .zuliprc file (using
`-c` or `--config-file`), you may connect to a different server.
You might choose to do that after exiting from one Zulip Terminal session, or
open another terminal and run it concurrently there.
Expand Down
34 changes: 17 additions & 17 deletions tests/cli/test_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def factory(platform: str, python: str) -> List[str]:

@pytest.fixture
def minimal_zuliprc(tmp_path: Path) -> str:
zuliprc_path = tmp_path / "zuliprc"
zuliprc_path = tmp_path / ".zuliprc"
with open(zuliprc_path, "w") as f:
f.write("[api]") # minimal to avoid Exception
os.chmod(zuliprc_path, 0o600)
Expand Down Expand Up @@ -389,7 +389,7 @@ def test_main_cannot_write_zuliprc_given_good_credentials(
tmp_path, unusable_path = unreadable_dir

# This is default base path to use
zuliprc_path = os.path.join(str(tmp_path), path_to_use)
zuliprc_path = str(Path(tmp_path) / path_to_use)
monkeypatch.setenv("HOME", zuliprc_path)

# Give some arbitrary input and fake that it's always valid
Expand All @@ -406,8 +406,8 @@ def test_main_cannot_write_zuliprc_given_good_credentials(

expected_line = (
"\x1b[91m"
f"{expected_exception}: zuliprc could not be created "
f"at {os.path.join(zuliprc_path, 'zuliprc')}"
f"{expected_exception}: .zuliprc could not be created "
f"at {Path(zuliprc_path) / '.config' / 'zulip' / '.zuliprc'}"
"\x1b[0m"
)
assert lines[-1] == expected_line
Expand All @@ -416,7 +416,7 @@ def test_main_cannot_write_zuliprc_given_good_credentials(
@pytest.fixture
def parameterized_zuliprc(tmp_path: Path) -> Callable[[Dict[str, str]], str]:
def func(config: Dict[str, str]) -> str:
zuliprc_path = tmp_path / "zuliprc"
zuliprc_path = tmp_path / ".zuliprc"
with open(zuliprc_path, "w") as f:
f.write("[api]\n\n") # minimal to avoid Exception
f.write("[zterm]\n")
Expand All @@ -431,10 +431,10 @@ def func(config: Dict[str, str]) -> str:
@pytest.mark.parametrize(
"config_key, config_value, footlinks_output",
[
("footlinks", "disabled", "'0' specified in zuliprc file from footlinks."),
("footlinks", "enabled", "'3' specified in zuliprc file from footlinks."),
("maximum-footlinks", "3", "'3' specified in zuliprc file."),
("maximum-footlinks", "0", "'0' specified in zuliprc file."),
("footlinks", "disabled", "'0' specified in .zuliprc file from footlinks."),
("footlinks", "enabled", "'3' specified in .zuliprc file from footlinks."),
("maximum-footlinks", "3", "'3' specified in .zuliprc file."),
("maximum-footlinks", "0", "'0' specified in .zuliprc file."),
],
ids=[
"footlinks_disabled",
Expand Down Expand Up @@ -475,12 +475,12 @@ def test_successful_main_function_with_config(
lines = captured.out.strip().split("\n")
expected_lines = expected_platform_output + [
"Loading with:",
" theme 'zt_dark' specified in zuliprc file (by alias 'default').",
" autohide setting 'autohide' specified in zuliprc file.",
" theme 'zt_dark' specified in .zuliprc file (by alias 'default').",
" autohide setting 'autohide' specified in .zuliprc file.",
" exit confirmation setting 'enabled' specified from default config.",
f" maximum footlinks value {footlinks_output}",
" color depth setting '256' specified in zuliprc file.",
" notify setting 'enabled' specified in zuliprc file.",
" color depth setting '256' specified in .zuliprc file.",
" notify setting 'enabled' specified in .zuliprc file.",
]
assert lines == expected_lines

Expand Down Expand Up @@ -563,7 +563,7 @@ def test_exit_with_error(
def test__write_zuliprc__success(
tmp_path: Path, id: str = "id", key: str = "key", url: str = "url"
) -> None:
path = os.path.join(str(tmp_path), "zuliprc")
path = Path(tmp_path) / ".zuliprc"

error_message = _write_zuliprc(path, api_key=key, server_url=url, login_id=id)

Expand All @@ -583,11 +583,11 @@ def test__write_zuliprc__fail_file_exists(
key: str = "key",
url: str = "url",
) -> None:
path = os.path.join(str(tmp_path), "zuliprc")
path = Path(tmp_path) / ".zuliprc"

error_message = _write_zuliprc(path, api_key=key, server_url=url, login_id=id)

assert error_message == "zuliprc already exists at " + path
assert error_message == ".zuliprc already exists at " + str(path)


@pytest.mark.parametrize(
Expand Down Expand Up @@ -624,7 +624,7 @@ def test_show_error_if_loading_zuliprc_with_open_permissions(
f"(it currently has permissions '{current_mode}')",
"This can often be achieved with a command such as:",
f" chmod og-rwx {minimal_zuliprc}",
"Consider regenerating the [api] part of your zuliprc to ensure "
"Consider regenerating the [api] part of your .zuliprc to ensure "
"your account is secure."
"\x1b[0m",
]
Expand Down
2 changes: 1 addition & 1 deletion tests/core/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def controller(self, mocker: MockerFixture) -> Controller:
MODULE + ".urwid.MainLoop", return_value=mocker.Mock()
)

self.config_file = "path/to/zuliprc"
self.config_file = "path/to/.zuliprc"
self.theme_name = "zt_dark"
self.theme = generate_theme("zt_dark", 256)
self.in_explore_mode = False
Expand Down
50 changes: 30 additions & 20 deletions zulipterminal/cli/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
import sys
import traceback
from enum import Enum
from os import path, remove
from os import path
from pathlib import Path
from typing import Dict, List, NamedTuple, Optional, Tuple

import requests
Expand All @@ -32,7 +33,7 @@

class ConfigSource(Enum):
DEFAULT = "from default config"
ZULIPRC = "in zuliprc file"
ZULIPRC = "in .zuliprc file"
COMMANDLINE = "on command line"


Expand Down Expand Up @@ -131,7 +132,7 @@ def parse_args(argv: List[str]) -> argparse.Namespace:
"-c",
action="store",
help="config file downloaded from your zulip "
"organization (default: ~/zuliprc)",
"organization (default: ~/.config/zulip/.zuliprc)",
)
parser.add_argument(
"--theme",
Expand Down Expand Up @@ -264,9 +265,9 @@ def get_api_key(realm_url: str) -> Optional[Tuple[str, str, str]]:
return None


def fetch_zuliprc(zuliprc_path: str) -> None:
def fetch_zuliprc(zuliprc_path: Path) -> None:
print(
f"{in_color('red', f'zuliprc file was not found at {zuliprc_path}')}"
f"{in_color('red', f'.zuliprc file was not found at {zuliprc_path}')}"
f"\nPlease enter your credentials to login into your Zulip organization."
f"\n"
f"\nNOTE: The {in_color('blue', 'Zulip server URL')}"
Expand Down Expand Up @@ -304,10 +305,10 @@ def fetch_zuliprc(zuliprc_path: str) -> None:


def _write_zuliprc(
to_path: str, *, login_id: str, api_key: str, server_url: str
to_path: Path, *, login_id: str, api_key: str, server_url: str
) -> str:
"""
Writes a zuliprc file, returning a non-empty error string on failure
Writes a .zuliprc file, returning a non-empty error string on failure
Only creates new private files; errors if file already exists
"""
try:
Expand All @@ -317,39 +318,39 @@ def _write_zuliprc(
f.write(f"[api]\nemail={login_id}\nkey={api_key}\nsite={server_url}")
return ""
except FileExistsError:
return f"zuliprc already exists at {to_path}"
return f".zuliprc already exists at {to_path}"
except OSError as ex:
return f"{ex.__class__.__name__}: zuliprc could not be created at {to_path}"
return f"{ex.__class__.__name__}: .zuliprc could not be created at {to_path}"


def parse_zuliprc(zuliprc_str: str) -> Dict[str, SettingData]:
zuliprc_path = path.expanduser(zuliprc_str)
zuliprc_path = Path(zuliprc_str).expanduser()
while not path.exists(zuliprc_path):
try:
fetch_zuliprc(zuliprc_path)
# Invalid user inputs (e.g. pressing arrow keys) may cause ValueError
except (OSError, ValueError):
# Remove zuliprc file if created.
if path.exists(zuliprc_path):
remove(zuliprc_path)
if zuliprc_path.exists():
zuliprc_path.unlink()
print(in_color("red", "\nInvalid Credentials, Please try again!\n"))
except EOFError:
# Assume that the user pressed Ctrl+D and continue the loop
print("\n")

mode = os.stat(zuliprc_path).st_mode
mode = Path(zuliprc_path).stat().st_mode
is_readable_by_group_or_others = mode & (stat.S_IRWXG | stat.S_IRWXO)

if is_readable_by_group_or_others:
print(
in_color(
"red",
"ERROR: Please ensure your zuliprc is NOT publicly accessible:\n"
"ERROR: Please ensure your .zuliprc is NOT publicly accessible:\n"
" {0}\n"
"(it currently has permissions '{1}')\n"
"This can often be achieved with a command such as:\n"
" chmod og-rwx {0}\n"
"Consider regenerating the [api] part of your zuliprc to ensure "
"Consider regenerating the [api] part of your .zuliprc to ensure "
"your account is secure.".format(zuliprc_path, stat.filemode(mode)),
)
)
Expand All @@ -360,9 +361,9 @@ def parse_zuliprc(zuliprc_str: str) -> Dict[str, SettingData]:
try:
res = zuliprc.read(zuliprc_path)
if len(res) == 0:
exit_with_error(f"Could not access zuliprc file at {zuliprc_path}")
exit_with_error(f"Could not access .zuliprc file at {zuliprc_path}")
except configparser.MissingSectionHeaderError:
exit_with_error(f"Failed to parse zuliprc file at {zuliprc_path}")
exit_with_error(f"Failed to parse .zuliprc file at {zuliprc_path}")

# Initialize with default settings
settings = {
Expand All @@ -387,11 +388,20 @@ def list_themes() -> str:
suffix += "[default theme]"
text += f" {theme} {suffix}\n"
return text + (
"Specify theme in zuliprc file or override "
"Specify theme in .zuliprc file or override "
"using -t/--theme options on command line."
)


def xdg_config_home() -> Path:
"""Return a Path corresponding to XDG_CONFIG_HOME."""
if (value := os.environ.get("XDG_CONFIG_HOME")) and (
environ_path := Path(value)
).is_absolute():
return environ_path
return Path.home() / ".config"


def main(options: Optional[List[str]] = None) -> None:
"""
Launch Zulip Terminal.
Expand Down Expand Up @@ -433,7 +443,7 @@ def main(options: Optional[List[str]] = None) -> None:
if args.config_file:
zuliprc_path = args.config_file
else:
zuliprc_path = "~/zuliprc"
zuliprc_path = xdg_config_home() / "zulip" / ".zuliprc"

print(
"Detected:"
Expand Down Expand Up @@ -518,7 +528,7 @@ def main(options: Optional[List[str]] = None) -> None:
helper_text = (
["Valid values are:"]
+ [f" {option}" for option in valid_remaining_values]
+ [f"Specify the {setting} option in zuliprc file."]
+ [f"Specify the {setting} option in .zuliprc file."]
)
exit_with_error(
"Invalid {} setting '{}' was specified {}.".format(
Expand Down

0 comments on commit 47c34fd

Please sign in to comment.