Skip to content

Commit b142762

Browse files
authored
Make ARM32 CI runner (#61)
1 parent de30dee commit b142762

5 files changed

Lines changed: 143 additions & 96 deletions

File tree

.github/workflows/ci.yaml

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -42,40 +42,56 @@ jobs:
4242
run: |
4343
cd docs && make html
4444
45-
pytest-linux-386:
45+
pytest-linux-container:
4646
runs-on: ubuntu-latest
47+
strategy:
48+
fail-fast: false
49+
matrix:
50+
include:
51+
- arch_name: linux/386
52+
qemu_platform: 386
53+
image_name: gfloat-linux-386:py310-uv
54+
image_tar: /tmp/gfloat-linux-386-image.tar
55+
cache_prefix: linux-386-image
56+
test_script: etc/test-linux-386.sh
57+
- arch_name: linux/arm32
58+
qemu_platform: arm
59+
image_name: gfloat-linux-arm32:py310-uv
60+
image_tar: /tmp/gfloat-linux-arm32-image.tar
61+
cache_prefix: linux-arm32-image
62+
test_script: etc/test-linux-arm32.sh
4763

4864
steps:
4965
- uses: actions/checkout@v6
5066

51-
- name: Restore cached linux/386 docker image
52-
id: cache-linux-386-image
67+
- name: Restore cached test image (${{ matrix.arch_name }})
68+
id: cache-test-image
5369
uses: actions/cache@v5
5470
with:
55-
path: /tmp/gfloat-linux-386-image.tar
56-
key: ${{ runner.os }}-linux-386-image-${{ hashFiles('etc/linux-386.Dockerfile', 'requirements.txt', 'requirements-dev.txt', 'pyproject.toml') }}
71+
path: ${{ matrix.image_tar }}
72+
key: ${{ runner.os }}-${{ matrix.cache_prefix }}-${{ hashFiles('etc/linux-container.Dockerfile', 'requirements.txt', 'requirements-dev.txt', 'pyproject.toml') }}
5773

58-
- name: Enable QEMU for 32-bit emulation
74+
- name: Enable QEMU emulation (${{ matrix.arch_name }})
5975
uses: docker/setup-qemu-action@v4
6076
with:
61-
platforms: 386
77+
platforms: ${{ matrix.qemu_platform }}
6278

63-
- name: Load cached linux/386 docker image
79+
- name: Load cached test image (${{ matrix.arch_name }})
6480
run: |
65-
if [ -f /tmp/gfloat-linux-386-image.tar ]; then
66-
docker load -i /tmp/gfloat-linux-386-image.tar
81+
if [ -f "${{ matrix.image_tar }}" ]; then
82+
docker load -i "${{ matrix.image_tar }}"
6783
fi
6884
69-
- name: Ensure linux/386 test image is available
85+
- name: Ensure test image is available (${{ matrix.arch_name }})
7086
run: |
71-
bash etc/test-linux-386.sh load
87+
bash "${{ matrix.test_script }}" load
7288
73-
- name: Run full unit tests on linux/386
89+
- name: Run unit tests (${{ matrix.arch_name }})
7490
run: |
75-
bash etc/test-linux-386.sh run -vv test
91+
bash "${{ matrix.test_script }}" run -vv test
7692
77-
- name: Save linux/386 docker image to cache path
93+
- name: Save test image to cache path (${{ matrix.arch_name }})
7894
if: always()
7995
run: |
80-
docker image inspect gfloat-linux-386:py310-uv >/dev/null 2>&1
81-
docker save gfloat-linux-386:py310-uv -o /tmp/gfloat-linux-386-image.tar
96+
docker image inspect "${{ matrix.image_name }}" >/dev/null 2>&1
97+
docker save "${{ matrix.image_name }}" -o "${{ matrix.image_tar }}"
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ RUN apt-get update \
88
&& apt-get install -y --no-install-recommends build-essential \
99
&& rm -rf /var/lib/apt/lists/*
1010

11+
RUN python -m pip install -U --no-cache-dir pip
12+
1113
RUN NINJAFLAGS='-v' python -m pip install -v --no-cache-dir \
1214
"numpy<2"
1315

etc/test-linux-386.sh

100644100755
Lines changed: 5 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -4,85 +4,11 @@
44

55
[ -n "${BASH_VERSION:-}" ] || exec bash "$0" "$@"
66

7-
set -euo pipefail
8-
97
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10-
REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
11-
12-
IMAGE="gfloat-linux-386:py310-uv"
13-
PLATFORM="linux/386"
14-
DOCKERFILE="etc/linux-386.Dockerfile"
15-
MODE="all"
16-
17-
if [[ "${1:-}" == "all" || "${1:-}" == "load" || "${1:-}" == "build" || "${1:-}" == "run" ]]; then
18-
MODE="$1"
19-
shift
20-
fi
21-
22-
usage() {
23-
echo "Usage: bash etc/test-linux-386.sh [all|load|build|run] [pytest args...]"
24-
echo " load Build image only if missing"
25-
echo " build Force rebuild image"
26-
echo " run Run tests (image must already exist)"
27-
echo " (no arg) Build image if missing, then run tests"
28-
echo ""
29-
echo "Examples:"
30-
echo " bash etc/test-linux-386.sh run test/test_encode.py::test_encode[binary32]"
31-
echo " bash etc/test-linux-386.sh run -k stochastic -q"
32-
}
33-
34-
build_image() {
35-
docker buildx build \
36-
--progress=plain \
37-
--platform "${PLATFORM}" \
38-
--load \
39-
-t "${IMAGE}" \
40-
-f "${REPO_DIR}/${DOCKERFILE}" \
41-
"${REPO_DIR}"
42-
}
43-
44-
run_tests() {
45-
docker run --rm \
46-
--platform "${PLATFORM}" \
47-
-v "${REPO_DIR}:/work" \
48-
-w /work \
49-
"${IMAGE}" \
50-
bash -lc '
51-
set -euo pipefail
52-
export PYTHONPATH="/work/src${PYTHONPATH:+:${PYTHONPATH}}"
53-
python -m pytest "$@"
54-
' _ "$@"
55-
}
56-
57-
if [[ "${MODE}" != "all" && "${MODE}" != "load" && "${MODE}" != "build" && "${MODE}" != "run" ]]; then
58-
usage
59-
exit 2
60-
fi
61-
62-
echo "Running unit tests in Docker (${PLATFORM})"
63-
64-
if ! docker info >/dev/null 2>&1; then
65-
echo "Docker daemon is not running; start Docker and rerun this script." >&2
66-
exit 1
67-
fi
68-
69-
if [[ "${MODE}" == "build" ]]; then
70-
echo "Force-building test image ${IMAGE} for ${PLATFORM}"
71-
build_image
72-
fi
73-
74-
if [[ "${MODE}" == "load" || "${MODE}" == "all" ]]; then
75-
if ! docker image inspect "${IMAGE}" >/dev/null 2>&1; then
76-
echo "Building test image ${IMAGE} for ${PLATFORM} (cached after first build)"
77-
build_image
78-
fi
79-
fi
808

81-
if [[ "${MODE}" == "run" || "${MODE}" == "all" ]]; then
82-
if ! docker image inspect "${IMAGE}" >/dev/null 2>&1; then
83-
echo "Image ${IMAGE} not found. Run 'bash etc/test-linux-386.sh load' first." >&2
84-
exit 1
85-
fi
9+
export GFLOAT_TEST_IMAGE="gfloat-linux-386:py310-uv"
10+
export GFLOAT_TEST_PLATFORM="linux/386"
11+
export GFLOAT_TEST_DOCKERFILE="etc/linux-container.Dockerfile"
12+
export GFLOAT_TEST_SCRIPT_NAME="test-linux-386.sh"
8613

87-
run_tests "$@"
88-
fi
14+
exec bash "${SCRIPT_DIR}/test-linux-container.sh" "$@"

etc/test-linux-arm32.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright (c) 2024 Graphcore Ltd. All rights reserved.
4+
5+
[ -n "${BASH_VERSION:-}" ] || exec bash "$0" "$@"
6+
7+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
8+
9+
export GFLOAT_TEST_IMAGE="gfloat-linux-arm32:py310-uv"
10+
export GFLOAT_TEST_PLATFORM="linux/arm/v7"
11+
export GFLOAT_TEST_DOCKERFILE="etc/linux-container.Dockerfile"
12+
export GFLOAT_TEST_SCRIPT_NAME="test-linux-arm32.sh"
13+
14+
exec bash "${SCRIPT_DIR}/test-linux-container.sh" "$@"

etc/test-linux-container.sh

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
#!/usr/bin/env bash
2+
3+
# Copyright (c) 2024 Graphcore Ltd. All rights reserved.
4+
5+
[ -n "${BASH_VERSION:-}" ] || exec bash "$0" "$@"
6+
7+
set -euo pipefail
8+
9+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
REPO_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
11+
12+
IMAGE="${GFLOAT_TEST_IMAGE:?GFLOAT_TEST_IMAGE is required}"
13+
PLATFORM="${GFLOAT_TEST_PLATFORM:?GFLOAT_TEST_PLATFORM is required}"
14+
DOCKERFILE="${GFLOAT_TEST_DOCKERFILE:?GFLOAT_TEST_DOCKERFILE is required}"
15+
SCRIPT_NAME="${GFLOAT_TEST_SCRIPT_NAME:-$(basename "$0")}"
16+
17+
MODE="all"
18+
if [[ "${1:-}" == "all" || "${1:-}" == "load" || "${1:-}" == "build" || "${1:-}" == "run" ]]; then
19+
MODE="$1"
20+
shift
21+
fi
22+
23+
usage() {
24+
echo "Usage: bash etc/${SCRIPT_NAME} [all|load|build|run] [pytest args...]"
25+
echo " load Build image only if missing"
26+
echo " build Force rebuild image"
27+
echo " run Run tests (image must already exist)"
28+
echo " (no arg) Build image if missing, then run tests"
29+
echo ""
30+
echo "Examples:"
31+
echo " bash etc/${SCRIPT_NAME} run test/test_encode.py::test_encode[binary32]"
32+
echo " bash etc/${SCRIPT_NAME} run -k stochastic -q"
33+
}
34+
35+
build_image() {
36+
docker buildx build \
37+
--progress=plain \
38+
--platform "${PLATFORM}" \
39+
--load \
40+
-t "${IMAGE}" \
41+
-f "${REPO_DIR}/${DOCKERFILE}" \
42+
"${REPO_DIR}"
43+
}
44+
45+
run_tests() {
46+
docker run --rm \
47+
--platform "${PLATFORM}" \
48+
-v "${REPO_DIR}:/work" \
49+
-w /work \
50+
"${IMAGE}" \
51+
bash -lc '
52+
set -euo pipefail
53+
export PYTHONPATH="/work/src${PYTHONPATH:+:${PYTHONPATH}}"
54+
python -m pytest "$@"
55+
' _ "$@"
56+
}
57+
58+
if [[ "${MODE}" != "all" && "${MODE}" != "load" && "${MODE}" != "build" && "${MODE}" != "run" ]]; then
59+
usage
60+
exit 2
61+
fi
62+
63+
echo "Running unit tests in Docker (${PLATFORM})"
64+
65+
if ! docker info >/dev/null 2>&1; then
66+
echo "Docker daemon is not running; start Docker and rerun this script." >&2
67+
exit 1
68+
fi
69+
70+
if [[ "${MODE}" == "build" ]]; then
71+
echo "Force-building test image ${IMAGE} for ${PLATFORM}"
72+
build_image
73+
fi
74+
75+
if [[ "${MODE}" == "load" || "${MODE}" == "all" ]]; then
76+
if ! docker image inspect "${IMAGE}" >/dev/null 2>&1; then
77+
echo "Building test image ${IMAGE} for ${PLATFORM} (cached after first build)"
78+
build_image
79+
fi
80+
fi
81+
82+
if [[ "${MODE}" == "run" || "${MODE}" == "all" ]]; then
83+
if ! docker image inspect "${IMAGE}" >/dev/null 2>&1; then
84+
echo "Image ${IMAGE} not found. Run 'bash etc/${SCRIPT_NAME} load' first." >&2
85+
exit 1
86+
fi
87+
88+
run_tests "$@"
89+
fi

0 commit comments

Comments
 (0)