/

Linear Demo Video Tickets Creator

Copy script

Copied

Copy script

Copied

Linear Demo Video Tickets Creator

Creates recurring demo video tickets in Linear for all team members on specified days. Automatically finds the keyboard team, gets all users, and creates tickets with detailed instructions for creating demo videos. Configurable parameters include team name, project name, demo days, and priority levels.

Created by

Stephen Roland - Keyboard Team

Requirements

linear.app logo

KEYBOARD_LINEAR_API_KEY

Script

Copy script

Copied

Copy script

Copied

const axios = require('axios');

// Linear API configuration
const LINEAR_API_URL = 'https://api.linear.app/graphql';
const LINEAR_API_KEY = process.env.KEYBOARD_LINEAR_API_KEY;

if (!LINEAR_API_KEY) {
    console.error('āŒ Linear API key not found in environment variables');
    process.exit(1);
}

// Linear expects just the API key, not "Bearer" prefix
const headers = {
    'Content-Type': 'application/json',
    'Authorization': LINEAR_API_KEY
};

console.log(`šŸš€ Starting Linear ticket creation for ${projectName} demo videos...`);

async function makeLinearRequest(query) {
    try {
        const response = await axios.post(LINEAR_API_URL, { query }, { headers });
        if (response.data.errors) {
            console.error('GraphQL Errors:', JSON.stringify(response.data.errors, null, 2));
            return null;
        }
        return response.data.data;
    } catch (error) {
        console.error('API Error:', error.response?.data || error.message);
        return null;
    }
}

// Get teams and users
async function getTeamsAndUsers() {
    const query = `
    query {
      teams {
        nodes {
          id
          name
          key
        }
      }
      users {
        nodes {
          id
          name
          email
          displayName
        }
      }
    }`;
    
    console.log('šŸ“‹ Fetching teams and users...');
    return await makeLinearRequest(query);
}

// Get teams and users first
const data = await getTeamsAndUsers();

if (!data) {
    console.error('āŒ Failed to fetch teams and users');
    process.exit(1);
}

console.log('šŸ“Š Available teams:');
data.teams.nodes.forEach(team => {
    console.log(`  • ${team.name} (${team.key}) - ID: ${team.id}`);
});

console.log('\nšŸ‘„ Available users:');
data.users.nodes.forEach(user => {
    console.log(`  • ${user.displayName || user.name} (${user.email}) - ID: ${user.id}`);
});

// Find the target team
let targetTeam = data.teams.nodes.find(team => 
    team.name.toLowerCase().includes(teamName.toLowerCase()) || 
    team.key.toLowerCase().includes(teamName.toLowerCase())
);

// If no specific team found, use the first available team
if (!targetTeam && data.teams.nodes.length > 0) {
    targetTeam = data.teams.nodes[0];
    console.log(`āš ļø No specific '${teamName}' team found, using first available team: ${targetTeam.name}`);
} else if (targetTeam) {
    console.log(`āœ… Found ${teamName} team: ${targetTeam.name} (${targetTeam.key})`);
}

if (!targetTeam) {
    console.error('āŒ No teams available');
    process.exit(1);
}

// Create tickets for each user
const users = data.users.nodes;
const createdTickets = [];

console.log(`\nšŸŽ¬ Creating demo video tickets for ${users.length} team members...`);

