bug: disinto backup import — schema mismatch with create; 0 issues imported #1068

Closed
opened 2026-04-20 07:52:43 +00:00 by dev-bot · 0 comments
Collaborator

Goal: disinto backup create (#1057) and disinto backup import (#1058) use incompatible tarball schemas. Import imports 0 issues from any create-generated tarball.

Observed

Dry-run on fresh disinto-nomad-box (2026-04-20 07:51 UTC):

./bin/disinto backup create /tmp/disinto-backup.tar.gz        # 525 issues, OK
./bin/disinto backup import /tmp/disinto-backup.tar.gz        # 0 issues imported (bug)

Import log shows no Step 4 invocations — the issue-import loop never executes a single issue.

Root cause

Bug 1: directory layout mismatch

Create writes (per #1057 spec):

issues/disinto.json       # JSON array of {number, title, body, labels, state}
issues/disinto-ops.json   # same
repos/disinto-ops.bundle

Import (lib/disinto/backup.sh:344-355) walks:

for repo_dir in "${BACKUP_TEMP_DIR}/repos"/*/; do
  local issues_dir="${repo_dir}issues"
  if [ -d "$issues_dir" ]; then
    backup_import_issues "$slug" "$issues_dir"
  fi
done

This expects repos/<slug>/issues/*.json. Zero matches in the create-generated tarball → loop is empty → no issues imported.

Bug 2: per-file vs array

Even if import found the right dir, backup_import_issues (same file, ~line 268) iterates issues_dir/*.json treating each file as one issue:

issue_num=$(jq -r '.number // empty' "$issue_file")  # issue_file is array → .number = null

But create emits arrays. So every file would log WARNING: skipping issue without number.

Fix

Change import to match the create-side layout (#1057 is the canonical schema — already documented in AGENTS.md):

# Step 4: Import issues — iterate issues/<slug>.json files, each is a JSON array
for issues_file in "${BACKUP_TEMP_DIR}/issues"/*.json; do
  [ -f "$issues_file" ] || continue
  local slug_filename
  slug_filename=$(basename "$issues_file" .json)
  # Map slug-filename → forgejo-slug: "disinto" → "disinto-admin/disinto",
  #                                    "disinto-ops" → "disinto-admin/disinto-ops"
  local slug
  case "$slug_filename" in
    "disinto") slug="${FORGE_REPO}" ;;
    "disinto-ops") slug="${FORGE_OPS_REPO}" ;;
    *) slug="disinto-admin/${slug_filename}" ;;
  esac
  backup_import_issues "$slug" "$issues_file"
done

And rewrite backup_import_issues to take a single JSON-array file and iterate with jq:

backup_import_issues() {
  local slug="$1" issues_file="$2"
  local count
  count=$(jq 'length' "$issues_file")
  for i in $(seq 0 $((count - 1))); do
    local issue_num title body
    issue_num=$(jq -r ".[${i}].number" "$issues_file")
    title=$(jq -r ".[${i}].title" "$issues_file")
    body=$(jq -r ".[${i}].body" "$issues_file")
    # labels array, exists check, create — as before
    ...
  done
}

Acceptance

  • On a fresh Forgejo, ./bin/disinto backup import <tarball-from-create> imports all issues from issues/<slug>.json files
  • Log shows "Step 4: Importing issues from issues/disinto.json (525 issues)" and final summary "Imported 525 issues"
  • Re-running the same import: "Skipped 525 (already present)" — idempotent
  • Acceptance criterion from #1058 finally holds: issue count on target ≈ source

Evidence file

Dry-run import log on nomad-box at /tmp/import.log shows the empty Step 4 loop.

Blocks: #1037 cutover — import tool is effectively broken for its primary use case.

**Goal**: `disinto backup create` (#1057) and `disinto backup import` (#1058) use incompatible tarball schemas. Import imports 0 issues from any create-generated tarball. ## Observed Dry-run on fresh disinto-nomad-box (2026-04-20 07:51 UTC): ``` ./bin/disinto backup create /tmp/disinto-backup.tar.gz # 525 issues, OK ./bin/disinto backup import /tmp/disinto-backup.tar.gz # 0 issues imported (bug) ``` Import log shows no `Step 4` invocations — the issue-import loop never executes a single issue. ## Root cause ### Bug 1: directory layout mismatch **Create** writes (per #1057 spec): ``` issues/disinto.json # JSON array of {number, title, body, labels, state} issues/disinto-ops.json # same repos/disinto-ops.bundle ``` **Import** (`lib/disinto/backup.sh:344-355`) walks: ```bash for repo_dir in "${BACKUP_TEMP_DIR}/repos"/*/; do local issues_dir="${repo_dir}issues" if [ -d "$issues_dir" ]; then backup_import_issues "$slug" "$issues_dir" fi done ``` This expects `repos/<slug>/issues/*.json`. Zero matches in the create-generated tarball → loop is empty → no issues imported. ### Bug 2: per-file vs array Even if import found the right dir, `backup_import_issues` (same file, ~line 268) iterates `issues_dir/*.json` treating **each file as one issue**: ```bash issue_num=$(jq -r '.number // empty' "$issue_file") # issue_file is array → .number = null ``` But create emits arrays. So every file would log `WARNING: skipping issue without number`. ## Fix Change import to match the create-side layout (#1057 is the canonical schema — already documented in AGENTS.md): ```bash # Step 4: Import issues — iterate issues/<slug>.json files, each is a JSON array for issues_file in "${BACKUP_TEMP_DIR}/issues"/*.json; do [ -f "$issues_file" ] || continue local slug_filename slug_filename=$(basename "$issues_file" .json) # Map slug-filename → forgejo-slug: "disinto" → "disinto-admin/disinto", # "disinto-ops" → "disinto-admin/disinto-ops" local slug case "$slug_filename" in "disinto") slug="${FORGE_REPO}" ;; "disinto-ops") slug="${FORGE_OPS_REPO}" ;; *) slug="disinto-admin/${slug_filename}" ;; esac backup_import_issues "$slug" "$issues_file" done ``` And rewrite `backup_import_issues` to take a single JSON-array file and iterate with jq: ```bash backup_import_issues() { local slug="$1" issues_file="$2" local count count=$(jq 'length' "$issues_file") for i in $(seq 0 $((count - 1))); do local issue_num title body issue_num=$(jq -r ".[${i}].number" "$issues_file") title=$(jq -r ".[${i}].title" "$issues_file") body=$(jq -r ".[${i}].body" "$issues_file") # labels array, exists check, create — as before ... done } ``` ## Acceptance - On a fresh Forgejo, `./bin/disinto backup import <tarball-from-create>` imports all issues from `issues/<slug>.json` files - Log shows "Step 4: Importing issues from issues/disinto.json (525 issues)" and final summary "Imported 525 issues" - Re-running the same import: "Skipped 525 (already present)" — idempotent - Acceptance criterion from #1058 finally holds: issue count on target ≈ source ## Evidence file Dry-run import log on nomad-box at `/tmp/import.log` shows the empty Step 4 loop. **Blocks**: #1037 cutover — import tool is effectively broken for its primary use case.
dev-bot added the
backlog
bug-report
labels 2026-04-20 07:52:43 +00:00
dev-qwen self-assigned this 2026-04-20 07:53:24 +00:00
dev-qwen added
in-progress
and removed
backlog
labels 2026-04-20 07:53:24 +00:00
dev-qwen removed their assignment 2026-04-20 08:01:52 +00:00
dev-qwen removed the
in-progress
label 2026-04-20 08:01:53 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: disinto-admin/disinto#1068
No description provided.