Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Also support Gitlab / Gitlab CI #378

Closed
VolkerH opened this issue Oct 18, 2024 · 14 comments · Fixed by #383
Closed

Also support Gitlab / Gitlab CI #378

VolkerH opened this issue Oct 18, 2024 · 14 comments · Fixed by #383

Comments

@VolkerH
Copy link

VolkerH commented Oct 18, 2024

Hi git-bob,

can you rewrite yourself that you use the Gitlab-API vs the Github API?
This would be useful for code that is hosted on a private, self-hosted Gitlab instance.

git-bob solve

Copy link
Contributor

This message was generated by git-bob (version: 0.9.2, model: gpt-4o-2024-08-06, log), an experimental AI-based assistant. It can make mistakes and has limitations. Check its messages carefully.

Hi @VolkerH,

thanks for reaching out! Unfortunately, I'm not allowed to respond to you directly.
I need approval from a person who has access.

Best,
git-bob

@haesleinhuepf
Copy link
Owner

git-bob what would be necessary to update in _github_utilities.py so that it supports gitlab instead of github?

git-bob comment

Copy link
Contributor

This message was generated by git-bob (version: 0.9.2, model: gpt-4o-2024-08-06, log), an experimental AI-based assistant. It can make mistakes and has limitations. Check its messages carefully.

git-bob comment

To support GitLab in _github_utilities.py, you can create a similar utility file for GitLab, say _gitlab_utilities.py, using the python-gitlab library.

Below is a minimal example of how functions could be adapted for GitLab:

import os
from gitlab import Gitlab

def get_gitlab_repository(repository, private_token):
    """
    Get the GitLab repository object.
    """
    gitlab_url = os.getenv('GITLAB_URL', 'https://gitlab.com')
    gl = Gitlab(gitlab_url, private_token=private_token)
    return gl.projects.get(repository)

def add_comment_to_issue(repository, issue, comment, private_token):
    """
    Add a comment to a specific GitLab issue.
    """
    repo = get_gitlab_repository(repository, private_token)
    issue_obj = repo.issues.get(issue)
    issue_obj.notes.create({'body': comment})

def list_issues(repository, state="opened", private_token=None):
    """
    List all GitLab issues with a defined state on a specified repository.
    """
    repo = get_gitlab_repository(repository, private_token)
    return {issue.iid: issue.title for issue in repo.issues.list(state=state)}

You will need to adjust the environment variables and additional functionality as needed.

@haesleinhuepf
Copy link
Owner

How about the other functions in _github_utilities.py ? git-bob comment

Copy link
Contributor

This message was generated by git-bob (version: 0.9.2, model: gpt-4o-2024-08-06, log), an experimental AI-based assistant. It can make mistakes and has limitations. Check its messages carefully.

To expand _github_utilities.py to support GitLab, you would need to adapt all functions to use GitLab's API via the python-gitlab library instead of GitHub's PyGithub library. Here's an example of how you could modify the remaining functions accordingly:

from gitlab import Gitlab

def get_conversation_on_issue(repository, issue, private_token):
    """
    Retrieve the entire conversation (title, body, and comments) of a specific GitLab issue.
    """
    repo = get_gitlab_repository(repository, private_token)
    issue_obj = repo.issues.get(issue)
    conversation = f"Issue Title: {issue_obj.title}\n\n"
    conversation += f"Issue Body:\n{issue_obj.description}\n\n"
    comments = issue_obj.notes.list()
    for comment in comments:
        conversation += f"Comment by {comment.author['username']}:\n{comment.body}\n\n"
    return conversation

def get_most_recently_commented_issue(repository, private_token):
    """
    Return the issue number of the issue in a repository with the most recent comment.
    """
    repo = get_gitlab_repository(repository, private_token)
    issues = repo.issues.list(order_by="updated_at", sort="desc")
    if not issues:
        raise ValueError("No issue found")
    return issues[0].iid

