Skip to content

Add read-only Azure reconciliation report - #739

Draft
dcollie2 wants to merge 2 commits into
mainfrom
feat/azure-reconciliation
Draft

dcollie2 wants to merge 2 commits into
mainfrom
feat/azure-reconciliation

Conversation

@dcollie2

@dcollie2 dcollie2 commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator

What Issue Does This PR Cover, If Any?

Part of #736 (does not close it). Finds the "find any others" items: topics with no document, 0 byte documents, duplicate topics, and files missing from or left behind in Azure.

What Changed? And Why Did It Change?

Adds bin/rails reconcile:azure. It compares topics, their documents, ActiveStorage and the Azure File Share, and writes one CSV row per discrepancy plus a count by language and category. It changes nothing.

Categories: topic_without_documents, empty_document, missing_in_storage, missing_in_azure, size_mismatch, wrong_folder, failed_delete, orphan_stale_copy, orphan_unmatched_name, orphan_no_topic, duplicate_topic, missing_directory, broken_topic.

  • The azure_file_shares gem's list drops the continuation marker and returns at most 5,000 entries per folder. AzureReconciliation::DirectoryLister follows NextMarker itself and reuses the gem's request signing.
  • Expected locations follow FileManager: Pi and v2 Content, v2 VideoContent for mp4, and the _Archive folders for archived topics.
  • A shadow-copy topic's file counts as a failed delete only when no current document uses the same name, since a re-uploaded file keeps its name.
  • A file whose name starts with an existing topic's prefix but matches none of its documents is reported as orphan_unmatched_name. That covers topics whose provider, date or file was changed after upload, because the Azure name doesn't change.

Options: LANGUAGE=<id|name>, SKIP_BLOB_CHECK=1 (skips one S3 request per file), DEST=<path> or DEST=- for stdout.

How Has This Been Tested?

  • 24 specs, written first, with a fake lister and Faraday test stubs. Rubocop passes.
  • Ran against a local development database with an empty fake Azure listing: 2,328 topics in under 2 seconds.
  • Not yet run against Azure. The paged, signed listing needs real credentials, so it still has to be checked on staging. If Azure rejects the signature, the error now includes Azure's AuthenticationErrorDetail.

Please Provide Screenshots

None. No UI changes.

Additional Comments

Independent of #735. To run on staging after deploying this branch:

bin/kamal app exec -d staging --reuse "bin/rails reconcile:azure DEST=-" > staging-reconcile.txt

The CSV starts at the line beginning category,language,.

🤖 Generated with Claude Code

dcollie2 and others added 2 commits November 7, 2026 13:00
Adds `bin/rails reconcile:azure`, which compares topics, their documents,
ActiveStorage and the Azure File Share and writes one CSV row per
discrepancy: topics with no documents, 0 byte documents, files missing
from storage or Azure, size mismatches, files in the wrong folder, failed
deletes, orphaned files and duplicate topics. It changes nothing.

The azure_file_shares gem's `list` drops the continuation marker and
returns at most 5,000 entries, so DirectoryLister pages through each
folder itself, reusing the gem's request signing.

Refs: #736

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Azure returns the error body as binary with a byte order mark, which
made the detail parsing raise an encoding error and hide the string
Azure signed.

Refs: #736

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

This branch has not been deployed

No deployments
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.

1 participant