@@ -78,19 +78,22 @@ if [[ -z "$PROJECT_ID" ]]; then |
| 78 | 78 | --description "shithub.sh production environment" \ |
| 79 | 79 | --no-header --format ID)" |
| 80 | 80 | fi |
| 81 | | -echo "project: $PROJECT_NAME (id $PROJECT_ID)" |
| 81 | +echo "project: $PROJECT_NAME (id $PROJECT_ID)" >&2 |
| 82 | 82 | |
| 83 | 83 | # --- 2. Droplets --- |
| 84 | +# Status messages must go to stderr so they don't pollute the captured |
| 85 | +# stdout (which the caller assigns to APP_ID etc.). Only the bare ID |
| 86 | +# goes to stdout. |
| 84 | 87 | create_or_skip_droplet() { |
| 85 | 88 | local name="$1" size="$2" tag="$3" |
| 86 | 89 | local existing |
| 87 | 90 | existing="$(doctl compute droplet list --no-header --format ID,Name | awk -v n="$name" '$2==n {print $1; exit}')" |
| 88 | 91 | if [[ -n "$existing" ]]; then |
| 89 | | - echo "droplet $name already exists (id $existing); skipping" |
| 92 | + echo "droplet $name already exists (id $existing); skipping" >&2 |
| 90 | 93 | echo "$existing" |
| 91 | 94 | return |
| 92 | 95 | fi |
| 93 | | - echo "creating droplet $name (size $size)..." |
| 96 | + echo "creating droplet $name (size $size)..." >&2 |
| 94 | 97 | local id |
| 95 | 98 | id="$(doctl compute droplet create "$name" \ |
| 96 | 99 | --image ubuntu-24-04-x64 \ |
@@ -110,38 +113,47 @@ BAK_ID="$(create_or_skip_droplet shithub-backup s-1vcpu-2gb shithub-backup)" |
| 110 | 113 | MON_ID="$(create_or_skip_droplet shithub-monitoring s-2vcpu-4gb shithub-monitoring)" |
| 111 | 114 | |
| 112 | 115 | # --- 3. Block volume + attach to shithub-app --- |
| 116 | +# Volume creation and attach are independent steps; on a re-run we may |
| 117 | +# find the volume created but never attached (if a prior run died here). |
| 118 | +# Handle both checks separately. |
| 113 | 119 | VOL_NAME="shithub-data" |
| 114 | 120 | VOL_ID="$(doctl compute volume list --no-header --format ID,Name | awk -v n="$VOL_NAME" '$2==n {print $1; exit}')" |
| 115 | 121 | if [[ -z "$VOL_ID" ]]; then |
| 116 | | - echo "creating 100 GB volume $VOL_NAME..." |
| 122 | + echo "creating 100 GB volume $VOL_NAME..." >&2 |
| 117 | 123 | VOL_ID="$(doctl compute volume create "$VOL_NAME" \ |
| 118 | 124 | --region "$PRIMARY_REGION" \ |
| 119 | 125 | --size 100GiB \ |
| 120 | 126 | --fs-type ext4 \ |
| 121 | 127 | --no-header --format ID)" |
| 122 | | - echo "attaching $VOL_NAME to shithub-app..." |
| 123 | | - doctl compute volume-action attach "$VOL_ID" "$APP_ID" --wait |
| 124 | 128 | else |
| 125 | | - echo "volume $VOL_NAME already exists (id $VOL_ID); skipping create" |
| 129 | + echo "volume $VOL_NAME already exists (id $VOL_ID); skipping create" >&2 |
| 130 | +fi |
| 131 | + |
| 132 | +# Check whether the volume is already attached. doctl prints DropletIDs |
| 133 | +# as a JSON-style array like [123456789] or []. |
| 134 | +ATTACHED_TO="$(doctl compute volume get "$VOL_ID" --no-header --format DropletIDs | tr -d '[]' | xargs)" |
| 135 | +if [[ -z "$ATTACHED_TO" ]]; then |
| 136 | + echo "attaching $VOL_NAME to shithub-app (id $APP_ID)..." >&2 |
| 137 | + doctl compute volume-action attach "$VOL_ID" "$APP_ID" --wait >&2 |
| 138 | +else |
| 139 | + echo "volume $VOL_NAME already attached to droplet(s) $ATTACHED_TO; skipping" >&2 |
| 126 | 140 | fi |
| 127 | 141 | |
| 128 | 142 | # --- 4. Spaces buckets --- |
| 129 | | -# doctl's spaces support exists but is limited; fall through to the s3-compatible API |
| 130 | | -# via the standard awscli isn't worth the extra dep here. We use doctl's native |
| 131 | | -# Spaces commands where they exist, and fall back to instructions for the rest. |
| 143 | +# doctl's spaces support varies by version. We attempt the native commands; |
| 144 | +# if they're not present, we print a fallback and continue. |
| 132 | 145 | create_or_skip_space() { |
| 133 | 146 | local name="$1" region="$2" |
| 134 | | - if doctl spaces buckets list --no-header --format Name | grep -qx "$name" 2>/dev/null; then |
| 135 | | - echo "Spaces bucket $name already exists; skipping" |
| 147 | + if doctl spaces buckets list --no-header --format Name 2>/dev/null | grep -qx "$name"; then |
| 148 | + echo "Spaces bucket $name already exists; skipping" >&2 |
| 136 | 149 | return |
| 137 | 150 | fi |
| 138 | | - echo "creating Spaces bucket $name in $region..." |
| 139 | | - # Newer doctl versions support `doctl spaces buckets create`; older ones don't. |
| 151 | + echo "creating Spaces bucket $name in $region..." >&2 |
| 140 | 152 | if doctl spaces buckets create "$name" --region "$region" >/dev/null 2>&1; then |
| 141 | | - echo " ok" |
| 153 | + echo " ok" >&2 |
| 142 | 154 | else |
| 143 | | - echo " doctl can't create the bucket (CLI version doesn't support it);" |
| 144 | | - echo " create '$name' in region '$region' via the dashboard." |
| 155 | + echo " doctl can't create the bucket (CLI version may not support it);" >&2 |
| 156 | + echo " create '$name' in region '$region' via the dashboard." >&2 |
| 145 | 157 | fi |
| 146 | 158 | } |
| 147 | 159 | |