Skip to content

Commit e0d275a

Browse files
authored
Merge pull request #1 from ejgallego/fix-ci-install-shell-lint
Fix install CI checks
2 parents 9a98b0f + 2881ffa commit e0d275a

7 files changed

Lines changed: 194 additions & 62 deletions

File tree

.github/workflows/ci.yml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ name: CI
66

77
on:
88
push:
9+
branches:
10+
- main
911
pull_request:
1012

1113
permissions:
@@ -57,3 +59,47 @@ jobs:
5759

5860
- name: Broker Slow Test
5961
run: bash tests/test-broker-slow.sh
62+
63+
install:
64+
runs-on: ubuntu-latest
65+
steps:
66+
- uses: actions/checkout@v4
67+
68+
- uses: ./.github/actions/lean-ci-setup
69+
70+
- name: Install Test
71+
run: bash tests/test-install.sh
72+
73+
toolchain-compat:
74+
runs-on: ubuntu-latest
75+
strategy:
76+
fail-fast: false
77+
matrix:
78+
toolchain:
79+
- leanprover/lean4:v4.29.0-rc6
80+
- leanprover/lean4:v4.29.0-rc5
81+
steps:
82+
- uses: actions/checkout@v4
83+
84+
- uses: ./.github/actions/lean-ci-setup
85+
86+
- name: Validate toolchain compatibility
87+
run: bash tests/test-toolchain-compat.sh '${{ matrix.toolchain }}'
88+
89+
broker-rocq:
90+
runs-on: ubuntu-latest
91+
steps:
92+
- uses: actions/checkout@v4
93+
94+
- uses: ./.github/actions/lean-ci-setup
95+
96+
- name: Set up OCaml / opam
97+
uses: ocaml/setup-ocaml@v3
98+
with:
99+
ocaml-compiler: 4.14.2
100+
101+
- name: Install Rocq Tooling
102+
run: bash tests/setup-rocq-opam.sh
103+
104+
- name: Broker Rocq Test
105+
run: bash tests/test-broker-rocq.sh

scripts/install-beam.sh

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,7 @@ hash_tool() {
289289
}
290290

291291
read_supported_toolchains() {
292-
local output_name="$1"
293-
local -n output_ref="$output_name"
294-
mapfile -t output_ref < <("$beam_cli" supported-toolchains lean)
292+
"$beam_cli" supported-toolchains lean
295293
}
296294

