前言

前两篇文章我们把 Gitea 搭起来、学会了升级维护,但你可能觉得——这跟 GitHub 也没啥区别啊?

真正的差距在于:GitHub 有 Actions,能自动跑测试、自动部署代码。

好消息是:Gitea 1.21+ 自带 Actions 功能,跟 GitHub Actions 几乎一模一样,甚至兼容它的工作流文件格式。

这篇文章会用一个真实的 Laravel 项目做例子,带你走通从"推代码"到"自动部署"的全流程。


Gitea Actions 是什么?

简单来说,Gitea Actions 就是 Gitea 内置的 CI/CD 系统。你可以在仓库里放一个配置文件,当代码推送、创建 PR、打标签时自动执行一系列任务。

比如:

场景自动干什么
推送代码到 master自动跑单元测试 + 代码规范检查
推送 v* 标签自动构建并部署到生产服务器
创建 Pull Request自动跑集成测试,把结果贴到 PR 评论里
定时任务每天凌晨自动备份数据库

两个核心概念:

  • Workflow(工作流):放在仓库 .gitea/workflows/ 目录下的 YAML 配置文件,定义什么事件触发、跑什么任务
  • Runner(运行器):负责实际执行工作流的程序,可以装在你的服务器上,也可以装在单独的机器上

第一步:启用 Gitea Actions

Gitea 1.21+ 默认开启了 Actions 功能。登录你的 Gitea 管理后台,检查一下:

  1. 进入「站点管理」→「设置」→「Actions」
  2. 确保 "启用 Actions" 是勾选状态
  3. 如果你用的是 1.21 之前的版本,先升级(上篇文章有教程)

确认开启后,我们下一步要装 Runner。


第二步:安装并注册 Runner

Runner 是真正干活的东西。可以理解成一台"打工机器"——Gitea 发出指令,Runner 执行。

2.1 下载 Runner

登录你的服务器(跟 Gitea 同一台就行),下载 Gitea Actions Runner:

# 创建目录
mkdir -p /opt/gitea-runner && cd /opt/gitea-runner

# 下载最新 runner
wget -O gitea-runner https://dl.gitea.com/act_runner/act_runner-linux-amd64

# 赋予执行权限
chmod +x gitea-runner

2.2 在 Gitea 获取注册令牌

