From b4611ff3d4cab85a429363739ea9fc72cec13833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Test=C3=A9?= Date: Mon, 6 Mar 2023 14:32:27 +0100 Subject: [PATCH 1/2] chore(ci): add benchmarks workflow Benchmarks would be run in GitHub Actions each time a commit is pushed into main and can also be triggered on demand on any branch. Benchmarks are executed in two flavors, one with AVX512 CPU instructions support and one without it. Results are parsed and then sent to Slab, Zama's CI bot, to be stored in database. --- .github/workflows/benchmark.yml | 115 +++++++++++++++++++ .github/workflows/start_benchmark.yml | 33 ++++++ Makefile | 109 ++++++++++++++++++ ci/benchmark_parser.py | 159 ++++++++++++++++++++++++++ ci/slab.toml | 9 ++ scripts/check_cargo_min_ver.sh | 60 ++++++++++ toolchain.txt | 1 + 7 files changed, 486 insertions(+) create mode 100644 .github/workflows/benchmark.yml create mode 100644 .github/workflows/start_benchmark.yml create mode 100644 Makefile create mode 100644 ci/benchmark_parser.py create mode 100644 ci/slab.toml create mode 100755 scripts/check_cargo_min_ver.sh create mode 100644 toolchain.txt diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 0000000..3349ad2 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,115 @@ +# Run FFT benchmarks on an AWS instance and return parsed results to Slab CI bot. +name: FFT benchmarks + +on: + workflow_dispatch: + inputs: + instance_id: + description: 'Instance ID' + type: string + instance_image_id: + description: 'Instance AMI ID' + type: string + instance_type: + description: 'Instance product type' + type: string + runner_name: + description: 'Action runner name' + type: string + request_id: + description: 'Slab request ID' + type: string + +env: + CARGO_TERM_COLOR: always + RESULTS_FILENAME: parsed_benchmark_results_${{ github.sha }}.json + +jobs: + run-fft-benchmarks: + name: Execute FFT benchmarks in EC2 + runs-on: ${{ github.event.inputs.runner_name }} + if: ${{ !cancelled() }} + steps: + - name: Instance configuration used + run: | + echo "IDs: ${{ inputs.instance_id }}" + echo "AMI: ${{ inputs.instance_image_id }}" + echo "Type: ${{ inputs.instance_type }}" + echo "Request ID: ${{ inputs.request_id }}" + + - name: Get benchmark date + run: | + echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}" + + - name: Checkout tfhe-rs repo with tags + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + with: + fetch-depth: 0 + + - name: Set up home + # "Install rust" step require root user to have a HOME directory which is not set. + run: | + echo "HOME=/home/ubuntu" >> "${GITHUB_ENV}" + + - name: Install rust + uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af + with: + toolchain: nightly + override: true + + - name: Run benchmarks + run: | + make bench + + - name: Parse results + run: | + COMMIT_DATE="$(git --no-pager show -s --format=%cd --date=iso8601-strict ${{ github.sha }})" + COMMIT_HASH="$(git describe --tags --dirty)" + python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \ + --database tfhe_rs_benchmarks \ + --hardware ${{ inputs.instance_type }} \ + --project-version "${COMMIT_HASH}" \ + --branch ${{ github.ref_name }} \ + --commit-date "${COMMIT_DATE}" \ + --bench-date "${{ env.BENCH_DATE }}" + + - name: Remove previous raw results + run: | + rm -rf target/criterion + + - name: Run benchmarks with AVX512 + run: | + make AVX512_SUPPORT=ON bench + + - name: Parse AVX512 results + run: | + python3 ./ci/benchmark_parser.py target/criterion ${{ env.RESULTS_FILENAME }} \ + --name-suffix avx512 \ + --append-results + + - name: Upload parsed results artifact + uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce + with: + name: ${{ github.sha }}_fft + path: ${{ env.RESULTS_FILENAME }} + + - name: Checkout Slab repo + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + with: + repository: zama-ai/slab + path: slab + token: ${{ secrets.CONCRETE_ACTIONS_TOKEN }} + + - name: Send data to Slab + shell: bash + run: | + echo "Computing HMac on downloaded artifact" + SIGNATURE="$(slab/scripts/hmac_calculator.sh ${{ env.RESULTS_FILENAME }} '${{ secrets.JOB_SECRET }}')" + echo "Sending results to Slab..." + curl -v -k \ + -H "Content-Type: application/json" \ + -H "X-Slab-Repository: ${{ github.repository }}" \ + -H "X-Slab-Command: store_data" \ + -H "X-Hub-Signature-256: sha256=${SIGNATURE}" \ + -d @${{ env.RESULTS_FILENAME }} \ + ${{ secrets.SLAB_URL }} diff --git a/.github/workflows/start_benchmark.yml b/.github/workflows/start_benchmark.yml new file mode 100644 index 0000000..9d00b44 --- /dev/null +++ b/.github/workflows/start_benchmark.yml @@ -0,0 +1,33 @@ +# Start all benchmark jobs on Slab CI bot. +name: Start all benchmarks + +on: + push: + branches: + - 'main' + workflow_dispatch: + +jobs: + start-benchmarks: + runs-on: ubuntu-latest + steps: + - name: Checkout Slab repo + uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + with: + repository: zama-ai/slab + path: slab + token: ${{ secrets.CONCRETE_ACTIONS_TOKEN }} + + - name: Start AWS job in Slab + shell: bash + # TODO: step result must be correlated to HTTP return code. + run: | + echo -n '{"command": "bench", "git_ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' > command.json + SIGNATURE="$(slab/scripts/hmac_calculator.sh command.json '${{ secrets.JOB_SECRET }}')" + curl -v -k \ + -H "Content-Type: application/json" \ + -H "X-Slab-Repository: ${{ github.repository }}" \ + -H "X-Slab-Command: start_aws" \ + -H "X-Hub-Signature-256: sha256=${SIGNATURE}" \ + -d @command.json \ + ${{ secrets.SLAB_URL }} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..c28c204 --- /dev/null +++ b/Makefile @@ -0,0 +1,109 @@ +SHELL:=$(shell /usr/bin/env which bash) +RS_CHECK_TOOLCHAIN:=$(shell cat toolchain.txt | tr -d '\n') +CARGO_RS_CHECK_TOOLCHAIN:=+$(RS_CHECK_TOOLCHAIN) +RS_BUILD_TOOLCHAIN:=nightly +CARGO_RS_BUILD_TOOLCHAIN:=+$(RS_BUILD_TOOLCHAIN) +MIN_RUST_VERSION:=1.65 +AVX512_SUPPORT?=OFF +# This is done to avoid forgetting it, we still precise the RUSTFLAGS in the commands to be able to +# copy paste the command in the terminal and change them if required without forgetting the flags +export RUSTFLAGS?=-C target-cpu=native + +ifeq ($(AVX512_SUPPORT),ON) + AVX512_FEATURE=nightly +else + AVX512_FEATURE= +endif + +.PHONY: rs_check_toolchain # Echo the rust toolchain used for checks +rs_check_toolchain: + @echo $(RS_CHECK_TOOLCHAIN) + +.PHONY: rs_build_toolchain # Echo the rust toolchain used for builds +rs_build_toolchain: + @echo $(RS_BUILD_TOOLCHAIN) + +.PHONY: install_rs_check_toolchain # Install the toolchain used for checks +install_rs_check_toolchain: + @rustup toolchain list | grep -q "$(RS_CHECK_TOOLCHAIN)" || \ + rustup toolchain install --profile default "$(RS_CHECK_TOOLCHAIN)" || \ + ( echo "Unable to install $(RS_CHECK_TOOLCHAIN) toolchain, check your rustup installation. \ + Rustup can be downloaded at https://rustup.rs/" && exit 1 ) + +.PHONY: install_rs_build_toolchain # Install the toolchain used for builds +install_rs_build_toolchain: + @( rustup toolchain list | grep -q "$(RS_BUILD_TOOLCHAIN)" && \ + ./scripts/check_cargo_min_ver.sh \ + --rust-toolchain "$(CARGO_RS_BUILD_TOOLCHAIN)" \ + --min-rust-version "$(MIN_RUST_VERSION)" ) || \ + rustup toolchain install --profile default "$(RS_BUILD_TOOLCHAIN)" || \ + ( echo "Unable to install $(RS_BUILD_TOOLCHAIN) toolchain, check your rustup installation. \ + Rustup can be downloaded at https://rustup.rs/" && exit 1 ) + +.PHONY: fmt # Format rust code +fmt: install_rs_check_toolchain + cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt + +.PHONT: check_fmt # Check rust code format +check_fmt: install_rs_check_toolchain + cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt --check + +.PHONY: clippy # Run clippy lints enabling the boolean, shortint, integer +clippy: install_rs_check_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \ + -- --no-deps -D warnings + +.PHONY: clippy_all_targets # Run clippy lints on all targets (benches, examples, etc.) +clippy_all_targets: + RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \ + -- --no-deps -D warnings + +.PHONY: clippy_all # Run all clippy targets +clippy_all: clippy clippy_all_targets + +.PHONY: build +build: install_rs_build_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) build --release \ + +.PHONY: test_default +test_default: install_rs_build_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release + +.PHONY: test_nightly +test_nightly: install_rs_build_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \ + --features=nightly + +.PHONY: test_no_default +test_no_default: install_rs_build_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \ + --no-default-features + +.PHONY: test_no_default_nightly +test_no_default_nightly: install_rs_build_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \ + --no-default-features \ + --features=nightly + +.PHONY: test_all +test_all: test_default test_nightly test_no_default test_no_default_nightly + +.PHONY: doc # Build rust doc +doc: install_rs_check_toolchain + RUSTDOCFLAGS="--html-in-header katex-header.html -Dwarnings" \ + cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" doc --no-deps + +.PHONY: bench # Run benchmarks +bench: install_rs_check_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \ + --features=$(AVX512_FEATURE) + +.PHONY: pcc # pcc stands for pre commit checks +pcc: check_fmt doc clippy_all + +.PHONY: conformance # Automatically fix problems that can be fixed +conformance: fmt + +.PHONY: help # Generate list of targets with descriptions +help: + @grep '^\.PHONY: .* #' Makefile | sed 's/\.PHONY: \(.*\) # \(.*\)/\1\t\2/' | expand -t30 | sort diff --git a/ci/benchmark_parser.py b/ci/benchmark_parser.py new file mode 100644 index 0000000..81fcdb1 --- /dev/null +++ b/ci/benchmark_parser.py @@ -0,0 +1,159 @@ +""" +benchmark_parser +---------------- + +Parse criterion benchmark or keys size results. +""" +import argparse +import csv +import pathlib +import json +import sys + + +parser = argparse.ArgumentParser() +parser.add_argument('results', + help='Location of criterion benchmark results directory.' + 'If the --key-size option is used, then the value would have to point to' + 'a CSV file.') +parser.add_argument('output_file', help='File storing parsed results') +parser.add_argument('-d', '--database', dest='database', + help='Name of the database used to store results') +parser.add_argument('-w', '--hardware', dest='hardware', + help='Hardware reference used to perform benchmark') +parser.add_argument('-V', '--project-version', dest='project_version', + help='Commit hash reference') +parser.add_argument('-b', '--branch', dest='branch', + help='Git branch name on which benchmark was performed') +parser.add_argument('--commit-date', dest='commit_date', + help='Timestamp of commit hash used in project_version') +parser.add_argument('--bench-date', dest='bench_date', + help='Timestamp when benchmark was run') +parser.add_argument('--name-suffix', dest='name_suffix', default='', + help='Suffix to append to each of the result test names') +parser.add_argument('--append-results', dest='append_results', action='store_true', + help='Append parsed results to an existing file') + + +def recursive_parse(directory, name_suffix=""): + """ + Parse all the benchmark results in a directory. It will attempt to parse all the files having a + .json extension at the top-level of this directory. + + :param directory: path to directory that contains raw results as :class:`pathlib.Path` + :param name_suffix: a :class:`str` suffix to apply to each test name found + + :return: :class:`list` of data points + """ + excluded_directories = ["child_generate", "fork", "parent_generate", "report"] + result_values = list() + for dire in directory.iterdir(): + if dire.name in excluded_directories or not dire.is_dir(): + continue + for subdir in dire.iterdir(): + if subdir.name != "new": + continue + + test_name = parse_benchmark_file(subdir) + for stat_name, value in parse_estimate_file(subdir).items(): + test_name_parts = list(filter(None, [test_name, stat_name, name_suffix])) + result_values.append({"value": value, "test": "_".join(test_name_parts)}) + + return result_values + + +def parse_benchmark_file(directory): + """ + Parse file containing details of the parameters used for a benchmark. + + :param directory: directory where a benchmark case results are located as :class:`pathlib.Path` + + :return: name of the test as :class:`str` + """ + raw_res = _parse_file_to_json(directory, "benchmark.json") + return raw_res["full_id"].replace(" ", "_") + + +def parse_estimate_file(directory): + """ + Parse file containing timing results for a benchmark. + + :param directory: directory where a benchmark case results are located as :class:`pathlib.Path` + + :return: :class:`dict` of data points + """ + raw_res = _parse_file_to_json(directory, "estimates.json") + return { + stat_name: raw_res[stat_name]["point_estimate"] + for stat_name in ("mean", "std_dev") + } + + +def _parse_file_to_json(directory, filename): + result_file = directory.joinpath(filename) + return json.loads(result_file.read_text()) + + +def dump_results(parsed_results, filename, input_args): + """ + Dump parsed results formatted as JSON to file. + + :param parsed_results: :class:`list` of data points + :param filename: filename for dump file as :class:`pathlib.Path` + :param input_args: CLI input arguments + """ + if input_args.append_results: + parsed_content = json.loads(filename.read_text()) + parsed_content["points"].extend(parsed_results) + filename.write_text(json.dumps(parsed_content)) + else: + filename.parent.mkdir(parents=True, exist_ok=True) + series = { + "database": input_args.database, + "hardware": input_args.hardware, + "project_version": input_args.project_version, + "branch": input_args.branch, + "insert_date": input_args.bench_date, + "commit_date": input_args.commit_date, + "points": parsed_results, + } + filename.write_text(json.dumps(series)) + + +def check_mandatory_args(input_args): + """ + Check for availability of required input arguments, the program will exit if one of them is + not present. If `append_results` flag is set, all the required arguments will be ignored. + + :param input_args: CLI input arguments + """ + if input_args.append_results: + return + + missing_args = list() + for arg_name in vars(input_args): + if arg_name in ["results_dir", "output_file", "name_suffix", "append_results"]: + continue + if not getattr(input_args, arg_name): + missing_args.append(arg_name) + + if missing_args: + for arg_name in missing_args: + print(f"Missing required argument: --{arg_name.replace('_', '-')}") + sys.exit(1) + + +if __name__ == "__main__": + args = parser.parse_args() + check_mandatory_args(args) + + raw_results = pathlib.Path(args.results) + print("Parsing benchmark results... ") + results = recursive_parse(raw_results, args.name_suffix) + print("Parsing results done") + + output_file = pathlib.Path(args.output_file) + print(f"Dump parsed results into '{output_file.resolve()}' ... ", end="") + dump_results(results, output_file, args) + + print("Done") diff --git a/ci/slab.toml b/ci/slab.toml new file mode 100644 index 0000000..cd67d3c --- /dev/null +++ b/ci/slab.toml @@ -0,0 +1,9 @@ +[profile.bench] +region = "eu-west-3" +image_id = "ami-04deffe45b5b236fd" +instance_type = "m6i.metal" + +[command.bench] +workflow = "benchmark.yml" +profile = "bench" +check_run_name = "FFT AWS Benchmarks" diff --git a/scripts/check_cargo_min_ver.sh b/scripts/check_cargo_min_ver.sh new file mode 100755 index 0000000..7ece83a --- /dev/null +++ b/scripts/check_cargo_min_ver.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +set -e + +function usage() { + echo "$0: check minimum cargo version" + echo + echo "--help Print this message" + echo "--rust-toolchain The toolchain to check the version for with leading" + echo "--min-rust-version Check toolchain version is >= to this version, default is 1.65" + echo +} + +RUST_TOOLCHAIN="" +# We set the default rust version 1.65 which is the minimum version required for stable GATs +MIN_RUST_VERSION="1.65" + +while [ -n "$1" ] +do + case "$1" in + "--help" | "-h" ) + usage + exit 0 + ;; + + "--rust-toolchain" ) + shift + RUST_TOOLCHAIN="$1" + ;; + + "--min-rust-version" ) + shift + MIN_RUST_VERSION="$1" + ;; + + *) + echo "Unknown param : $1" + exit 1 + ;; + esac + shift +done + +if [[ "${RUST_TOOLCHAIN::1}" != "+" ]]; then + RUST_TOOLCHAIN="+${RUST_TOOLCHAIN}" +fi + +ver_string="$(cargo ${RUST_TOOLCHAIN:+"${RUST_TOOLCHAIN}"} --version | \ + cut -d ' ' -f 2 | cut -d '-' -f 1)" +ver_major="$(echo "${ver_string}" | cut -d '.' -f 1)" +ver_minor="$(echo "${ver_string}" | cut -d '.' -f 2)" + +min_ver_major="$(echo "${MIN_RUST_VERSION}" | cut -d '.' -f 1)" +min_ver_minor="$(echo "${MIN_RUST_VERSION}" | cut -d '.' -f 2)" + +if [[ "${ver_major}" -ge "${min_ver_major}" ]] && [[ "${ver_minor}" -ge "${min_ver_minor}" ]]; then + exit 0 +fi + +exit 1 diff --git a/toolchain.txt b/toolchain.txt new file mode 100644 index 0000000..595cdb7 --- /dev/null +++ b/toolchain.txt @@ -0,0 +1 @@ +nightly-2023-01-30 From 038e56068e2bebf23bafc86b3813c3e50540725c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Test=C3=A9?= Date: Mon, 6 Mar 2023 16:54:11 +0100 Subject: [PATCH 2/2] chore(ci): improve build and testing workflows Builds are tested thoroughly after conformance check. All intriscis are tested and a commit check has been added. Liniting workflow had been removed due to duplication with conformance test (make pcc). --- .github/workflows/build.yml | 61 ------------------------------ .github/workflows/cargo_build.yml | 45 ++++++++++++++++++++++ .github/workflows/cargo_test.yml | 56 +++++++++++++++++++++++++++ .github/workflows/check_commit.yml | 32 ++++++++++++++++ .github/workflows/lint.yml | 46 ---------------------- Makefile | 43 ++++++++++++--------- 6 files changed, 158 insertions(+), 125 deletions(-) delete mode 100644 .github/workflows/build.yml create mode 100644 .github/workflows/cargo_build.yml create mode 100644 .github/workflows/cargo_test.yml create mode 100644 .github/workflows/check_commit.yml delete mode 100644 .github/workflows/lint.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 5e6dec7..0000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Test - -on: - push: - -env: - CARGO_TERM_COLOR: always - -jobs: - cargo-benches: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - name: Install FFTW - run: sudo apt install -y libfftw3-dev - - name: Compile benches - run: cargo bench --no-run - - cargo-tests: - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - - steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - - name: Test debug - run: cargo test - - name: Test debug serialization - run: cargo test --features=serde - - name: Test debug no-std - run: cargo test --no-default-features - - cargo-tests-nightly: - runs-on: ${{ matrix.os }} - - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - - steps: - - uses: actions/checkout@v2 - - name: Install Rust - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - - name: Test debug nightly - run: cargo test --features=nightly diff --git a/.github/workflows/cargo_build.yml b/.github/workflows/cargo_build.yml new file mode 100644 index 0000000..c14977f --- /dev/null +++ b/.github/workflows/cargo_build.yml @@ -0,0 +1,45 @@ +# Build concrete-fft +name: Cargo Build + +on: + pull_request: + +env: + CARGO_TERM_COLOR: always + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + cargo-builds: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + fail-fast: false + + steps: + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + + - name: Get rust toolchain to use for checks and lints + id: toolchain + run: | + echo "rs-toolchain=$(make rs_toolchain)" >> "${GITHUB_OUTPUT}" + + - name: Run pcc checks + run: | + make pcc + + - name: Build release + run: | + make build + + - name: Build release no-std + run: | + make build_no_std + + - name: Build benchmarks + run: | + make build_bench diff --git a/.github/workflows/cargo_test.yml b/.github/workflows/cargo_test.yml new file mode 100644 index 0000000..1783bcb --- /dev/null +++ b/.github/workflows/cargo_test.yml @@ -0,0 +1,56 @@ +# Test concrete-fft +name: Cargo Test + +on: + push: + +env: + CARGO_TERM_COLOR: always + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + cargo-tests: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] + steps: + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + + - name: Install Rust + uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af + with: + toolchain: stable + override: true + + - name: Test debug + run: make test + + - name: Test serialization + run: make test_serde + + - name: Test no-std + run: make test_no_std + + cargo-tests-nightly: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@ac593985615ec2ede58e132d2e21d2b1cbd6127c + + - name: Install Rust + uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af + with: + toolchain: nightly + override: true + + - name: Test nightly + run: make test_nightly + + - name: Test no-std nightly + run: make test_no_std_nightly diff --git a/.github/workflows/check_commit.yml b/.github/workflows/check_commit.yml new file mode 100644 index 0000000..516f5b9 --- /dev/null +++ b/.github/workflows/check_commit.yml @@ -0,0 +1,32 @@ +# Check commit and PR compliance +name: Check commit and PR compliance + +on: + pull_request: + +jobs: + check-commit-pr: + name: Check commit and PR + runs-on: ubuntu-latest + steps: + - name: Check first line + uses: gsactions/commit-message-checker@16fa2d5de096ae0d35626443bcd24f1e756cafee + with: + pattern: '^((feat|fix|chore|refactor|style|test|docs|doc)\(\w+\)\:) .+$' + flags: "gs" + error: 'Your first line has to contain a commit type and scope like "feat(my_feature): msg".' + excludeDescription: "true" # optional: this excludes the description body of a pull request + excludeTitle: "true" # optional: this excludes the title of a pull request + checkAllCommitMessages: "true" # optional: this checks all commits associated with a pull request + accessToken: ${{ secrets.GITHUB_TOKEN }} # github access token is only required if checkAllCommitMessages is true + + - name: Check line length + uses: gsactions/commit-message-checker@16fa2d5de096ae0d35626443bcd24f1e756cafee + with: + pattern: '(^.{0,74}$\r?\n?){0,20}' + flags: "gm" + error: "The maximum line length of 74 characters is exceeded." + excludeDescription: "true" # optional: this excludes the description body of a pull request + excludeTitle: "true" # optional: this excludes the title of a pull request + checkAllCommitMessages: "true" # optional: this checks all commits associated with a pull request + accessToken: ${{ secrets.GITHUB_TOKEN }} # github access token is only required if checkAllCommitMessages is true diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index d52e61f..0000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,46 +0,0 @@ -# Check formatting using rustfmt -# and lint with clippy -name: Rustfmt and Clippy check - -on: - push: - -jobs: - rustfmt: - name: rustfmt - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - components: rustfmt - override: true - - - name: Run cargo fmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all -- --check - - clippy: - name: clippy - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - - uses: actions-rs/toolchain@v1 - with: - profile: minimal - toolchain: stable - components: clippy - override: true - - - uses: actions-rs/clippy-check@v1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --all-targets --features=serde -- --no-deps -D warnings diff --git a/Makefile b/Makefile index c28c204..2536243 100644 --- a/Makefile +++ b/Makefile @@ -48,45 +48,52 @@ fmt: install_rs_check_toolchain check_fmt: install_rs_check_toolchain cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt --check -.PHONY: clippy # Run clippy lints enabling the boolean, shortint, integer +.PHONY: clippy # Run clippy lints clippy: install_rs_check_toolchain RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \ - -- --no-deps -D warnings - -.PHONY: clippy_all_targets # Run clippy lints on all targets (benches, examples, etc.) -clippy_all_targets: - RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \ - -- --no-deps -D warnings - -.PHONY: clippy_all # Run all clippy targets -clippy_all: clippy clippy_all_targets + --features=serde -- --no-deps -D warnings .PHONY: build build: install_rs_build_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) build --release + +.PHONY: build_no_std +build_no_std: install_rs_build_toolchain RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) build --release \ + --no-default-features + +.PHONY: build_bench +build_bench: install_rs_check_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) bench \ + --no-run + +.PHONY: test +test: install_rs_build_toolchain + RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release -.PHONY: test_default -test_default: install_rs_build_toolchain +.PHONY: test_serde +test_serde: install_rs_build_toolchain RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release + --features=serde .PHONY: test_nightly test_nightly: install_rs_build_toolchain RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \ --features=nightly -.PHONY: test_no_default -test_no_default: install_rs_build_toolchain +.PHONY: test_no_std +test_no_std: install_rs_build_toolchain RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \ --no-default-features -.PHONY: test_no_default_nightly -test_no_default_nightly: install_rs_build_toolchain +.PHONY: test_no_std_nightly +test_no_std_nightly: install_rs_build_toolchain RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --release \ --no-default-features \ --features=nightly .PHONY: test_all -test_all: test_default test_nightly test_no_default test_no_default_nightly +test_all: test test_serde test_nightly test_no_std test_no_std_nightly .PHONY: doc # Build rust doc doc: install_rs_check_toolchain @@ -99,7 +106,7 @@ bench: install_rs_check_toolchain --features=$(AVX512_FEATURE) .PHONY: pcc # pcc stands for pre commit checks -pcc: check_fmt doc clippy_all +pcc: check_fmt doc clippy .PHONY: conformance # Automatically fix problems that can be fixed conformance: fmt