vibed-infra 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +99 -0
- package/github/workflows/docker-build-reusable.yml +61 -0
- package/install-auto-update.sh +125 -0
- package/install.sh +259 -0
- package/lib/__pycache__/load_config.cpython-312.pyc +0 -0
- package/lib/env.sh +224 -0
- package/lib/fetch.sh +49 -0
- package/lib/generate.py +119 -0
- package/lib/load_config.py +110 -0
- package/lib/prompt.sh +25 -0
- package/lib/tls.sh +59 -0
- package/package.json +34 -0
- package/schema/packageconfig.md +88 -0
- package/skills/infra-cicd/SKILL.md +47 -0
- package/skills/infra-packager/SKILL.md +54 -0
- package/skills/system-gateway/SKILL.md +52 -0
- package/start.sh +15 -0
- package/templates/docker-compose.backend.yml +32 -0
- package/templates/docker-compose.gateway.yml +25 -0
- package/templates/docker-compose.workers.yml +17 -0
- package/templates/nginx/nginx.conf +21 -0
- package/update.sh +15 -0
package/README.md
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# vibed-infra
|
|
2
|
+
|
|
3
|
+
Product-agnostic VPS deployment packager: wget installer, Docker Compose layouts, digest-gated auto-update, TLS/nginx helpers, and CI workflow templates.
|
|
4
|
+
|
|
5
|
+
Published on npm as **`vibed-infra`**.
|
|
6
|
+
|
|
7
|
+
## Install (npm)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install vibed-infra
|
|
11
|
+
# or in a monorepo sibling checkout:
|
|
12
|
+
# "vibed-infra": "file:../vibed-infra"
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Resolve the packager root from Node:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
node -e "console.log(require('path').dirname(require.resolve('vibed-infra/package.json')))"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Run install locally (product `packageconfig.yaml` + templates required):
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
PACKAGER_RAW=/path/to/node_modules/vibed-infra \
|
|
25
|
+
PACKAGECONFIG_URL=/path/to/product/deploy/packageconfig.yaml \
|
|
26
|
+
PRODUCT_RAW=/path/to/product/deploy/templates \
|
|
27
|
+
bash /path/to/node_modules/vibed-infra/install.sh --profile api
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Or use the bin:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npx vibed-infra --profile api # still needs PACKAGECONFIG_URL / PRODUCT_RAW (or packageconfig rawBase)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Install (wget, operators)
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
wget -qO- https://raw.githubusercontent.com/naiemk/vibed-infra/main/install.sh | \
|
|
40
|
+
env INFRA_PROFILE=api \
|
|
41
|
+
PACKAGECONFIG_URL=https://raw.githubusercontent.com/ORG/REPO/main/deploy/packageconfig.yaml \
|
|
42
|
+
PRODUCT_RAW=https://raw.githubusercontent.com/ORG/REPO/main/deploy/templates \
|
|
43
|
+
bash
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Layout
|
|
47
|
+
|
|
48
|
+
| Path | Purpose |
|
|
49
|
+
|------|---------|
|
|
50
|
+
| `install.sh` | Single entrypoint (`INFRA_PROFILE` or `--profile`) |
|
|
51
|
+
| `start.sh` / `update.sh` | Generic lifecycle in install dir |
|
|
52
|
+
| `install-auto-update.sh` | Cron from profile flags |
|
|
53
|
+
| `lib/` | fetch, env, tls, prompt, generate |
|
|
54
|
+
| `templates/` | Generic compose/nginx skeletons |
|
|
55
|
+
| `schema/packageconfig.md` | Schema reference |
|
|
56
|
+
| `examples/vps-hello/` | Full product that installs API + worker + HTTPS gateway on a VPS |
|
|
57
|
+
| `skills/` | Cursor skills |
|
|
58
|
+
| `github/workflows/` | Reusable GHCR build workflow |
|
|
59
|
+
|
|
60
|
+
## Environment
|
|
61
|
+
|
|
62
|
+
| Variable | Meaning |
|
|
63
|
+
|----------|---------|
|
|
64
|
+
| `PACKAGER_RAW` | Base URL or local path to this package root |
|
|
65
|
+
| `PACKAGECONFIG_URL` | Product `packageconfig.yaml` (URL or path) |
|
|
66
|
+
| `PRODUCT_RAW` | Product templates base (overrides `rawBase`; opaque to infra) |
|
|
67
|
+
| `INFRA_PROFILE` | `api`, `nodes`, or `gateway` |
|
|
68
|
+
| `INSTALL_DIR` | Target directory (default `.`) |
|
|
69
|
+
|
|
70
|
+
## Publishing (npm)
|
|
71
|
+
|
|
72
|
+
Every **merge to `main`** runs [Publish npm](.github/workflows/publish.yml):
|
|
73
|
+
|
|
74
|
+
1. Bumps **minor** (`0.1.0` → `0.2.0`)
|
|
75
|
+
2. Commits `chore: release vibed-infra v…`, tags `v…`, pushes
|
|
76
|
+
3. Runs `npm publish` (needs repo secret `NPM_TOKEN`)
|
|
77
|
+
|
|
78
|
+
**Major versions** — set `version` in `package.json` yourself (e.g. `1.0.0`) and include **`[major]`** in the commit message (or PR merge commit). That publishes the pinned version with **no** automatic minor bump.
|
|
79
|
+
|
|
80
|
+
Manual run: Actions → Publish npm → `workflow_dispatch` (`minor` / `major` / `none`).
|
|
81
|
+
|
|
82
|
+
Set repository secret **`NPM_TOKEN`** (npm automation token with publish access) before the first merge to `main`.
|
|
83
|
+
|
|
84
|
+
Ordinary PRs only run CI (`npm test`); they do **not** publish.
|
|
85
|
+
|
|
86
|
+
## Example (VPS)
|
|
87
|
+
|
|
88
|
+
[`examples/vps-hello`](examples/vps-hello) is a complete product that uses this packager: `packageconfig.yaml`, install wrappers, start/update scripts, and a tiny API/UI/worker you can build on the box.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/naiemk/vibed-infra.git
|
|
92
|
+
cd vibed-infra
|
|
93
|
+
./examples/vps-hello/scripts/build-images.sh
|
|
94
|
+
mkdir -p ~/hello-vps/api && cd ~/hello-vps/api
|
|
95
|
+
bash /path/to/vibed-infra/examples/vps-hello/install/install-api.sh
|
|
96
|
+
# edit .env, then ./start-hello-api.sh
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Runbook: [`examples/vps-hello/README.md`](examples/vps-hello/README.md).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Reusable GHCR docker build — one image per workflow_call.
|
|
2
|
+
#
|
|
3
|
+
# Caller (repeat per image or use a matrix in the caller workflow):
|
|
4
|
+
#
|
|
5
|
+
# jobs:
|
|
6
|
+
# build-api:
|
|
7
|
+
# uses: ./infra/github/workflows/docker-build-reusable.yml
|
|
8
|
+
# with:
|
|
9
|
+
# dockerfile: deploy/Dockerfile.api
|
|
10
|
+
# image: ghcr.io/${{ github.repository_owner }}/my-api
|
|
11
|
+
# context: .
|
|
12
|
+
# secrets: inherit
|
|
13
|
+
|
|
14
|
+
name: Reusable docker build (single image)
|
|
15
|
+
|
|
16
|
+
on:
|
|
17
|
+
workflow_call:
|
|
18
|
+
inputs:
|
|
19
|
+
dockerfile:
|
|
20
|
+
required: true
|
|
21
|
+
type: string
|
|
22
|
+
image:
|
|
23
|
+
required: true
|
|
24
|
+
type: string
|
|
25
|
+
context:
|
|
26
|
+
required: false
|
|
27
|
+
type: string
|
|
28
|
+
default: .
|
|
29
|
+
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
packages: write
|
|
33
|
+
|
|
34
|
+
jobs:
|
|
35
|
+
build-push:
|
|
36
|
+
runs-on: ubuntu-latest
|
|
37
|
+
steps:
|
|
38
|
+
- uses: actions/checkout@v4
|
|
39
|
+
- uses: docker/setup-buildx-action@v3
|
|
40
|
+
- uses: docker/login-action@v3
|
|
41
|
+
with:
|
|
42
|
+
registry: ghcr.io
|
|
43
|
+
username: ${{ github.actor }}
|
|
44
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
45
|
+
- uses: docker/metadata-action@v5
|
|
46
|
+
id: meta
|
|
47
|
+
with:
|
|
48
|
+
images: ${{ inputs.image }}
|
|
49
|
+
tags: |
|
|
50
|
+
type=raw,value=main,enable={{is_default_branch}}
|
|
51
|
+
type=sha,prefix=main-,format=short
|
|
52
|
+
type=semver,pattern={{version}}
|
|
53
|
+
- uses: docker/build-push-action@v6
|
|
54
|
+
with:
|
|
55
|
+
context: ${{ inputs.context }}
|
|
56
|
+
file: ${{ inputs.dockerfile }}
|
|
57
|
+
push: true
|
|
58
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
59
|
+
labels: |
|
|
60
|
+
org.opencontainers.image.revision=${{ github.sha }}
|
|
61
|
+
org.opencontainers.image.source=https://github.com/${{ github.repository }}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Install cron for profile auto-update flags (.infra-profile + lib-env.sh).
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
|
+
cd "$SCRIPT_DIR"
|
|
6
|
+
# shellcheck source=lib-env.sh
|
|
7
|
+
source "$SCRIPT_DIR/lib-env.sh"
|
|
8
|
+
load_dotenv .env
|
|
9
|
+
|
|
10
|
+
PROFILE="${INFRA_PROFILE:-}"
|
|
11
|
+
ROLE="${ROLE:-}"
|
|
12
|
+
START_SCRIPT=""
|
|
13
|
+
UPDATE_SCRIPT=""
|
|
14
|
+
if [[ -f .infra-profile ]]; then
|
|
15
|
+
# shellcheck source=/dev/null
|
|
16
|
+
source .infra-profile
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
if [[ -z "$ROLE" && -n "$PROFILE" ]]; then
|
|
20
|
+
case "$PROFILE" in
|
|
21
|
+
api) ROLE=backend ;;
|
|
22
|
+
nodes) ROLE=workers ;;
|
|
23
|
+
gateway) ROLE=gateway ;;
|
|
24
|
+
esac
|
|
25
|
+
fi
|
|
26
|
+
if [[ -z "$ROLE" ]]; then
|
|
27
|
+
if compgen -G "start-*-gateway.sh" >/dev/null || [[ -f docker-compose.gateway.yml ]]; then
|
|
28
|
+
ROLE=gateway
|
|
29
|
+
elif compgen -G "start-*-nodes.sh" >/dev/null || [[ -f docker-compose.workers.yml ]]; then
|
|
30
|
+
ROLE=workers
|
|
31
|
+
else
|
|
32
|
+
ROLE=backend
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
UPDATE_SCRIPT="${UPDATE_SCRIPT:-update.sh}"
|
|
37
|
+
[[ -x "$SCRIPT_DIR/$UPDATE_SCRIPT" ]] || UPDATE_SCRIPT="update.sh"
|
|
38
|
+
|
|
39
|
+
cron_enabled=0
|
|
40
|
+
INTERVAL_MIN=30
|
|
41
|
+
CRON_OFFSET=0
|
|
42
|
+
|
|
43
|
+
case "$ROLE" in
|
|
44
|
+
backend|api)
|
|
45
|
+
ROLE=api
|
|
46
|
+
if role_auto_update_on API_AUTO_UPDATE; then cron_enabled=1; fi
|
|
47
|
+
INTERVAL_MIN="${API_AUTO_UPDATE_INTERVAL_MIN:-30}"
|
|
48
|
+
CRON_OFFSET=0
|
|
49
|
+
;;
|
|
50
|
+
workers|nodes)
|
|
51
|
+
ROLE=nodes
|
|
52
|
+
if role_auto_update_on NODES_AUTO_UPDATE; then cron_enabled=1; fi
|
|
53
|
+
INTERVAL_MIN="${NODES_AUTO_UPDATE_INTERVAL_MIN:-30}"
|
|
54
|
+
CRON_OFFSET=10
|
|
55
|
+
;;
|
|
56
|
+
gateway)
|
|
57
|
+
if role_auto_update_on UI_TESTNET_AUTO_UPDATE \
|
|
58
|
+
|| role_auto_update_on UI_MAINNET_AUTO_UPDATE \
|
|
59
|
+
|| role_auto_update_on GATEWAY_AUTO_UPDATE \
|
|
60
|
+
|| role_auto_update_on UI_AUTO_UPDATE; then
|
|
61
|
+
cron_enabled=1
|
|
62
|
+
fi
|
|
63
|
+
INTERVAL_MIN="${GATEWAY_AUTO_UPDATE_INTERVAL_MIN:-20}"
|
|
64
|
+
CRON_OFFSET=20
|
|
65
|
+
;;
|
|
66
|
+
*)
|
|
67
|
+
ROLE="${ROLE:-api}"
|
|
68
|
+
;;
|
|
69
|
+
esac
|
|
70
|
+
|
|
71
|
+
MARKER="# infra-auto-update:${ROLE}:${SCRIPT_DIR}"
|
|
72
|
+
build_cron_minutes() {
|
|
73
|
+
local cron_offset="${1:-0}"
|
|
74
|
+
local cron_interval="${2:-30}"
|
|
75
|
+
local m="$cron_offset" parts=()
|
|
76
|
+
while [[ "$m" -lt 60 ]]; do parts+=("$m"); m=$((m + cron_interval)); done
|
|
77
|
+
[[ "${#parts[@]}" -eq 0 ]] && parts=(0)
|
|
78
|
+
local IFS=,
|
|
79
|
+
echo "${parts[*]}"
|
|
80
|
+
}
|
|
81
|
+
CRON_MINUTES="$(build_cron_minutes "${CRON_OFFSET:-0}" "${INTERVAL_MIN:-30}")"
|
|
82
|
+
CRON_SCHED="${CRON_MINUTES} * * * *"
|
|
83
|
+
CRON_CMD="cd ${SCRIPT_DIR} && /bin/bash ${SCRIPT_DIR}/${UPDATE_SCRIPT} >/dev/null 2>&1"
|
|
84
|
+
dir_slug="$(basename "$SCRIPT_DIR" | tr -c 'A-Za-z0-9._-' '_')"
|
|
85
|
+
CRON_D_FILE="/etc/cron.d/infra-${ROLE}-${dir_slug}"
|
|
86
|
+
|
|
87
|
+
remove_user_crontab_marker() {
|
|
88
|
+
command -v crontab >/dev/null 2>&1 || return 0
|
|
89
|
+
local existing filtered
|
|
90
|
+
existing="$(crontab -l 2>/dev/null || true)"
|
|
91
|
+
filtered="$(printf '%s\n' "$existing" | grep -vF "$MARKER" || true)"
|
|
92
|
+
printf '%s\n' "$filtered" | sed '/^$/d' | crontab - 2>/dev/null || true
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if [[ "$cron_enabled" -eq 1 ]]; then
|
|
96
|
+
if [[ -d /etc/cron.d && -w /etc/cron.d ]]; then
|
|
97
|
+
cat >"$CRON_D_FILE" <<EOF
|
|
98
|
+
# Managed by infra install-auto-update.sh
|
|
99
|
+
# ${MARKER}
|
|
100
|
+
SHELL=/bin/bash
|
|
101
|
+
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
|
|
102
|
+
${CRON_SCHED} root ${CRON_CMD}
|
|
103
|
+
EOF
|
|
104
|
+
chmod 644 "$CRON_D_FILE"
|
|
105
|
+
echo "Installed $CRON_D_FILE (minutes ${CRON_MINUTES})"
|
|
106
|
+
remove_user_crontab_marker
|
|
107
|
+
elif command -v crontab >/dev/null 2>&1; then
|
|
108
|
+
existing="$(crontab -l 2>/dev/null || true)"
|
|
109
|
+
filtered="$(printf '%s\n' "$existing" | grep -vF "$MARKER" || true)"
|
|
110
|
+
{ printf '%s\n' "$filtered"; echo "${CRON_SCHED} ${CRON_CMD} ${MARKER}"; } | sed '/^$/d' | crontab -
|
|
111
|
+
echo "Installed user cron for $ROLE"
|
|
112
|
+
fi
|
|
113
|
+
else
|
|
114
|
+
rm -f "$CRON_D_FILE" 2>/dev/null || true
|
|
115
|
+
remove_user_crontab_marker
|
|
116
|
+
echo "Auto-update disabled for $ROLE"
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# Drop leftover cron markers from older product-specific installers
|
|
120
|
+
MARKER_LEGACY="# onchain-invoice-auto-update:${ROLE}:${SCRIPT_DIR}"
|
|
121
|
+
if command -v crontab >/dev/null 2>&1; then
|
|
122
|
+
existing="$(crontab -l 2>/dev/null || true)"
|
|
123
|
+
filtered="$(printf '%s\n' "$existing" | grep -vF "$MARKER_LEGACY" || true)"
|
|
124
|
+
[[ "$cron_enabled" -eq 1 ]] || printf '%s\n' "$filtered" | sed '/^$/d' | crontab - 2>/dev/null || true
|
|
125
|
+
fi
|
package/install.sh
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Infra packager — single wget entrypoint.
|
|
3
|
+
#
|
|
4
|
+
# wget -qO- $PACKAGER_RAW/install.sh | env INFRA_PROFILE=api PACKAGECONFIG_URL=... bash
|
|
5
|
+
#
|
|
6
|
+
# Product wrappers set PACKAGECONFIG_URL and INFRA_PROFILE.
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
PACKAGER_RAW="${PACKAGER_RAW:-${INFRA_RAW:-https://raw.githubusercontent.com/naiemk/vibed-infra/main}}"
|
|
10
|
+
DEST="${INSTALL_DIR:-.}"
|
|
11
|
+
PROFILE="${INFRA_PROFILE:-}"
|
|
12
|
+
|
|
13
|
+
while [[ $# -gt 0 ]]; do
|
|
14
|
+
case "$1" in
|
|
15
|
+
--profile) PROFILE="$2"; shift 2 ;;
|
|
16
|
+
--components)
|
|
17
|
+
IFS=',' read -ra _comps <<<"$2"
|
|
18
|
+
for c in "${_comps[@]}"; do
|
|
19
|
+
case "$c" in
|
|
20
|
+
app|backend|api) PROFILE=api; break ;;
|
|
21
|
+
workers|nodes) PROFILE=nodes; break ;;
|
|
22
|
+
gateway) PROFILE=gateway; break ;;
|
|
23
|
+
esac
|
|
24
|
+
done
|
|
25
|
+
shift 2
|
|
26
|
+
;;
|
|
27
|
+
--dest) DEST="$2"; shift 2 ;;
|
|
28
|
+
-h|--help)
|
|
29
|
+
echo "usage: install.sh [--profile api|nodes|gateway] [--dest DIR]"
|
|
30
|
+
echo "env: PACKAGECONFIG_URL PRODUCT_RAW PACKAGER_RAW INFRA_PROFILE INSTALL_DIR"
|
|
31
|
+
exit 0
|
|
32
|
+
;;
|
|
33
|
+
*) shift ;;
|
|
34
|
+
esac
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
mkdir -p "$DEST"
|
|
38
|
+
DEST="$(cd "$DEST" && pwd)"
|
|
39
|
+
|
|
40
|
+
# Bootstrap libs from packager (works when piped via wget|bash or local PACKAGER_RAW path).
|
|
41
|
+
INFRA_LIB="$DEST/.infra-lib"
|
|
42
|
+
mkdir -p "$INFRA_LIB"
|
|
43
|
+
_bootstrap_lib() {
|
|
44
|
+
local src="$1" dest="$2"
|
|
45
|
+
if [[ -f "$src" ]]; then
|
|
46
|
+
cp -f "$src" "$dest"
|
|
47
|
+
else
|
|
48
|
+
_fetch "${PACKAGER_RAW}/lib/$(basename "$dest")" "$dest"
|
|
49
|
+
fi
|
|
50
|
+
}
|
|
51
|
+
_fetch() {
|
|
52
|
+
local url="$1" out="$2"
|
|
53
|
+
if command -v curl >/dev/null 2>&1; then curl -fsSL "$url" -o "$out"; else wget -qO "$out" "$url"; fi
|
|
54
|
+
}
|
|
55
|
+
LOCAL_PACKAGER=0
|
|
56
|
+
if [[ "$PACKAGER_RAW" =~ ^/ ]] && [[ -d "$PACKAGER_RAW/lib" ]]; then
|
|
57
|
+
LOCAL_PACKAGER=1
|
|
58
|
+
fi
|
|
59
|
+
for lib in fetch.sh env.sh prompt.sh tls.sh load_config.py generate.py; do
|
|
60
|
+
if [[ "$LOCAL_PACKAGER" == "1" ]]; then
|
|
61
|
+
cp -f "${PACKAGER_RAW}/lib/${lib}" "${INFRA_LIB}/${lib}"
|
|
62
|
+
else
|
|
63
|
+
_fetch "${PACKAGER_RAW}/lib/${lib}" "${INFRA_LIB}/${lib}"
|
|
64
|
+
fi
|
|
65
|
+
done
|
|
66
|
+
for lib in start.sh update.sh install-auto-update.sh; do
|
|
67
|
+
if [[ "$LOCAL_PACKAGER" == "1" ]]; then
|
|
68
|
+
cp -f "${PACKAGER_RAW}/${lib}" "${DEST}/${lib}"
|
|
69
|
+
else
|
|
70
|
+
_fetch "${PACKAGER_RAW}/${lib}" "${DEST}/${lib}"
|
|
71
|
+
fi
|
|
72
|
+
chmod +x "${DEST}/${lib}"
|
|
73
|
+
done
|
|
74
|
+
|
|
75
|
+
# shellcheck source=/dev/null
|
|
76
|
+
source "${INFRA_LIB}/fetch.sh"
|
|
77
|
+
# shellcheck source=/dev/null
|
|
78
|
+
source "${INFRA_LIB}/prompt.sh"
|
|
79
|
+
# shellcheck source=/dev/null
|
|
80
|
+
source "${INFRA_LIB}/tls.sh"
|
|
81
|
+
|
|
82
|
+
cd "$DEST"
|
|
83
|
+
|
|
84
|
+
if [[ -z "$PROFILE" ]]; then
|
|
85
|
+
PROFILE="$(infra_pick_profile)"
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
PACKAGECONFIG_URL="${PACKAGECONFIG_URL:-${PACKAGE_CONFIG_URL:-}}"
|
|
89
|
+
if [[ -z "$PACKAGECONFIG_URL" ]]; then
|
|
90
|
+
echo "Set PACKAGECONFIG_URL to the product packageconfig.yaml (URL or path)" >&2
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
PC_LOCAL="$DEST/.packageconfig.yaml"
|
|
94
|
+
if [[ "$PACKAGECONFIG_URL" =~ ^/ ]]; then
|
|
95
|
+
cp -f "$PACKAGECONFIG_URL" "$PC_LOCAL"
|
|
96
|
+
else
|
|
97
|
+
infra_fetch "$PACKAGECONFIG_URL" "$PC_LOCAL"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
if [[ -z "${PRODUCT_RAW:-}" && -n "${ONCHAIN_INVOICE_RAW:-}" ]]; then
|
|
101
|
+
echo "warning: ONCHAIN_INVOICE_RAW is deprecated; use PRODUCT_RAW" >&2
|
|
102
|
+
PRODUCT_RAW="$ONCHAIN_INVOICE_RAW"
|
|
103
|
+
fi
|
|
104
|
+
if [[ -z "${PRODUCT_RAW:-}" ]]; then
|
|
105
|
+
PRODUCT_RAW="$(
|
|
106
|
+
python3 -c "
|
|
107
|
+
import sys
|
|
108
|
+
sys.path.insert(0, '${INFRA_LIB}')
|
|
109
|
+
from load_config import load_packageconfig
|
|
110
|
+
from pathlib import Path
|
|
111
|
+
print(load_packageconfig(Path('${PC_LOCAL}')).get('rawBase',''))
|
|
112
|
+
"
|
|
113
|
+
)"
|
|
114
|
+
fi
|
|
115
|
+
if [[ -z "$PRODUCT_RAW" ]]; then
|
|
116
|
+
echo "Set PRODUCT_RAW or packageconfig rawBase (product templates URL or path)" >&2
|
|
117
|
+
exit 1
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
echo "== infra install profile=$PROFILE dest=$DEST =="
|
|
121
|
+
|
|
122
|
+
_py_profile() {
|
|
123
|
+
python3 -c "
|
|
124
|
+
import sys, json
|
|
125
|
+
sys.path.insert(0, '${INFRA_LIB}')
|
|
126
|
+
from load_config import load_packageconfig, get_profile
|
|
127
|
+
from pathlib import Path
|
|
128
|
+
p = get_profile(load_packageconfig(Path('${PC_LOCAL}')), '${PROFILE}')
|
|
129
|
+
print(json.dumps(p))
|
|
130
|
+
"
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
PROF_JSON="$(_py_profile)"
|
|
134
|
+
ROLE="$(python3 -c "import json,sys; print(json.loads(sys.argv[1]).get('role','backend'))" "$PROF_JSON")"
|
|
135
|
+
START_SCRIPT="$(python3 -c "import json,sys; print(json.loads(sys.argv[1]).get('startScript','start.sh'))" "$PROF_JSON")"
|
|
136
|
+
UPDATE_SCRIPT="$(python3 -c "import json,sys; print(json.loads(sys.argv[1]).get('updateScript','update.sh'))" "$PROF_JSON")"
|
|
137
|
+
ENV_EXAMPLE="$(python3 -c "import json,sys; t=json.loads(sys.argv[1]).get('templates') or {}; print(t.get('envExample','.env.example'))" "$PROF_JSON")"
|
|
138
|
+
CONFIG_FILE="$(python3 -c "import json,sys; t=json.loads(sys.argv[1]).get('templates') or {}; print(t.get('config',''))" "$PROF_JSON")"
|
|
139
|
+
COMPOSE_TPL="$(python3 -c "import json,sys; t=json.loads(sys.argv[1]).get('templates') or {}; print(t.get('compose',''))" "$PROF_JSON")"
|
|
140
|
+
COMPOSE_MAINNET="$(python3 -c "import json,sys; t=json.loads(sys.argv[1]).get('templates') or {}; print(t.get('composeMainnet',''))" "$PROF_JSON")"
|
|
141
|
+
|
|
142
|
+
cp -f "${INFRA_LIB}/env.sh" "$DEST/lib-env.sh"
|
|
143
|
+
mkdir -p "$DEST/lib"
|
|
144
|
+
cp -f "${INFRA_LIB}/load_config.py" "$DEST/lib/"
|
|
145
|
+
cp -f "${INFRA_LIB}/generate.py" "$DEST/lib/"
|
|
146
|
+
|
|
147
|
+
if [[ -n "$ENV_EXAMPLE" ]]; then
|
|
148
|
+
infra_write_template "${PRODUCT_RAW}/${ENV_EXAMPLE}" "$DEST/${ENV_EXAMPLE}" 0
|
|
149
|
+
cp -f "$DEST/${ENV_EXAMPLE}" "$DEST/.env.example"
|
|
150
|
+
fi
|
|
151
|
+
if [[ -n "$CONFIG_FILE" ]]; then
|
|
152
|
+
infra_write_if_missing "${PRODUCT_RAW}/${CONFIG_FILE}" "$DEST/${CONFIG_FILE}" 0
|
|
153
|
+
infra_write_template "${PRODUCT_RAW}/${CONFIG_FILE}" "$DEST/${CONFIG_FILE}" 0
|
|
154
|
+
fi
|
|
155
|
+
if [[ -n "$COMPOSE_TPL" ]]; then
|
|
156
|
+
infra_write_if_missing "${PRODUCT_RAW}/${COMPOSE_TPL}" "$DEST/${COMPOSE_TPL}" 0
|
|
157
|
+
infra_write_template "${PRODUCT_RAW}/${COMPOSE_TPL}" "$DEST/${COMPOSE_TPL}" 0
|
|
158
|
+
fi
|
|
159
|
+
if [[ -n "$COMPOSE_MAINNET" ]]; then
|
|
160
|
+
infra_write_template "${PRODUCT_RAW}/${COMPOSE_MAINNET}" "$DEST/${COMPOSE_MAINNET}" 0
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
# Generic infra compose templates (optional USE_COMPOSE=1)
|
|
164
|
+
case "$ROLE" in
|
|
165
|
+
backend)
|
|
166
|
+
infra_write_template "${PACKAGER_RAW}/templates/docker-compose.backend.yml" "$DEST/docker-compose.backend.yml" 0
|
|
167
|
+
;;
|
|
168
|
+
workers)
|
|
169
|
+
infra_write_template "${PACKAGER_RAW}/templates/docker-compose.workers.yml" "$DEST/docker-compose.workers.yml" 0
|
|
170
|
+
;;
|
|
171
|
+
gateway)
|
|
172
|
+
infra_write_template "${PACKAGER_RAW}/templates/docker-compose.gateway.yml" "$DEST/docker-compose.gateway.yml" 0
|
|
173
|
+
;;
|
|
174
|
+
esac
|
|
175
|
+
|
|
176
|
+
if [[ -n "$START_SCRIPT" && "$START_SCRIPT" != "start.sh" ]]; then
|
|
177
|
+
infra_write_if_missing "${PRODUCT_RAW}/${START_SCRIPT}" "$DEST/${START_SCRIPT}" 1
|
|
178
|
+
infra_write_template "${PRODUCT_RAW}/${START_SCRIPT}" "$DEST/${START_SCRIPT}" 1
|
|
179
|
+
fi
|
|
180
|
+
if [[ -n "$UPDATE_SCRIPT" && "$UPDATE_SCRIPT" != "update.sh" ]]; then
|
|
181
|
+
infra_write_if_missing "${PRODUCT_RAW}/${UPDATE_SCRIPT}" "$DEST/${UPDATE_SCRIPT}" 1
|
|
182
|
+
infra_write_template "${PRODUCT_RAW}/${UPDATE_SCRIPT}" "$DEST/${UPDATE_SCRIPT}" 1
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
python3 -c "
|
|
186
|
+
import json, sys
|
|
187
|
+
extras = json.loads(sys.argv[1]).get('extras') or []
|
|
188
|
+
for e in extras:
|
|
189
|
+
print(e)
|
|
190
|
+
" "$PROF_JSON" | while read -r extra; do
|
|
191
|
+
[[ -n "$extra" ]] || continue
|
|
192
|
+
infra_write_if_missing "${PRODUCT_RAW}/${extra}" "$DEST/${extra}" 1
|
|
193
|
+
infra_write_template "${PRODUCT_RAW}/${extra}" "$DEST/${extra}" 1
|
|
194
|
+
done
|
|
195
|
+
|
|
196
|
+
if [[ "$ROLE" == "gateway" ]]; then
|
|
197
|
+
mkdir -p "$DEST/gateway/conf.d"
|
|
198
|
+
infra_write_if_missing "${PRODUCT_RAW}/gateway/nginx.conf" "$DEST/gateway/nginx.conf" 0
|
|
199
|
+
infra_write_template "${PRODUCT_RAW}/gateway/nginx.conf" "$DEST/gateway/nginx.conf" 0
|
|
200
|
+
if python3 "${INFRA_LIB}/generate.py" "$PC_LOCAL" --profile "$PROFILE" -o "$DEST/gateway/conf.d/domains.conf" 2>/dev/null; then
|
|
201
|
+
echo "generated: gateway/conf.d/domains.conf"
|
|
202
|
+
else
|
|
203
|
+
infra_write_if_missing "${PRODUCT_RAW}/gateway/conf.d/domains.conf" "$DEST/gateway/conf.d/domains.conf" 0
|
|
204
|
+
infra_write_template "${PRODUCT_RAW}/gateway/conf.d/domains.conf" "$DEST/gateway/conf.d/domains.conf" 0
|
|
205
|
+
fi
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
if [[ ! -f "$DEST/.env" ]]; then
|
|
209
|
+
cp "$DEST/.env.example" "$DEST/.env"
|
|
210
|
+
echo "created: $DEST/.env"
|
|
211
|
+
else
|
|
212
|
+
echo "exists: $DEST/.env"
|
|
213
|
+
fi
|
|
214
|
+
|
|
215
|
+
cat >"$DEST/.infra-profile" <<EOF
|
|
216
|
+
PROFILE=${PROFILE}
|
|
217
|
+
ROLE=${ROLE}
|
|
218
|
+
START_SCRIPT=${START_SCRIPT}
|
|
219
|
+
UPDATE_SCRIPT=${UPDATE_SCRIPT}
|
|
220
|
+
EOF
|
|
221
|
+
|
|
222
|
+
chmod +x "$DEST/${START_SCRIPT}" 2>/dev/null || true
|
|
223
|
+
INFRA_PROFILE="$PROFILE" ./install-auto-update.sh || true
|
|
224
|
+
|
|
225
|
+
if [[ "$ROLE" == "gateway" ]]; then
|
|
226
|
+
# shellcheck source=/dev/null
|
|
227
|
+
source "$DEST/lib-env.sh"
|
|
228
|
+
load_dotenv "$DEST/.env"
|
|
229
|
+
CERT_DIR="$(python3 -c "
|
|
230
|
+
import sys
|
|
231
|
+
sys.path.insert(0, '${INFRA_LIB}')
|
|
232
|
+
from load_config import load_packageconfig, get_profile
|
|
233
|
+
from pathlib import Path
|
|
234
|
+
p = get_profile(load_packageconfig(Path('${PC_LOCAL}')), '${PROFILE}')
|
|
235
|
+
sites = p.get('sites') or []
|
|
236
|
+
if not sites:
|
|
237
|
+
print('/etc/letsencrypt/live/example.com')
|
|
238
|
+
else:
|
|
239
|
+
s = sites[0]
|
|
240
|
+
print(s.get('tlsCertDir') or ('/etc/letsencrypt/live/' + s['host']))
|
|
241
|
+
")"
|
|
242
|
+
TLS_FULLCHAIN="${TLS_FULLCHAIN:-${CERT_DIR}/fullchain.pem}"
|
|
243
|
+
TLS_PRIVKEY="${TLS_PRIVKEY:-${CERT_DIR}/privkey.pem}"
|
|
244
|
+
mapfile -t DOMAIN_ARR < <(python3 -c "
|
|
245
|
+
import sys
|
|
246
|
+
sys.path.insert(0, '${INFRA_LIB}')
|
|
247
|
+
from load_config import load_packageconfig, get_profile
|
|
248
|
+
from pathlib import Path
|
|
249
|
+
p = get_profile(load_packageconfig(Path('${PC_LOCAL}')), '${PROFILE}')
|
|
250
|
+
for s in p.get('sites') or []:
|
|
251
|
+
print(s['host'])
|
|
252
|
+
for a in s.get('aliases') or []:
|
|
253
|
+
print(a)
|
|
254
|
+
")
|
|
255
|
+
infra_tls_offer_interactive "$TLS_FULLCHAIN" "$TLS_PRIVKEY" "${DOMAIN_ARR[@]}"
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
echo ""
|
|
259
|
+
echo "Install complete. Start: cd $DEST && ./${START_SCRIPT}"
|
|
Binary file
|