def list_repository_files(repository, private_token):
    """
    List all files in a given GitLab repository.
    """
    repo = get_gitlab_repository(repository, private_token)
    tree = repo.repository_tree()
    return [item['path'] for item in tree if item['type'] == 'blob']

def create_issue(repository, title, body, private_token):
    """
    Create a new GitLab issue.
    """
    repo = get_gitlab_repository(repository, private_token)
    issue_obj = repo.issues.create({'title': title, 'description': body})
    return issue_obj.iid

Each function calls get_gitlab_repository, similar to the GitHub version, but tailored to authenticate and fetch the project using GitLab's API. Adapt fields accordingly in issue and comment objects to match GitLab's API structure. Adjust logic as needed to handle differences between GitHub and GitLab APIs.

@haesleinhuepf
Copy link
Owner

Are there functions in _github_utilities.py we haven't discussed yet? git-bob comment

Copy link
Contributor

This message was generated by git-bob (version: 0.9.2, model: gpt-4o-2024-08-06, log), an experimental AI-based assistant. It can make mistakes and has limitations. Check its messages carefully.

Comment by github-actions[bot]:

It seems that there are additional functions in _github_utilities.py that need to be adapted for GitLab. Below is an example of how those remaining functions could be transformed to use the GitLab API:

def get_most_recent_comment_on_issue(repository, issue, private_token):
    """
    Retrieve the most recent comment on a specific GitLab issue.
    """
    repo = get_gitlab_repository(repository, private_token)
    issue_obj = repo.issues.get(issue)
    comments = issue_obj.notes.list(order_by='created_at', sort='desc')

    if comments:
        comment = comments[0]
        user = comment.author['username']
        text = comment.body
    else:
        user = issue_obj.author['username']
        text = issue_obj.description

    if text is None:
        text = ""

    return user, text

def get_repository_file_contents(repository, file_paths, private_token):
    """
    Retrieve the contents of specified files from a GitLab repository.
    """
    repo = get_gitlab_repository(repository, private_token)
    file_contents = {}

    for file_path in file_paths:
        try:
            file_obj = repo.files.get(file_path=file_path, ref='main')
            file_content = file_obj.decode().decode('utf-8')
            file_contents[file_path] = file_content
        except Exception as e:
            file_contents[file_path] = f"Error accessing {file_path}: {str(e)}"

    return file_contents

def send_pull_request(repository, source_branch, target_branch, title, description, private_token):
    """
    Create a merge request from a defined branch into the target branch.
    """
    repo = get_gitlab_repository(repository, private_token)

    if len(description) > 65535:
        print("Description is too long. Truncated to 65535 characters. This was the full description:", description)
        description = description[:65535]

    mr = repo.mergerequests.create({
        'source_branch': source_branch,
        'target_branch': target_branch,
        'title': title,
        'description': description
    })

    return f"Merge Request created: {mr.web_url}"

These examples illustrate how to implement the corresponding logic using the GitLab API, making sure to account for differences in API responses between GitHub and GitLab.

@haesleinhuepf
Copy link
Owner

Ok cool. git-bob implement this in a new file _gitlab_utilities.py . Also add a new file to the tests directory which tests the functions similar to how we test the github utilities.

@haesleinhuepf
Copy link
Owner

Ok, everything above looks great!

Next question: How would you implement the following functions in _gitlab_utilities.py, similar to the functions in _github_utilities.py but so that they use the gitlab API instead of the github api.

'copy_file_in_repository'
'download_to_repository'
'rename_file_in_repository'
'delete_file_from_repository'

git-bob think about this

Copy link
Contributor

This message was generated by git-bob (version: 0.9.2, model: gpt-4o-2024-08-06, log), an experimental AI-based assistant. It can make mistakes and has limitations. Check its messages carefully.

To implement the file manipulation functions using the GitLab API in _gitlab_utilities.py, here's how you could structure them:

from gitlab import Gitlab

def get_gitlab_repository(repository, private_token):
    gitlab_url = os.getenv('GITLAB_URL', 'https://gitlab.com')
    gl = Gitlab(gitlab_url, private_token=private_token)
    return gl.projects.get(repository)

def copy_file_in_repository(repository, branch_name, src_file_path, dest_file_path, commit_message, private_token):
    repo = get_gitlab_repository(repository, private_token)
    file_obj = repo.files.get(file_path=src_file_path, ref=branch_name)
    repo.files.create({
        'file_path': dest_file_path,
        'branch': branch_name,
        'content': file_obj.content,
        'encoding': 'base64',
        'commit_message': commit_message
    })

def download_to_repository(repository, branch_name, source_url, target_filename, private_token):
    import requests
    response = requests.get(source_url)
    if response.status_code != 200:
        raise Exception(f"Failed to download file. Status code: {response.status_code}")
    repo = get_gitlab_repository(repository, private_token)
    repo.files.create({
        'file_path': target_filename,
        'branch': branch_name,
        'content': response.content.decode('utf-8'),
        'commit_message': f"Downloaded {source_url}, saved as {target_filename}."
    })

def rename_file_in_repository(repository, branch_name, old_file_path, new_file_path, commit_message, private_token):
    repo = get_gitlab_repository(repository, private_token)
    file_obj = repo.files.get(file_path=old_file_path, ref=branch_name)
    repo.files.delete(file_path=old_file_path, branch=branch_name, commit_message=commit_message)
    repo.files.create({
        'file_path': new_file_path,
        'branch': branch_name,
        'content': file_obj.content,
        'encoding': 'base64',
        'commit_message': commit_message
    })

def delete_file_from_repository(repository, branch_name, file_path, commit_message, private_token):
    repo = get_gitlab_repository(repository, private_token)
    repo.files.delete(file_path=file_path, branch=branch_name, commit_message=commit_message)

For testing, add a new file test_gitlab_utilities.py similar to test_github_utilities.py:

def test_copy_file_in_repository():
    # Implement the test for copying files in GitLab repository

def test_download_to_repository():
    # Implement the test for downloading files to GitLab repository

def test_rename_file_in_repository():
    # Implement the test for renaming files in GitLab repository

def test_delete_file_from_repository():
    # Implement the test for deleting files from GitLab repository

This mock structure illustrates how to handle file operations using the GitLab API. Adjust based on actual implementation needs and constraints.

@haesleinhuepf
Copy link
Owner

Ok, everything above looks great!

Next question: How would you implement the following functions in _gitlab_utilities.py, similar to the functions in _github_utilities.py but so that they use the gitlab API instead of the github api? No need to write tests for now.

  • 'check_if_file_exists'
  • 'write_file_in_branch'
  • 'check_access_and_ask_for_approval'
  • 'get_github_issue_details'
  • 'get_contributors'
  • 'get_conversation_on_issue'
  • 'get_diff_of_pull_request'
  • 'create_branch'
  • 'get_diff_of_branches'
  • 'add_reaction_to_last_comment_in_issue'
  • 'add_reaction_to_issue'

git-bob solve

@haesleinhuepf
Copy link
Owner

git-bob think about this

Copy link
Contributor

This message was generated by git-bob (version: 0.9.2, model: gpt-4o-2024-08-06, log), an experimental AI-based assistant. It can make mistakes and has limitations. Check its messages carefully.

To implement the given functions using the GitLab API in _gitlab_utilities.py, you can structure them as follows:

from gitlab import Gitlab

def get_gitlab_repository(repository, private_token):
    gitlab_url = os.getenv('GITLAB_URL', 'https://gitlab.com')
    gl = Gitlab(gitlab_url, private_token=private_token)
    return gl.projects.get(repository)

def check_if_file_exists(repository, branch_name, file_path, private_token):
    repo = get_gitlab_repository(repository, private_token)
    try:
        repo.files.get(file_path=file_path, ref=branch_name)
        return True
    except:
        return False

