diff --git a/.github/workflows/_code-coverage.yml b/.github/workflows/_code-coverage.yml new file mode 100644 index 000000000..bf1e8b5ec --- /dev/null +++ b/.github/workflows/_code-coverage.yml @@ -0,0 +1,55 @@ +# Reusable workflow: code coverage build + Coveralls upload. +# Called by ci.yml. Do not add push/pull_request triggers here. +name: Code Coverage + +on: + workflow_call: + +permissions: + contents: read + +jobs: + code-coverage: + name: lcov + Coveralls + runs-on: ubuntu-latest + env: + CFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" + CXXFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" + LDFLAGS: "-fprofile-arcs -ftest-coverage -O0" + TRICK_CFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" + TRICK_CXXFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" + TRICK_SYSTEM_LDFLAGS: "-fprofile-arcs -ftest-coverage -O0" + TRICK_SYSTEM_CFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" + TRICK_SYSTEM_CXXFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" + steps: + - name: Install Dependencies + run: | + sudo apt-get update -qq + sudo apt-get install -y \ + bison clang flex git llvm make maven cmake zip \ + g++ libclang-dev llvm-dev \ + default-jdk libxml2-dev libmotif-dev libxt-dev \ + perl libdigest-md5-perl libudunits2-dev zlib1g-dev \ + python3-dev python3-pip python3-venv swig \ + libgtest-dev libgmock-dev lcov + - name: Checkout repository + uses: actions/checkout@v7 + - name: Configure Trick + run: | + echo "MAKEFLAGS=-j$(nproc)" >> "$GITHUB_ENV" + echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" + ./configure + - name: Build Trick + run: make + - name: Generate Code Coverage + run: | + python3 -m venv share/trick/trickops/.venv + . share/trick/trickops/.venv/bin/activate + pip3 install -r share/trick/trickops/requirements.txt + make code-coverage + - name: Upload to Coveralls + uses: coverallsapp/github-action@v2 + continue-on-error: true + with: + file: coverage.info + format: lcov diff --git a/.github/workflows/_style.yml b/.github/workflows/_style.yml new file mode 100644 index 000000000..a30db2722 --- /dev/null +++ b/.github/workflows/_style.yml @@ -0,0 +1,205 @@ +# Reusable workflow: style / format checks. +# Called by ci.yml. Do not add push/pull_request triggers here. +name: Style Checks + +on: + workflow_call: + +permissions: + contents: read + +jobs: + style: + name: Style Check + runs-on: ubuntu-26.04 + steps: + - name: Checkout repository + uses: actions/checkout@v7 + with: + fetch-depth: 0 + + - name: Install tools + run: | + sudo apt-get update -qq + sudo apt-get install -y clang-format cpanminus pipx maven + sudo cpanm --notest Perl::Tidy + pipx install ruff + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + + - name: Check clang-format on changed C/C++ lines + if: ${{ !cancelled() }} + run: | + set -euo pipefail + + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE=${{ github.event.pull_request.base.sha }} + else + git fetch origin master + BASE=$(git merge-base origin/master HEAD) + fi + + DIFF=$(git clang-format --diff "$BASE" --extensions c,h,cpp,hpp,cc,hh) || true + + if echo "$DIFF" | grep -q '^diff'; then + echo "$DIFF" + echo "" + echo "::error::Changed lines are not correctly formatted." + echo "::error::To fix, run: git clang-format ${{ github.base_ref || 'master' }}" + exit 1 + fi + echo "All changed C/C++ lines are correctly formatted." + + - name: Check perltidy on changed Perl files + if: ${{ !cancelled() }} + run: | + set -euo pipefail + + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE=${{ github.event.pull_request.base.sha }} + else + git fetch origin master + BASE=$(git merge-base origin/master HEAD) + fi + + # Collect changed Perl files: by extension and by shebang + PERL_FILES=() + while IFS= read -r f; do + [ -z "$f" ] && continue + case "$f" in + *.pl|*.pm|*.t) PERL_FILES+=("$f") ;; + *) if head -1 "$f" 2>/dev/null | grep -q '^#!.*perl'; then + PERL_FILES+=("$f") + fi ;; + esac + done < <(git diff --diff-filter=ACMR --name-only "$BASE") + + if [ ${#PERL_FILES[@]} -eq 0 ]; then + echo "No changed Perl files to check." + exit 0 + fi + + FAILED=0 + for f in "${PERL_FILES[@]}"; do + if ! perltidy -st -se -nb "$f" | diff -u "$f" -; then + echo "::error file=$f::$f is not perltidy-compliant." + FAILED=1 + fi + done + + if [ $FAILED -ne 0 ]; then + echo "" + echo "::error::To fix, run: perltidy -b " + exit 1 + fi + echo "All changed Perl files are correctly formatted." + + - name: Check ruff formatting on changed Python files + if: ${{ !cancelled() }} + run: | + set -euo pipefail + + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE=${{ github.event.pull_request.base.sha }} + else + git fetch origin master + BASE=$(git merge-base origin/master HEAD) + fi + + # Collect changed Python files: by extension and by shebang + PYTHON_FILES=() + while IFS= read -r f; do + [ -z "$f" ] && continue + case "$f" in + *.py) PYTHON_FILES+=("$f") ;; + *) if head -1 "$f" 2>/dev/null | grep -q '^#!.*python'; then + PYTHON_FILES+=("$f") + fi ;; + esac + done < <(git diff --diff-filter=ACMR --name-only "$BASE") + + if [ ${#PYTHON_FILES[@]} -eq 0 ]; then + echo "No changed Python files to check." + exit 0 + fi + + FAILED=0 + for f in "${PYTHON_FILES[@]}"; do + if ! ruff format --preview --check --diff "$f"; then + echo "::error file=$f::$f is not ruff-formatted." + FAILED=1 + fi + if ! ruff check --preview --select I --diff "$f"; then + echo "::error file=$f::$f has unsorted imports." + FAILED=1 + fi + done + + if [ $FAILED -ne 0 ]; then + echo "" + echo "::error::Changed Python files have formatting or import issues." + echo "::error::To fix, run: ruff check --preview --select I --fix && ruff format --preview " + exit 1 + fi + echo "All changed Python files are correctly formatted." + + - name: Check spotless on changed Java files + if: ${{ !cancelled() }} + run: | + set -euo pipefail + + if [ "${{ github.event_name }}" = "pull_request" ]; then + BASE=${{ github.event.pull_request.base.sha }} + else + git fetch origin master + BASE=$(git merge-base origin/master HEAD) + fi + + JAVA_FILES=$(git diff --diff-filter=ACMR --name-only "$BASE" | grep '\.java$' || true) + + if [ -z "$JAVA_FILES" ]; then + echo "No changed Java files to check." + exit 0 + fi + + echo "Changed Java files:" + echo "$JAVA_FILES" + + if ! mvn -f trick_source/java/pom.xml --batch-mode -q spotless:check; then + echo "" + echo "::error::Changed Java files are not correctly formatted." + echo "::error::To fix, run: mvn -f trick_source/java/pom.xml spotless:apply" + exit 1 + fi + echo "All changed Java files are correctly formatted." + + # TODO: Once the codebase is fully formatted, switch to whole-file checking: + # + # - name: Check formatting of changed files + # id: clang_format + # continue-on-error: true + # run: | + # set -euo pipefail + # + # if [ "${{ github.event_name }}" = "pull_request" ]; then + # BASE=${{ github.event.pull_request.base.sha }} + # else + # git fetch origin master + # BASE=$(git merge-base origin/master HEAD) + # fi + # + # FAILED=0 + # while IFS= read -r f; do + # [ -z "$f" ] && continue + # if ! clang-format -n --Werror --style=file "$f"; then + # echo "::error file=$f::$f is not correctly formatted." + # FAILED=1 + # fi + # done < <(git diff --diff-filter=ACMR --name-only "$BASE" -- \ + # '*.[hc]pp' '*.cc' '*.[ch]' '*.hh') + # + # if [ $FAILED -ne 0 ]; then + # echo "" + # echo "::error::To fix, run: clang-format -i --style=file " + # exit 1 + # fi + # echo "All changed C/C++ files are correctly formatted." diff --git a/.github/workflows/_test-linux.yml b/.github/workflows/_test-linux.yml new file mode 100644 index 000000000..584e3eddb --- /dev/null +++ b/.github/workflows/_test-linux.yml @@ -0,0 +1,147 @@ +# Reusable workflow: Linux build & test across distros. +# Called by ci.yml. Do not add push/pull_request triggers here. +name: Linux Build & Test + +on: + workflow_call: + +permissions: + contents: read + +jobs: + test-linux: + name: ${{ matrix.cfg.os }} ${{ matrix.cfg.tag }} + runs-on: ubuntu-latest + container: docker://${{ matrix.cfg.image }}:${{ matrix.cfg.tag }} + strategy: + fail-fast: false + matrix: + cfg: + - { os: ubuntu, tag: 24.04, arch: debian, image: ubuntu } + - { os: ubuntu, tag: 26.04, arch: debian, image: ubuntu } + - { os: oracle, tag: 8, arch: rhel, image: oraclelinux } + - { os: rocky, tag: 8, arch: rhel, image: rockylinux/rockylinux } + - { os: rocky, tag: 9, arch: rhel, image: rockylinux/rockylinux } + - { os: rocky, tag: 10, arch: rhel, image: rockylinux/rockylinux } + include: + - cfg: {} + deps: >- + bison + clang + flex + git + llvm + make + maven + cmake + swig + zip + gdb + conf_pkg: echo package manager already configured + enable_repo: "" + install_cmd: install -y + + #-------- Debian-based Dependencies ---------------- + - cfg: { arch: debian } + pkg_mgr: apt-get + conf_pkg: apt-get update + arch_deps: >- + curl + g++ + libx11-dev + libxml2-dev + libxt-dev + libmotif-common + libmotif-dev + zlib1g-dev + llvm-dev + libclang-dev + libudunits2-dev + libgtest-dev + libgmock-dev + default-jdk + python3-dev + python3-pip + python3-venv + + #-------- RHEL-based Dependencies (all versions) ---------------- + - cfg: { arch: rhel } + pkg_mgr: dnf + conf_pkg: | + dnf -y install epel-release + dnf -y update + dnf install -y 'dnf-command(config-manager)' + arch_deps: >- + clang-devel + diffutils + gcc + gcc-c++ + gtest-devel + gmock-devel + java-21-openjdk-devel + libxml2-devel + llvm-devel + llvm-static + ncurses-devel + openmotif + openmotif-devel + perl + perl-Digest-MD5 + udunits2 + udunits2-devel + which + zlib-devel + python3-devel + + #-------- RHEL 8: gtest lives in powertools ---------------- + - cfg: { arch: rhel, tag: 8 } + enable_repo: dnf config-manager --enable powertools + + #-------- RHEL 9/10: gtest lives in crb ---------------- + - cfg: { arch: rhel, tag: 9 } + enable_repo: dnf config-manager --enable crb + + - cfg: { arch: rhel, tag: 10 } + enable_repo: dnf config-manager --enable crb + + #-------- OL 8: gtest lives in codeready_builder ---------------- + - cfg: { os: oracle, tag: 8 } + enable_repo: dnf config-manager --enable ol8_codeready_builder + + steps: + - name: Set noninteractive mode + run: echo "DEBIAN_FRONTEND=noninteractive" >> "$GITHUB_ENV" + if: matrix.cfg.arch == 'debian' + - name: Update Package Manager + run: | + ${{ matrix.conf_pkg }} + ${{ matrix.enable_repo }} + - name: Install Dependencies + run: > + ${{ matrix.pkg_mgr }} + ${{ matrix.install_cmd }} + ${{ matrix.deps }} + ${{ matrix.arch_deps }} + - name: Checkout repository + uses: actions/checkout@v7 + - name: Configure Trick + run: | + echo "MAKEFLAGS=-j$(nproc)" >> "$GITHUB_ENV" + echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" + ./configure + - name: Build Trick + run: make + - name: Test Trick + run: | + python3 -m venv share/trick/trickops/.venv + . share/trick/trickops/.venv/bin/activate + pip3 install -r share/trick/trickops/requirements.txt + make test + - name: Upload Tests + uses: actions/upload-artifact@v7 + if: ${{ !cancelled() }} + with: + name: Test - Linux (${{ matrix.cfg.os }} ${{ matrix.cfg.tag }}) + path: trick_test/*.xml + if-no-files-found: warn + retention-days: 1 diff --git a/.github/workflows/_test-macos.yml b/.github/workflows/_test-macos.yml new file mode 100644 index 000000000..1190430ce --- /dev/null +++ b/.github/workflows/_test-macos.yml @@ -0,0 +1,57 @@ +# Reusable workflow: macOS build & test. +# Called by ci.yml. Do not add push/pull_request triggers here. +name: macOS Build & Test + +on: + workflow_call: + +permissions: + contents: read + +jobs: + test-macos: + name: macos-26 + runs-on: macos-26 + env: + MAKEFLAGS: -j4 + steps: + - name: Checkout repository + uses: actions/checkout@v7 + # Install googletest version 1.17.0 — the latest stable release as of 6/6/25 + # Note: Need to periodically update googletest version to ensure tests can run + # against the latest. + # Specifying the version to ensure CI builds are stable and not unexpectedly + # broken by upstream changes. + - name: Install gtest 1.17.0 + run: | + wget https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz + tar -xvf v1.17.0.tar.gz + cd googletest-1.17.0 + cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/opt/homebrew + cmake --build build + sudo cmake --install build + - name: Install dependencies + run: | + brew update || true + brew upgrade || true + brew install --cask xquartz + brew install udunits openmotif maven + brew install swig llvm + - name: Build Trick + run: | + ./configure + make + - name: Test + run: | + python3 -m venv share/trick/trickops/.venv + . share/trick/trickops/.venv/bin/activate + pip3 install -r share/trick/trickops/requirements.txt + make test + - name: Upload Tests + uses: actions/upload-artifact@v7 + if: ${{ !cancelled() }} + with: + name: Test - macOS + path: trick_test/*.xml + if-no-files-found: warn + retention-days: 1 diff --git a/.github/workflows/_trickops.yml b/.github/workflows/_trickops.yml new file mode 100644 index 000000000..cf2389b0d --- /dev/null +++ b/.github/workflows/_trickops.yml @@ -0,0 +1,61 @@ +# Reusable workflow: TrickOps unit and doc tests. +# Called by ci.yml. Do not add push/pull_request triggers here. +name: TrickOps Tests + +on: + workflow_call: + +permissions: + contents: read + +jobs: + trickops: + name: ${{ matrix.name }} + runs-on: ubuntu-latest + container: ${{ matrix.image }} + strategy: + fail-fast: false + matrix: + include: + - name: ubuntu 24.04 + image: ubuntu:24.04 + install: | + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y git python3 python3-venv perl perl-modules-5.38 qtbase5-dev wget unzip g++ make flex bison + qmake: qmake + - name: rocky 8 + image: rockylinux/rockylinux:8 + install: | + dnf install -y git python3-devel which perl perl-Digest-MD5 qt5-qtbase-devel bison clang flex make gcc gcc-c++ wget + qmake: qmake-qt5 + defaults: + run: + shell: bash + steps: + - name: Checkout repository + uses: actions/checkout@v7 + - name: Install dependencies + run: ${{ matrix.install }} + - name: Create virtual environment + run: | + python3 -m venv share/trick/trickops/.venv + . share/trick/trickops/.venv/bin/activate + pip3 install --upgrade pip && pip3 install -r share/trick/trickops/requirements.txt + - name: Build koviz + run: | + git clone --depth 1 https://github.com/nasa/koviz.git /tmp/koviz + cd /tmp/koviz && ${{ matrix.qmake }} && make + - name: Run unit and doc tests + run: | + . share/trick/trickops/.venv/bin/activate + export PATH="/tmp/koviz/bin:${PATH}" + cd share/trick/trickops/tests && ./run_tests.py + - name: Upload test logs + uses: actions/upload-artifact@v7 + if: always() + with: + name: TrickOps - ${{ matrix.name }} + path: | + share/trick/trickops/tests/*_doctest_log.txt + /tmp/log.* diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7cb59ea9f..713a117ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,495 +21,29 @@ concurrency: cancel-in-progress: true # ────────────────────────────────────────────────────────────────────────────── -# Stage 1: Style checks +# This file is the single source of truth for "what must pass on a PR." Each job +# delegates to a reusable workflow (the `_*.yml` files), which owns the actual +# steps and matrices. Gating (needs / if) stays here in the orchestrator. # ────────────────────────────────────────────────────────────────────────────── jobs: + # Stage 1: Style checks style: - name: Style Check - runs-on: ubuntu-26.04 - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 + uses: ./.github/workflows/_style.yml - - name: Install tools - run: | - sudo apt-get update -qq - sudo apt-get install -y clang-format perltidy python3-pip maven - pip3 install --break-system-packages ruff - - - name: Check clang-format on changed C/C++ lines - if: ${{ !cancelled() }} - run: | - set -euo pipefail - - if [ "${{ github.event_name }}" = "pull_request" ]; then - BASE=${{ github.event.pull_request.base.sha }} - else - git fetch origin master - BASE=$(git merge-base origin/master HEAD) - fi - - DIFF=$(git clang-format --diff "$BASE" --extensions c,h,cpp,hpp,cc,hh) || true - - if echo "$DIFF" | grep -q '^diff'; then - echo "$DIFF" - echo "" - echo "::error::Changed lines are not correctly formatted." - echo "::error::To fix, run: git clang-format ${{ github.base_ref || 'master' }}" - exit 1 - fi - echo "All changed C/C++ lines are correctly formatted." - - - name: Check perltidy on changed Perl files - if: ${{ !cancelled() }} - run: | - set -euo pipefail - - if [ "${{ github.event_name }}" = "pull_request" ]; then - BASE=${{ github.event.pull_request.base.sha }} - else - git fetch origin master - BASE=$(git merge-base origin/master HEAD) - fi - - # Collect changed Perl files: by extension and by shebang - PERL_FILES=() - while IFS= read -r f; do - [ -z "$f" ] && continue - case "$f" in - *.pl|*.pm|*.t) PERL_FILES+=("$f") ;; - *) if head -1 "$f" 2>/dev/null | grep -q '^#!.*perl'; then - PERL_FILES+=("$f") - fi ;; - esac - done < <(git diff --diff-filter=ACMR --name-only "$BASE") - - if [ ${#PERL_FILES[@]} -eq 0 ]; then - echo "No changed Perl files to check." - exit 0 - fi - - FAILED=0 - for f in "${PERL_FILES[@]}"; do - if ! perltidy -st -se -nb "$f" | diff -u "$f" -; then - echo "::error file=$f::$f is not perltidy-compliant." - FAILED=1 - fi - done - - if [ $FAILED -ne 0 ]; then - echo "" - echo "::error::To fix, run: perltidy -b " - exit 1 - fi - echo "All changed Perl files are correctly formatted." - - - name: Check ruff formatting on changed Python files - if: ${{ !cancelled() }} - run: | - set -euo pipefail - - if [ "${{ github.event_name }}" = "pull_request" ]; then - BASE=${{ github.event.pull_request.base.sha }} - else - git fetch origin master - BASE=$(git merge-base origin/master HEAD) - fi - - # Collect changed Python files: by extension and by shebang - PYTHON_FILES=() - while IFS= read -r f; do - [ -z "$f" ] && continue - case "$f" in - *.py) PYTHON_FILES+=("$f") ;; - *) if head -1 "$f" 2>/dev/null | grep -q '^#!.*python'; then - PYTHON_FILES+=("$f") - fi ;; - esac - done < <(git diff --diff-filter=ACMR --name-only "$BASE") - - if [ ${#PYTHON_FILES[@]} -eq 0 ]; then - echo "No changed Python files to check." - exit 0 - fi - - FAILED=0 - for f in "${PYTHON_FILES[@]}"; do - if ! ruff format --preview --check --diff "$f"; then - echo "::error file=$f::$f is not ruff-formatted." - FAILED=1 - fi - if ! ruff check --preview --select I --diff "$f"; then - echo "::error file=$f::$f has unsorted imports." - FAILED=1 - fi - done - - if [ $FAILED -ne 0 ]; then - echo "" - echo "::error::Changed Python files have formatting or import issues." - echo "::error::To fix, run: ruff check --preview --select I --fix && ruff format --preview " - exit 1 - fi - echo "All changed Python files are correctly formatted." - - - name: Check spotless on changed Java files - if: ${{ !cancelled() }} - run: | - set -euo pipefail - - if [ "${{ github.event_name }}" = "pull_request" ]; then - BASE=${{ github.event.pull_request.base.sha }} - else - git fetch origin master - BASE=$(git merge-base origin/master HEAD) - fi - - JAVA_FILES=$(git diff --diff-filter=ACMR --name-only "$BASE" | grep '\.java$' || true) - - if [ -z "$JAVA_FILES" ]; then - echo "No changed Java files to check." - exit 0 - fi - - echo "Changed Java files:" - echo "$JAVA_FILES" - - if ! mvn -f trick_source/java/pom.xml --batch-mode -q spotless:check; then - echo "" - echo "::error::Changed Java files are not correctly formatted." - echo "::error::To fix, run: mvn -f trick_source/java/pom.xml spotless:apply" - exit 1 - fi - echo "All changed Java files are correctly formatted." - - # TODO: Once the codebase is fully formatted, switch to whole-file checking: - # - # - name: Check formatting of changed files - # id: clang_format - # continue-on-error: true - # run: | - # set -euo pipefail - # - # if [ "${{ github.event_name }}" = "pull_request" ]; then - # BASE=${{ github.event.pull_request.base.sha }} - # else - # git fetch origin master - # BASE=$(git merge-base origin/master HEAD) - # fi - # - # FAILED=0 - # while IFS= read -r f; do - # [ -z "$f" ] && continue - # if ! clang-format -n --Werror --style=file "$f"; then - # echo "::error file=$f::$f is not correctly formatted." - # FAILED=1 - # fi - # done < <(git diff --diff-filter=ACMR --name-only "$BASE" -- \ - # '*.[hc]pp' '*.cc' '*.[ch]' '*.hh') - # - # if [ $FAILED -ne 0 ]; then - # echo "" - # echo "::error::To fix, run: clang-format -i --style=file " - # exit 1 - # fi - # echo "All changed C/C++ files are correctly formatted." - - # ────────────────────────────────────────────────────────────────────────── # Stage 2: Build & test - # ────────────────────────────────────────────────────────────────────────── - test-linux: - name: Linux (${{ matrix.cfg.os }} ${{ matrix.cfg.tag }}) needs: [style] - # TODO: Remove always() once style enforcement is stable — ideally builds only run on clean style. - if: always() - runs-on: ubuntu-latest - container: docker://${{ matrix.cfg.image }}:${{ matrix.cfg.tag }} - strategy: - fail-fast: false - matrix: - cfg: - - { os: ubuntu, tag: 24.04, arch: debian, image: ubuntu } - - { os: ubuntu, tag: 26.04, arch: debian, image: ubuntu } - - { os: oraclelinux, tag: 8, arch: rhel, image: oraclelinux } - - { os: rockylinux, tag: 8, arch: rhel, image: rockylinux/rockylinux } - - { os: rockylinux, tag: 9, arch: rhel, image: rockylinux/rockylinux } - - { os: rockylinux, tag: 10, arch: rhel, image: rockylinux/rockylinux } - include: - - cfg: {} - deps: >- - bison - clang - flex - git - llvm - make - maven - cmake - swig - zip - gdb - conf_pkg: echo package manager already configured - enable_repo: "" - install_cmd: install -y - - #-------- Debian-based Dependencies ---------------- - - cfg: { arch: debian } - pkg_mgr: apt-get - conf_pkg: apt-get update - arch_deps: >- - curl - g++ - libx11-dev - libxml2-dev - libxt-dev - libmotif-common - libmotif-dev - zlib1g-dev - llvm-dev - libclang-dev - libudunits2-dev - libgtest-dev - libgmock-dev - default-jdk - python3-dev - python3-pip - python3-venv - - #-------- RHEL-based Dependencies (all versions) ---------------- - - cfg: { arch: rhel } - pkg_mgr: dnf - conf_pkg: | - dnf -y install epel-release - dnf -y update - dnf install -y 'dnf-command(config-manager)' - arch_deps: >- - clang-devel - diffutils - gcc - gcc-c++ - gtest-devel - gmock-devel - java-21-openjdk-devel - libxml2-devel - llvm-devel - llvm-static - ncurses-devel - openmotif - openmotif-devel - perl - perl-Digest-MD5 - udunits2 - udunits2-devel - which - zlib-devel - python3-devel - - #-------- RHEL 8: gtest lives in powertools ---------------- - - cfg: { arch: rhel, tag: 8 } - enable_repo: dnf config-manager --enable powertools - - #-------- RHEL 9/10: gtest lives in crb ---------------- - - cfg: { arch: rhel, tag: 9 } - enable_repo: dnf config-manager --enable crb - - - cfg: { arch: rhel, tag: 10 } - enable_repo: dnf config-manager --enable crb - - #-------- OL 8: gtest lives in codeready_builder ---------------- - - cfg: { os: oraclelinux, tag: 8 } - enable_repo: dnf config-manager --enable ol8_codeready_builder - - steps: - - name: Set noninteractive mode - run: echo "DEBIAN_FRONTEND=noninteractive" >> "$GITHUB_ENV" - if: matrix.cfg.arch == 'debian' - - name: Update Package Manager - run: | - ${{ matrix.conf_pkg }} - ${{ matrix.enable_repo }} - - name: Install Dependencies - run: > - ${{ matrix.pkg_mgr }} - ${{ matrix.install_cmd }} - ${{ matrix.deps }} - ${{ matrix.arch_deps }} - - name: Checkout repository - uses: actions/checkout@v6 - - name: Configure Trick - run: | - echo "MAKEFLAGS=-j$(nproc)" >> "$GITHUB_ENV" - echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" - ./configure - - name: Build Trick - run: make - - name: Test Trick - run: | - python3 -m venv share/trick/trickops/.venv - . share/trick/trickops/.venv/bin/activate - pip3 install -r share/trick/trickops/requirements.txt - make test - - name: Upload Tests - uses: actions/upload-artifact@v7 - if: ${{ !cancelled() }} - with: - name: Trick_${{ matrix.cfg.os }}${{ matrix.cfg.tag }} - path: trick_test/*.xml - if-no-files-found: warn - retention-days: 1 + uses: ./.github/workflows/_test-linux.yml test-macos: - name: macOS needs: [style] - # TODO: Remove always() once style enforcement is stable — ideally builds only run on clean style. - if: always() - runs-on: macos-26 - env: - MAKEFLAGS: -j4 - steps: - - name: Checkout repository - uses: actions/checkout@v6 - # Install googletest version 1.17.0 — the latest stable release as of 6/6/25 - # Note: Need to periodically update googletest version to ensure tests can run - # against the latest. - # Specifying the version to ensure CI builds are stable and not unexpectedly - # broken by upstream changes. - - name: Install gtest 1.17.0 - run: | - wget https://github.com/google/googletest/archive/refs/tags/v1.17.0.tar.gz - tar -xvf v1.17.0.tar.gz - cd googletest-1.17.0 - cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/opt/homebrew - cmake --build build - sudo cmake --install build - - name: Install dependencies - run: | - brew update || true - brew upgrade || true - brew install --cask xquartz - brew install udunits openmotif maven - brew install swig llvm - - name: Build Trick - run: | - ./configure - make - - name: Test - run: | - python3 -m venv share/trick/trickops/.venv - . share/trick/trickops/.venv/bin/activate - pip3 install -r share/trick/trickops/requirements.txt - make test - - name: Upload Tests - uses: actions/upload-artifact@v7 - if: ${{ !cancelled() }} - with: - name: Trick_macos - path: trick_test/*.xml - if-no-files-found: warn - retention-days: 1 + uses: ./.github/workflows/_test-macos.yml trickops: - name: TrickOps (${{ matrix.name }}) needs: [style] - # TODO: Remove always() once style enforcement is stable — ideally builds only run on clean style. - if: always() - runs-on: ubuntu-latest - container: ${{ matrix.image }} - strategy: - fail-fast: false - matrix: - include: - - name: Ubuntu 24.04 - image: ubuntu:24.04 - install: | - export DEBIAN_FRONTEND=noninteractive - apt-get update - apt-get install -y git python3 python3-venv perl perl-modules-5.38 qtbase5-dev wget unzip g++ make flex bison - qmake: qmake - - name: RockyLinux 8 - image: rockylinux/rockylinux:8 - install: | - dnf install -y git python3-devel which perl perl-Digest-MD5 qt5-qtbase-devel bison clang flex make gcc gcc-c++ wget - qmake: qmake-qt5 - defaults: - run: - shell: bash - steps: - - name: Checkout repository - uses: actions/checkout@v6 - - name: Install dependencies - run: ${{ matrix.install }} - - name: Create virtual environment - run: | - python3 -m venv share/trick/trickops/.venv - . share/trick/trickops/.venv/bin/activate - pip3 install --upgrade pip && pip3 install -r share/trick/trickops/requirements.txt - - name: Build koviz - run: | - git clone --depth 1 https://github.com/nasa/koviz.git /tmp/koviz - cd /tmp/koviz && ${{ matrix.qmake }} && make - - name: Run unit and doc tests - run: | - . share/trick/trickops/.venv/bin/activate - export PATH="/tmp/koviz/bin:${PATH}" - cd share/trick/trickops/tests && ./run_tests.py - - name: Upload test logs - uses: actions/upload-artifact@v7 - if: always() - with: - name: trickops_${{ strategy.job-index }} - path: | - share/trick/trickops/tests/*_doctest_log.txt - /tmp/log.* + uses: ./.github/workflows/_trickops.yml code-coverage: - name: Code Coverage needs: [style] - # TODO: Remove always() once style enforcement is stable — ideally builds only run on clean style. - if: always() - runs-on: ubuntu-latest - env: - CFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" - CXXFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" - LDFLAGS: "-fprofile-arcs -ftest-coverage -O0" - TRICK_CFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" - TRICK_CXXFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" - TRICK_SYSTEM_LDFLAGS: "-fprofile-arcs -ftest-coverage -O0" - TRICK_SYSTEM_CFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" - TRICK_SYSTEM_CXXFLAGS: "-fprofile-arcs -ftest-coverage -fprofile-update=atomic -O0" - steps: - - name: Install Dependencies - run: | - sudo apt-get update -qq - sudo apt-get install -y \ - bison clang flex git llvm make maven cmake zip \ - g++ libclang-dev llvm-dev \ - default-jdk libxml2-dev libmotif-dev libxt-dev \ - perl libdigest-md5-perl libudunits2-dev zlib1g-dev \ - python3-dev python3-pip python3-venv swig \ - libgtest-dev libgmock-dev lcov - - name: Checkout repository - uses: actions/checkout@v6 - - name: Configure Trick - run: | - echo "MAKEFLAGS=-j$(nproc)" >> "$GITHUB_ENV" - echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" - ./configure - - name: Build Trick - run: make - - name: Generate Code Coverage - run: | - python3 -m venv share/trick/trickops/.venv - . share/trick/trickops/.venv/bin/activate - pip3 install -r share/trick/trickops/requirements.txt - make code-coverage - - name: Upload to Coveralls - uses: coverallsapp/github-action@v2 - continue-on-error: true - with: - file: coverage.info - format: lcov + uses: ./.github/workflows/_code-coverage.yml diff --git a/.github/workflows/test-report.yml b/.github/workflows/test-report.yml index 6694ab2c2..3e040e432 100644 --- a/.github/workflows/test-report.yml +++ b/.github/workflows/test-report.yml @@ -1,5 +1,13 @@ name: Test Report +# Tie each run's title back to the CI run that triggered it, so the Actions list +# shows e.g. "Test Report · CI #98 (branch) · " instead of a +# context-free "Test Report #63". +run-name: >- + Test Report · CI #${{ github.event.workflow_run.run_number }} + (${{ github.event.workflow_run.head_branch }}) · + ${{ github.event.workflow_run.display_title }} + on: workflow_run: workflows: [CI] @@ -19,11 +27,12 @@ jobs: - name: Publish Test Report uses: dorny/test-reporter@v3 with: - # Matches all Trick_* artifacts uploaded by the CI workflow. - artifact: /Trick_(.*)/ - name: Test Results ($1) + # Matches all "Test - *" artifacts uploaded by the CI workflow. + artifact: /Test - (.*)/ + name: $1 path: "*.xml" reporter: java-junit fail-on-error: false fail-on-empty: false max-annotations: 50 + use-actions-summary: false