YAML verify_ssl: true раньше подставлял захардкоженный путь /etc/ssl/certs/ca-certificates.crt, а REDMINE_VERIFY=true — bool True. Путь отсутствует на части дистрибутивов, семантика источников различалась. Теперь едино для YAML и env: - true (bool/строка) → True: стандартная проверка TLS средствами requests; - false (bool/строка) → False (+ сохраняется warning из #57); - иная строка → путь к CA-bundle как есть. - дефолт (значение не задано) → True вместо захардкоженного пути. Существующие тесты на путь-от-true переписаны под новую семантику (изменение поведения): test_verify_ssl_true_returns_default_ca_path → test_verify_ssl_true_returns_true. Closes #62
450 lines
20 KiB
Markdown
450 lines
20 KiB
Markdown
# Настройка redmine-reporter
|
||
|
||
## Источники конфигурации
|
||
|
||
Приоритет, от высшего к низшему:
|
||
|
||
```
|
||
CLI-флаги > переменные окружения > .env > YAML > кодовые дефолты
|
||
```
|
||
|
||
Если значение не задано на верхнем уровне, берётся уровень ниже. `.env` и YAML
|
||
работают одновременно — можно оставить оба, можно удалить `.env` после миграции.
|
||
|
||
## YAML-конфиг
|
||
|
||
Основной файл: `~/.config/redmine-reporter/config.yml`.
|
||
|
||
Создаётся с правами `0600` (владелец: чтение/запись, остальные: доступ запрещён).
|
||
Директория `~/.config/redmine-reporter/` — с правами `0700`.
|
||
|
||
Если права файла шире `0600`, при запуске выводится предупреждение.
|
||
|
||
### Структура
|
||
|
||
```yaml
|
||
redmine:
|
||
url: https://red.eltex.loc
|
||
api_key: ${REDMINE_API_KEY}
|
||
author: "Кокос А.А."
|
||
verify_ssl: true
|
||
|
||
period:
|
||
precision: date
|
||
default_from: "2026-06-01"
|
||
# default_to можно не указывать — тогда конец периода будет сегодня
|
||
default_to: "2026-06-30"
|
||
dynamic: false
|
||
last_used:
|
||
from: "2026-06-30T09:00:00"
|
||
to: "2026-06-30T12:00:00"
|
||
|
||
output:
|
||
dir: ~/reports
|
||
filename: "{author}_{from}_{to}.{ext}"
|
||
default_format: xlsx
|
||
|
||
report:
|
||
no_time: false
|
||
|
||
email:
|
||
html: false
|
||
smtp:
|
||
host: smtp.example.com
|
||
port: 587
|
||
user: bot@example.com
|
||
password: ${SMTP_PASSWORD}
|
||
tls: true
|
||
from: bot@example.com
|
||
to:
|
||
- boss@example.com
|
||
cc: []
|
||
bcc: []
|
||
subject: "Отчёт {author} за {period}"
|
||
body_text: "Во вложении отчёт."
|
||
attach: true
|
||
```
|
||
|
||
### `redmine.verify_ssl` — проверка TLS-сертификата
|
||
|
||
Управляет проверкой TLS-сертификата Redmine. Семантика едина с переменной
|
||
окружения `REDMINE_VERIFY`:
|
||
|
||
| Значение | Поведение |
|
||
|---|---|
|
||
| `true` (по умолчанию) | Стандартная проверка TLS средствами requests (системные CA / certifi) |
|
||
| `false` | Проверка отключена — при запуске выводится предупреждение о риске MITM |
|
||
| строка с путём, например `/etc/ssl/my-ca.pem` | Путь к собственному CA-bundle, передаётся в requests как есть |
|
||
|
||
До версии с унификацией `verify_ssl: true` подставлял захардкоженный путь
|
||
`/etc/ssl/certs/ca-certificates.crt`, который отсутствует на части
|
||
дистрибутивов. Теперь `true` в YAML и `REDMINE_VERIFY=true` в env работают
|
||
одинаково — оба включают стандартную проверку без привязки к конкретному пути.
|
||
|
||
### `period.precision` — точность периода
|
||
|
||
Определяет, как вычисляется следующий период после фиксации:
|
||
|
||
- `date` (по умолчанию) — период с точностью до дня. Следующий запуск (после `--commit`) начинается со следующего дня.
|
||
- `datetime` — период с точностью до секунды. При повторном запуске time entries с `created_on` и `updated_on` ранее `last_used.to` исключаются (AND-логика: запись исключается только если **оба** поля раньше cutoff). Это предотвращает дублирование при отправке отчёта внутри рабочего дня.
|
||
|
||
`last_used.from` / `last_used.to` записываются автоматически при `--commit`. Вручную редактировать не требуется.
|
||
|
||
### `period.default_to` — необязательное окончание периода
|
||
|
||
Если `period.default_to` не задан, а `period.default_from` задан, инструмент
|
||
использует сегодняшнюю дату в качестве конца периода.
|
||
|
||
```yaml
|
||
period:
|
||
default_from: "2026-07-01"
|
||
# default_to отсутствует → конец периода = сегодня
|
||
```
|
||
|
||
Это предотвращает устаревание периода, когда отчёт генерируется автоматически
|
||
(`--send`, `--commit`) без явного `--date`.
|
||
|
||
Аналогично работает `DEFAULT_TO_DATE`: если переменная не задана, а
|
||
`DEFAULT_FROM_DATE` задана, конец периода = сегодня.
|
||
|
||
### `--commit` — автофиксация периода
|
||
|
||
Флаг `--commit` сохраняет использованный период в YAML-конфиг, чтобы следующий запуск автоматически начинался с нового периода.
|
||
|
||
**Что делает:**
|
||
|
||
1. Генерирует отчёт как обычно.
|
||
2. Сохраняет отчёт в файл:
|
||
- Если указан `--output` — по явному пути.
|
||
- Если `--output` не указан — по шаблону из `output.dir` / `output.filename`.
|
||
3. Записывает `period.last_used.from` / `period.last_used.to` в YAML-конфиг.
|
||
4. При `period.precision: datetime` сохраняет текущий момент времени (ISO с секундами).
|
||
5. При `period.precision: date` сохраняет даты периода.
|
||
|
||
**Поведение `period.dynamic`:**
|
||
|
||
- `dynamic: true` — следующий запуск (без `--date`) вычисляет период от `last_used`:
|
||
- Полный календарный месяц → следующий полный месяц.
|
||
- Произвольный диапазон → та же длительность, начиная со дня после `last_used.to`.
|
||
- `dynamic: false` — `--commit` перезаписывает `default_from`/`default_to` на использованный период.
|
||
|
||
**Примеры:**
|
||
|
||
```bash
|
||
# Июнь 2026 → следующий запуск (без --date) → июль 2026
|
||
redmine-reporter --date 2026-06-01--2026-06-30 --commit
|
||
|
||
# Произвольный диапазон: 15-20 июня → следующий запуск → 21-26 июня
|
||
redmine-reporter --date 2026-06-15--2026-06-20 --commit
|
||
|
||
# С datetime-точностью: повторный запуск не дублирует записи
|
||
redmine-reporter --commit
|
||
```
|
||
|
||
### `email` — настройка отправки по почте
|
||
|
||
Секция `email` используется флагом `--send`. Если секция не настроена или `smtp.host`
|
||
пуст, `--send` завершится с ошибкой «Email не настроен».
|
||
|
||
**Все поля:**
|
||
|
||
| Поле | Тип | По умолчанию | Описание |
|
||
|---|---|---|---|
|
||
| `smtp.host` | строка | `""` | Адрес SMTP-сервера |
|
||
| `smtp.port` | число | `587` | Порт SMTP |
|
||
| `smtp.user` | строка | `""` | Логин для аутентификации |
|
||
| `smtp.password` | строка | `""` | Пароль (рекомендуется `${SMTP_PASSWORD}`) |
|
||
| `smtp.tls` | bool | `true` | Использовать STARTTLS |
|
||
| `from` | строка | `""` | Адрес отправителя |
|
||
| `to` | список | `[]` | Основные получатели |
|
||
| `cc` | список | `[]` | Копия |
|
||
| `bcc` | список | `[]` | Скрытая копия (не отображается в заголовках письма) |
|
||
| `subject` | строка | `"Отчёт {author} за {period}"` | Тема письма |
|
||
| `body_text` | строка | `"Во вложении отчёт."` | Текст письма (plain text) |
|
||
| `attach` | bool | `true` | Прикреплять файл отчёта. Если `false` — только текст |
|
||
| `html` | bool | `false` | Добавить HTML-версию тела письма (`multipart/alternative`) |
|
||
|
||
**Подстановки в `subject` и `body_text`:**
|
||
|
||
| Плейсхолдер | Описание | Пример |
|
||
|---|---|---|
|
||
| `{author}` | Имя автора из конфига или `--author` | `Кокос А.А.` |
|
||
| `{period}` | Строка диапазона дат | `2026-06-01--2026-06-30` |
|
||
|
||
**MIME-тип вложения** определяется по расширению файла:
|
||
|
||
| Расширение | MIME-тип |
|
||
|---|---|
|
||
| `.xlsx` | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet` |
|
||
| `.odt` | `application/vnd.oasis.opendocument.text` |
|
||
| `.csv` | `text/csv` |
|
||
| `.html` | `text/html` |
|
||
| `.json` | `application/json` |
|
||
| `.md` | `text/markdown` |
|
||
|
||
Неизвестное расширение → `application/octet-stream`.
|
||
|
||
**Пример конфигурации:**
|
||
|
||
```yaml
|
||
email:
|
||
html: false
|
||
smtp:
|
||
host: smtp.example.com
|
||
port: 587
|
||
user: bot@example.com
|
||
password: ${SMTP_PASSWORD}
|
||
tls: true
|
||
from: bot@example.com
|
||
to:
|
||
- boss@example.com
|
||
- team-lead@example.com
|
||
cc:
|
||
- manager@example.com
|
||
bcc: []
|
||
subject: "Отчёт {author} за {period}"
|
||
body_text: "Во вложении отчёт за период {period}."
|
||
attach: true
|
||
```
|
||
|
||
### `--send` — отправка отчёта по email
|
||
|
||
Флаг `--send` отправляет сгенерированный отчёт через SMTP сразу после сохранения
|
||
в файл. Требует настроенную секцию `email` в YAML-конфиге.
|
||
|
||
**Что делает:**
|
||
|
||
1. Генерирует отчёт как обычно.
|
||
2. Сохраняет отчёт в файл:
|
||
- Если указан `--output` — по явному пути.
|
||
- Если `--output` не указан — по шаблону из `output.dir` / `output.filename`.
|
||
3. Формирует MIME-письмо:
|
||
- Тема, plain-text тело и вложение (если `attach: true`).
|
||
- При `email.html: true` — дополнительно HTML-версия тела (`multipart/alternative`), сгенерированная из таблицы отчёта.
|
||
4. Отправляет через SMTP с TLS (таймаут 30 секунд).
|
||
|
||
**Файл отчёта сохраняется до попытки отправки** — при ошибке SMTP файл остаётся
|
||
на диске, данные не теряются.
|
||
|
||
**Ошибки SMTP:**
|
||
|
||
- Нет соединения → `"Не удалось подключиться к SMTP-серверу host:port"`
|
||
- Неверный логин/пароль → `"Ошибка аутентификации SMTP. Проверьте логин и пароль."`
|
||
- Таймаут → `"Таймаут соединения с SMTP-сервером."`
|
||
- Другая ошибка → `"Ошибка отправки письма: <детали>"`
|
||
|
||
Все ошибки выводятся в stderr, код возврата 1.
|
||
|
||
**Примеры:**
|
||
|
||
```bash
|
||
# Отправить отчёт за июнь (сохранится по шаблону output.filename)
|
||
redmine-reporter --date 2026-06-01--2026-06-30 --send
|
||
|
||
# С явным путём
|
||
redmine-reporter --date 2026-06-01--2026-06-30 --output ~/report.xlsx --send
|
||
|
||
# Отправить и зафиксировать период
|
||
redmine-reporter --date 2026-06-01--2026-06-30 --send --commit
|
||
```
|
||
|
||
**Совместимость с другими флагами:**
|
||
|
||
| Комбинация | Поведение |
|
||
|---|---|
|
||
| `--send` | Сохранить по шаблону → отправить |
|
||
| `--send --output X` | Сохранить в X → отправить |
|
||
| `--send --commit` | Сохранить → отправить → зафиксировать период |
|
||
| `--send` с `email.html: true` | Письмо с plain-text + HTML-таблицей |
|
||
| `--send` без `email` в конфиге | Ошибка, exit 1 |
|
||
| `--send` при ошибке SMTP | Файл сохранён, ошибка в stderr, exit 1 |
|
||
|
||
### `output` — путь и имя файла по умолчанию
|
||
|
||
Секция управляет тем, куда и с каким именем сохраняется отчёт, когда `--output` не содержит полного пути.
|
||
|
||
**Правила разрешения `--output`:**
|
||
|
||
| Аргумент `--output` | Поведение |
|
||
|---|---|
|
||
| `/полный/путь/report.xlsx` | Используется как есть, конфиг игнорируется |
|
||
| `xlsx` (bare format: `xlsx`, `odt`, `csv`, `md`, `html`, `json`) | Путь = `output.dir` + `output.filename`, расширение = bare format |
|
||
| `/tmp/report` (путь без расширения) | Дописывается `.default_format` → `/tmp/report.xlsx` |
|
||
|
||
**Шаблон имени файла:**
|
||
|
||
`output.filename` поддерживает подстановки:
|
||
|
||
| Плейсхолдер | Описание | Пример |
|
||
|---|---|---|
|
||
| `{author}` | Имя автора (пробелы → `_`) | `Кокос_А.А.` |
|
||
| `{from}` | Начало периода, `YYYY-MM-DD` | `2026-06-01` |
|
||
| `{to}` | Конец периода, `YYYY-MM-DD` | `2026-06-30` |
|
||
| `{date}` | Конец периода, `DD_MM_YYYY` | `30_06_2026` |
|
||
| `{ext}` | Расширение файла без точки | `xlsx` |
|
||
|
||
Неизвестные плейсхолдеры остаются в имени как есть.
|
||
|
||
Примеры:
|
||
|
||
```yaml
|
||
# По умолчанию
|
||
filename: "{author}_{from}_{to}.{ext}" # → Кокос_А.А._2026-06-01_2026-06-30.xlsx
|
||
|
||
# Русский формат даты
|
||
filename: "отчёт_{date}.{ext}" # → отчёт_30_06_2026.xlsx
|
||
|
||
# Без автора, только диапазон
|
||
filename: "report_{from}--{to}.{ext}" # → report_2026-06-01--2026-06-30.xlsx
|
||
```
|
||
|
||
### Подстановка переменных окружения
|
||
|
||
В любом строковом значении YAML можно использовать `${VAR}` — при загрузке
|
||
оно заменяется на значение переменной окружения:
|
||
|
||
```yaml
|
||
redmine:
|
||
api_key: ${REDMINE_API_KEY}
|
||
|
||
email:
|
||
smtp:
|
||
password: ${SMTP_PASSWORD}
|
||
```
|
||
|
||
Это безопаснее, чем хранить секреты plaintext в YAML. Однако plaintext-секреты
|
||
**не запрещены** — если вписать `api_key: "abc123"` напрямую, система примет.
|
||
Права `0600` — основная защита.
|
||
|
||
### `report` — настройки содержимого отчёта
|
||
|
||
Секция управляет тем, что попадает в сгенерированный отчёт.
|
||
|
||
| Поле | Тип | По умолчанию | Описание |
|
||
|---|---|---|---|
|
||
| `no_time` | bool | `false` | Не включать затраченное время в файл отчёта |
|
||
|
||
`report.no_time` применяется только в автоматических режимах (`--commit`, `--send`).
|
||
При ручном `--output` YAML-настройка игнорируется — там работает только CLI-флаг `--no-time`.
|
||
CLI-флаг `--no-time` всегда имеет приоритет над YAML.
|
||
|
||
**Примеры:**
|
||
|
||
```yaml
|
||
report:
|
||
no_time: true
|
||
```
|
||
|
||
```bash
|
||
# Автоматический режим: время не выводится
|
||
redmine-reporter --commit
|
||
|
||
# Ручной режим: report.no_time игнорируется, время выводится
|
||
redmine-reporter --output report.odt
|
||
|
||
# Ручной режим с явным флагом: время не выводится
|
||
redmine-reporter --output report.odt --no-time
|
||
```
|
||
|
||
## Разрешение выходного пути
|
||
|
||
Функция `resolve_output_path()` определяет итоговый путь к файлу:
|
||
|
||
1. `--output` не указан → консольный вывод.
|
||
2. `--output xlsx` (bare format) → путь формируется как `output.dir / output.filename` с подстановкой `{ext}` = bare format и дат из периода.
|
||
3. `--output /path/report` (без расширения) → дописывается `.output.default_format`.
|
||
4. `--output /path/report.csv` (с расширением) → используется как есть.
|
||
5. `--output /path/report.xyz` (неизвестное расширение) → используется как есть, форматтер выбирается по расширению.
|
||
|
||
## Миграция с `.env` на YAML
|
||
|
||
### Быстрый старт
|
||
|
||
```bash
|
||
# 1. Генерируем YAML из текущего .env
|
||
redmine-reporter --init-config
|
||
|
||
# 2. Проверяем, что создалось
|
||
cat ~/.config/redmine-reporter/config.yml
|
||
|
||
# 3. Редактируем под себя (шаблон имени, период, etc.)
|
||
vim ~/.config/redmine-reporter/config.yml
|
||
```
|
||
|
||
### Что делает `--init-config`
|
||
|
||
- Читает текущие значения из `.env` и переменных окружения.
|
||
- Формирует YAML со всеми секциями (`redmine`, `period`, `output`, `email`).
|
||
- Секреты (`REDMINE_API_KEY`, `SMTP_PASSWORD`) записывает как `${VAR}`, если
|
||
переменная существует, иначе — пустая строка.
|
||
- Создаёт файл с правами `0600`, директорию — с `0700`.
|
||
|
||
### Флаги миграции
|
||
|
||
| Флаг | Назначение |
|
||
|---|---|
|
||
| `--init-config` | Создать YAML и выйти |
|
||
| `--init-config --force` | Перезаписать существующий YAML |
|
||
| `--config-path PATH` | Сохранить YAML по указанному пути (по умолчанию `~/.config/redmine-reporter/config.yml`) |
|
||
|
||
Если `DEFAULT_TO_DATE` не задана, а `DEFAULT_FROM_DATE` задана, сгенерированный
|
||
YAML будет содержать пустое `default_to`, и при запуске инструмент использует
|
||
сегодняшнюю дату.
|
||
|
||
### Проверка после миграции
|
||
|
||
```bash
|
||
# Запустить без .env в текущей директории
|
||
cd /tmp
|
||
redmine-reporter --date 2026-06-01--2026-06-30
|
||
```
|
||
|
||
Если отработал — YAML-конфиг читается корректно. Если `REDMINE_URL is required` —
|
||
проверь права:
|
||
|
||
```bash
|
||
ls -la ~/.config/redmine-reporter/
|
||
# config.yml должно быть -rw------- (600)
|
||
# директория должна быть drwx------ (700)
|
||
```
|
||
|
||
### Сосуществование `.env` и YAML
|
||
|
||
Можно оставить оба источника. `.env` имеет **более высокий приоритет**, чем YAML:
|
||
|
||
```
|
||
.env значения переопределяют YAML, если заданы
|
||
YAML работает как базовый слой для всего, что не в .env
|
||
```
|
||
|
||
Это safe — если с YAML что-то пойдёт не так, просто положи `.env` обратно.
|
||
|
||
### Откат
|
||
|
||
```bash
|
||
rm ~/.config/redmine-reporter/config.yml
|
||
```
|
||
|
||
## `.env` (legacy)
|
||
|
||
Для обратной совместимости `.env` продолжает работать без изменений.
|
||
|
||
```ini
|
||
REDMINE_URL=https://red.eltex.loc
|
||
REDMINE_API_KEY=your-api-key
|
||
REDMINE_AUTHOR=Кокос А.А.
|
||
DEFAULT_FROM_DATE=2026-06-01
|
||
DEFAULT_TO_DATE=2026-06-30
|
||
```
|
||
|
||
Если ни `.env`, ни YAML не заданы — используются кодовые дефолты (текущий месяц
|
||
как период, стандартная проверка TLS, пустой автор).
|
||
|
||
## Безопасность
|
||
|
||
- YAML-конфиг: права `0600`, директория `0700`.
|
||
- Права шире `0600` → warning в stderr при каждом запуске.
|
||
- Секреты рекомендуется хранить через `${VAR}`, а не plaintext.
|
||
- `.env` **не рекомендуется** для постоянных настроек — оставьте его только
|
||
для CI/CD или временных переопределений.
|