Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
a919da7
feat(deploy):add native Netlify deployment support
Maou3434 May 26, 2026
b1b10ab
feat(deploy): add native Render deployment support via GitHub sync
Maou3434 May 29, 2026
4028a4a
fix(deploy): improve Render service URL fallback and prevent cross-pr…
Maou3434 May 29, 2026
ba818af
feat(deploy): add native Railway deployment support via GitHub sync
Maou3434 May 31, 2026
ca5a288
feat(deploy): resolve Netlify sync auth by using deploy keys
Maou3434 May 31, 2026
4a0e1e9
fix(daemon): deploy flow edge cases and optimize file sync
Maou3434 Jun 16, 2026
a5c997f
fix(daemon): avoid Netlify key accumulation and resolve repo visibili…
Maou3434 Jun 16, 2026
ec108b6
fix(daemon): support custom repository default branch names on Netlif…
Maou3434 Jun 16, 2026
5697f98
fix(daemon): avoid selecting stale deployment on Netlify trigger fall…
Maou3434 Jun 16, 2026
d93c6c9
fix daemon deploy timeout abort, file reconciliation, and railway dom…
Maou3434 Jun 16, 2026
2cb2f9b
fix(deploy): thread provider ID into retry link and localize git tree…
Maou3434 Jun 16, 2026
deea861
fix(deploy): verify Railway build success and map GitHub errors to 502
Maou3434 Jun 16, 2026
1b0217b
fix(deploy): poll Render deploy list for fresh deploy ID and handle d…
Maou3434 Jun 16, 2026
f2730b4
fix(deploy): heal GitHub key link and verify Netlify/Render build suc…
Maou3434 Jun 17, 2026
ad16f91
fix(web): use clarified social share modal titles and restore publish…
Maou3434 Jun 17, 2026
7c0a320
fix(deploy): self-heal stale resource IDs and handle GitHub duplicate…
Maou3434 Jun 17, 2026
9dcabe3
fix(deploy): fail lookups on non-OK list responses and enforce baseli…
Maou3434 Jun 17, 2026
7988234
fix(deploy): resolve Render and Railway recovery path correctness iss…
Maou3434 Jun 17, 2026
7095d39
chore: backfill missing translation keys in other locales
Maou3434 Jul 3, 2026
025e8e1
fix(deploy): resolve merge conflicts and fix test regressions in daem…
Maou3434 Jul 3, 2026
a07fd26
chore: sync rebase, update deploy tests and regenerate design systems
Maou3434 Jul 5, 2026
a2527aa
fix(test): resolve import issue in registry tests and CRLF issues in …
Maou3434 Jul 15, 2026
c1c54fa
fix(test): resolve deploy target mock regression after rebase
Maou3434 Jul 18, 2026
5ee1460
fix(daemon): remove local deploy config writes and encode GitHub path…
Maou3434 Jul 21, 2026
150571f
feat(deploy): make GitHub PAT instructions provider-aware for Netlify
Maou3434 Jul 21, 2026
2fb0559
fix(deploy): update tracking provider attribution and restore Playwri…
Maou3434 Jul 21, 2026
ab77493
test(deploy): fix type assertion in analytics deploy test
Maou3434 Jul 21, 2026
c2f44a2
test(deploy): resolve TypeScript strict null check in deploy analytic…
Maou3434 Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,857 changes: 1,839 additions & 18 deletions apps/daemon/src/deploy.ts

Large diffs are not rendered by default.

