Skip to content

Commit 5991388

Browse files
authored
Monorepo setup + release automation (#334)
## Summary This PR restructures the repository into a **monorepo** with separate packages and adds **release automation**. ### Monorepo structure ``` packages/ ├── mysql-on-sqlite/ # MySQL-on-SQLite driver ├── mysql-proxy/ # MySQL binary protocol proxy └── plugin-sqlite-database-integration/ # WordPress plugin ``` The plugin package links to the driver through a `wp-includes/database` symlink. The release build script (`composer run build`) resolves this symlink into a real copy, producing a self-contained plugin zip. WordPress setup for WP tests remains at the repository root for now. The tests for the legacy driver remain there as well (to be deleted soon). We eventually split it even further (`mysql-parser`, ...) and handle the grammar tools, but that can be addressed separately. ### Automated release workflow Release is automated with GitHub Actions. It requires only two manual steps: 1. **Create a draft release with a new tag and changelog.** The `release-prepare` workflow will automatically: - Use the draft tag to bump versions in `version.php`, `load.php`, and `readme.txt`. - Add the draft release changelog entry to `readme.txt`. - Build the plugin ZIP and attach it to the draft release. - Create a PR (`release/<version>` → `trunk`) and link it in the draft release body. 2. **Review and merge the PR.** The `release-publish` workflow will automatically: - Publish the draft release. - Publish the release artifact to WordPress.org. Closes #83, #200, and #328. Note that currently, the release workflow is aimed at releasing the SQLite plugin, which goes hand-in-hand with the driver changes. If we need to decouple the releases at some point (e.g., release a new driver version, but not plugin, or release the plugin only), then we'll need to address that. ### Testing I will test and fine-tune the release workflow on `trunk` using a temporary nonsense tag (`0.0.1-test1`, etc.). The WordPress.org part requires SVN credentials, and I will test and fine-tune it with the next plugin release. ### Next steps - Update Playground plugin-refresh logic once this is merged. - Update Studio to use the new release artifacts starting from the next release. - Further split the components (`mysql-parser`, ...). - Improve the monorepo setup tooling (do we need anything like NX?). - Set up Composer packages.
1 parent 783790c commit 5991388

105 files changed

Lines changed: 2157 additions & 246 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/mysql-proxy-tests.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ jobs:
2626
with:
2727
ignore-cache: "yes"
2828
composer-options: "--optimize-autoloader"
29-
working-directory: packages/wp-mysql-proxy
29+
working-directory: packages/mysql-proxy
3030

3131
- name: Run MySQL Proxy tests
3232
run: composer run test
33-
working-directory: packages/wp-mysql-proxy
33+
working-directory: packages/mysql-proxy

.github/workflows/phpunit-tests-run.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,22 @@ jobs:
7272
exit 1
7373
fi
7474
75-
- name: Install Composer dependencies
75+
- name: Install Composer dependencies (root)
7676
uses: ramsey/composer-install@v3
7777
with:
7878
ignore-cache: "yes"
7979
composer-options: "--optimize-autoloader"
8080

81+
- name: Install Composer dependencies (mysql-on-sqlite)
82+
uses: ramsey/composer-install@v3
83+
with:
84+
working-directory: packages/mysql-on-sqlite
85+
ignore-cache: "yes"
86+
composer-options: "--optimize-autoloader"
87+
8188
- name: Run PHPUnit tests
82-
run: php ./vendor/bin/phpunit -c ./phpunit.xml.dist
89+
run: php ./vendor/bin/phpunit -c ./phpunit.xml.dist
90+
working-directory: packages/mysql-on-sqlite
91+
92+
- name: Run PHPUnit tests for the legacy driver
93+
run: php ./vendor/bin/phpunit -c ./phpunit.xml.dist
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
name: Prepare release
2+
3+
on:
4+
release:
5+
types: [created, edited]
6+
7+
concurrency:
8+
group: release-prepare-${{ github.event.release.tag_name }}
9+
cancel-in-progress: false
10+
11+
jobs:
12+
prepare-release:
13+
name: Prepare release PR and build plugin zip
14+
if: >-
15+
github.repository == 'WordPress/sqlite-database-integration'
16+
&& github.event.release.draft == true
17+
runs-on: ubuntu-latest
18+
permissions:
19+
contents: write
20+
pull-requests: write
21+
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v4
25+
with:
26+
ref: trunk
27+
28+
- name: Extract version and changelog
29+
id: release_info
30+
env:
31+
RELEASE_TAG: ${{ github.event.release.tag_name }}
32+
RELEASE_BODY: ${{ github.event.release.body }}
33+
run: |
34+
# Extract version from tag (strip leading "v" if present).
35+
VERSION="${RELEASE_TAG#v}"
36+
if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
37+
echo "::error::Invalid version format: '$VERSION' (expected semver like '1.2.3')"
38+
exit 1
39+
fi
40+
echo "version=$VERSION" >> $GITHUB_OUTPUT
41+
42+
# Extract changelog, converting markdown list items to WP readme format.
43+
CHANGELOG=$(echo "$RELEASE_BODY" | sed 's/^- /* /' | sed 's/^[[:space:]]*- /* /')
44+
{
45+
echo "changelog<<CHANGELOG_EOF"
46+
echo "$CHANGELOG"
47+
echo "CHANGELOG_EOF"
48+
} >> $GITHUB_OUTPUT
49+
50+
# Branch name for the release PR.
51+
echo "branch=release/v$VERSION" >> $GITHUB_OUTPUT
52+
53+
- name: Create release branch
54+
env:
55+
BRANCH: ${{ steps.release_info.outputs.branch }}
56+
run: |
57+
git checkout -B "$BRANCH"
58+
59+
- name: Bump version numbers
60+
env:
61+
VERSION: ${{ steps.release_info.outputs.version }}
62+
run: |
63+
# Update version.php
64+
sed -i "s/define( 'SQLITE_DRIVER_VERSION', '.*' );/define( 'SQLITE_DRIVER_VERSION', '$VERSION' );/" \
65+
packages/mysql-on-sqlite/src/version.php
66+
67+
# Update plugin header
68+
sed -i "s/^\( \* Version:\).*/\1 $VERSION/" \
69+
packages/plugin-sqlite-database-integration/load.php
70+
71+
# Update readme.txt stable tag
72+
sed -i "s/^Stable tag:.*/Stable tag: $VERSION/" \
73+
packages/plugin-sqlite-database-integration/readme.txt
74+
75+
- name: Update changelog in readme.txt
76+
env:
77+
VERSION: ${{ steps.release_info.outputs.version }}
78+
CHANGELOG: ${{ steps.release_info.outputs.changelog }}
79+
run: |
80+
README="packages/plugin-sqlite-database-integration/readme.txt"
81+
82+
# Build the new changelog entry.
83+
ENTRY=$(printf "= %s =\n\n%s\n" "$VERSION" "$CHANGELOG")
84+
85+
if grep -q "^== Changelog ==" "$README"; then
86+
# Insert the new entry after the == Changelog == header.
87+
awk -v entry="$ENTRY" '
88+
/^== Changelog ==/ { print; print ""; print entry; next }
89+
{ print }
90+
' "$README" > "$README.tmp" && mv "$README.tmp" "$README"
91+
else
92+
# Add a changelog section at the end.
93+
printf "\n== Changelog ==\n\n%s\n" "$ENTRY" >> "$README"
94+
fi
95+
96+
- name: Commit version bump
97+
env:
98+
VERSION: ${{ steps.release_info.outputs.version }}
99+
BRANCH: ${{ steps.release_info.outputs.branch }}
100+
run: |
101+
git config user.name "github-actions[bot]"
102+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
103+
git add -A
104+
git commit -m "Bump version to $VERSION"
105+
git push -f origin "$BRANCH"
106+
107+
- name: Build plugin zip
108+
run: composer run build
109+
110+
- name: Upload zip to draft release
111+
env:
112+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
113+
RELEASE_TAG: ${{ github.event.release.tag_name }}
114+
run: |
115+
# Remove any previously uploaded zip.
116+
gh release delete-asset "$RELEASE_TAG" "sqlite-database-integration.zip" --yes 2>/dev/null || true
117+
# Upload the new zip.
118+
gh release upload "$RELEASE_TAG" "build/sqlite-database-integration.zip"
119+
120+
- name: Create or update pull request
121+
id: pr
122+
env:
123+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
124+
VERSION: ${{ steps.release_info.outputs.version }}
125+
BRANCH: ${{ steps.release_info.outputs.branch }}
126+
run: |
127+
# Check if a PR already exists for this branch.
128+
EXISTING_PR=$(gh pr list --head "$BRANCH" --base trunk --json number --jq '.[0].number // empty')
129+
130+
if [ -n "$EXISTING_PR" ]; then
131+
PR_URL=$(gh pr view "$EXISTING_PR" --json url --jq '.url')
132+
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
133+
else
134+
PR_URL=$(gh pr create \
135+
--base trunk \
136+
--head "$BRANCH" \
137+
--title "Release $VERSION" \
138+
--body "Version bump and changelog update for release $VERSION.")
139+
echo "pr_url=$PR_URL" >> $GITHUB_OUTPUT
140+
fi
141+
142+
- name: Add PR link to draft release
143+
env:
144+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
145+
RELEASE_TAG: ${{ github.event.release.tag_name }}
146+
RELEASE_BODY: ${{ github.event.release.body }}
147+
PR_URL: ${{ steps.pr.outputs.pr_url }}
148+
run: |
149+
PR_NOTE="> To publish the release, review and merge: $PR_URL"
150+
151+
# Remove any existing PR note, then append the new one.
152+
CLEAN_BODY=$(echo "$RELEASE_BODY" | grep -v "^> To publish the release, review and merge:")
153+
NEW_BODY=$(printf "%s\n\n%s" "$CLEAN_BODY" "$PR_NOTE")
154+
155+
gh release edit "$RELEASE_TAG" --notes "$NEW_BODY"
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
name: Publish release
2+
3+
on:
4+
pull_request:
5+
types: [closed]
6+
branches: [trunk]
7+
8+
jobs:
9+
publish-release:
10+
name: Publish draft release
11+
if: >-
12+
github.repository == 'WordPress/sqlite-database-integration'
13+
&& github.event.pull_request.merged == true
14+
&& startsWith(github.event.pull_request.head.ref, 'release/')
15+
runs-on: ubuntu-latest
16+
permissions:
17+
contents: write
18+
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
23+
- name: Extract version from branch name
24+
id: version
25+
env:
26+
BRANCH: ${{ github.event.pull_request.head.ref }}
27+
run: |
28+
# Extract version from branch name (release/v2.3.0 → 2.3.0).
29+
VERSION="${BRANCH#release/v}"
30+
echo "version=$VERSION" >> $GITHUB_OUTPUT
31+
echo "tag=v$VERSION" >> $GITHUB_OUTPUT
32+
33+
- name: Publish draft release
34+
env:
35+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
36+
TAG: ${{ steps.version.outputs.tag }}
37+
MERGE_SHA: ${{ github.event.pull_request.merge_commit_sha }}
38+
run: |
39+
# Get the current release body and remove the PR note.
40+
BODY=$(gh release view "$TAG" --json body --jq '.body' 2>/dev/null || echo "")
41+
CLEAN_BODY=$(echo "$BODY" | grep -v "^> To publish the release, review and merge:")
42+
43+
# Publish the release, targeting the merge commit.
44+
gh release edit "$TAG" \
45+
--draft=false \
46+
--target "$MERGE_SHA" \
47+
--notes "$CLEAN_BODY"
48+
49+
- name: Delete release branch
50+
env:
51+
BRANCH: ${{ github.event.pull_request.head.ref }}
52+
run: git push origin --delete "$BRANCH" 2>/dev/null || true
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Deploy to WordPress.org
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
concurrency:
8+
group: release-wporg-${{ github.event.release.tag_name }}
9+
cancel-in-progress: false
10+
11+
jobs:
12+
deploy:
13+
name: Deploy plugin to WordPress.org
14+
if: github.repository == 'WordPress/sqlite-database-integration'
15+
runs-on: ubuntu-latest
16+
environment: WordPress.org
17+
permissions:
18+
contents: read
19+
20+
steps:
21+
- name: Checkout repository
22+
uses: actions/checkout@v4
23+
with:
24+
sparse-checkout: bin/
25+
sparse-checkout-cone-mode: false
26+
27+
- name: Download plugin zip from release
28+
env:
29+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30+
RELEASE_TAG: ${{ github.event.release.tag_name }}
31+
run: |
32+
gh release download "$RELEASE_TAG" \
33+
--repo "${{ github.repository }}" \
34+
--pattern "sqlite-database-integration.zip" \
35+
--dir .
36+
unzip sqlite-database-integration.zip
37+
38+
- name: Verify release metadata
39+
env:
40+
RELEASE_TAG: ${{ github.event.release.tag_name }}
41+
run: |
42+
VERSION="${RELEASE_TAG#v}"
43+
bash bin/verify-release-metadata.sh sqlite-database-integration "$VERSION"
44+
45+
- name: Deploy to WordPress.org
46+
uses: 10up/action-wordpress-plugin-deploy@v2
47+
env:
48+
SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
49+
SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
50+
SLUG: sqlite-database-integration
51+
BUILD_DIR: sqlite-database-integration
Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,17 @@
1-
name: Verify plugin version
1+
name: Verify release metadata
22

33
on:
44
push:
55
branches:
6-
- main
6+
- trunk
77
pull_request:
88

99
jobs:
1010
verify-version:
11-
name: Assert the WordPress plugin header declares the same version as the SQLITE_DRIVER_VERSION constant
11+
name: Assert release metadata is internally consistent
1212
runs-on: ubuntu-latest
1313
steps:
1414
- uses: actions/checkout@v4
1515

16-
- name: Extract version from "load.php"
17-
id: load_version
18-
run: |
19-
VERSION=$(grep "Version:" load.php | sed "s/.*Version: \([^ ]*\).*/\1/")
20-
echo "load_version=$VERSION" >> $GITHUB_OUTPUT
21-
22-
- name: Extract version from "version.php"
23-
id: const_version
24-
run: |
25-
VERSION=$(php -r "require 'version.php'; echo SQLITE_DRIVER_VERSION;")
26-
echo "const_version=$VERSION" >> $GITHUB_OUTPUT
27-
28-
- name: Compare versions
29-
run: |
30-
if [ "${{ steps.load_version.outputs.load_version }}" != "${{ steps.const_version.outputs.const_version }}" ]; then
31-
echo "Version mismatch detected!"
32-
echo " load.php version: ${{ steps.load_version.outputs.load_version }}"
33-
echo " version.php constant: ${{ steps.const_version.outputs.const_version }}"
34-
exit 1
35-
fi
16+
- name: Verify release metadata
17+
run: bash bin/verify-release-metadata.sh packages/plugin-sqlite-database-integration

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ composer.lock
55
._.DS_Store
66
.DS_Store
77
._*
8+
/build
89
/wordpress
910
/.claude/settings.local.json

AGENTS.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,11 @@ It is a monorepo that includes the following components:
1919
- **WordPress plugin** — A plugin that adds SQLite support to WordPress.
2020
- **Test suites** — A set of extensive test suites to cover MySQL syntax and functionality.
2121

22+
The monorepo packages are placed under the `packages` directory.
23+
24+
The WordPress plugin links the SQLite driver using a symlink. The build script
25+
replaces the symlink with a copy of the driver for release.
26+
2227
The codebase is pure PHP with zero dependencies. It supports PHP 7.2 through 8.5,
2328
MySQL syntax from version 5.7 onward, and requires SQLite 3.37.0 or newer
2429
(with legacy mode down to 3.27.0).
@@ -40,8 +45,15 @@ The following commands are useful for development and testing:
4045
composer install # Install dependencies
4146
composer run check-cs # Check coding standards (PHPCS)
4247
composer run fix-cs # Auto-fix coding standards (PHPCBF)
48+
composer run build # Build the plugin zip
4349

44-
# Tests
50+
# SQLite driver tests (under packages/mysql-on-sqlite)
51+
cd packages/mysql-on-sqlite
52+
composer run test # Run unit tests
53+
composer run test tests/SomeTest.php # Run specific unit test file
54+
composer run test -- --filter testName # Run specific unit test class/method
55+
56+
# SQLite Database Integration plugin tests
4557
composer run test # Run unit tests
4658
composer run test tests/SomeTest.php # Run specific unit test file
4759
composer run test -- --filter testName # Run specific unit test class/method
@@ -56,6 +68,20 @@ composer run wp-test-e2e # Run WordPress E2E tests (Playwright)
5668
composer run wp-test-clean # Clean up WordPress environment (Docker and DB)
5769
```
5870

71+
## Release workflow
72+
Release is automated with GitHub Actions. It requires only two manual steps:
73+
74+
1. **Create a draft release with a new tag and changelog.**
75+
The `release-prepare` workflow will automatically:
76+
- Use the draft tag to bump versions in `version.php`, `load.php`, and `readme.txt`.
77+
- Add the draft release changelog entry to `readme.txt`.
78+
- Build the plugin ZIP and attach it to the draft release.
79+
- Create a PR (`release/<version>``trunk`) and link it in the draft release body.
80+
2. **Review and merge the PR.**
81+
The `release-publish` workflow will automatically:
82+
- Publish the draft release.
83+
- Publish the release artifact to WordPress.org.
84+
5985
## Architecture
6086
The project consists of multiple components providing different APIs that funnel
6187
into the SQLite driver to support diverse use cases both inside and outside the

0 commit comments

Comments
 (0)