Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 4 additions & 1 deletion compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ services:
build:
context: ./registry
dockerfile: Dockerfile
environment:
REGISTRY_USERNAME: docker
REGISTRY_PASSWORD: docker
ports:
- "5000:5000"
- "5002:5000"
volumes:
- registry-data:/var/lib/registry
healthcheck:
Expand Down
3 changes: 3 additions & 0 deletions web/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ ENCRYPTION_KEY=your-64-character-hex-string
# Public URL
APP_URL=http://localhost:3000

# Local dev origins allowed by Next.js, comma-separated (optional)
NEXT_ALLOWED_DEV_ORIGINS=192.168.1.10,dev.local

# Runtime release tag used for control-plane update checks (tip/dev disables prompts)
TECHULUS_CLOUD_VERSION=dev

Expand Down
1 change: 1 addition & 0 deletions web/app/api/v1/agent/builds/[id]/status/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ export async function POST(
images: [archImageUri],
finalImageUri: baseImageUri,
serviceId: shouldDeploy ? build.serviceId : undefined,
buildId,
});

await db
Expand Down
80 changes: 67 additions & 13 deletions web/lib/inngest/functions/build-workflow.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,46 @@
import { and, eq, isNull } from "drizzle-orm";
import { db } from "@/db";
import { builds, serviceReplicas, services } from "@/db/schema";
import { builds, serviceReplicas, services, workQueue } from "@/db/schema";
import { deployServiceInternal } from "@/lib/deploy-service";
import { inngest } from "../client";
import { inngestEvents } from "../events";

async function hasCompletedManifestWorkItem({
serviceId,
buildId,
buildGroupId,
}: {
serviceId: string;
buildId?: string;
buildGroupId?: string | null;
}) {
const completedManifestItems = await db
.select({ payload: workQueue.payload })
.from(workQueue)
.where(
and(
eq(workQueue.type, "create_manifest"),
eq(workQueue.status, "completed"),
),
);

return completedManifestItems.some((item) => {
try {
const payload = JSON.parse(item.payload) as {
serviceId?: string;
buildId?: string;
buildGroupId?: string;
};

if (payload.serviceId !== serviceId) return false;
if (buildGroupId) return payload.buildGroupId === buildGroupId;
return !payload.buildGroupId && payload.buildId === buildId;
} catch {
return false;
}
});
}

export const buildWorkflow = inngest.createFunction(
{
id: "build-workflow",
Expand Down Expand Up @@ -42,14 +78,23 @@ export const buildWorkflow = inngest.createFunction(
return { status: "failed", reason: result.data.error, buildId };
}

const manifestResult = await step.waitForEvent("wait-manifest", {
event: inngestEvents.manifestCompleted,
timeout: "10m",
if: `async.data.serviceId == "${serviceId}"`,
const manifestAlreadyCompleted = await step.run("check-existing-manifest", async () => {
return hasCompletedManifestWorkItem({
serviceId,
buildId,
});
});

if (!manifestResult) {
return { status: "completed_no_manifest", buildId };
if (!manifestAlreadyCompleted) {
const manifestResult = await step.waitForEvent("wait-manifest", {
event: inngestEvents.manifestCompleted,
timeout: "10m",
if: `async.data.serviceId == "${serviceId}"`,
});

if (!manifestResult) {
return { status: "completed_no_manifest", buildId };
}
}

const shouldDeploy = await step.run("check-auto-deploy", async () => {
Expand Down Expand Up @@ -118,14 +163,23 @@ export const buildWorkflow = inngest.createFunction(
return { status: "failed", reason: "build_failed", buildGroupId };
}

const manifestResult = await step.waitForEvent("wait-group-manifest", {
event: inngestEvents.manifestCompleted,
timeout: "10m",
if: `async.data.buildGroupId == "${buildGroupId}"`,
const manifestAlreadyCompleted = await step.run("check-existing-group-manifest", async () => {
return hasCompletedManifestWorkItem({
serviceId,
buildGroupId,
});
});

if (!manifestResult) {
return { status: "completed_no_manifest", buildGroupId };
if (!manifestAlreadyCompleted) {
const manifestResult = await step.waitForEvent("wait-group-manifest", {
event: inngestEvents.manifestCompleted,
timeout: "10m",
if: `async.data.buildGroupId == "${buildGroupId}"`,
});

if (!manifestResult) {
return { status: "completed_no_manifest", buildGroupId };
}
}

const shouldDeploy = await step.run("check-auto-deploy-group", async () => {
Expand Down
5 changes: 5 additions & 0 deletions web/next.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { NextConfig } from "next";

const allowedDevOrigins = process.env.NEXT_ALLOWED_DEV_ORIGINS?.split(",")
.map((origin) => origin.trim())
.filter(Boolean);

const nextConfig: NextConfig = {
output: "standalone",
...(allowedDevOrigins?.length ? { allowedDevOrigins } : {}),
};

export default nextConfig;
Loading