Files
pikasTech-unidesk/scripts/bootstrap/devops-install.sh
T
2026-05-18 06:59:51 +00:00

181 lines
5.0 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
repo_url="https://github.com/pikasTech/unidesk"
commit_id=""
environment="dev"
namespace="unidesk-ci"
work_dir="/home/ubuntu/.unidesk/bootstrap/devops"
image="unidesk-devops:dev"
dry_run="0"
usage() {
cat <<'EOF'
Usage:
devops-install.sh --commit <commit> [--env dev] [--repo-url URL] [--namespace unidesk-ci] [--dry-run]
This script is a one-shot D601 bootstrapper. It installs or repairs the UniDesk
DevOps control service in native k3s, then normal CI/CD should use DevOps APIs.
EOF
}
while [ "$#" -gt 0 ]; do
case "$1" in
--commit|--commit-id)
commit_id="${2:-}"
shift 2
;;
--env|--environment)
environment="${2:-}"
shift 2
;;
--repo-url)
repo_url="${2:-}"
shift 2
;;
--namespace)
namespace="${2:-}"
shift 2
;;
--work-dir)
work_dir="${2:-}"
shift 2
;;
--dry-run)
dry_run="1"
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "unknown argument: $1" >&2
usage >&2
exit 2
;;
esac
done
if ! [[ "$commit_id" =~ ^[0-9a-fA-F]{7,40}$ ]]; then
echo "--commit must be a 7-40 character git SHA" >&2
exit 2
fi
if [ "$environment" != "dev" ]; then
echo "only --env dev is supported by the first bootstrapper" >&2
exit 2
fi
log() {
printf '{"at":"%s","event":"%s"}\n' "$(date -Iseconds)" "$*"
}
need_cmd() {
if ! command -v "$1" >/dev/null 2>&1; then
echo "missing required command: $1" >&2
exit 1
fi
}
root_exec() {
if [ "$(id -u)" = "0" ]; then
"$@"
elif sudo -n true >/dev/null 2>&1; then
sudo -n "$@"
else
echo "root access is required for k3s containerd import" >&2
exit 1
fi
}
need_cmd git
need_cmd docker
need_cmd kubectl
if [ ! -f /etc/rancher/k3s/k3s.yaml ]; then
echo "native k3s kubeconfig not found: /etc/rancher/k3s/k3s.yaml" >&2
exit 1
fi
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
kubectl get nodes >/dev/null
log "bootstrap_preflight_ok"
if [ "$dry_run" = "1" ]; then
exit 0
fi
repo_dir="$work_dir/repo"
mkdir -p "$work_dir"
if [ ! -d "$repo_dir/.git" ]; then
rm -rf "$repo_dir"
git clone --no-checkout "$repo_url" "$repo_dir"
fi
git -C "$repo_dir" remote set-url origin "$repo_url"
git -C "$repo_dir" fetch --no-tags origin "$commit_id" || git -C "$repo_dir" fetch --no-tags origin '+refs/heads/*:refs/remotes/origin/*'
resolved="$(git -C "$repo_dir" rev-parse --verify "$commit_id^{commit}")"
git -C "$repo_dir" checkout --detach "$resolved"
log "source_ready commit=$resolved"
docker buildx build --load \
--progress=plain \
--label "unidesk.ai/service-id=devops" \
--label "unidesk.ai/source-repo=$repo_url" \
--label "unidesk.ai/source-commit=$resolved" \
--label "unidesk.ai/dockerfile=src/components/microservices/devops/Dockerfile" \
-t "$image" \
-f "$repo_dir/src/components/microservices/devops/Dockerfile" \
"$repo_dir"
archive="$work_dir/devops-image.tar"
rm -f "$archive"
docker save "$image" -o "$archive"
root_exec ctr --address /run/k3s/containerd/containerd.sock -n k8s.io images import "$archive"
manifest="$work_dir/devops.k8s.yaml"
cp "$repo_dir/src/components/microservices/k3sctl-adapter/k3s/devops.k8s.yaml" "$manifest"
python3 - "$manifest" "$image" "$repo_url" "$resolved" "$commit_id" <<'PY'
import re
import sys
path, image, repo, commit, requested = sys.argv[1:]
text = open(path, encoding="utf-8").read()
text = re.sub(r"image: unidesk-devops:[^\n]+", f"image: {image}", text)
text = text.replace("value: https://github.com/pikasTech/unidesk", f"value: {repo}")
text = text.replace("unidesk.ai/deploy-commit: replace-with-deploy-env-commit", f"unidesk.ai/deploy-commit: {commit}")
text = text.replace("unidesk.ai/deploy-requested-commit: replace-with-deploy-env-commit", f"unidesk.ai/deploy-requested-commit: {requested}")
text = text.replace("value: replace-with-deploy-env-commit", f"value: {commit}")
open(path, "w", encoding="utf-8").write(text)
PY
kubectl create namespace "$namespace" --dry-run=client -o yaml | kubectl apply -f -
kubectl apply -f "$manifest"
kubectl -n "$namespace" rollout status deployment/devops --timeout=180s
kubectl -n "$namespace" get --raw "/api/v1/namespaces/$namespace/services/http:devops:4286/proxy/health" >/tmp/unidesk-devops-health.json
receipt="$work_dir/receipt.json"
python3 - "$receipt" "$repo_url" "$resolved" "$commit_id" "$namespace" "$image" </tmp/unidesk-devops-health.json <<'PY'
import json
import sys
from datetime import datetime, timezone
path, repo, commit, requested, namespace, image = sys.argv[1:]
health = json.load(sys.stdin)
receipt = {
"installedAt": datetime.now(timezone.utc).isoformat(),
"repo": repo,
"commit": commit,
"requestedCommit": requested,
"namespace": namespace,
"image": image,
"health": health,
}
with open(path, "w", encoding="utf-8") as handle:
json.dump(receipt, handle, ensure_ascii=False, indent=2)
handle.write("\n")
print(json.dumps({"ok": True, "receipt": path, "health": health}, ensure_ascii=False))
PY