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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 5 additions & 3 deletions apps/controller/src/compute-providers/spawn-modal-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,11 @@ export async function spawnModalWorker(
const { namedPorts, environmentSnapshotId, environmentConfig } =
await getNamedPortsForTaskRun(taskRun);

const needsVmRuntime = Boolean(environmentConfig?.container_projects?.length);
if (environmentConfig?.container_projects?.length) {
throw new NonRetryableSpawnError(
'Modal does not currently support Docker Compose or Dockerfile projects. Choose E2B, Daytona, Blaxel, or Local Docker for this environment.',
);
}

const shouldEnableAuthBypass = shouldEnableAuthBypassForTaskRun({
environmentConfig,
Expand Down Expand Up @@ -252,7 +256,6 @@ export async function spawnModalWorker(
const configuredResources = resolveConfiguredComputeProviderResources({
provider: 'modal',
});

const modalConfig = {
tokenId: modalTokenId,
tokenSecret: modalTokenSecret,
Expand All @@ -269,7 +272,6 @@ export async function spawnModalWorker(
...(modalEcrOidcRoleArn ? { ecrOidcRoleArn: modalEcrOidcRoleArn } : {}),
...(modalEcrRegion ? { ecrRegion: modalEcrRegion } : {}),
...(parsedModalRegions ? { regions: parsedModalRegions } : {}),
...(needsVmRuntime ? { vmRuntime: true } : {}),
...(configuredResources.configuredCpuCores !== null
? { cpu: configuredResources.configuredCpuCores }
: {}),
Expand Down
30 changes: 30 additions & 0 deletions apps/dev/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Command } from 'commander';
import { execa } from 'execa';
import ora from 'ora';

import { PRODUCT_NAME } from '@roomote/types';
Expand All @@ -22,6 +23,33 @@ import {
// routing in deploy/caddy/Caddyfile.
const CADDY_DEV_PORT = 18080;

const DEVELOPMENT_WORKER_IMAGE_REPOSITORY = 'ghcr.io/roocodeinc/roomote-worker';

async function configureHostedDevelopmentWorkerImage(): Promise<void> {
if (process.env.ROOMOTE_DEVELOPMENT_WORKER_IMAGE_REF) {
return;
}

try {
const { stdout } = await execa('git', ['rev-parse', 'origin/develop'], {
cwd: process.cwd(),
});
// The publish workflow tags images with exactly the first eight SHA
// characters. Git's --short=8 means "at least eight" and may lengthen an
// ambiguous abbreviation, which would select a tag that was never pushed.
const imageRef = `${DEVELOPMENT_WORKER_IMAGE_REPOSITORY}:develop-${stdout.trim().slice(0, 8)}`;

process.env.ROOMOTE_DEVELOPMENT_WORKER_IMAGE_REF = imageRef;
// Modal consumes its base image directly instead of provisioning a named
// artifact. Preserve an operator override, otherwise pin it to the same
// immutable development image as E2B, Daytona, and Blaxel.
process.env.MODAL_BASE_IMAGE_REF ||= imageRef;
} catch {
// Source archives and shallow checkouts may not have origin/develop. The
// shared resolver retains the mutable :develop fallback for those cases.
}
}

class LocalDevStarter {
static async run(options: ScriptOptions): Promise<void> {
try {
Expand All @@ -35,6 +63,8 @@ class LocalDevStarter {

await EnvService.checkEnvVars();

await configureHostedDevelopmentWorkerImage();

await WatchmanService.checkInstalled();
await PM2Service.checkInstalled();
await PM2Service.stopServices({
Expand Down
2 changes: 1 addition & 1 deletion apps/worker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ RUN sudo apt-get update \

# Shared with WORKER_RUNTIME_SCHEMA_VERSION in @roomote/types. Local builds
# pass this explicitly; the default keeps direct/release Docker builds labeled.
ARG ROOMOTE_WORKER_RUNTIME_SCHEMA_VERSION=2
ARG ROOMOTE_WORKER_RUNTIME_SCHEMA_VERSION=3
LABEL dev.roomote.worker-image.schema-version="${ROOMOTE_WORKER_RUNTIME_SCHEMA_VERSION}"

WORKDIR /sandbox

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 71 additions & 0 deletions packages/compute-providers/src/e2b/build-e2b-template.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion packages/compute-providers/src/e2b/build-e2b-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,14 @@ export async function buildE2bWorkerTemplate(
? { username: registryUsername, password: registryPassword }
: undefined,
)
.runCmd('sudo docker compose version');
// E2B doesn't preserve a custom image's Dockerfile USER for template
// steps, and its build environment can reject sudo even for an image user
// that has passwordless sudo. Validate the baked-in Docker CLI directly as
// root, then restore Roomote's intended runtime identity.
.setUser('root')
.runCmd('/usr/bin/docker compose version')
.setUser('roomote')
.setWorkdir('/home/roomote');

const buildInfo = await Template.build(template, templateRef, {
apiKey,
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/types/src/compute-providers/capabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const MODAL_CAPABILITIES: ComputeProviderCapabilities = {
supportsStandbyResume: false,
supportsResume: true,
supportsFileWrite: true,
supportsContainerProjects: true,
supportsContainerProjects: false,
};

export const DAYTONA_CAPABILITIES: ComputeProviderCapabilities = {
Expand Down
25 changes: 25 additions & 0 deletions packages/types/src/setup-compute-config.test.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 12 additions & 11 deletions packages/types/src/setup-compute-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,9 @@ const DEFAULT_WORKER_IMAGE_REPOSITORY = 'ghcr.io/roocodeinc/roomote-worker';
export const DEVELOPMENT_MODAL_BASE_IMAGE_REF =
'ghcr.io/roocodeinc/roomote-worker:develop';

const ROOMOTE_DEVELOPMENT_WORKER_IMAGE_REF =
'ROOMOTE_DEVELOPMENT_WORKER_IMAGE_REF';

/**
* Derives the published worker image ref for the running app release:
* `<repo>:${RELEASE_VERSION}`, where the repo defaults to the official GHCR
Expand Down Expand Up @@ -638,9 +641,14 @@ export function resolveDerivedModalBaseImageRef(
return derivedFromWorkerImage;
}

return isDevelopmentRuntime(runtimeEnv)
? DEVELOPMENT_MODAL_BASE_IMAGE_REF
: null;
if (!isDevelopmentRuntime(runtimeEnv)) {
return null;
}

return (
runtimeEnv[ROOMOTE_DEVELOPMENT_WORKER_IMAGE_REF]?.trim() ||
DEVELOPMENT_MODAL_BASE_IMAGE_REF
);
}

/**
Expand Down Expand Up @@ -721,14 +729,7 @@ export function buildSetupComputeStatus(input: {
// are ignored so they cannot stick above release-derived images. Only a
// registry-qualified ref is hosted-ready; a bare local tag is not pullable
// by hosted providers.
const explicitWorkerImage = runtimeEnv.DOCKER_WORKER_IMAGE?.trim() || null;
const effectiveWorkerImage =
explicitWorkerImage ?? deriveWorkerImageFromReleaseVersion(runtimeEnv);
const hostedWorkerImageRef =
deriveModalBaseImageRefDefault(effectiveWorkerImage) ??
(isDevelopmentRuntime(runtimeEnv)
? DEVELOPMENT_MODAL_BASE_IMAGE_REF
: null);
const hostedWorkerImageRef = resolveDerivedModalBaseImageRef(runtimeEnv);
const derivedModalBaseImageRef = hostedWorkerImageRef;

const workerImage: SetupComputeWorkerImageStatus = {
Expand Down
2 changes: 1 addition & 1 deletion packages/types/src/worker-runtime-version.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* into Roomote worker images. Bump this when an existing image tag or hosted
* provider artifact must be rebuilt because the runtime contract changed.
*/
export const WORKER_RUNTIME_SCHEMA_VERSION = 2;
export const WORKER_RUNTIME_SCHEMA_VERSION = 3;

/** Stable string used in Docker labels and provider-side resource names. */
export const WORKER_RUNTIME_SCHEMA_TAG = `r${WORKER_RUNTIME_SCHEMA_VERSION}`;
Loading