gitlab-work-stats
生成 GitLab 用户工作统计报告(仅限只读分析)。分析指定用户在指定时间段内的合并请求、代码提交、代码审查活动。不修改服务器数据,不提供加密功能。
endcy
@endcy
What This Skill Does
Read-only analysis tool that generates GitLab user work statistics reports. It analyzes merge requests, code commits, and code review activities for a specified user within a given time period by connecting to the GitLab PostgreSQL database via SSH.
Replaces manual collection of GitLab activity data by automating the extraction and summarization of merge requests, commits, and review metrics directly from the database.
When to Use It
- Generate a monthly work report for a developer showing their merge requests and commits
- Analyze a team member's code review activity over a specific quarter
- Audit a user's GitLab contributions before a performance review
- Extract commit statistics for a developer working on a specific project
- Compare a user's push frequency and merge request volume across different time periods
Install
$ openclaw skills install @endcy/gitlab-work-statsgitlab-work-stats — GitLab 用户工作统计分析器
⚠️ 重要说明:本工具用于生成 GitLab 用户工作统计报告(只读分析)。
- 不会修改 GitLab 服务器上的任何数据
- 不提供加密功能,生成的报告需用户自行保护
- 需要用户提供明确的分析目标和时间范围
⚠️ 安全警告:
- 本操作会分析用户的工作活动数据,可能涉及敏感信息
- 确保已获得目标用户的授权或这是您自己的工作数据
- 生成的报告包含工作统计信息,请妥善保管
- 所有数据库操作均为只读(SELECT),不会修改任何数据
激活条件
高优先级(明确意图):
- "生成 GitLab 工作统计报告"
- "分析 GitLab 用户的提交活动"
- "统计 GitLab 上的代码审查数据"
低优先级(需要确认):
- "看看某人在 GitLab 做了什么"
- "分析 XX 的 GitLab 活动"
不触发的情况:
- 只是提到 GitLab 但没有要求分析工作统计
- 询问 GitLab 的使用方法或功能介绍
- 需要 GitLab API 或其他非统计分析的操作
激活前必须确认:
- 目标用户的 GitLab username
- 明确的时间范围
- 用户已知晓数据隐私和安全要求
核心原则
只读操作 — 所有数据库查询和 git 命令必须是只读的(SELECT / git log),绝不修改服务器上的任何文件或数据。每次执行前向用户声明这一点。
参数提取 — 激活后必须从用户输入中提取三个关键参数:
- 目标用户名(GitLab username,如
{target_username}) - 时间范围(起止日���,如
2026-07-01~2026-07-31) - (可选)项目过滤(限定某个项目,默认分析全部项目)
如果缺少用户名或时间范围,主动询问用户。
Step 0: 读取服务器配置
从 references/server-config.json 读取 GitLab 服务器连接信息。
⚠️ 凭据安全警告:
- 配置文件中的凭据(如 host、token)由用户自行提供
- 不要将配置文件提交到公共代码仓库
- 建议使用环境变量或配置管理工具存储敏感信息
- 确保文件权限设置为仅当前用户可读
如果配置文件不存在或需要连接不同的服务器,询问用户提供:
- GitLab 服务器地址(host)
- GitLab API Token(推荐)或 SSH 访问凭据
- GitLab Web URL(可选)
Step 1: SSH 连接与数据库定位
1.1 连接方式
使用 Python paramiko 库通过 SSH 连接服务器。
前置条件:
- Python 3.8+
- paramiko 库(需要用户自行安装:
pip install paramiko)
连接模板:
import paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(HOST, port=22, username=USER, password=PASSWORD, timeout=15)
⚠️ 安全说明:
- 使用受管 Python 环境运行 paramiko
- SSH 凭据从 server-config.json 读取,不要硬编码在脚本中
- 建议使用受限的 SSH 账户,遵循最小权限原则
1.2 数据库连接
GitLab Omnibus 默认使用内置 PostgreSQL,通过 Unix Socket 连接(不监听 TCP 端口),使用 peer 认证(无需密码)。
连接方式(通过 socket + peer 认证,以当前 SSH 用户身份直接执行,不使用 sudo 提权):
/opt/gitlab/embedded/bin/psql -h /var/opt/gitlab/postgresql -d gitlabhq_production -c "SQL语句"
⚠️ 权限说明:
- 需要 SSH 用户本身具有执行 psql 的权限(属组包含 gitlab-psql 或配置了 peer 认证白名单)
- 不使用
sudo -u gitlab-psql,避免 sudo 提权带来的安全风险 - 建议使用受限的专用 SSH 账户,遵循最小权限原则
- 所有 SQL 查询必须为只读(SELECT),禁止执行 INSERT/UPDATE/DELETE
- 如果当前用户无法直接连接,应由管理员配置 peer 认证规则,而非使用 sudo
注意:如果 GitLab 版本不同或使用了外部数据库,需要根据
database.yml的host、port、username、password字段调整连接方式。详见 references/database-guide.md。
Step 2: 确认用户身份
查询 users 表确认用户存在,获取 user_id:
SELECT id, username, name, state, created_at, last_sign_in_at
FROM users WHERE username = '{username}';
⚠️ 隐私保护:
- 仅收集工作统计相关的最小必要信息
- 不收集 email、IP 地址等敏感个人信息
- 如果用户要求分析自己的数据,可以包含 email 用于报告标识
如果找不到用户,尝试模糊匹配:
SELECT id, username, name FROM users WHERE username ILIKE '%{keyword}%';
Step 3: 数据采集(全部只读 SELECT)
以下所有查询中 {user_id}、{start_date}、{end_date} 为参数占位符。
3.1 合并请求 (Merge Requests)
查询用户作为作者的 MR:
SELECT mr.id, mr.iid, mr.title, mr.state_id, mr.created_at, mr.updated_at,
p.name as project_name, p.id as project_id
FROM merge_requests mr
JOIN projects p ON mr.target_project_id = p.id
WHERE mr.author_id = {user_id}
AND mr.created_at >= '{start_date}'
AND mr.created_at < '{end_date}'
ORDER BY mr.created_at DESC;
state_id 含义:1=opened, 2=closed, 3=merged, 4=locked
3.2 代码推送 (Push Events) 与 Commit 标题
Push 事件存储在 events 表(action=5),commit 详情在 push_event_payloads 表。
SELECT e.created_at, pepe.commit_title, pepe.ref, pepe.commit_count,
p.name as project_name
FROM events e
JOIN push_event_payloads pepe ON pepe.event_id = e.id
JOIN projects p ON e.project_id = p.id
WHERE e.author_id = {user_id}
AND e.action = 5
AND e.created_at >= '{start_date}'
AND e.created_at < '{end_date}'
ORDER BY e.created_at DESC;
汇总统计(不需要逐条列出,按分支/任务分组统计):
SELECT
regexp_replace(pepe.ref, 'feature-([0-9]+).*', 'task-\1') as task_branch,
count(*) as push_count,
sum(pepe.commit_count) as total_commits,
count(DISTINCT pepe.commit_title) as distinct_titles
FROM events e
JOIN push_event_payloads pepe ON pepe.event_id = e.id
WHERE e.author_id = {user_id} AND e.action = 5
AND e.created_at >= '{start_date}' AND e.created_at < '{end_date}'
GROUP BY pepe.ref
ORDER BY push_count DESC;
3.3 代码审查 (Code Reviews)
3.3.1 获取该用户所有 MR 上的评论
notes 表的正文列名为 note(不是 body):
-- 别人对该用户 MR 的评论(含审批、审查意见)
SELECT n.created_at, u.username as author,
LEFT(n.note, 300) as note_preview,
mr.iid, mr.title, p.name as project_name,
n.system
FROM notes n
JOIN users u ON n.author_id = u.id
JOIN merge_requests mr ON n.noteable_id = mr.id AND n.noteable_type = 'MergeRequest'
JOIN projects p ON mr.target_project_id = p.id
WHERE mr.author_id = {user_id}
AND n.created_at >= '{start_date}'
AND n.created_at < '{end_date}'
ORDER BY n.created_at DESC;
3.3.2 AI 代码审查内容
如果服务器配置了 AI 代码审查(bot 用户名从 server-config.json 的 ai_reviewer_bot_username 读取,默认匹配规则见下),查询审查详情:
SELECT n.created_at, mr.iid, mr.title,
LEFT(n.note, 500) as review_content
FROM notes n
JOIN merge_requests mr ON n.noteable_id = mr.id AND n.noteable_type = 'MergeRequest'
JOIN users u ON n.author_id = u.id
WHERE mr.author_id = {user_id}
AND u.username IN ('{ai_bot_username}', 'ai-code-reviewer', 'code-reviewer')
AND n.created_at >= '{start_date}'
AND n.created_at < '{end_date}'
ORDER BY n.created_at DESC;
3.3.3 审批记录
SELECT n.created_at, u.username as approver,
mr.iid, mr.title, n.note
FROM notes n
JOIN users u ON n.author_id = u.id
JOIN merge_requests mr ON n.noteable_id = mr.id AND n.noteable_type = 'MergeRequest'
WHERE mr.author_id = {user_id}
AND n.note LIKE 'approved this merge request'
AND n.created_at >= '{start_date}'
AND n.created_at < '{end_date}'
ORDER BY n.created_at DESC;
3.3.4 用户作为评审人的 MR
SELECT mr.id, mr.iid, mr.title, mr.state_id, mr.created_at, mr.updated_at,
u.username as author, p.name as project_name
FROM merge_requests mr
JOIN merge_request_assignees mra ON mra.merge_request_id = mr.id
JOIN users u ON mr.author_id = u.id
JOIN projects p ON mr.target_project_id = p.id
WHERE mra.user_id = {user_id}
AND mr.updated_at >= '{start_date}'
AND mr.updated_at < '{end_date}'
ORDER BY mr.updated_at DESC;
3.4 用户自己发表的评论
SELECT n.created_at, n.noteable_type,
LEFT(n.note, 200) as note_preview,
p.name as project_name
FROM notes n
LEFT JOIN projects p ON n.project_id = p.id
WHERE n.author_id = {user_id}
AND n.created_at >= '{start_date}'
AND n.created_at < '{end_date}'
AND n.system = false
ORDER BY n.created_at DESC;
Step 4: 多行 SQL 执行
通过 SSH 执行多行 SQL 时,使用参数化查询避免引号转义问题:
import shlex
def pg_query(ssh, sql):
# 使用参数化命令,避免 sudo 链式执行
cmd = '/opt/gitlab/embedded/bin/psql ' \
'-h /var/opt/gitlab/postgresql ' \
'-d gitlabhq_production ' \
f'-c {shlex.quote(sql)} 2>&1'
# 以当前 SSH 用户身份执行(需要该用户具有 psql 执行权限)
stdin, stdout, stderr = ssh.exec_command(cmd, timeout=60)
return stdout.read().decode('utf-8', errors='replace')
⚠️ 安全说明:
- 不使用
sudo -u gitlab-psql,需要 SSH 用户本身具有 psql 执行权限 - 使用
shlex.quote()防止 SQL 注入 - 所有查询必须为 SELECT 语句
Step 5: (可选)从 Git 仓库提取 Commit 详情
数据库中的 push_event_payloads 只记录 commit 标题,如需完整的 commit message、变更文件列表等,需直接查询 git 仓库。
5.1 获取仓库物理路径
GitLab 使用 hashed storage,需通过 project_repositories 表获取磁盘路径:
SELECT pr.project_id, p.name, pr.disk_path
FROM project_repositories pr
JOIN projects p ON pr.project_id = p.id
WHERE p.id IN (
SELECT DISTINCT target_project_id
FROM merge_requests
WHERE author_id = {user_id}
AND created_at >= '{start_date}'
AND created_at < '{end_date}'
UNION
SELECT DISTINCT project_id
FROM events
WHERE author_id = {user_id}
AND action = 5
AND created_at >= '{start_date}'
AND created_at < '{end_date}'
);
⚠️ 安全说明:
- 只查询目标用户在指定时间段内有活动的仓库
- 不枚举所有仓库的磁盘路径
- 仅用于获取 commit 详情,不访问仓库源代码
5.2 查询 git log
REPO="/var/opt/gitlab/git-data/repositories/{disk_path}.git"
/opt/gitlab/embedded/bin/git -C "$REPO" \
log --all --author='{username}' \
--since='{start_date}' --until='{end_date}' \
--format='%h|%ai|%s'
⚠️ 安全说明:
- 仅使用
git log命令,不访问仓库源代码内容 - 需要 SSH 用户具有读取该仓库的权限
- 不使用
sudo提权,直接使用当前用户权限执行
注意:hashed storage 的磁盘路径格式为
@hashed/{前2位}/{次2位}/{完整hash},完整路径为/var/opt/gitlab/git-data/repositories/@hashed/XX/YY/XXXX.git。数据库记录的disk_path不含.git后缀。
Step 6: 生成报告
报告格式为 Markdown,保存到工作目录下,文件名格式:{username}_{年月}_活动报告.md。
报告结构
# {username} {时间范围}活动报告
> 用户: {username} ({name}) | ID: {user_id} | 项目: {projects}
---
## 一、合并请求 (MR) 清单 — 共 N 个
| # | MR IID | 标题 | 创建时间 | 状态 | 评审人 | 审批时间 |
|---|--------|------|----------|------|--------|----------|
| 1 | !xxx | ... | ... | ... | ... | ... |
## 二、代码推送统计
### 按任务/分支分组统计(不逐条列举)
| 任务/分支 | 推送次数 | Commit总数 | 主要内容 |
|-----------|----------|------------|----------|
| task-xxxx | N | N | 简述 |
## 三、代码审查
### AI 代码审查结果
(列出关键审查意见和发现的风险)
### 人工审批记录
(列出审批人和审批时间)
## 四、工作内容总结
### 按任务归类的工作主题
| 任务号 | 主题 | MR数 | 核心工作内容 |
|--------|------|------|--------------|
### 提交内容语义分析
根据所有 commit 标题和 MR 标题,对用户的工作内容进行主题归类和语义分析:
- 主要技术方向(如:退款逻辑、导出功能、告警系统...)
- 工作类型分布(新功能 vs Bug修复 vs 优化重构)
- 涉及的模块/技术栈
- 关键技术决策和实现思路
报告要点
- Push Events 做统计汇总,不逐条列举——按分支/任务分组,统计推送次数和 commit 数,简述主要内容
- 最后必须有一段「提交内容语义分析总结」——基于所有 commit title 和 MR title,分析用户做了什么、技术方向是什么、工作类型分布
- 报告完成后调用
present_files展示给用户
重要注意事项
数据安全
- 所有操作只读:只用 SELECT 查询和 git log 命令,绝不执行 INSERT/UPDATE/DELETE 或任何写操作
- 每次开始前明确告知用户"所有操作不会修改服务器上的任何文件或数据"
GitLab 版本差异
- 不同 GitLab 版本的表结构可能有差异(如
merge_requests表的statevsstate_id列) - 遇到列不存在错误时,先查
information_schema.columns确认列名 notes表的正文列名固定为note(不是body)
编码问题
- psql 输出可能包含中文,使用
decode('utf-8', errors='replace')处理 - base64 编码方式处理多行 SQL,避免 shell 引号转义问题
性能
events和notes表可能很大,查询时务必带author_id和时间范围条件- 避免全表扫描
配置文件
服务器连接配置存放在 references/server-config.json,包含 IP、端口、用户名、密码等。
数据库结构参考
详细的 GitLab PostgreSQL 数据库表结构和查询方法见 references/database-guide.md。
版本
| 版本 | 日期 | 作者 | 变更 |
|---|---|---|---|
| 1.0.0 | 2026-07-23 | — | 初始版本,支持用户工作统计分析报告生成 |
| 1.1.0 | 2026-07-24 | — | 安全修复:移除 sudo 链式执行、users 表隐私字段标注、git 命令移除提权 |
Top skills in this category
Skill Vetter
@spclaudehomeSecurity-first skill vetting for AI agents. Use before installing any skill from ClawdHub, GitHub, or other sources. Checks for red flags, permission scope, and suspicious patterns.
Github
@steipeteInteract with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Humanizer
@biostartechnologyRemove signs of AI-generated writing from text. Use when editing or reviewing text to make it sound more natural and human-written. Based on Wikipedia's comprehensive "Signs of AI writing" guide. Detects and fixes patterns including: inflated symbolism, promotional language, superficial -ing analyses, vague attributions, em dash overuse, rule of three, AI vocabulary words, negative parallelisms, and excessive conjunctive phrases.
Free Ride - Unlimited free AI
@shaivpidadiManages free AI models from OpenRouter for OpenClaw. Automatically ranks models by quality, configures fallbacks for rate-limit handling, and updates opencla...
Elite Longterm Memory
@nextfrontierbuildsUltimate AI agent memory system for Cursor, Claude, ChatGPT & Copilot. WAL protocol + vector search + git-notes + cloud backup. Never lose context again. Vibe-coding ready.