Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
e5a20eb
Welcome ASCII Art File Committed
Anubhav9 Jun 29, 2025
235775b
Constants File Committed
Anubhav9 Jun 29, 2025
edfdf05
Init Module Committed
Anubhav9 Jun 29, 2025
4bdf035
Sanity Check File Committed
Anubhav9 Jun 29, 2025
c54c7d9
Constants File with messages committed
Anubhav9 Jun 29, 2025
8e7900b
RedCoffee File Committed
Anubhav9 Jun 29, 2025
9ce18e4
Init File Committed for Diagnose Module
Anubhav9 Jun 29, 2025
ab6f04c
Bug Fix : If Condition Fixed
Anubhav9 Jun 29, 2025
bdb1204
Creation of RedCoffee Reports Directory
Anubhav9 Jun 29, 2025
5238692
Tests Modified to accommodate the new changes
Anubhav9 Jun 29, 2025
251be57
Changes to Welcome Banner File
Anubhav9 Jun 29, 2025
e474291
Constants File changed to add Tab
Anubhav9 Jun 29, 2025
f3258b9
Libraries for ASCII Art added
Anubhav9 Jun 29, 2025
261c0f3
Changes committed in setup py file
Anubhav9 Jun 29, 2025
00c25ee
Constants Message file updated
Anubhav9 Jun 29, 2025
bad5d8e
ChangeLog updated
Anubhav9 Jun 29, 2025
ab34359
Sanity File modified
Anubhav9 Jun 29, 2025
8b4d156
RedCoffee File committed
Anubhav9 Jun 29, 2025
d912dd1
Version Bumped up to v2.16
Anubhav9 Jun 29, 2025
b1f1a2a
Update redcoffee.py
Anubhav9 Jun 29, 2025
f6a1bb1
correct Code of Directory committed
Anubhav9 Jun 29, 2025
497d378
Modifications in RedCoffee File
Anubhav9 Jun 29, 2025
c76fb4a
RedCoffee File committed
Anubhav9 Jun 29, 2025
d6652d7
Documentation updated with new changes
Anubhav9 Jun 29, 2025
5ffedbb
Readme file updated
Anubhav9 Jun 29, 2025
608a095
Update README.md
Anubhav9 Jun 29, 2025
367037e
Coming soon added in ReadMe file
Anubhav9 Jun 29, 2025
a407c32
Missing full stop added
Anubhav9 Jun 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Directory | Usage |
`reports` | Controls the stuff related to look and feel of the generated reports. <br> i)`templating.py` - Code containing the structure of the report. <br> 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 |
--------------------------|---------------------------------------|
Expand Down Expand Up @@ -66,3 +67,24 @@ 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)

## 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.



1 change: 1 addition & 0 deletions ascii_art/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

2 changes: 2 additions & 0 deletions ascii_art/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
REDCOFFEE_BANNER_TEXT = "REDCOFFEE"
REDCOFFEE_SUB_TEXT = "\t\tCode Quality.Brewed Instantly"
17 changes: 17 additions & 0 deletions ascii_art/welcome_banner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
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)
colored_banner = colorama.Fore.RED + banner
print(colored_banner,end="")
sub_text = colorama.Fore.YELLOW + constants.REDCOFFEE_SUB_TEXT
print(sub_text)
print("\n")
print(colorama.Style.RESET_ALL)


2 changes: 1 addition & 1 deletion core/analyser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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", ""))
Expand Down
2 changes: 1 addition & 1 deletion core/utils/sonarqube_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand Down
1 change: 1 addition & 0 deletions diagnose/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

15 changes: 15 additions & 0 deletions diagnose/constants.py
Original file line number Diff line number Diff line change
@@ -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 anubhavsanyal9@gmail.com"
62 changes: 62 additions & 0 deletions diagnose/sanity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
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
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)
8 changes: 8 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
8 changes: 8 additions & 0 deletions docs/documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,11 @@ In this case as well, the protocol flag will take the priority.
Please use the below command

<pre>redcoffee generatepdf --host=${YOUR_SONARQUBE_HOST_NAME} --project=${SONARQUBE_PROJECT_KEY} --path=${PATH WHERE PDF FILE IS TO BE STORED} --token=${SONARQUBE_USER_TOKEN}</pre>

### 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.
24 changes: 24 additions & 0 deletions redcoffee.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()


Expand All @@ -20,14 +23,35 @@ 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)
print(pick_random_support_message())
if path != resolved_path:
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)
def diagnose(protocol,host,token):
welcome_banner.generate_welcome_banner()
protocol = general_utils.handle_protocol_for_every_communication(
protocol, host)
if host.startswith("http") or host.startswith("https"):
host = general_utils.remove_protocol(host)
sanity.check_all_functioning_parameters(protocol,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()
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ sentry_sdk==2.22.0
setuptools==75.1.0
python-dotenv
mkdocs
pyfiglet
colorama
8 changes: 5 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -27,7 +27,9 @@
'setuptools',
'ipinfo',
'sentry_sdk',
'python-dotenv'
'python-dotenv',
'pyfiglet',
'colorama'
],
entry_points='''
[console_scripts]
Expand Down
2 changes: 1 addition & 1 deletion tests/unit_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down
22 changes: 17 additions & 5 deletions utils/general_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from pathlib import Path
from urllib.parse import urlparse
import constants

import os

def check_and_validate_file_path(path):
"""
Expand All @@ -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:
Expand All @@ -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


Expand Down Expand Up @@ -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:
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}")