-
Notifications
You must be signed in to change notification settings - Fork 80
169 lines (150 loc) · 8.91 KB
/
Copy pathdeploy-cloud.yml
File metadata and controls
169 lines (150 loc) · 8.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
name: Deploy Cloud
on:
workflow_dispatch:
inputs:
tag:
description: "Docker image tag to deploy (default: latest)"
required: false
default: "latest"
type: string
# One deploy at a time. On 2026-09-25 two runs of the same commit started two
# minutes apart; the second failed in `docker pull` ("unable to lease
# content") while the first was swapping containers. Worse cases are easy to
# picture: a second run recording the first's half-started release as its
# rollback point, or sharing /tmp/anythingmcp-deploy with it. Queue instead.
concurrency:
group: deploy-cloud
cancel-in-progress: false
jobs:
deploy:
name: Deploy to Cloud Droplet
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Copy config files to server
uses: appleboy/scp-action@v1.0.0
with:
host: ${{ secrets.CLOUD_HOST }}
username: root
key: ${{ secrets.CLOUD_SSH_KEY }}
source: "docker-compose.cloud.yml,deploy/cloud/Caddyfile,deploy/cloud/release.sh,deploy/cloud/uptime-probe.sh,deploy/cloud/systemd/anythingmcp-probe.service,deploy/cloud/systemd/anythingmcp-probe.timer,deploy/cloud/stuck-users-report.sh,deploy/cloud/systemd/anythingmcp-stuck-report.service,deploy/cloud/systemd/anythingmcp-stuck-report.timer,deploy/cloud/trial-report.sh,deploy/cloud/systemd/anythingmcp-trial-report.service,deploy/cloud/systemd/anythingmcp-trial-report.timer,deploy/motis/Dockerfile,deploy/motis/config.yml,deploy/motis/entrypoint.sh"
target: /tmp/anythingmcp-deploy
overwrite: true
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
env:
CRON_SECRET: ${{ secrets.ONBOARDING_CRON_SECRET }}
LICENSE_SERVICE_TOKEN: ${{ secrets.LICENSE_SERVICE_TOKEN }}
with:
host: ${{ secrets.CLOUD_HOST }}
username: root
key: ${{ secrets.CLOUD_SSH_KEY }}
envs: CRON_SECRET,LICENSE_SERVICE_TOKEN
# The action's default is 10m. A release can take longer (up to
# 5 min waiting for the new containers, the site checks, up to
# 2 min draining the old ones), and a timeout mid-release would
# leave it half done — the old colour still serving, but the job
# unable to say so.
command_timeout: 30m
script: |
set -e
cd /opt/anythingmcp-cloud
# Pull the requested tag AND refresh :latest, since
# docker-compose.cloud.yml references :latest. Without this
# the local :latest tag can point to an older image.
docker pull helpcodeai/anythingmcp:${{ inputs.tag }}
docker pull helpcodeai/anythingmcp:latest
# docker-compose.cloud.yml and the Caddyfile are NOT copied here:
# release.sh installs them, after saving the ones in use, so a
# failed release can put them back. The MOTIS build context is
# copied below; `docker compose up` builds it if the pinned image
# tag is missing (fresh droplet or a bumped tag), otherwise the
# existing image is reused.
# Uptime probe: a systemd timer on this host, every minute, mailing
# on a change of state. It is here and not only in GitHub Actions
# because the scheduled workflow ran three times in its first ten
# hours — GitHub's cron is best-effort — and the failure mode we
# have is the backend dying while the host stays up, which a timer
# on the host sees within a minute. Reads DOMAIN, SMTP_* and
# UPTIME_ALERT_TO from this directory's .env; with no
# UPTIME_ALERT_TO it only logs to the journal.
install -m 755 /tmp/anythingmcp-deploy/deploy/cloud/uptime-probe.sh ./uptime-probe.sh
install -m 644 /tmp/anythingmcp-deploy/deploy/cloud/systemd/anythingmcp-probe.service /etc/systemd/system/
install -m 644 /tmp/anythingmcp-deploy/deploy/cloud/systemd/anythingmcp-probe.timer /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now anythingmcp-probe.timer
systemctl is-active anythingmcp-probe.timer
# Weekly stuck-users report, Monday 07:00 Europe/Berlin: orgs whose
# tool calls have never succeeded, connectors failing for everyone,
# signups that never got going. Read-only on the database. Mails
# REPORT_TO (else UPTIME_ALERT_TO) through this directory's SMTP_*;
# with neither set it writes to the journal. The timer does not fire
# on install, only on the next Monday.
install -m 755 /tmp/anythingmcp-deploy/deploy/cloud/stuck-users-report.sh ./stuck-users-report.sh
install -m 644 /tmp/anythingmcp-deploy/deploy/cloud/systemd/anythingmcp-stuck-report.service /etc/systemd/system/
install -m 644 /tmp/anythingmcp-deploy/deploy/cloud/systemd/anythingmcp-stuck-report.timer /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now anythingmcp-stuck-report.timer
systemctl is-active anythingmcp-stuck-report.timer
# Daily trial report, 08:05 Europe/Berlin: licences that became paid,
# trials ending in the next 3 days and how far each workspace got,
# trials that ended yesterday. Read-only, same mail route as above.
install -m 755 /tmp/anythingmcp-deploy/deploy/cloud/trial-report.sh ./trial-report.sh
install -m 644 /tmp/anythingmcp-deploy/deploy/cloud/systemd/anythingmcp-trial-report.service /etc/systemd/system/
install -m 644 /tmp/anythingmcp-deploy/deploy/cloud/systemd/anythingmcp-trial-report.timer /etc/systemd/system/
systemctl daemon-reload
systemctl enable --now anythingmcp-trial-report.timer
systemctl is-active anythingmcp-trial-report.timer
mkdir -p ./deploy/motis
cp /tmp/anythingmcp-deploy/deploy/motis/* ./deploy/motis/
rm -rf ./deploy/cloud/db-rest
# Keep CRON_SECRET in the server .env in sync with the repo
# secret so the onboarding-reminders cron can authenticate.
# Upsert (replace-or-append) without disturbing other vars.
touch .env
if grep -q '^CRON_SECRET=' .env; then
sed -i "s#^CRON_SECRET=.*#CRON_SECRET=${CRON_SECRET}#" .env
else
echo "CRON_SECRET=${CRON_SECRET}" >> .env
fi
# Same for the licence service token, which anythingmcp.com checks
# to tell this backend apart from the public when it asks for a
# trial. Must match LICENSE_SERVICE_TOKEN on the website droplet.
if [ -n "${LICENSE_SERVICE_TOKEN}" ]; then
if grep -q '^LICENSE_SERVICE_TOKEN=' .env; then
sed -i "s#^LICENSE_SERVICE_TOKEN=.*#LICENSE_SERVICE_TOKEN=${LICENSE_SERVICE_TOKEN}#" .env
else
echo "LICENSE_SERVICE_TOKEN=${LICENSE_SERVICE_TOKEN}" >> .env
fi
fi
trap 'rm -rf /tmp/anythingmcp-deploy' EXIT
# Blue/green: start the new release next to the one serving, check
# it privately and then publicly, switch Caddy, stop the old one —
# see deploy/cloud/release.sh. A release that fails its checks is
# removed before it serves anything and this job goes red with the
# previous release still serving. The uptime probe is no longer
# silenced for the deploy: a healthy one has no downtime to hide.
# release.sh silences it itself only when it has to fall back to
# stop-then-start (no room on the host for two backends).
bash /tmp/anythingmcp-deploy/deploy/cloud/release.sh
docker image prune -f
# Keep the eight most recent app images and drop the rest. On
# 2026-09-19 the droplet held 35 of them going back four months,
# ~1.3 GB each — the bulk of the 34 GB under /var/lib/containerd,
# growing by one image per deploy.
#
# Safe because these are a local cache, not an archive: every tag
# is still on Docker Hub, so an older rollback target comes back
# with `docker pull helpcodeai/anythingmcp:<tag>`. Eight covers
# roughly six weeks of builds. An image a container is using
# cannot be removed — docker refuses, and the `|| true` keeps the
# deploy green when it does.
docker images helpcodeai/anythingmcp \
--format '{{.CreatedAt}}\t{{.Repository}}:{{.Tag}}' \
| sort -r | tail -n +9 | cut -f2 \
| while read -r img; do
[ "$img" = "helpcodeai/anythingmcp:latest" ] && continue
echo "pruning cached image $img"
docker rmi "$img" >/dev/null 2>&1 || true
done
echo "Deployment complete"