Skip to content

Commit 38f9f86

Browse files
committed
Rework CI and justfile
* Update just recipes to be more standardized and easier to adapt to any other project * update dependabot settings * update CI steps
1 parent 2fae716 commit 38f9f86

5 files changed

Lines changed: 179 additions & 47 deletions

File tree

.github/dependabot.yml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,34 @@
11
version: 2
22
updates:
3+
34
# Maintain dependencies for GitHub Actions
4-
- package-ecosystem: "github-actions"
5+
- package-ecosystem: github-actions
56
directory: "/"
67
schedule:
7-
interval: "weekly"
8+
interval: weekly
9+
groups:
10+
all-actions-version-updates:
11+
applies-to: version-updates
12+
patterns:
13+
- "*"
14+
all-actions-security-updates:
15+
applies-to: security-updates
16+
patterns:
17+
- "*"
18+
19+
# Update Rust dependencies
820
- package-ecosystem: cargo
921
directory: "/"
1022
schedule:
1123
interval: daily
1224
time: "02:00"
1325
open-pull-requests-limit: 10
26+
groups:
27+
all-cargo-version-updates:
28+
applies-to: version-updates
29+
patterns:
30+
- "*"
31+
all-cargo-security-updates:
32+
applies-to: security-updates
33+
patterns:
34+
- "*"

.github/workflows/ci.yml

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,40 +22,69 @@ jobs:
2222
name: Test
2323
runs-on: ubuntu-latest
2424
steps:
25+
- uses: actions/checkout@v4
26+
- if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'
27+
uses: Swatinem/rust-cache@v2
2528
- uses: taiki-e/install-action@v2
2629
with: { tool: just }
27-
- uses: actions/checkout@v4
28-
- name: Ensure this crate has not yet been published (on release)
29-
if: github.event_name == 'release'
30+
- if: github.event_name == 'release'
31+
name: Ensure this crate has not yet been published (on release)
3032
run: just check-if-published
31-
- uses: Swatinem/rust-cache@v2
32-
if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'
3333
- run: just ci-test
3434
- name: Check semver
3535
uses: obi1kenobi/cargo-semver-checks-action@v2
3636

37-
msrv:
37+
test-msrv:
3838
name: Test MSRV
3939
runs-on: ubuntu-latest
4040
steps:
41+
- uses: actions/checkout@v4
42+
- if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'
43+
uses: Swatinem/rust-cache@v2
4144
- uses: taiki-e/install-action@v2
4245
with: { tool: just }
46+
- name: Read MSRV
47+
id: msrv
48+
run: echo "value=$(just get-msrv)" >> $GITHUB_OUTPUT
49+
- name: Install MSRV Rust ${{ steps.msrv.outputs.value }}
50+
uses: dtolnay/rust-toolchain@stable
51+
with:
52+
toolchain: ${{ steps.msrv.outputs.value }}
53+
- run: just ci_mode=0 ci-test-msrv # Ignore warnings in MSRV
54+
55+
coverage:
56+
name: Code Coverage
57+
if: github.event_name != 'release'
58+
runs-on: ubuntu-latest
59+
steps:
4360
- uses: actions/checkout@v4
4461
- uses: Swatinem/rust-cache@v2
45-
if: github.event_name != 'release' && github.event_name != 'workflow_dispatch'
46-
- name: Read crate metadata
47-
id: metadata
48-
run: echo "rust-version=$(sed -ne 's/rust-version *= *\"\(.*\)\"/\1/p' Cargo.toml)" >> $GITHUB_OUTPUT
49-
- name: Install Rust
50-
uses: dtolnay/rust-toolchain@stable
62+
- uses: taiki-e/install-action@v2
63+
with: { tool: 'just,cargo-llvm-cov' }
64+
- name: Generate code coverage
65+
run: just ci-coverage
66+
- name: Upload coverage to Codecov
67+
uses: codecov/codecov-action@v5
5168
with:
52-
toolchain: ${{ steps.metadata.outputs.rust-version }}
53-
- run: just ci-test-msrv
69+
token: ${{ secrets.CODECOV_TOKEN }}
70+
files: target/llvm-cov/codecov.info
71+
fail_ci_if_error: true
5472

55-
publish:
73+
# This job checks if any of the previous jobs failed or were canceled.
74+
# This approach also allows some jobs to be skipped if they are not needed.
75+
ci-passed:
76+
if: always()
77+
needs: [ test, test-msrv ]
78+
runs-on: ubuntu-latest
79+
steps:
80+
- if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
81+
run: exit 1
82+
83+
release:
84+
# Some dependencies of the `ci-passed` job might be skipped, but we still want to run if the `ci-passed` job succeeded.
85+
if: always() && startsWith(github.ref, 'refs/tags/') && needs.ci-passed.result == 'success'
5686
name: Publish to crates.io
57-
if: startsWith(github.ref, 'refs/tags/')
58-
needs: [ test, msrv ]
87+
needs: [ ci-passed ]
5988
runs-on: ubuntu-latest
6089
steps:
6190
- uses: actions/checkout@v4

