create_issues.sh

#!/usr/bin/env bash
# Create the audit work-order backlog as GitHub Issues on MIZOKI-3-5/MIZOKICloudRun.
# Idempotent: skips any WO whose title already exists. Creates in dependency order and
# rewrites "Depends on: WO-nn" to real issue numbers. Requires: gh (authenticated) OR
# GH_TOKEN with issues:write; python3 + jq.
#
#   ./create_issues.sh            # create everything
#   ./create_issues.sh --dry-run  # print what would be created
set -euo pipefail
REPO="${REPO:-MIZOKI-3-5/MIZOKICloudRun}"
HERE="$(cd "$(dirname "$0")" && pwd)"
DRY="${1:-}"
MAP="$HERE/issue_map.json"; [ -f "$MAP" ] || echo '{}' > "$MAP"

api() { # method path [json-body]
  if command -v gh >/dev/null; then
    if [ -n "${3:-}" ]; then gh api -X "$1" "$2" --input - <<<"$3"; else gh api -X "$1" "$2"; fi
  else
    : "${GH_TOKEN:?set GH_TOKEN or install gh}"
    curl -sS -f -X "$1" -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" \
      "https://api.github.com/$2" ${3:+-d "$3"}
  fi
}

# 1. labels (bash 3.2 compatible — macOS /bin/bash has no associative arrays)
LABELS="P0=B60205 P1=D93F0B P2=FBCA04 P3=0E8A16 pkg:A=5319E7 pkg:B=5319E7 pkg:C=5319E7 pkg:D=5319E7 pkg:E=5319E7 pkg:F=5319E7 pkg:G=5319E7 pkg:H=5319E7 lane:ENG=0052CC lane:SEC=0052CC lane:MEAS=0052CC lane:CUST=0052CC lane:OPS=0052CC lane:OWNER=0052CC audit-2026-09-06=C2E0C6"
existing_labels="$(api GET "repos/$REPO/labels?per_page=100" | jq -r '.[].name')"
for kv in $LABELS; do
  l="${kv%%=*}"; c="${kv#*=}"
  grep -qxF "$l" <<<"$existing_labels" && continue
  echo "label: $l"; [ "$DRY" = "--dry-run" ] || api POST "repos/$REPO/labels" "$(jq -nc --arg n "$l" --arg c "$c" '{name:$n,color:$c}')" >/dev/null
done

# 2. existing issues (idempotency)
existing="$(api GET "repos/$REPO/issues?state=all&labels=audit-2026-09-06&per_page=100" | jq -c '[.[] | {n:.number,t:.title}]')"

# 3. create in dependency order (topological over manifest)
python3 - "$HERE/manifest.json" <<'EOF' > "$HERE/order.txt"
import json,sys
ts={t['id']:t for t in json.load(open(sys.argv[1]))}; done=[]; seen=set()
def visit(i):
    if i in seen: return
    seen.add(i); [visit(d) for d in ts[i]['deps'] if d in ts]; done.append(i)
for i in sorted(ts): visit(i)
print("\n".join(done))
EOF

while read -r wid; do
  t="$(jq -c --arg id "$wid" '.[] | select(.id==$id)' "$HERE/manifest.json")"
  title="$wid · $(jq -r .title <<<"$t")"
  num="$(jq -r --arg t "$title" '.[] | select(.t==$t) | .n' <<<"$existing" | head -1)"
  if [ -n "$num" ]; then echo "exists: #$num $title"; jq --arg k "$wid" --argjson v "$num" '.[$k]=$v' "$MAP" > "$MAP.tmp" && mv "$MAP.tmp" "$MAP"; continue; fi
  body="$(cat "$HERE/$wid.md")"
  # rewrite dependency refs to issue numbers already created
  for d in $(jq -r '.deps[]' <<<"$t"); do
    dn="$(jq -r --arg k "$d" '.[$k] // empty' "$MAP")"; [ -n "$dn" ] && body="${body//$d/#$dn ($d)}"
  done
  labels="$(jq -c '[.sev, ("pkg:"+.pkg), ("lane:"+.lane), "audit-2026-09-06"]' <<<"$t")"
  echo "create: $title  $labels"
  [ "$DRY" = "--dry-run" ] && continue
  num="$(api POST "repos/$REPO/issues" "$(jq -nc --arg t "$title" --arg b "$body" --argjson l "$labels" '{title:$t,body:$b,labels:$l}')" | jq -r .number)"
  jq --arg k "$wid" --argjson v "$num" '.[$k]=$v' "$MAP" > "$MAP.tmp" && mv "$MAP.tmp" "$MAP"
  echo "  -> #$num"
done < "$HERE/order.txt"

echo; echo "WO → issue map written to $MAP"; jq . "$MAP"
← All docsView source on GitHub →