297295
array_contains() {
@@ -308,8 +306,6 @@ array_contains() {
308306

309307
resolve_install_toolchains() {
310308
local repo_toolchain="$1"
311-
local output_name="$2"
312-
local -n output_ref="$output_name"
313309
local supported_toolchains=()
314310
local selected=()
315311
local toolchain=""
@@ -318,7 +314,7 @@ resolve_install_toolchains() {
318314
die "cannot combine --all-supported with --toolchain"
319315
fi
320316

321-
read_supported_toolchains supported_toolchains
317+
mapfile -t supported_toolchains < <(read_supported_toolchains)
322318
if [ "${#supported_toolchains[@]}" -eq 0 ]; then
323319
die "beam CLI reported no supported Lean toolchains"
324320
fi
@@ -341,7 +337,7 @@ resolve_install_toolchains() {
341337
selected=("$repo_toolchain")
342338
fi
343339

344-
output_ref=("${selected[@]}")
340+
printf '%s\n' "${selected[@]}"
345341
}
346342

347343
hash_tree() {
@@ -402,12 +398,11 @@ stage_runtime_tree() {
402398
local dest="$1"
403399
local entry=""
404400
local mode=""
405-
local manifest_group=""
406401
local src_rel=""
407402
local dest_rel=""
408403
mkdir -p "$dest"
409404
for entry in "${runtime_payload_spec[@]}"; do
410-
IFS='|' read -r mode manifest_group src_rel dest_rel <<< "$entry"
405+
IFS='|' read -r mode _ src_rel dest_rel <<< "$entry"
411406
case "$mode" in
412407
copy)
413408
copy_repo_path_if_present "$repo_root/$src_rel" "$dest/$dest_rel" "$dest"
@@ -471,12 +466,19 @@ prepare_install_environment() {
471466
local toolchain_name="$1"
472467
local selected_name="$2"
473468
local -n toolchain_ref="$toolchain_name"
474-
local -n selected_ref="$selected_name"
469+
local -n selected_toolchains_ref="$selected_name"
470+
local resolved_toolchains=""
475471
require_elan
476472
toolchain_ref="$(awk 'NR==1 {print $1}' "$repo_root/lean-toolchain")"
477473
require_repo_toolchain "$toolchain_ref"
478474
ensure_runtime_artifacts
479-
resolve_install_toolchains "$toolchain_ref" selected_ref
475+
resolved_toolchains="$(resolve_install_toolchains "$toolchain_ref")"
476+
# shellcheck disable=SC2034
477+
if [ -n "$resolved_toolchains" ]; then
478+
mapfile -t selected_toolchains_ref <<< "$resolved_toolchains"
479+
else
480+
selected_toolchains_ref=()
481+
fi
480482
verify_publish_targets
481483
mkdir -p "$bin_home" "$versions_root" "$state_root"
482484
}

supported-lean-toolchains

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# Validated Lean toolchains that beam is willing to serve.
22
leanprover/lean4:v4.29.0-rc6
3+
leanprover/lean4:v4.29.0-rc5

tests/test-broker-rocq.sh

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright (c) 2026 Lean FRO LLC. All rights reserved.
4+
# Released under Apache 2.0 license as described in the file LICENSE.
5+
# Author: Emilio J. Gallego Arias
6+
7+
set -euo pipefail
8+
9+
cd "$(dirname "$0")/.."
10+
11+
echo "[broker-rocq] build"
12+
lake build \
13+
beam-cli \
14+
beam-daemon \
15+
beam-client \
16+
beam-daemon-rocq-smoke-test \
17+
> /dev/null
18+
19+
ROCQ_LSP=""
20+
for candidate in "_opam/bin/coq-lsp" "_opam/_opam/bin/coq-lsp"; do
21+
if [ -x "$candidate" ]; then
22+
ROCQ_LSP="$candidate"
23+
break
24+
fi
25+
done
26+
27+
if [ -z "$ROCQ_LSP" ]; then
28+
echo "missing coq-lsp; run tests/setup-rocq-opam.sh first" >&2
29+
exit 1
30+
fi
31+
32+
if [ -d "_opam/_opam" ]; then
33+
eval "$(opam env --switch=./_opam --set-switch)"
34+
fi
35+
36+
echo "[broker-rocq] wrapper tests"
37+
BEAM_ROCQ_CMD="$PWD/$ROCQ_LSP" bash tests/test-beam-wrapper-rocq.sh > /dev/null
38+
39+
echo "[broker-rocq] smoke test"
40+
BEAM_ROCQ_CMD="$PWD/$ROCQ_LSP" .lake/build/bin/beam-daemon-rocq-smoke-test > /dev/null

tests/test-broker-slow.sh

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ lake build \
4444
beam-cli \
4545
beam-daemon \
4646
beam-client \
47-
beam-daemon-rocq-smoke-test \
4847
> /dev/null
4948

5049
echo "[broker-slow] bundle install"
@@ -54,29 +53,6 @@ echo "[broker-slow] wrapper tests"
5453
HOME="$tmp_env_root/home" CODEX_HOME="$tmp_env_root/codex" CLAUDE_HOME="$tmp_env_root/claude" \
5554
BEAM_INSTALL_BUNDLE_DIR="$tmp_bundle_dir" bash tests/test-beam-wrapper.sh > /dev/null
5655

57-
echo "[broker-slow] install tests"
58-
bash tests/test-install.sh > /dev/null
59-
6056
echo "[broker-slow] save replay tests"
6157
HOME="$tmp_env_root/home" CODEX_HOME="$tmp_env_root/codex" CLAUDE_HOME="$tmp_env_root/claude" \
6258
BEAM_INSTALL_BUNDLE_DIR="$tmp_bundle_dir" bash tests/test-broker-save-olean.sh > /dev/null
63-
64-
ROCQ_LSP=""
65-
for candidate in "_opam/bin/coq-lsp" "_opam/_opam/bin/coq-lsp"; do
66-
if [ -x "$candidate" ]; then
67-
ROCQ_LSP="$candidate"
68-
break
69-
fi
70-
done
71-
72-
if [ -n "$ROCQ_LSP" ]; then
73-
echo "[broker-slow] rocq wrapper tests"
74-
if [ -d "_opam/_opam" ]; then
75-
eval "$(opam env --switch=./_opam --set-switch)"
76-
fi
77-
BEAM_ROCQ_CMD="$PWD/$ROCQ_LSP" bash tests/test-beam-wrapper-rocq.sh > /dev/null
78-
echo "[broker-slow] rocq smoke test"
79-
BEAM_ROCQ_CMD="$PWD/$ROCQ_LSP" .lake/build/bin/beam-daemon-rocq-smoke-test > /dev/null
80-
else
81-
echo "[broker-slow] rocq skipped: install coq-lsp with tests/setup-rocq-opam.sh." >&2
82-
fi

tests/test-install.sh

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ export BEAM_INSTALL_ROOT="$tmp_root/install-root"
5858

5959
mkdir -p "$HOME" "$BEAM_INSTALL_ROOT"
6060

61-
toolchain="$(awk 'NR==1 {print $1}' lean-toolchain)"
61+
mapfile -t supported_toolchains < <(grep -v '^[[:space:]]*#' supported-lean-toolchains | sed '/^[[:space:]]*$/d')
62+
toolchain="${supported_toolchains[0]}"
6263
source_checkout="$tmp_root/source-checkout"
6364

6465
assert_file() {
@@ -117,14 +118,14 @@ assert_runtime_layout() {
117118
assert_manifest_metadata() {
118119
local manifest_path="$1"
119120
local expected_payload="$2"
120-
local expected_toolchain="$3"
121-
local expected_source_commit="$4"
122-
python3 - "$manifest_path" "$expected_payload" "$expected_toolchain" "$expected_source_commit" <<'PY'
121+
local expected_source_commit="$3"
122+
shift 3
123+
python3 - "$manifest_path" "$expected_payload" "$expected_source_commit" "$@" <<'PY'
123124
import json
124125
import os
125126
import sys
126127
127-
manifest_path, expected_payload, expected_toolchain, expected_source_commit = sys.argv[1:]
128+
manifest_path, expected_payload, expected_source_commit, *expected_toolchains = sys.argv[1:]
128129
with open(manifest_path, "r", encoding="utf-8") as f:
129130
manifest = json.load(f)
130131
layout = json.loads(os.environ["BEAM_INSTALL_LAYOUT_JSON"])
@@ -133,7 +134,7 @@ if manifest.get("schemaVersion") != 2:
133134
raise SystemExit(f"unexpected manifest schemaVersion: {manifest.get('schemaVersion')}")
134135
if manifest.get("payloadHash") != expected_payload:
135136
raise SystemExit(f"unexpected manifest payloadHash: {manifest.get('payloadHash')}")
136-
if manifest.get("toolchains") != [expected_toolchain]:
137+
if manifest.get("toolchains") != expected_toolchains:
137138
raise SystemExit(f"unexpected manifest toolchains: {manifest.get('toolchains')}")
138139
if "toolchain" in manifest:
139140
raise SystemExit(f"unexpected legacy manifest toolchain field: {manifest.get('toolchain')}")
@@ -206,25 +207,42 @@ path_without_elan() {
206207

207208
assert_bundle_layout() {
208209
local bundle_root="$1"
209-
local metadata
210-
metadata="$(find "$bundle_root" -name metadata.json | head -n 1 || true)"
211-
if [ -z "$metadata" ]; then
210+
shift
211+
local metadata_files=()
212+
local metadata=""
213+
local expected_toolchain=""
214+
local found=""
215+
mapfile -t metadata_files < <(find "$bundle_root" -name metadata.json | sort)
216+
if [ "${#metadata_files[@]}" -eq 0 ]; then
212217
echo "missing bundle metadata under $bundle_root" >&2
213218
exit 1
214219
fi
215-
if ! rg -n --fixed-strings "\"toolchain\": \"$toolchain\"" "$metadata" > /dev/null; then
216-
echo "bundle metadata does not mention expected toolchain $toolchain: $metadata" >&2
217-
exit 1
218-
fi
219-
220-
local workspace
221-
workspace="$(dirname "$metadata")/workspace"
222-
assert_file "$workspace/Beam.lean"
223-
assert_file "$workspace/Beam/Broker/Server.lean"
224-
assert_file "$workspace/RunAt/Internal/SaveArtifacts.lean"
225-
assert_file "$workspace/.lake/build/bin/beam-daemon"
226-
assert_file "$workspace/.lake/build/bin/beam-client"
227-
assert_file "$workspace/.lake/build/lib/librunAt_RunAt.so"
220+
for expected_toolchain in "$@"; do
221+
found=""
222+
for metadata in "${metadata_files[@]}"; do
223+
if command -v rg >/dev/null 2>&1; then
224+
if rg -n --fixed-strings "\"toolchain\": \"$expected_toolchain\"" "$metadata" > /dev/null; then
225+
found="$metadata"
226+
break
227+
fi
228+
elif grep -F "\"toolchain\": \"$expected_toolchain\"" "$metadata" > /dev/null; then
229+
found="$metadata"
230+
break
231+
fi
232+
done
233+
if [ -z "$found" ]; then
234+
echo "bundle metadata does not mention expected toolchain $expected_toolchain under $bundle_root" >&2
235+
exit 1
236+
fi
237+
local workspace
238+
workspace="$(dirname "$found")/workspace"
239+
assert_file "$workspace/Beam.lean"
240+
assert_file "$workspace/Beam/Broker/Server.lean"
241+
assert_file "$workspace/RunAt/Internal/SaveArtifacts.lean"
242+
assert_file "$workspace/.lake/build/bin/beam-daemon"
243+
assert_file "$workspace/.lake/build/bin/beam-client"
244+
assert_file "$workspace/.lake/build/lib/librunAt_RunAt.so"
245+
done
228246
}
229247

230248
rsync -a --exclude='.git' ./ "$source_checkout"/
@@ -327,20 +345,20 @@ assert_version_count "$BEAM_INSTALL_ROOT/versions" 1
327345
installed_version_root="$(python3 -c 'import os,sys; print(os.path.realpath(sys.argv[1]))' "$installed_runtime_root")"
328346
installed_payload_id="$(basename "$installed_version_root")"
329347
assert_file "$installed_runtime_root/manifest.json"
330-
BEAM_INSTALL_LAYOUT_JSON="$install_layout_json" assert_manifest_metadata "$installed_runtime_root/manifest.json" "$installed_payload_id" "$toolchain" "$expected_source_commit"
348+
BEAM_INSTALL_LAYOUT_JSON="$install_layout_json" assert_manifest_metadata "$installed_runtime_root/manifest.json" "$installed_payload_id" "$expected_source_commit" "$toolchain"
331349

332350
assert_not_exists "$CODEX_HOME"
333351
assert_not_exists "$CLAUDE_HOME"
334-
assert_bundle_layout "$BEAM_INSTALL_ROOT/state/install-bundles"
352+
assert_bundle_layout "$BEAM_INSTALL_ROOT/state/install-bundles" "$toolchain"
335353

336354
(
337355
cd "$source_checkout"
338356
bash scripts/install-beam.sh --all-supported > /dev/null
339357
)
340358

341359
assert_version_count "$BEAM_INSTALL_ROOT/versions" 1
342-
BEAM_INSTALL_LAYOUT_JSON="$install_layout_json" assert_manifest_metadata "$installed_runtime_root/manifest.json" "$installed_payload_id" "$toolchain" "$expected_source_commit"
343-
assert_bundle_layout "$BEAM_INSTALL_ROOT/state/install-bundles"
360+
BEAM_INSTALL_LAYOUT_JSON="$install_layout_json" assert_manifest_metadata "$installed_runtime_root/manifest.json" "$installed_payload_id" "$expected_source_commit" "$toolchain"
361+
assert_bundle_layout "$BEAM_INSTALL_ROOT/state/install-bundles" "${supported_toolchains[@]}"
344362

345363
(
346364
cd "$source_checkout"
@@ -354,7 +372,7 @@ for skills_home in "$CODEX_HOME" "$CLAUDE_HOME"; do
354372
assert_no_skill_socket_guidance "$skills_home/skills/rocq-beam/SKILL.md"
355373
done
356374
assert_version_count "$BEAM_INSTALL_ROOT/versions" 1
357-
BEAM_INSTALL_LAYOUT_JSON="$install_layout_json" assert_manifest_metadata "$installed_runtime_root/manifest.json" "$installed_payload_id" "$toolchain" "$expected_source_commit"
375+
BEAM_INSTALL_LAYOUT_JSON="$install_layout_json" assert_manifest_metadata "$installed_runtime_root/manifest.json" "$installed_payload_id" "$expected_source_commit" "$toolchain"
358376

359377
blocked_home="$tmp_root/blocked-home"
360378
blocked_install_root="$tmp_root/blocked-install-root"

tests/test-toolchain-compat.sh

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright (c) 2026 Lean FRO LLC. All rights reserved.
4+
# Released under Apache 2.0 license as described in the file LICENSE.
5+
# Author: Emilio J. Gallego Arias
6+
7+
set -euo pipefail
8+
9+
cd "$(dirname "$0")/.."
10+
11+
toolchain="${1:-}"
12+
if [ -z "$toolchain" ]; then
13+
echo "usage: bash tests/test-toolchain-compat.sh <toolchain>" >&2
14+
exit 1
15+
fi
16+
17+
tmp_bundle_dir="$(mktemp -d /tmp/beam-toolchain-bundles-XXXXXX)"
18+
tmp_env_root="$(mktemp -d /tmp/beam-toolchain-env-XXXXXX)"
19+
20+
expect_owned_tmp_dir() {
21+
case "$1" in
22+
/tmp/beam-toolchain-bundles-*|/tmp/beam-toolchain-env-*)
23+
;;
24+
*)
25+
echo "refusing to touch unexpected temp dir: $1" >&2
26+
exit 1
27+
;;
28+
esac
29+
}
30+
31+
cleanup() {
32+
expect_owned_tmp_dir "$tmp_bundle_dir"
33+
expect_owned_tmp_dir "$tmp_env_root"
34+
rm -rf -- "$tmp_bundle_dir" "$tmp_env_root"
35+
}
36+
trap cleanup EXIT
37+
38+
mkdir -p "$tmp_env_root/home" "$tmp_env_root/codex" "$tmp_env_root/claude"
39+
40+
echo "[toolchain-compat] build"
41+
lake build beam-cli > /dev/null
42+
43+
echo "[toolchain-compat] bundle install $toolchain"
44+
env -u BEAM_HOME -u BEAM_INSTALL_BUNDLE_DIR -u BEAM_CONTROL_DIR \
45+
HOME="$tmp_env_root/home" \
46+
CODEX_HOME="$tmp_env_root/codex" \
47+
CLAUDE_HOME="$tmp_env_root/claude" \
48+
BEAM_INSTALL_BUNDLE_DIR="$tmp_bundle_dir" \
49+
./.lake/build/bin/beam-cli bundle-install "$toolchain" > /dev/null

0 commit comments

Comments
 (0)