Skip to main content

Investigate: Script-level updates without full container rebuilds

IMPLEMENTATION RULES: Before implementing this plan, read and follow:

Status: Completed​

Goal: Enable updating toolbox scripts inside a running container without rebuilding the image.

GitHub Issues: #54 (supersedes #45)

Last Updated: 2026-02-16


Summary of Issue #54​

Issue #54 introduces two distinct concepts:

ConceptWhat changesCommandRebuild?Version
Script syncScript fixes, new tools, commandsdev-syncNotools.json version (auto)
Container updateBase image, OS packages, runtimesdev-updateYesversion.txt (manual)

The issue describes 4 main components:

  1. dev-sync command — downloads updated scripts from GitHub without rebuilding
  2. Version numbers in tools.json — top-level version + per-tool SCRIPT_VER
  3. Pre-commit hook — auto-bumps SCRIPT_VER patch version on commit
  4. Auto-sync on container start — runs dev-sync during startup

Plus supporting work:

  • CI/CD pipeline to generate and publish script bundles
  • Symlink management for new/removed dev-* commands
  • Atomic replacement with rollback
  • Website documentation showing tool versions

Current State​

What exists​

  • dev-update.sh — checks version.txt against GitHub, shows manual docker pull instructions
  • generate-tools-json.sh — generates tools.json from script metadata at build time
  • tools.json — has generated timestamp but no version field
  • Every install script has SCRIPT_VER — but it's not extracted into tools.json
  • extract_var() in the generator already handles SCRIPT_VER-style fields

What's missing​

  • No dev-sync command
  • No top-level version in tools.json
  • No per-tool version in tools.json (despite SCRIPT_VER existing in every script)
  • No pre-commit hook for auto-bumping
  • No CI/CD pipeline for script bundles
  • No scripts-version.txt on GitHub for quick version check

Phased Approach​

This is a large feature. I recommend breaking it into phases that each deliver value independently:

Phase 1: Foundation — Pre-commit hook + tools.json versions​

