Download app.py from MicroHealth/github-release-notes: direct link, hf CLI and curl.
- Browser
- Download file 9.76 kB
-
https://huggingface.co/spaces/MicroHealth/github-release-notes/resolve/d3dfcfe6eb071fa880407ec17deaaab4563a5944/app.py
- Command line
-
hf download hf://spaces/MicroHealth/github-release-notes@d3dfcfe6eb071fa880407ec17deaaab4563a5944/app.py
-
curl -L -o app.py https://huggingface.co/spaces/MicroHealth/github-release-notes/resolve/d3dfcfe6eb071fa880407ec17deaaab4563a5944/app.py
9.76 kB
| import dash | |
| from dash import dcc, html, Input, Output, State | |
| import dash_bootstrap_components as dbc | |
| from datetime import datetime, timedelta | |
| import google.generativeai as genai | |
| from github import Github, GithubException | |
| import gitlab | |
| import docx | |
| import tempfile | |
| import requests | |
| import os | |
| import threading | |
| import io | |
| # Initialize the Dash app | |
| app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) | |
| # Hugging Face variables | |
| HF_GEMINI_API_KEY = os.environ.get('HF_GEMINI_API_KEY') | |
| HF_GITHUB_TOKEN = os.environ.get('HF_GITHUB_TOKEN') | |
| # Global variable to store generated files | |
| generated_file = None | |
| def generate_release_notes(git_provider, repo_url, start_date, end_date, folder_location): | |
| global generated_file | |
| try: | |
| start_date = datetime.strptime(start_date, "%Y-%m-%d") | |
| end_date = datetime.strptime(end_date, "%Y-%m-%d") | |
| if git_provider == "GitHub": | |
| g = Github(HF_GITHUB_TOKEN) | |
| repo = g.get_repo(repo_url) | |
| commits = list(repo.get_commits(since=start_date, until=end_date)) | |
| commit_messages = [commit.commit.message for commit in commits] | |
| elif git_provider == "GitLab": | |
| gl = gitlab.Gitlab(url='https://gitlab.com', private_token=HF_GITHUB_TOKEN) | |
| project = gl.projects.get(repo_url) | |
| commits = project.commits.list(since=start_date.isoformat(), until=end_date.isoformat()) | |
| commit_messages = [commit.message for commit in commits] | |
| elif git_provider == "Gitea": | |
| base_url = "https://gitea.com/api/v1" | |
| headers = {"Authorization": f"token {HF_GITHUB_TOKEN}"} | |
| response = requests.get(f"{base_url}/repos/{repo_url}/commits", headers=headers, params={ | |
| "since": start_date.isoformat(), | |
| "until": end_date.isoformat() | |
| }) | |
| response.raise_for_status() | |
| commits = response.json() | |
| commit_messages = [commit['commit']['message'] for commit in commits] | |
| else: | |
| return "Unsupported Git provider", None | |
| commit_text = "\n".join(commit_messages) | |
| if not commit_text: | |
| return "No commits found in the specified date range.", None | |
| genai.configure(api_key=HF_GEMINI_API_KEY) | |
| model = genai.GenerativeModel('gemini-2.0-flash-lite') | |
| prompt = f"""Based on the following commit messages, generate comprehensive release notes: | |
| {commit_text} | |
| Please organize the release notes into sections such as: | |
| 1. New Features | |
| 2. Bug Fixes | |
| 3. Improvements | |
| 4. Breaking Changes (if any) | |
| Provide a concise summary for each item. Do not include any links, but keep issue numbers if present. | |
| Important formatting instructions: | |
| - The output should be plain text without any markdown or "-" for post processing | |
| - Use section titles followed by a colon (e.g., "New Features:") | |
| - Start each item on a new line | |
| - Be sure to briefly explain the why and benefits of the change for average users that are non-technical | |
| """ | |
| response = model.generate_content(prompt) | |
| release_notes = response.text | |
| # Create Markdown file | |
| markdown_content = "# Release Notes\n\n" | |
| for line in release_notes.split('\n'): | |
| line = line.strip() | |
| if line.endswith(':'): | |
| markdown_content += f"\n## {line}\n\n" | |
| elif line: | |
| markdown_content += f"- {line}\n" | |
| # Generate file name based on the current date | |
| file_name = f"{datetime.now().strftime('%m-%d-%Y')}.md" | |
| # Store the generated file content | |
| generated_file = io.BytesIO(markdown_content.encode()) | |
| generated_file.seek(0) | |
| # Update SUMMARY.md | |
| summary_url = "https://github.com/MicroHealthLLC/maiko-assistant/blob/main/documentation%2Freleases%2FSUMMARY.md" | |
| update_summary(summary_url, file_name) | |
| return release_notes, file_name | |
| except Exception as e: | |
| return f"An error occurred: {str(e)}", None | |
| def update_summary(summary_url, new_file_name): | |
| try: | |
| g = Github(HF_GITHUB_TOKEN) | |
| repo = g.get_repo("MicroHealthLLC/maiko-assistant") | |
| summary_content = repo.get_contents("documentation/releases/SUMMARY.md").decoded_content.decode() | |
| # Add new file to the top of the Releases section | |
| new_entry = f"{datetime.now().strftime('%b %d, %Y')}\n" | |
| updated_content = summary_content.replace("Releases\n", f"Releases\n{new_entry}") | |
| # Create a new branch for the PR | |
| base_branch = repo.default_branch | |
| new_branch = f"update-summary-{datetime.now().strftime('%Y%m%d%H%M%S')}" | |
| ref = repo.get_git_ref(f"heads/{base_branch}") | |
| repo.create_git_ref(ref=f"refs/heads/{new_branch}", sha=ref.object.sha) | |
| # Update SUMMARY.md in the new branch | |
| repo.update_file( | |
| "documentation/releases/SUMMARY.md", | |
| f"Update SUMMARY.md with new release notes {new_file_name}", | |
| updated_content, | |
| repo.get_contents("documentation/releases/SUMMARY.md").sha, | |
| branch=new_branch | |
| ) | |
| # Create a pull request | |
| pr = repo.create_pull( | |
| title=f"Update SUMMARY.md with new release notes {new_file_name}", | |
| body="Automatically generated PR to update SUMMARY.md with new release notes.", | |
| head=new_branch, | |
| base=base_branch | |
| ) | |
| return f"Pull request created: {pr.html_url}" | |
| except Exception as e: | |
| return f"Error updating SUMMARY.md: {str(e)}" | |
| # App layout | |
| app.layout = dbc.Container([ | |
| html.H1("Automated Release Notes Generator", className="mb-4"), | |
| dbc.Card([ | |
| dbc.CardBody([ | |
| dbc.Form([ | |
| dbc.Row([ | |
| dbc.Col([ | |
| dcc.Dropdown( | |
| id='git-provider', | |
| options=[ | |
| {'label': 'GitHub', 'value': 'GitHub'}, | |
| {'label': 'GitLab', 'value': 'GitLab'}, | |
| {'label': 'Gitea', 'value': 'Gitea'} | |
| ], | |
| placeholder="Select Git Provider" | |
| ) | |
| ], width=12, className="mb-3"), | |
| ]), | |
| dbc.Row([ | |
| dbc.Col([ | |
| dbc.Input(id='repo-url', placeholder="Repository URL (owner/repo)", type="text") | |
| ], width=12, className="mb-3"), | |
| ]), | |
| dbc.Row([ | |
| dbc.Col([ | |
| dbc.Input(id='start-date', placeholder="Start Date (YYYY-MM-DD)", type="text") | |
| ], width=12, className="mb-3"), | |
| ]), | |
| dbc.Row([ | |
| dbc.Col([ | |
| dbc.Input(id='end-date', placeholder="End Date (YYYY-MM-DD)", type="text") | |
| ], width=12, className="mb-3"), | |
| ]), | |
| dbc.Row([ | |
| dbc.Col([ | |
| dbc.Input(id='folder-location', placeholder="Folder Location", type="text") | |
| ], width=12, className="mb-3"), | |
| ]), | |
| dbc.Row([ | |
| dbc.Col([ | |
| dbc.Button("Generate Release Notes", id="generate-button", color="primary", className="mr-2"), | |
| dbc.Button("Download Markdown", id="download-button", color="secondary", className="mr-2", disabled=True), | |
| dbc.Button("Create PR", id="pr-button", color="info", disabled=True) | |
| ], width=12, className="mb-3"), | |
| ]), | |
| ]), | |
| ]) | |
| ], className="mb-4"), | |
| dbc.Card([ | |
| dbc.CardBody([ | |
| html.H4("Generated Release Notes"), | |
| html.Pre(id="output-notes", style={"white-space": "pre-wrap"}) | |
| ]) | |
| ]), | |
| dcc.Download(id="download-markdown") | |
| ]) | |
| def update_output(n_clicks, git_provider, repo_url, start_date, end_date, folder_location): | |
| if n_clicks is None: | |
| return "", True, True | |
| notes, file_name = generate_release_notes(git_provider, repo_url, start_date, end_date, folder_location) | |
| return notes, False, False | |
| def download_markdown(n_clicks): | |
| if n_clicks is None: | |
| return dash.no_update | |
| global generated_file | |
| if generated_file is None: | |
| return dash.no_update | |
| return dcc.send_bytes(generated_file.getvalue(), f"release_notes_{datetime.now().strftime('%Y%m%d%H%M%S')}.md") | |
| def create_pr(n_clicks, folder_location): | |
| if n_clicks is None: | |
| return dash.no_update | |
| # Implement PR creation logic here | |
| # For now, we'll just return a placeholder message | |
| return "PR Created" | |
| if __name__ == '__main__': | |
| print("Starting the Dash application...") | |
| app.run(debug=True, host='0.0.0.0', port=7860) | |
| print("Dash application has finished running.") |