diff --git a/.github/workflows/build_slim_packages.yaml b/.github/workflows/build_slim_packages.yaml index 4a987b43e..1d8eff11a 100644 --- a/.github/workflows/build_slim_packages.yaml +++ b/.github/workflows/build_slim_packages.yaml @@ -52,6 +52,9 @@ jobs: run: | make ${EMQX_NAME}-zip .ci/build_packages/tests.sh "$EMQX_PKG_NAME" zip + - name: run static checks + run: | + make static_checks - name: build and test deb/rpm packages run: | make ${EMQX_NAME}-pkg diff --git a/Makefile b/Makefile index d9383bbe6..33052d6f9 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,10 @@ proper: $(REBAR) ct: $(REBAR) conf-segs @ENABLE_COVER_COMPILE=1 $(REBAR) ct --name $(CT_NODE_NAME) -c -v +.PHONY: static_checks +static_checks: dialyzer xref + @$(REBAR) ct --suite apps/emqx/test/emqx_bpapi_suite --readable false + APPS=$(shell $(CURDIR)/scripts/find-apps.sh) ## app/name-ct targets are intended for local tests hence cover is not enabled diff --git a/apps/emqx/priv/.gitkeep b/apps/emqx/priv/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/apps/emqx/src/bpapi/emqx_bpapi_static_checks.erl b/apps/emqx/src/bpapi/emqx_bpapi_static_checks.erl index fe6673d54..91cbbc13b 100644 --- a/apps/emqx/src/bpapi/emqx_bpapi_static_checks.erl +++ b/apps/emqx/src/bpapi/emqx_bpapi_static_checks.erl @@ -16,7 +16,7 @@ -module(emqx_bpapi_static_checks). --export([dump/1, dump/0, check_compat/1]). +-export([run/0, dump/1, dump/0, check_compat/1]). -include_lib("emqx/include/logger.hrl"). @@ -34,6 +34,10 @@ , release => string() }. +-type dump_options() :: #{ reldir := file:name() + , plt := file:name() + }. + -type param_types() :: #{emqx_bpapi:var_name() => _Type}. %% Applications and modules we wish to ignore in the analysis: @@ -50,6 +54,19 @@ %% Functions related to BPAPI compatibility checking %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec run() -> boolean(). +run() -> + dump(), %% TODO: check return value + Dumps = filelib:wildcard(dumps_dir() ++ "/*.bpapi"), + case Dumps of + [] -> + ?ERROR("No BPAPI dumps are found in ~s, abort", [dumps_dir()]), + false; + _ -> + ?NOTICE("Running API compatibility checks for ~p", [Dumps]), + check_compat(Dumps) + end. + -spec check_compat([file:filename()]) -> boolean(). check_compat(DumpFilenames) -> put(bpapi_ok, true), @@ -143,15 +160,19 @@ get_param_types(Signatures, {M, F, A}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dump() -> - case {filelib:wildcard("*_plt"), filelib:wildcard("_build/emqx*/lib")} of + case { filelib:wildcard(project_root_dir() ++ "/*_plt") + , filelib:wildcard(project_root_dir() ++ "/_build/emqx*/lib") + } of {[PLT|_], [RelDir|_]} -> - dump(#{plt => PLT, reldir => RelDir}); + dump(#{ plt => PLT + , reldir => RelDir + }); _ -> error("failed to guess run options") end. %% Collect the local BPAPI modules to a dump file --spec dump(map()) -> boolean(). +-spec dump(dump_options()) -> boolean(). dump(Opts) -> put(bpapi_ok, true), PLT = prepare(Opts), @@ -207,7 +228,8 @@ is_bpapi_call({Module, _Function, _Arity}) -> -spec dump_api(fulldump()) -> ok. dump_api(Term = #{api := _, signatures := _, release := Release}) -> - Filename = filename:join(code:priv_dir(emqx), Release ++ ".bpapi"), + Filename = filename:join(dumps_dir(), Release ++ ".bpapi"), + ok = filelib:ensure_dir(Filename), file:write_file(Filename, io_lib:format("~0p.", [Term])). -spec collect_bpapis([mfa()]) -> api_dump(). @@ -263,3 +285,9 @@ format_call({M, F, A}) -> setnok() -> put(bpapi_ok, false). + +dumps_dir() -> + filename:join(project_root_dir(), "apps/emqx/test/emqx_bpapi_suite_data"). + +project_root_dir() -> + string:trim(os:cmd("git rev-parse --show-toplevel")). diff --git a/apps/emqx/test/emqx_bpapi_suite.erl b/apps/emqx/test/emqx_bpapi_suite.erl new file mode 100644 index 000000000..5d0a313f8 --- /dev/null +++ b/apps/emqx/test/emqx_bpapi_suite.erl @@ -0,0 +1,34 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2022 EMQ Technologies Co., Ltd. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%%-------------------------------------------------------------------- + +-module(emqx_bpapi_suite). + +-compile(export_all). +-compile(nowarn_export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("stdlib/include/assert.hrl"). + +all() -> emqx_common_test_helpers:all(?MODULE). + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +t_run_check(_) -> + ?assertMatch(true, emqx_bpapi_static_checks:run()).