見出し画像

【自動化】 ラベルから TODO を自動生成する方法

手動作業からの解放。僕が実践する 「ラベル → TODO 自動化」 の全工程

「また手動で TODO を作成するの?」

フリーランス開発者として 14 年間、この繰り返し作業に何度も心が折れそうになりました。

GitHub Issues にラベルを付けるたびに、Todoist に手動で TODO を追加する。

一見簡単な作業ですが、これが積み重なると、想像以上の時間と集中力を奪われます。

でも、ある日気づいたんです。

「この作業、絶対に自動化できるはず」と。

今日は、僕が実際に使っている「GitHub Issues のラベル → Todoist の TODO 自動生成」パイプラインを、設計から実装、応用編まで一気通貫でお伝えします。

手動作業からの解放。その先にある、本当に価値のある仕事に集中できる時間を、あなたも手に入れてみませんか?


なぜ僕は 「ラベル → TODO 自動化」 を選んだのか?

フリーランス開発者として、プロジェクト管理は生命線です。

でも、従来の方法では限界を感じていました。

従来の課題

1. 手動作業の非効率性

☑️ GitHub Issues にラベルを付ける
☑️ Todoist を開く
☑️ 新しい TODO を作成
☑️ プロジェクトを選択
☑️ 期限を設定
☑️ 説明を追加

この一連の流れを、毎回手動で行うのは時間の無駄でした。

一見簡単な作業ですが、これが積み重なると想像以上の時間を奪われます。

特に複数のプロジェクトを並行して進めていると、1 日に何度もこの作業を繰り返すことになり、1 日の作業時間の 10% 以上をタスク管理に費やしてしまうこともありました。

しかも、この作業は創造的な価値を生み出さない、単純な繰り返し作業なのです。

2. 情報の不整合

☑️ GitHub と Todoist で情報が分断される
☑️ 進捗状況の同期が困難
☑️ 重複作業の発生

最も深刻だったのは、この「情報の分断」です。GitHub で Issue の進捗を確認し、Todoist でタスクの状況を把握する。

この二重管理により、どちらが最新の情報なのかわからなくなることが頻繁にありました。

特に、クライアントとの打ち合わせで「このタスクの進捗はどうですか?」と聞かれたとき、どちらのツールを確認すればいいのか迷ってしまう。

そんな経験を何度もしました。

3. 集中力の分散

☑️ 開発に集中している最中に、タスク管理ツールを切り替える
☑️ 思考の流れが中断される

開発に没頭している最中に、突然「あ、このタスクを Todoist に追加しなきゃ」と気づいて、作業を中断する。

この瞬間、それまで築き上げていた思考の流れが完全に断ち切られてしまいます。再び集中状態に戻るまでに 15 分、場合によっては 30 分かかることもありました。

この「集中力の分散」こそが、フリーランス開発者にとって最も大きな損失だったのです。

自動化で得られる価値

1. 時間の節約

☑️ 1 回のラベル付けで、TODO が自動生成される
☑️ 手動作業時間を 90% 削減

これまで毎回 5 分かかっていた手動作業が、ラベルを付けるだけで瞬時に完了するようになりました。

単純計算でも、1 日 10 回のタスク作成で 50 分の時間を節約。1 ヶ月では 20 時間以上の時間を生み出せるのです。

2. 情報の一元化

☑️ GitHub Issues が真のソース
☑️ Todoist は実行のためのツール

GitHub Issues を「真実のソース」として位置づけることで、情報の重複や不整合が完全に解消されました。

プロジェクトの進捗状況を GitHub で確認し、実行すべきタスクは Todoist で管理する。

この明確な役割分担により、迷いなく作業に集中できる環境が整ったのです。

3. 集中力の維持

☑️ 開発フローを中断することなく、タスク管理が完了

最も価値が高いのは、この「集中力の維持」です。

開発に没頭している最中に、タスク管理ツールを切り替える必要がなくなりました。

思考の流れが中断されることなく、創造的な作業に集中し続けられる。これこそが、自動化の真の価値なのです。

今日作る自動化パイプラインの全体像

システム構成

GitHub Issues (ラベル付与)
    ↓
GitHub Actions (トリガー)
    ↓
Todoist API (TODO作成)
    ↓
Obsidian (記録・分析)

このシンプルな 4 段階のパイプラインが、僕の手動作業を完全に自動化してくれます。

GitHub Issues でラベルを付けるだけで、Todoist に TODO が自動生成され、Obsidian でその記録を分析できる。まるで魔法のような仕組みですが、実は各段階で確実に動作する、信頼性の高いシステムなのです。

主要コンポーネント

1. GitHub Actions Workflow

☑️ Issues のラベル変更を監視
☑️ 条件に応じて Todoist API を呼び出し

GitHub Actions は、この自動化の心臓部です。

Issue にラベルが付与された瞬間を検知し、条件に応じて次のステップに進む。まるで「見張り番」のような役割を果たしてくれます。

