forked from crtahlin/swarm_connect
-
Notifications
You must be signed in to change notification settings - Fork 0
305 lines (292 loc) · 18.2 KB
/
Copy pathdeploy.yml
File metadata and controls
305 lines (292 loc) · 18.2 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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
---
name: deploy
on:
push:
branches:
- master
- main
- dev
workflow_dispatch:
jobs:
build:
name: build
# Deploy targets are data, not code: set the repository variable DEPLOY_RUNNERS
# to a JSON array of runner labels to add or retire a host without a code
# change. Unset -> the single existing host, i.e. current behaviour.
# fail-fast is off so one unavailable host cannot block the others.
strategy:
fail-fast: false
matrix:
runner: ${{ fromJSON(vars.DEPLOY_RUNNERS || '["pljudfbee1"]') }}
runs-on: [self-hosted, "${{ matrix.runner }}"]
# Use staging environment for dev branch, production for main branch
environment: ${{ github.ref == 'refs/heads/dev' && 'staging' || (github.ref == 'refs/heads/main' && 'production' || '') }}
steps:
- name: checkout
uses: actions/checkout@v4
with:
submodules: recursive
fetch-depth: 0 # Need full history for commit count
- name: generate version
id: version
run: |
COMMIT_COUNT=$(git rev-list --count --no-merges HEAD)
SHORT_HASH=$(git rev-parse --short HEAD)
VERSION="0.${COMMIT_COUNT}.${SHORT_HASH}"
echo "version=${VERSION}" >> $GITHUB_OUTPUT
echo "Generated version: ${VERSION}"
- name: build docker image
run: docker build --build-arg VERSION=${{ steps.version.outputs.version }} -t swarm_connect:$GITHUB_REF_NAME .
- name: write env file for dev
if: github.ref == 'refs/heads/dev'
run: |
mkdir -p /opt/swarm_connect_dev_data
cat > /opt/swarm_connect_dev.env << 'EOF'
X402_ENABLED=${{ vars.X402_ENABLED || 'false' }}
X402_PAY_TO_ADDRESS=${{ vars.X402_PAY_TO_ADDRESS || '' }}
X402_NETWORK=${{ vars.X402_NETWORK || 'base-sepolia' }}
X402_FACILITATOR_URL=${{ vars.X402_FACILITATOR_URL || 'https://x402.org/facilitator' }}
X402_FREE_TIER_ENABLED=${{ vars.X402_FREE_TIER_ENABLED || 'true' }}
STAMP_POOL_ENABLED=${{ vars.STAMP_POOL_ENABLED || 'false' }}
STAMP_POOL_RESERVE_SMALL=${{ vars.STAMP_POOL_RESERVE_SMALL || '1' }}
STAMP_POOL_RESERVE_MEDIUM=${{ vars.STAMP_POOL_RESERVE_MEDIUM || '1' }}
STAMP_POOL_RESERVE_LARGE=${{ vars.STAMP_POOL_RESERVE_LARGE || '0' }}
STAMP_POOL_MAX_PURCHASES_PER_HOUR=${{ vars.STAMP_POOL_MAX_PURCHASES_PER_HOUR || '10' }}
POOL_DAILY_ALLOWANCES=${{ vars.POOL_DAILY_ALLOWANCES }}
POOL_DEFAULT_DAILY_ALLOWANCE=${{ vars.POOL_DEFAULT_DAILY_ALLOWANCE || '-1' }}
X402_POOL_MARKUP_PERCENT=${{ vars.X402_POOL_MARKUP_PERCENT || '100' }}
X402_ALLOW_TESTNET_PAID_BYPASS=${{ vars.X402_ALLOW_TESTNET_PAID_BYPASS || 'false' }}
X402_MAX_STAMP_BZZ=${{ vars.X402_MAX_STAMP_BZZ || '5.0' }}
STAMP_DAILY_BZZ_PER_CALLER=${{ vars.STAMP_DAILY_BZZ_PER_CALLER || '0.5' }}
TRUSTED_PROXY_HOPS=${{ vars.TRUSTED_PROXY_HOPS || '1' }}
STAMP_POOL_CHECK_INTERVAL_SECONDS=${{ vars.STAMP_POOL_CHECK_INTERVAL_SECONDS || '900' }}
STAMP_POOL_MIN_TTL_HOURS=${{ vars.STAMP_POOL_MIN_TTL_HOURS || '24' }}
STAMP_POOL_TOPUP_HOURS=${{ vars.STAMP_POOL_TOPUP_HOURS || '168' }}
STAMP_POOL_DEFAULT_DURATION_HOURS=${{ vars.STAMP_POOL_DEFAULT_DURATION_HOURS || '168' }}
STAMP_POOL_STATE_FILE=${{ vars.STAMP_POOL_STATE_FILE || 'data/pool_state.json' }}
STAMP_OWNERSHIP_FILE=${{ vars.STAMP_OWNERSHIP_FILE || 'data/stamp_owners.json' }}
NOTARY_ENABLED=${{ vars.NOTARY_ENABLED || 'false' }}
NOTARY_PRIVATE_KEY=${{ secrets.NOTARY_PRIVATE_KEY || '' }}
CORS_ALLOWED_ORIGINS=${{ vars.CORS_ALLOWED_ORIGINS || '*' }}
CORS_ALLOW_CREDENTIALS=${{ vars.CORS_ALLOW_CREDENTIALS || 'false' }}
MAX_UPLOAD_SIZE_MB=${{ vars.MAX_UPLOAD_SIZE_MB || '10' }}
CHUNK_UPLOAD_ENABLED=${{ vars.CHUNK_UPLOAD_ENABLED || 'false' }}
CHUNK_UPLOAD_MAX_BYTES_PER_REQUEST=${{ vars.CHUNK_UPLOAD_MAX_BYTES_PER_REQUEST || '4104' }}
CHUNK_UPLOAD_FREE_TIER_ENABLED=${{ vars.CHUNK_UPLOAD_FREE_TIER_ENABLED || 'true' }}
CHUNK_UPLOAD_FREE_TIER_MB_PER_DAY=${{ vars.CHUNK_UPLOAD_FREE_TIER_MB_PER_DAY || '100' }}
X402_BANDWIDTH_USD_PER_GB=${{ vars.X402_BANDWIDTH_USD_PER_GB || '0.10' }}
BANDWIDTH_CREDIT_MIN_TOPUP_MB=${{ vars.BANDWIDTH_CREDIT_MIN_TOPUP_MB || '100' }}
BANDWIDTH_CREDIT_MAX_TOPUP_MB=${{ vars.BANDWIDTH_CREDIT_MAX_TOPUP_MB || '1000000' }}
BANDWIDTH_CREDIT_STATE_FILE=${{ vars.BANDWIDTH_CREDIT_STATE_FILE || 'data/bandwidth_credit.json' }}
DEBUG_ALLOWED_ADDRESSES=${{ vars.DEBUG_ALLOWED_ADDRESSES || '' }}
DEBUG_SIG_MAX_AGE_SECONDS=${{ vars.DEBUG_SIG_MAX_AGE_SECONDS || '300' }}
RATE_LIMIT_ENABLED=${{ vars.RATE_LIMIT_ENABLED || 'true' }}
RATE_LIMIT_PER_MINUTE=${{ vars.RATE_LIMIT_PER_MINUTE || '60' }}
RATE_LIMIT_BURST=${{ vars.RATE_LIMIT_BURST || '10' }}
METRICS_ENABLED=${{ vars.METRICS_ENABLED || 'true' }}
METRICS_BALANCE_POLL_SECONDS=${{ vars.METRICS_BALANCE_POLL_SECONDS || '60' }}
GNOSIS_RPC_URL=${{ vars.GNOSIS_RPC_URL || '' }}
GNOSIS_PRIVATE_KEY=${{ secrets.GNOSIS_PRIVATE_KEY || '' }}
GNOSIS_CHAIN_ID=${{ vars.GNOSIS_CHAIN_ID || '100' }}
POSTAGE_STAMP_CONTRACT_ADDRESS=${{ vars.POSTAGE_STAMP_CONTRACT_ADDRESS || '' }}
BZZ_TOKEN_ADDRESS=${{ vars.BZZ_TOKEN_ADDRESS || '' }}
STAMP_PURCHASE_FOR_OTHERS_ENABLED=${{ vars.STAMP_PURCHASE_FOR_OTHERS_ENABLED || 'false' }}
STAMP_FOR_OTHERS_REQUIRE_WHITELIST=${{ vars.STAMP_FOR_OTHERS_REQUIRE_WHITELIST || 'true' }}
STAMP_FOR_OTHERS_OWNER_WHITELIST=${{ vars.STAMP_FOR_OTHERS_OWNER_WHITELIST || '' }}
STAMP_FOR_OTHERS_MAX_DEPTH=${{ vars.STAMP_FOR_OTHERS_MAX_DEPTH || '22' }}
STAMP_FOR_OTHERS_MAX_BZZ=${{ vars.STAMP_FOR_OTHERS_MAX_BZZ || '1.0' }}
STAMP_FOR_OTHERS_MAX_DURATION_HOURS=${{ vars.STAMP_FOR_OTHERS_MAX_DURATION_HOURS || '168' }}
STAMP_FOR_OTHERS_FREE_TIER_ENABLED=${{ vars.STAMP_FOR_OTHERS_FREE_TIER_ENABLED || 'false' }}
GNOSIS_XDAI_CRITICAL_THRESHOLD=${{ vars.GNOSIS_XDAI_CRITICAL_THRESHOLD || '0.005' }}
GNOSIS_XDAI_WARN_THRESHOLD=${{ vars.GNOSIS_XDAI_WARN_THRESHOLD || '0.05' }}
GNOSIS_XBZZ_WARN_THRESHOLD=${{ vars.GNOSIS_XBZZ_WARN_THRESHOLD || '0.5' }}
X402_XDAI_WARN_THRESHOLD=${{ vars.X402_XDAI_WARN_THRESHOLD || '0.1' }}
X402_XBZZ_WARN_THRESHOLD=${{ vars.X402_XBZZ_WARN_THRESHOLD || '10.0' }}
X402_CHEQUEBOOK_WARN_THRESHOLD=${{ vars.X402_CHEQUEBOOK_WARN_THRESHOLD || '4.0' }}
GATEWAY_ENVIRONMENT=development
EOF
- name: write env file for main
if: github.ref == 'refs/heads/main'
run: |
mkdir -p /opt/swarm_connect_data
cat > /opt/swarm_connect.env << 'EOF'
X402_ENABLED=${{ vars.X402_ENABLED || 'false' }}
X402_PAY_TO_ADDRESS=${{ vars.X402_PAY_TO_ADDRESS || '' }}
X402_NETWORK=${{ vars.X402_NETWORK || 'base' }}
X402_FACILITATOR_URL=${{ vars.X402_FACILITATOR_URL || 'https://x402.org/facilitator' }}
X402_FREE_TIER_ENABLED=${{ vars.X402_FREE_TIER_ENABLED || 'true' }}
STAMP_POOL_ENABLED=${{ vars.STAMP_POOL_ENABLED || 'false' }}
STAMP_POOL_RESERVE_SMALL=${{ vars.STAMP_POOL_RESERVE_SMALL || '1' }}
STAMP_POOL_RESERVE_MEDIUM=${{ vars.STAMP_POOL_RESERVE_MEDIUM || '1' }}
STAMP_POOL_RESERVE_LARGE=${{ vars.STAMP_POOL_RESERVE_LARGE || '0' }}
STAMP_POOL_MAX_PURCHASES_PER_HOUR=${{ vars.STAMP_POOL_MAX_PURCHASES_PER_HOUR || '10' }}
POOL_DAILY_ALLOWANCES=${{ vars.POOL_DAILY_ALLOWANCES }}
POOL_DEFAULT_DAILY_ALLOWANCE=${{ vars.POOL_DEFAULT_DAILY_ALLOWANCE || '-1' }}
X402_POOL_MARKUP_PERCENT=${{ vars.X402_POOL_MARKUP_PERCENT || '100' }}
X402_ALLOW_TESTNET_PAID_BYPASS=${{ vars.X402_ALLOW_TESTNET_PAID_BYPASS || 'false' }}
X402_MAX_STAMP_BZZ=${{ vars.X402_MAX_STAMP_BZZ || '5.0' }}
STAMP_DAILY_BZZ_PER_CALLER=${{ vars.STAMP_DAILY_BZZ_PER_CALLER || '0.5' }}
TRUSTED_PROXY_HOPS=${{ vars.TRUSTED_PROXY_HOPS || '1' }}
STAMP_POOL_CHECK_INTERVAL_SECONDS=${{ vars.STAMP_POOL_CHECK_INTERVAL_SECONDS || '900' }}
STAMP_POOL_MIN_TTL_HOURS=${{ vars.STAMP_POOL_MIN_TTL_HOURS || '24' }}
STAMP_POOL_TOPUP_HOURS=${{ vars.STAMP_POOL_TOPUP_HOURS || '168' }}
STAMP_POOL_DEFAULT_DURATION_HOURS=${{ vars.STAMP_POOL_DEFAULT_DURATION_HOURS || '168' }}
STAMP_POOL_STATE_FILE=${{ vars.STAMP_POOL_STATE_FILE || 'data/pool_state.json' }}
STAMP_OWNERSHIP_FILE=${{ vars.STAMP_OWNERSHIP_FILE || 'data/stamp_owners.json' }}
NOTARY_ENABLED=${{ vars.NOTARY_ENABLED || 'false' }}
NOTARY_PRIVATE_KEY=${{ secrets.NOTARY_PRIVATE_KEY || '' }}
CORS_ALLOWED_ORIGINS=${{ vars.CORS_ALLOWED_ORIGINS || '*' }}
CORS_ALLOW_CREDENTIALS=${{ vars.CORS_ALLOW_CREDENTIALS || 'false' }}
MAX_UPLOAD_SIZE_MB=${{ vars.MAX_UPLOAD_SIZE_MB || '10' }}
CHUNK_UPLOAD_ENABLED=${{ vars.CHUNK_UPLOAD_ENABLED || 'false' }}
CHUNK_UPLOAD_MAX_BYTES_PER_REQUEST=${{ vars.CHUNK_UPLOAD_MAX_BYTES_PER_REQUEST || '4104' }}
CHUNK_UPLOAD_FREE_TIER_ENABLED=${{ vars.CHUNK_UPLOAD_FREE_TIER_ENABLED || 'true' }}
CHUNK_UPLOAD_FREE_TIER_MB_PER_DAY=${{ vars.CHUNK_UPLOAD_FREE_TIER_MB_PER_DAY || '100' }}
X402_BANDWIDTH_USD_PER_GB=${{ vars.X402_BANDWIDTH_USD_PER_GB || '0.10' }}
BANDWIDTH_CREDIT_MIN_TOPUP_MB=${{ vars.BANDWIDTH_CREDIT_MIN_TOPUP_MB || '100' }}
BANDWIDTH_CREDIT_MAX_TOPUP_MB=${{ vars.BANDWIDTH_CREDIT_MAX_TOPUP_MB || '1000000' }}
BANDWIDTH_CREDIT_STATE_FILE=${{ vars.BANDWIDTH_CREDIT_STATE_FILE || 'data/bandwidth_credit.json' }}
DEBUG_ALLOWED_ADDRESSES=${{ vars.DEBUG_ALLOWED_ADDRESSES || '' }}
DEBUG_SIG_MAX_AGE_SECONDS=${{ vars.DEBUG_SIG_MAX_AGE_SECONDS || '300' }}
RATE_LIMIT_ENABLED=${{ vars.RATE_LIMIT_ENABLED || 'true' }}
RATE_LIMIT_PER_MINUTE=${{ vars.RATE_LIMIT_PER_MINUTE || '60' }}
RATE_LIMIT_BURST=${{ vars.RATE_LIMIT_BURST || '10' }}
METRICS_ENABLED=${{ vars.METRICS_ENABLED || 'true' }}
METRICS_BALANCE_POLL_SECONDS=${{ vars.METRICS_BALANCE_POLL_SECONDS || '60' }}
GNOSIS_RPC_URL=${{ vars.GNOSIS_RPC_URL || '' }}
GNOSIS_PRIVATE_KEY=${{ secrets.GNOSIS_PRIVATE_KEY || '' }}
GNOSIS_CHAIN_ID=${{ vars.GNOSIS_CHAIN_ID || '100' }}
POSTAGE_STAMP_CONTRACT_ADDRESS=${{ vars.POSTAGE_STAMP_CONTRACT_ADDRESS || '' }}
BZZ_TOKEN_ADDRESS=${{ vars.BZZ_TOKEN_ADDRESS || '' }}
STAMP_PURCHASE_FOR_OTHERS_ENABLED=${{ vars.STAMP_PURCHASE_FOR_OTHERS_ENABLED || 'false' }}
STAMP_FOR_OTHERS_REQUIRE_WHITELIST=${{ vars.STAMP_FOR_OTHERS_REQUIRE_WHITELIST || 'true' }}
STAMP_FOR_OTHERS_OWNER_WHITELIST=${{ vars.STAMP_FOR_OTHERS_OWNER_WHITELIST || '' }}
STAMP_FOR_OTHERS_MAX_DEPTH=${{ vars.STAMP_FOR_OTHERS_MAX_DEPTH || '22' }}
STAMP_FOR_OTHERS_MAX_BZZ=${{ vars.STAMP_FOR_OTHERS_MAX_BZZ || '1.0' }}
STAMP_FOR_OTHERS_MAX_DURATION_HOURS=${{ vars.STAMP_FOR_OTHERS_MAX_DURATION_HOURS || '168' }}
STAMP_FOR_OTHERS_FREE_TIER_ENABLED=${{ vars.STAMP_FOR_OTHERS_FREE_TIER_ENABLED || 'false' }}
GNOSIS_XDAI_CRITICAL_THRESHOLD=${{ vars.GNOSIS_XDAI_CRITICAL_THRESHOLD || '0.005' }}
GNOSIS_XDAI_WARN_THRESHOLD=${{ vars.GNOSIS_XDAI_WARN_THRESHOLD || '0.05' }}
GNOSIS_XBZZ_WARN_THRESHOLD=${{ vars.GNOSIS_XBZZ_WARN_THRESHOLD || '0.5' }}
X402_XDAI_WARN_THRESHOLD=${{ vars.X402_XDAI_WARN_THRESHOLD || '0.1' }}
X402_XBZZ_WARN_THRESHOLD=${{ vars.X402_XBZZ_WARN_THRESHOLD || '10.0' }}
X402_CHEQUEBOOK_WARN_THRESHOLD=${{ vars.X402_CHEQUEBOOK_WARN_THRESHOLD || '4.0' }}
GATEWAY_ENVIRONMENT=main
EOF
- name: deploy
run: |
# Clone into a temporary directory and swap it in only on success.
#
# This used to be `rm -rf swarm_connect && git clone`, which destroys
# the working checkout BEFORE knowing whether the replacement can be
# obtained. On 2026-09-02 the clone failed and the host was left with
# no /opt/swarm_connect at all: the running containers were unaffected,
# but nothing could be deployed or recreated until it was restored by
# hand. A deploy failure should leave the previous state intact.
#
# Authenticated, because anonymous git operations are rate limited per
# IP and every deploy clones the full repository. When that limit is
# reached GitHub answers the upload-pack POST with 401 — ref discovery
# still returns 200, so the repo looks reachable right up to the point
# it fails, and git reports it as a missing username rather than as
# throttling. The token is the job's own, scoped to this repository.
cd /opt/
rm -rf swarm_connect.new
git clone -b "$GITHUB_REF_NAME" \
"https://x-access-token:${{ github.token }}@github.com/datafund/swarm_connect.git" \
swarm_connect.new
rm -rf swarm_connect.old
if [ -d swarm_connect ]; then mv swarm_connect swarm_connect.old; fi
mv swarm_connect.new swarm_connect
rm -rf swarm_connect.old
cd swarm_connect
cat > .env << EOF
GRAFANA_CLOUD_PROM_USERNAME=${{ secrets.GRAFANA_CLOUD_PROM_USERNAME }}
GRAFANA_CLOUD_API_TOKEN=${{ secrets.GRAFANA_CLOUD_API_TOKEN }}
EOF
# Host-specific compose settings (Bee endpoints, COMPOSE_PROFILES, p2p
# ports, ...). Lives outside the clone so it survives the rm -rf above.
# Absent on hosts that need no overrides -> defaults in docker-compose.yml
# apply and the deploy is unchanged.
if [ -f /opt/swarm_connect_host.env ]; then
echo "Applying host overrides from /opt/swarm_connect_host.env"
cat /opt/swarm_connect_host.env >> .env
fi
# Pin the compose project name. By default it is derived from the
# directory name, which makes container AND VOLUME names depend on a
# filesystem path: the Bee volumes are swarm_connect_bee-data and
# swarm_connect_bee-dev-data. Deploying from a different directory
# would therefore look for volumes that do not exist, create empty
# ones, and start Bee with no keystore — a new wallet, with the funded
# ones orphaned.
#
# Pinning it makes the path irrelevant to identity. Written last so
# nothing in the host env file can override it.
echo "COMPOSE_PROJECT_NAME=swarm_connect" >> .env
# Recreate ONLY the service this branch owns. `docker compose up -d
# --force-recreate` with no argument recreates every service, so a push
# to dev restarted production and vice versa. That is not free: the
# stamp pool buys a reserve on startup, and one such restart cost about
# 0.7 BZZ for nothing.
#
# --no-deps keeps it from dragging the other environment's containers
# in through depends_on.
if [ "$GITHUB_REF_NAME" = "dev" ]; then
BRANCH_SERVICE=provenance_gateway_dev
else
BRANCH_SERVICE=provenance_gateway
fi
echo "Recreating $BRANCH_SERVICE (branch $GITHUB_REF_NAME)"
docker compose up -d --force-recreate --no-deps "$BRANCH_SERVICE"
# Host-level services (the bundled Bee nodes and the metrics agent) live
# in their own file because they are not branch-specific. Same project
# name, so they share the gateways' network and keep their existing
# container and volume names. No --force-recreate here: the Bee nodes
# are stateful and restarting them costs peer connections and sync
# progress, so anything already running is left alone.
docker compose -f docker-compose.host.yml up -d
# Alloy is recreated every deploy, and it has to be.
#
# Its config is bind-mounted as a single FILE. A file bind mount pins
# the inode, and `git pull` replaces the file rather than editing it
# in place — so the container goes on reading the old inode forever.
# Compose will not help: the service definition is unchanged, so
# `up -d` sees nothing to do.
#
# The result was silent. Every edit to config.alloy since
# 2026-08-25 deployed successfully and changed nothing, because the
# running Alloy had never seen any of them. The host label and the
# env() -> sys.env() migration were both dead on arrival, and the Bee
# scrape targets would have been too.
#
# Alloy holds no state worth keeping and starts in about a second, so
# recreating it unconditionally is cheap and removes the whole class
# of problem. The alternative — mounting the directory — fixes the
# inode issue but still needs a restart, because Alloy reads its
# config at startup.
docker compose -f docker-compose.host.yml up -d --force-recreate --no-deps alloy
- name: post-deploy diagnostics
run: |
echo "=== Container status ==="
docker ps -a --filter "name=swarm_connect" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
echo ""
echo "=== Waiting 10s for container startup ==="
sleep 10
echo ""
if [ "$GITHUB_REF_NAME" = "dev" ]; then
CONTAINER="swarm_connect-provenance_gateway_dev-1"
PORT=8900
ENV_FILE="/opt/swarm_connect_dev.env"
else
CONTAINER="swarm_connect-provenance_gateway-1"
PORT=8899
ENV_FILE="/opt/swarm_connect.env"
fi
echo "=== Container logs (last 30 lines) ==="
docker logs "$CONTAINER" --tail 30 2>&1 || echo "Container not found"
echo ""
echo "=== Health check from localhost ==="
curl -s "http://localhost:${PORT}/health" 2>&1 || echo "Cannot reach container"
echo ""
echo "=== Env file (redacted) ==="
cat "$ENV_FILE" 2>/dev/null | grep -v PRIVATE_KEY | head -20 || echo "Env file not found"