Skip to content

Plugin Directory: Don't queue a deleted /trunk for import - #819

Closed
obenland wants to merge 1 commit into
WordPress:trunkfrom
obenland:svn-watcher-deleted-trunk
Closed

Plugin Directory: Don't queue a deleted /trunk for import#819
obenland wants to merge 1 commit into
WordPress:trunkfrom
obenland:svn-watcher-deleted-trunk

Conversation

@obenland

Copy link
Copy Markdown
Member

Problem

SVN_Watcher::summarize_plugin_changes() records a deleted /trunk as a touched version:

if ( 'trunk' == $path_parts[1] ) {
    $plugin['tags_touched'][] = 'trunk';          // ← D /slug/trunk lands here too
} elseif ( 'tags' == $path_parts[1] && isset( $path_parts[2] ) ) {
    if ( $is_deletion && ! isset( $path_parts[3] ) ) {
        $plugin['tags_deleted'][] = $path_parts[2];   // ← the tags branch does check

The tags branch distinguishes a deletion; the trunk branch doesn't. So Builder::export_plugin() is asked to export a URL that has just been removed.

Observed impact

Some release workflows delete trunk in the same commit that adds the tag, then re-create it from that tag moments later. If svn-watch polls in the gap, the import fails:

E_USER_WARNING: ZIP build failed for wp-better-permalinks trunk:
  Builder::export_plugin: URL 'https://plugins.svn.wordpress.org/wp-better-permalinks/trunk' doesn't exist
r3653587  22:08:26  A /wp-better-permalinks/trunk (from tags/4.3.1:3653586)
          22:08:18  ← import runs, trunk missing
r3653586  22:08:01  A /wp-better-permalinks/tags/4.3.1/…  +  D /wp-better-permalinks/trunk

Same thing thirteen minutes later on webp-converter-for-media (31-second window, r3653600 → r3653604). That committer has used this release pattern for months — 17 D /trunk commits in webp-converter-for-media's last 40 revisions, back to 2025-12-08 — so the warning fires whenever the timing lines up.

It resolves itself: the commit that re-creates trunk queues another import, which builds the ZIP correctly. The cost is a failed import cycle and a warning that looks like a broken plugin rather than an in-progress release.

Fix

Treat the removal of the bare /slug/trunk path as a deletion, and skip it. A file deletion inside trunk (/slug/trunk/old.php) is still a change to trunk and still queues it.

Trunk is deliberately not added to tags_deleted: the importer runs remove_release() over that list, and trunk has no release of its own to remove.

An import is still queued for the plugin — the commit that removed trunk also added the tag, and the stable-tag fallback may have changed — it just no longer names a version that can't be exported. A commit that deletes nothing but trunk yields an entry with an empty tags_touched, which the importer already handles.

Tests

Adds 3 cases to tests/SVN_Watcher_Log_Summary_Test.php, built from the r3653586 log entry:

  • test_trunk_deletion_is_not_queued_as_a_touched_version — the regression.
  • test_file_deletion_within_trunk_still_touches_trunk — guards against over-correcting.
  • test_recreated_trunk_is_queued_again — including when both commits land in one batch of revisions.

Verified against the pre-fix code:

# before
.....F...  9 / 9
1) test_trunk_deletion_is_not_queued_as_a_touched_version
   Failed asserting that two arrays are identical.
    Array &0 (
        0 => '4.3.1'
   +    1 => 'trunk'
    )
Tests: 9, Assertions: 27, Failures: 1.

# after
.........  9 / 9
OK (9 tests, 29 assertions)

The two guard tests pass either way, by design.

Note

Follow-up to #804, which touched the same function but not this branch of it. No Meta Trac ticket — happy to open one and reference it if that's preferred.

Copilot AI lite review requested due to automatic review settings August 18, 2026 22:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props obenland.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

A commit that deletes /trunk was recorded as having touched it, so the
importer queued a ZIP build against a URL that no longer exists.

Release workflows that delete trunk in the tag commit and re-create it
from that tag moments later hit this whenever the watcher polls in
between, failing the build with "URL '.../trunk' doesn't exist". The
re-creation is a change of its own and queues trunk again once it's
there.

Mirrors the existing handling for deleted tags, except that trunk isn't
recorded in tags_deleted: that removes the matching release, and trunk
has no release of its own to remove.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@obenland
obenland force-pushed the svn-watcher-deleted-trunk branch from 0b8caa9 to 3dbb9b3 Compare August 18, 2026 22:47
@obenland obenland closed this Aug 18, 2026
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