Files
stack/docs/scratchpads/t-a292e96f-gitea-pr-metadata.md
Hermes Agent 5cd6c83b9b
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
fix git pr metadata tmpfile cleanup
2026-06-18 16:16:34 -05:00

7.9 KiB

t_a292e96f — Gitea PR metadata wrapper fix

Objective

Repair Mosaic git wrappers so Gitea PR metadata and merge preflight work for U-Connect PRs on git.uscllc.com without selecting the unrelated git.mosaicstack.dev tea login.

Findings

  • Reproduced the failure from /src/uconnect-worktrees/t_39ce717c-authentik-smoke-gate with the current pr-metadata.sh:
    • PR #1905 returned JSON with number=null, baseRefName="", headRefName="".
    • PR #1908 returned JSON with number=null, baseRefName="", headRefName="".
  • Root cause: the wrapper treated HTTP/API error payloads as PR payloads and normalized missing fields to empty strings.
  • The credential loader can return a non-working git.uscllc.com API token in this environment, while host-specific ~/.git-credentials basic auth succeeds. The wrapper now falls back by host before normalization.
  • tea login list has only git.mosaicstack.dev configured here; pr-merge.sh previously forced --login mosaicstack, which is invalid for git.uscllc.com and caused Login name mosaicstack does not exist.

Changes

  • packages/mosaic/framework/tools/git/detect-platform.sh
    • Added get_gitea_basic_auth <host> to retrieve host-specific HTTPS credentials from ~/.git-credentials without printing secrets.
  • packages/mosaic/framework/tools/git/pr-metadata.sh
    • Uses strict bash mode.
    • Checks Gitea HTTP status and fails nonzero on API errors/non-JSON instead of emitting empty branch fields.
    • Falls back from token auth to host-specific basic auth.
    • Normalizes standard head.ref/base.ref and fallback branch fields.
    • Requires non-empty headRefName and baseRefName.
    • Preserves GitHub gh pr view behavior.
  • packages/mosaic/framework/tools/git/pr-merge.sh
    • Reads metadata once for base-branch policy preflight.
    • Selects a tea login only when its configured URL matches the repo host.
    • Falls back to authenticated Gitea merge API when no matching tea login exists, avoiding the wrong mosaicstack login for USC repos.
    • Keeps squash-only and main-only merge policy.
  • packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh
    • Added fixture-based regression harness for standard Gitea fields, fallback branch fields, refs/pull/<n>/head plus head.label normalization, and API error payloads.

Documentation / changelog note

This repository currently has no root CHANGELOG.md; the scratchpad and docs/TASKS.md carry the task-level change record for this wrapper fix.

Verification log

  • Red regression check: copied the new test-pr-metadata-gitea.sh harness next to origin/main wrapper scripts and ran it with MOSAIC_TEST_WORK_DIR=$PWD/.mosaic-test-work/pr-metadata-gitea-red; it failed as expected with headRefName='' and baseRefName='' on the fixture API-error path.
  • bash -n packages/mosaic/framework/tools/git/{detect-platform.sh,pr-metadata.sh,pr-merge.sh,test-pr-metadata-gitea.sh}: passed.
  • shellcheck -x -P . -e SC1090 packages/mosaic/framework/tools/git/{detect-platform.sh,pr-metadata.sh,pr-merge.sh,test-pr-metadata-gitea.sh}: passed.
  • MOSAIC_TEST_WORK_DIR=$PWD/.mosaic-test-work/pr-metadata-gitea packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh: passed; verifies standard Gitea fields, fallback branch fields, refs/pull/<n>/head label normalization, and nonzero API-error handling.
  • Installed wrapper parity: /home/hermes/.config/mosaic/tools/git/{detect-platform.sh,pr-metadata.sh,pr-merge.sh} byte-match the PR source copies after validation, so active U-Connect wrapper invocations use the same fix while source PR review runs.
  • Live sanitized U-Connect metadata from /src/uconnect with MOSAIC_CREDENTIALS_FILE=/src/jarvis-brain/credentials.json:
    • PR #1905: number=1905, baseRefName=main, headRefName=edith/t_39ce717c-authentik-smoke-gate, state=open, host=git.uscllc.com.
    • PR #1908: number=1908, baseRefName=main, headRefName=fix/t_23fa9e1d-portal-health-backend, state=closed, host=git.uscllc.com.
  • Merge preflight dry runs from installed wrappers:
    • PR #1905: Dry run: would merge PR #1905 on git.uscllc.com with authenticated Gitea API fallback (base=main, method=squash).
    • PR #1908: Dry run: would merge PR #1908 on git.uscllc.com with authenticated Gitea API fallback (base=main, method=squash).
  • PR: https://git.mosaicstack.dev/mosaicstack/stack/pulls/518, branch fix/t-a292e96f-gitea-pr-metadata.
  • CI: Recent PR/push pipelines failed before clone/test execution due Woodpecker/Kubernetes PVC API timeout: dial tcp 10.43.0.1:443: i/o timeout. No repository test step executed in CI; local targeted verification above remains clean.

