| 135dfe5 | | | 1 | #!/bin/bash |
| 135dfe5 | | | 2 | # Sets up the Grove hub on a fresh DigitalOcean droplet. |
| 5f0fbcf | | | 3 | # The hub is a full Grove instance: Mononoke + Bridge + API + Hub API + Web + Registry. |
| 5f0fbcf | | | 4 | # |
| 135dfe5 | | | 5 | # Usage: bash setup.sh <domain> |
| 135dfe5 | | | 6 | # e.g. bash setup.sh grove.host |
| 135dfe5 | | | 7 | |
| 135dfe5 | | | 8 | set -euo pipefail |
| 135dfe5 | | | 9 | |
| 135dfe5 | | | 10 | DOMAIN="${1:?Usage: setup.sh <domain>}" |
| 135dfe5 | | | 11 | SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" |
| 5f0fbcf | | | 12 | GROVE_DIR="/opt/grove" |
| 135dfe5 | | | 13 | |
| 5f0fbcf | | | 14 | mkdir -p "$GROVE_DIR" |
| 5f0fbcf | | | 15 | mkdir -p "$GROVE_DIR/data/mononoke" |
| 5f0fbcf | | | 16 | mkdir -p "$GROVE_DIR/data/api" |
| 135dfe5 | | | 17 | |
| 5f0fbcf | | | 18 | # ── Copy configs ────────────────────────────────────────────────── |
| 5f0fbcf | | | 19 | |
| 5f0fbcf | | | 20 | cp "$SCRIPT_DIR/Caddyfile" "$GROVE_DIR/" |
| 5f0fbcf | | | 21 | cp "$SCRIPT_DIR/docker-compose.yml" "$GROVE_DIR/" |
| 5f0fbcf | | | 22 | |
| 5f0fbcf | | | 23 | # ── Generate JWT secret and write .env ──────────────────────────── |
| 135dfe5 | | | 24 | |
| 135dfe5 | | | 25 | JWT_SECRET=$(openssl rand -base64 32) |
| 5f0fbcf | | | 26 | cat > "$GROVE_DIR/.env" <<EOF |
| 135dfe5 | | | 27 | DOMAIN=$DOMAIN |
| 135dfe5 | | | 28 | JWT_SECRET=$JWT_SECRET |
| 135dfe5 | | | 29 | EOF |
| 135dfe5 | | | 30 | |
| 0d60b20 | | | 31 | # ── Generate TLS certificates for Mononoke ───────────────────── |
| 0d60b20 | | | 32 | |
| 0d60b20 | | | 33 | GROVE_TLS_DIR=/data/grove/tls "$SCRIPT_DIR/../scripts/generate-certs.sh" "$DOMAIN" |
| 0d60b20 | | | 34 | |
| 5f0fbcf | | | 35 | # ── Write Mononoke config ──────────────────────────────────────── |
| 5f0fbcf | | | 36 | |
| 5f0fbcf | | | 37 | mkdir -p "$GROVE_DIR/config/mononoke/common" |
| 36387cc | | | 38 | mkdir -p "$GROVE_DIR/config/mononoke/repo_definitions/grove" |
| 36387cc | | | 39 | mkdir -p "$GROVE_DIR/config/mononoke/repos/grove" |
| 5f0fbcf | | | 40 | |
| 5f0fbcf | | | 41 | cat > "$GROVE_DIR/config/mononoke/common/common.toml" <<'TOML' |
| 5f0fbcf | | | 42 | enable_http_control_api = true |
| 5f0fbcf | | | 43 | |
| 5f0fbcf | | | 44 | [internal_identity] |
| 5f0fbcf | | | 45 | identity_type = "SERVICE" |
| 5f0fbcf | | | 46 | identity_data = "grove" |
| 5f0fbcf | | | 47 | TOML |
| 5f0fbcf | | | 48 | |
| 5f0fbcf | | | 49 | cat > "$GROVE_DIR/config/mononoke/common/storage.toml" <<'TOML' |
| 5f0fbcf | | | 50 | [default] |
| 5f0fbcf | | | 51 | |
| 5f0fbcf | | | 52 | [default.metadata] |
| 5f0fbcf | | | 53 | [default.metadata.local] |
| 5f0fbcf | | | 54 | local_db_path = "/data/mononoke/metadata" |
| 5f0fbcf | | | 55 | |
| 5f0fbcf | | | 56 | [default.blobstore] |
| 5f0fbcf | | | 57 | [default.blobstore.blob_files] |
| 5f0fbcf | | | 58 | path = "/data/mononoke/blobs" |
| 5f0fbcf | | | 59 | |
| 5f0fbcf | | | 60 | [default.mutable_blobstore] |
| 5f0fbcf | | | 61 | [default.mutable_blobstore.blob_files] |
| 5f0fbcf | | | 62 | path = "/data/mononoke/mutable_blobs" |
| 5f0fbcf | | | 63 | TOML |
| 5f0fbcf | | | 64 | |
| 36387cc | | | 65 | cat > "$GROVE_DIR/config/mononoke/repo_definitions/grove/server.toml" <<'TOML' |
| 5f0fbcf | | | 66 | repo_id = 0 |
| 36387cc | | | 67 | repo_name = "grove" |
| 36387cc | | | 68 | repo_config = "grove" |
| 5f0fbcf | | | 69 | enabled = true |
| 6c9fcae | | | 70 | hipster_acl = "default" |
| 5f0fbcf | | | 71 | TOML |
| 5f0fbcf | | | 72 | |
| 36387cc | | | 73 | cat > "$GROVE_DIR/config/mononoke/repos/grove/server.toml" <<'TOML' |
| 5f0fbcf | | | 74 | storage_config = "default" |
| 5f0fbcf | | | 75 | |
| 0d60b20 | | | 76 | [hook_manager_params] |
| 0d60b20 | | | 77 | disable_acl_checker = true |
| 0d60b20 | | | 78 | |
| 5f0fbcf | | | 79 | [push] |
| 5f0fbcf | | | 80 | pure_push_allowed = true |
| 5f0fbcf | | | 81 | |
| 5f0fbcf | | | 82 | [pushrebase] |
| 5f0fbcf | | | 83 | rewritedates = false |
| 5f0fbcf | | | 84 | |
| 5f0fbcf | | | 85 | [source_control_service] |
| 5f0fbcf | | | 86 | permit_writes = true |
| 5f0fbcf | | | 87 | permit_service_writes = true |
| 5f0fbcf | | | 88 | |
| 6c9fcae | | | 89 | [git_configs.git_bundle_uri_config.uri_generator_type.local_fs] |
| 6c9fcae | | | 90 | |
| 0d60b20 | | | 91 | [infinitepush] |
| 0d60b20 | | | 92 | allow_writes = true |
| 0d60b20 | | | 93 | |
| 0d60b20 | | | 94 | [commit_cloud_config] |
| 0d60b20 | | | 95 | |
| 5f0fbcf | | | 96 | [derived_data_config] |
| 5f0fbcf | | | 97 | enabled_config_name = "default" |
| 5f0fbcf | | | 98 | |
| 5f0fbcf | | | 99 | [derived_data_config.available_configs.default] |
| 5f0fbcf | | | 100 | types = [ |
| 5f0fbcf | | | 101 | "blame", |
| 5f0fbcf | | | 102 | "changeset_info", |
| 5f0fbcf | | | 103 | "fastlog", |
| 5f0fbcf | | | 104 | "filenodes", |
| 5f0fbcf | | | 105 | "fsnodes", |
| 5f0fbcf | | | 106 | "git_commits", |
| 5f0fbcf | | | 107 | "git_delta_manifests_v2", |
| 6c9fcae | | | 108 | "unodes", |
| 5f0fbcf | | | 109 | "hgchangesets", |
| 5f0fbcf | | | 110 | "skeleton_manifests", |
| 1688ad1 | | | 111 | "skeleton_manifests_v2", |
| 6c9fcae | | | 112 | "ccsm", |
| 5f0fbcf | | | 113 | ] |
| 0d60b20 | | | 114 | |
| 0d60b20 | | | 115 | [derived_data_config.available_configs.default.git_delta_manifest_v2_config] |
| 0d60b20 | | | 116 | max_inlined_object_size = 2000 |
| 0d60b20 | | | 117 | max_inlined_delta_size = 2000 |
| 0d60b20 | | | 118 | delta_chunk_size = 1000000 |
| 5f0fbcf | | | 119 | TOML |
| 5f0fbcf | | | 120 | |
| 5f0fbcf | | | 121 | # ── Pull bootstrap images from ghcr.io ─────────────────────────── |
| 5f0fbcf | | | 122 | |
| 5f0fbcf | | | 123 | docker pull ghcr.io/letterpress-labs/grove-mononoke:latest |
| 5f0fbcf | | | 124 | docker pull ghcr.io/letterpress-labs/grove-api:latest |
| 5f0fbcf | | | 125 | docker pull ghcr.io/letterpress-labs/grove-hub-api:latest |
| 5f0fbcf | | | 126 | docker pull ghcr.io/letterpress-labs/grove-web:latest |
| 5f0fbcf | | | 127 | |
| 5f0fbcf | | | 128 | # Tag for local registry (images now in Docker cache) |
| 5f0fbcf | | | 129 | docker tag ghcr.io/letterpress-labs/grove-api:latest localhost:5000/grove-api:latest |
| 5f0fbcf | | | 130 | docker tag ghcr.io/letterpress-labs/grove-hub-api:latest localhost:5000/grove-hub-api:latest |
| 5f0fbcf | | | 131 | docker tag ghcr.io/letterpress-labs/grove-web:latest localhost:5000/grove-web:latest |
| 5f0fbcf | | | 132 | |
| 5f0fbcf | | | 133 | # ── Start ───────────────────────────────────────────────────────── |
| 5f0fbcf | | | 134 | |
| 5f0fbcf | | | 135 | cd "$GROVE_DIR" |
| 135dfe5 | | | 136 | docker compose up -d |
| 135dfe5 | | | 137 | |
| 5f0fbcf | | | 138 | # ── Seed local registry ────────────────────────────────────────── |
| 5f0fbcf | | | 139 | |
| 5f0fbcf | | | 140 | echo "Seeding local registry..." |
| 5f0fbcf | | | 141 | for i in $(seq 1 30); do |
| 5f0fbcf | | | 142 | if curl -sf http://localhost:5000/v2/ > /dev/null 2>&1; then |
| 5f0fbcf | | | 143 | docker push localhost:5000/grove-api:latest |
| 5f0fbcf | | | 144 | docker push localhost:5000/grove-hub-api:latest |
| 5f0fbcf | | | 145 | docker push localhost:5000/grove-web:latest |
| 5f0fbcf | | | 146 | echo "Local registry seeded." |
| 5f0fbcf | | | 147 | break |
| 5f0fbcf | | | 148 | fi |
| 5f0fbcf | | | 149 | sleep 2 |
| 5f0fbcf | | | 150 | done |
| 5f0fbcf | | | 151 | |
| 5f0fbcf | | | 152 | # ── Wait for health ────────────────────────────────────────────── |
| 5f0fbcf | | | 153 | |
| 5f0fbcf | | | 154 | echo "Waiting for Grove Bridge to be healthy..." |
| 5f0fbcf | | | 155 | for i in $(seq 1 60); do |
| 5f0fbcf | | | 156 | if curl -sf http://localhost:3100/health > /dev/null 2>&1; then |
| 5f0fbcf | | | 157 | echo "Grove Bridge is healthy." |
| 5f0fbcf | | | 158 | break |
| 5f0fbcf | | | 159 | fi |
| 5f0fbcf | | | 160 | sleep 5 |
| 5f0fbcf | | | 161 | done |
| 5f0fbcf | | | 162 | |
| 135dfe5 | | | 163 | echo "Hub is running at https://$DOMAIN" |