Вступление
Я стараюсь содержать свою домашнюю директорию (~) в чистоте и порядке. Когда в процессе изучения Terraform я увидел новые файлы .terraformrc и папку .terraform.d, стало понятно: этому не место среди личных документов и настроек. Конфигурационные файлы должны жить там, где им и положено — в .config.
Проверка соответсвующей страницы в Arch Wiki показала, что Terraform отнесён к категории Hardcoded — то есть не поддерживает спецификацию XDG Base Directory. В прикреплённом issue на GitHub #15389 обсуждение длится с 2017 года. И при этом проблема актуальна до сих пор.
Я решил, что пришло время стать тем самым “энтузиастом с навыками программирования” и исправить ситуацию — не только для себя, но и для всего open-source сообщества.
Изучение опыта других проектов
Прежде чем лезть в код, я полез изучать, как подобную задачу решали другие проекты. В рамках этого изучения я ставил себе 2 цели:
- Как правильно реализовать спецификацию.
- Как организовать плавный переход, чтобы не сломать существующие workflows.
Что я вынес из изучения:
- zoxide (Rust)
В их PR я увидел:- Использование библиотеки
dirsдля работы с XDG. - Стратегию fallback: сначала проверяем новый путь (XDG), затем старый (
~/.config). - Важную пометку:
FIXME: fallback to old database location; remove in next breaking version. Это ключевой момент — старый путь можно убрать только в мажорной версии, ломающей обратную совместимость.
- Использование библиотеки
- lf (Go)
Файловый менеджерlf, которым я пользуюсь, уже поддерживает XDG. Но он не делает fallback к старому расположению — просто использует новый стандарт. Это точно не то, как я хочу реализовтаь. Вырезка кода отсюда:u, err := user.Current() config := cmp.Or( os.Getenv("LF_CONFIG_HOME"), os.Getenv("XDG_CONFIG_HOME"), filepath.Join(gUser.HomeDir, ".config"), ) - rclone (Go)
Это для меня эталонный пример! В коммите реализована именно та логика, которую я искал:- Проверить конфиг в XDG-пути.
- Если нет — проверить в старом месте (
~/.config/rclone). - Если и там нет — использовать XDG для создания нового.
Формирование стратегии
Главный принцип: “Не навреди”.
Terraform — инструмент автоматизации, он работает в CI/CD, скриптах, у тысяч людей на сотнях тысяч серверов.
Резкое изменение поведения может сломать процессы и создать невидимые инциденты по всему миру.
И выбросить вникуда сотни-тысяч человеко-часов инженеров по всему миру.
Это точно не то, чего я хочу.
Моя стратегия:
- Приоритет XDG: если пользователь самостоятельно создал папку
~/.config/terraform— используем её. - Fallback на старый путь: если XDG-папки нет — продолжаем работать по-старому, сохраняя полную обратную совместимость.
- Создание новых файлов: если конфига нет вообще — продолжаем работать по старому (это случай инициализации terraform в docker контейнере внутри CI/CD процесса).
Почему именно так?
Многие гайды провайдеров (например, Yandex Cloud) явно указывают путь ~/.terraformrc. Резкий уход от этого сломает документацию и привычные workflows.
Реализация
Важное открытие: не нужно изобретать велосипед. В Go уже есть готовая функция os.UserConfigDir(), которая корректно возвращает путь к конфигам с учётом ОС (Linux, macOS, Windows).
Логика проверки XDG:
func configDirXDG() (string, error) {
configDir, err := os.UserConfigDir()
if err != nil {
return "", err
}
terraformConfigDir := filepath.Join(configDir, "terraform")
// Проверяем, существует ли папка
if _, err := os.Stat(terraformConfigDir); err != nil {
return "", err
}
return terraformConfigDir, nil
}
Проверяем именно существование папки, а не файлов. Если папка создана — значит, пользователь сознательно перешёл на XDG.
Тестирование:
Используем временные директории и t.Setenv("HOME", tmpdir) для изоляции тестов. Пример:
t.Run("empty home of user", func(t *testing.T) {
tmpdir := t.TempDir()
t.Setenv(envHome, tmpdir)
got, err := configDir()
// Ожидаем старый путь ~/.terraform.d
want := filepath.Join(tmpdir, ".terraform.d")
if got != want {
t.Errorf("configDir() = %v, want %v", got, want)
}
})
Итог
Пул-реквест #38046 готов и ожидает ревью. В нём:
- Добавлена поддержка XDG Base Directory.
- Сохранена полная обратная совместимость.
- Существующие пользователи не заметят изменений, если не создадут папку вручную.
- Фанаты XDG спеки (например я), смогут явно создать нужную папку и перевезти туда свои конфиги.
Что дал мне (и надеюсь тебе, читататель) этот опыт:
- Изучение чужих решений экономит время и помогает избежать ошибок.
- Стандартная библиотека языка Go — одна из лучших. Есть готовые стандартные решения, как например
os.UserConfigDir(). - При работе с инфраструктурными инструментами стабильность важнее новизны.
- Open-source — это про совместное улучшение экосистемы, даже если issue висит 7 лет.
Буду рад, если мой опыт поможет другим инициировать подобные улучшения в проектах, которые они используют.
Ссылки: