|
1 | 1 | #!/usr/bin/env -S just --working-directory . --justfile |
2 | | -# Load project-specific properties from the `.env` file |
3 | 2 |
|
4 | | -set dotenv-load := true |
5 | | - |
6 | | -# Since this is a first recipe it's being run by default. |
7 | | -# Faster checks need to be executed first for better UX. For example |
8 | | -# codespell is very fast. cargo fmt does not need to download crates etc. |
| 3 | +clippy := "cargo clippy --quiet --workspace --no-deps --all-targets" |
| 4 | +clippy_args := "-D warnings" |
| 5 | +nextest_args := "--locked --workspace" |
| 6 | +udeps_args := "--quiet --workspace --all-features --all-targets" |
9 | 7 |
|
10 | 8 | # Perform all checks |
11 | | -check: spelling formatting docs lints dependencies tests |
| 9 | +[parallel] |
| 10 | +check: spell fmt doc lints deps unused-deps recipes test integration-test |
12 | 11 |
|
13 | | -# Checks common spelling mistakes |
14 | | -spelling: |
| 12 | +# Check spelling |
| 13 | +[group('ci')] |
| 14 | +[metadata('pacman', 'codespell')] |
| 15 | +spell: |
15 | 16 | codespell |
16 | 17 |
|
17 | | -# Checks source code formatting |
18 | | -formatting: |
| 18 | +# Check source code formatting |
| 19 | +[group('ci')] |
| 20 | +[metadata('pacman', 'rustup')] |
| 21 | +fmt: |
19 | 22 | just --unstable --fmt --check |
20 | 23 | # We're using nightly to properly group imports, see .rustfmt.toml |
21 | | - cargo +nightly fmt --all -- --check |
| 24 | + rustup component add --toolchain nightly rustfmt |
| 25 | + cargo +nightly fmt --quiet --all -- --check |
22 | 26 |
|
23 | | -# Lints the source code |
| 27 | +# Lint the source code |
| 28 | +[group('ci')] |
| 29 | +[metadata('gitlabci-job', '{"artifacts":{"when":"always","paths":["target/clippy"]}}')] |
| 30 | +[metadata('pacman', 'rust')] |
24 | 31 | lints: |
25 | | - cargo clippy --workspace --no-deps --all-targets -- -D warnings |
| 32 | + #!/usr/bin/bash |
| 33 | + set -euo pipefail |
26 | 34 |
|
27 | | -# Checks for issues with dependencies |
28 | | -dependencies: |
29 | | - cargo deny check |
| 35 | + if [ "${CI:-}" = "true" ]; then |
| 36 | + ARGS=(--message-format=json) |
| 37 | + else |
| 38 | + ARGS=() |
| 39 | + fi |
30 | 40 |
|
31 | | -# Runs all unit tests. By default ignored tests are not run. Run with `ignored=true` to run only ignored tests |
32 | | -tests: |
33 | | - cargo test --all |
| 41 | + mkdir -p target |
| 42 | + {{ clippy }} "${ARGS[@]}" -- {{ clippy_args }} | tee target/clippy |
34 | 43 |
|
35 | | -# Build docs for this crate only |
36 | | -docs: |
37 | | - cargo doc --no-deps |
| 44 | +# Create lints report |
| 45 | +[metadata('gitlabci-job', '{"stage":"deploy","when":"always","artifacts":{"reports":{"codequality":"target/codeclimate.json"}}}')] |
| 46 | +[metadata('pacman', 'cargo-sonar', 'rust')] |
| 47 | +create-codeclimate: |
| 48 | + # deny reports can be tested by adding a yanked dep e.g. `cargo add ed25519-dalek@0.9.0` |
| 49 | + cargo codeclimate \ |
| 50 | + --clippy --clippy-path "target/clippy" \ |
| 51 | + --deny --deny-path "target/deny" \ |
| 52 | + --udeps --udeps-path "target/udeps" \ |
| 53 | + --codeclimate-path target/codeclimate.json |
| 54 | + |
| 55 | +# Check for issues with dependencies |
| 56 | +[group('ci')] |
| 57 | +[metadata('gitlabci-job', '{"artifacts":{"when":"always","paths":["target/deny"]}}')] |
| 58 | +[metadata('pacman', 'cargo-deny')] |
| 59 | +deps: |
| 60 | + #!/usr/bin/bash |
| 61 | + set -euo pipefail |
38 | 62 |
|
39 | | -# Installs packages required to build |
40 | | -[linux] |
41 | | -install-packages: |
42 | | - sudo apt-get install --assume-yes --no-install-recommends $UBUNTU_PACKAGES |
| 63 | + if [ "${CI:-}" = "true" ]; then |
| 64 | + ARGS=(--format json) |
| 65 | + else |
| 66 | + ARGS=() |
| 67 | + fi |
43 | 68 |
|
44 | | -[macos] |
45 | | -[windows] |
46 | | -install-packages: |
47 | | - echo no-op |
| 69 | + mkdir -p target |
| 70 | + cargo deny "${ARGS[@]}" check -D advisory-not-detected -D license-not-encountered -D no-license-field 2>&1 | tee target/deny |
48 | 71 |
|
49 | | -# Checks for commit messages |
50 | | -check-commits REFS='main..': |
51 | | - #!/usr/bin/env bash |
| 72 | +# Check for unused dependencies |
| 73 | +[group('ci')] |
| 74 | +[metadata('gitlabci-job', '{"artifacts":{"when":"always","paths":["target/udeps"]}}')] |
| 75 | +[metadata('pacman', 'cargo-machete', 'cargo-udeps', 'rust')] |
| 76 | +unused-deps: |
| 77 | + #!/usr/bin/bash |
52 | 78 | set -euo pipefail |
53 | | - for commit in $(git rev-list "{{ REFS }}"); do |
| 79 | + |
| 80 | + if [ "${CI:-}" = "true" ]; then |
| 81 | + ARGS=(--output json) |
| 82 | + else |
| 83 | + ARGS=() |
| 84 | + fi |
| 85 | + |
| 86 | + mkdir -p target |
| 87 | + cargo +nightly udeps {{ udeps_args }} "${ARGS[@]}" | tee target/udeps |
| 88 | + cargo +nightly machete |
| 89 | + |
| 90 | +# Run unit tests |
| 91 | +[group('ci')] |
| 92 | +[metadata('pacman', 'cargo-nextest', 'rustup')] |
| 93 | +test: |
| 94 | + #!/usr/bin/bash |
| 95 | + set -euxo pipefail |
| 96 | + |
| 97 | + if [ "${CI:-}" = "true" ]; then |
| 98 | + PROFILE=ci |
| 99 | + else |
| 100 | + PROFILE=default |
| 101 | + fi |
| 102 | + |
| 103 | + cargo +nightly nextest run {{ nextest_args }} --profile "$PROFILE" |
| 104 | + cargo +nightly nextest run --no-default-features {{ nextest_args }} --profile "$PROFILE" |
| 105 | + |
| 106 | +# Run integration tests |
| 107 | +[metadata('pacman', 'git', 'jq', 'openssh', 'tangler', 'tree')] |
| 108 | +integration-test: |
| 109 | + #!/usr/bin/bash |
| 110 | + set -euo pipefail |
| 111 | + cargo +nightly build --locked |
| 112 | + target=$(cargo +nightly metadata --format-version 1 | jq --raw-output '.target_directory') |
| 113 | + tangler sh < README.md | sed --quiet --regexp-extended 's/^\$ (.*)/\1/p' | PATH="$target/debug:$PATH" bash -euxo pipefail - |
| 114 | + |
| 115 | +# Report on all tests |
| 116 | +[metadata('gitlabci-job', '{"coverage":"/Line coverage: ([0-9.]*)%/","artifacts":{"when":"always","reports":{"junit":"target/nextest/ci/junit.xml","metrics":"target/metrics.txt","coverage_report":{"coverage_format":"cobertura","path":"target/coverage.xml"}}}}')] |
| 117 | +[metadata('pacman', 'rust', 'cargo-llvm-cov', 'rustup')] |
| 118 | +report-test: |
| 119 | + #!/usr/bin/bash |
| 120 | + # enabling "x" here will garble text output that's parsed by GitLab for code coverage |
| 121 | + set -euo pipefail |
| 122 | + |
| 123 | + rustup component add --toolchain nightly llvm-tools-preview |
| 124 | + |
| 125 | + # shellcheck disable=SC1090 |
| 126 | + source <(cargo +nightly llvm-cov show-env --export-prefix --doctests --branch) |
| 127 | + cargo +nightly llvm-cov clean |
| 128 | + |
| 129 | + just test integration-test |
| 130 | + |
| 131 | + # explicitly use "target" (even if CARGO_TARGET_DIR is somewhere else) so that |
| 132 | + # local tools (such as https://github.com/ryanluker/vscode-coverage-gutters) can find the file |
| 133 | + cargo +nightly llvm-cov --quiet report --cobertura --output-path target/coverage.xml > /dev/null 2>&1 |
| 134 | + |
| 135 | + LINE_RATE=$(head target/coverage.xml | sed -nE 's/(.*coverage.*line-rate=")([^"]*)".*/\2/p') |
| 136 | + LINE_PERCENT=$(echo "$LINE_RATE" | awk '{print $1 * 100}') |
| 137 | + printf 'Line coverage: %s%%\n' "$LINE_PERCENT" |
| 138 | + |
| 139 | + BRANCH_RATE=$(head target/coverage.xml | sed -nE 's/(.*coverage.*branch-rate=")([^"]*)".*/\2/p') |
| 140 | + printf 'line_coverage_ratio %s\nbranch_coverage_ratio %s\n' "$LINE_RATE" "$BRANCH_RATE" > target/metrics.txt |
| 141 | + |
| 142 | +# Generate HTML report for the coverage |
| 143 | +coverage-html-report: report-test |
| 144 | + #!/usr/bin/bash |
| 145 | + set -euo pipefail |
| 146 | + # shellcheck disable=SC1090 |
| 147 | + source <(cargo +nightly llvm-cov show-env --export-prefix) |
| 148 | + cargo +nightly llvm-cov --quiet report --html > /dev/null 2>&1 |
| 149 | + printf "The coverage report is in file://%s/llvm-cov/html/index.html\n" "${CARGO_TARGET_DIR:-target}" |
| 150 | + |
| 151 | +# Build docs |
| 152 | +[group('ci')] |
| 153 | +[metadata('pacman', 'rust')] |
| 154 | +doc: |
| 155 | + RUSTDOCFLAGS='-D warnings' cargo doc --quiet --no-deps --document-private-items |
| 156 | + |
| 157 | +# Check commit messages |
| 158 | +[metadata('pacman', 'codespell', 'git')] |
| 159 | +commits: |
| 160 | + #!/usr/bin/env bash |
| 161 | + set -Eeuo pipefail |
| 162 | +
|
| 163 | + # fetch default branch if it is set |
| 164 | + if [[ -v CI_DEFAULT_BRANCH ]]; then |
| 165 | + git fetch origin "$CI_DEFAULT_BRANCH" |
| 166 | + refs="origin/$CI_DEFAULT_BRANCH" |
| 167 | + else |
| 168 | + refs="main" |
| 169 | + fi |
| 170 | +
|
| 171 | + commits=$(git rev-list "${refs}..") |
| 172 | + for commit in $commits; do |
54 | 173 | MSG="$(git show -s --format=%B "$commit")" |
55 | 174 | CODESPELL_RC="$(mktemp)" |
56 | 175 | git show "$commit:.codespellrc" > "$CODESPELL_RC" |
57 | 176 | if ! grep -q "Signed-off-by: " <<< "$MSG"; then |
58 | | - printf "Commit %s lacks \"Signed-off-by\" line.\n" "$commit" |
| 177 | + printf "⛔ Commit %s lacks \"Signed-off-by\" line.\n" "$commit" |
59 | 178 | printf "%s\n" \ |
60 | 179 | " Please use:" \ |
61 | 180 | " git rebase --signoff main && git push --force-with-lease" \ |
62 | 181 | " See https://developercertificate.org/ for more details." |
63 | 182 | exit 1; |
64 | 183 | elif ! codespell --config "$CODESPELL_RC" - <<< "$MSG"; then |
65 | | - printf "The spelling in commit %s needs improvement.\n" "$commit" |
| 184 | + printf "⛔ The spelling in commit %s needs improvement.\n" "$commit" |
| 185 | + exit 1; |
| 186 | + elif grep "WIP: " <<< "$MSG"; then |
| 187 | + printf "⛔ Commit %s includes a 'WIP' marker which should be removed.\n" "$commit" |
66 | 188 | exit 1; |
67 | 189 | else |
68 | | - printf "Commit %s is good.\n" "$commit" |
| 190 | + printf "✅ Commit %s is good.\n" "$commit" |
69 | 191 | fi |
70 | 192 | done |
71 | 193 |
|
| 194 | +# Lint justfile recipes |
| 195 | +[group('ci')] |
| 196 | +[metadata('pacman', 'nodejs', 'shellcheck')] |
| 197 | +recipes: |
| 198 | + #!/usr/bin/env bash |
| 199 | + set -euo pipefail |
| 200 | + T=$(mktemp -d) |
| 201 | + node scripts/ci/export-shell.ts "$T" |
| 202 | + for file in "$T"/*.sh; do |
| 203 | + echo "Checking $file..." |
| 204 | + shellcheck --shell bash "$file" |
| 205 | + done |
| 206 | +
|
72 | 207 | # Fixes common issues. Files need to be git add'ed |
73 | 208 | fix: |
74 | 209 | #!/usr/bin/env bash |
|
85 | 220 | # try to fix clippy issues |
86 | 221 | cargo clippy --fix --allow-staged |
87 | 222 |
|
88 | | - # fmt must be last as clippy's changes may break formatting |
| 223 | + # fmt must be last as clippy changes may break formatting |
89 | 224 | cargo +nightly fmt --all |
0 commit comments