feat: add gitea-deploy skill, tools, and sdlc foreground service rule

- skills/gitea-deploy/: new skill for Gitea deployment automation
- tools/: shared utility scripts
- skills/sdlc/SKILL.md: add Foreground Service Rule for long-running
  processes (background start + readiness polling pattern)
This commit is contained in:
Team
2026-04-25 14:11:58 +08:00
parent b6e3cef844
commit f20bc770f5
11 changed files with 455 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
{
"name": "gitea-deploy",
"description": "Deploys a self-hosted Gitea Git server on a VPS using Docker and pushes the current project to it. Use when you want to self-host git, set up Gitea on a VPS, or push a project to a private Git server.",
"prompt": "file://prompts/gitea-deploy.md",
"tools": ["execute_bash", "fs_read", "fs_write"],
"allowedTools": ["fs_read"],
"resources": [
"skill://.kiro/skills/gitea-deploy/SKILL.md"
],
"welcomeMessage": "Ready to deploy Gitea on your VPS. Tell me your VPS host, SSH user, and key path to get started."
}
+29
View File
@@ -0,0 +1,29 @@
You are a deployment specialist for self-hosted Gitea Git servers. When the user wants to deploy Gitea or push a project to a VPS, activate the `gitea-deploy` skill and follow its instructions exactly.
Always collect required inputs before executing any commands: VPS host, SSH user, SSH key path, admin credentials, and repository name. Never guess credentials. Never print passwords in output.
When the user sends a greeting or help request (e.g., "hi", "hello", "help", "你好", "帮助", "?"), respond with:
---
👋 **Gitea Deploy** — 自动化部署自托管 Git 服务器
**功能:**
- 在 VPS 上通过 Docker 部署 Gitea
- 自动安装 Docker(如未安装)
- 通过 API 初始化 Gitea 管理员账号和仓库
- 将当前项目推送到自托管 Gitea
**执行步骤:**
1. 收集 VPS 信息(host、SSH 用户、密钥路径、管理员账号)
2. 验证 SSH 连通性
3. 检测并安装 Docker(如需要)
4. 部署 Gitea`docker compose up -d`
5. 等待服务就绪,通过 REST API 初始化
6. 创建仓库,配置 git remote,推送代码
7. 输出 Web URL 和 SSH clone URL
**使用示例:**
- `Deploy Gitea on my VPS at 123.45.67.89, SSH user root`
- `在我的 VPS 上搭建 Gitea,然后把这个项目推上去`
- `My VPS is git.example.com, Docker is already installed, just push my project`
---
+56
View File
@@ -0,0 +1,56 @@
# gitea-deploy
Automate deploying a self-hosted Gitea Git server on a VPS and pushing the current project to it.
## Architecture
![Architecture](assets/gitea-deploy-architecture.svg)
## Workflow
![Workflow](assets/gitea-deploy-workflow.svg)
## When to Use
- You want to self-host a private Git server on your own VPS
- You need to push a project to a Gitea instance (new or existing)
- You want to migrate away from GitHub/GitLab to a self-hosted solution
Trigger phrases: "deploy gitea", "self-host git", "setup git server", "push to my vps", "搭建git服务器", "部署gitea"
## How It Works
1. Collects VPS connection info and credentials from the user
2. Validates local git repo and SSH connectivity
3. Installs Docker on the VPS if not present
4. Deploys Gitea via `docker compose up -d`
5. Polls until Gitea is healthy, then initializes via REST API
6. Creates the target repository and pushes all local branches
7. Reports the web URL and SSH clone URL
## Requirements
- SSH access to a Linux VPS
- Local project must be (or become) a git repository
- Gitea web port (default `3000`) and SSH port (default `2222`) must be open on the VPS firewall
## File Structure
```
skills/gitea-deploy/
├── SKILL.md
├── README.md
├── assets/
│ ├── architecture.puml
│ ├── gitea-deploy-architecture.svg
│ ├── workflow.puml
│ └── gitea-deploy-workflow.svg
└── evals/
└── evals.json
```
## Evals
```bash
python scripts/run_evals.py gitea-deploy
```
+167
View File
@@ -0,0 +1,167 @@
---
name: gitea-deploy
description: Automates deploying a self-hosted Gitea Git server on a VPS using Docker, then pushes the current project to it. Use when the user wants to self-host a Git server, set up Gitea on a VPS, push a project to a private Git server, or migrate away from GitHub/GitLab. Triggers on phrases like "deploy gitea", "self-host git", "setup git server", "push to my vps", "host my own git", "搭建git服务器", "自托管git", "部署gitea", "推送到vps".
metadata:
author: common-skills
version: "1.0"
compatibility:
tools:
- execute_bash
requirements:
- SSH access to a VPS (Linux)
- Docker available or installable on VPS
- git initialized in the local project
---
# Gitea Deploy
Automate deploying a self-hosted Gitea instance on a VPS and pushing the current project to it.
## Inputs
Collect from the user before starting:
| Field | Example |
|---|---|
| VPS host | `123.45.67.89` or `git.example.com` |
| SSH user | `root` or `ubuntu` |
| SSH private key path | `~/.ssh/id_rsa` |
| Gitea admin username | `admin` |
| Gitea admin password | (user provides, never log it) |
| Gitea web port | `3000` (default) |
| SSH port for Gitea | `2222` (default) |
| Repository name | defaults to current directory name |
If any field is missing, ask for it. Never guess credentials.
## Workflow
### 1. Validate Local Prerequisites
```bash
git rev-parse --is-inside-work-tree # confirm we're in a git repo
git status --short # show current state
```
If not a git repo, run `git init && git add -A && git commit -m "chore: initial commit"` after confirming with the user.
### 2. Test SSH Connectivity
```bash
ssh -i <key_path> -o StrictHostKeyChecking=no -o ConnectTimeout=10 <user>@<host> "echo OK"
```
If this fails, stop and report the error. Do not proceed.
### 3. Check / Install Docker on VPS
```bash
ssh -i <key_path> <user>@<host> "docker --version 2>/dev/null || (curl -fsSL https://get.docker.com | sh)"
```
Report the Docker version found or installed.
### 4. Deploy Gitea via Docker Compose
Upload and run a `docker-compose.yml` on the VPS:
```bash
ssh -i <key_path> <user>@<host> "mkdir -p ~/gitea"
cat <<'EOF' | ssh -i <key_path> <user>@<host> "cat > ~/gitea/docker-compose.yml"
services:
gitea:
image: gitea/gitea:latest
restart: always
environment:
- USER_UID=1000
- USER_GID=1000
ports:
- "<web_port>:3000"
- "<ssh_port>:22"
volumes:
- ./data:/data
EOF
ssh -i <key_path> <user>@<host> "cd ~/gitea && docker compose up -d"
```
### 5. Wait for Gitea to Be Ready
Poll until the health endpoint responds (max 60 seconds):
```bash
ssh -i <key_path> <user>@<host> \
"for i in \$(seq 1 12); do curl -sf http://localhost:<web_port>/api/healthz && break || sleep 5; done"
```
If not ready after 60 s, show the container logs:
```bash
ssh -i <key_path> <user>@<host> "docker logs gitea-gitea-1 --tail 30"
```
### 6. Initialize Gitea via API
Create the admin user and the target repository using the Gitea API:
```bash
# Create admin user (first-time setup)
ssh -i <key_path> <user>@<host> \
"docker exec gitea-gitea-1 gitea admin user create \
--username <admin_user> --password '<admin_pass>' \
--email admin@localhost --admin --must-change-password=false"
# Create repository
curl -sf -X POST "http://<host>:<web_port>/api/v1/user/repos" \
-u "<admin_user>:<admin_pass>" \
-H "Content-Type: application/json" \
-d "{\"name\":\"<repo_name>\",\"private\":true,\"auto_init\":false}"
```
If the admin user already exists, skip creation (exit code 1 is acceptable).
### 7. Configure Local Git Remote and Push
```bash
# Add or update remote
git remote remove gitea 2>/dev/null || true
git remote add gitea "ssh://git@<host>:<ssh_port>/<admin_user>/<repo_name>.git"
# Push all branches
git push -u gitea --all
git push gitea --tags
```
If push fails due to host key, add `-o StrictHostKeyChecking=no` to the SSH command used by git:
```bash
GIT_SSH_COMMAND="ssh -i <key_path> -o StrictHostKeyChecking=no" git push -u gitea --all
```
### 8. Verify and Report
```bash
curl -sf "http://<host>:<web_port>/api/v1/repos/<admin_user>/<repo_name>" \
-u "<admin_user>:<admin_pass>" | python3 -m json.tool
```
Report to the user:
- Gitea web URL: `http://<host>:<web_port>`
- Repository URL: `http://<host>:<web_port>/<admin_user>/<repo_name>`
- SSH clone URL: `ssh://git@<host>:<ssh_port>/<admin_user>/<repo_name>.git`
## Edge Cases
- **Docker already installed**: Step 3 detects and skips installation.
- **Gitea already running**: Step 4 `docker compose up -d` is idempotent; existing containers are reused.
- **Admin user already exists**: Step 6 catches the error and continues.
- **Remote `gitea` already exists**: Step 7 removes and re-adds it.
- **No commits yet**: Step 1 detects and offers to create an initial commit.
## Security Notes
- Never print the admin password in command output. Reference it as `<admin_pass>`.
- Use `-o StrictHostKeyChecking=no` only for the initial setup; advise the user to remove it afterward.
- Recommend the user change the admin password after first login.
- For production use, suggest adding Nginx + Let's Encrypt for HTTPS.
@@ -0,0 +1,35 @@
@startuml gitea-deploy-architecture
skinparam componentStyle rectangle
skinparam defaultFontName Arial
skinparam backgroundColor #FAFAFA
package "gitea-deploy Skill" {
component "SKILL.md\n(instructions)" as SKILL
component "evals/evals.json" as EVALS
}
package "Local Machine" {
component "git repo\n(project)" as REPO
component "SSH private key" as KEY
}
package "VPS" {
component "Docker Engine" as DOCKER
component "docker-compose.yml" as COMPOSE
database "Gitea Data\n(~/gitea/data)" as DATA
component "Gitea Container\n(:3000, :22)" as GITEA
}
actor Developer
Developer --> SKILL : triggers skill
SKILL --> KEY : uses for SSH auth
SKILL --> DOCKER : installs if missing
SKILL --> COMPOSE : uploads & runs
COMPOSE --> GITEA : starts container
GITEA --> DATA : persists data
SKILL --> GITEA : initializes via REST API
SKILL --> REPO : git remote add + push
REPO --> GITEA : git push (SSH)
SKILL --> Developer : reports URLs
@enduml
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 12 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

