Files
redmine-reporter/docs/CONFIG.md
Кокос Артем Николаевич 3accb1212c
Some checks failed
checks / checks (3.10) (push) Has been cancelled
checks / checks (3.11) (push) Has been cancelled
checks / checks (3.12) (push) Has been cancelled
checks / checks (3.13) (push) Has been cancelled
fix: use OS trust store for TLS verification via truststore
Regression from #62: verify_ssl true used to resolve to the system CA
bundle path, so corporate CAs installed in the OS worked; after the
unification true became requests' default (certifi), breaking setups
with a corporate CA in the system store.

Now verify_ssl true injects truststore, so requests verifies against
the OS trust store on any platform. verify_ssl false / custom CA path
behavior is unchanged. Tests mock truststore via an autouse fixture to
keep the pytest process free of global ssl mutation.

Refs #62
2026-07-17 18:39:53 +07:00

26 KiB
Raw Permalink Blame History

Настройка redmine-reporter

Оглавление

Источники конфигурации

Приоритет, от высшего к низшему:

CLI-флаги  >  переменные окружения  >  .env  >  YAML  >  кодовые дефолты

Нюанс с override при автозагрузке .env и --config — см. Сосуществование .env и YAML.

Если значение не задано на верхнем уровне, берётся уровень ниже. .env и YAML работают одновременно — можно оставить оба, можно удалить .env после миграции.

YAML-конфиг

Основной файл: ~/.config/redmine-reporter/config.yml.

Создаётся с правами 0600 (владелец: чтение/запись, остальные: доступ запрещён). Директория ~/.config/redmine-reporter/с правами 0700.

Если права файла шире 0600, при запуске выводится предупреждение.

Структура

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 (по умолчанию) Проверка по системному хранилищу CA операционной системы (через truststore) — корпоративные CA, добавленные в ОС, работают без настройки
false Проверка отключена — при запуске выводится предупреждение о риске MITM
строка с путём, например /etc/ssl/my-ca.pem Путь к собственному CA-bundle, передаётся в requests как есть

До версии с унификацией verify_ssl: true подставлял захардкоженный путь /etc/ssl/certs/ca-certificates.crt, который существует только в Debian/Ubuntu. Теперь true в YAML и REDMINE_VERIFY=true в env работают одинаково — оба включают проверку по системному хранилищу CA на любой ОС (через truststore), без привязки к конкретному пути.

period.precision — точность периода

Определяет, как вычисляется следующий период после фиксации:

  • date (по умолчанию) — период с точностью до дня. Следующий запуск (после --commit) начинается со следующего дня.
  • datetime — период с точностью до секунды. При повторном запуске time entries, у которых created_on или updated_on раньше last_used.to, исключаются: запись сохраняется, только если оба поля не раньше cutoff (AND-логика). Записи без дат (оба поля отсутствуют) сохраняются; если задано только одно поле, проверяется оно. Это предотвращает дублирование при отправке отчёта внутри рабочего дня. Cutoff применяется всегда, когда вычислен (precision: datetime и задан last_used.to) — в том числе при явном --date.

last_used.from / last_used.to записываются автоматически при --commit. Вручную редактировать не требуется.

period.default_to — необязательное окончание периода

Если period.default_to не задан, а period.default_from задан, инструмент использует сегодняшнюю дату в качестве конца периода.

period:
  default_from: "2026-07-01"
  # default_to отсутствует → конец периода = сегодня

Это предотвращает устаревание периода, когда отчёт генерируется автоматически (--send, --commit) без явного --date.

Аналогично работает DEFAULT_TO_DATE: если переменная не задана, а DEFAULT_FROM_DATE задана, конец периода = сегодня.

Обратное не работает: period.default_to без period.default_fromDEFAULT_TO_DATE без DEFAULT_FROM_DATE) игнорируется — период определяется остальными источниками.

Период по умолчанию — текущий месяц

Если период не задан ни одним из источников (--date, DEFAULT_FROM_DATE, period.default_from), отчёт строится за текущий месяц: начало периода — 1-е число текущего месяца, конец — сегодняшняя дата. Период вычисляется на момент запуска и не хранится в коде или конфиге.

--date — формат диапазона

Флаг --date принимает два формата:

  • Даты: YYYY-MM-DD--YYYY-MM-DD (например, 2026-06-01--2026-06-30).
  • Datetime с секундами: YYYY-MM-DDTHH:MM:SS--YYYY-MM-DDTHH:MM:SS (например, 2026-06-30T09:00:00--2026-06-30T12:00:00).

Обе границы должны быть в одном формате — смешанная точность отвергается. Начало позже конца (start > end) — ошибка.

