diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5dbd64c --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,41 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + allow: + - dependency-type: "direct" + update-type: "version-update:semver-patch" + - dependency-type: "direct" + update-type: "version-update:semver-minor" + + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" + allow: + - dependency-type: "direct" + update-type: "version-update:semver-patch" + - dependency-type: "direct" + update-type: "version-update:semver-minor" + + - package-ecosystem: "bundler" + directory: "/apps/epp_proxy/priv/test_backend_app" + schedule: + interval: "weekly" + allow: + - dependency-type: "direct" + update-type: "version-update:semver-patch" + - dependency-type: "direct" + update-type: "version-update:semver-minor" + + - package-ecosystem: "hex" + directory: "/" + schedule: + interval: "weekly" + allow: + - dependency-type: "direct" + update-type: "version-update:semver-patch" + - dependency-type: "direct" + update-type: "version-update:semver-minor" diff --git a/.github/workflows/auto-approve-merge-bot.yml b/.github/workflows/auto-approve-merge-bot.yml new file mode 100644 index 0000000..d302327 --- /dev/null +++ b/.github/workflows/auto-approve-merge-bot.yml @@ -0,0 +1,71 @@ +name: Auto approve & merge Dependabot and Renovate PRs + +on: + pull_request: + types: [opened, edited, synchronize, reopened, labeled] + branches: [master] + +permissions: + contents: write + pull-requests: write + +jobs: + auto-approve-merge: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Install GitHub CLI + run: | + sudo apt-get update + sudo apt-get install -y gh + + - name: Auto approve PR + if: github.actor == 'dependabot[bot]' || github.actor == 'renovate[bot]' + uses: hmarr/auto-approve-action@v3 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Fetch Dependabot metadata + if: github.actor == 'dependabot[bot]' + id: metadata + uses: dependabot/fetch-metadata@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Check if PR should be auto-merged + id: check_auto_merge + run: | + if [ "${{ github.actor }}" == "dependabot[bot]" ]; then + if [[ "${{ steps.metadata.outputs.update-type }}" == "version-update:semver-patch" || "${{ steps.metadata.outputs.update-type }}" == "version-update:semver-minor" ]]; then + echo "auto_merge=true" >> $GITHUB_OUTPUT + else + echo "auto_merge=false" >> $GITHUB_OUTPUT + fi + elif [ "${{ github.actor }}" == "renovate[bot]" ]; then + patch_label=$(echo "${{ github.event.pull_request.labels.*.name }}" | grep -w patch || true) + minor_label=$(echo "${{ github.event.pull_request.labels.*.name }}" | grep -w minor || true) + if [ -n "$patch_label" ] || [ -n "$minor_label" ]; then + echo "auto_merge=true" >> $GITHUB_OUTPUT + else + echo "auto_merge=false" >> $GITHUB_OUTPUT + fi + fi + shell: bash + + - name: Wait for CI checks + if: steps.check_auto_merge.outputs.auto_merge == 'true' + uses: lewagon/wait-on-check-action@v1.7.0 + with: + ref: ${{ github.event.pull_request.head.sha }} + repo-token: ${{ secrets.GITHUB_TOKEN }} + wait-interval: 30 + running-workflow-name: 'auto-approve-merge' + + - name: Auto-merge PR + if: steps.check_auto_merge.outputs.auto_merge == 'true' + run: | + gh pr merge --auto --merge ${{ github.event.pull_request.number }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-merged-mattermost.yml b/.github/workflows/pr-merged-mattermost.yml new file mode 100644 index 0000000..2cffe8e --- /dev/null +++ b/.github/workflows/pr-merged-mattermost.yml @@ -0,0 +1,46 @@ +name: Notify Mattermost on merged PRs + +on: + pull_request: + types: [closed] + branches: [master] + +permissions: + contents: read + pull-requests: read + +jobs: + notify: + if: github.event.pull_request.merged == true && + ( + github.event.pull_request.user.login == 'renovate[bot]' || + github.event.pull_request.user.login == 'dependabot[bot]' || + github.event.pull_request.user.login == 'snyk-bot' + ) && + ( + github.event.pull_request.merged_by.login == 'github-actions[bot]' || + github.event.pull_request.merged_by.login == 'renovate[bot]' + ) + runs-on: ubuntu-latest + steps: + - name: Send Mattermost notification + env: + MATTERMOST_BOT_TOKEN: ${{ secrets.MATTERMOST_BOT_TOKEN }} + MATTERMOST_CHANNEL_ID: ${{ secrets.MATTERMOST_CHANNEL_ID }} + MATTERMOST_BASE_URL: ${{ secrets.MATTERMOST_BASE_URL }} + REPO: ${{ github.repository }} + ACTOR: ${{ github.event.pull_request.merged_by.login }} + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_URL: ${{ github.event.pull_request.html_url }} + MERGED_AT: ${{ github.event.pull_request.merged_at }} + run: | + if [ -z "$MATTERMOST_BOT_TOKEN" ] || [ -z "$MATTERMOST_CHANNEL_ID" ] || [ -z "$MATTERMOST_BASE_URL" ]; then + echo "Missing Mattermost secrets (MATTERMOST_BOT_TOKEN, MATTERMOST_CHANNEL_ID, MATTERMOST_BASE_URL)" >&2 + exit 1 + fi + curl -sS -X POST \ + -H "Authorization: Bearer $MATTERMOST_BOT_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"channel_id\":\"$MATTERMOST_CHANNEL_ID\",\"message\":\"[$REPO] PR #$PR_NUMBER: \\\"$PR_TITLE\\\" was merged by $ACTOR.\\nMerged at: $MERGED_AT\\nLink: $PR_URL\"}" \ + "$MATTERMOST_BASE_URL/api/v4/posts" diff --git a/.github/workflows/run-automatest-tests.yml b/.github/workflows/run-automatest-tests.yml index e5b13e2..8a80ff2 100644 --- a/.github/workflows/run-automatest-tests.yml +++ b/.github/workflows/run-automatest-tests.yml @@ -1,6 +1,8 @@ name: Proxy automated tests -on: [push] +on: + pull_request: + branches: [master] jobs: test: @@ -14,9 +16,6 @@ jobs: - name: Start the container run: docker run -d --name tester proxy-tester - - name: Install rebar dependencies - run: docker exec -i tester bash -l -c "source /root/.asdf/asdf.sh && rebar3 as test get-deps" - - name: Integrated Ruby app setup run: docker exec -d tester bash -l -c "cd apps/epp_proxy/priv/test_backend_app && bundle install && bundle exec rackup" @@ -27,7 +26,45 @@ jobs: - name: Run tests run: | - docker exec -i -e DEBUG=1 tester bash -l -c "source /root/.asdf/asdf.sh && rebar3 ct --sys_config config/test.config --readable=false --cover --verbose=true" + docker exec -i -e DEBUG=1 tester bash -l -c "source /root/.asdf/asdf.sh && rebar3 as test ct --sys_config config/test.config --readable=false --cover --verbose=true" - name: Show test coverage - run: docker exec -i tester bash -l -c "source /root/.asdf/asdf.sh && rebar3 cover --verbose" \ No newline at end of file + run: docker exec -i tester bash -l -c "source /root/.asdf/asdf.sh && rebar3 as test cover --verbose" + + - name: Generate Cobertura report + if: success() + run: | + docker exec -i tester bash -l -c 'source /root/.asdf/asdf.sh && cd /opt/erlang/epp_proxy && \ + mkdir -p _build/test/covertool && \ + (rebar3 as test covertool generate -s || \ + (COVERTOOL_EBIN=$(find _build -type d -path "*/covertool/ebin" | head -1) && \ + test -n "$COVERTOOL_EBIN" && \ + erl -noshell -pa "$COVERTOOL_EBIN" -eval "\ + {ok,_} = cover:start(),\ + ok = cover:import(\"_build/test/cover/ct.coverdata\"),\ + Config = {config, epp_proxy, 0, no_import, \"_build/test/covertool/epp_proxy.covertool.xml\", false, [\"apps/epp_proxy/src\"], [\"_build/test/lib/epp_proxy/ebin\"], no_callback},\ + ok = covertool:generate_report(Config, cover:imported_modules()),\ + halt(0)."))' + + - name: Copy coverage report from container + if: success() + run: | + docker cp tester:/opt/erlang/epp_proxy/_build/test/covertool/epp_proxy.covertool.xml ./cov.xml + docker cp tester:/opt/erlang/epp_proxy/_build/test/cover ./coverage || true + + - uses: qltysh/qlty-action/coverage@v2 + if: success() + with: + token: ${{ secrets.QLTY_COVERAGE_TOKEN }} + files: cov.xml + format: cobertura + strip-prefix: /opt/erlang/epp_proxy/ + + - uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage + path: | + cov.xml + coverage/ + if-no-files-found: ignore \ No newline at end of file diff --git a/apps/epp_proxy/src/epp_proxy.appup.src b/apps/epp_proxy/src/epp_proxy.appup.src index 8f462bc..5b7c32b 100644 --- a/apps/epp_proxy/src/epp_proxy.appup.src +++ b/apps/epp_proxy/src/epp_proxy.appup.src @@ -1,6 +1,6 @@ AppInfo = rebar3_appup_utils:find_app_info(<<"epp_proxy">>, STATE), -"{{vsn}}" = rebar_app_info:original_vsn(AppInfo), -{"{{vsn}}", +Vsn = rebar_app_info:original_vsn(AppInfo), +{Vsn, [ {<<".*">>, [{restart_application, epp_proxy}]} ], diff --git a/rebar.config b/rebar.config index aac0dea..767e419 100644 --- a/rebar.config +++ b/rebar.config @@ -32,12 +32,6 @@ ] }. -{provider_hooks, [ - {pre, [{tar, {appup, tar}}]}, - {post, [{compile, {appup, compile}}, - {clean, {appup, clean}}]} -]}. - {overrides, [{override, syslog, [ {plugins, [pc]}, @@ -54,11 +48,19 @@ ]}. {profiles, [{prod, [{relx, [{dev_mode, false}, - {include_erts, true}]}] + {include_erts, true}]}, + {plugins, [rebar3_appup_plugin]}, + {provider_hooks, [ + {pre, [{tar, {appup, tar}}]}, + {post, [{compile, {appup, compile}}, + {clean, {appup, clean}}]} + ]}] }, - {test, [{deps, [proper]}]}] + {test, [{deps, [proper]}, + {plugins, [{covertool, {git, "https://github.com/covertool/covertool.git", {tag, "2.0.7"}}}]}]}] }. +{covertool, [{coverdata_files, ["ct.coverdata"]}]}. + {plugins, [rebar3_auto, - {erl_tidy_prv_fmt, ".*", {git, "https://github.com/tsloughter/erl_tidy.git", {branch, "master"}}}, - rebar3_appup_plugin]}. + {erl_tidy_prv_fmt, ".*", {git, "https://github.com/tsloughter/erl_tidy.git", {branch, "master"}}}]}. diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..1276b6a --- /dev/null +++ b/renovate.json @@ -0,0 +1,53 @@ +{ + "extends": [ + "config:base" + ], + "packageRules": [ + { + "matchUpdateTypes": ["patch", "pin", "digest"], + "automerge": true, + "automergeType": "pr", + "addLabels": ["patch"] + }, + { + "matchUpdateTypes": ["minor"], + "automerge": true, + "automergeType": "pr", + "addLabels": ["minor"] + }, + { + "matchUpdateTypes": ["major"], + "automerge": false, + "addLabels": ["major"] + }, + { + "matchManagers": ["hex"], + "addLabels": ["hex"] + }, + { + "matchManagers": ["bundler"], + "addLabels": ["bundler"] + }, + { + "matchManagers": ["docker"], + "addLabels": ["docker"] + }, + { + "matchManagers": ["github-actions"], + "addLabels": ["github-actions"] + } + ], + "rangeStrategy": "replace", + "separateMajorMinor": true, + "prCreation": "immediate", + "prHourlyLimit": 2, + "prConcurrentLimit": 10, + "dependencyDashboard": true, + "rebaseWhen": "behind-base-branch", + "labels": [ + "dependencies" + ], + "schedule": [ + "before 2am" + ] +}