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: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Changed
- `make versions-up` is now the whole pin flow: builds core, main,
rust, and cloak at latest upstream, writes versions.env from the
exact versions built (no post-build re-fetch race), and opens the
pin PR on chore/version-pins-refresh; PR=0 keeps the bump local
(#538)
- README core-value section rewritten around operator outcomes —
full-speed YOLO, host out of vendor code's reach, explicit boundary,
identity as a launch flag, official CLIs stock, zero workflow tax
Expand Down
5 changes: 5 additions & 0 deletions DEV-LOGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
- Minimal markdown markers, no unnecessary formatting, minimal emojis.
- Reference issue numbers in the format `#<issue-number>` for easy linking.

# [2026-08-05] Dev Log: versions-up pins what it built and PRs the bump #538
- Why: team flow was `versions-up && versions-pin && build-cloak` plus a hand-written PR. versions-pin re-fetches upstream AFTER the build, so pins could drift to a version never build-tested (claude-code ships several times a day). Cloak wasn't covered by versions-up at all.
- What: version-upgrade.sh now builds cloak too (cloakbrowser wrapper resolved from npm, joins the manifest and --only), then writes versions.env from the exact resolved build args — write_version_pins moved to shared version-pins.sh, round-trip guard intact — and calls new scripts/versions-pr.sh: commit in a temp worktree detached at origin/main (push HEAD:chore/version-pins-refresh, user checkout untouched), gh pr create unless one is already open. PR=0 opts out; a failed PR stage warns and exits 0 — a dead push must not sink a finished build. Tests: cloak build args, pin write, outage no-write, PR soft-fail in tests/version-upgrade.sh; hermetic real-git-fake-gh suite in tests/versions-pr.sh.
- Result: one command replaces the 3-command dance and pins are guaranteed build-tested. versions-pin stays for pin-only refresh; build-cloak stays standalone.

# [2026-07-28] Dev Log: home dir chown race bricks containers #506
- Why: intermittent `env: 'claude': Permission denied` on fresh containers. /home/deva stuck at build UID 1001 mode 750 (noble HOME_MODE) after remap to host UID — user can't traverse its own home. usermod's implicit home-tree chown walks live host mounts (~/.claude churning under concurrent sessions), aborts mid-walk with rc=12 AFTER updating passwd; shadow chowns the top dir last, so it never gets fixed. The 7511464 whitelist chowns subdirs, never $DEVA_HOME itself. Latent since 5807889 dropped the recursive home chown; only bites when the walk races live mounts, which is why sibling containers were fine.
- What: explicit non-recursive `chown "$DEVA_UID:$DEVA_GID" "$DEVA_HOME"` in setup_nonroot_user, after the usermod block, using the adapted DEVA_UID so the usermod-failed-entirely variant stays consistent. Devlog with full forensics in docs/devlog/20260728-home-dir-chown-race.org. Verified by fault injection: stub usermod (passwd updated, chown skipped, exit 12) reproduces the brick unpatched, comes out clean patched.
Expand Down
10 changes: 8 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -282,9 +282,12 @@ versions-up:
BUILD_IMAGE=$(MAIN_IMAGE) \
CORE_IMAGE=$(CORE_IMAGE) \
RUST_IMAGE=$(RUST_IMAGE) \
CLOAK_IMAGE=$(CLOAK_IMAGE) \
DOCKERFILE=$(DOCKERFILE) \
RUST_DOCKERFILE=$(RUST_DOCKERFILE) \
CLOAK_DOCKERFILE=$(CLOAK_DOCKERFILE) \
ONLY=$(ONLY) \
$(if $(PR),PR=$(PR)) \
$(VERSION_QUERY_OVERRIDES) \
./scripts/version-upgrade.sh

Expand Down Expand Up @@ -455,8 +458,10 @@ help:
@echo " buildx-multi-rust Build multi-arch Rust and push"
@echo " toolchains List pinned toolchains and managed build tools"
@echo " versions Compare built vs latest versions with changelogs"
@echo " versions-up Build both images with latest upstream agent versions"
@echo " versions-up Build all images at latest upstream versions,"
@echo " pin the built versions, and PR the bump"
@echo " ONLY=cctrace upgrades one tool, rest stay pinned"
@echo " PR=0 skips the auto commit + pull request"
@echo " versions-pin Refresh $(VERSION_PINS_FILE) from upstream"
@echo " CHANGELOG=1 also shows changelogs for updated tools"
@echo " scripts List repo helper scripts"
Expand Down Expand Up @@ -514,5 +519,6 @@ help:
@echo " make versions-pin CHANGELOG=1 # Refresh pins + show changelogs"
@echo " make versions # Check current versions"
@echo " make PLAYWRIGHT_VERSION=1.60.0 build-rust # Override rust browser tooling"
@echo " make versions-up # Upgrade to latest upstream versions"
@echo " make versions-up # Build latest, pin what was built, PR the bump"
@echo " make versions-up PR=0 # Same, but keep the pin bump local"
@echo " make versions-up ONLY=cctrace # Upgrade just cctrace, rest pinned"
34 changes: 0 additions & 34 deletions scripts/update-version-pins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -184,40 +184,6 @@ show_changelogs() {
fi
}

# ── Write versions.env ───────────────────────────────────────────────────

write_version_pins() {
cat > "$VERSION_PINS_FILE" <<EOF
# Shared image version pins for local and release builds.
# Update this file when we intentionally move default toolchain or CLI versions.

NODE_MAJOR=$NODE_MAJOR
GO_VERSION=$GO_VERSION
PYTHON_VERSION=$PYTHON_VERSION
DELTA_VERSION=$DELTA_VERSION
TMUX_VERSION=$TMUX_VERSION
TMUX_SHA256=$TMUX_SHA256

CLAUDE_CODE_VERSION=$CLAUDE_CODE_VERSION
CCTRACE_VERSION=$CCTRACE_VERSION
CODEX_VERSION=$CODEX_VERSION
GEMINI_CLI_VERSION=$GEMINI_CLI_VERSION
GROK_CLI_VERSION=$GROK_CLI_VERSION
KIMI_CODE_VERSION=$KIMI_CODE_VERSION
CCX_VERSION=$CCX_VERSION
COPILOT_API_VERSION=$COPILOT_API_VERSION
PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION
# CloakBrowser npm wrapper version. This also pins the Chromium binary:
# the wrapper hardcodes per-arch free-binary versions (linux-x64 146.x.x.5,
# linux-arm64 146.x.x.3), so bumping the wrapper is what moves Chromium.
CLOAKBROWSER_WRAPPER_VERSION=$CLOAKBROWSER_WRAPPER_VERSION

RUST_TOOLCHAINS=$RUST_TOOLCHAINS
RUST_DEFAULT_TOOLCHAIN=$RUST_DEFAULT_TOOLCHAIN
RUST_TARGETS=$RUST_TARGETS
EOF
}

# ── Arg parsing ──────────────────────────────────────────────────────────

while [[ $# -gt 0 ]]; do
Expand Down
36 changes: 36 additions & 0 deletions scripts/version-pins.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,42 @@ emit_version_pins() {
done
}

# Rewrite the pin file from current variable values. The heredoc is a
# second copy of the file layout — a pin missing here is silently deleted
# on the next rewrite, which is why tests/version-upgrade.sh round-trips
# the file byte-for-byte. Every writer must go through this function.
write_version_pins() {
cat > "$VERSION_PINS_FILE" <<EOF
# Shared image version pins for local and release builds.
# Update this file when we intentionally move default toolchain or CLI versions.

NODE_MAJOR=$NODE_MAJOR
GO_VERSION=$GO_VERSION
PYTHON_VERSION=$PYTHON_VERSION
DELTA_VERSION=$DELTA_VERSION
TMUX_VERSION=$TMUX_VERSION
TMUX_SHA256=$TMUX_SHA256

CLAUDE_CODE_VERSION=$CLAUDE_CODE_VERSION
CCTRACE_VERSION=$CCTRACE_VERSION
CODEX_VERSION=$CODEX_VERSION
GEMINI_CLI_VERSION=$GEMINI_CLI_VERSION
GROK_CLI_VERSION=$GROK_CLI_VERSION
KIMI_CODE_VERSION=$KIMI_CODE_VERSION
CCX_VERSION=$CCX_VERSION
COPILOT_API_VERSION=$COPILOT_API_VERSION
PLAYWRIGHT_VERSION=$PLAYWRIGHT_VERSION
# CloakBrowser npm wrapper version. This also pins the Chromium binary:
# the wrapper hardcodes per-arch free-binary versions (linux-x64 146.x.x.5,
# linux-arm64 146.x.x.3), so bumping the wrapper is what moves Chromium.
CLOAKBROWSER_WRAPPER_VERSION=$CLOAKBROWSER_WRAPPER_VERSION

RUST_TOOLCHAINS=$RUST_TOOLCHAINS
RUST_DEFAULT_TOOLCHAIN=$RUST_DEFAULT_TOOLCHAIN
RUST_TARGETS=$RUST_TARGETS
EOF
}

emit_github_outputs() {
local output_file=$1
local var key
Expand Down
89 changes: 83 additions & 6 deletions scripts/version-upgrade.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ _CLI_KIMI="${KIMI_CODE_VERSION:-}"
_CLI_CCX="${CCX_VERSION:-}"
_CLI_COPILOT="${COPILOT_API_VERSION:-}"
_CLI_PLAYWRIGHT="${PLAYWRIGHT_VERSION:-}"
_CLI_CLOAKBROWSER="${CLOAKBROWSER_WRAPPER_VERSION:-}"

# shellcheck disable=SC1091
source "$SCRIPT_DIR/version-pins.sh"
Expand Down Expand Up @@ -48,28 +49,37 @@ PROXY_ARGS=()
# native Linux Engine needs the explicit host-gateway mapping during build.
[[ ${#PROXY_ARGS[@]} -gt 0 ]] && PROXY_ARGS+=(--add-host "host.docker.internal:host-gateway")
RUST_IMAGE=${RUST_IMAGE:-ghcr.io/thevibeworks/deva:rust}
CLOAK_IMAGE=${CLOAK_IMAGE:-ghcr.io/thevibeworks/deva:cloak}
DOCKERFILE=${DOCKERFILE:-Dockerfile}
RUST_DOCKERFILE=${RUST_DOCKERFILE:-Dockerfile.rust}
CLOAK_DOCKERFILE=${CLOAK_DOCKERFILE:-Dockerfile.cloak}
COUNTDOWN=${COUNTDOWN:-5}
AUTO_YES=${AUTO_YES:-}
PR=${PR:-1}

usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS]

Builds core, main, rust, and cloak images at the latest upstream
versions, then writes versions.env from the exact versions built and
opens a pin-bump PR (branch chore/version-pins-refresh).

Options:
-y, --yes Skip confirmation countdown
--only LIST Upgrade only these tools (comma-separated); the rest
stay pinned to versions.env. Tools: claude-code,
cctrace, codex, gemini-cli, grok-cli, kimi-code,
ccx, copilot-api, playwright
ccx, copilot-api, playwright, cloakbrowser
-h, --help Show this help

Environment:
ONLY Same as --only (e.g. make versions-up ONLY=cctrace)
PR PR=0 skips the auto commit + pull request
MAIN_IMAGE Main image name (default: ghcr.io/thevibeworks/deva:latest)
CORE_IMAGE Core image name (default: ghcr.io/thevibeworks/deva:core)
RUST_IMAGE Rust image name (default: ghcr.io/thevibeworks/deva:rust)
CLOAK_IMAGE Cloak image name (default: ghcr.io/thevibeworks/deva:cloak)
VERSION_PINS_FILE Shared version pin file (default: versions.env)
CLAUDE_CODE_VERSION Override claude-code version
CCTRACE_VERSION Override cctrace version
Expand Down Expand Up @@ -106,7 +116,7 @@ apply_only_filter() {
[[ -n $ONLY ]] || return 0

local tool
local known="claude-code cctrace codex gemini-cli grok-cli kimi-code ccx copilot-api playwright"
local known="claude-code cctrace codex gemini-cli grok-cli kimi-code ccx copilot-api playwright cloakbrowser"
for tool in ${ONLY//,/ }; do
case " $known " in
*" $tool "*) ;;
Expand All @@ -125,6 +135,7 @@ apply_only_filter() {
tool_selected ccx || _CLI_CCX="${_CLI_CCX:-$CCX_VERSION}"
tool_selected copilot-api || _CLI_COPILOT="${_CLI_COPILOT:-$COPILOT_API_VERSION}"
tool_selected playwright || _CLI_PLAYWRIGHT="${_CLI_PLAYWRIGHT:-$PLAYWRIGHT_VERSION}"
tool_selected cloakbrowser || _CLI_CLOAKBROWSER="${_CLI_CLOAKBROWSER:-$CLOAKBROWSER_WRAPPER_VERSION}"

echo "Selective upgrade: $ONLY (all other tools pinned to versions.env)"
echo ""
Expand All @@ -145,16 +156,38 @@ main() {

load_versions "$CHECK_IMAGE"

# CloakBrowser wrapper lives outside the tool registry (its version
# label is only on the cloak image, which CHECK_IMAGE never is), so
# resolve it here: CLI override wins, then npm latest, then the pin.
local _pin_cloak_wrapper="$CLOAKBROWSER_WRAPPER_VERSION"
local cloak_wrapper_ver="$_CLI_CLOAKBROWSER"
if [[ -z $cloak_wrapper_ver ]]; then
cloak_wrapper_ver=$(_npm_registry_latest cloakbrowser) || true
if [[ -z $cloak_wrapper_ver ]]; then
echo -e "${YELLOW}Warning: Failed to fetch latest cloakbrowser, using pinned: ${_pin_cloak_wrapper}${RESET}" >&2
cloak_wrapper_ver="$_pin_cloak_wrapper"
fi
fi
local _wrapper_stale=0
[[ "$(normalize_version "$cloak_wrapper_ver")" != "$(normalize_version "$_pin_cloak_wrapper")" ]] && _wrapper_stale=1
Comment on lines +171 to +172

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Inspect the cloak image before declaring it current

When the configured pin already equals the upstream wrapper but $CLOAK_IMAGE is missing or still contains an older wrapper—for example, after running make versions-pin without rebuilding—this comparison sets _wrapper_stale to zero because it compares upstream against the pin rather than the built cloak image. If the other images are current, the subsequent early exit skips the cloak build, so make versions-up does not fulfill its new promise to bring all images up to date; determine the current wrapper from the cloak image label and compare that with the resolved version.

Useful? React with 👍 / 👎.


if print_version_summary; then
echo -e "${GREEN}All versions up-to-date. Nothing to upgrade.${RESET}"
exit 0
if [[ $_wrapper_stale -eq 0 ]]; then
echo -e "${GREEN}All versions up-to-date. Nothing to upgrade.${RESET}"
exit 0
fi
echo -e "${CYAN}Agent CLIs up-to-date; cloakbrowser wrapper moved ${_pin_cloak_wrapper} -> ${cloak_wrapper_ver}.${RESET}"
fi

# --only: gate on the selected tools, not the whole manifest — a lagging
# unselected tool must not trigger a rebuild of nothing but pins.
if [[ -n $ONLY ]]; then
local _t _cur _lat _only_needs_update=0
for _t in ${ONLY//,/ }; do
if [[ $_t == cloakbrowser ]]; then
[[ $_wrapper_stale -eq 1 ]] && _only_needs_update=1
continue
fi
_cur=$(normalize_version "$(get_current "$_t")")
_lat=$(normalize_version "$(get_latest "$_t")")
if [[ -z $_cur || $_cur == "-" || $_cur != "$_lat" ]]; then
Expand Down Expand Up @@ -209,6 +242,7 @@ main() {
"CCX|ccx_ver|_CLI_CCX|ccx"
"Copilot API|copilot_ver|_CLI_COPILOT|copilot-api"
"Playwright|playwright_ver|_CLI_PLAYWRIGHT|playwright"
"CloakBrowser|cloak_wrapper_ver|_CLI_CLOAKBROWSER|cloakbrowser"
)

local _lines_upgrade=() _lines_pinned=() _lines_current=() _lines_new=()
Expand All @@ -218,8 +252,15 @@ main() {
IFS='|' read -r _label _var _cli_var _tool <<< "$_mp"
local _val=${!_var:-}
local _cli_val=${!_cli_var:-}
local _cur=$(get_current "$_tool")
local _type=$(get_tool_field "$_tool" type)
local _cur _type
if [[ $_tool == cloakbrowser ]]; then
# Not in the registry: current = the versions.env pin.
_cur="$_pin_cloak_wrapper"
_type="npm"
else
_cur=$(get_current "$_tool")
_type=$(get_tool_field "$_tool" type)
fi
local _pad=$(printf "%-14s" "$_label")

local _fmt_val _fmt_cur
Expand Down Expand Up @@ -344,9 +385,45 @@ main() {
--build-arg RUST_TARGETS="$RUST_TARGETS" \
-t "$RUST_IMAGE" .

echo ""
section "Building Cloak Image"
docker build -f "$CLOAK_DOCKERFILE" \
${PROXY_ARGS[@]+"${PROXY_ARGS[@]}"} \
--build-arg BASE_IMAGE="$RUST_IMAGE" \
--build-arg CLOAKBROWSER_WRAPPER_VERSION="$cloak_wrapper_ver" \
-t "$CLOAK_IMAGE" .

echo ""
echo -e "${GREEN}${BOLD}All images upgraded successfully${RESET}"
echo -e "${DIM}Completed: $(date '+%Y-%m-%d %H:%M:%S')${RESET}"

# Pin exactly what was built. A re-fetch here could pick up a version
# published mid-build and pin something never build-tested.
echo ""
section "Pinning versions.env"
CLAUDE_CODE_VERSION="$claude_ver"
CCTRACE_VERSION="$cctrace_ver"
CODEX_VERSION="$codex_ver"
GEMINI_CLI_VERSION="$gemini_ver"
GROK_CLI_VERSION="$grok_ver"
KIMI_CODE_VERSION="$kimi_ver"
CCX_VERSION="$ccx_ver"
COPILOT_API_VERSION="$copilot_ver"
PLAYWRIGHT_VERSION="$playwright_ver"
CLOAKBROWSER_WRAPPER_VERSION="$cloak_wrapper_ver"
write_version_pins
echo -e "${GREEN}Wrote ${VERSION_PINS_FILE##*/} from the built versions${RESET}"

if [[ $PR == 0 ]]; then
echo -e "${DIM}PR=0: skipping pin commit + pull request${RESET}"
return 0
fi
echo ""
section "Opening Pin PR"
if ! bash "$SCRIPT_DIR/versions-pr.sh"; then
echo -e "${YELLOW}PR creation failed; pins are written locally.${RESET}" >&2
echo -e "${YELLOW}Retry with: ./scripts/versions-pr.sh${RESET}" >&2
fi
}

main
Loading
Loading