Skip to content

Commit 6035020

Browse files
authored
Migrate from ENV to YAML config (#59)
1 parent 036bfa4 commit 6035020

19 files changed

+749
-635
lines changed

.gitignore

+8-1
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ ENV/
129129
env.bak/
130130
venv.bak/
131131

132+
# Docker Compose ENV
133+
docker-compose.env
134+
135+
# Rodhaj configurations
136+
config.yml
137+
config.yaml
138+
132139
# Spyder project settings
133140
.spyderproject
134141
.spyproject
@@ -176,4 +183,4 @@ inv.yml
176183
test-scripts/
177184

178185
# Ruff cache
179-
.ruff_cache/
186+
.ruff_cache/

bot/cogs/utilities.py

+2-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from discord.ext import commands
1010
from discord.utils import format_dt
1111
from libs.utils import Embed, RoboContext, human_timedelta, is_docker
12+
from pygit2.enums import SortMode
1213
from rodhaj import Rodhaj
1314

1415

@@ -46,9 +47,7 @@ def format_commit(self, commit: pygit2.Commit) -> str:
4647
def get_last_commits(self, count: int = 5):
4748
repo = pygit2.Repository(".git")
4849
commits = list(
49-
itertools.islice(
50-
repo.walk(repo.head.target, pygit2.GIT_SORT_TOPOLOGICAL), count
51-
)
50+
itertools.islice(repo.walk(repo.head.target, SortMode.TOPOLOGICAL), count)
5251
)
5352
return "\n".join(self.format_commit(c) for c in commits)
5453

bot/launcher.py

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,24 @@
11
import os
22
import signal
3+
from pathlib import Path
34

45
import asyncpg
56
import discord
67
from aiohttp import ClientSession
7-
from environs import Env
88
from libs.utils import KeyboardInterruptHandler, RodhajLogger
9+
from libs.utils.config import RodhajConfig
910
from rodhaj import Rodhaj
1011

1112
if os.name == "nt":
1213
from winloop import run
1314
else:
1415
from uvloop import run
1516

16-
# Hope not to trip pyright
17-
env = Env()
18-
env.read_env()
17+
config_path = Path(__file__).parent / "config.yml"
18+
config = RodhajConfig(config_path)
1919

20-
TOKEN = env("TOKEN")
21-
DEV_MODE = env.bool("DEV_MODE", False)
22-
POSTGRES_URI = env("POSTGRES_URI")
20+
TOKEN = config["rodhaj"]["token"]
21+
POSTGRES_URI = config["postgres_uri"]
2322

2423
intents = discord.Intents.default()
2524
intents.message_content = True
@@ -31,7 +30,7 @@ async def main() -> None:
3130
dsn=POSTGRES_URI, min_size=25, max_size=25, command_timeout=30
3231
) as pool:
3332
async with Rodhaj(
34-
intents=intents, session=session, pool=pool, dev_mode=DEV_MODE
33+
config=config, intents=intents, session=session, pool=pool
3534
) as bot:
3635
bot.loop.add_signal_handler(signal.SIGTERM, KeyboardInterruptHandler(bot))
3736
bot.loop.add_signal_handler(signal.SIGINT, KeyboardInterruptHandler(bot))

