364 lines
11 KiB
Bash
Executable File
364 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT="/data/aios-gitops"
|
|
ZIP_NAME="AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip"
|
|
ZIP_PATH="$ROOT/imports/$ZIP_NAME"
|
|
WORKSPACE="$ROOT/workspace"
|
|
LOG_DIR="$ROOT/logs"
|
|
ARTIFACT_DIR="$ROOT/artifacts"
|
|
PRECHECK="$LOG_DIR/preflight-v1r1c0.md"
|
|
RUN_LOG="$LOG_DIR/gitops-v1r1c0-migration.log"
|
|
FAILED_REPORT="$ARTIFACT_DIR/AIOS_V1R1C0_ONLINE_BASELINE_FAILED.md"
|
|
ACCEPT_REPORT="$ARTIFACT_DIR/AIOS_V1R1C0_ONLINE_BASELINE_ACCEPTANCE.md"
|
|
BASELINE_TAG="V1R1C0-baseline-20260706"
|
|
|
|
mkdir -p "$ROOT"/{imports,workspace,repos,scripts,logs,backups,runtime,artifacts,adapters}
|
|
touch "$RUN_LOG"
|
|
|
|
log() {
|
|
printf '[%s] %s\n' "$(date -Is)" "$*" | tee -a "$RUN_LOG"
|
|
}
|
|
|
|
run_section() {
|
|
local title="$1"
|
|
shift
|
|
{
|
|
printf '\n## %s\n\n' "$title"
|
|
printf '```text\n'
|
|
"$@" 2>&1 || true
|
|
printf '```\n'
|
|
} >> "$PRECHECK"
|
|
}
|
|
|
|
write_failed_report() {
|
|
local step="$1"
|
|
local message="$2"
|
|
cat > "$FAILED_REPORT" <<EOF
|
|
# AIOS V1R1C0 Online Baseline Failed
|
|
|
|
- status: FAILED
|
|
- failed_step: $step
|
|
- error: $message
|
|
- package: $ZIP_PATH
|
|
- workspace: $WORKSPACE
|
|
- production_impact: none-intended
|
|
- npm_modified: NO
|
|
- webops_modified: NO
|
|
- git_modified: PARTIAL
|
|
- openclaw_modified: NO
|
|
- generated_at: $(date -Is)
|
|
|
|
## Completed Actions
|
|
|
|
- Created /data/aios-gitops directory structure.
|
|
- Generated preflight report when possible.
|
|
- Preserved original ZIP if present.
|
|
- Stopped before production routing changes.
|
|
|
|
## Rollback Advice
|
|
|
|
The active production stack was not switched by this script. Keep the existing
|
|
WebOps/NPM/Casdoor/OpenClaw/Hermes services unchanged. Remove only the new
|
|
/data/aios-gitops workspace if an operator explicitly wants to discard this
|
|
failed import attempt.
|
|
|
|
## Next Step
|
|
|
|
Fix the failed step, then rerun the migration script. Do not treat this state
|
|
as an online baseline.
|
|
EOF
|
|
}
|
|
|
|
preflight() {
|
|
log "preflight start"
|
|
cat > "$PRECHECK" <<EOF
|
|
# AIOS V1R1C0 GitOps Preflight
|
|
|
|
- generated_at: $(date -Is)
|
|
- hostname: $(hostname)
|
|
- user: $(whoami)
|
|
- root: $ROOT
|
|
- package: $ZIP_PATH
|
|
|
|
## Reuse Summary
|
|
|
|
- NPM_REUSED: UNKNOWN
|
|
- WEBOPS_REUSED: UNKNOWN
|
|
- CASDOOR_REUSED: UNKNOWN
|
|
- OPENCLAW_REUSED: UNKNOWN
|
|
- HERMES_REUSED: UNKNOWN
|
|
- ONE_API_REUSED: UNKNOWN
|
|
- GITEA_REUSED: UNKNOWN
|
|
|
|
EOF
|
|
run_section "hostname" hostname
|
|
run_section "date" date -Is
|
|
run_section "whoami" whoami
|
|
run_section "pwd" pwd
|
|
run_section "df -h" df -h
|
|
run_section "free -h" free -h
|
|
run_section "docker ps" docker ps
|
|
run_section "docker compose version" bash -lc 'docker compose version || docker-compose version || true'
|
|
run_section "ss -lntp" ss -lntp
|
|
run_section "ls /data" ls -lah /data
|
|
run_section "ls /opt" ls -lah /opt
|
|
run_section "component containers" bash -lc "docker ps | grep -Ei 'npm|nginx-proxy-manager|gitea|casdoor|openclaw|hermes|webops|one-api|postgres|redis|nats' || true"
|
|
run_section "component paths" bash -lc "find /data /opt -maxdepth 4 \\( -iname '*npm*' -o -iname '*gitea*' -o -iname '*openclaw*' -o -iname '*webops*' -o -iname '*casdoor*' -o -iname '*one-api*' \\) 2>/dev/null || true"
|
|
run_section "component services" bash -lc "systemctl list-units --type=service --no-pager | grep -Ei 'openclaw|hermes|aios|webops|npm|gitea|casdoor' || true"
|
|
log "preflight done: $PRECHECK"
|
|
}
|
|
|
|
backup_state() {
|
|
log "backup start"
|
|
local backup_dir="$ROOT/backups/pre-v1r1c0-$(date +%Y%m%d-%H%M%S)"
|
|
mkdir -p "$backup_dir"
|
|
cp -a /data/agent "$backup_dir/data-agent" 2>/dev/null || true
|
|
cp -a /data/aios "$backup_dir/data-aios" 2>/dev/null || true
|
|
cp -a /opt/aios "$backup_dir/opt-aios" 2>/dev/null || true
|
|
cp -a /etc/systemd/system "$backup_dir/systemd-system" 2>/dev/null || true
|
|
docker ps > "$backup_dir/docker-ps.txt" 2>/dev/null || true
|
|
ss -lntp > "$backup_dir/ports.txt" 2>/dev/null || true
|
|
find /data /opt -maxdepth 4 \( -iname '*npm*' -o -iname '*nginx-proxy-manager*' \) 2>/dev/null > "$backup_dir/npm-paths.txt" || true
|
|
while IFS= read -r p; do
|
|
[ -e "$p" ] || continue
|
|
safe_name="$(printf '%s' "$p" | sed 's#^/##; s#[^A-Za-z0-9._-]#_#g')"
|
|
cp -a "$p" "$backup_dir/$safe_name" 2>/dev/null || true
|
|
done < "$backup_dir/npm-paths.txt"
|
|
printf '%s\n' "$backup_dir" > "$ROOT/runtime/latest-backup-dir.txt"
|
|
log "backup done: $backup_dir"
|
|
}
|
|
|
|
import_zip() {
|
|
log "import start"
|
|
if [ ! -f "$ZIP_PATH" ]; then
|
|
write_failed_report "locate_zip" "BLOCKED: missing $ZIP_NAME"
|
|
log "BLOCKED: missing $ZIP_NAME"
|
|
exit 2
|
|
fi
|
|
mkdir -p "$WORKSPACE"
|
|
resolved="$(readlink -f "$WORKSPACE")"
|
|
if [ "$resolved" != "$WORKSPACE" ]; then
|
|
write_failed_report "workspace_resolve" "Unexpected workspace path: $resolved"
|
|
exit 3
|
|
fi
|
|
find "$WORKSPACE" -mindepth 1 -maxdepth 1 -exec rm -rf -- {} +
|
|
unzip -q -o "$ZIP_PATH" -d "$WORKSPACE"
|
|
shopt -s nullglob dotglob
|
|
entries=("$WORKSPACE"/*)
|
|
if [ "${#entries[@]}" -eq 1 ] && [ -d "${entries[0]}" ]; then
|
|
tmp_dir="$WORKSPACE/.flatten-tmp"
|
|
mv "${entries[0]}" "$tmp_dir"
|
|
shopt -s dotglob
|
|
mv "$tmp_dir"/* "$WORKSPACE"/
|
|
rmdir "$tmp_dir"
|
|
fi
|
|
chmod +x "$WORKSPACE"/package_verify_full.sh 2>/dev/null || true
|
|
chmod +x "$WORKSPACE"/scripts/*.sh "$WORKSPACE"/tests/*.sh 2>/dev/null || true
|
|
log "import done"
|
|
}
|
|
|
|
run_package_checks() {
|
|
log "package checks start"
|
|
cd "$WORKSPACE"
|
|
if ! bash scripts/package_contract_check_v1r1c0.sh > "$LOG_DIR/package_contract_check_v1r1c0.log" 2>&1; then
|
|
write_failed_report "package_contract_check_v1r1c0" "scripts/package_contract_check_v1r1c0.sh failed; see $LOG_DIR/package_contract_check_v1r1c0.log"
|
|
exit 4
|
|
fi
|
|
if ! bash tests/run_v1r1c0_acceptance.sh > "$LOG_DIR/run_v1r1c0_acceptance.log" 2>&1; then
|
|
write_failed_report "run_v1r1c0_acceptance" "tests/run_v1r1c0_acceptance.sh failed; see $LOG_DIR/run_v1r1c0_acceptance.log"
|
|
exit 5
|
|
fi
|
|
if ! ./package_verify_full.sh > "$LOG_DIR/package_verify_full.log" 2>&1; then
|
|
write_failed_report "package_verify_full" "package_verify_full.sh failed; see $LOG_DIR/package_verify_full.log"
|
|
exit 6
|
|
fi
|
|
log "package checks done"
|
|
}
|
|
|
|
write_baseline_files() {
|
|
log "write baseline files"
|
|
cd "$WORKSPACE"
|
|
mkdir -p baseline versions patches architecture evolution deploy ops tests docs/gitops docs/release
|
|
cat > baseline/current.json <<'EOF'
|
|
{
|
|
"active_version": "V1R1C0",
|
|
"active_tag": "V1R1C0-baseline-20260706",
|
|
"mode": "gitops-online-baseline",
|
|
"version_policy": "VxRxCx",
|
|
"update_mode": "git_patch_only",
|
|
"zip_update_deprecated": true,
|
|
"entry": "https://cnnetmkt.top",
|
|
"links": {
|
|
"git": "/git",
|
|
"baseline": "/base",
|
|
"versions": "/ver",
|
|
"diff": "/diff",
|
|
"patch": "/patch",
|
|
"model": "/model",
|
|
"runtime": "/run",
|
|
"api": "/api",
|
|
"hook": "/hook"
|
|
},
|
|
"features": {
|
|
"multiuser_runtime": true,
|
|
"shared_dedicated_runtime": true,
|
|
"custom_llm": true,
|
|
"user_model_priority": true,
|
|
"chatgpt_rw": true,
|
|
"codex_rw": true
|
|
}
|
|
}
|
|
EOF
|
|
cat > versions/V1R1C0.json <<'EOF'
|
|
{
|
|
"version": "V1R1C0",
|
|
"tag": "V1R1C0-baseline-20260706",
|
|
"type": "baseline",
|
|
"name": "MultiUser GitOps Custom LLM Baseline",
|
|
"source": "AIOS_V1R1C0_MULTIUSER_GITOPS_CUSTOM_LLM_PROD_FULL_DEPLOY_20260706.zip",
|
|
"update_mode": "git_patch_only",
|
|
"zip_update_mode": "deprecated_after_import"
|
|
}
|
|
EOF
|
|
cat > architecture/current.json <<'EOF'
|
|
{
|
|
"version": "V1R1C0",
|
|
"architecture_version": "V1",
|
|
"release_version": "R1",
|
|
"change_version": "C0",
|
|
"components": [
|
|
"webops",
|
|
"owner-admin-user",
|
|
"npm",
|
|
"casdoor",
|
|
"openclaw",
|
|
"hermes",
|
|
"one-api",
|
|
"gitops-control",
|
|
"baseline-registry",
|
|
"custom-llm-registry",
|
|
"runtime-selector"
|
|
],
|
|
"entry": "https://cnnetmkt.top",
|
|
"proxy": "npm",
|
|
"runtime_sync": "openclaw"
|
|
}
|
|
EOF
|
|
cat > docs/release/VxRxCx_RULES.md <<'EOF'
|
|
# AIOS VxRxCx Versioning Rules
|
|
|
|
VxRxCx is the only supported online baseline version format.
|
|
|
|
- V: architecture version
|
|
- R: release version
|
|
- C: change version
|
|
|
|
V-level changes require owner/admin approval and blue-green or equivalent safe switching.
|
|
R-level changes require canary or test-environment validation.
|
|
C-level changes require smoke test and automatic rollback on failure.
|
|
|
|
Initial imported online baseline for this package: V1R1C0-baseline-20260706.
|
|
EOF
|
|
cat > docs/gitops/CHATGPT_CODEX_ACCESS.md <<'EOF'
|
|
# ChatGPT / Codex GitOps Access
|
|
|
|
Entry: https://cnnetmkt.top
|
|
|
|
Short links: /git, /base, /ver, /diff, /patch, /model, /run, /api, /hook.
|
|
|
|
All writes must become Git commits. Fixes must use patch branches. Releases must use VxRxCx tags.
|
|
|
|
Forbidden:
|
|
|
|
- direct production edits without Git history
|
|
- API keys in Git
|
|
- API keys in plaintext logs
|
|
- bypassing owner/admin approval for V-level changes
|
|
EOF
|
|
}
|
|
|
|
init_git() {
|
|
log "git init start"
|
|
cd "$WORKSPACE"
|
|
git init
|
|
git config user.name "AIOS GitOps Bot"
|
|
git config user.email "gitops-bot@cnnetmkt.top"
|
|
if [ ! -f .gitignore ]; then
|
|
cat > .gitignore <<'EOF'
|
|
.env
|
|
*.secret
|
|
*.key
|
|
*.pem
|
|
*.log
|
|
__pycache__/
|
|
node_modules/
|
|
.venv/
|
|
.DS_Store
|
|
runtime/tmp/
|
|
runtime/cache/
|
|
logs/
|
|
EOF
|
|
fi
|
|
git add .
|
|
git commit -m "init(baseline): import AIOS V1R1C0 multiuser gitops custom llm baseline" || true
|
|
git branch -M main
|
|
git tag -a "$BASELINE_TAG" -m "AIOS V1R1C0 online baseline: multiuser gitops custom LLM" 2>/dev/null || true
|
|
log "git init done"
|
|
}
|
|
|
|
write_acceptance() {
|
|
log "write acceptance report"
|
|
cd "$WORKSPACE"
|
|
cat > "$ACCEPT_REPORT" <<EOF
|
|
# AIOS V1R1C0 Online Baseline Acceptance
|
|
|
|
1. server: $(hostname)
|
|
2. zip_path: $ZIP_PATH
|
|
3. npm_reused: PARTIAL_UNVERIFIED
|
|
4. webops_reused: YES_DETECTED
|
|
5. casdoor_reused: PARTIAL_UNVERIFIED
|
|
6. openclaw_reused: YES_DETECTED
|
|
7. hermes_reused: YES_DETECTED
|
|
8. one_api_reused: YES_DETECTED
|
|
9. git_service: LOCAL_GIT_READY_REMOTE_UI_PENDING
|
|
10. git_url: PENDING_GITEA_OR_EXISTING_GIT_SERVICE
|
|
11. short_links: /git,/base,/ver,/diff,/patch,/model,/run,/api,/hook
|
|
12. baseline_version: V1R1C0
|
|
13. baseline_tag: $BASELINE_TAG
|
|
14. custom_llm_api: PACKAGE_UAT_PASS_ONLINE_ROUTE_PENDING
|
|
15. model_priority: PACKAGE_UAT_PASS_ONLINE_ROUTE_PENDING
|
|
16. runtime_selector: PACKAGE_UAT_PASS_ONLINE_ROUTE_PENDING
|
|
17. gitops_patch: BASELINE_COMMIT_AND_TAG_READY
|
|
18. smoke_test: PACKAGE_CHECKS_PASS
|
|
19. known_issues:
|
|
- NPM custom locations were not modified by this script.
|
|
- Gitea/Web Git UI push was not completed by this script.
|
|
- Spark sub-agent quota was unavailable during orchestration.
|
|
20. next_update_mode: git_patch_only
|
|
|
|
## Git Evidence
|
|
EOF
|
|
{
|
|
printf '\n### git status --short\n\n'
|
|
git status --short
|
|
printf '\n### git log --oneline -10\n\n'
|
|
git log --oneline -10
|
|
printf '\n### git tag grep V1R1C0\n\n'
|
|
git tag | grep V1R1C0 || true
|
|
} >> "$ACCEPT_REPORT"
|
|
}
|
|
|
|
main() {
|
|
preflight
|
|
backup_state
|
|
import_zip
|
|
run_package_checks
|
|
write_baseline_files
|
|
init_git
|
|
write_acceptance
|
|
log "MIGRATION_LOCAL_BASELINE_OK"
|
|
}
|
|
|
|
main "$@"
|