Skip to content
Merged
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
126 changes: 88 additions & 38 deletions .agents/scripts/auto-update-helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,23 @@ get_local_version() {
#######################################
get_remote_version() {
local version=""

# Prefer authenticated gh api (higher rate limit: 5000/hr vs 60/hr)
# This avoids the "remote=unknown" failures seen during overnight polling
# when unauthenticated API quota is exhausted.
# See: https://github.com/marcusquinn/aidevops/issues/4142
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
version=$(gh api repos/marcusquinn/aidevops/contents/VERSION \
--jq '.content' 2>/dev/null |
base64 -d 2>/dev/null |
tr -d '\n')
if [[ -n "$version" ]]; then
echo "$version"
return 0
fi
Comment on lines +311 to +319

Choose a reason for hiding this comment

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

medium

The gh api command uses --jq '.content', which relies on jq being available. To align with the repository rule of preferring built-in tools like grep over external dependencies like jq for simple tasks, consider parsing the JSON output directly using grep or sed instead of gh's --jq flag. This avoids the jq dependency entirely and improves portability.

Suggested change
if command -v gh &>/dev/null && gh auth status &>/dev/null; then
version=$(gh api repos/marcusquinn/aidevops/contents/VERSION \
--jq '.content' 2>/dev/null |
base64 -d 2>/dev/null |
tr -d '\n')
if [[ -n "$version" ]]; then
echo "$version"
return 0
fi
if command -v gh >/dev/null && gh auth status >/dev/null; then
version=$(gh api repos/marcusquinn/aidevops/contents/VERSION 2>/dev/null | \
grep -oP '"content":\s*"\K[^"]+' | \
base64 -d 2>/dev/null | \
tr -d '\n')
if [[ -n "$version" ]]; then
echo "$version"
return 0
fi
fi
References
  1. For shell scripts that need to be portable, prefer built-in tools like grep over external dependencies like jq for simple tasks, to avoid installation requirements on diverse systems.

fi

# Fallback: unauthenticated curl (60 req/hr limit)
if command -v jq &>/dev/null; then
version=$(curl --proto '=https' -fsSL --max-time 10 \
"https://api.github.com/repos/marcusquinn/aidevops/contents/VERSION" 2>/dev/null |
Expand All @@ -314,7 +331,8 @@ get_remote_version() {
return 0
fi
fi
# Fallback to raw (CDN-cached, may be up to 5 min stale)

# Last resort: raw.githubusercontent.com (CDN-cached, may be up to 5 min stale)
curl --proto '=https' -fsSL --max-time 10 \
"https://raw.githubusercontent.com/marcusquinn/aidevops/main/VERSION" 2>/dev/null |
tr -d '\n' || echo "unknown"
Expand Down Expand Up @@ -384,6 +402,18 @@ update_state() {
return 0
}

#######################################
# Run all periodic freshness checks (skills, OpenClaw, tools, upstream watch).
# Extracted to avoid duplicating the same 4-call block at every exit point
# in cmd_check(). Each check has its own internal time gate.
#######################################
run_freshness_checks() {
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
}

#######################################
# Check skill freshness and auto-update if stale (24h gate)
# Called from cmd_check after the main aidevops update logic.
Expand Down Expand Up @@ -1011,10 +1041,7 @@ cmd_check() {
if [[ "$current" == "unknown" || "$remote" == "unknown" ]]; then
log_warn "Could not determine versions (local=$current, remote=$remote)"
update_state "check" "$current" "version_unknown"
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
run_freshness_checks
return 0
fi

Expand Down Expand Up @@ -1042,10 +1069,7 @@ cmd_check() {
fi
fi

check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
run_freshness_checks
return 0
fi

Expand All @@ -1057,32 +1081,62 @@ cmd_check() {
if [[ ! -d "$INSTALL_DIR/.git" ]]; then
log_error "Install directory is not a git repo: $INSTALL_DIR"
update_state "update" "$remote" "no_git_repo"
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
run_freshness_checks
return 1
fi

# Clean up any working tree changes left by setup.sh or other processes
# (e.g., chmod on tracked scripts, scan results written to repo)
# This ensures git pull --ff-only won't be blocked by dirty files.
# See: https://github.com/marcusquinn/aidevops/issues/2286
if ! git -C "$INSTALL_DIR" diff --quiet 2>/dev/null || ! git -C "$INSTALL_DIR" diff --cached --quiet 2>/dev/null; then
log_info "Cleaning up stale working tree changes..."
if ! git -C "$INSTALL_DIR" reset HEAD -- . 2>>"$LOG_FILE"; then
log_warn "git reset HEAD failed during working tree cleanup"
fi
if ! git -C "$INSTALL_DIR" checkout -- . 2>>"$LOG_FILE"; then
log_warn "git checkout -- . failed during working tree cleanup"
fi
fi

# Ensure we're on the main branch (detached HEAD or stale branch blocks pull)
# Mirrors recovery logic from aidevops.sh cmd_update()
# See: https://github.com/marcusquinn/aidevops/issues/4142
local current_branch
current_branch=$(git -C "$INSTALL_DIR" branch --show-current 2>/dev/null || echo "")
if [[ "$current_branch" != "main" ]]; then
log_info "Not on main branch ($current_branch), switching..."
if ! git -C "$INSTALL_DIR" checkout main --quiet 2>>"$LOG_FILE" &&
! git -C "$INSTALL_DIR" checkout -b main origin/main --quiet 2>>"$LOG_FILE"; then
log_error "Failed to switch to main branch from '$current_branch' in $INSTALL_DIR"
update_state "update" "$remote" "branch_switch_failed"
run_freshness_checks
return 1
fi
fi

# Pull latest changes
if ! git -C "$INSTALL_DIR" fetch origin main --quiet 2>>"$LOG_FILE"; then
log_error "git fetch failed"
update_state "update" "$remote" "fetch_failed"
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
run_freshness_checks
return 1
fi
Comment on lines +1102 to 1124

Choose a reason for hiding this comment

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

high

There's a logic error in the order of operations here. The git fetch command on line 1119 needs to run before the branch switching logic (lines 1102-1116). The git checkout -b main origin/main command on line 1110 depends on origin/main being fetched and up-to-date. Without fetching first, this command may fail if origin/main is not available locally or is stale, preventing the script from recovering from a detached HEAD or incorrect branch.

While this mirrors the logic in aidevops.sh, that implementation appears to have the same issue. Fixing it here would be a great improvement.

	# Pull latest changes
	if ! git -C "$INSTALL_DIR" fetch origin main --quiet 2>>"$LOG_FILE"; then
		log_error "git fetch failed"
		update_state "update" "$remote" "fetch_failed"
		run_freshness_checks
		return 1
	fi

	# Ensure we're on the main branch (detached HEAD or stale branch blocks pull)
	# Mirrors recovery logic from aidevops.sh cmd_update()
	# See: https://github.com/marcusquinn/aidevops/issues/4142
	local current_branch
	current_branch=$(git -C "$INSTALL_DIR" branch --show-current 2>/dev/null || echo "")
	if [[ "$current_branch" != "main" ]]; then
		log_info "Not on main branch ($current_branch), switching..."
		if ! git -C "$INSTALL_DIR" checkout main --quiet 2>>"$LOG_FILE" &&
			! git -C "$INSTALL_DIR" checkout -b main origin/main --quiet 2>>"$LOG_FILE"; then
			log_error "Failed to switch to main branch from '$current_branch' in $INSTALL_DIR"
			update_state "update" "$remote" "branch_switch_failed"
			run_freshness_checks
			return 1
		fi
	fi


if ! git -C "$INSTALL_DIR" pull --ff-only origin main --quiet 2>>"$LOG_FILE"; then
log_error "git pull --ff-only failed (local changes?)"
update_state "update" "$remote" "pull_failed"
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
return 1
# Fast-forward failed (diverged history or persistent dirty state).
# Since we just fetched origin/main, reset to it — the repo is managed
# by aidevops and should always track origin/main exactly.
# See: https://github.com/marcusquinn/aidevops/issues/2288
log_warn "git pull --ff-only failed — falling back to reset"
if git -C "$INSTALL_DIR" reset --hard origin/main --quiet 2>>"$LOG_FILE"; then
log_info "Reset to origin/main succeeded"
else
log_error "git reset --hard origin/main also failed"
update_state "update" "$remote" "pull_failed"
run_freshness_checks
return 1
fi
fi

# Run setup.sh non-interactively to deploy agents
Expand All @@ -1105,24 +1159,20 @@ cmd_check() {
else
log_error "setup.sh failed (exit code: $?)"
update_state "update" "$remote" "setup_failed"
check_skill_freshness
check_openclaw_freshness
check_tool_freshness
check_upstream_watch
run_freshness_checks
return 1
fi

# Run daily skill freshness check (24h gate)
check_skill_freshness

# Run daily OpenClaw update check (24h gate)
check_openclaw_freshness

# Run 6-hourly tool freshness check (idle-gated)
check_tool_freshness
# Clean up any working tree changes setup.sh may have introduced
# (e.g., chmod on tracked scripts, scan results written to repo)
# Prevents dirty tree from blocking the next update cycle.
# See: https://github.com/marcusquinn/aidevops/issues/2286
if ! git -C "$INSTALL_DIR" checkout -- . 2>>"$LOG_FILE"; then
log_warn "Post-setup working tree cleanup failed — next update cycle may see dirty state"
fi

# Run daily upstream watch check (24h gate)
check_upstream_watch
# Run periodic freshness checks (skills, OpenClaw, tools, upstream watch)
run_freshness_checks

return 0
}
Expand Down
Loading