Skip to content

Commit 47c34fd

Browse files
committed
run/docs: Move the default configuration file.
Switched from using '~/zuliprc' to 'XDG_CONFIG/zulip/.zuliprc'. Default value of XDG_CONFIG is .config.
1 parent 440a3fa commit 47c34fd

File tree

5 files changed

+59
-49
lines changed

5 files changed

+59
-49
lines changed

README.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,13 @@ though our commit log should be very readable.
160160

161161
## Running for the first time
162162

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

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

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

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

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

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

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

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

208208
## Configuration
209209

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

docs/FAQ.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ Yes. There are five supported themes:
160160
You can specify one of them on the command-line using the command-line option
161161
`--theme <theme>` or `-t <theme>` (where _theme_ is the name of the theme, or
162162
its alias).
163-
You can also specify it in the `zuliprc` file like this:
163+
You can also specify it in the `.zuliprc` file like this:
164164
```
165165
[zterm]
166166
theme=<theme_name>
@@ -195,7 +195,7 @@ This behavior was introduced in version 0.7.0, to avoid a message with many
195195
links having a very long footlinks list below it.
196196

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

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

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

274274
One session of Zulip Terminal represents a connection of one user to one Zulip
275275
server.
276-
Each session refers to a zuliprc file to identify how to connect to the server,
277-
so by running Zulip Terminal and specifying a different zuliprc file (using
276+
Each session refers to a .zuliprc file to identify how to connect to the server,
277+
so by running Zulip Terminal and specifying a different .zuliprc file (using
278278
`-c` or `--config-file`), you may connect to a different server.
279279
You might choose to do that after exiting from one Zulip Terminal session, or
280280
open another terminal and run it concurrently there.

tests/cli/test_run.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def factory(platform: str, python: str) -> List[str]:
165165

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

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

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

407407
expected_line = (
408408
"\x1b[91m"
409-
f"{expected_exception}: zuliprc could not be created "
410-
f"at {os.path.join(zuliprc_path, 'zuliprc')}"
409+
f"{expected_exception}: .zuliprc could not be created "
410+
f"at {Path(zuliprc_path) / '.config' / 'zulip' / '.zuliprc'}"
411411
"\x1b[0m"
412412
)
413413
assert lines[-1] == expected_line
@@ -416,7 +416,7 @@ def test_main_cannot_write_zuliprc_given_good_credentials(
416416
@pytest.fixture
417417
def parameterized_zuliprc(tmp_path: Path) -> Callable[[Dict[str, str]], str]:
418418
def func(config: Dict[str, str]) -> str:
419-
zuliprc_path = tmp_path / "zuliprc"
419+
zuliprc_path = tmp_path / ".zuliprc"
420420
with open(zuliprc_path, "w") as f:
421421
f.write("[api]\n\n") # minimal to avoid Exception
422422
f.write("[zterm]\n")
@@ -431,10 +431,10 @@ def func(config: Dict[str, str]) -> str:
431431
@pytest.mark.parametrize(
432432
"config_key, config_value, footlinks_output",
433433
[
434-
("footlinks", "disabled", "'0' specified in zuliprc file from footlinks."),
435-
("footlinks", "enabled", "'3' specified in zuliprc file from footlinks."),
436-
("maximum-footlinks", "3", "'3' specified in zuliprc file."),
437-
("maximum-footlinks", "0", "'0' specified in zuliprc file."),
434+
("footlinks", "disabled", "'0' specified in .zuliprc file from footlinks."),
435+
("footlinks", "enabled", "'3' specified in .zuliprc file from footlinks."),
436+
("maximum-footlinks", "3", "'3' specified in .zuliprc file."),
437+
("maximum-footlinks", "0", "'0' specified in .zuliprc file."),
438438
],
439439
ids=[
440440
"footlinks_disabled",
@@ -475,12 +475,12 @@ def test_successful_main_function_with_config(
475475
lines = captured.out.strip().split("\n")
476476
expected_lines = expected_platform_output + [
477477
"Loading with:",
478-
" theme 'zt_dark' specified in zuliprc file (by alias 'default').",
479-
" autohide setting 'autohide' specified in zuliprc file.",
478+
" theme 'zt_dark' specified in .zuliprc file (by alias 'default').",
479+
" autohide setting 'autohide' specified in .zuliprc file.",
480480
" exit confirmation setting 'enabled' specified from default config.",
481481
f" maximum footlinks value {footlinks_output}",
482-
" color depth setting '256' specified in zuliprc file.",
483-
" notify setting 'enabled' specified in zuliprc file.",
482+
" color depth setting '256' specified in .zuliprc file.",
483+
" notify setting 'enabled' specified in .zuliprc file.",
484484
]
485485
assert lines == expected_lines
486486

@@ -563,7 +563,7 @@ def test_exit_with_error(
563563
def test__write_zuliprc__success(
564564
tmp_path: Path, id: str = "id", key: str = "key", url: str = "url"
565565
) -> None:
566-
path = os.path.join(str(tmp_path), "zuliprc")
566+
path = Path(tmp_path) / ".zuliprc"
567567

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

@@ -583,11 +583,11 @@ def test__write_zuliprc__fail_file_exists(
583583
key: str = "key",
584584
url: str = "url",
585585
) -> None:
586-
path = os.path.join(str(tmp_path), "zuliprc")
586+
path = Path(tmp_path) / ".zuliprc"
587587

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

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

592592

593593
@pytest.mark.parametrize(
@@ -624,7 +624,7 @@ def test_show_error_if_loading_zuliprc_with_open_permissions(
624624
f"(it currently has permissions '{current_mode}')",
625625
"This can often be achieved with a command such as:",
626626
f" chmod og-rwx {minimal_zuliprc}",
627-
"Consider regenerating the [api] part of your zuliprc to ensure "
627+
"Consider regenerating the [api] part of your .zuliprc to ensure "
628628
"your account is secure."
629629
"\x1b[0m",
630630
]

tests/core/test_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def controller(self, mocker: MockerFixture) -> Controller:
4242
MODULE + ".urwid.MainLoop", return_value=mocker.Mock()
4343
)
4444

45-
self.config_file = "path/to/zuliprc"
45+
self.config_file = "path/to/.zuliprc"
4646
self.theme_name = "zt_dark"
4747
self.theme = generate_theme("zt_dark", 256)
4848
self.in_explore_mode = False

zulipterminal/cli/run.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
import sys
1111
import traceback
1212
from enum import Enum
13-
from os import path, remove
13+
from os import path
14+
from pathlib import Path
1415
from typing import Dict, List, NamedTuple, Optional, Tuple
1516

1617
import requests
@@ -32,7 +33,7 @@
3233

3334
class ConfigSource(Enum):
3435
DEFAULT = "from default config"
35-
ZULIPRC = "in zuliprc file"
36+
ZULIPRC = "in .zuliprc file"
3637
COMMANDLINE = "on command line"
3738

3839

@@ -131,7 +132,7 @@ def parse_args(argv: List[str]) -> argparse.Namespace:
131132
"-c",
132133
action="store",
133134
help="config file downloaded from your zulip "
134-
"organization (default: ~/zuliprc)",
135+
"organization (default: ~/.config/zulip/.zuliprc)",
135136
)
136137
parser.add_argument(
137138
"--theme",
@@ -264,9 +265,9 @@ def get_api_key(realm_url: str) -> Optional[Tuple[str, str, str]]:
264265
return None
265266

266267

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

305306

306307
def _write_zuliprc(
307-
to_path: str, *, login_id: str, api_key: str, server_url: str
308+
to_path: Path, *, login_id: str, api_key: str, server_url: str
308309
) -> str:
309310
"""
310-
Writes a zuliprc file, returning a non-empty error string on failure
311+
Writes a .zuliprc file, returning a non-empty error string on failure
311312
Only creates new private files; errors if file already exists
312313
"""
313314
try:
@@ -317,39 +318,39 @@ def _write_zuliprc(
317318
f.write(f"[api]\nemail={login_id}\nkey={api_key}\nsite={server_url}")
318319
return ""
319320
except FileExistsError:
320-
return f"zuliprc already exists at {to_path}"
321+
return f".zuliprc already exists at {to_path}"
321322
except OSError as ex:
322-
return f"{ex.__class__.__name__}: zuliprc could not be created at {to_path}"
323+
return f"{ex.__class__.__name__}: .zuliprc could not be created at {to_path}"
323324

324325

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

340-
mode = os.stat(zuliprc_path).st_mode
341+
mode = Path(zuliprc_path).stat().st_mode
341342
is_readable_by_group_or_others = mode & (stat.S_IRWXG | stat.S_IRWXO)
342343

343344
if is_readable_by_group_or_others:
344345
print(
345346
in_color(
346347
"red",
347-
"ERROR: Please ensure your zuliprc is NOT publicly accessible:\n"
348+
"ERROR: Please ensure your .zuliprc is NOT publicly accessible:\n"
348349
" {0}\n"
349350
"(it currently has permissions '{1}')\n"
350351
"This can often be achieved with a command such as:\n"
351352
" chmod og-rwx {0}\n"
352-
"Consider regenerating the [api] part of your zuliprc to ensure "
353+
"Consider regenerating the [api] part of your .zuliprc to ensure "
353354
"your account is secure.".format(zuliprc_path, stat.filemode(mode)),
354355
)
355356
)
@@ -360,9 +361,9 @@ def parse_zuliprc(zuliprc_str: str) -> Dict[str, SettingData]:
360361
try:
361362
res = zuliprc.read(zuliprc_path)
362363
if len(res) == 0:
363-
exit_with_error(f"Could not access zuliprc file at {zuliprc_path}")
364+
exit_with_error(f"Could not access .zuliprc file at {zuliprc_path}")
364365
except configparser.MissingSectionHeaderError:
365-
exit_with_error(f"Failed to parse zuliprc file at {zuliprc_path}")
366+
exit_with_error(f"Failed to parse .zuliprc file at {zuliprc_path}")
366367

367368
# Initialize with default settings
368369
settings = {
@@ -387,11 +388,20 @@ def list_themes() -> str:
387388
suffix += "[default theme]"
388389
text += f" {theme} {suffix}\n"
389390
return text + (
390-
"Specify theme in zuliprc file or override "
391+
"Specify theme in .zuliprc file or override "
391392
"using -t/--theme options on command line."
392393
)
393394

394395

396+
def xdg_config_home() -> Path:
397+
"""Return a Path corresponding to XDG_CONFIG_HOME."""
398+
if (value := os.environ.get("XDG_CONFIG_HOME")) and (
399+
environ_path := Path(value)
400+
).is_absolute():
401+
return environ_path
402+
return Path.home() / ".config"
403+
404+
395405
def main(options: Optional[List[str]] = None) -> None:
396406
"""
397407
Launch Zulip Terminal.
@@ -433,7 +443,7 @@ def main(options: Optional[List[str]] = None) -> None:
433443
if args.config_file:
434444
zuliprc_path = args.config_file
435445
else:
436-
zuliprc_path = "~/zuliprc"
446+
zuliprc_path = xdg_config_home() / "zulip" / ".zuliprc"
437447

438448
print(
439449
"Detected:"
@@ -518,7 +528,7 @@ def main(options: Optional[List[str]] = None) -> None:
518528
helper_text = (
519529
["Valid values are:"]
520530
+ [f" {option}" for option in valid_remaining_values]
521-
+ [f"Specify the {setting} option in zuliprc file."]
531+
+ [f"Specify the {setting} option in .zuliprc file."]
522532
)
523533
exit_with_error(
524534
"Invalid {} setting '{}' was specified {}.".format(

0 commit comments

Comments
 (0)