+45
View File
@@ -0,0 +1,45 @@
@startuml gitea-deploy-workflow
skinparam defaultFontName Arial
skinparam backgroundColor #FAFAFA
actor Developer
participant "gitea-deploy\nSkill" as SKILL
participant "Local git" as GIT
participant "VPS (SSH)" as VPS
participant "Gitea API" as API
Developer -> SKILL : deploy gitea + push project
SKILL -> Developer : collect VPS host, SSH user, key, credentials
SKILL -> GIT : git rev-parse (check repo)
alt not a git repo
SKILL -> Developer : offer git init
Developer -> GIT : confirm → git init + initial commit
end
SKILL -> VPS : ssh echo OK (connectivity test)
alt SSH fails
SKILL -> Developer : report error, stop
end
SKILL -> VPS : docker --version
alt Docker not installed
VPS -> VPS : curl get.docker.com | sh
end
SKILL -> VPS : upload docker-compose.yml
SKILL -> VPS : docker compose up -d
loop poll every 5s (max 60s)
SKILL -> VPS : curl /api/healthz
end
SKILL -> VPS : gitea admin user create
SKILL -> API : POST /api/v1/user/repos
SKILL -> GIT : git remote add gitea ssh://...
SKILL -> GIT : git push -u gitea --all
SKILL -> API : GET /api/v1/repos/... (verify)
SKILL -> Developer : report web URL + clone URL
@enduml
+30
View File
@@ -0,0 +1,30 @@
{
"skill_name": "gitea-deploy",
"evals": [
{
"id": 1,
"prompt": "I want to self-host my git server on my VPS at 192.168.1.100. SSH user is root, key is ~/.ssh/id_rsa. Set up Gitea and push this project to it.",
"expected_output": "Collects missing info (admin credentials, repo name), tests SSH, installs Docker if needed, deploys Gitea via docker compose, initializes via API, adds git remote, pushes, and reports the web URL and clone URL."
},
{
"id": 2,
"prompt": "Deploy Gitea on my server git.mycompany.com. Docker is already installed. Admin user should be 'devops'.",
"expected_output": "Skips Docker installation (detects it's already present), deploys Gitea, creates the devops admin user, creates the repo, pushes the project, and reports success."
},
{
"id": 3,
"prompt": "我想把这个项目推送到我自己VPS上的GiteaVPS是 45.77.1.23SSH用户是ubuntu。",
"expected_output": "Asks for missing fields (SSH key path, admin credentials), then executes the full deployment workflow in order, reporting the final Gitea URL in Chinese."
},
{
"id": 4,
"prompt": "Set up Gitea on my VPS but the current directory is not a git repo yet.",
"expected_output": "Detects no git repo, offers to run git init + initial commit, then proceeds with the full deployment after user confirms."
},
{
"id": 5,
"prompt": "I already have Gitea running on port 3000 on my VPS. Just push my project to it.",
"expected_output": "Skips Docker/Gitea installation steps, goes directly to creating the repo via API and pushing via git remote."
}
]
}
+42
View File
@@ -220,6 +220,48 @@ Output format — `impl-plan.md`:
- Update `specs/STATUS.md` to reflect current progress.
- Do not proceed to Phase 6 until all tasks in impl-plan are implemented.
### Foreground Service Rule
Some tasks require starting a long-running foreground service (e.g. `uvicorn`, `npm run dev`, `docker run`, `flask run`). These commands block the terminal and will stall the implementation plan if run synchronously.
**Rule: Always start foreground services in the background and verify readiness before continuing.**
Pattern:
```bash
# Start in background, redirect logs
<command> > /tmp/<service>.log 2>&1 &
SERVICE_PID=$!
# Wait for readiness (poll the health endpoint or log output)
for i in $(seq 1 30); do
if <readiness-check>; then
echo "<service> is ready"
break
fi
sleep 1
done
```
Readiness check by service type:
| Service | Readiness Check |
|---|---|
| HTTP server (uvicorn, flask, fastapi, express, vite) | `curl -sf http://localhost:<port>` |
| Vite dev server | `curl -sf http://localhost:5173` (or configured port) |
| Docker container | `docker inspect --format='{{.State.Health.Status}}' <name>` == `healthy`; or `curl` the exposed port |
| PostgreSQL | `pg_isready -h localhost -p 5432` |
| Redis | `redis-cli ping` |
| Generic TCP port | `nc -z localhost <port>` |
After the readiness check passes, continue with the next implementation step. If the service fails to become ready within 30 seconds, print the last 20 lines of the log file and stop:
```bash
echo "Service failed to start. Last logs:"
tail -20 /tmp/<service>.log
exit 1
```
**Never use `&& sleep N` as a substitute for a real readiness check.** Fixed sleeps are fragile — use polling loops instead.
---
## Phase 6 — Verification
+38
View File
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# kp-get.sh — 安全从 KeePassXC 数据库获取条目属性
# 用法: ./kp-get.sh "Entry Title" [Password|UserName|URL]
#
# 主密码优先级:
# 1. KEEPASS_MASTER 环境变量
# 2. 系统密钥环 (secret-tool, Linux desktop)
# 3. GPG 加密文件 (~/.keepass/.master.gpg)
# 4. 交互输入
set -euo pipefail
DB="${KEEPASS_DB:-$HOME/.keepass/passwords.kdbx}"
ENTRY="${1:?用法: $0 \"Entry Title\" [Password|UserName|URL]}"
ATTR="${2:-Password}"
if [[ -z "${KEEPASS_MASTER:-}" ]]; then
KEEPASS_MASTER="$(secret-tool lookup service keepassxc account default 2>/dev/null || true)"
fi
if [[ -z "${KEEPASS_MASTER:-}" && -f "$HOME/.keepass/.master.gpg" ]]; then
KEEPASS_MASTER="$(gpg -q -d "$HOME/.keepass/.master.gpg" 2>/dev/null || true)"
fi
if [[ -z "${KEEPASS_MASTER:-}" && -f "$HOME/.keepass/.master.enc" ]]; then
KEEPASS_MASTER="$(openssl enc -d -aes-256-cbc -pbkdf2 -in "$HOME/.keepass/.master.enc" 2>/dev/null || true)"
fi
if [[ -z "${KEEPASS_MASTER:-}" && -f "$HOME/.keepass/.master" ]]; then
KEEPASS_MASTER="$(cat "$HOME/.keepass/.master")"
fi
if [[ -z "${KEEPASS_MASTER:-}" ]]; then
read -rsp "Master password: " KEEPASS_MASTER
echo
fi
echo "$KEEPASS_MASTER" | keepassxc-cli show -q "$DB" "$ENTRY" -a "$ATTR"