초기 commit — onboard-linux-node.sh + install-eu-windows.ps1 + README

This commit is contained in:
root2 2026-05-29 13:32:14 +00:00
commit 034c734346
3 changed files with 350 additions and 0 deletions

53
README.md Normal file
View File

@ -0,0 +1,53 @@
# washtime/tools
자체 운영 도구 / 셋업 자동화 스크립트 모음.
이 repo 는 **public** — 누구나 raw URL 로 받을 수 있다 (인증 키 등 비밀은 절대 commit 금지).
---
## 새 리눅스 서버 onboarding
새 노드 (오라클 프리티어 / 라즈베리파이 / 다른 PC) 에서:
```bash
bash <(curl -fsSL http://192.168.1.40:3000/washtime/tools/raw/branch/main/onboard-linux-node.sh)
```
(외부망에서는 LAN IP 안 보임 → 플린트3 도착 + DDNS 셋업 후 `git.washtime.xxx` 로 교체.)
스크립트가 인터랙티브로 물어보는 것:
- Gitea host (default: `192.168.1.40`)
- admin API token (`Washtime_GitAdmin` 의 `admin-write` 토큰 — Web UI 의 `Settings → Applications → Generate New Token` 에서 발급)
- 노드 이름 (예: `oracle-jp-1`)
- Clone 할 repo (콤마 구분, 예: `one,family-viewer,tools`)
자동 처리:
- ed25519 SSH key 생성 + Gitea 에 자동 등록
- `~/.ssh/config``gitea` 별칭 추가
- repo clone
- 1분 주기 auto-pull systemd timer 설치
---
## 새 Windows PC 에 External Uploader 설치
관리자 PowerShell 에서:
```powershell
iwr -useb http://192.168.1.40:3000/washtime/tools/raw/branch/main/install-eu-windows.ps1 | iex
```
(외부망에서는 위 URL 의 host 를 외부 도메인으로 교체.)
자동 처리:
- 최신 installer (`ExternalUploader-Setup-latest.exe`, 510MB) 자동 다운로드
- SHA256 + 크기 검증
- installer 실행 (GUI)
- 설치 완료 후 daemon 이 백그라운드에서 자동 업데이트 처리 — 사용자 액션 더 이상 X
---
## 기타 도구
(추후 추가될 운영 자동화 스크립트 — DB 마이그레이션 헬퍼, 백업 검증, 헬스 체크 등)

84
install-eu-windows.ps1 Normal file
View File

@ -0,0 +1,84 @@
# washtime External Uploader — Windows 자동 설치
# 한 줄 실행 (PowerShell 관리자):
# iwr -useb http://192.168.1.40:3000/washtime/tools/raw/branch/main/install-eu-windows.ps1 | iex
#
# 동작:
# 1) 최신 installer .exe 다운로드 (washtime 서버에서)
# 2) 다운로드 검증 (SHA256, 옵션)
# 3) 자동 실행 (silent 또는 GUI)
# 4) 설치 후 auto_updater 가 자동으로 차후 업데이트 처리
#
# 이후 사용자는 더 이상 액션 불필요 — daemon 이 백그라운드에서 publisher-certs API 보고
# 자동으로 새 버전 다운로드 + helper.exe 가 swap 처리.
$ErrorActionPreference = 'Stop'
# ───── 설정 ─────
$installerUrl = 'http://washtime.asuscomm.com:5055/downloads/ExternalUploader-Setup-latest.exe'
$installerLocal = "$env:TEMP\ExternalUploader-Setup.exe"
$installMode = 'gui' # 'gui' = NSIS GUI / 'silent' = /S 무인설치
Write-Host ''
Write-Host '════════════════════════════════════════════════════════' -ForegroundColor Cyan
Write-Host ' washtime External Uploader 자동 설치' -ForegroundColor Cyan
Write-Host '════════════════════════════════════════════════════════' -ForegroundColor Cyan
Write-Host ''
# ───── 1) 다운로드 ─────
Write-Host "[1/3] installer 다운로드…" -ForegroundColor Yellow
Write-Host " URL: $installerUrl"
Write-Host " 저장: $installerLocal"
try {
$ProgressPreference = 'SilentlyContinue' # iwr 진행률 표시 끔 (속도 위해)
Invoke-WebRequest -Uri $installerUrl -OutFile $installerLocal -UseBasicParsing
$size = (Get-Item $installerLocal).Length
Write-Host " ✔ 다운로드 완료 ($([math]::Round($size/1MB, 1)) MB)" -ForegroundColor Green
} catch {
Write-Host " ✗ 다운로드 실패: $_" -ForegroundColor Red
exit 1
}
# ───── 2) 검증 ─────
Write-Host ''
Write-Host '[2/3] 파일 검증…' -ForegroundColor Yellow
$hash = (Get-FileHash $installerLocal -Algorithm SHA256).Hash
Write-Host " SHA256: $hash"
if ($size -lt 100MB) {
Write-Host ' ✗ 파일 크기 비정상 (100MB 미만). 다운로드 손상 의심.' -ForegroundColor Red
exit 1
}
Write-Host ' ✔ OK' -ForegroundColor Green
# ───── 3) 설치 ─────
Write-Host ''
Write-Host '[3/3] installer 실행…' -ForegroundColor Yellow
if ($installMode -eq 'silent') {
Write-Host ' silent 모드 (/S)'
$proc = Start-Process -FilePath $installerLocal -ArgumentList '/S' -Wait -PassThru
if ($proc.ExitCode -eq 0) {
Write-Host ' ✔ 설치 완료' -ForegroundColor Green
} else {
Write-Host " ✗ installer exit code: $($proc.ExitCode)" -ForegroundColor Red
exit 1
}
} else {
Write-Host ' GUI 모드 — installer 창이 열리면 따라가세요'
Start-Process -FilePath $installerLocal -Wait
Write-Host ' ✔ installer 종료됨' -ForegroundColor Green
}
# ───── 정리 ─────
Remove-Item $installerLocal -Force -ErrorAction SilentlyContinue
Write-Host ''
Write-Host '════════════════════════════════════════════════════════' -ForegroundColor Cyan
Write-Host ' ✅ 설치 완료' -ForegroundColor Green
Write-Host '════════════════════════════════════════════════════════' -ForegroundColor Cyan
Write-Host ''
Write-Host '다음 단계:'
Write-Host ' 1) 시작 메뉴에서 "ExternalUploader" 실행'
Write-Host ' 2) 시스템 트레이의 아이콘 우클릭 → 설정'
Write-Host ' 3) Memily 폴더 (~/Memily/<board-name>) 에 미디어 넣으면 자동 업로드'
Write-Host ''
Write-Host '향후 업데이트는 daemon 이 백그라운드에서 자동 처리 (사용자 액션 X).'
Write-Host ''

213
onboard-linux-node.sh Executable file
View File

