Skip to content

Commit e13ab9b

Browse files
committed
Add common project files
Signed-off-by: Wiktor Kwapisiewicz <wiktor@metacode.biz>
1 parent 332be9a commit e13ab9b

File tree

17 files changed

+551
-31
lines changed

17 files changed

+551
-31
lines changed

.clippy.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
doc-valid-idents = ["OpenPGP"]

.config/git_allowed_signers

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
wiktor@metacode.biz ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQv2RJtGurpNLWyiGz9sSuX8agzV98gHW2ZG/7vFkIQrPlaYsd/OH1z7BZNeCHs5vcoq6c2Eh5s6a0vcH4n181TKfjgpbq4t7OFNygWBJplXIZvIlsY//UCxfp5ZdKWJfrYUu/0HeEv5r/7ZcpwF/omC97aM0ipmAeQ8QEGLfgGW427ATa/r2SFwK/4h0C+BTUnMj/YC/4KI/MPWA6x7RdAw+RbVjZd4kT2ZPXcUdruSqDQ4vSP/b8gERv1IjWUn+HHteRJgR2SwNmsuuT/Ko3FRFfXxXPV2yMEvUY2+DoU781VhZJl0aqpW5bIhlK5VE5rGvmMuE5S7XwYDM9V0Wl

.config/nextest.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[profile.ci.junit]
2+
path = "junit.xml"

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text eol=lf

.github/workflows/just.yml

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: Check
2+
3+
on: [pull_request]
4+
5+
jobs:
6+
generate-matrix:
7+
name: Generate jobs
8+
runs-on: ubuntu-latest
9+
outputs:
10+
jobs: ${{ steps.set-matrix.outputs.jobs }}
11+
steps:
12+
- uses: actions/checkout@v6
13+
- uses: taiki-e/install-action@just
14+
- run: node ./scripts/ci/create-github-checks.js
15+
id: set-matrix
16+
17+
run-task:
18+
name: ${{ matrix.jobs.doc }}
19+
runs-on: ubuntu-latest
20+
needs: [generate-matrix]
21+
container:
22+
image: archlinux
23+
strategy:
24+
fail-fast: false
25+
max-parallel: 6
26+
matrix: ${{fromJSON(needs.generate-matrix.outputs.jobs)}}
27+
28+
steps:
29+
- uses: actions/checkout@v6
30+
- run: pacman-key --init
31+
- run: pacman -Sy --needed --noconfirm archlinux-keyring
32+
- run: pacman -Syu --needed --noconfirm ${{ matrix.jobs.packages }}
33+
- run: just --justfile ${{ matrix.jobs.file }} ${{ matrix.jobs.name }}

.justfile

Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
1+
#!/usr/bin/env -S just --working-directory . --justfile
2+
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"
7+
8+
# Perform all checks
9+
[parallel]
10+
check: spell fmt doc lints deps unused-deps recipes test integration-test
11+
12+
# Check spelling
13+
[group('ci')]
14+
[metadata('pacman', 'codespell')]
15+
spell:
16+
codespell
17+
18+
# Check source code formatting
19+
[group('ci')]
20+
[metadata('pacman', 'rustup')]
21+
fmt:
22+
just --unstable --fmt --check
23+
# We're using nightly to properly group imports, see .rustfmt.toml
24+
rustup component add --toolchain nightly rustfmt
25+
cargo +nightly fmt --quiet --all -- --check
26+
27+
# Lint the source code
28+
[group('ci')]
29+
[metadata('gitlabci-job', '{"artifacts":{"when":"always","paths":["target/clippy"]}}')]
30+
[metadata('pacman', 'rust', 'python')]
31+
lints:
32+
#!/usr/bin/bash
33+
set -euo pipefail
34+
35+
if [ "${CI:-}" = "true" ]; then
36+
ARGS=(--message-format=json)
37+
else
38+
ARGS=()
39+
fi
40+
41+
mkdir -p target
42+
{{ clippy }} "${ARGS[@]}" -- {{ clippy_args }} | tee target/clippy
43+
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
62+
63+
if [ "${CI:-}" = "true" ]; then
64+
ARGS=(--format json)
65+
else
66+
ARGS=()
67+
fi
68+
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
71+
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', 'python')]
76+
unused-deps:
77+
#!/usr/bin/bash
78+
set -euo pipefail
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+
[metadata('pacman', 'cargo-nextest')]
92+
test:
93+
#!/usr/bin/bash
94+
set -euxo pipefail
95+
96+
if [ "${CI:-}" = "true" ]; then
97+
PROFILE=ci
98+
else
99+
PROFILE=default
100+
fi
101+
102+
cargo +nightly nextest run {{ nextest_args }} --profile "$PROFILE"
103+
cargo +nightly nextest run --no-default-features {{ nextest_args }} --profile "$PROFILE"
104+
105+
# Run integration tests
106+
[metadata('pacman', 'git', 'jq', 'openssh', 'tangler', 'tree')]
107+
integration-test:
108+
#!/usr/bin/bash
109+
set -euo pipefail
110+
cargo +nightly build --locked
111+
target=$(cargo +nightly metadata --format-version 1 | jq --raw-output '.target_directory')
112+
tangler sh < README.md | sed --quiet --regexp-extended 's/^\$ (.*)/\1/p' | PATH="$target/debug:$PATH" bash -euxo pipefail -
113+
114+
# Report on all tests
115+
[group('ci')]
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', 'python')]
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', 'python')]
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
173+
MSG="$(git show -s --format=%B "$commit")"
174+
CODESPELL_RC="$(mktemp)"
175+
git show "$commit:.codespellrc" > "$CODESPELL_RC"
176+
if ! grep -q "Signed-off-by: " <<< "$MSG"; then
177+
printf "⛔ Commit %s lacks \"Signed-off-by\" line.\n" "$commit"
178+
printf "%s\n" \
179+
" Please use:" \
180+
" git rebase --signoff main && git push --force-with-lease" \
181+
" See https://developercertificate.org/ for more details."
182+
exit 1;
183+
elif ! codespell --config "$CODESPELL_RC" - <<< "$MSG"; then
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"
188+
exit 1;
189+
else
190+
printf "✅ Commit %s is good.\n" "$commit"
191+
fi
192+
done
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+
207+
# Fixes common issues. Files need to be git add'ed
208+
fix:
209+
#!/usr/bin/env bash
210+
set -euo pipefail
211+
if ! git diff-files --quiet ; then
212+
echo "Working tree has changes. Please stage them: git add ."
213+
exit 1
214+
fi
215+
216+
codespell --write-changes
217+
just --unstable --fmt
218+
# try to fix rustc issues
219+
cargo fix --allow-staged
220+
# try to fix clippy issues
221+
cargo clippy --fix --allow-staged
222+
223+
# fmt must be last as clippy changes may break formatting
224+
cargo +nightly fmt --all

