Repository navigation
Expand file tree
/
Copy pathinit-prod-env.sh
More file actions
executable file
·82 lines (68 loc) · 3.27 KB
/
Copy pathinit-prod-env.sh
File metadata and controls
executable file
·82 lines (68 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#!/usr/bin/env bash
# Creates deploy/.env.prod on the VPS from deploy/.env.prod.example.
#
# Why a script: the production database must never run with the local app/app defaults, and passwords should be
# generated on the host that uses them, never typed, pasted or committed. The script also derives the image name and
# compose project name from the git remote, so the two values most likely to be mistyped never are.
#
# Every empty *PASSWORD= line in deploy/.env.prod.example (POSTGRES_PASSWORD and any password a brick adds) gets a fresh value.
#
# Usage: scripts/init-prod-env.sh [--force] (or: make prod-init)
set -euo pipefail
force=false
[[ "${1:-}" == "--force" ]] && force=true
root="$(git rev-parse --show-toplevel)"
example="$root/deploy/.env.prod.example"
target="$root/deploy/.env.prod"
if [[ -e "$target" && "$force" == false ]]; then
echo "Refusing: $target already exists. Re-run with --force to replace it (this generates NEW passwords," >&2
echo "which only makes sense before the first 'make prod-up' or together with a database reset)." >&2
exit 1
fi
remote="$(git -C "$root" remote get-url origin 2>/dev/null || true)"
slug="$(printf '%s' "$remote" | sed -E 's#^(git@github\.com:|https://github\.com/|ssh://git@github\.com/)##; s#\.git$##' | tr '[:upper:]' '[:lower:]')"
if [[ -z "$slug" || "$slug" != */* ]]; then
echo "Could not derive owner/repo from git remote '$remote'; add the GitHub remote first." >&2
exit 1
fi
project="${slug##*/}"
image="ghcr.io/${slug}"
cat <<BANNER
This writes production secrets to:
$target
It will contain a freshly generated value for every "*PASSWORD=" line (POSTGRES_PASSWORD and any password a brick adds)
and is chmod 600. It is gitignored and must stay on this host.
$( [[ "$force" == true ]] && printf '\n --force: the existing file will be OVERWRITTEN and every generated password REPLACED.\n' )
Derived from the git remote:
APP_IMAGE=$image
COMPOSE_PROJECT_NAME=$project
BANNER
read -r -p " Type 'yes' to continue: " answer
[[ "$answer" == "yes" ]] || { echo "Aborted; nothing written."; exit 1; }
umask 077
# Every empty *PASSWORD= line in the example gets its own generated value: the core database password and whatever
# an enabled brick asks for (bricks/README.md). Lines with a value are kept as they are. The whole file is built in
# memory first, so a failing openssl (set -e via pipefail) aborts before anything is written.
content="$(
sed -e "s#^APP_IMAGE=.*#APP_IMAGE=${image}#" \
-e "s#^COMPOSE_PROJECT_NAME=.*#COMPOSE_PROJECT_NAME=${project}#" \
"$example" \
| while IFS= read -r line || [[ -n "$line" ]]; do
if [[ "$line" =~ ^([A-Z0-9_]*PASSWORD)=$ ]]; then
value="$(openssl rand -hex 24)" || { echo "openssl failed; no password generated, nothing written." >&2; exit 1; }
printf '%s=%s\n' "${BASH_REMATCH[1]}" "$value"
else
printf '%s\n' "$line"
fi
done
)"
printf '%s\n' "$content" > "$target"
chmod 600 "$target"
cat <<NEXT
Written $target
Still to fill by hand (REQUIRED block):
DOMAIN= public hostname, DNS A record already pointing at this VPS
ACME_EMAIL= Let's Encrypt account e-mail
Optional: make add BRICK=<name> for observability, backups or object storage (make bricks lists them).
Then: make prod-up
NEXT