-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathpublish.sh
More file actions
162 lines (150 loc) · 5.69 KB
/
publish.sh
File metadata and controls
162 lines (150 loc) · 5.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#!/usr/bin/env bash
# Publishes the entire ObjectiveAI release across all registries, in
# dependency order. Each wave dispatches its per-package publish scripts
# in parallel; the next wave only starts after every registry in the
# prior wave reports the new version live. No manual retries.
#
# Idempotent: re-running after a partial failure skips packages that are
# already live at the current VERSION and resumes from the rest.
#
# Usage:
# bash publish.sh # full production release (dispatches GHA)
# bash publish.sh --build-only # local sanity check across all packages
#
# Requires: gh CLI authenticated; relevant secrets set on the repo
# (CARGO_REGISTRY_TOKEN, NPM_TOKEN, PYPI_API_TOKEN).
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "$0")" && pwd)"
VERSION="$(awk '/^version = "/ { gsub(/version = "|"/, ""); print; exit }' "$REPO_ROOT/objectiveai-sdk-rs/Cargo.toml")"
TIMEOUT_SECS=1200 # 20 minutes per package
POLL_SECS=15
# Entries: "<dir>|<registry>|<published-name>"
# registry ∈ {crates, pypi, npm, go, github-release}
WAVE_1=(
"objectiveai-sdk-rs-macros|crates|objectiveai-sdk-macros"
"objectiveai-sdk-go|go|objectiveai-sdk-go"
)
WAVE_2=(
"objectiveai-sdk-rs|crates|objectiveai-sdk"
)
WAVE_3=(
"objectiveai-mcp-proxy|crates|objectiveai-mcp-proxy"
"objectiveai-mcp-filesystem|crates|objectiveai-mcp-filesystem"
"objectiveai-sdk-py|pypi|objectiveai-sdk"
"objectiveai-sdk-js|npm|@objectiveai/sdk"
)
WAVE_4=(
"objectiveai-api|crates|objectiveai-api"
)
WAVE_5=(
"objectiveai-cli|crates|objectiveai-cli"
)
WAVE_6=(
"objectiveai-mcp|crates|objectiveai-mcp"
"objectiveai-cocoindex|pypi|objectiveai-cocoindex"
)
# ── --build-only fast path: everything in parallel, no wave/wait logic ──
if [[ "${1:-}" == "--build-only" ]]; then
pids=()
for entry in "${WAVE_1[@]}" "${WAVE_2[@]}" "${WAVE_3[@]}" "${WAVE_4[@]}" "${WAVE_5[@]}" "${WAVE_6[@]}"; do
dir="${entry%%|*}"
bash "$REPO_ROOT/$dir/publish.sh" --build-only &
pids+=($!)
done
failed=false
for pid in "${pids[@]}"; do wait "$pid" || failed=true; done
$failed && exit 1
exit 0
fi
# ── registry liveness probe ─────────────────────────────────────────────
is_live() {
local registry="$1" name="$2" version="$3"
case "$registry" in
crates)
curl -fsS -o /dev/null 2>/dev/null "https://crates.io/api/v1/crates/$name/$version"
;;
pypi)
curl -fsS -o /dev/null 2>/dev/null "https://pypi.org/pypi/$name/$version/json"
;;
npm)
curl -fsS -o /dev/null 2>/dev/null "https://registry.npmjs.org/$name/$version"
;;
go)
git -C "$REPO_ROOT" ls-remote --tags origin "refs/tags/$name/v$version" \
| grep -q "refs/tags/$name/v$version$"
;;
github-release)
gh release view "v$version" \
--repo "$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
>/dev/null 2>&1
;;
*)
echo "unknown registry: $registry" >&2; return 2
;;
esac
}
wait_for_live() {
local label="$1" registry="$2" name="$3" version="$4"
local started=$SECONDS
printf " · waiting for %s on %s..." "$label" "$registry"
while (( SECONDS - started < TIMEOUT_SECS )); do
if is_live "$registry" "$name" "$version"; then
printf " live (%ds)\n" $(( SECONDS - started ))
return 0
fi
sleep "$POLL_SECS"
printf "."
done
printf " TIMED OUT (%ds)\n" $(( SECONDS - started ))
return 1
}
# ── wave executor ───────────────────────────────────────────────────────
run_wave() {
local wave_name="$1"; shift
local entries=("$@")
echo
echo "=== $wave_name ==="
# 1. Dispatch each per-package script in parallel — but skip any
# package already live at the current VERSION (idempotence).
local pids=() labels=() to_wait=()
for entry in "${entries[@]}"; do
local dir registry name
IFS='|' read -r dir registry name <<<"$entry"
[[ -z "$name" ]] && name="$dir"
if is_live "$registry" "$name" "$VERSION"; then
echo " · $dir already live at $VERSION on $registry — skip"
continue
fi
bash "$REPO_ROOT/$dir/publish.sh" &
pids+=($!)
labels+=("$dir")
to_wait+=("$entry")
done
# 2. Block on dispatch completion (a few seconds each — just the
# `gh workflow run` call returning OK, or the sdk-go tag push).
local dispatch_failed=false
for i in "${!pids[@]}"; do
if ! wait "${pids[$i]}"; then
echo " ✗ dispatch failed: ${labels[$i]}" >&2
dispatch_failed=true
fi
done
$dispatch_failed && return 1
# 3. Poll each registry until the new version is live.
for entry in "${to_wait[@]}"; do
local dir registry name
IFS='|' read -r dir registry name <<<"$entry"
[[ -z "$name" ]] && name="$dir"
wait_for_live "$dir" "$registry" "$name" "$VERSION" || return 1
done
}
# ── go ──────────────────────────────────────────────────────────────────
echo "Publishing ObjectiveAI $VERSION across all registries..."
run_wave "Wave 1 — leaves (no upstream deps)" "${WAVE_1[@]}"
run_wave "Wave 2 — depends on objectiveai-sdk-macros" "${WAVE_2[@]}"
run_wave "Wave 3 — depends on objectiveai-sdk" "${WAVE_3[@]}"
run_wave "Wave 4 — depends on objectiveai-mcp-proxy (api)" "${WAVE_4[@]}"
run_wave "Wave 5 — depends on api (cli)" "${WAVE_5[@]}"
run_wave "Wave 6 — depends on cli / sdk-py on PyPI" "${WAVE_6[@]}"
echo
echo "✓ All packages published at $VERSION"