Skip to content

Commit 303c8dc

Browse files
committed
Merge origin/main into claude/epic-meitner-145193
2 parents ce1cc45 + 7162793 commit 303c8dc

73 files changed

Lines changed: 1457 additions & 956 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Preview sweep
2+
3+
# Safety net for the per-PR previews (.github/workflows/preview.yml): force
4+
# pushes, cancelled runs, or teardown failures can orphan a preview stack.
5+
# Nightly, list every deployed preview and destroy any whose PR is no longer
6+
# open.
7+
8+
on:
9+
schedule:
10+
- cron: "23 5 * * *"
11+
workflow_dispatch:
12+
13+
permissions:
14+
contents: read
15+
pull-requests: read
16+
17+
jobs:
18+
sweep:
19+
name: Destroy previews for closed PRs
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
24+
- uses: oven-sh/setup-bun@v2
25+
with:
26+
bun-version: 1.3.11
27+
28+
- name: Sweep
29+
env:
30+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_PREVIEW_API_TOKEN }}
31+
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_PREVIEW_ACCOUNT_ID }}
32+
GH_TOKEN: ${{ github.token }}
33+
run: |
34+
set -euo pipefail
35+
for pr in $(bun apps/host-cloudflare/scripts/preview.ts list | jq -r '.[].pr'); do
36+
state="$(gh pr view "$pr" --repo "$GITHUB_REPOSITORY" --json state -q .state 2>/dev/null || echo UNKNOWN)"
37+
if [ "$state" != "OPEN" ]; then
38+
echo "PR #$pr is $state — destroying its preview"
39+
bun apps/host-cloudflare/scripts/preview.ts destroy --pr "$pr"
40+
else
41+
echo "PR #$pr is open — keeping its preview"
42+
fi
43+
done

.github/workflows/preview.yml

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: Preview
2+
3+
# Per-PR preview deploys of the Cloudflare host to a dedicated preview account.
4+
# Every same-repo PR gets its own isolated stack (Worker + D1 + Access app)
5+
# behind Cloudflare Access; it is torn down when the PR closes. Fork PRs are
6+
# skipped — they must not run with the deploy token.
7+
#
8+
# Required repo configuration:
9+
# secret CLOUDFLARE_PREVIEW_API_TOKEN — scoped token (Workers/D1/R2/Access edit)
10+
# var CLOUDFLARE_PREVIEW_ACCOUNT_ID — the preview Cloudflare account
11+
# var PREVIEW_ACCESS_TEAM_DOMAIN — Zero Trust team domain
12+
# var PREVIEW_ACCESS_EMAILS — comma-separated emails allowed through Access
13+
14+
on:
15+
pull_request:
16+
types: [opened, reopened, synchronize, closed]
17+
18+
permissions:
19+
contents: read
20+
pull-requests: write
21+
22+
concurrency:
23+
group: preview-${{ github.event.pull_request.number }}
24+
cancel-in-progress: ${{ github.event.action != 'closed' }}
25+
26+
jobs:
27+
deploy:
28+
name: Deploy preview
29+
if: github.event.action != 'closed' && github.event.pull_request.head.repo.full_name == github.repository
30+
runs-on: ubuntu-latest
31+
steps:
32+
- uses: actions/checkout@v4
33+
34+
- uses: oven-sh/setup-bun@v2
35+
with:
36+
bun-version: 1.3.11
37+
38+
- run: bun install --frozen-lockfile
39+
40+
- name: Deploy
41+
id: deploy
42+
working-directory: apps/host-cloudflare
43+
env:
44+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_PREVIEW_API_TOKEN }}
45+
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_PREVIEW_ACCOUNT_ID }}
46+
PREVIEW_ACCESS_TEAM_DOMAIN: ${{ vars.PREVIEW_ACCESS_TEAM_DOMAIN }}
47+
PREVIEW_ACCESS_EMAILS: ${{ vars.PREVIEW_ACCESS_EMAILS }}
48+
run: bun scripts/preview.ts deploy --pr ${{ github.event.pull_request.number }}
49+
50+
- name: Comment preview URL
51+
uses: actions/github-script@v7
52+
env:
53+
PREVIEW_URL: ${{ steps.deploy.outputs.url }}
54+
with:
55+
script: |
56+
const marker = "<!-- executor-preview -->";
57+
const body = [
58+
marker,
59+
"### Cloudflare preview",
60+
"",
61+
`| | |`,
62+
`|---|---|`,
63+
`| Console | ${process.env.PREVIEW_URL} |`,
64+
`| MCP | \`${process.env.PREVIEW_URL}/mcp\` |`,
65+
`| Deployed commit | ${context.payload.pull_request.head.sha} |`,
66+
"",
67+
"Sign-in is Cloudflare Access (one-time PIN to an allowed email). " +
68+
"The preview has its own database and encryption key; it is destroyed when this PR closes.",
69+
].join("\n");
70+
const { data: comments } = await github.rest.issues.listComments({
71+
...context.repo,
72+
issue_number: context.issue.number,
73+
per_page: 100,
74+
});
75+
const existing = comments.find((c) => c.body && c.body.startsWith(marker));
76+
if (existing) {
77+
await github.rest.issues.updateComment({ ...context.repo, comment_id: existing.id, body });
78+
} else {
79+
await github.rest.issues.createComment({ ...context.repo, issue_number: context.issue.number, body });
80+
}
81+
82+
teardown:
83+
name: Tear down preview
84+
if: github.event.action == 'closed' && github.event.pull_request.head.repo.full_name == github.repository
85+
runs-on: ubuntu-latest
86+
steps:
87+
- uses: actions/checkout@v4
88+
89+
- uses: oven-sh/setup-bun@v2
90+
with:
91+
bun-version: 1.3.11
92+
93+
# destroy talks straight to the Cloudflare API — no install needed.
94+
- name: Destroy
95+
env:
96+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_PREVIEW_API_TOKEN }}
97+
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_PREVIEW_ACCOUNT_ID }}
98+
run: bun apps/host-cloudflare/scripts/preview.ts destroy --pr ${{ github.event.pull_request.number }}
99+
100+
- name: Mark comment as torn down
101+
uses: actions/github-script@v7
102+
with:
103+
script: |
104+
const marker = "<!-- executor-preview -->";
105+
const { data: comments } = await github.rest.issues.listComments({
106+
...context.repo,
107+
issue_number: context.issue.number,
108+
per_page: 100,
109+
});
110+
const existing = comments.find((c) => c.body && c.body.startsWith(marker));
111+
if (existing) {
112+
await github.rest.issues.updateComment({
113+
...context.repo,
114+
comment_id: existing.id,
115+
body: `${marker}\n### Cloudflare preview\n\nTorn down — the PR is closed.`,
116+
});
117+
}