def write_file_in_branch(repository, branch_name, file_path, new_content, commit_message, private_token):
    repo = get_gitlab_repository(repository, private_token)
    try:
        file_obj = repo.files.get(file_path=file_path, ref=branch_name)
        file_obj.content = new_content
        file_obj.save(branch=branch_name, commit_message=commit_message)
    except:
        repo.files.create({
            'file_path': file_path,
            'branch': branch_name,
            'content': new_content,
            'encoding': 'base64',
            'commit_message': commit_message
        })

def check_access_and_ask_for_approval(user, repository, issue, private_token):
    # In GitLab, you might check user roles or permissions
    # Here we'll just simulate the logic by checking membership
    repo = get_gitlab_repository(repository, private_token)
    access = False

    try:
        member = repo.members.get(user)
        access = True if member.access_level >= 30 else False
    except:
        access = False

    if not access:
        # Simulate asking for approval, for example by commenting
        issue_obj = repo.issues.get(issue)
        issue_obj.notes.create({'body': f"User {user} needs approval for access."})

    return access

def get_gitlab_issue_details(repository, issue, private_token):
    repo = get_gitlab_repository(repository, private_token)
    issue_obj = repo.issues.get(issue)
    return f"Issue #{issue_obj.iid}: {issue_obj.title}\nDetails:\n{issue_obj.description}"

def get_contributors(repository, private_token):
    repo = get_gitlab_repository(repository, private_token)
    return [member.username for member in repo.repository_contributors()]

def get_conversation_on_issue(repository, issue, private_token):
    repo = get_gitlab_repository(repository, private_token)
    issue_obj = repo.issues.get(issue)
    conversation = f"Issue Title: {issue_obj.title}\n\nIssue Body:\n{issue_obj.description}\n\n"
    comments = issue_obj.notes.list()
    for comment in comments:
        conversation += f"Comment by {comment.author['username']}:\n{comment.body}\n\n"
    return conversation

def get_diff_of_pull_request(repository, mergerequest_id, private_token):
    repo = get_gitlab_repository(repository, private_token)
    mr = repo.mergerequests.get(mergerequest_id)
    return mr.diffs()

def create_branch(repository, branch_name, ref_branch='main', private_token=None):
    repo = get_gitlab_repository(repository, private_token)
    branch = repo.branches.create({'branch': branch_name, 'ref': ref_branch})
    return branch.name

def get_diff_of_branches(repository, source_branch, target_branch, private_token):
    repo = get_gitlab_repository(repository, private_token)
    return repo.commits.diff(source_branch, target_branch)

def add_reaction_to_last_comment_in_issue(repository, issue, reaction, private_token):
    repo = get_gitlab_repository(repository, private_token)
    issue_obj = repo.issues.get(issue)
    comments = issue_obj.notes.list(order_by='created_at', sort='desc')
    if comments:
        comments[0].award_emoji.create({'name': reaction})

def add_reaction_to_issue(repository, issue, reaction, private_token):
    repo = get_gitlab_repository(repository, private_token)
    issue_obj = repo.issues.get(issue)
    issue_obj.award_emoji.create({'name': reaction})

These implementations use the python-gitlab library to handle interactions with the GitLab API, adapting similarly to the GitHub functions provided. Adjust specifics further based on testing and additional requirements.

@haesleinhuepf
Copy link
Owner

Ok, git-bob implement this in a new file _gitlab_utilities.py , but remove the private_token from all the functions and use os.environ['GITLAB_API_KEY'] within get_gitlab_repository instead. Ensure that all functions in the newly created _gitlab_utilities.py have exactly the same signature like the functions in _github_utilities.py. Also make sure to implement all functions in _github_utilities.py . We need an identical API in _gitlab_utilities.py like in _github_utilities.py

Also do not implement any tests yet.

git-bob solve

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment