Compare commits

...
2 Commits
Author SHA1 Message Date
SHAREit 1d7328e7c7 fix: use env credentials for pipeline push 2026-10-16 15:36:53 +08:00
Ads Center 22b35b4408 feat: publish click gap 20261010-20261016 2026-10-16 15:19:22 +08:00
12 changed files with 73 additions and 10 deletions
+1
View File
@@ -5,6 +5,7 @@
*.sqlite *.sqlite
*.sqlite3 *.sqlite3
*.log *.log
*.tmp
__pycache__/ __pycache__/
*.py[cod] *.py[cod]
.cache/ .cache/
+10 -8
View File
@@ -23,16 +23,18 @@ clicks_diff_rate REAL, updated_at TEXT NOT NULL
## 手工运行 ## 手工运行
mac2 推荐始终通过包装脚本运行:
```sh ```sh
cd /Users/shareit./Library/CloudStorage/OneDrive-个人/ubuntu/Gitea/af-click-gap cd /Users/shareit./Library/CloudStorage/OneDrive-个人/ubuntu/Gitea/af-click-gap
python3 -m py_compile scripts/af_click_gap_pipeline.py scripts/run_pipeline.sh
python3 scripts/af_click_gap_pipeline.py --help scripts/run_pipeline.sh --start-date 2026-10-10 --end-date 2026-10-16
python3 scripts/af_click_gap_pipeline.py --dry-run
python3 scripts/af_click_gap_pipeline.py --push # 写 JSON,commit 并 push
python3 scripts/af_click_gap_pipeline.py --no-push # 写 JSON 和 commit,不 push
python3 scripts/af_click_gap_pipeline.py --start-date 2026-10-10 --end-date 2026-10-16 --no-push
``` ```
包装脚本默认定位项目根目录,并读取 sibling 项目 `../direct-report-dashboard/.env`(mac2 默认路径为 `~/ubuntu/Gitea/af-click-gap` 与 `~/ubuntu/Gitea/direct-report-dashboard`)。也可以用 `PROJECT_ROOT` 和 `ENV_FILE` 覆盖路径。它只解析 `AGENTSTAGE_USER`、`AGENTSTAGE_TOKEN`,不执行整个 `.env`;凭据只进入子进程环境,不写入文件、remote URL、命令行参数或日志。脚本使用临时 `GIT_ASKPASS` 响应 Git 的用户名和密码提示,临时文件权限为仅当前用户可读写,并在退出时清理。
不要直接运行 `python3 scripts/af_click_gap_pipeline.py --push`:该命令可能使用 mac2 Keychain 中没有仓库写权限的旧凭据。包装脚本会执行相同的 SQLite 最近 7 天提取和 `--push`,额外参数会继续传给流水线。需要只检查数据时可直接使用 `python3 scripts/af_click_gap_pipeline.py --dry-run`。
`--dry-run` 只读取数据库并在仓库外临时目录生成、校验 JSON,不写 `data` 或 Git。正式运行先原子替换 `data/` 输出目录中的数据文件,再执行 Git;数据库查询或校验失败不会发布文件。Git 安全检查只允许本次生成的 `data/*.json`(含 `data/index.json`),工作区存在其它未提交改动时拒绝继续。push 失败会保留本地 commit,不会 force push。 `--dry-run` 只读取数据库并在仓库外临时目录生成、校验 JSON,不写 `data` 或 Git。正式运行先原子替换 `data/` 输出目录中的数据文件,再执行 Git;数据库查询或校验失败不会发布文件。Git 安全检查只允许本次生成的 `data/*.json`(含 `data/index.json`),工作区存在其它未提交改动时拒绝继续。push 失败会保留本地 commit,不会 force push。
## 看板 ## 看板
@@ -41,7 +43,7 @@ python3 scripts/af_click_gap_pipeline.py --start-date 2026-10-10 --end-date 2026
## mac2 launchd ## mac2 launchd
模板文件为 `launchd/com.roasbloom.af-click-gap.plist.template`,必须在 mac2 使用实际 Python 路径和项目路径渲染后安装: launchd 会调用 `scripts/run_pipeline.sh`,因此也使用 sibling 项目 `.env` 中的凭据;模板通过 `PYTHON_BIN` 传入 mac2 的 Python 路径,并保留工作目录和 stdout/stderr 日志路径:
```sh ```sh
cd /Users/shareit./Library/CloudStorage/OneDrive-个人/ubuntu/Gitea/af-click-gap cd /Users/shareit./Library/CloudStorage/OneDrive-个人/ubuntu/Gitea/af-click-gap
@@ -57,7 +59,7 @@ launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.roasbloom.af-
## Git 创建和推送 ## Git 创建和推送
远程仓库约定为 Gitea `https://git.ops.roasbloom.site/liuji/af-click-gap.git`。创建前应检查仓库是否已存在,确认不存在后使用 macOS Keychain 中已有的 Git 凭证创建,再在本地配置 `origin` 并推送当前 `main` 分支。认证信息只通过现有 credential helper 使用,不写入本项目或命令输出。 远程仓库约定为 Gitea `https://git.ops.roasbloom.site/liuji/af-click-gap.git`。仓库创建和首次配置可使用已有流程;日常 mac2 发布必须使用 `scripts/run_pipeline.sh`,它从 `/Users/shareit./Library/CloudStorage/OneDrive-个人/ubuntu/Gitea/direct-report-dashboard/.env`(或 `ENV_FILE` 指定路径)读取 `AGENTSTAGE_USER` 和 `AGENTSTAGE_TOKEN`。凭据不进入 Git、remote URL、命令行、临时 askpass 文件或日志,且 `.env` 绝不提交。
## 验证和安全 ## 验证和安全
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
View File
Executable → Regular
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"schema_version": 1, "schema_version": 1,
"generated_at": "2026-10-16T15:13:23+08:00", "generated_at": "2026-10-16T15:19:21+08:00",
"files": [ "files": [
{ {
"date": "20261016", "date": "20261016",
@@ -3,7 +3,8 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict> <plist version="1.0"><dict>
<key>Label</key><string>com.roasbloom.af-click-gap</string> <key>Label</key><string>com.roasbloom.af-click-gap</string>
<key>ProgramArguments</key><array><string>__MAC2_PYTHON3__</string><string>__PROJECT_DIR__/scripts/af_click_gap_pipeline.py</string><string>--push</string></array> <key>ProgramArguments</key><array><string>__PROJECT_DIR__/scripts/run_pipeline.sh</string></array>
<key>EnvironmentVariables</key><dict><key>PYTHON_BIN</key><string>__MAC2_PYTHON3__</string></dict>
<key>WorkingDirectory</key><string>__PROJECT_DIR__</string> <key>WorkingDirectory</key><string>__PROJECT_DIR__</string>
<key>StartCalendarInterval</key><dict><key>Hour</key><integer>2</integer><key>Minute</key><integer>0</integer></dict> <key>StartCalendarInterval</key><dict><key>Hour</key><integer>2</integer><key>Minute</key><integer>0</integer></dict>
<key>StandardOutPath</key><string>__LOG_DIR__/pipeline.stdout.log</string><key>StandardErrorPath</key><string>__LOG_DIR__/pipeline.stderr.log</string> <key>StandardOutPath</key><string>__LOG_DIR__/pipeline.stdout.log</string><key>StandardErrorPath</key><string>__LOG_DIR__/pipeline.stderr.log</string>
+59
View File
@@ -0,0 +1,59 @@
#!/bin/sh
# Run the SQLite pipeline with the repository's dedicated Gitea credential.
set -eu
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
PROJECT_ROOT=${PROJECT_ROOT:-$(CDPATH= cd -- "$SCRIPT_DIR/.." && pwd)}
ENV_FILE=${ENV_FILE:-$(CDPATH= cd -- "$PROJECT_ROOT/../direct-report-dashboard" && pwd)/.env}
PYTHON_BIN=${PYTHON_BIN:-python3}
if [ ! -r "$ENV_FILE" ]; then
printf '%s\n' "Credential file is not readable: $ENV_FILE" >&2
exit 1
fi
# Read only the two Git credential variables; do not source the .env file.
dotenv_value() {
awk -v wanted="$1" '
$0 ~ "^[[:space:]]*" wanted "[[:space:]]*=" {
sub("^[[:space:]]*" wanted "[[:space:]]*=", "")
if ($0 ~ /^".*"[[:space:]]*$/) {
sub(/^"/, ""); sub(/"[[:space:]]*$/, "")
} else if ($0 ~ /^'"'"'.*'"'"'[[:space:]]*$/) {
sub(/^'"'"'/, ""); sub(/'"'"'[[:space:]]*$/, "")
}
print
exit
}
' "$ENV_FILE"
}
AGENTSTAGE_USER=$(dotenv_value AGENTSTAGE_USER)
AGENTSTAGE_TOKEN=$(dotenv_value AGENTSTAGE_TOKEN)
if [ -z "$AGENTSTAGE_USER" ] || [ -z "$AGENTSTAGE_TOKEN" ]; then
printf '%s\n' "AGENTSTAGE_USER and AGENTSTAGE_TOKEN are required in $ENV_FILE" >&2
exit 1
fi
export AGENTSTAGE_USER AGENTSTAGE_TOKEN
umask 077
ASKPASS=$(mktemp "${TMPDIR:-/tmp}/af-click-gap-askpass.XXXXXX")
cleanup() {
rm -f -- "$ASKPASS"
}
trap cleanup EXIT HUP INT TERM
cat >"$ASKPASS" <<'ASKPASS_SCRIPT'
#!/bin/sh
case "$1" in
*[Uu]sername*) printf '%s\n' "$AGENTSTAGE_USER" ;;
*[Pp]assword*) printf '%s\n' "$AGENTSTAGE_TOKEN" ;;
*) exit 1 ;;
esac
ASKPASS_SCRIPT
chmod 700 "$ASKPASS"
export GIT_ASKPASS="$ASKPASS"
export GIT_TERMINAL_PROMPT=0
"$PYTHON_BIN" "$PROJECT_ROOT/scripts/af_click_gap_pipeline.py" --push "$@"
exit $?