僕が何も考えずにラベルを付けるだけで、システムが自動的に判断してくれるのです。

2. Todoist API 連携

☑️ プロジェクトの自動作成
☑️ TODO の詳細設定
☑️ 期限の自動計算

Todoist API との連携により、単なる TODO 作成を超えた、高度なタスク管理が実現されます。

プロジェクトの自動作成、期限の自動計算、優先度の設定など、手動では面倒な作業をすべて自動化。

これにより、僕は「何を作るか」に集中でき、「どう管理するか」はシステムに任せられるようになりました。

3. Obsidian 連携

☑️ 自動化ログの記録
☑️ パフォーマンス分析

最後に、Obsidian による記録と分析機能。自動化の効果を数値で確認し、改善点を見つけ出す。

これにより、システムは単なる「便利ツール」から「成長するパートナー」へと進化します。

毎日の自動化ログを見るたびに、どれだけの時間を節約できているかを実感できるのです。

ステップ 1: GitHub Actions Workflow の基本設計

ワークフローファイルの作成

`.github/workflows/label-to-todo.yml` を作成します:

name: Label to Todo Automation

on:
  issues:
    types: [labeled, unlabeled]
  issue_comment:
    types: [created]

jobs:
  create-todo:
    if: github.event.label.name == 'todo' || github.event.label.name == 'bug' || github.event.label.name == 'enhancement'
    runs-on: ubuntu-latest
    
    steps:
    - name: Checkout repository
      uses: actions/checkout@v4
      
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: '18'
        
    - name: Install dependencies
      run: npm install @doist/todoist-api-typescript
      
    - name: Create Todoist task
      env:
        TODOIST_API_TOKEN: ${{ secrets.TODOIST_API_TOKEN }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: |
        node scripts/create-todo.js

この YAML ファイルは、GitHub の「自動化レシピ」のようなものです。

Issue にラベルが付いたとき(`labeled`)や、ラベルが外されたとき(`unlabeled`)に自動で実行されます。

特定のラベル(`todo`、`bug`、`enhancement`)が付いたときだけ動作し、Node.js 環境を準備して、Todoist にタスクを作成するスクリプトを実行します。

基本的なスクリプト実装

`scripts/create-todo.js`:

const { TodoistApi } = require('@doist/todoist-api-typescript');

async function createTodoFromIssue() {
  const api = new TodoistApi(process.env.TODOIST_API_TOKEN);
  
  const issue = context.payload.issue;
  const label = context.payload.label;
  
  // プロジェクト名を生成
  const projectName = `GitHub: ${context.payload.repository.name}`;
  
  // プロジェクトを取得または作成
  let project = await findOrCreateProject(api, projectName);
  
  // TODO を作成
  const task = await api.addTask({
    content: `[GitHub] ${issue.title}`,
    project_id: project.id,
    description: `Issue: ${issue.html_url}\n\n${issue.body}`,
    due_string: calculateDueDate(label.name),
    labels: [label.name]
  });
  
  console.log(`Created task: ${task.id}`);
}

function calculateDueDate(labelName) {
  const dueDateMap = {
    'urgent': 'today',
    'high': 'tomorrow',
    'medium': 'next week',
    'low': 'next month'
  };
  
  return dueDateMap[labelName] || 'next week';
}

async function findOrCreateProject(api, projectName) {
  const projects = await api.getProjects();
  let project = projects.find(p => p.name === projectName);
  
  if (!project) {
    project = await api.addProject({
      name: projectName,
      color: 'blue'
    });
  }
  
  return project;
}

createTodoFromIssue().catch(console.error);

この JavaScript スクリプトは、GitHub Issue の情報を Todoist のタスクに変換する「翻訳者」の役割を果たします。

Issue のタイトルと説明を取得し、ラベルに応じて期限を自動設定。プロジェクトが存在しない場合は自動作成し、Todoist に新しいタスクを追加します。

ラベル名(`urgent`、`high` など)に応じて、今日、明日、来週といった期限を自動で決めてくれる賢い仕組みです。

ステップ 2: 高度な機能の実装

ラベル別の詳細設定

const labelConfig = {
  'bug': {
    priority: 4,
    dueString: 'today',
    labels: ['bug', 'github'],
    projectSuffix: 'Bugs'
  },
  'enhancement': {
    priority: 3,
    dueString: 'next week',
    labels: ['enhancement', 'github'],
    projectSuffix: 'Features'
  },
  'documentation': {
    priority: 2,
    dueString: 'next month',
    labels: ['documentation', 'github'],
    projectSuffix: 'Docs'
  }
};

async function createDetailedTodo(issue, label) {
  const config = labelConfig[label.name] || labelConfig['enhancement'];
  
  const task = await api.addTask({
    content: `[${label.name.toUpperCase()}] ${issue.title}`,
    project_id: await getProjectId(config.projectSuffix),
    description: generateDescription(issue),
    priority: config.priority,
    due_string: config.dueString,
    labels: config.labels,
    section_id: await getSectionId(label.name)
  });
  
  return task;
}

function generateDescription(issue) {
  return `
## GitHub Issue
- **URL**: ${issue.html_url}
- **Number**: #${issue.number}
- **Author**: ${issue.user.login}
- **Created**: ${new Date(issue.created_at).toLocaleDateString()}

## Description
${issue.body || 'No description provided'}

## Labels
${issue.labels.map(l => `- ${l.name}`).join('\n')}
  `.trim();
}

このコードは、ラベルごとに異なる設定を適用する「賢い分類システム」です。

バグ(`bug`)は最優先で今日中、機能追加(`enhancement`)は来週、ドキュメント(`documentation`)は来月といった具合に、ラベルに応じて自動的に優先度と期限を決めます。

さらに、Issue の詳細情報(URL、作成者、作成日など)を美しく整形して、Todoist のタスク説明に含めてくれます。

セクション管理の実装

async function getSectionId(sectionName) {
  const sections = await api.getSections();
  let section = sections.find(s => s.name === sectionName);
  
  if (!section) {
    section = await api.addSection({
      name: sectionName,
      project_id: await getProjectId('Main')
    });
  }
  
  return section.id;
}

この関数は、Todoist 内でタスクを整理する「フォルダー」のような役割を果たします。

ラベルごとに専用のセクション(例: バグ用、機能追加用)を作成し、関連するタスクをグループ化。

セクションが存在しない場合は自動作成して、タスクを適切な場所に配置してくれます。

ステップ 3: エラーハンドリングとログ管理

堅牢なエラーハンドリング

class TodoAutomationError extends Error {
  constructor(message, context) {
    super(message);
    this.context = context;
    this.timestamp = new Date().toISOString();
  }
}

async function createTodoWithErrorHandling(issue, label) {
  try {
    // バリデーション
    validateInput(issue, label);
    
    // API 呼び出し
    const task = await createDetailedTodo(issue, label);
    
    // 成功ログ
    await logSuccess(issue, task);
    
    return task;
    
  } catch (error) {
    // エラーログ
    await logError(error, { issue, label });
    
    // フォールバック処理
    await createFallbackTodo(issue, label);
    
    throw new TodoAutomationError(
      `Failed to create todo: ${error.message}`,
      { issue, label, originalError: error }
    );
  }
}

function validateInput(issue, label) {
  if (!issue || !issue.title) {
    throw new Error('Invalid issue: missing title');
  }
  
  if (!label || !label.name) {
    throw new Error('Invalid label: missing name');
  }
  
  if (issue.state === 'closed') {
    throw new Error('Cannot create todo for closed issue');
  }
}

このコードは、自動化システムの「安全装置」のような役割を果たします。

データの妥当性をチェックし、エラーが発生した場合は詳細な情報を記録。

さらに、通常の処理が失敗した場合でも、簡易版のタスクを作成する「フォールバック機能」を提供します。

これにより、システムが完全に停止することなく、継続して動作し続けます。

Obsidian 連携によるログ管理

async function logToObsidian(logData) {
  const fs = require('fs').promises;
  const path = require('path');
  
  const logEntry = {
    timestamp: new Date().toISOString(),
    type: logData.type,
    issue: {
      number: logData.issue.number,
      title: logData.issue.title,
      url: logData.issue.html_url
    },
    todo: {
      id: logData.todo?.id,
      content: logData.todo?.content
    },
    error: logData.error
  };
  
  const logFile = path.join(process.env.OBSIDIAN_VAULT_PATH, 'Logs', 'todo-automation.md');
  const logContent = `\n## ${logEntry.timestamp}\n\`\`\`json\n${JSON.stringify(logEntry, null, 2)}\n\`\`\`\n`;
  
  await fs.appendFile(logFile, logContent);
}

この関数は、自動化の「活動記録」を作成する「日記係」のような役割を果たします。

成功したタスク作成やエラーの詳細を、Obsidian のノートに時系列で記録。

JSON 形式で構造化されたデータとして保存するため、後から分析や問題の特定が容易になります。まるで自動化システムの「健康診断書」のような存在です。

ここから先は

12,779字 / 1画像

■メンバーシップ 15 年目フリーランス開発者のひとり戦略を支える作戦会議室です。\ 案件に追われる…

ベーシックプラン

¥500 / 月

『フリーランス開発ラボ – 裏ログ』は、個人開発者の日常に潜む秘密のノウハウをお届け。普段公開できない環境構築設定、効率化テク、リアルな失敗談から学び、あなたの開発力と生産性を加速させます。

開発スキルをグッと引き上げる “秘密の実践ノウハウ” を手に入れませんか? 『フリーランス開発ラボ – 裏ログ』では、500円で以下を配…

Amazon Payで支払うと最大2%還元のチャンス! 9/30まで

最後まで読んでいただき、ありがとうございます。もしこの記事が「役に立った」「心に響いた」と感じたら、珈琲一杯分サポートいただけると嬉しいです。あなたの温かい応援を力に、また次の創作活動に励みます。