Skip to content

Commit e452a30

Browse files
committed
Bound GitHub release helper network calls
1 parent c9bcbfb commit e452a30

2 files changed

Lines changed: 104 additions & 10 deletions

File tree

scripts/release/gh-release.sh

Lines changed: 82 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,81 @@ fail() {
1111
exit 1
1212
}
1313

14+
run_bounded() {
15+
local seconds="$1"
16+
shift
17+
18+
if ! [[ "$seconds" =~ ^[1-9][0-9]*$ ]]; then
19+
fail "timeout must be a positive integer number of seconds"
20+
fi
21+
if [ "$#" -eq 0 ]; then
22+
fail "run_bounded requires a command"
23+
fi
24+
25+
if command -v timeout >/dev/null 2>&1; then
26+
timeout "${seconds}s" "$@"
27+
return
28+
fi
29+
30+
local marker
31+
marker="$(mktemp "${TMPDIR:-/tmp}/ray-gh-release-timeout.XXXXXX")"
32+
rm -f "$marker"
33+
34+
"$@" &
35+
local command_pid=$!
36+
(
37+
sleep "$seconds"
38+
if kill -0 "$command_pid" 2>/dev/null; then
39+
: >"$marker"
40+
kill -TERM "$command_pid" 2>/dev/null || true
41+
sleep 5
42+
kill -KILL "$command_pid" 2>/dev/null || true
43+
fi
44+
) &
45+
local watchdog_pid=$!
46+
local status=0
47+
48+
wait "$command_pid" || status=$?
49+
kill "$watchdog_pid" 2>/dev/null || true
50+
wait "$watchdog_pid" 2>/dev/null || true
51+
52+
if [ -e "$marker" ]; then
53+
rm -f "$marker"
54+
return 124
55+
fi
56+
57+
rm -f "$marker"
58+
return "$status"
59+
}
60+
61+
remote_tag_exists() {
62+
local tag="$1"
63+
if run_bounded 60 git ls-remote --exit-code --tags origin "refs/tags/$tag" >/dev/null 2>&1; then
64+
return 0
65+
fi
66+
67+
local status=$?
68+
if [ "$status" -eq 2 ]; then
69+
return 1
70+
fi
71+
72+
fail "could not check remote tag $tag (exit $status)"
73+
}
74+
75+
github_release_exists() {
76+
local tag="$1"
77+
if run_bounded 60 gh release view "$tag" >/dev/null 2>&1; then
78+
return 0
79+
fi
80+
81+
local status=$?
82+
if [ "$status" -eq 124 ]; then
83+
fail "timed out checking GitHub release: $tag"
84+
fi
85+
86+
return 1
87+
}
88+
1489
usage() {
1590
sed -n '1,80p' <<'EOF'
1691
Usage: bash scripts/release/gh-release.sh [--dry-run | --yes]
@@ -79,7 +154,7 @@ if [ "$BRANCH" != "main" ]; then
79154
fail "release helper must run from main; current branch is ${BRANCH:-detached}"
80155
fi
81156

82-
git fetch --tags origin refs/heads/main:refs/remotes/origin/main
157+
run_bounded 120 git fetch --tags origin refs/heads/main:refs/remotes/origin/main
83158
LOCAL_HEAD="$(git rev-parse HEAD)"
84159
REMOTE_HEAD="$(git rev-parse origin/main)"
85160
if [ "$LOCAL_HEAD" != "$REMOTE_HEAD" ]; then
@@ -90,21 +165,21 @@ for tag in "$TAG_CORE" "$TAG_SDK"; do
90165
if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
91166
fail "local tag already exists: $tag"
92167
fi
93-
if git ls-remote --exit-code --tags origin "refs/tags/$tag" >/dev/null 2>&1; then
168+
if remote_tag_exists "$tag"; then
94169
fail "remote tag already exists: $tag"
95170
fi
96-
if gh release view "$tag" >/dev/null 2>&1; then
171+
if github_release_exists "$tag"; then
97172
fail "GitHub release already exists: $tag"
98173
fi
99174
done
100175

101-
if ! gh auth status >/dev/null; then
176+
if ! run_bounded 60 gh auth status >/dev/null; then
102177
fail "gh is not authenticated"
103178
fi
104179

105180
git tag -a "$TAG_CORE" -m "Release $TAG_CORE (@razroo/ray-core v$VER)"
106181
git tag -a "$TAG_SDK" -m "Release $TAG_SDK (@razroo/ray-sdk v$VER)"
107-
git push origin "$TAG_CORE" "$TAG_SDK"
108-
gh release create "$TAG_CORE" --generate-notes --title "$TAG_CORE"
109-
gh release create "$TAG_SDK" --generate-notes --title "$TAG_SDK"
182+
run_bounded 120 git push origin "$TAG_CORE" "$TAG_SDK"
183+
run_bounded 120 gh release create "$TAG_CORE" --generate-notes --title "$TAG_CORE"
184+
run_bounded 120 gh release create "$TAG_SDK" --generate-notes --title "$TAG_SDK"
110185
echo "Done. Actions publish to npm when each release is in published state."

scripts/release/gh-release.test.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,27 @@ test("gh release helper gates destructive releases on clean synced main", async
4646
assert.match(contents, /refs\/heads\/main:refs\/remotes\/origin\/main/);
4747
assert.match(contents, /git rev-parse HEAD/);
4848
assert.match(contents, /git rev-parse origin\/main/);
49-
assert.match(contents, /git ls-remote --exit-code --tags origin/);
50-
assert.match(contents, /gh release view "\$tag"/);
51-
assert.match(contents, /gh auth status/);
49+
assert.match(contents, /remote_tag_exists "\$tag"/);
50+
assert.match(contents, /github_release_exists "\$tag"/);
5251
assert.match(contents, /bun \.\/scripts\/release\/check-source\.mjs "\$VER"/);
5352
});
53+
54+
test("gh release helper bounds network release operations", async () => {
55+
const contents = await readFile(scriptPath, "utf8");
56+
57+
assert.match(contents, /run_bounded\(\) \{/);
58+
assert.match(contents, /timeout "\$\{seconds\}s" "\$@"/);
59+
assert.match(contents, /return 124/);
60+
assert.match(
61+
contents,
62+
/run_bounded 120 git fetch --tags origin refs\/heads\/main:refs\/remotes\/origin\/main/,
63+
);
64+
assert.match(contents, /run_bounded 60 git ls-remote --exit-code --tags origin/);
65+
assert.match(contents, /fail "could not check remote tag \$tag \(exit \$status\)"/);
66+
assert.match(contents, /run_bounded 60 gh release view "\$tag"/);
67+
assert.match(contents, /fail "timed out checking GitHub release: \$tag"/);
68+
assert.match(contents, /run_bounded 60 gh auth status/);
69+
assert.match(contents, /run_bounded 120 git push origin "\$TAG_CORE" "\$TAG_SDK"/);
70+
assert.match(contents, /run_bounded 120 gh release create "\$TAG_CORE"/);
71+
assert.match(contents, /run_bounded 120 gh release create "\$TAG_SDK"/);
72+
});

0 commit comments

Comments
 (0)