.github/workflows/dependabot.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
steps:
1111
- name: Dependabot metadata
1212
id: metadata
13-
uses: dependabot/fetch-metadata@v2.3.0
13+
uses: dependabot/fetch-metadata@v2
1414
with:
1515
github-token: "${{ secrets.GITHUB_TOKEN }}"
1616
- name: Approve Dependabot PRs

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,5 @@ Licensed under either of
7676

7777
Unless you explicitly state otherwise, any contribution intentionally
7878
submitted for inclusion in the work by you, as defined in the
79-
Apache-2.0 license, shall be dual licensed as above, without any
79+
Apache-2.0 license, shall be dual-licensed as above, without any
8080
additional terms or conditions.

justfile

Lines changed: 108 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,37 @@
11
#!/usr/bin/env just --justfile
22

3+
main_crate := 'tilejson'
4+
features_flag := '--all-features'
5+
6+
# if running in CI, treat warnings as errors by setting RUSTFLAGS and RUSTDOCFLAGS to '-D warnings' unless they are already set
7+
# Use `CI=true just ci-test` to run the same tests as in GitHub CI.
8+
# Use `just env-info` to see the current values of RUSTFLAGS and RUSTDOCFLAGS
9+
ci_mode := if env('CI', '') != '' {'1'} else {''}
10+
export RUSTFLAGS := env('RUSTFLAGS', if ci_mode == '1' {'-D warnings'} else {''})
11+
export RUSTDOCFLAGS := env('RUSTDOCFLAGS', if ci_mode == '1' {'-D warnings'} else {''})
12+
export RUST_BACKTRACE := env('RUST_BACKTRACE', if ci_mode == '1' {'1'} else {''})
13+
314
@_default:
4-
just --list
15+
{{just_executable()}} --list
16+
17+
# Build the project
18+
build:
19+
cargo build --workspace --all-targets {{features_flag}}
520

6-
# Quick compile
21+
# Quick compile without building a binary
722
check:
8-
RUSTFLAGS='-D warnings' cargo check
23+
cargo check --workspace --all-targets {{features_flag}}
924