@ -0,0 +1,213 @@
#!/bin/bash
# washtime — 새 리눅스 노드 onboarding
# 한 줄 실행:
# bash <(curl -fsSL http://192.168.1.40:3000/washtime/tools/raw/branch/main/onboard-linux-node.sh)
#
# 작동:
# 1) ssh ed25519 key 생성 (또는 기존 사용)
# 2) Gitea API 로 그 public key 등록 (admin token 인터랙티브 입력)
# 3) ~/.ssh/config 에 gitea host 별칭 추가
# 4) 지정한 repo clone
# 5) 1분 주기 auto-pull systemd timer 설치
set -euo pipefail
GITEA_HOST_DEFAULT="192.168.1.40"
GITEA_SSH_PORT_DEFAULT="2222"
GITEA_API_PORT_DEFAULT="3000"
GITEA_USER_DEFAULT="root2"
ORG_DEFAULT="washtime"
# ───────── 입력 ─────────
read -rp "Gitea host (default: $GITEA_HOST_DEFAULT): " GITEA_HOST
GITEA_HOST="${GITEA_HOST:-$GITEA_HOST_DEFAULT}"
read -rp "Gitea SSH port (default: $GITEA_SSH_PORT_DEFAULT): " GITEA_SSH_PORT
GITEA_SSH_PORT="${GITEA_SSH_PORT:-$GITEA_SSH_PORT_DEFAULT}"
read -rp "Gitea API port (default: $GITEA_API_PORT_DEFAULT): " GITEA_API_PORT
GITEA_API_PORT="${GITEA_API_PORT:-$GITEA_API_PORT_DEFAULT}"
read -rp "Gitea Web 계정명 (default: $GITEA_USER_DEFAULT): " GITEA_USER
GITEA_USER="${GITEA_USER:-$GITEA_USER_DEFAULT}"
read -rsp "Gitea admin API token (입력 가려짐): " GITEA_TOKEN
echo
read -rp "이 노드 이름 (예: oracle-cloud, laptop-mac. SSH key 라벨에 사용): " NODE_NAME
[[ -z "$NODE_NAME" ]] && { echo "노드 이름 필수"; exit 1; }
read -rp "Org (default: $ORG_DEFAULT): " ORG
ORG="${ORG:-$ORG_DEFAULT}"
read -rp "Clone 할 repo (콤마 구분, 예: one,family-viewer,tools): " REPOS_STR
[[ -z "$REPOS_STR" ]] && { echo "Repo 필수"; exit 1; }
read -rp "Clone 위치 (default: \$HOME): " CLONE_BASE
CLONE_BASE="${CLONE_BASE:-$HOME}"
# ───────── 1) git 설치 ─────────
echo "[1/5] git / curl 설치 확인…"
if ! command -v git &>/dev/null; then
if command -v apt-get &>/dev/null; then
sudo apt-get update -qq && sudo apt-get install -y git curl
elif command -v dnf &>/dev/null; then
sudo dnf install -y git curl
elif command -v yum &>/dev/null; then
sudo yum install -y git curl
else
echo "패키지 매니저 모름. git 직접 설치 필요."; exit 1
fi
fi
# ───────── 2) SSH key 생성 ─────────
KEY_FILE="$HOME/.ssh/id_ed25519_gitea"
echo "[2/5] SSH key 확인…"
mkdir -p "$HOME/.ssh"
chmod 700 "$HOME/.ssh"
if [[ ! -f "$KEY_FILE" ]]; then
ssh-keygen -t ed25519 -N "" -C "$(whoami)@$NODE_NAME→gitea" -f "$KEY_FILE"
echo " ✔ 새 key 생성됨"
else
echo " ✔ 기존 key 재사용"
fi
PUBKEY=$(cat "${KEY_FILE}.pub")
# ───────── 3) Gitea 에 key 등록 ─────────
echo "[3/5] Gitea 에 SSH key 등록…"
RESP=$(curl -fsS -H "Authorization: token $GITEA_TOKEN" \
-X POST -H "Content-Type: application/json" \
-d "{\"title\":\"$NODE_NAME\",\"key\":\"$PUBKEY\"}" \
"http://${GITEA_HOST}:${GITEA_API_PORT}/api/v1/user/keys" 2>&1) || {
# 이미 등록되어 있으면 OK
if echo "$RESP" | grep -q "already exists"; then
echo " ✔ 이미 등록되어있음"
else
echo " ✗ 등록 실패: $RESP"; exit 1
fi
}
echo " ✔ 등록 완료"
# ───────── 4) ~/.ssh/config ─────────
echo "[4/5] ~/.ssh/config 설정…"
if ! grep -q "^Host gitea$" "$HOME/.ssh/config" 2>/dev/null; then
cat >> "$HOME/.ssh/config" << EOF
Host gitea
HostName ${GITEA_HOST}
Port ${GITEA_SSH_PORT}
User git
IdentityFile ${KEY_FILE}
IdentitiesOnly yes
StrictHostKeyChecking accept-new
EOF
chmod 600 "$HOME/.ssh/config"
echo " ✔ gitea 별칭 추가됨"
else
echo " ✔ gitea 별칭 이미 있음"
fi
# SSH 테스트
ssh -o BatchMode=yes -T gitea 2>&1 | head -1 || true
# ───────── 5) repo clone + auto-pull systemd ─────────
echo "[5/5] repo clone + auto-pull systemd…"
IFS=',' read -ra REPOS <<< "$REPOS_STR"
mkdir -p "$CLONE_BASE/scripts"
for REPO in "${REPOS[@]}"; do
REPO="${REPO// /}"
TARGET="${CLONE_BASE}/${REPO}"
if [[ -d "$TARGET/.git" ]]; then
echo "$TARGET 이미 git repo, pull만"
(cd "$TARGET" && git pull --ff-only) || echo "$REPO pull 실패"
else
if [[ -d "$TARGET" ]]; then
BAK="${TARGET}.backup-$(date +%Y%m%d-%H%M%S)"
mv "$TARGET" "$BAK"
echo " ↻ 기존 $TARGET 백업: $BAK"
fi
git clone "gitea:${ORG}/${REPO}.git" "$TARGET" || { echo "$REPO clone 실패"; continue; }
echo "$REPO clone 완료 → $TARGET"
fi
done
# auto-pull 스크립트 (단순 ff-merge, service restart 매핑은 노드별 사용자가 추가)
PULL_SH="${CLONE_BASE}/scripts/git-auto-pull.sh"
cat > "$PULL_SH" << EOF
#!/bin/bash
# washtime auto-pull — ${NODE_NAME}
set -e
for REPO in ${REPOS[@]}; do
REPO="\${REPO// /}"
R="${CLONE_BASE}/\$REPO"
[[ ! -d "\$R/.git" ]] && continue
cd "\$R"
OLD=\$(git rev-parse HEAD)
git fetch --quiet origin main 2>&1
NEW=\$(git rev-parse origin/main)
[[ "\$OLD" == "\$NEW" ]] && continue
echo "[\$(date +%Y-%m-%dT%H:%M:%S)] \$REPO: \$OLD..\$NEW"
if ! git diff --quiet || ! git diff --quiet --cached; then
git stash push -u -m "auto-pull-\$(date +%s)"
fi
git merge --ff-only \$NEW || { echo " ❌ ff 불가"; continue; }
# TODO: 변경 파일별 service restart hook 추가
done
EOF
chmod +x "$PULL_SH"
# systemd service + timer (sudo 필요)
SVC_FILE="/etc/systemd/system/washtime-git-pull.service"
TMR_FILE="/etc/systemd/system/washtime-git-pull.timer"
if [[ ! -f "$SVC_FILE" ]]; then
sudo tee "$SVC_FILE" > /dev/null << EOF
[Unit]
Description=washtime git auto-pull (${NODE_NAME})
After=network.target
[Service]
Type=oneshot
User=$(whoami)
WorkingDirectory=${CLONE_BASE}
ExecStart=${PULL_SH}
StandardOutput=append:/tmp/washtime-git-pull.log
StandardError=append:/tmp/washtime-git-pull.log
EOF
sudo tee "$TMR_FILE" > /dev/null << EOF
[Unit]
Description=washtime git auto-pull every 1 min
[Timer]
OnBootSec=30s
OnUnitActiveSec=60s
AccuracySec=5s
Persistent=true
Unit=washtime-git-pull.service
[Install]
WantedBy=timers.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now washtime-git-pull.timer
echo " ✔ systemd timer 활성 (1분 주기)"
fi
# 즉시 한 번 실행
sudo systemctl start washtime-git-pull.service 2>/dev/null || true
echo
echo "========================================"
echo " ✅ onboarding 완료"
echo "========================================"
echo " 노드: $NODE_NAME"
echo " Clone 위치: $CLONE_BASE"
echo " Repo: ${REPOS_STR}"
echo " Auto-pull: washtime-git-pull.timer (1분 주기)"
echo " Log: /tmp/washtime-git-pull.log"
echo
echo " 강제 pull: sudo systemctl start washtime-git-pull.service"
echo " 로그 보기: tail -f /tmp/washtime-git-pull.log"
echo " 타이머 끄기: sudo systemctl disable --now washtime-git-pull.timer"
echo