.rustfmt.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# CHECK https://github.com/rust-lang/rustfmt/issues/5083
2+
group_imports = "StdExternalCrate"
3+
# CHECK https://github.com/rust-lang/rustfmt/issues/3348
4+
format_code_in_doc_comments = true
5+
reorder_imports = true

CONTRIBUTING.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Contributing
2+
3+
Thanks for taking the time to contribute to this project!
4+
5+
All changes need to:
6+
7+
- pass basic checks, including tests, formatting and lints,
8+
- be signed-off.
9+
10+
## Basic checks
11+
12+
We are using standard Rust ecosystem tools including `rustfmt` and `clippy` with one minor difference.
13+
Due to a couple of `rustfmt` features being available only in nightly (see the `.rustfmt.toml` file) nightly `rustfmt` is necessary.
14+
15+
All of these details are captured in a `.justfile` and can be checked by running [`just`](https://just.systems/) (just version 1.42.0 is required due to support for extended attributes).
16+
17+
To run all checks locally before sending them to CI you can set your git hooks directory:
18+
19+
```sh
20+
git config core.hooksPath scripts/hooks/
21+
```
22+
23+
## Developer Certificate of Origin
24+
25+
The sign-off is a simple line at the end of the git commit message, which certifies that you wrote it or otherwise have the right to pass it on as a open-source patch.
26+
27+
The rules are pretty simple: if you can [certify the below][DCO]:
28+
29+
```
30+
Developer's Certificate of Origin 1.1
31+
32+
By making a contribution to this project, I certify that:
33+
34+
(a) The contribution was created in whole or in part by me and I
35+
have the right to submit it under the open source license
36+
indicated in the file; or
37+
38+
(b) The contribution is based upon previous work that, to the best
39+
of my knowledge, is covered under an appropriate open source
40+
license and I have the right under that license to submit that
41+
work with modifications, whether created in whole or in part
42+
by me, under the same open source license (unless I am
43+
permitted to submit under a different license), as indicated
44+
in the file; or
45+
46+
(c) The contribution was provided directly to me by some other
47+
person who certified (a), (b) or (c) and I have not modified
48+
it.
49+
50+
(d) I understand and agree that this project and the contribution
51+
are public and that a record of the contribution (including all
52+
personal information I submit with it, including my sign-off) is
53+
maintained indefinitely and may be redistributed consistent with
54+
this project or the open source license(s) involved.
55+
```
56+
57+
then you just add a line saying
58+
59+
Signed-off-by: Random J Developer <random@developer.example.org>
60+
61+
using your name.
62+
63+
If you set your `user.name` and `user.email`, you can sign your commit automatically with [`git commit --signoff`][GSO].
64+
65+
To sign-off your last commit:
66+
67+
git commit --amend --signoff
68+
69+
[DCO]: https://developercertificate.org
70+
[GSO]: https://git-scm.com/docs/git-commit#git-commit---signoff
71+
72+
If you want to fix multiple commits use:
73+
74+
git rebase --signoff main
75+
76+
To check if your commits are correctly signed-off locally use `just commits`.

Cargo.lock

Lines changed: 0 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)