# sdtab
[English](README.md)
systemd のユーザータイマーとサービスを crontab のように管理するCLIツール。
```bash
# タイマーを追加(cron構文)
sdtab add "0 9 * * *" "uv run ./report.py"
# 常駐サービスを追加
sdtab add "@service" "node server.js" --restart on-failure
# 一覧表示
sdtab list
```
```
NAME TYPE SCHEDULE COMMAND STATUS
report timer 0 9 * * * uv run ./report.py Tue 2026-03-03 09:00:00 JST
web service @service node server.js ● active
Web Server
```
## 特徴
- **cron構文** — おなじみの `* * * * *` 形式で、systemd の OnCalendar に自動変換
- **常駐サービス** — `@service` でリスタートポリシー付きのデーモンを作成
- **リソース制限** — メモリ・CPU・I/O の上限をユニットごとに設定
- **エクスポート/インポート** — `sdtab export` で TOML に書き出し、`sdtab apply` で別マシンに復元
- **失敗通知** — ユニット失敗時に Slack webhook で通知(`OnFailure=` メカニズム)
- **色付きステータス** — `● active`(緑)、`● failed`(赤)、`○ inactive`(黄)で一目瞭然。description はグレーのサブラインで表示。パイプ時は自動で色なし
- **systemd 以外の依存なし** — データベースもデーモンも不要、ユニットファイルだけ
## インストール
```bash
cargo install --git https://github.com/kok1eee/systemdtab
```
ソースからビルドする場合:
```bash
git clone https://github.com/kok1eee/systemdtab
cd systemdtab
cargo build --release
cp target/release/sdtab ~/.local/bin/
```
### 必要環境
- systemd が動作する Linux(ユーザーセッション限定 — システムレベルのユニットは非対応)
- Rust 1.70+
> **注意**: sdtab は **ユーザーレベル** のユニットのみを管理します(`systemctl --user`)。root 権限が必要なシステムサービスの作成・管理はできません。`loginctl enable-linger` が失敗する場合は、システム管理者にリンガーの有効化を依頼してください。
## クイックスタート
```bash
# 初期化(linger有効化、設定ディレクトリ作成)
sdtab init
# オプション: Slack 失敗通知を設定
sdtab init --slack-webhook "https://hooks.slack.com/services/T.../B.../xxx"
# 毎日9時に実行するタスクを追加
sdtab add "0 9 * * *" "./backup.sh" --name backup --memory-max 512M
# 常駐サービスを追加
sdtab add "@service" "node dist/index.js" --name web --restart on-failure --env-file .env
# 状態を確認
sdtab list
sdtab status backup
# ログを確認
sdtab logs web -f
# 設定をファイルにエクスポート
sdtab export -o Sdtabfile.toml
# ファイルから設定を適用(別マシンで)
sdtab apply Sdtabfile.toml --dry-run
sdtab apply Sdtabfile.toml
```
## コマンド一覧
| `sdtab init [--slack-webhook URL] [--slack-mention USER_ID]` | linger 有効化 + ディレクトリ作成 + Slack通知設定 |
| `sdtab add "<schedule>" "<command>" [--dry-run]` | タイマーを追加 |
| `sdtab add "@service" "<command>" [--dry-run]` | 常駐サービスを追加 |
| `sdtab list [--json]` | 管理中のタイマー・サービス一覧(長いコマンドは省略表示) |
| `sdtab status <name>` | 詳細ステータス表示(次回5回分の実行時刻付き) |
| `sdtab edit <name>` | $EDITOR でユニットファイルを編集 |
| `sdtab logs <name> [-f] [-n N]` | ログ表示(journalctl) |
| `sdtab restart <name>` | サービスを再起動 |
| `sdtab enable <name>` | タイマー・サービスを有効化 |
| `sdtab disable <name>` | 一時停止(ファイルは保持) |
| `sdtab remove <name>` | 停止・無効化してユニットファイルを削除 |
| `sdtab export [-o <file>]` | 設定を TOML でエクスポート |
| `sdtab apply <file> [--prune] [--dry-run]` | TOML から一括適用 |
> `sdtab remove` は実行中のユニットを停止・無効化してからファイルを削除します。`sdtab apply --prune` は `sdtab-` プレフィックス付きのユニットのみを削除対象とし、手動で作成した systemd ユニットには影響しません。
> `sdtab apply` は変更されたユニットを**停止せずにファイルを上書き → daemon-reload** で更新します。restart が必要なユニットだけを再起動します: サービスの description のみの変更は restart をスキップ、タイマーのサービス側変更(コマンド、env など)は次回トリガーで反映されるため timer の restart は不要です。
> `sdtab init` は [Claude Code](https://docs.anthropic.com/en/docs/claude-code) のスキルファイルを `~/.claude/commands/sdtab.md` にインストールし、どのプロジェクトからでも `/sdtab` コマンドを使えるようにします。
## スケジュール構文
標準的な cron 式と便利なショートカット:
| `*/5 * * * *` | 5分ごと |
| `0 9 * * *` | 毎日 9:00 |
| `0 9 * * Mon-Fri` | 平日 9:00 |
| `@daily` | 1日1回(深夜0時) |
| `@hourly` | 1時間ごと |
| `@reboot` | システム起動時 |
| `@daily/3` | 毎日 3:00 |
| `@weekly/Mon,Wed` | 毎週月曜と水曜 |
| `@service` | 常駐サービス(タイマーではない) |
## add オプション
| `--name <name>` | ユニット名(省略時はコマンドから自動生成) |
| `--workdir <path>` | 作業ディレクトリ(省略時はカレント) |
| `--description <text>` | 説明文 |
| `--env-file <path>` | 環境変数ファイル |
| `--restart <policy>` | `always` / `on-failure` / `no`(サービスのみ、デフォルト: `always`) |
| `--memory-max <size>` | メモリ上限(例: `512M`, `1G`) |
| `--cpu-quota <percent>` | CPU使用率上限(例: `50%`, `200%`) |
| `--io-weight <N>` | I/O優先度: 1-10000(デフォルト: 100) |
| `--timeout-stop <duration>` | 停止タイムアウト(例: `30s`) |
| `--exec-start-pre <cmd>` | ExecStart 前に実行するコマンド |
| `--exec-stop-post <cmd>` | プロセス停止後に実行するコマンド |
| `--log-level-max <level>` | 保存ログレベル上限(例: `warning`, `err`) |
| `--random-delay <duration>` | タイマー発火のランダム遅延(例: `5m`) |
| `--env <KEY=VALUE>` | 環境変数(複数指定可) |
| `--no-notify` | このユニットの失敗通知を無効化 |
| `--dry-run` | ユニットファイルをプレビュー(作成しない) |
## 失敗通知
Slack webhook を設定すると、ユニット失敗時に通知を受け取れます:
```bash
sdtab init --slack-webhook "https://hooks.slack.com/services/T.../B.../xxx"
# ユーザーメンション付き
sdtab init --slack-webhook "https://hooks.slack.com/services/T.../B.../xxx" --slack-mention "U0700J8MN3W"
```
テンプレートユニット `sdtab-notify@.service` が作成され、以降追加されるすべてのユニットに `OnFailure=sdtab-notify@%n.service` が設定されます。タイマーやサービスが失敗すると、systemd が通知ユニットを起動し、`curl` で Slack にメッセージを送信します。
特定のユニットで通知を無効化するには:
```bash
sdtab add "0 9 * * *" "./quiet-task.sh" --no-notify
```
`Sdtabfile.toml` では `no_notify = true` を使用:
```toml
[timers.quiet-task]
schedule = "0 9 * * *"
command = "./quiet-task.sh"
workdir = "/home/user"
no_notify = true
```
## エクスポート形式
`sdtab export` は TOML ファイルを出力します:
```toml
[timers.backup]
schedule = "0 3 * * *"
command = "./backup.sh"
workdir = "/home/user/project"
memory_max = "512M"
[services.web]
command = "node dist/index.js"
workdir = "/home/user/app"
description = "Web Server"
restart = "on-failure"
env_file = "/home/user/.env"
```
`sdtab apply Sdtabfile.toml` でファイルからユニットを一括作成できます。`--prune` を付けると sdtab 管理下のユニットでファイルにないものを削除します。
## 仕組み
sdtab は `~/.config/systemd/user/` に `sdtab-` プレフィックス付きの標準的な systemd ユニットファイルを生成します。独自のデーモンやデータベースは不要で、すべてが素の systemd です。
```
~/.config/systemd/user/
├── sdtab-backup.service # [Service] 定義
├── sdtab-backup.timer # [Timer] OnCalendar 付き
├── sdtab-web.service # 常駐サービス
├── sdtab-notify@.service # 失敗通知テンプレート(webhook 設定時)
```
メタデータはサービスファイル内のコメント(`# sdtab:type=`, `# sdtab:cron=` など)として保存されるため、外部データベースなしで元の設定を復元できます。
## 他ツールとの比較
| コマンド1つでタイマー作成 | Yes | Yes | No(crontab ファイル経由) | Yes | Yes |
| 常駐サービス対応 | Yes(`@service`) | No | No(oneshot のみ) | No | No |
| リソース制限(メモリ/CPU) | `--memory-max`, `--cpu-quota` | No | 手動(ユニットファイル編集) | No | No |
| 設定エクスポート/インポート | Yes(TOML) | `crontab -l`(テキスト) | No | No | No |
| 機械可読出力 | `--json` | No | No | No | No |
| バックエンド | systemd ネイティブ | crond | systemd(自動生成) | 独自デーモン | 独自デーモン |
| root 不要のユーザー実行 | Yes | Yes | システムレベル | root 必要 | root 必要 |
## テスト
cron パーサー、ユニットファイル生成、TOML シリアライズは 93 個のユニットテストでカバーされています:
```bash
cargo test
```
## AI エージェント対応
`sdtab init` は [Claude Code](https://docs.anthropic.com/en/docs/claude-code) のスキルファイルを `~/.claude/commands/sdtab.md` にインストールします。以降、どのプロジェクトからでも自然言語で systemd タイマーを管理できます:
```
You> /sdtab 毎朝9時にreport.pyを実行して
```
Claude Code が意図を解釈し、`sdtab add "0 9 * * *" "uv run ./report.py" --dry-run` で確認を求めた後、タイマーを作成します。
スキルは**グローバルに動作**します。sdtab リポジトリの中だけでなく、インストール後はどのプロジェクトの Claude Code セッションからでも `/sdtab` でタイマーやサービスの作成・管理が可能です。
その他:
- **`CLAUDE.md`** — AI エージェントが自動で読み込むプロジェクト指示書
- **`--dry-run`** — 実行前にユニットファイルをプレビュー
- **`--json`** — プログラムから扱いやすい機械可読出力
```bash
sdtab list --json
```
```json
[
{"name":"backup","type":"timer","schedule":"0 3 * * *","command":"./backup.sh","status":"Mon 2026-03-02 03:00:00 JST"},
{"name":"web","type":"service","schedule":"@service","command":"node index.js","status":"active"}
]
```
## ライセンス
MIT