bot/libs/utils/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
is_manager as is_manager,
55
is_mod as is_mod,
66
)
7+
from .config import RodhajConfig as RodhajConfig
78
from .context import GuildContext as GuildContext, RoboContext as RoboContext
89
from .embeds import (
910
Embed as Embed,

bot/libs/utils/config.py

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from pathlib import Path
2+
from typing import Any, Generic, Optional, TypeVar, Union, overload
3+
4+
import yaml
5+
6+
_T = TypeVar("_T")
7+
8+
9+
class RodhajConfig(Generic[_T]):
10+
def __init__(self, path: Path):
11+
self.path = path
12+
self._config: dict[str, Union[_T, Any]] = {}
13+
self.load_from_file()
14+
15+
def load_from_file(self) -> None:
16+
try:
17+
with open(self.path, "r") as f:
18+
self._config: dict[str, Union[_T, Any]] = yaml.safe_load(f.read())
19+
except FileNotFoundError:
20+
self._config = {}
21+
22+
@property
23+
def rodhaj(self) -> _T:
24+
return self._config.get("rodhaj", {})
25+
26+
@overload
27+
def get(self, key: Any) -> Optional[Union[_T, Any]]:
28+
...
29+
30+
@overload
31+
def get(self, key: Any, default: Any) -> Union[_T, Any]:
32+
...
33+
34+
def get(self, key: Any, default: Any = None) -> Optional[Union[_T, Any]]:
35+
"""Retrieves a config entry."""
36+
return self._config.get(str(key), default)
37+
38+
def __contains__(self, item: Any) -> bool:
39+
return str(item) in self._config
40+
41+
def __getitem__(self, item: Any) -> Union[_T, Any]:
42+
return self._config[str(item)]
43+
44+
def __len__(self) -> int:
45+
return len(self._config)
46+
47+
def all(self) -> dict[str, Union[_T, Any]]:
48+
return self._config

bot/migrations.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import asyncio
22
import datetime
3-
import os
43
import re
54
import traceback
65
from functools import wraps
@@ -10,15 +9,16 @@
109

1110
import asyncpg
1211
import click
13-
from dotenv import load_dotenv
12+
from libs.utils.config import RodhajConfig
1413
from typing_extensions import Self
1514

16-
load_dotenv()
15+
path = Path(__file__).parent / "config.yml"
16+
config = RodhajConfig(path)
1717

1818
BE = TypeVar("BE", bound=BaseException)
1919

2020
REVISION_FILE = re.compile(r"(?P<kind>V)(?P<version>[0-9]+)__(?P<description>.+).sql")
21-
POSTGRES_URI = os.environ["POSTGRES_URI"]
21+
POSTGRES_URI = config["postgres_uri"]
2222

2323
CREATE_MIGRATIONS_TABLE = """
2424
CREATE TABLE IF NOT EXISTS migrations (

bot/rodhaj.py

+6-5
Original file line numberDiff line numberDiff line change
@@ -19,23 +19,22 @@
1919
RodhajHelp,
2020
send_error_embed,
2121
)
22+
from libs.utils.config import RodhajConfig
2223
from libs.utils.reloader import Reloader
2324

2425
if TYPE_CHECKING:
2526
from cogs.tickets import Tickets
2627

27-
TRANSPROGRAMMER_GUILD_ID = 1183302385020436480
28-
2928

3029
class Rodhaj(commands.Bot):
3130
"""Main bot for Rodhaj"""
3231

3332
def __init__(
3433
self,
34+
config: RodhajConfig,
3535
intents: discord.Intents,
3636
session: ClientSession,
3737
pool: asyncpg.Pool,
38-
dev_mode: bool = False,
3938
*args,
4039
**kwargs,
4140
):
@@ -57,9 +56,11 @@ def __init__(
5756
self.session = session
5857
self.partial_config: Optional[PartialConfig] = None
5958
self.pool = pool
60-
self.transprogrammer_guild_id = TRANSPROGRAMMER_GUILD_ID
6159
self.version = str(VERSION)
62-
self._dev_mode = dev_mode
60+
self.transprogrammer_guild_id = config.rodhaj.get(
61+
"guild_id", 1183302385020436480
62+
)
63+
self._dev_mode = config.rodhaj.get("dev_mode", False)
6364
self._reloader = Reloader(self, Path(__file__).parent)
6465

6566
### Ticket related utils

config-example.yml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# --------------------------------- #
2+
# Rodhaj's Configuration file #
3+
# --------------------------------- #
4+
# This holds the configuration for Rodhaj. This file is not settable during runtime.
5+
# If you wish to change the values, change and save, and restart your bot
6+
7+
# Entries pertaining to Rodhaj are located here
8+
rodhaj:
9+
10+
# The token that Rodhaj will use.
11+
# Ensure that this is kept secret, as these are just like the password to your bot.
12+
token: ""
13+
14+
# The Guild ID that is associated with Rodhaj. This will be the guild that Rodhaj
15+
# will operate in, and will be the guild that Rodhaj will use to associate tickets and co.
16+
guild_id: 12345
17+
18+
# The dev mode for Rodhaj. This will enable certain features
19+
# such as an extension and library hot reloader in order to
20+
# make development easier
21+
# Note: Set this to false or remove this entry when running Rodhaj in production
22+
dev_mode: False
23+
24+
# The PostgreSQL connection URI that is used to connect to the database
25+
# The URI must be valid, and components will need to be quoted.
26+
# See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING
27+
postgres_uri: "postgresql://user:password@localhost:5432/user"

docker-compose-dev.yml

+5-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ name: "rodhaj-dev"
33
services:
44
postgres:
55
container_name: Rodhaj-Postgres
6-
image: postgres:15
6+
image: rodhaj-pg:dev-latest
7+
build:
8+
context: ./docker/pg
9+
dockerfile: Dockerfile
710
env_file:
8-
- bot/.env
11+
- docker-compose.env
912
volumes:
1013
- postgres_volume:/var/lib/postgresql/data
1114
ports:

docker/pg/Dockerfile

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM postgres:15
2+
COPY /init.sh /docker-entrypoint-initdb.d/

docker/pg/init.sh

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
set -e
3+
4+
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
5+
CREATE ROLE rodhaj WITH LOGIN PASSWORD "$RODHAJ_PASSWORD";
6+
CREATE DATABASE rodhaj OWNER rodhaj;
7+
EOSQL

docs/dev-guide/index.rst

+1
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ Rodhaj offers a developer guide so future developers can easily get started with
1010
intro
1111
contributing
1212
faq
13+
migrating-to-yaml

docs/dev-guide/intro.rst

+11-3
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ Setup
5353
poetry install \
5454
&& poetry run pre-commit install
5555
56-
3. Copy over the ``.env`` template over to the ``bot`` directory. Modify the values as appropriate.
56+
3. Copy over the ``config-example.yml`` template over to the ``bot`` directory. Modify the values as appropriate.
5757

5858
.. code-block:: bash
5959
60-
cp envs/dev.env bot/.env
60+
cp config-example.yml bot/config.yml
6161
6262
4. Run the SQL migrations
6363

@@ -91,7 +91,15 @@ Using Docker
9191
^^^^^^^^^^^^
9292

9393
If you decide to use Docker to run the local PostgreSQL server, then a
94-
pre-built Docker Compose file is provided. Simply run the following:
94+
pre-built Docker Compose file is provided. Setup instructions are as follows:
95+
96+
1. Copy ``envs/docker.env`` to ``docker-compose.env`` within the root of the repo. Modify as appropriate.
97+
98+
.. code-block:: bash
99+
100+
cp envs/docker.env docker-compose.env
101+
102+
2. Run the following command to start the PostgreSQL server
95103

96104
.. code-block:: bash
97105

docs/dev-guide/migrating-to-yaml.rst

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
=================
2+
Migrating to YAML
3+
=================
4+
5+
Rodhaj now uses an new config format, YAML.
6+
Previously, the project utilized an ENV file for configuration, but
7+
due to issues such as the inability for better structured data and others,
8+
the project has migrated to YAML.
9+
10+
The details of the motivation can be found in
11+
`PR #59 <https://github.com/transprogrammer/rodhaj/pull/59>`_.
12+
13+
.. note::
14+
15+
- ``*.yaml`` files are ignored and not read by the config loader. Ensure that your YAML file uses the ``*.yml`` file extension.
16+
- The config file is stored in the same location as the ``.env`` file. It will always be read under ``bot/config.yml``.
17+
18+
Changes
19+
=======
20+
21+
Grouped entries
22+
---------------
23+
24+
The bot token, and others are merged into one mapping. This mapping key is denoted
25+
by ``rodhaj``. Any and all configuration options in relation to the bot can be found
26+
under that mapping.
27+
28+
To illustrate, an example comparison of the old and new config format is shown below:
29+
30+
``.env``
31+
32+
.. code-block:: bash
33+
34+
TOKEN=...
35+
DEV_MODE=False
36+
37+
``config.yml``
38+
39+
.. code-block:: yaml
40+
41+
rodhaj:
42+
token: ...
43+
dev_mode: False # This key-value pair can also be removed to disable the dev mode feature
44+
45+
Rodhaj's Guild ID can now be set via the config
46+
-----------------------------------------------
47+
48+
Instead of hardcoding the Guild ID into the code, to allow for flexibity in development,
49+
the Guild ID can now be set via the config. This is acheived through the ``guild_id`` entry as illustrated.
50+
51+
.. code-block:: yaml
52+
53+
rodhaj:
54+
guild_id: 1234567890 # This ID is the guild that is associated with Rodhaj for operations
55+
56+
PostgreSQL Connection URI
57+
-------------------------
58+
59+
The PostgreSQL URI is mostly the same, but is now under the ``postgres_uri`` entry.

envs/dev.env

-16
This file was deleted.

envs/docker.env

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Docker Compose ENV file
2+
# This is used for setting up the PostgreSQL server
3+
# found within the docker compose file
4+
5+
# Docker PostgreSQL root credientials
6+
# This account has root access, so please ensure that this is a secure password
7+
POSTGRES_USER=postgres
8+
POSTGRES_PASSWORD=password
9+
10+
# Account for the rodhaj user on PostgreSQL
11+
# This is used to create the internal user, rodhaj
12+
RODHAJ_PASSWORD=password

0 commit comments

Comments
 (0)