Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 34 additions & 4 deletions .github/workflows/deploy-wporg.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: Deploy to WordPress.org SVN

on:
release:
types: [released]
workflow_dispatch:
inputs:
version:
Expand All @@ -16,16 +18,44 @@ permissions:
contents: read

concurrency:
group: cortext-wporg-deploy-${{ inputs.version }}
group: cortext-wporg-deploy-${{ github.event_name == 'release' && github.event.release.tag_name || inputs.version }}
cancel-in-progress: false

# Stable GitHub Releases go to WordPress.org. The dry run runs first; the SVN
# commit waits for wordpress-org approval. Manual dispatch stays available for
# reruns and one-off deploys.
jobs:
dry-run:
name: Dry run
runs-on: ubuntu-latest
env:
VERSION: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.version }}
steps:
- uses: actions/checkout@v6.0.2

- name: Check release is deployable
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail

if ! [[ "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Release versions must use the WordPress-style format 0.1.0, without a leading \"v\". Received \"$VERSION\"."
exit 1
fi

release_json="$(gh release view "$VERSION" --json isPrerelease,assets)"

if [ "$(jq -r '.isPrerelease' <<< "$release_json")" = "true" ]; then
echo "::error::Release $VERSION is a prerelease, and prereleases do not deploy to WordPress.org."
exit 1
fi

if ! jq -e '.assets[] | select(.name == "cortext.zip")' <<< "$release_json" > /dev/null; then
echo "::error::Release $VERSION has no cortext.zip asset."
exit 1
fi

- name: Install Subversion
run: |
sudo apt-get update
Expand All @@ -34,15 +64,16 @@ jobs:
- name: Preview WordPress.org SVN deploy
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ inputs.version }}
run: ./scripts/deploy-wporg.sh --version "$VERSION"

publish:
name: Publish
needs: dry-run
if: ${{ inputs.commit }}
if: ${{ github.event_name == 'release' || inputs.commit }}
runs-on: ubuntu-latest
environment: wordpress-org
env:
VERSION: ${{ github.event_name == 'release' && github.event.release.tag_name || inputs.version }}
steps:
- uses: actions/checkout@v6.0.2

Expand All @@ -56,7 +87,6 @@ jobs:
GH_TOKEN: ${{ github.token }}
WPORG_USERNAME: ${{ secrets.WPORG_USERNAME }}
WPORG_PASSWORD: ${{ secrets.WPORG_PASSWORD }}
VERSION: ${{ inputs.version }}
run: |
set -euo pipefail

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release-desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ jobs:
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
tag_name: ${{ inputs.version }}
# Empty when run standalone without a ref: creation then
# defaults to the default branch, and reruns against an
# existing draft keep its pinned commit.
target_commitish: ${{ inputs.ref }}
draft: true
prerelease: ${{ inputs.prerelease }}
files: apps/desktop/dist/*.dmg
4 changes: 4 additions & 0 deletions .github/workflows/release-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ jobs:
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
tag_name: ${{ steps.milestone.outputs.version }}
# Empty when run standalone without a ref: creation then
# defaults to the default branch, and reruns against an
# existing draft keep its pinned commit.
target_commitish: ${{ inputs.ref }}
name: ${{ steps.milestone.outputs.version }}
body_path: release-notes.md
draft: true
Expand Down
63 changes: 38 additions & 25 deletions docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ small for now; we can add more steps once we have a regular release cadence.
`0.2.0`.
- Patch milestones, such as `0.1.1`, may be open at the same time for hotfix
releases.
- After publishing a non-patch release, the workflow creates the next
non-patch milestone, such as `0.3.0` after `0.2.0`. Patch releases do not
create a next milestone.
- After publishing a release, close its milestone.
- Preparing a non-patch release creates the next non-patch milestone, such as
`0.3.0` after `0.2.0`. Patch releases do not create a next milestone.
- Preparing a release also closes its milestone. The cut is the version bump
commit; PRs merged after that belong to the next milestone, even if the
draft is still waiting to be published or deployed.

The first milestone is `0.1.0`.

Expand Down Expand Up @@ -137,12 +138,39 @@ desktop run only uploads the DMG. You can also run either workflow on its own
from the Actions tab, which is handy for rebuilding just the DMG against a draft
that already exists.

Once the draft looks right, publish it. Publishing a stable Release starts the
WordPress.org deploy.

## Deploying to WordPress.org

After the GitHub Release is published, deploy that same ZIP to WordPress.org
SVN. SVN is only the distribution channel; GitHub stays the source of truth.
Publishing a stable GitHub Release deploys the release ZIP to WordPress.org
SVN. SVN is just the distribution channel; GitHub stays the source of truth.

The "Deploy to WordPress.org SVN" workflow listens for stable Releases.
Prereleases do not start the automatic deploy. If you later promote a
prerelease to a stable Release, the deploy runs then. Before touching SVN, the
workflow checks the version format, confirms the Release is stable, and makes
sure `cortext.zip` is attached. Then it runs a dry run so you can inspect the
SVN status.

The actual SVN commit runs in a separate publish job behind the `wordpress-org`
GitHub environment. Store `WPORG_USERNAME` and `WPORG_PASSWORD` there as
environment secrets. The environment needs required reviewers, and it must allow
tag refs matching `*.*.*`; the workflow runs on the release tag, so a
branch-only environment rejects the publish job. Review the dry-run output
before approving.

Preview the SVN deploy first:
You can still run the same workflow from the Actions tab for reruns and one-off
deploys. Pass a `version` and choose whether to set `commit`. Without `commit`,
the workflow stops after the dry run.

The script downloads the GitHub Release ZIP, syncs it into `trunk/`, copies
`assets/wordpress-org/` into the SVN assets directory, removes deleted SVN
entries, creates `tags/<version>`, and checks that the plugin header,
`CORTEXT_VERSION`, and stable tag all match. For non-interactive use, set
`WPORG_USERNAME` and `WPORG_PASSWORD`.

To preview the SVN deploy locally:

```bash
pnpm run deploy:wporg -- --version <version>
Expand All @@ -152,29 +180,14 @@ Dry runs also work for an already-published version. If `tags/<version>` already
exists, the script stages a local `tags/__dry-run-<version>` copy so the rest of
the flow can still be checked without touching the real tag.

If the status output looks right, publish it:
To publish from your machine instead of GitHub Actions:

```bash
pnpm run deploy:wporg -- --version <version> --commit --username <wporg-user>
```

The "Deploy to WordPress.org SVN" workflow runs the same script. It always runs
a dry-run job first. If `commit` is `true`, a separate publish job uses the
`wordpress-org` GitHub environment, where `WPORG_USERNAME` and `WPORG_PASSWORD`
live as environment secrets. Configure that environment with required reviewers
and restrict it to `main` before using it for real releases.

The script downloads the GitHub Release ZIP, syncs it into `trunk/`, copies
`assets/wordpress-org/` into the SVN assets directory, removes deleted SVN
entries, creates `tags/<version>`, and checks that the plugin header,
`CORTEXT_VERSION`, and stable tag all match. For non-interactive use, set
`WPORG_USERNAME` and `WPORG_PASSWORD`.

When a release succeeds, the workflow closes the released milestone. If it is a
non-patch release, it also creates the next non-patch milestone if it does not
already exist. For example, `2.0.0` creates `2.1.0` and `0.2.0` creates
`0.3.0`. A patch release such as `0.1.1` closes `0.1.1` but does not create a
next milestone.
When we add another distribution channel, such as a desktop update feed, give it
its own workflow on the same release event.

## Desktop app

Expand Down