Skip to content

Add .deployignore support for Preview Sites - #2924

Merged
ivan-ottinger merged 10 commits into
trunkfrom
stu-16-add-deployignore-support
Mar 31, 2026
Merged

Add .deployignore support for Preview Sites#2924
ivan-ottinger merged 10 commits into
trunkfrom
stu-16-add-deployignore-support

Conversation

@ivan-ottinger

@ivan-ottinger ivan-ottinger commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

Related issues

How AI was used in this PR

AI-assisted implementation with human review of all changes, architecture decisions, and edge cases. Step-by-step iteration. I have tested the PR as well.

Proposed Changes

  • Add a shared createDeployIgnoreFilter utility (tools/common/lib/deploy-ignore.ts) that reads a .deployignore file from the site root and creates a filter using the ignore npm package (gitignore pattern syntax)
    • the ignore package is widely used and I consider it is better to use it instead of re-inventing the wheel
  • .git, node_modules, .DS_Store, Thumbs.db) are excluded by default
  • Preview Sites: Update archiveSiteContent and validateSiteSize to use the deploy-ignore filter when archiving wp-content for upload
  • Remove the old isExcludedFromArchive hardcoded filter from fs-utils.ts, replacing it with the new deploy-ignore utility. calculateDirectorySizeForArchive retains a fallback to the original .git/node_modules exclusion when called without a filter

Note: Site Sync integration with .deployignore will be handled in a follow-up PR that also updates the DefaultExporter filtering. The shared utility introduced here is designed to be reused for that.

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.

  1. Create a local site in Studio and install the wordpress-seo plugin
  2. Add a .deployignore file at the site root (e.g., ~/Studio/my-site/.deployignore) with the following content:
    # Ignore vendor dependencies in my-plugin, except important-lib
    wp-content/plugins/my-plugin/vendor/*
    !wp-content/plugins/my-plugin/vendor/important-lib
    
    # Ignore all log files
    *.log
    
    # Ignore old uploads
    wp-content/uploads/2024
    
    # Ignore a specific plugin
    wp-content/plugins/wordpress-seo
    
  3. Create matching test files/folders in the site's wp-content:
    # Plugin with build artifacts
    mkdir -p wp-content/plugins/my-plugin/vendor/some-lib
    mkdir -p wp-content/plugins/my-plugin/vendor/important-lib
    echo '<?php // plugin entry' > wp-content/plugins/my-plugin/my-plugin.php
    echo '<?php // vendor file' > wp-content/plugins/my-plugin/vendor/some-lib/autoload.php
    echo '<?php // important' > wp-content/plugins/my-plugin/vendor/important-lib/loader.php
    echo 'build log' > wp-content/plugins/my-plugin/build.log
    
    # .git and node_modules inside the plugin (built-in defaults)
    mkdir -p wp-content/plugins/my-plugin/.git
    echo 'ref: refs/heads/main' > wp-content/plugins/my-plugin/.git/HEAD
    mkdir -p wp-content/plugins/my-plugin/node_modules/some-package
    echo '{"name":"some-package"}' > wp-content/plugins/my-plugin/node_modules/some-package/package.json
    
    # Log file at wp-content level
    echo 'error log content' > wp-content/debug.log
    
    # Old and new uploads
    mkdir -p wp-content/uploads/2024
    echo 'old photo' > wp-content/uploads/2024/photo.jpg
    mkdir -p wp-content/uploads/2025
    echo 'new photo' > wp-content/uploads/2025/photo.jpg
    
  4. Create/update a Preview Site and verify that matched files are excluded from the upload (you can use the JN tool to get the site SSH details: 3ad28-pb).
    • vendor/some-lib/ should be excluded
    • vendor/important-lib/ should be included (negation pattern)
    • *.log files should be excluded
    • uploads/2024/ should be excluded, uploads/2025/ should be present
    • wordpress-seo plugin should be excluded
  5. Verify that .git and node_modules inside my-plugin are excluded by built-in defaults (even without .deployignore entries for them)
  6. Verify that site export (backup) is unaffected by .deployignore. Please note that the backup already excludes files/folders like .git, node_modules, cache by default (unrelated to this PR).
  7. The preview site should work as expected - without any issues.

Pre-merge Checklist

  • Have you checked for TypeScript, React or other console errors?
Allow users to place a .deployignore file at the site root to control
which files are excluded when pushing to Preview Sites or syncing to
WordPress.com/Pressable. Uses gitignore pattern syntax via the `ignore`
npm package. Built-in defaults (.git, node_modules, .DS_Store, Thumbs.db)
are always applied regardless of whether a .deployignore file exists.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds shared .deployignore-based filtering (gitignore syntax via ignore npm package) so Preview Site uploads and Site Sync file-tree building can consistently exclude user-specified paths plus a set of built-in defaults.

Changes:

  • Introduces createDeployIgnoreFilter utility in @studio/common and adds unit tests for it.
  • Updates CLI Preview Site archiving + size validation to apply deploy-ignore rules when scanning/archiving wp-content.
  • Updates Studio Site Sync file-tree building to use deploy-ignore rules, while keeping dotfile hiding as a UI-specific rule; refactors sync exclusion constants accordingly.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tools/common/package.json Adds ignore dependency used to parse .deployignore.
tools/common/lib/deploy-ignore.ts New shared utility to build ignore filter from defaults + .deployignore.
tools/common/lib/tests/deploy-ignore.test.ts Unit coverage for default rules, patterns, negation, globs, additional defaults.
tools/common/lib/fs-utils.ts Extends archive size calculation to accept an ignore filter + path prefix.
apps/cli/lib/archive.ts Applies deploy-ignore filtering when archiving wp-content for preview uploads.
apps/cli/lib/validation.ts Applies deploy-ignore filtering when computing wp-content size for limits.
apps/cli/lib/tests/validation.test.ts Updates mocks/assertions for new deploy-ignore usage in validation.
apps/studio/src/modules/sync/constants.ts Renames/reshapes sync exclusions into SYNC_ADDITIONAL_DEFAULTS.
apps/studio/src/modules/sync/lib/tree-utils.ts Uses deploy-ignore filter when deciding whether to exclude sync tree entries.
apps/studio/src/ipc-handlers.ts Builds deploy-ignore filter once per tree request and threads it through recursion.
package-lock.json Locks ignore@7.0.5 for tools/common.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/common/lib/deploy-ignore.ts Outdated
Comment thread tools/common/lib/deploy-ignore.ts Outdated
Log a warning for unexpected errors (e.g., permission issues) instead
of silently falling back to defaults. Uses the existing isErrnoException
helper, matching the pattern in fs-utils.ts.
Site Sync integration with .deployignore will be handled in a follow-up
PR that also updates the DefaultExporter filtering.
@ivan-ottinger ivan-ottinger changed the title Add .deployignore support for Preview Sites and Site Sync Mar 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tools/common/lib/deploy-ignore.ts Outdated
Comment thread apps/cli/lib/archive.ts
Comment thread apps/cli/lib/archive.ts
Comment thread tools/common/lib/deploy-ignore.ts Outdated
* Default patterns that are always excluded from deploys,
* regardless of whether a .deployignore file exists.
*/
const DEPLOY_IGNORE_DEFAULTS = [ '.git', 'node_modules', '.DS_Store', 'Thumbs.db' ];

