try sonar cube
This commit is contained in:
parent
ce17502029
commit
88f5a7241c
0
project/.env.sample
Normal file
0
project/.env.sample
Normal file
1
project/.gitignore
vendored
Normal file
1
project/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.env
|
8
project/.idea/.gitignore
vendored
Normal file
8
project/.idea/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
12
project/.idea/inspectionProfiles/Project_Default.xml
Normal file
12
project/.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="PyPep8NamingInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
|
<option name="ignoredErrors">
|
||||||
|
<list>
|
||||||
|
<option value="N802" />
|
||||||
|
</list>
|
||||||
|
</option>
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
6
project/.idea/inspectionProfiles/profiles_settings.xml
Normal file
6
project/.idea/inspectionProfiles/profiles_settings.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
7
project/.idea/misc.xml
Normal file
7
project/.idea/misc.xml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="Python 3.13 (project)" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (project)" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
8
project/.idea/modules.xml
Normal file
8
project/.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/project.iml" filepath="$PROJECT_DIR$/.idea/project.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
10
project/.idea/project.iml
Normal file
10
project/.idea/project.iml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="Python 3.13 (project)" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
6
project/.idea/vcs.xml
Normal file
6
project/.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
0
project/.scannerwork/.sonar_lock
Normal file
0
project/.scannerwork/.sonar_lock
Normal file
6
project/.scannerwork/report-task.txt
Normal file
6
project/.scannerwork/report-task.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
projectKey=Huanshere_VideoLingo
|
||||||
|
serverUrl=http://localhost:9000
|
||||||
|
serverVersion=25.1.0.102122
|
||||||
|
dashboardUrl=http://localhost:9000/dashboard?id=Huanshere_VideoLingo
|
||||||
|
ceTaskId=40f9d777-d9dd-4aa1-b034-825996aa9162
|
||||||
|
ceTaskUrl=http://localhost:9000/api/ce/task?id=40f9d777-d9dd-4aa1-b034-825996aa9162
|
175
project/main.py
Normal file
175
project/main.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import random
|
||||||
|
import zipfile
|
||||||
|
import io
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import shutil
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
# === LOAD CONFIGURATION FROM .env ===
|
||||||
|
if not os.path.exists(".env"):
|
||||||
|
print("Warning: .env file not found!")
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
GITHUB_API_URL = os.getenv("GITHUB_API_URL", "https://api.github.com")
|
||||||
|
SONAR_URL = os.getenv("SONAR_URL", "http://localhost:9000")
|
||||||
|
SONAR_TOKEN = os.getenv("SONAR_TOKEN")
|
||||||
|
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
|
||||||
|
|
||||||
|
HEADERS = {"Authorization": f"token {GITHUB_TOKEN}"} if GITHUB_TOKEN else {}
|
||||||
|
|
||||||
|
EXTRACT_DIR = "project_repo" # Directory to store the extracted repo
|
||||||
|
|
||||||
|
# === SUPPORTED LANGUAGES ===
|
||||||
|
LANGUAGES = ["PHP", "JavaScript", "Python"]
|
||||||
|
|
||||||
|
|
||||||
|
def check_github_rate_limit():
|
||||||
|
"""Check GitHub API rate limits."""
|
||||||
|
response = requests.get(f"{GITHUB_API_URL}/rate_limit", headers=HEADERS)
|
||||||
|
if response.status_code == 403:
|
||||||
|
print("GitHub API rate limit exceeded. Try again later.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_repo(language, min_stars=0, max_stars=100000):
|
||||||
|
search_url = f"{GITHUB_API_URL}/search/repositories?q=language:{language}+stars:{min_stars}..{max_stars}&sort=stars&order=desc"
|
||||||
|
response = requests.get(search_url, headers=HEADERS).json()
|
||||||
|
|
||||||
|
if "items" not in response or not response["items"]:
|
||||||
|
print("Error fetching repositories:", response)
|
||||||
|
return None
|
||||||
|
|
||||||
|
repo_list = response["items"]
|
||||||
|
if not repo_list:
|
||||||
|
print("No repositories found for the given criteria.")
|
||||||
|
return None
|
||||||
|
return random.choice(repo_list)["full_name"]
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_branch(repo):
|
||||||
|
url = f"{GITHUB_API_URL}/repos/{repo}"
|
||||||
|
response = requests.get(url, headers=HEADERS).json()
|
||||||
|
return response.get("default_branch", "main")
|
||||||
|
|
||||||
|
|
||||||
|
def download_and_extract_repo(repo):
|
||||||
|
branch = get_default_branch(repo)
|
||||||
|
zip_url = f"https://github.com/{repo}/archive/refs/heads/{branch}.zip"
|
||||||
|
|
||||||
|
response = requests.get(zip_url, headers=HEADERS)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print(f"Error downloading repository {repo}. HTTP {response.status_code}: {response.text}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
if os.path.exists(EXTRACT_DIR):
|
||||||
|
shutil.rmtree(EXTRACT_DIR)
|
||||||
|
|
||||||
|
with zipfile.ZipFile(io.BytesIO(response.content), "r") as zip_ref:
|
||||||
|
zip_ref.extractall(EXTRACT_DIR)
|
||||||
|
|
||||||
|
return os.path.join(EXTRACT_DIR, os.listdir(EXTRACT_DIR)[0])
|
||||||
|
|
||||||
|
|
||||||
|
def get_source_files(project_path, extensions):
|
||||||
|
source_files = []
|
||||||
|
for root, _, files in os.walk(project_path):
|
||||||
|
for file in files:
|
||||||
|
if file.endswith(tuple(extensions)):
|
||||||
|
source_files.append(os.path.join(root, file))
|
||||||
|
return source_files
|
||||||
|
|
||||||
|
|
||||||
|
def run_sonar_scanner(project_path, project_key):
|
||||||
|
if shutil.which("sonar-scanner") is None:
|
||||||
|
print("Error: SonarScanner is not installed.")
|
||||||
|
return
|
||||||
|
|
||||||
|
sonar_cmd = [
|
||||||
|
"sonar-scanner",
|
||||||
|
f"-Dsonar.projectKey={project_key}",
|
||||||
|
f"-Dsonar.sources={project_path}",
|
||||||
|
f"-Dsonar.host.url={SONAR_URL}",
|
||||||
|
f"-Dsonar.login={SONAR_TOKEN}",
|
||||||
|
"-Dsonar.scm.disabled=true",
|
||||||
|
"-Dsonar.exclusions=**/*.md"
|
||||||
|
]
|
||||||
|
|
||||||
|
print("Running SonarScanner...")
|
||||||
|
try:
|
||||||
|
subprocess.run(sonar_cmd, check=True)
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
print(f"SonarQube error skipped: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_latest_task_id(project_key):
|
||||||
|
url = f"{SONAR_URL}/api/ce/component?component={project_key}"
|
||||||
|
headers = {"Authorization": f"Bearer {SONAR_TOKEN}"}
|
||||||
|
response = requests.get(url, headers=headers).json()
|
||||||
|
return response.get("current", {}).get("id")
|
||||||
|
|
||||||
|
|
||||||
|
def wait_for_sonar_analysis(task_id, max_retries=15, delay=10):
|
||||||
|
url = f"{SONAR_URL}/api/ce/task?id={task_id}"
|
||||||
|
headers = {"Authorization": f"Bearer {SONAR_TOKEN}"}
|
||||||
|
|
||||||
|
for _ in range(max_retries):
|
||||||
|
response = requests.get(url, headers=headers).json()
|
||||||
|
status = response.get("task", {}).get("status", "PENDING")
|
||||||
|
if status == "SUCCESS":
|
||||||
|
print("SonarQube analysis completed successfully.")
|
||||||
|
return True
|
||||||
|
time.sleep(delay)
|
||||||
|
print("SonarQube analysis did not complete in time.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_sonar_metrics(project_key):
|
||||||
|
task_id = get_latest_task_id(project_key)
|
||||||
|
if not task_id or not wait_for_sonar_analysis(task_id):
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
url = f"{SONAR_URL}/api/measures/component"
|
||||||
|
metric_keys = "code_smells,bugs,vulnerabilities,security_hotspots,duplicated_lines_density,sqale_index,ncloc"
|
||||||
|
params = {"component": project_key, "metricKeys": metric_keys}
|
||||||
|
headers = {"Authorization": f"Bearer {SONAR_TOKEN}"}
|
||||||
|
|
||||||
|
response = requests.get(url, params=params, headers=headers)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if "component" in data and "measures" in data["component"]:
|
||||||
|
metrics = {m["metric"]: m.get("value", 0) for m in data["component"]["measures"]}
|
||||||
|
print("SonarQube Metrics:")
|
||||||
|
for key, value in metrics.items():
|
||||||
|
print(f" {key}: {value}")
|
||||||
|
return metrics.get("code_smells", 0), metrics.get("ncloc", 0)
|
||||||
|
|
||||||
|
print("Failed to fetch SonarQube metrics.")
|
||||||
|
return 0, 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
check_github_rate_limit()
|
||||||
|
selected_language = random.choice(LANGUAGES)
|
||||||
|
repo_name = get_random_repo(selected_language, min_stars=50, max_stars=10000)
|
||||||
|
|
||||||
|
if repo_name:
|
||||||
|
print(f"Selected {selected_language} Repository: {repo_name}")
|
||||||
|
project_path = download_and_extract_repo(repo_name)
|
||||||
|
|
||||||
|
if project_path:
|
||||||
|
print(f"Extracted to: {project_path}")
|
||||||
|
extensions = {"PHP": [".php"], "JavaScript": [".js"], "Python": [".py"]}
|
||||||
|
source_files = get_source_files(project_path, extensions[selected_language])
|
||||||
|
print(f"Found {len(source_files)} {selected_language} files")
|
||||||
|
|
||||||
|
project_key = repo_name.replace("/", "_")
|
||||||
|
run_sonar_scanner(project_path, project_key)
|
||||||
|
bad_smells, total_lines = get_sonar_metrics(project_key)
|
||||||
|
print(f"Total Bad Smells Detected: {bad_smells}")
|
||||||
|
print(f"Total Lines of Code: {total_lines}")
|
||||||
|
|
||||||
|
shutil.rmtree(EXTRACT_DIR, ignore_errors=True)
|
||||||
|
print("Cleanup completed!")
|
1
project/notes.txt
Normal file
1
project/notes.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Starred but Smelly? Investigating the link between GitHub Popularity and Code Quality.
|
Loading…
Reference in New Issue
Block a user