feat(config): add TOML config file support

- Add config.py with load_config(), find_default_config(), get_token(),
  and get_map_path()
- Auto-discover timesheets.toml in cwd; override with --config flag
- Priority: CLI flag > config file > env var / cwd default
- Add timesheets.example.toml as a committed reference template
- Add timesheets.toml to .gitignore to prevent accidental secret leakage
- Document config file format in AGENTS.md
This commit is contained in:
Jef Roosens 2026-05-22 10:47:05 +02:00
parent 267ad5b1b5
commit 29698b1241
Signed by: Jef Roosens
GPG key ID: 119385BCAA005C21
6 changed files with 183 additions and 5 deletions

View file

@ -3,6 +3,7 @@ import os
import sys
from datetime import date
from .config import find_default_config, get_map_path, get_token, load_config
from .output import print_summary, write_csv
from .parser import aggregate_rows, filter_rows_by_date, parse_document
from .projects import load_project_map
@ -32,6 +33,11 @@ def build_parser() -> argparse.ArgumentParser:
),
)
parser.add_argument(
"--config",
help="Path to a TOML config file. Defaults to timesheets.toml in the current working directory if it exists.",
default=None,
)
parser.add_argument(
"--token",
help="Joplin API token. Falls back to the JOPLIN_TOKEN environment variable.",
@ -68,8 +74,8 @@ def build_parser() -> argparse.ArgumentParser:
return parser
def _resolve_token(args: argparse.Namespace) -> str:
token = args.token or os.environ.get("JOPLIN_TOKEN")
def _resolve_token(args: argparse.Namespace, config: dict) -> str:
token = args.token or get_token(config) or os.environ.get("JOPLIN_TOKEN")
if not token:
print(
"Error: Joplin API token required. "
@ -83,6 +89,10 @@ def _resolve_token(args: argparse.Namespace) -> str:
def main() -> None:
args = build_parser().parse_args()
# Load config file: explicit --config flag, else auto-discover timesheets.toml
config_path = args.config if args.config is not None else find_default_config()
config = load_config(config_path)
if args.day is None:
target_date = date.today()
else:
@ -101,7 +111,7 @@ def main() -> None:
# Late import so joppy is only required when --joplin is used
from .joplin import fetch_week_note
token = _resolve_token(args)
token = _resolve_token(args, config)
try:
content = fetch_week_note(token, target_date)
except RuntimeError as e:
@ -129,8 +139,8 @@ def main() -> None:
aggregated = aggregate_rows(rows)
# Resolve project map: explicit --map flag, else project_map.json in cwd
map_path = args.map
# Resolve project map: CLI flag > config file > project_map.json in cwd
map_path = args.map or get_map_path(config)
if map_path is None:
default_map = os.path.join(os.getcwd(), "project_map.json")
if os.path.exists(default_map):