CI项目用到submodule问题解决

最近在自己尝试写一个FastAPI的项目, 其中用到了Git的Submodules功能和Gitlab的CI功能, 这两个组合就出现了一些坑, 记录一下

Gitlab-CI & Git submodule的组合

项目目录结构

项目结构

其中utils下的两个package为其他项目, 并添加到了主项目的submodule里面, 本地构建dockerimage不会报错, 使用CI就会报错

.gitlab-ci.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# This file is a template, and might need editing before it works on your project.
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Docker.gitlab-ci.yml

# Build a Docker image with CI/CD and push to the GitLab registry.
# Docker-in-Docker documentation: https://docs.gitlab.com/ee/ci/docker/using_docker_build.html
#
# This template uses one generic job with conditional builds
# for the default branch and all other (MR) branches.

docker-build:
# Use the official docker image.
image: docker:latest
stage: build
services:
- docker:dind
# Default branch leaves tag empty (= latest tag)
# All other branches are tagged with the escaped branch name (commit ref slug)
script:
- |
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
tag=""
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'"
else
tag=":$CI_COMMIT_REF_SLUG"
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
fi
- docker "$CI_BUILD_ARGS" build --pull -t "$CI_REGISTRY_IMAGE${tag}" .
- docker "$CI_BUILD_ARGS" push "$CI_REGISTRY_IMAGE${tag}"
# Run this job in a branch where a Dockerfile exists
rules:
- if: $CI_COMMIT_BRANCH
exists:
- Dockerfile

👆🏻是我从官方提取的.gitlab-ci.yml文件

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM python:3.9-slim
MAINTAINER Rex Chow <879582094@qq.com>

ENV TZ='Asia/Shanghai'
ENV TIME_OUT=20
ENV LOG_LEVEL='info'
ENV CF_ZONE='example.com'

COPY app /app
WORKDIR /app
EXPOSE 80

RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple --no-cache-dir -r /app/requirements.txt && \
rm -rf ~/.cache/pip && \
mv /app/utils/weather_forcast/SimHei.ttf /usr/local/lib/python3.9/site-packages/matplotlib/mpl-data/fonts/ttf/
CMD ["sh", "-c", "uvicorn main:app --host 0.0.0.0 --port 80 --log-level $LOG_LEVEL"]

问题复现

push以后, CI流水线提示报错

没有文件

定位问题

这个是在我submodule里的一个字体文件, 不应该没有的, 猜测可能由于submodule没拉取导致, 启动一个历史容器查看

submodule未拉取

果然submodule没有拉取, 文件夹都是空的

解决过程

查了CSDN1的博客, 发现问题原因, 需要配置ci设置, 修改CI文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# This file is a template, and might need editing before it works on your project.
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Docker.gitlab-ci.yml

# Build a Docker image with CI/CD and push to the GitLab registry.
# Docker-in-Docker documentation: https://docs.gitlab.com/ee/ci/docker/using_docker_build.html
#
# This template uses one generic job with conditional builds
# for the default branch and all other (MR) branches.

docker-build:
# Use the official docker image.
image: docker:latest
stage: build
# 这里添加submodule的策略
variables:
GIT_SUBMODULE_STRATEGY: normal
services:
- docker:dind
# Default branch leaves tag empty (= latest tag)
# All other branches are tagged with the escaped branch name (commit ref slug)
script:
- |
if [[ "$CI_COMMIT_BRANCH" == "$CI_DEFAULT_BRANCH" ]]; then
tag=""
echo "Running on default branch '$CI_DEFAULT_BRANCH': tag = 'latest'"
else
tag=":$CI_COMMIT_REF_SLUG"
echo "Running on branch '$CI_COMMIT_BRANCH': tag = $tag"
fi
- docker "$CI_BUILD_ARGS" build --pull -t "$CI_REGISTRY_IMAGE${tag}" .
- docker "$CI_BUILD_ARGS" push "$CI_REGISTRY_IMAGE${tag}"
# Run this job in a branch where a Dockerfile exists
rules:
- if: $CI_COMMIT_BRANCH
exists:
- Dockerfile

再次执行CI流水线, 仍然提示问题

缺少SSH支持

查阅了Gitlab runner项目的issues23, 发现需要配置runner的pre_clone_script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
concurrent = 1
check_interval = 0

[session_server]
session_timeout = 1800

[[runners]]
name = "Global_Shared_Runner"
url = "******"
token = "*******"
executor = "docker"
# 这里增加一行
pre_clone_script = "sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && apk add openssh-client"
[runners.custom_build_dir]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
tls_verify = false
image = "apline"
privileged = false
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache"]
shm_size = 0

👆🏻是我的runner的config.toml, 增加配置后, 再次执行, 提示权限问题

缺少权限

此时参考第一次的博文4, 增加Deploy key并修改.gitmodules, 但是我的版本是CE的, 只能使用Project Access Token, 参考官方手册5可以分别给各个submodule创建一个

创建Project Access Token

权限设置为Reporter即可, 详细权限可参见: Gitlab permission and role cheat sheet | Rex Chow's Blog

查了很多博客文档6, 大体都说通过Project Access Token进行clone操作语句是git clone https://oauth2:<access_token>@gitlab.com/.../xxx.git, 没有一个说明白为什么username是oauth2?

搜索了一下官方文档7, 发现了端倪. Gitlab是支持oauth2认证方式和Personal/project/group access tokens方式, 需要通过以下两种方法实现:

  1. 传递parameter的方式(全部适用)

    1
    curl "https://gitlab.example.com/api/v4/projects?private_token=<your_access_token>"

  2. 添加到请求头headers(全部适用)

    1
    curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects"

    或使用OAuth兼容类构造请求头

    1
    curl --header "Authorization: Bearer <your_access_token>" "https://gitlab.example.com/api/v4/projects"

因此, 需要修改原来的.gitmodules文件中项目的地址, 从SSH改为HTTP(S)才能使用, 故此上面修改runner的配置就可以不必修改了.

修改submodule的URL

总结

使用了submodule的git, 想通过Gitlab CI进行自动构建, 需要按照以下步骤操作才能实现:

  1. 确认.gitlab-ci.yml相关build的step包含submodule策略

    1
    2
    variables:
    GIT_SUBMODULE_STRATEGY: normal # 或者 recursive

  2. 为每个submodule创建一个Project Access Token, 并修改.gitmodule文件中submodule的URL为以下格式

    http(s)://oauth2:<project_access_token>@gitlab.com/.../xxx.git

  3. 重试流水线, 此时应该可以顺利部署


  1. Gitlab CI 拉取 submodules_麦田里的守望者-Jiang的博客-CSDN博客_git submodule 拉取↩︎

  2. Docker: error cannot run ssh: No such file or directory (#2075) · Issues · GitLab.org / gitlab-runner · GitLab↩︎

  3. How to use pre_clone_script in config.toml? (#2352) · Issues · GitLab.org / gitlab-runner · GitLab↩︎

  4. Gitlab CI 拉取 submodules_麦田里的守望者-Jiang的博客-CSDN博客_git submodule 拉取↩︎

  5. Project access tokens | GitLab↩︎

  6. Gitlab使用Access Token来clone项目_StefanTimber的博客-CSDN博客↩︎

  7. API Docs | GitLab↩︎