|
import gradio as gr |
|
from github import Github, GithubException |
|
import os |
|
import requests |
|
import re |
|
from collections import Counter |
|
import jinja2 |
|
|
|
|
|
GITHUB_TOKEN = os.getenv('GITHUB_TOKEN') |
|
if not GITHUB_TOKEN: |
|
raise ValueError("GITHUB_TOKEN environment variable not set!") |
|
g = Github(GITHUB_TOKEN) |
|
|
|
|
|
PROJECT_TEMPLATES = { |
|
"flask": { |
|
"params": { |
|
"database_type": { |
|
"type": "choice", |
|
"default": "sqlite", |
|
"choices": ["sqlite", "postgresql", "mysql"], |
|
"description": "Type of database to configure for the Flask app." |
|
} |
|
}, |
|
"files": { |
|
"app.py": """from flask import Flask |
|
from flask_sqlalchemy import SQLAlchemy |
|
import os |
|
|
|
basedir = os.path.abspath(os.path.dirname(__file__)) |
|
|
|
app = Flask(__name__) |
|
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL') or \\ |
|
'{{database_uri}}' # Placeholder for database URI |
|
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False |
|
|
|
db = SQLAlchemy(app) |
|
|
|
class HelloWorld(db.Model): |
|
id = db.Column(db.Integer, primary_key=True) |
|
message = db.Column(db.String(128)) |
|
|
|
def __repr__(self): |
|
return f'<HelloWorld {self.message}>' |
|
|
|
@app.route('/') |
|
def hello(): |
|
return "Hello, Flask!" |
|
|
|
@app.route('/db_test') |
|
def db_test(): |
|
try: |
|
HelloWorld.query.first() # Simple DB query to test connection |
|
return "Database connection successful!" |
|
except Exception as e: |
|
return f"Database connection failed: {e}" |
|
|
|
|
|
if __name__ == '__main__': |
|
with app.app_context(): # Create application context for DB operations |
|
db.create_all() |
|
if not HelloWorld.query.first(): # Initialize DB with a default entry if empty |
|
default_hello = HelloWorld(message="Hello, Database!") |
|
db.session.add(default_hello) |
|
db.session.commit() |
|
app.run(debug=True)""", |
|
"requirements.txt": """Flask |
|
Flask-SQLAlchemy |
|
{{sqlalchemy_dependency}} # Placeholder for SQLAlchemy dependency""", |
|
".gitignore": "__pycache__/\n*.pyc\nvenv/\ninstance/\n*.db" |
|
}, |
|
"post_process": "install_dependencies" |
|
}, |
|
"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/" |
|
} |
|
}, |
|
"django": { |
|
"files": { |
|
"manage.py": """#!/usr/bin/env python |
|
import os |
|
import sys |
|
|
|
if __name__ == "__main__": |
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings") |
|
try: |
|
from django.core.management import execute_from_command_line |
|
except ImportError as exc: |
|
raise ImportError( |
|
"Couldn't import Django. Are you sure it is installed and " |
|
"available on your PYTHONPATH environment variable? Did you " |
|
"forget to activate a virtual environment?" |
|
) from exc |
|
execute_from_command_line(sys.argv)""", |
|
"myapp/settings.py": """import os |
|
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|
SECRET_KEY = 'your_secret_key_here' |
|
DEBUG = True |
|
ALLOWED_HOSTS = [] |
|
INSTALLED_APPS = [ |
|
'django.contrib.admin', |
|
'django.contrib.auth', |
|
'django.contrib.contenttypes', |
|
'django.contrib.sessions', |
|
'django.contrib.messages', |
|
'django.contrib.staticfiles', |
|
] |
|
MIDDLEWARE = [ |
|
'django.middleware.security.SecurityMiddleware', |
|
'django.contrib.sessions.middleware.SessionMiddleware', |
|
'django.middleware.common.CommonMiddleware', |
|
'django.middleware.csrf.CsrfViewMiddleware', |
|
'django.contrib.auth.middleware.AuthenticationMiddleware', |
|
'django.contrib.messages.middleware.MessageMiddleware', |
|
'django.middleware.clickjacking.XFrameOptionsMiddleware', |
|
] |
|
ROOT_URLCONF = 'myapp.urls' |
|
TEMPLATES = [ |
|
{ |
|
'BACKEND': 'django.template.backends.django.DjangoTemplates', |
|
'DIRS': [], |
|
'APP_DIRS': True, |
|
'OPTIONS': { |
|
'context_processors': [ |
|
'django.template.context_processors.debug', |
|
'django.template.context_processors.request', |
|
'django.contrib.auth.context_processors.auth', |
|
'django.contrib.messages.context_processors.messages', |
|
], |
|
}, |
|
}, |
|
] |
|
WSGI_APPLICATION = 'myapp.wsgi.application' |
|
DATABASES = { |
|
'default': { |
|
'ENGINE': 'django.db.backends.sqlite3', |
|
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), |
|
} |
|
} |
|
AUTH_PASSWORD_VALIDATORS = [ |
|
{'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',}, |
|
{'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',}, |
|
{'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',}, |
|
{'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',}, |
|
] |
|
LANGUAGE_CODE = 'en-us' |
|
TIME_ZONE = 'UTC' |
|
USE_I18N = True |
|
USE_L10N = True |
|
USE_TZ = True |
|
STATIC_URL = '/static/' |
|
""", |
|
"myapp/urls.py": """from django.contrib import admin |
|
from django.urls import path |
|
from django.http import HttpResponse |
|
|
|
def home(request): |
|
return HttpResponse("Hello, Django!") |
|
|
|
urlpatterns = [ |
|
path('admin/', admin.site.urls), |
|
path('', home, name='home'), |
|
]""", |
|
"myapp/wsgi.py": """import os |
|
from django.core.wsgi import get_wsgi_application |
|
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapp.settings") |
|
application = get_wsgi_application()""", |
|
"requirements.txt": "Django", |
|
".gitignore": "__pycache__/\n*.pyc\nvenv/\ndb.sqlite3\n", |
|
}, |
|
"rename_files": {"myapp": "{{repo_name_snake_case}}"} |
|
}, |
|
"nodejs_express": { |
|
"files": { |
|
"server.js": """const express = require('express') |
|
const app = express() |
|
const port = 3000 |
|
|
|
app.get('/', (req, res) => { |
|
res.send('Hello World from Express!') |
|
}) |
|
|
|
app.listen(port, () => { |
|
console.log(`Server listening on port ${port}`) |
|
})""", |
|
"package.json": """{ |
|
"name": "express-app", |
|
"version": "1.0.0", |
|
"description": "", |
|
"main": "server.js", |
|
"scripts": { |
|
"start": "node server.js" |
|
}, |
|
"dependencies": { |
|
"express": "^4.17.1" |
|
} |
|
}""", |
|
".gitignore": "node_modules/\n" |
|
} |
|
}, |
|
"static_website": { |
|
"files": { |
|
"index.html": """<!DOCTYPE html> |
|
<html> |
|
<head> |
|
<title>Simple Static Website</title> |
|
</head> |
|
<body> |
|
<h1>Hello from Static Website!</h1> |
|
<p>This is a basic HTML page.</p> |
|
</body> |
|
</html>""", |
|
"style.css": """body { |
|
font-family: sans-serif; |
|
margin: 20px; |
|
}""", |
|
"script.js": """console.log("Hello from JavaScript!");""", |
|
".gitignore": "" |
|
} |
|
}, |
|
"python_script": { |
|
"files": { |
|
"main.py": """def main(): |
|
print("Hello from Python script!") |
|
|
|
if __name__ == "__main__": |
|
main()""", |
|
"requirements.txt": "", |
|
".gitignore": "__pycache__/\n*.pyc\nvenv/\n" |
|
} |
|
}, |
|
"empty": { |
|
"files": { |
|
"README.md": "# {{repo_name}}", |
|
".gitignore": "" |
|
}, |
|
"rename_files": {"README.md": "{{readme_filename}}"} |
|
}, |
|
"shadcn": { |
|
"files": { |
|
"package.json": """{ |
|
"name": "shadcn-react-app", |
|
"private": true, |
|
"version": "0.0.0", |
|
"type": "module", |
|
"scripts": { |
|
"dev": "vite", |
|
"build": "vite build", |
|
"preview": "vite preview", |
|
"lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", |
|
"preview": "vite preview" |
|
}, |
|
"dependencies": { |
|
"@radix-ui/react-slot": "^1.0.2", |
|
"class-variance-authority": "^0.7.0", |
|
"clsx": "^2.1.0", |
|
"lucide-react": "^0.303.0", |
|
"react": "^18.2.0", |
|
"react-dom": "^18.2.0", |
|
"tailwind-merge": "^2.2.0", |
|
"tailwindcss-animate": "^1.0.7" |
|
}, |
|
"devDependencies": { |
|
"@vitejs/plugin-react": "^4.2.1", |
|
"autoprefixer": "^10.4.16", |
|
"eslint": "^8.55.0", |
|
"eslint-plugin-react": "^7.33.2", |
|
"eslint-plugin-react-hooks": "^4.6.0", |
|
"eslint-plugin-react-refresh": "^0.4.5", |
|
"postcss": "^8.4.33", |
|
"tailwindcss": "^3.4.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 + Shadcn</title> |
|
</head> |
|
<body class="bg-background"> |
|
<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 { Button } from "./components/ui/button" |
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./components/ui/card" |
|
|
|
|
|
function App() { |
|
return ( |
|
<div className="container mx-auto py-10"> |
|
<h1 className="text-3xl font-bold text-center mb-5"> |
|
Witaj w aplikacji Shadcn UI! |
|
</h1> |
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> |
|
<Card> |
|
<CardHeader> |
|
<CardTitle>Karta 1</CardTitle> |
|
<CardDescription>Prosta karta z Shadcn UI.</CardDescription> |
|
</CardHeader> |
|
<CardContent> |
|
<p>Zawartość karty.</p> |
|
<Button className="mt-4">Przycisk Akcji</Button> |
|
</CardContent> |
|
</Card> |
|
|
|
<Card> |
|
<CardHeader> |
|
<CardTitle>Karta 2</CardTitle> |
|
<CardDescription>Kolejna karta dla przykładu.</CardDescription> |
|
</CardHeader> |
|
<CardContent> |
|
<p>Więcej zawartości.</p> |
|
<Button variant="secondary" className="mt-4">Przycisk Wtórny</Button> |
|
</CardContent> |
|
</Card> |
|
|
|
<Card className="lg:col-span-1 md:col-span-2"> |
|
<CardHeader> |
|
<CardTitle>Dłuższa Karta</CardTitle> |
|
<CardDescription>Karta zajmująca więcej miejsca.</CardDescription> |
|
</CardHeader> |
|
<CardContent> |
|
<p> |
|
Ta karta demonstruje jak karty mogą dostosowywać się do różnych |
|
rozmiarów ekranów i układów. Shadcn UI i Tailwind CSS |
|
dają dużą elastyczność w projektowaniu interfejsów. |
|
</p> |
|
<Button variant="destructive" className="mt-4">Przycisk Destrukcyjny</Button> |
|
</CardContent> |
|
</Card> |
|
</div> |
|
</div> |
|
) |
|
} |
|
|
|
export default App""", |
|
"src/index.css": """@tailwind base; |
|
@tailwind components; |
|
@tailwind utilities; |
|
|
|
@layer base { |
|
:root { |
|
--background: 0 0% 100%; |
|
--foreground: 222.2 84.9% 4.9%; |
|
|
|
--card: 0 0% 100%; |
|
--card-foreground: 222.2 84.9% 4.9%; |
|
|
|
--popover: 0 0% 100%; |
|
--popover-foreground: 222.2 84.9% 4.9%; |
|
|
|
--primary: 221.2 83.2% 53.3%; |
|
--primary-foreground: 210 40% 98%; |
|
|
|
--secondary: 210 40% 96.1%; |
|
--secondary-foreground: 222.2 47.4% 11.2%; |
|
|
|
--muted: 210 40% 96.1%; |
|
--muted-foreground: 215.4 16.3% 46.9%; |
|
|
|
--accent: 210 40% 96.1%; |
|
--accent-foreground: 222.2 47.4% 11.2%; |
|
|
|
--destructive: 0 84.2% 60.2%; |
|
--destructive-foreground: 210 40% 98%; |
|
|
|
--border: 214.3 31.8% 91.4%; |
|
--input: 214.3 31.8% 91.4%; |
|
--ring: 221.2 83.2% 53.3%; |
|
|
|
--radius: 0.5rem; |
|
} |
|
|
|
.dark { |
|
--background: 222.2 84.9% 4.9%; |
|
--foreground: 210 40% 98%; |
|
|
|
--card: 222.2 84.9% 4.9%; |
|
--card-foreground: 210 40% 98%; |
|
|
|
--popover: 222.2 84.9% 4.9%; |
|
--popover-foreground: 210 40% 98%; |
|
|
|
--primary: 217.2 91.2% 59.8%; |
|
--primary-foreground: 222.2 47.4% 11.2%; |
|
|
|
--secondary: 217.2 32.6% 17.5%; |
|
--secondary-foreground: 210 40% 98%; |
|
|
|
--muted: 217.2 32.6% 17.5%; |
|
--muted-foreground: 215 20.2% 65.1%; |
|
|
|
--accent: 217.2 32.6% 17.5%; |
|
--accent-foreground: 210 40% 98%; |
|
|
|
--destructive: 0 62.8% 30.6%; |
|
--destructive-foreground: 210 40% 98%; |
|
|
|
--border: 217.2 32.6% 17.5%; |
|
--input: 217.2 32.6% 17.5%; |
|
--ring: 224.9 98.6% 67.3%; |
|
} |
|
} |
|
|
|
@layer components { |
|
.container { |
|
@apply max-w-7xl mx-auto px-4 sm:px-6 lg:px-8; |
|
} |
|
} |
|
""", |
|
"postcss.config.js": """module.exports = { |
|
plugins: { |
|
tailwindcss: {}, |
|
autoprefixer: {}, |
|
}, |
|
}""", |
|
"tailwind.config.js": """/** @type {import('tailwindcss').Config} */ |
|
module.exports = { |
|
darkMode: ["class"], |
|
content: [ |
|
'./pages/**/*.{js,jsx}', |
|
'./components/**/*.{js,jsx}', |
|
'./app/**/*.{js,jsx}', |
|
'./src/**/*.{js,jsx}', |
|
], |
|
prefix: "", |
|
theme: { |
|
container: { |
|
center: true, |
|
padding: "2rem", |
|
screens: { |
|
"2xl": "1400px", |
|
}, |
|
}, |
|
extend: { |
|
colors: { |
|
border: "hsl(var(--border))", |
|
input: "hsl(var(--input))", |
|
ring: "hsl(var(--ring))", |
|
background: "hsl(var(--background))", |
|
foreground: "hsl(var(--foreground))", |
|
primary: { |
|
DEFAULT: "hsl(var(--primary))", |
|
foreground: "hsl(var(--primary-foreground))", |
|
}, |
|
secondary: { |
|
DEFAULT: "hsl(var(--secondary))", |
|
foreground: "hsl(var(--secondary-foreground))", |
|
}, |
|
destructive: { |
|
DEFAULT: "hsl(var(--destructive))", |
|
foreground: "hsl(var(--destructive-foreground))", |
|
}, |
|
muted: { |
|
DEFAULT: "hsl(var(--muted))", |
|
foreground: "hsl(var(--muted-foreground))", |
|
}, |
|
accent: { |
|
DEFAULT: "hsl(var(--accent))", |
|
foreground: "hsl(var(--accent-foreground))", |
|
}, |
|
popover: { |
|
DEFAULT: "hsl(var(--popover))", |
|
foreground: "hsl(var(--popover-foreground))", |
|
}, |
|
card: { |
|
DEFAULT: "hsl(var(--card))", |
|
foreground: "hsl(var(--card-foreground))", |
|
}, |
|
}, |
|
borderRadius: { |
|
lg: "var(--radius)", |
|
md: "calc(var(--radius) - 2px)", |
|
sm: "calc(var(--radius) - 4px)", |
|
}, |
|
keyframes: { |
|
"accordion-down": { |
|
from: { height: "0" }, |
|
to: { height: "var(--radix-accordion-content-height)" }, |
|
}, |
|
"accordion-up": { |
|
from: { height: "var(--radix-accordion-content-height)" }, |
|
}, |
|
}, |
|
animation: { |
|
"accordion-down": "accordion-down 0.2s ease-out", |
|
"accordion-up": "accordion-up 0.2s ease-out", |
|
}, |
|
}, |
|
}, |
|
plugins: [require("tailwindcss-animate")], |
|
}""", |
|
"src/components/ui/button.jsx": """import * as React from "react" |
|
import { cn } from "@/lib/utils" |
|
import { Slot } from "@radix-ui/react-slot" |
|
import { cva } from "class-variance-authority"; |
|
|
|
const buttonVariants = cva( |
|
"inline-flex items-center justify-center rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", |
|
{ |
|
variants: { |
|
variant: { |
|
default: "bg-primary text-primary-foreground hover:bg-primary/90", |
|
destructive: |
|
"bg-destructive text-destructive-foreground hover:bg-destructive/90", |
|
outline: |
|
"border border-input bg-background hover:bg-accent hover:text-accent-foreground", |
|
secondary: |
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80", |
|
ghost: "hover:bg-accent hover:text-accent-foreground", |
|
link: "underline-offset-4 hover:underline text-primary", |
|
}, |
|
size: { |
|
default: "h-10 px-4 py-2", |
|
sm: "h-9 rounded-md px-3", |
|
lg: "h-11 rounded-md px-8", |
|
icon: "h-10 w-10", |
|
}, |
|
}, |
|
defaultVariants: { |
|
variant: "default", |
|
size: "default", |
|
}, |
|
} |
|
) |
|
|
|
const Button = React.forwardRef(({ className, variant, size, asChild = false, ...props }, ref) => { |
|
const Comp = asChild ? Slot : "button" |
|
return (<Comp |
|
className={cn(buttonVariants({ variant, size, className }))} |
|
ref={ref} {...props} />) |
|
}) |
|
Button.displayName = "Button" |
|
|
|
export { Button, buttonVariants }""", |
|
"src/components/ui/card.jsx": """import * as React from "react" |
|
import { cn } from "@/lib/utils" |
|
|
|
const Card = React.forwardRef(({ className, ...props }, ref) => (<div |
|
className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)} |
|
ref={ref} |
|
{...props} />)) |
|
Card.displayName = "Card" |
|
|
|
const CardHeader = React.forwardRef(({ className, ...props }, ref) => (<div |
|
className={cn("flex flex-col space-y-1.5 p-6", className)} |
|
ref={ref} |
|
{...props} />)) |
|
CardHeader.displayName = "CardHeader" |
|
|
|
const CardTitle = React.forwardRef(({ className, ...props }, ref) => (<h3 |
|
className={cn("text-lg font-semibold leading-none tracking-tight", className)} |
|
ref={ref} |
|
{...props} />)) |
|
CardTitle.displayName = "CardTitle" |
|
|
|
const CardDescription = React.forwardRef(({ className, ...props }, ref) => (<p |
|
className={cn("text-sm text-muted-foreground", className)} |
|
ref={ref} |
|
{...props} />)) |
|
CardDescription.displayName = "CardDescription" |
|
|
|
const CardContent = React.forwardRef(({ className, ...props }, ref) => (<div |
|
className={cn("p-6 pt-0", className)} |
|
ref={ref} |
|
{...props} />)) |
|
CardContent.displayName = "CardContent" |
|
|
|
const CardFooter = React.forwardRef(({ className, ...props }, ref) => (<div |
|
className={cn("flex items-center p-6 pt-0", className)} |
|
ref={ref} |
|
{...props} />)) |
|
CardFooter.displayName = "CardFooter" |
|
|
|
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }""", |
|
"src/lib/utils.js": """import { clsx } from "clsx" |
|
import { twMerge } from "tailwind-merge" |
|
|
|
export function cn(...inputs) { |
|
return twMerge(clsx(inputs)) |
|
}""" |
|
}, |
|
"post_process": "shadcn_setup" |
|
}, |
|
} |
|
|
|
|
|
def get_file_content(owner, repo_name, path, branch="main"): |
|
"""Fetches file content from a GitHub repository.""" |
|
url = f"https://raw.githubusercontent.com/{owner}/{repo_name}/{branch}/{path}" |
|
try: |
|
response = requests.get(url) |
|
response.raise_for_status() |
|
return response.text |
|
except requests.exceptions.RequestException as e: |
|
return f"Error fetching file: {e}" |
|
|
|
|
|
def extract_repo_info(url): |
|
"""Extracts owner and repo name from a GitHub URL.""" |
|
match = re.search(r"github\.com/([^/]+)/([^/]+)", url) |
|
return match.group(1), match.group(2) if match else (None, None) |
|
|
|
|
|
def analyze_file_content(content, file_path): |
|
"""Analyzes file content and returns statistics.""" |
|
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", |
|
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, |
|
tag: str = None, |
|
name: str = None, |
|
file_url: str = None, |
|
repo_url: str = None, |
|
template_name: str = None, |
|
template_params: dict = None |
|
): |
|
"""Manages GitHub repositories.""" |
|
user = g.get_user() |
|
|
|
try: |
|
if action == "import_repository": |
|
if not all([owner, repo_name, vcs_url]): |
|
raise ValueError("Missing parameters: owner, repo_name, vcs_url") |
|
try: |
|
user.get_repo(repo_name) |
|
return "Repository already exists." |
|
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' |
|
response = requests.put(import_url, json={'vcs_url': vcs_url, 'vcs': 'git'}, headers=headers) |
|
response.raise_for_status() |
|
return "Repository import started." |
|
|
|
elif action == "create_repository": |
|
if not repo_name: |
|
raise ValueError("Missing parameter: repo_name") |
|
repo = user.create_repo(name=repo_name) |
|
return f"Repository **{repo_name}** created! [Open repository]({repo.html_url})" |
|
|
|
elif action == "create_project_from_template": |
|
if not all([repo_name, template_name]): |
|
raise ValueError("Missing parameters: repo_name, template_name") |
|
if template_name not in PROJECT_TEMPLATES: |
|
raise ValueError( |
|
f"Unknown template: {template_name}. Available: {', '.join(PROJECT_TEMPLATES.keys())}" |
|
) |
|
repo = user.create_repo(name=repo_name) |
|
template = PROJECT_TEMPLATES[template_name] |
|
|
|
|
|
template_params_final = {} |
|
if "params" in template: |
|
for param_name, param_config in template["params"].items(): |
|
template_params_final[param_name] = template_params.get(param_name, |
|
param_config.get("default")) if template_params else param_config.get( |
|
"default") |
|
|
|
|
|
repo_name_snake_case = repo_name.replace("-", "_") |
|
readme_filename = "README.md" |
|
|
|
env = jinja2.Environment() |
|
|
|
for file_path, file_content in template["files"].items(): |
|
final_file_path = file_path |
|
final_file_content = file_content |
|
|
|
|
|
if "rename_files" in template: |
|
for old_name, new_name_template in template["rename_files"].items(): |
|
if file_path == old_name: |
|
final_file_path = new_name_template.replace("{{repo_name_snake_case}}", |
|
repo_name_snake_case).replace("{{repo_name}}", |
|
repo_name).replace( |
|
"{{readme_filename}}", readme_filename) |
|
|
|
|
|
template_render = env.from_string(final_file_content) |
|
final_file_content = template_render.render( |
|
repo_name=repo_name, |
|
repo_name_snake_case=repo_name_snake_case, |
|
readme_filename=readme_filename, |
|
**template_params_final |
|
) |
|
|
|
repo.create_file( |
|
final_file_path, |
|
f"Create {final_file_path} from template {template_name}", |
|
final_file_content, |
|
branch="main", |
|
) |
|
|
|
|
|
if "post_process" in template: |
|
post_process_action = template["post_process"] |
|
if post_process_action == "install_dependencies": |
|
print( |
|
f"Post-processing: install_dependencies for {repo_name} (Not fully implemented in this example).") |
|
elif post_process_action == "shadcn_setup": |
|
instructions = f""" |
|
**Konfiguracja Shadcn UI wymaga dodatkowych kroków po stronie klienta!** |
|
|
|
1. **Sklonuj repozytorium:** `git clone https://github.com/{user.login}/{repo_name}.git` |
|
2. **Przejdź do katalogu projektu:** `cd {repo_name}` |
|
3. **Zainstaluj zależności:** `npm install` (lub `yarn install`, `pnpm install`) |
|
4. **Zainicjuj Shadcn UI:** `npx shadcn-ui@latest init` |
|
5. **Uruchom aplikację:** `npm run dev` (lub odpowiednia komenda dla Twojego menedżera pakietów) |
|
|
|
Po wykonaniu tych kroków, Twoja aplikacja Shadcn UI będzie w pełni skonfigurowana i gotowa do rozwoju. |
|
""" |
|
return f"Repository **{repo_name}** created from template **{template_name}**! [Open repository]({repo.html_url})\n\n{instructions}" |
|
else: |
|
print(f"Unknown post-process action: {post_process_action}") |
|
|
|
return f"Repository **{repo_name}** created from template **{template_name}**! [Open repository]({repo.html_url})" |
|
|
|
elif action == "create_file": |
|
if not all([repo_name, path, content, message]): |
|
raise ValueError("Missing parameters: repo_name, path, content, message") |
|
repo = user.get_repo(repo_name) |
|
repo.create_file(path, message, content, branch=branch) |
|
return f"File **`{path}`** created in repository **`{repo_name}`** on branch **`{branch}`**." |
|
|
|
elif action == "get_file": |
|
if not all([repo_name, path]): |
|
raise ValueError("Missing parameters: repo_name, path") |
|
repo = user.get_repo(repo_name) |
|
file_content = repo.get_contents(path, ref=branch) |
|
return ( |
|
f"File content of **`{path}`** from **`{repo_name}`**:\n\n`\n{file_content.decoded_content.decode()}\n`" |
|
) |
|
|
|
elif action == "get_file_content_by_url": |
|
if not file_url: |
|
raise ValueError("Missing parameter: file_url") |
|
file_content = get_file_content(None, None, None, None) |
|
return f"File content from URL **`{file_url}`**:\n\n`\n{file_content}\n`" |
|
|
|
elif action == "delete_file": |
|
if not all([repo_name, path]): |
|
raise ValueError("Missing parameters: repo_name, path") |
|
repo = user.get_repo(repo_name) |
|
file_contents = repo.get_contents(path, ref=branch) |
|
repo.delete_file(path, "Delete file", file_contents.sha, branch=branch) |
|
return f"File **`{path}`** deleted from repository **`{repo_name}`** on branch **`{branch}`**." |
|
|
|
elif action == "update_file": |
|
if not all([repo_name, path, content, message]): |
|
raise ValueError("Missing parameters: 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"File **`{path}`** updated in repository **`{repo_name}`** on branch **`{branch}`**." |
|
|
|
elif action == "list_branches": |
|
if not repo_name: |
|
raise ValueError("Missing parameter: repo_name") |
|
repo = user.get_repo(repo_name) |
|
branches = repo.get_branches() |
|
branch_list = "\n".join([f"- `{branch.name}`" for branch in branches]) |
|
return f"Branches in repository **`{repo_name}`**:\n{branch_list}" |
|
|
|
elif action == "create_branch": |
|
if not all([repo_name, base, head]): |
|
raise ValueError("Missing parameters: 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"Branch **`{head}`** created from **`{base}`** in repository **`{repo_name}`**." |
|
|
|
elif action == "delete_branch": |
|
if not all([repo_name, branch]): |
|
raise ValueError("Missing parameters: repo_name, branch") |
|
repo = user.get_repo(repo_name) |
|
repo.get_git_ref(f"heads/{branch}").delete() |
|
return f"Branch **`{branch}`** deleted from repository **`{repo_name}`**." |
|
|
|
elif action == "create_pull_request": |
|
if not all([repo_name, title, body, base, head]): |
|
raise ValueError("Missing parameters: 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 created! [Open Pull Request]({pr.html_url})" |
|
|
|
elif action == "list_open_pull_requests": |
|
if not repo_name: |
|
raise ValueError("Missing parameter: repo_name") |
|
repo = user.get_repo(repo_name) |
|
open_prs = repo.get_pulls(state='open') |
|
if not open_prs: |
|
return f"No open pull requests in repository **`{repo_name}`**." |
|
prs_list = "\n".join([f"- [{pr.title}]({pr.html_url})" for pr in open_prs]) |
|
return f"Open pull requests in repository **`{repo_name}`**:\n{prs_list}" |
|
|
|
elif action == "create_issue": |
|
if not all([repo_name, title, body]): |
|
raise ValueError("Missing parameters: repo_name, title, body") |
|
repo = user.get_repo(repo_name) |
|
issue = repo.create_issue(title=title, body=body) |
|
return f"Issue created! [Open Issue]({issue.html_url})" |
|
|
|
elif action == "list_issues": |
|
if not repo_name: |
|
raise ValueError("Missing parameter: repo_name") |
|
repo = user.get_repo(repo_name) |
|
issues = repo.get_issues(state='open') |
|
if not issues: |
|
return f"No open issues in repository **`{repo_name}`**." |
|
issues_list = "\n".join([f"- [{issue.title}]({issue.html_url})" for issue in issues]) |
|
return f"Open issues in repository **`{repo_name}`**:\n{issues_list}" |
|
|
|
elif action == "add_label_to_issue": |
|
if not all([repo_name, issue_number, labels]): |
|
raise ValueError("Missing parameters: 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"Labels **`{labels}`** added to issue **#{issue_number}** in repository **`{repo_name}`**." |
|
) |
|
|
|
elif action == "close_issue": |
|
if not all([repo_name, issue_number]): |
|
raise ValueError("Missing parameters: 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}** closed in repository **`{repo_name}`**." |
|
|
|
elif action == "add_comment_to_issue": |
|
if not all([repo_name, issue_number, message]): |
|
raise ValueError("Missing parameters: 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"Comment added to issue **#{issue_number}** in repository **`{repo_name}`**." |
|
|
|
elif action == "create_release": |
|
if not all([repo_name, tag, name, message]): |
|
raise ValueError("Missing parameters: 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}`** created in repository **`{repo_name}`**! [Open Release]({release.html_url})" |
|
) |
|
|
|
elif action == "list_releases": |
|
if not repo_name: |
|
raise ValueError("Missing parameter: repo_name") |
|
repo = user.get_repo(repo_name) |
|
releases = repo.get_releases() |
|
if not releases: |
|
return f"No releases in repository **`{repo_name}`**." |
|
releases_list = "\n".join([f"- [{release.tag_name}]({release.html_url})" for release in releases]) |
|
return f"Releases in repository **`{repo_name}`**:\n{releases_list}" |
|
|
|
elif action == "fork_repository": |
|
if not repo_name: |
|
raise ValueError("Missing parameter: repo_name") |
|
repo = g.get_repo(repo_name) |
|
fork = user.create_fork(repo) |
|
return f"Repository **`{repo_name}`** forked! [Open fork]({fork.html_url})" |
|
|
|
elif action == "list_forks": |
|
if not repo_name: |
|
raise ValueError("Missing parameter: repo_name") |
|
repo = g.get_repo(repo_name) |
|
forks = repo.get_forks() |
|
if not forks: |
|
return f"No forks of repository **`{repo_name}`**." |
|
forks_list = "\n".join([f"- [{fork.full_name}]({fork.html_url})" for fork in forks]) |
|
return f"Forks of repository **`{repo_name}`**:\n{forks_list}" |
|
|
|
elif action == "list_files": |
|
if not all([owner, repo_name]): |
|
raise ValueError("Missing parameters: owner, repo_name") |
|
repo = g.get_repo(f"{owner}/{repo_name}") |
|
contents = repo.get_contents("" if not path else path) |
|
if not contents: |
|
return f"No files in path **`{path}`** of repository **`{repo_name}`**." |
|
files_list = "\n".join([f"- [{content.name}]({content.download_url})" for content in contents]) |
|
return f"Files in path **`{path}`** of repository **`{repo_name}`**:\n{files_list}" |
|
|
|
elif action == "get_repository_info": |
|
if not all([owner, repo_name]): |
|
raise ValueError("Missing parameters: owner, repo_name") |
|
repo = g.get_repo(f"{owner}/{repo_name}") |
|
info = { |
|
"Name": repo.name, |
|
"Description": repo.description, |
|
"URL": repo.html_url, |
|
"Owner": repo.owner.login, |
|
"Default branch": repo.default_branch, |
|
"Language": repo.language, |
|
"Stars": repo.stargazers_count, |
|
"Forks": repo.forks_count, |
|
"Created at": str(repo.created_at), |
|
"Last updated": str(repo.updated_at), |
|
} |
|
info_md = "\n".join([f"- **{key}**: {value}" for key, value in info.items()]) |
|
return f"Repository info for **`{repo_name}`**:\n{info_md}" |
|
|
|
elif action == "get_file_content": |
|
if not all([owner, repo_name, path]): |
|
raise ValueError("Missing parameters: owner, repo_name, path") |
|
content_text = get_file_content(owner, repo_name, path, branch) |
|
return ( |
|
f"File content of **`{path}`** from repository **`{repo_name}`**:\n\n`\n{content_text}\n`" |
|
) |
|
|
|
elif action == "analyze_repository_by_url": |
|
if not repo_url: |
|
raise ValueError("Missing parameter: repo_url") |
|
owner, repo_name = extract_repo_info(repo_url) |
|
if not owner or not repo_name: |
|
raise ValueError("Invalid repository URL") |
|
|
|
repo = g.get_repo(f"{owner}/{repo_name}") |
|
contents = repo.get_contents("") |
|
file_analyses = [] |
|
for content in contents: |
|
if content.type == "file": |
|
file_content = content.decoded_content.decode() |
|
analysis = analyze_file_content(file_content, content.path) |
|
file_analyses.append({ |
|
"name": content.name, |
|
"path": content.path, |
|
"analysis": analysis, |
|
}) |
|
analysis_md = "Repository file analysis:\n" + "\n".join([ |
|
f"- **{f['path']}**:\n" |
|
f" - Lines: {f['analysis']['line_count']}\n" |
|
f" - Words: {f['analysis']['word_count']}\n" |
|
f" - Extension: {f['analysis']['file_extension']}" |
|
for f in file_analyses |
|
]) |
|
return analysis_md |
|
|
|
elif action == "analyze_repository_content": |
|
if not all([owner, repo_name]): |
|
raise ValueError("Missing parameters: owner, repo_name") |
|
repo = g.get_repo(f"{owner}/{repo_name}") |
|
contents = repo.get_contents("") |
|
file_analyses = [] |
|
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, |
|
}) |
|
analysis_md = "Repository content analysis:\n" + "\n".join([ |
|
f"- **{f['path']}**:\n" |
|
f" - Lines: {f['analysis']['line_count']}\n" |
|
f" - Words: {f['analysis']['word_count']}\n" |
|
f" - Extension: {f['analysis']['file_extension']}" |
|
for f in file_analyses |
|
]) |
|
return analysis_md |
|
|
|
else: |
|
raise ValueError(f"Unknown action: {action}") |
|
|
|
|
|
except GithubException as e: |
|
return f"**GitHub Error:** {e}" |
|
except ValueError as e: |
|
return f"**Error:** {e}" |
|
except requests.exceptions.RequestException as e: |
|
return f"**Connection Error:** {e}" |
|
except Exception as e: |
|
return f"**Unexpected Error:** {e}" |
|
|
|
|
|
with gr.Blocks() as demo: |
|
gr.Markdown("# GitHub Tool Plugingit (Simplified Interface)") |
|
|
|
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="Select Action", |
|
) |
|
repo_name = gr.Textbox(label="Repository Name") |
|
template_name = gr.Dropdown( |
|
choices=[""] + list(PROJECT_TEMPLATES.keys()), |
|
label="Project Template", |
|
value="", |
|
allow_custom_value=True, |
|
) |
|
|
|
template_params_ui = {} |
|
with gr.Row(): |
|
for template_key, template_config in PROJECT_TEMPLATES.items(): |
|
if "params" in template_config: |
|
with gr.Column(visible=False, elem_classes=f"params_{template_key}") as params_section: |
|
for param_name, param_details in template_config["params"].items(): |
|
if param_details["type"] == "choice": |
|
template_params_ui[param_name] = gr.Dropdown( |
|
choices=param_details["choices"], |
|
label=param_details["description"] or param_name, |
|
value=param_details["default"] |
|
) |
|
else: |
|
template_params_ui[param_name] = gr.Textbox( |
|
label=param_details["description"] or param_name, |
|
value=param_details["default"] if "default" in param_details else "" |
|
) |
|
template_params_ui[template_key] = params_section |
|
|
|
branch = gr.Textbox(label="Branch", value="main") |
|
path = gr.Textbox(label="File Path") |
|
content = gr.Code(label="File Content", lines=5, language='python') |
|
message = gr.Textbox(label="Message/Comment") |
|
owner = gr.Textbox(label="Owner") |
|
vcs_url = gr.Textbox(label="VCS URL") |
|
title = gr.Textbox(label="Title") |
|
body = gr.Textbox(label="Body") |
|
base = gr.Textbox(label="Base Branch") |
|
head = gr.Textbox(label="Head Branch/New Branch") |
|
issue_number = gr.Number(label="Issue Number", precision=0) |
|
labels = gr.Textbox(label="Labels (comma-separated)") |
|
tag = gr.Textbox(label="Tag") |
|
release_name = gr.Textbox(label="Release Name") |
|
file_url = gr.Textbox(label="File URL") |
|
repo_url = gr.Textbox(label="Repository URL") |
|
|
|
|
|
def show_template_params(template_name): |
|
visibility_map = {key: gr.Column.update(visible=False) for key in PROJECT_TEMPLATES if |
|
"params" in PROJECT_TEMPLATES[key]} |
|
if template_name and template_name in PROJECT_TEMPLATES and "params" in PROJECT_TEMPLATES[template_name]: |
|
visibility_map[template_name] = gr.Column.update(visible=True) |
|
return [visibility_map.get(key, gr.Column.update(visible=False)) for key in PROJECT_TEMPLATES if |
|
"params" in PROJECT_TEMPLATES[key]] |
|
|
|
template_param_visibility_outputs = [template_params_ui[key] for key in PROJECT_TEMPLATES if |
|
"params" in PROJECT_TEMPLATES[key]] |
|
template_name.change( |
|
show_template_params, |
|
inputs=[template_name], |
|
outputs=template_param_visibility_outputs |
|
) |
|
|
|
run_button = gr.Button("Execute") |
|
output = gr.Markdown(label="Result") |
|
|
|
input_components = [ |
|
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, |
|
] |
|
|
|
|
|
for template_key in PROJECT_TEMPLATES: |
|
if "params" in PROJECT_TEMPLATES[template_key]: |
|
for param_name in template_params_ui: |
|
if param_name in PROJECT_TEMPLATES[template_key]["params"]: |
|
input_components.append(template_params_ui[param_name]) |
|
|
|
run_button.click( |
|
github_tool, |
|
inputs=input_components, |
|
outputs=output, |
|
api_name="github_tool" |
|
) |
|
|
|
demo.launch() |