Source code for squadds.database.github
import json
import os
import shutil
from datetime import date, datetime
import git
from dotenv import load_dotenv
from github import Github
from github.Auth import Auth
from github.GithubException import GithubException
[docs]
def login_to_github():
"""Logs in to GitHub using a token stored in environment variables."""
load_dotenv() # Load environment variables
github_token = os.getenv("GITHUB_TOKEN")
if github_token is None:
raise ValueError("GitHub token not found in environment variables.")
return Github(github_token)
[docs]
def clone_repository(repo_url, clone_dir):
"""
Clone the given repository into the specified directory.
Parameters:
- repo_url (str): URL of the repository to clone.
- clone_dir (str): Path to the directory where the repo should be cloned.
Returns:
- git.Repo: The cloned Git repository object.
"""
# Remove the directory if it exists
if os.path.exists(clone_dir):
shutil.rmtree(clone_dir)
# Clone the repository
print(f"Cloning repository from {repo_url} into {clone_dir}...")
repo = git.Repo.clone_from(repo_url, clone_dir)
return repo
[docs]
def fork_repository(github_token):
"""
Forks the specified GitHub repository to the authenticated user's account.
- github_token (str): GitHub Personal Access Token with appropriate permissions.
Returns:
- str: URL of the forked repository if successful, None otherwise.
"""
original_repo_name = "LFL-Lab/SQuADDS_DB"
# Authenticate with GitHub
g = Github(github_token)
# Get the authenticated user
user = g.get_user()
try:
# Get the original repository
original_repo = g.get_repo(original_repo_name)
# Fork the repository to the authenticated user's account
print(f"Forking the repository: {original_repo_name} to {user.login}'s account...")
forked_repo = user.create_fork(original_repo)
print(f"Repository forked successfully: {forked_repo.html_url}")
return forked_repo.html_url
except Exception as e:
print(f"Error forking repository: {e}")
return None
[docs]
def read_json_file(file_path):
"""
Read and return the contents of the specified JSON file.
Parameters:
- file_path (str): Path to the JSON file.
Returns:
- dict: The contents of the JSON file as a Python dictionary.
"""
if not os.path.exists(file_path):
raise FileNotFoundError(f"File not found: {file_path}")
print(f"Reading JSON file from {file_path}...")
with open(file_path, 'r') as file:
data = json.load(file)
return data
[docs]
def append_to_json(data, new_entry):
"""
Append the new entry to the JSON data, ensuring the format is maintained.
Parameters:
- data (dict): Existing JSON data.
- new_entry (dict): New entry to add to the JSON data.
Returns:
- dict: Updated JSON data.
"""
print("Appending new entry to JSON data...")
data.append(new_entry)
return data
[docs]
def save_json_file(file_path, data):
"""
Save the updated data back to the JSON file.
Parameters:
- file_path (str): Path to the JSON file.
- data (dict): Updated JSON data.
"""
print(f"Saving updated JSON data to {file_path}...")
with open(file_path, 'w') as file:
json.dump(data, file, indent=4)
[docs]
def commit_changes(repo, file_path, commit_message):
"""
Commit changes to the specified file in the repository.
Parameters:
- repo (git.Repo): The Git repository object.
- file_path (str): Path to the file to commit.
- commit_message (str): Commit message.
Returns:
- str: The commit hash if successful.
"""
print(f"Staging and committing changes to {file_path}...")
repo.git.add(file_path)
commit = repo.index.commit(commit_message)
print(f"Commit successful with message: {commit_message}")
return commit.hexsha
[docs]
def push_changes(repo, branch_name='main', github_token=None):
"""
Push the committed changes to the remote repository.
Parameters:
- repo (git.Repo): The Git repository object.
- branch_name (str): The name of the branch to push to.
- github_token (str): GitHub Personal Access Token (optional).
Returns:
- bool: True if push is successful, False otherwise.
"""
try:
# Check and update remote URL if GitHub token is provided
if github_token:
# Construct the URL with the token for authentication
remote_url = repo.remotes.origin.url
if remote_url.startswith("https://"):
repo_name = remote_url.split("github.com/")[1]
authenticated_url = f"https://{github_token}@github.com/{repo_name}"
repo.remotes.origin.set_url(authenticated_url)
print(f"Updated remote URL with token for authentication")
else:
print("Remote URL is not HTTPS, cannot update with token.")
# Push changes to the remote repository
print(f"Pushing changes to the remote {branch_name} branch...")
push_result = repo.remote(name='origin').push(refspec=f'{branch_name}:{branch_name}')
print("Push successful.")
return True
except Exception as e:
print(f"Push failed: {e}")
return False
[docs]
def contribute_measured_data(new_entry, pr_title="PR For Contributing New Data", pr_body="This PR is for contributing new data to the SQuADDS Measured Devices Database."):
"""
Update the JSON file in the given repository by appending a new entry and committing the changes.
Parameters:
- new_entry (dict): New entry to append to the JSON file.
- pr_title (str): The title of the pull request.
- pr_body (str): The body description of the pull request.
Returns:
- str: Commit hash if successful, None otherwise.
"""
# Variable setup
original_repo_name = "LFL-Lab/SQuADDS_DB"
TEMP_CLONE_DIR = "./temp_forked_repo"
json_file_path = "measured_device_database.json"
load_dotenv()
github_token = os.getenv("GITHUB_TOKEN")
if github_token is None:
raise ValueError("GitHub token not found in environment variables.")
gh_name = get_github_username(github_token)
forked_repo_name = f"{gh_name}/SQuADDS_DB"
branch_name = "main"
# Step 0: Fork the repository
forked_repo_url = fork_repository(github_token)
# Step 1: Clone the repository
repo = clone_repository(forked_repo_url, TEMP_CLONE_DIR)
# Step 2: Check out the main branch
print("Checking out the main branch...")
repo.git.checkout('main')
# Step 3: Read the JSON file
full_json_file_path = os.path.join(TEMP_CLONE_DIR, json_file_path)
data = read_json_file(full_json_file_path)
# Step 4: Append the new entry to the JSON data
updated_data = append_to_json(data, new_entry)
# Step 5: Save the updated JSON data back to the file
save_json_file(full_json_file_path, updated_data)
# Step 6: Commit the changes with a unique datetime-based message
commit_message = f"Update JSON dataset with new entry - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
commit_hash = commit_changes(repo, json_file_path, commit_message)
# Step 7: Push the changes to the remote repository
if (push_changes(repo, "main", github_token)):
print("Successfully pushed changes to the remote repository.")
else:
print("Failed to push changes to the remote repository.")
return
# Step 8: Create a Pull Request on GitHub
pr_url = create_pull_request(forked_repo_name, branch_name, pr_title, pr_body, github_token)
print(f"Pull request URL: {pr_url}")
[docs]
def create_pull_request(forked_repo_name, branch_name, pr_title, pr_body, github_token):
"""
Creates a pull request from the specified branch in the forked repository to the original repository.
Parameters:
- forked_repo_name (str): The full name of the forked repository (e.g., 'your_username/repo_name').
- branch_name (str): The name of the branch from which to create the PR.
- pr_title (str): The title of the pull request.
- pr_body (str): The body description of the pull request.
- github_token (str): GitHub Personal Access Token for authentication.
Returns:
- str: URL of the created pull request if successful, None otherwise.
"""
original_repo_name = "LFL-Lab/SQuADDS_DB"
# Authenticate with GitHub using the provided token
g = Github(github_token)
user = g.get_user()
try:
# Get the original repository
original_repo = g.get_repo(original_repo_name)
# Create a pull request
print(f"Creating a pull request from {forked_repo_name}:{branch_name} to {original_repo_name}:main...")
pull_request = original_repo.create_pull(
title=pr_title,
body=pr_body,
head=f"{user.login}:{branch_name}", # Forked repository and branch
base="main" # Target branch in the original repository
)
print("Pull request created successfully")
return pull_request.html_url
except Exception as e:
print(f"Error creating pull request: {e}")
return None
[docs]
def get_github_username(github_token):
"""
Get the GitHub username associated with the provided token.
Parameters:
- github_token (str): GitHub Personal Access Token.
Returns:
- str: GitHub username if successful, None otherwise.
"""
try:
# Authenticate with GitHub using the token
g = Github(github_token)
# Get the authenticated user
user = g.get_user()
# Return the username
return user.login
except Exception as e:
print(f"Error retrieving GitHub username: {e}")
return None