add commits..
This commit is contained in:
+117
-65
@@ -26,10 +26,10 @@ const moduleCacheRoot = resolve(homedir(), '.caches')
|
||||
const artifactCacheRoot = resolve(homedir(), '.cache', 'autonomy-studio', 'artifacts')
|
||||
const studioRuntimeRoot = resolve(homedir(), 'autonomy-studio', 'runtime')
|
||||
const autowareDockerImage = 'ghcr.io/autowarefoundation/autoware-universe:humble-20240301-cuda'
|
||||
const nav2DockerImage = 'nav2-dev:humble'
|
||||
const nav2DockerImage = 'future-nav2-gazebo:humble'
|
||||
const autowareRosBridgeImage = 'autoware-ros-bridge'
|
||||
const slamRosBridgeImage = 'slam-ros-bridge'
|
||||
const slamSimulationRuntimeImage = 'nav2-slam:humble'
|
||||
const slamSimulationRuntimeImage = 'future-nav2-gazebo:humble'
|
||||
const slamSimulationRuntimeContainerName = 'nav2-slam'
|
||||
const rosBridgePort = 8765
|
||||
const exampleCodeRoot = resolve(homedir(), '다운로드', 'example-code')
|
||||
@@ -199,6 +199,14 @@ type RuntimeImageArtifact = {
|
||||
required?: boolean | null
|
||||
}
|
||||
|
||||
const defaultNav2GazeboRuntimeImage: RuntimeImageArtifact = {
|
||||
image: 'future-nav2-gazebo:humble',
|
||||
url: 'https://pub-ca72c40797144462bdb7d200c215d361.r2.dev/studio-artifacts/runtime-images/nav2/gazebo/latest/future-nav2-gazebo-humble.tar.zst',
|
||||
fileName: 'future-nav2-gazebo-humble.tar.zst',
|
||||
checksumSha256: '54230e039c1fa5dc82615cef3828c5acae7b8bea8e921b70cfdc5351d8301644',
|
||||
required: true,
|
||||
}
|
||||
|
||||
type ModuleInstallOptions = {
|
||||
prebuiltArtifacts?: ModulePrebuiltArtifact[] | null
|
||||
runtimeImages?: RuntimeImageArtifact[] | null
|
||||
@@ -236,6 +244,10 @@ type WorldLayoutObject = {
|
||||
|
||||
type WorldLayout = {
|
||||
version: 1
|
||||
bounds: {
|
||||
width: number
|
||||
depth: number
|
||||
}
|
||||
objects: WorldLayoutObject[]
|
||||
}
|
||||
|
||||
@@ -255,6 +267,7 @@ type SavedMapArtifactDownload = {
|
||||
function defaultWorldLayout(): WorldLayout {
|
||||
return {
|
||||
version: 1,
|
||||
bounds: { width: 10, depth: 10 },
|
||||
objects: []
|
||||
}
|
||||
}
|
||||
@@ -278,7 +291,14 @@ function normalizeWorldLayout(value: unknown): WorldLayout {
|
||||
size: tuple(object.size, [1, 1, 1])
|
||||
}]
|
||||
}).filter((object) => !['wall-1', 'pillar-1'].includes(object.id))
|
||||
return { version: 1, objects }
|
||||
const boundsInput = (value as { bounds?: unknown }).bounds
|
||||
const bounds = boundsInput && typeof boundsInput === 'object'
|
||||
? boundsInput as { width?: unknown; depth?: unknown }
|
||||
: {}
|
||||
const size = (input: unknown, fallback: number): number => (
|
||||
typeof input === 'number' && Number.isFinite(input) ? Math.min(40, Math.max(2, input)) : fallback
|
||||
)
|
||||
return { version: 1, bounds: { width: size(bounds.width, 10), depth: size(bounds.depth, 10) }, objects }
|
||||
}
|
||||
|
||||
function worldLayoutPath(workspaceRoot?: string | null): string {
|
||||
@@ -766,6 +786,17 @@ function resolveNav2RuntimeImage(
|
||||
return nav2Image || nav2DockerImage
|
||||
}
|
||||
|
||||
function requiredRuntimeImages(
|
||||
runtimeImages: RuntimeImageArtifact[] | null | undefined,
|
||||
runtimeImage: string,
|
||||
): RuntimeImageArtifact[] {
|
||||
const images = (runtimeImages ?? []).filter((image) => image?.image?.trim())
|
||||
if (runtimeImage !== nav2DockerImage || images.some((image) => image.image?.trim() === runtimeImage)) {
|
||||
return images
|
||||
}
|
||||
return [...images, defaultNav2GazeboRuntimeImage]
|
||||
}
|
||||
|
||||
async function resolveCustomModuleRuntimeImage(moduleRoot: string, fallbackImage: string): Promise<string> {
|
||||
try {
|
||||
const config = JSON.parse(await readFile(resolve(moduleRoot, '.autonomy-studio-module.json'), 'utf8')) as { dockerImage?: unknown }
|
||||
@@ -1153,7 +1184,11 @@ async function ensureArtifactFile(url?: string | null, fileName?: string | null,
|
||||
|
||||
const targetPath = artifactCachePath(artifactUrl, fileName)
|
||||
const expected = checksumSha256?.trim().toLowerCase()
|
||||
if (!existsSync(targetPath)) await downloadArtifact(artifactUrl, targetPath)
|
||||
// Module artifact URLs are intentionally stable (for example, `latest`).
|
||||
// Do not reuse a same-named local file, or a source update can run with an
|
||||
// older install tree even though R2 already contains the current artifact.
|
||||
await rm(targetPath, { force: true })
|
||||
await downloadArtifact(artifactUrl, targetPath)
|
||||
if (expected) {
|
||||
let actual = createHash('sha256').update(await readFile(targetPath)).digest('hex').toLowerCase()
|
||||
if (actual !== expected) {
|
||||
@@ -2507,7 +2542,7 @@ function bridgeContainerCommand(stack: VisualizationStack): string {
|
||||
function normalizeVisualizationStack(value?: string | null): VisualizationStack {
|
||||
if (value === 'slam') return 'slam'
|
||||
if (value === 'slam3d') return 'slam3d'
|
||||
return value === 'nav2' ? 'nav2' : 'autoware'
|
||||
return value === 'nav2' || value === 'local' || value === 'platform' ? 'nav2' : 'autoware'
|
||||
}
|
||||
|
||||
async function readContainerEnvironmentValue(containerName: string, key: string): Promise<string | null> {
|
||||
@@ -2722,10 +2757,7 @@ function withRuntimeProfileDefaults(
|
||||
if (!profile) return profile
|
||||
const target = normalizeRuntimeTarget(profile.target)
|
||||
const prefix = target === 'remote-jetson' ? `JETSON_${stackEnvPrefix(stack)}` : `LOCAL_${stackEnvPrefix(stack)}`
|
||||
const dedicatedReadOnlyBridge = profile.type?.toLowerCase() === 'real_slam_3d'
|
||||
const bridgeImage = dedicatedReadOnlyBridge
|
||||
? profile.bridge?.image
|
||||
: envValue(`${prefix}_BRIDGE_IMAGE`) ?? profile.bridge?.image
|
||||
const bridgeImage = envValue(`${prefix}_BRIDGE_IMAGE`) ?? profile.bridge?.image
|
||||
const runtimeImage = envValue(`${prefix}_RUNTIME_IMAGE`)
|
||||
const agentUrl = target === 'remote-jetson'
|
||||
? envValue(`${prefix}_AGENT_URL`) ?? envValue('JETSON_AGENT_URL') ?? profile.agent?.url
|
||||
@@ -2790,7 +2822,7 @@ async function startVisualizationBridge(
|
||||
rosNetwork?: RosNetworkConfig | null,
|
||||
savedMapDatabasePath?: string | null,
|
||||
): Promise<VisualizationBridgeStatus> {
|
||||
const stack = normalizeVisualizationStack(environmentProfile?.type?.toLowerCase() ?? stackInput)
|
||||
const stack = normalizeVisualizationStack(stackInput ?? environmentProfile?.type?.toLowerCase())
|
||||
const resolvedProfile = withRuntimeProfileDefaults(environmentProfile, stack)
|
||||
const target = normalizeRuntimeTarget(resolvedProfile?.target)
|
||||
const bridge = resolvedProfile?.bridge
|
||||
@@ -2869,7 +2901,7 @@ async function startVisualizationBridge(
|
||||
'-e',
|
||||
`ROS_DOMAIN_ID=${requestedRosDomainId}`,
|
||||
'-e',
|
||||
'ROS_LOCALHOST_ONLY=0',
|
||||
`ROS_LOCALHOST_ONLY=${rosNetworkEnabled(rosNetwork) ? '0' : '1'}`,
|
||||
'-e',
|
||||
`AUTONOMY_STUDIO_ROS_NETWORK=${requestedRosNetworkKey}`,
|
||||
'-e',
|
||||
@@ -3150,8 +3182,9 @@ async function installRemoteWorkspaceModule(
|
||||
' if not url: continue',
|
||||
' name = item.get("fileName") or os.path.basename(url.split("?", 1)[0]) or "artifact.tar"',
|
||||
' path = os.path.join(cache, name)',
|
||||
' if not os.path.exists(path):',
|
||||
' subprocess.run(["curl", "-fL", url, "-o", path], check=True)',
|
||||
' # Stable latest URLs must not reuse a same-named local artifact.',
|
||||
' if os.path.exists(path): os.unlink(path)',
|
||||
' subprocess.run(["curl", "-fL", url, "-o", path], check=True)',
|
||||
' expected = (item.get("checksumSha256") or "").strip().lower()',
|
||||
' if expected:',
|
||||
' import hashlib',
|
||||
@@ -3531,6 +3564,7 @@ function buildNav2DockerRunScript(): string {
|
||||
'GPU_ENABLED=${6:-false}',
|
||||
'shift 6 || true',
|
||||
'FASTDDS_CONFIG_PATH=${FASTDDS_CONFIG_PATH:-}',
|
||||
'ROS_LOCALHOST_ONLY_VALUE=${ROS_LOCALHOST_ONLY:-1}',
|
||||
'LOCALIZATION_MODE=slam',
|
||||
'filtered_args=()',
|
||||
'for arg in "$@"; do',
|
||||
@@ -3591,6 +3625,7 @@ function buildNav2DockerRunScript(): string {
|
||||
' --rm -d --network host --ipc=host',
|
||||
' --name "$CONTAINER_NAME"',
|
||||
' -e "ROS_DOMAIN_ID=$ROS_DOMAIN_ID_VALUE"',
|
||||
' -e "ROS_LOCALHOST_ONLY=$ROS_LOCALHOST_ONLY_VALUE"',
|
||||
' -e "TURTLEBOT3_MODEL=${TURTLEBOT3_MODEL:-burger}"',
|
||||
' -e "MODULE_ROOT=/modules"',
|
||||
' -e "MODULE_FOLDER=$MODULE_FOLDER"',
|
||||
@@ -3603,7 +3638,7 @@ function buildNav2DockerRunScript(): string {
|
||||
' args+=(--gpus all -e NVIDIA_VISIBLE_DEVICES=all -e NVIDIA_DRIVER_CAPABILITIES=compute,utility,graphics)',
|
||||
'fi',
|
||||
'if [ -n "$FASTDDS_CONFIG_PATH" ] && [ -f "$FASTDDS_CONFIG_PATH" ]; then',
|
||||
' args+=(-v "$FASTDDS_CONFIG_PATH:/fastdds_profiles.xml:ro" -e "FASTDDS_DEFAULT_PROFILES_FILE=/fastdds_profiles.xml" -e "FASTRTPS_DEFAULT_PROFILES_FILE=/fastdds_profiles.xml" -e ROS_LOCALHOST_ONLY=0)',
|
||||
' args+=(-v "$FASTDDS_CONFIG_PATH:/fastdds_profiles.xml:ro" -e "FASTDDS_DEFAULT_PROFILES_FILE=/fastdds_profiles.xml" -e "FASTRTPS_DEFAULT_PROFILES_FILE=/fastdds_profiles.xml")',
|
||||
'fi',
|
||||
'if [ -d /dev/dri ]; then args+=(--device /dev/dri); fi',
|
||||
'if [ -n "${DISPLAY:-}" ] && [ -d /tmp/.X11-unix ]; then',
|
||||
@@ -3999,7 +4034,6 @@ async function startWorkspaceModule(
|
||||
const dockerInfo = parseModuleDockerInfo(targetFolderName)
|
||||
if (!dockerInfo || !isAllowedContainerName(dockerInfo.containerKey)) throw new Error('모듈 폴더명 형식이 올바르지 않습니다.')
|
||||
|
||||
await prepareRuntimeImages(runtimeImages)
|
||||
if (isNav2ModuleFolder(targetFolderName)) {
|
||||
await ensureNav2RuntimeAssets()
|
||||
const fastDdsConfigPath = await prepareLocalRosNetworkConfig(rosNetwork, rosDomainId)
|
||||
@@ -4009,6 +4043,7 @@ async function startWorkspaceModule(
|
||||
moduleRoot,
|
||||
resolveNav2RuntimeImage(runtimeImages, targetFolderName, rosParams),
|
||||
)
|
||||
await prepareRuntimeImages(requiredRuntimeImages(runtimeImages, runtimeImage))
|
||||
await runCommand(
|
||||
'bash',
|
||||
[
|
||||
@@ -4022,12 +4057,16 @@ async function startWorkspaceModule(
|
||||
...splitRosParams(rosParams)
|
||||
],
|
||||
undefined,
|
||||
fastDdsConfigPath ? { FASTDDS_CONFIG_PATH: fastDdsConfigPath } : undefined,
|
||||
{
|
||||
ROS_LOCALHOST_ONLY: rosNetworkEnabled(rosNetwork) ? '0' : '1',
|
||||
...(fastDdsConfigPath ? { FASTDDS_CONFIG_PATH: fastDdsConfigPath } : {}),
|
||||
},
|
||||
30000
|
||||
)
|
||||
return { ok: true, message: '실행 요청을 보냈습니다.' }
|
||||
}
|
||||
|
||||
await prepareRuntimeImages(runtimeImages)
|
||||
await ensureStudioRuntimeAssets()
|
||||
const scriptPath = resolveAutowareDockerRunScript()
|
||||
if (!existsSync(scriptPath)) throw new Error(`${scriptPath} 파일을 찾을 수 없습니다.`)
|
||||
@@ -4082,7 +4121,6 @@ async function buildWorkspaceModule(
|
||||
const dockerInfo = parseModuleDockerInfo(targetFolderName)
|
||||
if (!dockerInfo || !isAllowedContainerName(dockerInfo.containerKey)) throw new Error('모듈 폴더명 형식이 올바르지 않습니다.')
|
||||
|
||||
await prepareRuntimeImages(runtimeImages)
|
||||
if (isNav2ModuleFolder(targetFolderName)) {
|
||||
await ensureNav2RuntimeAssets()
|
||||
const rosParams = typeof rosParamsOverride === 'string' ? rosParamsOverride.trim() : await readModuleRosParams(moduleRoot)
|
||||
@@ -4090,6 +4128,7 @@ async function buildWorkspaceModule(
|
||||
moduleRoot,
|
||||
resolveNav2RuntimeImage(runtimeImages, targetFolderName, rosParams),
|
||||
)
|
||||
await prepareRuntimeImages(requiredRuntimeImages(runtimeImages, runtimeImage))
|
||||
await runCommand(
|
||||
'bash',
|
||||
[
|
||||
@@ -4106,6 +4145,7 @@ async function buildWorkspaceModule(
|
||||
return { ok: true, message: '빌드가 완료되었습니다.' }
|
||||
}
|
||||
|
||||
await prepareRuntimeImages(runtimeImages)
|
||||
await runCommand(
|
||||
'docker',
|
||||
[
|
||||
@@ -4124,6 +4164,62 @@ async function buildWorkspaceModule(
|
||||
return { ok: true, message: '빌드가 완료되었습니다.' }
|
||||
}
|
||||
|
||||
async function prepareRemoteRuntimeImages(
|
||||
target: RemoteWorkspaceTarget,
|
||||
runtimeImages?: RuntimeImageArtifact[] | null,
|
||||
): Promise<void> {
|
||||
const artifacts = (runtimeImages ?? [])
|
||||
.filter((image) => image?.image?.trim())
|
||||
.map((image) => ({
|
||||
image: image.image?.trim() ?? '',
|
||||
url: image.url?.trim() ?? '',
|
||||
fileName: image.url?.trim() ? safeArtifactFileName(image.url.trim(), image.fileName) : '',
|
||||
checksumSha256: image.checksumSha256?.trim() ?? '',
|
||||
required: Boolean(image.required),
|
||||
}))
|
||||
const artifactsJson = JSON.stringify(artifacts)
|
||||
|
||||
await runSsh(
|
||||
target,
|
||||
[
|
||||
'set -e',
|
||||
`RUNTIME_IMAGE_JSON="$(printf %s ${quoteShellSingle(encodeBase64(artifactsJson))} | base64 -d)"`,
|
||||
'ARTIFACT_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/autonomy-studio/artifacts"',
|
||||
'mkdir -p "$ARTIFACT_CACHE"',
|
||||
'LOCK_FILE="$ARTIFACT_CACHE/runtime-images.lock"',
|
||||
'flock "$LOCK_FILE" python3 - "$RUNTIME_IMAGE_JSON" "$ARTIFACT_CACHE" <<\'PY\'',
|
||||
'import json, os, subprocess, sys',
|
||||
'images = json.loads(sys.argv[1] or "[]")',
|
||||
'cache = sys.argv[2]',
|
||||
'for item in images:',
|
||||
' image = (item.get("image") or "").strip()',
|
||||
' if not image: continue',
|
||||
' exists = subprocess.run(["docker", "image", "inspect", image], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0',
|
||||
' if exists: continue',
|
||||
' url = (item.get("url") or "").strip()',
|
||||
' if not url:',
|
||||
' if item.get("required"): raise SystemExit(f"runtime image artifact url is missing: {image}")',
|
||||
' continue',
|
||||
' name = item.get("fileName") or os.path.basename(url.split("?", 1)[0]) or "runtime-image.tar"',
|
||||
' path = os.path.join(cache, name)',
|
||||
' if os.path.exists(path): os.unlink(path)',
|
||||
' subprocess.run(["curl", "-fL", url, "-o", path], check=True)',
|
||||
' expected = (item.get("checksumSha256") or "").strip().lower()',
|
||||
' if expected:',
|
||||
' import hashlib',
|
||||
' with open(path, "rb") as fp: actual = hashlib.sha256(fp.read()).hexdigest().lower()',
|
||||
' if actual != expected: raise SystemExit(f"runtime image checksum mismatch: {name}")',
|
||||
' if path.lower().endswith(".tar.zst") or path.lower().endswith(".tzst"):',
|
||||
' import shlex',
|
||||
' subprocess.run(f"zstd -dc {shlex.quote(path)} | docker load", shell=True, check=True)',
|
||||
' else:',
|
||||
' subprocess.run(["docker", "load", "-i", path], check=True)',
|
||||
'PY'
|
||||
].join('\n'),
|
||||
900000,
|
||||
)
|
||||
}
|
||||
|
||||
async function startRemoteWorkspaceModule(
|
||||
targetInput: RemoteWorkspaceTarget,
|
||||
moduleName: string,
|
||||
@@ -4140,17 +4236,6 @@ async function startRemoteWorkspaceModule(
|
||||
|
||||
const dockerInfo = parseModuleDockerInfo(targetFolderName)
|
||||
if (!dockerInfo || !isAllowedContainerName(dockerInfo.containerKey)) throw new Error('모듈 폴더명 형식이 올바르지 않습니다.')
|
||||
const runtimeImageArtifacts = (runtimeImages ?? [])
|
||||
.filter((image) => image?.image?.trim())
|
||||
.map((image) => ({
|
||||
image: image.image?.trim() ?? '',
|
||||
url: image.url?.trim() ?? '',
|
||||
fileName: image.url?.trim() ? safeArtifactFileName(image.url.trim(), image.fileName) : '',
|
||||
checksumSha256: image.checksumSha256?.trim() ?? '',
|
||||
required: Boolean(image.required),
|
||||
}))
|
||||
const runtimeImageJson = JSON.stringify(runtimeImageArtifacts)
|
||||
|
||||
const existsOutput = await runSshOutput(target, [
|
||||
`ROOT=${quoteShellSingle(target.workspaceRoot)}`,
|
||||
...expandRemotePathVar('ROOT'),
|
||||
@@ -4172,43 +4257,7 @@ async function startRemoteWorkspaceModule(
|
||||
? await ensureRemoteNav2RuntimeAssets(target)
|
||||
: await ensureRemoteStudioRuntimeAssets(target)
|
||||
const fastDdsConfigPath = await prepareRemoteRosNetworkConfig(target, runtimeRoot, rosNetwork, rosDomainId)
|
||||
await runSsh(
|
||||
target,
|
||||
[
|
||||
'set -e',
|
||||
`RUNTIME_IMAGE_JSON="$(printf %s ${quoteShellSingle(encodeBase64(runtimeImageJson))} | base64 -d)"`,
|
||||
'ARTIFACT_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/autonomy-studio/artifacts"',
|
||||
'mkdir -p "$ARTIFACT_CACHE"',
|
||||
'python3 - "$RUNTIME_IMAGE_JSON" "$ARTIFACT_CACHE" <<\'PY\'',
|
||||
'import json, os, subprocess, sys',
|
||||
'images = json.loads(sys.argv[1] or "[]")',
|
||||
'cache = sys.argv[2]',
|
||||
'for item in images:',
|
||||
' image = (item.get("image") or "").strip()',
|
||||
' if not image: continue',
|
||||
' exists = subprocess.run(["docker", "image", "inspect", image], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL).returncode == 0',
|
||||
' if exists: continue',
|
||||
' url = (item.get("url") or "").strip()',
|
||||
' if not url:',
|
||||
' if item.get("required"): raise SystemExit(f"runtime image artifact url is missing: {image}")',
|
||||
' continue',
|
||||
' name = item.get("fileName") or os.path.basename(url.split("?", 1)[0]) or "runtime-image.tar"',
|
||||
' path = os.path.join(cache, name)',
|
||||
' if not os.path.exists(path): subprocess.run(["curl", "-fL", url, "-o", path], check=True)',
|
||||
' expected = (item.get("checksumSha256") or "").strip().lower()',
|
||||
' if expected:',
|
||||
' import hashlib',
|
||||
' with open(path, "rb") as fp: actual = hashlib.sha256(fp.read()).hexdigest().lower()',
|
||||
' if actual != expected: raise SystemExit(f"runtime image checksum mismatch: {name}")',
|
||||
' if path.lower().endswith(".tar.zst") or path.lower().endswith(".tzst"):',
|
||||
' import shlex',
|
||||
' subprocess.run(f"zstd -dc {shlex.quote(path)} | docker load", shell=True, check=True)',
|
||||
' else:',
|
||||
' subprocess.run(["docker", "load", "-i", path], check=True)',
|
||||
'PY'
|
||||
].join('\n'),
|
||||
900000
|
||||
)
|
||||
await prepareRemoteRuntimeImages(target, runtimeImages)
|
||||
const currentStatus = await runSshOutput(target, `timeout 12s docker ps -a --filter ${quoteShellSingle(`name=^/${dockerInfo.containerKey}$`)} --format "{{.Status}}" 2>/dev/null || true`, 20000)
|
||||
if (/^Up\b/i.test(currentStatus.trim())) return { ok: true, message: '이미 실행 중입니다.' }
|
||||
if (currentStatus.trim()) {
|
||||
@@ -4257,9 +4306,11 @@ async function startRemoteWorkspaceModule(
|
||||
...expandRemotePathVar('ROOT'),
|
||||
`PARAMS=${quoteShellSingle(remoteRosParams)}`,
|
||||
`FASTDDS_CONFIG_PATH=${quoteShellSingle(fastDdsConfigPath ?? '')}`,
|
||||
`ROS_LOCALHOST_ONLY=${rosNetworkEnabled(rosNetwork) ? '0' : '1'}`,
|
||||
...expandRemotePathVar('FASTDDS_CONFIG_PATH'),
|
||||
'read -r -a EXTRA_ARGS <<< "$PARAMS"',
|
||||
'export FASTDDS_CONFIG_PATH',
|
||||
'export ROS_LOCALHOST_ONLY',
|
||||
`bash "$RUNTIME_ROOT/docker/docker_run_nav2.sh" ${quoteShellSingle(dockerInfo.containerKey)} "$ROOT" ${quoteShellSingle(targetFolderName)} ${quoteShellSingle(runtimeImage!)} ${quoteShellSingle(String(rosDomainId ?? process.env.ROS_DOMAIN_ID ?? '0').trim() || '0')} ${gpuEnabled ? 'true' : 'false'} "\${EXTRA_ARGS[@]}"`
|
||||
].join('\n')
|
||||
: [
|
||||
@@ -4298,6 +4349,7 @@ async function buildRemoteWorkspaceModule(
|
||||
targetFolderName,
|
||||
await resolveRemoteNav2RuntimeImage(runtimeImages),
|
||||
)
|
||||
await prepareRemoteRuntimeImages(target, runtimeImages)
|
||||
await runSsh(target, [
|
||||
'set -e',
|
||||
`RUNTIME_ROOT=${quoteShellSingle(runtimeRoot)}`,
|
||||
|
||||
Reference in New Issue
Block a user