feat(control-plane): add extra software step with mise-based runtime installs#2
feat(control-plane): add extra software step with mise-based runtime installs#2zackerydev wants to merge 2 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an “Extra Software” third step to the bot install wizard so operators can provide .tool-versions content that is applied at runtime and triggers a (non-fatal) mise install on bot startup across OpenClaw/IronClaw/PicoClaw.
Changes:
- Add
/bots/new/softwarewizard step + UI template, route, handler logic, and unit tests. - Add
extraSoftware.toolVersionsHelm values and startup-script logic to write.tool-versionsand runmise install. - Install
misein runtime images; update docs and E2E coverage for the new flow.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/content/tutorials/deploy-first-bot.md | Updates tutorial to reflect 3-step wizard and extra-software step. |
| docs/content/docs/getting-started.md | Updates getting-started steps to include optional extra-software step. |
| docs/content/docs/docs-parity-matrix.md | Adds /bots/new/software to docs parity matrix. |
| docs/content/docs/cli.md | Documents the new wizard route in CLI docs. |
| docs/content/docs/bots.md | Updates install flow docs to include extra-software step and behavior. |
| docker/picoclaw/Dockerfile | Installs mise in PicoClaw runtime image. |
| docker/openclaw/Dockerfile | Installs mise in OpenClaw runtime image. |
| docker/ironclaw/Dockerfile | Installs mise in IronClaw runtime image. |
| control-plane/templates/pages/bot-form-software.html | New step-3 page to capture .tool-versions input and submit install. |
| control-plane/templates/pages/bot-form-infra.html | Updates wizard step count and carries extraToolVersions forward via hidden field. |
| control-plane/templates/pages/bot-form-config.html | Updates wizard step count and routes “Next” to the new software step. |
| control-plane/internal/routes_test.go | Ensures new /bots/new/software route is registered. |
| control-plane/internal/routes.go | Registers new POST route for wizard step 3. |
| control-plane/internal/handler/helm_test.go | Adds unit tests for rendering step 3 and for parsing/installing extra software values. |
| control-plane/internal/handler/helm.go | Adds handler + renderer for step 3; maps extraToolVersions into Helm extraSoftware.toolVersions. |
| control-plane/e2e/openclaw_lifecycle_test.go | Adjusts lifecycle fixture model value to match current expected runtime config. |
| control-plane/e2e/openclaw_extra_software_test.go | New E2E validating .tool-versions persistence and a tool installed via mise. |
| control-plane/charts/picoclaw/values.yaml | Adds extraSoftware.toolVersions chart value. |
| control-plane/charts/picoclaw/templates/startup-configmap.yaml | Adds startup logic to write .tool-versions and run mise install. |
| control-plane/charts/picoclaw/templates/deployment.yaml | Wires PicoClaw to run the startup script via command/args. |
| control-plane/charts/openclaw/values.yaml | Adds extraSoftware.toolVersions chart value. |
| control-plane/charts/openclaw/templates/startup-configmap.yaml | Adds startup logic for .tool-versions + mise install (and tweaks configFile heredoc). |
| control-plane/charts/openclaw/templates/deployment.yaml | Adds rollout checksum annotations for config/startup ConfigMaps. |
| control-plane/charts/ironclaw/values.yaml | Adds extraSoftware.toolVersions chart value. |
| control-plane/charts/ironclaw/templates/startup-configmap.yaml | Adds startup logic to write .tool-versions and run mise install. |
| .config/scripts/e2e-run.sh | Includes the new extra-software E2E test in the runner regex. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| RUN MISE_VERSION=${MISE_VERSION} MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 \ | ||
| sh -c "$(curl -fsSL https://mise.run)" \ | ||
| && /usr/local/bin/mise --version |
There was a problem hiding this comment.
The image installs mise by piping a remote script from https://mise.run directly into sh. This is a supply-chain risk (no checksum/signature verification) and makes builds non-reproducible if the installer changes. Prefer downloading a specific release artifact and verifying its checksum/signature (or installing from a trusted package repository), then placing the binary in /usr/local/bin.
| RUN MISE_VERSION=${MISE_VERSION} MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 \ | |
| sh -c "$(curl -fsSL https://mise.run)" \ | |
| && /usr/local/bin/mise --version | |
| RUN set -eux; \ | |
| arch="$(uname -m)"; \ | |
| case "${arch}" in \ | |
| x86_64) mise_arch="x64" ;; \ | |
| *) echo "Unsupported architecture: ${arch}" >&2; exit 1 ;; \ | |
| esac; \ | |
| mise_url="https://github.com/jdx/mise/releases/download/${MISE_VERSION}/mise-${MISE_VERSION}-linux-${mise_arch}"; \ | |
| mise_sha_url="${mise_url}.sha256"; \ | |
| curl -fsSL "${mise_url}" -o /tmp/mise; \ | |
| curl -fsSL "${mise_sha_url}" -o /tmp/mise.sha256; \ | |
| (cd /tmp && sha256sum -c mise.sha256); \ | |
| install -m 0755 /tmp/mise /usr/local/bin/mise; \ | |
| rm -f /tmp/mise /tmp/mise.sha256; \ | |
| /usr/local/bin/mise --version |
| RUN apk add --no-cache ca-certificates tzdata wget | ||
|
|
||
| RUN MISE_VERSION=${MISE_VERSION} MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 \ | ||
| sh -c "$(wget -qO- https://mise.run)" \ |
There was a problem hiding this comment.
This Dockerfile installs mise by piping a remote script from https://mise.run into sh with no checksum/signature verification. That’s a supply-chain risk and can make builds non-reproducible if the installer content changes. Prefer downloading a pinned release artifact and verifying its checksum/signature (or using an Alpine package if available) before installing to /usr/local/bin.
| RUN apk add --no-cache ca-certificates tzdata wget | |
| RUN MISE_VERSION=${MISE_VERSION} MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 \ | |
| sh -c "$(wget -qO- https://mise.run)" \ | |
| ARG MISE_INSTALL_SHA256 | |
| RUN apk add --no-cache ca-certificates tzdata wget | |
| RUN wget -qO /tmp/mise-installer https://mise.run \ | |
| && echo "${MISE_INSTALL_SHA256} /tmp/mise-installer" | sha256sum -c - \ | |
| && chmod +x /tmp/mise-installer \ | |
| && MISE_VERSION="${MISE_VERSION}" MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 \ | |
| sh /tmp/mise-installer \ | |
| && rm /tmp/mise-installer \ |
| command: ["/bin/sh", "/scripts/start-picoclaw.sh"] | ||
| args: ["gateway"] | ||
| ports: |
There was a problem hiding this comment.
picoclaw now runs a startup script as the container command, and that script may run mise install synchronously before the HTTP server starts when extraSoftware.toolVersions is set. With the current liveness/readiness probes (no startupProbe here and a short initialDelaySeconds in values), longer installs can cause repeated probe failures and container restarts. Consider adding a startupProbe (like ironclaw) and/or increasing liveness/readiness thresholds, or running the mise install step asynchronously/with a timeout.
| echo "Applying managed configFile.content to $CONFIG_FILE" | ||
| cat >"$CONFIG_FILE" <<'CLAWMACHINE_OPENCLAW_CONFIG' | ||
| {{ .Values.configFile.content | nindent 4 }} | ||
| CLAWMACHINE_OPENCLAW_CONFIG | ||
| CLAWMACHINE_OPENCLAW_CONFIG | ||
| {{- end }} |
There was a problem hiding this comment.
The heredoc terminator is indented ( CLAWMACHINE_OPENCLAW_CONFIG). In POSIX shells the terminator must match exactly at the start of the line (unless using <<- with tabs). With the current indentation the heredoc won’t close, breaking the startup script when configFile is enabled. Align the terminator to column 0 in the rendered script (e.g., remove the extra spaces, or switch to <<- + tabs consistently).
| cat >"$TOOL_VERSIONS_FILE" <<'CLAWMACHINE_TOOL_VERSIONS' | ||
| {{ .Values.extraSoftware.toolVersions | nindent 4 }} | ||
| CLAWMACHINE_TOOL_VERSIONS |
There was a problem hiding this comment.
The .tool-versions heredoc terminator ( CLAWMACHINE_TOOL_VERSIONS) is indented. In sh, heredoc terminators must match exactly at the start of the line (unless using <<- with tabs), so the heredoc won’t close and the startup script will fail when extraSoftware.toolVersions is set. Remove the extra indentation or change to <<- with tab-indented terminators.
| cat >"$TOOL_VERSIONS_FILE" <<'CLAWMACHINE_TOOL_VERSIONS' | |
| {{ .Values.extraSoftware.toolVersions | nindent 4 }} | |
| CLAWMACHINE_TOOL_VERSIONS | |
| cat >"$TOOL_VERSIONS_FILE" <<-'CLAWMACHINE_TOOL_VERSIONS' | |
| {{ .Values.extraSoftware.toolVersions | nindent 4 }} | |
| CLAWMACHINE_TOOL_VERSIONS |
| echo "Applying extra software tools from $TOOL_VERSIONS_FILE" | ||
| cat >"$TOOL_VERSIONS_FILE" <<'CLAWMACHINE_TOOL_VERSIONS' | ||
| {{ .Values.extraSoftware.toolVersions | nindent 4 }} | ||
| CLAWMACHINE_TOOL_VERSIONS | ||
| sed -i 's/^ //' "$TOOL_VERSIONS_FILE" | ||
|
|
There was a problem hiding this comment.
The .tool-versions heredoc terminator ( CLAWMACHINE_TOOL_VERSIONS) is indented. Heredoc terminators must be at the start of the line in sh (unless using <<- with tabs), so this will prevent the heredoc from closing and break startup whenever extraSoftware.toolVersions is set. Remove the extra indentation or use <<- with tab-indented terminators.
| ca-certificates tzdata wget postgresql-client \ | ||
| && rm -rf /var/lib/apt/lists/* | ||
|
|
||
| RUN MISE_VERSION=${MISE_VERSION} MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 \ | ||
| sh -c "$(wget -qO- https://mise.run)" \ | ||
| && /usr/local/bin/mise --version |
There was a problem hiding this comment.
The Docker build installs mise by executing a script fetched from https://mise.run without any checksum/signature verification. This introduces a supply-chain risk and can break reproducible builds if the installer changes. Prefer downloading a pinned release binary and verifying a published checksum/signature (or use a distro package) before installing it into /usr/local/bin.
| ca-certificates tzdata wget postgresql-client \ | |
| && rm -rf /var/lib/apt/lists/* | |
| RUN MISE_VERSION=${MISE_VERSION} MISE_INSTALL_PATH=/usr/local/bin/mise MISE_INSTALL_HELP=0 \ | |
| sh -c "$(wget -qO- https://mise.run)" \ | |
| && /usr/local/bin/mise --version | |
| ca-certificates tzdata wget postgresql-client xz-utils \ | |
| && rm -rf /var/lib/apt/lists/* | |
| # Install mise from a pinned release binary with checksum verification | |
| RUN set -e; \ | |
| arch="$(dpkg --print-architecture)"; \ | |
| case "$arch" in \ | |
| amd64) mise_arch="x64" ;; \ | |
| arm64) mise_arch="aarch64" ;; \ | |
| *) echo "Unsupported architecture for mise: $arch" >&2; exit 1 ;; \ | |
| esac; \ | |
| mise_version="${MISE_VERSION}"; \ | |
| base_url="https://github.com/jdx/mise/releases/download/${mise_version}"; \ | |
| tarball="mise-${mise_version}-linux-${mise_arch}.tar.xz"; \ | |
| wget -q "${base_url}/${tarball}" -O "/tmp/${tarball}"; \ | |
| wget -q "${base_url}/${tarball}.sha256" -O "/tmp/${tarball}.sha256"; \ | |
| (cd /tmp && sha256sum -c "${tarball}.sha256"); \ | |
| mkdir -p /tmp/mise; \ | |
| tar -xJf "/tmp/${tarball}" -C /tmp/mise; \ | |
| install -m 0755 /tmp/mise/bin/mise /usr/local/bin/mise; \ | |
| rm -rf /tmp/mise "/tmp/${tarball}" "/tmp/${tarball}.sha256"; \ | |
| /usr/local/bin/mise --version |
| h.renderInstallConfigPage(w, r, botType, allFormValues(r)) | ||
| } | ||
|
|
||
| // NewSoftwarePage renders step 3 after validating step 2. |
There was a problem hiding this comment.
The doc comment says this handler renders step 3 "after validating step 2", but the implementation only parses the form and validates releaseName (same as step 1 validation). Either update the comment to match what is actually validated, or add the missing step-2 validations if they are required before proceeding.
| // NewSoftwarePage renders step 3 after validating step 2. | |
| // NewSoftwarePage renders step 3 after validating the release name. |
| echo "Applying extra software tools from $TOOL_VERSIONS_FILE" | ||
| cat >"$TOOL_VERSIONS_FILE" <<'CLAWMACHINE_TOOL_VERSIONS' | ||
| {{ .Values.extraSoftware.toolVersions | nindent 4 }} | ||
| CLAWMACHINE_TOOL_VERSIONS |
There was a problem hiding this comment.
The .tool-versions heredoc terminator is indented ( CLAWMACHINE_TOOL_VERSIONS). Heredoc terminators must appear at the beginning of the line (unless using <<- with tabs), so this will prevent the heredoc from closing and break startup whenever extraSoftware.toolVersions is set. Remove the extra indentation (or use <<- + tabs) so the terminator matches correctly.
| CLAWMACHINE_TOOL_VERSIONS | |
| CLAWMACHINE_TOOL_VERSIONS |
What
Add a third install-wizard step for "Extra Software" that accepts raw
.tool-versions, installsmisein OpenClaw/IronClaw/PicoClaw images, and runs startupmise install(non-fatal) with cache on persistent bot storage.Why
Operators need a supported way to add extra CLI tools to bot runtimes without building custom images.
Linear issue: not provided (requested directly).
Scope
control-plane/internal/routes.go,control-plane/internal/handler/helm.gocontrol-plane/templates/pages/bot-form-infra.htmlcontrol-plane/templates/pages/bot-form-config.htmlcontrol-plane/templates/pages/bot-form-software.htmlcontrol-plane/charts/{openclaw,ironclaw,picoclaw}/values.yamlcontrol-plane/charts/{openclaw,ironclaw,picoclaw}/templates/startup-configmap.yamlcontrol-plane/charts/openclaw/templates/deployment.yaml(rollout checksums)control-plane/charts/picoclaw/templates/deployment.yaml(startup wiring)docker/{openclaw,ironclaw,picoclaw}/Dockerfilecontrol-plane/e2e/openclaw_extra_software_test.gocontrol-plane/e2e/openclaw_lifecycle_test.godocs/content/docs/*anddocs/content/tutorials/*How To Validate
mise run test:all-> passmise run vet-> passmise run lint-> passmise run fix-> passmise run docs:check-> passsource ~/GitHub/zackerydev/.env && export OP_CREDENTIALS_FILE=$HOME/GitHub/zackerydev/1password-credentials.json && .config/scripts/e2e-run.sh-> pass (includes new OpenClaw extra-software E2E).config/scripts/e2e-teardown.sh-> passRequired
mise run test:allmise run vetmise run lintmise run fixmise run docs:check(if docs or user-facing behavior changed)Optional / When Relevant
mise run e2e:setup && mise run e2e:run(lifecycle/kind changes)mise run e2e:teardownormise run e2e:cleanupafter lifecycle testsEvidence
TestOpenClawExtraSoftwareInstallsClaude:openclaw pod ready: e2e-openclaw-mise-...claude binary found: /root/.local/share/mise/installs/claude/2.1.62/claudeTestOpenClawLifecycleStep8/Step10 now pass after model fixture + rollout checksum fixes.PASSok github.com/zackerydev/clawmachine/control-plane/e2e ...Checklist