feat/002_init-remote
Raw Download raw file

tissue import

Import issues from external sources.

Tissue Commands

# Import from GitHub
tissue import github --repo user/repo
tissue import github --repo user/repo --token $GITHUB_TOKEN

# Import from JSON
tissue import issues.json

Manual Workflow (bash + git)

Import from GitHub Issues

# Import GitHub issues using GitHub CLI or API
import_from_github() {
    REPO="$1"
    TOKEN="${2:-$GITHUB_TOKEN}"

    cd ../issues 2>/dev/null || return 1

    echo "Importing issues from GitHub: $REPO"

    # Using GitHub CLI (if installed)
    if command -v gh &> /dev/null; then
        # List all issues and export as JSON
        gh issue list --repo "$REPO" --limit 1000 --json number,title,state,body,labels,assignees,createdAt,updatedAt > github_issues.json

        # Process each issue
        jq -c '.[]' github_issues.json | while read -r issue; do
            NUMBER=$(echo "$issue" | jq -r '.number')
            TITLE=$(echo "$issue" | jq -r '.title')
            STATE=$(echo "$issue" | jq -r '.state')
            BODY=$(echo "$issue" | jq -r '.body // ""')
            CREATED=$(echo "$issue" | jq -r '.createdAt')
            UPDATED=$(echo "$issue" | jq -r '.updatedAt')

            # Map GitHub state to tissue status
            case "$STATE" in
                open) STATUS="open" ;;
                closed) STATUS="closed" ;;
                *) STATUS="open" ;;
            esac

            # Extract labels
            LABELS=$(echo "$issue" | jq -r '.labels[].name' | tr '\n' ',' | sed 's/,$//')

            # Extract assignees
            ASSIGNEES=$(echo "$issue" | jq -r '.assignees[].login' | tr '\n' ',' | sed 's/,$//')

            # Create issue file with empty frontmatter
            PADDED_NUM=$(printf "%03d" "$NUMBER")
            SAFE_TITLE=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | \
                         tr ' ' '-' | tr -cd '[:alnum:]-' | cut -c1-50)
            FILENAME="${PADDED_NUM}_${SAFE_TITLE}.md"

            # Create minimal file
            echo -e "---\n---\n\n# ${TITLE}\n\n${BODY}\n\n---\n*Imported from GitHub: ${REPO}#${NUMBER}*" > "$FILENAME"

            # Build frontmatter using yq
            yq --front-matter '.id = "github-'"$NUMBER"'"' -i "$FILENAME"
            yq --front-matter '.title = "'"$TITLE"'"' -i "$FILENAME"
            yq --front-matter '.status = "'"$STATUS"'"' -i "$FILENAME"
            yq --front-matter '.priority = "medium"' -i "$FILENAME"

            # Convert labels to array
            if [ -n "$LABELS" ]; then
                LABELS_JSON=$(echo "$LABELS" | jq -R 'split(",") | map(ltrimstr(" ") | rtrimstr(" "))')
                yq --front-matter ".tags = $LABELS_JSON" -i "$FILENAME"
            else
                yq --front-matter '.tags = []' -i "$FILENAME"
            fi

            yq --front-matter '.created = "'"$CREATED"'"' -i "$FILENAME"
            yq --front-matter '.updated = "'"$UPDATED"'"' -i "$FILENAME"
            yq --front-matter '.author = "imported"' -i "$FILENAME"
            yq --front-matter '.assignee = "'"${ASSIGNEES:-unassigned}"'"' -i "$FILENAME"
            yq --front-matter '.source = "github:'"$REPO"'#'"$NUMBER"'"' -i "$FILENAME"

            echo "Imported issue #${NUMBER}: ${TITLE}"
        done

        rm github_issues.json
    else
        # Using curl and GitHub API
        echo "Using GitHub API directly..."

        # Fetch issues via API
        curl -s \
            ${TOKEN:+-H "Authorization: token $TOKEN"} \
            "https://api.github.com/repos/${REPO}/issues?state=all&per_page=100" \
            > github_issues.json

        # Process with jq (same as above)
        # ... (similar processing logic)
    fi

    # Commit imported issues
    git add -A
    git commit -m "Import issues from GitHub: $REPO"

    echo "Import complete!"
    cd - > /dev/null
}

