mozilla / mozilla/markfluence

Add an attachment-prune subcommand for orphaned attachments

Open
#99 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

enhancement
Dominant language
Go
Stars
2
Forks
0
Avg merge
2h 8m
Merged PRs (30d)
49

Description

markfluence never deletes, so an attachment it uploaded stays on the page after the page stops referencing it. There is no way to clean those up except by hand in the Confluence UI. Add attachment-prune PAGE to remove them.

What makes an attachment prunable

Two conditions, both required:

  • Unreferenced. The page body does not name it. cmd/export/export.go already computes this as referencedNames(storage), matching ri:filename anywhere in the raw storage rather than only inside ac:image — an attachment link target or a reference inside a passthrough macro counts as referenced.
  • Managed by markfluence. client.AttachmentMeta.Managed is true only for attachments carrying the markfluence: comment prefix. This half is not optional: an attachment someone added in the Confluence UI is unreferenced by definition if it is not embedded, so pruning on "unreferenced" alone would delete a PDF a human attached for reference. That is a S5 violation.

Report the checksum, do not branch on it

Every managed attachment records a SHA-256 in its comment, which makes it possible to say whether deleting an orphan loses content: if its checksum matches an attachment the page still references, the bytes survive under another name. If it matches nothing, this is the only copy on the page.

That distinction should be reported, not encoded as different behaviour. Branching on it guesses; showing it lets the operator decide.

$ markfluence attachment-prune docs/guides/index.md --dry-run
  orphan  assets%2Fbrand.png     content also attached as ..%2Fassets%2Fbrand.png
  orphan  old-diagram.png        only copy on this page
  keep    handbook.pdf           not managed by markfluence

Worth noting that the second case is usually still safe: a managed attachment came from a local file, so republishing reproduces it. The exception is when that file has since been deleted from the repo — which is exactly when the checksum will not match anything.

Guarantees

This command exists in the shape it does because of docs/guarantees.md:

  • S4 — removal is a command's stated purpose or it does not happen. This is why pruning must not be a step inside update. Publishing that quietly deleted things would be removal as a side effect.
  • S5 — markfluence removes only what markfluence created. The Managed gate.
  • S6 — a command that removes says what it will remove first and honours --dry-run.

S4-S6 are currently Vacuous: nothing in markfluence removes anything. This would be the first command to exercise them, so their status changes when it lands.

Shape

attachment-prune PAGE, matching its three siblings — singular noun, noun-first, so attachment-<TAB> still completes as a group. PAGE goes through internal/pageref like the others, so it accepts a page id, a Confluence URL, or a .md file with a page_id — pointing at the markdown file whose images just changed is the obvious way to invoke this.

Single page to start, like attachment-list. Batch pruning across a space has a much larger blast radius and can follow if it is wanted.

Prerequisites

  • A delete method in the client. There is none: http.MethodDelete appears only in send's idempotent-methods list.
  • referencedNames has to move somewhere shared. It is local to cmd/export today, and export and prune must not each have their own scanner — same rule as the traversal clamp in internal/attachfile.
  • A schema entry: the command enum plus an if/then branch constraining results.items and summary, or internal/schematest fails.

Relationship to the file-organization work

The root-relative naming model in _plans/025_file-organization.md removes the most common source of orphans: today, moving a markdown file renames every attachment it references, because the name encodes the path relative to the referencing file. Under that model moving a page changes nothing, so nothing is stranded.

This command handles what is left over: moving or renaming an asset (whose identity does follow its location, by design), removing an image from a page, and orphans that already exist from before the model lands. It is a cleanup tool for the residual rather than a fix for the naming problem.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with cmd/export/export.go for referencedNames, then inspect internal/pageref, internal/attachfile, the client attachment metadata, and internal/schematest. Read docs/guarantees.md and the attachment-list siblings before defining the command. Done means a single-page attachment-prune command supports dry-run reporting, managed-only deletion, page references, and the required schema entry.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
api, cli
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.