Skip to content

Commit bfbeaa3

Browse files
committed
feat(chapel): Wave 2 — add chapel-multilocale gate (#87 option A)
Adds a 7th strict gate to chapel-ci.yml that exercises real multilocale execution by building Chapel 2.8.0 from source with `CHPL_COMM=gasnet` and `CHPL_LAUNCHER=smp`, then running `mass-panic --numLocales=2` against the same synthetic 2-repo corpus as `chapel-e2e`. Closes the Wave 1 gap: the stock `.deb` ships `CHPL_COMM=none` and rejects `-nl >1`, so until now the multi-locale code path had no CI coverage. The `smp` launcher and `smp` GASNet substrate let two locales run as oversubscribed local processes on a single ubuntu-22.04 runner — verification, not performance. Implementation choice — owner picked option A from issue #87: - Build from source with `CHPL_COMM=gasnet`, aggressive caching. - Not option B (chapel-multilocale .deb — none published upstream). - Not option C (self-hosted runner — no infrastructure to maintain). Cache strategy: - `$CHPL_HOME = /opt/chapel-multilocale` cached on `actions/cache@v4`. - Key stable on `${runner.os}-chapel-multilocale-2.8.0-gasnet-smp-v1`. - Bump `CHAPEL_MULTILOCALE_CACHE_GEN` env var to invalidate. - Cold build: ~30-40 min on 2-core runner. Warm restore: ~30s. - Cache eviction after 7 days idle (GitHub policy); chapel/** touches in normal repo activity keep it warm. Aggregator gate updated: - `chapel-ci-gate` now waits on 7 jobs (added `chapel-multilocale`). - `R_MULTILOCALE` env var added to the success-aggregation loop. - Doc comments updated from "six gates" → "seven gates". Acceptance criteria from #87 (partial closure): - [x] `chapel-multilocale` job defined and wired into aggregator - [ ] Job green on PR + main ≥1 merge cycle (this PR) - [ ] Added to Base ruleset `required_status_checks` (separate ruleset edit) - [ ] ~50-repo benchmark for README perf claim (separate PR; needs either a beefier runner or self-hosted CI to be meaningful) The aggregator job is the only one in the ruleset, so 7 → 7 is a no-op there: when this PR is green and merged, the aggregator just covers one more underlying job. Ruleset bump is therefore optional unless the owner wants per-gate visibility. Refs: #87
1 parent 4c8b800 commit bfbeaa3

1 file changed

Lines changed: 129 additions & 11 deletions

File tree

.github/workflows/chapel-ci.yml

Lines changed: 129 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,20 +16,25 @@
1616
# - SUCCESS if all 6 underlying jobs succeeded on a relevant change.
1717
# - FAILURE if any underlying job failed.
1818
#
19-
# Six strict jobs (no continue-on-error anywhere):
19+
# Seven strict jobs (no continue-on-error anywhere):
2020
# 1. chapel-parse-check — chpl --parse-only on every module
2121
# 2. chapel-build — chpl build of mass-panic + smoke (no toolbox)
2222
# 3. chapel-smoke — chapel/smoke/two_repo_smoke (Chapel data flow)
2323
# 4. chapel-e2e — mass-panic -nl 1 on a synthetic 2-repo manifest
24-
# True -nl 2 requires CHPL_COMM=gasnet which the
25-
# stock .deb doesn't ship; tracked for Wave 2.
2624
# 5. chapel-cli-contract — panic-attack describe-contract vs expected fixture
2725
# 6. chapel-rust-diff — rayon assemblyline vs Chapel single-locale parity
26+
# 7. chapel-multilocale — mass-panic -nl 2 on the same synthetic 2-repo
27+
# corpus, against a Chapel built from source with
28+
# CHPL_COMM=gasnet + CHPL_LAUNCHER=smp (single-host
29+
# oversubscription). The source build is cached on
30+
# $CHPL_HOME; cold ~30-40 min, warm ~30s restore.
31+
# Closes the gap left by Wave 1 (issue #87).
2832
#
2933
# Plus the always-on aggregator: `chapel-ci-gate`.
3034
#
31-
# Wave 2 hardening tracker: SHA-pin the Chapel 2.8.0 .deb download. Today the
32-
# workflow trusts the HTTPS endpoint at chapel-lang/chapel releases.
35+
# Wave 2 hardening tracker: SHA-pin the Chapel 2.8.0 .deb + source tarball
36+
# downloads. Today the workflow trusts the HTTPS endpoints at chapel-lang/chapel
37+
# releases.
3338

3439
name: chapel-ci
3540

@@ -48,6 +53,11 @@ concurrency:
4853
env:
4954
CHAPEL_VERSION: "2.8.0"
5055
CHAPEL_DEB_URL: "https://github.com/chapel-lang/chapel/releases/download/2.8.0/chapel-2.8.0-1.ubuntu22.amd64.deb"
56+
# Source tarball used by chapel-multilocale to build with CHPL_COMM=gasnet.
57+
CHAPEL_SRC_URL: "https://github.com/chapel-lang/chapel/releases/download/2.8.0/chapel-2.8.0.tar.gz"
58+
# $CHPL_HOME for the multilocale build. Cache key bumps via CHAPEL_MULTILOCALE_CACHE_GEN.
59+
CHAPEL_MULTILOCALE_HOME: /opt/chapel-multilocale
60+
CHAPEL_MULTILOCALE_CACHE_GEN: "v1"
5161

5262
jobs:
5363
detect-relevant-changes:
@@ -248,12 +258,118 @@ jobs:
248258
- name: rayon vs Chapel single-locale aggregate parity
249259
run: ./chapel/tests/rayon_vs_chapel_diff.sh
250260

261+
chapel-multilocale:
262+
name: chapel-multilocale
263+
needs: detect-relevant-changes
264+
if: needs.detect-relevant-changes.outputs.relevant == 'true'
265+
runs-on: ubuntu-22.04
266+
timeout-minutes: 75
267+
env:
268+
CHPL_HOME: /opt/chapel-multilocale
269+
steps:
270+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
271+
272+
# Cache the entire built-from-source Chapel tree. Key is stable across
273+
# PRs as long as the version, conduit, launcher and cache-gen marker
274+
# don't change. Cold build is ~30-40 min on a 2-core runner; warm
275+
# restore is ~30s.
276+
- name: Cache multilocale Chapel ($CHPL_HOME)
277+
id: chapel-cache
278+
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
279+
with:
280+
path: ${{ env.CHAPEL_MULTILOCALE_HOME }}
281+
key: ${{ runner.os }}-chapel-multilocale-${{ env.CHAPEL_VERSION }}-gasnet-smp-${{ env.CHAPEL_MULTILOCALE_CACHE_GEN }}
282+
283+
- name: Install Chapel build dependencies
284+
if: steps.chapel-cache.outputs.cache-hit != 'true'
285+
run: |
286+
set -euo pipefail
287+
sudo apt-get update -qq
288+
sudo apt-get install -y --no-install-recommends \
289+
build-essential gcc g++ make perl python3 \
290+
m4 autoconf automake libtool libunwind-dev pkg-config
291+
292+
- name: Build Chapel from source with CHPL_COMM=gasnet
293+
if: steps.chapel-cache.outputs.cache-hit != 'true'
294+
run: |
295+
set -euo pipefail
296+
curl -fsSL --retry 3 -o /tmp/chapel-src.tar.gz "${{ env.CHAPEL_SRC_URL }}"
297+
sudo mkdir -p /opt
298+
sudo tar -xzf /tmp/chapel-src.tar.gz -C /opt
299+
sudo mv "/opt/chapel-${{ env.CHAPEL_VERSION }}" "${{ env.CHAPEL_MULTILOCALE_HOME }}"
300+
sudo chown -R "$(id -u):$(id -g)" "${{ env.CHAPEL_MULTILOCALE_HOME }}"
301+
cd "${{ env.CHAPEL_MULTILOCALE_HOME }}"
302+
# Configure for single-host oversubscribed multilocale:
303+
# CHPL_COMM=gasnet — multilocale communication layer
304+
# CHPL_COMM_SUBSTRATE=smp — shared-memory substrate (no NIC needed)
305+
# CHPL_LAUNCHER=smp — spawn locales as local processes
306+
export CHPL_HOME="${{ env.CHAPEL_MULTILOCALE_HOME }}"
307+
export CHPL_COMM=gasnet
308+
export CHPL_COMM_SUBSTRATE=smp
309+
export CHPL_LAUNCHER=smp
310+
export CHPL_TARGET_COMPILER=gnu
311+
source util/setchplenv.bash
312+
# Build chpl + runtime + GASNet+smp substrate
313+
make -j"$(nproc)"
314+
# Sanity: confirm we have a multilocale chpl
315+
./bin/*/chpl --about | grep -E 'CHPL_COMM:\s+gasnet'
316+
317+
- name: Activate multilocale Chapel
318+
id: activate
319+
run: |
320+
set -euo pipefail
321+
export CHPL_HOME="${{ env.CHAPEL_MULTILOCALE_HOME }}"
322+
source "$CHPL_HOME/util/setchplenv.bash"
323+
# Persist env to subsequent steps via GITHUB_ENV
324+
{
325+
echo "CHPL_HOME=$CHPL_HOME"
326+
echo "CHPL_COMM=gasnet"
327+
echo "CHPL_COMM_SUBSTRATE=smp"
328+
echo "CHPL_LAUNCHER=smp"
329+
echo "CHPL_TARGET_COMPILER=gnu"
330+
echo "PATH=$CHPL_HOME/bin/$(uname -s)-$(uname -m):$PATH"
331+
} >> "$GITHUB_ENV"
332+
chpl --version
333+
chpl --about | grep CHPL_COMM
334+
335+
- name: Build mass-panic against multilocale Chapel
336+
working-directory: chapel
337+
run: |
338+
set -euo pipefail
339+
chpl src/MassPanic.chpl src/Protocol.chpl src/Imaging.chpl src/Temporal.chpl -o mass-panic
340+
341+
- name: End-to-end -nl 2 exercise (oversubscribed locales on single runner)
342+
run: |
343+
set -euo pipefail
344+
WORK=$(mktemp -d /tmp/chapel-multilocale-XXXXXX)
345+
trap 'rm -rf "$WORK"' EXIT
346+
mkdir -p "$WORK/corpus/repo-alpha/src" "$WORK/corpus/repo-beta/src"
347+
echo 'pub unsafe fn a() {}' > "$WORK/corpus/repo-alpha/src/lib.rs"
348+
echo 'pub unsafe fn b() {}' > "$WORK/corpus/repo-beta/src/lib.rs"
349+
for d in repo-alpha repo-beta; do
350+
(cd "$WORK/corpus/$d" && git init -q && git add -A && git -c user.email=ci@example.com -c user.name=ci commit -q -m init)
351+
done
352+
# The smp launcher spawns N processes on the local host. -nl 2 is
353+
# the minimum non-trivial multilocale exercise; oversubscription
354+
# is fine for verification (latency, not throughput, matters here).
355+
./chapel/mass-panic \
356+
--repoDirectory="$WORK/corpus" \
357+
--numLocales=2 \
358+
--quiet \
359+
--outputDir="$WORK/out"
360+
# Two-locale run produced a system image
361+
ls "$WORK/out"/system-image-*.json >/dev/null
362+
# And that image references both repos (cross-locale aggregation)
363+
grep -q 'repo-alpha' "$WORK/out"/system-image-*.json
364+
grep -q 'repo-beta' "$WORK/out"/system-image-*.json
365+
echo "chapel-multilocale: PASS (-nl 2, gasnet+smp)"
366+
251367
# Always-on aggregator. This is the ONLY job listed in the Base ruleset's
252368
# required_status_checks rule. If detect-relevant-changes determined nothing
253369
# in this PR touches Chapel-relevant paths, the gate passes immediately
254-
# (the six per-task jobs above skip via their `if:` guard). If a relevant
370+
# (the seven per-task jobs above skip via their `if:` guard). If a relevant
255371
# change is present, the gate inspects each job's result and only passes
256-
# when ALL six succeeded.
372+
# when ALL seven succeeded.
257373
chapel-ci-gate:
258374
name: chapel-ci-gate
259375
needs:
@@ -264,6 +380,7 @@ jobs:
264380
- chapel-e2e
265381
- chapel-cli-contract
266382
- chapel-rust-diff
383+
- chapel-multilocale
267384
if: always()
268385
runs-on: ubuntu-22.04
269386
steps:
@@ -276,11 +393,12 @@ jobs:
276393
R_E2E: ${{ needs.chapel-e2e.result }}
277394
R_CLI: ${{ needs.chapel-cli-contract.result }}
278395
R_DIFF: ${{ needs.chapel-rust-diff.result }}
396+
R_MULTILOCALE: ${{ needs.chapel-multilocale.result }}
279397
run: |
280398
set -euo pipefail
281399
echo "detect-relevant-changes.outputs.relevant=$RELEVANT"
282-
printf 'parse-check=%s\nbuild=%s\nsmoke=%s\ne2e=%s\ncli-contract=%s\nrust-diff=%s\n' \
283-
"$R_PARSE" "$R_BUILD" "$R_SMOKE" "$R_E2E" "$R_CLI" "$R_DIFF"
400+
printf 'parse-check=%s\nbuild=%s\nsmoke=%s\ne2e=%s\ncli-contract=%s\nrust-diff=%s\nmultilocale=%s\n' \
401+
"$R_PARSE" "$R_BUILD" "$R_SMOKE" "$R_E2E" "$R_CLI" "$R_DIFF" "$R_MULTILOCALE"
284402
if [[ "$RELEVANT" != "true" ]]; then
285403
echo "chapel-ci-gate: SKIP (no chapel-relevant paths changed) → PASS"
286404
exit 0
@@ -291,7 +409,7 @@ jobs:
291409
exit 1
292410
fi
293411
fail=0
294-
for r in "$R_PARSE" "$R_BUILD" "$R_SMOKE" "$R_E2E" "$R_CLI" "$R_DIFF"; do
412+
for r in "$R_PARSE" "$R_BUILD" "$R_SMOKE" "$R_E2E" "$R_CLI" "$R_DIFF" "$R_MULTILOCALE"; do
295413
case "$r" in
296414
success) ;;
297415
*) fail=$((fail + 1)) ;;
@@ -301,4 +419,4 @@ jobs:
301419
echo "chapel-ci-gate: $fail dependent job(s) did not succeed → FAIL"
302420
exit 1
303421
fi
304-
echo "chapel-ci-gate: all six gates green → PASS"
422+
echo "chapel-ci-gate: all seven gates green → PASS"

0 commit comments

Comments
 (0)