import_from_github "user/repo"

Import from JSON

# Import issues from JSON file
import_from_json() {
    JSON_FILE="$1"

    if [ ! -f "$JSON_FILE" ]; then
        echo "File not found: $JSON_FILE"
        return 1
    fi

    cd ../issues 2>/dev/null || return 1

    echo "Importing issues from: $JSON_FILE"

    # Process each issue in JSON
    jq -c '.[]' "$JSON_FILE" | while read -r issue; do
        # Extract fields (with defaults)
        TITLE=$(echo "$issue" | jq -r '.title // "Untitled Issue"')
        STATUS=$(echo "$issue" | jq -r '.status // "open"')
        PRIORITY=$(echo "$issue" | jq -r '.priority // "medium"')
        DESCRIPTION=$(echo "$issue" | jq -r '.description // .body // ""')
        TAGS=$(echo "$issue" | jq -r '.tags // [] | join(",")')
        ASSIGNEE=$(echo "$issue" | jq -r '.assignee // ""')
        CREATED=$(echo "$issue" | jq -r '.created // "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"')
        UPDATED=$(echo "$issue" | jq -r '.updated // "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"')

        # Generate issue number
        ISSUE_NUM=$(($(ls -1 *.md 2>/dev/null | wc -l) + 1))
        PADDED_NUM=$(printf "%03d" "$ISSUE_NUM")
        SAFE_TITLE=$(echo "$TITLE" | tr '[:upper:]' '[:lower:]' | \
                     tr ' ' '-' | tr -cd '[:alnum:]-' | cut -c1-50)
        FILENAME="${PADDED_NUM}_${SAFE_TITLE}.md"

        # Create minimal file
        echo -e "---\n---\n\n# ${TITLE}\n\n${DESCRIPTION}" > "$FILENAME"

        # Build frontmatter using yq
        IMPORT_ID="import-$(date +%s%N | sha256sum | cut -c1-8)"
        yq --front-matter '
            .id = "'"$IMPORT_ID"'" |
            .title = "'"$TITLE"'" |
            .status = "'"$STATUS"'" |
            .priority = "'"$PRIORITY"'" |
            .created = "'"$CREATED"'" |
            .updated = "'"$UPDATED"'" |
            .author = "imported"
        ' -i "$FILENAME"

        # Handle tags
        if [ -n "$TAGS" ]; then
            TAGS_JSON=$(echo "$TAGS" | jq -R 'split(",") | map(ltrimstr(" ") | rtrimstr(" "))')
            yq --front-matter ".tags = $TAGS_JSON" -i "$FILENAME"
        else
            yq --front-matter '.tags = []' -i "$FILENAME"
        fi

        # Handle assignee
        if [ -n "$ASSIGNEE" ]; then
            yq --front-matter '.assignee = "'"$ASSIGNEE"'"' -i "$FILENAME"
        else
            yq --front-matter '.assignee = null' -i "$FILENAME"
        fi

        echo "Imported: ${FILENAME}"
    done

    git add -A
    git commit -m "Import issues from JSON: $(basename "$JSON_FILE")"

    echo "Import complete!"
    cd - > /dev/null
}

import_from_json "issues.json"

What tissue does for you:

  1. Imports from multiple platforms (GitHub, GitLab, Jira)
  2. Handles various formats (JSON, CSV, API)
  3. Maps external fields to tissue format
  4. Validates imported data
  5. Prevents duplicate imports
  6. Creates backups before importing
  7. Maintains source references
  8. Handles authentication for APIs
  9. Provides rollback capability
  10. Generates import summaries