@ivan-ottinger ivan-ottinger Mar 30, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The .DS_Store and Thumbs.db are a new addition - as I don't think we need those synced to the preview site.

@ivan-ottinger
ivan-ottinger marked this pull request as ready for review March 30, 2026 14:25
@ivan-ottinger
ivan-ottinger requested a review from a team March 30, 2026 14:25

@fredrikekelund fredrikekelund left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is a great addition 👍 I left a couple of comments on the implementation, but the core functionality looks good and tests well for me. I think we should address my comments, but approving now to unblock you, @ivan-ottinger

Comment thread tools/common/lib/deploy-ignore.ts Outdated
Comment thread tools/common/lib/fs-utils.ts
Comment thread tools/common/lib/fs-utils.ts Outdated
Remove the separate DEFAULT_EXCLUDED_DIRECTORIES fallback and the
Ignore type re-export. Instead, construct a default ignore instance
from the exported DEPLOY_IGNORE_DEFAULTS when no filter is provided.
@wpmobilebot

wpmobilebot commented Mar 31, 2026

Copy link
Copy Markdown
Collaborator

📊 Performance Test Results

Comparing be4ce31 vs trunk

app-size

Metric trunk be4ce31 Diff Change
App Size (Mac) 1327.45 MB 1327.69 MB +0.24 MB ⚪ 0.0%

site-editor

Metric trunk be4ce31 Diff Change
load 1875 ms 1825 ms 50 ms 🟢 -2.7%

site-startup

Metric trunk be4ce31 Diff Change
siteCreation 8173 ms 8171 ms 2 ms ⚪ 0.0%
siteStartup 4846 ms 4855 ms +9 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@ivan-ottinger

Copy link
Copy Markdown
Contributor Author