No CI/CD changes needed. Can be done now.

  1. Add .githooks/pre-commit (hook code provided in issue #54)
  2. Add SCRIPT_VER extraction to generate-tools-json.sh
  3. Add top-level "version" field to tools.json
  4. Document git config core.hooksPath .githooks in contributor docs

Value: Every script change gets a version bump. tools.json tracks versions. Foundation for everything else.

Phase 2: CI/CD — Bundle generation pipeline​

Requires GitHub Actions workflow.

  1. Create workflow that runs on push to main
  2. Runs generate-tools-json.sh to build tools.json with versions
  3. Compares against previous tools.json to detect changes
  4. If changed: bumps top-level version, creates tar.gz bundle, publishes as GitHub release
  5. Creates scripts-version.txt as a lightweight version check file

Value: Script bundles are available for dev-sync to download.

Phase 3: dev-sync command​

Requires Phase 2 (needs bundles to exist).

  1. Create dev-sync.sh in manage/
  2. Fetch scripts-version.txt from GitHub
  3. Compare with local tools.json version
  4. If newer: download bundle, extract to temp, atomic swap
  5. Create/remove symlinks for new/removed dev-* commands
  6. Report what changed (per-tool version diffs)
  7. Handle edge cases (offline, self-update, permissions)

Value: Users can update scripts without rebuilding.

Phase 4: Auto-sync on startup​

Requires Phase 3.

  1. Add dev-sync --quiet to entrypoint or postStartCommand
  2. Cache sync status to /tmp/devcontainer-toolbox-sync-status
  3. Handle offline gracefully (skip silently)
  4. Show notifications in dev-help/dev-setup if sync failed

Value: Scripts are always up-to-date automatically.

Phase 5: Website documentation​

Can be done anytime after Phase 1.

  1. Update website generator to include version field from tools.json
  2. Show version on tool detail pages

Value: Users can see which version of each tool is documented.


Recommendation​

Start with Phase 1 — it's self-contained, delivers value immediately, and is the foundation for all other phases. The pre-commit hook and tools.json version fields can be implemented and merged independently.

Phases 2-4 are more complex and involve CI/CD pipeline work. They should be planned as separate issues/PRs.

Phase 5 is a small documentation improvement that can be done anytime.


What to implement now​

For this session, I recommend implementing Phase 1 only:

  1. .githooks/pre-commit — auto-bump hook (code provided in issue)
  2. generate-tools-json.sh — add SCRIPT_VER extraction + top-level version
  3. Contributor documentation update

This gives us the version infrastructure that all future phases build on, without needing CI/CD changes or new commands.


Investigation: Distribution Method for Phase 2-3​

Context​

  • Scripts live in .devcontainer/additions/ (44 files, 1.3MB) and .devcontainer/manage/ (332K)
  • Total ~1.6MB of shell scripts
  • Container mounts these at /opt/devcontainer-toolbox/
  • Current GitHub release already has a dev_containers.zip asset (423KB)
  • dev-sync needs to: check version, download scripts, replace atomically
  • Containers have curl, git, tar, unzip available

Option A: tar.gz bundle via GitHub Releases​

How it works: CI creates a tar.gz of additions/ + manage/, attaches to GitHub release. dev-sync downloads and extracts.

ProCon
Single download, atomic swapRequires CI workflow to create bundles
Works offline after downloadDownloads everything even for 1-file change
Simple: curl + tarNeed to manage release assets
Can pin to specific versionExtra release artifact to maintain

Option B: GitHub raw file download (git archive)​

How it works: dev-sync uses git archive --remote or GitHub API to download just the .devcontainer/ tree as a tarball.

ProCon
Always latest from mainGitHub doesn't support git archive --remote
No CI workflow neededGitHub API tarball includes entire repo
No release managementMust filter to just .devcontainer/ locally
Simple infrastructureRate-limited (60 req/hr unauthenticated)

Option C: GitHub API — download individual changed files​

How it works: dev-sync fetches tools.json to find changed versions, then downloads only the changed files via GitHub raw content API.

ProCon
Minimal bandwidthMany HTTP requests for multiple changes
Surgical updatesComplex diff/merge logic
No CI changes neededRate-limited (60 req/hr unauthenticated)
Hard to handle deleted/renamed files
No atomicity — partial update risk

Option D: Reuse existing zip release asset​

How it works: The build-image.yml workflow already creates dev_containers.zip. dev-sync downloads this existing asset.

ProCon
Already exists — no new CI workZip contains entire .devcontainer/ tree
Same asset users download for install~423KB (acceptable size)
Versioned via GitHub releasesIncludes Dockerfile, configs not needed for sync
unzip is available in containerNeed to filter to just additions/ + manage/

Option E: Lightweight version file + tar.gz (hybrid)​

How it works: CI publishes a tiny scripts-version.txt to a known URL (GitHub Pages or release). dev-sync checks this first (1 HTTP request). Only downloads the bundle if version differs.

ProCon
Fast version check (< 1KB)Two-step process
Bundle only downloaded when neededRequires version file to be published
Works with either tar.gz or zip
Minimal bandwidth for "no update" case

Comparison Matrix​

CriteriaA: tar.gzB: git archiveC: individual filesD: existing zipE: hybrid
CI work neededMediumNoneNoneNoneLow
Download size~200KB~full repoVariable423KB~200KB
AtomicityYesYesNoYesYes
Bandwidth efficientMediumPoorBest case goodMediumBest
ComplexityLowMediumHighLowestLow
Offline supportYesNoNoYesYes
Rate limit riskNoneMediumHighNoneNone

Recommendation​

Option D+E hybrid: Use the existing dev_containers.zip release asset with a lightweight version check.

  1. Version check: dev-sync fetches version.txt from GitHub raw content (1 small request, already works in dev-update.sh)
  2. Download: If version differs, download dev_containers.zip from the latest GitHub release (already generated by CI)
  3. Extract: Unzip to temp, copy additions/ + manage/ atomically
  4. No new CI workflow needed — piggyback on the existing release pipeline

This avoids building a new CI pipeline entirely. The existing build-image.yml already creates the zip. We just need dev-sync to consume it.

The only addition to CI: publish a scripts-version.txt alongside the zip (a one-line file containing the version from tools.json). This is a minor change to the existing workflow.


Progress​

  • Phase 1: Pre-commit hook + tools.json versions — ✅ DONE (PLAN-dev-sync-foundation, PR #55)
  • Phase 5: Website shows tool versions — ✅ DONE (version column on tool detail pages)
  • Phase 2: CI/CD — scripts-version.txt generation — ✅ DONE (PLAN-dev-sync-command, Phase 1)
  • Phase 3: dev-sync command — ✅ DONE (PLAN-dev-sync-command, Phase 2)
  • Phase 4: Auto-sync on startup — ✅ DONE (PLAN-dev-sync-command, Phase 3)

Next Steps​

All phases complete. See PLAN-dev-sync-command.md for the full implementation.