Action
Create tickets in Linear
Create issues in Linear based on the current draft document.
Expects a structure like this:
# Project name
## Issue title one
This is the description of the issue. Anything is considered part of the description, up to the following H2 tag.
Descriptions can contain markdown.
## Issue title two
This is the description of the second issue.
Multiple workspaces
If you wish to store multiple sets of credentials for different workspaces, you can specify an identifier for the workspace in the project title, like so:
# Project using default credentials
# Project in other team [OtherTeam]
Running multiple times
The action will add links to the created issues in the draft, and will skip creating issues for previously created issues. A great way to iteratively add more issues for a project.
Steps
-
script
function createTask(credential, teamId, currentTask, newContent) { let { title, description, project } = currentTask; teamId = credential.getValue("teamId") || teamId; const apiKey = credential.getValue("apiKey"); description = description.join("\n").trim(); if(description.includes('_Issue: [')){ return; } const query = ` mutation IssueCreate($title: String!, $stateId: String, $teamId: String!, $description: String) { issueCreate( input: { title: $title description: $description teamId: $teamId, stateId: $stateId } ) { success issue { identifier url } } } `; const http = HTTP.create(); const response = http.request({ method: "POST", url: "https://api.linear.app/graphql", headers: { "Authorization": `Bearer ${apiKey}` }, data: { query, variables: { title, description, stateId, teamId } } }); if (response.success) { const { issue } = response.responseData.data.issueCreate; if (newContent.length && newContent[newContent.length - 1].trim() !== "") { newContent.push(""); } newContent.push(`_Issue: [${issue.identifier}](${issue.url})_\n\n`); } else { newContent.push(`\nERROR: ${response.statusCode} -- ${response.error}`); } } // Save a version to allow easy restoring draft.saveVersion(); const lines = draft.content.split("\n"); const newContent = []; let project, team; if (lines[0].startsWith('#')) { project = lines.shift().replace(/^# /, ""); newContent.push(`# ${project}`); } if(project && project.match(/\[\w+\]/)){ team = project.substring(project.indexOf('[')+1).split(']')[0] } const credential = Credential.create('Linear' + (team ? ` - ${team}` : ''), "Credentials for a Linear Team"); credential.addPasswordField('apiKey', "API key"); credential.addTextField('teamId', "Team ID (optional)"); credential.authorize(); // Get default team let teamId; if (!credential.getValue("teamId")) { const http = HTTP.create(); const {responseData: team} = http.request({ method: "POST", url: "https://api.linear.app/graphql", headers: { "Authorization": `Bearer ${credential.getValue("apiKey")}` }, data: { query: '{ teams { nodes { id } } }' } }); teamId = team.data.teams.nodes[0].id; } // Get default state let stateId; if(!stateId){ const http = HTTP.create(); const {responseData: states} = http.request({ method: "POST", url: "https://api.linear.app/graphql", headers: { "Authorization": `Bearer ${credential.getValue("apiKey")}` }, data: { query: '{ workflowStates { nodes { id name } } }' } }); stateId = states.data.workflowStates.nodes?.find(({name}) => name.toLowerCase().trim() === 'backlog')?.id; } let currentTask = null; for (const line of lines) { const l = line.trim(); if (l.startsWith('## ')) { if (currentTask) { createTask(credential, teamId, currentTask, newContent); } currentTask = { project, stateId, title: l.replace(/^[#]+\s+/, ''), description: [] } } else if (currentTask) { currentTask.description.push(line || ' '); } newContent.push(line); } if (currentTask) { createTask(credential, teamId, currentTask, newContent); } draft.content = newContent.join("\n"); draft.update();
Options
-
After Success Default , Tags: linear Notification Info Log Level Info
Items available in the Drafts Directory are uploaded by community members. Use appropriate caution reviewing downloaded items before use.