This is a great addition 👍 I left a couple of comments on the implementation, but the core functionality looks good and tests well for me. I think we should address my comments, but approving now to unblock you, @ivan-ottinger

Thanks for the review and feedback, Fredrik. I have updated the PR. Next I will retest the logic on both macOS and Windows and then merge.

@ivan-ottinger

Copy link
Copy Markdown
Contributor Author

Thanks for the review and feedback, Fredrik. I have updated the PR. Next I will retest the logic on both macOS and Windows and then merge.

Everything is testing well. The files are properly excluded on both macOS and Windows. I did not observe any regressions.

@ivan-ottinger
ivan-ottinger merged commit 2854d6c into trunk Mar 31, 2026
11 checks passed
@ivan-ottinger
ivan-ottinger deleted the stu-16-add-deployignore-support branch March 31, 2026 11:10
@georgestephanis

Copy link
Copy Markdown

@ivan-ottinger I was just looking for something similar on this -- does this / could this also support .deployignore files in subdirectories, relative to that directory?

For example, if I manage a theme or plugin that I've got checked out inside of a local synced site in Studio, could I have a .deployignore file in that theme or plugin to exclude its src and npm/composer directories?

@ivan-ottinger

Copy link
Copy Markdown
Contributor Author

@ivan-ottinger I was just looking for something similar on this -- does this / could this also support .deployignore files in subdirectories, relative to that directory?

For example, if I manage a theme or plugin that I've got checked out inside of a local synced site in Studio, could I have a .deployignore file in that theme or plugin to exclude its src and npm/composer directories?

Great idea, George! Currently .deployignore is only read from the site root, so subdirectory ignore files aren't supported.

It could be a nice enhancement though. I have opened up a follow-up issue (STU-1499) for it so we can explore that approach further.

ivan-ottinger added a commit that referenced this pull request Apr 1, 2026
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
fredrikekelund pushed a commit that referenced this pull request Apr 2, 2026
* Add .deployignore support for Preview Sites and Site Sync

Allow users to place a .deployignore file at the site root to control
which files are excluded when pushing to Preview Sites or syncing to
WordPress.com/Pressable. Uses gitignore pattern syntax via the `ignore`
npm package. Built-in defaults (.git, node_modules, .DS_Store, Thumbs.db)
are always applied regardless of whether a .deployignore file exists.

* Only silence ENOENT when reading .deployignore

Log a warning for unexpected errors (e.g., permission issues) instead
of silently falling back to defaults. Uses the existing isErrnoException
helper, matching the pattern in fs-utils.ts.

* Revert sync changes to focus PR on Preview Sites only

Site Sync integration with .deployignore will be handled in a follow-up
PR that also updates the DefaultExporter filtering.

* Remove unused additionalDefaults parameter from createDeployIgnoreFilter

* Update comment to clarify defaults can be overridden via negation

* Use single filtering path with shared DEPLOY_IGNORE_DEFAULTS

Remove the separate DEFAULT_EXCLUDED_DIRECTORIES fallback and the
Ignore type re-export. Instead, construct a default ignore instance
from the exported DEPLOY_IGNORE_DEFAULTS when no filter is provided.

* Simplify relative path computation with path.relative
ivan-ottinger added a commit that referenced this pull request Apr 23, 2026
* Add .deployignore support to Site Sync

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

* Check deployIgnore on top-level archive paths

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.

* Filter meta.json plugin and theme lists against deployIgnore

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.

* Update SYNC_ADDITIONAL_DEFAULTS comment to clarify overridability

* Add tests for additionalDefaults parameter and negation override

* Apply .deployignore to CLI sync push

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

* Simplify createDeployIgnoreFilter with a single basePatterns argument

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.

* Move DEPLOY_IGNORE_DEFAULTS into SYNC_IGNORE_DEFAULTS

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).

* Inline deploy defaults in SYNC_IGNORE_DEFAULTS to fix renderer build

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.

* Drop redundant Windows path normalization around ignore.ignores()

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.

* Fix import order in CLI export types

* Extract DEPLOY_IGNORE_DEFAULTS to a renderer-safe module

Move the constant to deploy-ignore-defaults.ts so sync/constants.ts
can spread it via ...DEPLOY_IGNORE_DEFAULTS without pulling in fs.

* Show dotfiles in the sync tree UI

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.

* Revert dotfile visibility in sync tree

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.

* Document dotfile handling in sync filter and tree UI

* Add ignore as a direct dependency and upgrade to v7

* Use path.join for consistent separators when building ignore paths

* Rename exporter option deployIgnore to ignoreFilter

* Fix import order in CLI export command

* Move shouldExcludeFromSync to its own module and use path.basename
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

5 participants