Repository navigation
Expand file tree
/
Copy pathdeploy.sh
More file actions
182 lines (159 loc) · 5.91 KB
/
Copy pathdeploy.sh
File metadata and controls
182 lines (159 loc) · 5.91 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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env bash
# One-click Docker deploy for AutoCoder on a VPS with DuckDNS + Traefik + Let's Encrypt.
# Prompts for domain, DuckDNS token, email, repo, branch, and target install path.
set -euo pipefail
if [[ $EUID -ne 0 ]]; then
echo "Please run as root (sudo)." >&2
exit 1
fi
prompt_required() {
local var_name="$1" prompt_msg="$2"
local value
while true; do
read -r -p "$prompt_msg: " value
if [[ -n "$value" ]]; then
printf -v "$var_name" '%s' "$value"
export "${var_name?}"
return
fi
echo "Value cannot be empty."
done
}
echo "=== AutoCoder VPS Deploy (Docker + Traefik + DuckDNS + Let's Encrypt) ==="
prompt_required DOMAIN "Enter your DuckDNS domain (e.g., myapp.duckdns.org)"
prompt_required DUCKDNS_TOKEN "Enter your DuckDNS token"
prompt_required LETSENCRYPT_EMAIL "Enter email for Let's Encrypt notifications"
# Extract subdomain for DuckDNS API (it expects just the subdomain, not full domain)
if [[ "${DOMAIN}" == *.duckdns.org ]]; then
DUCKDNS_SUBDOMAIN="${DOMAIN%.duckdns.org}"
else
echo "WARNING: Domain '${DOMAIN}' does not end with .duckdns.org."
echo "DuckDNS API requires a duckdns.org subdomain."
read -r -p "Enter just the DuckDNS subdomain (without .duckdns.org): " DUCKDNS_SUBDOMAIN
if [[ -z "$DUCKDNS_SUBDOMAIN" ]]; then
echo "DuckDNS subdomain cannot be empty." >&2
exit 1
fi
fi
read -r -p "Git repo URL [https://github.com/heidi-dang/autocoder.git]: " REPO_URL
REPO_URL=${REPO_URL:-https://github.com/heidi-dang/autocoder.git}
read -r -p "Git branch to deploy [main]: " DEPLOY_BRANCH
DEPLOY_BRANCH=${DEPLOY_BRANCH:-main}
read -r -p "Install path [/opt/autocoder]: " APP_DIR
APP_DIR=${APP_DIR:-/opt/autocoder}
read -r -p "App internal port (container) [8888]: " APP_PORT
APP_PORT=${APP_PORT:-8888}
echo
echo "Domain: $DOMAIN"
echo "Repo: $REPO_URL"
echo "Branch: $DEPLOY_BRANCH"
echo "Path: $APP_DIR"
echo
read -r -p "Proceed? [y/N]: " CONFIRM
if [[ "${CONFIRM,,}" != "y" ]]; then
echo "Aborted."
exit 1
fi
ensure_packages() {
echo "Installing Docker & prerequisites..."
# Detect OS type
if [[ -f /etc/os-release ]]; then
. /etc/os-release
OS_ID="$ID"
OS_LIKE="${ID_LIKE:-}"
else
echo "ERROR: Cannot detect OS type. /etc/os-release not found."
exit 1
fi
# Detect Docker distribution for repository URL
if [[ "$OS_ID" == "debian" ]]; then
DOCKER_DIST="debian"
elif [[ "$OS_ID" == "ubuntu" ]]; then
DOCKER_DIST="ubuntu"
elif [[ "$OS_LIKE" == *"ubuntu"* ]]; then
DOCKER_DIST="ubuntu"
elif [[ "$OS_LIKE" == *"debian"* ]]; then
DOCKER_DIST="debian"
else
DOCKER_DIST="ubuntu"
fi
# Check for Debian/Ubuntu family
if [[ "$OS_ID" == "debian" || "$OS_ID" == "ubuntu" ]] || [[ "$OS_LIKE" == *"debian"* || "$OS_LIKE" == *"ubuntu"* ]]; then
echo "Detected Debian/Ubuntu-based system, using apt-get..."
apt-get update -y
apt-get install -y ca-certificates curl git gnupg
install -m 0755 -d /etc/apt/keyrings
if [[ ! -f /etc/apt/keyrings/docker.gpg ]]; then
curl -fsSL https://download.docker.com/linux/$DOCKER_DIST/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$DOCKER_DIST \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list
apt-get update -y
fi
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
systemctl enable --now docker
else
echo "ERROR: Unsupported OS: $OS_ID"
echo "This script currently supports Debian/Ubuntu-based systems only."
echo "Please install Docker manually or add support for your distribution."
exit 1
fi
}
configure_duckdns() {
echo "Configuring DuckDNS..."
local cron_file="/etc/cron.d/duckdns"
local token_file="/etc/duckdns_token"
echo "$DUCKDNS_TOKEN" > "$token_file"
chmod 600 "$token_file"
cat > "$cron_file" <<EOF
*/5 * * * * root curl -fsS --get --data-urlencode "domains=$DUCKDNS_SUBDOMAIN" --data-urlencode "token@$token_file" --data-urlencode "ip=" "https://www.duckdns.org/update" >/var/log/duckdns.log 2>&1
EOF
chmod 600 "$cron_file"
# Run once immediately
curl -fsS --get --data-urlencode "domains=$DUCKDNS_SUBDOMAIN" --data-urlencode "token@$token_file" --data-urlencode "ip=" "https://www.duckdns.org/update" >/var/log/duckdns.log 2>&1
}
clone_repo() {
if [[ -d "$APP_DIR/.git" ]]; then
echo "Repo already exists, pulling latest..."
git -C "$APP_DIR" fetch --all
git -C "$APP_DIR" checkout "$DEPLOY_BRANCH"
git -C "$APP_DIR" pull --ff-only origin "$DEPLOY_BRANCH"
else
echo "Cloning repository..."
mkdir -p "$APP_DIR"
git clone --branch "$DEPLOY_BRANCH" "$REPO_URL" "$APP_DIR"
fi
}
write_env() {
echo "Writing deploy env (.env.deploy)..."
cat > "$APP_DIR/.env.deploy" <<EOF
DOMAIN=$DOMAIN
LETSENCRYPT_EMAIL=$LETSENCRYPT_EMAIL
APP_PORT=$APP_PORT
EOF
echo "DuckDNS token stored in /etc/cron.d/duckdns (not in repo)."
}
prepare_ssl_storage() {
mkdir -p "$APP_DIR/letsencrypt"
touch "$APP_DIR/letsencrypt/acme.json"
chmod 600 "$APP_DIR/letsencrypt/acme.json"
}
run_compose() {
echo "Bringing up stack with Traefik reverse proxy and TLS..."
cd "$APP_DIR"
docker network inspect traefik-proxy >/dev/null 2>&1 || docker network create traefik-proxy
docker compose --env-file .env.deploy -f docker-compose.yml -f docker-compose.traefik.yml pull || true
docker compose --env-file .env.deploy -f docker-compose.yml -f docker-compose.traefik.yml up -d --build
}
ensure_packages
configure_duckdns
clone_repo
write_env
prepare_ssl_storage
run_compose
echo
echo "Deployment complete."
echo "Check: http://$DOMAIN (will redirect to https after cert is issued)."
echo "Logs: docker compose -f docker-compose.yml -f docker-compose.traefik.yml logs -f"
echo "To update: rerun this script; it will git pull and restart."