Skip to main content

max / makenotwork

5.8 KB · 143 lines History Blame Raw
1 #!/usr/bin/env bash
2 # ota-publish.sh — Publish an OTA update to makenot.work
3 #
4 # DEPRECATED: superseded by `mnw-cli ota publish` (typed, tested, retried).
5 # This script also predates the SyncKit SDK-key auth field, so it no longer
6 # authenticates against the current server. Kept for reference only.
7 # See: _private/docs/meta/ota-release-runbook.md
8 #
9 # Usage:
10 # ./deploy/ota-publish.sh --slug goingson --version 0.2.2 --target linux --arch x86_64 \
11 # --artifact path/to/bundle.tar.gz [--notes "Bug fixes"] [--signature "..."]
12 #
13 # Environment:
14 # MNW_OTA_EMAIL — MNW account email (or set via --email)
15 # MNW_OTA_PASSWORD — MNW account password (or set via --password)
16 # MNW_OTA_API_KEY — SyncKit app API key (or set via --api-key)
17 # MNW_OTA_SERVER — Server URL (default: https://makenot.work)
18
19 set -euo pipefail
20
21 SERVER="${MNW_OTA_SERVER:-https://makenot.work}"
22 EMAIL="${MNW_OTA_EMAIL:-}"
23 PASSWORD="${MNW_OTA_PASSWORD:-}"
24 API_KEY="${MNW_OTA_API_KEY:-}"
25
26 SLUG=""
27 VERSION=""
28 TARGET=""
29 ARCH=""
30 ARTIFACT=""
31 NOTES=""
32 SIGNATURE=""
33
34 usage() {
35 echo "Usage: $0 --slug SLUG --version X.Y.Z --target OS --arch ARCH --artifact FILE"
36 echo ""
37 echo "Required:"
38 echo " --slug App slug (e.g. goingson, balanced-breakfast, audiofiles)"
39 echo " --version Semver version (e.g. 0.2.2)"
40 echo " --target Target OS: linux, darwin, windows"
41 echo " --arch Architecture: x86_64, aarch64"
42 echo " --artifact Path to the built artifact file"
43 echo ""
44 echo "Optional:"
45 echo " --notes Release notes (default: empty)"
46 echo " --signature Minisign signature for Tauri verification"
47 echo " --email MNW account email (overrides MNW_OTA_EMAIL)"
48 echo " --password MNW account password (overrides MNW_OTA_PASSWORD)"
49 echo " --api-key SyncKit API key (overrides MNW_OTA_API_KEY)"
50 echo " --server Server URL (overrides MNW_OTA_SERVER)"
51 echo ""
52 echo "Environment variables: MNW_OTA_EMAIL, MNW_OTA_PASSWORD, MNW_OTA_API_KEY, MNW_OTA_SERVER"
53 exit 1
54 }
55
56 while [[ $# -gt 0 ]]; do
57 case $1 in
58 --slug) SLUG="$2"; shift 2 ;;
59 --version) VERSION="$2"; shift 2 ;;
60 --target) TARGET="$2"; shift 2 ;;
61 --arch) ARCH="$2"; shift 2 ;;
62 --artifact) ARTIFACT="$2"; shift 2 ;;
63 --notes) NOTES="$2"; shift 2 ;;
64 --signature) SIGNATURE="$2"; shift 2 ;;
65 --email) EMAIL="$2"; shift 2 ;;
66 --password) PASSWORD="$2"; shift 2 ;;
67 --api-key) API_KEY="$2"; shift 2 ;;
68 --server) SERVER="$2"; shift 2 ;;
69 -h|--help) usage ;;
70 *) echo "Unknown option: $1"; usage ;;
71 esac
72 done
73
74 # Validate required args
75 [[ -z "$SLUG" ]] && { echo "Error: --slug required"; usage; }
76 [[ -z "$VERSION" ]] && { echo "Error: --version required"; usage; }
77 [[ -z "$TARGET" ]] && { echo "Error: --target required"; usage; }
78 [[ -z "$ARCH" ]] && { echo "Error: --arch required"; usage; }
79 [[ -z "$ARTIFACT" ]] && { echo "Error: --artifact required"; usage; }
80 [[ -z "$EMAIL" ]] && { echo "Error: MNW_OTA_EMAIL or --email required"; usage; }
81 [[ -z "$PASSWORD" ]] && { echo "Error: MNW_OTA_PASSWORD or --password required"; usage; }
82 [[ -z "$API_KEY" ]] && { echo "Error: MNW_OTA_API_KEY or --api-key required"; usage; }
83
84 [[ ! -f "$ARTIFACT" ]] && { echo "Error: artifact file not found: $ARTIFACT"; exit 1; }
85
86 FILE_SIZE=$(stat -f%z "$ARTIFACT" 2>/dev/null || stat -c%s "$ARTIFACT" 2>/dev/null)
87 echo "Publishing OTA update: $SLUG v$VERSION ($TARGET/$ARCH, ${FILE_SIZE} bytes)"
88
89 # Step 1: Authenticate
90 echo " Authenticating..."
91 AUTH_RESPONSE=$(curl -sf -X POST "$SERVER/api/sync/auth" \
92 -H "Content-Type: application/json" \
93 -d "{\"email\":\"$EMAIL\",\"password\":\"$PASSWORD\",\"api_key\":\"$API_KEY\"}")
94
95 TOKEN=$(echo "$AUTH_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['token'])")
96 APP_ID=$(echo "$AUTH_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['app_id'])")
97 echo " Authenticated (app: $APP_ID)"
98
99 # Step 2: Create release
100 echo " Creating release v$VERSION..."
101 RELEASE_BODY=$(python3 -c "import json,sys; print(json.dumps({'version':sys.argv[1],'notes':sys.argv[2],'signature':sys.argv[3]}))" "$VERSION" "$NOTES" "$SIGNATURE")
102 RELEASE_RESPONSE=$(curl -sf -X POST "$SERVER/api/sync/ota/apps/$APP_ID/releases" \
103 -H "Content-Type: application/json" \
104 -H "Authorization: Bearer $TOKEN" \
105 -d "$RELEASE_BODY")
106
107 RELEASE_ID=$(echo "$RELEASE_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
108 echo " Release created: $RELEASE_ID"
109
110 # Step 3: Register artifact and get presigned upload URL
111 echo " Registering artifact ($TARGET/$ARCH, $FILE_SIZE bytes)..."
112 ARTIFACT_BODY=$(printf '{"target":"%s","arch":"%s","file_size":%s}' "$TARGET" "$ARCH" "$FILE_SIZE")
113 ARTIFACT_RESPONSE=$(curl -sf -X POST "$SERVER/api/sync/ota/apps/$APP_ID/releases/$RELEASE_ID/artifacts" \
114 -H "Content-Type: application/json" \
115 -H "Authorization: Bearer $TOKEN" \
116 -d "$ARTIFACT_BODY")
117
118 UPLOAD_URL=$(echo "$ARTIFACT_RESPONSE" | python3 -c "import sys,json; print(json.load(sys.stdin)['upload_url'])")
119 echo " Got presigned upload URL"
120
121 # Step 4: Upload artifact to S3
122 echo " Uploading artifact..."
123 curl -sf -X PUT "$UPLOAD_URL" \
124 -H "Content-Type: application/octet-stream" \
125 --data-binary "@$ARTIFACT"
126 echo " Upload complete"
127
128 # Step 5: Verify updater endpoint
129 echo " Verifying updater endpoint..."
130 HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
131 "$SERVER/api/sync/ota/$SLUG/$TARGET/$ARCH/0.0.1")
132
133 if [[ "$HTTP_CODE" == "200" ]]; then
134 echo " Updater check returns 200 — update is live"
135 else
136 echo " Warning: updater check returned $HTTP_CODE (expected 200)"
137 echo " The release was created but the updater endpoint may not be serving it yet."
138 fi
139
140 echo ""
141 echo "Published: $SLUG v$VERSION ($TARGET/$ARCH)"
142 echo "Updater URL: $SERVER/api/sync/ota/$SLUG/$TARGET/$ARCH/$VERSION"
143