Code Builder self-updates
A wordpress plugin that used to serve, deploy hardcoded codes from github and share domain and server with the existing wordpress site.
Install
The author publishes release zips, so WP-CLI can install straight from GitHub:
wp plugin install https://github.com/jeydweirdough/code-builder/releases/download/v1.1.0/code-builder-1.1.0.zipShips its own WordPress updater (built-in updater), so new versions show up under Dashboard → Updates.
Readme
Code Builder
Deploys Git repositories on your own WordPress server. It clones the repo to
disk, runs its install and build commands, and serves the build output as real
files at /app/{project}/ — with per-project access control, live build logs,
and one-click rollback across the last 5 builds.
Because the output is real files on disk, everything that normally breaks when
a site is reassembled from a database works here: relative paths, @font-face
URLs, video files, source maps, and bundler-hashed filenames like
index-BaR9xQ7z.js.
A project can also take over the site root
instead of living at /app/, be built in CI and posted here
when the server can't build it, and keep its
rollback history in cloud storage when disk
is tight. Projects → System Health tells you what your server actually
supports.
Filling in the Build settings
This is the part most people get stuck on, so start here.
You usually don't have to fill anything in. Leave Install Command, Build
Command and Output Directory blank and hit Deploy. Code Builder reads your
repository — its package.json, lockfile and config files — works out the right
values, uses them, and then writes them back into the form so you can see
exactly what it decided and adjust from there.
Fill a field in yourself only when you want to override that decision. A value you type always wins; a blank field means "figure it out".
The three fields
| Field | What it is | Blank means |
|---|---|---|
| Install Command | Fetches dependencies. Runs first. | Detected from your lockfile |
| Build Command | Compiles the site. Runs second. | Detected from your package.json scripts |
| Output Directory | The folder that gets published | Auto-probed after the build |
All three are optional. A repository of hand-written HTML needs none of them.
What gets detected, by project type
Find your project in the left column. The three right-hand columns are what Code Builder fills in — and what you should enter if you'd rather set it manually.
| Your repository has… | Install Command | Build Command | Output Directory |
|---|---|---|---|
vite.config.js / .ts |
npm install |
npm run build |
dist |
| Vite + React | npm install |
npm run build |
dist |
| Vite + Vue | npm install |
npm run build |
dist |
next.config.js or a next dependency |
npm install |
npm run build |
out |
angular.json |
npm install |
npm run build |
dist (see note) |
nuxt.config.ts or a nuxt dependency |
npm install |
npm run build |
dist |
react-scripts (Create React App) |
npm install |
npm run build |
build |
| React without a bundler config | npm install |
npm run build |
build |
tailwind.config.js, or @tailwindcss/cli |
npm install |
npm run build:css |
(blank — repo root) |
composer.json only |
composer install --no-dev --optimize-autoloader |
(none) | (blank — repo root) |
index.php / header.php, no JS build |
(none) | (none) | (blank — repo root) |
Plain .html / .css / .js files |
(none) | (none) | (blank — repo root) |
npm install is shortened for readability in that table — the real detected
value carries flags (npm install --no-audit --no-fund), and a pnpm/yarn/bun
project gets its own tool instead. The next section gives the exact strings.
Angular and Nuxt nest one level deeper than everyone else —
dist/my-app/browser. You don't need to write that out. When the folder you point at contains exactly one folder and nothing else, Code Builder walks down into it automatically, up to three levels.
Which package manager gets used
Your lockfile decides, not your preference. Running npm install in a repo
with a pnpm-lock.yaml produces a different dependency tree than the one the
project's authors tested, and that difference is a real source of "works
locally, breaks on the server".
| Lockfile in the repo | Install Command used |
|---|---|
pnpm-lock.yaml |
pnpm install --frozen-lockfile |
bun.lockb or bun.lock |
bun install |
yarn.lock |
yarn install --frozen-lockfile |
package-lock.json |
npm install --no-audit --no-fund |
package.json, no lockfile |
npm install --no-audit --no-fund |
composer.json, no package.json |
composer install --no-dev --optimize-autoloader |
The build command uses the same tool: a pnpm project gets pnpm run build, not
npm run build.
Why
npm installand notnpm ci?npm ciis stricter and technically more correct, but it hard-fails whenpackage-lock.jsonandpackage.jsonhave drifted apart — which happens constantly in repos wherepackage.jsongets edited by hand. A deploy shouldn't need babysitting, so the more forgiving command is the default. Typenpm ciinto the field yourself if you want the strict behaviour.
Which npm script gets picked
Code Builder looks through your package.json scripts block in this order and
takes the first one that exists:
build → build:prod → build:production → build:css → export → generate
If none of those exist, no build command runs — which is correct for a repository that has nothing to compile.
That build:css entry is there for a specific and common case: a hand-written
HTML or PHP site whose only build step compiles a Tailwind stylesheet in place.
Such a repo typically has no build script at all, just:
{
"scripts": {
"build:css": "tailwindcss -i ./assets/input.css -o ./assets/tailwind.css --minify"
}
}
Nothing is bundled, so the output directory is the repository root — the site is the source tree, with one stylesheet freshly compiled inside it. Leave Output Directory blank for these.
How Output Directory is auto-probed
When you leave it blank, Code Builder looks for the first of these that exists and isn't empty, then descends into any single-folder nesting:
dist → build → out → _site → public
If none of them exist, it publishes the repository root. That is the right answer for static sites, PHP sites and Tailwind-only sites, and the wrong answer for a bundled app whose build failed to produce anything — which is why an empty result stops the deploy with an error rather than publishing a source tree.
Rules for the command fields
One command per field. No shell operators.
This is rejected:
npm install && npm run build ✗ no &&
npm run build; echo done ✗ no ;
npm run build | tee build.log ✗ no pipes
npm run build > output.txt ✗ no redirects
npm run $(cat which-script.txt) ✗ no command substitution
Chain steps inside a package.json script instead, and call that:
{
"scripts": {
"deploy": "npm run clean && npm run build:css && vite build"
}
}
…then set Build Command to npm run deploy. This is better anyway: the steps
live in your repo where they're version-controlled and reviewable, and the build
log attributes failures to the step that actually failed.
The executable must be one of these:
npm npx pnpm pnpx yarn bun bunx node
composer php git make hugo tailwindcss sass vite
A command that breaks either rule isn't saved, and the form tells you so rather than accepting it and silently skipping the step at deploy time.
Flags and arguments are fine — composer install --no-dev,
npx tailwindcss -i ./src/input.css -o ./dist/out.css --minify. Quoted
arguments with spaces in them are preserved.
The other project settings
Root Directory
For a monorepo: the subfolder the build runs in. If your site lives in
packages/web/, put that here, and Install/Build/Output are all resolved
relative to it. Blank means the repository root.
Framework
Cosmetic — it sets the badge shown on the project card. What actually determines the build is the three command fields above. It's filled in automatically on first deploy.
Who can view it
Checked on every request, including images, fonts and scripts.
| Setting | Who gets through |
|---|---|
| Public | Everyone |
| Logged-in users | Any logged-in WordPress user (others are sent to the login form) |
| Administrators only | Users with manage_options |
This is meaningful rather than decorative: build files are made unreadable
directly on disk (a deny rule is written into
wp-content/code-builder/), so /app/{project}/ is the only route to them.
SPA fallback
Turn this on for a client-routed app — React Router, Vue Router, TanStack
Router. Without it, loading /app/mysite/dashboard directly returns 404,
because there is no dashboard file on disk; with it, index.html is served
and your router takes over.
Requests that look like an asset (anything with an extension other than
.html) still 404 properly. Handing index.html to a missing .js request
would turn a clean 404 into a JavaScript syntax error, which is much harder to
debug.
Leave it off for static sites and multi-page sites — it would mask genuine broken links.
You may not need it at all: an extensionless URL is already matched against a
matching .html file, so /app/mysite/about finds about.html on its own.
That covers most static site generators without any fallback.
Auto-deploy
Deploys on every push to the tracked branch. Two mechanisms, and you get whichever works on your host:
- Deploy hook — copy the Payload URL and Secret from the project's Settings tab into your repository's Settings → Webhooks (content type: JSON). Instant.
- Hourly check — if GitHub can't reach your site (local, staging behind a VPN, IP-allowlisted), a scheduled check compares the branch's latest commit against the last one deployed and deploys only when it has changed.
Serving a project at the site root
By default a project lives at /app/{project}/. If you want it to be the
site — at example.com/ rather than example.com/app/mysite/ — go to
GitHub Defaults → Override Controls, pick the project, and tick what it
should take over.
There are two switches because they're useful separately.
Override Main Site (/)
The project becomes your site's front end. It answers / and every page URL
beneath it, and a URL the build has no file for returns the project's 404.
This means your WordPress pages, posts and archives stop being reachable at their URLs. That is the intended behaviour, not a side effect: two different answers for one URL is exactly what makes a migrated site feel broken. Nothing is deleted — unticking the box brings everything back immediately.
What is never taken over, so you can always get back in and turn it off:
/wp-admin/ /wp-login.php /wp-json/ admin-ajax.php
wp-cron.php xmlrpc.php /feed/ wp-sitemap.xml
robots.txt ?rest_route=… ?preview=… /app/{project}/
That last one matters: the project stays addressable at its own /app/ URL even
while it's serving the root, so you can always compare the two.
If the project has never been deployed, nothing is taken over. With no build on disk the override stands aside entirely and your site carries on as it was. The same applies if a build's files are later deleted — it fails open rather than blanking the site.
Override Assets (/assets/)
Serves static files — images, CSS, JS, fonts — from the build at root-level
paths. Useful on its own when you want project pages to stay at /app/{project}/
but their assets to resolve from /assets/….
One thing to know: "asset" is decided by file extension, and
.xmlcounts as one. If a plugin such as Yoast serves/sitemap.xml, it will 404 while this is ticked. WordPress's ownwp-sitemap.xmlis unaffected.
Deploying a build made somewhere else
Some repositories can't be built on the WordPress host at all — proc_open is
disabled, or the build needs more memory or a newer Node than the server has.
For those, build in CI and post the finished output here.
Endpoint — shown on the project's Settings tab:
POST /wp-json/code-builder/v1/deploy-artifact/{project-slug}
From GitHub Actions, or any CI:
cd dist && zip -qr ../build.zip . && cd ..
gzip -kc build.log > build.log.gz
curl -f -X POST "https://example.com/wp-json/code-builder/v1/deploy-artifact/mysite" \
-H "X-CPB-Token: $CODE_BUILDER_SECRET" \
-F "artifact=@build.zip" \
-F "log=@build.log.gz" \
-F "sha=$GITHUB_SHA"
$CODE_BUILDER_SECRET is the same webhook secret from the Settings tab. Only
artifact is required.
Everything after extraction is the normal pipeline, so an artifact deploy gets
the same release directory, the same rollback, the same pruning and the same
access control as a git deploy. It needs no git, no Node and no proc_open —
only PHP's zip extension.
The build log
Send your CI log as log and it's stored with the deployment and shown in the
Logs tab, so the record of how a build was produced lives next to the build.
Gzipped or plain text — either works. Compression is detected from the file's
own bytes rather than a header, because a CI job that gzips but can't set
Content-Encoding is common, and so is a header that claims gzip over plain
text. Only the last 2,000 lines (up to 500 KB) are kept; the end of a build
log is the part that explains it.
Read the full README on GitHub →
Releases
| Tag | Published | Asset | Downloads |
|---|---|---|---|
| v1.1.0 | Aug 6, 2026 | code-builder-1.1.0.zip | 0 |