diff --git a/.github/workflows/test.yml b/.github/workflows/ci.yml similarity index 54% rename from .github/workflows/test.yml rename to .github/workflows/ci.yml index 0b2866c..79edf7f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Test +name: CI on: push: @@ -21,7 +21,7 @@ jobs: fail-fast: false matrix: include: - - { os: ubuntu-latest, container: '', shell_path: /bin/bash, label: bash-5.x-linux } + - { os: ubuntu-latest, container: '', shell_path: /bin/bash, label: bash-5.x-linux, coverage: true } - { os: macos-latest, container: '', shell_path: /bin/bash, label: bash-3.2-macos } - { os: macos-latest, container: '', shell_path: brew, label: bash-5.x-macos } - { os: ubuntu-latest, container: rockylinux:8, shell_path: /bin/bash, label: bash-4.4-linux } @@ -47,6 +47,17 @@ jobs: if: matrix.container == '' && runner.os == 'Linux' && contains(matrix.shell_path, 'zsh') run: sudo apt-get update && sudo apt-get install -y zsh + - name: Install kcov (coverage, Linux) + if: matrix.coverage + run: | + sudo apt-get update + sudo apt-get install -y cmake binutils-dev libcurl4-openssl-dev libdw-dev libiberty-dev + wget -q https://github.com/SimonKagstrom/kcov/archive/refs/tags/v43.tar.gz + tar xzf v43.tar.gz + (cd kcov-43 && mkdir -p build && cd build && cmake -DCMAKE_INSTALL_PREFIX="$HOME/kcov" .. && make && make install) + rm -rf kcov-43 v43.tar.gz + echo "$HOME/kcov/bin" >> "$GITHUB_PATH" + - name: Install Homebrew bash (macOS bash 5.x) if: matrix.shell_path == 'brew' run: | @@ -70,5 +81,40 @@ jobs: - name: Run tests (${{ matrix.label }}) # test.sh self-sources ~/.xshrc, so running it under $SHELL_PATH makes # the utilities execute under that shell (bash, or zsh's ksh emulation). + if: matrix.coverage != true run: | "$SHELL_PATH" ~/.xsh/repo/xsh-lib/core/test.sh + + - name: Run tests with coverage (${{ matrix.label }}) + # Pass the script DIRECTLY (not `bash test.sh`): kcov then uses its shell + # collector. `bash test.sh` makes kcov trace the bash ELF binary and + # record 0 lines. --include-path scopes the report to this lib's function + # utilities; script-type utilities run as child `bash` and aren't traced. + if: matrix.coverage + run: | + chmod +x "$HOME/.xsh/repo/xsh-lib/core/test.sh" + # Scope to the function utilities (script utilities run as child bash and + # aren't instrumentable). + kcov --include-path="$HOME/.xsh/repo/xsh-lib/core/functions" "$PWD/coverage" "$HOME/.xsh/repo/xsh-lib/core/test.sh" + # Normalize kcov's cobertura to repo-relative, line-only paths. + cob="$(find "$PWD/coverage" -name cobertura.xml | head -1)" + sed -i -E \ + -e 's#[^<]*#.#' \ + -e 's#filename="#filename="functions/#g' \ + -e 's/ branch(es)?-(rate|covered|valid)="[^"]*"//g' \ + "$cob" + # Keep only the cobertura so codecov-action's auto-search uploads just it + # (kcov also writes coverage.json/codecov.json with absolute paths). + find "$PWD/coverage" -type f ! -name cobertura.xml -delete + + - name: Upload coverage to Codecov + if: matrix.coverage + uses: codecov/codecov-action@v5 + env: + # codecov CLI's GPG self-check on its own download fails intermittently. + CC_SKIP_VALIDATION: 'true' + with: + # Mirror alexzhangs/xsh's WORKING setup: let codecov-action auto-discover + # the kcov report (no files:/disable_search:). + token: ${{ secrets.CODECOV_TOKEN }} + fail_ci_if_error: false diff --git a/README.md b/README.md index c6ba598..505ed52 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,9 @@ [![GitHub](https://img.shields.io/github/license/xsh-lib/core.svg?style=flat-square)](https://github.com/xsh-lib/core/) [![GitHub last commit](https://img.shields.io/github/last-commit/xsh-lib/core.svg?style=flat-square)](https://github.com/xsh-lib/core/commits/master) -[![Test](https://github.com/xsh-lib/core/actions/workflows/test.yml/badge.svg)](https://github.com/xsh-lib/core/actions/workflows/test.yml) +[![CI](https://github.com/xsh-lib/core/actions/workflows/ci.yml/badge.svg)](https://github.com/xsh-lib/core/actions/workflows/ci.yml) +[![codecov](https://codecov.io/github/xsh-lib/core/graph/badge.svg?token=9N6P7MY18U)](https://codecov.io/github/xsh-lib/core) +[![CodeFactor](https://www.codefactor.io/repository/github/xsh-lib/core/badge)](https://www.codefactor.io/repository/github/xsh-lib/core) [![GitHub issues](https://img.shields.io/github/issues/xsh-lib/core.svg?style=flat-square)](https://github.com/xsh-lib/core/issues) [![GitHub pull requests](https://img.shields.io/github/issues-pr/xsh-lib/core.svg?style=flat-square)](https://github.com/xsh-lib/core/pulls) @@ -18,10 +20,18 @@ About xsh and its libraries, check out [xsh document](https://github.com/alexzha ## Requirements -Tested with bash: +`xsh-lib/core` is tested in CI ([GitHub Actions](https://github.com/xsh-lib/core/actions/workflows/ci.yml)) on every push and pull request, across the following shell/OS combinations: -* 4.3.48 on Linux -* 3.2.57 on macOS +| Shell | Version | OS | Tested | +|-------|---------|-----------------------|:------:| +| bash | 3.2 | macOS | ✅ | +| bash | 4.4 | Linux (rockylinux:8) | ✅ | +| bash | 5.x | Linux (ubuntu-latest) | ✅ | +| bash | 5.x | macOS (Homebrew) | ✅ | +| zsh | 5.x | Linux (ubuntu-latest) | ✅ | +| zsh | 5.x | macOS | ✅ | + +zsh utilities run under xsh's ksh emulation and require **xsh ≥ 0.7.0**. This project is still at version 0.x, and should be considered immature. diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..c9f4b0a --- /dev/null +++ b/codecov.yml @@ -0,0 +1,17 @@ +# Coverage is scoped to the function utilities and the cobertura is rewritten to +# repo-relative paths (functions/...) in CI, so no path fixes are needed here. +# Coverage is informational, not a merge gate (only function-type utilities are +# instrumentable; script utilities run as child processes kcov cannot trace). +# Coverage is scoped to the function utilities and rewritten to repo-relative +# paths (functions/...) in CI, so no path fixes/ignores are needed here. +# Informational only (script utilities aren't instrumentable). +coverage: + status: + project: + default: + informational: true + patch: + default: + informational: true + +comment: false