.oxlintrc.jsonc

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,19 @@
3737
"error",
3838
{
3939
"forbid": [
40-
{ "element": "button", "message": "Use <Button> from @executor/react" },
40+
{
41+
"element": "button",
42+
"message": "Use <Button> from @executor/react",
43+
},
4144
{ "element": "input", "message": "Use <Input> from @executor/react" },
42-
{ "element": "textarea", "message": "Use <Textarea> from @executor/react" },
43-
{ "element": "select", "message": "Use <Select> or <NativeSelect> from @executor/react" },
45+
{
46+
"element": "textarea",
47+
"message": "Use <Textarea> from @executor/react",
48+
},
49+
{
50+
"element": "select",
51+
"message": "Use <Select> or <NativeSelect> from @executor/react",
52+
},
4453
{ "element": "table", "message": "Use <Table> from @executor/react" },
4554
{ "element": "label", "message": "Use <Label> from @executor/react" },
4655
],
@@ -53,6 +62,7 @@
5362
"apps/cli/src/**/*.{ts,tsx}",
5463
"apps/desktop/src/main.ts",
5564
"scripts/**/*.{ts,js}",
65+
"apps/*/scripts/**/*.{ts,js}",
5666
"packages/kernel/runtime-*/src/**/*.{ts,tsx,js,mjs}",
5767
],
5868
"rules": {

apps/cloud/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
"@executor-js/cli": "workspace:*",
8080
"@rhyssul/portless": "^0.13.0",
8181
"@tailwindcss/vite": "catalog:",
82+
"@tanstack/virtual-file-routes": "^1.162.0",
8283
"@types/react": "catalog:",
8384
"@types/react-dom": "catalog:",
8485
"@vitejs/plugin-react": "catalog:",

apps/cloud/scripts/build.mjs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,13 @@ console.log(`[build] VITE_PUBLIC_ANALYTICS_PATH=${process.env.VITE_PUBLIC_ANALYT
1818

1919
rmSync(new URL("../dist/", import.meta.url), { force: true, recursive: true });
2020

21-
const steps = ["turbo run build --filter @executor-js/vite-plugin", "vite build"];
21+
const steps = [
22+
// Workspace packages whose exports app code (or this very vite config)
23+
// resolves from dist under Node — vite's config loader externalizes bare
24+
// imports, so they must be built before vite starts.
25+
"turbo run build --filter @executor-js/vite-plugin --filter @executor-js/react",
26+
"vite build",
27+
];
2228

2329
for (const step of steps) {
2430
const result = spawnSync(step, { stdio: "inherit", shell: true });

0 commit comments

Comments
 (0)