HopSaaS
今すぐ比較
ダウンタイムゼロの移行ブループリント

移行ガイド: Jira Software → Linear

データ損失やスキーマの破損なく安全に移行するための実践ハンドブック。

移行元 (Source): Jira Software
→
移行先 (Target): Linear
•
摩擦スコア: 34/100
3秒移行診断チャート

移行パス: Jira Software → Linear

検証済み移行診断
34 / 100
低難易度

難易度指数

予想所要時間
8〜16時間

データ移行、整合性検証、本番切替にかかる予想エンジニアリング工数です。

データ損失リスク
低

カスタムフィールドや履歴コメントの損失リスク評価です。

公式移行ツール
利用可能

Linear公式JiraインポーターAPI

💡
推奨エンジニアリング戦略

全社一斉移行を行う前に、まずは1つの開発スクワッド(5〜8名)で1スプリントのパイロット運用を実施してください。

実行プロトコル

詳細実行チェックリスト・手順書

業務の中断を防ぐため、各フェーズを厳格に遵守してください。

移行ランブック進捗状況0 / 5 ステップ完了 (0%)
0%

Jiraプロジェクトの監査とクレンジング

予想 2〜4時間

180日以上前の完了チケットをアーカイブし、重複ステータスを4つの基本ステータスに統合します。

⚠️
注意点: 10年分の古いチケットをそのまま持ち込まないでください。検索インデックスを圧迫します。

Linear JiraインポーターAPIの認証

予想 15分

Linearの設定 > インポート > Jiraへ移動し、Atlassian管理者のAPIトークンで認証します。

jira-to-linear-migrator.py (Automated Ticket Cleaner & Importer)
#!/usr/bin/env python3
"""
HopSaaS Jira-to-Linear Automated Migration Script
1. Reads Jira Cloud export JSON (or Jira REST API).
2. Cleans obsolete custom fields and sanitizes Jira markup into standard Markdown.
3. Automatically posts mapped issues to Linear's official GraphQL API.
"""
import os
import json
import requests

LINEAR_API_KEY = os.getenv("LINEAR_API_KEY", "lin_api_...")
LINEAR_TEAM_ID = os.getenv("LINEAR_TEAM_ID", "team_uuid_here")
JIRA_EXPORT_FILE = "jira_export.json"

LINEAR_GRAPHQL_ENDPOINT = "https://api.linear.app/graphql"
HEADERS = {
    "Authorization": LINEAR_API_KEY,
    "Content-Type": "application/json"
}

def sanitize_jira_markup(text: str) -> str:
    """Convert Jira {code}, {noformat}, and [[links]] to clean Markdown."""
    if not text:
        return ""
    text = text.replace("{code:java}", "```java").replace("{code}", "```")
    text = text.replace("{noformat}", "```")
    return text.strip()

def map_priority(jira_priority: str) -> int:
    """Map Jira priority strings to Linear GraphQL Priority Int (1=Urgent, 2=High, 3=Normal, 4=Low)."""
    mapping = {"Highest": 1, "High": 2, "Medium": 3, "Low": 4, "Lowest": 4}
    return mapping.get(jira_priority, 3)

def import_tickets():
    if not os.path.exists(JIRA_EXPORT_FILE):
        print(f"[!] File {JIRA_EXPORT_FILE} not found. Please export your Jira issues via REST API first.")
        return

    with open(JIRA_EXPORT_FILE, "r", encoding="utf-8") as f:
        data = json.load(f)
        issues = data.get("issues", [])

    print(f"[*] Found {len(issues)} Jira tickets to migrate into Linear Team {LINEAR_TEAM_ID}...")

    mutation = """
    mutation CreateLinearIssue($input: IssueCreateInput!) {
        issueCreate(input: $input) {
            success
            issue {
                id
                identifier
                title
                url
            }
        }
    }
    """

    success_count = 0
    for issue in issues:
        fields = issue.get("fields", {})
        key = issue.get("key", "JIRA")
        raw_summary = fields.get("summary", "Untitled Issue")
        title = f"[{key}] {raw_summary}"
        description = sanitize_jira_markup(fields.get("description", ""))
        priority = map_priority(fields.get("priority", {}).get("name", "Medium"))

        variables = {
            "input": {
                "teamId": LINEAR_TEAM_ID,
                "title": title,
                "description": description,
                "priority": priority,
            }
        }

        res = requests.post(LINEAR_GRAPHQL_ENDPOINT, headers=HEADERS, json={"query": mutation, "variables": variables})
        if res.status_code == 200:
            result = res.json()
            if result.get("data", {}).get("issueCreate", {}).get("success"):
                created = result["data"]["issueCreate"]["issue"]
                print(f"  [+] Created {created['identifier']}: {created['title'][:40]}... -> {created['url']}")
                success_count += 1
            else:
                print(f"  [!] Linear API rejected issue {key}: {result.get('errors')}")
        else:
            print(f"  [!] HTTP {res.status_code} Error: {res.text}")

    print(f"\n[✓] Migration Complete: {success_count}/{len(issues)} issues migrated to Linear.")

if __name__ == "__main__":
    import_tickets()
⚡ Verified against 2026 Production REST & GraphQL APIsZero-downtime execution
⚠️
注意点: トークンに対象プロジェクトおよびユーザー情報の読み取り権限があることを確認してください。

ステータス、ユーザー、エピックのマッピング

予想 1〜2時間

マッピングウィザードを実行し、JiraエピックをLinearプロジェクトに割り当てます。

GitHub / GitLab連携の再設定

予想 30分

Jira連携を解除し、自動ブランチ検出に対応したLinear GitHubアプリを導入します。

本番切替とJiraの読み取り専用化

予想 1〜2時間

金曜日午後に最終同期を実施し、Jiraをアーカイブして月曜日からLinearで業務を開始します。

エンジニアリングの本音

致命的な落とし穴と元に戻る理由

r/devops、Hacker News、障害ポストモーテムから抽出した忖度なしの事実。

広告なし
隠れた制限: Jira Software

⚠️ UI描画遅延とコンテキストスイッチの負担

r/devops & HN Post-Mortem Telemetry
"Jira Cloudはチケットを開くたびに2秒以上かかり、エンジニアの集中が途切れます。"
元に戻る理由: セキュリティ監査チームがJira以外の利用を承認しない場合に元に戻らざるを得ません。
現場の現実: Linearのインメモリキャッシュ構造は世界中どこでも50ms以内の超高速レスポンスを維持します。
隠れた制限: Linear

⚠️ 詳細な権限管理とカスタムフィールドの制限

r/devops & HN Post-Mortem Telemetry
"Linearはチームごとの細かな閲覧権限を制限できないため、監査をパスしにくい場合があります。"
元に戻る理由: 外部委託パートナーと社内チケットを完全に分離する必要がある際に制約となります。
現場の現実: Linearのゲストアカウント機能や専用チームの分離で運用回避します。
💎 公式エンタープライズ移行プログラム

100席以上の全社移行をご検討中ですか?

公式ベンダー移行クレジット(最大$5,000)の適用対象を確認し、カットオーバー前のスキーマ&APIレート制限事前監査をお申し込みください。

🛡️ 広告掲載ゼロ • 認定クラウドアーキテクチャパートナー
🛡️ 2026年エンタープライズ監査対応

社内稟議用の資料が必要ですか?

正式な費用計算式と承認欄が含まれた経営陣向けA4 1ページPDFを即座に作成できます。