2026-06-18 — PR #549 functional blocker remediation

Assignment

Coordinator mos-claude assigned remediation for PR #549: fix packages/mosaic/framework/tools/git/pr-metadata.sh tmpfile cleanup where an EXIT trap references function-local body_file after the function returns inside RAW=$(...), producing body_file: unbound variable on the authenticated success path and failing to clean up safely on early set -e exits.

Plan

  1. Add a non-vacuous Gitea test that exercises curl_gitea_pull with stubbed curl and GITEA_TOKEN instead of MOSAIC_GITEA_PR_METADATA_RAW_FILE.
  2. Prove the new test is RED against the current PR head.
  3. Replace the function-local EXIT cleanup with robust function-scoped tmpfile cleanup.
  4. Re-run targeted tests, bash -n, and review gates; commit and push branch only. Do not merge.

Constraints / assumptions

  • Do not modify prior injection/JSON fixes in issue-edit, issue-assign, or milestone-create.
  • Worker role: do not modify docs/TASKS.md; orchestrator remains the single writer.
  • Budget: no explicit token cap provided; keep scope to shell wrapper + targeted regression harness.

Remediation results

  • Rebased fix/tooling-eval-injection-jq-json onto origin/main; branch was already current.
  • Added a curl-stub regression path that does not use MOSAIC_GITEA_PR_METADATA_RAW_FILE, so it exercises curl_gitea_pull and its temp body file.
  • RED evidence: copied the new harness next to the pre-fix HEAD version of pr-metadata.sh; MOSAIC_TEST_WORK_DIR=$PWD/.mosaic-test-work/pr-metadata-red-work .../test-pr-metadata-gitea.sh failed with body_file: unbound variable on the curl success path.
  • Fix: replaced EXIT temp-file cleanup with a RETURN-scoped cleanup function that removes the body file while the function-local variable is still in scope, preserves the original return status, and clears the RETURN trap.
  • GREEN evidence:
    • MOSAIC_TEST_WORK_DIR=$PWD/.mosaic-test-work/pr-metadata-gitea-current packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh passed.
    • bash -n packages/mosaic/framework/tools/git/pr-metadata.sh packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh passed.
    • shellcheck -x -P . -e SC1090 packages/mosaic/framework/tools/git/pr-metadata.sh packages/mosaic/framework/tools/git/test-pr-metadata-gitea.sh passed.

Review remediation

  • Codex review returned one should-fix: the early-exit test used chmod 000, which is not root-safe in container CI.
  • Remediation: changed the stubbed 2xx/cat-failure mode to replace the curl output with a broken symlink, which fails deterministically even as root and still validates cleanup via rm -f -- "$body_file".

Second review remediation

  • Codex review found the 2xx cat "$body_file" read could be masked under command substitution semantics because the branch returned 0 unconditionally.
  • Remediation: both authenticated 2xx branches now use cat "$body_file" || return $? before returning success.
  • Strengthened the broken-symlink test to require the body-read failure and reject the later Gitea API returned non-JSON parse-failure path, so the test verifies the helper-level failure propagation rather than eventual downstream failure.

Final review gate

  • Codex review after remediation: approved (0 blockers, 0 should-fix, 0 suggestions).