Skip to content

Celerity CI

Celerity CI #3713

Workflow file for this run

name: Celerity CI
on:
push:
pull_request:
workflow_dispatch:
inputs:
test-head:
description: "Test against 'HEAD' revisions"
type: boolean
required: true
default: false
tag-latest:
description: "Tag 'HEAD' revisions as 'latest' if successful"
type: boolean
required: true
default: false
# We use nightly builds to determine whether CI passes for "HEAD" revisions
# of DPC++ and hipSYCL. If so, these revisions are tagged as "latest" and
# used for all subsequent CI runs.
schedule:
# Every night at 05:00 UTC
- cron: "0 5 * * *"
jobs:
find-duplicate-workflows:
runs-on: [ self-hosted, slurm ]
outputs:
should_skip: ${{ steps.skip-check.outputs.should_skip }}
steps:
- id: skip-check
uses: fkirc/skip-duplicate-actions@v5.3.0
with:
concurrent_skipping: "never"
skip_after_successful_duplicate: "false"
do_not_skip: '["workflow_dispatch", "schedule"]'
cancel_others: "true"
# Run Clang-Tidy checks
#
# Note:
# - The action we use for this (ZedThree/clang-tidy-review) is a Docker action.
# However, to obtain the required compile_commands.json file, we need our own
# Docker container environment. We therefore run this job within a build container
# and manually call the job's Python script.
# - This action currently only supports pull_request triggers (as it creates review
# comments), so we have to run it regardless of skip-duplicate-action's outcome
# (as otherwise pull_request triggers will usually be skipped due to the push
# trigger already running).
#
# TODO: This should be combined with the report (or "lint") step, really
clang-tidy:
if: github.event.pull_request
runs-on: [ self-hosted, slurm-nvidia ]
env:
container-workspace: <placeholder>
build-dir: /root/build
container:
image: ghcr.io/celerity/celerity-lint
volumes:
- ccache:/ccache
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
# Here and in jobs below: We need to manually set the container workspace
# path as an environment variable, as (curiously) the `github.workspace` context
# variable contains the path on the container host (but $GITHUB_WORKSPACE is correct).
- name: Set container workspace environment variable
run: echo "container-workspace=$GITHUB_WORKSPACE" > $GITHUB_ENV
- uses: actions/checkout@v3
with:
submodules: true
# We only want to configure CMake, so we build the "help" target,
# which doesn't actually do anything (other than print all targets).
- name: Configure CMake
run: bash -o pipefail -c "bash /root/build-celerity.sh ${{ env.container-workspace }} --build-type Debug --target help"
- name: Check clang-tidy
working-directory: ${{ env.build-dir }}
run: |
git clone https://github.com/ZedThree/clang-tidy-review.git
cd clang-tidy-review
git checkout v0.9.0
pip install -r ./requirements.txt
cd ${{ env.container-workspace }}
python3 ${{ env.build-dir }}/clang-tidy-review/review.py \
--clang_tidy_binary=clang-tidy \
--token=${{ github.token }} \
--repo=${{ github.repository }} \
--pr=${{ github.event.pull_request.number }} \
--build_dir=${{ env.build-dir }} \
--config_file=${{ env.container-workspace }}/.clang-tidy \
--include="*.h,*.cc,*.[ch]pp" \
--lgtm-comment-body=""
# We need to jump through some hoops to have different build matrices based on what triggered the workflow.
# For normal CI runs we want to build and test against everything except the "HEAD" revisions, whereas during
# nightly builds we *only* want those.
#
# Current workaround is to represent the matrix as a JSON object, which is then deserialized in the next job.
read-build-matrix:
needs: find-duplicate-workflows
if: needs.find-duplicate-workflows.outputs.should_skip != 'true'
runs-on: self-hosted
outputs:
matrix: ${{ steps.read-json-matrix.outputs.matrix }}
steps:
- uses: actions/checkout@v3
- id: read-json-matrix
name: Read build matrix from file
shell: python
run: |
import json
import os
with open("${{ github.workspace }}/.github/workflows/build_matrix.json") as f:
matrices = json.load(f)
if '${{ github.event_name != 'schedule' && inputs.test-head == false }}' == 'true':
matrix = matrices['default']
else:
matrix = matrices['nightly']
with open(os.environ['GITHUB_OUTPUT'], 'a') as fh:
print('matrix={ "include":%s }' % json.dumps(matrix), file=fh)
build-and-test:
needs: [find-duplicate-workflows, read-build-matrix]
if: ${{ needs.find-duplicate-workflows.outputs.should_skip != 'true' }}
runs-on: [ self-hosted, "slurm-${{ matrix.platform }}" ]
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.read-build-matrix.outputs.matrix) }}
# These outputs are required by the image tagging step, only set during nightly builds.
outputs:
dpcpp-HEAD-Debug-works: ${{ steps.set-head-results.outputs.dpcpp-HEAD-Debug-works }}
dpcpp-HEAD-Release-works: ${{ steps.set-head-results.outputs.dpcpp-HEAD-Release-works }}
dpcpp-HEAD-ubuntu-version: ${{ steps.set-head-results.outputs.dpcpp-HEAD-ubuntu-version }}
hipsycl-HEAD-Debug-works: ${{ steps.set-head-results.outputs.hipsycl-HEAD-Debug-works }}
hipsycl-HEAD-Release-works: ${{ steps.set-head-results.outputs.hipsycl-HEAD-Release-works }}
hipsycl-HEAD-ubuntu-version: ${{ steps.set-head-results.outputs.hipsycl-HEAD-ubuntu-version }}
env:
build-name: ${{ matrix.platform }}-ubuntu${{ matrix.ubuntu-version }}-${{ matrix.sycl }}-${{ matrix.sycl-version }}-${{ matrix.build-type }}
container-workspace: <placeholder>
build-dir: /root/build
examples-build-dir: /root/build-examples
container:
image: ghcr.io/celerity/celerity-build/${{ matrix.sycl }}:ubuntu${{ matrix.ubuntu-version }}-${{ matrix.sycl-version }}
volumes:
- ccache:/ccache
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
# We limit DPC++ to Level-Zero devices (instead of e.g. also showing OpenCL or CUDA devices).
# Most importantly, despite its naming, this currently also avoids the "Unified Runtime over Level-Zero", which
# doesn't seem to be quite ready for production use yet (= it crashes).
- name: Set environment variables
run: |
echo "container-workspace=$GITHUB_WORKSPACE" > $GITHUB_ENV
echo "ONEAPI_DEVICE_SELECTOR=level_zero:*" >> $GITHUB_ENV
echo "SIMSYCL_SYSTEM=$GITHUB_WORKSPACE/ci/simsycl-system.json" >> $GITHUB_ENV
- name: Print exact SYCL revision used for this CI run
run: cat /VERSION
- uses: actions/checkout@v3
with:
submodules: true
- name: Build and install Celerity
run: bash -o pipefail -c "bash /root/build-celerity.sh ${{ env.container-workspace }} --build-type ${{ matrix.build-type }} --target install 2>&1 | tee ${{ env.build-name }}.log"
# Upload build log for report step
- name: Upload build logs
if: always()
uses: actions/upload-artifact@v3
with:
name: ${{ env.build-name }}
path: ${{ env.build-name }}.log
- name: Build examples against installed Celerity
run: bash /root/build-examples.sh ${{ env.container-workspace }}/examples --build-type ${{ matrix.build-type }}
- name: Reset coverage counters
if: matrix.build-type == 'Debug' && matrix.sycl == 'simsycl' # celerity-build/simsycl image enables --coverage for Debug builds
run: fastcov -z -d "${{ env.build-dir }}"
# For a run with ASan, we need to fake dlclose() so all shared libraries are still mapped when leaks are evaluated
# - otherwise, we will see "<unknown module>" instead of a proper trace if it originated in an unloaded library
# (see https://github.com/google/sanitizers/issues/89#issuecomment-406316683). LD_PRELOAD will be set to CI_LD_PRELOAD
# by /root/capture-backtrace.sh (otherwise we would also preload for other processes like bash, which we don't want to do).
# Also, hwloc (used by OpenMPI) will attempt to dlopen(RTLD_DEEPBIND) for GPU backend discovery which is incompatible
# with sanitizers but also irrelevant for a SimSYCL build, so we set HWLOC_COMPONENTS accordingly.
- name: Prepare Sanitizer run
if: matrix.sycl == 'simsycl' && matrix.build-type == 'Debug'
run: |
echo "ASAN_OPTIONS=detect_leaks=1" >> $GITHUB_ENV
echo "LSAN_OPTIONS=suppressions=$GITHUB_WORKSPACE/ci/lsan.suppressions" >> $GITHUB_ENV
echo "int dlclose(void *p) { return 0; }" | cc -x c -shared -o libfake_dlclose.so -
echo "CI_LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libasan.so.6:$(pwd)/libfake_dlclose.so" >> $GITHUB_ENV
echo "HWLOC_COMPONENTS=-cuda,-opencl,-rsmi" >> $GITHUB_ENV
- name: Run unit tests
timeout-minutes: 5
working-directory: ${{ env.build-dir }}
run: ${{ env.container-workspace }}/ci/run-unit-tests.sh
- name: Run examples
timeout-minutes: 5
# We build examples twice, but only run the installed version (which probably has more failure modes)
working-directory: ${{ env.examples-build-dir }}
run: ${{ env.container-workspace }}/ci/run-examples.sh /data/Lenna.png 1 2 4
- name: Run debugging tests
if: matrix.build-type == 'Debug' && matrix.sycl != 'dpcpp' # newer DPC++ generates DWARF5 which is incompatible with Ubuntu 20.04's GDB
run: ${{ env.container-workspace }}/test/debug/pretty-print-test.py ${{ env.build-dir }}/test/debug/pretty_printables
- name: Run system tests
working-directory: ${{ env.build-dir }}
run: ${{ env.container-workspace }}/ci/run-system-tests.sh 2 4
- name: Run integration tests
working-directory: ${{ env.build-dir }}
run: ${{ env.container-workspace }}/test/integration/run-integration-tests.py . ${{ matrix.platform }}
- name: Upload stack traces (if any)
if: always()
uses: actions/upload-artifact@v3
with:
name: ${{ env.build-name }}
path: |
${{ env.build-dir }}/*.trace
${{ env.examples-build-dir }}/*.trace
if-no-files-found: ignore
- id: set-head-results
name: Set outputs for HEAD builds
if: matrix.sycl-version == 'HEAD'
run: |
echo "${{ matrix.sycl }}-HEAD-${{ matrix.build-type }}-works=1" >> "$GITHUB_OUTPUT"
echo "${{ matrix.sycl }}-HEAD-ubuntu-version=${{ matrix.ubuntu-version }}" >> "$GITHUB_OUTPUT"
- name: Collect & report coverage
if: matrix.build-type == 'Debug' && matrix.sycl == 'simsycl' # celerity-build/simsycl image enables --coverage for Debug builds
env:
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
run: |
"${{ env.container-workspace }}/ci/capture-coverage.sh" "${{ env.container-workspace }}" "${{ env.build-dir }}" -o lcov.info
coveralls report lcov.info
# Tag "HEAD" images that built and tested successfully as "latest".
# This is only done for nightly builds (or when specifying the "tag-latest" option on manually triggered runs).
tag-latest-containers:
needs: [find-duplicate-workflows, build-and-test]
# Run this step regardless of result of `build-and-test` (hence the `always()`),
# since we always want to tag images that were successful, even if others weren't.
if: always() && needs.find-duplicate-workflows.outputs.should_skip != 'true' && (github.event_name == 'schedule' || (inputs.test-head && inputs.tag-latest))
runs-on: slurm-${{ matrix.platform }}
strategy:
fail-fast: false
matrix:
include:
- sycl: "dpcpp"
platform: "intel"
- sycl: "hipsycl"
platform: "nvidia"
env:
image-basename-dpcpp: ghcr.io/celerity/celerity-build/dpcpp:ubuntu${{ needs.build-and-test.outputs.dpcpp-HEAD-ubuntu-version }}
image-basename-hipsycl: ghcr.io/celerity/celerity-build/hipsycl:ubuntu${{ needs.build-and-test.outputs.hipsycl-HEAD-ubuntu-version }}
permissions:
packages: write
steps:
- name: Log into Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- if: matrix.sycl == 'dpcpp'
run: |
if [[ "${{ needs.build-and-test.outputs.dpcpp-HEAD-Debug-works }}" -eq 1 ]] && [[ "${{ needs.build-and-test.outputs.dpcpp-HEAD-Release-works }}" -eq 1 ]]; then
docker tag ${{ env.image-basename-dpcpp }}-HEAD ${{ env.image-basename-dpcpp }}-latest
docker push ${{ env.image-basename-dpcpp }}-latest
else
exit 1
fi
- if: matrix.sycl == 'hipsycl'
run: |
if [[ "${{ needs.build-and-test.outputs.hipsycl-HEAD-Debug-works }}" -eq 1 ]] && [[ "${{ needs.build-and-test.outputs.hipsycl-HEAD-Release-works }}" -eq 1 ]]; then
docker tag ${{ env.image-basename-hipsycl }}-HEAD ${{ env.image-basename-hipsycl }}-latest
docker push ${{ env.image-basename-hipsycl }}-latest
else
exit 1
fi
report:
needs: [find-duplicate-workflows, build-and-test]
if: ${{ needs.find-duplicate-workflows.outputs.should_skip != 'true' }}
runs-on: [ self-hosted, slurm ]
env:
container-workspace: <placeholder>
container:
image: ghcr.io/celerity/celerity-lint
credentials:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Set container workspace environment variable
run: echo "container-workspace=$GITHUB_WORKSPACE" > $GITHUB_ENV
- uses: actions/checkout@v3
- name: Check code formatting
id: formatting
working-directory: ${{ env.container-workspace }}
shell: bash
run: |
unformatted=$("./ci/find-unformatted-files.sh")
{
echo 'unformatted-files<<EOF'
echo "$unformatted"
echo EOF
} >> "$GITHUB_OUTPUT"
- uses: "celerity/ci-report-action@v7"
with:
gh-token: ${{ secrets.GITHUB_TOKEN }}
unformatted-files: ${{ steps.formatting.outputs.unformatted-files }}