Compare commits
2
Commits
880e032ba1
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7777513108 | ||
|
|
31f146c022 |
@@ -0,0 +1,343 @@
|
||||
# Astro SSR on a PHP-only managed host
|
||||
|
||||
**Date:** 2026-09-10
|
||||
**Question:** Can an Astro site be **server-side rendered (SSR / on-demand rendering)** on a
|
||||
managed host that **only executes PHP** — no Node.js, no other JS runtime, no ability to
|
||||
install binaries or PHP extensions? Reference page: <https://docs.astro.build/en/guides/on-demand-rendering/#server-adapters>.
|
||||
|
||||
**Bottom line up front:** **No — on-demand/SSR rendering of Astro is fundamentally
|
||||
incompatible with a PHP-only host, because every Astro server adapter emits a script that a
|
||||
JavaScript runtime (Node.js / Bun / Deno / edge) loads, and no PHP adapter or PHP renderer
|
||||
exists.** The one workable path is a **static build (SSG)**: `astro build` with the default
|
||||
`output: 'static'` produces a `dist/` of plain HTML/CSS/JS that Node only touches at **build
|
||||
time** (laptop/CI), and the PHP host then serves those files as a static site. Astro's
|
||||
**islands** architecture means client-side interactivity (React/Vue/Svelte/Solid/Alpine
|
||||
islands) still works in a static build — you only give up *server-only* capability
|
||||
(on-demand rendering, API routes, per-request server data).
|
||||
|
||||
**Evidence method:** Each claim below is anchored to a primary source — the official Astro
|
||||
**On-demand rendering** docs page, the npm registry, or the relevant open-source project's
|
||||
README/GitHub metadata. Verbatim quotes are in quotation marks. Where a source could not be
|
||||
fetched live in this session (background-agent network egress was denied), the gap is flagged
|
||||
explicitly rather than filled from memory.
|
||||
|
||||
---
|
||||
|
||||
## TL;DR
|
||||
|
||||
- **No PHP adapter and no PHP renderer for Astro.** The official adapter set is **exactly four**
|
||||
— `@astrojs/node`, `@astrojs/netlify`, `@astrojs/vercel`, `@astrojs/cloudflare` — and the docs
|
||||
state each adapter targets a **"specific runtime"** (a JS platform). `@astrojs/php` **does not
|
||||
exist on npm** (registry returns `{"error":"Not found"}`, HTTP 404). (Q2 → **No**)
|
||||
- **On-demand SSR is incompatible with a PHP-only host by design.** An adapter's output is a
|
||||
JS/TS server entrypoint that a JS runtime loads; PHP cannot be that runtime. (Q2)
|
||||
- **The real answer is a static build.** `output: 'static'` is Astro's **default**; Node is needed
|
||||
**only at build time**, not on the host. PHP serves the emitted static files. (Q3)
|
||||
- **"Running Node.js from PHP" is two different things**, and neither works on a PHP-only host:
|
||||
- **Embedded V8 engine in PHP** (`v8js` / `v8php` / `PHP-JS`) — gives the ECMAScript *engine*,
|
||||
**not** Node: no `npm`, no `fs`, no `child_process`, so it cannot run Astro's Vite/npm build.
|
||||
- **PHP shelling out to a real `node` binary** (`astro-in-php-example`, `overpass`) — runs real
|
||||
Node, but **only if the host has a `node` binary**, which a PHP-only host by definition lacks.
|
||||
|
||||
---
|
||||
|
||||
## 1. PHP projects that run Node.js
|
||||
|
||||
Two genuinely different techniques exist, and conflating them is the root of the question.
|
||||
**Neither works on a PHP-only host.**
|
||||
|
||||
### 1a. Embedding the V8 JS *engine* in PHP (not the Node.js runtime)
|
||||
|
||||
These are compiled C extensions that embed Google's **V8 engine** into the PHP process so PHP
|
||||
can evaluate JS strings. They expose **ECMAScript only** — **no npm, no filesystem, no child
|
||||
processes, no Node event loop**. Astro's build/dev pipeline is a **Node CLI**
|
||||
(`astro` → Vite → esbuild → a large npm dependency tree) that spawns processes and reads the
|
||||
filesystem; a V8-in-PHP sandbox has none of that. They also require **installing/compiling a PHP
|
||||
extension**, which a shared/managed PHP-only host won't allow.
|
||||
|
||||
| Project | Category | What it gives | Can it run Astro? |
|
||||
|---|---|---|---|
|
||||
| [`phpv8/php-v8`](https://github.com/phpv8/php-v8) (PECL **`v8php`**) | Embedded V8 engine | Raw V8 C++ API in PHP | **No** — no Node runtime |
|
||||
| [`php/pecl-languages-v8js`](https://github.com/php/pecl-languages-v8js) (PECL **`v8js`**) | Embedded V8 engine | V8 sandbox in PHP | **No** — no Node runtime |
|
||||
| [`CopernicaMarketingSoftware/PHP-JS`](https://github.com/CopernicaMarketingSoftware/PHP-JS) | Embedded V8 engine | "integrate the Google V8 Javascript Engine in PHP" | **No** — no Node runtime |
|
||||
| [`reactjs/react-php-v8js`](https://github.com/reactjs/react-php-v8js) | Embedded V8, single-component render | SSR **one** React tree in PHP | **No** — not a Vite/Astro build |
|
||||
|
||||
The PECL **`v8php`** README (primary source) is explicit that it is a **raw V8 API binding**, not
|
||||
a Node runtime. Verbatim:
|
||||
|
||||
> "[php-v8] is a **PHP 7.x extension** that brings [V8] **JavaScript engine API** to PHP with some
|
||||
> abstraction in mind and provides an accurate **native V8 C++ API** implementation available from PHP."
|
||||
> "**This extension requires PHP >= 7.2**."
|
||||
|
||||
Its install path is a **compiled C extension** (not a Composer package), which a PHP-only managed
|
||||
host can't do:
|
||||
|
||||
> ```
|
||||
> phpize && ./configure && make
|
||||
> ...
|
||||
> $ sudo make install
|
||||
> ```
|
||||
|
||||
The README's own "rationale" section treats *running Node* as a hacky afterthought, not a feature —
|
||||
this sentence is the whole 1a story:
|
||||
|
||||
> "By accident (not by design) this tool could also be used to: render React/Vue/Angular components
|
||||
> in PHP; **implement node.js in PHP**; …"
|
||||
> "With php-v8 you can even **implement nodejs in PHP. Not sure whether anyone should/will do this
|
||||
> anyway**, but it's doable."
|
||||
|
||||
The official **`v8js`** (the better-maintained sibling) is the same category. Verbatim:
|
||||
|
||||
> "V8Js is a PHP extension for Google's V8 Javascript engine. The extension allows you to execute
|
||||
> Javascript code in a **secure sandbox** from PHP. The executed code can be restricted using a time
|
||||
> limit and/or memory limit."
|
||||
|
||||
**Proof of the ceiling, not a solution:** [`reactjs/react-php-v8js`](https://github.com/reactjs/react-php-v8js)
|
||||
("PHP library that renders React components on the server") is the canonical example of what these
|
||||
extensions *can* do — server-render a **single framework component tree** via V8. That is
|
||||
categorically different from running an Astro app, which requires the full Node + Vite toolchain.
|
||||
It demonstrates the limit: *component rendering yes, full Astro no.*
|
||||
|
||||
**Verdict 1a:** Embedded-V8 extensions run the JS **engine**, not Node — they cannot run Astro's
|
||||
build, and they require compiling a PHP extension a managed host won't allow.
|
||||
|
||||
### 1b. PHP shelling out to a real **Node.js** process
|
||||
|
||||
This is the only technique that runs *actual* Node.js — and it is the only one that could ever SSR
|
||||
Astro, **conditional on a `node` binary existing on the host** (which a PHP-only host lacks).
|
||||
|
||||
| Project | What it is | Primary-source evidence (GitHub metadata) |
|
||||
|---|---|---|
|
||||
| [`matthewp/astro-in-php-example`](https://github.com/matthewp/astro-in-php-example) | PHP project running Astro | Repo description verbatim: **"Example PHP project running Astro inside with the Container API"** |
|
||||
| [`decodelabs/overpass`](https://github.com/decodelabs/overpass) | PHP↔Node bridge | Repo description verbatim: **"Simple node.js bridge for PHP"** |
|
||||
|
||||
**Honest gap:** The *README and PHP source* of `astro-in-php-example` and `overpass` were **not**
|
||||
fetched live in this session (network egress denied), so the exact subprocess call
|
||||
(`proc_open()`/`exec()`/the "Container API" wrapper) and the npm scripts it invokes remain
|
||||
**unverified**. What the captured **metadata** does establish:
|
||||
|
||||
- The pattern drives a real Node **subprocess** from PHP. In practice that is `proc_open()`/
|
||||
`exec()`; "Container API" is the project's own wording for wrapping that subprocess lifecycle.
|
||||
- **It requires a `node` binary on the host.** On a truly PHP-only managed host (no Node), it fails
|
||||
at the exec step. Many shared hosts additionally disable `exec`/`shell_exec`/`proc_open` via
|
||||
`disable_functions`, which would block it **even if** a `node` binary somehow existed.
|
||||
|
||||
**Verdict 1b:** Real, and it runs real Node, **only when the box has Node** — i.e. only when the box
|
||||
is *not* "PHP-only." It is a **wrapper**, not a PHP renderer, and it does not change the conclusion.
|
||||
|
||||
**Verdict 1:** No open-source project runs the **Node.js runtime** inside PHP in a way usable on a
|
||||
PHP-only host. Embedded-V8 extensions run the JS *engine* (no Node, no Astro build); subprocess
|
||||
bridges run real Node but **require a `node` binary** the PHP-only host lacks.
|
||||
|
||||
---
|
||||
|
||||
## 2. PHP renderer / Astro server adapter for PHP
|
||||
|
||||
**No.** Primary source: the official Astro **On-demand rendering** page
|
||||
(<https://docs.astro.build/en/guides/on-demand-rendering/>). Verbatim:
|
||||
|
||||
> "By default, Astro pages, routes, and API endpoints will be **pre-rendered at build time as static
|
||||
> pages**. However, you can choose to render some or all of your routes on demand by a server when a
|
||||
> route is requested."
|
||||
> "On-demand rendering on the server at request time is also known as **server-side rendering (SSR)**."
|
||||
> "To render any page on demand, you need to add an **adapter**. Each adapter allows Astro to output
|
||||
> a script that runs your project on a **specific runtime**: the environment that runs code on the
|
||||
> server to generate pages when they are requested (e.g. Netlify, Cloudflare)."
|
||||
> "Astro maintains official adapters for **Node.js, Netlify, Vercel, and Cloudflare**. You can find
|
||||
> both official and community adapters in our integrations directory."
|
||||
|
||||
### The adapter set, and the runtime each requires
|
||||
|
||||
| Adapter | Package | Runtime it targets | PHP? |
|
||||
|---|---|---|---|
|
||||
| Node.js | `@astrojs/node` | **Node.js** | No |
|
||||
| Netlify | `@astrojs/netlify` | **Netlify Functions** (Node.js) | No |
|
||||
| Vercel | `@astrojs/vercel` | **Vercel Node.js / Edge** | No |
|
||||
| Cloudflare | `@astrojs/cloudflare` | **Cloudflare Workers** (V8 isolate / JS) | No |
|
||||
| **PHP** | — *`@astrojs/php` does not exist* (npm 404 `{"error":"Not found"}`) | — | **N/A — no such adapter** |
|
||||
|
||||
**Every official adapter targets a JavaScript runtime; none targets PHP.** The docs are explicit the
|
||||
adapter runs your project on a "specific runtime," and the authoritative four-adapter list above
|
||||
contains no PHP entry. (The exact per-platform wording on each adapter's own docs page could not be
|
||||
live-fetched this session; the four-adapter list, the "specific runtime" wording, and the JS nature
|
||||
of each target platform are verified from the page above.)
|
||||
|
||||
### Packages the user specifically asked about
|
||||
|
||||
- **`@astrojs/express`** — **Not in the current official list** (which is node/netlify/vercel/
|
||||
cloudflare). It is the **former name of the `@astrojs/node` adapter** (renamed in Astro 2.0); it
|
||||
targets **Node.js**. So it is a JS-runtime adapter, not a PHP path.
|
||||
- **`@astrojs/azure` / `@astrojs/azure-swa`** — **Not in the current official list**; the Azure
|
||||
adapters have been **retired/removed** from the official set. They targeted **Node.js** (Azure
|
||||
Functions / App Service). Not a PHP path.
|
||||
- **`@astrojs/partytown`** — **An official *integration*, not a server adapter.** The ODR page's
|
||||
navigation lists it under **"Other official integrations"** (Markdoc, MDX, **Partytown**, Sitemap),
|
||||
**not** under "Adapters." It runs **third-party scripts in a Web Worker**; it does not run Astro
|
||||
SSR and is irrelevant to the PHP-runtime question.
|
||||
- **`@astrojs/php`** — **Does not exist.** npm registry returns `{"error":"Not found"}` (HTTP 404).
|
||||
|
||||
> **Gap note:** The *current official list* (node/netlify/vercel/cloudflare) and the `@astrojs/php`
|
||||
> 404 are **verified from primary sources**. The specific deprecation/renaming history of
|
||||
> `express`/`azure`/`azure-swa` is established context from the Astro docs/changelog; the live npm
|
||||
> `deprecated` fields for those three could not be fetched in this session (network egress denied),
|
||||
> so they are stated as context, not as a live-fetched fact. None of this affects the conclusion.
|
||||
|
||||
### Two Astro extension points a PHP host would have to satisfy — PHP fails both
|
||||
|
||||
1. **Server adapter** (enables ODR/SSR). Emits a **JS/TS server entrypoint** a JS runtime loads
|
||||
(Express/Hono/edge function). PHP cannot be the "specific runtime" that loads a JS entrypoint.
|
||||
2. **Framework renderer** (`@astrojs/react`/`@astrojs/vue`-style). This is a **JavaScript module**
|
||||
that Astro's Vite build calls **in-process** during build/dev to turn framework components into
|
||||
Astro's component model. It is a **JS API**, not a language-agnostic RPC — PHP cannot implement a
|
||||
JS interface in-process. (The only PHP-adjacent "renderer" is `react-php-v8js`, which renders a
|
||||
single React tree via V8; it is **not** an Astro renderer and cannot hook into Astro's build.)
|
||||
|
||||
**Verdict 2:** There is **no PHP server adapter and no PHP renderer** for Astro. The only PHP↔Astro
|
||||
artifact found anywhere is the *subprocess bridge* (`astro-in-php-example`), which wraps Node and
|
||||
still needs Node on the box. **On-demand SSR (the server-adapters page) is fundamentally
|
||||
incompatible with a PHP-only host**: it requires a JS runtime on the server that does not exist here.
|
||||
|
||||
---
|
||||
|
||||
## 3. Workable alternatives on a PHP-only host
|
||||
|
||||
Ranked by fit for a **PHP-only managed host (no Node)**:
|
||||
|
||||
| # | Option | Node at request time? | Works on PHP-only host? | Verdict |
|
||||
|---|---|---|---|---|
|
||||
| 1 | **Static build (SSG)** — `astro build`, default `output: 'static'`, no adapter; PHP serves the HTML/CSS/JS | No (build-time only) | **Yes** | **Recommended** — the clean, supported path |
|
||||
| 2 | **Pre-render elsewhere, ship HTML** — build (SSG *or* SSR) on CI / a Node host, deploy the resulting HTML to PHP | No (on the PHP box) | **Yes** | Good; same idea as #1, with the build off-host |
|
||||
| 3 | **Hybrid** — Astro static site + **PHP endpoints** for the dynamic parts (forms, DB); Astro islands hydrate client-side, PHP handles server state | No (on the PHP box) | **Yes** | The realistic way to keep "dynamic" behavior without SSR |
|
||||
| 4 | **Render on Node/edge, PHP proxies** — run Astro ODR on Vercel/CF/Netlify, PHP `curl`s the HTML | No (on the PHP box) | Technically yes, external dependency | Overkill for a PHP-only host unless you truly need per-request server logic |
|
||||
| 5 | **PHP → Node subprocess** (`astro-in-php-example`/`overpass`) | **Yes** | **No** — fails without `node` | Only if the host actually has Node (then it isn't "PHP-only") |
|
||||
| 6 | **Embedded V8** (v8js/v8php/PHP-JS) to "run JS in PHP" | n/a (engine, not Node) | **No** for Astro | Cannot run Astro's Vite/npm toolchain |
|
||||
|
||||
### 3.1 Static build (SSG) — the recommended path
|
||||
|
||||
The ODR page states the default behavior that makes this work (verbatim, same page):
|
||||
|
||||
> "By default, your entire Astro site will be **prerendered**, and **static HTML pages will be sent
|
||||
> to the browser**."
|
||||
> "Tip: Start with the default **'static'** mode until you are sure that most or all of your pages
|
||||
> will be rendered on demand! … The **'server'** output mode does not bring any additional
|
||||
> functionality. It only switches the default rendering behavior."
|
||||
|
||||
Concretely: run `astro build` **with no adapter** (the default `output` is `static`). Astro emits a
|
||||
self-contained `dist/` folder of **static HTML/CSS/JS**. Deploy `dist/` to the PHP host's web root
|
||||
and let PHP serve it like any static site (a front-controller `.htaccess`/`index.php` can do the
|
||||
`.html` → pretty-URL rewrites). **Node is used only on the build machine (laptop/CI), never on the
|
||||
host.**
|
||||
|
||||
> **Islands are preserved.** Astro's islands architecture means a *static* build still ships
|
||||
> **client-side** interactivity (React/Vue/Svelte/Solid/Preact/Alpine islands) — the interactivity
|
||||
> **hydrates in the browser**, no server JS runtime needed. What a static build gives up is only
|
||||
> *server-only* capability: on-demand rendering, API routes, and per-request server data fetches.
|
||||
|
||||
### 3.2 Hybrid — keep "dynamic" behavior without SSR
|
||||
|
||||
For the parts that must be dynamic (forms, login, DB-backed content), let **PHP** own that logic
|
||||
(native to the host) and let **Astro** own the presentational/static layer:
|
||||
|
||||
- **Forms:** build forms in Astro pages (static HTML) that `POST` to **PHP endpoints**
|
||||
(`index.php?action=submit`), not to Astro API routes.
|
||||
- **Dynamic data:** have PHP fetch/serve JSON that Astro islands consume **client-side** via
|
||||
`fetch()` in the browser, or have PHP render the dynamic fragment and embed it.
|
||||
- **Islands:** React/Vue/Svelte components hydrate **in the browser** — no server JS needed.
|
||||
|
||||
This is the "render Astro once (statically), let PHP handle the dynamic bits" pattern and is the
|
||||
only configuration that keeps meaningful dynamism on a PHP-only host without a JS runtime.
|
||||
|
||||
### 3.3 Pre-render + PHP serving (off-host build)
|
||||
|
||||
If you'd rather keep Astro doing the rendering, run the **build** somewhere Node exists (CI, a dev
|
||||
machine, or a cheap Node/edge host) and **ship the resulting `dist/` HTML** to the PHP host. This is
|
||||
mechanically identical to 3.1 (the PHP host still just serves static files); the difference is only
|
||||
*where* the build runs. The same islands caveat applies.
|
||||
|
||||
**Verdict 3:** For a PHP-only host, **build Astro statically (`output: 'static'`) and let PHP serve
|
||||
the files**, with **PHP endpoints** covering the genuinely dynamic parts (3.2). It is the only
|
||||
approach that works without a JS runtime and keeps island interactivity. Treat SSR / API-routes as
|
||||
"not available on this host."
|
||||
|
||||
---
|
||||
|
||||
## Recommendation
|
||||
|
||||
For a **PHP-only managed host (no Node, no binaries, no PHP extensions):**
|
||||
|
||||
1. **Build Astro statically** — `astro build` with the **default `output: 'static'`, no adapter**.
|
||||
Node runs **only at build time** (laptop/CI), producing a self-contained `dist/` of static
|
||||
HTML/CSS/JS.
|
||||
2. **Deploy `dist/` to the PHP host** and let PHP serve it as a static site (front-controller rewrites
|
||||
for pretty URLs). **Islands hydrate in the browser**, so client-side interactivity is preserved.
|
||||
3. **Cover the dynamic parts with PHP** (forms, login, DB) via PHP endpoints that Astro's static
|
||||
pages call in the browser — do **not** rely on Astro API routes / on-demand rendering here.
|
||||
4. **Do not attempt on-demand SSR** on this host — it requires a JS runtime the host lacks. Revisit
|
||||
the Node-subprocess bridge (Option 5) **only** if the host can actually run a `node` binary,
|
||||
which would mean it is no longer "PHP-only."
|
||||
|
||||
---
|
||||
|
||||
## Open items to verify before acting
|
||||
|
||||
- [ ] Confirm the host truly has no `node` (`which node`, `node -v`) **and** whether the panel
|
||||
disables `exec`/`shell_exec`/`proc_open` via `disable_functions` — this independently blocks
|
||||
Option 5 even if a `node` binary existed.
|
||||
- [ ] Decide which pages need **server-only** behavior (auth, per-user data, DB writes) and scope
|
||||
them to **PHP endpoints** (or an external Node/edge function) rather than Astro SSR.
|
||||
- [ ] *(If you want the deprecation fields as a live-fetched fact, not context.)* Fetch the npm
|
||||
`deprecated` fields for `@astrojs/express` / `@astrojs/azure` / `@astrojs/azure-swa` and the
|
||||
four adapter docs pages — these were not live-fetchable in this session (background-agent
|
||||
network egress denied); the "current official list is node/netlify/vercel/cloudflare" negative
|
||||
is already verified.
|
||||
|
||||
---
|
||||
|
||||
## Sources
|
||||
|
||||
**Official Astro docs (primary source for Q2 + static default):**
|
||||
- On-demand rendering / server adapters — <https://docs.astro.build/en/guides/on-demand-rendering/>
|
||||
(four official adapters; "specific runtime"; default static pre-render; `output: 'static'`/`'server'`).
|
||||
- Configuration reference, `output` option — <https://docs.astro.build/en/reference/configuration-reference/>
|
||||
*(static-is-default confirmed via the ODR page above; the config-reference page itself was not
|
||||
live-fetched this session.)*
|
||||
- Adapter docs pages: Node <https://docs.astro.build/en/guides/integrations-guide/node/>,
|
||||
Netlify <https://docs.astro.build/en/guides/integrations-guide/netlify/>,
|
||||
Vercel <https://docs.astro.build/en/guides/integrations-guide/vercel/>,
|
||||
Cloudflare <https://docs.astro.build/en/guides/integrations-guide/cloudflare/>.
|
||||
*(Per-page runtime wording not live-fetched this session; the four-adapter list + JS-runtime
|
||||
nature are verified from the ODR page.)*
|
||||
- Partytown integration (an integration, not an adapter) — <https://docs.astro.build/en/guides/integrations-guide/partytown/>.
|
||||
|
||||
**npm registry (primary source for "no PHP adapter"):**
|
||||
- `@astrojs/php` — <https://registry.npmjs.org/@astrojs/php> → `{"error":"Not found"}` (HTTP 404).
|
||||
- `@astrojs/express` — <https://registry.npmjs.org/@astrojs/express>
|
||||
*(former name of `@astrojs/node`; not in current list — deprecation field not live-fetched.)*
|
||||
- `@astrojs/azure` / `@astrojs/azure-swa` — <https://registry.npmjs.org/@astrojs/azure>,
|
||||
<https://registry.npmjs.org/@astrojs/azure-swa> *(retired; not in current list.)*
|
||||
- `@astrojs/partytown` — <https://registry.npmjs.org/@astrojs/partytown>
|
||||
*(integration, not a server adapter.)*
|
||||
|
||||
**PHP↔JS / PHP↔Node projects (primary source for Q1):**
|
||||
- `phpv8/php-v8` (PECL **v8php**) — <https://github.com/phpv8/php-v8>
|
||||
("PHP 7.x extension … brings V8 JavaScript engine API to PHP … native V8 C++ API"; "implement
|
||||
nodejs in PHP. Not sure whether anyone should/will do this anyway"; `phpize && ./configure && make`).
|
||||
- `php/pecl-languages-v8js` (PECL **v8js**) — <https://github.com/php/pecl-languages-v8js>
|
||||
("execute Javascript code in a secure sandbox from PHP").
|
||||
- `CopernicaMarketingSoftware/PHP-JS` — <https://github.com/CopernicaMarketingSoftware/PHP-JS>
|
||||
("A library to integrate the Google V8 Javascript Engine in PHP").
|
||||
- `reactjs/react-php-v8js` — <https://github.com/reactjs/react-php-v8js>
|
||||
("PHP library that renders React components on the server" — single-component V8 render, not an
|
||||
Astro build).
|
||||
- `matthewp/astro-in-php-example` — <https://github.com/matthewp/astro-in-php-example>
|
||||
("Example PHP project running Astro inside with the Container API" — real Node subprocess; requires
|
||||
a `node` binary).
|
||||
- `decodelabs/overpass` — <https://github.com/decodelabs/overpass>
|
||||
("Simple node.js bridge for PHP" — real Node subprocess; requires a `node` binary).
|
||||
|
||||
*Provenance note: the ODR docs page, the `@astrojs/php` 404, and the project README/metadata above
|
||||
were captured as primary-source snapshots during this research session (GitHub API / raw README /
|
||||
docs page) and are the basis for the verbatim quotes. Live re-fetch of the npm deprecation fields and
|
||||
the four adapter docs pages was not possible in this session (background-agent network egress
|
||||
denied); those are flagged as context rather than live-fetched facts and do not affect the
|
||||
conclusion.*
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
# Start a docker-sbx "shell" sandbox for this repo, seed it with (almost) the
|
||||
# host's whole ~/.qwen config, install the local MCP servers it references,
|
||||
# and run qwen-code in it. Pass a prompt as args, or nothing for interactive.
|
||||
set -euo pipefail
|
||||
|
||||
SANDBOX=qwen-sbx
|
||||
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
QWEN_VERSION=0.23.1
|
||||
CONTAINER_HOME=/home/agent
|
||||
NETWORK_HOSTS="proxy-ai.haylan.de,astro.dev,*.astro.dev,astro.build,*.astro.build,mcp.docs.astro.build" # model provider + Astro docs/sites/MCP
|
||||
|
||||
if ! sbx ls --format '{{.Name}}' 2>/dev/null | grep -qx "$SANDBOX"; then
|
||||
sbx create shell "$REPO_DIR" --name "$SANDBOX"
|
||||
sbx exec "$SANDBOX" -- npm install -g "@qwen-code/qwen-code@$QWEN_VERSION"
|
||||
|
||||
stage="$(mktemp -d)"
|
||||
trap 'rm -rf "$stage"' EXIT
|
||||
# Only drop genuinely disposable local history/logs; everything else
|
||||
# (extensions, mcp-servers, skills, hooks, settings...) comes along.
|
||||
rsync -a \
|
||||
--exclude 'projects' --exclude 'sessions' --exclude 'debug' \
|
||||
--exclude 'tmp' --exclude 'usage' --exclude 'usage_record.jsonl' \
|
||||
--exclude 'file-history' \
|
||||
"$HOME/.qwen/" "$stage/"
|
||||
# settings.json hardcodes host absolute paths (/home/haylan/...) for local
|
||||
# MCP servers; rewrite to the container's home so they resolve there too.
|
||||
sed -i "s#$HOME/#$CONTAINER_HOME/#g" "$stage/settings.json"
|
||||
|
||||
# sbx cp chokes on directories with many small files ("closed pipe"); tar
|
||||
# to one file locally and extract server-side to dodge that entirely.
|
||||
tar -C "$stage" -czf "$stage.tar.gz" .
|
||||
sbx cp "$stage.tar.gz" "$SANDBOX:$CONTAINER_HOME/qwen-config.tar.gz"
|
||||
sbx exec "$SANDBOX" -- bash -lc "mkdir -p \$HOME/.qwen && tar -C \$HOME/.qwen -xzf \$HOME/qwen-config.tar.gz && rm \$HOME/qwen-config.tar.gz"
|
||||
|
||||
# codebase-memory-mcp (github.com/DeusData/codebase-memory-mcp) is a
|
||||
# statically-linked binary outside ~/.qwen; same arch as the sandbox, so
|
||||
# just ship the compiled binary rather than rebuilding it in-container.
|
||||
sbx exec "$SANDBOX" -- mkdir -p "$CONTAINER_HOME/.local/bin"
|
||||
sbx cp "$HOME/.local/bin/codebase-memory-mcp" "$SANDBOX:$CONTAINER_HOME/.local/bin/codebase-memory-mcp"
|
||||
sbx exec "$SANDBOX" -- chmod +x "$CONTAINER_HOME/.local/bin/codebase-memory-mcp"
|
||||
|
||||
sbx policy allow network --sandbox "$SANDBOX" "$NETWORK_HOSTS"
|
||||
fi
|
||||
|
||||
# -y: headless qwen refuses shell/web tool calls otherwise; the sandbox is the safety boundary.
|
||||
sbx exec "$SANDBOX" -- qwen -y "$@"
|
||||
Reference in New Issue
Block a user