Skip to content

Fix manage API gaps found migrating LexBox - #32

Merged
hahn-kev merged 2 commits into
masterfrom
claude/hgresume-manage-api-migration-b69787
Oct 28, 2026
Merged

hahn-kev merged 2 commits into
masterfrom
claude/hgresume-manage-api-migration-b69787

Conversation

@hahn-kev-bot

@hahn-kev-bot hahn-kev-bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

AI summary

Two bugs in /api/manage, both found while moving LexBox's HgService onto it (sillsdev/languageforge-lexbox#2821).

Uploaded .hg/hgrc survived FinishReset. Mercurial trusts a repo-level hgrc (hooks, extensions), so a user-uploaded one shouldn't land on the server. LexBox already stripped it; the port in #24 didn't. FinishReset now deletes it.

GET /repos/{code}/backup failed under Kestrel with a 409. It zipped with ZipFile.CreateFromDirectory straight into the response body. Kestrel rejects synchronous writes, and ApiExceptionHandler turned the resulting InvalidOperationException into a "Conflict". The unit tests wrote into a MemoryStream, so they never saw it.

  • Backup now uses ZipFile.CreateFromDirectoryAsync. .NET still flushes each entry's DeflateStream synchronously on dispose, so the endpoint also sets AllowSynchronousIO for that one response. The bulk of the zip is still written asynchronously.
  • InvalidOperationException no longer maps to 409. CopyRepo's "destination not empty" throws AlreadyExistsException, which still does, so other bugs surface as 500s instead of a misleading 409.

Image: LexBox needs an image with this before #2821 merges. Once this merges to master the workflow publishes v<merge date>; until then there's pr-32.

Test plan

  • FinishResetDropsUploadedHgrc theory (.hg at zip root and nested)
  • ManageEndpointTests runs backup through WebApplicationFactory, whose TestServer rejects sync IO like Kestrel. It failed with the CI error before the fix and passes after.
  • HgResume.Api.Tests 19/19 locally
  • ManageFacts.Backup_RestoresTheSameRepo (backup → finish-reset round trip against the container) in CI; Docker wasn't available locally

🤖 Generated with Claude Code

Mercurial trusts a repo's hgrc (hooks, extensions) on the server, and an
uploaded reset zip can carry one. LexBox's HgService already strips it;
the manage API port didn't, so LexBox moving onto /api/manage would lose
that protection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GET /api/manage/repos/{code}/backup zipped synchronously into the response
body, which Kestrel rejects, and the exception handler turned the resulting
InvalidOperationException into a 409. Write the zip with
CreateFromDirectoryAsync and allow synchronous IO on that one response,
since .NET still flushes each entry's DeflateStream synchronously on dispose.

Stop mapping every InvalidOperationException to 409: CopyRepo's "destination
not empty" now throws AlreadyExistsException, so other bugs surface as 500s.

Tests: backup through WebApplicationFactory (TestServer rejects sync IO like
Kestrel) and a backup/restore round trip against the container.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hahn-kev-bot hahn-kev-bot changed the title Drop uploaded .hg/hgrc in FinishReset Fix manage API gaps found migrating LexBox: hgrc strip, backup under Kestrel Oct 27, 2026
hahn-kev added a commit to sillsdev/languageforge-lexbox that referenced this pull request Oct 28, 2026
sillsdev/hgresume#32 fixes the manage API backup endpoint (sync zip write
failing under Kestrel) and strips uploaded hgrc files. Swap to the dated
tag once it merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@hahn-kev hahn-kev changed the title Fix manage API gaps found migrating LexBox: hgrc strip, backup under Kestrel Fix manage API gaps found migrating LexBox Oct 28, 2026
@hahn-kev
hahn-kev merged commit 780a781 into master Oct 28, 2026
5 checks passed
@hahn-kev
hahn-kev deleted the claude/hgresume-manage-api-migration-b69787 branch October 28, 2026 03:36
hahn-kev added a commit to sillsdev/languageforge-lexbox that referenced this pull request Oct 28, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hahn-kev added a commit to sillsdev/languageforge-lexbox that referenced this pull request Nov 5, 2026
* Move HgService repo filesystem ops onto hgresume's manage API

The lexbox-api pod no longer mounts the hg repos volume. Init, copy,
delete, backup, reset, finish-reset, soft-delete, cleanup, dir-cache
invalidation and the abandoned-transaction check now call hgresume's
/api/manage with the shared X-Manage-Secret. The repo-root chown init
container moves to the hg pod, ahead of populate-test-repos.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Temporarily run hgresume pr-32 image

sillsdev/hgresume#32 fixes the manage API backup endpoint (sync zip write
failing under Kestrel) and strips uploaded hgrc files. Swap to the dated
tag once it merges.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Pin hgresume v2026-10-28 (includes sillsdev/hgresume#32)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Address CodeRabbit review on the manage API migration

- BackupRepo disposes the headers-only response on any error status, not
  just 404, so a 401/500 from hgresume doesn't hold a connection open.
- Drop HgConfig.ResetCleanupAgeDays: nothing reads it since cleanup moved
  to hgresume, which owns HGRESUME_RESET_CLEANUP_AGE_DAYS.
- Bound the post-forward HasAbandonedTransactions check to 5s, since it
  runs while the send/receive ticket is still held.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Drop the abandoned-transaction check from the hg proxy

It now costs an HTTP call to hgresume on every proxied send/receive request
while the project's ticket is held, only to add a telemetry tag. The
hasAbandonedTransactions GraphQL field still reports it on demand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Kevin Hahn <kevin_hahn@sil.org>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants