CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
deploy-crontech.sh
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| 7738c58 | 1 | #!/usr/bin/env bash |
| 2 | # ============================================================================ | |
| 3 | # Gluecron deploy to Crontech bare-metal | |
| 4 | # ============================================================================ | |
| 5 | # Idempotent. Run as root on the Crontech box (45.76.171.37). Re-runnable. | |
| 6 | # | |
| 7 | # Usage: | |
| 8 | # bash scripts/deploy-crontech.sh | |
| 9 | # | |
| 10 | # Environment (prompted if not set): | |
| 11 | # DATABASE_URL Neon Postgres pooled connection string | |
| 12 | # SITE_ADMIN_USERNAME GitHub-style username for the bootstrap admin | |
| 13 | # (default: ccantynz-alt) | |
| 14 | # SITE_DOMAIN Public domain for HTTPS (default: gluecron.com) | |
| 15 | # | |
| 16 | # What it does: | |
| 17 | # 1. Installs Bun if missing | |
| 18 | # 2. bun install (frozen lockfile) | |
| 19 | # 3. Ensures /data/repos exists for bare git repos | |
| 20 | # 4. Writes /etc/gluecron.env (chmod 600) | |
| 21 | # 5. Runs DB migrations | |
| 22 | # 6. Writes /etc/systemd/system/gluecron.service + enables it | |
| 23 | # 7. Adds <domain> { reverse_proxy localhost:3000 } to Caddyfile + reloads | |
| 24 | # 8. Smoke-tests localhost:3000/healthz | |
| 25 | # ============================================================================ | |
| 26 | ||
| 27 | set -euo pipefail | |
| 28 | ||
| 29 | REPO_DIR=${REPO_DIR:-/opt/gluecron} | |
| 30 | BARE_REPOS=${BARE_REPOS:-/data/repos} | |
| 5d82f61 | 31 | PORT=${PORT:-3010} |
| 7738c58 | 32 | SITE_DOMAIN=${SITE_DOMAIN:-gluecron.com} |
| 33 | SITE_ADMIN_USERNAME=${SITE_ADMIN_USERNAME:-ccantynz-alt} | |
| 34 | ||
| 35 | cd "$REPO_DIR" || { | |
| 36 | echo "ERROR: $REPO_DIR doesn't exist. Clone the repo first:" | |
| 37 | echo " git clone https://github.com/ccantynz-alt/Gluecron.com.git $REPO_DIR" | |
| 38 | exit 1 | |
| 39 | } | |
| 40 | ||
| 41 | echo "==> Pulling latest source" | |
| 42 | git pull --ff-only origin main | |
| 43 | ||
| 44 | # ---- DATABASE_URL prompt ------------------------------------------------- | |
| 45 | if [ -z "${DATABASE_URL:-}" ]; then | |
| 46 | if [ -f /etc/gluecron.env ] && grep -q '^DATABASE_URL=' /etc/gluecron.env; then | |
| 47 | DATABASE_URL=$(grep '^DATABASE_URL=' /etc/gluecron.env | cut -d= -f2-) | |
| 48 | echo "==> Reusing existing DATABASE_URL from /etc/gluecron.env" | |
| 49 | else | |
| 50 | echo | |
| 51 | echo "Paste the Neon DATABASE_URL (postgresql://... ?sslmode=require ...):" | |
| 52 | read -r DATABASE_URL | |
| 53 | fi | |
| 54 | fi | |
| 55 | ||
| 56 | APP_BASE_URL=${APP_BASE_URL:-https://${SITE_DOMAIN}} | |
| 57 | ||
| 58 | # ---- Bun install --------------------------------------------------------- | |
| 59 | if ! command -v bun >/dev/null && [ ! -x "$HOME/.bun/bin/bun" ]; then | |
| 60 | echo "==> Installing Bun" | |
| 61 | curl -fsSL https://bun.sh/install | bash | |
| 62 | fi | |
| 63 | export PATH="$HOME/.bun/bin:$PATH" | |
| 64 | echo "==> bun: $(bun --version)" | |
| 65 | ||
| 66 | # ---- Dependencies -------------------------------------------------------- | |
| 67 | echo "==> Installing project dependencies" | |
| 68 | bun install --frozen-lockfile | |
| 69 | ||
| 70 | # ---- Bare-repo dir ------------------------------------------------------- | |
| 71 | mkdir -p "$BARE_REPOS" | |
| 72 | chmod 755 "$BARE_REPOS" | |
| 73 | ||
| 74 | # ---- Env file ------------------------------------------------------------ | |
| 75 | echo "==> Writing /etc/gluecron.env" | |
| 76 | umask 077 | |
| 77 | cat > /etc/gluecron.env <<EOF | |
| 78 | DATABASE_URL=${DATABASE_URL} | |
| 79 | APP_BASE_URL=${APP_BASE_URL} | |
| 80 | SITE_ADMIN_USERNAME=${SITE_ADMIN_USERNAME} | |
| 81 | GIT_REPOS_PATH=${BARE_REPOS} | |
| 82 | PORT=${PORT} | |
| 83 | DEMO_SEED_ON_BOOT=1 | |
| 84 | EMAIL_PROVIDER=log | |
| ad6af41 | 85 | EMAIL_FROM="gluecron <no-reply@${SITE_DOMAIN}>" |
| 7738c58 | 86 | NODE_ENV=production |
| 87 | EOF | |
| 88 | chmod 600 /etc/gluecron.env | |
| 89 | umask 022 | |
| 90 | ||
| 91 | # ---- Migrations ---------------------------------------------------------- | |
| 92 | echo "==> Running database migrations" | |
| 93 | set -a; source /etc/gluecron.env; set +a | |
| 94 | bun run src/db/migrate.ts || { | |
| 95 | echo "WARNING: migration command failed — continuing (it may be idempotent already-applied)" | |
| 96 | } | |
| 97 | ||
| 98 | # ---- systemd unit -------------------------------------------------------- | |
| 99 | BUN_BIN="$(command -v bun)" | |
| 100 | echo "==> Writing /etc/systemd/system/gluecron.service" | |
| 101 | cat > /etc/systemd/system/gluecron.service <<EOF | |
| 102 | [Unit] | |
| 103 | Description=Gluecron — AI-native code intelligence platform | |
| 104 | After=network-online.target | |
| 105 | Wants=network-online.target | |
| 106 | ||
| 107 | [Service] | |
| 108 | Type=simple | |
| 109 | User=root | |
| 110 | WorkingDirectory=${REPO_DIR} | |
| 111 | EnvironmentFile=/etc/gluecron.env | |
| 112 | ExecStart=${BUN_BIN} run src/index.ts | |
| 113 | Restart=always | |
| 114 | RestartSec=5 | |
| 115 | StandardOutput=journal | |
| 116 | StandardError=journal | |
| 117 | SyslogIdentifier=gluecron | |
| 118 | LimitNOFILE=65536 | |
| 119 | ||
| 120 | [Install] | |
| 121 | WantedBy=multi-user.target | |
| 122 | EOF | |
| 123 | ||
| 124 | echo "==> Reloading systemd + (re)starting gluecron" | |
| 125 | systemctl daemon-reload | |
| 126 | systemctl enable gluecron >/dev/null 2>&1 || true | |
| 127 | systemctl restart gluecron | |
| 128 | ||
| 129 | # ---- Caddy ---------------------------------------------------------------- | |
| 130 | CADDYFILE="/etc/caddy/Caddyfile" | |
| 131 | if [ -f "$CADDYFILE" ]; then | |
| 132 | if ! grep -q "^${SITE_DOMAIN} *{" "$CADDYFILE"; then | |
| 133 | echo "==> Adding ${SITE_DOMAIN} to Caddyfile" | |
| 134 | cat >> "$CADDYFILE" <<EOF | |
| 135 | ||
| 136 | ${SITE_DOMAIN} { | |
| 137 | reverse_proxy localhost:${PORT} | |
| 138 | } | |
| 139 | www.${SITE_DOMAIN} { | |
| 140 | redir https://${SITE_DOMAIN}{uri} permanent | |
| 141 | } | |
| 142 | EOF | |
| 143 | if command -v caddy >/dev/null; then | |
| 144 | caddy validate --config "$CADDYFILE" --adapter caddyfile && systemctl reload caddy | |
| 145 | else | |
| 146 | systemctl reload caddy || systemctl restart caddy || true | |
| 147 | fi | |
| 148 | else | |
| 149 | echo "==> ${SITE_DOMAIN} already in Caddyfile (skipping)" | |
| 150 | fi | |
| 151 | else | |
| 152 | echo "WARNING: $CADDYFILE not found — skipping Caddy config" | |
| 153 | echo " You'll need to add a reverse_proxy to localhost:${PORT} manually" | |
| 154 | fi | |
| 155 | ||
| 156 | # ---- Smoke test ---------------------------------------------------------- | |
| 157 | echo | |
| 158 | echo "==> Waiting for /healthz to respond (up to 30s)" | |
| 159 | for i in $(seq 1 10); do | |
| 160 | if curl -sf "http://localhost:${PORT}/healthz" >/dev/null; then | |
| 161 | echo "✓ gluecron is up at http://localhost:${PORT}" | |
| 162 | echo | |
| 163 | echo "============================================================" | |
| 164 | echo "DONE. Next steps:" | |
| 165 | echo " 1. DNS: ensure A record for ${SITE_DOMAIN} → this server's IP" | |
| 166 | echo " 2. Visit: https://${SITE_DOMAIN}" | |
| 167 | echo " 3. Register account '${SITE_ADMIN_USERNAME}' → instant site admin" | |
| 168 | echo | |
| 169 | echo "Useful commands:" | |
| 170 | echo " systemctl status gluecron" | |
| 171 | echo " journalctl -u gluecron -f" | |
| 172 | echo " bash scripts/deploy-crontech.sh # re-deploy after git pull" | |
| 173 | echo "============================================================" | |
| 174 | exit 0 | |
| 175 | fi | |
| 176 | sleep 3 | |
| 177 | done | |
| 178 | ||
| 179 | echo | |
| 180 | echo "✗ gluecron did not respond on /healthz after 30s" | |
| 181 | echo "Diagnose with:" | |
| 182 | echo " systemctl status gluecron" | |
| 183 | echo " journalctl -u gluecron -n 100 --no-pager" | |
| 184 | exit 1 |