Add .deployignore support to Site Sync - #2955
Conversation
Integrate the deploy-ignore filter (from PR #2924) into the Site Sync flow. Files matching .deployignore patterns are now hidden from the sync tree UI and excluded from the push archive. - Add additionalDefaults parameter to createDeployIgnoreFilter for sync-specific Studio-internal exclusions - Update shouldExcludeFromSync to use the ignore filter alongside the existing dotfile check - Thread the deploy-ignore filter through listLocalFileTree recursion - Pass the filter via ExportOptions to DefaultExporter for archive filtering during sync push - Downgrade ignore package from v7 to v5 to match the rest of the dependency tree and avoid TypeScript type conflicts
When specificSelectionPaths is undefined, the exporter reads wp-content from disk directly. Top-level directories like wordpress-seo need to be checked against the deploy-ignore filter before archiving, not just their children in the callback.
The server-side import uses meta.json to install plugins/themes from WordPress.org. Without filtering this list, excluded plugins would be reinstalled by the server even though their files were excluded from the archive.
…ore-to-site-sync
| studioJson.plugins = this.options.deployIgnore | ||
| ? plugins.filter( | ||
| ( p: StudioJsonPluginOrTheme ) => | ||
| ! this.options.deployIgnore!.ignores( `wp-content/plugins/${ p.name }` ) | ||
| ) | ||
| : plugins; | ||
| studioJson.themes = this.options.deployIgnore | ||
| ? themes.filter( | ||
| ( t: StudioJsonPluginOrTheme ) => | ||
| ! this.options.deployIgnore!.ignores( `wp-content/themes/${ t.name }` ) | ||
| ) | ||
| : themes; |
There was a problem hiding this comment.
These changes are required because otherwise the .deployignored plugins/themes would be reinstalled on the target site.
There was a problem hiding this comment.
Pull request overview
Extends existing .deployignore support to Studio’s Site Sync flow (sync tree UI + push/export), ensuring ignored files are consistently hidden/excluded and that meta.json doesn’t re-trigger server-side reinstalls of excluded plugins/themes.
Changes:
- Adds
additionalDefaultssupport to the shared deploy-ignore filter and updates sync defaults to use it. - Updates Sync tree listing/exclusion logic to rely on the deploy-ignore filter (while keeping dotfile hiding UI-only).
- Passes deploy-ignore filtering through the export pipeline and filters
meta.jsonplugin/theme lists accordingly; downgradesignoreto v5 to avoid type conflicts.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/common/package.json | Downgrades ignore dependency to ^5.3.2. |
| tools/common/lib/deploy-ignore.ts | Adds additionalDefaults parameter to createDeployIgnoreFilter. |
| package-lock.json | Updates lockfile entries to reflect ignore@5.3.2 usage. |
| apps/studio/src/modules/sync/lib/tree-utils.ts | Reworks sync exclusion logic to use a deploy-ignore filter + dotfile hiding. |
| apps/studio/src/modules/sync/lib/ipc-handlers.ts | Creates deploy-ignore filter during push export and passes it into ExportOptions. |
| apps/studio/src/modules/sync/constants.ts | Renames/adjusts sync exclusion defaults to Studio-internal items only. |
| apps/studio/src/lib/import-export/export/types.ts | Adds optional deployIgnore?: Ignore to ExportOptions. |
| apps/studio/src/lib/import-export/export/exporters/default-exporter.ts | Applies deploy-ignore filtering during wp-content archive creation and filters meta.json plugins/themes. |
| apps/studio/src/ipc-handlers.ts | Updates listLocalFileTree to build/reuse deploy-ignore filter when listing sync tree. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Noting that #3010 changed a lot of the sync logic on trunk. The CLI now also supports sync, and a lot of the internals moved to |
fredrikekelund
left a comment
There was a problem hiding this comment.
As I mentioned in #2955 (comment), a lot of the sync internals were moved around when we added sync support to the CLI. We should update this PR to reflect that.
Also, we currently have two copies of the exporter implementation: one for the CLI and another for the app. We should make sure this PR updates both places.
Lastly, I left some more detailed comments on the implementation.
…ore-to-site-sync # Conflicts: # apps/studio/src/ipc-handlers.ts # apps/studio/src/modules/sync/constants.ts # apps/studio/src/modules/sync/lib/ipc-handlers.ts # apps/studio/src/modules/sync/lib/tree-utils.ts
📊 Performance Test ResultsComparing e01525a vs trunk app-size
site-editor
site-startup
Results are median values from multiple test runs. Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff) |
Mirror the desktop app's sync push changes in the CLI: - Add optional deployIgnore to CLI ExportOptions - Use the filter in CLI DefaultExporter addWpContent and meta.json - Create the filter with SYNC_ADDITIONAL_DEFAULTS in the push command
Replace the additionalDefaults parameter with basePatterns, which completely overrides the built-in DEPLOY_IGNORE_DEFAULTS when provided. Callers that want Studio-internal sync exclusions on top of the base defaults now spread both arrays explicitly.
Spread DEPLOY_IGNORE_DEFAULTS into the sync base patterns so sync callers can pass a single constant instead of building the combined array at each call site. Rename SYNC_ADDITIONAL_DEFAULTS to SYNC_IGNORE_DEFAULTS to parallel DEPLOY_IGNORE_DEFAULTS and reflect that it is the complete base set for sync (no longer "additional" to a function-internal default).
Spreading DEPLOY_IGNORE_DEFAULTS into SYNC_IGNORE_DEFAULTS caused Vite to pull deploy-ignore.ts (and its `fs` import) into the renderer bundle via sync/constants.ts consumers. Inline the four patterns instead and add a unit test that asserts SYNC_IGNORE_DEFAULTS is a superset of DEPLOY_IGNORE_DEFAULTS to catch drift automatically.
|
I will continue with updates and testing tomorrow with fresh brain. |
The `ignore` library already converts backslashes to forward slashes internally on Windows (via its `makePosix` converter, active when `process.platform === 'win32'`). Our `.replace(/\\/g, '/')` calls at each `.ignores()` call site were no-ops on macOS/Linux and redundant duplication on Windows.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 15 changed files in this pull request and generated 4 comments.
Comments suppressed due to low confidence (1)
tools/common/lib/fs-utils.ts:39
path.relative()returns OS-specific separators (e.g.,\on Windows). PassingignorePathwith backslashes intoig.ignores()can cause.deployignorepatterns that use/(likewp-content/plugins/**) to stop matching on Windows, and it can also break the built-in defaults when scanning nested paths. NormalizefileRelativeToRoot/ignorePathto POSIX (replace\\with/) before callingignores()(and keeppathPrefixjoins consistent).
const fileRelativeToRoot = path.relative( directoryPath, filePath );
const ignorePath = pathPrefix
? `${ pathPrefix }/${ fileRelativeToRoot }`
: fileRelativeToRoot;
try {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ore-to-site-sync
|
Alright, I have addressed the feedback and tested the changes on both macOS and Windows (pushing through the UI and the Studio CLI. Everything is running well, the files are getting excluded as described in the From what I see this is the main remaining change to agree on. We can wait for the response from Antonio and/or Kat. Apart from that, the PR will be ready for another review/test round. |
…ore-to-site-sync # Conflicts: # apps/cli/commands/push.ts # apps/cli/lib/import-export/export/exporters/default-exporter.ts # apps/cli/lib/import-export/export/types.ts
Move the constant to deploy-ignore-defaults.ts so sync/constants.ts can spread it via ...DEPLOY_IGNORE_DEFAULTS without pulling in fs.
Remove the blanket startsWith('.') check from shouldExcludeFromSync.
Dotfiles were previously hidden from the sync file selector but still
pushed inside selected directories, making them easy to overlook. They
are now visible so users can see what gets deployed. Dotfiles matched
by deploy-ignore defaults (.git, .DS_Store) are still excluded.
…ore-to-site-sync # Conflicts: # apps/studio/src/ipc-handlers.ts
The server-side restore system automatically skips dotfiles, so showing them in the sync tree gives users a false expectation that they will be deployed. Restore the trunk behavior of hiding dotfiles from the tree.
|
Hey @fredrikekelund 👋🏼🙂 I think we should be good merging this one. Feel free to let me know what you think. |
…ore-to-site-sync # Conflicts: # apps/studio/src/ipc-handlers.ts # apps/studio/src/modules/sync/lib/ipc-handlers.ts
|
Taking another look at this now |
fredrikekelund
left a comment
There was a problem hiding this comment.
LGTM 👍 I left a couple of comments that'd be nice to address before landing this, not least the ignore dependency management.
Also interested to hear what you think about reading .gitignore, too, but that can easily be a follow-up.
There was a problem hiding this comment.
Sorry for not thinking of this sooner, but I feel like it'd make sense to look for a .gitignore file here, too. Thoughts, @ivan-ottinger? I guess this could also be a follow-up PR.
There was a problem hiding this comment.
No worried. Do you mean to merge records from both files and use that to decide what should get synced to WordPress.com / Pressable? 🤔 Wouldn't it be mixing two different concepts?
There was a problem hiding this comment.
Do you mean to merge records from both files and use that to decide what should get synced to WordPress.com / Pressable?
Yep, exactly. npm is an interesting example to look at. It uses an .npmignore file by default to determine what to include when publishing a package, and falls back to .gitignore (docs).
Wouldn't it be mixing two different concepts?
.gitignore might not be comprehensive in terms of what you want and don't want to sync to your remote site, but it's still a good indicator – especially if there's no .deployignore file.
.gitignore is specifically mentioned in #137, too.
| studioJson.plugins = this.options.deployIgnore | ||
| ? plugins.filter( | ||
| ( p: StudioJsonPluginOrTheme ) => | ||
| ! this.options.deployIgnore!.ignores( `wp-content/plugins/${ p.name }` ) | ||
| ) | ||
| : plugins; | ||
| studioJson.themes = this.options.deployIgnore | ||
| ? themes.filter( | ||
| ( t: StudioJsonPluginOrTheme ) => | ||
| ! this.options.deployIgnore!.ignores( `wp-content/themes/${ t.name }` ) | ||
| ) | ||
| : themes; |
There was a problem hiding this comment.
What we should really do, instead of asserting the array entry types, is use zod to parse the JSON returned by WP-CLI in getSitePlugins and getSiteThemes.
There was a problem hiding this comment.
I first tried to put it directly into this PR, but then decided to leave it for a new follow-up task: STU-1630 task - so we can unblock this PR.
…ore-to-site-sync # Conflicts: # apps/cli/commands/push.ts # apps/cli/lib/import-export/export/exporters/default-exporter.ts # apps/studio/src/lib/import-export/export/exporters/default-exporter.ts # apps/studio/src/lib/import-export/export/types.ts # apps/studio/src/modules/sync/lib/ipc-handlers.ts
|
Hey @fredrikekelund 👋🏼 I have applied the latest changes that also required adjustment related to recent #3129 PR. Now we have a new I have tested everything on macOS and Windows and haven't observed any regressions. |
Related issues
How AI was used in this PR
AI-assisted implementation with human iteration and review of all changes, architecture decisions, and edge cases. I have manually tested the changes as well.
Proposed Changes
Follow-up to #2924, which added
.deployignoresupport for Preview Sites. This PR extends it to Site Sync (push to WordPress.com / Pressable).createDeployIgnoreFilterto accept abasePatternsargument so the sync path can pass Studio-internal exclusions (database,db.php,debug.log,sqlite-database-integration,cache) alongsideDEPLOY_IGNORE_DEFAULTSlistLocalFileTreeandshouldExcludeFromSyncto use the deploy-ignore filter. Files matching.deployignorepatterns are hidden from the sync dialogDefaultExporteraccepts an optionalignoreFilterinExportOptions, used byaddWpContent()alongside existing hardcoded exclusions (isExactPathExcluded,isPathExcludedByPattern). Thestudio exportcommand gains a hidden--apply-deploy-ignoreflag; Studio's sync push sets it when shelling to the CLI. Regular site exports (Studio UI, directstudio exportCLI) do not apply the filter, preserving pre-existing backup behavior.meta.jsonagainst the deploy-ignore filter. Without this, the server-side import would reinstall excluded plugins from WordPress.org even though their files were excluded from the archiveSYNC_EXCLUSIONStoSYNC_IGNORE_DEFAULTS— a superset ofDEPLOY_IGNORE_DEFAULTSplus Studio-internal items (database,db.php,debug.log,sqlite-database-integration,cache)..replace(/\\/g, '/')path normalization infs-utils.ts— theignorelibrary handles Windows backslash conversion internallyignoreas a direct dependency inapps/cliandapps/studio(previously relied on transitive resolution)Testing Instructions
Tip
It is probably easier to ask your AI assistant to perform the steps 2 and 3 for you, so you don't have to create the testing files manually.
.deployignorefile at the site root (e.g.,~/Studio/my-site/.deployignore) with the following content:wp-content:wordpress-seoplugin on the sitewordpress-seoplugin is hidden from the treeakismetandmy-pluginare still visibleuploads/2024directory is hiddenuploads/2025is still visiblevendor/some-lib/is excluded from the pushvendor/important-lib/is included (negation pattern)*.logfiles are excludeduploads/2024/is excluded,uploads/2025/is presentwordpress-seoplugin is excluded and not reinstalled by the server.deployignorefile:.gitandnode_modulesinsidemy-pluginare excluded by built-in defaults.deployignore— the filter is only applied during sync push, not when using Studio UI "Export site" or runningstudio exportdirectlyPre-merge Checklist