mirror of
https://github.com/different-ai/openwork
synced 2026-04-26 01:25:10 +02:00
Ships the last-missing piece of the Electron migration. After this PR
merges there are three one-shot scripts under scripts/migration/ that
carry the whole lifecycle from "cut v0.12.0" through "delete src-tauri":
01-cut-migration-release.mjs # bumps versions, writes the release env
# fragment, tags, pushes.
02-validate-migration.mjs # guided smoke test against a cut release.
03-post-migration-cleanup.mjs # deletes src-tauri, flips defaults,
# scrubs Tauri docs (dry-run by default).
Code landing in the same PR (dormant until a release sets
VITE_OPENWORK_MIGRATION_RELEASE=1):
- apps/desktop/src-tauri/src/commands/migration.rs gains
migrate_to_electron() — downloads the matching Electron .zip, verifies
the Apple signature, swaps the .app bundle in place via a detached
shell script, relaunches, and exits. Windows + Linux branches are
stubbed with clear TODOs for the follow-up.
- apps/app/src/app/lib/migration.ts grows migrateToElectron() + a
"later" defer helper.
- apps/app/src/react-app/shell/migration-prompt.tsx adds the one-time
"OpenWork is moving to a new engine" modal. Mounted from
providers.tsx. Gated on isTauriRuntime() AND the build-time flag, so
Electron users and all non-migration-release builds never render it.
- apps/app/vite.config.ts loads apps/app/.env.migration-release when
present so the prompt gets the release-specific download URLs.
- .gitignore allows the migration-release fragment to be committed only
on the tagged migration-release commit (removed in cleanup step).
Release workflow:
- .github/workflows/release-macos-aarch64.yml gains a publish-electron
job alongside the Tauri jobs. Gated on RELEASE_PUBLISH_ELECTRON repo
var OR the new publish_electron workflow_dispatch input (default
false). Uses the existing Apple Dev ID secrets — no new credential
story. Produces latest-mac.yml alongside Tauri's latest.json so a
v0.12.0 release serves both updaters.
Verified:
pnpm --filter @openwork/app typecheck ✓
cargo check --manifest-path apps/desktop/src-tauri/Cargo.toml ✓
node --check on all mjs scripts ✓
python yaml parse of release-macos-aarch64.yml ✓
Not verified (needs a real release cycle):
end-to-end migration from a signed Tauri .app to a signed Electron
.app through the detached-script install swap.
Co-authored-by: Benjamin Shafii <benjamin@openworklabs.com>
88 lines
3.0 KiB
TypeScript
88 lines
3.0 KiB
TypeScript
import os from "node:os";
|
|
import { existsSync, readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { defineConfig } from "vite";
|
|
import react from "@vitejs/plugin-react";
|
|
import tailwindcss from "@tailwindcss/vite";
|
|
|
|
const portValue = Number.parseInt(process.env.PORT ?? "", 10);
|
|
const devPort = Number.isFinite(portValue) && portValue > 0 ? portValue : 5173;
|
|
const allowedHosts = new Set<string>();
|
|
const envAllowedHosts = process.env.VITE_ALLOWED_HOSTS ?? "";
|
|
|
|
const addHost = (value?: string | null) => {
|
|
const trimmed = value?.trim();
|
|
if (!trimmed) return;
|
|
allowedHosts.add(trimmed);
|
|
};
|
|
|
|
envAllowedHosts.split(",").forEach(addHost);
|
|
addHost(process.env.OPENWORK_PUBLIC_HOST ?? null);
|
|
const hostname = os.hostname();
|
|
addHost(hostname);
|
|
const shortHostname = hostname.split(".")[0];
|
|
if (shortHostname && shortHostname !== hostname) {
|
|
addHost(shortHostname);
|
|
}
|
|
const appRoot = resolve(fileURLToPath(new URL(".", import.meta.url)));
|
|
|
|
// Load the Tauri → Electron migration-release fragment if present. Written
|
|
// by scripts/migration/01-cut-migration-release.mjs for the specific
|
|
// release commit; absent otherwise so every other build has the migration
|
|
// prompt dormant. Pre-parsed here so Vite's define/import.meta.env picks
|
|
// up the keys without a custom plugin.
|
|
function loadMigrationReleaseEnv(): Record<string, string> {
|
|
const fragmentPath = resolve(appRoot, ".env.migration-release");
|
|
if (!existsSync(fragmentPath)) return {};
|
|
const out: Record<string, string> = {};
|
|
const raw = readFileSync(fragmentPath, "utf8");
|
|
for (const line of raw.split(/\r?\n/)) {
|
|
const trimmed = line.trim();
|
|
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
const eq = trimmed.indexOf("=");
|
|
if (eq < 0) continue;
|
|
const key = trimmed.slice(0, eq).trim();
|
|
if (!key.startsWith("VITE_")) continue;
|
|
out[key] = trimmed.slice(eq + 1).trim();
|
|
}
|
|
return out;
|
|
}
|
|
const migrationReleaseEnv = loadMigrationReleaseEnv();
|
|
|
|
// Electron packaged builds load index.html via `file://`, so asset URLs
|
|
// must be relative. Tauri serves via its own protocol so absolute paths
|
|
// work there. Gate on an env var the electron build script sets.
|
|
const isElectronPackagedBuild = process.env.OPENWORK_ELECTRON_BUILD === "1";
|
|
|
|
export default defineConfig({
|
|
base: isElectronPackagedBuild ? "./" : "/",
|
|
define: Object.fromEntries(
|
|
Object.entries(migrationReleaseEnv).map(([k, v]) => [
|
|
`import.meta.env.${k}`,
|
|
JSON.stringify(v),
|
|
]),
|
|
),
|
|
plugins: [
|
|
{
|
|
name: "openwork-dev-server-id",
|
|
configureServer(server) {
|
|
server.middlewares.use("/__openwork_dev_server_id", (_req, res) => {
|
|
res.setHeader("Content-Type", "application/json");
|
|
res.end(JSON.stringify({ appRoot }));
|
|
});
|
|
},
|
|
},
|
|
tailwindcss(),
|
|
react(),
|
|
],
|
|
server: {
|
|
port: devPort,
|
|
strictPort: true,
|
|
...(allowedHosts.size > 0 ? { allowedHosts: Array.from(allowedHosts) } : {}),
|
|
},
|
|
build: {
|
|
target: "esnext",
|
|
},
|
|
});
|