From 4dc5bf68123b69f8579d31e7cf6c0f260d156e18 Mon Sep 17 00:00:00 2001 From: Mischa Date: Sat, 4 Jul 2026 14:02:13 +0200 Subject: [PATCH] build: add per-environment (test/prod) Makefile targets Split env config into local vs remote: - .env: local/shared config, always loaded (docker compose + make test) - .env.test / .env.prod: full remote config, loaded on demand via ENV Remote targets now generate -test/-prod variants (e.g. remote-install-prod); a guard-env prerequisite blocks bare remote targets with no environment set. Refresh .env.example to document the two-tier layout and add .env.prod/.env.test to gitignore and the never-read list in CLAUDE.md. Co-Authored-By: Claude Opus 4.8 --- .env.example | 64 ++++++++++++++++++++++++++++++++++++++++++---------- .gitignore | 2 ++ CLAUDE.md | 2 +- Makefile | 49 +++++++++++++++++++++++++++++++--------- 4 files changed, 93 insertions(+), 24 deletions(-) diff --git a/.env.example b/.env.example index ae61e26..9cb20e4 100644 --- a/.env.example +++ b/.env.example @@ -1,27 +1,67 @@ -# SSH connection -REMOTE_USER=root +# .env.example — template for the project's environment files. +# +# There are TWO kinds of env file, loaded by the Makefile in this order: +# +# .env → LOCAL / shared config. ALWAYS loaded. NO remote credentials. +# Used by local targets (docker compose ${UID}/${GID} + the +# travel-memories env_file, and `make test-post` / `make test`). +# Copy the LOCAL section below into it. +# +# .env.test → REMOTE config for the test environment. +# .env.prod → REMOTE config for production. +# Loaded only when a remote target sets ENV, e.g. +# `make remote-install-prod`. Copy the REMOTE section below into +# each, with the values for that environment. +# +# .env, .env.test and .env.prod are all gitignored — never commit real values. +# This .example file is the only one that IS committed; keep its values as +# placeholders. + +# ───────────────────────────────────────────────────────────────────────────── +# LOCAL → copy into .env +# ───────────────────────────────────────────────────────────────────────────── + +# Host user/group id for container file ownership (docker-compose ${UID}:${GID}). +# Match your local user: run `id -u` / `id -g` (usually 1000 on a single-user box). +UID=1000 +GID=1000 + +# Local Grav dev server + test login, used by `make test-post` / `make test` +# (scripts/test-post.sh). Must be a valid Grav site login on the local instance. +GRAV_BASE_URL=http://localhost:8081 +GRAV_TEST_USER=your-local-grav-user +GRAV_TEST_PASS=your-local-grav-password +GRAV_USER_DIR=/absolute/path/to/travel-blog-intotheeast/user + +# travel-memories service (docker-compose `env_file: .env`). Fill in whatever +# that Flask app needs — e.g. its Immich connection. Leave commented until set. +# IMMICH_URL= +# IMMICH_API_KEY= + +# ───────────────────────────────────────────────────────────────────────────── +# REMOTE → copy into .env.test AND .env.prod (with per-env values) +# ───────────────────────────────────────────────────────────────────────────── + +# SSH connection to the target server. +REMOTE_USER=deploy REMOTE_HOST=example.com REMOTE_PORT=22 REMOTE_HOME=/home/example.com -# Server paths (override here if your setup differs from the Makefile defaults) +# Server paths. Optional — default to $(REMOTE_HOME)/public_html and +# $(REMOTE_HOME)/site-config. Set explicitly only if the layout differs +# (e.g. a per-domain webroot like /home/deploy/domains/test.example.com/public_html). WEBROOT=/home/example.com/public_html SITE_CONFIG_DIR=/home/example.com/site-config -# Grav +# Grav version installed by scripts/server-install.sh (remote-install). GRAV_VERSION=2.0.0-rc.10 -# Repos +# Repos cloned/pulled on the server. USER_REPO=https://gitea.example.com/org/intotheeast-user.git MAIN_REPO=https://gitea.example.com/org/travel-blog-intotheeast.git -# Gitea credentials — never commit these; only ever in .env (local) or ~/.env-project (server, temporary) +# Gitea credentials used by remote-install / remote-env-setup. GITEA_HOST=gitea.example.com GITEA_USER=deploy-user GITEA_TOKEN=your-gitea-personal-access-token - -# Test credentials — used by 'make test-post' (must be a valid Grav site login user) -GRAV_TEST_USER=mischa -GRAV_TEST_PASS=TravelBlog2026! -GRAV_BASE_URL=http://localhost:8081 -GRAV_USER_DIR=/home/mischa/Projects/travel-blog-intotheeast/user diff --git a/.gitignore b/.gitignore index 3129eda..146355b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # Environment .env +.env.prod +.env.test # Grav CMS /user/ diff --git a/CLAUDE.md b/CLAUDE.md index 7048c42..54f7d86 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -113,7 +113,7 @@ After updating, also create the new trip's page tree under `user/pages/01.trips/ ### Environment -**Never read `.env`** — it contains sensitive credentials. You may pass it to commands (e.g. `docker compose`, `make`) but never read its contents directly. Ask the user if you need environment-specific information. +**Never read `.env`, `.env.prod`, or `.env.test`** — they contain sensitive credentials. You may pass them to commands (e.g. `docker compose`, `make`) but never read their contents directly. Ask the user if you need environment-specific information. ### Remote operations diff --git a/Makefile b/Makefile index 0cdae43..d97c29a 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,13 @@ +# Local/shared config — always loaded. Keep remote credentials OUT of here; +# those live in .env.test / .env.prod. (docker compose also reads .env directly +# for ${UID}/${GID} substitution and the travel-memories env_file.) -include .env + +# Remote config — loaded only when targeting an environment. ENV is set +# automatically by the env-suffixed remote targets (e.g. `make remote-install-prod`); +# each .env. holds a full, self-contained set of remote vars. +ENV ?= +-include .env.$(ENV) export REMOTE_PORT ?= 22 @@ -6,6 +15,24 @@ SSH := ssh -p $(REMOTE_PORT) $(REMOTE_USER)@$(REMOTE_HOST) WEBROOT ?= $(REMOTE_HOME)/public_html SITE_CONFIG_DIR ?= $(REMOTE_HOME)/site-config +# ── Environment guard + generated per-env remote targets ────────────────────── +# Every remote-* target below gains `-test` / `-prod` variants, e.g. +# make remote-install-prod → runs remote-install with ENV=prod +# Calling a bare remote target (no ENV) fails via guard-env. +REMOTE_TARGETS := remote-env-setup remote-env-remove remote-wipe remote-install \ + remote-fetch remote-fetch-content remote-install-plugins remote-upgrade-grav \ + remote-clean remote-maintenance-on remote-maintenance-off +ENVS := test prod + +guard-env: + @test -n "$(ENV)" || { echo "ERROR: no environment. Use an env-suffixed target, e.g. 'make remote-install-prod'."; exit 1; } + @test -f ".env.$(ENV)" || { echo "ERROR: missing .env.$(ENV)"; exit 1; } + +define make-env-target +$(1)-$(2): ; @$$(MAKE) --no-print-directory $(1) ENV=$(2) +endef +$(foreach t,$(REMOTE_TARGETS),$(foreach e,$(ENVS),$(eval $(call make-env-target,$(t),$(e))))) + # ── Tests ───────────────────────────────────────────────────────────────────── test-config: @@ -80,21 +107,21 @@ content-pull: # ── Remote credentials ───────────────────────────────────────────────────────── -remote-env-setup: +remote-env-setup: guard-env @$(SSH) "printf 'GITEA_HOST=%s\nGITEA_USER=%s\nGITEA_TOKEN=%s\n' \ '$(GITEA_HOST)' '$(GITEA_USER)' '$(GITEA_TOKEN)' > ~/.env-intotheeast && chmod 600 ~/.env-intotheeast" @echo "Credentials written to server. Run 'make remote-env-remove' when done." -remote-env-remove: +remote-env-remove: guard-env @$(SSH) "rm -f ~/.env-intotheeast" @echo "Credentials removed from server." # ── Remote: initial install ──────────────────────────────────────────────────── -remote-wipe: +remote-wipe: guard-env $(SSH) "cd $(WEBROOT) && rm -rf assets backup bin cache images logs system tmp vendor webserver-configs index.php .htaccess CHANGELOG.md LICENSE.txt README.md" -remote-install: +remote-install: guard-env $(SSH) "WEBROOT=$(WEBROOT) \ SITE_CONFIG_DIR=$(SITE_CONFIG_DIR) \ USER_REPO=$(USER_REPO) \ @@ -108,24 +135,24 @@ remote-install: # ── Remote: ongoing maintenance ──────────────────────────────────────────────── -remote-fetch: +remote-fetch: guard-env $(SSH) "git -C $(SITE_CONFIG_DIR) checkout main && git -C $(SITE_CONFIG_DIR) pull" -remote-fetch-content: +remote-fetch-content: guard-env $(SSH) "git -C $(WEBROOT)/user fetch origin main && git -C $(WEBROOT)/user sparse-checkout disable && git -C $(WEBROOT)/user reset --hard origin/main" -remote-install-plugins: +remote-install-plugins: guard-env $(SSH) "cd $(WEBROOT) && php bin/gpm install $(shell cat plugins.txt | tr '\n' ' ') -y" -remote-upgrade-grav: +remote-upgrade-grav: guard-env $(SSH) "cd $(WEBROOT) && php bin/grav upgrade" -remote-clean: +remote-clean: guard-env $(SSH) "cd $(WEBROOT) && php bin/grav clearcache" -remote-maintenance-on: +remote-maintenance-on: guard-env $(SSH) "bash -s on $(WEBROOT)" < scripts/server-maintenance.sh -remote-maintenance-off: +remote-maintenance-off: guard-env $(SSH) "bash -s off $(WEBROOT)" < scripts/server-maintenance.sh