--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 на использованный период.

Нюанс: при --commit YAML-файл перезаписывается целиком через yaml.dump — пользовательские комментарии и ручное форматирование в файле теряются.

Примеры:

# Июнь 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.

Пример конфигурации:

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 (таймаут 30 секунд). STARTTLS включается только при email.smtp.tls: true; аутентификация — только если задан email.smtp.user.

Файл отчёта сохраняется до попытки отправки — при ошибке SMTP файл остаётся на диске, данные не теряются.

Ошибки SMTP:

  • Нет соединения → "Не удалось подключиться к SMTP-серверу host:port"
  • Неверный логин/пароль → "Ошибка аутентификации SMTP. Проверьте логин и пароль."
  • Таймаут → "Таймаут соединения с SMTP-сервером."
  • Другая ошибка → "Ошибка отправки письма: <детали>"

Все ошибки выводятся в stderr, код возврата 1.

Примеры:

# Отправить отчёт за июнь (сохранится по шаблону 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

Неизвестные плейсхолдеры остаются в имени как есть.

Примеры:

# По умолчанию
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} — при загрузке оно заменяется на значение переменной окружения:

redmine:
  api_key: ${REDMINE_API_KEY}

email:
  smtp:
    password: ${SMTP_PASSWORD}

Это безопаснее, чем хранить секреты plaintext в YAML. Однако plaintext-секреты не запрещены — если вписать api_key: "abc123" напрямую, система примет. Права 0600 — основная защита.

report — настройки содержимого отчёта

Секция управляет тем, что попадает в сгенерированный отчёт.

Поле Тип По умолчанию Описание
no_time bool false Не включать затраченное время в файл отчёта
status_translation map[str,str] {} Переопределение/дополнение перевода статусов

report.no_time применяется только в автоматических режимах (--commit, --send). При ручном --output YAML-настройка игнорируется — там работает только CLI-флаг --no-time. CLI-флаг --no-time всегда имеет приоритет над YAML.

Примеры:

report:
  no_time: true
# Автоматический режим: время не выводится
redmine-reporter --commit

# Ручной режим: report.no_time игнорируется, время выводится
redmine-reporter --output report.odt

# Ручной режим с явным флагом: время не выводится
redmine-reporter --output report.odt --no-time

report.status_translation — перевод статусов

По умолчанию статусы Redmine переводятся встроенным словарём (NewВ работе, ClosedЗакрыто и т.д.). Секция report.status_translation переопределяет отдельные переводы и/или добавляет новые статусы; не указанные здесь статусы переводятся встроенным словарём, а совсем неизвестные выводятся как есть.

report:
  status_translation:
    "New": "Новая"
    "Wait Release": "Ожидает релиза"
    "Custom Status": "Кастомный статус"

Разрешение выходного пути

Функция 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

Быстрый старт

# 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, report, email).
  • В конец файла дописывает закомментированный пример report.status_translation.
  • Секреты (REDMINE_API_KEY, SMTP_PASSWORD) записывает как ${VAR}, если переменная существует, иначе — пустая строка.
  • Создаёт файл с правами 0600, директорию — с 0700.

Флаги миграции

Флаг Назначение
--init-config Создать YAML и выйти
--init-config --force Перезаписать существующий YAML
--config-path PATH Путь к YAML-конфигу: загрузка при запуске, запись при --init-config и --commit (по умолчанию ~/.config/redmine-reporter/config.yml)

Если DEFAULT_TO_DATE не задана, а DEFAULT_FROM_DATE задана, сгенерированный YAML будет содержать пустое default_to, и при запуске инструмент использует сегодняшнюю дату.

Проверка после миграции

# Запустить без .env в текущей директории
cd /tmp
redmine-reporter --date 2026-06-01--2026-06-30

Если отработал — YAML-конфиг читается корректно. Если REDMINE_URL is required — проверь права:

ls -la ~/.config/redmine-reporter/
# config.yml должно быть -rw------- (600)
# директория должна быть drwx------ (700)

Сосуществование .env и YAML

Можно оставить оба источника. .env имеет более высокий приоритет, чем YAML:

.env значения переопределяют YAML, если заданы
YAML работает как базовый слой для всего, что не в .env

Автозагрузка .env (без --config) не перебивает реальные переменные окружения (override=False). Исключение — --config FILE: указанный .env загружается с override=True и перебивает переменные окружения (но не CLI-флаги).

Это safe — если с YAML что-то пойдёт не так, просто положи .env обратно.

Откат

rm ~/.config/redmine-reporter/config.yml

.env (legacy)

Для обратной совместимости .env продолжает работать без изменений.

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 или временных переопределений.