From e5a20eb4817748d397aef8f840e2fe190edaf85f Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 07:58:30 +0530 Subject: [PATCH 01/28] Welcome ASCII Art File Committed --- ascii_art/welcome_banner.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 ascii_art/welcome_banner.py diff --git a/ascii_art/welcome_banner.py b/ascii_art/welcome_banner.py new file mode 100644 index 0000000..e6cb51d --- /dev/null +++ b/ascii_art/welcome_banner.py @@ -0,0 +1,14 @@ +import pyfiglet +import colorama +from ascii_art import constants + +def generate_welcome_banner(): + """ + Generate a welcome banner using pyfiglet and colorama. + """ + banner = pyfiglet.figlet_format(constants.REDCOFFEE_BANNER_TEXT,end="") + colored_banner = colorama.Fore.RED + banner + print(colored_banner) + sub_text = colorama.Fore.YELLOW + constants.REDCOFFEE_SUB_TEXT + print(sub_text) + From 235775b3d1d7412fdb8a62df0eb46ce3d0358f9f Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 07:59:14 +0530 Subject: [PATCH 02/28] Constants File Committed --- ascii_art/constants.py | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 ascii_art/constants.py diff --git a/ascii_art/constants.py b/ascii_art/constants.py new file mode 100644 index 0000000..deb1fb7 --- /dev/null +++ b/ascii_art/constants.py @@ -0,0 +1,2 @@ +REDCOFFEE_BANNER_TEXT = "REDCOFFEE" +REDCOFFEE_SUB_TEXT = "Code Quality.Brewed Instantly" From edfdf058915bfd5b615c0c2dcef4f852a6280aa3 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 07:59:37 +0530 Subject: [PATCH 03/28] Init Module Committed --- ascii_art/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 ascii_art/__init__.py diff --git a/ascii_art/__init__.py b/ascii_art/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/ascii_art/__init__.py @@ -0,0 +1 @@ + From 4bdf0357016d9e3ec8f7ebc276574cea13018298 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:00:49 +0530 Subject: [PATCH 04/28] Sanity Check File Committed --- diagnose/sanity.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 diagnose/sanity.py diff --git a/diagnose/sanity.py b/diagnose/sanity.py new file mode 100644 index 0000000..cba5049 --- /dev/null +++ b/diagnose/sanity.py @@ -0,0 +1,60 @@ +from diagnose import constants +import requests + +def check_all_functioning_parameters(protocol,host,token): + """ + Check if all the parameters are functioning correctly. + """ + total_checks = 3 + unsuccessful_checks = 0 + confirm_if_user_token = token.startswith("squ_") + if confirm_if_user_token: + print(constants.MESSAGE_BASE_SONARQUBE_USER_TOKEN) + else: + unsuccessful_checks += 1 + print(constants.MESSAGE_SONARQUBE_NOT_A_USER_TOKEN) + print(constants.MESSAGE_SONARQUBE_USER_TOKEN_HINT_MESSAGE) + print(constants.MESSAGE_SONARQUBE_USER_TOKEN_GENERATION_STEPS) + + try: + healthcheck_response= requests.get(f"{protocol}://{host}{constants.HEALTHCHECK_ENDPOINT}") + if healthcheck_response.status_code == 200: + status = healthcheck_response.json()["health"] + if status == "GREEN" or status == "YELLOW": + print(constants.MESSAGE_SONARQUBE_SERVER_REACHABLE) + authenticate_token = requests.get(f"{protocol}://{host}/api/authentication/validate",auth=(token,"")) + if authenticate_token.status_code == 200: + token_result = authenticate_token.json()["valid"] + if token_result == "true": + print(constants.MESSAGE_USER_TOKEN_VALIDATED) + else: + unsuccessful_checks += 1 + print(constants.MESSAGE_USER_TOKEN_SOMETHING_WRONG) + else: + unsuccessful_checks += 1 + authentication_status_code = authenticate_token.status_code + if authentication_status_code == 401: + print(constants.MESSAGE_USER_TOKEN_401) + elif authentication_status_code == 403: + print(constants.MESSAGE_USER_TOKEN_403) + else: + print(constants.MESSAGE_USER_TOKEN_SOMETHING_WRONG) + else: + unsuccessful_checks += 1 + print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE) + print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE_HINT_MESSAGE) + + print(f"Final Diagnosis: {total_checks - unsuccessful_checks} out of {total_checks} checks passed.") + except Exception as e: + print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE) + print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE_HINT_MESSAGE) + print(f"Diagnosis Failed. Only able to validate SonarQube User Token. Failed {unsuccessful_checks} and rest cannot be validated") + + +def connect_for_support(): + """ + Connect for support. Opening Github Issues or shooting an email + """ + print(constants.MESSAGE_SUPPORT_APOLOGIES) + print(constants.MESSAGE_SUPPORT_DIAGNOSE_COMMAND) + print(constants.MESSAGE_SUPPORT_EMAIL) From c54c7d9baf31569e885be6cb03ab14c0e50f2f57 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:01:33 +0530 Subject: [PATCH 05/28] Constants File with messages committed --- diagnose/constants.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 diagnose/constants.py diff --git a/diagnose/constants.py b/diagnose/constants.py new file mode 100644 index 0000000..7288161 --- /dev/null +++ b/diagnose/constants.py @@ -0,0 +1,15 @@ +HEALTHCHECK_ENDPOINT = "/api/system/health" +MESSAGE_BASE_SONARQUBE_USER_TOKEN = "✅ SonarQube User Token is being passed." +MESSAGE_SONARQUBE_NOT_A_USER_TOKEN = "❌ SonarQube User Token is not being passed." +MESSAGE_SONARQUBE_USER_TOKEN_HINT_MESSAGE = "SonarQube User Token starts with 'squ_'. User Token is required to generate the report." +MESSAGE_SONARQUBE_USER_TOKEN_GENERATION_STEPS = "To generate a new SonarQube User Token, please go to your SonarQube account and then navigate to User -> My Account -> Security -> Generate New Token and then select SonarQube User Token." +MESSAGE_SONARQUBE_SERVER_REACHABLE = "✅ SonarQube Server is reachable." +MESSAGE_SONARQUBE_SERVER_UNREACHABLE = "❌ SonarQube Server is unreachable." +MESSAGE_SONARQUBE_SERVER_UNREACHABLE_HINT_MESSAGE = "Please check if the SonarQube Server is reachable and if you have supplied the correct protocol and host. Sometimes VPNs can cause the issue of SonarQube Server being unreachable , so please try to connect to the SonarQube Server without VPN if possible" +MESSAGE_USER_TOKEN_VALIDATED = "✅ SonarQube User Token is valid." +MESSAGE_USER_TOKEN_SOMETHING_WRONG = "❌ Something went wrong while validating the SonarQube User Token. Please try again with a valid token." +MESSAGE_USER_TOKEN_401 = "❌ SonarQube User Token is invalid. We are getting response code 401 which validates that you are Unauthorised to access the SonarQube Server. Please try again with a valid token." +MESSAGE_USER_TOKEN_403 = "❌ SonarQube User Token is invalid. We are getting response code 403 which validates that you are Forbidden to access the SonarQube Server. Please try again with a valid token." +MESSAGE_SUPPORT_APOLOGIES = "🙂‍↕️ Please accept my heartiest apologies if RedCoffee is not working as expected for you. As a sole maintainer of this project, I am trying my best to make it work for everyone. If you are facing any issue, please raise an issue on our GitHub repository. https://github.com/Anubhav9/redcoffee/issues" +MESSAGE_SUPPORT_DIAGNOSE_COMMAND = "Did we check the redcoffee diagnose command? If not, please run the command again and check if the issue persists. If it does, please raise an issue on our GitHub repository" +MESSAGE_SUPPORT_EMAIL = "Still not sure what's happening? Please shoot an email to anubhav.9@gmail.com" From 8e7900bb6376ba374b58753ea4b169022d381b32 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:02:21 +0530 Subject: [PATCH 06/28] RedCoffee File Committed --- redcoffee.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/redcoffee.py b/redcoffee.py index ab39b50..dcc32a7 100644 --- a/redcoffee.py +++ b/redcoffee.py @@ -3,6 +3,9 @@ from dotenv import load_dotenv from utils import general_utils from core import analyser +from diagnose import sanity +from ascii_art import welcome_banner + load_dotenv() @@ -20,6 +23,7 @@ def cli(): @click.option("--protocol", type=click.Choice(["http", "https"], case_sensitive=False), required=False, help="The protocol that you want to enforce - HTTP or HTTPS") def generatepdf(host, project, path, token, protocol): + welcome_banner.generate_welcome_banner() resolved_path = str(general_utils.check_and_validate_file_path(path)) analyser.generate_final_report_and_transmit_to_sentry( resolved_path, host, project, token, protocol) @@ -27,7 +31,25 @@ def generatepdf(host, project, path, token, protocol): if path != resolved_path: print(warning_for_path_change(resolved_path)) +@click.command() +@click.option("--host", help="The host url where SonarQube server is running", required=True) +@click.option("--token", help="SonarQube Global Analysis Token", required=True) +@click.option("--protocol", type=click.Choice(["http", "https"], case_sensitive=False), required=False, + help="The protocol that you want to enforce - HTTP or HTTPS") +def diagnose(protocol,host,token): + welcome_banner.generate_welcome_banner() + new_host = general_utils.remove_protocol(host) + new_protocol = general_utils.handle_protocol_for_every_communication(protocol,new_host) + sanity.check_all_functioning_parameters(new_protocol,new_host,token) + +@click.command() +def support(): + welcome_banner.generate_welcome_banner() + sanity.connect_for_support() + cli.add_command(generatepdf) +cli.add_command(diagnose) +cli.add_command(support) if __name__ == "__main__": cli() From 9ce18e498ae6b31801404a7242f86756daf42f6a Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:08:53 +0530 Subject: [PATCH 07/28] Init File Committed for Diagnose Module --- diagnose/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 diagnose/__init__.py diff --git a/diagnose/__init__.py b/diagnose/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/diagnose/__init__.py @@ -0,0 +1 @@ + From ab6f04ca700d200faa20597952df2f1ee59bb765 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:11:02 +0530 Subject: [PATCH 08/28] Bug Fix : If Condition Fixed --- core/utils/sonarqube_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/utils/sonarqube_utils.py b/core/utils/sonarqube_utils.py index 04f6603..e0f63ee 100644 --- a/core/utils/sonarqube_utils.py +++ b/core/utils/sonarqube_utils.py @@ -8,7 +8,7 @@ def get_duplication_map(host_name, project_name, auth_token, protocol): protocol_type = handle_protocol_for_every_communication( protocol, host_name) - if host_name.startswith("http") or host_name.startswith("http"): + if host_name.startswith("http") or host_name.startswith("https"): host_name = general_utils.remove_protocol(host_name) DUPLICATION_URL = f"{protocol_type}{host_name}/api/measures/component_tree?component={project_name}&metricKeys=duplicated_lines" logging.info(f"Generated Duplication URL is :: {DUPLICATION_URL}") From bdb1204b4fec38920cdb5dcc40118eab9ce8f5af Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:12:28 +0530 Subject: [PATCH 09/28] Creation of RedCoffee Reports Directory --- utils/general_utils.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/utils/general_utils.py b/utils/general_utils.py index 82a2e6a..bd2d732 100644 --- a/utils/general_utils.py +++ b/utils/general_utils.py @@ -2,7 +2,7 @@ from pathlib import Path from urllib.parse import urlparse import constants - +import os def check_and_validate_file_path(path): """ @@ -12,7 +12,8 @@ def check_and_validate_file_path(path): path: The file path being provided by the user """ if path is None: - resolved_directory = Path.home() / "Downloads" / constants.FALLBACK_FILE_NAME + create_redcoffee_report_directory() + resolved_directory = Path.home() / "redcoffee-reports" / constants.FALLBACK_FILE_NAME return resolved_directory else: @@ -30,9 +31,8 @@ def check_and_validate_file_path(path): resolved_directory = path return resolved_directory else: - logging.info( - "Path does not exists, we will fallback to defaults") - resolved_directory = Path.home() / "Downloads" / constants.FALLBACK_FILE_NAME + create_redcoffee_report_directory() + resolved_directory = Path.home() / "redcoffee-reports" / constants.FALLBACK_FILE_NAME return resolved_directory @@ -72,3 +72,15 @@ def remove_protocol(url): """ parsed_url = urlparse(url) return parsed_url.netloc + parsed_url.path + + +def create_redcoffee_report_directory(): + """ + Create the redcoffee-reports directory + """ + try: + redcoffee_report_directory = os.mkdir(os.path.join(Path.home(),"redcoffee-reports"),exist_ok=True) + except Exception as e: + logging.info(f"Error creating redcoffee-reports directory: {e}") + + From 52386922945b3ce36b6345dc8aed44766990427c Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:13:34 +0530 Subject: [PATCH 10/28] Tests Modified to accommodate the new changes --- tests/unit_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit_test.py b/tests/unit_test.py index e52e348..5447290 100644 --- a/tests/unit_test.py +++ b/tests/unit_test.py @@ -63,7 +63,7 @@ def test_path_validation(path_name, case): if case == "file_name_wit_pdf": assert returned_result == "car-loan-report.pdf", "Mismatch in Path found" elif case == "invalid_directory_name" or case == "invalid_directory_name_ending_with_pdf": - resolved_path = Path.home() / "Downloads" / "generated-sonarqube-report.pdf" + resolved_path = Path.home() / "redcoffee-reports" / "generated-sonarqube-report.pdf" assert returned_result == resolved_path, "Mismatch in Path found" elif case == "random": print("Hello from Alaska"+ str(Path.home())) From 251be57eebb24c719bb8a74feb982166c13eb0ad Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:43:28 +0530 Subject: [PATCH 11/28] Changes to Welcome Banner File --- ascii_art/welcome_banner.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ascii_art/welcome_banner.py b/ascii_art/welcome_banner.py index e6cb51d..60c3bcc 100644 --- a/ascii_art/welcome_banner.py +++ b/ascii_art/welcome_banner.py @@ -6,9 +6,12 @@ def generate_welcome_banner(): """ Generate a welcome banner using pyfiglet and colorama. """ - banner = pyfiglet.figlet_format(constants.REDCOFFEE_BANNER_TEXT,end="") + banner = pyfiglet.figlet_format(constants.REDCOFFEE_BANNER_TEXT) colored_banner = colorama.Fore.RED + banner - print(colored_banner) + print(colored_banner,end="") sub_text = colorama.Fore.YELLOW + constants.REDCOFFEE_SUB_TEXT print(sub_text) + print("\n") + print(colorama.Style.RESET_ALL) + From e474291b9936fff5bef92220913c467aee6d6ac3 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:44:17 +0530 Subject: [PATCH 12/28] Constants File changed to add Tab --- ascii_art/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ascii_art/constants.py b/ascii_art/constants.py index deb1fb7..29c3ec4 100644 --- a/ascii_art/constants.py +++ b/ascii_art/constants.py @@ -1,2 +1,2 @@ REDCOFFEE_BANNER_TEXT = "REDCOFFEE" -REDCOFFEE_SUB_TEXT = "Code Quality.Brewed Instantly" +REDCOFFEE_SUB_TEXT = "\t\tCode Quality.Brewed Instantly" From f3258b9d32b903e6dad88cff66f04868721e0790 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:49:01 +0530 Subject: [PATCH 13/28] Libraries for ASCII Art added --- requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/requirements.txt b/requirements.txt index 51f5a3e..2ed201a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,3 +7,5 @@ sentry_sdk==2.22.0 setuptools==75.1.0 python-dotenv mkdocs +pyfiglet +colorama From 261c0f3dd3b094783ebbc586d339c11db60c4eb2 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 08:53:56 +0530 Subject: [PATCH 14/28] Changes committed in setup py file --- setup.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 52c501d..10d173a 100644 --- a/setup.py +++ b/setup.py @@ -11,14 +11,14 @@ setup( name='redcoffee', - version='2.15', + version='2.16', author="Anubhav Sanyal", description='A command-line tool to generate PDF for SonarQube Reports', long_description=README, long_description_content_type='text/markdown', # Change this if your README is not markdown packages=find_packages(), # Automatically find packages - py_modules=['redcoffee', 'styling', 'constants','support'], + py_modules=['redcoffee', 'styling', 'constants','support','ascii_art','diagnose'], install_requires=[ 'click', 'reportlab', @@ -27,7 +27,9 @@ 'setuptools', 'ipinfo', 'sentry_sdk', - 'python-dotenv' + 'python-dotenv', + 'pyfiglet', + 'colorama' ], entry_points=''' [console_scripts] From 00c25ee3a9b1aae4f70f8239f34a4129072ff1e6 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 09:05:41 +0530 Subject: [PATCH 15/28] Constants Message file updated --- diagnose/constants.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/diagnose/constants.py b/diagnose/constants.py index 7288161..7da2278 100644 --- a/diagnose/constants.py +++ b/diagnose/constants.py @@ -10,6 +10,6 @@ MESSAGE_USER_TOKEN_SOMETHING_WRONG = "❌ Something went wrong while validating the SonarQube User Token. Please try again with a valid token." MESSAGE_USER_TOKEN_401 = "❌ SonarQube User Token is invalid. We are getting response code 401 which validates that you are Unauthorised to access the SonarQube Server. Please try again with a valid token." MESSAGE_USER_TOKEN_403 = "❌ SonarQube User Token is invalid. We are getting response code 403 which validates that you are Forbidden to access the SonarQube Server. Please try again with a valid token." -MESSAGE_SUPPORT_APOLOGIES = "🙂‍↕️ Please accept my heartiest apologies if RedCoffee is not working as expected for you. As a sole maintainer of this project, I am trying my best to make it work for everyone. If you are facing any issue, please raise an issue on our GitHub repository. https://github.com/Anubhav9/redcoffee/issues" -MESSAGE_SUPPORT_DIAGNOSE_COMMAND = "Did we check the redcoffee diagnose command? If not, please run the command again and check if the issue persists. If it does, please raise an issue on our GitHub repository" -MESSAGE_SUPPORT_EMAIL = "Still not sure what's happening? Please shoot an email to anubhav.9@gmail.com" +MESSAGE_SUPPORT_APOLOGIES = "🙂‍↕️ Please accept my heartiest apologies if RedCoffee is not working as expected for you. As a sole maintainer of this project, I am trying my best to make it work for everyone. If you are facing any issue, please raise an issue on our GitHub repository - https://github.com/Anubhav9/redcoffee/issues" +MESSAGE_SUPPORT_DIAGNOSE_COMMAND = "❓ Did we check the redcoffee diagnose command? If not, please run the command again and check if the issue persists. If it does, please raise an issue on our GitHub repository" +MESSAGE_SUPPORT_EMAIL = "✍️ Still not sure what's happening? Please shoot an email to anubhavsanyal9@gmail.com" From bad5d8eda6f6ffc28120d26d71e863e8f6fd1836 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 09:07:21 +0530 Subject: [PATCH 16/28] ChangeLog updated --- docs/changelog.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/changelog.md b/docs/changelog.md index ae813b6..b23d36b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,13 @@ ## Change Log for RedCoffee +### Version 2.16 ( Released on June 29,2025): +* Inclusion of a new command : `redcoffee diagnose` - This does a sanity check whether the required infra/parameters needed by RedCoffee is up and running or not. This checks for SonarQube Connectivity and validity / authenticity of the provided SonarQube Token (which ideally should be the SonarQube user token). The user can get a hint of what is wrong with RedCoffee post running this command. +* Inclusion of a new command : `redcoffee support`- This encourages the end user to create a Github Issue in case they feel something is wrong with RedCoffee and needs an urgent attention. Alternatively, the maintener's email has been given as well , incase , someone wants to reach out directly. +* Addition of a new ASCII Banner. Users will be greeted with this ASCII art when they run any commands of RedCoffee. +* Bug Fix - Saw errors on Sentry regarding Invalid Path Supplied. This was happening because when no path was supplied, RedCoffee was trying to create reports in Downloads Directory. However, we were not checking if Downloads directory exists or not. Added the code to create a new directory called `redcoffee-reports` where RedCoffee reports will be stored in case user does not supply the required path. +* Minor Bug Fix - An if condition in the code was incorrect. This was however not creating any issues. But fixed it just in case. + + ### Version 2.15 ( Released on May 25,2025 ) * No Changes from a Customer POV. The Code / Repository structure has been modified to segregate components and follow the Single Responsibility Principle. * Better adherence to PEP8 structure. From ab3435918b9b414e590cf0f1c68c89bd707f0215 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 09:54:21 +0530 Subject: [PATCH 17/28] Sanity File modified --- diagnose/sanity.py | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/diagnose/sanity.py b/diagnose/sanity.py index cba5049..77971f9 100644 --- a/diagnose/sanity.py +++ b/diagnose/sanity.py @@ -1,37 +1,40 @@ from diagnose import constants import requests +from utils import general_utils def check_all_functioning_parameters(protocol,host,token): """ Check if all the parameters are functioning correctly. - """ + """ total_checks = 3 - unsuccessful_checks = 0 + successful_checks = 0 + unsuccessful_checks = 3 confirm_if_user_token = token.startswith("squ_") if confirm_if_user_token: print(constants.MESSAGE_BASE_SONARQUBE_USER_TOKEN) + successful_checks=successful_checks+1 else: - unsuccessful_checks += 1 + print(constants.MESSAGE_SONARQUBE_NOT_A_USER_TOKEN) print(constants.MESSAGE_SONARQUBE_USER_TOKEN_HINT_MESSAGE) print(constants.MESSAGE_SONARQUBE_USER_TOKEN_GENERATION_STEPS) try: - healthcheck_response= requests.get(f"{protocol}://{host}{constants.HEALTHCHECK_ENDPOINT}") + healthcheck_response= requests.get(f"{protocol}{host}{constants.HEALTHCHECK_ENDPOINT}",auth=(token,"")) if healthcheck_response.status_code == 200: status = healthcheck_response.json()["health"] if status == "GREEN" or status == "YELLOW": + successful_checks=successful_checks+1 print(constants.MESSAGE_SONARQUBE_SERVER_REACHABLE) - authenticate_token = requests.get(f"{protocol}://{host}/api/authentication/validate",auth=(token,"")) + authenticate_token = requests.get(f"{protocol}{host}/api/authentication/validate",auth=(token,"")) if authenticate_token.status_code == 200: token_result = authenticate_token.json()["valid"] - if token_result == "true": + if token_result == True: + successful_checks=successful_checks+1 print(constants.MESSAGE_USER_TOKEN_VALIDATED) else: - unsuccessful_checks += 1 print(constants.MESSAGE_USER_TOKEN_SOMETHING_WRONG) else: - unsuccessful_checks += 1 authentication_status_code = authenticate_token.status_code if authentication_status_code == 401: print(constants.MESSAGE_USER_TOKEN_401) @@ -40,15 +43,14 @@ def check_all_functioning_parameters(protocol,host,token): else: print(constants.MESSAGE_USER_TOKEN_SOMETHING_WRONG) else: - unsuccessful_checks += 1 print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE) print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE_HINT_MESSAGE) - print(f"Final Diagnosis: {total_checks - unsuccessful_checks} out of {total_checks} checks passed.") + print(f"Final Diagnosis: {successful_checks} out of {total_checks} checks passed.") except Exception as e: print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE) print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE_HINT_MESSAGE) - print(f"Diagnosis Failed. Only able to validate SonarQube User Token. Failed {unsuccessful_checks} and rest cannot be validated") + print(f"Diagnosis Failed. Only able to validate SonarQube User Token. Passed {successful_checks} and rest cannot be validated") def connect_for_support(): From 8b4d15654f4bde11fc922665bc233775e630a3cc Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 09:55:17 +0530 Subject: [PATCH 18/28] RedCoffee File committed --- redcoffee.py | 113 +++++++++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 53 deletions(-) diff --git a/redcoffee.py b/redcoffee.py index dcc32a7..77971f9 100644 --- a/redcoffee.py +++ b/redcoffee.py @@ -1,55 +1,62 @@ -import click -from support import pick_random_support_message, warning_for_path_change -from dotenv import load_dotenv +from diagnose import constants +import requests from utils import general_utils -from core import analyser -from diagnose import sanity -from ascii_art import welcome_banner -load_dotenv() - - -@click.group() -def cli(): - pass - - -@click.command() -@click.option("--host", help="The host url where SonarQube server is running", required=True) -@click.option("--project", help="Name of the Project Key that we want to search for in SonarQube report ", - required=True) -@click.option("--path", help="Path where we want to the PDF Report", required=False) -@click.option("--token", help="SonarQube Global Analysis Token", required=True) -@click.option("--protocol", type=click.Choice(["http", "https"], case_sensitive=False), required=False, - help="The protocol that you want to enforce - HTTP or HTTPS") -def generatepdf(host, project, path, token, protocol): - welcome_banner.generate_welcome_banner() - resolved_path = str(general_utils.check_and_validate_file_path(path)) - analyser.generate_final_report_and_transmit_to_sentry( - resolved_path, host, project, token, protocol) - print(pick_random_support_message()) - if path != resolved_path: - print(warning_for_path_change(resolved_path)) - -@click.command() -@click.option("--host", help="The host url where SonarQube server is running", required=True) -@click.option("--token", help="SonarQube Global Analysis Token", required=True) -@click.option("--protocol", type=click.Choice(["http", "https"], case_sensitive=False), required=False, - help="The protocol that you want to enforce - HTTP or HTTPS") -def diagnose(protocol,host,token): - welcome_banner.generate_welcome_banner() - new_host = general_utils.remove_protocol(host) - new_protocol = general_utils.handle_protocol_for_every_communication(protocol,new_host) - sanity.check_all_functioning_parameters(new_protocol,new_host,token) - -@click.command() -def support(): - welcome_banner.generate_welcome_banner() - sanity.connect_for_support() - - -cli.add_command(generatepdf) -cli.add_command(diagnose) -cli.add_command(support) -if __name__ == "__main__": - cli() +def check_all_functioning_parameters(protocol,host,token): + """ + Check if all the parameters are functioning correctly. + """ + total_checks = 3 + successful_checks = 0 + unsuccessful_checks = 3 + confirm_if_user_token = token.startswith("squ_") + if confirm_if_user_token: + print(constants.MESSAGE_BASE_SONARQUBE_USER_TOKEN) + successful_checks=successful_checks+1 + else: + + print(constants.MESSAGE_SONARQUBE_NOT_A_USER_TOKEN) + print(constants.MESSAGE_SONARQUBE_USER_TOKEN_HINT_MESSAGE) + print(constants.MESSAGE_SONARQUBE_USER_TOKEN_GENERATION_STEPS) + + try: + healthcheck_response= requests.get(f"{protocol}{host}{constants.HEALTHCHECK_ENDPOINT}",auth=(token,"")) + if healthcheck_response.status_code == 200: + status = healthcheck_response.json()["health"] + if status == "GREEN" or status == "YELLOW": + successful_checks=successful_checks+1 + print(constants.MESSAGE_SONARQUBE_SERVER_REACHABLE) + authenticate_token = requests.get(f"{protocol}{host}/api/authentication/validate",auth=(token,"")) + if authenticate_token.status_code == 200: + token_result = authenticate_token.json()["valid"] + if token_result == True: + successful_checks=successful_checks+1 + print(constants.MESSAGE_USER_TOKEN_VALIDATED) + else: + print(constants.MESSAGE_USER_TOKEN_SOMETHING_WRONG) + else: + authentication_status_code = authenticate_token.status_code + if authentication_status_code == 401: + print(constants.MESSAGE_USER_TOKEN_401) + elif authentication_status_code == 403: + print(constants.MESSAGE_USER_TOKEN_403) + else: + print(constants.MESSAGE_USER_TOKEN_SOMETHING_WRONG) + else: + print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE) + print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE_HINT_MESSAGE) + + print(f"Final Diagnosis: {successful_checks} out of {total_checks} checks passed.") + except Exception as e: + print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE) + print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE_HINT_MESSAGE) + print(f"Diagnosis Failed. Only able to validate SonarQube User Token. Passed {successful_checks} and rest cannot be validated") + + +def connect_for_support(): + """ + Connect for support. Opening Github Issues or shooting an email + """ + print(constants.MESSAGE_SUPPORT_APOLOGIES) + print(constants.MESSAGE_SUPPORT_DIAGNOSE_COMMAND) + print(constants.MESSAGE_SUPPORT_EMAIL) From d912dd1b68a9af60ec9f28d86e3d9a979eeaea9d Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 09:59:24 +0530 Subject: [PATCH 19/28] Version Bumped up to v2.16 --- core/analyser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/analyser.py b/core/analyser.py index 6bf8c5d..58c594d 100644 --- a/core/analyser.py +++ b/core/analyser.py @@ -16,7 +16,7 @@ load_dotenv() -redcoffee_current_version = "v2.15" +redcoffee_current_version = "v2.16" sentry_integration = SentryIntegration( os.environ.get("SENTRY_DSN_URL", ""), False, 1.0, 1.0) ipinfo_integration = IPInfoIntegration(os.environ.get("IP_INFO_ACCESS_TOKEN", "")) From b1f1a2a5da8827a48da40ca162dcb94048a58d75 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 10:14:49 +0530 Subject: [PATCH 20/28] Update redcoffee.py --- redcoffee.py | 113 ++++++++++++++++++++++++--------------------------- 1 file changed, 53 insertions(+), 60 deletions(-) diff --git a/redcoffee.py b/redcoffee.py index 77971f9..dcc32a7 100644 --- a/redcoffee.py +++ b/redcoffee.py @@ -1,62 +1,55 @@ -from diagnose import constants -import requests +import click +from support import pick_random_support_message, warning_for_path_change +from dotenv import load_dotenv from utils import general_utils +from core import analyser +from diagnose import sanity +from ascii_art import welcome_banner -def check_all_functioning_parameters(protocol,host,token): - """ - Check if all the parameters are functioning correctly. - """ - total_checks = 3 - successful_checks = 0 - unsuccessful_checks = 3 - confirm_if_user_token = token.startswith("squ_") - if confirm_if_user_token: - print(constants.MESSAGE_BASE_SONARQUBE_USER_TOKEN) - successful_checks=successful_checks+1 - else: - - print(constants.MESSAGE_SONARQUBE_NOT_A_USER_TOKEN) - print(constants.MESSAGE_SONARQUBE_USER_TOKEN_HINT_MESSAGE) - print(constants.MESSAGE_SONARQUBE_USER_TOKEN_GENERATION_STEPS) - - try: - healthcheck_response= requests.get(f"{protocol}{host}{constants.HEALTHCHECK_ENDPOINT}",auth=(token,"")) - if healthcheck_response.status_code == 200: - status = healthcheck_response.json()["health"] - if status == "GREEN" or status == "YELLOW": - successful_checks=successful_checks+1 - print(constants.MESSAGE_SONARQUBE_SERVER_REACHABLE) - authenticate_token = requests.get(f"{protocol}{host}/api/authentication/validate",auth=(token,"")) - if authenticate_token.status_code == 200: - token_result = authenticate_token.json()["valid"] - if token_result == True: - successful_checks=successful_checks+1 - print(constants.MESSAGE_USER_TOKEN_VALIDATED) - else: - print(constants.MESSAGE_USER_TOKEN_SOMETHING_WRONG) - else: - authentication_status_code = authenticate_token.status_code - if authentication_status_code == 401: - print(constants.MESSAGE_USER_TOKEN_401) - elif authentication_status_code == 403: - print(constants.MESSAGE_USER_TOKEN_403) - else: - print(constants.MESSAGE_USER_TOKEN_SOMETHING_WRONG) - else: - print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE) - print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE_HINT_MESSAGE) - - print(f"Final Diagnosis: {successful_checks} out of {total_checks} checks passed.") - except Exception as e: - print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE) - print(constants.MESSAGE_SONARQUBE_SERVER_UNREACHABLE_HINT_MESSAGE) - print(f"Diagnosis Failed. Only able to validate SonarQube User Token. Passed {successful_checks} and rest cannot be validated") - - -def connect_for_support(): - """ - Connect for support. Opening Github Issues or shooting an email - """ - print(constants.MESSAGE_SUPPORT_APOLOGIES) - print(constants.MESSAGE_SUPPORT_DIAGNOSE_COMMAND) - print(constants.MESSAGE_SUPPORT_EMAIL) +load_dotenv() + + +@click.group() +def cli(): + pass + + +@click.command() +@click.option("--host", help="The host url where SonarQube server is running", required=True) +@click.option("--project", help="Name of the Project Key that we want to search for in SonarQube report ", + required=True) +@click.option("--path", help="Path where we want to the PDF Report", required=False) +@click.option("--token", help="SonarQube Global Analysis Token", required=True) +@click.option("--protocol", type=click.Choice(["http", "https"], case_sensitive=False), required=False, + help="The protocol that you want to enforce - HTTP or HTTPS") +def generatepdf(host, project, path, token, protocol): + welcome_banner.generate_welcome_banner() + resolved_path = str(general_utils.check_and_validate_file_path(path)) + analyser.generate_final_report_and_transmit_to_sentry( + resolved_path, host, project, token, protocol) + print(pick_random_support_message()) + if path != resolved_path: + print(warning_for_path_change(resolved_path)) + +@click.command() +@click.option("--host", help="The host url where SonarQube server is running", required=True) +@click.option("--token", help="SonarQube Global Analysis Token", required=True) +@click.option("--protocol", type=click.Choice(["http", "https"], case_sensitive=False), required=False, + help="The protocol that you want to enforce - HTTP or HTTPS") +def diagnose(protocol,host,token): + welcome_banner.generate_welcome_banner() + new_host = general_utils.remove_protocol(host) + new_protocol = general_utils.handle_protocol_for_every_communication(protocol,new_host) + sanity.check_all_functioning_parameters(new_protocol,new_host,token) + +@click.command() +def support(): + welcome_banner.generate_welcome_banner() + sanity.connect_for_support() + + +cli.add_command(generatepdf) +cli.add_command(diagnose) +cli.add_command(support) +if __name__ == "__main__": + cli() From f6a1bb17f2e752a9e04a23fd327ca8c54a52a874 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 10:23:19 +0530 Subject: [PATCH 21/28] correct Code of Directory committed --- utils/general_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/general_utils.py b/utils/general_utils.py index bd2d732..dbbb030 100644 --- a/utils/general_utils.py +++ b/utils/general_utils.py @@ -79,7 +79,7 @@ def create_redcoffee_report_directory(): Create the redcoffee-reports directory """ try: - redcoffee_report_directory = os.mkdir(os.path.join(Path.home(),"redcoffee-reports"),exist_ok=True) + os.makedirs(os.path.join(Path.home(), "redcoffee-reports"), exist_ok=True) except Exception as e: logging.info(f"Error creating redcoffee-reports directory: {e}") From 497d378e2f0e9046e1bb4f59e55331732f2a9c49 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 10:26:33 +0530 Subject: [PATCH 22/28] Modifications in RedCoffee File --- redcoffee.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/redcoffee.py b/redcoffee.py index dcc32a7..f50b423 100644 --- a/redcoffee.py +++ b/redcoffee.py @@ -32,15 +32,20 @@ def generatepdf(host, project, path, token, protocol): print(warning_for_path_change(resolved_path)) @click.command() +@click.option("--protocol", type=click.Choice(["http", "https"], case_sensitive=False), required=False, + help="The protocol that you want to enforce - HTTP or HTTPS",default=None) @click.option("--host", help="The host url where SonarQube server is running", required=True) @click.option("--token", help="SonarQube Global Analysis Token", required=True) -@click.option("--protocol", type=click.Choice(["http", "https"], case_sensitive=False), required=False, - help="The protocol that you want to enforce - HTTP or HTTPS") def diagnose(protocol,host,token): welcome_banner.generate_welcome_banner() - new_host = general_utils.remove_protocol(host) - new_protocol = general_utils.handle_protocol_for_every_communication(protocol,new_host) - sanity.check_all_functioning_parameters(new_protocol,new_host,token) + if protocol is None: + protocol = general_utils.handle_protocol_for_every_communication( + protocol, host) + if host.startswith("http") or host.startswith("http"): + host = general_utils.remove_protocol(host) + host = general_utils.remove_protocol(host) + protocol = general_utils.handle_protocol_for_every_communication(protocol,host) + sanity.check_all_functioning_parameters(protocol,host,token) @click.command() def support(): From c76fb4adac3419b479c2182a5b2e94dae5b4dbdd Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 10:42:57 +0530 Subject: [PATCH 23/28] RedCoffee File committed --- redcoffee.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/redcoffee.py b/redcoffee.py index f50b423..2577992 100644 --- a/redcoffee.py +++ b/redcoffee.py @@ -38,13 +38,10 @@ def generatepdf(host, project, path, token, protocol): @click.option("--token", help="SonarQube Global Analysis Token", required=True) def diagnose(protocol,host,token): welcome_banner.generate_welcome_banner() - if protocol is None: - protocol = general_utils.handle_protocol_for_every_communication( + protocol = general_utils.handle_protocol_for_every_communication( protocol, host) - if host.startswith("http") or host.startswith("http"): + if host.startswith("http") or host.startswith("https"): host = general_utils.remove_protocol(host) - host = general_utils.remove_protocol(host) - protocol = general_utils.handle_protocol_for_every_communication(protocol,host) sanity.check_all_functioning_parameters(protocol,host,token) @click.command() From d6652d74edf7da63fdaffa7343618194cff63134 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 11:13:48 +0530 Subject: [PATCH 24/28] Documentation updated with new changes --- docs/documentation.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/documentation.md b/docs/documentation.md index 8ba4a9d..609a7d1 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -48,3 +48,11 @@ In this case as well, the protocol flag will take the priority. Please use the below command
redcoffee generatepdf --host=${YOUR_SONARQUBE_HOST_NAME} --project=${SONARQUBE_PROJECT_KEY} --path=${PATH WHERE PDF FILE IS TO BE STORED} --token=${SONARQUBE_USER_TOKEN}
+ +### Latest Command addition in Version 2.16 and above + +Pleased to introduce 2 new commands from v2.16 and onwards + +`redcoffee diagnose --host={host_name} --token={sonarqube_user_token}` - This is the healthcheck / sanity command. This makes sures that all the required infra / configurations needed for RedCoffee to be up and running are working fine. If you see something failing over here, its better to cross check the values being supplied. + +`redcoffee support` - This is the support command. After running the `redcoffee diagnose` command, if you still could not figure out whats wrong or you need additional support or you might have figured out a bug or you want to request for a new feature, this command provides you with instruction on how to raise it to the maintainer. From 5ffedbbd9ee24f9dd59b90d67484a291d97929cc Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 11:29:39 +0530 Subject: [PATCH 25/28] Readme file updated --- README.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README.md b/README.md index 75f2307..5b89f08 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ Directory | Usage | `reports` | Controls the stuff related to look and feel of the generated reports.
i)`templating.py` - Code containing the structure of the report.
ii) `utils` - Code / Utility functions essential for the report templating to be up and running. | `utils` | General Utility Functions. `tests` | Contains the Unit Tests. | +`ascii_art` | Contains the logic for the Welcome Banner | Files | Usage | --------------------------|---------------------------------------| @@ -66,3 +67,19 @@ redcoffee generatepdf --host=${YOUR_SONARQUBE_HOST_NAME} --project=${SONARQUBE_P Please visit the Github Page for this project to stay updated with the latest changes - [Github Page Documentation for RedCoffee](https://anubhav9.github.io/RedCoffee) +## Latest Updates + +Updates as of June 29,2025 ( v2.16 ) + +2 new commands have been added + +* `redcoffee support` - If you encounter a bug or you feel RedCoffee is not working as expected or you want to raise a new feature request, `redcoffee support` command will guide on how to reach out to the maintener of this project. + +![image](https://github.com/user-attachments/assets/019f262e-9527-499e-a05b-1b62f70ba135) + +* `redcoffee diagnose --host={host_name} --token={sonarqube_usertoken} ` - This is the basic healthcheck / sanity command. This helps you to diagnose if the required infra / configurations required for RedCoffee to be up and running are working fine or not. In case, you see something is failing in this command, I would please request to cross check the parameters being supplied. In case, it is still failing, please use the `redcoffee support` command to raise this issue to the maintener of this project + +![image](https://github.com/user-attachments/assets/340d8095-c530-4cb8-a1b0-6de5a22bb14f) + + + From 608a0958621506f17e9900ef7a91865f09ab102f Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 11:30:50 +0530 Subject: [PATCH 26/28] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b89f08..033510a 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Please visit the Github Page for this project to stay updated with the latest ch ## Latest Updates -Updates as of June 29,2025 ( v2.16 ) +Updates as of June 29,2025 - `v2.16` 2 new commands have been added From 367037e42b69719bb9293a85cf04b1ef3c299917 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 11:34:47 +0530 Subject: [PATCH 27/28] Coming soon added in ReadMe file --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 033510a..2f36031 100644 --- a/README.md +++ b/README.md @@ -81,5 +81,10 @@ Updates as of June 29,2025 - `v2.16` ![image](https://github.com/user-attachments/assets/340d8095-c530-4cb8-a1b0-6de5a22bb14f) +## Coming Soon + +* Ability to email the report , right from the generate command - Users will be able to email the report to the stakeholders right from the generate command. A new flag would be added for the same. +* Support for Jenkins - While Github Actions is already supported by RedCoffee, support for Jekins was a long due. The next version should have the support for Jenkins available. + From a407c329794447b87d9103061ce49fca408ed7c9 Mon Sep 17 00:00:00 2001 From: Anubhav Sanyal Date: Sun, 29 Jun 2025 11:35:17 +0530 Subject: [PATCH 28/28] Missing full stop added --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f36031..4196979 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Directory | Usage | `reports` | Controls the stuff related to look and feel of the generated reports.
i)`templating.py` - Code containing the structure of the report.
ii) `utils` - Code / Utility functions essential for the report templating to be up and running. | `utils` | General Utility Functions. `tests` | Contains the Unit Tests. | -`ascii_art` | Contains the logic for the Welcome Banner | +`ascii_art` | Contains the logic for the Welcome Banner. | Files | Usage | --------------------------|---------------------------------------|