Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

install.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.

install.shBlame221 lines · 1 contributor
46d6165Claude1#!/usr/bin/env bash
2# =============================================================================
3# Gluecron one-command install.
4#
5# curl -sSL https://gluecron.com/install | bash
6#
7# What it does (every step is idempotent + verbose):
8# 1. Verify git, curl, jq are on PATH
9# 2. Resolve Gluecron host (default https://gluecron.com, env override)
10# 3. Locate Claude Desktop config (macOS / Linux / WSL)
11# 4. Sign in (env vars or interactive prompt) and capture session cookie
12# 5. Mint a fresh PAT via POST /api/v2/auth/install-token
13# 6. Merge a 'gluecron' MCP server entry into claude_desktop_config.json
14# 7. If we're inside a GitHub-origin repo, offer to import it
15# 8. Print a "you're done" success banner
16#
17# No third-party tools beyond plain curl, jq, git. Idempotent. Safe to re-run.
18# =============================================================================
19
20set -euo pipefail
21
22# ── pretty printers (match scripts/bootstrap-hetzner.sh style) ───────────────
23say() { printf "\n\033[1;34m> %s\033[0m\n" "$*"; }
24ok() { printf " \033[32mv\033[0m %s\n" "$*"; }
25warn() { printf " \033[33m!\033[0m %s\n" "$*"; }
26fail() { printf " \033[31mx\033[0m %s\n" "$*"; exit 1; }
27
28# ── 1. Prerequisites ────────────────────────────────────────────────────────
29say "[1/7] Checking prerequisites"
30MISSING=()
31for tool in git curl jq; do
32 if ! command -v "$tool" >/dev/null 2>&1; then
33 MISSING+=("$tool")
34 fi
35done
36if [ "${#MISSING[@]}" -gt 0 ]; then
37 warn "Missing tools: ${MISSING[*]}"
38 echo " Install them and re-run, e.g.:"
39 for tool in "${MISSING[@]}"; do
40 case "$tool" in
41 jq) echo " macOS: brew install jq | Debian/Ubuntu: sudo apt-get install -y jq" ;;
42 git) echo " macOS: xcode-select --install | Debian/Ubuntu: sudo apt-get install -y git" ;;
43 curl) echo " macOS: (preinstalled) | Debian/Ubuntu: sudo apt-get install -y curl" ;;
44 esac
45 done
46 fail "Please install the missing tools above and re-run."
47fi
48ok "git, curl, jq present"
49
50# ── 2. Host ─────────────────────────────────────────────────────────────────
51say "[2/7] Resolving Gluecron host"
52HOST="${GLUECRON_HOST:-https://gluecron.com}"
53HOST="${HOST%/}" # strip trailing slash
54ok "Using host: $HOST"
55
56# ── 3. Locate Claude Desktop config ────────────────────────────────────────
57say "[3/7] Locating Claude Desktop config"
58UNAME_S="$(uname -s 2>/dev/null || echo unknown)"
59MAC_CONF="$HOME/Library/Application Support/Claude/claude_desktop_config.json"
60LINUX_CONF="$HOME/.config/Claude/claude_desktop_config.json"
61CLAUDE_CONF=""
62
63case "$UNAME_S" in
64 Darwin*)
65 CLAUDE_CONF="$MAC_CONF"
66 ;;
67 Linux*)
68 # WSL detection
69 if grep -qi microsoft /proc/version 2>/dev/null; then
70 if [ -d "$HOME/.config/Claude" ]; then
71 CLAUDE_CONF="$LINUX_CONF"
72 else
73 CLAUDE_CONF="$LINUX_CONF"
74 warn "WSL detected but no existing Claude config — using $CLAUDE_CONF"
75 fi
76 else
77 CLAUDE_CONF="$LINUX_CONF"
78 fi
79 ;;
80 *)
81 warn "Unknown platform '$UNAME_S' — defaulting to macOS path."
82 CLAUDE_CONF="$MAC_CONF"
83 ;;
84esac
85
86mkdir -p "$(dirname "$CLAUDE_CONF")"
87if [ ! -f "$CLAUDE_CONF" ]; then
88 echo '{}' > "$CLAUDE_CONF"
89 ok "Created empty $CLAUDE_CONF"
90else
91 ok "Found existing $CLAUDE_CONF"
92fi
93
94# ── 4. Sign in ──────────────────────────────────────────────────────────────
95say "[4/7] Signing in to $HOST"
96USERNAME="${GLUECRON_USERNAME:-}"
97PASSWORD="${GLUECRON_PASSWORD:-}"
98if [ -z "$USERNAME" ]; then
99 if [ ! -t 0 ]; then
100 fail "GLUECRON_USERNAME is unset and stdin is not a TTY. Re-run as: GLUECRON_USERNAME=you GLUECRON_PASSWORD=*** curl -sSL $HOST/install | bash"
101 fi
102 printf " Gluecron username: "
103 read -r USERNAME
104fi
105if [ -z "$PASSWORD" ]; then
106 if [ ! -t 0 ]; then
107 fail "GLUECRON_PASSWORD is unset and stdin is not a TTY."
108 fi
109 printf " Gluecron password: "
110 stty -echo
111 read -r PASSWORD
112 stty echo
113 printf "\n"
114fi
115
116COOKIE_JAR="$(mktemp -t gluecron-cookies.XXXXXX)"
117trap 'rm -f "$COOKIE_JAR"' EXIT
118
119LOGIN_BODY="username=$(printf %s "$USERNAME" | jq -sRr @uri)&password=$(printf %s "$PASSWORD" | jq -sRr @uri)"
120LOGIN_STATUS=$(
121 curl -sS -o /dev/null -w "%{http_code}" \
122 -X POST \
123 -H "Content-Type: application/x-www-form-urlencoded" \
124 -c "$COOKIE_JAR" \
125 --data "$LOGIN_BODY" \
126 "$HOST/login" || echo 000
127)
128if ! grep -q "session" "$COOKIE_JAR" 2>/dev/null; then
129 fail "Login failed (HTTP $LOGIN_STATUS). Check username/password."
130fi
131ok "Signed in as $USERNAME"
132
133# ── 5. Mint PAT ─────────────────────────────────────────────────────────────
134say "[5/7] Minting a fresh PAT"
135SHORT_TS=$(date +%s | tail -c 7)
136MINT_BODY=$(jq -nc --arg name "gluecron-install-$SHORT_TS" --arg scope "admin" \
137 '{name: $name, scope: $scope}')
138MINT_RESPONSE=$(
139 curl -sS \
140 -X POST \
141 -H "Content-Type: application/json" \
142 -b "$COOKIE_JAR" \
143 --data "$MINT_BODY" \
144 "$HOST/api/v2/auth/install-token"
145)
146PAT=$(printf %s "$MINT_RESPONSE" | jq -r '.token // empty')
147if [ -z "$PAT" ]; then
148 ERR=$(printf %s "$MINT_RESPONSE" | jq -r '.error // .hint // .')
149 fail "PAT mint failed: $ERR"
150fi
151PAT_PREFIX=$(printf %s "$PAT" | cut -c1-12)
152ok "Created PAT $PAT_PREFIX..."
153
154# ── 6. Merge MCP server entry into Claude Desktop config ───────────────────
155say "[6/7] Wiring Claude Desktop -> Gluecron MCP"
156TMP_CONF="$(mktemp -t gluecron-conf.XXXXXX)"
157jq --arg url "$HOST/mcp" --arg auth "Bearer $PAT" '
158 . as $root
159 | (.mcpServers // {}) as $servers
160 | $root
161 | .mcpServers = ($servers + {
162 "gluecron": {
163 "transport": "http",
164 "url": $url,
165 "headers": { "Authorization": $auth }
166 }
167 })
168' "$CLAUDE_CONF" > "$TMP_CONF"
169mv "$TMP_CONF" "$CLAUDE_CONF"
170ok "Updated $CLAUDE_CONF"
171
172# ── 7. Optional repo import ─────────────────────────────────────────────────
173say "[7/7] Repo import (optional)"
174if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
175 ORIGIN_URL=$(git config --get remote.origin.url || true)
176 if [ -n "$ORIGIN_URL" ] && printf %s "$ORIGIN_URL" | grep -q "github.com"; then
177 if [ -t 0 ]; then
178 printf " Import this repo (%s) to Gluecron? [y/N] " "$ORIGIN_URL"
179 read -r ANSWER
180 else
181 ANSWER="${GLUECRON_IMPORT:-n}"
182 fi
183 case "$ANSWER" in
184 y|Y|yes|YES)
185 IMPORT_BODY="repo_url=$(printf %s "$ORIGIN_URL" | jq -sRr @uri)"
186 IMPORT_STATUS=$(
187 curl -sS -o /dev/null -w "%{http_code}" \
188 -X POST \
189 -H "Authorization: Bearer $PAT" \
190 -H "Content-Type: application/x-www-form-urlencoded" \
191 --data "$IMPORT_BODY" \
192 "$HOST/import/github/repo" || echo 000
193 )
194 case "$IMPORT_STATUS" in
195 2*|3*) ok "Import dispatched (HTTP $IMPORT_STATUS)" ;;
196 *) warn "Import returned HTTP $IMPORT_STATUS — you can re-run from $HOST/import" ;;
197 esac
198 ;;
199 *)
200 ok "Skipped import"
201 ;;
202 esac
203 else
204 ok "No GitHub origin detected — skipping import"
205 fi
206else
207 ok "Not inside a git repo — skipping import"
208fi
209
210# ── Done ────────────────────────────────────────────────────────────────────
211printf "\n"
212printf "\033[1;32m============================================================\033[0m\n"
213printf "\033[1;32m GLUECRON INSTALL COMPLETE\033[0m\n"
214printf "\033[1;32m============================================================\033[0m\n"
215printf " Host: %s\n" "$HOST"
216printf " PAT prefix: %s...\n" "$PAT_PREFIX"
217printf " MCP config: %s\n" "$CLAUDE_CONF"
218printf "\n"
219printf " v Done. Restart Claude Desktop and try:\n"
220printf " \"Open a PR on this repo with a one-line README fix\"\n"
221printf "\n"