action-wordpress-plugin-deploy: shipping a plugin to WordPress.org from a Git tag
Deploy your plugin to the WordPress.org repository using GitHub Actions
At a glance
- What is it?
- 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.
- Who is it for?
- 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.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 113 days ago.
- What is it written in?
- Mainly Shell, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
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
.gitignoreAnd the equivalent .gitattributes is:
# Directories
/.wordpress-org export-ignore
/.github export-ignore
# Files
/.gitattributes export-ignore
/.gitignore export-ignoreThe 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:
- uses: actions/checkout@v4The 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.
Editorial 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.
Frequently asked questions
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.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/10up-action-wordpress-plugin-deploy)Was this useful?