From 890fb833bb859e23c1900d0128e1ce3d1554d355 Mon Sep 17 00:00:00 2001 From: Volodumur Yatsenko <65122611+Volodumur-Yatsenko@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:59:52 +0300 Subject: [PATCH 01/18] Add pull request template with checklist --- .github/pull_request_template.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b13eec8 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ +## Checklist before requesting a review +- [ ] I have performed a self-review of my code +- [ ] If it is a core feature, I have added thorough tests +- [ ] Do we need to implement analytics? +- [ ] Will this be part of a product update? If yes, please write one phrase about this update From 17218229eca0b04ca945a73dd787f3cca7b37c82 Mon Sep 17 00:00:00 2001 From: Volodumur Yatsenko <65122611+Volodumur-Yatsenko@users.noreply.github.com> Date: Wed, 15 Apr 2026 16:02:13 +0300 Subject: [PATCH 02/18] Create pull_request_template.md --- .github/pull_request_template.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000..b13eec8 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ +## Checklist before requesting a review +- [ ] I have performed a self-review of my code +- [ ] If it is a core feature, I have added thorough tests +- [ ] Do we need to implement analytics? +- [ ] Will this be part of a product update? If yes, please write one phrase about this update From aa396da640731d0c3941c84941aaa80ea275947c Mon Sep 17 00:00:00 2001 From: Volodumur Yatsenko <65122611+Volodumur-Yatsenko@users.noreply.github.com> Date: Wed, 15 Apr 2026 16:41:02 +0300 Subject: [PATCH 03/18] Create CODEOWNERS --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..c9686c6 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +@softservedata From 60f8101a9079f7231ab76e8a51ded9b129340c41 Mon Sep 17 00:00:00 2001 From: Volodumur Yatsenko <65122611+Volodumur-Yatsenko@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:13:04 +0300 Subject: [PATCH 04/18] Replace require_code_owner_reviews access with safe navigation operator --- test/script_test.rb | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/script_test.rb diff --git a/test/script_test.rb b/test/script_test.rb new file mode 100644 index 0000000..32b1f89 --- /dev/null +++ b/test/script_test.rb @@ -0,0 +1,5 @@ +def test_approve_from_user + # ... other test code ... + assert_equal true, @obj.rules_required_pull_request_reviews('main')&.[]("require_code_owner_reviews") + # ... other test code ... +end \ No newline at end of file From af1047fbe2d16751e0c654b9816586889927deba Mon Sep 17 00:00:00 2001 From: Volodumur Yatsenko <65122611+Volodumur-Yatsenko@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:29:52 +0300 Subject: [PATCH 05/18] Update script_test.rb to use safe navigation operator on line 102 --- .github/tests/test/script_test.rb | 114 +----------------------------- 1 file changed, 1 insertion(+), 113 deletions(-) diff --git a/.github/tests/test/script_test.rb b/.github/tests/test/script_test.rb index 7d69c61..dc37253 100644 --- a/.github/tests/test/script_test.rb +++ b/.github/tests/test/script_test.rb @@ -1,113 +1 @@ -require 'test/unit' -require_relative '../src/script' - -class ScriptTest < Test::Unit::TestCase - - def setup - url = ENV['URL'].nil? ? '' : ENV["URL"] - token = ENV['TOKEN'].nil? ? '' : ENV["TOKEN"] - @secrets_token = ENV['SECRETS_TOKEN'] - @obj = GithubApi.new(url, token) - end - - def test_health_check - assert_not_nil(@obj.instance_variable_get('@repo_uri'), 'Url alive') - assert_not_nil(@obj.instance_variable_get('@token'), 'Token alive') - end - - def test_token_present - actual = @secrets_token =~ /^ghp_\w{36}$/ - assert_not_nil(actual, "Secret with name 'PAT' with valid personal access token doesn't exist") - end - - def test_deploy_key_present - response = @obj.deploy_keys - assert_not_nil(response, "Access denied") - deploy_key = response.find {|element| element['title'] == 'DEPLOY_KEY'} - assert_not_nil(deploy_key, "The deploy key with name 'DEPLOY_KEY' doesn't exist") - end - - def test_main_present - actual = @obj.branch_exist?('main') - assert(actual, 'Branch main is not present') - end - - def test_main_protected - actual = @obj.branch_protected?('main') - assert(actual, 'Branch main is not protected') - end - - def test_develop_present - actual = @obj.branch_exist?('develop') - assert(actual, 'Branch develop is not present') - end - - def test_develop_protected - actual = @obj.branch_protected?('develop') - assert(actual, 'Branch develop is not protected') - end - - def test_develop_default - actual = @obj.default_branch - expected = 'develop' - assert_equal(expected, actual, 'Default branch isn\'t develop') - end - - def test_codeowners_contains_user - user_name = 'softservedata' - content = @obj.file_branch('CODEOWNERS', 'main') || @obj.file_branch('.github/CODEOWNERS', 'main') || @obj.file_branch('docs/CODEOWNERS', 'main') - assert_not_nil(content, 'File CODEOWNERS doesn\'t exist on main branch') - assert(content.include?(user_name), "User #{user_name} doesn't present in CODEOWNERS") - end - - def test_codeowners_not_present_develop - content = @obj.file_branch('CODEOWNERS', 'develop') - assert_nil(content, 'File CODEOWNERS exist on develop branch') - end - - def test_deny_merge_main - classic_rules = @obj.rules_required_pull_request_reviews('main') - rulesets = @obj.get_branch_ruleset('main') - rulesets_rules = rulesets&.find { |rule| rule['type'] == 'pull_request' } - assert_not_nil(classic_rules || rulesets_rules, 'We should not allow merge to main branch without PR') - end - - def test_deny_merge_develop - classic_rules = @obj.rules_required_pull_request_reviews('develop') - rulesets = @obj.get_branch_ruleset('develop') - rulesets_rules = rulesets&.find { |rule| rule['type'] == 'pull_request' } - assert_not_nil(classic_rules || rulesets_rules, 'We should not allow merge to develop branch without PR ') - end - - def test_2_approvals_develop - classic_required_approving_review_count = @obj.rules_required_pull_request_reviews('develop').nil? || @obj.rules_required_pull_request_reviews('develop')["required_approving_review_count"] - pull_request_rulesets_rules = @obj.get_branch_ruleset('develop') - rulesets_required_approving_review_count = pull_request_rulesets_rules&.find { |rule| rule['type'] == 'pull_request' }&.[]('parameters')&.[]('required_approving_review_count') - expected = 2 - required_approving_review_count = classic_required_approving_review_count == expected || rulesets_required_approving_review_count == expected - assert_true(required_approving_review_count, 'We should have 2 approvals before merge to develop branch') - end - - def test_without_approval_main - classic_required_approving_review_count = @obj.rules_required_pull_request_reviews('main').nil? || @obj.rules_required_pull_request_reviews('main')["required_approving_review_count"] - pull_request_rulesets_rules = @obj.get_branch_ruleset('main') - rulesets_required_approving_review_count = pull_request_rulesets_rules&.find { |rule| rule['type'] == 'pull_request' }&.[]('parameters')&.[]('required_approving_review_count') - expected = 0 - required_approving_review_count = classic_required_approving_review_count == expected || rulesets_required_approving_review_count == expected - assert_true(required_approving_review_count, 'We shouldn\'t have any approvals before merge to main branch') - end - - def test_approve_from_user - user_name = 'softservedata' - classic_require_code_owner_review = @obj.rules_required_pull_request_reviews('main')["require_code_owner_reviews"] - pull_request_rulesets_rules = @obj.get_branch_ruleset('main') - rulesets_require_code_owner_review = pull_request_rulesets_rules&.find { |rule| rule['type'] == 'pull_request' }&.[]('parameters')&.[]('require_code_owner_review') - assert(classic_require_code_owner_review || rulesets_require_code_owner_review, "We should not allow merge to main branch without approve from #{user_name}") - end - - def test_PR_template_present - actual = @obj.file_branch('.github/pull_request_template.md', 'main') - assert_not_nil(actual, 'Pull request template is absent') - end - -end + classic_require_code_owner_review = @obj.rules_required_pull_request_reviews('main')&.[]("require_code_owner_reviews") \ No newline at end of file From 7f5cf6af520e95e00cd605da1b4a719285395f9f Mon Sep 17 00:00:00 2001 From: Volodumur Yatsenko <65122611+Volodumur-Yatsenko@users.noreply.github.com> Date: Wed, 15 Apr 2026 17:33:25 +0300 Subject: [PATCH 06/18] Add CODEOWNERS file for repository ownership --- .github/CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..13cc56b --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +@@softservedata From 339f5d21910118a38b513f63964c66ee74967943 Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 17:54:30 +0300 Subject: [PATCH 07/18] delete test --- test/script_test.rb | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 test/script_test.rb diff --git a/test/script_test.rb b/test/script_test.rb deleted file mode 100644 index 32b1f89..0000000 --- a/test/script_test.rb +++ /dev/null @@ -1,5 +0,0 @@ -def test_approve_from_user - # ... other test code ... - assert_equal true, @obj.rules_required_pull_request_reviews('main')&.[]("require_code_owner_reviews") - # ... other test code ... -end \ No newline at end of file From 77685bbbcb524a00c7b91bc2c56580e7844c1617 Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 18:00:02 +0300 Subject: [PATCH 08/18] delete codeowners --- .github/CODEOWNERS | 1 - 1 file changed, 1 deletion(-) delete mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index c9686c6..0000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1 +0,0 @@ -@softservedata From 598ed1a78552b7a99bc71f472227b1f5f693f590 Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 18:04:25 +0300 Subject: [PATCH 09/18] delete mark down --- .github/pull_request_template.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index b13eec8..0000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,5 +0,0 @@ -## Checklist before requesting a review -- [ ] I have performed a self-review of my code -- [ ] If it is a core feature, I have added thorough tests -- [ ] Do we need to implement analytics? -- [ ] Will this be part of a product update? If yes, please write one phrase about this update From 55089cdac839fa335599aa71941ee8b03b9f862e Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 18:16:24 +0300 Subject: [PATCH 10/18] change md --- .github/pull_request_template.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index b13eec8..f4535ab 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,5 +1,11 @@ +## Describe your changes +... + +## Issue ticket number and link +... + ## Checklist before requesting a review - [ ] I have performed a self-review of my code - [ ] If it is a core feature, I have added thorough tests - [ ] Do we need to implement analytics? -- [ ] Will this be part of a product update? If yes, please write one phrase about this update +- [ ] Will this be part of a product update? If yes, please write one phrase about this update \ No newline at end of file From 3652ece75056c82b804f84b5ee56ab4de0a74817 Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 18:28:42 +0300 Subject: [PATCH 11/18] change test --- .github/tests/test/script_test.rb | 114 +++++++++++++++++++++++++++++- 1 file changed, 113 insertions(+), 1 deletion(-) diff --git a/.github/tests/test/script_test.rb b/.github/tests/test/script_test.rb index dc37253..89583f8 100644 --- a/.github/tests/test/script_test.rb +++ b/.github/tests/test/script_test.rb @@ -1 +1,113 @@ - classic_require_code_owner_review = @obj.rules_required_pull_request_reviews('main')&.[]("require_code_owner_reviews") \ No newline at end of file +require 'test/unit' +require_relative '../src/script' + +class ScriptTest < Test::Unit::TestCase + + def setup + url = ENV['URL'].nil? ? '' : ENV["URL"] + token = ENV['TOKEN'].nil? ? '' : ENV["TOKEN"] + @secrets_token = ENV['SECRETS_TOKEN'] + @obj = GithubApi.new(url, token) + end + + def test_health_check + assert_not_nil(@obj.instance_variable_get('@repo_uri'), 'Url alive') + assert_not_nil(@obj.instance_variable_get('@token'), 'Token alive') + end + + def test_token_present + actual = @secrets_token =~ /^ghp_\w{36}$/ + assert_not_nil(actual, "Secret with name 'PAT' with valid personal access token doesn't exist") + end + + def test_deploy_key_present + response = @obj.deploy_keys + assert_not_nil(response, "Access denied") + deploy_key = response.find {|element| element['title'] == 'DEPLOY_KEY'} + assert_not_nil(deploy_key, "The deploy key with name 'DEPLOY_KEY' doesn't exist") + end + + def test_main_present + actual = @obj.branch_exist?('main') + assert(actual, 'Branch main is not present') + end + + def test_main_protected + actual = @obj.branch_protected?('main') + assert(actual, 'Branch main is not protected') + end + + def test_develop_present + actual = @obj.branch_exist?('develop') + assert(actual, 'Branch develop is not present') + end + + def test_develop_protected + actual = @obj.branch_protected?('develop') + assert(actual, 'Branch develop is not protected') + end + + def test_develop_default + actual = @obj.default_branch + expected = 'develop' + assert_equal(expected, actual, 'Default branch isn\'t develop') + end + + def test_codeowners_contains_user + user_name = 'softservedata' + content = @obj.file_branch('CODEOWNERS', 'main') || @obj.file_branch('.github/CODEOWNERS', 'main') || @obj.file_branch('docs/CODEOWNERS', 'main') + assert_not_nil(content, 'File CODEOWNERS doesn\'t exist on main branch') + assert(content.include?(user_name), "User #{user_name} doesn't present in CODEOWNERS") + end + + def test_codeowners_not_present_develop + content = @obj.file_branch('CODEOWNERS', 'develop') + assert_nil(content, 'File CODEOWNERS exist on develop branch') + end + + def test_deny_merge_main + classic_rules = @obj.rules_required_pull_request_reviews('main') + rulesets = @obj.get_branch_ruleset('main') + rulesets_rules = rulesets&.find { |rule| rule['type'] == 'pull_request' } + assert_not_nil(classic_rules || rulesets_rules, 'We should not allow merge to main branch without PR') + end + + def test_deny_merge_develop + classic_rules = @obj.rules_required_pull_request_reviews('develop') + rulesets = @obj.get_branch_ruleset('develop') + rulesets_rules = rulesets&.find { |rule| rule['type'] == 'pull_request' } + assert_not_nil(classic_rules || rulesets_rules, 'We should not allow merge to develop branch without PR ') + end + + def test_2_approvals_develop + classic_required_approving_review_count = @obj.rules_required_pull_request_reviews('develop').nil? || @obj.rules_required_pull_request_reviews('develop')["required_approving_review_count"] + pull_request_rulesets_rules = @obj.get_branch_ruleset('develop') + rulesets_required_approving_review_count = pull_request_rulesets_rules&.find { |rule| rule['type'] == 'pull_request' }&.[]('parameters')&.[]('required_approving_review_count') + expected = 2 + required_approving_review_count = classic_required_approving_review_count == expected || rulesets_required_approving_review_count == expected + assert_true(required_approving_review_count, 'We should have 2 approvals before merge to develop branch') + end + + def test_without_approval_main + classic_required_approving_review_count = @obj.rules_required_pull_request_reviews('main').nil? || @obj.rules_required_pull_request_reviews('main')["required_approving_review_count"] + pull_request_rulesets_rules = @obj.get_branch_ruleset('main') + rulesets_required_approving_review_count = pull_request_rulesets_rules&.find { |rule| rule['type'] == 'pull_request' }&.[]('parameters')&.[]('required_approving_review_count') + expected = 0 + required_approving_review_count = classic_required_approving_review_count == expected || rulesets_required_approving_review_count == expected + assert_true(required_approving_review_count, 'We shouldn\'t have any approvals before merge to main branch') + end + + def test_approve_from_user + user_name = 'softservedata' + classic_require_code_owner_review = @obj.rules_required_pull_request_reviews('main')["require_code_owner_reviews"] + pull_request_rulesets_rules = @obj.get_branch_ruleset('main') + rulesets_require_code_owner_review = pull_request_rulesets_rules&.find { |rule| rule['type'] == 'pull_request' }&.[]('parameters')&.[]('require_code_owner_review') + assert(classic_require_code_owner_review || rulesets_require_code_owner_review, "We should not allow merge to main branch without approve from #{user_name}") + end + + def test_PR_template_present + actual = @obj.file_branch('.github/pull_request_template.md', 'main') + assert_not_nil(actual, 'Pull request template is absent') + end + +end \ No newline at end of file From 6d0ab767c2ffdd5092864a092783deef16f79fbc Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 22:23:28 +0300 Subject: [PATCH 12/18] add discordhook --- .github/workflows/discord.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/workflows/discord.yml diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml new file mode 100644 index 0000000..fb3ddb0 --- /dev/null +++ b/.github/workflows/discord.yml @@ -0,0 +1,16 @@ +name: Discord PR Notification + +on: + pull_request: + types: [opened] + +jobs: + notify: + runs-on: ubuntu-latest + + steps: + - name: Send message to Discord + run: | + curl -X POST ${{ secrets.DISCORD_WEBHOOK }} \ + -H "Content-Type: application/json" \ + -d '{"content": "New PR created: ${{ github.event.pull_request.html_url }}"}' \ No newline at end of file From 4b288d2921e95d5d1cdfaaa29aafb3a5d1dff934 Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 22:31:23 +0300 Subject: [PATCH 13/18] add test file --- src/.file | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/.file diff --git a/src/.file b/src/.file new file mode 100644 index 0000000..e69de29 From 5d014cb6ea198c0ead4953c8ad85ec71ff264312 Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 22:33:50 +0300 Subject: [PATCH 14/18] test --- .github/workflows/discord.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml index fb3ddb0..897ce50 100644 --- a/.github/workflows/discord.yml +++ b/.github/workflows/discord.yml @@ -4,6 +4,11 @@ on: pull_request: types: [opened] +on: + push: + pull_request: + types: [opened] + jobs: notify: runs-on: ubuntu-latest From e4a0446d459e7cce12f1ccf6cec86fa3911abc08 Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 22:34:35 +0300 Subject: [PATCH 15/18] test2 --- src/.file | 1 + 1 file changed, 1 insertion(+) diff --git a/src/.file b/src/.file index e69de29..0f0c75f 100644 --- a/src/.file +++ b/src/.file @@ -0,0 +1 @@ +dfgdfg \ No newline at end of file From 11f45e342ad154d524aedd78b3bc5cb81db09c4a Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 22:36:52 +0300 Subject: [PATCH 16/18] add test --- src/.file | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/.file b/src/.file index 0f0c75f..2e377e6 100644 --- a/src/.file +++ b/src/.file @@ -1 +1 @@ -dfgdfg \ No newline at end of file +dfgdfgasdasd \ No newline at end of file From 4bca3ffbac33776f0d7ee54098f7825f8920bd13 Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 22:42:53 +0300 Subject: [PATCH 17/18] change --- .github/workflows/discord.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/discord.yml diff --git a/.github/workflows/discord.yml b/.github/workflows/discord.yml new file mode 100644 index 0000000..897ce50 --- /dev/null +++ b/.github/workflows/discord.yml @@ -0,0 +1,21 @@ +name: Discord PR Notification + +on: + pull_request: + types: [opened] + +on: + push: + pull_request: + types: [opened] + +jobs: + notify: + runs-on: ubuntu-latest + + steps: + - name: Send message to Discord + run: | + curl -X POST ${{ secrets.DISCORD_WEBHOOK }} \ + -H "Content-Type: application/json" \ + -d '{"content": "New PR created: ${{ github.event.pull_request.html_url }}"}' \ No newline at end of file From 897c67a7cdd3999a84d071010279ec59b4313d82 Mon Sep 17 00:00:00 2001 From: Volodumur Date: Wed, 15 Apr 2026 23:09:35 +0300 Subject: [PATCH 18/18] test new pull request --- test2.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 test2.txt diff --git a/test2.txt b/test2.txt new file mode 100644 index 0000000..3bd58a7 --- /dev/null +++ b/test2.txt @@ -0,0 +1 @@ +new PR test