3、搭建Git服务器:GitLab安装与配置、仓库权限管理、Webhook与CI触发机制

好,咱们进入第三章。这一章要解决一个实际问题:嵌入式团队的代码该放哪儿

你可能用过Gitee、GitHub,甚至公司自建的Gerrit。但在嵌入式环境里,我个人的经验是——GitLab是最稳妥的选择。为什么?因为它能私有化部署,权限控制细,而且CI/CD集成得最自然。我在几个项目里都踩过坑,后面会一一说到。

3.1 GitLab的安装与配置

先说说安装。GitLab的部署方式有好几种,我推荐用Docker。原因很简单:嵌入式团队的服务器环境往往很杂,有Ubuntu、CentOS,甚至还有跑在Windows上的WSL。Docker能屏蔽这些差异。

这是我常用的docker-compose.yml配置:

version: '3.8'
services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    restart: always
    hostname: 'gitlab.example.com'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://gitlab.example.com'
        gitlab_rails['gitlab_shell_ssh_port'] = 2222
    ports:
      - '80:80'
      - '443:443'
      - '2222:22'
    volumes:
      - './config:/etc/gitlab'
      - './logs:/var/log/gitlab'
      - './data:/var/opt/gitlab'

嗯,这里有个坑要注意。嵌入式团队经常用SSH协议拉代码,但GitLab默认的SSH端口是22。如果你的服务器上已经跑了别的SSH服务(比如OpenSSH),端口会冲突。我建议把GitLab的SSH端口映射到2222,然后在客户端的~/.ssh/config里配一下:

Host gitlab.example.com
  Port 2222

启动后,第一次访问会要求设置root密码。我个人习惯立刻创建一个普通用户,别用root干日常活。安全第一。

小提示:嵌入式团队经常有多个项目共用同一个MCU SDK的情况。我建议在GitLab里建一个Group(比如叫embedded-sdk),然后把所有SDK相关的仓库都放进去。这样权限管理会清晰很多。

3.2 仓库权限管理

权限管理这块,说实话,很多嵌入式团队做得不够细。我见过最夸张的情况是——整个团队用一个共享账号。谁提交了代码?不知道。谁改了关键配置?查不到。

GitLab的权限模型分五个级别:Guest、Reporter、Developer、Maintainer、Owner。对于嵌入式项目,我建议这样分配:

角色 权限级别 典型人员
只读成员 Reporter 测试工程师、硬件工程师
开发成员 Developer 嵌入式软件工程师
核心维护者 Maintainer 技术负责人、架构师
管理员 Owner 团队Leader、DevOps

你可能会问:为什么测试只给Reporter?因为嵌入式测试经常需要拉取固件进行验证,但不应该让他们直接修改代码。我曾经遇到过测试同学不小心push了测试脚本到master分支,结果CI直接触发了一次错误的构建……嗯,从那以后我就严格限制了权限。

另外,分支保护是必须的。在GitLab的项目设置里,找到「Protected Branches」,把master/main分支保护起来:

  • 只有Maintainer才能合并
  • 不允许直接push
  • 必须通过Merge Request
注意:嵌入式项目经常有release分支(比如release/v1.2.3)。我建议也把这些分支保护起来。否则,有人不小心在release分支上改了代码,整个发布流程就乱了。

3.3 Webhook与CI触发机制

好,到了最核心的部分——怎么让GitLab和你的CI系统联动

Webhook说白了就是一个回调。当GitLab里发生某些事件(比如push、merge、tag)时,它会向一个URL发送HTTP请求。你的CI系统收到这个请求后,就知道「哦,有代码更新了,该干活了」。

我常用的Webhook配置是这样的:

# 在GitLab项目设置 -> Webhooks 里添加
URL: http://your-ci-server:8080/webhook
Secret Token: your-secret-token
Trigger:
  - Push events
  - Merge request events
  - Tag push events

这里有个细节:Secret Token一定要配。我见过有人图省事不配,结果被外部恶意触发CI,白白浪费了构建资源。嵌入式编译一次可能十几分钟,要是被刷几百次……你想想看,服务器直接卡死。

对于嵌入式项目,我建议的触发策略是:

  • Push事件:触发单元测试和静态检查(快,几分钟搞定)
  • Merge Request事件:触发完整编译+烧录测试(慢,但必须做)
  • Tag事件:触发发布流程(生成固件、打包、上传)

为什么这样分?因为嵌入式编译太慢了。如果每次push都做全量编译,开发效率会很低。我之前的团队就是吃了这个亏——每次push等20分钟编译,大家怨声载道。后来改成push只做语法检查,MR才做全量编译,效率提升了一倍。

最后,贴一个简单的Webhook接收端示例(Python Flask):

from flask import Flask, request, jsonify
import hmac
import hashlib

app = Flask(__name__)
SECRET_TOKEN = b'your-secret-token'

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # 验证签名
    signature = request.headers.get('X-Gitlab-Token')
    if signature != SECRET_TOKEN:
        return jsonify({'error': 'Unauthorized'}), 401

    # 解析事件类型
    event = request.headers.get('X-Gitlab-Event')
    payload = request.json

    if event == 'Push Hook':
        # 触发轻量级CI任务
        trigger_lightweight_ci(payload)
    elif event == 'Merge Request Hook':
        # 触发全量CI任务
        trigger_full_ci(payload)
    elif event == 'Tag Push Hook':
        # 触发发布流程
        trigger_release(payload)

    return jsonify({'status': 'ok'}), 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)
核心要点:
  • GitLab用Docker部署最省心,注意SSH端口冲突
  • 权限分级:测试给Reporter,开发给Developer,核心给Maintainer
  • 分支保护是底线,release分支也要保护
  • Webhook一定要配Secret Token,防止恶意触发
  • 根据事件类型区分CI任务,别让push做全量编译

好了,这一章就到这里。下一章我们会讲CI Runner的部署——说白了就是谁来干活的问题。嵌入式环境里,Runner的配置比Web端复杂得多,到时候我会分享一些实战中的血泪教训。