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 @@ -236,29 +236,61 @@ def assign_issue(repo_full_name, issue_number, assignees):
token = settings.get_password('personal_access_token')
if not token:
frappe.throw(_('GitHub Personal Access Token not configured in GitHub Settings'))

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

github_usernames = []

try:
local = frappe.get_doc('Repository Issue', {
'repository': repo_full_name,
'issue_number': int(issue_number)
})

# Reset assignees table
local.set('assignees_table', [])

for user_id in assignees:
# 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 Assignee Mapping"
)
continue # skip if no GitHub username

github_usernames.append(gh_username)

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

local.save(ignore_permissions=True)

except Exception:
frappe.log_error(frappe.get_traceback(), "Failed to update local Repository Issue assignees")

# send to GitHub (GitHub API needs GitHub usernames)
payload = {'assignees': github_usernames}
try:
resp = github_request('PATCH', f"/repos/{repo_full_name}/issues/{issue_number}", token, data=payload)
resp = github_request(
'PATCH',
f"/repos/{repo_full_name}/issues/{issue_number}",
token,
data=payload
)
except Exception as e:
frappe.throw(_('GitHub assign issue failed: {0}').format(str(e)))
if resp:
try:
local = frappe.get_doc('Repository Issue', {'repository': repo_full_name, 'issue_number': int(issue_number)})
# Update assignees table
local.set('assignees_table', [])
for assignee in assignees:
local.append('assignees_table', {
'user': assignee
})
local.save(ignore_permissions=True)
except Exception:
pass
return resp

return resp

@frappe.whitelist()
Expand Down
10 changes: 9 additions & 1 deletion erpnext_github_integration/patches/after_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,19 @@ def create_custom_fields_and_scripts():
insert_after='project',
description='GitHub repository this task belongs to'
),
dict(
fieldname='github_issue_doc',
label='GitHub Issue Doc',
fieldtype='Link',
options='Repository Issue',
insert_after='github_repo',
description='GitHub issue doc if this task is linked to an issue'
),
dict(
fieldname='github_issue_number',
label='GitHub Issue Number',
fieldtype='Int',
insert_after='github_repo',
insert_after='github_issue_doc',
description='GitHub issue number if this task is linked to an issue'
),
dict(
Expand Down
2 changes: 1 addition & 1 deletion erpnext_github_integration/public/js/project_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ frappe.ui.form.on('Project', {
}
frappe.call({
method: 'erpnext_github_integration.github_api.sync_repo',
args: {repo_full_name: repo},
args: {repository: repo},
callback: function(r) {
frappe.msgprint(__('Repository sync completed.'));
frm.reload_doc();
Expand Down
58 changes: 45 additions & 13 deletions erpnext_github_integration/public/js/task_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,26 @@ frappe.ui.form.on('Task', {
frappe.msgprint(__('Please set the GitHub Repo (link to Repository) in the Task field "GitHub Repo"'));
return;
}

frappe.prompt([
{'fieldname':'title','fieldtype':'Data','label':'Issue Title','reqd':1},
{'fieldname':'body','fieldtype':'Text','label':'Issue Body'}
], function(values){
frappe.call({
method: 'erpnext_github_integration.github_api.create_issue',
args: {repo_full_name: repo, title: values.title, body: values.body},
args: {repository: repo, title: values.title, body: values.body},
callback: function(r) {
if (r.message) {
frappe.msgprint(__('Created issue: {0}').format(r.message.issue.get('html_url') || r.message.issue.get('url') || ''));
frm.set_value('github_issue_number', r.message.issue.get('number'));
let issue = r.message.issue;
let url = issue.html_url || issue.url;

frappe.msgprint(__('Created issue: <a href="{0}" target="_blank">{0}</a>').format(url));

// 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 Down Expand Up @@ -55,21 +63,45 @@ frappe.ui.form.on('Task', {
frm.add_custom_button(__('Assign Issue'), function() {
let repo = frm.doc.github_repo;
let issue_no = frm.doc.github_issue_number;

if (!repo || !issue_no) {
frappe.msgprint(__('This Task must have GitHub Repo and GitHub Issue Number set.'));
return;
}
frappe.prompt([
{'fieldname':'assignees','fieldtype':'Data','label':'Assignees (comma separated GitHub usernames)','reqd':1}
], function(values){
frappe.call({
method: 'erpnext_github_integration.github_api.assign_issue',
args: {repo_full_name: repo, issue_number: issue_no, assignees: values.assignees},
callback: function(r) {
frappe.msgprint(__('Assigned issue {0}').format(issue_no));

// fetch ERPNext users with github_username set
frappe.db.get_list('User', {
fields: ['name', 'full_name', 'github_username'],
filters: { enabled: 1 },
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: 'assignees',
fieldtype: 'MultiSelectPills',
label: 'Assign to Users',
options: user_options,
reqd: 1
}
});
}, __('Assign Issue'));
], function(values) {
frappe.call({
method: 'erpnext_github_integration.github_api.assign_issue',
args: {
repo_full_name: repo,
issue_number: issue_no,
assignees: values.assignees
},
callback: function(r) {
frappe.msgprint(__('Assigned issue {0}', [issue_no]));
}
});
}, __('Assign Issue'));
});
});

frm.add_custom_button(__('Add PR Reviewer'), function() {
Expand Down
Loading