first commit

This commit is contained in:
jsahn
2026-09-02 14:57:29 +09:00
commit c0b140d27f
60 changed files with 7520 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
FROM python:3.12-slim
ENV PYTHONUNBUFFERED=1 PIP_NO_CACHE_DIR=1
WORKDIR /app
COPY pyproject.toml README.md ./
COPY sebrief ./sebrief
RUN pip install --no-cache-dir . \
&& mkdir -p /app/data /app/reports /app/logs
VOLUME ["/app/data", "/app/reports", "/app/logs"]
CMD ["python", "-m", "sebrief.cli", "daemon"]
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!-- 로그인 시 Apple container 런타임과 sebrief 컨테이너를 기동하는 one-shot 에이전트.
__CONTAINER__/__ROOT__ 는 install-apple-container.sh --boot 가 치환한다. -->
<plist version="1.0">
<dict>
<key>Label</key><string>com.sebrief.container</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>__CONTAINER__ system start &amp;&amp; __CONTAINER__ start sebrief; __CONTAINER__ start sebrief-dashboard</string>
</array>
<key>RunAtLoad</key><true/>
<key>StandardOutPath</key><string>__ROOT__/logs/container-boot.log</string>
<key>StandardErrorPath</key><string>__ROOT__/logs/container-boot.log</string>
</dict>
</plist>
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.sebrief.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/Users/jsahn/Personal_works/sebrief/.venv/bin/python</string>
<string>-m</string>
<string>sebrief.cli</string>
<string>daemon</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/jsahn/Personal_works/sebrief</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
<key>PYTHONUNBUFFERED</key>
<string>1</string>
</dict>
<!-- 프로세스가 죽으면 즉시 재시작, 로그인 시 자동 기동 -->
<key>KeepAlive</key>
<true/>
<key>RunAtLoad</key>
<true/>
<key>ThrottleInterval</key>
<integer>30</integer>
<key>StandardOutPath</key>
<string>/Users/jsahn/Personal_works/sebrief/logs/launchd.out.log</string>
<key>StandardErrorPath</key>
<string>/Users/jsahn/Personal_works/sebrief/logs/launchd.err.log</string>
</dict>
</plist>
+28
View File
@@ -0,0 +1,28 @@
# docker compose -f deploy/compose.yaml up -d --build
services:
sebrief:
build:
context: ..
dockerfile: deploy/Dockerfile
container_name: sebrief
env_file:
- ../.env
environment:
SEBRIEF_DATA_DIR: /app/data
SEBRIEF_REPORTS_DIR: /app/reports
SEBRIEF_LOG_DIR: /app/logs
volumes:
- ../data:/app/data
- ../reports:/app/reports
- ../logs:/app/logs
restart: unless-stopped
dashboard:
build:
context: ../dashboard
container_name: sebrief-dashboard
volumes:
- ../reports:/data/reports:ro
ports:
- "8091:8091"
restart: unless-stopped
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# macOS: Apple container(https://github.com/apple/container)로 빌드·상주 실행.
# 실행: bash deploy/install-apple-container.sh # 이미지 빌드 + 컨테이너 (재)기동
# bash deploy/install-apple-container.sh --boot # + 로그인 시 자동 기동(LaunchAgent) 등록
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
NAME=sebrief
CONTAINER_BIN="$(command -v container || true)"
if [ -z "$CONTAINER_BIN" ]; then
echo "Apple container CLI가 없습니다: brew install container (macOS 15+, Apple Silicon)" >&2
exit 1
fi
if [ ! -f "$ROOT/.env" ]; then
echo "$ROOT/.env 가 없습니다: cp .env.example .env 후 MINIMAX_API_KEY를 넣으세요" >&2
exit 1
fi
DASHBOARD=sebrief-dashboard
DASHBOARD_PORT="${SEBRIEF_DASHBOARD_PORT:-8091}"
mkdir -p "$ROOT/data" "$ROOT/reports" "$ROOT/logs"
container system start
container build -t "$NAME" -f "$ROOT/deploy/Dockerfile" "$ROOT"
container build -t "$DASHBOARD" -f "$ROOT/dashboard/Dockerfile" "$ROOT/dashboard"
# 기존 컨테이너는 갈아끼운다 (이미지 갱신 반영). container에는 restart 정책이 없어
# 자동 기동은 --boot 의 LaunchAgent가 담당한다.
container stop "$NAME" "$DASHBOARD" >/dev/null 2>&1 || true
container delete "$NAME" "$DASHBOARD" >/dev/null 2>&1 || true
container run --detach --name "$NAME" \
--env-file "$ROOT/.env" \
--env SEBRIEF_DATA_DIR=/app/data \
--env SEBRIEF_REPORTS_DIR=/app/reports \
--env SEBRIEF_LOG_DIR=/app/logs \
--volume "$ROOT/data:/app/data" \
--volume "$ROOT/reports:/app/reports" \
--volume "$ROOT/logs:/app/logs" \
"$NAME"
container run --detach --name "$DASHBOARD" \
--volume "$ROOT/reports:/data/reports:ro" \
--publish "0.0.0.0:$DASHBOARD_PORT:8091" \
"$DASHBOARD"
if [ "${1:-}" = "--boot" ]; then
PLIST_SRC="$ROOT/deploy/com.sebrief.container.plist"
PLIST_DST="$HOME/Library/LaunchAgents/com.sebrief.container.plist"
mkdir -p "$HOME/Library/LaunchAgents"
sed -e "s#__CONTAINER__#$CONTAINER_BIN#g" -e "s#__ROOT__#$ROOT#g" "$PLIST_SRC" > "$PLIST_DST"
launchctl bootout "gui/$(id -u)/com.sebrief.container" 2>/dev/null || true
launchctl bootstrap "gui/$(id -u)" "$PLIST_DST"
echo "boot agent: $PLIST_DST (로그인 시 container system start + container start $NAME $DASHBOARD)"
fi
echo "dashboard: http://localhost:$DASHBOARD_PORT"
echo "status: container list --all"
echo "logs: container logs --follow $NAME (파일: tail -f $ROOT/logs/sebrief.log)"
echo "stop: container stop $NAME $DASHBOARD"
echo "remove: container stop $NAME $DASHBOARD && container delete $NAME $DASHBOARD"
+16
View File
@@ -0,0 +1,16 @@
#!/usr/bin/env bash
# macOS: launchd 사용자 에이전트로 등록/갱신. 실행: bash deploy/install-launchd.sh
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
PLIST_SRC="$ROOT/deploy/com.sebrief.daemon.plist"
PLIST_DST="$HOME/Library/LaunchAgents/com.sebrief.daemon.plist"
mkdir -p "$ROOT/logs" "$HOME/Library/LaunchAgents"
# 프로젝트 경로가 다르면 plist 안의 경로를 현재 위치로 치환
sed "s#/Users/jsahn/Personal_works/sebrief#$ROOT#g" "$PLIST_SRC" > "$PLIST_DST"
launchctl bootout "gui/$(id -u)/com.sebrief.daemon" 2>/dev/null || true
launchctl bootstrap "gui/$(id -u)" "$PLIST_DST"
launchctl kickstart -k "gui/$(id -u)/com.sebrief.daemon"
echo "installed: $PLIST_DST"
echo "status: launchctl print gui/$(id -u)/com.sebrief.daemon | head -20"
echo "logs: tail -f $ROOT/logs/sebrief.log"
echo "remove: launchctl bootout gui/$(id -u)/com.sebrief.daemon && rm $PLIST_DST"
+26
View File
@@ -0,0 +1,26 @@
# Linux systemd unit. 설치:
# sudo cp deploy/sebrief.service /etc/systemd/system/sebrief.service
# sudo systemctl daemon-reload && sudo systemctl enable --now sebrief
# journalctl -u sebrief -f
[Unit]
Description=SE-Brief - weekly OA software engineering papers digest daemon
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=sebrief
WorkingDirectory=/opt/sebrief
EnvironmentFile=/opt/sebrief/.env
Environment=PYTHONUNBUFFERED=1
ExecStart=/opt/sebrief/.venv/bin/python -m sebrief.cli daemon
Restart=always
RestartSec=30
# 하드닝
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/sebrief/data /opt/sebrief/reports /opt/sebrief/logs
[Install]
WantedBy=multi-user.target