# action-wordpress-plugin-deploy: shipping a plugin to WordPress.org from a Git tag

> The 10up action-wordpress-plugin-deploy GitHub Action commits the contents of a Git tag to the WordPress.org plugin repository over Subversion, using the same tag name, with file exclusion through .distignore or .gitattributes and an optional ZIP build. It is a narrow, well documented deploy step with dry-run and build directory support.

**10up/action-wordpress-plugin-deploy** — Deploy your plugin to the WordPress.org repository using GitHub Actions

- Repository: https://github.com/10up/action-wordpress-plugin-deploy
- Stars: 664 · Forks: 115
- Language: Shell
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/10up-action-wordpress-plugin-deploy

## What the action actually moves: a tag, then a Subversion commit

The action is one job, and doing it well is the point. It commits the contents of your Git tag to the WordPress.org plugin repository using the same tag name. Underneath that sentence is a two world translation: WordPress.org still distributes plugins through a Subversion repository with a trunk directory and a top-level assets directory, while plugin authors live in Git.

So the action reads a tag from Git, filters out the files you do not want to publish, and commits what is left to the WordPress.org Subversion repository. The commit carries the same name as the tag, which is what makes the SVN history readable from the Git history.

Two details beyond the core commit matter. Files can be excluded using either a .distignore file or a .gitattributes file, which gives you two ways to express the same intent depending on whether you have a build step. And anything in a .wordpress-org subdirectory is moved to the top-level assets directory in Subversion, which is where plugin banners, icons and screenshots live, at the same level as trunk. The plugin code stays in trunk, and the WordPress.org specific files move up one level, exactly as the WordPress.org directory layout expects.

The repository itself is a small one: action.yml as the action definition, deploy.sh as the shell script that does the work, an examples/ directory with workflow files, plus CHANGELOG.md, CONTRIBUTING.md, CREDITS.md and the LICENSE file. The primary language is recorded as Shell, which is consistent with the implementation being a shell script driven by the action metadata.

## Two secrets, four environment variables and two inputs

The configuration surface is small enough to hold in your head, which is a good sign for something that runs in your release pipeline.

Two secrets are required: SVN_USERNAME and SVN_PASSWORD. The documentation notes that secrets are set in your repository settings and cannot be viewed once stored, which is the standard GitHub behaviour rather than a quirk of this action.

Four optional environment variables tune the behaviour. SLUG defaults to the repository name and is there for the case where your WordPress.org repository has a different slug or a different capitalisation. VERSION defaults to the tag name, and the documentation explicitly says it is not recommended to set this except for testing. ASSETS_DIR defaults to .wordpress-org and lets you point at a different location for the WordPress.org specific assets that belong in the top-level assets directory. BUILD_DIR defaults to false, and setting it to the directory where you build your plugin files makes the action copy and deploy from that directory instead. Both absolute and relative paths work, and a relative path is concatenated with the repository root. All files and folders in the build directory are deployed, and .distignore or .gitattributes are ignored when a build directory is used.

The two inputs are generate-zip and dry-run, both defaulting to false. generate-zip produces a ZIP file from the SVN trunk directory and outputs a zip-path variable for later workflow steps. dry-run skips the final Subversion commit step, which the documentation describes as useful for debugging before a real commit. A dry run does not require the SVN secret at all, which makes it a cheap first check.

The single output is zip-path, the fully qualified path including the filename, intended for uploading as a release asset.

## Excluding files, and the asymmetry between .distignore and .gitattributes

The exclusion mechanism is where the two modes of this action genuinely differ, and the documentation is candid about it.

If a .distignore file is present it is used. If not, the action looks for a .gitattributes file, and failing that it writes a basic temporary .gitattributes into place before proceeding, so that no Git or GitHub specific files are included. That fallback alone prevents a .github directory from being published to WordPress.org, which is the kind of detail you would otherwise discover after a release.

The guidance is to use .distignore for modern plugin setups with a build step and no built files committed to the repository. It is useful particularly when there are built files listed in .gitignore, and it is a file already used by WP-CLI. Use .gitattributes for plugins that do not run a build step in the Actions workflow, and it also lets GitHub generated ZIP files contain the same contents as what is committed to WordPress.org.

The baseline .distignore contents are:

```
/.wordpress-org
/.git
/.github
/node_modules

.distignore
.gitignore
```

And the equivalent .gitattributes is:

```gitattributes
# Directories
/.wordpress-org export-ignore
/.github export-ignore

# Files
/.gitattributes export-ignore
/.gitignore export-ignore
```

The documented limitation is worth internalising. .distignore is for files to be ignored only, and it does not currently allow negation like .gitignore, because that comes from the expected syntax of the wp dist-archive command in WP-CLI. A .distinclude style file might be allowed in future, or WP-CLI might change its syntax and the action would follow for consistency. Note also that .distignore has to contain more than .gitattributes does, because the .gitattributes method also respects .gitignore.

## Wiring the action into a release workflow

