diff --git a/README.md b/README.md index da399c2..d8629e6 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ The installer will: - Add `~/.config/mosaic/bin` to your PATH - Sync runtime adapters and skills - Run a health audit +- Detect existing installs and prompt to keep or overwrite local files - Prompt you to run `mosaic init` to set up your agent identity ## First Run @@ -96,24 +97,58 @@ mosaic init # Generate SOUL.md (agent identity) mosaic doctor # Health audit — detect drift and missing files mosaic sync # Sync skills from canonical source mosaic bootstrap # Bootstrap a repo with Mosaic standards -mosaic upgrade [path] # Clean up stale per-project files (see below) -mosaic upgrade --all # Upgrade all projects in ~/src -mosaic upgrade --dry-run # Preview without changes +mosaic upgrade check # Check release upgrade status (no changes) +mosaic upgrade # Upgrade installed Mosaic release (keeps SOUL.md by default) +mosaic upgrade --dry-run # Preview release upgrade without changes +mosaic upgrade --ref main # Upgrade from a specific branch/tag/commit ref +mosaic upgrade --overwrite # Upgrade release and overwrite local files +mosaic upgrade project ... # Project file cleanup mode (see below) ``` +## Upgrading Mosaic Release + +Upgrade the installed framework in place: + +```bash +# Default (safe): keep local SOUL.md + memory +mosaic upgrade + +# Check current/target release info without changing files +mosaic upgrade check + +# Non-interactive +mosaic upgrade --yes + +# Pull a specific ref +mosaic upgrade --ref main + +# Force full overwrite (fresh install semantics) +mosaic upgrade --overwrite --yes +``` + +`mosaic upgrade` re-runs the remote installer and passes install mode controls (`keep`/`overwrite`). +This is the manual upgrade path today and is suitable for future app-driven update checks. + ## Upgrading Projects After centralizing AGENTS.md and SOUL.md, existing projects may have stale files: ```bash # Preview what would change across all projects -mosaic upgrade --all --dry-run +mosaic upgrade project --all --dry-run # Apply to all projects -mosaic upgrade --all +mosaic upgrade project --all # Apply to a specific project -mosaic upgrade ~/src/my-project +mosaic upgrade project ~/src/my-project +``` + +Backward compatibility is preserved for historical usage: + +```bash +mosaic upgrade --all # still routes to project-upgrade +mosaic upgrade ~/src/my-repo # still routes to project-upgrade ``` What it does per project: @@ -186,6 +221,10 @@ Pull the latest and re-run the installer: cd ~/src/mosaic-bootstrap && git pull && bash install.sh ``` +If an existing install is detected, the installer prompts for: +- `keep` (recommended): preserve local `SOUL.md` and `memory/` +- `overwrite`: replace everything in `~/.config/mosaic` + Or use the one-liner again — it always pulls the latest: ```bash diff --git a/bin/mosaic b/bin/mosaic index e98109a..f856af5 100755 --- a/bin/mosaic +++ b/bin/mosaic @@ -14,6 +14,9 @@ set -euo pipefail # mosaic doctor [args...] Health audit # mosaic sync [args...] Sync skills # mosaic bootstrap Bootstrap a repo +# mosaic upgrade release Upgrade installed Mosaic release +# mosaic upgrade check Check release upgrade status (no changes) +# mosaic upgrade project [args] Upgrade project-local stale files MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}" VERSION="0.1.0" @@ -34,7 +37,10 @@ Management: doctor [args...] Audit runtime state and detect drift sync [args...] Sync skills from canonical source bootstrap Bootstrap a repo with Mosaic standards - upgrade [path] Clean up stale SOUL.md/CLAUDE.md in a project + upgrade [mode] [args] Upgrade release (default) or project files + upgrade check Check release upgrade status (no changes) + release-upgrade [...] Upgrade installed Mosaic release + project-upgrade [...] Clean up stale SOUL.md/CLAUDE.md in a project Options: -h, --help Show this help @@ -147,11 +153,54 @@ run_bootstrap() { exec "$MOSAIC_HOME/bin/mosaic-bootstrap-repo" "$@" } -run_upgrade() { +run_release_upgrade() { + check_mosaic_home + exec "$MOSAIC_HOME/bin/mosaic-release-upgrade" "$@" +} + +run_project_upgrade() { check_mosaic_home exec "$MOSAIC_HOME/bin/mosaic-upgrade" "$@" } +run_upgrade() { + check_mosaic_home + + # Default: upgrade installed release + if [[ $# -eq 0 ]]; then + run_release_upgrade + fi + + case "$1" in + release) + shift + run_release_upgrade "$@" + ;; + check) + shift + run_release_upgrade --dry-run "$@" + ;; + project) + shift + run_project_upgrade "$@" + ;; + + # Backward compatibility for historical project-upgrade usage. + --all|--root) + run_project_upgrade "$@" + ;; + --dry-run|--ref|--keep|--overwrite|-y|--yes) + run_release_upgrade "$@" + ;; + -*) + run_release_upgrade "$@" + ;; + *) + run_project_upgrade "$@" + ;; + esac +} + # Main router if [[ $# -eq 0 ]]; then usage @@ -170,6 +219,8 @@ case "$command" in sync) run_sync "$@" ;; bootstrap) run_bootstrap "$@" ;; upgrade) run_upgrade "$@" ;; + release-upgrade) run_release_upgrade "$@" ;; + project-upgrade) run_project_upgrade "$@" ;; help|-h|--help) usage ;; version|-v|--version) echo "mosaic $VERSION" ;; *) diff --git a/bin/mosaic-release-upgrade b/bin/mosaic-release-upgrade new file mode 100755 index 0000000..282650b --- /dev/null +++ b/bin/mosaic-release-upgrade @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +set -euo pipefail + +# mosaic-release-upgrade — Upgrade installed Mosaic framework release. +# +# This re-runs the remote installer with explicit install mode controls. +# Default behavior is safe/idempotent (keep SOUL.md + memory). +# +# Usage: +# mosaic-release-upgrade +# mosaic-release-upgrade --ref main --keep +# mosaic-release-upgrade --ref v0.2.0 --overwrite --yes + +MOSAIC_HOME="${MOSAIC_HOME:-$HOME/.config/mosaic}" +REMOTE_SCRIPT_URL="${MOSAIC_REMOTE_INSTALL_URL:-https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.sh}" +BOOTSTRAP_REF="${MOSAIC_BOOTSTRAP_REF:-main}" +INSTALL_MODE="${MOSAIC_INSTALL_MODE:-keep}" # keep|overwrite +YES=false +DRY_RUN=false + +usage() { + cat < Bootstrap archive ref (branch/tag/commit). Default: main + --keep Keep local files (SOUL.md, memory/) during upgrade (default) + --overwrite Overwrite target install directory contents + -y, --yes Skip confirmation prompt + --dry-run Show actions without executing + -h, --help Show this help +USAGE +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --ref) + [[ $# -lt 2 ]] && { echo "Missing value for --ref" >&2; exit 1; } + BOOTSTRAP_REF="$2" + shift 2 + ;; + --keep) + INSTALL_MODE="keep" + shift + ;; + --overwrite) + INSTALL_MODE="overwrite" + shift + ;; + -y|--yes) + YES=true + shift + ;; + --dry-run) + DRY_RUN=true + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + usage >&2 + exit 1 + ;; + esac +done + +case "$INSTALL_MODE" in + keep|overwrite) ;; + *) + echo "[mosaic-release-upgrade] Invalid install mode: $INSTALL_MODE" >&2 + exit 1 + ;; +esac + +current_version="unknown" +if [[ -x "$MOSAIC_HOME/bin/mosaic" ]]; then + current_version="$("$MOSAIC_HOME/bin/mosaic" --version 2>/dev/null | awk '{print $2}' || true)" + [[ -n "$current_version" ]] || current_version="unknown" +fi + +echo "[mosaic-release-upgrade] Current version: $current_version" +echo "[mosaic-release-upgrade] Target ref: $BOOTSTRAP_REF" +echo "[mosaic-release-upgrade] Install mode: $INSTALL_MODE" +echo "[mosaic-release-upgrade] Installer URL: $REMOTE_SCRIPT_URL" + +if [[ "$DRY_RUN" == "true" ]]; then + echo "[mosaic-release-upgrade] Dry run: no changes applied." + exit 0 +fi + +if [[ "$YES" != "true" && -t 0 ]]; then + printf "Proceed with Mosaic release upgrade? [y/N]: " + read -r confirm + case "${confirm:-n}" in + y|Y|yes|YES) ;; + *) + echo "[mosaic-release-upgrade] Aborted." + exit 1 + ;; + esac +fi + +if command -v curl >/dev/null 2>&1; then + curl -sL "$REMOTE_SCRIPT_URL" | \ + MOSAIC_BOOTSTRAP_REF="$BOOTSTRAP_REF" \ + MOSAIC_INSTALL_MODE="$INSTALL_MODE" \ + MOSAIC_HOME="$MOSAIC_HOME" \ + sh +elif command -v wget >/dev/null 2>&1; then + wget -qO- "$REMOTE_SCRIPT_URL" | \ + MOSAIC_BOOTSTRAP_REF="$BOOTSTRAP_REF" \ + MOSAIC_INSTALL_MODE="$INSTALL_MODE" \ + MOSAIC_HOME="$MOSAIC_HOME" \ + sh +else + echo "[mosaic-release-upgrade] ERROR: curl or wget required." >&2 + exit 1 +fi + diff --git a/bin/mosaic-release-upgrade.ps1 b/bin/mosaic-release-upgrade.ps1 new file mode 100644 index 0000000..016ed7a --- /dev/null +++ b/bin/mosaic-release-upgrade.ps1 @@ -0,0 +1,65 @@ +# mosaic-release-upgrade.ps1 — Upgrade installed Mosaic framework release (Windows) +# +# Usage: +# mosaic-release-upgrade.ps1 +# mosaic-release-upgrade.ps1 -Ref main -Keep +# mosaic-release-upgrade.ps1 -Ref v0.2.0 -Overwrite -Yes +# +param( + [string]$Ref = $(if ($env:MOSAIC_BOOTSTRAP_REF) { $env:MOSAIC_BOOTSTRAP_REF } else { "main" }), + [switch]$Keep, + [switch]$Overwrite, + [switch]$Yes, + [switch]$DryRun +) + +$ErrorActionPreference = "Stop" + +$MosaicHome = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" } +$RemoteInstallerUrl = if ($env:MOSAIC_REMOTE_INSTALL_URL) { + $env:MOSAIC_REMOTE_INSTALL_URL +} else { + "https://git.mosaicstack.dev/mosaic/bootstrap/raw/branch/main/remote-install.ps1" +} + +$installMode = if ($Overwrite) { "overwrite" } elseif ($Keep) { "keep" } elseif ($env:MOSAIC_INSTALL_MODE) { $env:MOSAIC_INSTALL_MODE } else { "keep" } +if ($installMode -notin @("keep", "overwrite")) { + Write-Host "[mosaic-release-upgrade] Invalid install mode: $installMode" -ForegroundColor Red + exit 1 +} + +$currentVersion = "unknown" +$mosaicCmd = Join-Path $MosaicHome "bin\mosaic.ps1" +if (Test-Path $mosaicCmd) { + try { + $currentVersion = (& $mosaicCmd --version) -replace '^mosaic\s+', '' + } + catch { + $currentVersion = "unknown" + } +} + +Write-Host "[mosaic-release-upgrade] Current version: $currentVersion" +Write-Host "[mosaic-release-upgrade] Target ref: $Ref" +Write-Host "[mosaic-release-upgrade] Install mode: $installMode" +Write-Host "[mosaic-release-upgrade] Installer URL: $RemoteInstallerUrl" + +if ($DryRun) { + Write-Host "[mosaic-release-upgrade] Dry run: no changes applied." + exit 0 +} + +if (-not $Yes) { + $confirmation = Read-Host "Proceed with Mosaic release upgrade? [y/N]" + if ($confirmation -notin @("y", "Y", "yes", "YES")) { + Write-Host "[mosaic-release-upgrade] Aborted." + exit 1 + } +} + +$env:MOSAIC_BOOTSTRAP_REF = $Ref +$env:MOSAIC_INSTALL_MODE = $installMode +$env:MOSAIC_HOME = $MosaicHome + +Invoke-RestMethod -Uri $RemoteInstallerUrl | Invoke-Expression + diff --git a/bin/mosaic.ps1 b/bin/mosaic.ps1 index 47175c7..943db54 100644 --- a/bin/mosaic.ps1 +++ b/bin/mosaic.ps1 @@ -31,7 +31,10 @@ Management: doctor [args...] Audit runtime state and detect drift sync [args...] Sync skills from canonical source bootstrap Bootstrap a repo with Mosaic standards - upgrade [path] Clean up stale SOUL.md/CLAUDE.md in a project + upgrade [mode] [args] Upgrade release (default) or project files + upgrade check Check release upgrade status (no changes) + release-upgrade [...] Upgrade installed Mosaic release + project-upgrade [...] Clean up stale SOUL.md/CLAUDE.md in a project Options: -h, --help Show this help @@ -142,6 +145,48 @@ switch ($command) { & (Join-Path $MosaicHome "bin\mosaic-bootstrap-repo") @remaining } "upgrade" { + Assert-MosaicHome + if ($remaining.Count -eq 0) { + & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") + break + } + + $mode = $remaining[0] + $tail = if ($remaining.Count -gt 1) { $remaining[1..($remaining.Count - 1)] } else { @() } + + switch -Regex ($mode) { + "^release$" { + & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @tail + } + "^check$" { + & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") -DryRun @tail + } + "^project$" { + Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow + & (Join-Path $MosaicHome "bin\mosaic-upgrade") @tail + } + "^(--all|--root)$" { + Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow + & (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining + } + "^(--dry-run|--ref|--keep|--overwrite|-y|--yes)$" { + & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining + } + "^-.*" { + & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining + } + default { + Write-Host "[mosaic] NOTE: treating positional argument as project path." -ForegroundColor Yellow + Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow + & (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining + } + } + } + "release-upgrade" { + Assert-MosaicHome + & (Join-Path $MosaicHome "bin\mosaic-release-upgrade.ps1") @remaining + } + "project-upgrade" { Assert-MosaicHome Write-Host "[mosaic] NOTE: mosaic-upgrade requires bash. Use Git Bash or WSL." -ForegroundColor Yellow & (Join-Path $MosaicHome "bin\mosaic-upgrade") @remaining diff --git a/install.ps1 b/install.ps1 index a3949fb..d8423df 100644 --- a/install.ps1 +++ b/install.ps1 @@ -8,16 +8,96 @@ $ErrorActionPreference = "Stop" $SourceDir = $PSScriptRoot $TargetDir = if ($env:MOSAIC_HOME) { $env:MOSAIC_HOME } else { Join-Path $env:USERPROFILE ".config\mosaic" } +$InstallMode = if ($env:MOSAIC_INSTALL_MODE) { $env:MOSAIC_INSTALL_MODE.ToLowerInvariant() } else { "prompt" } # prompt|keep|overwrite +$PreservePaths = @("SOUL.md", "memory") function Write-Ok { param([string]$Msg) Write-Host " ✓ " -ForegroundColor Green -NoNewline; Write-Host $Msg } function Write-Warn { param([string]$Msg) Write-Host " ⚠ " -ForegroundColor Yellow -NoNewline; Write-Host $Msg } function Write-Fail { param([string]$Msg) Write-Host " ✗ " -ForegroundColor Red -NoNewline; Write-Host $Msg } function Write-Step { param([string]$Msg) Write-Host ""; Write-Host $Msg -ForegroundColor White -BackgroundColor DarkGray } +function Test-ExistingInstall { + if (-not (Test-Path $TargetDir)) { return $false } + return (Test-Path (Join-Path $TargetDir "bin\mosaic")) -or (Test-Path (Join-Path $TargetDir "AGENTS.md")) -or (Test-Path (Join-Path $TargetDir "SOUL.md")) +} + +function Select-InstallMode { + switch ($InstallMode) { + "prompt" { } + "keep" { return } + "overwrite" { return } + default { + Write-Fail "Invalid MOSAIC_INSTALL_MODE '$InstallMode'. Use: prompt, keep, overwrite." + exit 1 + } + } + + if (-not (Test-ExistingInstall)) { + $script:InstallMode = "overwrite" + return + } + + if (-not [Environment]::UserInteractive) { + Write-Warn "Existing install detected without interactive input; defaulting to keep local files." + $script:InstallMode = "keep" + return + } + + Write-Host "" + Write-Host "Existing Mosaic install detected at: $TargetDir" + Write-Host "Choose reinstall mode:" + Write-Host " 1) keep Keep local files (SOUL.md, memory/) while updating framework" + Write-Host " 2) overwrite Replace everything in $TargetDir" + Write-Host " 3) cancel Abort install" + $selection = Read-Host "Selection [1/2/3] (default: 1)" + + $normalizedSelection = if ($null -eq $selection) { "" } else { $selection.ToLowerInvariant() } + switch ($normalizedSelection) { + "" { $script:InstallMode = "keep" } + "1" { $script:InstallMode = "keep" } + "k" { $script:InstallMode = "keep" } + "keep" { $script:InstallMode = "keep" } + "2" { $script:InstallMode = "overwrite" } + "o" { $script:InstallMode = "overwrite" } + "overwrite" { $script:InstallMode = "overwrite" } + "3" { Write-Fail "Install cancelled."; exit 1 } + "c" { Write-Fail "Install cancelled."; exit 1 } + "cancel" { Write-Fail "Install cancelled."; exit 1 } + default { + Write-Warn "Unrecognized selection '$selection'; defaulting to keep." + $script:InstallMode = "keep" + } + } +} + # ── Install framework ──────────────────────────────────────── Write-Step " Installing Mosaic framework " if (-not (Test-Path $TargetDir)) { New-Item -ItemType Directory -Path $TargetDir -Force | Out-Null } +Select-InstallMode + +if ($InstallMode -eq "keep") { + Write-Ok "Install mode: keep local SOUL.md/memory while updating framework" +} +else { + Write-Ok "Install mode: overwrite existing files" +} + +$preserveTmp = $null +if ($InstallMode -eq "keep") { + $preserveTmp = Join-Path ([System.IO.Path]::GetTempPath()) ("mosaic-preserve-" + [Guid]::NewGuid().ToString("N")) + New-Item -ItemType Directory -Path $preserveTmp -Force | Out-Null + foreach ($relPath in $PreservePaths) { + $src = Join-Path $TargetDir $relPath + if (Test-Path $src) { + $dstParent = Join-Path $preserveTmp (Split-Path $relPath -Parent) + if (-not [string]::IsNullOrEmpty($dstParent) -and -not (Test-Path $dstParent)) { + New-Item -ItemType Directory -Path $dstParent -Force | Out-Null + } + Copy-Item $src (Join-Path $preserveTmp $relPath) -Recurse -Force + } + } +} Get-ChildItem $TargetDir -Exclude ".git" | Remove-Item -Recurse -Force Get-ChildItem $SourceDir -Exclude ".git" | ForEach-Object { @@ -30,6 +110,24 @@ Get-ChildItem $SourceDir -Exclude ".git" | ForEach-Object { } } +if ($InstallMode -eq "keep" -and $null -ne $preserveTmp) { + foreach ($relPath in $PreservePaths) { + $src = Join-Path $preserveTmp $relPath + if (Test-Path $src) { + $dst = Join-Path $TargetDir $relPath + if (Test-Path $dst) { + Remove-Item $dst -Recurse -Force + } + $dstParent = Split-Path $dst -Parent + if (-not (Test-Path $dstParent)) { + New-Item -ItemType Directory -Path $dstParent -Force | Out-Null + } + Copy-Item $src $dst -Recurse -Force + } + } + Remove-Item -Path $preserveTmp -Recurse -Force -ErrorAction SilentlyContinue +} + Write-Ok "Framework installed to $TargetDir" # ── Post-install tasks ─────────────────────────────────────── diff --git a/install.sh b/install.sh index b05f188..3fd4a37 100755 --- a/install.sh +++ b/install.sh @@ -3,6 +3,8 @@ set -euo pipefail SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TARGET_DIR="${MOSAIC_HOME:-$HOME/.config/mosaic}" +INSTALL_MODE="${MOSAIC_INSTALL_MODE:-prompt}" # prompt|keep|overwrite +PRESERVE_PATHS=("SOUL.md" "memory") # Colors (disabled if not a terminal) if [[ -t 1 ]]; then @@ -17,17 +19,125 @@ warn() { echo -e " ${YELLOW}⚠${RESET} $1" >&2; } fail() { echo -e " ${RED}✗${RESET} $1" >&2; } step() { echo -e "\n${BOLD}$1${RESET}"; } +is_existing_install() { + [[ -d "$TARGET_DIR" ]] || return 1 + [[ -f "$TARGET_DIR/bin/mosaic" || -f "$TARGET_DIR/AGENTS.md" || -f "$TARGET_DIR/SOUL.md" ]] +} + +select_install_mode() { + case "$INSTALL_MODE" in + keep|overwrite|prompt) ;; + *) + fail "Invalid MOSAIC_INSTALL_MODE='$INSTALL_MODE'. Use: prompt, keep, overwrite." + exit 1 + ;; + esac + + if ! is_existing_install; then + INSTALL_MODE="overwrite" + return + fi + + case "$INSTALL_MODE" in + keep|overwrite) + ;; + prompt) + if [[ -t 0 ]]; then + echo "" + echo "Existing Mosaic install detected at: $TARGET_DIR" + echo "Choose reinstall mode:" + echo " 1) keep Keep local files (SOUL.md, memory/) while updating framework" + echo " 2) overwrite Replace everything in $TARGET_DIR" + echo " 3) cancel Abort install" + printf "Selection [1/2/3] (default: 1): " + read -r selection + + case "${selection:-1}" in + 1|k|K|keep|KEEP) INSTALL_MODE="keep" ;; + 2|o|O|overwrite|OVERWRITE) INSTALL_MODE="overwrite" ;; + 3|c|C|cancel|CANCEL|n|N|no|NO) + fail "Install cancelled." + exit 1 + ;; + *) + warn "Unrecognized selection '$selection'; defaulting to keep." + INSTALL_MODE="keep" + ;; + esac + else + warn "Existing install detected without interactive input; defaulting to keep local files." + INSTALL_MODE="keep" + fi + ;; + esac +} + +sync_framework() { + local source_real target_real + source_real="$(cd "$SOURCE_DIR" && pwd -P)" + target_real="$(mkdir -p "$TARGET_DIR" && cd "$TARGET_DIR" && pwd -P)" + + if [[ "$source_real" == "$target_real" ]]; then + warn "Source and target are the same directory; skipping file sync." + return + fi + + if command -v rsync >/dev/null 2>&1; then + local rsync_args=(-a --delete --exclude ".git") + + if [[ "$INSTALL_MODE" == "keep" ]]; then + local path + for path in "${PRESERVE_PATHS[@]}"; do + rsync_args+=(--exclude "$path") + done + fi + + rsync "${rsync_args[@]}" "$SOURCE_DIR/" "$TARGET_DIR/" + return + fi + + local preserve_tmp="" + if [[ "$INSTALL_MODE" == "keep" ]]; then + preserve_tmp="$(mktemp -d "${TMPDIR:-/tmp}/mosaic-preserve-XXXXXX")" + local path + for path in "${PRESERVE_PATHS[@]}"; do + if [[ -e "$TARGET_DIR/$path" ]]; then + mkdir -p "$preserve_tmp/$(dirname "$path")" + cp -R "$TARGET_DIR/$path" "$preserve_tmp/$path" + fi + done + fi + + find "$TARGET_DIR" -mindepth 1 -maxdepth 1 ! -name ".git" -exec rm -rf {} + + cp -R "$SOURCE_DIR"/. "$TARGET_DIR"/ + rm -rf "$TARGET_DIR/.git" + + if [[ -n "$preserve_tmp" ]]; then + local path + for path in "${PRESERVE_PATHS[@]}"; do + if [[ -e "$preserve_tmp/$path" ]]; then + rm -rf "$TARGET_DIR/$path" + mkdir -p "$TARGET_DIR/$(dirname "$path")" + cp -R "$preserve_tmp/$path" "$TARGET_DIR/$path" + fi + done + rm -rf "$preserve_tmp" + fi +} + step "Installing Mosaic framework" mkdir -p "$TARGET_DIR" +select_install_mode -if command -v rsync >/dev/null 2>&1; then - rsync -a --delete "$SOURCE_DIR/" "$TARGET_DIR/" +if [[ "$INSTALL_MODE" == "keep" ]]; then + ok "Install mode: keep local SOUL.md/memory while updating framework" else - rm -rf "$TARGET_DIR"/* - cp -R "$SOURCE_DIR"/* "$TARGET_DIR"/ + ok "Install mode: overwrite existing files" fi +sync_framework + chmod +x "$TARGET_DIR"/bin/* chmod +x "$TARGET_DIR"/install.sh diff --git a/remote-install.ps1 b/remote-install.ps1 index d94dcba..635ab11 100644 --- a/remote-install.ps1 +++ b/remote-install.ps1 @@ -8,12 +8,13 @@ # $ErrorActionPreference = "Stop" -$ArchiveUrl = "https://git.mosaicstack.dev/mosaic/bootstrap/archive/main.zip" +$BootstrapRef = if ($env:MOSAIC_BOOTSTRAP_REF) { $env:MOSAIC_BOOTSTRAP_REF } else { "main" } +$ArchiveUrl = "https://git.mosaicstack.dev/mosaic/bootstrap/archive/$BootstrapRef.zip" $WorkDir = Join-Path $env:TEMP "mosaic-bootstrap-$PID" $ZipPath = "$WorkDir.zip" try { - Write-Host "[mosaic] Downloading bootstrap archive..." + Write-Host "[mosaic] Downloading bootstrap archive (ref: $BootstrapRef)..." New-Item -ItemType Directory -Path $WorkDir -Force | Out-Null Invoke-WebRequest -Uri $ArchiveUrl -OutFile $ZipPath -UseBasicParsing diff --git a/remote-install.sh b/remote-install.sh index e0ae3fa..95cb016 100755 --- a/remote-install.sh +++ b/remote-install.sh @@ -9,7 +9,8 @@ # set -eu -ARCHIVE_URL="https://git.mosaicstack.dev/mosaic/bootstrap/archive/main.tar.gz" +BOOTSTRAP_REF="${MOSAIC_BOOTSTRAP_REF:-main}" +ARCHIVE_URL="https://git.mosaicstack.dev/mosaic/bootstrap/archive/${BOOTSTRAP_REF}.tar.gz" TMPDIR_BASE="${TMPDIR:-/tmp}" WORK_DIR="$TMPDIR_BASE/mosaic-bootstrap-$$" @@ -18,7 +19,7 @@ cleanup() { } trap cleanup EXIT -echo "[mosaic] Downloading bootstrap archive..." +echo "[mosaic] Downloading bootstrap archive (ref: $BOOTSTRAP_REF)..." mkdir -p "$WORK_DIR"