for (const user of users) {
    // Create first day ticket
    const day1Mutation = `
    mutation {
      issueCreate(
        input: {
          title: "Create Demo Video for ${projectName} - ${day1}"
          description: "## Demo Video Task\\n\\n**Assigned to:** ${user.displayName || user.name}\\n\\n**Task:** Create a demo video showcasing the ${projectName} project features and functionality.\\n\\n### Requirements:\\n- Record a comprehensive demo of current features\\n- Highlight key functionality and user workflows\\n- Keep video concise but informative (5-10 minutes recommended)\\n- Include any recent updates or improvements\\n\\n**Due:** Every ${day1}\\n\\n**Note:** This is a recurring task. Please complete and update this ticket every ${day1} with your demo video."
          teamId: "${targetTeam.id}"
          assigneeId: "${user.id}"
          priority: ${priority}
        }
      ) {
        success
        issue {
          id
          title
          assignee {
            name
          }
        }
      }
    }`;

    const day1Result = await makeLinearRequest(day1Mutation);
    
    if (day1Result?.issueCreate?.success) {
        console.log(`āœ… ${day1} ticket created for ${user.displayName || user.name} - ID: ${day1Result.issueCreate.issue.id}`);
        createdTickets.push({
            day: day1,
            user: user.displayName || user.name,
            ticketId: day1Result.issueCreate.issue.id
        });
    } else {
        console.log(`āŒ Failed to create ${day1} ticket for ${user.displayName || user.name}`);
    }

    // Create second day ticket
    const day2Mutation = `
    mutation {
      issueCreate(
        input: {
          title: "Create Demo Video for ${projectName} - ${day2}"
          description: "## Demo Video Task\\n\\n**Assigned to:** ${user.displayName || user.name}\\n\\n**Task:** Create a demo video showcasing the ${projectName} project features and functionality.\\n\\n### Requirements:\\n- Record a comprehensive demo of current features\\n- Highlight key functionality and user workflows\\n- Keep video concise but informative (5-10 minutes recommended)\\n- Include any recent updates or improvements\\n\\n**Due:** Every ${day2}\\n\\n**Note:** This is a recurring task. Please complete and update this ticket every ${day2} with your demo video."
          teamId: "${targetTeam.id}"
          assigneeId: "${user.id}"
          priority: ${priority}
        }
      ) {
        success
        issue {
          id
          title
          assignee {
            name
          }
        }
      }
    }`;

    const day2Result = await makeLinearRequest(day2Mutation);
    
    if (day2Result?.issueCreate?.success) {
        console.log(`āœ… ${day2} ticket created for ${user.displayName || user.name} - ID: ${day2Result.issueCreate.issue.id}`);
        createdTickets.push({
            day: day2,
            user: user.displayName || user.name,
            ticketId: day2Result.issueCreate.issue.id
        });
    } else {
        console.log(`āŒ Failed to create ${day2} ticket for ${user.displayName || user.name}`);
    }

    // Small delay to avoid rate limiting
    await new Promise(resolve => setTimeout(resolve, 500));
}

console.log(`\nšŸ“‹ Summary of created tickets:`);
console.log(`Total tickets created: ${createdTickets.length}`);
console.log(`Team: ${targetTeam.name} (${targetTeam.key})`);

createdTickets.forEach(ticket => {
    console.log(`  • ${ticket.day} - ${ticket.user} - Ticket ID: ${ticket.ticketId}`);
});

// Verify tickets by querying them
console.log('\nšŸ” Verifying created tickets...');
const verifyQuery = `
query {
  issues(filter: { team: { id: { eq: "${targetTeam.id}" } } }) {
    nodes {
      id
      title
      assignee {
        name
      }
      createdAt
    }
  }
}`;

const verificationResult = await makeLinearRequest(verifyQuery);
if (verificationResult?.issues?.nodes) {
    const recentTickets = verificationResult.issues.nodes.filter(issue => 
        issue.title.includes(`Demo Video for ${projectName}`)
    );
    console.log(`āœ… Verification complete: ${recentTickets.length} demo video tickets found in ${targetTeam.name} team`);
    
    if (recentTickets.length > 0) {
        console.log('\nšŸŽÆ Successfully created tickets:');
        recentTickets.forEach(ticket => {
            console.log(`  • ${ticket.title} - Assigned to: ${ticket.assignee?.name || 'Unassigned'}`);
        });
    }
} else {
    console.log('āš ļø Could not verify tickets - but creation calls appeared successful');
}

console.log('\nšŸŽ‰ Demo video ticket creation process completed!');