Compare commits

7 Commits
Author SHA1 Message Date
github-actions[bot] 3c57ea64c1 chore(main): release 0.4.2 (#28)
Release / release-please (push) Failing after 2m29s
Release / publish (push) Has been skipped
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-07-27 09:26:31 +10:00
Danielandopenhands cf65198825 fix: accept CSV telegram ID config (#27)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-07-27 09:21:52 +10:00
DanielandRenovate Bot 20928f4ab6 chore(deps): update googleapis/release-please-action action to v5 (#26)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2026-07-27 09:11:10 +10:00
DanielandRenovate Bot ffbeef5207 chore(deps): update docker/setup-qemu-action action to v4 (#25)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2026-07-27 08:11:21 +10:00
DanielandRenovate Bot 57c9f7554f chore(deps): update docker/setup-buildx-action action to v4 (#24)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2026-07-27 07:11:11 +10:00
DanielandRenovate Bot 09e1d5d4b0 chore(deps): update docker/metadata-action action to v6 (#23)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2026-07-27 06:11:43 +10:00
DanielandRenovate Bot 2cd5930e2b chore(deps): update docker/login-action action to v4 (#22)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2026-07-27 05:11:43 +10:00
8 changed files with 126 additions and 19 deletions
+4 -4
View File
@@ -31,15 +31,15 @@ jobs:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Log in to GHCR - name: Log in to GHCR
# Only log in when pushing (not on PRs from forks) # Only log in when pushing (not on PRs from forks)
if: github.event_name == 'push' if: github.event_name == 'push'
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -47,7 +47,7 @@ jobs:
- name: Docker metadata - name: Docker metadata
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: ghcr.io/${{ github.repository }} images: ghcr.io/${{ github.repository }}
tags: | tags: |
+5 -5
View File
@@ -23,7 +23,7 @@ jobs:
release_created: ${{ steps.release.outputs.release_created }} release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }} tag_name: ${{ steps.release.outputs.tag_name }}
steps: steps:
- uses: googleapis/release-please-action@v4 - uses: googleapis/release-please-action@v5
id: release id: release
with: with:
release-type: python release-type: python
@@ -36,13 +36,13 @@ jobs:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Set up QEMU - name: Set up QEMU
uses: docker/setup-qemu-action@v3 uses: docker/setup-qemu-action@v4
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v4
- name: Log in to GHCR - name: Log in to GHCR
uses: docker/login-action@v3 uses: docker/login-action@v4
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -50,7 +50,7 @@ jobs:
- name: Docker metadata - name: Docker metadata
id: meta id: meta
uses: docker/metadata-action@v5 uses: docker/metadata-action@v6
with: with:
images: ghcr.io/${{ github.repository }} images: ghcr.io/${{ github.repository }}
tags: | tags: |
+1 -1
View File
@@ -263,7 +263,7 @@ Note: Pre-commit will run these checks automatically on commit, so you usually d
**Configuration issues:** **Configuration issues:**
1. Check `.env` file syntax (should be `KEY=value`) 1. Check `.env` file syntax (should be `KEY=value`)
2. For lists, use JSON format: `STEWARD__TELEGRAM__ALLOWED_USER_IDS='[123, 456]'` 2. For lists, prefer CSV format: `STEWARD__TELEGRAM__ALLOWED_USER_IDS=123,456` (JSON lists are also supported)
3. New format variables use `STEWARD__SECTION__KEY` pattern 3. New format variables use `STEWARD__SECTION__KEY` pattern
4. Check `CONFIGURATION.md` for examples 4. Check `CONFIGURATION.md` for examples
+7
View File
@@ -1,5 +1,12 @@
# Changelog # Changelog
## [0.4.2](https://github.com/djw4/steward/compare/v0.4.1...v0.4.2) (2026-07-26)
### Bug Fixes
* accept CSV telegram ID config ([#27](https://github.com/djw4/steward/issues/27)) ([cf65198](https://github.com/djw4/steward/commit/cf651988251986caf4748ff30c388e6b3d9f6b5c))
## [0.4.1](https://github.com/djw4/steward/compare/v0.4.0...v0.4.1) (2026-07-26) ## [0.4.1](https://github.com/djw4/steward/compare/v0.4.0...v0.4.1) (2026-07-26)
+5 -5
View File
@@ -141,8 +141,8 @@ telegram:
**Environment Variables:** **Environment Variables:**
- `STEWARD__TELEGRAM__BOT_TOKEN` - Telegram bot token (REQUIRED) - `STEWARD__TELEGRAM__BOT_TOKEN` - Telegram bot token (REQUIRED)
- `STEWARD__TELEGRAM__ALLOWED_USER_IDS` - JSON list: `[123,456,789]` - `STEWARD__TELEGRAM__ALLOWED_USER_IDS` - Comma-separated or JSON list: `123,456,789`
- `STEWARD__TELEGRAM__GROUP_IDS` - JSON list: `[-1005308306472]` - `STEWARD__TELEGRAM__GROUP_IDS` - Comma-separated or JSON list: `-1005308306472`
### OpenAI Section ### OpenAI Section
@@ -211,10 +211,10 @@ STEWARD__OPENAI__MODEL="gpt-4-turbo"
STEWARD__ANALYSIS__CRON_HOUR="9" STEWARD__ANALYSIS__CRON_HOUR="9"
``` ```
For list values, use JSON format: For list values, use comma-separated integers. JSON lists are also accepted for compatibility:
```bash ```bash
STEWARD__TELEGRAM__ALLOWED_USER_IDS='[123456789, 987654321]' STEWARD__TELEGRAM__ALLOWED_USER_IDS=123456789,987654321
STEWARD__TELEGRAM__GROUP_IDS='[-1005308306472]' STEWARD__TELEGRAM__GROUP_IDS=-1005308306472
``` ```
## Best Practices ## Best Practices
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "steward" name = "steward"
version = "0.4.1" version = "0.4.2"
description = "A long-running, AI-assisted personal operations platform" description = "A long-running, AI-assisted personal operations platform"
readme = "README.md" readme = "README.md"
requires-python = ">=3.12" requires-python = ">=3.12"
+40 -3
View File
@@ -8,16 +8,50 @@ Supports configuration via:
Environment variables take precedence over config file values. Environment variables take precedence over config file values.
""" """
import json
import logging import logging
import os import os
from pathlib import Path from pathlib import Path
from typing import Any
from omegaconf import OmegaConf from omegaconf import OmegaConf
from pydantic import BaseModel, ConfigDict, Field from pydantic import BaseModel, ConfigDict, Field, field_validator
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _parse_int_list(value: Any) -> Any:
if isinstance(value, int):
return [value]
if not isinstance(value, str):
return value
text = value.strip()
if not text:
return []
if len(text) >= 2 and text[0] == text[-1] and text[0] in {"'", '"'}:
text = text[1:-1].strip()
if not text:
return []
if text.startswith("["):
try:
parsed = json.loads(text)
except json.JSONDecodeError:
pass
else:
if isinstance(parsed, list):
return parsed
try:
return [int(item.strip().strip("'\"")) for item in text.split(",") if item.strip()]
except ValueError as exc:
raise ValueError(
"expected a comma-separated list of integers or a JSON integer list"
) from exc
class TelegramConfig(BaseModel): class TelegramConfig(BaseModel):
"""Telegram bot configuration.""" """Telegram bot configuration."""
@@ -25,6 +59,11 @@ class TelegramConfig(BaseModel):
allowed_user_ids: list[int] = Field(default_factory=list, description="Allowed user IDs") allowed_user_ids: list[int] = Field(default_factory=list, description="Allowed user IDs")
group_ids: list[int] = Field(default_factory=list, description="Allowed group/channel IDs") group_ids: list[int] = Field(default_factory=list, description="Allowed group/channel IDs")
@field_validator("allowed_user_ids", "group_ids", mode="before")
@classmethod
def parse_int_list(cls, value: Any) -> Any:
return _parse_int_list(value)
class OpenAIConfig(BaseModel): class OpenAIConfig(BaseModel):
"""OpenAI/LLM configuration.""" """OpenAI/LLM configuration."""
@@ -186,8 +225,6 @@ def _load_config_from_env() -> dict:
# Try to parse value as JSON first (for lists, etc.) # Try to parse value as JSON first (for lists, etc.)
if isinstance(cfg[section], dict): if isinstance(cfg[section], dict):
try: try:
import json
cfg[section][setting_key] = json.loads(value) cfg[section][setting_key] = json.loads(value)
except (json.JSONDecodeError, ValueError): except (json.JSONDecodeError, ValueError):
cfg[section][setting_key] = value cfg[section][setting_key] = value
+63
View File
@@ -0,0 +1,63 @@
"""Tests for Steward configuration loading."""
import pytest
from steward import config as config_module
from steward.config import TelegramConfig, reload_settings
@pytest.fixture(autouse=True)
def reset_settings_singleton():
config_module._settings_instance = None
yield
config_module._settings_instance = None
def test_telegram_config_accepts_csv_id_lists():
config = TelegramConfig(
allowed_user_ids="8861600138, 123456789",
group_ids="-5308306472,-1005308306472",
)
assert config.allowed_user_ids == [8861600138, 123456789]
assert config.group_ids == [-5308306472, -1005308306472]
def test_telegram_config_preserves_list_id_values():
config = TelegramConfig(
allowed_user_ids=[8861600138, 123456789],
group_ids=[-5308306472, -1005308306472],
)
assert config.allowed_user_ids == [8861600138, 123456789]
assert config.group_ids == [-5308306472, -1005308306472]
def test_settings_accepts_csv_id_lists_from_env(monkeypatch):
monkeypatch.setenv("STEWARD__TELEGRAM__ALLOWED_USER_IDS", "8861600138,123456789")
monkeypatch.setenv("STEWARD__TELEGRAM__GROUP_IDS", "-5308306472,-1005308306472")
settings = reload_settings()
assert settings.telegram_allowed_user_ids == [8861600138, 123456789]
assert settings.telegram_group_ids == [-5308306472, -1005308306472]
def test_settings_accepts_single_id_values_from_env(monkeypatch):
monkeypatch.setenv("STEWARD__TELEGRAM__ALLOWED_USER_IDS", "8861600138")
monkeypatch.setenv("STEWARD__TELEGRAM__GROUP_IDS", "-5308306472")
settings = reload_settings()
assert settings.telegram_allowed_user_ids == [8861600138]
assert settings.telegram_group_ids == [-5308306472]
def test_settings_accepts_quoted_json_id_lists_from_env(monkeypatch):
monkeypatch.setenv("STEWARD__TELEGRAM__ALLOWED_USER_IDS", "'[8861600138]'")
monkeypatch.setenv("STEWARD__TELEGRAM__GROUP_IDS", "'[-5308306472]'")
settings = reload_settings()
assert settings.telegram_allowed_user_ids == [8861600138]
assert settings.telegram_group_ids == [-5308306472]