From e1f07d1c053c304d4db3bd59ddf0a06998de6f02 Mon Sep 17 00:00:00 2001 From: Sam Pepper <124164618+PepperMoJ@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:58:54 +0000 Subject: [PATCH] :fire: Removed all unowned repositories code (#4993) --- .../job-alarm-unowned-repository.yaml | 33 ---- bin/unowned_repositories.py | 88 --------- services/slack_service.py | 9 - test/test_bin/test_unowned_repositories.py | 177 ------------------ test/test_services/test_slack_service.py | 22 --- 5 files changed, 329 deletions(-) delete mode 100644 .github/workflows/job-alarm-unowned-repository.yaml delete mode 100644 bin/unowned_repositories.py delete mode 100644 test/test_bin/test_unowned_repositories.py diff --git a/.github/workflows/job-alarm-unowned-repository.yaml b/.github/workflows/job-alarm-unowned-repository.yaml deleted file mode 100644 index 1fd9edac3..000000000 --- a/.github/workflows/job-alarm-unowned-repository.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: 🤖 Alarm Unowned Repositories - -on: - schedule: - - cron: "0 10 * * 1" # once a week Monday at 10am - workflow_dispatch: # So we can trigger manually - -jobs: - report-on-unowned-github-repositories: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0 - with: - python-version: "3.11" - cache: "pipenv" - - name: Install Pipenv - run: | - pip install pipenv - pipenv install - - run: pipenv run python3 -m bin.unowned_repositories ministryofjustice "${ADMIN_GITHUB_TOKEN}" "${ADMIN_SLACK_TOKEN}" - env: - ADMIN_GITHUB_TOKEN: ${{ secrets.OPS_ENG_GENERAL_ADMIN_BOT_PAT }} - ADMIN_SLACK_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} - - name: Report failure to Slack - if: always() - uses: ravsamhq/notify-slack-action@472601e839b758e36c455b5d3e5e1a217d4807bd # 2.5.0 - with: - status: ${{ job.status }} - notify_when: "failure" - notification_title: "Failed to report on unowned_repositories" - env: - SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} diff --git a/bin/unowned_repositories.py b/bin/unowned_repositories.py deleted file mode 100644 index 0eea909a1..000000000 --- a/bin/unowned_repositories.py +++ /dev/null @@ -1,88 +0,0 @@ -import sys -from config.logging_config import logging -from services.github_service import GithubService -from services.slack_service import SlackService - - -def get_cli_arguments() -> tuple[str, str, str] | ValueError: - expected_number_of_parameters = 4 - if len(sys.argv) != expected_number_of_parameters: - raise ValueError("Incorrect number of input parameters") - - organisation_name = sys.argv[1] - admin_github_token = sys.argv[2] - slack_token = sys.argv[3] - return organisation_name, admin_github_token, slack_token - - -def get_org_teams(github_service: GithubService): - org_teams = [] - ignore_teams = ["organisation-security-auditor", "all-org-members"] - for team_name in github_service.get_team_names(): - if team_name in ignore_teams: - continue - team_repository_names = github_service.get_team_repository_names( - team_name) - team_user_names = github_service.get_team_user_names(team_name) - org_teams.append( - { - "name": team_name, - "repositories": team_repository_names, - "number_of_users": len(team_user_names) - } - ) - return org_teams - - -def get_unowned_repositories(github_service: GithubService) -> list: - repositories_with_no_associations = [] - org_teams = get_org_teams(github_service) - - for repository_name in github_service.get_org_repo_names(): - repository_has_team = False - - for team in org_teams: - for team_repository_name in team["repositories"]: - # Check repository has any team that has users in it - if team_repository_name == repository_name and team["number_of_users"] > 0: - repository_has_team = True - break - if repository_has_team is True: - break - - if repository_has_team is False: - # Check repository has any outside collaborators or direct users - if (len(github_service.get_repository_collaborators(repository_name)) == 0 - and len(github_service.get_repository_direct_users(repository_name)) == 0): - # Repository has no owners - repositories_with_no_associations.append(repository_name) - - # Print the repositories that have no owner - if len(repositories_with_no_associations) > 0: - logging.warning( - "Repositories with no owners:") - repositories_with_no_associations.sort() - for repository_name in repositories_with_no_associations: - logging.warning(repository_name) - - return repositories_with_no_associations - - -def send_slack_message(slack_service: SlackService, unowned_repositories: list): - slack_service.send_unowned_repos_slack_message(unowned_repositories) - - -def main(): - logging.info("Start") - - organisation_name, admin_github_token, slack_token = get_cli_arguments() - slack_service = SlackService(slack_token) - github_service = GithubService(admin_github_token, organisation_name) - unowned_repositories = get_unowned_repositories(github_service) - if len(unowned_repositories) > 0: - send_slack_message(slack_service, unowned_repositories) - logging.info("Finished") - - -if __name__ == "__main__": - main() diff --git a/services/slack_service.py b/services/slack_service.py index d0c81fba3..edf4cf7a6 100644 --- a/services/slack_service.py +++ b/services/slack_service.py @@ -206,15 +206,6 @@ def send_undelivered_email_alert_to_operations_engineering( blocks = self._create_block_with_message(message) self._send_alert_to_operations_engineering(blocks) - def send_unowned_repos_slack_message(self, repositories: list): - message = ( - f"*Unowned Repositories Automation*\n\n" - f"Repositories on the GitHub Organisation that have no team or collaborator:\n" - f"{repositories}" - ) - blocks = self._create_block_with_message(message) - self._send_alert_to_operations_engineering(blocks) - def send_alert_for_poc_repositories(self, repositories): message = ( "The following POC GitHub Repositories persist:\n\n" diff --git a/test/test_bin/test_unowned_repositories.py b/test/test_bin/test_unowned_repositories.py deleted file mode 100644 index 811e6ee7c..000000000 --- a/test/test_bin/test_unowned_repositories.py +++ /dev/null @@ -1,177 +0,0 @@ -import unittest -from unittest.mock import patch, MagicMock -from bin.unowned_repositories import ( - main, - get_cli_arguments, - get_org_teams, - get_unowned_repositories, - send_slack_message -) - - -@patch("bin.unowned_repositories.GithubService", new=MagicMock) -@patch("bin.unowned_repositories.SlackService", new=MagicMock) -@patch("bin.unowned_repositories.get_cli_arguments") -@patch("bin.unowned_repositories.get_unowned_repositories") -@patch("bin.unowned_repositories.send_slack_message") -class TestUnownedRepositoriesMain(unittest.TestCase): - def test_main(self, mock_send_slack_message, mock_get_unowned_repositories, mock_get_cli_arguments): - mock_get_cli_arguments.return_value = "", "", "" - mock_get_unowned_repositories.return_value = [] - main() - mock_get_unowned_repositories.assert_called_once() - mock_get_cli_arguments.assert_called_once() - mock_send_slack_message.assert_not_called() - - def test_main_raises_slack_message(self, mock_send_slack_message, mock_get_unowned_repositories, mock_get_cli_arguments): - mock_get_cli_arguments.return_value = "", "", "" - mock_get_unowned_repositories.return_value = ["some-repo"] - main() - mock_get_unowned_repositories.assert_called_once() - mock_get_cli_arguments.assert_called_once() - mock_send_slack_message.assert_called() - - -class TestUnownedRepositories(unittest.TestCase): - @patch("sys.argv", ["", "", "", ""]) - def test_get_cli_arguments_correct_length(self): - args = get_cli_arguments() - self.assertEqual(len(args), 3) - - @patch("sys.argv", [""]) - def test_get_cli_arguments_raises_error(self): - self.assertRaises( - ValueError, get_cli_arguments) - - @patch("services.github_service.GithubService") - def test_get_org_teams_no_teams_exist(self, mock_github_service): - mock_github_service.get_team_names.return_value = [] - teams = get_org_teams(mock_github_service) - self.assertEqual(len(teams), 0) - - @patch("services.github_service.GithubService") - def test_get_org_teams_ignore_teams(self, mock_github_service): - mock_github_service.get_team_names.return_value = ["all-org-members"] - teams = get_org_teams(mock_github_service) - self.assertEqual(len(teams), 0) - - mock_github_service.get_team_names.return_value = [ - "organisation-security-auditor"] - teams = get_org_teams(mock_github_service) - self.assertEqual(len(teams), 0) - - @patch("services.github_service.GithubService") - def test_get_org_teams(self, mock_github_service): - mock_github_service.get_team_names.return_value = ["some-team"] - mock_github_service.get_team_repository_names.return_value = [ - "some-repo"] - mock_github_service.get_team_user_names.return_value = ["some-user"] - expected_result = { - "name": "some-team", - "repositories": ["some-repo"], - "number_of_users": 1 - } - teams = get_org_teams(mock_github_service) - self.assertEqual(len(teams), 1) - self.assertEqual(teams[0], expected_result) - - @patch("services.github_service.GithubService") - @patch("bin.unowned_repositories.get_org_teams") - def test_get_unowned_repositories_when_no_org_repositories_exist(self, mock_get_org_teams, mock_github_service): - mock_get_org_teams.return_value = [] - mock_github_service.get_org_repo_names.return_value = [] - repos = get_unowned_repositories(mock_github_service) - self.assertEqual(len(repos), 0) - mock_github_service.get_repository_collaborators.assert_not_called() - - @patch("services.github_service.GithubService") - @patch("bin.unowned_repositories.get_org_teams") - def test_get_unowned_repositories_when_repo_has_a_collaborator(self, mock_get_org_teams, mock_github_service): - mock_get_org_teams.return_value = [] - mock_github_service.get_repository_collaborators.return_value = [ - "some-collaborator"] - mock_github_service.get_org_repo_names.return_value = ["org-repo"] - repos = get_unowned_repositories(mock_github_service) - self.assertEqual(len(repos), 0) - mock_github_service.get_repository_collaborators.assert_called() - - @patch("services.github_service.GithubService") - @patch("bin.unowned_repositories.get_org_teams") - def test_get_unowned_repositories_when_repo_has_no_collaborators(self, mock_get_org_teams, mock_github_service): - mock_get_org_teams.return_value = [] - mock_github_service.get_repository_collaborators.return_value = [] - mock_github_service.get_org_repo_names.return_value = ["org-repo"] - repos = get_unowned_repositories(mock_github_service) - self.assertEqual(len(repos), 1) - mock_github_service.get_repository_collaborators.assert_called() - - @patch("services.github_service.GithubService") - @patch("bin.unowned_repositories.get_org_teams") - def test_get_unowned_repositories_when_no_matching_org_repository_exists_but_has_collaborators(self, mock_get_org_teams, mock_github_service): - team = { - "name": "some-team", - "repositories": ["some-repo"], - "number_of_users": 1 - } - mock_get_org_teams.return_value = [team] - mock_github_service.get_repository_collaborators.return_value = [ - "some-user"] - mock_github_service.get_org_repo_names.return_value = ["org-repo"] - repos = get_unowned_repositories(mock_github_service) - self.assertEqual(len(repos), 0) - mock_github_service.get_repository_collaborators.assert_called() - - @patch("services.github_service.GithubService") - @patch("bin.unowned_repositories.get_org_teams") - def test_get_unowned_repositories_when_repo_has_a_team(self, mock_get_org_teams, mock_github_service): - team = { - "name": "some-team", - "repositories": ["org-repo"], - "number_of_users": 1 - } - mock_get_org_teams.return_value = [team] - mock_github_service.get_repository_collaborators.return_value = [] - mock_github_service.get_org_repo_names.return_value = ["org-repo"] - repos = get_unowned_repositories(mock_github_service) - self.assertEqual(len(repos), 0) - mock_github_service.get_repository_collaborators.assert_not_called() - - @patch("services.github_service.GithubService") - @patch("bin.unowned_repositories.get_org_teams") - def test_repository_with_no_collaborators_and_no_direct_users(self, mock_get_org_teams, mock_github_service): - mock_get_org_teams.return_value = [] - mock_github_service.get_repository_collaborators.return_value = [] - mock_github_service.get_repository_direct_users.return_value = [] - mock_github_service.get_org_repo_names.return_value = ["org-repo"] - repos = get_unowned_repositories(mock_github_service) - self.assertEqual(len(repos), 1) - - @patch("services.github_service.GithubService") - @patch("bin.unowned_repositories.get_org_teams") - def test_repository_with_collaborators_but_no_direct_users(self, mock_get_org_teams, mock_github_service): - mock_get_org_teams.return_value = [] - mock_github_service.get_repository_collaborators.return_value = ["collaborator"] - mock_github_service.get_repository_direct_users.return_value = [] - mock_github_service.get_org_repo_names.return_value = ["org-repo"] - repos = get_unowned_repositories(mock_github_service) - self.assertEqual(len(repos), 0) - - @patch("services.github_service.GithubService") - @patch("bin.unowned_repositories.get_org_teams") - def test_repository_with_direct_users_but_no_collaborators(self, mock_get_org_teams, mock_github_service): - mock_get_org_teams.return_value = [] - mock_github_service.get_repository_collaborators.return_value = [] - mock_github_service.get_repository_direct_users.return_value = ["direct-user"] - mock_github_service.get_org_repo_names.return_value = ["org-repo"] - repos = get_unowned_repositories(mock_github_service) - self.assertEqual(len(repos), 0) - - @patch("services.slack_service.SlackService") - def test_send_slack_message(self, mock_slack_service): - send_slack_message(mock_slack_service, ["repo"]) - mock_slack_service.send_unowned_repos_slack_message.assert_called_with([ - "repo"]) - - -if __name__ == "__main__": - unittest.main() diff --git a/test/test_services/test_slack_service.py b/test/test_services/test_slack_service.py index 3c3113432..d7059d3bf 100644 --- a/test/test_services/test_slack_service.py +++ b/test/test_services/test_slack_service.py @@ -477,28 +477,6 @@ def test_send_github_rejoin_report(self): ) -@patch("slack_sdk.WebClient.__new__") -class SendUnownedReposAlertToOperationsEngineering(unittest.TestCase): - - def test_downstream_services_called(self, mock_slack_client: MagicMock): - repos = ["some-repo1", "some-repo2", "some-repo3"] - SlackService( - "").send_unowned_repos_slack_message(repos) - mock_slack_client.return_value.chat_postMessage.assert_called_with( - channel="C033QBE511V", - mrkdown=True, - blocks=[ - { - "type": "section", - "text": { - "type": "mrkdwn", - "text": '*Unowned Repositories Automation*\n\nRepositories on the GitHub Organisation that have no team or collaborator:\n[\'some-repo1\', \'some-repo2\', \'some-repo3\']' - } - } - ] - ) - - @patch('slack_sdk.WebClient.users_list') class GetAllSlackUsernamesTest(unittest.TestCase):