From 9f38f5f26a97e79a006f34d741bb135e9438f347 Mon Sep 17 00:00:00 2001 From: firest Date: Tue, 8 Aug 2023 13:53:08 +0800 Subject: [PATCH 01/11] fix(calendar): make date parse error reason more sense --- apps/emqx/src/emqx_calendar.erl | 23 +++++++++++---- .../test/emqx_rule_funcs_SUITE.erl | 29 +++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/apps/emqx/src/emqx_calendar.erl b/apps/emqx/src/emqx_calendar.erl index dce73ec45..8a424ac2b 100644 --- a/apps/emqx/src/emqx_calendar.erl +++ b/apps/emqx/src/emqx_calendar.erl @@ -158,9 +158,9 @@ offset_second_(Offset) when is_list(Offset) -> _ -> error({bad_time_offset, Offset}) end, - Hour = list_to_int_or_error(HourStr, {bad_time_offset_hour, HourStr}), - Minute = list_to_int_or_error(MinuteStr, {bad_time_offset_minute, MinuteStr}), - Second = list_to_int_or_error(SecondStr, {bad_time_offset_second, SecondStr}), + Hour = str_to_int_or_error(HourStr, {bad_time_offset_hour, HourStr}), + Minute = str_to_int_or_error(MinuteStr, {bad_time_offset_minute, MinuteStr}), + Second = str_to_int_or_error(SecondStr, {bad_time_offset_second, SecondStr}), (Hour =< 23) orelse error({bad_time_offset_hour, Hour}), (Minute =< 59) orelse error({bad_time_offset_minute, Minute}), (Second =< 59) orelse error({bad_time_offset_second, Second}), @@ -417,7 +417,11 @@ do_parse_date_str(Date, [Key | Formatter], Result) -> <> = Date, case lists:member(Key, ?DATE_PART) of true -> - do_parse_date_str(Tail, Formatter, Result#{Key => erlang:binary_to_integer(DatePart)}); + %% Note: Here is a fix to make the error reason more sense + %% when the format or date can't be matched, + %% but the root reason comment underneath at + PartValue = str_to_int_or_error(DatePart, bad_formatter_or_date), + do_parse_date_str(Tail, Formatter, Result#{Key => PartValue}); false -> case lists:member(Key, ?DATE_ZONE_NAME) of true -> @@ -425,6 +429,13 @@ do_parse_date_str(Date, [Key | Formatter], Result) -> parsed_offset => offset_second(DatePart) }); false -> + %% + %% Here should have compared the date part with the key, + %% but for compatibility, we can't fix it here + %% e.g. + %% date_to_unix_ts('second','%Y-%m-%d %H-%M-%S', '2022-05-26 10:40:12') + %% this is valid in 4.x, but actually '%H-%M-%S' can't match with '10:40:12' + %% We cannot ensure whether there are more exceptions in the user's rule do_parse_date_str(Tail, Formatter, Result) end end. @@ -456,10 +467,12 @@ dm(10) -> 273; dm(11) -> 304; dm(12) -> 334. -list_to_int_or_error(Str, Error) -> +str_to_int_or_error(Str, Error) -> case string:to_integer(Str) of {Int, []} -> Int; + {Int, <<>>} -> + Int; _ -> error(Error) end. diff --git a/apps/emqx_rule_engine/test/emqx_rule_funcs_SUITE.erl b/apps/emqx_rule_engine/test/emqx_rule_funcs_SUITE.erl index 6e67f6cd1..65be15405 100644 --- a/apps/emqx_rule_engine/test/emqx_rule_funcs_SUITE.erl +++ b/apps/emqx_rule_engine/test/emqx_rule_funcs_SUITE.erl @@ -1031,6 +1031,35 @@ timezone_to_offset_seconds_helper(FunctionName) -> apply_func(FunctionName, [local]), ok. +t_parse_date_errors(_) -> + ?assertError( + bad_formatter_or_date, + emqx_rule_funcs:date_to_unix_ts( + second, <<"%Y-%m-%d %H:%M:%S">>, <<"2022-059999-26 10:40:12">> + ) + ), + ?assertError( + bad_formatter_or_date, + emqx_rule_funcs:date_to_unix_ts(second, <<"%y-%m-%d %H:%M:%S">>, <<"2022-05-26 10:40:12">>) + ), + + %% Compatibility test + UnixTs = 1653561612, + ?assertEqual( + UnixTs, + emqx_rule_funcs:date_to_unix_ts(second, <<"%Y-%m-%d %H:%M:%S">>, <<"2022-05-26 10:40:12">>) + ), + + ?assertEqual( + UnixTs, + emqx_rule_funcs:date_to_unix_ts(second, <<"%Y-%m-%d %H-%M-%S">>, <<"2022-05-26 10:40:12">>) + ), + + ?assertEqual( + UnixTs, + emqx_rule_funcs:date_to_unix_ts(second, <<"%Y-%m-%d %H:%M:%S">>, <<"2022-05-26 10-40-12">>) + ). + %%------------------------------------------------------------------------------ %% Utility functions %%------------------------------------------------------------------------------ From 65a6e36df11ef0ecc9d3fc8234ee361161926ec6 Mon Sep 17 00:00:00 2001 From: firest Date: Tue, 8 Aug 2023 14:31:14 +0800 Subject: [PATCH 02/11] chore: update changes --- changes/ce/perf-11405.en.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 changes/ce/perf-11405.en.md diff --git a/changes/ce/perf-11405.en.md b/changes/ce/perf-11405.en.md new file mode 100644 index 000000000..412116d90 --- /dev/null +++ b/changes/ce/perf-11405.en.md @@ -0,0 +1 @@ +Improve the error reason of the `date_to_unix_ts` to make more sense. From 1ca202fef7be253eb8cab5cf876d44b6a9ed37c1 Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Tue, 8 Aug 2023 15:23:30 -0300 Subject: [PATCH 03/11] ci: fix build package script Since the runner changed from `ubuntu-22.04` to self hosted runner, `sudo` is no longer passwordless. --- .github/workflows/_push-entrypoint.yaml | 4 ++-- .github/workflows/build_and_push_docker_images.yaml | 1 - .github/workflows/build_packages.yaml | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/_push-entrypoint.yaml b/.github/workflows/_push-entrypoint.yaml index 9faceebbc..62a692ec9 100644 --- a/.github/workflows/_push-entrypoint.yaml +++ b/.github/workflows/_push-entrypoint.yaml @@ -123,7 +123,8 @@ jobs: publish: ${{ needs.prepare.outputs.release }} otp_vsn: ${{ needs.prepare.outputs.otp_vsn }} elixir_vsn: ${{ needs.prepare.outputs.elixir_vsn }} - runner: ${{ needs.prepare.outputs.runner }} + # workaround: self-hosted runners do not have access to org-level secrets? + runner: ubuntu-22.04 builder_vsn: ${{ needs.prepare.outputs.builder_vsn }} compile: @@ -188,4 +189,3 @@ jobs: runner: ${{ needs.prepare.outputs.runner }} builder: ${{ needs.prepare.outputs.builder }} ct-matrix: ${{ needs.prepare.outputs.ct-matrix }} - diff --git a/.github/workflows/build_and_push_docker_images.yaml b/.github/workflows/build_and_push_docker_images.yaml index 33128e40d..1622fe974 100644 --- a/.github/workflows/build_and_push_docker_images.yaml +++ b/.github/workflows/build_and_push_docker_images.yaml @@ -170,4 +170,3 @@ jobs: EMQX_NAME=${{ matrix.profile }}${{ steps.pre-meta.outputs.img_suffix }} EXTRA_DEPS=${{ steps.pre-meta.outputs.extra_deps }} file: source/${{ matrix.os[2] }} - diff --git a/.github/workflows/build_packages.yaml b/.github/workflows/build_packages.yaml index 94841edc8..86aaf516c 100644 --- a/.github/workflows/build_packages.yaml +++ b/.github/workflows/build_packages.yaml @@ -261,7 +261,7 @@ jobs: name: ${{ matrix.profile }} path: packages/${{ matrix.profile }} - name: install dos2unix - run: sudo apt-get update && sudo apt install -y dos2unix + run: apt-get update && apt install -y dos2unix - name: get packages run: | set -eu From acb87403a50f0dfbabdf93c7d76abee2b48b96f4 Mon Sep 17 00:00:00 2001 From: Thales Macedo Garitezi Date: Tue, 8 Aug 2023 17:44:03 -0300 Subject: [PATCH 04/11] ci: fix path to Dockerfile --- .github/workflows/build_and_push_docker_images.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_and_push_docker_images.yaml b/.github/workflows/build_and_push_docker_images.yaml index 1622fe974..5deefd1e8 100644 --- a/.github/workflows/build_and_push_docker_images.yaml +++ b/.github/workflows/build_and_push_docker_images.yaml @@ -169,4 +169,4 @@ jobs: build-args: | EMQX_NAME=${{ matrix.profile }}${{ steps.pre-meta.outputs.img_suffix }} EXTRA_DEPS=${{ steps.pre-meta.outputs.extra_deps }} - file: source/${{ matrix.os[2] }} + file: ${{ matrix.os[2] }} From 177ec161a13017a94cd4866d014bd557fabeee56 Mon Sep 17 00:00:00 2001 From: firest Date: Tue, 8 Aug 2023 18:48:45 +0800 Subject: [PATCH 05/11] fix(ldap): improve configuration name and docs --- apps/emqx_ldap/src/emqx_ldap.erl | 10 ++++++---- apps/emqx_ldap/test/emqx_ldap_SUITE.erl | 2 +- apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl | 4 ++-- apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl | 2 +- rel/i18n/emqx_ldap.hocon | 9 +++++---- 5 files changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/emqx_ldap/src/emqx_ldap.erl b/apps/emqx_ldap/src/emqx_ldap.erl index d505f92d0..85ba73df9 100644 --- a/apps/emqx_ldap/src/emqx_ldap.erl +++ b/apps/emqx_ldap/src/emqx_ldap.erl @@ -54,10 +54,11 @@ fields(config) -> {pool_size, fun ?ECS:pool_size/1}, {username, fun ensure_username/1}, {password, fun ?ECS:password/1}, - {base_object, + {base_dn, ?HOCON(binary(), #{ - desc => ?DESC(base_object), + desc => ?DESC(base_dn), required => true, + example => <<"uid=${username},ou=testdevice,dc=emqx,dc=io">>, validator => fun emqx_schema:non_empty_string/1 })}, {filter, @@ -66,6 +67,7 @@ fields(config) -> #{ desc => ?DESC(filter), default => <<"(objectClass=mqttUser)">>, + example => <<"(& (objectClass=mqttUser) (uid=${username}))">>, validator => fun emqx_schema:non_empty_string/1 } )} @@ -229,9 +231,9 @@ log(Level, Format, Args) -> ). prepare_template(Config, State) -> - do_prepare_template(maps:to_list(maps:with([base_object, filter], Config)), State). + do_prepare_template(maps:to_list(maps:with([base_dn, filter], Config)), State). -do_prepare_template([{base_object, V} | T], State) -> +do_prepare_template([{base_dn, V} | T], State) -> do_prepare_template(T, State#{base_tokens => emqx_placeholder:preproc_tmpl(V)}); do_prepare_template([{filter, V} | T], State) -> do_prepare_template(T, State#{filter_tokens => emqx_placeholder:preproc_tmpl(V)}); diff --git a/apps/emqx_ldap/test/emqx_ldap_SUITE.erl b/apps/emqx_ldap/test/emqx_ldap_SUITE.erl index a191da3bd..8a90f6f02 100644 --- a/apps/emqx_ldap/test/emqx_ldap_SUITE.erl +++ b/apps/emqx_ldap/test/emqx_ldap_SUITE.erl @@ -154,7 +154,7 @@ ldap_config(Config) -> " password = public\n" " pool_size = 8\n" " server = \"~s:~b\"\n" - " base_object=\"uid=${username},ou=testdevice,dc=emqx,dc=io\"\n" + " base_dn=\"uid=${username},ou=testdevice,dc=emqx,dc=io\"\n" " filter =\"(objectClass=mqttUser)\"\n" " ~ts\n" "", diff --git a/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl b/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl index fb3b9fc36..d3b7a90f4 100644 --- a/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl +++ b/apps/emqx_ldap/test/emqx_ldap_authn_SUITE.erl @@ -167,7 +167,7 @@ t_update(_Config) -> CorrectConfig = raw_ldap_auth_config(), IncorrectConfig = CorrectConfig#{ - <<"base_object">> => <<"ou=testdevice,dc=emqx,dc=io">> + <<"base_dn">> => <<"ou=testdevice,dc=emqx,dc=io">> }, {ok, _} = emqx:update_config( @@ -208,7 +208,7 @@ raw_ldap_auth_config() -> <<"mechanism">> => <<"password_based">>, <<"backend">> => <<"ldap">>, <<"server">> => ldap_server(), - <<"base_object">> => <<"uid=${username},ou=testdevice,dc=emqx,dc=io">>, + <<"base_dn">> => <<"uid=${username},ou=testdevice,dc=emqx,dc=io">>, <<"username">> => <<"cn=root,dc=emqx,dc=io">>, <<"password">> => <<"public">>, <<"pool_size">> => 8 diff --git a/apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl b/apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl index de037ddf1..e6424e8ca 100644 --- a/apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl +++ b/apps/emqx_ldap/test/emqx_ldap_authz_SUITE.erl @@ -138,7 +138,7 @@ raw_ldap_authz_config() -> <<"enable">> => <<"true">>, <<"type">> => <<"ldap">>, <<"server">> => ldap_server(), - <<"base_object">> => <<"uid=${username},ou=testdevice,dc=emqx,dc=io">>, + <<"base_dn">> => <<"uid=${username},ou=testdevice,dc=emqx,dc=io">>, <<"username">> => <<"cn=root,dc=emqx,dc=io">>, <<"password">> => <<"public">>, <<"pool_size">> => 8 diff --git a/rel/i18n/emqx_ldap.hocon b/rel/i18n/emqx_ldap.hocon index cd2865d85..99e00e63a 100644 --- a/rel/i18n/emqx_ldap.hocon +++ b/rel/i18n/emqx_ldap.hocon @@ -8,16 +8,17 @@ The LDAP default port 389 is used if `[:Port]` is not specified.""" server.label: """Server Host""" -base_object.desc: +base_dn.desc: """The name of the base object entry (or possibly the root) relative to which the Search is to be performed.""" -base_object.label: -"""Base Object""" +base_dn.label: +"""Base DN""" filter.desc: """The filter that defines the conditions that must be fulfilled in order -for the Search to match a given entry.""" +for the Search to match a given entry.
+The syntax of the filter follows RFC 4515 and also supports placeholders.""" filter.label: """Filter""" From 92d4f6cb0b45e116ff2648da413dbdcb2ef5c674 Mon Sep 17 00:00:00 2001 From: Ivan Dyachkov Date: Wed, 9 Aug 2023 09:22:55 +0200 Subject: [PATCH 06/11] ci: pass secrets to docker and packages workflows --- .github/workflows/_push-entrypoint.yaml | 2 ++ .../build_and_push_docker_images.yaml | 9 +++++++++ .github/workflows/build_packages.yaml | 19 +++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/.github/workflows/_push-entrypoint.yaml b/.github/workflows/_push-entrypoint.yaml index 62a692ec9..c1589d6db 100644 --- a/.github/workflows/_push-entrypoint.yaml +++ b/.github/workflows/_push-entrypoint.yaml @@ -109,6 +109,7 @@ jobs: elixir_vsn: ${{ needs.prepare.outputs.elixir_vsn }} runner: ${{ needs.prepare.outputs.runner }} builder_vsn: ${{ needs.prepare.outputs.builder_vsn }} + secrets: inherit build_and_push_docker_images: if: ${{ needs.prepare.outputs.release == 'true' }} @@ -126,6 +127,7 @@ jobs: # workaround: self-hosted runners do not have access to org-level secrets? runner: ubuntu-22.04 builder_vsn: ${{ needs.prepare.outputs.builder_vsn }} + secrets: inherit compile: runs-on: ${{ needs.prepare.outputs.runner }} diff --git a/.github/workflows/build_and_push_docker_images.yaml b/.github/workflows/build_and_push_docker_images.yaml index 5deefd1e8..6c5093bcb 100644 --- a/.github/workflows/build_and_push_docker_images.yaml +++ b/.github/workflows/build_and_push_docker_images.yaml @@ -34,6 +34,15 @@ on: builder_vsn: required: true type: string + secrets: + DOCKER_HUB_USER: + required: true + DOCKER_HUB_TOKEN: + required: true + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true workflow_dispatch: inputs: ref: diff --git a/.github/workflows/build_packages.yaml b/.github/workflows/build_packages.yaml index 86aaf516c..2fd7d2d90 100644 --- a/.github/workflows/build_packages.yaml +++ b/.github/workflows/build_packages.yaml @@ -25,6 +25,25 @@ on: builder_vsn: required: true type: string + secrets: + AWS_ACCESS_KEY_ID: + required: true + AWS_SECRET_ACCESS_KEY: + required: true + AWS_DEFAULT_REGION: + required: true + AWS_S3_BUCKET: + required: true + AWS_CLOUDFRONT_ID: + required: true + APPLE_ID_PASSWORD: + required: true + APPLE_DEVELOPER_IDENTITY: + required: true + APPLE_DEVELOPER_ID_BUNDLE: + required: true + APPLE_DEVELOPER_ID_BUNDLE_PASSWORD: + required: true workflow_dispatch: inputs: ref: From 34759b64f5f3d3730b564d46df80be9396e7549b Mon Sep 17 00:00:00 2001 From: Ivan Dyachkov Date: Wed, 9 Aug 2023 09:23:08 +0200 Subject: [PATCH 07/11] ci(docker): pass PKG_VSN to Dockerfile to avoid calling pkg-vsn.sh --- .github/workflows/build_and_push_docker_images.yaml | 1 + deploy/docker/Dockerfile | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/build_and_push_docker_images.yaml b/.github/workflows/build_and_push_docker_images.yaml index 6c5093bcb..e3c3ff722 100644 --- a/.github/workflows/build_and_push_docker_images.yaml +++ b/.github/workflows/build_and_push_docker_images.yaml @@ -178,4 +178,5 @@ jobs: build-args: | EMQX_NAME=${{ matrix.profile }}${{ steps.pre-meta.outputs.img_suffix }} EXTRA_DEPS=${{ steps.pre-meta.outputs.extra_deps }} + PKG_VSN=${{ inputs.version }} file: ${{ matrix.os[2] }} diff --git a/deploy/docker/Dockerfile b/deploy/docker/Dockerfile index 5242970d2..61a143cae 100644 --- a/deploy/docker/Dockerfile +++ b/deploy/docker/Dockerfile @@ -5,6 +5,7 @@ FROM ${BUILD_FROM} AS builder COPY . /emqx ARG EMQX_NAME=emqx +ARG PKG_VSN ENV EMQX_RELUP=false RUN export PROFILE=${EMQX_NAME%%-elixir} \ From c35ad653f3abdbb732416736bf8539f50b03980a Mon Sep 17 00:00:00 2001 From: Ivan Dyachkov Date: Wed, 9 Aug 2023 09:33:17 +0200 Subject: [PATCH 08/11] ci: process both string and boolean values of inputs.publish --- .github/workflows/build_and_push_docker_images.yaml | 2 +- .github/workflows/build_packages.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_and_push_docker_images.yaml b/.github/workflows/build_and_push_docker_images.yaml index e3c3ff722..6c7bdbeff 100644 --- a/.github/workflows/build_and_push_docker_images.yaml +++ b/.github/workflows/build_and_push_docker_images.yaml @@ -169,7 +169,7 @@ jobs: - uses: docker/build-push-action@v3 with: - push: ${{ inputs.publish == 'true' || github.repository_owner != 'emqx' }} + push: ${{ inputs.publish == 'true' || inputs.publish || github.repository_owner != 'emqx' }} pull: true no-cache: true platforms: linux/amd64,linux/arm64 diff --git a/.github/workflows/build_packages.yaml b/.github/workflows/build_packages.yaml index 2fd7d2d90..cbe95b974 100644 --- a/.github/workflows/build_packages.yaml +++ b/.github/workflows/build_packages.yaml @@ -268,7 +268,7 @@ jobs: needs: - mac - linux - if: ${{ inputs.publish == 'true' }} + if: inputs.publish == 'true' || inputs.publish strategy: fail-fast: false matrix: From c587a753da34406b1aaff61e244539309b92ec80 Mon Sep 17 00:00:00 2001 From: Ivan Dyachkov Date: Wed, 9 Aug 2023 14:05:36 +0200 Subject: [PATCH 09/11] fix(docker): use 'make $PROFILE-docker' in CI --- .github/workflows/_push-entrypoint.yaml | 11 +-- .../build_and_push_docker_images.yaml | 99 +++++-------------- build | 88 +++++++++++++---- scripts/parse-git-ref.sh | 11 +-- scripts/shelltest/parse-git-ref.test | 30 +++--- 5 files changed, 115 insertions(+), 124 deletions(-) diff --git a/.github/workflows/_push-entrypoint.yaml b/.github/workflows/_push-entrypoint.yaml index c1589d6db..a65df1234 100644 --- a/.github/workflows/_push-entrypoint.yaml +++ b/.github/workflows/_push-entrypoint.yaml @@ -23,7 +23,6 @@ jobs: container: 'ghcr.io/emqx/emqx-builder/5.1-3:1.14.5-25.3.2-1-ubuntu22.04' outputs: profile: ${{ steps.parse-git-ref.outputs.profile }} - edition: ${{ steps.parse-git-ref.outputs.edition }} release: ${{ steps.parse-git-ref.outputs.release }} latest: ${{ steps.parse-git-ref.outputs.latest }} version: ${{ steps.parse-git-ref.outputs.version }} @@ -50,12 +49,10 @@ jobs: run: | JSON="$(./scripts/parse-git-ref.sh $GITHUB_REF)" PROFILE=$(echo "$JSON" | jq -cr '.profile') - EDITION=$(echo "$JSON" | jq -cr '.edition') RELEASE=$(echo "$JSON" | jq -cr '.release') LATEST=$(echo "$JSON" | jq -cr '.latest') VERSION="$(./pkg-vsn.sh "$PROFILE")" echo "profile=$PROFILE" | tee -a $GITHUB_OUTPUT - echo "edition=$EDITION" | tee -a $GITHUB_OUTPUT echo "release=$RELEASE" | tee -a $GITHUB_OUTPUT echo "latest=$LATEST" | tee -a $GITHUB_OUTPUT echo "version=$VERSION" | tee -a $GITHUB_OUTPUT @@ -107,8 +104,8 @@ jobs: publish: ${{ needs.prepare.outputs.release }} otp_vsn: ${{ needs.prepare.outputs.otp_vsn }} elixir_vsn: ${{ needs.prepare.outputs.elixir_vsn }} - runner: ${{ needs.prepare.outputs.runner }} builder_vsn: ${{ needs.prepare.outputs.builder_vsn }} + runner: ${{ needs.prepare.outputs.runner }} secrets: inherit build_and_push_docker_images: @@ -118,15 +115,13 @@ jobs: uses: ./.github/workflows/build_and_push_docker_images.yaml with: profile: ${{ needs.prepare.outputs.profile }} - edition: ${{ needs.prepare.outputs.edition }} version: ${{ needs.prepare.outputs.version }} - latest: ${{ needs.prepare.outputs.latest }} publish: ${{ needs.prepare.outputs.release }} + latest: ${{ needs.prepare.outputs.latest }} otp_vsn: ${{ needs.prepare.outputs.otp_vsn }} elixir_vsn: ${{ needs.prepare.outputs.elixir_vsn }} - # workaround: self-hosted runners do not have access to org-level secrets? - runner: ubuntu-22.04 builder_vsn: ${{ needs.prepare.outputs.builder_vsn }} + runner: ${{ needs.prepare.outputs.runner }} secrets: inherit compile: diff --git a/.github/workflows/build_and_push_docker_images.yaml b/.github/workflows/build_and_push_docker_images.yaml index 6c7bdbeff..b0d6aa481 100644 --- a/.github/workflows/build_and_push_docker_images.yaml +++ b/.github/workflows/build_and_push_docker_images.yaml @@ -10,9 +10,6 @@ on: profile: required: true type: string - edition: - required: true - type: string version: required: true type: string @@ -28,10 +25,10 @@ on: elixir_vsn: required: true type: string - runner: + builder_vsn: required: true type: string - builder_vsn: + runner: required: true type: string secrets: @@ -47,17 +44,12 @@ on: inputs: ref: required: false - version: - required: true - type: string profile: required: false type: string default: 'emqx' - edition: - required: false - type: string - default: 'Opensource' + version: + required: true latest: required: false type: boolean @@ -74,14 +66,14 @@ on: required: false type: string default: '1.14.5' - runner: - required: false - type: string - default: 'ubuntu-22.04' builder_vsn: required: false type: string default: '5.1-3' + runner: + required: false + type: string + default: 'ubuntu-22.04' jobs: docker: @@ -92,18 +84,10 @@ jobs: matrix: profile: - ${{ inputs.profile }} + - ${{ inputs.profile }}-elixir registry: - 'docker.io' - 'public.ecr.aws' - os: - - [debian11, "debian:11-slim", "deploy/docker/Dockerfile"] - builder: - - ${{ inputs.builder_vsn }} - otp: - - ${{ inputs.otp_vsn }} - elixir: - - 'no_elixir' - - ${{ inputs.elixir_vsn }} steps: - uses: actions/checkout@v3 @@ -130,53 +114,20 @@ jobs: password: ${{ secrets.AWS_SECRET_ACCESS_KEY }} ecr: true - - name: prepare for docker/metadata-action - id: pre-meta - shell: bash + - name: Build docker image + env: + PROFILE: ${{ matrix.profile }} + DOCKER_REGISTRY: ${{ matrix.registry }} + DOCKER_ORG: ${{ github.repository_owner }} + DOCKER_LATEST: ${{ inputs.latest }} + DOCKER_PUSH: ${{ inputs.publish == 'true' || inputs.publish || github.repository_owner != 'emqx' }} + DOCKER_BUILD_NOCACHE: true + DOCKER_PLATFORMS: linux/amd64,linux/arm64 + EMQX_RUNNER: 'debian:11-slim' + EMQX_DOCKERFILE: 'deploy/docker/Dockerfile' + PKG_VSN: ${{ inputs.version }} + EMQX_BUILDER_VSN: ${{ inputs.builder_vsn }} + EMQX_OTP_VSN: ${{ inputs.otp_vsn }} + EMQX_ELIXIR_VSN: ${{ inputs.elixir_vsn }} run: | - extra_labels= - img_suffix= - if [ "${{ matrix.elixir }}" != 'no_elixir' ]; then - img_suffix="-elixir" - extra_labels="org.opencontainers.image.elixir.version=${{ matrix.elixir }}" - fi - extra_deps= - if [[ "${{ matrix.profile }}" = *enterprise* ]]; then - extra_deps='libsasl2-2,libsasl2-modules-gssapi-mit' - fi - - echo "img_suffix=$img_suffix" >> $GITHUB_OUTPUT - echo "extra_labels=$extra_labels" >> $GITHUB_OUTPUT - echo "extra_deps=$extra_deps" >> $GITHUB_OUTPUT - - - uses: docker/metadata-action@v4 - id: meta - with: - images: | - ${{ matrix.registry }}/${{ github.repository_owner }}/${{ matrix.profile }} - flavor: | - latest=${{ matrix.elixir == 'no_elixir' }} - suffix=${{ steps.pre-meta.outputs.img_suffix }} - tags: | - type=semver,pattern={{major}}.{{minor}},value=${{ inputs.version }} - type=semver,pattern={{version}},value=${{ inputs.version }} - type=raw,value=${{ inputs.version }} - type=raw,value=latest,enable=${{ inputs.latest }} - labels: | - org.opencontainers.image.otp.version=${{ matrix.otp }} - org.opencontainers.image.edition=${{ inputs.edition }} - ${{ steps.pre-meta.outputs.extra_labels }} - - - uses: docker/build-push-action@v3 - with: - push: ${{ inputs.publish == 'true' || inputs.publish || github.repository_owner != 'emqx' }} - pull: true - no-cache: true - platforms: linux/amd64,linux/arm64 - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - build-args: | - EMQX_NAME=${{ matrix.profile }}${{ steps.pre-meta.outputs.img_suffix }} - EXTRA_DEPS=${{ steps.pre-meta.outputs.extra_deps }} - PKG_VSN=${{ inputs.version }} - file: ${{ matrix.os[2] }} + ./build ${PROFILE} docker diff --git a/build b/build index fb0d213f2..df4cb19e8 100755 --- a/build +++ b/build @@ -368,21 +368,81 @@ docker_cleanup() { } ## Build the default docker image based on debian 11. -## NOTE: docker image build in github action does not call this -## function, see build_and_push_docker_images.yaml make_docker() { - EMQX_BUILDER="${EMQX_BUILDER:-${EMQX_DEFAULT_BUILDER}}" - EMQX_RUNNER="${EMQX_RUNNER:-${EMQX_DEFAULT_RUNNER}}" - EMQX_DOCKERFILE="${EMQX_DOCKERFILE:-deploy/docker/Dockerfile}" + local EMQX_BUILDER_VERSION="${EMQX_BUILDER_VERSION:-5.1-3}" + local EMQX_BUILDER_PLATFORM="${EMQX_BUILDER_PLATFORM:-debian11}" + local EMQX_BUILDER_OTP="${EMQX_BUILDER_OTP:-25.3.2-1}" + local EMQX_BUILDER_ELIXIR="${EMQX_BUILDER_ELIXIR:-1.14.5}" + local EMQX_BUILDER=${EMQX_BUILDER:-ghcr.io/emqx/emqx-builder/${EMQX_BUILDER_VERSION}:${EMQX_BUILDER_ELIXIR}-${EMQX_BUILDER_OTP}-${EMQX_BUILDER_PLATFORM}} + local EMQX_RUNNER="${EMQX_RUNNER:-${EMQX_DEFAULT_RUNNER}}" + local EMQX_DOCKERFILE="${EMQX_DOCKERFILE:-deploy/docker/Dockerfile}" + local PKG_VSN="${PKG_VSN:-$(./pkg-vsn.sh)}" + # shellcheck disable=SC2155 + local VSN_MAJOR="$(echo "$PKG_VSN" | cut -d . -f 1)" + # shellcheck disable=SC2155 + local VSN_MINOR="$(echo "$PKG_VSN" | cut -d . -f 2)" + # shellcheck disable=SC2155 + local VSN_PATCH="$(echo "$PKG_VSN" | cut -d . -f 3)" + local SUFFIX='' if [[ "$PROFILE" = *-elixir ]]; then - PKG_VSN="$PKG_VSN-elixir" + SUFFIX="-elixir" fi - local default_tag="emqx/${PROFILE%%-elixir}:${PKG_VSN}" - EMQX_IMAGE_TAG="${EMQX_IMAGE_TAG:-$default_tag}" + local DOCKER_REGISTRY="${REGISTRY:-docker.io}" + local DOCKER_ORG="${DOCKER_ORG:-emqx}" + local EMQX_BASE_DOCKER_TAG="${DOCKER_REGISTRY}/${DOCKER_ORG}/${PROFILE%%-elixir}" + local default_tag="${EMQX_BASE_DOCKER_TAG}:${PKG_VSN}${SUFFIX}" + local EMQX_IMAGE_TAG="${EMQX_IMAGE_TAG:-$default_tag}" + local EDITION=Opensource + local LICENSE='Apache-2.0' ## extra_deps is a comma separated list of debian 11 package names - local extra_deps='' + local EXTRA_DEPS='' if [[ "$PROFILE" = *enterprise* ]]; then - extra_deps='libsasl2-2,libsasl2-modules-gssapi-mit' + EXTRA_DEPS='libsasl2-2,libsasl2-modules-gssapi-mit' + EDITION=Enterprise + LICENSE='(Apache-2.0 AND BSL-1.1)' + fi + # shellcheck disable=SC2155 + local ISO_8601_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" + # shellcheck disable=SC2155 + local GIT_REVISION="$(git rev-parse HEAD)" + local DOCKER_BUILDX_ARGS=( + --build-arg BUILD_FROM="${EMQX_BUILDER}" \ + --build-arg RUN_FROM="${EMQX_RUNNER}" \ + --build-arg EMQX_NAME="${PROFILE}" \ + --build-arg EXTRA_DEPS="${EXTRA_DEPS}" \ + --build-arg PKG_VSN="${PKG_VSN}" \ + --file "${EMQX_DOCKERFILE}" \ + --label org.opencontainers.image.title="${PROFILE}" \ + --label org.opencontainers.image.edition="${EDITION}" \ + --label org.opencontainers.image.version="${PKG_VSN}" \ + --label org.opencontainers.image.revision="${GIT_REVISION}" \ + --label org.opencontainers.image.created="${ISO_8601_DATE}" \ + --label org.opencontainers.image.source='https://github.com/emqx/emqx' \ + --label org.opencontainers.image.url='https://github.com/emqx/emqx' \ + --label org.opencontainers.image.documentation='https://www.emqx.io/docs/en/latest/' \ + --label org.opencontainers.image.description='The most scalable open-source MQTT broker for IoT, IIoT, and connected vehicles' \ + --label org.opencontainers.image.licenses="${LICENSE}" \ + --label org.opencontainers.image.otp.version="${EMQX_BUILDER_OTP}" \ + --tag "${EMQX_IMAGE_TAG}" \ + --tag "${EMQX_BASE_DOCKER_TAG}:${VSN_MAJOR}.${VSN_MINOR}${SUFFIX}" \ + --tag "${EMQX_BASE_DOCKER_TAG}:${VSN_MAJOR}.${VSN_MINOR}.${VSN_PATCH}${SUFFIX}" \ + --provenance false \ + --pull + ) + if [ "${DOCKER_BUILD_NOCACHE:-false}" = true ]; then + DOCKER_BUILDX_ARGS+=(--no-cache) + fi + if [ "${SUFFIX}" = '-elixir' ]; then + DOCKER_BUILDX_ARGS+=(--label org.opencontainers.image.elixir.version="${EMQX_BUILDER_ELIXIR}") + fi + if [ "${DOCKER_LATEST:-false}" = true ]; then + DOCKER_BUILDX_ARGS+=(--tag "${DOCKER_REGISTRY}/${DOCKER_ORG}/${PROFILE}:latest") + fi + if [ "${DOCKER_PLATFORMS:-default}" != 'default' ]; then + DOCKER_BUILDX_ARGS+=(--platform "${DOCKER_PLATFORMS}") + fi + if [ "${DOCKER_PUSH-false}" = true ]; then + DOCKER_BUILDX_ARGS+=(--push) fi # shellcheck disable=SC2015 [ -f ./.dockerignore ] && mv ./.dockerignore ./.dockerignore.bak || true @@ -393,13 +453,7 @@ make_docker() { echo '/*.lock' } >> ./.dockerignore set -x - docker build --no-cache --pull \ - --build-arg BUILD_FROM="${EMQX_BUILDER}" \ - --build-arg RUN_FROM="${EMQX_RUNNER}" \ - --build-arg EMQX_NAME="${PROFILE}" \ - --build-arg EXTRA_DEPS="${extra_deps}" \ - --tag "${EMQX_IMAGE_TAG}" \ - -f "${EMQX_DOCKERFILE}" . + docker buildx build "${DOCKER_BUILDX_ARGS[@]}" . [[ "${DEBUG:-}" -eq 1 ]] || set +x echo "${EMQX_IMAGE_TAG}" > ./.docker_image_tag } diff --git a/scripts/parse-git-ref.sh b/scripts/parse-git-ref.sh index a486f2589..23d88d2eb 100755 --- a/scripts/parse-git-ref.sh +++ b/scripts/parse-git-ref.sh @@ -18,32 +18,26 @@ is_latest() { if [[ $1 =~ ^refs/tags/v[5-9]+\.[0-9]+\.[0-9]+$ ]]; then PROFILE=emqx - EDITION=Opensource RELEASE=true LATEST=$(is_latest "$1") elif [[ $1 =~ ^refs/tags/v[5-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then PROFILE=emqx - EDITION=Opensource RELEASE=true LATEST=$(is_latest "$1") elif [[ $1 =~ ^refs/tags/e[5-9]+\.[0-9]+\.[0-9]+$ ]]; then PROFILE=emqx-enterprise - EDITION=Enterprise RELEASE=true LATEST=$(is_latest "$1") elif [[ $1 =~ ^refs/tags/e[5-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then PROFILE=emqx-enterprise - EDITION=Enterprise RELEASE=true LATEST=$(is_latest "$1") elif [[ $1 =~ ^refs/tags/v[5-9]+\.[0-9]+\.[0-9]+-(alpha|beta|rc)\.[0-9]+$ ]]; then PROFILE=emqx - EDITION=Opensource RELEASE=true LATEST=false elif [[ $1 =~ ^refs/tags/e[5-9]+\.[0-9]+\.[0-9]+-(alpha|beta|rc)\.[0-9]+$ ]]; then PROFILE=emqx-enterprise - EDITION=Enterprise RELEASE=true LATEST=false elif [[ $1 =~ ^refs/tags/.+ ]]; then @@ -51,17 +45,14 @@ elif [[ $1 =~ ^refs/tags/.+ ]]; then exit 1 elif [[ $1 =~ ^refs/heads/master$ ]]; then PROFILE=emqx - EDITION=Opensource RELEASE=false LATEST=false elif [[ $1 =~ ^refs/heads/release-[5-9][0-9]+$ ]]; then PROFILE=emqx-enterprise - EDITION=Enterprise RELEASE=false LATEST=false elif [[ $1 =~ ^refs/heads/ci/.* ]]; then PROFILE=emqx - EDITION=Opensource RELEASE=false LATEST=false else @@ -70,5 +61,5 @@ else fi cat <>> -{"profile": "emqx", "edition": "Opensource", "release": true, "latest": false} +{"profile": "emqx", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/v5.1.5.1 >>> -{"profile": "emqx", "edition": "Opensource", "release": true, "latest": false} +{"profile": "emqx", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/v5.2.0-alpha.1 >>> -{"profile": "emqx", "edition": "Opensource", "release": true, "latest": false} +{"profile": "emqx", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/v5.2.0-alpha-1 @@ -30,62 +30,62 @@ Unrecognized tag: refs/tags/v5.2.0-alpha-1 ./parse-git-ref.sh refs/tags/v5.2.0-beta.1 >>> -{"profile": "emqx", "edition": "Opensource", "release": true, "latest": false} +{"profile": "emqx", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/v5.2.0-rc.1 >>> -{"profile": "emqx", "edition": "Opensource", "release": true, "latest": false} +{"profile": "emqx", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/e5.1.0 >>> -{"profile": "emqx-enterprise", "edition": "Enterprise", "release": true, "latest": false} +{"profile": "emqx-enterprise", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/e5.1.5.1 >>> -{"profile": "emqx-enterprise", "edition": "Enterprise", "release": true, "latest": false} +{"profile": "emqx-enterprise", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/e5.2.0-alpha.1 >>> -{"profile": "emqx-enterprise", "edition": "Enterprise", "release": true, "latest": false} +{"profile": "emqx-enterprise", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/e5.2.0-beta.1 >>> -{"profile": "emqx-enterprise", "edition": "Enterprise", "release": true, "latest": false} +{"profile": "emqx-enterprise", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/e5.2.0-rc.1 >>> -{"profile": "emqx-enterprise", "edition": "Enterprise", "release": true, "latest": false} +{"profile": "emqx-enterprise", "release": true, "latest": false} >>>= 0 ./parse-git-ref.sh refs/tags/e5.1.99 >>> -{"profile": "emqx-enterprise", "edition": "Enterprise", "release": true, "latest": true} +{"profile": "emqx-enterprise", "release": true, "latest": true} >>>= 0 ./parse-git-ref.sh refs/tags/v5.1.99 >>> -{"profile": "emqx", "edition": "Opensource", "release": true, "latest": true} +{"profile": "emqx", "release": true, "latest": true} >>>= 0 ./parse-git-ref.sh refs/heads/master >>> -{"profile": "emqx", "edition": "Opensource", "release": false, "latest": false} +{"profile": "emqx", "release": false, "latest": false} >>>= 0 ./parse-git-ref.sh refs/heads/release-51 >>> -{"profile": "emqx-enterprise", "edition": "Enterprise", "release": false, "latest": false} +{"profile": "emqx-enterprise", "release": false, "latest": false} >>>= 0 ./parse-git-ref.sh refs/heads/ci/foobar >>> -{"profile": "emqx", "edition": "Opensource", "release": false, "latest": false} +{"profile": "emqx", "release": false, "latest": false} >>>= 0 ./parse-git-ref.sh refs/heads/release-44 From 42b0a131ce671b57a3ecbc8e8eeaa4cded605019 Mon Sep 17 00:00:00 2001 From: Ivan Dyachkov Date: Wed, 9 Aug 2023 14:45:34 +0200 Subject: [PATCH 10/11] fix(semver): use a.b.c-d version pattern instead of a.b.c.d for extra releases --- scripts/parse-git-ref.sh | 4 ++-- scripts/shelltest/parse-git-ref.test | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/parse-git-ref.sh b/scripts/parse-git-ref.sh index 23d88d2eb..5209afcc1 100755 --- a/scripts/parse-git-ref.sh +++ b/scripts/parse-git-ref.sh @@ -20,7 +20,7 @@ if [[ $1 =~ ^refs/tags/v[5-9]+\.[0-9]+\.[0-9]+$ ]]; then PROFILE=emqx RELEASE=true LATEST=$(is_latest "$1") -elif [[ $1 =~ ^refs/tags/v[5-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then +elif [[ $1 =~ ^refs/tags/v[5-9]+\.[0-9]+\.[0-9]+-[0-9]+$ ]]; then PROFILE=emqx RELEASE=true LATEST=$(is_latest "$1") @@ -28,7 +28,7 @@ elif [[ $1 =~ ^refs/tags/e[5-9]+\.[0-9]+\.[0-9]+$ ]]; then PROFILE=emqx-enterprise RELEASE=true LATEST=$(is_latest "$1") -elif [[ $1 =~ ^refs/tags/e[5-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then +elif [[ $1 =~ ^refs/tags/e[5-9]+\.[0-9]+\.[0-9]+-[0-9]+$ ]]; then PROFILE=emqx-enterprise RELEASE=true LATEST=$(is_latest "$1") diff --git a/scripts/shelltest/parse-git-ref.test b/scripts/shelltest/parse-git-ref.test index 14129e5dc..a77f075ad 100644 --- a/scripts/shelltest/parse-git-ref.test +++ b/scripts/shelltest/parse-git-ref.test @@ -13,7 +13,7 @@ Unrecognized git ref: v5.2.0 {"profile": "emqx", "release": true, "latest": false} >>>= 0 -./parse-git-ref.sh refs/tags/v5.1.5.1 +./parse-git-ref.sh refs/tags/v5.1.5-1 >>> {"profile": "emqx", "release": true, "latest": false} >>>= 0 @@ -43,7 +43,7 @@ Unrecognized tag: refs/tags/v5.2.0-alpha-1 {"profile": "emqx-enterprise", "release": true, "latest": false} >>>= 0 -./parse-git-ref.sh refs/tags/e5.1.5.1 +./parse-git-ref.sh refs/tags/e5.1.5-1 >>> {"profile": "emqx-enterprise", "release": true, "latest": false} >>>= 0 From 446421d38b788e2abaa624240276fde7e8366aa4 Mon Sep 17 00:00:00 2001 From: Ivan Dyachkov Date: Wed, 9 Aug 2023 15:01:28 +0200 Subject: [PATCH 11/11] fix(docker): separate product url, description and docs url for ce and ee also fix latest tag for elixir version --- build | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/build b/build index df4cb19e8..cbbee1a95 100755 --- a/build +++ b/build @@ -394,12 +394,18 @@ make_docker() { local EMQX_IMAGE_TAG="${EMQX_IMAGE_TAG:-$default_tag}" local EDITION=Opensource local LICENSE='Apache-2.0' + local PRODUCT_URL='https://www.emqx.io' + local PRODUCT_DESCRIPTION='Official docker image for EMQX, the most scalable open-source MQTT broker for IoT, IIoT, and connected vehicles.' + local DOCUMENTATION_URL='https://www.emqx.io/docs/en/latest/' ## extra_deps is a comma separated list of debian 11 package names local EXTRA_DEPS='' if [[ "$PROFILE" = *enterprise* ]]; then EXTRA_DEPS='libsasl2-2,libsasl2-modules-gssapi-mit' EDITION=Enterprise LICENSE='(Apache-2.0 AND BSL-1.1)' + PRODUCT_URL='https://www.emqx.com/en/products/emqx' + PRODUCT_DESCRIPTION='Official docker image for EMQX Enterprise, an enterprise MQTT platform at scale. ' + DOCUMENTATION_URL='https://docs.emqx.com/en/enterprise/latest/' fi # shellcheck disable=SC2155 local ISO_8601_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" @@ -418,9 +424,9 @@ make_docker() { --label org.opencontainers.image.revision="${GIT_REVISION}" \ --label org.opencontainers.image.created="${ISO_8601_DATE}" \ --label org.opencontainers.image.source='https://github.com/emqx/emqx' \ - --label org.opencontainers.image.url='https://github.com/emqx/emqx' \ - --label org.opencontainers.image.documentation='https://www.emqx.io/docs/en/latest/' \ - --label org.opencontainers.image.description='The most scalable open-source MQTT broker for IoT, IIoT, and connected vehicles' \ + --label org.opencontainers.image.url="${PRODUCT_URL}" \ + --label org.opencontainers.image.description="${PRODUCT_DESCRIPTION}" \ + --label org.opencontainers.image.documentation="${DOCUMENTATION_URL}" \ --label org.opencontainers.image.licenses="${LICENSE}" \ --label org.opencontainers.image.otp.version="${EMQX_BUILDER_OTP}" \ --tag "${EMQX_IMAGE_TAG}" \ @@ -436,12 +442,12 @@ make_docker() { DOCKER_BUILDX_ARGS+=(--label org.opencontainers.image.elixir.version="${EMQX_BUILDER_ELIXIR}") fi if [ "${DOCKER_LATEST:-false}" = true ]; then - DOCKER_BUILDX_ARGS+=(--tag "${DOCKER_REGISTRY}/${DOCKER_ORG}/${PROFILE}:latest") + DOCKER_BUILDX_ARGS+=(--tag "${DOCKER_REGISTRY}/${DOCKER_ORG}/${PROFILE}:latest${SUFFIX}") fi if [ "${DOCKER_PLATFORMS:-default}" != 'default' ]; then DOCKER_BUILDX_ARGS+=(--platform "${DOCKER_PLATFORMS}") fi - if [ "${DOCKER_PUSH-false}" = true ]; then + if [ "${DOCKER_PUSH:-false}" = true ]; then DOCKER_BUILDX_ARGS+=(--push) fi # shellcheck disable=SC2015