初始提交:AI 开发发布体系接入 skill(SKILL.md + Gitea 安装脚本)
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
---
|
||||
name: ai-system-bootstrap
|
||||
description: 为新服务器+新项目搭建完整的 AI 开发发布体系(Gitea 安装、规范目录 standards/、双仓库双环境、发布流程)。当用户要求"搭建体系/接入新项目/初始化 AI 开发流程"时使用本技能。
|
||||
---
|
||||
|
||||
# AI 开发发布体系接入(新服务器 + 新项目)
|
||||
|
||||
本技能把一台新服务器和一个新项目接入标准化的 AI 开发发布体系:
|
||||
|
||||
```
|
||||
AI 在开发目录改代码并实测 → commit 到开发分支 → push 到本机 Gitea
|
||||
→ 用户在 Gitea 网页合并到生产分支 → 生产目录 git pull + 清缓存 → 验证
|
||||
```
|
||||
|
||||
**执行原则**:按第 0~7 步顺序执行;每步的验证点没通过不进下一步;涉及生产目录/生产库的写操作必须先给用户过目并确认。
|
||||
|
||||
---
|
||||
|
||||
## 第 0 步:收集信息(缺哪项问用户,不要猜)
|
||||
|
||||
| 项 | 说明 |
|
||||
|---|---|
|
||||
| 项目生产域名 / 测试域名 | 例:`www.xxx.com` / `test.xxx.com` |
|
||||
| Gitea 域名 | 建议 `git.<项目域名>`,用项目自己的域名,不要复用其他项目的 |
|
||||
| 服务器面板 | 宝塔 / 其他 / 无面板(本 skill 以宝塔为默认,其他面板类比调整) |
|
||||
| web 运行用户 | 宝塔默认 `www`;别的环境可能是 `www-data`/`nginx`,**后续所有属主操作用它** |
|
||||
| 项目技术栈 | 语言/框架/构建方式(影响 coding 规范和 .gitignore) |
|
||||
| 是否已有生产在跑 | 有则**先做全量备份**再动任何东西 |
|
||||
| 域名备案状态 | 中国大陆服务器未备案的域名不能用,需用已备案的过渡域名,并记录切换计划 |
|
||||
|
||||
## 第 1 步:安装 Gitea(本机自建,走 `install_gitea.sh`)
|
||||
|
||||
```bash
|
||||
sudo ./install_gitea.sh git.example.com
|
||||
```
|
||||
|
||||
脚本做的事:建 `git` 系统用户 → `/www/gitea/{custom,data,log}` → 下载校验 Gitea 最新稳定版 → 写 app.ini(SQLite、监听 127.0.0.1:3000、禁止注册、强制私有库)→ systemd 服务 → 启动并验证 `curl 127.0.0.1:3000` 返回 200。
|
||||
|
||||
手工等价操作与已知坑(脚本失败时按此排查):
|
||||
|
||||
1. **不要**预配 `INTERNAL_TOKEN_URI`——首次启动会因文件不存在而崩溃,让 Gitea 自动生成。
|
||||
2. 下载地址列表:`https://dl.gitea.com/gitea/version.json` 取最新版本号;sha256 文件校验时文件名要与原始名一致。
|
||||
3. 防火墙**不开** 3000 端口,只走反代。
|
||||
|
||||
## 第 2 步:Gitea 域名与反代
|
||||
|
||||
1. **用户操作**:DNS 添加 A 记录 `<gitea域名> → 服务器公网 IP`(未备案过渡域名同理)。
|
||||
2. 宝塔建反向代理站点:域名 = Gitea 域名,目标 `http://127.0.0.1:3000`,全站转发,开 WebSocket 支持。
|
||||
3. **必须在 server 块加 `client_max_body_size 0;`**——否则 git push 大仓库会被 nginx 拒(默认 1m)。
|
||||
4. `nginx -t` 通过后 reload,`curl -H "Host: <gitea域名>" http://127.0.0.1/` 返回 200。
|
||||
5. DNS 生效后面板申请 Let's Encrypt 证书(选对域名!宝塔常会自动附加别名域名,别选错)。
|
||||
6. 未备案过渡期:先用已备案域名访问,在规范里记录"备案成功后切换"待办。
|
||||
|
||||
## 第 3 步:管理员与项目仓库
|
||||
|
||||
```bash
|
||||
# 管理员(密码生成后交付用户,建议用户随即改掉)
|
||||
sudo -u git gitea admin user create --config /www/gitea/custom/conf/app.ini \
|
||||
--admin --username admin --password '<强密码>' --email admin@<项目域名> --must-change-password=false
|
||||
# 建私有仓库(也可让用户网页操作)
|
||||
```
|
||||
|
||||
1. 建一个**私有**仓库,命名 = 项目名(如 `xxx-erp`)。
|
||||
2. 生成 API token(_basic auth 调 `POST /api/v1/users/admin/tokens`,scope all),存 `/root/.gitea_token`(600)。
|
||||
3. 凭据写 `/root/.git-credentials`(600),格式 `http://admin:<token>@<gitea域名>`。
|
||||
|
||||
## 第 4 步:开发目录 git 化
|
||||
|
||||
1. `.gitignore` 至少排除:运行时目录(runtime/ 等)、用户上传目录、IDE 目录。**vendor/依赖目录是否入库先问用户**(老项目倾向全量存档防兼容问题,要强制 `git add -f` 补齐依赖内部 .gitignore 排除的文件)。
|
||||
2. `git init -b <开发分支>`(建议 `test` 或 `develop`)→ 配置 user.name/email → `git add -A` → 检查无垃圾文件 → 首次提交。
|
||||
3. remote + 凭据(credential.helper store)→ push。
|
||||
4. 提交身份、凭据都是**仓库级**配置;删过 .git 重建后要重配。
|
||||
|
||||
## 第 5 步:提炼 standards/ 规范目录(本技能只给骨架,内容必须重新提炼)
|
||||
|
||||
在项目根创建:
|
||||
|
||||
```
|
||||
standards/
|
||||
├── README.md # 目录说明+索引,开头放"AI 记忆指令"(读到即写入持久记忆)
|
||||
├── behavior.md # 行为约定:硬性禁止/危险链路/工作方式(含发布流程,见第 7 步)
|
||||
├── coding.md # 代码规范——通读项目代码后按真实写法提炼,禁止照抄其他项目
|
||||
├── database.md # 数据库规范——含增量 SQL 约定与改库发布流程(见第 7 步)
|
||||
└── manual.md # 完整手册:公共方法清单、场景配方、带文件路径/行号的实例
|
||||
```
|
||||
|
||||
提炼要求:**只写该项目真实遵循的写法**(读代码统计套路,不抄框架官方理论);硬性约定进 standards,查阅资料进 manual,两者冲突以 standards 为准。
|
||||
|
||||
同批创建:根 `README.md`(顶部"导航(新 AI 由此开始)":AI 记忆指令 + 必读顺序 + 第一级目录一览 + 寻路规则"进入目录先读其 README,首次用到某目录时创建其 README")+ `ai_work/`(README 索引 + done/ 归档机制)+ `sql/`(若项目含数据库)+ 各工具入口文件(CODEBUDDY.md/CLAUDE.md 等指向 standards)。
|
||||
|
||||
## 第 6 步:生产目录 git 化(有生产在跑时)
|
||||
|
||||
**先备份**:`tar -czf /www/backup/<项目>_pre_git_$(date +%Y%m%d_%H%M%S).tar.gz -C /www/wwwroot <生产目录名>`,并**单独留存环境专属文件副本**。
|
||||
|
||||
1. `git init -b <生产分支>` + remote + fetch。
|
||||
2. **审查差异**后再 reset:`git -C <生产目录> diff --stat FETCH_HEAD`——差异只允许是环境配置;出现业务代码差异必须先停下问用户。
|
||||
3. `git reset --hard FETCH_HEAD` → **立即恢复环境专属文件**(数据库配置、密钥、extra/环境配置、宝塔 `public/.user.ini` 等)。
|
||||
4. `git update-index --skip-worktree <环境专属文件...>`——**一份都不能漏**,漏一个就是生产事故。
|
||||
5. ⚠️ 宝塔的 `public/.user.ini` 带 `chattr +i` 锁:改它前 `chattr -i`,改完 `chattr +i`;reset 遇 "unable to unlink" 就是它。
|
||||
6. **操作后必须验证**:`git status` 干净(未跟踪除外);include/读取环境配置确认指向本环境;`chown -R <web用户>:<web用户>` 全目录(.git 和带锁文件除外);站点首页 200。
|
||||
7. 设置本地 master 上游跟踪,让以后 `git pull` 直接可用。
|
||||
|
||||
> **操作红线**:跨目录操作一律用绝对路径或 `git -C <路径>`——Bash 会话的工作目录可能在轮次间静默复位,凭"刚才 cd 过"操作是事故之源。危险命令(rm/reset/cp 覆盖)前后必须验证位置与关键文件内容。
|
||||
|
||||
## 第 7 步:发布流程写入 standards/behavior.md
|
||||
|
||||
- 开发分支 = AI 开发环境;任务完成 → commit + push → **提醒用户去 Gitea 合并到生产分支**(不会 git 的用户给指路:拉请求 → base=生产分支 / compare=开发分支 → 创建 → 合并;合并标题格式 `发布:<改动一句话>`;标题加 `WIP:` 前缀会锁合并按钮,用于未完成的草稿)。
|
||||
- 更新生产 = `git -C <生产目录> pull && sudo -u <web用户> php think clear`(按项目实际清缓存命令),**清缓存/写 runtime 的命令必须以 web 用户身份跑**,否则属主变 root 全站 500。完成后验证首页 200。
|
||||
- **改库流程**:增量 SQL 存 `sql/YYYY-MM-DD_描述.sql` 与代码一起提交;**SQL 不随 git pull 走**——生产库要单独执行,顺序固定**先库后代码**;执行生产库前把 SQL 给用户确认并备份生产库。
|
||||
- web 可写目录(runtime/、uploads/ 等)属主必须是 web 用户;root 操作后必须恢复。
|
||||
|
||||
## 完成验证清单(全部打勾才算接入完成)
|
||||
|
||||
- [ ] Gitea 可通过域名 HTTPS 访问,禁止注册,仓库强制私有
|
||||
- [ ] 管理员密码已交付用户
|
||||
- [ ] 开发分支已 push,remote/凭据可用
|
||||
- [ ] standards/ 五件套齐全且内容来自项目真实代码
|
||||
- [ ] 根 README 导航 + ai_work/ + 工具入口文件就位
|
||||
- [ ] 生产目录 git 化完成,skip-worktree 清单核实,站点 200
|
||||
- [ ] 发布流程、改库流程已写入 behavior.md
|
||||
- [ ] 全量备份文件已留存并告知用户路径
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env bash
|
||||
# install_gitea.sh —— 新服务器一键安装 Gitea(自包含体系用)
|
||||
# 用法: sudo ./install_gitea.sh <gitea域名> 例: sudo ./install_gitea.sh git.example.com
|
||||
# 安装后仍需人工完成: DNS A 记录、宝塔反向代理(含 client_max_body_size 0)、SSL 证书、创建管理员
|
||||
set -euo pipefail
|
||||
|
||||
DOMAIN="${1:?用法: sudo ./install_gitea.sh <gitea域名>}"
|
||||
BASE=/www/gitea
|
||||
[ "$(id -u)" -eq 0 ] || { echo "必须以 root 运行"; exit 1; }
|
||||
|
||||
echo "==> 1/7 创建 git 系统用户与目录"
|
||||
id git >/dev/null 2>&1 || useradd --system --shell /bin/bash --home-dir /home/git --create-home git
|
||||
mkdir -p $BASE/custom/conf $BASE/data $BASE/log
|
||||
|
||||
echo "==> 2/7 查询最新稳定版"
|
||||
VERSION=$(curl -s https://dl.gitea.com/gitea/version.json | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
|
||||
[ -n "$VERSION" ] || { echo "无法获取版本号,检查网络"; exit 1; }
|
||||
echo " 最新稳定版: $VERSION"
|
||||
|
||||
echo "==> 3/7 下载并校验二进制"
|
||||
cd /tmp
|
||||
curl -sL -o gitea-$VERSION-linux-amd64 https://dl.gitea.com/gitea/$VERSION/gitea-$VERSION-linux-amd64
|
||||
curl -sL -o gitea-$VERSION-linux-amd64.sha256 https://dl.gitea.com/gitea/$VERSION/gitea-$VERSION-linux-amd64.sha256
|
||||
sha256sum -c gitea-$VERSION-linux-amd64.sha256
|
||||
install -m 755 gitea-$VERSION-linux-amd64 /usr/local/bin/gitea
|
||||
rm -f gitea-$VERSION-linux-amd64*
|
||||
|
||||
echo "==> 4/7 写配置 app.ini"
|
||||
SECRET=$(openssl rand -hex 32)
|
||||
cat > $BASE/custom/conf/app.ini <<EOF
|
||||
APP_NAME = $(echo $DOMAIN | cut -d. -f2-) Git
|
||||
RUN_MODE = prod
|
||||
RUN_USER = git
|
||||
|
||||
[repository]
|
||||
ROOT = $BASE/data/repositories
|
||||
DEFAULT_PRIVATE = private
|
||||
FORCE_PRIVATE = true
|
||||
|
||||
[server]
|
||||
PROTOCOL = http
|
||||
HTTP_ADDR = 127.0.0.1
|
||||
HTTP_PORT = 3000
|
||||
DOMAIN = $DOMAIN
|
||||
ROOT_URL = http://$DOMAIN/
|
||||
APP_DATA_PATH = $BASE/data
|
||||
DISABLE_SSH = false
|
||||
START_SSH_SERVER = false
|
||||
|
||||
[database]
|
||||
DB_TYPE = sqlite3
|
||||
PATH = $BASE/data/gitea.db
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = true
|
||||
SECRET_KEY = $SECRET
|
||||
|
||||
[service]
|
||||
DISABLE_REGISTRATION = true
|
||||
SHOW_REGISTRATION_BUTTON = false
|
||||
|
||||
[log]
|
||||
MODE = file
|
||||
LEVEL = Info
|
||||
ROOT_PATH = $BASE/log
|
||||
|
||||
[session]
|
||||
PROVIDER = file
|
||||
PROVIDER_CONFIG = $BASE/data/sessions
|
||||
|
||||
[lfs]
|
||||
PATH = $BASE/data/lfs
|
||||
EOF
|
||||
echo " 注意: 不要手工添加 INTERNAL_TOKEN_URI,首次启动 Gitea 会自动生成内部令牌"
|
||||
chown -R git:git $BASE /home/git
|
||||
|
||||
echo "==> 5/7 systemd 服务"
|
||||
cat > /etc/systemd/system/gitea.service <<EOF
|
||||
[Unit]
|
||||
Description=Gitea ($DOMAIN)
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=git
|
||||
Group=git
|
||||
WorkingDirectory=$BASE
|
||||
ExecStart=/usr/local/bin/gitea web --config $BASE/custom/conf/app.ini
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
LimitNOFILE=65535
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
systemctl daemon-reload && systemctl enable --now gitea
|
||||
|
||||
echo "==> 6/7 等待启动"
|
||||
for i in $(seq 1 30); do
|
||||
sleep 2
|
||||
CODE=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:3000/ || true)
|
||||
[ "$CODE" = "200" ] && break
|
||||
done
|
||||
[ "$CODE" = "200" ] || { echo "启动失败,查看: journalctl -u gitea -n 30"; exit 1; }
|
||||
|
||||
echo "==> 7/7 完成。后续人工步骤:"
|
||||
cat <<TIP
|
||||
1. DNS 添加 A 记录: $DOMAIN -> 本机公网 IP
|
||||
2. 宝塔建反向代理站点: $DOMAIN -> http://127.0.0.1:3000,server 块加 client_max_body_size 0;
|
||||
(不加快递大 push 会被 nginx 默认 1m 限制拒绝)
|
||||
3. DNS 生效后申请 SSL 证书(注意选对域名)
|
||||
4. 创建管理员:
|
||||
sudo -u git gitea admin user create --config $BASE/custom/conf/app.ini \\
|
||||
--admin --username admin --password '<强密码>' --email admin@$DOMAIN --must-change-password=false
|
||||
5. 生成 API token 并配置 /root/.git-credentials (600)
|
||||
TIP
|
||||
Reference in New Issue
Block a user