Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • pkg-rebase-trixie/rust-coreutils
  • pkg/rust-coreutils
2 results
Show changes
Commits on Source (72)
Showing
with 2146 additions and 454 deletions
[target.x86_64-unknown-redox]
linker = "x86_64-unknown-redox-gcc"
[target.x86_64-unknown-redox]
linker = "x86_64-unknown-redox-gcc"
[target.'cfg(clippy)']
rustflags = [
"-Wclippy::use_self",
"-Wclippy::needless_pass_by_value",
"-Wclippy::semicolon_if_nothing_returned",
"-Wclippy::single_char_pattern",
"-Wclippy::explicit_iter_loop",
"-Wclippy::if_not_else",
]
{
"git": {
"sha1": "17a4a224f3f2f3f4ae465a210ba2260e9c47db17"
},
"path_in_vcs": ""
}
\ No newline at end of file
task:
name: stable x86_64-unknown-freebsd-12
freebsd_instance:
image: freebsd-12-1-release-amd64
setup_script:
- pkg install -y curl gmake
- curl https://sh.rustup.rs -sSf --output rustup.sh
- sh rustup.sh -y --profile=minimal
build_script:
- . $HOME/.cargo/env
- cargo build
test_script:
- . $HOME/.cargo/env
- cargo test
msrv = "1.70.0"
cognitive-complexity-threshold = 24
[codespell]
ignore-words-list = crate
skip = ./.git/**,./.vscode/cspell.dictionaries/**,./target/**,./tests/fixtures/**
[profile.ci]
retries = 2
status-level = "all"
final-status-level = "skip"
failure-output = "immediate-final"
fail-fast = false
# EditorConfig (is awesome): http://EditorConfig.org
# EditorConfig (is awesome!; ref: http://EditorConfig.org; v2022.02.11 [rivy])
# * top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
# default ~ utf-8, unix-style newlines with a newline ending every file, 4 space indentation
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
max_line_length = 100
trim_trailing_whitespace = true
[{[Mm]akefile{,.*},*.{mak,mk,[Mm][Aa][Kk],[Mm][Kk]},[Gg][Nn][Uu]makefile}]
# makefiles ~ TAB-style indentation
indent_style = tab
[*.bash]
# `bash` shell scripts
indent_size = 4
indent_style = space
# * ref: <https://github.com/foxundermoon/vs-shell-format/blob/bc56a8e367b04bbf7d9947b767dc82516a6155b7/src/shFormat.ts>
# shell_variant = bash ## allow `shellcheck` to decide via script hash-bang/sha-bang line
switch_case_indent = true
[*.{bat,cmd,[Bb][Aa][Tt],[Cc][Mm][Dd]}]
# DOS/Win requires BAT/CMD files to have CRLF EOLNs
# BAT/CMD ~ DOS/Win requires BAT/CMD files to have CRLF EOLNs
end_of_line = crlf
[[Mm]akefile{,.*}]
# TAB-style indentation
[*.{cjs,cjx,cts,ctx,js,jsx,mjs,mts,mtx,ts,tsx,json,jsonc}]
# js/ts/json ~ Prettier/XO-style == TAB indention + SPACE alignment
indent_size = 2
indent_style = tab
[*.go]
# go ~ TAB-style indentation (SPACE-style alignment); ref: <https://blog.golang.org/gofmt>@@<https://archive.is/wip/9B6FC>
indent_style = tab
[*.{yml,[Yy][Mm][Ll]}]
[*.{markdown,md,mkd,[Mm][Dd],[Mm][Kk][Dd],[Mm][Dd][Oo][Ww][Nn],[Mm][Kk][Dd][Oo][Ww][Nn],[Mm][Aa][Rr][Kk][Dd][Oo][Ww][Nn]}]
# markdown
indent_size = 2
indent_style = space
[*.sh]
# POSIX shell scripts
indent_size = 4
indent_style = space
# * ref: <https://github.com/foxundermoon/vs-shell-format/blob/bc56a8e367b04bbf7d9947b767dc82516a6155b7/src/shFormat.ts>
# shell_variant = posix ## allow `shellcheck` to decide via script hash-bang/sha-bang line
switch_case_indent = true
[*.{sln,vc{,x}proj{,.*},[Ss][Ln][Nn],[Vv][Cc]{,[Xx]}[Pp][Rr][Oo][Jj]{,.*}}]
# MSVC sln/vcproj/vcxproj files, when used, will persistantly revert to CRLF EOLNs and eat final EOLs
end_of_line = crlf
insert_final_newline = false
[*.{yaml,yml,[Yy][Mm][Ll],[Yy][Aa][Mm][Ll]}]
# YAML
indent_size = 2
indent_style = space
github: uutils
# Number of days of inactivity before an issue/PR becomes stale
daysUntilStale: 365
# spell-checker:ignore (labels) wontfix
# Number of days of inactivity before an issue/PR becomes stale
daysUntilStale: 1095
# Number of days of inactivity before a stale issue/PR is closed
daysUntilClose: 365
daysUntilClose: 1095
# Issues with these labels will never be considered stale
exemptLabels:
- pinned
......
This diff is collapsed.
name: CheckScripts
# spell-checker:ignore ludeeus mfinelli
env:
SCRIPT_DIR: 'util'
on:
push:
branches:
- main
paths:
- 'util/**/*.sh'
pull_request:
branches:
- main
paths:
- 'util/**/*.sh'
# 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' }}
jobs:
shell_check:
name: ShellScript/Check
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
env:
SHELLCHECK_OPTS: -s bash
with:
severity: warning
scandir: ${{ env.SCRIPT_DIR }}
format: tty
shell_fmt:
name: ShellScript/Format
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- name: Setup shfmt
uses: mfinelli/setup-shfmt@v3
- name: Run shfmt
shell: bash
run: |
# fmt options: bash syntax, 4 spaces indent, indent for switch-case
echo "## show the differences between formatted and original scripts..."
find ${{ env.SCRIPT_DIR }} -name "*.sh" -print0 | xargs -0 shfmt -ln=bash -i 4 -ci -d || true
name: FixPR
# spell-checker:ignore Swatinem dtolnay
# Trigger automated fixes for PRs being merged (with associated commits)
env:
BRANCH_TARGET: main
on:
# * only trigger on pull request closed to specific branches
# ref: https://github.community/t/trigger-workflow-only-on-pull-request-merge/17359/9
pull_request:
branches:
- main # == env.BRANCH_TARGET ## unfortunately, env context variables are only available in jobs/steps (see <https://github.community/t/how-to-use-env-context/16975/2>)
types: [ closed ]
jobs:
code_deps:
# Refresh dependencies (ie, 'Cargo.lock') and show updated dependency tree
if: github.event.pull_request.merged == true ## only for PR merges
name: Update/dependencies
runs-on: ${{ matrix.job.os }}
strategy:
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v4
- name: Initialize job variables
id: vars
shell: bash
run: |
# surface MSRV from CICD workflow
RUST_MIN_SRV=$(grep -P "^\s+RUST_MIN_SRV:" .github/workflows/CICD.yml | grep -Po "(?<=\x22)\d+[.]\d+(?:[.]\d+)?(?=\x22)" )
echo "RUST_MIN_SRV=${RUST_MIN_SRV}" >> $GITHUB_OUTPUT
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ steps.vars.outputs.RUST_MIN_SRV }}
- uses: Swatinem/rust-cache@v2
- name: Ensure updated 'Cargo.lock'
shell: bash
run: |
# Ensure updated 'Cargo.lock'
# * 'Cargo.lock' is required to be in a format that `cargo` of MinSRV can interpret (eg, v1-format for MinSRV < v1.38)
cargo fetch --locked --quiet || cargo +${{ steps.vars.outputs.RUST_MIN_SRV }} update
- name: Info
shell: bash
run: |
# Info
## environment
echo "## environment"
echo "CI='${CI}'"
## tooling info display
echo "## tooling"
which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true
rustup -V 2>/dev/null
rustup show active-toolchain
cargo -V
rustc -V
cargo tree -V
## dependencies
echo "## dependency list"
cargo fetch --locked --quiet
## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors
RUSTUP_TOOLCHAIN=stable cargo tree --locked --no-dedupe -e=no-dev --prefix=none --features ${{ matrix.job.features }} | grep -vE "$PWD" | sort --unique
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
uses: EndBug/add-and-commit@v9
with:
new_branch: ${{ env.BRANCH_TARGET }}
default_author: github_actions
message: "maint ~ refresh 'Cargo.lock'"
add: Cargo.lock
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
code_format:
# Recheck/refresh code formatting
if: github.event.pull_request.merged == true ## only for PR merges
name: Update/format
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v4
- name: Initialize job variables
id: vars
shell: bash
run: |
# target-specific options
# * CARGO_FEATURES_OPTION
CARGO_FEATURES_OPTION='' ;
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
echo "CARGO_FEATURES_OPTION=${CARGO_FEATURES_OPTION}" >> $GITHUB_OUTPUT
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: rustfmt
- uses: Swatinem/rust-cache@v2
- name: "`cargo fmt`"
shell: bash
run: |
cargo fmt
- name: "`cargo fmt` tests"
shell: bash
run: |
# `cargo fmt` of tests
find tests -name "*.rs" -print0 | xargs -0 cargo fmt --
- name: Commit any changes (to '${{ env.BRANCH_TARGET }}')
uses: EndBug/add-and-commit@v9
with:
new_branch: ${{ env.BRANCH_TARGET }}
default_author: github_actions
message: "maint ~ rustfmt (`cargo fmt`)"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
name: GNU
on: [push, pull_request]
jobs:
gnu:
name: Run GNU tests
runs-on: ubuntu-latest
steps:
# Checks out a copy of your repository on the ubuntu-latest machine
- name: Checkout code uutil
uses: actions/checkout@v2
with:
path: 'uutils'
- name: Chechout GNU coreutils
uses: actions/checkout@v2
with:
repository: 'coreutils/coreutils'
path: 'gnu'
- name: Chechout GNU corelib
uses: actions/checkout@v2
with:
repository: 'coreutils/gnulib'
path: 'gnulib'
fetch-depth: 0 # gnu gets upset if gnulib is a shallow checkout
- name: Install `rust` toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
default: true
profile: minimal # minimal component installation (ie, no documentation)
components: rustfmt
- name: Build binaries
shell: bash
run: |
sudo apt-get update
sudo apt-get install autoconf autopoint bison texinfo gperf gcc g++ gdb python-pyinotify python3-sphinx
pushd uutils
make PROFILE=release
BUILDDIR="$PWD/target/release/"
cp "${BUILDDIR}/install" "${BUILDDIR}/ginstall" # The GNU tests rename this script before running, to avoid confusion with the make target
# Create *sum binaries
for sum in b2sum md5sum sha1sum sha224sum sha256sum sha384sum sha512sum
do
sum_path="${BUILDDIR}/${sum}"
test -f "${sum_path}" || cp "${BUILDDIR}/hashsum" "${sum_path}"
done
test -f "${BUILDDIR}/[" || cp "${BUILDDIR}/test" "${BUILDDIR}/["
popd
GNULIB_SRCDIR="$PWD/gnulib"
pushd gnu/
# Any binaries that aren't built become `false` so their tests fail
for binary in $(./build-aux/gen-lists-of-programs.sh --list-progs)
do
bin_path="${BUILDDIR}/${binary}"
test -f "${bin_path}" || { echo "'${binary}' was not built with uutils, using the 'false' program"; cp "${BUILDDIR}/false" "${bin_path}"; }
done
./bootstrap --gnulib-srcdir="$GNULIB_SRCDIR"
./configure --quiet --disable-gcc-warnings
#Add timeout to to protect against hangs
sed -i 's|"\$@|/usr/bin/timeout 600 "\$@|' build-aux/test-driver
# Change the PATH in the Makefile to test the uutils coreutils instead of the GNU coreutils
sed -i "s/^[[:blank:]]*PATH=.*/ PATH='${BUILDDIR//\//\\/}\$(PATH_SEPARATOR)'\"\$\$PATH\" \\\/" Makefile
sed -i 's| tr | /usr/bin/tr |' tests/init.sh
make
# Generate the factor tests, so they can be fixed
for i in {00..36}
do
make tests/factor/t${i}.sh
done
grep -rl 'path_prepend_' tests/* | xargs sed -i 's|path_prepend_ ./src||'
sed -i -e 's|^seq |/usr/bin/seq |' -e 's|sha1sum |/usr/bin/sha1sum |' tests/factor/t*sh
# Remove tests checking for --version & --help
# Not really interesting for us and logs are too big
sed -i -e '/tests\/misc\/invalid-opt.pl/ D' \
-e '/tests\/misc\/help-version.sh/ D' \
-e '/tests\/misc\/help-version-getopt.sh/ D' \
Makefile
# Use the system coreutils where the test fails due to error in a util that is not the one being tested
sed -i 's|stat|/usr/bin/stat|' tests/chgrp/basic.sh tests/cp/existing-perm-dir.sh tests/touch/60-seconds.sh tests/misc/sort-compress-proc.sh
sed -i 's|ls -|/usr/bin/ls -|' tests/chgrp/posix-H.sh tests/chown/deref.sh tests/cp/same-file.sh tests/misc/mknod.sh tests/mv/part-symlink.sh tests/du/8gb.sh
sed -i 's|mkdir |/usr/bin/mkdir |' tests/cp/existing-perm-dir.sh tests/rm/empty-inacc.sh
sed -i 's|timeout \([[:digit:]]\)| /usr/bin/timeout \1|' tests/tail-2/inotify-rotate.sh tests/tail-2/inotify-dir-recreate.sh tests/tail-2/inotify-rotate-resources.sh tests/cp/parent-perm-race.sh tests/ls/infloop.sh tests/misc/sort-exit-early.sh tests/misc/sort-NaN-infloop.sh tests/misc/uniq-perf.sh tests/tail-2/inotify-only-regular.sh tests/tail-2/pipe-f2.sh tests/tail-2/retry.sh tests/tail-2/symlink.sh tests/tail-2/wait.sh tests/tail-2/pid.sh tests/dd/stats.sh tests/tail-2/follow-name.sh tests/misc/shuf.sh # Don't break the function called 'grep_timeout'
sed -i 's|chmod |/usr/bin/chmod |' tests/du/inacc-dir.sh tests/mkdir/p-3.sh tests/tail-2/tail-n0f.sh tests/cp/fail-perm.sh tests/du/inaccessible-cwd.sh tests/mv/i-2.sh tests/chgrp/basic.sh tests/misc/shuf.sh
sed -i 's|sort |/usr/bin/sort |' tests/ls/hyperlink.sh tests/misc/test-N.sh
sed -i 's|split |/usr/bin/split |' tests/misc/factor-parallel.sh
sed -i 's|truncate |/usr/bin/truncate |' tests/split/fail.sh
sed -i 's|dd |/usr/bin/dd |' tests/du/8gb.sh tests/tail-2/big-4gb.sh tests/cp/fiemap-2.sh init.cfg
sed -i 's|id -|/usr/bin/id -|' tests/misc/runcon-no-reorder.sh
sed -i 's|touch |/usr/bin/touch |' tests/cp/preserve-link.sh tests/cp/reflink-perm.sh tests/ls/block-size.sh tests/ls/abmon-align.sh tests/ls/rt-1.sh tests/mv/update.sh tests/misc/ls-time.sh tests/misc/stat-nanoseconds.sh tests/misc/time-style.sh tests/misc/test-N.sh
sed -i 's|ln -|/usr/bin/ln -|' tests/cp/link-deref.sh
sed -i 's|printf |/usr/bin/printf |' tests/dd/ascii.sh
sed -i 's|cp |/usr/bin/cp |' tests/mv/hard-2.sh
sed -i 's|paste |/usr/bin/paste |' tests/misc/od-endian.sh
sed -i 's|seq |/usr/bin/seq |' tests/misc/sort-discrim.sh
#Add specific timeout to tests that currently hang to limit time spent waiting
sed -i 's|seq \$|/usr/bin/timeout 0.1 seq \$|' tests/misc/seq-precision.sh tests/misc/seq-long-double.sh
sed -i 's|cat |/usr/bin/timeout 0.1 cat |' tests/misc/cat-self.sh
test -f "${BUILDDIR}/getlimits" || cp src/getlimits "${BUILDDIR}"
- name: Run GNU tests
shell: bash
run: |
BUILDDIR="${PWD}/uutils/target/release"
GNULIB_DIR="${PWD}/gnulib"
pushd gnu
timeout -sKILL 2h make -j "$(nproc)" check SUBDIRS=. RUN_EXPENSIVE_TESTS=yes RUN_VERY_EXPENSIVE_TESTS=yes VERBOSE=no || : # Kill after 4 hours in case something gets stuck in make
- name: Extract tests info
shell: bash
run: |
if test -f gnu/tests/test-suite.log
then
TOTAL=$( grep "# TOTAL:" gnu/tests/test-suite.log|cut -d' ' -f2-)
PASS=$( grep "# PASS:" gnu/tests/test-suite.log|cut -d' ' -f2-)
SKIP=$( grep "# SKIP:" gnu/tests/test-suite.log|cut -d' ' -f2-)
FAIL=$( grep "# FAIL:" gnu/tests/test-suite.log|cut -d' ' -f2-)
XPASS=$( grep "# XPASS:" gnu/tests/test-suite.log|cut -d' ' -f2-)
ERROR=$( grep "# ERROR:" gnu/tests/test-suite.log|cut -d' ' -f2-)
echo "::warning ::GNU testsuite = $TOTAL / $PASS / $FAIL / $ERROR"
else
echo "::error ::Failed to get summary of test results"
fi
- uses: actions/upload-artifact@v2
with:
name: test-report
path: gnu/tests/**/*.log
name: GnuComment
on:
workflow_run:
workflows: ["GnuTests"]
types:
- completed
permissions: {}
jobs:
post-comment:
permissions:
actions: read # to list workflow runs artifacts
pull-requests: write # to comment on pr
runs-on: ubuntu-latest
if: >
github.event.workflow_run.event == 'pull_request'
steps:
- name: 'Download artifact'
uses: actions/github-script@v7
with:
script: |
// List all artifacts from GnuTests
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: ${{ github.event.workflow_run.id }},
});
// Download the "comment" artifact, which contains a PR number (NR) and result.txt
var matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "comment"
})[0];
var download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
var fs = require('fs');
fs.writeFileSync('${{ github.workspace }}/comment.zip', Buffer.from(download.data));
- run: unzip comment.zip
- name: 'Comment on PR'
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
var fs = require('fs');
var issue_number = Number(fs.readFileSync('./NR'));
var content = fs.readFileSync('./result.txt');
if (content.toString().trim().length > 7) { // 7 because we have backquote + \n
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue_number,
body: 'GNU testsuite comparison:\n```\n' + content + '```'
});
}
name: GnuTests
# spell-checker:ignore (abbrev/names) CodeCov gnulib GnuTests Swatinem
# spell-checker:ignore (jargon) submodules
# spell-checker:ignore (libs/utils) autopoint chksum gperf lcov libexpect pyinotify 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:
- main
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' }}
jobs:
gnu:
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: Run GNU tests
runs-on: ubuntu-22.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; }
# * config
path_GNU="gnu"
path_GNU_tests="${path_GNU}/tests"
path_UUTILS="uutils"
path_reference="reference"
outputs path_GNU path_GNU_tests path_reference path_UUTILS
#
repo_default_branch="${{ github.event.repository.default_branch }}"
repo_GNU_ref="v9.5"
repo_reference_branch="${{ github.event.repository.default_branch }}"
outputs repo_default_branch repo_GNU_ref repo_reference_branch
#
SUITE_LOG_FILE="${path_GNU_tests}/test-suite.log"
ROOT_SUITE_LOG_FILE="${path_GNU_tests}/test-suite-root.log"
TEST_LOGS_GLOB="${path_GNU_tests}/**/*.log" ## note: not usable at bash CLI; [why] double globstar not enabled by default b/c MacOS includes only bash v3 which doesn't have double globstar support
TEST_FILESET_PREFIX='test-fileset-IDs.sha1#'
TEST_FILESET_SUFFIX='.txt'
TEST_SUMMARY_FILE='gnu-result.json'
TEST_FULL_SUMMARY_FILE='gnu-full-result.json'
outputs SUITE_LOG_FILE ROOT_SUITE_LOG_FILE TEST_FILESET_PREFIX TEST_FILESET_SUFFIX TEST_LOGS_GLOB TEST_SUMMARY_FILE TEST_FULL_SUMMARY_FILE
- name: Checkout code (uutil)
uses: actions/checkout@v4
with:
path: '${{ steps.vars.outputs.path_UUTILS }}'
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: rustfmt
- uses: Swatinem/rust-cache@v2
with:
workspaces: "./${{ steps.vars.outputs.path_UUTILS }} -> target"
- name: Checkout code (GNU coreutils)
uses: actions/checkout@v4
with:
repository: 'coreutils/coreutils'
path: '${{ steps.vars.outputs.path_GNU }}'
ref: ${{ steps.vars.outputs.repo_GNU_ref }}
submodules: recursive
- name: Retrieve reference artifacts
uses: dawidd6/action-download-artifact@v6
# 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: "${{ steps.vars.outputs.repo_reference_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: "${{ steps.vars.outputs.path_reference }}"
- 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
- 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 sv_SE
sudo locale-gen --keep-existing sv_SE.UTF-8
sudo locale-gen --keep-existing en_US
sudo locale-gen --keep-existing ru_RU.KOI8-R
sudo update-locale
echo "After:"
locale -a
- name: Build binaries
shell: bash
run: |
## Build binaries
cd '${{ steps.vars.outputs.path_UUTILS }}'
bash util/build-gnu.sh --release-build
- name: Run GNU tests
shell: bash
run: |
## Run GNU tests
path_GNU='${{ steps.vars.outputs.path_GNU }}'
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
bash "${path_UUTILS}/util/run-gnu-test.sh"
- name: Run GNU root tests
shell: bash
run: |
## Run GNU root tests
path_GNU='${{ steps.vars.outputs.path_GNU }}'
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
bash "${path_UUTILS}/util/run-gnu-test.sh" run-root
- name: Extract testing info into JSON
shell: bash
run : |
## Extract testing info into JSON
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
python ${path_UUTILS}/util/gnu-json-result.py ${{ steps.vars.outputs.path_GNU_tests }} > ${{ steps.vars.outputs.TEST_FULL_SUMMARY_FILE }}
- 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='${{ steps.vars.outputs.path_UUTILS }}'
#
SUITE_LOG_FILE='${{ steps.vars.outputs.SUITE_LOG_FILE }}'
ROOT_SUITE_LOG_FILE='${{ steps.vars.outputs.ROOT_SUITE_LOG_FILE }}'
ls -al ${SUITE_LOG_FILE} ${ROOT_SUITE_LOG_FILE}
if test -f "${SUITE_LOG_FILE}"
then
source ${path_UUTILS}/util/analyze-gnu-results.sh ${SUITE_LOG_FILE} ${ROOT_SUITE_LOG_FILE}
if [[ "$TOTAL" -eq 0 || "$TOTAL" -eq 1 ]]; then
echo "::error ::Failed to parse test results from '${SUITE_LOG_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
else
echo "::error ::Failed to find summary of test results (missing '${SUITE_LOG_FILE}'); failing early"
exit 1
fi
# Compress logs before upload (fails otherwise)
gzip ${{ steps.vars.outputs.TEST_LOGS_GLOB }}
- name: Reserve SHA1/ID of 'test-summary'
uses: actions/upload-artifact@v4
with:
name: "${{ steps.summary.outputs.HASH }}"
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
- name: Reserve test results summary
uses: actions/upload-artifact@v4
with:
name: test-summary
path: "${{ steps.vars.outputs.TEST_SUMMARY_FILE }}"
- name: Reserve test logs
uses: actions/upload-artifact@v4
with:
name: test-logs
path: "${{ steps.vars.outputs.TEST_LOGS_GLOB }}"
- name: Upload full json results
uses: actions/upload-artifact@v4
with:
name: gnu-full-result.json
path: ${{ steps.vars.outputs.TEST_FULL_SUMMARY_FILE }}
- name: Compare test failures VS reference
shell: bash
run: |
## Compare test failures VS reference
have_new_failures=""
REF_LOG_FILE='${{ steps.vars.outputs.path_reference }}/test-logs/test-suite.log'
ROOT_REF_LOG_FILE='${{ steps.vars.outputs.path_reference }}/test-logs/test-suite-root.log'
REF_SUMMARY_FILE='${{ steps.vars.outputs.path_reference }}/test-summary/gnu-result.json'
REPO_DEFAULT_BRANCH='${{ steps.vars.outputs.repo_default_branch }}'
path_UUTILS='${{ steps.vars.outputs.path_UUTILS }}'
# https://github.com/uutils/coreutils/issues/4294
# https://github.com/uutils/coreutils/issues/4295
IGNORE_INTERMITTENT="${path_UUTILS}/.github/workflows/ignore-intermittent.txt"
mkdir -p ${{ steps.vars.outputs.path_reference }}
COMMENT_DIR="${{ steps.vars.outputs.path_reference }}/comment"
mkdir -p ${COMMENT_DIR}
echo ${{ github.event.number }} > ${COMMENT_DIR}/NR
COMMENT_LOG="${COMMENT_DIR}/result.txt"
# The comment log might be downloaded from a previous run
# We only want the new changes, so remove it if it exists.
rm -f ${COMMENT_LOG}
touch ${COMMENT_LOG}
compare_tests() {
local new_log_file=$1
local ref_log_file=$2
local test_type=$3 # "standard" or "root"
if test -f "${ref_log_file}"; then
echo "Reference ${test_type} test log SHA1/ID: $(sha1sum -- "${ref_log_file}") - ${test_type}"
REF_ERROR=$(sed -n "s/^ERROR: \([[:print:]]\+\).*/\1/p" "${ref_log_file}"| sort)
CURRENT_RUN_ERROR=$(sed -n "s/^ERROR: \([[:print:]]\+\).*/\1/p" "${new_log_file}" | sort)
REF_FAILING=$(sed -n "s/^FAIL: \([[:print:]]\+\).*/\1/p" "${ref_log_file}"| sort)
CURRENT_RUN_FAILING=$(sed -n "s/^FAIL: \([[:print:]]\+\).*/\1/p" "${new_log_file}" | sort)
echo "Detailled information:"
echo "REF_ERROR = ${REF_ERROR}"
echo "CURRENT_RUN_ERROR = ${CURRENT_RUN_ERROR}"
echo "REF_FAILING = ${REF_FAILING}"
echo "CURRENT_RUN_FAILING = ${CURRENT_RUN_FAILING}"
# Compare failing and error tests
for LINE in ${CURRENT_RUN_FAILING}
do
if ! grep -Fxq ${LINE}<<<"${REF_FAILING}"
then
if ! grep ${LINE} ${IGNORE_INTERMITTENT}
then
MSG="GNU test failed: ${LINE}. ${LINE} is passing on '${REPO_DEFAULT_BRANCH}'. Maybe you have to rebase?"
echo "::error ::$MSG"
echo $MSG >> ${COMMENT_LOG}
have_new_failures="true"
else
MSG="Skip an intermittent issue ${LINE} (fails in this run but passes in the 'main' branch)"
echo "::warning ::$MSG"
echo $MSG >> ${COMMENT_LOG}
echo ""
fi
fi
done
for LINE in ${REF_FAILING}
do
if ! grep -Fxq ${LINE}<<<"${CURRENT_RUN_FAILING}"
then
if ! grep ${LINE} ${IGNORE_INTERMITTENT}
then
MSG="Congrats! The gnu test ${LINE} is no longer failing!"
echo "::warning ::$MSG"
echo $MSG >> ${COMMENT_LOG}
else
MSG="Skipping an intermittent issue ${LINE} (passes in this run but fails in the 'main' branch)"
echo "::warning ::$MSG"
echo $MSG >> ${COMMENT_LOG}
echo ""
fi
fi
done
for LINE in ${CURRENT_RUN_ERROR}
do
if ! grep -Fxq ${LINE}<<<"${REF_ERROR}"
then
MSG="GNU test error: ${LINE}. ${LINE} is passing on '${REPO_DEFAULT_BRANCH}'. Maybe you have to rebase?"
echo "::error ::$MSG"
echo $MSG >> ${COMMENT_LOG}
have_new_failures="true"
fi
done
for LINE in ${REF_ERROR}
do
if ! grep -Fxq ${LINE}<<<"${CURRENT_RUN_ERROR}"
then
MSG="Congrats! The gnu test ${LINE} is no longer ERROR!"
echo "::warning ::$MSG"
echo $MSG >> ${COMMENT_LOG}
fi
done
else
echo "::warning ::Skipping ${test_type} test failure comparison; no prior reference test logs are available."
fi
}
# Compare standard tests
compare_tests '${{ steps.vars.outputs.path_GNU_tests }}/test-suite.log' "${REF_LOG_FILE}" "standard"
# Compare root tests
compare_tests '${{ steps.vars.outputs.path_GNU_tests }}/test-suite-root.log' "${ROOT_REF_LOG_FILE}" "root"
if test -n "${have_new_failures}" ; then exit -1 ; 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: ${{ steps.vars.outputs.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='${{ steps.vars.outputs.path_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
gnu_coverage:
name: Run GNU tests with coverage
runs-on: ubuntu-22.04
steps:
- name: Checkout code uutil
uses: actions/checkout@v4
with:
path: 'uutils'
- name: Checkout GNU coreutils
uses: actions/checkout@v4
with:
repository: 'coreutils/coreutils'
path: 'gnu'
ref: 'v9.5'
submodules: recursive
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
components: rustfmt
- uses: taiki-e/install-action@grcov
- uses: Swatinem/rust-cache@v2
with:
workspaces: "./uutils -> target"
- name: Install dependencies
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
- name: Add various locales
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 update-locale
echo "After:"
locale -a
- name: Build binaries
env:
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
RUSTDOCFLAGS: "-Cpanic=abort"
run: |
## Build binaries
cd uutils
bash util/build-gnu.sh
- name: Run GNU tests
run: bash uutils/util/run-gnu-test.sh
- name: Generate coverage data (via `grcov`)
id: coverage
run: |
## Generate coverage data
cd uutils
COVERAGE_REPORT_DIR="target/debug"
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
mkdir -p "${COVERAGE_REPORT_DIR}"
sudo chown -R "$(whoami)" "${COVERAGE_REPORT_DIR}"
# display coverage files
grcov . --output-type files --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique
# generate coverage report
grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"
echo "report=${COVERAGE_REPORT_FILE}" >> $GITHUB_OUTPUT
- name: Upload coverage results (to Codecov.io)
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ${{ steps.coverage.outputs.report }}
flags: gnutests
name: gnutests
working-directory: uutils
name: Android
# spell-checker:ignore TERMUX reactivecircus Swatinem noaudio pkill swiftshader dtolnay juliangruber
on:
pull_request:
push:
branches:
- main
permissions:
contents: read # to fetch code (actions/checkout)
# 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:
TERMUX: v0.118.0
KEY_POSTFIX: nextest+rustc-hash+adb+sshd+upgrade+XGB+inc18
COMMON_EMULATOR_OPTIONS: -no-window -noaudio -no-boot-anim -camera-back none -gpu swiftshader_indirect
EMULATOR_DISK_SIZE: 12GB
EMULATOR_HEAP_SIZE: 2048M
EMULATOR_BOOT_TIMEOUT: 1200 # 20min
jobs:
test_android:
name: Test builds
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest] # , macos-latest
cores: [4] # , 6
ram: [4096, 8192]
api-level: [28]
target: [google_apis_playstore]
arch: [x86, x86_64] # , arm64-v8a
exclude:
- ram: 8192
arch: x86
- ram: 4096
arch: x86_64
runs-on: ${{ matrix.os }}
env:
EMULATOR_RAM_SIZE: ${{ matrix.ram }}
EMULATOR_CORES: ${{ matrix.cores }}
RUNNER_OS: ${{ matrix.os }}
AVD_CACHE_KEY: "set later due to limitations of github actions not able to concatenate env variables"
steps:
- name: Concatenate values to environment file
run: |
echo "AVD_CACHE_KEY=${{ matrix.os }}-${{ matrix.cores }}-${{ matrix.ram }}-${{ matrix.api-level }}-${{ matrix.target }}-${{ matrix.arch }}+termux-${{ env.TERMUX }}+${{ env.KEY_POSTFIX }}" >> $GITHUB_ENV
- name: Collect information about runner
if: always()
continue-on-error: true
run: |
hostname
uname -a
free -mh
df -Th
cat /proc/cpuinfo
- name: (Linux) create links from home to data partition
if: ${{ runner.os == 'Linux' }}
continue-on-error: true
run: |
ls -lah /mnt/
cat /mnt/DATALOSS_WARNING_README.txt
sudo mkdir /mnt/data
sudo chmod a+rwx /mnt/data
mkdir /mnt/data/.android && ln -s /mnt/data/.android ~/.android
mkdir /mnt/data/work && ln -s /mnt/data/work ~/work
- name: Enable KVM group perms (linux hardware acceleration)
if: ${{ runner.os == 'Linux' }}
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm
- uses: actions/checkout@v4
- name: Collect information about runner
if: always()
continue-on-error: true
run: |
free -mh
df -Th
- name: Restore AVD cache
uses: actions/cache/restore@v4
id: avd-cache
continue-on-error: true
with:
path: |
~/.android/avd/*
~/.android/avd/*/snapshots/*
~/.android/adb*
~/__rustc_hash__
key: avd-${{ env.AVD_CACHE_KEY }}
- name: Collect information about runner after AVD cache
if: always()
continue-on-error: true
run: |
free -mh
df -Th
ls -lah /mnt/data
du -sch /mnt/data
- name: Delete AVD Lockfile when run from cache
if: steps.avd-cache.outputs.cache-hit == 'true'
run: |
rm -f \
~/.android/avd/*.avd/*.lock \
~/.android/avd/*/*.lock
- name: Create and cache emulator image
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: reactivecircus/android-emulator-runner@v2.31.0
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: ${{ matrix.arch }}
ram-size: ${{ env.EMULATOR_RAM_SIZE }}
heap-size: ${{ env.EMULATOR_HEAP_SIZE }}
disk-size: ${{ env.EMULATOR_DISK_SIZE }}
cores: ${{ env.EMULATOR_CORES }}
force-avd-creation: true
emulator-options: ${{ env.COMMON_EMULATOR_OPTIONS }} -no-snapshot-load
emulator-boot-timeout: ${{ env.EMULATOR_BOOT_TIMEOUT }}
script: |
util/android-commands.sh init "${{ matrix.arch }}" "${{ matrix.api-level }}" "${{ env.TERMUX }}"
- name: Save AVD cache
if: steps.avd-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
~/.android/avd/*
~/.android/avd/*/snapshots/*
~/.android/adb*
~/__rustc_hash__
key: avd-${{ env.AVD_CACHE_KEY }}
- uses: juliangruber/read-file-action@v1
id: read_rustc_hash
with:
# ~ expansion didn't work
path: ${{ runner.os == 'Linux' && '/home/runner/__rustc_hash__' || '/Users/runner/__rustc_hash__' }}
trim: true
- name: Restore rust cache
id: rust-cache
uses: actions/cache/restore@v4
with:
path: ~/__rust_cache__
# The version vX at the end of the key is just a development version to avoid conflicts in
# the github cache during the development of this workflow
key: ${{ matrix.arch }}_${{ matrix.target}}_${{ steps.read_rustc_hash.outputs.content }}_${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }}_v3
- name: Collect information about runner ressources
if: always()
continue-on-error: true
run: |
free -mh
df -Th
- name: Build and Test
uses: reactivecircus/android-emulator-runner@v2.31.0
with:
api-level: ${{ matrix.api-level }}
target: ${{ matrix.target }}
arch: ${{ matrix.arch }}
ram-size: ${{ env.EMULATOR_RAM_SIZE }}
heap-size: ${{ env.EMULATOR_HEAP_SIZE }}
disk-size: ${{ env.EMULATOR_DISK_SIZE }}
cores: ${{ env.EMULATOR_CORES }}
force-avd-creation: false
emulator-options: ${{ env.COMMON_EMULATOR_OPTIONS }} -no-snapshot-save -snapshot ${{ env.AVD_CACHE_KEY }}
emulator-boot-timeout: ${{ env.EMULATOR_BOOT_TIMEOUT }}
# This is not a usual script. Every line is executed in a separate shell with `sh -c`. If
# one of the lines returns with error the whole script is failed (like running a script with
# set -e) and in consequences the other lines (shells) are not executed.
script: |
util/android-commands.sh sync_host
util/android-commands.sh build
util/android-commands.sh tests
if [[ "${{ steps.rust-cache.outputs.cache-hit }}" != 'true' ]]; then util/android-commands.sh sync_image; fi; exit 0
- name: Collect information about runner ressources
if: always()
continue-on-error: true
run: |
free -mh
df -Th
- name: Save rust cache
if: steps.rust-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: ~/__rust_cache__
key: ${{ matrix.arch }}_${{ matrix.target}}_${{ steps.read_rustc_hash.outputs.content }}_${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }}_v3
- name: archive any output (error screenshots)
if: always()
uses: actions/upload-artifact@v4
with:
name: test_output_${{ env.AVD_CACHE_KEY }}
path: output
- name: Collect information about runner ressources
if: always()
continue-on-error: true
run: |
free -mh
df -Th
name: Code Quality
# spell-checker:ignore TERMUX reactivecircus Swatinem noaudio pkill swiftshader dtolnay juliangruber
on:
pull_request:
push:
branches:
- main
env:
# * style job configuration
STYLE_FAIL_ON_FAULT: true ## (bool) fail the build if a style job contains a fault (error or warning); may be overridden on a per-job basis
permissions:
contents: read # to fetch code (actions/checkout)
# 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' }}
jobs:
style_format:
name: Style/format
runs-on: ${{ matrix.job.os }}
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: rustfmt
- uses: Swatinem/rust-cache@v2
- 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; }
# failure mode
unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in
''|0|f|false|n|no|off) FAULT_TYPE=warning ;;
*) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;;
esac;
outputs FAIL_ON_FAULT FAULT_TYPE
- name: "`cargo fmt` testing"
shell: bash
run: |
## `cargo fmt` testing
unset fault
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
# * convert any errors/warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
S=$(cargo fmt -- --check) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s\n" "$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+${PWD//\//\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*$/::${fault_type} file=\1,line=\2::${fault_prefix}: \`cargo fmt\`: style violation (file:'\1', line:\2; use \`cargo fmt -- \"\1\"\`)/p" ; fault=true ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
style_lint:
name: Style/lint
runs-on: ${{ matrix.job.os }}
env:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
- { os: macos-latest , features: feat_os_macos }
- { os: windows-latest , features: feat_os_windows }
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
components: clippy
- uses: Swatinem/rust-cache@v2
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.5
- 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; }
# failure mode
unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in
''|0|f|false|n|no|off) FAULT_TYPE=warning ;;
*) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;;
esac;
outputs FAIL_ON_FAULT FAULT_TYPE
- name: "`cargo clippy` lint testing"
uses: nick-fields/retry@v3
with:
max_attempts: 3
retry_on: error
timeout_minutes: 90
shell: bash
command: |
## `cargo clippy` lint testing
unset fault
CLIPPY_FLAGS="-W clippy::default_trait_access -W clippy::manual_string_new -W clippy::cognitive_complexity -W clippy::implicit_clone -W clippy::range-plus-one -W clippy::redundant-clone -W clippy::match_bool"
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
S=$(cargo clippy --all-targets --features ${{ matrix.job.features }} -pcoreutils -- ${CLIPPY_FLAGS} -D warnings 2>&1) && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*$/::${fault_type} file=\2,line=\3,col=\4::${fault_prefix}: \`cargo clippy\`: \1 (file:'\2', line:\3)/p;" -e '}' ; fault=true ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
style_spellcheck:
name: Style/spelling
runs-on: ${{ matrix.job.os }}
strategy:
matrix:
job:
- { os: ubuntu-latest , features: feat_os_unix }
steps:
- uses: actions/checkout@v4
- 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; }
# failure mode
unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in
''|0|f|false|n|no|off) FAULT_TYPE=warning ;;
*) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;;
esac;
outputs FAIL_ON_FAULT FAULT_TYPE
- name: Install/setup prerequisites
shell: bash
run: |
sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell -g ;
- name: Run `cspell`
shell: bash
run: |
## Run `cspell`
unset fault
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
# * find cspell configuration ; note: avoid quotes around ${cfg_file} b/c `cspell` (v4) doesn't correctly dequote the config argument (or perhaps a subshell expansion issue?)
cfg_files=($(shopt -s nullglob ; echo {.vscode,.}/{,.}c[sS]pell{.json,.config{.js,.cjs,.json,.yaml,.yml},.yaml,.yml} ;))
cfg_file=${cfg_files[0]}
unset CSPELL_CFG_OPTION ; if [ -n "$cfg_file" ]; then CSPELL_CFG_OPTION="--config $cfg_file" ; fi
S=$(cspell ${CSPELL_CFG_OPTION} --no-summary --no-progress "**/*") && printf "%s\n" "$S" || { printf "%s\n" "$S" ; printf "%s" "$S" | sed -E -n "s/${PWD//\//\\/}\/(.*):(.*):(.*) - (.*)/::${fault_type} file=\1,line=\2,col=\3::${fault_type^^}: \4 (file:'\1', line:\2)/p" ; fault=true ; true ; }
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
toml_format:
name: Style/toml
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Check
run: npx --yes @taplo/cli fmt --check
name: FreeBSD
# spell-checker:ignore sshfs usesh vmactions taiki Swatinem esac fdescfs fdesc
env:
# * style job configuration
STYLE_FAIL_ON_FAULT: true ## (bool) fail the build if a style job contains a fault (error or warning); may be overridden on a per-job basis
on:
pull_request:
push:
branches:
- main
permissions:
contents: read # to fetch code (actions/checkout)
# 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' }}
jobs:
style:
name: Style and Lint
runs-on: ${{ matrix.job.os }}
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-22.04 , features: unix }
env:
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.5
- name: Prepare, build and test
uses: vmactions/freebsd-vm@v1.0.7
with:
usesh: true
sync: rsync
copyback: false
# We need jq and GNU coreutils to run show-utils.sh and bash to use inline shell string replacement
prepare: pkg install -y curl sudo jq coreutils bash
run: |
## Prepare, build, and test
# implementation modelled after ref: <https://github.com/rust-lang/rustup/pull/2783>
# * NOTE: All steps need to be run in this block, otherwise, we are operating back on the mac host
set -e
#
TEST_USER=tester
REPO_NAME=${GITHUB_WORKSPACE##*/}
WORKSPACE_PARENT="/home/runner/work/${REPO_NAME}"
WORKSPACE="${WORKSPACE_PARENT}/${REPO_NAME}"
#
pw adduser -n ${TEST_USER} -d /root/ -g wheel -c "Coreutils user to build" -w random
chown -R ${TEST_USER}:wheel /root/ "${WORKSPACE_PARENT}"/
whoami
#
# Further work needs to be done in a sudo as we are changing users
sudo -i -u ${TEST_USER} bash << EOF
set -e
whoami
curl https://sh.rustup.rs -sSf --output rustup.sh
sh rustup.sh -y -c rustfmt,clippy --profile=minimal -t stable
. ${HOME}/.cargo/env
## VARs setup
cd "${WORKSPACE}"
unset FAIL_ON_FAULT ; case '${{ env.STYLE_FAIL_ON_FAULT }}' in
''|0|f|false|n|no|off) FAULT_TYPE=warning ;;
*) FAIL_ON_FAULT=true ; FAULT_TYPE=error ;;
esac;
FAULT_PREFIX=\$(echo "\${FAULT_TYPE}" | tr '[:lower:]' '[:upper:]')
# * determine sub-crate utility list
UTILITY_LIST="\$(./util/show-utils.sh --features ${{ matrix.job.features }})"
CARGO_UTILITY_LIST_OPTIONS="\$(for u in \${UTILITY_LIST}; do echo -n "-puu_\${u} "; done;)"
## Info
# environment
echo "## environment"
echo "CI='${CI}'"
echo "REPO_NAME='${REPO_NAME}'"
echo "TEST_USER='${TEST_USER}'"
echo "WORKSPACE_PARENT='${WORKSPACE_PARENT}'"
echo "WORKSPACE='${WORKSPACE}'"
echo "FAULT_PREFIX='\${FAULT_PREFIX}'"
echo "UTILITY_LIST='\${UTILITY_LIST}'"
env | sort
# tooling info
echo "## tooling info"
cargo -V
rustc -V
#
# To ensure that files are cleaned up, we don't want to exit on error
set +e
unset FAULT
## cargo fmt testing
echo "## cargo fmt testing"
# * convert any errors/warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
S=\$(cargo fmt -- --check) && printf "%s\n" "\$S" || { printf "%s\n" "\$S" ; printf "%s\n" "\$S" | sed -E -n -e "s/^Diff[[:space:]]+in[[:space:]]+\${PWD//\//\\\\/}\/(.*)[[:space:]]+at[[:space:]]+[^0-9]+([0-9]+).*\$/::\${FAULT_TYPE} file=\1,line=\2::\${FAULT_PREFIX}: \\\`cargo fmt\\\`: style violation (file:'\1', line:\2; use \\\`cargo fmt -- \"\1\"\\\`)/p" ; FAULT=true ; }
## cargo clippy lint testing
if [ -z "\${FAULT}" ]; then
echo "## cargo clippy lint testing"
# * convert any warnings to GHA UI annotations; ref: <https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-a-warning-message>
S=\$(cargo clippy --all-targets \${CARGO_UTILITY_LIST_OPTIONS} -- -W clippy::manual_string_new -D warnings 2>&1) && printf "%s\n" "\$S" || { printf "%s\n" "\$S" ; printf "%s" "\$S" | sed -E -n -e '/^error:/{' -e "N; s/^error:[[:space:]]+(.*)\\n[[:space:]]+-->[[:space:]]+(.*):([0-9]+):([0-9]+).*\$/::\${FAULT_TYPE} file=\2,line=\3,col=\4::\${FAULT_PREFIX}: \\\`cargo clippy\\\`: \1 (file:'\2', line:\3)/p;" -e '}' ; FAULT=true ; }
fi
# Clean to avoid to rsync back the files
cargo clean
if [ -n "\${FAIL_ON_FAULT}" ] && [ -n "\${FAULT}" ]; then exit 1 ; fi
EOF
test:
name: Tests
runs-on: ${{ matrix.job.os }}
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
job:
- { os: ubuntu-22.04 , features: unix }
env:
mem: 4096
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
steps:
- uses: actions/checkout@v4
- uses: Swatinem/rust-cache@v2
- name: Run sccache-cache
uses: mozilla-actions/sccache-action@v0.0.5
- name: Prepare, build and test
uses: vmactions/freebsd-vm@v1.0.7
with:
usesh: true
sync: rsync
copyback: false
prepare: pkg install -y curl gmake sudo
run: |
## Prepare, build, and test
# implementation modelled after ref: <https://github.com/rust-lang/rustup/pull/2783>
# * NOTE: All steps need to be run in this block, otherwise, we are operating back on the mac host
set -e
#
# We need a file-descriptor file system to test test_ls::test_ls_io_errors
mount -t fdescfs fdesc /dev/fd
#
TEST_USER=tester
REPO_NAME=${GITHUB_WORKSPACE##*/}
WORKSPACE_PARENT="/home/runner/work/${REPO_NAME}"
WORKSPACE="${WORKSPACE_PARENT}/${REPO_NAME}"
#
pw adduser -n ${TEST_USER} -d /root/ -g wheel -c "Coreutils user to build" -w random
# chown -R ${TEST_USER}:wheel /root/ "${WORKSPACE_PARENT}"/
chown -R ${TEST_USER}:wheel /root/ "${WORKSPACE_PARENT}"/
whoami
#
# Further work needs to be done in a sudo as we are changing users
sudo -i -u ${TEST_USER} sh << EOF
set -e
whoami
curl https://sh.rustup.rs -sSf --output rustup.sh
sh rustup.sh -y --profile=minimal
. $HOME/.cargo/env
# Install nextest
mkdir -p ~/.cargo/bin
curl -LsSf https://get.nexte.st/latest/freebsd | tar zxf - -C ~/.cargo/bin
## Info
# environment
echo "## environment"
echo "CI='${CI}'"
echo "REPO_NAME='${REPO_NAME}'"
echo "TEST_USER='${TEST_USER}'"
echo "WORKSPACE_PARENT='${WORKSPACE_PARENT}'"
echo "WORKSPACE='${WORKSPACE}'"
env | sort
# tooling info
echo "## tooling info"
cargo -V
cargo nextest --version
rustc -V
#
# To ensure that files are cleaned up, we don't want to exit on error
set +e
cd "${WORKSPACE}"
unset FAULT
cargo build || FAULT=1
export PATH=~/.cargo/bin:${PATH}
export RUST_BACKTRACE=1
export CARGO_TERM_COLOR=always
if (test -z "\$FAULT"); then cargo nextest run --hide-progress-bar --profile ci --features '${{ matrix.job.features }}' || FAULT=1 ; fi
if (test -z "\$FAULT"); then cargo nextest run --hide-progress-bar --profile ci --all-features -p uucore || FAULT=1 ; fi
# Clean to avoid to rsync back the files
cargo clean
if (test -n "\$FAULT"); then exit 1 ; fi
EOF
name: Fuzzing
# spell-checker:ignore fuzzer
on:
pull_request:
push:
branches:
- main
permissions:
contents: read # to fetch code (actions/checkout)
# 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' }}
jobs:
fuzz-build:
name: Build the fuzzers
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- name: Install `cargo-fuzz`
run: cargo install cargo-fuzz
- uses: Swatinem/rust-cache@v2
with:
shared-key: "cargo-fuzz-cache-key"
cache-directories: "fuzz/target"
- name: Run `cargo-fuzz build`
run: cargo +nightly fuzz build
fuzz-run:
needs: fuzz-build
name: Run the fuzzers
runs-on: ubuntu-latest
timeout-minutes: 5
env:
RUN_FOR: 60
strategy:
matrix:
test-target:
- { name: fuzz_test, should_pass: true }
# https://github.com/uutils/coreutils/issues/5311
- { name: fuzz_date, should_pass: false }
- { name: fuzz_expr, should_pass: true }
- { name: fuzz_printf, should_pass: false }
- { name: fuzz_echo, should_pass: true }
- { name: fuzz_seq, should_pass: false }
- { name: fuzz_sort, should_pass: false }
- { name: fuzz_wc, should_pass: false }
- { name: fuzz_cut, should_pass: false }
- { name: fuzz_split, should_pass: false }
- { name: fuzz_tr, should_pass: false }
- { name: fuzz_env, should_pass: false }
- { name: fuzz_parse_glob, should_pass: true }
- { name: fuzz_parse_size, should_pass: true }
- { name: fuzz_parse_time, should_pass: true }
- { name: fuzz_seq_parse_number, should_pass: true }
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@nightly
- name: Install `cargo-fuzz`
run: cargo install cargo-fuzz
- uses: Swatinem/rust-cache@v2
with:
shared-key: "cargo-fuzz-cache-key"
cache-directories: "fuzz/target"
- name: Restore Cached Corpus
uses: actions/cache/restore@v4
with:
key: corpus-cache-${{ matrix.test-target.name }}
path: |
fuzz/corpus/${{ matrix.test-target.name }}
- name: Run ${{ matrix.test-target.name }} for XX seconds
shell: bash
continue-on-error: ${{ !matrix.test-target.name.should_pass }}
run: |
cargo +nightly fuzz run ${{ matrix.test-target.name }} -- -max_total_time=${{ env.RUN_FOR }} -detect_leaks=0
- name: Save Corpus Cache
uses: actions/cache/save@v4
with:
key: corpus-cache-${{ matrix.test-target.name }}
path: |
fuzz/corpus/${{ matrix.test-target.name }}