You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
207 lines
7.5 KiB
Bash
207 lines
7.5 KiB
Bash
#!/usr/bin/env bash
|
|
# migrate-nbd-to-beans.sh — Migrate nbd tickets to beans
|
|
#
|
|
# Usage: bash scripts/migrate-nbd-to-beans.sh [project-dir]
|
|
# Defaults to current directory if no arg given.
|
|
#
|
|
# Requirements: beans, jq
|
|
|
|
set -euo pipefail
|
|
|
|
PROJECT_DIR="${1:-.}"
|
|
PROJECT_DIR="$(cd "$PROJECT_DIR" && pwd)"
|
|
NBD_DIR="$PROJECT_DIR/.nbd/tickets"
|
|
MAP_FILE="/tmp/nbd_id_map_$(echo "$PROJECT_DIR" | tr '/' '_').tsv"
|
|
|
|
# ── Preflight ────────────────────────────────────────────────────────────────
|
|
|
|
if [[ ! -d "$NBD_DIR" ]]; then
|
|
echo "ERROR: No .nbd/tickets directory found in $PROJECT_DIR" >&2
|
|
exit 1
|
|
fi
|
|
|
|
command -v beans >/dev/null 2>&1 || { echo "ERROR: beans not found in PATH" >&2; exit 1; }
|
|
command -v jq >/dev/null 2>&1 || { echo "ERROR: jq not found in PATH" >&2; exit 1; }
|
|
|
|
# ── Field mappers ─────────────────────────────────────────────────────────────
|
|
|
|
map_priority() {
|
|
local p="${1:-5}"
|
|
if [[ "$p" -ge 8 ]]; then echo "critical"
|
|
elif [[ "$p" -eq 7 ]]; then echo "high"
|
|
elif [[ "$p" -ge 5 ]]; then echo "normal"
|
|
elif [[ "$p" -ge 3 ]]; then echo "low"
|
|
else echo "deferred"
|
|
fi
|
|
}
|
|
|
|
map_status() {
|
|
case "$1" in
|
|
todo) echo "todo" ;;
|
|
in_progress) echo "in-progress" ;;
|
|
done) echo "completed" ;;
|
|
closed) echo "scrapped" ;;
|
|
archived) echo "completed" ;;
|
|
backlog) echo "draft" ;;
|
|
*) echo "todo" ;;
|
|
esac
|
|
}
|
|
|
|
map_type() {
|
|
case "$1" in
|
|
project) echo "epic" ;;
|
|
feature) echo "feature" ;;
|
|
task) echo "task" ;;
|
|
bug) echo "bug" ;;
|
|
*) echo "task" ;;
|
|
esac
|
|
}
|
|
|
|
# ── Parse TOML frontmatter field (string value, strips surrounding quotes) ───
|
|
|
|
parse_field() {
|
|
local field="$1"
|
|
local file="$2"
|
|
awk -v field="$field" '
|
|
/^\+\+\+$/ { fm_count++; if (fm_count == 2) exit; next }
|
|
fm_count == 1 && $0 ~ "^" field " = " {
|
|
line = $0
|
|
sub("^" field " = \"?", "", line)
|
|
sub("\"?$", "", line)
|
|
print line
|
|
exit
|
|
}
|
|
' "$file"
|
|
}
|
|
|
|
# ── Extract body (everything after second +++) ────────────────────────────────
|
|
|
|
extract_body() {
|
|
local file="$1"
|
|
awk '/^\+\+\+$/ { count++; if (count == 2) { body=1; next } } body { print }' "$file"
|
|
}
|
|
|
|
# ── Extract dep IDs from a TOML array string ─────────────────────────────────
|
|
# Input: dependencies = ["abc123", "def456"]
|
|
# Output: abc123\ndef456 (one per line)
|
|
|
|
extract_dep_ids() {
|
|
echo "$1" | grep -oE '[0-9a-f]{6}' || true
|
|
}
|
|
|
|
# ── Init ──────────────────────────────────────────────────────────────────────
|
|
|
|
echo "==> Initializing beans in $PROJECT_DIR"
|
|
(cd "$PROJECT_DIR" && beans init)
|
|
|
|
# Clear map file: columns are old_id <TAB> new_id <TAB> deps_raw
|
|
> "$MAP_FILE"
|
|
|
|
tickets_created=0
|
|
tickets_failed=0
|
|
deps_wired=0
|
|
warnings=()
|
|
|
|
# ── Pass 1: Create beans ──────────────────────────────────────────────────────
|
|
|
|
echo ""
|
|
echo "==> Pass 1: Creating beans from nbd tickets"
|
|
|
|
body_tmp="$(mktemp)"
|
|
trap 'rm -f "$body_tmp"' EXIT
|
|
|
|
for ticket_file in "$NBD_DIR"/*.md; do
|
|
[[ -f "$ticket_file" ]] || continue
|
|
old_id="$(basename "$ticket_file" .md)"
|
|
|
|
# Parse frontmatter fields
|
|
title="$(parse_field title "$ticket_file")"
|
|
priority_num="$(parse_field priority "$ticket_file")"
|
|
status_raw="$(parse_field status "$ticket_file")"
|
|
type_raw="$(parse_field ticket_type "$ticket_file")"
|
|
deps_line="$(awk '/^\+\+\+$/ { fm_count++; if (fm_count == 2) exit; next } fm_count == 1 && /^dependencies = / { print; exit }' "$ticket_file")"
|
|
|
|
# Map to beans values
|
|
beans_priority="$(map_priority "${priority_num:-5}")"
|
|
beans_status="$(map_status "${status_raw:-todo}")"
|
|
beans_type="$(map_type "${type_raw:-task}")"
|
|
|
|
# Write body to temp file to avoid quoting issues
|
|
extract_body "$ticket_file" > "$body_tmp"
|
|
|
|
# Create the bean
|
|
result="$(cd "$PROJECT_DIR" && beans create \
|
|
--json \
|
|
--type "$beans_type" \
|
|
--status "$beans_status" \
|
|
--priority "$beans_priority" \
|
|
--body-file "$body_tmp" \
|
|
"$title" 2>&1)" || true
|
|
|
|
if new_id="$(echo "$result" | jq -r '.bean.id // .id' 2>/dev/null)" && [[ -n "$new_id" && "$new_id" != "null" ]]; then
|
|
printf '%s\t%s\t%s\n' "$old_id" "$new_id" "$deps_line" >> "$MAP_FILE"
|
|
(( tickets_created++ )) || true
|
|
echo " ✓ $old_id -> $new_id [$beans_status/$beans_priority/$beans_type] $title"
|
|
else
|
|
(( tickets_failed++ )) || true
|
|
warnings+=("WARN: failed to create ticket for nbd/$old_id: $title")
|
|
echo " ✗ $old_id FAILED: $title"
|
|
echo " Response: $result"
|
|
fi
|
|
done
|
|
|
|
# ── Pass 2: Wire dependencies ─────────────────────────────────────────────────
|
|
|
|
echo ""
|
|
echo "==> Pass 2: Wiring dependencies"
|
|
|
|
while IFS=$'\t' read -r old_id new_id deps_line; do
|
|
dep_ids="$(extract_dep_ids "$deps_line")"
|
|
[[ -z "$dep_ids" ]] && continue
|
|
|
|
while IFS= read -r dep_old_id; do
|
|
[[ -z "$dep_old_id" ]] && continue
|
|
|
|
# Look up the new beans ID for this dependency
|
|
dep_new_id="$(awk -F'\t' -v id="$dep_old_id" '$1 == id { print $2 }' "$MAP_FILE")"
|
|
|
|
if [[ -z "$dep_new_id" ]]; then
|
|
warnings+=("WARN: dep $dep_old_id (for $old_id -> $new_id) not found in map — skipped")
|
|
echo " ! dep $dep_old_id not found in map (for $new_id)"
|
|
continue
|
|
fi
|
|
|
|
result="$(cd "$PROJECT_DIR" && beans update "$new_id" --blocked-by "$dep_new_id" --json 2>&1)" || true
|
|
if echo "$result" | jq -e '.bean.id // .id' > /dev/null 2>&1; then
|
|
(( deps_wired++ )) || true
|
|
echo " ✓ $new_id blocked-by $dep_new_id (was $old_id blocked-by $dep_old_id)"
|
|
else
|
|
warnings+=("WARN: failed to wire dep $dep_new_id -> $new_id: $result")
|
|
echo " ✗ failed to wire $dep_new_id -> $new_id"
|
|
fi
|
|
done <<< "$dep_ids"
|
|
done < "$MAP_FILE"
|
|
|
|
# ── Cleanup ───────────────────────────────────────────────────────────────────
|
|
|
|
echo ""
|
|
echo "==> Removing .nbd directory"
|
|
rm -rf "$PROJECT_DIR/.nbd"
|
|
echo " Removed $PROJECT_DIR/.nbd"
|
|
|
|
# ── Summary ───────────────────────────────────────────────────────────────────
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " Migration complete for: $PROJECT_DIR"
|
|
echo " Tickets created : $tickets_created"
|
|
echo " Tickets failed : $tickets_failed"
|
|
echo " Deps wired : $deps_wired"
|
|
if [[ ${#warnings[@]} -gt 0 ]]; then
|
|
echo " Warnings:"
|
|
for w in "${warnings[@]}"; do
|
|
echo " - $w"
|
|
done
|
|
fi
|
|
echo "=========================================="
|