Skip to content

[Feat] jira 연동 작업 #9

[Feat] jira 연동 작업

[Feat] jira 연동 작업 #9

name: Sync GitHub Issues with Jira
on:
issues:
types: [opened, edited, labeled, unlabeled, closed, reopened]
issue_comment:
types: [created, edited, deleted]
jobs:
sync_with_jira:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Sync GitHub Issues with Jira
env:
JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }}
JIRA_EMAIL: ${{ secrets.JIRA_EMAIL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
node <<EOF
const https = require('https');
const jiraBaseUrl = process.env.JIRA_BASE_URL;
const jiraEmail = process.env.JIRA_EMAIL;
const jiraApiToken = process.env.JIRA_API_TOKEN;
const githubToken = process.env.GITHUB_TOKEN;
const issue = process.env.GITHUB_EVENT_PATH ? require(process.env.GITHUB_EVENT_PATH).issue : null;
const action = process.env.GITHUB_EVENT_PATH ? require(process.env.GITHUB_EVENT_PATH).action : null;
if (!issue) {
console.log('No issue information available.');
process.exit(0);
}
const jiraAuth = Buffer.from(\`\${jiraEmail}:\${jiraApiToken}\`).toString('base64');
const jiraSearchUrl = new URL('/rest/api/3/search', jiraBaseUrl);
function httpsRequest(url, options, data) {
return new Promise((resolve, reject) => {
const req = https.request(url, options, (res) => {
let body = '';
res.on('data', (chunk) => {
body += chunk;
});
res.on('end', () => {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(JSON.parse(body));
} else {
reject(new Error(body));
}
});
});
req.on('error', reject);
if (data) {
req.write(data);
}
req.end();
});
}
async function getJiraIssueKey(issueTitle) {
try {
console.log("issueTitle" + issueTitle)
const jql = \`project=NOFFICE AND "Epic Link"=NOFFICE-33 AND summary~"\${issueTitle}"\`;
const searchParams = new URLSearchParams({ jql: jql });
jiraSearchUrl.search = searchParams.toString();
const options = {
method: 'GET',
headers: {
'Authorization': \`Basic \${jiraAuth}\`,
'Content-Type': 'application/json'
}
};
const response = await httpsRequest(jiraSearchUrl, options);
const issues = response.issues;
if (issues.length > 0) {
return issues[0].key;
} else {
console.log('No matching Jira issue found.');
return null;
}
} catch (error) {
console.error('Error fetching Jira issue:', error.message);
return null;
}
}
async function updateJiraIssue(jiraIssueKey) {
try {
const jiraIssueUrl = new URL(\`/rest/api/3/issue/\${jiraIssueKey}\`, jiraBaseUrl);
const githubIssueUrl = issue.html_url;
const updateData = JSON.stringify({
fields: {
summary: issue.title,
description: issue.body + \`\n\n[View on GitHub](\${githubIssueUrl})\`
}
});
const options = {
method: 'PUT',
headers: {
'Authorization': \`Basic \${jiraAuth}\`,
'Content-Type': 'application/json'
}
};
await httpsRequest(jiraIssueUrl, options, updateData);
if (action === 'labeled' || action === 'unlabeled') {
const labels = issue.labels.map(label => label.name);
const updateLabelsData = JSON.stringify({
update: {
labels: labels.map(label => ({ set: { value: label } }))
}
});
await httpsRequest(jiraIssueUrl, options, updateLabelsData);
}
if (action === 'closed') {
const transitionData = JSON.stringify({
transition: {
id: '31' // Transition ID for Done
}
});
await httpsRequest(new URL(\`\${jiraIssueUrl}/transitions\`, jiraBaseUrl), {
method: 'POST',
headers: options.headers
}, transitionData);
}
if (action === 'reopened') {
const transitionData = JSON.stringify({
transition: {
id: '11' // Transition ID for Reopened
}
});
await httpsRequest(new URL(\`\${jiraIssueUrl}/transitions\`, jiraBaseUrl), {
method: 'POST',
headers: options.headers
}, transitionData);
}
console.log('Jira issue updated successfully.');
} catch (error) {
console.error('Error updating Jira issue:', error.message);
}
}
async function main() {
const jiraIssueKey = await getJiraIssueKey(issue.title);
if (jiraIssueKey) {
await updateJiraIssue(jiraIssueKey);
}
}
main();
EOF