94 changes: 85 additions & 9 deletions apps/daemon/src/routes/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function registerDeployRoutes(app: Express, ctx: RegisterDeployRoutesDeps
const { PROJECTS_DIR } = ctx.paths;
const { randomUUID } = ctx.ids;
const { getProject } = ctx.projectStore;
const { VERCEL_PROVIDER_ID, CLOUDFLARE_PAGES_PROVIDER_ID, isDeployProviderId, publicDeployConfigForProvider, readDeployConfig, writeDeployConfig, listCloudflarePagesZones, DeployError, listDeployments, publicDeployments, getDeployment, buildDeployFileSet, cloudflarePagesProjectNameForDeploy, deployToCloudflarePages, deployToVercel, upsertDeployment, publicDeployment, cloudflarePagesDeploymentMetadata, prepareDeployPreflight } = ctx.deploy;
const { VERCEL_PROVIDER_ID, CLOUDFLARE_PAGES_PROVIDER_ID, NETLIFY_PROVIDER_ID, RENDER_PROVIDER_ID, RAILWAY_PROVIDER_ID, isDeployProviderId, publicDeployConfigForProvider, readDeployConfig, writeDeployConfig, listCloudflarePagesZones, DeployError, listDeployments, publicDeployments, getDeployment, buildDeployFileSet, cloudflarePagesProjectNameForDeploy, deployToCloudflarePages, deployToVercel, deployToNetlify, deployToRender, deployToRailway, upsertDeployment, publicDeployment, cloudflarePagesDeploymentMetadata, prepareDeployPreflight } = ctx.deploy;
// ---- Deploy --------------------------------------------------------------

app.get('/api/deploy/config', async (req, res) => {
Expand Down Expand Up @@ -128,11 +128,38 @@ export function registerDeployRoutes(app: Express, ctx: RegisterDeployRoutesDeps
priorMetadata: prior?.providerMetadata,
target,
})
: await deployToVercel({
config: await readDeployConfig(VERCEL_PROVIDER_ID),
files,
projectId: req.params.id,
});
: providerId === NETLIFY_PROVIDER_ID
? await deployToNetlify({
config: await readDeployConfig(NETLIFY_PROVIDER_ID),
files,
projectId: req.params.id,
projectsRoot: PROJECTS_DIR,
projectMetadata: deployProject?.metadata,
priorMetadata: prior?.providerMetadata,
})
: providerId === RENDER_PROVIDER_ID
? await deployToRender({
config: await readDeployConfig(RENDER_PROVIDER_ID),
files,
projectId: req.params.id,
projectsRoot: PROJECTS_DIR,
projectMetadata: deployProject?.metadata,
priorMetadata: prior?.providerMetadata,
})
: providerId === RAILWAY_PROVIDER_ID
? await deployToRailway({
config: await readDeployConfig(RAILWAY_PROVIDER_ID),
files,
projectId: req.params.id,
projectsRoot: PROJECTS_DIR,
projectMetadata: deployProject?.metadata,
priorMetadata: prior?.providerMetadata,
})
: await deployToVercel({
config: await readDeployConfig(VERCEL_PROVIDER_ID),
files,
projectId: req.params.id,
});
const now = Date.now();
/** @type {import('@open-design/contracts').DeployProjectFileResponse} */
const body = upsertDeployment(db, {
Expand All @@ -151,7 +178,9 @@ export function registerDeployRoutes(app: Express, ctx: RegisterDeployRoutesDeps
providerMetadata:
providerId === CLOUDFLARE_PAGES_PROVIDER_ID
? (result.providerMetadata ?? cloudflarePagesDeploymentMetadata(cloudflarePagesProjectName))
: prior?.providerMetadata,
: (providerId === NETLIFY_PROVIDER_ID || providerId === RENDER_PROVIDER_ID || providerId === RAILWAY_PROVIDER_ID)
? result.providerMetadata
: prior?.providerMetadata,
createdAt: prior?.createdAt ?? now,
updatedAt: now,
});
Expand Down Expand Up @@ -220,7 +249,21 @@ export interface RegisterDeploymentCheckRoutesDeps extends RouteDeps<'db' | 'htt
export function registerDeploymentCheckRoutes(app: Express, ctx: RegisterDeploymentCheckRoutesDeps) {
const { db } = ctx;
const { sendApiError } = ctx.http;
const { getDeploymentById, CLOUDFLARE_PAGES_PROVIDER_ID, cloudflarePagesProjectNameFromDeployment, checkCloudflarePagesDeploymentLinks, checkDeploymentUrl, upsertDeployment, publicDeployment } = ctx.deploy;
const {
getDeploymentById,
CLOUDFLARE_PAGES_PROVIDER_ID,
cloudflarePagesProjectNameFromDeployment,
checkCloudflarePagesDeploymentLinks,
checkDeploymentUrl,
upsertDeployment,
publicDeployment,
RAILWAY_PROVIDER_ID,
checkRailwayDeploymentLinks,
NETLIFY_PROVIDER_ID,
checkNetlifyDeploymentLinks,
RENDER_PROVIDER_ID,
checkRenderDeploymentLinks,
} = ctx.deploy;

app.post(
'/api/projects/:id/deployments/:deploymentId/check-link',
Expand All @@ -239,6 +282,39 @@ export function registerDeploymentCheckRoutes(app: Express, ctx: RegisterDeploym
'deployment not found',
);
}
if (existing.providerId === RAILWAY_PROVIDER_ID) {
const checked = await checkRailwayDeploymentLinks(existing);
const now = Date.now();
const body = upsertDeployment(db, {
...existing,
...checked,
reachableAt: checked.status === 'ready' ? now : existing.reachableAt,
updatedAt: now,
});
return res.json(publicDeployment(body));
}
if (existing.providerId === NETLIFY_PROVIDER_ID) {
const checked = await checkNetlifyDeploymentLinks(existing);
const now = Date.now();
const body = upsertDeployment(db, {
...existing,
...checked,
reachableAt: checked.status === 'ready' ? now : existing.reachableAt,
updatedAt: now,
});
return res.json(publicDeployment(body));
}
if (existing.providerId === RENDER_PROVIDER_ID) {
const checked = await checkRenderDeploymentLinks(existing);
const now = Date.now();
const body = upsertDeployment(db, {
...existing,
...checked,
reachableAt: checked.status === 'ready' ? now : existing.reachableAt,
updatedAt: now,
});
return res.json(publicDeployment(body));
}
const stableCloudflareProjectName =
existing.providerId === CLOUDFLARE_PAGES_PROVIDER_ID
? cloudflarePagesProjectNameFromDeployment(existing)
Expand Down Expand Up @@ -268,7 +344,7 @@ export function registerDeploymentCheckRoutes(app: Express, ctx: RegisterDeploym
statusMessage: result.reachable
? 'Public link is ready.'
: result.statusMessage ||
'Vercel is still preparing the public link.',
'Provider is still preparing the public link.',
reachableAt: result.reachable ? now : existing.reachableAt,
updatedAt: now,
});
Expand Down
18 changes: 18 additions & 0 deletions apps/daemon/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -671,6 +671,15 @@ import {
readDeployConfig,
VERCEL_PROVIDER_ID,
writeDeployConfig,
NETLIFY_PROVIDER_ID,
deployToNetlify,
RENDER_PROVIDER_ID,
deployToRender,
RAILWAY_PROVIDER_ID,
deployToRailway,
checkRailwayDeploymentLinks,
checkNetlifyDeploymentLinks,
checkRenderDeploymentLinks,
} from './deploy.js';
import {
checkCloudflarePagesDeploymentLinks,
Expand Down Expand Up @@ -2864,6 +2873,9 @@ export async function startServer({
const deployDeps = {
VERCEL_PROVIDER_ID,
CLOUDFLARE_PAGES_PROVIDER_ID,
NETLIFY_PROVIDER_ID,
RENDER_PROVIDER_ID,
RAILWAY_PROVIDER_ID,
isDeployProviderId,
publicDeployConfigForProvider,
readDeployConfig,
Expand All @@ -2885,6 +2897,12 @@ export async function startServer({
publicDeployment,
cloudflarePagesDeploymentMetadata,
prepareDeployPreflight,
deployToNetlify,
deployToRender,
deployToRailway,
checkRailwayDeploymentLinks,
checkNetlifyDeploymentLinks,
checkRenderDeploymentLinks,
};
const mediaDeps = {
MEDIA_PROVIDERS,
Expand Down
Loading
Loading