Skip to content

Commit e90bbd3

Browse files
committed
Document new features across READMEs and Sphinx API docs
1 parent 7304b1b commit e90bbd3

File tree

9 files changed

+442
-3
lines changed

9 files changed

+442
-3
lines changed

README.md

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,21 @@ facade.
2727
- **Entry-point plugins** — third-party packages register their own `FA_*` actions via `[project.entry-points."automation_file.actions"]`; `build_default_registry()` picks them up automatically
2828
- **Incremental directory sync** — rsync-style mirror with size+mtime or checksum change detection, optional delete of extras, dry-run (`FA_sync_dir`)
2929
- **Directory manifests** — JSON snapshot of every file's checksum under a root, with separate missing/modified/extra reporting on verify (`FA_write_manifest`, `FA_verify_manifest`)
30-
- **Notification sinks** — webhook / Slack / SMTP with a fanout manager that does per-sink error isolation and sliding-window dedup; auto-notify on trigger + scheduler failures (`FA_notify_send`, `FA_notify_list`)
30+
- **Notification sinks** — webhook / Slack / SMTP / Telegram / Discord / Teams / PagerDuty with a fanout manager that does per-sink error isolation and sliding-window dedup; auto-notify on trigger + scheduler failures (`FA_notify_send`, `FA_notify_list`)
3131
- **Config file + secret providers** — declare notification sinks / defaults in `automation_file.toml`; `${env:…}` and `${file:…}` references resolve through an Env/File/Chained provider abstraction so secrets stay out of the file itself
32+
- **Config hot reload**`ConfigWatcher` polls `automation_file.toml` and re-applies sinks / defaults on change without restart
33+
- **Shell / grep / JSON edit / tar / backup rotation**`FA_run_shell` (argument-list subprocess with timeout), `FA_grep` (streaming text search), `FA_json_get` / `FA_json_set` / `FA_json_delete` (in-place JSON editing), `FA_create_tar` / `FA_extract_tar`, `FA_rotate_backups`
34+
- **FTP / FTPS backend** — plain FTP or explicit FTPS via `FTP_TLS.auth()`; auto-registered as `FA_ftp_*`
35+
- **Cross-backend copy**`FA_copy_between` moves data between any two backends via `local://`, `s3://`, `drive://`, `azure://`, `dropbox://`, `sftp://`, `ftp://` URIs
36+
- **Scheduler overlap guard** — running jobs are skipped on the next fire unless `allow_overlap=True`
37+
- **Server action ACL**`allowed_actions=(...)` restricts which commands TCP / HTTP servers will dispatch
38+
- **Variable substitution** — opt-in `${env:VAR}` / `${date:%Y-%m-%d}` / `${uuid}` / `${cwd}` expansion in action arguments via `execute_action(..., substitute=True)`
39+
- **Conditional execution**`FA_if_exists` / `FA_if_newer` / `FA_if_size_gt` run a nested action list only when a guard passes
40+
- **SQLite audit log**`AuditLog(db_path)` records every action execution with actor / status / duration; query via `recent` / `count` / `purge`
41+
- **File integrity monitor**`IntegrityMonitor` polls a tree against a manifest and fires a callback + notification on drift
42+
- **HTTPActionClient SDK** — typed Python client for the HTTP action server with shared-secret auth, loopback guard, and OPTIONS-based ping
43+
- **AES-256-GCM file encryption**`encrypt_file` / `decrypt_file` with `generate_key()` / `key_from_password()` (PBKDF2-HMAC-SHA256); JSON actions `FA_encrypt_file` / `FA_decrypt_file`
44+
- **Prometheus metrics exporter**`start_metrics_server()` exposes `automation_file_actions_total{action,status}` counters and `automation_file_action_duration_seconds{action}` histograms
3245
- PySide6 GUI (`python -m automation_file ui`) with a tab per backend, the JSON-action runner, and dedicated tabs for Triggers, Scheduler, and live Progress
3346
- Rich CLI with one-shot subcommands plus legacy JSON-batch flags
3447
- Project scaffolding (`ProjectBuilder`) for executor-based automations
@@ -499,6 +512,118 @@ silently becoming empty strings. Custom provider chains can be built from
499512
`ChainedSecretProvider` / `EnvSecretProvider` / `FileSecretProvider` and
500513
passed as `AutomationConfig.load(path, provider=…)`.
501514

