feat: allow manual sync through workflow_dispatch

This commit is contained in:
Matyáš Caras 2024-07-30 19:53:06 +02:00
parent ac2825a020
commit 4b20d41574
Signed by: hernik
GPG key ID: 2A3175F98820C5C6
5 changed files with 315 additions and 32 deletions

View file

@ -36,6 +36,8 @@ jobs:
> It's okay to omit some of the `on` listening types, but it is needed to keep the `issue.opened` type,
> because it creates the task and the comment to track the task across action runs.
If you want to sync your current repository, run the workflow manually (`workflow_dispatch`). **This will only sync open issues.** Make sure to check the available parameters below.
### Parameters
| Parameter | Description | Required |
|--------------|--------------------------------------------------------------------------------------------------|-----------------------------|
@ -45,6 +47,8 @@ jobs:
| github-token | GitHub token used to create issue comments; you should use the default `secrets.GITHUB_TOKEN` | Yes |
| task-id | ID of the task under which subtasks will be created from issues | If `tasklist-id` is not set |
| tasklist-id | ID of the tasklist where tasks will be created from issues | If `task-id` is not set |
| create-tasks-for-unknown | Whether to create new tasks for issues without a Freelo comment from actions bot; used only when running the workflow manually | If `task-id` is not set |
| manually-sync-new-comments | Whether to sync new comments when running the workflow manually | If `task-id` is not set |
### Linking GitHub users to Freelo users
The action will look for a `freelo.txt` file inside of your `.github` folder (the one where Action workflows are stored).

View file

@ -19,6 +19,12 @@ inputs:
description: 'If not empty, will submit created issues as a subtask to the set task; either this or tasklist-id has to be entered.'
tasklist-id:
description: 'ID of the tasklist where tasks from GitHub issues should be created; either this or task-id has to be entered.'
create-tasks-for-unknown:
description: 'Whether to create a new task for issues that do not have a Freelo comment from the actions bot. Either true or false.'
required: true
manually-sync-new-comments:
description: 'Whether to sync comments when running manually'
required: true
runs:
using: 'node20'
main: 'dist/index.js'

146
dist/index.js vendored
View file

@ -45331,6 +45331,8 @@ var projectId = core.getInput("project-id");
var taskId = core.getInput("task-id");
var tasklistId = core.getInput("tasklist-id");
var token = core.getInput("github-token");
var createTasks = core.getInput("create-tasks-for-unknown");
var syncComments = core.getInput("manually-sync-new-comments");
if (!token) {
core.setFailed("No GitHub token passed");
throw new Error("No GitHub token passed");
@ -45365,7 +45367,121 @@ try {
}
try {
if (!action) {
throw new Error("No action was passed");
const issues = await octokit.rest.issues.listForRepo({ ...github.context.repo, state: "open" });
for (const i of issues.data) {
const currentTaskId = await freeloId(i.number);
if (!currentTaskId || currentTaskId.length === 1) {
console.log(`${i.number} has no Freelo comment`);
if (!createTasks) {
continue;
}
console.log(`Creating task for ${i.number}`);
const taskComment2 = `
Created by: ${freeloMention(i.user?.login ?? "Unknown")}<br>
Description: ${import_sanitize_html.default(i.body ?? "None", sanitizeOptions)}<br>
GitHub issue: <a href="${i.pull_request ? i.pull_request.url : i.url}">#${i.number}</a><br>
Assigned to: ${i.assignee ? `${freeloMention(i.assignee.login)}` : "Nobody"}<br>
${i.pull_request ? "<b>This is a pull request<b><br>" : ""}
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const labels2 = [];
if (i.labels) {
for (const label of i.labels) {
if (typeof label === "string") {
labels2.push({ name: label });
continue;
}
if (!label.name)
continue;
labels2.push({
name: label.name,
color: label.color ?? `#${label.color}`
});
}
}
const taskContent = {
name: i.title,
comment: {
content: taskComment2
},
labels: labels2
};
const res = await axios_default.post(!taskId ? `${apiEndpoint}/project/${projectId}/tasklist/${tasklistId}/tasks` : `${apiEndpoint}/task/${taskId}/subtasks`, taskContent, defaultOptions);
if (res.status > 399) {
console.error(res.data);
throw new Error("Got an error response from Freelo API");
}
octokit.rest.issues.createComment({
issue_number: i.number,
...github.context.repo,
body: `Freelo task assigned: <a href="https://app.freelo.io/task/${res.data.id}">${res.data.id}</a><br>Please do not edit or delete this comment as it is used to prevent duplication of tasks.`
});
console.log(`Created task ${res.data.id} in Freelo`);
continue;
}
const titleRes = await axios_default.post(`${apiEndpoint}/task/${currentTaskId[1]}`, {
name: i.title
}, defaultOptions);
if (titleRes.status > 399) {
console.error(titleRes.data);
throw new Error("Got an error response from Freelo API");
}
const taskComment = `
Created by: ${freeloMention(i.user?.login ?? "Unknown")}<br>
Description: ${import_sanitize_html.default(i.body ?? "None", sanitizeOptions)}<br>
GitHub issue: <a href="${i.url}">#${i.number}</a><br>
Assigned to: ${i.assignee ? `${freeloMention(i.assignee.login)}` : "Nobody"}<br>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const labels = [];
if (i.labels) {
for (const label of i.labels) {
if (typeof label === "string") {
labels.push({ name: label });
continue;
}
if (!label.name)
continue;
labels.push({
name: label.name,
color: label.color ?? `#${label.color}`
});
}
}
const labelRes = await axios_default.post(`${apiEndpoint}/task-labels/add-to-task/${currentTaskId}`, { labels }, defaultOptions);
if (labelRes.status > 399) {
console.error(labelRes.data);
throw new Error("Got an error response from Freelo API");
}
const bodyRes = await axios_default.post(`${apiEndpoint}/task/${currentTaskId[1]}/description`, {
comment: { content: taskComment },
labels
}, defaultOptions);
if (bodyRes.status > 399) {
console.error(bodyRes.data);
throw new Error("Got an error response from Freelo API");
}
console.log(`Updated issue ${i.number} in Freelo as ${currentTaskId}`);
if (!syncComments) {
continue;
}
for (const c of (await octokit.rest.issues.listComments({ ...github.context.repo, issue_number: i.number })).data) {
const taskComment2 = `
Comment <a href="${c.url}">${c.id}</a> by: ${freeloMention(c.user?.login ?? "Unknown")}<br>
${import_sanitize_html.default(c.body ?? "Comment not found", sanitizeOptions)}<br>
GitHub issue: <a href="${i.url}">#${i.number}</a><br>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const res = await axios_default.post(`${apiEndpoint}/task/${currentTaskId}/comments`, {
content: taskComment2
});
if (res.status > 399) {
console.error(res.data);
throw new Error("Got an error response from Freelo API");
}
console.log(`Created comment ${res.data.id}`);
}
}
}
if (!email || !apiKey || !projectId) {
throw new Error("You are missing a required parameter. Check the documentation for details.");
@ -45382,7 +45498,7 @@ try {
GitHub issue: <a href="${issue.pull_request ? issue.pull_request.url : issue.url}">#${issue.number}</a><br>
Assigned to: ${issue.assignee ? `${freeloMention(issue.assignee.login)}` : "Nobody"}<br>
${issue.pull_request ? "<b>This is a pull request<b><br>" : ""}
<i>(This action was performed automatically)</i>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const labels = [];
if (issue.labels) {
@ -45411,7 +45527,7 @@ try {
}
case "edited": {
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -45427,7 +45543,7 @@ try {
Description: ${import_sanitize_html.default(issue.body ?? "None", sanitizeOptions)}<br>
GitHub issue: <a href="${issue.url}">#${issue.number}</a><br>
Assigned to: ${issue.assignee ? `${freeloMention(issue.assignee.login)}` : "Nobody"}<br>
<i>(This action was performed automatically)</i>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const labels = [];
if (issue.labels) {
@ -45452,7 +45568,7 @@ try {
}
case "closed": {
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("No Freelo task ID identified");
break;
}
@ -45464,12 +45580,12 @@ try {
break;
}
case "reopened": {
const currentTaskid = await freeloId(issue.number);
if (!currentTaskid || currentTaskid.length === 0) {
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 1) {
console.log("No Freelo task ID identified");
break;
}
const res = await axios_default.post(`${apiEndpoint}/task/${currentTaskid[1]}/activate`, null, defaultOptions);
const res = await axios_default.post(`${apiEndpoint}/task/${currentTaskId[1]}/activate`, null, defaultOptions);
if (res.status > 399) {
console.error(res.data);
throw new Error("Got an error response from Freelo API");
@ -45480,7 +45596,7 @@ try {
if (!github.context.payload.assignee || !userPairing[github.context.payload.assignee.login])
break;
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -45497,7 +45613,7 @@ try {
if (!github.context.payload.assignee || !userPairing[github.context.payload.assignee.login])
break;
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -45525,7 +45641,7 @@ try {
switch (action) {
case "created": {
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -45533,7 +45649,7 @@ try {
Comment <a href="${comment.url}">${comment.id}</a> by: ${freeloMention(comment.user.login)}<br>
${import_sanitize_html.default(comment.body, sanitizeOptions)}<br>
GitHub issue: <a href="${issue.url}">#${issue.number}</a><br>
<i>(This action was performed automatically)</i>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const res = await axios_default.post(`${apiEndpoint}/task/${currentTaskId}/comments`, {
content: taskComment
@ -45547,7 +45663,7 @@ try {
}
case "deleted": {
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -45571,7 +45687,7 @@ try {
}
case "edited": {
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -45589,7 +45705,7 @@ try {
Comment <a href="${comment.url}">${comment.id}</a> by: ${freeloMention(comment.user.login)}<br>
${import_sanitize_html.default(comment.body, sanitizeOptions)}<br>
GitHub issue: <a href="${issue.url}">#${issue.number}</a><br>
<i>(This action was performed automatically)</i>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const res = await axios_default.post(`${apiEndpoint}/comment/${findComment[0].id}`, {
content: taskComment

View file

@ -1,6 +1,6 @@
interface Label {
name: string;
color: string;
color?: string;
}
interface NewTask {

View file

@ -12,6 +12,8 @@ const projectId = getInput("project-id");
const taskId = getInput("task-id");
const tasklistId = getInput("tasklist-id");
const token = getInput("github-token");
const createTasks = getInput("create-tasks-for-unknown");
const syncComments = getInput("manually-sync-new-comments")
if (!token) {
setFailed("No GitHub token passed");
@ -82,8 +84,163 @@ async function freeloId(issue_number: number): Promise<string | undefined> {
try {
if (!action) {
// TODO: Is run manually, check all issues
throw new Error("No action was passed");
// Check what needs to be synced
const issues = await octokit.rest.issues.listForRepo({ ...context.repo, state:"open" });
for (const i of issues.data) {
const currentTaskId = await freeloId(i.number);
if (!currentTaskId || currentTaskId.length === 1) {
console.log(`${i.number} has no Freelo comment`);
if (!createTasks) {
continue;
}
console.log(`Creating task for ${i.number}`);
const taskComment = `
Created by: ${freeloMention(i.user?.login ?? "Unknown")}<br>
Description: ${sanitize(i.body ?? "None", sanitizeOptions)}<br>
GitHub issue: <a href="${i.pull_request ? i.pull_request.url : i.url}">#${i.number}</a><br>
Assigned to: ${i.assignee ? `${freeloMention(i.assignee.login)}` : "Nobody"}<br>
${i.pull_request ? "<b>This is a pull request<b><br>" : ""}
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const labels: Label[] = [];
if (i.labels) {
for (const label of i.labels) {
if(typeof(label) === "string"){
labels.push({name:label})
continue
}
if (!label.name) continue;
labels.push({
name: label.name,
color: label.color ?? `#${label.color}`,
});
}
}
const taskContent: NewTask = {
name: i.title,
comment: {
content: taskComment,
},
labels,
};
const res = await axios.post(
!taskId
? `${apiEndpoint}/project/${projectId}/tasklist/${tasklistId}/tasks`
: `${apiEndpoint}/task/${taskId}/subtasks`,
taskContent,
defaultOptions,
);
// handle potential error response
if (res.status > 399) {
console.error(res.data);
throw new Error("Got an error response from Freelo API");
}
// create an issue comment so we can track if the task has been already created
octokit.rest.issues.createComment({
issue_number: i.number,
...context.repo,
body: `Freelo task assigned: <a href="https://app.freelo.io/task/${res.data.id}">${res.data.id}</a><br>Please do not edit or delete this comment as it is used to prevent duplication of tasks.`,
});
console.log(`Created task ${res.data.id} in Freelo`)
continue;
}
// Update known tasks
// Edit task title
const titleRes = await axios.post(
`${apiEndpoint}/task/${currentTaskId[1]}`,
{
name: i.title,
},
defaultOptions,
);
if (titleRes.status > 399) {
console.error(titleRes.data);
throw new Error("Got an error response from Freelo API");
}
const taskComment = `
Created by: ${freeloMention(i.user?.login ?? "Unknown")}<br>
Description: ${sanitize(i.body ?? "None", sanitizeOptions)}<br>
GitHub issue: <a href="${i.url}">#${i.number}</a><br>
Assigned to: ${i.assignee ? `${freeloMention(i.assignee.login)}` : "Nobody"}<br>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const labels: Label[] = [];
if (i.labels) {
for (const label of i.labels) {
if(typeof(label) === "string"){
labels.push({name:label})
continue
}
if (!label.name) continue;
labels.push({
name: label.name,
color: label.color ?? `#${label.color}`,
});
}
}
// Edit task labels
const labelRes = await axios.post(
`${apiEndpoint}/task-labels/add-to-task/${currentTaskId}`,
{ labels },
defaultOptions,
);
if (labelRes.status > 399) {
console.error(labelRes.data);
throw new Error("Got an error response from Freelo API");
}
// Edit task body
const bodyRes = await axios.post(
`${apiEndpoint}/task/${currentTaskId[1]}/description`,
{
comment: { content: taskComment },
labels,
},
defaultOptions,
);
if (bodyRes.status > 399) {
console.error(bodyRes.data);
throw new Error("Got an error response from Freelo API");
}
console.log(`Updated issue ${i.number} in Freelo as ${currentTaskId}`)
// Sync comments
if(!syncComments) {
continue;
}
for (const c of (await octokit.rest.issues.listComments({...context.repo,issue_number:i.number})).data) {
// New comment, add to Freelo task
const taskComment = `
Comment <a href="${c.url}">${c.id}</a> by: ${freeloMention(c.user?.login ?? "Unknown")}<br>
${sanitize(c.body ?? "Comment not found", sanitizeOptions)}<br>
GitHub issue: <a href="${i.url}">#${i.number}</a><br>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
// Create comment
const res = await axios.post(
`${apiEndpoint}/task/${currentTaskId}/comments`,
{
content: taskComment,
},
);
if (res.status > 399) {
console.error(res.data);
throw new Error("Got an error response from Freelo API");
}
console.log(`Created comment ${res.data.id}`);
}
}
}
if (!email || !apiKey || !projectId) {
throw new Error(
@ -108,7 +265,7 @@ try {
GitHub issue: <a href="${issue.pull_request ? issue.pull_request.url : issue.url}">#${issue.number}</a><br>
Assigned to: ${issue.assignee ? `${freeloMention(issue.assignee.login)}` : "Nobody"}<br>
${issue.pull_request ? "<b>This is a pull request<b><br>" : ""}
<i>(This action was performed automatically)</i>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const labels: Label[] = [];
@ -151,7 +308,7 @@ try {
case "edited": {
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -173,7 +330,7 @@ try {
Description: ${sanitize(issue.body ?? "None", sanitizeOptions)}<br>
GitHub issue: <a href="${issue.url}">#${issue.number}</a><br>
Assigned to: ${issue.assignee ? `${freeloMention(issue.assignee.login)}` : "Nobody"}<br>
<i>(This action was performed automatically)</i>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
const labels: Label[] = [];
@ -213,7 +370,7 @@ try {
case "closed": {
// Issue closed, finish task
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("No Freelo task ID identified");
break;
}
@ -231,15 +388,15 @@ try {
}
case "reopened": {
// Issue re-opened, activate task
const currentTaskid = await freeloId(issue.number);
if (!currentTaskid || currentTaskid.length === 0) {
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 1) {
console.log("No Freelo task ID identified");
break;
}
// Reactivate
const res = await axios.post(
`${apiEndpoint}/task/${currentTaskid[1]}/activate`,
`${apiEndpoint}/task/${currentTaskId[1]}/activate`,
null,
defaultOptions,
);
@ -258,7 +415,7 @@ try {
)
break;
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -284,7 +441,7 @@ try {
)
break;
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -330,7 +487,7 @@ try {
case "created": {
// New comment, add to Freelo task
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -339,7 +496,7 @@ try {
Comment <a href="${comment.url}">${comment.id}</a> by: ${freeloMention(comment.user.login)}<br>
${sanitize(comment.body, sanitizeOptions)}<br>
GitHub issue: <a href="${issue.url}">#${issue.number}</a><br>
<i>(This action was performed automatically)</i>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
// Create comment
@ -359,7 +516,7 @@ try {
case "deleted": {
// Find comment, delete it
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -393,7 +550,7 @@ try {
case "edited": {
// Find comment, edit it
const currentTaskId = await freeloId(issue.number);
if (!currentTaskId || currentTaskId.length === 0) {
if (!currentTaskId || currentTaskId.length === 1) {
console.log("Comment found, but no Freelo task ID identified");
break;
}
@ -419,7 +576,7 @@ try {
Comment <a href="${comment.url}">${comment.id}</a> by: ${freeloMention(comment.user.login)}<br>
${sanitize(comment.body, sanitizeOptions)}<br>
GitHub issue: <a href="${issue.url}">#${issue.number}</a><br>
<i>(This action was performed automatically)</i>
<i>(This action was performed automatically, please do not edit this comment)</i>
`;
// Create comment