diff --git a/apps/emqx/include/emqx_release.hrl b/apps/emqx/include/emqx_release.hrl index 25b340f50..ecb0baabc 100644 --- a/apps/emqx/include/emqx_release.hrl +++ b/apps/emqx/include/emqx_release.hrl @@ -31,11 +31,11 @@ %% NOTE: ALso make sure to follow the instructions in end of %% `apps/emqx/src/bpapi/README.md' -%% Community edition +%% Opensource edition -define(EMQX_RELEASE_CE, "5.1.0-alpha.1"). %% Enterprise edition -define(EMQX_RELEASE_EE, "5.1.0-alpha.1"). -%% the HTTP API version +%% The HTTP API version -define(EMQX_API_VERSION, "5.0"). diff --git a/apps/emqx/src/emqx_release.erl b/apps/emqx/src/emqx_release.erl index 4ecf8598b..9a930649e 100644 --- a/apps/emqx/src/emqx_release.erl +++ b/apps/emqx/src/emqx_release.erl @@ -21,7 +21,10 @@ edition_vsn_prefix/0, edition_longstr/0, description/0, - version/0 + version/0, + version_with_prefix/0, + vsn_compare/1, + vsn_compare/2 ]). -include("emqx_release.hrl"). @@ -68,6 +71,10 @@ edition_vsn_prefix() -> edition_longstr() -> maps:get(edition(), ?EMQX_REL_NAME). +%% @doc Return the release version with prefix. +version_with_prefix() -> + edition_vsn_prefix() ++ version(). + %% @doc Return the release version. version() -> case lists:keyfind(emqx_vsn, 1, ?MODULE:module_info(compile)) of @@ -92,3 +99,47 @@ version() -> build_vsn() -> maps:get(edition(), ?EMQX_REL_VSNS). + +%% @doc Compare the given version with the current running version, +%% return 'newer' 'older' or 'same'. +vsn_compare("v" ++ Vsn) -> + vsn_compare(?EMQX_RELEASE_CE, Vsn); +vsn_compare("e" ++ Vsn) -> + vsn_compare(?EMQX_RELEASE_EE, Vsn). + +%% @priv Compare the second argument with the first argument, return +%% 'newer' 'older' or 'same' semver comparison result. +vsn_compare(Vsn1, Vsn2) -> + ParsedVsn1 = parse_vsn(Vsn1), + ParsedVsn2 = parse_vsn(Vsn2), + case ParsedVsn1 =:= ParsedVsn2 of + true -> + same; + false when ParsedVsn1 < ParsedVsn2 -> + newer; + false -> + older + end. + +%% @priv Parse the version string to a tuple. +%% Return {{Major, Minor, Patch}, Suffix}. +%% Where Suffix is either an empty string or a tuple like {"rc", 1}. +%% NOTE: taking the nature ordering of the suffix: +%% {"alpha", _} < {"beta", _} < {"rc", _} < "" +parse_vsn(Vsn) -> + try + [V1, V2, V3 | Suffix0] = string:tokens(Vsn, ".-"), + Suffix = + case Suffix0 of + "" -> + %% For the case like "5.1.0" + ""; + [ReleaseStage, Number] -> + %% For the case like "5.1.0-rc.1" + {ReleaseStage, list_to_integer(Number)} + end, + {{list_to_integer(V1), list_to_integer(V2), list_to_integer(V3)}, Suffix} + catch + _:_ -> + erlang:error({invalid_version_string, Vsn}) + end. diff --git a/apps/emqx/test/emqx_release_tests.erl b/apps/emqx/test/emqx_release_tests.erl new file mode 100644 index 000000000..45d9a53c4 --- /dev/null +++ b/apps/emqx/test/emqx_release_tests.erl @@ -0,0 +1,56 @@ +%%-------------------------------------------------------------------- +%% Copyright (c) 2023 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_release_tests). + +-include_lib("eunit/include/eunit.hrl"). + +vsn_compre_test_() -> + CurrentVersion = emqx_release:version_with_prefix(), + [ + {"must be 'same' when comparing with current version", fun() -> + ?assertEqual(same, emqx_release:vsn_compare(CurrentVersion)) + end}, + {"must be 'same' when comparing same version strings", fun() -> + ?assertEqual(same, emqx_release:vsn_compare("1.1.1", "1.1.1")) + end}, + {"1.1.1 is older than 1.1.2", fun() -> + ?assertEqual(older, emqx_release:vsn_compare("1.1.2", "1.1.1")), + ?assertEqual(newer, emqx_release:vsn_compare("1.1.1", "1.1.2")) + end}, + {"1.1.9 is older than 1.1.10", fun() -> + ?assertEqual(older, emqx_release:vsn_compare("1.1.10", "1.1.9")), + ?assertEqual(newer, emqx_release:vsn_compare("1.1.9", "1.1.10")) + end}, + {"alpha is older than beta", fun() -> + ?assertEqual(older, emqx_release:vsn_compare("1.1.1-beta.1", "1.1.1-alpha.2")), + ?assertEqual(newer, emqx_release:vsn_compare("1.1.1-alpha.2", "1.1.1-beta.1")) + end}, + {"beta is older than rc", fun() -> + ?assertEqual(older, emqx_release:vsn_compare("1.1.1-rc.1", "1.1.1-beta.2")), + ?assertEqual(newer, emqx_release:vsn_compare("1.1.1-beta.2", "1.1.1-rc.1")) + end}, + {"rc is older than official cut", fun() -> + ?assertEqual(older, emqx_release:vsn_compare("1.1.1", "1.1.1-rc.1")), + ?assertEqual(newer, emqx_release:vsn_compare("1.1.1-rc.1", "1.1.1")) + end}, + {"invalid version string will crash", fun() -> + ?assertError({invalid_version_string, "1.1.a"}, emqx_release:vsn_compare("v1.1.a")), + ?assertError( + {invalid_version_string, "1.1.1-alpha"}, emqx_release:vsn_compare("e1.1.1-alpha") + ) + end} + ].