515+
### Variable substitution in action lists
516+
Opt in with `substitute=True` and `${…}` references expand at dispatch time:
517+
518+
```python
519+
from automation_file import execute_action
520+
521+
execute_action(
522+
[["FA_create_file", {"file_path": "reports/${date:%Y-%m-%d}/${uuid}.txt"}]],
523+
substitute=True,
524+
)
525+
```
526+
527+
Supports `${env:VAR}`, `${date:FMT}` (strftime), `${uuid}`, `${cwd}`. Unknown
528+
names raise `SubstitutionException` — no silent empty strings.
529+
530+
### Conditional execution
531+
Run a nested action list only when a path-based guard passes:
532+
533+
```json
534+
[
535+
["FA_if_exists", {"path": "/data/in/job.json",
536+
"then": [["FA_copy_file", {"source": "/data/in/job.json",
537+
"target": "/data/processed/job.json"}]]}],
538+
["FA_if_newer", {"source": "/src", "target": "/dst",
539+
"then": [["FA_sync_dir", {"src": "/src", "dst": "/dst"}]]}],
540+
["FA_if_size_gt", {"path": "/logs/app.log", "size": 10485760,
541+
"then": [["FA_run_shell", {"command": ["logrotate", "/logs/app.log"]}]]}]
542+
]
543+
```
544+
545+
### SQLite audit log
546+
`AuditLog` writes one row per action with short-lived connections and a
547+
module-level lock:
548+
549+
```python
550+
from automation_file import AuditLog
551+
552+
audit = AuditLog("audit.sqlite3")
553+
audit.record(action="FA_copy_file", actor="ops",
554+
status="ok", duration_ms=12, detail={"src": "a", "dst": "b"})
555+
556+
for row in audit.recent(limit=50):
557+
print(row["timestamp"], row["action"], row["status"])
558+
```
559+
560+
### File integrity monitor
561+
Poll a tree against a manifest and fire a callback + notification on drift:
562+
563+
```python
564+
from automation_file import IntegrityMonitor, notification_manager, write_manifest
565+
566+
write_manifest("/srv/site", "/srv/MANIFEST.json")
567+
568+
mon = IntegrityMonitor(
569+
root="/srv/site",
570+
manifest_path="/srv/MANIFEST.json",
571+
interval=60.0,
572+
manager=notification_manager,
573+
on_drift=lambda summary: print("drift:", summary),
574+
)
575+
mon.start()
576+
```
577+
578+
Manifest-load errors are surfaced as drift so tamper and config issues
579+
aren't silently different code paths.
580+
581+
### AES-256-GCM file encryption
582+
Authenticated encryption with a self-describing envelope. Derive a key from
583+
a password or generate one directly:
584+
585+
```python
586+
from automation_file import encrypt_file, decrypt_file, key_from_password
587+
588+
key = key_from_password("correct horse battery staple", salt=b"app-salt-v1")
589+
encrypt_file("secret.pdf", "secret.pdf.enc", key, associated_data=b"v1")
590+
decrypt_file("secret.pdf.enc", "secret.pdf", key, associated_data=b"v1")
591+
```
592+
593+
Tamper is detected via GCM's authentication tag and reported as
594+
`CryptoException("authentication failed")`. JSON actions:
595+
`FA_encrypt_file`, `FA_decrypt_file`.
596+
597+
### HTTPActionClient Python SDK
598+
Typed client for the HTTP action server; enforces loopback by default and
599+
carries the shared secret for you:
600+
601+
```python
602+
from automation_file import HTTPActionClient
603+
604+
with HTTPActionClient("http://127.0.0.1:9944", shared_secret="s3cr3t") as client:
605+
client.ping() # OPTIONS /actions
606+
result = client.execute([["FA_create_dir", {"dir_path": "x"}]])
607+
```
608+
609+
Auth failures map to `HTTPActionClientException` with `kind="unauthorized"`;
610+
404 responses report the server exists but does not expose `/actions`.
611+
612+
### Prometheus metrics exporter
613+
`ActionExecutor` records one counter row and one histogram sample per
614+
action. Serve them on a loopback `/metrics` endpoint:
615+
616+
```python
617+
from automation_file import start_metrics_server
618+
619+
server = start_metrics_server(host="127.0.0.1", port=9945)
620+
# curl http://127.0.0.1:9945/metrics
621+
```
622+
623+
Exports `automation_file_actions_total{action,status}` and
624+
`automation_file_action_duration_seconds{action}`. Non-loopback binds
625+
require `allow_non_loopback=True` explicitly.
626+
502627
### DAG action executor
503628
Run actions in dependency order; independent branches fan out across a
504629
thread pool. Each node is `{"id": ..., "action": [...], "depends_on":

README.zh-CN.md

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,21 @@ TCP / HTTP 服务器执行的 JSON 驱动动作。内附 PySide6 GUI,每个功
2525
- **Entry-point 插件** — 第三方包通过 `[project.entry-points."automation_file.actions"]` 注册自定义 `FA_*` 动作;`build_default_registry()` 会自动加载
2626
- **增量目录同步** — rsync 风格镜像,支持 size+mtime 或 checksum 变更检测,可选删除多余文件,支持干跑(`FA_sync_dir`
2727
- **目录 manifest** — 以 JSON 快照记录树下每个文件的校验和,验证时分开报告 missing / modified / extra(`FA_write_manifest``FA_verify_manifest`
28-
- **通知 sink** — webhook / Slack / SMTP,fanout 管理器做单 sink 错误隔离与滑动窗口去重;trigger + scheduler 失败时自动通知(`FA_notify_send``FA_notify_list`
28+
- **通知 sink** — webhook / Slack / SMTP / Telegram / Discord / Teams / PagerDuty,fanout 管理器做单 sink 错误隔离与滑动窗口去重;trigger + scheduler 失败时自动通知(`FA_notify_send``FA_notify_list`
2929
- **配置文件 + 密钥提供者** — 在 `automation_file.toml` 声明通知 sink / 默认值;`${env:…}``${file:…}` 引用通过 Env / File / Chained 提供者抽象解析,让密钥不留在配置文件中
30+
- **配置热加载**`ConfigWatcher` 轮询 `automation_file.toml`,变更时即时应用 sink / 默认值,无需重启
31+
- **Shell / grep / JSON 编辑 / tar / 备份轮转**`FA_run_shell`(参数列表式 subprocess,含超时)、`FA_grep`(流式文本搜索)、`FA_json_get` / `FA_json_set` / `FA_json_delete`(原地 JSON 编辑)、`FA_create_tar` / `FA_extract_tar``FA_rotate_backups`
32+
- **FTP / FTPS 后端** — 纯 FTP 或通过 `FTP_TLS.auth()` 的显式 FTPS;自动注册为 `FA_ftp_*`
33+
- **跨后端复制**`FA_copy_between` 通过 `local://``s3://``drive://``azure://``dropbox://``sftp://``ftp://` URI 在任意两个后端之间搬运数据
34+
- **调度器重叠防护** — 正在执行的作业在下次触发时会被跳过,除非显式传入 `allow_overlap=True`
35+
- **服务器动作 ACL**`allowed_actions=(...)` 限制 TCP / HTTP 服务器可派发的命令
36+
- **变量替换** — 动作参数中可选使用 `${env:VAR}` / `${date:%Y-%m-%d}` / `${uuid}` / `${cwd}`,通过 `execute_action(..., substitute=True)` 展开
37+
- **条件执行**`FA_if_exists` / `FA_if_newer` / `FA_if_size_gt` 仅在路径守卫通过时执行嵌套动作清单
38+
- **SQLite 审计日志**`AuditLog(db_path)` 为每个动作记录 actor / status / duration;通过 `recent` / `count` / `purge` 查询
39+
- **文件完整性监控**`IntegrityMonitor` 按 manifest 轮询整棵树,检测到 drift 时触发 callback + 通知
40+
- **HTTPActionClient SDK** — HTTP 动作服务器的类型化 Python 客户端,具 shared-secret 认证、loopback 守护与 OPTIONS ping
41+
- **AES-256-GCM 文件加密**`encrypt_file` / `decrypt_file` 搭配 `generate_key()` / `key_from_password()`(PBKDF2-HMAC-SHA256);JSON 动作 `FA_encrypt_file` / `FA_decrypt_file`
42+
- **Prometheus metrics 导出器**`start_metrics_server()` 提供 `automation_file_actions_total{action,status}` 计数器与 `automation_file_action_duration_seconds{action}` 直方图
3043
- PySide6 GUI(`python -m automation_file ui`)每个后端一个页签,含 JSON 动作执行器,另有 Triggers、Scheduler、实时 Progress 专属页签
3144
- 功能丰富的 CLI,包含一次性子命令与旧式 JSON 批量标志
3245
- 项目脚手架(`ProjectBuilder`)协助构建以 executor 为核心的自动化项目
@@ -489,6 +502,115 @@ config.apply_to(notification_manager)
489502
`FileSecretProvider` 构建自定义提供者链,并以
490503
`AutomationConfig.load(path, provider=…)` 传入。
491504

