ci: use macos-latest
instead of macos-13
#20610
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: GnuTests | |
# spell-checker:ignore (abbrev/names) CodeCov gnulib GnuTests Swatinem | |
# spell-checker:ignore (jargon) submodules devel | |
# spell-checker:ignore (libs/utils) autopoint chksum getenforce gperf lcov libexpect limactl pyinotify setenforce shopt texinfo valgrind libattr libcap taiki-e | |
# spell-checker:ignore (options) Ccodegen Coverflow Cpanic Zpanic | |
# spell-checker:ignore (people) Dawid Dziurla * dawidd dtolnay | |
# spell-checker:ignore (vars) FILESET SUBDIRS XPASS | |
# * note: to run a single test => `REPO/util/run-gnu-test.sh PATH/TO/TEST/SCRIPT` | |
on: | |
pull_request: | |
push: | |
branches: | |
- '*' | |
permissions: | |
contents: read | |
# End the current execution if there is a new changeset in the PR. | |
concurrency: | |
group: ${{ github.workflow }}-${{ github.ref }} | |
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} | |
env: | |
DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} | |
TEST_FULL_SUMMARY_FILE: 'gnu-full-result.json' | |
TEST_ROOT_FULL_SUMMARY_FILE: 'gnu-root-full-result.json' | |
TEST_SELINUX_FULL_SUMMARY_FILE: 'selinux-gnu-full-result.json' | |
TEST_SELINUX_ROOT_FULL_SUMMARY_FILE: 'selinux-root-gnu-full-result.json' | |
REPO_GNU_REF: "v9.7" | |
jobs: | |
native: | |
name: Run GNU tests (native) | |
runs-on: ubuntu-24.04 | |
steps: | |
#### Get the code, setup cache | |
- name: Checkout code (uutils) | |
uses: actions/checkout@v4 | |
with: | |
path: 'uutils' | |
persist-credentials: false | |
- uses: dtolnay/rust-toolchain@master | |
with: | |
toolchain: stable | |
components: rustfmt | |
- uses: Swatinem/rust-cache@v2 | |
with: | |
workspaces: "./uutils -> target" | |
- name: Checkout code (GNU coreutils) | |
uses: actions/checkout@v4 | |
with: | |
repository: 'coreutils/coreutils' | |
path: 'gnu' | |
ref: ${{ env.REPO_GNU_REF }} | |
submodules: false | |
persist-credentials: false | |
- name: Override submodule URL and initialize submodules | |
# Use github instead of upstream git server | |
run: | | |
git submodule sync --recursive | |
git config submodule.gnulib.url https://github.com/coreutils/gnulib.git | |
git submodule update --init --recursive --depth 1 | |
working-directory: gnu | |
#### Build environment setup | |
- name: Install dependencies | |
shell: bash | |
run: | | |
## Install dependencies | |
sudo apt-get update | |
sudo apt-get install -y autoconf autopoint bison texinfo gperf gcc g++ gdb python3-pyinotify jq valgrind libexpect-perl libacl1-dev libattr1-dev libcap-dev libselinux1-dev attr quilt | |
- name: Add various locales | |
shell: bash | |
run: | | |
## Add various locales | |
echo "Before:" | |
locale -a | |
## Some tests fail with 'cannot change locale (en_US.ISO-8859-1): No such file or directory' | |
## Some others need a French locale | |
sudo locale-gen | |
sudo locale-gen --keep-existing fr_FR | |
sudo locale-gen --keep-existing fr_FR.UTF-8 | |
sudo locale-gen --keep-existing es_ES.UTF-8 | |
sudo locale-gen --keep-existing sv_SE | |
sudo locale-gen --keep-existing sv_SE.UTF-8 | |
sudo locale-gen --keep-existing en_US | |
sudo locale-gen --keep-existing en_US.UTF-8 | |
sudo locale-gen --keep-existing ru_RU.KOI8-R | |
sudo update-locale | |
echo "After:" | |
locale -a | |
### Build | |
- name: Build binaries | |
shell: bash | |
run: | | |
## Build binaries | |
cd 'uutils' | |
bash util/build-gnu.sh --release-build | |
### Run tests as user | |
- name: Run GNU tests | |
shell: bash | |
run: | | |
## Run GNU tests | |
path_GNU='gnu' | |
path_UUTILS='uutils' | |
bash "uutils/util/run-gnu-test.sh" | |
- name: Extract testing info from individual logs into JSON | |
shell: bash | |
run : | | |
path_UUTILS='uutils' | |
python uutils/util/gnu-json-result.py gnu/tests > ${{ env.TEST_FULL_SUMMARY_FILE }} | |
### Run tests as root | |
- name: Run GNU root tests | |
shell: bash | |
run: | | |
## Run GNU root tests | |
path_GNU='gnu' | |
path_UUTILS='uutils' | |
bash "uutils/util/run-gnu-test.sh" run-root | |
- name: Extract testing info from individual logs (run as root) into JSON | |
shell: bash | |
run : | | |
path_UUTILS='uutils' | |
python uutils/util/gnu-json-result.py gnu/tests > ${{ env.TEST_ROOT_FULL_SUMMARY_FILE }} | |
### Upload artifacts | |
- name: Upload full json results | |
uses: actions/upload-artifact@v4 | |
with: | |
name: gnu-full-result | |
path: ${{ env.TEST_FULL_SUMMARY_FILE }} | |
- name: Upload root json results | |
uses: actions/upload-artifact@v4 | |
with: | |
name: gnu-root-full-result | |
path: ${{ env.TEST_ROOT_FULL_SUMMARY_FILE }} | |
- name: Compress test logs | |
shell: bash | |
run : | | |
# Compress logs before upload (fails otherwise) | |
gzip gnu/tests/*/*.log | |
- name: Upload test logs | |
uses: actions/upload-artifact@v4 | |
with: | |
name: test-logs | |
path: | | |
gnu/tests/*.log | |
gnu/tests/*/*.log.gz | |
selinux: | |
name: Run GNU tests (SELinux) | |
runs-on: ubuntu-24.04 | |
steps: | |
#### Get the code, setup cache | |
- name: Checkout code (uutils) | |
uses: actions/checkout@v4 | |
with: | |
path: 'uutils' | |
persist-credentials: false | |
- uses: dtolnay/rust-toolchain@master | |
with: | |
toolchain: stable | |
components: rustfmt | |
- uses: Swatinem/rust-cache@v2 | |
with: | |
workspaces: "./uutils -> target" | |
- name: Checkout code (GNU coreutils) | |
uses: actions/checkout@v4 | |
with: | |
repository: 'coreutils/coreutils' | |
path: 'gnu' | |
ref: ${{ env.REPO_GNU_REF }} | |
submodules: false | |
persist-credentials: false | |
- name: Override submodule URL and initialize submodules | |
# Use github instead of upstream git server | |
run: | | |
git submodule sync --recursive | |
git config submodule.gnulib.url https://github.com/coreutils/gnulib.git | |
git submodule update --init --recursive --depth 1 | |
working-directory: gnu | |
#### Lima build environment setup | |
- name: Setup Lima | |
uses: lima-vm/lima-actions/setup@v1 | |
id: lima-actions-setup | |
- name: Cache ~/.cache/lima | |
uses: actions/cache@v4 | |
with: | |
path: ~/.cache/lima | |
key: lima-${{ steps.lima-actions-setup.outputs.version }} | |
- name: Start Fedora VM with SELinux | |
run: limactl start --plain --name=default --cpus=4 --disk=40 --memory=8 --network=lima:user-v2 template://fedora | |
- name: Setup SSH | |
uses: lima-vm/lima-actions/ssh@v1 | |
- name: Verify SELinux Status and Configuration | |
run: | | |
lima getenforce | |
lima ls -laZ /etc/selinux | |
lima sudo sestatus | |
# Ensure we're running in enforcing mode | |
lima sudo setenforce 1 | |
lima getenforce | |
# Create test files with SELinux contexts for testing | |
lima sudo mkdir -p /var/test_selinux | |
lima sudo touch /var/test_selinux/test_file | |
lima sudo chcon -t etc_t /var/test_selinux/test_file | |
lima ls -Z /var/test_selinux/test_file # Verify context | |
- name: Install dependencies in VM | |
run: | | |
lima sudo dnf -y update | |
lima sudo dnf -y install git autoconf autopoint bison texinfo gperf gcc g++ gdb jq libacl-devel libattr-devel libcap-devel libselinux-devel attr rustup clang-devel texinfo-tex wget automake patch quilt | |
lima rustup-init -y --default-toolchain stable | |
- name: Copy the sources to VM | |
run: | | |
rsync -a -e ssh . lima-default:~/work/ | |
### Build | |
- name: Build binaries | |
run: | | |
lima bash -c "cd ~/work/uutils/ && bash util/build-gnu.sh --release-build" | |
### Run tests as user | |
- name: Generate SELinux tests list | |
run: | | |
# Find and list all tests that require SELinux | |
lima bash -c "cd ~/work/gnu/ && grep -l 'require_selinux_' -r tests/ > ~/work/uutils/selinux-tests.txt" | |
lima bash -c "cd ~/work/uutils/ && cat selinux-tests.txt" | |
# Count the tests | |
lima bash -c "cd ~/work/uutils/ && echo 'Found SELinux tests:'; wc -l selinux-tests.txt" | |
- name: Run GNU SELinux tests | |
run: | | |
lima sudo setenforce 1 | |
lima getenforce | |
lima cat /proc/filesystems | |
lima bash -c "cd ~/work/uutils/ && bash util/run-gnu-test.sh \$(cat selinux-tests.txt)" | |
- name: Extract testing info from individual logs into JSON | |
shell: bash | |
run : | | |
lima bash -c "cd ~/work/gnu/ && python3 ../uutils/util/gnu-json-result.py tests > ~/work/${{ env.TEST_SELINUX_FULL_SUMMARY_FILE }}" | |
### Run tests as root | |
- name: Run GNU SELinux root tests | |
run: | | |
lima bash -c "cd ~/work/uutils/ && CI=1 bash util/run-gnu-test.sh run-root \$(cat selinux-tests.txt)" | |
- name: Extract testing info from individual logs (run as root) into JSON | |
shell: bash | |
run : | | |
lima bash -c "cd ~/work/gnu/ && python3 ../uutils/util/gnu-json-result.py tests > ~/work/${{ env.TEST_SELINUX_ROOT_FULL_SUMMARY_FILE }}" | |
### Upload artifacts | |
- name: Collect test logs and test results from VM | |
run: | | |
mkdir -p gnu/tests-selinux | |
# Copy the json output back from the Lima VM to the host | |
rsync -v -a -e ssh lima-default:~/work/*.json ./ | |
# Copy the test directory now | |
rsync -v -a -e ssh lima-default:~/work/gnu/tests/ ./gnu/tests-selinux/ | |
- name: Upload SELinux json results | |
uses: actions/upload-artifact@v4 | |
with: | |
name: selinux-gnu-full-result | |
path: ${{ env.TEST_SELINUX_FULL_SUMMARY_FILE }} | |
- name: Upload SELinux root json results | |
uses: actions/upload-artifact@v4 | |
with: | |
name: selinux-root-gnu-full-result | |
path: ${{ env.TEST_SELINUX_ROOT_FULL_SUMMARY_FILE }} | |
- name: Compress SELinux test logs | |
shell: bash | |
run : | | |
# Compress logs before upload (fails otherwise) | |
gzip gnu/tests-selinux/*/*.log | |
- name: Upload SELinux test logs | |
uses: actions/upload-artifact@v4 | |
with: | |
name: selinux-test-logs | |
path: | | |
gnu/tests-selinux/*.log | |
gnu/tests-selinux/*/*.log.gz | |
aggregate: | |
needs: [native, selinux] | |
permissions: | |
actions: read # for dawidd6/action-download-artifact to query and download artifacts | |
contents: read # for actions/checkout to fetch code | |
pull-requests: read # for dawidd6/action-download-artifact to query commit hash | |
name: Aggregate GNU test results | |
runs-on: ubuntu-24.04 | |
steps: | |
- name: Initialize workflow variables | |
id: vars | |
shell: bash | |
run: | | |
## VARs setup | |
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } | |
# | |
TEST_SUMMARY_FILE='gnu-result.json' | |
AGGREGATED_SUMMARY_FILE='aggregated-result.json' | |
outputs TEST_SUMMARY_FILE AGGREGATED_SUMMARY_FILE | |
- name: Checkout code (uutils) | |
uses: actions/checkout@v4 | |
with: | |
path: 'uutils' | |
persist-credentials: false | |
- name: Retrieve reference artifacts | |
uses: dawidd6/action-download-artifact@v11 | |
# ref: <https://github.com/dawidd6/action-download-artifact> | |
continue-on-error: true ## don't break the build for missing reference artifacts (may be expired or just not generated yet) | |
with: | |
workflow: GnuTests.yml | |
branch: "${{ env.DEFAULT_BRANCH }}" | |
# workflow_conclusion: success ## (default); * but, if commit with failed GnuTests is merged into the default branch, future commits will all show regression errors in GnuTests CI until o/w fixed | |
workflow_conclusion: completed ## continually recalibrates to last commit of default branch with a successful GnuTests (ie, "self-heals" from GnuTest regressions, but needs more supervision for/of regressions) | |
path: "reference" | |
- name: Download full json results | |
uses: actions/download-artifact@v4 | |
with: | |
name: gnu-full-result | |
path: results | |
merge-multiple: true | |
- name: Download root json results | |
uses: actions/download-artifact@v4 | |
with: | |
name: gnu-root-full-result | |
path: results | |
merge-multiple: true | |
- name: Download selinux json results | |
uses: actions/download-artifact@v4 | |
with: | |
name: selinux-gnu-full-result | |
path: results | |
merge-multiple: true | |
- name: Download selinux root json results | |
uses: actions/download-artifact@v4 | |
with: | |
name: selinux-root-gnu-full-result | |
path: results | |
merge-multiple: true | |
- name: Extract/summarize testing info | |
id: summary | |
shell: bash | |
run: | | |
## Extract/summarize testing info | |
outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } | |
path_UUTILS='uutils' | |
json_count=$(ls -l results/*.json | wc -l) | |
if [[ "$json_count" -ne 4 ]]; then | |
echo "::error ::Failed to download all results json files (expected 4 files, found $json_count); failing early" | |
ls -lR results || true | |
exit 1 | |
fi | |
# Look at all individual results and summarize | |
eval $(python3 uutils/util/analyze-gnu-results.py -o=${{ steps.vars.outputs.AGGREGATED_SUMMARY_FILE }} results/*.json) | |
if [[ "$TOTAL" -eq 0 || "$TOTAL" -eq 1 ]]; then | |
echo "::error ::Failed to parse test results from '${{ env.TEST_FULL_SUMMARY_FILE }}'; failing early" | |
exit 1 | |
fi | |
output="GNU tests summary = TOTAL: $TOTAL / PASS: $PASS / FAIL: $FAIL / ERROR: $ERROR / SKIP: $SKIP" | |
echo "${output}" | |
if [[ "$FAIL" -gt 0 || "$ERROR" -gt 0 ]]; then | |
echo "::warning ::${output}" | |
fi | |
jq -n \ | |
--arg date "$(date --rfc-email)" \ | |
--arg sha "$GITHUB_SHA" \ | |
--arg total "$TOTAL" \ | |
--arg pass "$PASS" \ | |
--arg skip "$SKIP" \ | |
--arg fail "$FAIL" \ | |
--arg xpass "$XPASS" \ | |
--arg error "$ERROR" \ | |
'{($date): { sha: $sha, total: $total, pass: $pass, skip: $skip, fail: $fail, xpass: $xpass, error: $error, }}' > '${{ steps.vars.outputs.TEST_SUMMARY_FILE }}' | |
HASH=$(sha1sum '${{ steps.vars.outputs.TEST_SUMMARY_FILE }}' | cut --delim=" " -f 1) | |
outputs HASH | |
- name: Upload SHA1/ID of 'test-summary' | |
uses: actions/upload-artifact@v4 | |
with: | |
name: "${{ steps.summary.outputs.HASH }}" | |
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}" | |
- name: Upload test results summary | |
uses: actions/upload-artifact@v4 | |
with: | |
name: test-summary | |
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}" | |
- name: Upload aggregated json results | |
uses: actions/upload-artifact@v4 | |
with: | |
name: aggregated-result | |
path: ${{ steps.vars.outputs.AGGREGATED_SUMMARY_FILE }} | |
- name: Compare test failures VS reference | |
shell: bash | |
run: | | |
## Compare test failures VS reference using JSON files | |
REF_SUMMARY_FILE='reference/aggregated-result/aggregated-result.json' | |
CURRENT_SUMMARY_FILE='${{ steps.vars.outputs.AGGREGATED_SUMMARY_FILE }}' | |
REPO_DEFAULT_BRANCH='${{ env.DEFAULT_BRANCH }}' | |
path_UUTILS='uutils' | |
# Path to ignore file for intermittent issues | |
IGNORE_INTERMITTENT="uutils/.github/workflows/ignore-intermittent.txt" | |
# Set up comment directory | |
COMMENT_DIR="reference/comment" | |
mkdir -p ${COMMENT_DIR} | |
echo ${{ github.event.number }} > ${COMMENT_DIR}/NR | |
COMMENT_LOG="${COMMENT_DIR}/result.txt" | |
COMPARISON_RESULT=0 | |
if test -f "${CURRENT_SUMMARY_FILE}"; then | |
if test -f "${REF_SUMMARY_FILE}"; then | |
echo "Reference summary SHA1/ID: $(sha1sum -- "${REF_SUMMARY_FILE}")" | |
echo "Current summary SHA1/ID: $(sha1sum -- "${CURRENT_SUMMARY_FILE}")" | |
python3 uutils/util/compare_test_results.py \ | |
--ignore-file "${IGNORE_INTERMITTENT}" \ | |
--output "${COMMENT_LOG}" \ | |
"${CURRENT_SUMMARY_FILE}" "${REF_SUMMARY_FILE}" | |
COMPARISON_RESULT=$? | |
else | |
echo "::warning ::Skipping test comparison; no prior reference summary is available at '${REF_SUMMARY_FILE}'." | |
fi | |
else | |
echo "::error ::Failed to find summary of test results (missing '${CURRENT_SUMMARY_FILE}'); failing early" | |
exit 1 | |
fi | |
if [ ${COMPARISON_RESULT} -eq 1 ]; then | |
echo "ONLY_INTERMITTENT=false" >> $GITHUB_ENV | |
echo "::error ::Found new non-intermittent test failures" | |
exit 1 | |
else | |
echo "ONLY_INTERMITTENT=true" >> $GITHUB_ENV | |
echo "::notice ::No new test failures detected" | |
fi | |
- name: Upload comparison log (for GnuComment workflow) | |
if: success() || failure() # run regardless of prior step success/failure | |
uses: actions/upload-artifact@v4 | |
with: | |
name: comment | |
path: reference/comment/ | |
- name: Compare test summary VS reference | |
if: success() || failure() # run regardless of prior step success/failure | |
shell: bash | |
run: | | |
## Compare test summary VS reference | |
REF_SUMMARY_FILE='reference/test-summary/gnu-result.json' | |
if test -f "${REF_SUMMARY_FILE}"; then | |
echo "Reference SHA1/ID: $(sha1sum -- "${REF_SUMMARY_FILE}")" | |
mv "${REF_SUMMARY_FILE}" main-gnu-result.json | |
python uutils/util/compare_gnu_result.py | |
else | |
echo "::warning ::Skipping test summary comparison; no prior reference summary is available." | |
fi |