@rainbow-robotics/muscat-plugin-kit 1.10.1-dev.1 → 1.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +282 -282
- package/cli/README.md +274 -274
- package/cli/bin/create-muscat-plugin.mjs +8 -8
- package/cli/bin/muscat-pk.mjs +155 -155
- package/cli/bundle-templates.mjs +31 -31
- package/cli/docker/Dockerfile.cpp +28 -28
- package/cli/docker/Dockerfile.dev +30 -30
- package/cli/docker/Dockerfile.python +21 -21
- package/cli/m5-e2e.mjs +69 -69
- package/cli/prepack.mjs +10 -10
- package/cli/src/ai-rules.mjs +70 -70
- package/cli/src/build.mjs +447 -447
- package/cli/src/check-package.mjs +13 -13
- package/cli/src/completion.mjs +166 -166
- package/cli/src/cpp.mjs +78 -78
- package/cli/src/cppIde.mjs +75 -75
- package/cli/src/dev.mjs +165 -165
- package/cli/src/devBack.mjs +734 -727
- package/cli/src/devUi.mjs +247 -243
- package/cli/src/esbuild.mjs +56 -56
- package/cli/src/host_agent.py +223 -223
- package/cli/src/manifest.mjs +287 -287
- package/cli/src/mcp.mjs +1071 -1071
- package/cli/src/muscatLocal.mjs +209 -209
- package/cli/src/new.mjs +343 -343
- package/cli/src/pluginDevServer.mjs +111 -111
- package/cli/src/ports.mjs +193 -193
- package/cli/src/python.mjs +112 -112
- package/cli/src/ros2.mjs +109 -109
- package/cli/src/sdk.mjs +117 -117
- package/cli/src/storybook.mjs +168 -168
- package/cli/src/type.mjs +141 -141
- package/cli/src/uninstall.mjs +81 -81
- package/cli/src/update.mjs +295 -295
- package/cli/src/util.mjs +441 -441
- package/cli/src/zenohDev.mjs +83 -81
- package/cli/storybook-dist/_design-tokens.css +4957 -4957
- package/cli/storybook-dist/_stories-src/NxAvatar.stories.tsx +27 -27
- package/cli/storybook-dist/_stories-src/NxPagination.stories.tsx +28 -28
- package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordion.stories.tsx +46 -46
- package/cli/storybook-dist/_stories-src/RBAccordion/RBAccordionGroup.stories.tsx +73 -73
- package/cli/storybook-dist/_stories-src/RBBadge/RBBadge.stories.tsx +93 -93
- package/cli/storybook-dist/_stories-src/RBBattery/RBBattery.stories.tsx +74 -74
- package/cli/storybook-dist/_stories-src/RBButton/RBCheckIconButton.stories.tsx +28 -28
- package/cli/storybook-dist/_stories-src/RBButton/RBCheckLabelButton.stories.tsx +84 -84
- package/cli/storybook-dist/_stories-src/RBButton/RBIconButton.stories.tsx +57 -57
- package/cli/storybook-dist/_stories-src/RBButton/RBInlineLabelButton.stories.tsx +41 -41
- package/cli/storybook-dist/_stories-src/RBButton/RBLabelButton.stories.tsx +60 -60
- package/cli/storybook-dist/_stories-src/RBDatePicker/RBDateInput.stories.tsx +44 -44
- package/cli/storybook-dist/_stories-src/RBDatePicker/RBRangeDateInput.stories.tsx +56 -56
- package/cli/storybook-dist/_stories-src/RBDialog/RBAlert.stories.tsx +51 -51
- package/cli/storybook-dist/_stories-src/RBDialog/RBConfirm.stories.tsx +55 -55
- package/cli/storybook-dist/_stories-src/RBDialog/RBPrompt.stories.tsx +52 -52
- package/cli/storybook-dist/_stories-src/RBDivider/RBDivider.stories.tsx +93 -93
- package/cli/storybook-dist/_stories-src/RBEmptyView/RBEmptyView.stories.tsx +49 -49
- package/cli/storybook-dist/_stories-src/RBIcons/RBColorIcons.stories.tsx +108 -108
- package/cli/storybook-dist/_stories-src/RBIcons/RBIcons.stories.tsx +106 -106
- package/cli/storybook-dist/_stories-src/RBInput/RBCheckBox.stories.tsx +89 -89
- package/cli/storybook-dist/_stories-src/RBInput/RBMultiRangeInput.stories.tsx +64 -64
- package/cli/storybook-dist/_stories-src/RBInput/RBRadioBox.stories.tsx +65 -65
- package/cli/storybook-dist/_stories-src/RBInput/RBRadioButton.stories.tsx +68 -68
- package/cli/storybook-dist/_stories-src/RBInput/RBSelectBox.stories.tsx +216 -216
- package/cli/storybook-dist/_stories-src/RBInput/RBSideNumberInput.stories.tsx +90 -90
- package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextArea.stories.tsx +45 -45
- package/cli/storybook-dist/_stories-src/RBInput/RBText/RBTextInput.stories.tsx +50 -50
- package/cli/storybook-dist/_stories-src/RBInput/RBTextField.stories.tsx +102 -102
- package/cli/storybook-dist/_stories-src/RBJoyStick/RBJoyStick.stories.tsx +31 -31
- package/cli/storybook-dist/_stories-src/RBLight/RBLight.stories.tsx +36 -36
- package/cli/storybook-dist/_stories-src/RBPagination/RBPagination.stories.tsx +49 -49
- package/cli/storybook-dist/_stories-src/RBShadcn/RBCard/RBCard.stories.tsx +89 -89
- package/cli/storybook-dist/_stories-src/RBShadcn/RBCommand/RBCommand.stories.tsx +116 -116
- package/cli/storybook-dist/_stories-src/RBShadcn/RBDialog/RBDialog.stories.tsx +74 -74
- package/cli/storybook-dist/_stories-src/RBShadcn/RBDrawer/RBDrawer.stories.tsx +99 -99
- package/cli/storybook-dist/_stories-src/RBShadcn/RBDropdownMenu/RBDropdownMenu.stories.tsx +123 -123
- package/cli/storybook-dist/_stories-src/RBShadcn/RBMenubar/RBMenubar.stories.tsx +135 -135
- package/cli/storybook-dist/_stories-src/RBShadcn/RBPopover/RBPopover.stories.tsx +69 -69
- package/cli/storybook-dist/_stories-src/RBShadcn/RBResizable/RBResizable.stories.tsx +49 -49
- package/cli/storybook-dist/_stories-src/RBShadcn/RBSheet/RBSheet.stories.tsx +91 -91
- package/cli/storybook-dist/_stories-src/RBShadcn/RBSlider/RBSlider.stories.tsx +38 -38
- package/cli/storybook-dist/_stories-src/RBShadcn/RBSwitch/RBSwitch.stories.tsx +112 -112
- package/cli/storybook-dist/_stories-src/RBShadcn/RBTabs/RBTabs.stories.tsx +75 -75
- package/cli/storybook-dist/_stories-src/RBShadcn/RBToggle/RBToggle.stories.tsx +51 -51
- package/cli/storybook-dist/_stories-src/RBShadcn/RBToggleGroup/RBToggleGroup.stories.tsx +84 -84
- package/cli/storybook-dist/_stories-src/RBTab/RBChipTab.stories.tsx +102 -102
- package/cli/storybook-dist/_stories-src/RBTab/RBChipsBoxTab.stories.tsx +78 -78
- package/cli/storybook-dist/_stories-src/RBTab/RBTab.stories.tsx +57 -57
- package/cli/storybook-dist/_stories-src/RBTable/RBTable.stories.tsx +177 -177
- package/cli/storybook-dist/_stories-src/RBTable/RBTableRow.stories.tsx +64 -64
- package/cli/storybook-dist/_stories-src/RBToast/RBToast.stories.tsx +117 -117
- package/cli/storybook-dist/_stories-src/RBTooltip/RBTooltip.stories.tsx +50 -50
- package/cli/storybook-dist/_stories-src/loading/RBSpinner.stories.tsx +30 -30
- package/cli/storybook-dist/_stories-src/nx-button/NxButton.stories.tsx +54 -54
- package/cli/storybook-dist/_stories-src/nx-button/NxPointerHoldButton.stories.tsx +57 -57
- package/cli/storybook-dist/_stories-src/nx-icons/NxIcons.stories.tsx +48 -48
- package/cli/storybook-dist/_stories-src/nx-inputs/NxNumberInput.stories.tsx +103 -103
- package/cli/storybook-dist/_stories-src/nx-inputs/NxRangeInput.stories.tsx +27 -27
- package/cli/storybook-dist/_stories-src/nx-inputs/NxSelectBox.stories.tsx +67 -67
- package/cli/storybook-dist/_stories-src/nx-inputs/NxSlideRange.stories.tsx +45 -45
- package/cli/storybook-dist/_stories-src/nx-inputs/NxSwitch.stories.tsx +27 -27
- package/cli/storybook-dist/_stories-src/nx-inputs/NxTextArea.stories.tsx +48 -48
- package/cli/storybook-dist/_stories-src/nx-inputs/NxTextInput.stories.tsx +46 -46
- package/cli/storybook-dist/assets/{Color-YHDXOIA2-D6JqZzDJ.js → Color-YHDXOIA2-B4IER17l.js} +1 -1
- package/cli/storybook-dist/assets/{Docs-ESb7JAHg.js → Docs-C8GCHFtK.js} +3 -3
- package/cli/storybook-dist/assets/{Docs-6gD2kUo1.js → Docs-CgdEMPQP.js} +2 -2
- package/cli/storybook-dist/assets/{Docs-B9wd8Ok7.js → Docs-Ck3WHvk6.js} +3 -3
- package/cli/storybook-dist/assets/{Docs-CgFbWWXM.js → Docs-D9NHsoeZ.js} +2 -2
- package/cli/storybook-dist/assets/{Docs-DBQup8Bu.js → Docs-DNYQC7nl.js} +3 -3
- package/cli/storybook-dist/assets/{Docs-CMjovnSY.js → Docs-DUfeiM4I.js} +4 -4
- package/cli/storybook-dist/assets/{DocsRenderer-CFRXHY34-BOUI1AHi.js → DocsRenderer-CFRXHY34-C0WhyDoR.js} +1 -1
- package/cli/storybook-dist/assets/{NxAvatar.stories-DsYFMUh3.js → NxAvatar.stories-CwnK8AUF.js} +1 -1
- package/cli/storybook-dist/assets/{NxButton.stories-CHfNcqsc.js → NxButton.stories-ueYdosfQ.js} +8 -8
- package/cli/storybook-dist/assets/{NxNumberInput.stories-BFw_4vFE.js → NxNumberInput.stories-M5ZCLbrK.js} +1 -1
- package/cli/storybook-dist/assets/{NxPagination.stories-DVfxyw4M.js → NxPagination.stories-bxxUnHEL.js} +1 -1
- package/cli/storybook-dist/assets/{NxPointerHoldButton.stories-BzyxUWoZ.js → NxPointerHoldButton.stories-BHxB2kr5.js} +1 -1
- package/cli/storybook-dist/assets/{NxRangeInput.stories-DmRdzBc1.js → NxRangeInput.stories-BKfr-fp4.js} +6 -6
- package/cli/storybook-dist/assets/{NxSelectBox.stories-B1KkNUOf.js → NxSelectBox.stories-CG8FuTBq.js} +1 -1
- package/cli/storybook-dist/assets/{NxSlideRange.stories-CyOdvfQ6.js → NxSlideRange.stories-BRTFtMTL.js} +1 -1
- package/cli/storybook-dist/assets/{NxSwitch.stories-DrDBw0qp.js → NxSwitch.stories-Bwuyzn-R.js} +1 -1
- package/cli/storybook-dist/assets/{NxTextArea.stories-DTB2aqNS.js → NxTextArea.stories-COq2sGqu.js} +1 -1
- package/cli/storybook-dist/assets/{NxTextInput.stories-DU0rucmM.js → NxTextInput.stories-CjsBLz05.js} +1 -1
- package/cli/storybook-dist/assets/{RBAccordion.stories-DTfOtXtp.js → RBAccordion.stories-BARwuZ0Q.js} +3 -3
- package/cli/storybook-dist/assets/{RBAccordionGroup.stories-9y9BjrUe.js → RBAccordionGroup.stories-COrPxETW.js} +10 -10
- package/cli/storybook-dist/assets/{RBAlert.stories-DLTAv9fT.js → RBAlert.stories-BQrkjZPt.js} +3 -3
- package/cli/storybook-dist/assets/{RBBadge.stories-BNMACewA.js → RBBadge.stories-BTsLL-lK.js} +1 -1
- package/cli/storybook-dist/assets/{RBBattery.stories-B3kXMAt5.js → RBBattery.stories-DAeQdJbc.js} +1 -1
- package/cli/storybook-dist/assets/{RBCard.stories-Y-s8R2n7.js → RBCard.stories-HyhOE80R.js} +39 -39
- package/cli/storybook-dist/assets/{RBCheckBox.stories-DBuT9tL4.js → RBCheckBox.stories-KFk0WqX_.js} +1 -1
- package/cli/storybook-dist/assets/{RBCheckIconButton.stories-CWu1TL56.js → RBCheckIconButton.stories-CXbbktF5.js} +1 -1
- package/cli/storybook-dist/assets/{RBCheckLabelButton.stories-sCu2MGCX.js → RBCheckLabelButton.stories-DWfYz_9Z.js} +17 -17
- package/cli/storybook-dist/assets/{RBChipTab.stories-BX9qLJkR.js → RBChipTab.stories-JPEgVFAS.js} +5 -5
- package/cli/storybook-dist/assets/{RBChipsBoxTab.stories-35u1aJ-P.js → RBChipsBoxTab.stories-CTPuWXrQ.js} +1 -1
- package/cli/storybook-dist/assets/{RBColorIcons.stories-DAOZ5Ies.js → RBColorIcons.stories-DiFRZ5jk.js} +1 -1
- package/cli/storybook-dist/assets/RBCommand.stories-Pf1HAVIR.js +73 -0
- package/cli/storybook-dist/assets/{RBConfirm.stories-iMXcUh7Z.js → RBConfirm.stories-D1cEPUXk.js} +3 -3
- package/cli/storybook-dist/assets/{RBDateInput.stories-hK5rXECf.js → RBDateInput.stories-RPYpCjig.js} +1 -1
- package/cli/storybook-dist/assets/RBDialog.stories-DJH-0XSR.js +38 -0
- package/cli/storybook-dist/assets/{RBDivider.stories-BAK-iZ1Z.js → RBDivider.stories-uEj1vOgK.js} +1 -1
- package/cli/storybook-dist/assets/{RBDrawer.stories-B2nqf2Nh.js → RBDrawer.stories-BiMzElb2.js} +12 -12
- package/cli/storybook-dist/assets/RBDropdownMenu.stories-Da_TBkla.js +86 -0
- package/cli/storybook-dist/assets/{RBEmptyView.stories-nCenrgBh.js → RBEmptyView.stories-BHeKGfPS.js} +1 -1
- package/cli/storybook-dist/assets/{RBIconButton.stories-DRH7O3sp.js → RBIconButton.stories-NEsRFoYp.js} +1 -1
- package/cli/storybook-dist/assets/{RBIcons.stories-D3PyJXG8.js → RBIcons.stories-CcZ-TTLS.js} +1 -1
- package/cli/storybook-dist/assets/{RBInlineLabelButton.stories-modQbt10.js → RBInlineLabelButton.stories-B0wpdQyR.js} +1 -1
- package/cli/storybook-dist/assets/{RBJoyStick.stories-BY14qVgR.js → RBJoyStick.stories-DEgBou3K.js} +1 -1
- package/cli/storybook-dist/assets/{RBLabelButton.stories-DzF7vGwm.js → RBLabelButton.stories-DT8drIsF.js} +1 -1
- package/cli/storybook-dist/assets/{RBLight.stories-lF4FRmVh.js → RBLight.stories-Dh-aoeyG.js} +1 -1
- package/cli/storybook-dist/assets/{RBMenubar.stories-XhrI7bsV.js → RBMenubar.stories-D-H7UqVh.js} +1 -1
- package/cli/storybook-dist/assets/{RBMultiRangeInput.stories-DprllQDl.js → RBMultiRangeInput.stories-hlclv6jI.js} +1 -1
- package/cli/storybook-dist/assets/{RBPagination.stories-2v2gBJfC.js → RBPagination.stories-DWCeoZ_S.js} +1 -1
- package/cli/storybook-dist/assets/{RBPopover.stories-C8iLIZpv.js → RBPopover.stories-C514M09_.js} +1 -1
- package/cli/storybook-dist/assets/{RBPrompt.stories-Th_HrWHI.js → RBPrompt.stories-BSItvfiT.js} +1 -1
- package/cli/storybook-dist/assets/{RBRadioBox.stories-CoHuLaN3.js → RBRadioBox.stories-BBb4YbFU.js} +1 -1
- package/cli/storybook-dist/assets/{RBRadioButton.stories-DULxE_ff.js → RBRadioButton.stories-CXyOGJCv.js} +1 -1
- package/cli/storybook-dist/assets/{RBRangeDateInput.stories-D8A9KBSS.js → RBRangeDateInput.stories-qvhubu0e.js} +1 -1
- package/cli/storybook-dist/assets/{RBResizable.stories-C41QfrQ3.js → RBResizable.stories-CZKeyGjH.js} +14 -14
- package/cli/storybook-dist/assets/{RBSelectBox.stories-C1gNmwzw.js → RBSelectBox.stories-CRKP3ybf.js} +1 -1
- package/cli/storybook-dist/assets/RBSheet.stories-sCM3ZJUa.js +39 -0
- package/cli/storybook-dist/assets/{RBSideNumberInput.stories-9LlgQ8nh.js → RBSideNumberInput.stories-Bb7gw8RF.js} +3 -3
- package/cli/storybook-dist/assets/{RBSlider.stories-oUgo5K9d.js → RBSlider.stories-23OAQU5z.js} +3 -3
- package/cli/storybook-dist/assets/{RBSpinner.stories-DDpohbY2.js → RBSpinner.stories-2xcQdcmt.js} +1 -1
- package/cli/storybook-dist/assets/{RBSwitch.stories-gW-GN7QO.js → RBSwitch.stories-BACUaJm_.js} +7 -7
- package/cli/storybook-dist/assets/{RBTab.stories-CN-PV3HH.js → RBTab.stories-CDHE3sVs.js} +1 -1
- package/cli/storybook-dist/assets/{RBTable.stories-BAZLpxtM.js → RBTable.stories-C0Cfhfl7.js} +29 -29
- package/cli/storybook-dist/assets/{RBTableRow.stories-CwjVrszB.js → RBTableRow.stories-BF8kIkpp.js} +1 -1
- package/cli/storybook-dist/assets/{RBTabs.stories-JuEo2sUL.js → RBTabs.stories-BdJWRq1o.js} +7 -7
- package/cli/storybook-dist/assets/{RBTextArea.stories-D0_Pv4qZ.js → RBTextArea.stories-CGy2xeYH.js} +3 -3
- package/cli/storybook-dist/assets/{RBTextField.stories-CCsOvZqV.js → RBTextField.stories-IfUOzxCM.js} +15 -15
- package/cli/storybook-dist/assets/{RBTextInput.stories-Cccn3VNm.js → RBTextInput.stories-VjXNFo4R.js} +3 -3
- package/cli/storybook-dist/assets/{RBToast.stories-I5617t0K.js → RBToast.stories-BzcobblV.js} +1 -1
- package/cli/storybook-dist/assets/{RBToggle.stories-DsgKjGsJ.js → RBToggle.stories-DuLE5mKv.js} +4 -4
- package/cli/storybook-dist/assets/{RBToggleGroup.stories-BZEzSDuz.js → RBToggleGroup.stories-CuR0N0GJ.js} +5 -5
- package/cli/storybook-dist/assets/{RBTooltip.stories-4oSyAWsA.js → RBTooltip.stories--aAgNC8_.js} +1 -1
- package/cli/storybook-dist/assets/{SideNumberInput-DSKbd0bG.js → SideNumberInput-DyWQxYUj.js} +743 -743
- package/cli/storybook-dist/assets/{entry-preview-B35XREGs.js → entry-preview-D7IHsza_.js} +1 -1
- package/cli/storybook-dist/assets/{iframe-BB797lmL.js → iframe-CbaIp5I-.js} +2 -2
- package/cli/storybook-dist/assets/{index-BU2567EP.js → index-C6CBgMuG.js} +3 -3
- package/cli/storybook-dist/assets/{preview-C7DaSWyc.js → preview-B-iYLDuW.js} +2 -2
- package/cli/storybook-dist/assets/{preview-CbuRHit4.js → preview-CRWqc9DH.js} +1 -1
- package/cli/storybook-dist/assets/{react-resizable-panels.browser-ChIvlwo7.js → react-resizable-panels.browser-8hC8dr-6.js} +8 -8
- package/cli/storybook-dist/iframe.html +2 -2
- package/cli/storybook-dist/index.html +1 -1
- package/cli/storybook-dist/project.json +1 -1
- package/cli/templates/common/.mcp.json +8 -8
- package/cli/templates/common/README.md +1231 -1231
- package/cli/templates/common/assets/icon.svg +45 -45
- package/cli/templates/common/manifest.json +26 -26
- package/cli/templates/common/package.json +22 -22
- package/cli/templates/common/py/main.py +125 -125
- package/cli/templates/common/tsconfig.json +13 -13
- package/cli/templates/common/ui/content.tsx +216 -216
- package/cli/templates/common/ui/style.css +6 -6
- package/cli/templates/common/vite.config.ts +10 -10
- package/cli/templates/cpp/cpp/main.cpp +73 -73
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/__PLUGIN_SLUG__/node.py +25 -25
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/package.xml +10 -10
- package/cli/templates/ros2/ros2_ws/src/__PLUGIN_SLUG__/setup.py +18 -18
- package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/package.xml +11 -11
- package/cli/templates/ros2-cpp/ros2_ws/src/__PLUGIN_SLUG__/src/node.cpp +23 -23
- package/cli/vite.d.mts +25 -25
- package/cli/vite.mjs +292 -292
- package/package.json +3 -3
- package/cli/storybook-dist/assets/RBCommand.stories-BSqXDkKw.js +0 -73
- package/cli/storybook-dist/assets/RBDialog.stories-BAktz-ku.js +0 -38
- package/cli/storybook-dist/assets/RBDropdownMenu.stories-DbW2fWAj.js +0 -86
- package/cli/storybook-dist/assets/RBSheet.stories-_6VH-RUm.js +0 -39
package/cli/src/python.mjs
CHANGED
|
@@ -1,112 +1,112 @@
|
|
|
1
|
-
// Shared Python Docker helpers (Ubuntu 24.04 + Python 3.12 — matches the robot).
|
|
2
|
-
// Used by dev:back (hot-reload) and build (lock + wheel vendor).
|
|
3
|
-
import { spawnSync } from "node:child_process";
|
|
4
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
-
import { dirname, resolve } from "node:path";
|
|
6
|
-
import { canSkipImageBuild, imageKey, imageKeyLabel, dockerUserFlags, hasBin, log, pruneMuskatImages, resolveDockerfile, buildCacheFlags } from "./util.mjs";
|
|
7
|
-
|
|
8
|
-
export const PYTHON_DEV_IMAGE = "muscat-pk-python-dev";
|
|
9
|
-
|
|
10
|
-
export function assertDockerReady() {
|
|
11
|
-
if (!hasBin("docker")) {
|
|
12
|
-
throw new Error("Docker not found. The Python backend runs in a container (Ubuntu 24.04 + Python 3.12).");
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Build the dev image from the kit's bundled Dockerfile (Docker layer-caches). */
|
|
17
|
-
export async function ensureImage(image = PYTHON_DEV_IMAGE, { platform = null } = {}) {
|
|
18
|
-
const dockerfile = await resolveDockerfile("Dockerfile.python");
|
|
19
|
-
const key = imageKey(dockerfile);
|
|
20
|
-
if (canSkipImageBuild(image, key)) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
log.step(`docker build -t ${image}${platform ? ` (${platform})` : ""} (first run is slow, then cached)…`);
|
|
24
|
-
const platformFlags = platform ? ["--platform", platform] : [];
|
|
25
|
-
const r = spawnSync(
|
|
26
|
-
"docker",
|
|
27
|
-
["build", ...buildCacheFlags(), ...platformFlags, ...imageKeyLabel(key), "-t", image, "-f", dockerfile, dirname(dockerfile)],
|
|
28
|
-
{ stdio: "inherit" },
|
|
29
|
-
);
|
|
30
|
-
if (r.status !== 0) throw new Error("docker build failed.");
|
|
31
|
-
pruneMuskatImages();
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// pip --platform does NOT auto-expand manylinux tags (unlike sys_tags()) — exact match only.
|
|
35
|
-
// Specifying all common glibc milestones ensures packages built for any Ubuntu era are found.
|
|
36
|
-
const PIP_PLATFORM = {
|
|
37
|
-
amd64: [
|
|
38
|
-
"manylinux_2_5_x86_64",
|
|
39
|
-
"manylinux_2_17_x86_64",
|
|
40
|
-
"manylinux_2_27_x86_64",
|
|
41
|
-
"manylinux_2_28_x86_64",
|
|
42
|
-
"manylinux_2_34_x86_64",
|
|
43
|
-
"manylinux_2_35_x86_64",
|
|
44
|
-
"manylinux_2_39_x86_64",
|
|
45
|
-
"linux_x86_64",
|
|
46
|
-
],
|
|
47
|
-
arm64: [
|
|
48
|
-
"manylinux_2_17_aarch64",
|
|
49
|
-
"manylinux_2_27_aarch64",
|
|
50
|
-
"manylinux_2_28_aarch64",
|
|
51
|
-
"manylinux_2_34_aarch64",
|
|
52
|
-
"manylinux_2_35_aarch64",
|
|
53
|
-
"manylinux_2_39_aarch64",
|
|
54
|
-
"linux_aarch64",
|
|
55
|
-
],
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export async function lockAndVendor(pluginRoot, archs = ["amd64", "arm64"]) {
|
|
59
|
-
// ponytail: pip --platform used intentionally (not QEMU) — downloads wheels without running target arch code.
|
|
60
|
-
const reqTxt = resolve(pluginRoot, "requirements.txt");
|
|
61
|
-
if (!existsSync(reqTxt)) {
|
|
62
|
-
log.warn("requirements.txt not found — lock/vendor skipped");
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
await ensureImage();
|
|
67
|
-
|
|
68
|
-
// A pinned pre-release (e.g. rainbow-rb-sdk==0.1.4.dev1) pins its sibling rainbow-rb-*
|
|
69
|
-
// pre-releases too; uv rejects those transitive pre-releases unless allowed.
|
|
70
|
-
const reqText = readFileSync(resolve(pluginRoot, "requirements.txt"), "utf8");
|
|
71
|
-
const prerelease = /==\s*\S*\d(a|b|rc|\.dev)\d*\s*$/im.test(reqText) ? " --prerelease=allow" : "";
|
|
72
|
-
log.step(`generating lock (uv pip compile${prerelease})…`);
|
|
73
|
-
const lockResult = spawnSync(
|
|
74
|
-
"docker",
|
|
75
|
-
[
|
|
76
|
-
"run", "--rm",
|
|
77
|
-
...dockerUserFlags(),
|
|
78
|
-
"-v", `${pluginRoot}:/plugin`,
|
|
79
|
-
PYTHON_DEV_IMAGE,
|
|
80
|
-
"bash", "-c",
|
|
81
|
-
"set -e && cd /plugin && mkdir -p dist && uv pip compile requirements.txt --python-version 3.12 -o dist/requirements.lock" + prerelease,
|
|
82
|
-
],
|
|
83
|
-
{ stdio: "inherit" },
|
|
84
|
-
);
|
|
85
|
-
if (lockResult.status !== 0) throw new Error("lock generation failed (uv pip compile)");
|
|
86
|
-
log.ok("dist/requirements.lock ready");
|
|
87
|
-
|
|
88
|
-
for (const arch of archs) {
|
|
89
|
-
const pipPlatforms = PIP_PLATFORM[arch];
|
|
90
|
-
if (!pipPlatforms) throw new Error(`unknown arch '${arch}'`);
|
|
91
|
-
log.step(`vendoring wheels for ${arch}…`);
|
|
92
|
-
const r = spawnSync(
|
|
93
|
-
"docker",
|
|
94
|
-
[
|
|
95
|
-
"run", "--rm",
|
|
96
|
-
...dockerUserFlags(),
|
|
97
|
-
"-e", "PIP_ROOT_USER_ACTION=ignore",
|
|
98
|
-
"-v", `${pluginRoot}:/plugin`,
|
|
99
|
-
"-w", "/plugin",
|
|
100
|
-
PYTHON_DEV_IMAGE,
|
|
101
|
-
"bash", "-c",
|
|
102
|
-
"mkdir -p dist/wheels && pip3 download -r dist/requirements.lock --no-deps --only-binary=:all: -d dist/wheels -q " +
|
|
103
|
-
"--python-version 312 --implementation cp --abi cp312 " +
|
|
104
|
-
pipPlatforms.map((p) => `--platform ${p}`).join(" "),
|
|
105
|
-
],
|
|
106
|
-
{ stdio: "inherit" },
|
|
107
|
-
);
|
|
108
|
-
// A ZIP missing wheels fails to import on the robot — stop instead.
|
|
109
|
-
if (r.status !== 0) throw new Error(`wheel download failed for ${arch} (see pip output above)`);
|
|
110
|
-
log.ok(`dist/wheels/ ready (${arch})`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
1
|
+
// Shared Python Docker helpers (Ubuntu 24.04 + Python 3.12 — matches the robot).
|
|
2
|
+
// Used by dev:back (hot-reload) and build (lock + wheel vendor).
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
+
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { canSkipImageBuild, imageKey, imageKeyLabel, dockerUserFlags, hasBin, log, pruneMuskatImages, resolveDockerfile, buildCacheFlags } from "./util.mjs";
|
|
7
|
+
|
|
8
|
+
export const PYTHON_DEV_IMAGE = "muscat-pk-python-dev";
|
|
9
|
+
|
|
10
|
+
export function assertDockerReady() {
|
|
11
|
+
if (!hasBin("docker")) {
|
|
12
|
+
throw new Error("Docker not found. The Python backend runs in a container (Ubuntu 24.04 + Python 3.12).");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** Build the dev image from the kit's bundled Dockerfile (Docker layer-caches). */
|
|
17
|
+
export async function ensureImage(image = PYTHON_DEV_IMAGE, { platform = null } = {}) {
|
|
18
|
+
const dockerfile = await resolveDockerfile("Dockerfile.python");
|
|
19
|
+
const key = imageKey(dockerfile);
|
|
20
|
+
if (canSkipImageBuild(image, key)) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
log.step(`docker build -t ${image}${platform ? ` (${platform})` : ""} (first run is slow, then cached)…`);
|
|
24
|
+
const platformFlags = platform ? ["--platform", platform] : [];
|
|
25
|
+
const r = spawnSync(
|
|
26
|
+
"docker",
|
|
27
|
+
["build", ...buildCacheFlags(), ...platformFlags, ...imageKeyLabel(key), "-t", image, "-f", dockerfile, dirname(dockerfile)],
|
|
28
|
+
{ stdio: "inherit" },
|
|
29
|
+
);
|
|
30
|
+
if (r.status !== 0) throw new Error("docker build failed.");
|
|
31
|
+
pruneMuskatImages();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// pip --platform does NOT auto-expand manylinux tags (unlike sys_tags()) — exact match only.
|
|
35
|
+
// Specifying all common glibc milestones ensures packages built for any Ubuntu era are found.
|
|
36
|
+
const PIP_PLATFORM = {
|
|
37
|
+
amd64: [
|
|
38
|
+
"manylinux_2_5_x86_64",
|
|
39
|
+
"manylinux_2_17_x86_64",
|
|
40
|
+
"manylinux_2_27_x86_64",
|
|
41
|
+
"manylinux_2_28_x86_64",
|
|
42
|
+
"manylinux_2_34_x86_64",
|
|
43
|
+
"manylinux_2_35_x86_64",
|
|
44
|
+
"manylinux_2_39_x86_64",
|
|
45
|
+
"linux_x86_64",
|
|
46
|
+
],
|
|
47
|
+
arm64: [
|
|
48
|
+
"manylinux_2_17_aarch64",
|
|
49
|
+
"manylinux_2_27_aarch64",
|
|
50
|
+
"manylinux_2_28_aarch64",
|
|
51
|
+
"manylinux_2_34_aarch64",
|
|
52
|
+
"manylinux_2_35_aarch64",
|
|
53
|
+
"manylinux_2_39_aarch64",
|
|
54
|
+
"linux_aarch64",
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export async function lockAndVendor(pluginRoot, archs = ["amd64", "arm64"]) {
|
|
59
|
+
// ponytail: pip --platform used intentionally (not QEMU) — downloads wheels without running target arch code.
|
|
60
|
+
const reqTxt = resolve(pluginRoot, "requirements.txt");
|
|
61
|
+
if (!existsSync(reqTxt)) {
|
|
62
|
+
log.warn("requirements.txt not found — lock/vendor skipped");
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
await ensureImage();
|
|
67
|
+
|
|
68
|
+
// A pinned pre-release (e.g. rainbow-rb-sdk==0.1.4.dev1) pins its sibling rainbow-rb-*
|
|
69
|
+
// pre-releases too; uv rejects those transitive pre-releases unless allowed.
|
|
70
|
+
const reqText = readFileSync(resolve(pluginRoot, "requirements.txt"), "utf8");
|
|
71
|
+
const prerelease = /==\s*\S*\d(a|b|rc|\.dev)\d*\s*$/im.test(reqText) ? " --prerelease=allow" : "";
|
|
72
|
+
log.step(`generating lock (uv pip compile${prerelease})…`);
|
|
73
|
+
const lockResult = spawnSync(
|
|
74
|
+
"docker",
|
|
75
|
+
[
|
|
76
|
+
"run", "--rm",
|
|
77
|
+
...dockerUserFlags(),
|
|
78
|
+
"-v", `${pluginRoot}:/plugin`,
|
|
79
|
+
PYTHON_DEV_IMAGE,
|
|
80
|
+
"bash", "-c",
|
|
81
|
+
"set -e && cd /plugin && mkdir -p dist && uv pip compile requirements.txt --python-version 3.12 -o dist/requirements.lock" + prerelease,
|
|
82
|
+
],
|
|
83
|
+
{ stdio: "inherit" },
|
|
84
|
+
);
|
|
85
|
+
if (lockResult.status !== 0) throw new Error("lock generation failed (uv pip compile)");
|
|
86
|
+
log.ok("dist/requirements.lock ready");
|
|
87
|
+
|
|
88
|
+
for (const arch of archs) {
|
|
89
|
+
const pipPlatforms = PIP_PLATFORM[arch];
|
|
90
|
+
if (!pipPlatforms) throw new Error(`unknown arch '${arch}'`);
|
|
91
|
+
log.step(`vendoring wheels for ${arch}…`);
|
|
92
|
+
const r = spawnSync(
|
|
93
|
+
"docker",
|
|
94
|
+
[
|
|
95
|
+
"run", "--rm",
|
|
96
|
+
...dockerUserFlags(),
|
|
97
|
+
"-e", "PIP_ROOT_USER_ACTION=ignore",
|
|
98
|
+
"-v", `${pluginRoot}:/plugin`,
|
|
99
|
+
"-w", "/plugin",
|
|
100
|
+
PYTHON_DEV_IMAGE,
|
|
101
|
+
"bash", "-c",
|
|
102
|
+
"mkdir -p dist/wheels && pip3 download -r dist/requirements.lock --no-deps --only-binary=:all: -d dist/wheels -q " +
|
|
103
|
+
"--python-version 312 --implementation cp --abi cp312 " +
|
|
104
|
+
pipPlatforms.map((p) => `--platform ${p}`).join(" "),
|
|
105
|
+
],
|
|
106
|
+
{ stdio: "inherit" },
|
|
107
|
+
);
|
|
108
|
+
// A ZIP missing wheels fails to import on the robot — stop instead.
|
|
109
|
+
if (r.status !== 0) throw new Error(`wheel download failed for ${arch} (see pip output above)`);
|
|
110
|
+
log.ok(`dist/wheels/ ready (${arch})`);
|
|
111
|
+
}
|
|
112
|
+
}
|
package/cli/src/ros2.mjs
CHANGED
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
// Shared ROS2 Docker helpers (Jazzy + Python 3.12 — matches the robot backend).
|
|
2
|
-
// Used by dev:back (interactive dev loop) and build (export install/ into the ZIP).
|
|
3
|
-
import { spawnSync } from "node:child_process";
|
|
4
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
5
|
-
import { dirname, resolve } from "node:path";
|
|
6
|
-
import { canSkipImageBuild, cppSdkVersion, pluginRootOrNull, imageKey, imageKeyLabel, dockerUserFlags, hasBin, log, pruneMuskatImages, resolveDockerfile, buildCacheFlags } from "./util.mjs";
|
|
7
|
-
import { syncCppIde } from "./cppIde.mjs";
|
|
8
|
-
|
|
9
|
-
export const ROS2_DEV_IMAGE = "muscat-pk-ros2-dev";
|
|
10
|
-
|
|
11
|
-
/** First package directory under ros2_ws/src (the colcon package name). */
|
|
12
|
-
export function ros2PackageName(root) {
|
|
13
|
-
const srcDir = resolve(root, "ros2_ws/src");
|
|
14
|
-
if (!existsSync(srcDir)) return null;
|
|
15
|
-
const dirs = readdirSync(srcDir, { withFileTypes: true })
|
|
16
|
-
.filter((d) => d.isDirectory())
|
|
17
|
-
.map((d) => d.name);
|
|
18
|
-
return dirs[0] ?? null;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function assertDockerReady() {
|
|
22
|
-
if (!hasBin("docker")) {
|
|
23
|
-
throw new Error("Docker not found. ROS2 needs Docker (Jazzy + Python 3.12 container).");
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const ARCH_DOCKER_PLATFORM = {
|
|
28
|
-
amd64: "linux/amd64",
|
|
29
|
-
arm64: "linux/arm64",
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/** Build the dev image from the kit's bundled Dockerfile (Docker layer-caches). */
|
|
33
|
-
export async function ensureImage(image = ROS2_DEV_IMAGE, { platform = null, root = pluginRootOrNull() } = {}) {
|
|
34
|
-
await buildImage(image, platform, root);
|
|
35
|
-
// Dev images feed the editor's SDK headers: the native one (dev) or the host-arch one (build).
|
|
36
|
-
const hostArch = process.arch === "arm64" ? "arm64" : "amd64";
|
|
37
|
-
if (root && (image === ROS2_DEV_IMAGE || image === `${ROS2_DEV_IMAGE}-${hostArch}`)) syncCppIde(root, image);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function buildImage(image, platform, root) {
|
|
41
|
-
const dockerfile = await resolveDockerfile("Dockerfile.dev");
|
|
42
|
-
const sdkVer = cppSdkVersion(root);
|
|
43
|
-
const key = imageKey(dockerfile, sdkVer ?? "");
|
|
44
|
-
if (canSkipImageBuild(image, key)) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
log.step(`docker build -t ${image}${platform ? ` (${platform})` : ""} (first run is slow, then cached)…`);
|
|
48
|
-
const platformFlags = platform ? ["--platform", platform] : [];
|
|
49
|
-
const buildArgs = [...(sdkVer ? ["--build-arg", `RB_SDK_VERSION=${sdkVer}`] : []), ...imageKeyLabel(key)];
|
|
50
|
-
const r = spawnSync(
|
|
51
|
-
"docker",
|
|
52
|
-
["build", ...buildCacheFlags(), ...platformFlags, ...buildArgs, "-t", image, "-f", dockerfile, dirname(dockerfile)],
|
|
53
|
-
{ stdio: "inherit" },
|
|
54
|
-
);
|
|
55
|
-
if (r.status !== 0) throw new Error("docker build failed.");
|
|
56
|
-
pruneMuskatImages();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Run `colcon build` inside the container, producing ros2_ws/install on the host
|
|
61
|
-
* (the workspace is mounted). symlink=true for dev (fast, edit-in-place);
|
|
62
|
-
* symlink=false for export builds that ship in the ZIP (real copies, relocatable).
|
|
63
|
-
*/
|
|
64
|
-
/**
|
|
65
|
-
* arch=null → dev mode, builds to ros2_ws/install/ (native)
|
|
66
|
-
* arch="amd64"|"arm64" → export mode, builds to ros2_ws/install-{arch}/ using arch-specific image
|
|
67
|
-
*/
|
|
68
|
-
/**
|
|
69
|
-
* symlink=true (dev): ros2_ws/install/ — local dev workspace, not for ZIP
|
|
70
|
-
* symlink=false, arch=null (rclpy export): dist/ros2_ws/install/
|
|
71
|
-
* symlink=false, arch="amd64"|"arm64" (rclcpp export): dist/ros2_ws/install-{arch}/
|
|
72
|
-
*/
|
|
73
|
-
export function colconBuild(root, { image = ROS2_DEV_IMAGE, symlink = false, arch = null } = {}) {
|
|
74
|
-
// paths are relative to ros2_ws/ inside the container
|
|
75
|
-
let installBase;
|
|
76
|
-
if (symlink) {
|
|
77
|
-
installBase = "install";
|
|
78
|
-
} else if (arch) {
|
|
79
|
-
installBase = `../dist/ros2_ws/install-${arch}`;
|
|
80
|
-
} else {
|
|
81
|
-
installBase = "../dist/ros2_ws/install";
|
|
82
|
-
}
|
|
83
|
-
const flag = symlink ? "--symlink-install" : "";
|
|
84
|
-
const buildBase = symlink ? "build" : arch ? `build-${arch}` : "build-dist";
|
|
85
|
-
const script = [
|
|
86
|
-
"pip install -r requirements.txt --break-system-packages --ignore-installed -q || true",
|
|
87
|
-
"cd ros2_ws",
|
|
88
|
-
// Export builds use their own build cache: sharing build/ with dev's --symlink-install
|
|
89
|
-
// (or another arch) breaks the next colcon build.
|
|
90
|
-
// rclcpp export: ship librb_sdk.so in <pkg>/lib/ (robot has no SDK) when the node links it.
|
|
91
|
-
`colcon build ${flag} --build-base ${buildBase} --install-base ${installBase}`.replace(" ", " ") +
|
|
92
|
-
(arch ? ` --cmake-args '-DCMAKE_INSTALL_RPATH=$ORIGIN/..'` : ""),
|
|
93
|
-
...(arch ? [`for d in ${installBase}/*/lib; do if readelf -d $(find "$d" -mindepth 2 -type f -executable) 2>/dev/null | grep -q librb_sdk; then cp -a $(dpkg -L rainbow-rb-sdk | grep -E '/librb_sdk[.]so[.][0-9]') "$d"/; fi; done`] : []),
|
|
94
|
-
].join(" && ");
|
|
95
|
-
const label = arch
|
|
96
|
-
? `colcon build (${arch}) → dist/ros2_ws/install-${arch}/…`
|
|
97
|
-
: `colcon build${symlink ? " --symlink-install" : ""} (in container)…`;
|
|
98
|
-
log.step(label);
|
|
99
|
-
const actualImage = arch ? `${ROS2_DEV_IMAGE}-${arch}` : image;
|
|
100
|
-
const dockerPlatform = arch ? ARCH_DOCKER_PLATFORM[arch] : null;
|
|
101
|
-
const platformFlags = dockerPlatform ? ["--platform", dockerPlatform] : [];
|
|
102
|
-
const r = spawnSync(
|
|
103
|
-
"docker",
|
|
104
|
-
// python/cpp 와 같이 호스트 uid 로 돌린다 — root 소유 산출물이 남으면 사용자가 지우지 못한다.
|
|
105
|
-
["run", "--rm", ...platformFlags, ...dockerUserFlags(), "-e", "PIP_ROOT_USER_ACTION=ignore", "-v", `${root}:/plugin`, "-w", "/plugin", actualImage, "bash", "-lc", script],
|
|
106
|
-
{ cwd: root, stdio: "inherit" },
|
|
107
|
-
);
|
|
108
|
-
if (r.status !== 0) throw new Error(`colcon build failed${arch ? ` (${arch})` : ""}.`);
|
|
109
|
-
}
|
|
1
|
+
// Shared ROS2 Docker helpers (Jazzy + Python 3.12 — matches the robot backend).
|
|
2
|
+
// Used by dev:back (interactive dev loop) and build (export install/ into the ZIP).
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
5
|
+
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { canSkipImageBuild, cppSdkVersion, pluginRootOrNull, imageKey, imageKeyLabel, dockerUserFlags, hasBin, log, pruneMuskatImages, resolveDockerfile, buildCacheFlags } from "./util.mjs";
|
|
7
|
+
import { syncCppIde } from "./cppIde.mjs";
|
|
8
|
+
|
|
9
|
+
export const ROS2_DEV_IMAGE = "muscat-pk-ros2-dev";
|
|
10
|
+
|
|
11
|
+
/** First package directory under ros2_ws/src (the colcon package name). */
|
|
12
|
+
export function ros2PackageName(root) {
|
|
13
|
+
const srcDir = resolve(root, "ros2_ws/src");
|
|
14
|
+
if (!existsSync(srcDir)) return null;
|
|
15
|
+
const dirs = readdirSync(srcDir, { withFileTypes: true })
|
|
16
|
+
.filter((d) => d.isDirectory())
|
|
17
|
+
.map((d) => d.name);
|
|
18
|
+
return dirs[0] ?? null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function assertDockerReady() {
|
|
22
|
+
if (!hasBin("docker")) {
|
|
23
|
+
throw new Error("Docker not found. ROS2 needs Docker (Jazzy + Python 3.12 container).");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const ARCH_DOCKER_PLATFORM = {
|
|
28
|
+
amd64: "linux/amd64",
|
|
29
|
+
arm64: "linux/arm64",
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Build the dev image from the kit's bundled Dockerfile (Docker layer-caches). */
|
|
33
|
+
export async function ensureImage(image = ROS2_DEV_IMAGE, { platform = null, root = pluginRootOrNull() } = {}) {
|
|
34
|
+
await buildImage(image, platform, root);
|
|
35
|
+
// Dev images feed the editor's SDK headers: the native one (dev) or the host-arch one (build).
|
|
36
|
+
const hostArch = process.arch === "arm64" ? "arm64" : "amd64";
|
|
37
|
+
if (root && (image === ROS2_DEV_IMAGE || image === `${ROS2_DEV_IMAGE}-${hostArch}`)) syncCppIde(root, image);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function buildImage(image, platform, root) {
|
|
41
|
+
const dockerfile = await resolveDockerfile("Dockerfile.dev");
|
|
42
|
+
const sdkVer = cppSdkVersion(root);
|
|
43
|
+
const key = imageKey(dockerfile, sdkVer ?? "");
|
|
44
|
+
if (canSkipImageBuild(image, key)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
log.step(`docker build -t ${image}${platform ? ` (${platform})` : ""} (first run is slow, then cached)…`);
|
|
48
|
+
const platformFlags = platform ? ["--platform", platform] : [];
|
|
49
|
+
const buildArgs = [...(sdkVer ? ["--build-arg", `RB_SDK_VERSION=${sdkVer}`] : []), ...imageKeyLabel(key)];
|
|
50
|
+
const r = spawnSync(
|
|
51
|
+
"docker",
|
|
52
|
+
["build", ...buildCacheFlags(), ...platformFlags, ...buildArgs, "-t", image, "-f", dockerfile, dirname(dockerfile)],
|
|
53
|
+
{ stdio: "inherit" },
|
|
54
|
+
);
|
|
55
|
+
if (r.status !== 0) throw new Error("docker build failed.");
|
|
56
|
+
pruneMuskatImages();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Run `colcon build` inside the container, producing ros2_ws/install on the host
|
|
61
|
+
* (the workspace is mounted). symlink=true for dev (fast, edit-in-place);
|
|
62
|
+
* symlink=false for export builds that ship in the ZIP (real copies, relocatable).
|
|
63
|
+
*/
|
|
64
|
+
/**
|
|
65
|
+
* arch=null → dev mode, builds to ros2_ws/install/ (native)
|
|
66
|
+
* arch="amd64"|"arm64" → export mode, builds to ros2_ws/install-{arch}/ using arch-specific image
|
|
67
|
+
*/
|
|
68
|
+
/**
|
|
69
|
+
* symlink=true (dev): ros2_ws/install/ — local dev workspace, not for ZIP
|
|
70
|
+
* symlink=false, arch=null (rclpy export): dist/ros2_ws/install/
|
|
71
|
+
* symlink=false, arch="amd64"|"arm64" (rclcpp export): dist/ros2_ws/install-{arch}/
|
|
72
|
+
*/
|
|
73
|
+
export function colconBuild(root, { image = ROS2_DEV_IMAGE, symlink = false, arch = null } = {}) {
|
|
74
|
+
// paths are relative to ros2_ws/ inside the container
|
|
75
|
+
let installBase;
|
|
76
|
+
if (symlink) {
|
|
77
|
+
installBase = "install";
|
|
78
|
+
} else if (arch) {
|
|
79
|
+
installBase = `../dist/ros2_ws/install-${arch}`;
|
|
80
|
+
} else {
|
|
81
|
+
installBase = "../dist/ros2_ws/install";
|
|
82
|
+
}
|
|
83
|
+
const flag = symlink ? "--symlink-install" : "";
|
|
84
|
+
const buildBase = symlink ? "build" : arch ? `build-${arch}` : "build-dist";
|
|
85
|
+
const script = [
|
|
86
|
+
"pip install -r requirements.txt --break-system-packages --ignore-installed -q || true",
|
|
87
|
+
"cd ros2_ws",
|
|
88
|
+
// Export builds use their own build cache: sharing build/ with dev's --symlink-install
|
|
89
|
+
// (or another arch) breaks the next colcon build.
|
|
90
|
+
// rclcpp export: ship librb_sdk.so in <pkg>/lib/ (robot has no SDK) when the node links it.
|
|
91
|
+
`colcon build ${flag} --build-base ${buildBase} --install-base ${installBase}`.replace(" ", " ") +
|
|
92
|
+
(arch ? ` --cmake-args '-DCMAKE_INSTALL_RPATH=$ORIGIN/..'` : ""),
|
|
93
|
+
...(arch ? [`for d in ${installBase}/*/lib; do if readelf -d $(find "$d" -mindepth 2 -type f -executable) 2>/dev/null | grep -q librb_sdk; then cp -a $(dpkg -L rainbow-rb-sdk | grep -E '/librb_sdk[.]so[.][0-9]') "$d"/; fi; done`] : []),
|
|
94
|
+
].join(" && ");
|
|
95
|
+
const label = arch
|
|
96
|
+
? `colcon build (${arch}) → dist/ros2_ws/install-${arch}/…`
|
|
97
|
+
: `colcon build${symlink ? " --symlink-install" : ""} (in container)…`;
|
|
98
|
+
log.step(label);
|
|
99
|
+
const actualImage = arch ? `${ROS2_DEV_IMAGE}-${arch}` : image;
|
|
100
|
+
const dockerPlatform = arch ? ARCH_DOCKER_PLATFORM[arch] : null;
|
|
101
|
+
const platformFlags = dockerPlatform ? ["--platform", dockerPlatform] : [];
|
|
102
|
+
const r = spawnSync(
|
|
103
|
+
"docker",
|
|
104
|
+
// python/cpp 와 같이 호스트 uid 로 돌린다 — root 소유 산출물이 남으면 사용자가 지우지 못한다.
|
|
105
|
+
["run", "--rm", ...platformFlags, ...dockerUserFlags(), "-e", "PIP_ROOT_USER_ACTION=ignore", "-v", `${root}:/plugin`, "-w", "/plugin", actualImage, "bash", "-lc", script],
|
|
106
|
+
{ cwd: root, stdio: "inherit" },
|
|
107
|
+
);
|
|
108
|
+
if (r.status !== 0) throw new Error(`colcon build failed${arch ? ` (${arch})` : ""}.`);
|
|
109
|
+
}
|