505+
### 动作清单变量替换
506+
`substitute=True` 启用后,`${…}` 引用会在派发时展开:
507+
508+
```python
509+
from automation_file import execute_action
510+
511+
execute_action(
512+
[["FA_create_file", {"file_path": "reports/${date:%Y-%m-%d}/${uuid}.txt"}]],
513+
substitute=True,
514+
)
515+
```
516+
517+
支持 `${env:VAR}``${date:FMT}`(strftime)、`${uuid}``${cwd}`。未知名称
518+
会抛出 `SubstitutionException`,不会默默变成空字符串。
519+
520+
### 条件执行
521+
只在路径守卫通过时执行嵌套动作清单:
522+
523+
```json
524+
[
525+
["FA_if_exists", {"path": "/data/in/job.json",
526+
"then": [["FA_copy_file", {"source": "/data/in/job.json",
527+
"target": "/data/processed/job.json"}]]}],
528+
["FA_if_newer", {"source": "/src", "target": "/dst",
529+
"then": [["FA_sync_dir", {"src": "/src", "dst": "/dst"}]]}],
530+
["FA_if_size_gt", {"path": "/logs/app.log", "size": 10485760,
531+
"then": [["FA_run_shell", {"command": ["logrotate", "/logs/app.log"]}]]}]
532+
]
533+
```
534+
535+
### SQLite 审计日志
536+
`AuditLog` 以短连接 + 模块级锁为每个动作写入一条记录:
537+
538+
```python
539+
from automation_file import AuditLog
540+
541+
audit = AuditLog("audit.sqlite3")
542+
audit.record(action="FA_copy_file", actor="ops",
543+
status="ok", duration_ms=12, detail={"src": "a", "dst": "b"})
544+
545+
for row in audit.recent(limit=50):
546+
print(row["timestamp"], row["action"], row["status"])
547+
```
548+
549+
### 文件完整性监控
550+
按 manifest 轮询整棵树,检测到 drift 时触发 callback + 通知:
551+
552+
```python
553+
from automation_file import IntegrityMonitor, notification_manager, write_manifest
554+
555+
write_manifest("/srv/site", "/srv/MANIFEST.json")
556+
557+
mon = IntegrityMonitor(
558+
root="/srv/site",
559+
manifest_path="/srv/MANIFEST.json",
560+
interval=60.0,
561+
manager=notification_manager,
562+
on_drift=lambda summary: print("drift:", summary),
563+
)
564+
mon.start()
565+
```
566+
567+
加载 manifest 时的错误也会被视为 drift,让篡改与配置问题走同一条处理
568+
路径。
569+
570+
### AES-256-GCM 文件加密
571+
带认证的加密与自描述封包格式。可由密码派生密钥或直接生成密钥:
572+
573+
```python
574+
from automation_file import encrypt_file, decrypt_file, key_from_password
575+
576+
key = key_from_password("correct horse battery staple", salt=b"app-salt-v1")
577+
encrypt_file("secret.pdf", "secret.pdf.enc", key, associated_data=b"v1")
578+
decrypt_file("secret.pdf.enc", "secret.pdf", key, associated_data=b"v1")
579+
```
580+
581+
篡改由 GCM 认证 tag 检测,以 `CryptoException("authentication failed")`
582+
报告。JSON 动作:`FA_encrypt_file``FA_decrypt_file`
583+
584+
### HTTPActionClient Python SDK
585+
HTTP 动作服务器的类型化客户端;默认强制 loopback,并自动携带 shared
586+
secret:
587+
588+
```python
589+
from automation_file import HTTPActionClient
590+
591+
with HTTPActionClient("http://127.0.0.1:9944", shared_secret="s3cr3t") as client:
592+
client.ping() # OPTIONS /actions
593+
result = client.execute([["FA_create_dir", {"dir_path": "x"}]])
594+
```
595+
596+
认证失败会转换为 `HTTPActionClientException(kind="unauthorized")`;
597+
404 则表示服务器存在但未对外提供 `/actions`
598+
599+
### Prometheus metrics 导出器
600+
`ActionExecutor` 为每个动作记录一条计数器与一条直方图样本。在 loopback
601+
`/metrics` 端点提供:
602+
603+
```python
604+
from automation_file import start_metrics_server
605+
606+
server = start_metrics_server(host="127.0.0.1", port=9945)
607+
# curl http://127.0.0.1:9945/metrics
608+
```
609+
610+
导出 `automation_file_actions_total{action,status}` 以及
611+
`automation_file_action_duration_seconds{action}`。若要绑定非 loopback
612+
地址必须显式传入 `allow_non_loopback=True`
613+
492614
### DAG 动作执行器
493615
按依赖顺序执行动作;独立分支通过线程池并行展开。每个节点的形式为
494616
`{"id": ..., "action": [...], "depends_on": [...]}`

0 commit comments

Comments
 (0)