Enable automatic synchronization only after the same pinned workflow succeeds as a dry run and as a real draft write.
Choose a direction
| Direction | Guide | Mode |
|---|---|---|
| Merged Markdown publishes WordPress Pages | GitHub to WordPress | publish, or reconcile in a combined workflow |
| Gutenberg edits open a Markdown pull request | WordPress to GitHub | propose, or reconcile in a combined workflow |
Use the combined reconcile workflow below when both authoring paths are valid for the same Page tree.
Stage 1: manual dry run
Use workflow_dispatch, status: draft, delete-mode: trash, and dry-run: true. Review the Action summary and confirm that every planned create, update, and delete is expected.
Stage 2: manual draft write
After explicit approval, change only dry-run to false. The workflow can now create, update, and trash WordPress Pages, so keep the trigger manual while you inspect:
- the Page hierarchy;
- titles and headings;
- Gutenberg block rendering;
- rewritten internal links;
- exact GitHub source actions;
- unexpected unmanaged conflicts.
Stage 3: path-scoped synchronization
After the draft lifecycle succeeds, add the default-branch trigger:
on: push: branches: [main] paths: - "docs/**/*.md" - "docs/**/*.markdown" - "docs/**/*.json" - ".github/workflows/sync-docs.yml" workflow_dispatch:permissions: contents: read
Keep status: draft if WordPress remains an editorial review gate. Set status: publish only when merged Markdown should update public Pages automatically.
Automatic sync includes removals
Deleting a managed Markdown file schedules its managed WordPress Page for Trash, or permanent deletion when delete-mode: force. Review that policy before enabling the push trigger.
Stage 4: reconcile WordPress edits
After normal publishing is stable, use one workflow for push-based publishing and scheduled WordPress polling:
Before the first reverse-sync run, open Settings → Actions → General for the repository. Under Workflow permissions, enable Allow GitHub Actions to create and approve pull requests, then select Save. For another repository, open the same settings page under its owner and repository name.

on: push: branches: [main] paths: ["docs/**", ".github/workflows/sync-docs.yml"] schedule: - cron: "3/5 * * * *" workflow_dispatch:permissions: contents: write pull-requests: writeconcurrency: group: docspress-sync cancel-in-progress: falsejobs: sync: # Do not publish a merged WordPress proposal back to WordPress. if: >- github.event_name != 'push' || !contains( github.event.head_commit.message, format('from {0}/docspress/wordpress-sync', github.repository_owner) ) runs-on: ubuntu-latest steps: - uses: actions/checkout@FULL_COMMIT_SHA - uses: Automattic/docspress@FULL_COMMIT_SHA with: mode: reconcile wordpress-site: example.wordpress.com wordpress-access-token: ${{ secrets.WP_ACCESS_TOKEN }} docs-dir: docs root-slug: docs status: publish
DocsPress compares the live Gutenberg tree with the tree generated from the current Markdown, then rewrites only source regions whose blocks changed. Unchanged frontmatter, spacing, code-fence languages, tables, and readable block envelopes stay byte-for-byte intact. Plain core blocks become ordinary Markdown. DocsPress and non-portable core blocks use semantic, lossless envelopes instead of exposing raw wp:* comments. If DocsPress cannot map the blocks safely, the run fails instead of regenerating the whole file.
The Action updates one action-owned branch and pull request instead of opening duplicates on every poll.
The job condition skips the push event created when GitHub merges the action-owned docspress/wordpress-sync branch. Scheduled and manual runs still reconcile normally. If you set a custom pull-request-branch, use the same branch name in the condition. As a fallback for workflows without the condition, the Action recognizes its configured branch in GitHub's merge commit and exits successfully without reading or writing WordPress.
While that pull request is open, reconcile leaves the WordPress-only Page untouched. After the pull request merges, the next run recognizes that both sides converge and refreshes the Page sentinel; GitHub-only changes to other Pages can continue publishing in the same run.
Observe each run
The Action exports counters for created, updated, deleted, unchanged, proposed, skipped, and conflict operations plus summary-json for downstream jobs.
The first synchronization walkthrough shows the run overview and generated Sync Summary with tightly cropped screenshots. Use the same counters to review every automatic run.
Pin and update intentionally
Use full verified commit SHAs for both actions/checkout and Automattic/docspress. When adopting a newer DocsPress revision:
- inspect its
action.ymlinputs; - review source and bundled
dist/changes; - update the SHA;
- return to a manual dry run;
- restore automatic synchronization after verification.