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:
- Imports from multiple platforms (GitHub, GitLab, Jira)
- Handles various formats (JSON, CSV, API)
- Maps external fields to tissue format
- Validates imported data
- Prevents duplicate imports
- Creates backups before importing
- Maintains source references
- Handles authentication for APIs
- Provides rollback capability
- Generates import summaries