deploy: split runtime env into individual secrets
This commit is contained in:
@@ -118,33 +118,65 @@ jobs:
|
|||||||
"${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/docker-compose.yml"
|
"${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/docker-compose.yml"
|
||||||
|
|
||||||
- name: Write environment file
|
- name: Write environment file
|
||||||
|
env:
|
||||||
|
POSTGRES_DB: ${{ secrets.POSTGRES_DB }}
|
||||||
|
POSTGRES_USER: ${{ secrets.POSTGRES_USER }}
|
||||||
|
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
|
||||||
|
CRANK_DATABASE_URL: ${{ secrets.CRANK_DATABASE_URL }}
|
||||||
|
CRANK_STORAGE_ROOT: ${{ secrets.CRANK_STORAGE_ROOT }}
|
||||||
|
CRANK_PUBLISH_HOST: ${{ secrets.CRANK_PUBLISH_HOST }}
|
||||||
|
CRANK_ADMIN_BIND: ${{ secrets.CRANK_ADMIN_BIND }}
|
||||||
|
CRANK_MCP_BIND: ${{ secrets.CRANK_MCP_BIND }}
|
||||||
|
CRANK_MCP_REFRESH_MS: ${{ secrets.CRANK_MCP_REFRESH_MS }}
|
||||||
|
CRANK_LOG_LEVEL: ${{ secrets.CRANK_LOG_LEVEL }}
|
||||||
|
CRANK_SECRET_PROVIDER: ${{ secrets.CRANK_SECRET_PROVIDER }}
|
||||||
|
CRANK_MASTER_KEY: ${{ secrets.CRANK_MASTER_KEY }}
|
||||||
|
CRANK_PUBLIC_BASE_URL: ${{ secrets.CRANK_PUBLIC_BASE_URL }}
|
||||||
|
CRANK_MCP_PUBLIC_URL: ${{ secrets.CRANK_MCP_PUBLIC_URL }}
|
||||||
|
CRANK_SESSION_SECRET: ${{ secrets.CRANK_SESSION_SECRET }}
|
||||||
|
CRANK_PASSWORD_PEPPER: ${{ secrets.CRANK_PASSWORD_PEPPER }}
|
||||||
|
CRANK_SESSION_TTL_HOURS: ${{ secrets.CRANK_SESSION_TTL_HOURS }}
|
||||||
|
CRANK_BOOTSTRAP_ADMIN_EMAIL: ${{ secrets.CRANK_BOOTSTRAP_ADMIN_EMAIL }}
|
||||||
|
CRANK_BOOTSTRAP_ADMIN_PASSWORD: ${{ secrets.CRANK_BOOTSTRAP_ADMIN_PASSWORD }}
|
||||||
|
CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME: ${{ secrets.CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME }}
|
||||||
|
CRANK_DEMO_SEED: ${{ secrets.CRANK_DEMO_SEED }}
|
||||||
run: |
|
run: |
|
||||||
|
tmp_env="$(mktemp)"
|
||||||
|
append_if_set() {
|
||||||
|
if [ -n "$2" ]; then
|
||||||
|
printf '%s=%s\n' "$1" "$2" >> "$tmp_env"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
: > "$tmp_env"
|
||||||
|
append_if_set POSTGRES_DB "$POSTGRES_DB"
|
||||||
|
append_if_set POSTGRES_USER "$POSTGRES_USER"
|
||||||
|
append_if_set POSTGRES_PASSWORD "$POSTGRES_PASSWORD"
|
||||||
|
append_if_set CRANK_DATABASE_URL "$CRANK_DATABASE_URL"
|
||||||
|
append_if_set CRANK_STORAGE_ROOT "$CRANK_STORAGE_ROOT"
|
||||||
|
append_if_set CRANK_PUBLISH_HOST "$CRANK_PUBLISH_HOST"
|
||||||
|
append_if_set CRANK_ADMIN_BIND "$CRANK_ADMIN_BIND"
|
||||||
|
append_if_set CRANK_MCP_BIND "$CRANK_MCP_BIND"
|
||||||
|
append_if_set CRANK_MCP_REFRESH_MS "$CRANK_MCP_REFRESH_MS"
|
||||||
|
append_if_set CRANK_LOG_LEVEL "$CRANK_LOG_LEVEL"
|
||||||
|
append_if_set CRANK_SECRET_PROVIDER "$CRANK_SECRET_PROVIDER"
|
||||||
|
append_if_set CRANK_MASTER_KEY "$CRANK_MASTER_KEY"
|
||||||
|
append_if_set CRANK_PUBLIC_BASE_URL "$CRANK_PUBLIC_BASE_URL"
|
||||||
|
append_if_set CRANK_MCP_PUBLIC_URL "$CRANK_MCP_PUBLIC_URL"
|
||||||
|
append_if_set CRANK_SESSION_SECRET "$CRANK_SESSION_SECRET"
|
||||||
|
append_if_set CRANK_PASSWORD_PEPPER "$CRANK_PASSWORD_PEPPER"
|
||||||
|
append_if_set CRANK_SESSION_TTL_HOURS "$CRANK_SESSION_TTL_HOURS"
|
||||||
|
append_if_set CRANK_BOOTSTRAP_ADMIN_EMAIL "$CRANK_BOOTSTRAP_ADMIN_EMAIL"
|
||||||
|
append_if_set CRANK_BOOTSTRAP_ADMIN_PASSWORD "$CRANK_BOOTSTRAP_ADMIN_PASSWORD"
|
||||||
|
append_if_set CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME "$CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME"
|
||||||
|
append_if_set CRANK_DEMO_SEED "$CRANK_DEMO_SEED"
|
||||||
{
|
{
|
||||||
printf '%s\n' "${{ secrets.DEPLOY_ENV_FILE }}"
|
|
||||||
printf 'CRANK_ADMIN_API_IMAGE=%s:%s\n' "${{ env.ADMIN_API_IMAGE }}" "${{ env.IMAGE_TAG }}"
|
printf 'CRANK_ADMIN_API_IMAGE=%s:%s\n' "${{ env.ADMIN_API_IMAGE }}" "${{ env.IMAGE_TAG }}"
|
||||||
printf 'CRANK_MCP_SERVER_IMAGE=%s:%s\n' "${{ env.MCP_SERVER_IMAGE }}" "${{ env.IMAGE_TAG }}"
|
printf 'CRANK_MCP_SERVER_IMAGE=%s:%s\n' "${{ env.MCP_SERVER_IMAGE }}" "${{ env.IMAGE_TAG }}"
|
||||||
printf 'CRANK_UI_IMAGE=%s:%s\n' "${{ env.UI_IMAGE }}" "${{ env.IMAGE_TAG }}"
|
printf 'CRANK_UI_IMAGE=%s:%s\n' "${{ env.UI_IMAGE }}" "${{ env.IMAGE_TAG }}"
|
||||||
} | ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" \
|
} >> "$tmp_env"
|
||||||
|
cat "$tmp_env" | ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" \
|
||||||
"mkdir -p '${{ secrets.DEPLOY_PATH }}' && cat > '${{ secrets.DEPLOY_PATH }}/.env'"
|
"mkdir -p '${{ secrets.DEPLOY_PATH }}' && cat > '${{ secrets.DEPLOY_PATH }}/.env'"
|
||||||
|
rm -f "$tmp_env"
|
||||||
- name: Apply deploy overrides
|
|
||||||
run: |
|
|
||||||
if [ -n "${{ secrets.DEPLOY_MASTER_KEY }}" ]; then
|
|
||||||
ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" "
|
|
||||||
set -e
|
|
||||||
cd '${{ secrets.DEPLOY_PATH }}'
|
|
||||||
sed -i '/^CRANK_MASTER_KEY=/d' .env
|
|
||||||
printf 'CRANK_MASTER_KEY=%s\n' '${{ secrets.DEPLOY_MASTER_KEY }}' >> .env
|
|
||||||
"
|
|
||||||
fi
|
|
||||||
if [ -n "${{ secrets.DEPLOY_DEMO_SEED }}" ]; then
|
|
||||||
ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" "
|
|
||||||
set -e
|
|
||||||
cd '${{ secrets.DEPLOY_PATH }}'
|
|
||||||
sed -i '/^CRANK_DEMO_SEED=/d' .env
|
|
||||||
printf 'CRANK_DEMO_SEED=%s\n' '${{ secrets.DEPLOY_DEMO_SEED }}' >> .env
|
|
||||||
"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Validate required environment variables
|
- name: Validate required environment variables
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
+30
-11
@@ -212,7 +212,7 @@ CD для MVP должен:
|
|||||||
1. push в `main`
|
1. push в `main`
|
||||||
2. успешный `CI`
|
2. успешный `CI`
|
||||||
3. build and push images в registry
|
3. build and push images в registry
|
||||||
4. upload env и deployment files
|
4. upload runtime env и deployment files
|
||||||
5. `docker login` на registry на целевом хосте
|
5. `docker login` на registry на целевом хосте
|
||||||
6. `docker compose config -q`
|
6. `docker compose config -q`
|
||||||
7. `docker compose pull`
|
7. `docker compose pull`
|
||||||
@@ -233,19 +233,38 @@ CD для MVP должен:
|
|||||||
|
|
||||||
Они используются только для `docker login` на целевом Linux host перед `docker compose pull`.
|
Они используются только для `docker login` на целевом Linux host перед `docker compose pull`.
|
||||||
|
|
||||||
Для точечных runtime override без переписывания всего `DEPLOY_ENV_FILE` можно использовать
|
Монолитный `DEPLOY_ENV_FILE` больше не используется.
|
||||||
отдельные deployment secrets. Сейчас поддержан:
|
|
||||||
|
|
||||||
- `DEPLOY_MASTER_KEY`
|
Workflow собирает `.env` на сервере из отдельных GitHub secrets. Для runtime-переменных нужно
|
||||||
- `DEPLOY_DEMO_SEED`
|
создавать secrets с теми же именами, что и у приложений:
|
||||||
|
|
||||||
Если задан `DEPLOY_MASTER_KEY`, workflow удаляет `CRANK_MASTER_KEY` из сгенерированного `.env`
|
- `POSTGRES_DB`
|
||||||
и записывает туда значение из `DEPLOY_MASTER_KEY`. Это позволяет ротировать master key или
|
- `POSTGRES_USER`
|
||||||
добавлять его отдельно, не переписывая весь `DEPLOY_ENV_FILE`.
|
- `POSTGRES_PASSWORD`
|
||||||
|
- `CRANK_DATABASE_URL`
|
||||||
|
- `CRANK_STORAGE_ROOT`
|
||||||
|
- `CRANK_PUBLISH_HOST`
|
||||||
|
- `CRANK_ADMIN_BIND`
|
||||||
|
- `CRANK_MCP_BIND`
|
||||||
|
- `CRANK_MCP_REFRESH_MS`
|
||||||
|
- `CRANK_LOG_LEVEL`
|
||||||
|
- `CRANK_SECRET_PROVIDER`
|
||||||
|
- `CRANK_MASTER_KEY`
|
||||||
|
- `CRANK_PUBLIC_BASE_URL`
|
||||||
|
- `CRANK_MCP_PUBLIC_URL`
|
||||||
|
- `CRANK_SESSION_SECRET`
|
||||||
|
- `CRANK_PASSWORD_PEPPER`
|
||||||
|
- `CRANK_SESSION_TTL_HOURS`
|
||||||
|
- `CRANK_BOOTSTRAP_ADMIN_EMAIL`
|
||||||
|
- `CRANK_BOOTSTRAP_ADMIN_PASSWORD`
|
||||||
|
- `CRANK_BOOTSTRAP_ADMIN_DISPLAY_NAME`
|
||||||
|
- `CRANK_DEMO_SEED`
|
||||||
|
|
||||||
Если задан `DEPLOY_DEMO_SEED`, workflow удаляет `CRANK_DEMO_SEED` из сгенерированного `.env`
|
Практически это означает:
|
||||||
и записывает туда значение из `DEPLOY_DEMO_SEED`. Это позволяет временно включать и выключать
|
|
||||||
demo seed отдельным secret, не переписывая основной production `.env`.
|
- одна runtime-переменная = один GitHub secret;
|
||||||
|
- ротация одного секрета не требует переписывать весь набор env;
|
||||||
|
- `.env` на сервере каждый deploy собирается заново из актуальных secrets.
|
||||||
|
|
||||||
## 12. Что нельзя забыть
|
## 12. Что нельзя забыть
|
||||||
|
|
||||||
|
|||||||
@@ -142,11 +142,14 @@ Demo/deployment:
|
|||||||
|
|
||||||
- `CRANK_DEMO_SEED=true`
|
- `CRANK_DEMO_SEED=true`
|
||||||
|
|
||||||
В deployment workflow этот флаг можно переопределять отдельно через GitHub secret
|
В deployment workflow больше не используется монолитный `DEPLOY_ENV_FILE`.
|
||||||
`DEPLOY_DEMO_SEED`, не меняя основной `DEPLOY_ENV_FILE`.
|
|
||||||
|
|
||||||
Аналогично `CRANK_MASTER_KEY` можно прокидывать отдельным GitHub secret `DEPLOY_MASTER_KEY`,
|
`.env` на сервере собирается из отдельных GitHub secrets, причем имя секрета совпадает с именем
|
||||||
если не хочется каждый раз переписывать весь `DEPLOY_ENV_FILE` ради одного ключа.
|
runtime env-переменной. Это значит, что:
|
||||||
|
|
||||||
|
- `CRANK_MASTER_KEY` хранится как GitHub secret `CRANK_MASTER_KEY`;
|
||||||
|
- `CRANK_DEMO_SEED` хранится как GitHub secret `CRANK_DEMO_SEED`;
|
||||||
|
- и так же для остальных runtime-переменных.
|
||||||
|
|
||||||
## 8.1. Delivery artifacts
|
## 8.1. Delivery artifacts
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user