gh / app.py
adowu's picture
Update app.py
5bfba57 verified
raw
history blame
23.9 kB
import gradio as gr
from github import Github, GithubException
import os
import requests
import re
from collections import Counter
# Załaduj token z pliku .env lub ustaw bezpośrednio
GITHUB_TOKEN = os.getenv('GITHUB_TOKEN')
g = Github(GITHUB_TOKEN)
# Szablony projektów - definicje jako słowniki:
PROJECT_TEMPLATES = {
"flask": {
"files": {
"app.py": """from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, Flask!"
if __name__ == '__main__':
app.run(debug=True)""",
"requirements.txt": "Flask",
".gitignore": "__pycache__/\n*.pyc\nvenv/\ninstance/"
}
},
"react": {
"files": {
"package.json": """{
"name": "react-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.2.1",
"vite": "^5.0.8"
}}""",
"vite.config.js": """import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react()],
})""",
"index.html": """<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
</html>""",
"src/main.jsx": """import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>,
)""",
"src/App.jsx": """import React from 'react'
import './App.css'
function App() {
return (
<>
<h1>Hello from React!</h1>
</>
)
}
export default App""",
"src/index.css": """body {
margin: 0;
font-family: sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: monospace;
}""",
".gitignore": "node_modules/\ndist/"
}
}}
def get_file_content(owner, repo_name, path, branch="main"):
url = f"https://raw.githubusercontent.com/{owner}/{repo_name}/{branch}/{path}"
response = requests.get(url)
if response.status_code == 200:
return response.text
else:
return f"Błąd pobierania pliku: {response.status_code}"
def extract_repo_info(url):
"""Wyodrębnia nazwę użytkownika i repozytorium z linku GitHub."""
match = re.search(r"github\.com/([^/]+)/([^/]+)", url)
if match:
return match.group(1), match.group(2)
else:
return None, None
def analyze_file_content(content, file_path):
"""Analizuje zawartość pliku i zwraca statystyki."""
lines = content.splitlines()
word_count = sum(len(line.split()) for line in lines)
line_count = len(lines)
file_extension = file_path.split('.')[-1].lower() if '.' in file_path else "unknown"
return {
"line_count": line_count,
"word_count": word_count,
"file_extension": file_extension,
}
def github_tool(
action: str,
repo_name: str = None,
branch: str = "main", # domyślna gałąź
path: str = None,
content: str = None,
message: str = None,
owner: str = None,
vcs_url: str = None,
title: str = None,
body: str = None,
base: str = None,
head: str = None,
issue_number: int = None,
labels: str = None, # etykiety oddzielone przecinkami
tag: str = None,
name: str = None, # nazwa release
file_url: str = None, # URL pliku do pobrania,
repo_url: str = None, # Link do repozytorium
template_name: str = None, # Nazwa szablonu projektu
):
"""Narzędzie do zarządzania repozytoriami GitHub."""
user = g.get_user()
try:
if action == "import_repository":
if not all([owner, repo_name, vcs_url]):
raise ValueError(
"Brakujące parametry: owner, repo_name, vcs_url")
# Sprawdź, czy repozytorium już istnieje
try:
repo = user.get_repo(repo_name)
return "Repozytorium o tej nazwie już istnieje."
except GithubException:
pass
headers = {
'Authorization': f'token {GITHUB_TOKEN}',
'Accept': 'application/vnd.github.v3+json',
}
import_url = f'https://api.github.com/repos/{owner}/{repo_name}/import'
payload = {'vcs_url': vcs_url, 'vcs': 'git'}
response = requests.put(import_url, json=payload, headers=headers)
if response.status_code == 201:
return "Import repozytorium został rozpoczęty."
else:
return f"Błąd importu: {response.status_code}, {response.json()}"
elif action == "create_repository":
if not repo_name:
raise ValueError("Brakujący parametr: repo_name")
repo = user.create_repo(name=repo_name)
return f"Repozytorium **{repo_name}** utworzone! [Otwórz repozytorium]({repo.html_url})"
elif action == "create_project_from_template":
if not all([repo_name, template_name]):
raise ValueError("Brakujące parametry: repo_name, template_name")
if template_name not in PROJECT_TEMPLATES:
raise ValueError(f"Nieznany szablon projektu: {template_name}. Dostępne szablony: {', '.join(PROJECT_TEMPLATES.keys())}")
repo = user.create_repo(name=repo_name)
template = PROJECT_TEMPLATES[template_name]
for file_path, file_content in template["files"].items():
repo.create_file(file_path, f"Utworzenie {file_path} z szablonu {template_name}", file_content, branch="main")
return f"Repozytorium **{repo_name}** utworzone z szablonu **{template_name}**! [Otwórz repozytorium]({repo.html_url})"
elif action == "create_file":
if not all([repo_name, path, content, message]):
raise ValueError(
"Brakujące parametry: repo_name, path, content, message")
repo = user.get_repo(repo_name)
repo.create_file(path, message, content, branch=branch)
return f"Plik **`{path}`** utworzony w repozytorium **`{repo_name}`** na gałęzi **`{branch}`**."
elif action == "get_file":
if not all([repo_name, path]):
raise ValueError("Brakujące parametry: repo_name, path")
repo = user.get_repo(repo_name)
file_content = repo.get_contents(path, ref=branch)
return f"Zawartość pliku **`{path}`** z repozytorium **`{repo_name}`**:\n\n`\n{file_content.decoded_content.decode()}\n`" # Dodano formatowanie Markdown
elif action == "get_file_content_by_url":
if not file_url:
raise ValueError("Brakujący parametr: file_url")
response = requests.get(file_url)
response.raise_for_status() # Sprawdź czy nie ma błędu HTTP
return f"Zawartość pliku z URL **`{file_url}`**:\n\n`\n{response.text}\n`" # Dodano formatowanie Markdown
elif action == "delete_file":
if not all([repo_name, path]):
raise ValueError("Brakujące parametry: repo_name, path")
repo = user.get_repo(repo_name)
file_contents = repo.get_contents(path, ref=branch)
repo.delete_file(path, "Usunięcie pliku",
file_contents.sha, branch=branch)
return f"Plik **`{path}`** usunięty z repozytorium **`{repo_name}`** na gałęzi **`{branch}`**."
elif action == "update_file":
if not all([repo_name, path, content, message]):
raise ValueError(
"Brakujące parametry: repo_name, path, content, message")
repo = user.get_repo(repo_name)
file_contents = repo.get_contents(path, ref=branch)
repo.update_file(path, message, content,
file_contents.sha, branch=branch)
return f"Plik **`{path}`** zaktualizowany w repozytorium **`{repo_name}`** na gałęzi **`{branch}`**."
elif action == "list_branches":
if not repo_name:
raise ValueError("Brakujący parametr: repo_name")
repo = user.get_repo(repo_name)
branches = repo.get_branches()
branch_list = "\n".join([f"- `{branch.name}`" for branch in branches]) # Formatowanie Markdown
return f"Gałęzie w repozytorium **`{repo_name}`**:\n{branch_list}"
elif action == "create_branch":
if not all([repo_name, base, head]): # base jako źródło, head jako nowa nazwa
raise ValueError("Brakujące parametry: repo_name, base, head")
repo = user.get_repo(repo_name)
source_branch = repo.get_branch(base)
repo.create_git_ref(ref=f"refs/heads/{head}",
sha=source_branch.commit.sha)
return f"Gałąź **`{head}`** utworzona z **`{base}`** w repozytorium **`{repo_name}`**."
elif action == "delete_branch":
if not all([repo_name, branch]):
raise ValueError("Brakujące parametry: repo_name, branch")
repo = user.get_repo(repo_name)
repo.get_git_ref(f"heads/{branch}").delete()
return f"Gałąź **`{branch}`** usunięta z repozytorium **`{repo_name}`**."
elif action == "create_pull_request":
if not all([repo_name, title, body, base, head]):
raise ValueError(
"Brakujące parametry: repo_name, title, body, base, head")
repo = user.get_repo(repo_name)
pr = repo.create_pull(title=title, body=body, base=base, head=head)
return f"Pull request utworzony! [Otwórz Pull Request]({pr.html_url})"
elif action == "list_open_pull_requests":
if not repo_name:
raise ValueError("Brakujący parametr: repo_name")
repo = user.get_repo(repo_name)
open_prs = repo.get_pulls(state='open')
if not open_prs:
return f"Brak otwartych pull requestów w repozytorium **`{repo_name}`**."
prs_list = "\n".join([f"- [{pr.title}]({pr.html_url})" for pr in open_prs]) # Formatowanie Markdown
return f"Otwarte pull requesty w repozytorium **`{repo_name}`**:\n{prs_list}"
elif action == "create_issue":
if not all([repo_name, title, body]):
raise ValueError("Brakujące parametry: repo_name, title, body")
repo = user.get_repo(repo_name)
issue = repo.create_issue(title=title, body=body)
return f"Issue utworzone! [Otwórz Issue]({issue.html_url})"
elif action == "list_issues":
if not repo_name:
raise ValueError("Brakujący parametr: repo_name")
repo = user.get_repo(repo_name)
issues = repo.get_issues(state='open')
if not issues:
return f"Brak otwartych issues w repozytorium **`{repo_name}`**."
issues_list = "\n".join([f"- [{issue.title}]({issue.html_url})" for issue in issues]) # Formatowanie Markdown
return f"Otwarte issues w repozytorium **`{repo_name}`**:\n{issues_list}"
elif action == "add_label_to_issue":
if not all([repo_name, issue_number, labels]):
raise ValueError(
"Brakujące parametry: repo_name, issue_number, labels")
repo = user.get_repo(repo_name)
issue = repo.get_issue(number=int(issue_number))
for label in labels.split(","):
issue.add_to_labels(label.strip())
return f"Etykiety **`{labels}`** dodane do issue **#{issue_number}** w repozytorium **`{repo_name}`**."
elif action == "close_issue":
if not all([repo_name, issue_number]):
raise ValueError("Brakujące parametry: repo_name, issue_number")
repo = user.get_repo(repo_name)
issue = repo.get_issue(number=int(issue_number))
issue.edit(state='closed')
return f"Issue **#{issue_number}** zamknięte w repozytorium **`{repo_name}`**."
elif action == "add_comment_to_issue":
if not all([repo_name, issue_number, message]): # message jako treść komentarza
raise ValueError(
"Brakujące parametry: repo_name, issue_number, message")
repo = user.get_repo(repo_name)
issue = repo.get_issue(number=int(issue_number))
issue.create_comment(body=message)
return f"Komentarz dodany do issue **#{issue_number}** w repozytorium **`{repo_name}`**."
elif action == "create_release":
if not all([repo_name, tag, name, message]):
raise ValueError(
"Brakujące parametry: repo_name, tag, name, message")
repo = user.get_repo(repo_name)
release = repo.create_git_release(
tag=tag, name=name, message=message)
return f"Release **`{name}`** utworzone w repozytorium **`{repo_name}`**! [Otwórz Release]({release.html_url})"
elif action == "list_releases":
if not repo_name:
raise ValueError("Brakujący parametr: repo_name")
repo = user.get_repo(repo_name)
releases = repo.get_releases()
if not releases:
return f"Brak release'ów w repozytorium **`{repo_name}`**."
releases_list = "\n".join([f"- [{release.tag_name}]({release.html_url})" for release in releases]) # Formatowanie Markdown
return f"Releases w repozytorium **`{repo_name}`**:\n{releases_list}"
elif action == "fork_repository":
if not repo_name:
raise ValueError("Brakujący parametr: repo_name")
repo = g.get_repo(repo_name) # Pobierz repozytorium do forkowania
fork = user.create_fork(repo)
return f"Repozytorium **`{repo_name}`** zostało zforkowane! [Otwórz fork]({fork.html_url})"
elif action == "list_forks":
if not repo_name:
raise ValueError("Brakujący parametr: repo_name")
repo = g.get_repo(repo_name) # Pobierz repo, którego forki chcesz wyświetlić
forks = repo.get_forks()
if not forks:
return f"Brak forków repozytorium **`{repo_name}`**."
forks_list = "\n".join([f"- [{fork.full_name}]({fork.html_url})" for fork in forks]) # Formatowanie Markdown
return f"Linki do forków repozytorium **`{repo_name}`**:\n{forks_list}"
elif action == "list_files":
if not all([owner, repo_name]):
raise ValueError("Brakujące parametry: owner, repo_name")
repo = g.get_repo(f"{owner}/{repo_name}")
# Dodaj obsługę pustej ścieżki:
if not path:
contents = repo.get_contents("") # Pobierz zawartość głównego katalogu
else:
contents = repo.get_contents(path)
if not contents:
return f"Brak plików w ścieżce **`{path}`** repozytorium **`{repo_name}`**." # Komunikat, gdy brak plików
files_list = "\n".join([f"- [{content.name}]({content.download_url})" for content in contents]) # Formatowanie Markdown
return f"Pliki w ścieżce **`{path}`** repozytorium **`{repo_name}`**:\n{files_list}"
elif action == "get_repository_info":
if not all([owner, repo_name]):
raise ValueError("Brakujące parametry: owner, repo_name")
repo = g.get_repo(f"{owner}/{repo_name}")
info = {
"Nazwa": repo.name,
"Opis": repo.description,
"URL": repo.html_url,
"Właściciel": repo.owner.login,
"Gałąź domyślna": repo.default_branch,
"Język": repo.language,
"Liczba gwiazdek": repo.stargazers_count,
"Liczba forków": repo.forks_count,
"Utworzone": str(repo.created_at), # Konwersja datetime na string
"Ostatnia aktualizacja": str(repo.updated_at) # Konwersja datetime na string
}
info_md = "\n".join([f"- **{key}**: {value}" for key, value in info.items()]) # Formatowanie Markdown
return f"Informacje o repozytorium **`{repo_name}`**:\n{info_md}"
elif action == "get_file_content":
if not all([owner, repo_name, path]):
raise ValueError("Brakujące parametry: owner, repo_name, path")
content_text = get_file_content(owner, repo_name, path, branch)
return f"Zawartość pliku **`{path}`** z repozytorium **`{repo_name}`**:\n\n`\n{content_text}\n`" # Dodano formatowanie Markdown
elif action == "analyze_repository_by_url":
if not repo_url:
raise ValueError("Brakujący parametr: repo_url")
owner, repo_name = extract_repo_info(repo_url)
if not owner or not repo_name:
raise ValueError("Nieprawidłowy link do repozytorium")
try:
repo = g.get_repo(f"{owner}/{repo_name}")
# Pobierz listę plików i katalogów
contents = repo.get_contents("")
# Iteruj po liście i pobieraj zawartość plików
file_analyses = []
for content in contents:
if content.type == "file":
file_content = content.decoded_content.decode()
analysis = analyze_file_content(file_content, content.path) # ANALIZA PLIKU
file_analyses.append({
"name": content.name,
"path": content.path,
"analysis": analysis, # DODAJEMY WYNIKI ANALIZY
})
# Formatowanie wyjścia analizy (można dostosować)
analysis_md = "Analiza plików repozytorium:\n" + "\n".join([
f"- **{f['path']}**:\n"
f" - Liczba linii: {f['analysis']['line_count']}\n"
f" - Liczba słów: {f['analysis']['word_count']}\n"
f" - Rozszerzenie pliku: {f['analysis']['file_extension']}"
for f in file_analyses
])
return analysis_md
except GithubException as e:
return f"Błąd GitHub: {str(e)}"
raise ValueError(f"Nieznana akcja: {action}") # Correctly placed for unknown action handling
elif action == "analyze_repository_content":
if not all([owner, repo_name]):
raise ValueError("Brakujące parametry: owner, repo_name")
try:
repo = g.get_repo(f"{owner}/{repo_name}")
# Pobierz listę plików i katalogów
contents = repo.get_contents("")
file_analyses = []
# Iteruj po liście i pobieraj zawartość plików
for content in contents:
if content.type == "file":
file_content = get_file_content(owner, repo_name, content.path, branch)
analysis = analyze_file_content(file_content, content.path)
file_analyses.append({
"name": content.name,
"path": content.path,
"analysis": analysis,
})
# Formatowanie wyjścia analizy
analysis_md = "Analiza zawartości plików repozytorium:\n" + "\n".join([
f"- **{f['path']}**:\n"
f" - Liczba linii: {f['analysis']['line_count']}\n"
f" - Liczba słów: {f['analysis']['word_count']}\n"
f" - Rozszerzenie pliku: {f['analysis']['file_extension']}"
for f in file_analyses
])
return analysis_md
raise ValueError(f"Nieznana akcja: {action}")
except GithubException as e:
return f"**Błąd GitHub:** {str(e)}"
except ValueError as e:
return f"**Błąd:** {str(e)}"
except requests.exceptions.RequestException as e:
return f"**Błąd połączenia:** {str(e)}"
with gr.Blocks() as demo:
gr.Markdown("# Narzędzie GitHub Plugingit (Uproszczony Interfejs)")
with gr.Column():
action = gr.Dropdown(
choices=[
"import_repository",
"create_repository",
"create_project_from_template",
"create_file",
"get_file",
"get_file_content_by_url",
"delete_file",
"update_file",
"list_branches",
"create_branch",
"delete_branch",
"create_pull_request",
"list_open_pull_requests",
"create_issue",
"list_issues",
"add_label_to_issue",
"close_issue",
"add_comment_to_issue",
"create_release",
"list_releases",
"fork_repository",
"list_forks",
"list_files",
"get_repository_info",
"get_file_content",
"analyze_repository_by_url",
"analyze_repository_content",
],
label="Wybierz akcję",
)
repo_name = gr.Textbox(label="Nazwa repozytorium")
template_name = gr.Dropdown(
choices=list(PROJECT_TEMPLATES.keys()),
label="Szablon projektu",
allow_none=True,
)
branch = gr.Textbox(label="Gałąź", value="main")
path = gr.Textbox(label="Ścieżka do pliku")
content = gr.Code(label="Zawartość pliku", lines=5, language='python')
message = gr.Textbox(label="Wiadomość/Komentarz")
owner = gr.Textbox(label="Właściciel")
vcs_url = gr.Textbox(label="URL VCS")
title = gr.Textbox(label="Tytuł")
body = gr.Textbox(label="Treść")
base = gr.Textbox(label="Gałąź bazowa")
head = gr.Textbox(label="Gałąź docelowa/Nowa gałąź")
issue_number = gr.Number(label="Numer issue", precision=0)
labels = gr.Textbox(label="Etykiety (oddzielone przecinkami)")
tag = gr.Textbox(label="Tag")
release_name = gr.Textbox(label="Nazwa release")
file_url = gr.Textbox(label="URL pliku")
repo_url = gr.Textbox(label="Link do repozytorium")
run_button = gr.Button("Wykonaj")
output = gr.Markdown(label="Wynik")
run_button.click(
github_tool,
inputs=[
action,
repo_name,
branch,
path,
content,
message,
owner,
vcs_url,
title,
body,
base,
head,
issue_number,
labels,
tag,
release_name,
file_url,
repo_url,
template_name,
],
outputs=output,
api_name="github_tool" # API NAME JEST KLUCZOWE!
)
demo.launch()