在浏览器里打开你的 Gitea:

  1. 点击右上角你的头像 → 「站点管理」
  2. 左侧菜单 → 「Actions」→ 「Runners」
  3. 点击 "创建 Runner"
  4. 复制显示的注册令牌(一串长字符串,类似 xxxxxxxxxx

2.3 注册 Runner

回到服务器,执行注册命令:

# 注册(记得替换 URL 和 TOKEN)
./gitea-runner register \
  --instance http://git.ay.lc:3000 \
  --token 你的注册令牌 \
  --name my-runner \
  --labels ubuntu-latest:docker://node:20-bullseye

参数说明:

参数说明
--instance你的 Gitea 地址
--token上一步获取的注册令牌
--name给这个 Runner 起个名字
--labels声明 Runner 能跑什么环境,这里是 ubuntu-latest 标签

注册成功后,会在当前目录生成一个 .runner 配置文件。

2.4 启动 Runner

# 前台启动(先试一下能不能跑)
./gitea-runner daemon

看到 Ctrl+C to stop 就说明成功了。按 Ctrl+C 停掉,我们把它做成服务。

# 创建系统服务
cat > /etc/systemd/system/gitea-runner.service << 'EOF'
[Unit]
Description=Gitea Actions Runner
After=docker.service

[Service]
ExecStart=/opt/gitea-runner/gitea-runner daemon
Restart=always
RestartSec=10
WorkingDirectory=/opt/gitea-runner
User=root

[Install]
WantedBy=multi-user.target
EOF

# 启动并设置开机自启
systemctl daemon-reload
systemctl start gitea-runner
systemctl enable gitea-runner

# 检查状态
systemctl status gitea-runner

回到 Gitea 管理后台的 Runner 页面,你会看到你的 Runner 已经在线了 ✅


第三步:写你的第一个 Workflow

准备工作做完,来写真正的 CI/CD 流水线。

创建一个 Laravel 项目的自动测试工作流

假设你的 Gitea 上有一个 Laravel 项目仓库(比如聊天室项目 chatroom),你想在每次推送代码时自动跑测试。

在你的仓库根目录创建 .gitea/workflows/test.yml

name: Laravel Tests
on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 设置 PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: mbstring, pdo_mysql, xml, gd, redis

      - name: 复制环境配置
        run: cp .env.example .env

      - name: 安装 Composer 依赖
        run: composer install --no-interaction --prefer-dist

      - name: 生成应用密钥
        run: php artisan key:generate

      - name: 运行 PHPUnit 测试
        run: php artisan test

把这个文件推送到仓库:

git add .gitea/workflows/test.yml
git commit -m "添加 Gitea Actions 自动测试工作流"
git push

推送完成后,去 Gitea 仓库页面 → 「Actions」选项卡,你会看到一次新的运行正在执行。点进去可以看实时日志。

一个常见坑: Docker 环境下 composer install 可能会超时或内存不足。可以在 Runner 的 daemon 启动参数里加 --docker-network-mode host 来解决网络问题。


第四步:实战——自动部署到生产服务器

测试只是第一步,真正爽的是自动部署

我们来实现一个工作流:推送 v* 标签(比如 v1.0.1)时,自动部署到生产服务器

在你的仓库根目录创建 .gitea/workflows/deploy.yml

name: Deploy to Production
on:
  push:
    tags:
      - 'v*'

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: 检出代码
        uses: actions/checkout@v4

      - name: 设置 PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          extensions: mbstring, pdo_mysql, redis

      - name: 安装依赖(生产环境)
        run: |
          composer install --no-interaction --prefer-dist --no-dev
          npm ci --production

      - name: 编译前端资源
        run: npm run build

      - name: 打包
        run: |
          tar -czf deploy.tar.gz \
            --exclude='.git' \
            --exclude='node_modules' \
            --exclude='tests' \
            --exclude='storage/logs/*' \
            .

      - name: 部署到服务器
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          port: ${{ secrets.DEPLOY_PORT }}
          key: ${{ secrets.DEPLOY_SSH_KEY }}
          source: "deploy.tar.gz"
          target: "/tmp"

      - name: 远程执行部署脚本
        uses: appleboy/ssh-action@v1.0.3
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          port: ${{ secrets.DEPLOY_PORT }}
          key: ${{ secrets.DEPLOY_SSH_KEY }}
          script: |
            cd /www/wwwroot/chat.ay.lc
            # 备份当前版本
            cp -r . ../chat.ay.lc.bak
            # 解压新版本
            tar -xzf /tmp/deploy.tar.gz -C .
            # 执行部署后操作
            php artisan migrate --force
            php artisan optimize
            php artisan queue:restart
            # 清理
            rm /tmp/deploy.tar.gz
            rm -rf ../chat.ay.lc.bak

配置仓库 Secrets

上面的工作流用了 ${{ secrets.xxx }} 变量,你不能直接把服务器密码写在 YAML 文件里(不安全)。Gitea 提供了 Secrets 功能来安全存储敏感信息:

  1. 进入你的仓库 → 「设置」→ 「Actions」→ 「Secrets」
  2. 逐个添加以下 Secrets:
Secret 名称
DEPLOY_HOST你的服务器 IP(比如 8.130.123.25
DEPLOY_USERSSH 登录用户名(比如 root
DEPLOY_PORTSSH 端口(比如 8898
DEPLOY_SSH_KEY你的 SSH 私钥(cat ~/.ssh/id_rsa 的内容)

Secrets 存入后,任何人(包括你)在 Gitea 网页上都无法再查看明文,只能用在工作流里。密码学角度的原则是:密钥只在传输瞬间存在于内存中,永远不入日志。

触发部署

配置好后,推送一个标签:

# 打标签并推送
git tag v1.0.1
git push origin v1.0.1

Gitea Actions 会自动触发部署工作流,跑到 deploy 那一栏你就能看到传输进度和部署日志。


第五步:玩点更高级的

多步骤并行加速

如果一个工作流里有多个互不依赖的任务,可以并行跑:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: php artisan test --parallel

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: PHP 代码规范检查
        run: vendor/bin/pint --test

  # 测试和规范检查都通过后,再部署
  deploy:
    needs: [test, lint]
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/master'
    steps:
      - run: echo "可以部署了!"

发通知到飞书/钉钉/企业微信

工作流跑失败了,怎么第一时间知道?加个通知步骤:

- name: 发送飞书通知
  if: failure()
  uses: appleboy/telegram-action@v0.1.1
  with:
    # 这里换成你的 Webhook
    webhook: ${{ secrets.FEISHU_WEBHOOK }}
    message: |
      ❌ 部署失败!
      仓库:${{ github.repository }}
      分支:${{ github.ref }}
      提交:${{ github.sha }}
      查看详情:${{ github.server_url }}/${{ github.repository }}/actions

if: failure() 是关键——只有前置步骤失败时才发通知,不制造噪音。

定时任务(Cron)

Gitea Actions 也支持 Cron 定时触发:

on:
  schedule:
    # 每天早上 6 点备份数据库
    - cron: '0 6 * * *'

jobs:
  backup:
    runs-on: ubuntu-latest
    steps:
      - name: 备份数据库
        run: mysqldump -h ${{ secrets.DB_HOST }} -u ${{ secrets.DB_USER }} -p${{ secrets.DB_PASS }} myapp > backup.sql

      - name: 上传到备份空间
        uses: appleboy/scp-action@v0.1.7
        with:
          host: ${{ secrets.BACKUP_HOST }}
          username: ${{ secrets.BACKUP_USER }}
          key: ${{ secrets.BACKUP_KEY }}
          source: "backup.sql"
          target: "/backup/$(date +%Y%m%d)"

常见坑和解决方案

我在实际使用中踩过这些坑,列出来省你点时间:

Runner 连不上 Gitea

Failed to connect to gitea instance: dial tcp: connect: connection refused

原因:Runner 用 localhost 连 Gitea,但 Gitea 只监听 0.0.0.0:3000 的 Docker 网络。
解决:注册时用 http://git.你的域名:3000 代替 localhost,或者 --instance http://宿主机IP:3000

Docker 容器里 composer install 超时

The process '/usr/bin/composer' failed with exit code 5

原因:Runner 默认的 Docker 容器没装 PHP/Composer。
解决:更换镜像标签为包含 PHP 的镜像,或者用一个自定义 Docker 镜像:

runs-on: ubuntu-latest
container:
  image: serversideup/php:8.4-fpm-nginx

SCP 传文件到服务器失败

dial tcp 8.130.123.25:22: connect: connection refused

原因:你的 SSH 端口不是 22。
解决appleboy/scp-actionport 参数,明确指定你的 SSH 端口:

- uses: appleboy/scp-action@v0.1.7
  with:
    port: ${{ secrets.DEPLOY_PORT }}

工作流一直卡在 pending 状态

原因:Runner 离线了,或者没有匹配的标签。
检查

systemctl status gitea-runner

如果离线了,重启 Runner,去管理后台确认在线状态。


写在最后

有了 Gitea Actions,你的 Gitea 就从"私有的 GitHub"升级成了"私有的 GitHub + GitLab CI"。每次推代码都会自动跑测试、自动部署,再也不用 SSH 到服务器手动敲命令了。

回顾一下整篇文章:

  1. ✅ 启用 Gitea Actions
  2. ✅ 安装并注册 Runner
  3. ✅ 写 Laravel 自动测试工作流
  4. ✅ 实现标签推送自动部署
  5. ✅ 多步骤并行 + 失败通知 + 定时任务

从第一篇的"搭起来",到第二篇的"管好它",再到今天的"让它帮你干活"——你的 Gitea 已经成为一个完整的 DevOps 平台了。

下期预告:Gitea + Docker Registry 搭建私有镜像仓库,敬请期待~