초기 commit — onboard-linux-node.sh + install-eu-windows.ps1 + README
This commit is contained in:
commit
034c734346
53
README.md
Normal file
53
README.md
Normal 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
84
install-eu-windows.ps1
Normal 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
213
onboard-linux-node.sh
Executable 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
|
||||||
Loading…
Reference in New Issue
Block a user