mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-24 02:02:42 +00:00
* fix(android): restore development builds after Expo upgrade Port the native touch-cancellation patch to the gesture-handler API while retaining drawing order and surviving native gestures. Match the bundled Skia font scanner and Unicode paragraph construction APIs. * test(e2e): hide the developer error toast instead of clicking it away The locator handler that dismissed #error-toast could match a toast that closed before its click; dispatchEvent then waited for the gone button until the test timed out. Hiding the toast with CSS keeps it off app actions with no race. * test(e2e): desktop browser-tabs hides developer toasts on its attached page * test(cli): keep the Hub deploy fixture alive on malformed requests An empty request body crashed the HTTP fixture outside its assertions. Return HTTP 400 for invalid JSON and exercise that request before the full validation and installation assertions.
131 lines
4.3 KiB
TypeScript
131 lines
4.3 KiB
TypeScript
#!/usr/bin/env npx tsx
|
|
|
|
import assert from "node:assert/strict";
|
|
import { createServer } from "node:http";
|
|
import type { AddressInfo } from "node:net";
|
|
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
|
|
import { tmpdir } from "node:os";
|
|
import path from "node:path";
|
|
import { runLocalPaseo } from "./helpers/local-cli.js";
|
|
|
|
const cwd = await mkdtemp(path.join(tmpdir(), "paseo-hub-installed-"));
|
|
const requests: Array<{ url: string | undefined; body: unknown }> = [];
|
|
const server = createServer((request, response) => {
|
|
let body = "";
|
|
request.setEncoding("utf8");
|
|
request.on("data", (chunk: string) => {
|
|
body += chunk;
|
|
});
|
|
request.on("end", () => {
|
|
let parsed: unknown;
|
|
try {
|
|
parsed = JSON.parse(body);
|
|
} catch {
|
|
response.writeHead(400);
|
|
response.end();
|
|
return;
|
|
}
|
|
requests.push({ url: request.url, body: parsed });
|
|
const validation = request.url?.endsWith("/validate") === true;
|
|
response.writeHead(validation ? 200 : 201, { "content-type": "application/json" });
|
|
response.end(
|
|
JSON.stringify(
|
|
validation
|
|
? { projectSlug: "studio", valid: true }
|
|
: {
|
|
projectSlug: "studio",
|
|
version: 1,
|
|
versionId: "2de5b143-1c88-42db-8a8d-71ca2af97830",
|
|
active: true,
|
|
},
|
|
),
|
|
);
|
|
});
|
|
});
|
|
|
|
try {
|
|
const workflows = path.join(cwd, ".paseo", "workflows");
|
|
await mkdir(path.join(workflows, "partials"), { recursive: true });
|
|
const files = [
|
|
{
|
|
path: ".paseo/hub.yml",
|
|
content:
|
|
"environments:\n studio:\n kind: daemon\n daemon: local\n cwd: /workspace\nagents:\n codex-safe:\n provider: codex\n options:\n sandbox_workspace_write:\n writable_roots: [/var/cache/npm]\n network_access: false\n",
|
|
},
|
|
{
|
|
path: ".paseo/workflows/run.yml",
|
|
content:
|
|
"name: run\non: manual.run\nmax_runtime: 1h\ninputs:\n repo:\n type: string\n choices: [studio]\n agent:\n type: string\n choices: [codex-safe]\nsteps:\n - id: work\n environment: ${{ paseo.inputs.repo }}\n max_runtime: 30m\n idle_timeout: 5m\n agent: ${{ paseo.inputs.agent }}\n prompt:\n - include: partials/instructions.md\n - text: ${{ paseo.prompt }}\n",
|
|
},
|
|
{
|
|
path: ".paseo/workflows/partials/instructions.md",
|
|
content: "Keep structured provider options unchanged.\n",
|
|
},
|
|
];
|
|
for (const file of files) await writeFile(path.join(cwd, file.path), file.content);
|
|
|
|
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
|
|
const address = server.address() as AddressInfo;
|
|
const origin = `http://127.0.0.1:${address.port}`;
|
|
// A malformed request must not kill the fixture or satisfy a deployment assertion.
|
|
const malformed = await fetch(`${origin}/api/v1/configurations/validate`, { method: "POST" });
|
|
assert.equal(malformed.status, 400);
|
|
assert.equal(requests.length, 0);
|
|
|
|
const validate = await runLocalPaseo(
|
|
[
|
|
"hub",
|
|
"deploy",
|
|
"-p",
|
|
"studio",
|
|
"--dry-run",
|
|
"--hub",
|
|
origin,
|
|
"--api-key",
|
|
"test-secret",
|
|
"--json",
|
|
],
|
|
{},
|
|
cwd,
|
|
);
|
|
assert.equal(validate.exitCode, 0, validate.stderr);
|
|
assert.deepEqual(JSON.parse(validate.stdout), {
|
|
projectSlug: "studio",
|
|
valid: true,
|
|
workflows: 1,
|
|
origin,
|
|
});
|
|
|
|
const install = await runLocalPaseo(
|
|
["hub", "deploy", "-p", "studio", "--hub", origin, "--api-key", "test-secret", "--json"],
|
|
{},
|
|
cwd,
|
|
);
|
|
assert.equal(install.exitCode, 0, install.stderr);
|
|
|
|
assert.deepEqual(
|
|
requests.map(({ url, body }) => ({ url, body })),
|
|
[
|
|
{
|
|
url: "/api/v1/configurations/validate",
|
|
body: { projectSlug: "studio", files: files.toSorted(compareFiles) },
|
|
},
|
|
{
|
|
url: "/api/v1/configurations/install",
|
|
body: { projectSlug: "studio", files: files.toSorted(compareFiles) },
|
|
},
|
|
],
|
|
);
|
|
console.log("✅ installed Hub CLI deploys and validates the canonical bundle");
|
|
} finally {
|
|
server.closeAllConnections();
|
|
await new Promise<void>((resolve) => server.close(() => resolve()));
|
|
await rm(cwd, { recursive: true });
|
|
}
|
|
|
|
function compareFiles(left: { path: string }, right: { path: string }): number {
|
|
if (left.path < right.path) return -1;
|
|
if (left.path > right.path) return 1;
|
|
return 0;
|
|
}
|