Skip to content

Accept a file path in pull-files --only - #564

Open
gavande1 wants to merge 23 commits into
WordPress:trunkfrom
gavande1:fix/539-only-file-paths
Open

Accept a file path in pull-files --only#564
gavande1 wants to merge 23 commits into
WordPress:trunkfrom
gavande1:fix/539-only-file-paths

Conversation

@gavande1

@gavande1 gavande1 commented Aug 12, 2026

Copy link
Copy Markdown

Related issues

Proposed Changes

pull-files --only PATH used to accept only directory paths. Passing a single file crashed the export server with list_dir does not exist or is not accessible, and the whole pull failed.

This PR changes that so the exporter now accepts a file, directory, or symlink for the directory param sent to the file_index endpoint. The endpoint tracks the requested path and resolves symlinks (if --follow-symlinks is enabled).

Aside from making pull-files --only PATH_TO_FILE work, this PR also makes pull-files --only PATH_TO_SYMLINKED_DIR work better in the sense that Reprint will now return an index containing symlink_dir -> target (this was previously implemented separately in #572, but I folded it into this PR).

Testing Instructions

TBD

@gavande1
gavande1 force-pushed the fix/539-only-file-paths branch from 41d7a0b to 107af9f Compare August 12, 2026 15:33
@gavande1
gavande1 marked this pull request as ready for review August 12, 2026 15:36
@gavande1

gavande1 commented Aug 12, 2026

Copy link
Copy Markdown
Author

cc @fredrikekelund @adamziel I am looking for early review.

@gavande1
gavande1 force-pushed the fix/539-only-file-paths branch from ae9860a to 5f33fbb Compare August 13, 2026 06:56
Named paths were indexed without the storage-path and default-skip checks
that traversal applies, so a file inside the Reprint storage path was
indexed when selected explicitly. They were also all inspected in start()
and returned by one step, which could exceed the batch size and left them
out of the cursor.

Each named path is now one step, carried in the cursor, and subject to the
same omissions as a path reached by traversal.
@adamziel

Copy link
Copy Markdown
Collaborator

Could we test this repro: pull --only /site/foo.php, delete the remote file, then run the same pull again? I think resolve_directories() fails before missing-file handling runs, leaving the local copy behind.

@gavande1

Copy link
Copy Markdown
Author

Hey @adamziel, I am able to reproduce the issue on this branch. The resolve_directories() throws before endpoint_file_index() builds an index, so the diff never runs and the local copy stays behind.

The same thing happens on trunk with a directory root, so it looks pre-existing rather than new here.

What we can do here is skip vanished entries in resolve_directories() instead of throwing. That alone is not enough though: with a single --only FILE root that is gone, the skip empties $directories and "No valid directories specified" throws somewhere in export.php. So we would also need an empty root set to mean "index nothing", which changes behavior for every caller. Do you prefer it as a follow-up, or in this PR?

Also, without --abort the re-run short-circuits on already_complete and never contacts the server, so the stale copy survives with no error at all.

Comment thread packages/reprint-server/src/class-file-index-processor.php
Comment thread packages/reprint-server/src/utils.php Outdated
Comment thread packages/reprint-server/src/class-file-index-processor.php Outdated
Comment thread packages/reprint-server/src/class-file-index-processor.php Outdated
Comment thread packages/reprint-server/src/class-file-index-processor.php Outdated
Comment thread packages/reprint-server/src/class-file-index-processor.php Outdated
Comment thread packages/reprint-server/src/class-file-index-processor.php Outdated
Comment thread packages/reprint-server/src/class-file-index-processor.php Outdated
Comment thread packages/reprint-server/src/class-file-index-processor.php

$entries = [];
if ($this->follow_symlinks) {
// dirname() so a symlinked file is not repeated as an intermediate entry.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// dirname() so a symlinked file is not repeated as an intermediate entry.
// Record any symlinked descendants. The `index_entries_for_path` call
// below ignores them and only considers one specific path.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Actually that's not true, it does not ignore symlinks 👀 :

            $resolved_symlink = self::resolve_symlink_target($path);
            $link_target = $resolved_symlink["target"];
            if ($follow_symlinks) {
                $intermediate_symlinks = $resolved_symlink["intermediates"];
            }

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@gavande1 Suppose /current is a symlink to /releases/42, and the user requests --only /current/wp-config.php. This code receives /releases/42/wp-config.php because the parent path was already resolved. It can no longer find the /current symlink, so pull creates <fs-root>/releases/42/wp-config.php but not <fs-root>/current. Let's preserve the requested path when finding parent symlinks and add a test for this case.

@adamziel adamziel left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do you prefer it as a follow-up, or in this PR?

A follow-up sounds fine! That being said, I found one blocker

@fredrikekelund

Copy link
Copy Markdown
Collaborator

Rahul is AFK this week, so I'm taking over this PR. Thanks for the initial review, @adamziel 👍

@fredrikekelund fredrikekelund self-assigned this Aug 17, 2026
@fredrikekelund fredrikekelund changed the title Accept a file path in files-pull --only Accept a file path in pull-files --only Aug 17, 2026
@fredrikekelund

Copy link
Copy Markdown
Collaborator

@adamziel, I've refactored this pretty drastically. The big thing is that FileIndexProcessor now reasons about arrays containing requested_path, resolved_path, and type for every root entry. This is much more expressive and makes it easier to ensure the requested path, its resolved parent directories, and the resolved target are added to the index.

I opened #572 on Thursday to fix a case where pull-files --only wp-content/plugins/akismet would transfer akismet as a directory instead of a symlink and a resolved target. I moved that functionality into this PR today. It's closely related to the first purpose of this PR.

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.

pull-files --only should support file paths, not just directories

3 participants