aidevops 3.13.10 → 3.13.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/VERSION +1 -1
- package/aidevops.sh +93 -1
- package/package.json +1 -1
- package/setup-modules/schedulers-platform.sh +81 -44
- package/setup.sh +1 -1
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.13.
|
|
1
|
+
3.13.11
|
package/aidevops.sh
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# AI DevOps Framework CLI
|
|
6
6
|
# Usage: aidevops <command> [options]
|
|
7
7
|
#
|
|
8
|
-
# Version: 3.13.
|
|
8
|
+
# Version: 3.13.11
|
|
9
9
|
|
|
10
10
|
set -euo pipefail
|
|
11
11
|
|
|
@@ -521,6 +521,90 @@ _update_check_setsid() {
|
|
|
521
521
|
return 0
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
+
# GH#21735: Notify operator when framework workflow templates change.
|
|
525
|
+
# When .agents/templates/workflows/*.yml or *-reusable.yml workflows change
|
|
526
|
+
# in a framework update, downstream repos that use these as workflow_call
|
|
527
|
+
# callers may have drifted from the new template. Detection and remediation
|
|
528
|
+
# both already exist (`aidevops check-workflows`, `aidevops sync-workflows
|
|
529
|
+
# --apply`); the gap was the notification surface — operators only learned
|
|
530
|
+
# of drift when downstream CI failed (canonical incident: a managed
|
|
531
|
+
# downstream repo's issue-sync.yml failed silently after the upstream
|
|
532
|
+
# template added a new input).
|
|
533
|
+
#
|
|
534
|
+
# This check inspects the SHA-window diff for changes to workflow caller
|
|
535
|
+
# templates and reusable workflows, prints a warning, and emits a daily
|
|
536
|
+
# advisory so the next session greeting surfaces it if the operator
|
|
537
|
+
# misses the inline output.
|
|
538
|
+
#
|
|
539
|
+
# Args: $1=old_sha, $2=new_sha
|
|
540
|
+
# Returns: 0 (always — informational only, never breaks update)
|
|
541
|
+
_update_check_workflow_drift() {
|
|
542
|
+
local old_sha="$1"
|
|
543
|
+
local new_sha="$2"
|
|
544
|
+
[[ -z "$old_sha" || -z "$new_sha" || "$old_sha" == "$new_sha" ]] && return 0
|
|
545
|
+
# `.git` is a directory in a regular repo and a file in a worktree;
|
|
546
|
+
# `-e` covers both so the helper is testable from a worktree.
|
|
547
|
+
[[ ! -e "$INSTALL_DIR/.git" ]] && return 0
|
|
548
|
+
|
|
549
|
+
# Files that propagate to downstream caller workflows OR are themselves
|
|
550
|
+
# reusable workflow definitions referenced by downstream callers.
|
|
551
|
+
# Internal .github/workflows/*.yml hotfixes (e.g. self-test runs) are
|
|
552
|
+
# intentionally skipped to avoid false-positive nags.
|
|
553
|
+
local relevant_files
|
|
554
|
+
relevant_files=$(git -C "$INSTALL_DIR" diff --name-only "$old_sha" "$new_sha" -- \
|
|
555
|
+
'.agents/templates/workflows/' \
|
|
556
|
+
'.github/workflows/' \
|
|
557
|
+
2>/dev/null \
|
|
558
|
+
| grep -E '(\.agents/templates/workflows/.*\.ya?ml$|\.github/workflows/.*-reusable\.ya?ml$)' \
|
|
559
|
+
|| true)
|
|
560
|
+
[[ -z "$relevant_files" ]] && return 0
|
|
561
|
+
|
|
562
|
+
local file_count
|
|
563
|
+
file_count=$(printf '%s\n' "$relevant_files" | wc -l | tr -d ' ')
|
|
564
|
+
echo ""
|
|
565
|
+
print_warning "Workflow templates updated ($file_count file(s)) — downstream callers may have drifted."
|
|
566
|
+
print_info " Detect drift: aidevops check-workflows"
|
|
567
|
+
print_info " Apply fix: aidevops sync-workflows --apply [--repo OWNER/REPO]"
|
|
568
|
+
|
|
569
|
+
# Persist as advisory so the next session greeting surfaces it even if
|
|
570
|
+
# the operator misses the inline warning. Day-stamped ID makes repeated
|
|
571
|
+
# updates within the same day idempotent (one advisory per day);
|
|
572
|
+
# 'aidevops security dismiss <id>' silences a specific day's advisory.
|
|
573
|
+
_update_emit_workflow_drift_advisory "$relevant_files" || true
|
|
574
|
+
return 0
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
# Companion to _update_check_workflow_drift — separated for testability.
|
|
578
|
+
# Args: $1=relevant_files (newline-separated)
|
|
579
|
+
# Returns: 0 (always — fail-open; advisory write must never break update)
|
|
580
|
+
_update_emit_workflow_drift_advisory() {
|
|
581
|
+
local relevant_files="$1"
|
|
582
|
+
local advisories_dir="${HOME}/.aidevops/advisories"
|
|
583
|
+
local adv_id
|
|
584
|
+
adv_id="workflow-drift-$(date +%Y%m%d)"
|
|
585
|
+
local dismissed_file="$advisories_dir/dismissed.txt"
|
|
586
|
+
|
|
587
|
+
# Skip if today's advisory was already dismissed.
|
|
588
|
+
if [[ -f "$dismissed_file" ]] && grep -qxF "$adv_id" "$dismissed_file" 2>/dev/null; then
|
|
589
|
+
return 0
|
|
590
|
+
fi
|
|
591
|
+
|
|
592
|
+
mkdir -p "$advisories_dir" 2>/dev/null || return 0
|
|
593
|
+
local adv_file="$advisories_dir/${adv_id}.advisory"
|
|
594
|
+
|
|
595
|
+
{
|
|
596
|
+
printf 'Workflow templates changed — downstream caller workflows may have drifted.\n'
|
|
597
|
+
printf '\n'
|
|
598
|
+
printf 'Files changed in this update:\n'
|
|
599
|
+
printf '%s\n' "$relevant_files" | sed 's|^| |'
|
|
600
|
+
printf '\n'
|
|
601
|
+
printf 'Detect drift: aidevops check-workflows\n'
|
|
602
|
+
printf 'Apply fix: aidevops sync-workflows --apply [--repo OWNER/REPO]\n'
|
|
603
|
+
printf 'Background: reference/reusable-workflows.md\n'
|
|
604
|
+
} >"$adv_file" 2>/dev/null || return 0
|
|
605
|
+
return 0
|
|
606
|
+
}
|
|
607
|
+
|
|
524
608
|
# Verify supply chain signature after pulling framework updates.
|
|
525
609
|
# Checks that the HEAD commit is signed by the trusted maintainer key.
|
|
526
610
|
# Non-blocking: warns on failure, does not abort the update.
|
|
@@ -687,6 +771,11 @@ cmd_update() {
|
|
|
687
771
|
print_info "Re-running setup to deploy latest scripts..."
|
|
688
772
|
bash "$INSTALL_DIR/setup.sh" --non-interactive
|
|
689
773
|
fi
|
|
774
|
+
# GH#21735: workflow templates can change between
|
|
775
|
+
# releases without triggering has_code_drift (templates
|
|
776
|
+
# live outside the deploy-affecting paths). Check the
|
|
777
|
+
# template subset separately and surface drift.
|
|
778
|
+
_update_check_workflow_drift "$deployed_sha" "$local_hash"
|
|
690
779
|
fi
|
|
691
780
|
fi
|
|
692
781
|
fi
|
|
@@ -717,6 +806,9 @@ cmd_update() {
|
|
|
717
806
|
git log --oneline "$old_hash..$new_hash" | grep -E '^[a-f0-9]+ (feat|fix|refactor|perf|docs):' | head -20
|
|
718
807
|
[[ "$total_commits" -gt 20 ]] && echo " ... and more (run 'git log --oneline' in $INSTALL_DIR for full list)"
|
|
719
808
|
fi
|
|
809
|
+
# GH#21735: surface workflow template drift so the
|
|
810
|
+
# operator can resync downstream callers before CI bites.
|
|
811
|
+
_update_check_workflow_drift "$old_hash" "$new_hash"
|
|
720
812
|
fi
|
|
721
813
|
echo ""
|
|
722
814
|
# Verify supply chain integrity before applying changes
|
package/package.json
CHANGED
|
@@ -47,27 +47,22 @@ fi
|
|
|
47
47
|
# --- Functions ---
|
|
48
48
|
|
|
49
49
|
# Resolve and validate the log directory from config for contribution watch.
|
|
50
|
-
#
|
|
50
|
+
# Delegates resolution to _resolve_log_dir (shared-constants.sh), then applies
|
|
51
|
+
# install-time character validation (safe for shell paths and cron lines).
|
|
51
52
|
# Prints the resolved absolute path. Returns 1 on invalid characters.
|
|
52
53
|
_resolve_cw_log_dir() {
|
|
53
54
|
local _cw_log_dir
|
|
54
|
-
|
|
55
|
-
if type _jsonc_get &>/dev/null; then
|
|
56
|
-
_cw_log_dir=$(_jsonc_get "paths.log_dir" "~/.aidevops/logs")
|
|
57
|
-
else
|
|
58
|
-
_cw_log_dir="~/.aidevops/logs"
|
|
59
|
-
fi
|
|
55
|
+
_cw_log_dir=$(_resolve_log_dir)
|
|
60
56
|
# Whitelist: only allow characters safe in shell paths and cron lines.
|
|
61
|
-
# Reject anything outside [A-Za-z0-9_./
|
|
57
|
+
# Reject anything outside [A-Za-z0-9_./ -] (tilde already expanded by _resolve_log_dir).
|
|
62
58
|
# Store regex in variable — bash [[ =~ ]] requires unquoted RHS for regex,
|
|
63
59
|
# and a variable avoids quoting issues with special chars in the pattern.
|
|
64
|
-
local _cw_log_dir_re='^[A-Za-z0-9_./
|
|
60
|
+
local _cw_log_dir_re='^[A-Za-z0-9_./ -]+$'
|
|
65
61
|
if ! [[ "$_cw_log_dir" =~ $_cw_log_dir_re ]]; then
|
|
66
62
|
# Redirect to stderr so $() captures only the path result
|
|
67
|
-
print_error "Invalid characters in paths.log_dir (only [A-Za-z0-9_./
|
|
63
|
+
print_error "Invalid characters in paths.log_dir (only [A-Za-z0-9_./ -] allowed): $_cw_log_dir" >&2
|
|
68
64
|
return 1
|
|
69
65
|
fi
|
|
70
|
-
_cw_log_dir="${_cw_log_dir/#\~/$HOME}"
|
|
71
66
|
printf '%s' "$_cw_log_dir"
|
|
72
67
|
return 0
|
|
73
68
|
}
|
|
@@ -310,18 +305,35 @@ setup_complexity_scan() {
|
|
|
310
305
|
return 0
|
|
311
306
|
}
|
|
312
307
|
|
|
313
|
-
# Install
|
|
314
|
-
#
|
|
308
|
+
# Install the dedicated merge-pass launchd plist (macOS).
|
|
309
|
+
# Supersedes the t2862 sh.aidevops.pulse-merge-routine approach (t21247, GH#21247):
|
|
310
|
+
# - Label: com.aidevops.aidevops-supervisor-merge
|
|
311
|
+
# - Script: pulse-wrapper.sh --merge-only (full bootstrap + merge only)
|
|
312
|
+
# - Interval: 60s (down from 120s) — targets <=90s PR-green-to-merged latency
|
|
313
|
+
# - Log: pulse-merge.log (isolated from main pulse log)
|
|
314
|
+
#
|
|
315
|
+
# Args: $1=label $2=wrapper_script $3=log_dir
|
|
315
316
|
_install_pulse_merge_routine_launchd() {
|
|
316
317
|
local pmr_label="$1"
|
|
317
318
|
local pmr_script="$2"
|
|
318
319
|
local _pmr_log_dir="$3"
|
|
319
320
|
local pmr_plist="$HOME/Library/LaunchAgents/${pmr_label}.plist"
|
|
320
321
|
|
|
321
|
-
|
|
322
|
+
# One-time migration: unload and remove the legacy sh.aidevops.pulse-merge-routine
|
|
323
|
+
# plist (installed by t2862) to avoid running two merge passes simultaneously.
|
|
324
|
+
local _legacy_pmr_label="sh.aidevops.pulse-merge-routine"
|
|
325
|
+
local _legacy_pmr_plist="$HOME/Library/LaunchAgents/${_legacy_pmr_label}.plist"
|
|
326
|
+
if [[ -f "$_legacy_pmr_plist" ]]; then
|
|
327
|
+
launchctl unload "$_legacy_pmr_plist" 2>/dev/null || true
|
|
328
|
+
rm -f "$_legacy_pmr_plist"
|
|
329
|
+
print_info "Removed legacy pulse-merge-routine plist (superseded by ${pmr_label})"
|
|
330
|
+
fi
|
|
331
|
+
|
|
332
|
+
local _xml_pmr_script _xml_pmr_home _xml_pmr_log_dir _xml_bash_bin
|
|
322
333
|
_xml_pmr_script=$(_xml_escape "$pmr_script")
|
|
323
334
|
_xml_pmr_home=$(_xml_escape "$HOME")
|
|
324
335
|
_xml_pmr_log_dir=$(_xml_escape "$_pmr_log_dir")
|
|
336
|
+
_xml_bash_bin=$(_xml_escape "$(_resolve_modern_bash)")
|
|
325
337
|
|
|
326
338
|
local pmr_plist_content
|
|
327
339
|
pmr_plist_content=$(
|
|
@@ -334,16 +346,16 @@ _install_pulse_merge_routine_launchd() {
|
|
|
334
346
|
<string>${pmr_label}</string>
|
|
335
347
|
<key>ProgramArguments</key>
|
|
336
348
|
<array>
|
|
337
|
-
<string>$
|
|
349
|
+
<string>${_xml_bash_bin}</string>
|
|
338
350
|
<string>${_xml_pmr_script}</string>
|
|
339
|
-
<string
|
|
351
|
+
<string>--merge-only</string>
|
|
340
352
|
</array>
|
|
341
353
|
<key>StartInterval</key>
|
|
342
|
-
<integer>
|
|
354
|
+
<integer>60</integer>
|
|
343
355
|
<key>StandardOutPath</key>
|
|
344
|
-
<string>${_xml_pmr_log_dir}/pulse-merge
|
|
356
|
+
<string>${_xml_pmr_log_dir}/pulse-merge.log</string>
|
|
345
357
|
<key>StandardErrorPath</key>
|
|
346
|
-
<string>${_xml_pmr_log_dir}/pulse-merge
|
|
358
|
+
<string>${_xml_pmr_log_dir}/pulse-merge.log</string>
|
|
347
359
|
<key>EnvironmentVariables</key>
|
|
348
360
|
<dict>
|
|
349
361
|
<key>PATH</key>
|
|
@@ -352,8 +364,6 @@ _install_pulse_merge_routine_launchd() {
|
|
|
352
364
|
<string>${_xml_pmr_home}</string>
|
|
353
365
|
</dict>
|
|
354
366
|
<key>RunAtLoad</key>
|
|
355
|
-
<true/>
|
|
356
|
-
<key>KeepAlive</key>
|
|
357
367
|
<false/>
|
|
358
368
|
<key>ProcessType</key>
|
|
359
369
|
<string>Background</string>
|
|
@@ -361,50 +371,77 @@ _install_pulse_merge_routine_launchd() {
|
|
|
361
371
|
<true/>
|
|
362
372
|
<key>Nice</key>
|
|
363
373
|
<integer>10</integer>
|
|
374
|
+
<key>SoftResourceLimits</key>
|
|
375
|
+
<dict>
|
|
376
|
+
<key>NumberOfFiles</key>
|
|
377
|
+
<integer>4096</integer>
|
|
378
|
+
</dict>
|
|
364
379
|
</dict>
|
|
365
380
|
</plist>
|
|
366
381
|
PMR_PLIST
|
|
367
382
|
)
|
|
368
383
|
|
|
369
384
|
if _launchd_install_if_changed "$pmr_label" "$pmr_plist" "$pmr_plist_content"; then
|
|
370
|
-
print_info "Pulse merge
|
|
385
|
+
print_info "Pulse merge pass enabled (launchd, every 60s via --merge-only)"
|
|
371
386
|
else
|
|
372
|
-
print_warning "Failed to load pulse merge
|
|
387
|
+
print_warning "Failed to load pulse merge pass LaunchAgent"
|
|
373
388
|
fi
|
|
374
389
|
return 0
|
|
375
390
|
}
|
|
376
391
|
|
|
377
|
-
# Install
|
|
378
|
-
#
|
|
392
|
+
# Install the dedicated merge-pass scheduler via systemd or cron (Linux).
|
|
393
|
+
# Supersedes the t2862 aidevops-pulse-merge-routine approach (t21247, GH#21247):
|
|
394
|
+
# - Command: pulse-wrapper.sh --merge-only
|
|
395
|
+
# - Interval: every 60s (cron * * * * *, systemd OnUnitActiveSec=60)
|
|
396
|
+
# - Log: pulse-merge.log
|
|
397
|
+
# Args: $1=wrapper_script $2=log_dir
|
|
379
398
|
_install_pulse_merge_routine_linux() {
|
|
380
399
|
local pmr_script="$1"
|
|
381
400
|
local _pmr_log_dir="$2"
|
|
382
|
-
local pmr_systemd="aidevops-pulse-merge
|
|
401
|
+
local pmr_systemd="aidevops-pulse-merge"
|
|
402
|
+
|
|
403
|
+
# One-time migration: remove legacy systemd/cron entry for aidevops-pulse-merge-routine.
|
|
404
|
+
if _systemd_user_available 2>/dev/null; then
|
|
405
|
+
systemctl --user disable --now "aidevops-pulse-merge-routine.timer" 2>/dev/null || true
|
|
406
|
+
rm -f "$HOME/.config/systemd/user/aidevops-pulse-merge-routine.service" 2>/dev/null || true
|
|
407
|
+
rm -f "$HOME/.config/systemd/user/aidevops-pulse-merge-routine.timer" 2>/dev/null || true
|
|
408
|
+
systemctl --user daemon-reload 2>/dev/null || true
|
|
409
|
+
fi
|
|
410
|
+
if crontab -l 2>/dev/null | grep -qF "pulse-merge-routine"; then
|
|
411
|
+
crontab -l 2>/dev/null | grep -v 'aidevops: pulse-merge-routine' | crontab - 2>/dev/null || true
|
|
412
|
+
fi
|
|
413
|
+
|
|
383
414
|
_install_scheduler_linux \
|
|
384
415
|
"$pmr_systemd" \
|
|
385
|
-
"aidevops: pulse-merge-
|
|
386
|
-
"
|
|
387
|
-
"\"${pmr_script}\"
|
|
388
|
-
"
|
|
389
|
-
"${_pmr_log_dir}/pulse-merge
|
|
416
|
+
"aidevops: pulse-merge (--merge-only)" \
|
|
417
|
+
"* * * * *" \
|
|
418
|
+
"\"${pmr_script}\" --merge-only" \
|
|
419
|
+
"60" \
|
|
420
|
+
"${_pmr_log_dir}/pulse-merge.log" \
|
|
390
421
|
"" \
|
|
391
|
-
"Pulse merge
|
|
392
|
-
"Failed to install pulse merge
|
|
422
|
+
"Pulse merge pass enabled (every 60s via --merge-only)" \
|
|
423
|
+
"Failed to install pulse merge pass scheduler" \
|
|
393
424
|
"true" \
|
|
394
425
|
"true"
|
|
395
426
|
return 0
|
|
396
427
|
}
|
|
397
428
|
|
|
398
|
-
# Setup
|
|
399
|
-
#
|
|
400
|
-
#
|
|
401
|
-
#
|
|
402
|
-
#
|
|
403
|
-
#
|
|
429
|
+
# Setup the dedicated merge-pass scheduler (t21247, GH#21247).
|
|
430
|
+
#
|
|
431
|
+
# Supersedes t2862/GH#20919 (pulse-merge-routine.sh, 120s interval). The new
|
|
432
|
+
# approach runs pulse-wrapper.sh --merge-only every 60s so green PRs merge
|
|
433
|
+
# within <=90s of CI completion regardless of dispatch-cycle length (~23 min
|
|
434
|
+
# average). The full pulse bootstrap ensures all PULSE_* config and repo state
|
|
435
|
+
# are available; the separate lockdir (pulse-merge-instance.lock) prevents
|
|
436
|
+
# overlap with the main dispatch cycle lock.
|
|
437
|
+
#
|
|
438
|
+
# Requires: pulse-wrapper.sh must be installed and executable.
|
|
439
|
+
# The in-cycle merge pass in pulse-wrapper.sh is kept as defense-in-depth —
|
|
440
|
+
# it short-circuits when a merge pass ran within the last 60s.
|
|
404
441
|
setup_pulse_merge_routine() {
|
|
405
|
-
local
|
|
406
|
-
local pmr_label="
|
|
407
|
-
if ! [[ -x "$
|
|
442
|
+
local pmr_wrapper="$HOME/.aidevops/agents/scripts/pulse-wrapper.sh"
|
|
443
|
+
local pmr_label="com.aidevops.aidevops-supervisor-merge"
|
|
444
|
+
if ! [[ -x "$pmr_wrapper" ]]; then
|
|
408
445
|
return 0
|
|
409
446
|
fi
|
|
410
447
|
|
|
@@ -415,9 +452,9 @@ setup_pulse_merge_routine() {
|
|
|
415
452
|
|
|
416
453
|
# Install/update scheduled runner
|
|
417
454
|
if [[ "$(uname -s)" == "Darwin" ]]; then
|
|
418
|
-
_install_pulse_merge_routine_launchd "$pmr_label" "$
|
|
455
|
+
_install_pulse_merge_routine_launchd "$pmr_label" "$pmr_wrapper" "$_pmr_log_dir"
|
|
419
456
|
else
|
|
420
|
-
_install_pulse_merge_routine_linux "$
|
|
457
|
+
_install_pulse_merge_routine_linux "$pmr_wrapper" "$_pmr_log_dir"
|
|
421
458
|
fi
|
|
422
459
|
return 0
|
|
423
460
|
}
|
package/setup.sh
CHANGED
|
@@ -12,7 +12,7 @@ shopt -s inherit_errexit 2>/dev/null || true
|
|
|
12
12
|
# AI Assistant Server Access Framework Setup Script
|
|
13
13
|
# Helps developers set up the framework for their infrastructure
|
|
14
14
|
#
|
|
15
|
-
# Version: 3.13.
|
|
15
|
+
# Version: 3.13.11
|
|
16
16
|
#
|
|
17
17
|
# Quick Install:
|
|
18
18
|
# npm install -g aidevops && aidevops update (recommended)
|