снят мёртвый godotenv.Load() вместе с зависимостью
- переменных окружения наш рабочий код больше не читает нигде: ушли вызов, импорт и предупреждение на старте - у оставленной строки .env в .gitignore и у запрета forbidigo переписаны причины: прежние называли живым снесённого читателя - в документы записано, что барьеры против .env стоят без читателя, и правило парности .gitignore с .dockerignore
This commit is contained in:
+3
-2
@@ -46,8 +46,9 @@ Thumbs.db
|
|||||||
# Config files
|
# Config files
|
||||||
config.toml
|
config.toml
|
||||||
|
|
||||||
# Переменные окружения: файл читает сам бинарник при старте, и секрет в нём
|
# Переменные окружения: сервис их не читает, настройки приезжают из TOML.
|
||||||
# оказывается тем же способом, каким оказывается в конфиге.
|
# Строка стоит против того, чтобы секрет завёлся здесь руками: из этого файла
|
||||||
|
# он попадает в git тем же способом, каким попал бы из конфига.
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Sample and test audio files
|
# Sample and test audio files
|
||||||
|
|||||||
+2
-3
@@ -64,9 +64,8 @@ linters:
|
|||||||
msg: 'пишем через slog, а не в stdout напрямую (docs/conventions/logging.md)'
|
msg: 'пишем через slog, а не в stdout напрямую (docs/conventions/logging.md)'
|
||||||
# Конфигурация приезжает из TOML. Перечислены все способы прочитать
|
# Конфигурация приезжает из TOML. Перечислены все способы прочитать
|
||||||
# окружение, а не один: `os.Getenv` без соседей обходится `os.LookupEnv`
|
# окружение, а не один: `os.Getenv` без соседей обходится `os.LookupEnv`
|
||||||
# одной правкой. Окружение читает только godotenv в точке входа
|
# одной правкой. Наш рабочий код окружение не читает вовсе — это
|
||||||
# `cmd/transcriber` — он кладёт .env в окружение процесса, а не в
|
# правило и держит.
|
||||||
# настройки.
|
|
||||||
#
|
#
|
||||||
# Чего правило не ловит: `fmt.Fprintln(os.Stdout, …)` и
|
# Чего правило не ловит: `fmt.Fprintln(os.Stdout, …)` и
|
||||||
# `os.Stdout.WriteString` — первый аргумент по имени функции не судится.
|
# `os.Stdout.WriteString` — первый аргумент по имени функции не судится.
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/joho/godotenv"
|
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
|
|
||||||
ffmpegconv "git.vakhrushev.me/av/transcriber/internal/adapter/converter/ffmpeg"
|
ffmpegconv "git.vakhrushev.me/av/transcriber/internal/adapter/converter/ffmpeg"
|
||||||
@@ -111,11 +110,6 @@ func run(logger *slog.Logger) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Загружаем переменные окружения из .env файла
|
|
||||||
if err := godotenv.Load(); err != nil {
|
|
||||||
logger.Warn("Warning: .env file not found, using system environment variables")
|
|
||||||
}
|
|
||||||
|
|
||||||
db, err := sqliterepo.Open(cfg.Storage.DataDir, sqliterepo.Settings{
|
db, err := sqliterepo.Open(cfg.Storage.DataDir, sqliterepo.Settings{
|
||||||
BusyTimeoutMs: cfg.Storage.BusyTimeoutMs,
|
BusyTimeoutMs: cfg.Storage.BusyTimeoutMs,
|
||||||
ReadConnections: cfg.Storage.ReadConnections,
|
ReadConnections: cfg.Storage.ReadConnections,
|
||||||
|
|||||||
@@ -10,15 +10,17 @@
|
|||||||
|
|
||||||
**Механизировано:** запрет `os.Getenv` — `forbidigo` в `.golangci.yml`
|
**Механизировано:** запрет `os.Getenv` — `forbidigo` в `.golangci.yml`
|
||||||
([go-linters.md](go-linters.md), «Механизировано»). Он держит правило «настройки
|
([go-linters.md](go-linters.md), «Механизировано»). Он держит правило «настройки
|
||||||
приезжают из TOML»; `godotenv` в `cmd/transcriber` по-прежнему загружает `.env`, но кладёт
|
приезжают из TOML»: наш рабочий код окружение не читает вовсе — читателя `.env`
|
||||||
его в окружение процесса, а не в настройки приложения.
|
в `cmd/transcriber` сняли 2026-08-23 вместе с зависимостью. Окружение остаётся
|
||||||
|
у границы SDK: `aws-sdk-go-v2` в `internal/adapter/recognizer/yandex/s3.go`
|
||||||
|
зовёт `config.LoadDefaultConfig`, а тот читает `AWS_PROFILE`, `AWS_CA_BUNDLE`,
|
||||||
|
`AWS_ENDPOINT_URL` и `AWS_ENDPOINT_URL_S3` и смотрит `~/.aws/config`.
|
||||||
|
|
||||||
## Принципы
|
## Принципы
|
||||||
|
|
||||||
- **Конфигурация — только TOML.** Переменные окружения для конфигурации **не
|
- **Конфигурация — только TOML.** Переменные окружения для конфигурации **не
|
||||||
используем**: окружение наследуется дочерними процессами и видно через
|
используем**: окружение наследуется дочерними процессами и видно через
|
||||||
`/proc/<pid>/environ` — для секретов это слабее файла под `0600`.
|
`/proc/<pid>/environ` — для секретов это слабее файла под `0600`.
|
||||||
*Расхождение:* `cmd/transcriber` зовёт `godotenv.Load()` и молча продолжает без файла.
|
|
||||||
- Грузим **один раз при старте** в одну типизированную структуру `Config`
|
- Грузим **один раз при старте** в одну типизированную структуру `Config`
|
||||||
(под-структуры по секциям). Дальше по коду читаем только её — чтения файла в
|
(под-структуры по секциям). Дальше по коду читаем только её — чтения файла в
|
||||||
прикладном коде нет, только загрузчик `internal/config`.
|
прикладном коде нет, только загрузчик `internal/config`.
|
||||||
@@ -127,6 +129,16 @@ Ansible из `pet-project-server`). Приложение просто читае
|
|||||||
текст остаётся как есть — иначе за разборчивость отказа платили бы там, где
|
текст остаётся как есть — иначе за разборчивость отказа платили бы там, где
|
||||||
платить не за что.
|
платить не за что.
|
||||||
|
|
||||||
|
**Файл, способный нести секрет, называется в `.gitignore` и в
|
||||||
|
`.dockerignore`.** Путей наружу у такого файла два, и закрывает
|
||||||
|
их разное: git держит `.gitignore`, а контекст сборки образа — `.dockerignore`,
|
||||||
|
потому что docker `.gitignore` не читает. Сегодня в обоих названы `config.toml`
|
||||||
|
и `.env`. Правило записано прозой и держится чтением: сверка двух списков стала
|
||||||
|
бы проверкой над проверкой, а такие проект не заводит
|
||||||
|
([../../CLAUDE.md](../../CLAUDE.md), «Запреты»). До 2026-08-23 парность не
|
||||||
|
называл ни один документ, и прогон, снимавший мёртвого читателя `.env`, снял
|
||||||
|
строку с одной стороны — вернуло её ревью.
|
||||||
|
|
||||||
## Проверка и остановка на старте
|
## Проверка и остановка на старте
|
||||||
|
|
||||||
Конфиг проверяем **на старте, до приёма трафика**. Негодный конфиг — лог `ERROR`
|
Конфиг проверяем **на старте, до приёма трафика**. Негодный конфиг — лог `ERROR`
|
||||||
|
|||||||
@@ -336,6 +336,17 @@ Storage, оттуда его читает SpeechKit. Третий путь —
|
|||||||
Всё перечисленное лежит в `config.toml`. Файл в `.gitignore`, на сервер его
|
Всё перечисленное лежит в `config.toml`. Файл в `.gitignore`, на сервер его
|
||||||
кладёт Ansible; `gitleaks` на pre-commit смотрит только индекс коммита.
|
кладёт Ansible; `gitleaks` на pre-commit смотрит только индекс коммита.
|
||||||
|
|
||||||
|
**Строки `.env` в `.gitignore` и в `.dockerignore` стоят без читателя, и снимать
|
||||||
|
их поэтому нельзя.** Читателя сняли 2026-08-23 вместе с зависимостью
|
||||||
|
`godotenv`: наш рабочий код окружение не читает, и файл, положенный рядом с
|
||||||
|
бинарником, ничего не меняет. Барьеры остались против другого — против того,
|
||||||
|
чтобы секрет завёлся в этом файле руками и уехал из него теми же двумя путями,
|
||||||
|
какими уехал бы из конфига: в git и в контекст сборки образа. Второй барьер
|
||||||
|
нужен отдельно от первого: `Dockerfile` копирует корень целиком (`COPY . .`), а
|
||||||
|
`.gitignore` docker не читает — чужой `.env` лёг бы слоем образа. Инвариант,
|
||||||
|
ради которого барьеры стоят, — «Секрет не покидает конфиг» из
|
||||||
|
[../CLAUDE.md](../CLAUDE.md).
|
||||||
|
|
||||||
Целевой периметр добавляет к списку пять записей, и первая из них — новый вид
|
Целевой периметр добавляет к списку пять записей, и первая из них — новый вид
|
||||||
секрета, которого сегодня в проекте нет вовсе:
|
секрета, которого сегодня в проекте нет вовсе:
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ require (
|
|||||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3
|
github.com/aws/aws-sdk-go-v2/service/s3 v1.97.3
|
||||||
github.com/aws/smithy-go v1.27.7
|
github.com/aws/smithy-go v1.27.7
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/joho/godotenv v1.5.1
|
|
||||||
github.com/pressly/goose/v3 v3.27.3
|
github.com/pressly/goose/v3 v3.27.3
|
||||||
github.com/prometheus/client_golang v1.23.0
|
github.com/prometheus/client_golang v1.23.0
|
||||||
github.com/stretchr/testify v1.11.1
|
github.com/stretchr/testify v1.11.1
|
||||||
|
|||||||
@@ -61,8 +61,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|
||||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
|
||||||
github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk=
|
github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk=
|
||||||
github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
|||||||
Reference in New Issue
Block a user