diff --git a/.config/flakebox/.gitignore b/.config/flakebox/.gitignore new file mode 100644 index 0000000..3fec32c --- /dev/null +++ b/.config/flakebox/.gitignore @@ -0,0 +1 @@ +tmp/ diff --git a/.config/flakebox/bin/flakebox-in-each-cargo-workspace b/.config/flakebox/bin/flakebox-in-each-cargo-workspace new file mode 100755 index 0000000..addfa85 --- /dev/null +++ b/.config/flakebox/bin/flakebox-in-each-cargo-workspace @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +# Run a given command in every directory that contains cargo workspace +# Right now it just scans for `Cargo.lock` + +set -euo pipefail + +find . -name Cargo.lock | while read -r path ; do + ( + cd "$(dirname "$path")" + "$@" + ) +done diff --git a/.config/flakebox/id b/.config/flakebox/id new file mode 100644 index 0000000..a9a3750 --- /dev/null +++ b/.config/flakebox/id @@ -0,0 +1 @@ +d752795b0c685fbc54be836b6c4230b20d3c1bfd43afcdfec1a451f875585b13988fe9a84e24c816432ab5daca57d9960eaf47239a457855ead99210d25b80a7 diff --git a/.config/flakebox/shellHook.sh b/.config/flakebox/shellHook.sh new file mode 100644 index 0000000..136ed52 --- /dev/null +++ b/.config/flakebox/shellHook.sh @@ -0,0 +1,32 @@ +#!/usr/bin/env bash +root="$(git rev-parse --show-toplevel)" +dot_git="$(git rev-parse --git-common-dir)" +if [[ ! -d "${dot_git}/hooks" ]]; then mkdir -p "${dot_git}/hooks"; fi +# fix old bug +rm -f "${dot_git}/hooks/comit-msg" +rm -f "${dot_git}/hooks/commit-msg" +ln -sf "${root}/misc/git-hooks/commit-msg" "${dot_git}/hooks/commit-msg" + +root="$(git rev-parse --show-toplevel)" +dot_git="$(git rev-parse --git-common-dir)" +if [[ ! -d "${dot_git}/hooks" ]]; then mkdir -p "${dot_git}/hooks"; fi +# fix old bug +rm -f "${dot_git}/hooks/pre-comit" +rm -f "${dot_git}/hooks/pre-commit" +ln -sf "${root}/misc/git-hooks/pre-commit" "${dot_git}/hooks/pre-commit" + +# set template +git config commit.template misc/git-hooks/commit-template.txt + +if ! flakebox lint --silent; then + >&2 echo "ℹ️ Project recommendations detected. Run 'flakebox lint' for more info." +fi + +if [ -n "${DIRENV_IN_ENVRC:-}" ]; then + # and not set DIRENV_LOG_FORMAT + if [ -n "${DIRENV_LOG_FORMAT:-}" ]; then + >&2 echo "💡 Set 'DIRENV_LOG_FORMAT=\"\"' in your shell environment variables for a cleaner output of direnv" + fi +fi + +>&2 echo "💡 Run 'just' for a list of available 'just ...' helper recipes" diff --git a/.github/workflows/flakebox-ci.yml b/.github/workflows/flakebox-ci.yml new file mode 100644 index 0000000..c5d3852 --- /dev/null +++ b/.github/workflows/flakebox-ci.yml @@ -0,0 +1,80 @@ +# THIS FILE IS AUTOGENERATED FROM FLAKEBOX CONFIGURATION + +jobs: + build: + name: Build + runs-on: ${{ matrix.runs-on }} + steps: + - uses: actions/checkout@v4 + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@v4 + - name: Magic Nix Cache + uses: DeterminateSystems/magic-nix-cache-action@v2 + - name: Build on ${{ matrix.host }} + run: nix build -L .#ci.mindmap + strategy: + matrix: + host: + - macos-x86_64 + - macos-aarch64 + - linux + include: + - host: linux + runs-on: ubuntu-latest + timeout: 60 + - host: macos-x86_64 + runs-on: macos-12 + timeout: 60 + - host: macos-aarch64 + runs-on: macos-14 + timeout: 60 + timeout-minutes: ${{ matrix.timeout }} + flake: + name: Flake self-check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Check Nix flake inputs + uses: DeterminateSystems/flake-checker-action@v5 + with: + fail-mode: true + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@v4 + - name: Magic Nix Cache + uses: DeterminateSystems/magic-nix-cache-action@v2 + - name: Cargo Cache + uses: actions/cache@v3 + with: + key: ${{ runner.os }}-${{ hashFiles('Cargo.lock') }} + path: ~/.cargo + - name: Commit Check + run: '# run the same check that git `pre-commit` hook does + + nix develop --ignore-environment .#lint --command ./misc/git-hooks/pre-commit + + ' +name: CI +'on': + merge_group: + branches: + - master + - main + pull_request: + branches: + - master + - main + push: + branches: + - master + - main + tags: + - v* + workflow_dispatch: {} + + +# THIS FILE IS AUTOGENERATED FROM FLAKEBOX CONFIGURATION diff --git a/.github/workflows/flakebox-flakehub-publish.yml b/.github/workflows/flakebox-flakehub-publish.yml new file mode 100644 index 0000000..5b81f6e --- /dev/null +++ b/.github/workflows/flakebox-flakehub-publish.yml @@ -0,0 +1,35 @@ +# THIS FILE IS AUTOGENERATED FROM FLAKEBOX CONFIGURATION + +jobs: + flakehub-publish: + permissions: + contents: read + id-token: write + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ (inputs.tag != null) && format('refs/tags/{0}', inputs.tag) || '' + }} + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@v4 + - name: Flakehub Push + uses: DeterminateSystems/flakehub-push@main + with: + name: ${{ github.repository }} + tag: ${{ inputs.tag }} + visibility: public +name: Publish to Flakehub +'on': + push: + tags: + - v?[0-9]+.[0-9]+.[0-9]+* + workflow_dispatch: + inputs: + tags: + description: The existing tag to publish to FlakeHub + required: true + type: string + + +# THIS FILE IS AUTOGENERATED FROM FLAKEBOX CONFIGURATION diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..35cd282 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,5 @@ +group_imports = "StdExternalCrate" +wrap_comments = true +format_code_in_doc_comments = true +imports_granularity = "Module" +edition = "2021" diff --git a/justfile b/justfile new file mode 100644 index 0000000..022c1f6 --- /dev/null +++ b/justfile @@ -0,0 +1,119 @@ +# THIS FILE IS AUTOGENERATED FROM FLAKEBOX CONFIGURATION + +alias b := build +alias c := check +alias t := test + + +[private] +default: + @just --list + + +# run `cargo build` on everything +build *ARGS="--workspace --all-targets": + #!/usr/bin/env bash + set -euo pipefail + if [ ! -f Cargo.toml ]; then + cd {{invocation_directory()}} + fi + cargo build {{ARGS}} + + +# run `cargo check` on everything +check *ARGS="--workspace --all-targets": + #!/usr/bin/env bash + set -euo pipefail + if [ ! -f Cargo.toml ]; then + cd {{invocation_directory()}} + fi + cargo check {{ARGS}} + + +# run all checks recommended before opening a PR +final-check: lint clippy + #!/usr/bin/env bash + set -euo pipefail + if [ ! -f Cargo.toml ]; then + cd {{invocation_directory()}} + fi + cargo test --doc + just test + + +# run code formatters +format: + #!/usr/bin/env bash + set -euo pipefail + if [ ! -f Cargo.toml ]; then + cd {{invocation_directory()}} + fi + cargo fmt --all + nixpkgs-fmt $(echo **.nix) + + +# run lints (git pre-commit hook) +lint: + #!/usr/bin/env bash + set -euo pipefail + env NO_STASH=true $(git rev-parse --git-common-dir)/hooks/pre-commit + + +# run tests +test: build + #!/usr/bin/env bash + set -euo pipefail + if [ ! -f Cargo.toml ]; then + cd {{invocation_directory()}} + fi + cargo test + + +# run and restart on changes +watch *ARGS="-x run": + #!/usr/bin/env bash + set -euo pipefail + if [ ! -f Cargo.toml ]; then + cd {{invocation_directory()}} + fi + env RUST_LOG=${RUST_LOG:-debug} cargo watch {{ARGS}} + + +# run `cargo clippy` on everything +clippy *ARGS="--locked --offline --workspace --all-targets": + cargo clippy {{ARGS}} -- --deny warnings --allow deprecated + +# run `cargo clippy --fix` on everything +clippy-fix *ARGS="--locked --offline --workspace --all-targets": + cargo clippy {{ARGS}} --fix + + +# run `semgrep` +semgrep: + env SEMGREP_ENABLE_VERSION_CHECK=0 \ + semgrep --error --no-rewrite-rule-ids --config .config/semgrep.yaml + + +# check typos +[no-exit-message] +typos *PARAMS: + #!/usr/bin/env bash + set -eo pipefail + + export FLAKEBOX_GIT_LS + FLAKEBOX_GIT_LS="$(git ls-files)" + export FLAKEBOX_GIT_LS_TEXT + FLAKEBOX_GIT_LS_TEXT="$(echo "$FLAKEBOX_GIT_LS" | grep -v -E "^db/|\.(png|ods|jpg|jpeg|woff2|keystore|wasm|ttf|jar|ico)\$")" + + + if ! echo "$FLAKEBOX_GIT_LS_TEXT" | typos {{PARAMS}} --file-list - --force-exclude ; then + >&2 echo "Typos found: Valid new words can be added to '.typos.toml'" + return 1 + fi + +# fix all typos +[no-exit-message] +typos-fix-all: + just typos -w + +# THIS FILE IS AUTOGENERATED FROM FLAKEBOX CONFIGURATION diff --git a/misc/git-hooks/commit-msg b/misc/git-hooks/commit-msg new file mode 100755 index 0000000..b4fe09a --- /dev/null +++ b/misc/git-hooks/commit-msg @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Sanitize file first, by removing leading lines that are empty or start with a hash, +# as `convco` currently does not do it automatically (but git will) +# TODO: next release of convco should be able to do it automatically +MESSAGE="$( + while read -r line ; do + # skip any initial comments (possibly from previous run) + if [ -z "${body_detected:-}" ] && { [[ "$line" =~ ^#.*$ ]] || [ "$line" == "" ]; }; then + continue + fi + body_detected="true" + + echo "$line" + done < "$1" +)" + +# convco fails on fixup!, so remove fixup! prefix +MESSAGE="${MESSAGE#fixup! }" +if ! convco check --from-stdin <<<"$MESSAGE" ; then + >&2 echo "Please follow conventional commits(https://www.conventionalcommits.org)" + >&2 echo "Use git recommit to fix your commit" + exit 1 +fi diff --git a/misc/git-hooks/commit-template.txt b/misc/git-hooks/commit-template.txt new file mode 100644 index 0000000..a9aff55 --- /dev/null +++ b/misc/git-hooks/commit-template.txt @@ -0,0 +1,2 @@ + +# Explain *why* this change is being made width limit ->| diff --git a/misc/git-hooks/pre-commit b/misc/git-hooks/pre-commit new file mode 100755 index 0000000..7959e97 --- /dev/null +++ b/misc/git-hooks/pre-commit @@ -0,0 +1,170 @@ +#!/usr/bin/env bash + +set -euo pipefail + +set +e +git diff-files --quiet +is_unclean=$? +set -e + +# Revert `git stash` on exit +function revert_git_stash { + >&2 echo "Unstashing uncommitted changes..." + git stash pop -q +} + +# Stash pending changes and revert them when script ends +if [ -z "${NO_STASH:-}" ] && [ $is_unclean -ne 0 ]; then + >&2 echo "Stashing uncommitted changes..." + GIT_LITERAL_PATHSPECS=0 git stash -q --keep-index + trap revert_git_stash EXIT +fi + +export FLAKEBOX_GIT_LS +FLAKEBOX_GIT_LS="$(git ls-files)" +export FLAKEBOX_GIT_LS_TEXT +FLAKEBOX_GIT_LS_TEXT="$(echo "$FLAKEBOX_GIT_LS" | grep -v -E "\.(png|ods|jpg|jpeg|woff2|keystore|wasm|ttf|jar|ico|gif)\$")" + + +function check_nothing() { + true +} +export -f check_nothing + +function check_cargo_fmt() { + set -euo pipefail + + flakebox-in-each-cargo-workspace cargo fmt --all --check + +} +export -f check_cargo_fmt + +function check_cargo_lock() { + set -euo pipefail + + # https://users.rust-lang.org/t/check-if-the-cargo-lock-is-up-to-date-without-building-anything/91048/5 + flakebox-in-each-cargo-workspace cargo update --workspace --locked + +} +export -f check_cargo_lock + +function check_leftover_dbg() { + set -euo pipefail + + errors="" + for path in $(echo "$FLAKEBOX_GIT_LS_TEXT" | grep '.*\.rs'); do + if grep 'dbg!(' "$path" > /dev/null; then + >&2 echo "$path contains dbg! macro" + errors="true" + fi + done + + if [ -n "$errors" ]; then + >&2 echo "Fix the problems above or use --no-verify" 1>&2 + return 1 + fi + +} +export -f check_leftover_dbg + +function check_semgrep() { + set -euo pipefail + + # semgrep is not available on MacOS + if ! command -v semgrep > /dev/null ; then + >&2 echo "Skipping semgrep check: not available" + return 0 + fi + + if [ ! -f .config/semgrep.yaml ] ; then + >&2 echo "Skipping semgrep check: .config/semgrep.yaml doesn't exist" + return 0 + fi + + if [ ! -s .config/semgrep.yaml ] ; then + >&2 echo "Skipping semgrep check: .config/semgrep.yaml empty" + return 0 + fi + + env SEMGREP_ENABLE_VERSION_CHECK=0 \ + semgrep -q --error --no-rewrite-rule-ids --config .config/semgrep.yaml + +} +export -f check_semgrep + +function check_shellcheck() { + set -euo pipefail + + for path in $(echo "$FLAKEBOX_GIT_LS_TEXT" | grep -E '.*\.sh$'); do + shellcheck --severity=warning "$path" + done + +} +export -f check_shellcheck + +function check_trailing_newline() { + set -euo pipefail + + errors="" + for path in $(echo "$FLAKEBOX_GIT_LS_TEXT"); do + + # extra branches for clarity + if [ ! -s "$path" ]; then + # echo "$path is empty" + true + elif [ -z "$(tail -c 1 < "$path")" ]; then + # echo "$path ends with a newline or with a null byte" + true + else + >&2 echo "$path doesn't end with a newline" 1>&2 + errors="true" + fi + done + + if [ -n "$errors" ]; then + >&2 echo "Fix the problems above or use --no-verify" 1>&2 + return 1 + fi + +} +export -f check_trailing_newline + +function check_trailing_whitespace() { + set -euo pipefail + + rev="HEAD" + if ! git rev-parse -q 1>/dev/null HEAD 2>/dev/null ; then + >&2 echo "Warning: no commits yet, checking against --root" + rev="--root" + fi + if ! git diff --check $rev ; then + >&2 echo "Trailing whitespace detected. Please remove them before committing." + return 1 + fi + +} +export -f check_trailing_whitespace + +function check_typos() { + set -euo pipefail + + if ! echo "$FLAKEBOX_GIT_LS_TEXT" | typos --file-list - --force-exclude ; then + >&2 echo "Typos found: Valid new words can be added to '.typos.toml'" + return 1 + fi + +} +export -f check_typos + +parallel \ + --nonotice \ +::: \ + check_cargo_fmt \ + check_cargo_lock \ + check_leftover_dbg \ + check_semgrep \ + check_shellcheck \ + check_trailing_newline \ + check_trailing_whitespace \ + check_typos \ + check_nothing