The documented way to start is to copy the contents of one of the example workflow files into .github/workflows/deploy.yml and push that to your repository. The file can have a different name, but it has to live in that directory. The recommendation is to use ubuntu-latest for compatibility with the required dependencies in the action.

Three example workflows are provided, covering the common trigger choices. One deploys when you publish a new release and attaches a ZIP file to it. One deploys when you push a new tag. One deploys when you push a new tag and creates a release with the ZIP attached.

One step is called out as required regardless of which example you start from, the checkout of the repository for use during the workflow run:

```yaml
- uses: actions/checkout@v4
```

The ZIP angle deserves a note because it addresses a specific WordPress.org convention. The documentation notes that if you want to attach a ZIP file with the proper contents that decompresses to a folder name without a version number, as WordPress generally expects, you can add steps to your workflow that generate the ZIP and attach it to the GitHub release. The generate-zip input and the zip-path output exist for that, and the examples show the shape of the wiring.

There is a second action worth knowing about, and the documentation points at it explicitly. For updating the readme and items in the assets directory between releases, the WordPress.org Plugin Readme/Assets Update Action is the tool for that job, hosted at 10up/action-wordpress-plugin-asset-update. The distinction is clean: this action handles the versioned code release, the other handles the metadata that lives outside trunk.

## Support level, releases and the MIT terms

The support level is stated as Active, with the meaning spelled out: 10up is actively working on this and expects to continue work for the foreseeable future, including keeping it tested up to the most recent version of WordPress. Bug reports, feature requests, questions and pull requests are welcome.

The release history supports that. Version 2.3.0 was published on 2025-01-21, and before that 2.2.2 on 2023-09-07 and 2.2.1 on 2023-09-05. Two patch releases two days apart in September 2023, then a gap of about sixteen months to 2.3.0, shows a project that ships when there is something to ship rather than on a schedule. The last push to the repository was on 2026-07-02, and the default branch is develop, which is the usual 10up pattern of building releases from a development line.

The licensing is MIT, described as available for use and remix, and the repository carries the LICENSE file. For an action that runs with your Subversion credentials, that matters in a practical sense: the script that handles those credentials is short enough to read, and the MIT terms let you fork it if your deployment needs to differ.

The 10up project also publishes a collection of WordPress focused GitHub Actions, linked from the README, and the repository has a CONTRIBUTING.md for anyone who wants to help. There is a CREDITS.md file as well, which is where the upstream projects and contributors the action builds on are acknowledged.

## Conclusion

action-wordpress-plugin-deploy suits a plugin team that already publishes from Git tags and wants the WordPress.org SVN mirror updated without a human running WP-CLI by hand. It does not suit a plugin whose readme and assets change between releases, since the documentation points that case at a separate asset update action, and it does not suit you if you need inclusion rules, because .distignore supports only files to ignore and cannot negate. Before wiring it into a release workflow, add a dry-run step first, then confirm your exclusion file matches what WP-CLI would produce, since the syntax is inherited from wp dist-archive.

## FAQ

### What does the 10up action-wordpress-plugin-deploy action do?

It commits the contents of your Git tag to the WordPress.org plugin repository using the same tag name, over Subversion. It can exclude files via .distignore or .gitattributes, and moves anything from a .wordpress-org subdirectory into the top-level assets directory.

### Which secrets and variables does the WordPress.org deploy action need?

SVN_USERNAME and SVN_PASSWORD are required. The optional variables are SLUG, defaulting to the repository name, VERSION, defaulting to the tag name, ASSETS_DIR, defaulting to .wordpress-org, and BUILD_DIR, defaulting to false, which sets the directory the action copies and deploys from.

### Can I test the WordPress.org deploy action without committing to Subversion?

Yes. The dry-run input, defaulting to false, skips the final Subversion commit step for debugging, and a dry run does not require the SVN secret. The generate-zip input additionally produces a ZIP from the SVN trunk directory and outputs a zip-path variable.

### Should I use .distignore or .gitattributes to exclude files?

Use .distignore for modern plugin setups with a build step and no committed build output, and .gitattributes for plugins with no build step in the workflow. The .distignore method supports only files to ignore, with no negation, because it follows the syntax of the WP-CLI wp dist-archive command.

### How do I update the readme and assets between plugin releases?

The README points to a separate action for that, the WordPress.org Plugin Readme/Assets Update Action at 10up/action-wordpress-plugin-asset-update, since this action covers the versioned code release rather than metadata outside trunk.

## Sources

- [10up/action-wordpress-plugin-deploy on GitHub](https://github.com/10up/action-wordpress-plugin-deploy)
- [Issues](https://github.com/10up/action-wordpress-plugin-deploy/issues)
- [License: MIT](https://github.com/10up/action-wordpress-plugin-deploy/blob/develop/LICENSE)
- [README](https://github.com/10up/action-wordpress-plugin-deploy/blob/develop/README.md)
- [Releases](https://github.com/10up/action-wordpress-plugin-deploy/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/10up-action-wordpress-plugin-deploy
