Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 47 additions & 15 deletions erpnext_github_integration/github_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,29 +299,61 @@ def add_pr_reviewer(repo_full_name, pr_number, reviewers):
token = settings.get_password('personal_access_token')
if not token:
frappe.throw(_('GitHub Personal Access Token not configured in GitHub Settings'))

# normalize reviewers input
if isinstance(reviewers, str):
try:
reviewers = json.loads(reviewers)
except Exception:
reviewers = [r.strip() for r in reviewers.split(',') if r.strip()]
payload = {'reviewers': reviewers}

github_usernames = []

try:
resp = github_request('POST', f"/repos/{repo_full_name}/pulls/{pr_number}/requested_reviewers", token, data=payload)
local = frappe.get_doc('Repository Pull Request', {
'repository': repo_full_name,
'pr_number': int(pr_number)
})

# Reset reviewers table
local.set('reviewers_table', [])

for user_id in reviewers:
# map ERPNext user → GitHub username
gh_username = frappe.db.get_value("User", user_id, "github_username")

if not gh_username:
frappe.log_error(
f"User {user_id} has no GitHub username set",
"GitHub Reviewer Mapping"
)
continue # skip if no GitHub username

github_usernames.append(gh_username)

# store ERPNext user (email / id) in local doc
local.append('reviewers_table', {
'pull_request': local.name,
'user': user_id
})

local.save(ignore_permissions=True)

except Exception:
frappe.log_error(frappe.get_traceback(), "Failed to update local Repository Pull Request reviewers")

# send to GitHub (GitHub API needs GitHub usernames)
payload = {'reviewers': github_usernames}
try:
resp = github_request(
'POST',
f"/repos/{repo_full_name}/pulls/{pr_number}/requested_reviewers",
token,
data=payload
)
except Exception as e:
frappe.throw(_('GitHub add PR reviewer failed: {0}').format(str(e)))
if resp:
try:
local = frappe.get_doc('Repository Pull Request', {'repository': repo_full_name, 'pr_number': int(pr_number)})
# Update reviewers table
local.set('reviewers_table', [])
for reviewer in reviewers:
local.append('reviewers_table', {
'user': reviewer
})
local.save(ignore_permissions=True)
except Exception:
pass
return resp

return resp

@frappe.whitelist()
Expand Down
68 changes: 48 additions & 20 deletions erpnext_github_integration/public/js/task_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ frappe.ui.form.on('Task', {
// Save both: local doc link & GitHub issue number
frm.set_value('github_issue_doc', r.message.local_doc);
frm.set_value('github_issue_number', issue.number);
frm.set_value('github_repo', repo);

frm.save();
}
}
Expand All @@ -47,12 +45,13 @@ frappe.ui.form.on('Task', {
], function(values){
frappe.call({
method: 'erpnext_github_integration.github_api.create_pull_request',
args: {repo_full_name: repo, title: values.title, head: values.head, base: values.base, body: values.body},
args: {repository: repo, title: values.title, head: values.head, base: values.base, body: values.body},
callback: function(r) {
if (r.message) {
frappe.msgprint(__('Created PR: {0}').format(r.message.pull_request.get('html_url') || ''));
frm.set_value('github_pr_number', r.message.pull_request.get('number'));
frm.set_value('github_repo', repo);
let pr = r.message.pull_request;
let url = pr.html_url || pr.url;
frappe.msgprint(__('Created pull request: <a href="{0}" target="_blank">{0}</a>').format(url));
frm.set_value('github_pr_number', pr.number);
frm.save();
}
}
Expand Down Expand Up @@ -104,25 +103,54 @@ frappe.ui.form.on('Task', {
});
});

frm.add_custom_button(__('Add PR Reviewer'), function() {
frm.add_custom_button(__('Assign PR Reviewer'), function() {
let repo = frm.doc.github_repo;
let pr_no = frm.doc.github_pr_number;
if (!repo || !pr_no) {
let pr_number = frm.doc.github_pr_number;

if (!repo || !pr_number) {
frappe.msgprint(__('This Task must have GitHub Repo and GitHub PR Number set.'));
return;
}
frappe.prompt([
{'fieldname':'reviewers','fieldtype':'Data','label':'Reviewers (comma separated GitHub usernames)','reqd':1}
], function(values){
frappe.call({
method: 'erpnext_github_integration.github_api.add_pr_reviewer',
args: {repo_full_name: repo, pr_number: pr_no, reviewers: values.reviewers},
callback: function(r) {
frappe.msgprint(__('Added reviewers to PR {0}').format(pr_no));

// fetch ERPNext users with github_username set
frappe.db.get_list('User', {
fields: ['name', 'full_name', 'github_username'],
filters: { enabled: 1, github_username: ['!=', ''] },
limit: 100
}).then(users => {
let user_options = users.map(u => ({
value: u.name,
label: `${u.full_name || u.name} (${u.github_username || 'no GitHub'})`
}));

frappe.prompt([
{
fieldname: 'reviewers',
fieldtype: 'MultiSelectPills',
label: 'Reviewers',
options: user_options,
reqd: 1
}
});
}, __('Add PR Reviewer'));
});
], function(values) {
// values.reviewers will already be an array of github_usernames
let selected = values.reviewers || [];

frappe.call({
method: 'erpnext_github_integration.github_api.add_pr_reviewer',
args: {
repo_full_name: repo,
pr_number: pr_number,
reviewers: selected
},
callback: function(r) {
if (r.message) {
frappe.msgprint("Message: " + r.message);
frappe.msgprint(__('Reviewers assigned to PR {0}', [pr_number]));
}
}
});
}, __('Assign PR Reviewer'));
});
});
}
});
Loading