1025
# Verify that the current version of the crate is not the same as the one published on crates.io
11-
check-if-published:
26+
check-if-published: (assert-cmd 'jq')
1227
#!/usr/bin/env bash
13-
LOCAL_VERSION="$(grep '^version =' Cargo.toml | sed -E 's/version = "([^"]*)".*/\1/')"
14-
echo "Detected crate version: $LOCAL_VERSION"
15-
CRATE_NAME="$(grep '^name =' Cargo.toml | head -1 | sed -E 's/name = "(.*)"/\1/')"
16-
echo "Detected crate name: $CRATE_NAME"
28+
set -euo pipefail
29+
LOCAL_VERSION="$({{just_executable()}} get-crate-field version)"
30+
echo "Detected crate version: '$LOCAL_VERSION'"
31+
CRATE_NAME="$({{just_executable()}} get-crate-field name)"
32+
echo "Detected crate name: '$CRATE_NAME'"
1733
PUBLISHED_VERSION="$(cargo search ${CRATE_NAME} | grep "^${CRATE_NAME} =" | sed -E 's/.* = "(.*)".*/\1/')"
18-
echo "Published crate version: $PUBLISHED_VERSION"
34+
echo "Published crate version: '$PUBLISHED_VERSION'"
1935
if [ "$LOCAL_VERSION" = "$PUBLISHED_VERSION" ]; then
2036
echo "ERROR: The current crate version has already been published."
2137
exit 1
@@ -24,50 +40,116 @@ check-if-published:
2440
fi
2541
2642
# Run all tests as expected by CI
27-
ci-test: rust-info test-fmt clippy check test test-doc
43+
ci-test: env-info test-fmt clippy check test test-doc && assert-git-is-clean
2844

2945
# Run minimal subset of tests to ensure compatibility with MSRV
30-
ci-test-msrv: rust-info check test
46+
ci-test-msrv: env-info check test
3147

3248
# Clean all build artifacts
3349
clean:
3450
cargo clean
3551
rm -f Cargo.lock
3652

37-
# Run cargo clippy
38-
clippy:
39-
cargo clippy --workspace --all-targets -- -D warnings
53+
# Run cargo clippy to lint the code
54+
clippy *args:
55+
cargo clippy --workspace --all-targets {{features_flag}} {{args}}
56+
57+
# Generate code coverage report. Will install `cargo llvm-cov` if missing.
58+
coverage *args='--no-clean --open': (cargo-install 'cargo-llvm-cov')
59+
cargo llvm-cov --workspace --all-targets {{features_flag}} --include-build-script {{args}}
4060

4161
# Build and open code documentation
4262
docs:
4363
cargo doc --no-deps --open
4464

45-
# Run cargo fmt
65+
# Print environment info
66+
env-info:
67+
@echo "Running {{if ci_mode == '1' {'in CI mode'} else {'in dev mode'} }} on {{os()}} / {{arch()}}"
68+
{{just_executable()}} --version
69+
rustc --version
70+
cargo --version
71+
rustup --version
72+
@echo "RUSTFLAGS='$RUSTFLAGS'"
73+
@echo "RUSTDOCFLAGS='$RUSTDOCFLAGS'"
74+
75+
# Reformat all code `cargo fmt`. If nightly is available, use it for better results
4676
fmt:
47-
cargo +nightly fmt -- --config imports_granularity=Module,group_imports=StdExternalCrate
77+
#!/usr/bin/env bash
78+
set -euo pipefail
79+
if rustup component list --toolchain nightly | grep rustfmt &> /dev/null; then
80+
echo 'Reformatting Rust code using nightly Rust fmt to sort imports'
81+
cargo +nightly fmt --all -- --config imports_granularity=Module,group_imports=StdExternalCrate
82+
else
83+
echo 'Reformatting Rust with the stable cargo fmt. Install nightly with `rustup install nightly` for better results'
84+
cargo fmt --all
85+
fi
86+
87+
# Get any package's field from the metadata
88+
get-crate-field field package=main_crate:
89+
cargo metadata --format-version 1 | jq -r '.packages | map(select(.name == "{{package}}")) | first | .{{field}}'
90+
91+
# Get the minimum supported Rust version (MSRV) for the crate
92+
get-msrv: (get-crate-field 'rust_version')
4893

4994
# Find the minimum supported Rust version (MSRV) using cargo-msrv extension, and update Cargo.toml
50-
msrv:
51-
cargo msrv find --write-msrv
95+
msrv: (cargo-install 'cargo-msrv')
96+
cargo msrv find --write-msrv --ignore-lockfile {{features_flag}}
5297

53-
rust-info:
54-
rustc --version
55-
cargo --version
98+
# Check semver compatibility with prior published version. Install it with `cargo install cargo-semver-checks`
99+
semver *args: (cargo-install 'cargo-semver-checks')
100+
cargo semver-checks {{features_flag}} {{args}}
56101

57-
# Run all tests
102+
# Run all unit and integration tests
58103
test:
59-
RUSTFLAGS='-D warnings' cargo test
104+
cargo test --workspace --all-targets {{features_flag}}
60105

61106
# Test documentation
62107
test-doc:
63-
cargo test --doc
64-
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps
108+
cargo test --doc {{features_flag}}
109+
cargo doc {{features_flag}} --no-deps
65110

66111
# Test code formatting
67112
test-fmt:
68113
cargo fmt --all -- --check
69114

70-
# Update dependencies, including breaking changes
115+
# Find unused dependencies. Install it with `cargo install cargo-udeps`
116+
udeps: (cargo-install 'cargo-udeps')
117+
cargo +nightly udeps --workspace --all-targets {{features_flag}}
118+
119+
# Update all dependencies, including breaking changes. Requires nightly toolchain (install with `rustup install nightly`)
71120
update:
72121
cargo +nightly -Z unstable-options update --breaking
73122
cargo update
123+
124+
# Ensure that a certain command is available
125+
[private]
126+
assert-cmd command:
127+
@if ! type {{command}} > /dev/null; then \
128+
echo "Command '{{command}}' could not be found. Please make sure it has been installed on your computer." ;\
129+
exit 1 ;\
130+
fi
131+
132+
# Make sure the git repo has no uncommitted changes
133+
[private]
134+
assert-git-is-clean:
135+
@if [ -n "$(git status --untracked-files --porcelain)" ]; then \
136+
>&2 echo "ERROR: git repo is no longer clean. Make sure compilation and tests artifacts are in the .gitignore, and no repo files are modified." ;\
137+
>&2 echo "######### git status ##########" ;\
138+
git status ;\
139+
exit 1 ;\
140+
fi
141+
142+
# Check if a certain Cargo command is installed, and install it if needed
143+
[private]
144+
cargo-install $COMMAND $INSTALL_CMD='' *args='':
145+
#!/usr/bin/env bash
146+
set -euo pipefail
147+
if ! command -v $COMMAND > /dev/null; then
148+
if ! command -v cargo-binstall > /dev/null; then
149+
echo "$COMMAND could not be found. Installing it with cargo install ${INSTALL_CMD:-$COMMAND} --locked {{args}}"
150+
cargo install ${INSTALL_CMD:-$COMMAND} --locked {{args}}
151+
else
152+
echo "$COMMAND could not be found. Installing it with cargo binstall ${INSTALL_CMD:-$COMMAND} --locked {{args}}"
153+
cargo binstall ${INSTALL_CMD:-$COMMAND} --locked {{args}}
154+
fi
155+
fi

0 commit comments

Comments
 (0)