mirror of
https://github.com/uutils/coreutils
synced 2024-12-14 23:32:39 +00:00
adf4bab03c
Generating the tests to run in build.rs created problems for tooling. For example, cargo fmt, was ignoring the test_*.rs files and needed to be passed these files manually to be formatted. Now we simply use the feature mechanism to decide which tests to run.
1105 lines
51 KiB
YAML
1105 lines
51 KiB
YAML
name: CICD
|
|
|
|
# spell-checker:ignore (acronyms) CICD MSVC musl
|
|
# spell-checker:ignore (env/flags) Awarnings Ccodegen Coverflow Cpanic Dwarnings RUSTDOCFLAGS RUSTFLAGS Zpanic
|
|
# spell-checker:ignore (jargon) SHAs deps dequote softprops subshell toolchain
|
|
# spell-checker:ignore (names) CodeCOV MacOS MinGW Peltoche rivy
|
|
# spell-checker:ignore (shell/tools) choco clippy dmake dpkg esac fakeroot gmake grcov halium lcov libssl mkdir popd printf pushd rsync rustc rustfmt rustup shopt xargs
|
|
# spell-checker:ignore (misc) aarch alnum armhf bindir busytest coreutils gnueabihf issuecomment maint nullglob onexitbegin onexitend pell runtest tempfile testsuite uutils DESTDIR multisize Swatinem
|
|
|
|
env:
|
|
PROJECT_NAME: coreutils
|
|
PROJECT_DESC: "Core universal (cross-platform) utilities"
|
|
PROJECT_AUTH: "uutils"
|
|
RUST_MIN_SRV: "1.59.0" ## MSRV v1.59.0
|
|
# * 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: [push, pull_request]
|
|
|
|
permissions:
|
|
contents: read # to fetch code (actions/checkout)
|
|
|
|
jobs:
|
|
cargo-deny:
|
|
name: Style/cargo-deny
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: EmbarkStudios/cargo-deny-action@v1
|
|
|
|
style_deps:
|
|
## ToDO: [2021-11-10; rivy] 'Style/deps' needs more informative output and better integration of results into the GHA dashboard
|
|
name: Style/deps
|
|
runs-on: ${{ matrix.job.os }}
|
|
# env:
|
|
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
# note: `cargo-udeps` panics when processing stdbuf/libstdbuf ("uu_stdbuf_libstdbuf"); either b/c of the 'cpp' crate or 'libstdbuf' itself
|
|
# ... b/c of the panic, a more limited feature set is tested (though only excluding `stdbuf`)
|
|
- { os: ubuntu-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" }
|
|
- { os: macos-latest , features: "feat_Tier1,feat_require_unix,feat_require_unix_utmpx" }
|
|
- { os: windows-latest , features: feat_os_windows }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Initialize workflow variables
|
|
id: vars
|
|
shell: bash
|
|
run: |
|
|
## VARs setup
|
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; 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
|
|
# target-specific options
|
|
# * CARGO_FEATURES_OPTION
|
|
CARGO_FEATURES_OPTION='' ;
|
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
|
outputs CARGO_FEATURES_OPTION
|
|
## note: requires 'nightly' toolchain b/c `cargo-udeps` uses the `rustc` '-Z save-analysis' option
|
|
## * ... ref: <https://github.com/est31/cargo-udeps/issues/73>
|
|
- name: Install `rust` toolchain
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: nightly
|
|
default: true
|
|
profile: minimal
|
|
- name: Install `cargo-udeps`
|
|
uses: actions-rs/install@v0.1
|
|
with:
|
|
crate: cargo-udeps
|
|
version: latest
|
|
use-tool-cache: false
|
|
env:
|
|
RUSTUP_TOOLCHAIN: stable
|
|
- name: Detect unused dependencies
|
|
shell: bash
|
|
run: |
|
|
## Detect unused dependencies
|
|
unset fault
|
|
fault_type="${{ steps.vars.outputs.FAULT_TYPE }}"
|
|
fault_prefix=$(echo "$fault_type" | tr '[:lower:]' '[:upper:]')
|
|
#
|
|
cargo +nightly udeps ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all-targets &> udeps.log || cat udeps.log
|
|
grep --ignore-case "all deps seem to have been used" udeps.log || { printf "%s\n" "::${fault_type} ::${fault_prefix}: \`cargo udeps\`: style violation (unused dependency found)" ; fault=true ; }
|
|
if [ -n "${{ steps.vars.outputs.FAIL_ON_FAULT }}" ] && [ -n "$fault" ]; then exit 1 ; fi
|
|
|
|
style_format:
|
|
name: Style/format
|
|
runs-on: ${{ matrix.job.os }}
|
|
# env:
|
|
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- { os: ubuntu-latest , features: feat_os_unix }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Initialize workflow variables
|
|
id: vars
|
|
shell: bash
|
|
run: |
|
|
## VARs setup
|
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; 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
|
|
# target-specific options
|
|
# * CARGO_FEATURES_OPTION
|
|
CARGO_FEATURES_OPTION='' ;
|
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
|
outputs CARGO_FEATURES_OPTION
|
|
- 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: "`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:
|
|
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
|
|
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@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Initialize workflow variables
|
|
id: vars
|
|
shell: bash
|
|
run: |
|
|
## VARs setup
|
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; 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
|
|
# target-specific options
|
|
# * CARGO_FEATURES_OPTION
|
|
CARGO_FEATURES_OPTION='--all-features' ;
|
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
|
outputs CARGO_FEATURES_OPTION
|
|
# * determine sub-crate utility list
|
|
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
|
echo UTILITY_LIST=${UTILITY_LIST}
|
|
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
|
|
outputs CARGO_UTILITY_LIST_OPTIONS
|
|
- name: Install/setup prerequisites
|
|
shell: bash
|
|
run: |
|
|
case '${{ matrix.job.os }}' in
|
|
macos-latest) brew install coreutils ;; # needed for show-utils.sh
|
|
esac
|
|
- name: Install `rust` toolchain
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
components: clippy
|
|
- name: "`cargo clippy` lint testing"
|
|
shell: bash
|
|
run: |
|
|
## `cargo clippy` lint testing
|
|
unset fault
|
|
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 ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} ${{ steps.vars.outputs.CARGO_UTILITY_LIST_OPTIONS }} -- -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 }}
|
|
# env:
|
|
# STYLE_FAIL_ON_FAULT: false # overrides workflow default
|
|
strategy:
|
|
matrix:
|
|
job:
|
|
- { os: ubuntu-latest , features: feat_os_unix }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Initialize workflow variables
|
|
id: vars
|
|
shell: bash
|
|
run: |
|
|
## VARs setup
|
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; 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: |
|
|
## Install/setup prerequisites
|
|
# * pin installed cspell to v4.2.8 (cspell v5+ is broken for NodeJS < v12)
|
|
## maint: [2021-11-10; rivy] `cspell` version may be advanced to v5 when used with NodeJS >= v12
|
|
sudo apt-get -y update ; sudo apt-get -y install npm ; sudo npm install cspell@4.2.8 -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
|
|
# * `cspell`
|
|
## maint: [2021-11-10; rivy] the `--no-progress` option for `cspell` is a `cspell` v5+ option
|
|
# 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 ; }
|
|
S=$(cspell ${CSPELL_CFG_OPTION} --no-summary "**/*") && 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
|
|
|
|
doc_warnings:
|
|
name: Documentation/warnings
|
|
runs-on: ${{ matrix.job.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- { os: ubuntu-latest , features: feat_os_unix }
|
|
# for now, don't build it on mac & windows because the doc is only published from linux
|
|
# + it needs a bunch of duplication for build
|
|
# and I don't want to add a doc step in the regular build to avoid long builds
|
|
# - { os: macos-latest , features: feat_os_macos }
|
|
# - { os: windows-latest , features: feat_os_windows }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Initialize workflow variables
|
|
id: vars
|
|
shell: bash
|
|
run: |
|
|
## VARs setup
|
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; 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
|
|
# target-specific options
|
|
# * CARGO_FEATURES_OPTION
|
|
CARGO_FEATURES_OPTION='--all-features' ;
|
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
|
outputs CARGO_FEATURES_OPTION
|
|
# * determine sub-crate utility list
|
|
UTILITY_LIST="$(./util/show-utils.sh ${CARGO_FEATURES_OPTION})"
|
|
echo UTILITY_LIST=${UTILITY_LIST}
|
|
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
|
|
outputs CARGO_UTILITY_LIST_OPTIONS
|
|
- name: Install `rust` toolchain
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
components: clippy
|
|
- name: "`cargo doc` with warnings"
|
|
shell: bash
|
|
run: |
|
|
RUSTDOCFLAGS="-Dwarnings" cargo doc ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-deps --workspace --document-private-items
|
|
|
|
|
|
min_version:
|
|
name: MinRustV # Minimum supported rust version (aka, MinSRV or MSRV)
|
|
runs-on: ${{ matrix.job.os }}
|
|
strategy:
|
|
matrix:
|
|
job:
|
|
- { os: ubuntu-latest , features: feat_os_unix }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Initialize workflow variables
|
|
id: vars
|
|
shell: bash
|
|
run: |
|
|
## VARs setup
|
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
|
# target-specific options
|
|
# * CARGO_FEATURES_OPTION
|
|
unset CARGO_FEATURES_OPTION
|
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features "${{ matrix.job.features }}"' ; fi
|
|
outputs CARGO_FEATURES_OPTION
|
|
- name: Install `rust` toolchain (v${{ env.RUST_MIN_SRV }})
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: ${{ env.RUST_MIN_SRV }}
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
- name: Confirm MinSRV compatible 'Cargo.lock'
|
|
shell: bash
|
|
run: |
|
|
## Confirm MinSRV compatible '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 || { echo "::error file=Cargo.lock::Incompatible (or out-of-date) 'Cargo.lock' file; update using \`cargo +${{ env.RUST_MIN_SRV }} update\`" ; exit 1 ; }
|
|
- name: Confirm MinSRV equivalence for '.clippy.toml'
|
|
shell: bash
|
|
run: |
|
|
## Confirm MinSRV equivalence for '.clippy.toml'
|
|
# * ensure '.clippy.toml' MSRV configuration setting is equal to ${{ env.RUST_MIN_SRV }}
|
|
CLIPPY_MSRV=$(grep -P "(?i)^\s*msrv\s*=\s*" .clippy.toml | grep -oP "\d+([.]\d+)+")
|
|
if [ "${CLIPPY_MSRV}" != "${{ env.RUST_MIN_SRV }}" ]; then { echo "::error file=.clippy.toml::Incorrect MSRV configuration for clippy (found '${CLIPPY_MSRV}'; should be '${{ env.RUST_MIN_SRV }}'); update '.clippy.toml' with 'msrv = \"${{ env.RUST_MIN_SRV }}\"'" ; exit 1 ; } ; fi
|
|
- 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"
|
|
## * using the 'stable' toolchain is necessary to avoid "unexpected '--filter-platform'" errors
|
|
RUSTUP_TOOLCHAIN=stable cargo fetch --locked --quiet
|
|
RUSTUP_TOOLCHAIN=stable cargo tree --all --locked --no-dev-dependencies --no-indent ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} | grep -vE "$PWD" | sort --unique
|
|
- name: Test
|
|
uses: actions-rs/cargo@v1
|
|
with:
|
|
command: test
|
|
args: -v ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} -p uucore -p coreutils
|
|
env:
|
|
RUSTFLAGS: "-Awarnings --cfg unsound_local_offset"
|
|
|
|
deps:
|
|
name: Dependencies
|
|
runs-on: ${{ matrix.job.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- { os: ubuntu-latest , features: feat_os_unix }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Install `rust` toolchain
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
- name: "`cargo update` testing"
|
|
shell: bash
|
|
run: |
|
|
## `cargo update` 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>
|
|
cargo fetch --locked --quiet || { echo "::error file=Cargo.lock::'Cargo.lock' file requires update (use \`cargo +${{ env.RUST_MIN_SRV }} update\`)" ; exit 1 ; }
|
|
|
|
build_makefile:
|
|
name: Build/Makefile
|
|
needs: [ min_version, deps ]
|
|
runs-on: ${{ matrix.job.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- { os: ubuntu-latest , features: feat_os_unix }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Install `rust` toolchain
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
- name: "`make build`"
|
|
shell: bash
|
|
run: |
|
|
make build
|
|
- name: "`make test`"
|
|
shell: bash
|
|
run: |
|
|
make test
|
|
|
|
|
|
build_rust_stable:
|
|
name: Build/stable
|
|
needs: [ min_version, deps ]
|
|
runs-on: ${{ matrix.job.os }}
|
|
timeout-minutes: 90
|
|
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@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Install `rust` toolchain
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
- name: Test
|
|
uses: actions-rs/cargo@v1
|
|
with:
|
|
command: test
|
|
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
|
|
|
build_rust_nightly:
|
|
name: Build/nightly
|
|
needs: [ min_version, deps ]
|
|
runs-on: ${{ matrix.job.os }}
|
|
timeout-minutes: 90
|
|
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@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Install `rust` toolchain
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: nightly
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
- name: Test
|
|
uses: actions-rs/cargo@v1
|
|
with:
|
|
command: test
|
|
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
|
|
|
compute_size:
|
|
name: Binary sizes
|
|
needs: [ min_version, deps ]
|
|
runs-on: ${{ matrix.job.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- { os: ubuntu-latest , features: feat_os_unix }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Install dependencies
|
|
shell: bash
|
|
run: |
|
|
## Install dependencies
|
|
sudo apt-get update
|
|
sudo apt-get install jq
|
|
- name: Install `rust` toolchain
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
- name: "`make install`"
|
|
shell: bash
|
|
run: |
|
|
make install DESTDIR=target/size-release/
|
|
make install MULTICALL=y DESTDIR=target/size-multi-release/
|
|
# strip the results
|
|
strip target/size*/usr/local/bin/*
|
|
- name: "Compute sizes"
|
|
shell: bash
|
|
run: |
|
|
SIZE=$(du -s target/size-release/usr/local/bin/|awk '{print $1}')
|
|
SIZEMULTI=$(du -s target/size-multi-release/usr/local/bin/|awk '{print $1}')
|
|
jq -n \
|
|
--arg date "$(date --rfc-email)" \
|
|
--arg sha "$GITHUB_SHA" \
|
|
--arg size "$SIZE" \
|
|
--arg multisize "$SIZEMULTI" \
|
|
'{($date): { sha: $sha, size: $size, multisize: $multisize, }}' > size-result.json
|
|
- uses: actions/upload-artifact@v3
|
|
with:
|
|
name: size-result
|
|
path: size-result.json
|
|
|
|
build:
|
|
permissions:
|
|
contents: write # to create GitHub release (softprops/action-gh-release)
|
|
|
|
name: Build
|
|
needs: [ min_version, deps ]
|
|
runs-on: ${{ matrix.job.os }}
|
|
timeout-minutes: 90
|
|
env:
|
|
DOCKER_OPTS: '--volume /etc/passwd:/etc/passwd --volume /etc/group:/etc/group'
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
# { os , target , cargo-options , features , use-cross , toolchain }
|
|
- { os: ubuntu-latest , target: arm-unknown-linux-gnueabihf, features: feat_os_unix_gnueabihf, use-cross: use-cross, }
|
|
- { os: ubuntu-latest , target: aarch64-unknown-linux-gnu , features: feat_os_unix_gnueabihf , use-cross: use-cross }
|
|
- { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
|
# - { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_selinux , use-cross: use-cross }
|
|
# - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } ## note: older windows platform; not required, dev-FYI only
|
|
# - { os: ubuntu-18.04 , target: i586-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross } ## note: older windows platform; not required, dev-FYI only
|
|
- { os: ubuntu-latest , target: i686-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
|
- { os: ubuntu-latest , target: i686-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross }
|
|
- { os: ubuntu-latest , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
|
- { os: ubuntu-latest , target: x86_64-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross }
|
|
# Commented until https://github.com/uutils/coreutils/issues/3210 is fixed
|
|
#- { os: ubuntu-18.04 , target: i686-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
|
#- { os: ubuntu-18.04 , target: i686-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross }
|
|
#- { os: ubuntu-18.04 , target: x86_64-unknown-linux-gnu , features: feat_os_unix , use-cross: use-cross }
|
|
#- { os: ubuntu-18.04 , target: x86_64-unknown-linux-musl , features: feat_os_unix_musl , use-cross: use-cross }
|
|
- { os: macos-latest , target: x86_64-apple-darwin , features: feat_os_macos }
|
|
- { os: windows-latest , target: i686-pc-windows-msvc , features: feat_os_windows }
|
|
- { os: windows-latest , target: x86_64-pc-windows-gnu , features: feat_os_windows } ## note: requires rust >= 1.43.0 to link correctly
|
|
- { os: windows-latest , target: x86_64-pc-windows-msvc , features: feat_os_windows }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Initialize workflow variables
|
|
id: vars
|
|
shell: bash
|
|
run: |
|
|
## VARs setup
|
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
|
# toolchain
|
|
TOOLCHAIN="stable" ## default to "stable" toolchain
|
|
# * specify alternate/non-default TOOLCHAIN for *-pc-windows-gnu targets; gnu targets on Windows are broken for the standard *-pc-windows-msvc toolchain (refs: GH:rust-lang/rust#47048, GH:rust-lang/rust#53454, GH:rust-lang/cargo#6754)
|
|
case ${{ matrix.job.target }} in *-pc-windows-gnu) TOOLCHAIN="stable-${{ matrix.job.target }}" ;; esac;
|
|
# * use requested TOOLCHAIN if specified
|
|
if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi
|
|
outputs TOOLCHAIN
|
|
# staging directory
|
|
STAGING='_staging'
|
|
outputs STAGING
|
|
# determine EXE suffix
|
|
EXE_suffix="" ; case '${{ matrix.job.target }}' in *-pc-windows-*) EXE_suffix=".exe" ;; esac;
|
|
outputs EXE_suffix
|
|
# parse commit reference info
|
|
echo GITHUB_REF=${GITHUB_REF}
|
|
echo GITHUB_SHA=${GITHUB_SHA}
|
|
REF_NAME=${GITHUB_REF#refs/*/}
|
|
unset REF_BRANCH ; case "${GITHUB_REF}" in refs/heads/*) REF_BRANCH=${GITHUB_REF#refs/heads/} ;; esac;
|
|
unset REF_TAG ; case "${GITHUB_REF}" in refs/tags/*) REF_TAG=${GITHUB_REF#refs/tags/} ;; esac;
|
|
REF_SHAS=${GITHUB_SHA:0:8}
|
|
outputs REF_NAME REF_BRANCH REF_TAG REF_SHAS
|
|
# parse target
|
|
unset TARGET_ARCH
|
|
case '${{ matrix.job.target }}' in
|
|
aarch64-*) TARGET_ARCH=arm64 ;;
|
|
arm-*-*hf) TARGET_ARCH=armhf ;;
|
|
i586-*) TARGET_ARCH=i586 ;;
|
|
i686-*) TARGET_ARCH=i686 ;;
|
|
x86_64-*) TARGET_ARCH=x86_64 ;;
|
|
esac;
|
|
unset TARGET_OS ; case '${{ matrix.job.target }}' in *-linux-*) TARGET_OS=linux ;; *-apple-*) TARGET_OS=macos ;; *-windows-*) TARGET_OS=windows ;; esac;
|
|
outputs TARGET_ARCH TARGET_OS
|
|
# package name
|
|
PKG_suffix=".tar.gz" ; case '${{ matrix.job.target }}' in *-pc-windows-*) PKG_suffix=".zip" ;; esac;
|
|
PKG_BASENAME=${PROJECT_NAME}-${REF_TAG:-$REF_SHAS}-${{ matrix.job.target }}
|
|
PKG_NAME=${PKG_BASENAME}${PKG_suffix}
|
|
outputs PKG_suffix PKG_BASENAME PKG_NAME
|
|
# deployable tag? (ie, leading "vM" or "M"; M == version number)
|
|
unset DEPLOY ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DEPLOY='true' ; fi
|
|
outputs DEPLOY
|
|
# DPKG architecture?
|
|
unset DPKG_ARCH
|
|
case ${{ matrix.job.target }} in
|
|
x86_64-*-linux-*) DPKG_ARCH=amd64 ;;
|
|
*-linux-*) DPKG_ARCH=${TARGET_ARCH} ;;
|
|
esac
|
|
outputs DPKG_ARCH
|
|
# DPKG version?
|
|
unset DPKG_VERSION ; if [[ $REF_TAG =~ ^[vV]?[0-9].* ]]; then DPKG_VERSION=${REF_TAG/#[vV]/} ; fi
|
|
outputs DPKG_VERSION
|
|
# DPKG base name/conflicts?
|
|
DPKG_BASENAME=${PROJECT_NAME}
|
|
DPKG_CONFLICTS=${PROJECT_NAME}-musl
|
|
case ${{ matrix.job.target }} in *-musl) DPKG_BASENAME=${PROJECT_NAME}-musl ; DPKG_CONFLICTS=${PROJECT_NAME} ;; esac;
|
|
outputs DPKG_BASENAME DPKG_CONFLICTS
|
|
# DPKG name
|
|
unset DPKG_NAME;
|
|
if [[ -n $DPKG_ARCH && -n $DPKG_VERSION ]]; then DPKG_NAME="${DPKG_BASENAME}_${DPKG_VERSION}_${DPKG_ARCH}.deb" ; fi
|
|
outputs DPKG_NAME
|
|
# target-specific options
|
|
# * CARGO_FEATURES_OPTION
|
|
CARGO_FEATURES_OPTION='' ;
|
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features=${{ matrix.job.features }}' ; fi
|
|
outputs CARGO_FEATURES_OPTION
|
|
# * CARGO_USE_CROSS (truthy)
|
|
CARGO_USE_CROSS='true' ; case '${{ matrix.job.use-cross }}' in ''|0|f|false|n|no) unset CARGO_USE_CROSS ;; esac;
|
|
outputs CARGO_USE_CROSS
|
|
# ** pass needed environment into `cross` container (iff `cross` not already configured via "Cross.toml")
|
|
if [ -n "${CARGO_USE_CROSS}" ] && [ ! -e "Cross.toml" ] ; then
|
|
cargo install --version 0.2.1 cross
|
|
printf "[build.env]\npassthrough = [\"CI\"]\n" > Cross.toml
|
|
fi
|
|
# * test only library and/or binaries for arm-type targets
|
|
unset CARGO_TEST_OPTIONS ; case '${{ matrix.job.target }}' in aarch64-* | arm-*) CARGO_TEST_OPTIONS="--bins" ;; esac;
|
|
outputs CARGO_TEST_OPTIONS
|
|
# * executable for `strip`?
|
|
STRIP="strip"
|
|
case ${{ matrix.job.target }} in
|
|
aarch64-*-linux-gnu) STRIP="aarch64-linux-gnu-strip" ;;
|
|
arm-*-linux-gnueabihf) STRIP="arm-linux-gnueabihf-strip" ;;
|
|
*-pc-windows-msvc) STRIP="" ;;
|
|
esac;
|
|
outputs STRIP
|
|
- name: Create all needed build/work directories
|
|
shell: bash
|
|
run: |
|
|
## Create build/work space
|
|
mkdir -p '${{ steps.vars.outputs.STAGING }}'
|
|
mkdir -p '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}'
|
|
mkdir -p '${{ steps.vars.outputs.STAGING }}/dpkg'
|
|
- name: Install/setup prerequisites
|
|
shell: bash
|
|
run: |
|
|
## Install/setup prerequisites
|
|
case '${{ matrix.job.target }}' in
|
|
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
|
|
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
|
|
esac
|
|
case '${{ matrix.job.os }}' in
|
|
macos-latest) brew install coreutils ;; # needed for testing
|
|
esac
|
|
case '${{ matrix.job.os }}' in
|
|
ubuntu-*)
|
|
# pinky is a tool to show logged-in users from utmp, and gecos fields from /etc/passwd.
|
|
# In GitHub Action *nix VMs, no accounts log in, even the "runner" account that runs the commands. The account also has empty gecos fields.
|
|
# To work around this for pinky tests, we create a fake login entry for the GH runner account...
|
|
FAKE_UTMP='[7] [999999] [tty2] [runner] [tty2] [] [0.0.0.0] [2022-02-22T22:22:22,222222+00:00]'
|
|
# ... by dumping the login records, adding our fake line, then reverse dumping ...
|
|
(utmpdump /var/run/utmp ; echo $FAKE_UTMP) | sudo utmpdump -r -o /var/run/utmp
|
|
# ... and add a full name to each account with a gecos field but no full name.
|
|
sudo sed -i 's/:,/:runner name,/' /etc/passwd
|
|
# We also create a couple optional files pinky looks for
|
|
touch /home/runner/.project
|
|
echo "foo" > /home/runner/.plan
|
|
;;
|
|
esac
|
|
- name: rust toolchain ~ install
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: ${{ env.RUST_MIN_SRV }}
|
|
target: ${{ matrix.job.target }}
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
- name: Initialize toolchain-dependent workflow variables
|
|
id: dep_vars
|
|
shell: bash
|
|
run: |
|
|
## Dependent VARs setup
|
|
outputs() { step_id="dep_vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
|
# * determine sub-crate utility list
|
|
UTILITY_LIST="$(./util/show-utils.sh ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }})"
|
|
echo UTILITY_LIST=${UTILITY_LIST}
|
|
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
|
|
outputs CARGO_UTILITY_LIST_OPTIONS
|
|
- name: Info
|
|
shell: bash
|
|
run: |
|
|
## Info
|
|
# commit info
|
|
echo "## commit"
|
|
echo GITHUB_REF=${GITHUB_REF}
|
|
echo GITHUB_SHA=${GITHUB_SHA}
|
|
# 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
|
|
cargo tree --locked --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --all --no-dev-dependencies --no-indent | grep -vE "$PWD" | sort --unique
|
|
- name: Build
|
|
uses: actions-rs/cargo@v1
|
|
with:
|
|
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
|
|
command: build
|
|
args: --release --target=${{ matrix.job.target }} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
|
toolchain: ${{ env.RUST_MIN_SRV }}
|
|
- name: Test
|
|
uses: actions-rs/cargo@v1
|
|
with:
|
|
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
|
|
command: test
|
|
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }}
|
|
toolchain: ${{ env.RUST_MIN_SRV }}
|
|
- name: Test individual utilities
|
|
uses: actions-rs/cargo@v1
|
|
with:
|
|
use-cross: ${{ steps.vars.outputs.CARGO_USE_CROSS }}
|
|
command: test
|
|
args: --target=${{ matrix.job.target }} ${{ steps.vars.outputs.CARGO_TEST_OPTIONS}} ${{ matrix.job.cargo-options }} ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }}
|
|
toolchain: ${{ env.RUST_MIN_SRV }}
|
|
- name: Archive executable artifacts
|
|
uses: actions/upload-artifact@v3
|
|
with:
|
|
name: ${{ env.PROJECT_NAME }}-${{ matrix.job.target }}
|
|
path: target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}
|
|
- name: Package
|
|
shell: bash
|
|
run: |
|
|
## Package artifact(s)
|
|
# binary
|
|
cp 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/'
|
|
# `strip` binary (if needed)
|
|
if [ -n "${{ steps.vars.outputs.STRIP }}" ]; then "${{ steps.vars.outputs.STRIP }}" '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' ; fi
|
|
# README and LICENSE
|
|
# * spell-checker:ignore EADME ICENSE
|
|
(shopt -s nullglob; for f in [R]"EADME"{,.*}; do cp $f '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' ; done)
|
|
(shopt -s nullglob; for f in [L]"ICENSE"{-*,}{,.*}; do cp $f '${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_BASENAME }}/' ; done)
|
|
# core compressed package
|
|
pushd '${{ steps.vars.outputs.STAGING }}/' >/dev/null
|
|
case '${{ matrix.job.target }}' in
|
|
*-pc-windows-*) 7z -y a '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* | tail -2 ;;
|
|
*) tar czf '${{ steps.vars.outputs.PKG_NAME }}' '${{ steps.vars.outputs.PKG_BASENAME }}'/* ;;
|
|
esac
|
|
popd >/dev/null
|
|
# dpkg
|
|
if [ -n "${{ steps.vars.outputs.DPKG_NAME }}" ]; then
|
|
DPKG_DIR="${{ steps.vars.outputs.STAGING }}/dpkg"
|
|
# binary
|
|
install -Dm755 'target/${{ matrix.job.target }}/release/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}' "${DPKG_DIR}/usr/bin/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}"
|
|
if [ -n "${{ steps.vars.outputs.STRIP }}" ]; then "${{ steps.vars.outputs.STRIP }}" "${DPKG_DIR}/usr/bin/${{ env.PROJECT_NAME }}${{ steps.vars.outputs.EXE_suffix }}" ; fi
|
|
# README and LICENSE
|
|
(shopt -s nullglob; for f in [R]"EADME"{,.*}; do install -Dm644 "$f" "${DPKG_DIR}/usr/share/doc/${{ env.PROJECT_NAME }}/$f" ; done)
|
|
(shopt -s nullglob; for f in [L]"ICENSE"{-*,}{,.*}; do install -Dm644 "$f" "${DPKG_DIR}/usr/share/doc/${{ env.PROJECT_NAME }}/$f" ; done)
|
|
# control file
|
|
mkdir -p "${DPKG_DIR}/DEBIAN"
|
|
printf "Package: ${{ steps.vars.outputs.DPKG_BASENAME }}\nVersion: ${{ steps.vars.outputs.DPKG_VERSION }}\nSection: utils\nPriority: optional\nMaintainer: ${{ env.PROJECT_AUTH }}\nArchitecture: ${{ steps.vars.outputs.DPKG_ARCH }}\nProvides: ${{ env.PROJECT_NAME }}\nConflicts: ${{ steps.vars.outputs.DPKG_CONFLICTS }}\nDescription: ${{ env.PROJECT_DESC }}\n" > "${DPKG_DIR}/DEBIAN/control"
|
|
# build dpkg
|
|
fakeroot dpkg-deb --build "${DPKG_DIR}" "${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.DPKG_NAME }}"
|
|
fi
|
|
- name: Publish
|
|
uses: softprops/action-gh-release@v1
|
|
if: steps.vars.outputs.DEPLOY
|
|
with:
|
|
files: |
|
|
${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.PKG_NAME }}
|
|
${{ steps.vars.outputs.STAGING }}/${{ steps.vars.outputs.DPKG_NAME }}
|
|
env:
|
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
test_busybox:
|
|
name: Tests/BusyBox test suite
|
|
needs: [ min_version, deps ]
|
|
runs-on: ${{ matrix.job.os }}
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- { os: ubuntu-latest }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Install/setup prerequisites
|
|
shell: bash
|
|
run: |
|
|
## Install/setup prerequisites
|
|
make prepare-busytest
|
|
- name: Install `rust` toolchain
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: stable
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
- name: "Run BusyBox test suite"
|
|
shell: bash
|
|
run: |
|
|
## Run BusyBox test suite
|
|
bindir=$(pwd)/target/debug
|
|
cd tmp/busybox-*/testsuite
|
|
output=$(bindir=$bindir ./runtest 2>&1 || true)
|
|
printf "%s\n" "${output}"
|
|
n_fails=$(echo "$output" | grep "^FAIL:\s" | wc --lines)
|
|
if [ $n_fails -gt 0 ] ; then echo "::warning ::${n_fails}+ test failures" ; fi
|
|
|
|
test_android:
|
|
name: Test Android builds
|
|
needs: [ min_version, deps ]
|
|
runs-on: macos-latest
|
|
timeout-minutes: 90
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
api-level: [28]
|
|
target: [default]
|
|
arch: [x86] # , arm64-v8a
|
|
env:
|
|
TERMUX: v0.118.0
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- name: AVD cache
|
|
uses: actions/cache@v3
|
|
id: avd-cache
|
|
with:
|
|
path: |
|
|
~/.android/avd/*
|
|
~/.android/avd/*/snapshots/*
|
|
~/.android/adb*
|
|
key: avd-${{ matrix.api-level }}-${{ matrix.arch }}+termux-${{ env.TERMUX }}
|
|
- name: Create and cache emulator image
|
|
if: steps.avd-cache.outputs.cache-hit != 'true'
|
|
uses: reactivecircus/android-emulator-runner@v2
|
|
with:
|
|
api-level: ${{ matrix.api-level }}
|
|
target: ${{ matrix.target }}
|
|
arch: ${{ matrix.arch }}
|
|
ram-size: 2048M
|
|
disk-size: 5120M
|
|
force-avd-creation: true
|
|
emulator-options: -no-snapshot-load -noaudio -no-boot-anim -camera-back none
|
|
script: |
|
|
wget https://github.com/termux/termux-app/releases/download/${{ env.TERMUX }}/termux-app_${{ env.TERMUX }}+github-debug_${{ matrix.arch }}.apk
|
|
util/android-commands.sh snapshot termux-app_${{ env.TERMUX }}+github-debug_${{ matrix.arch }}.apk
|
|
adb -s emulator-5554 emu avd snapshot save ${{ matrix.api-level }}-${{ matrix.arch }}+termux-${{ env.TERMUX }}
|
|
echo "Emulator image created."
|
|
pkill -9 qemu-system-x86_64
|
|
- name: Build and Test on Android
|
|
uses: reactivecircus/android-emulator-runner@v2
|
|
with:
|
|
api-level: ${{ matrix.api-level }}
|
|
target: ${{ matrix.target }}
|
|
arch: ${{ matrix.arch }}
|
|
ram-size: 2048M
|
|
disk-size: 5120M
|
|
force-avd-creation: false
|
|
emulator-options: -no-snapshot-save -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -snapshot ${{ matrix.api-level }}-${{ matrix.arch }}+termux-${{ env.TERMUX }}
|
|
script: |
|
|
util/android-commands.sh sync
|
|
util/android-commands.sh build
|
|
util/android-commands.sh tests
|
|
|
|
test_freebsd:
|
|
name: Tests/FreeBSD test suite
|
|
needs: [ min_version, deps ]
|
|
runs-on: ${{ matrix.job.os }}
|
|
timeout-minutes: 90
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- { os: macos-12 , features: unix } ## GHA MacOS-11.0 VM won't have VirtualBox; refs: <https://github.com/actions/virtual-environments/issues/4060> , <https://github.com/actions/virtual-environments/pull/4010>
|
|
env:
|
|
mem: 2048
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
- name: Prepare, build and test
|
|
## spell-checker:ignore (ToDO) sshfs usesh vmactions
|
|
uses: vmactions/freebsd-vm@v0.2.9
|
|
with:
|
|
usesh: true
|
|
# sync: sshfs
|
|
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
|
|
#
|
|
TEST_USER=tester
|
|
REPO_NAME=${GITHUB_WORKSPACE##*/}
|
|
WORKSPACE_PARENT="/Users/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/ "/Users/runner/work/${REPO_NAME}"/
|
|
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
|
|
## 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
|
|
rustc -V
|
|
#
|
|
cd "${WORKSPACE}"
|
|
unset FAULT
|
|
cargo build || FAULT=1
|
|
cargo test --features "${{ matrix.job.features }}" || FAULT=1
|
|
cargo test --features "${{ matrix.job.features }}" -p uucore || FAULT=1
|
|
# Clean to avoid to rsync back the files
|
|
cargo clean
|
|
if (test -n "$FAULT"); then exit 1 ; fi
|
|
EOF
|
|
|
|
coverage:
|
|
name: Code Coverage
|
|
runs-on: ${{ matrix.job.os }}
|
|
timeout-minutes: 90
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
job:
|
|
- { os: ubuntu-latest , features: unix }
|
|
- { os: macos-latest , features: macos }
|
|
- { os: windows-latest , features: windows }
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
- uses: Swatinem/rust-cache@v1
|
|
# - name: Reattach HEAD ## may be needed for accurate code coverage info
|
|
# run: git checkout ${{ github.head_ref }}
|
|
- name: Initialize workflow variables
|
|
id: vars
|
|
shell: bash
|
|
run: |
|
|
## VARs setup
|
|
outputs() { step_id="vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
|
# toolchain
|
|
TOOLCHAIN="nightly" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support
|
|
# * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files
|
|
case ${{ matrix.job.os }} in windows-*) TOOLCHAIN="$TOOLCHAIN-x86_64-pc-windows-gnu" ;; esac;
|
|
# * use requested TOOLCHAIN if specified
|
|
if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi
|
|
outputs TOOLCHAIN
|
|
# staging directory
|
|
STAGING='_staging'
|
|
outputs STAGING
|
|
# target-specific options
|
|
# * CARGO_FEATURES_OPTION
|
|
CARGO_FEATURES_OPTION='--all-features' ; ## default to '--all-features' for code coverage
|
|
if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features=${{ matrix.job.features }}' ; fi
|
|
outputs CARGO_FEATURES_OPTION
|
|
# * CODECOV_FLAGS
|
|
CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' )
|
|
outputs CODECOV_FLAGS
|
|
- name: Install/setup prerequisites
|
|
shell: bash
|
|
run: |
|
|
## Install/setup prerequisites
|
|
case '${{ matrix.job.os }}' in
|
|
macos-latest) brew install coreutils ;; # needed for testing
|
|
esac
|
|
case '${{ matrix.job.os }}' in
|
|
ubuntu-latest)
|
|
# pinky is a tool to show logged-in users from utmp, and gecos fields from /etc/passwd.
|
|
# In GitHub Action *nix VMs, no accounts log in, even the "runner" account that runs the commands. The account also has empty gecos fields.
|
|
# To work around this for pinky tests, we create a fake login entry for the GH runner account...
|
|
FAKE_UTMP='[7] [999999] [tty2] [runner] [tty2] [] [0.0.0.0] [2022-02-22T22:22:22,222222+00:00]'
|
|
# ... by dumping the login records, adding our fake line, then reverse dumping ...
|
|
(utmpdump /var/run/utmp ; echo $FAKE_UTMP) | sudo utmpdump -r -o /var/run/utmp
|
|
# ... and add a full name to each account with a gecos field but no full name.
|
|
sudo sed -i 's/:,/:runner name,/' /etc/passwd
|
|
# We also create a couple optional files pinky looks for
|
|
touch /home/runner/.project
|
|
echo "foo" > /home/runner/.plan
|
|
;;
|
|
esac
|
|
- name: rust toolchain ~ install
|
|
uses: actions-rs/toolchain@v1
|
|
with:
|
|
toolchain: ${{ steps.vars.outputs.TOOLCHAIN }}
|
|
default: true
|
|
profile: minimal # minimal component installation (ie, no documentation)
|
|
- name: Initialize toolchain-dependent workflow variables
|
|
id: dep_vars
|
|
shell: bash
|
|
run: |
|
|
## Dependent VARs setup
|
|
outputs() { step_id="dep_vars"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo ::set-output name=${var}::${!var}; done; }
|
|
# * determine sub-crate utility list
|
|
UTILITY_LIST="$(./util/show-utils.sh ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }})"
|
|
CARGO_UTILITY_LIST_OPTIONS="$(for u in ${UTILITY_LIST}; do echo "-puu_${u}"; done;)"
|
|
outputs CARGO_UTILITY_LIST_OPTIONS
|
|
- name: Test uucore
|
|
uses: actions-rs/cargo@v1
|
|
with:
|
|
command: test
|
|
args: --no-fail-fast -p uucore
|
|
env:
|
|
CARGO_INCREMENTAL: "0"
|
|
RUSTC_WRAPPER: ""
|
|
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
|
RUSTDOCFLAGS: "-Cpanic=abort"
|
|
# RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }}
|
|
- name: Test
|
|
uses: actions-rs/cargo@v1
|
|
with:
|
|
command: test
|
|
args: ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast
|
|
env:
|
|
CARGO_INCREMENTAL: "0"
|
|
RUSTC_WRAPPER: ""
|
|
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
|
RUSTDOCFLAGS: "-Cpanic=abort"
|
|
# RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }}
|
|
- name: Test individual utilities
|
|
uses: actions-rs/cargo@v1
|
|
with:
|
|
command: test
|
|
args: --no-fail-fast ${{ steps.dep_vars.outputs.CARGO_UTILITY_LIST_OPTIONS }}
|
|
env:
|
|
CARGO_INCREMENTAL: "0"
|
|
RUSTC_WRAPPER: ""
|
|
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort"
|
|
RUSTDOCFLAGS: "-Cpanic=abort"
|
|
# RUSTUP_TOOLCHAIN: ${{ steps.vars.outputs.TOOLCHAIN }}
|
|
- name: "`grcov` ~ install"
|
|
id: build_grcov
|
|
shell: bash
|
|
run: |
|
|
git clone https://github.com/mozilla/grcov.git ~/grcov/
|
|
cd ~/grcov
|
|
# Hardcode the version of crossbeam-epoch. See
|
|
# https://github.com/uutils/coreutils/issues/3680
|
|
sed -i -e "s|tempfile =|crossbeam-epoch = \"=0.9.8\"\ntempfile =|" Cargo.toml
|
|
cargo install --path .
|
|
cd -
|
|
# Uncomment when the upstream issue
|
|
# https://github.com/mozilla/grcov/issues/849 is fixed
|
|
# uses: actions-rs/install@v0.1
|
|
# with:
|
|
# crate: grcov
|
|
# version: latest
|
|
# use-tool-cache: false
|
|
- name: Generate coverage data (via `grcov`)
|
|
id: coverage
|
|
shell: bash
|
|
run: |
|
|
## Generate coverage data
|
|
COVERAGE_REPORT_DIR="target/debug"
|
|
COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info"
|
|
# GRCOV_IGNORE_OPTION='--ignore build.rs --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*"' ## `grcov` ignores these params when passed as an environment variable (why?)
|
|
# GRCOV_EXCLUDE_OPTION='--excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()"' ## `grcov` ignores these params when passed as an environment variable (why?)
|
|
mkdir -p "${COVERAGE_REPORT_DIR}"
|
|
# display coverage files
|
|
~/.cargo/bin/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
|
|
~/.cargo/bin/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 ::set-output name=report::${COVERAGE_REPORT_FILE}
|
|
- name: Upload coverage results (to Codecov.io)
|
|
uses: codecov/codecov-action@v3
|
|
# if: steps.vars.outputs.HAS_CODECOV_TOKEN
|
|
with:
|
|
# token: ${{ secrets.CODECOV_TOKEN }}
|
|
file: ${{ steps.coverage.outputs.report }}
|
|
## flags: IntegrationTests, UnitTests, ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
|
flags: ${{ steps.vars.outputs.CODECOV_FLAGS }}
|
|
name: codecov-umbrella
|
|
fail_ci_if_error: false
|