如何在GitLab CI流水线中从本地推送到代码仓库?

79

在我的CI流水线中,我生成了一个名为public/graph.png的构件,它可以可视化我的代码的某些方面。在稍后的步骤中,我希望能够在CI流水线内将其提交到仓库中。以下是.gitlab-ci.yml文件的相关部分:

commit-graph:
  stage: pages
  script:
    - git config user.email "cipipeline@example.com"
    - git config user.name "CI Pipeline"
    - cd /group/project
    - mv public/graph.png .
    - git add graph.png
    - git commit -m "committing graph.png [ci skip]"
    - echo $CI_COMMIT_REF_NAME
    - git push origin HEAD:$CI_COMMIT_REF_NAME
当管道在GitLab内运行时,它会失败并出现以下错误:

$ git config user.email "cipipeline@dhgitlab.dunnhumby.co.uk"
$ git config user.name "CI Pipeline"
$ cd /group/project
$ mv public/graph.png .
$ git add graph.png
$ git commit -m "committing graph.png [ci skip]"
[detached HEAD 22a50d1] committing graph.png [ci skip]
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 graph.png
$ echo $CI_COMMIT_REF_NAME
jamiet/my-branch
$ git push origin HEAD:$CI_COMMIT_REF_NAME
fatal: unable to access 'https://gitlab-ci-token:xxxxxxxxxxxxxxxxxxxx@example.com/group/project/project.git/': server certificate verification failed. CAfile: /etc/ssl/certs/ca-certificates.crt CRLfile: none

我不确定我做错了什么,也不太懂SSL的知识,有人能提供建议吗?
我们自己托管GitLab。
12个回答

71

现在有一种更干净的方法来解决这个问题,而不是使用 SSH,而是使用项目范围访问令牌,也可以查看这个答案

在 GitLab 项目中创建一个项目范围访问令牌,以便它与项目相关联,而不是与个人相关联。接下来将此令牌存储为 GitLab CI/CD 变量。您现在可以使用以下内容连接:

push-back-to-remote:
  script:
    - git config user.email "my-email@email.com"
    - git config user.name "ci-bot"
    - git remote add gitlab_origin https://oauth2:$ACCESS_TOKEN@gitlab.com/path-to-project.git
    - git add .
    - git commit -m "push back from pipeline"
    - git push gitlab_origin HEAD:main -o ci.skip # prevent triggering pipeline again

12
听起来是最好的选择,不过项目访问令牌仅适用于高级或更高级别的用户。 - kidroca
9
最佳解决方案!项目访问令牌在自托管实例上可用。 但是请注意:从GitLab 16开始,它们将需要一个过期日期! - StefanTo
4
注意:项目访问令牌在自托管实例的所有层级上都可用。 - debuglevel
2
如果您的git推送由于启用了“存储库设置>推送规则>拒绝未经验证的用户”而被拒绝,请注意每个项目访问令牌都会隐式创建一个“机器人用户”,该用户还带有代表已验证用户的电子邮件地址。因此,您可以通过将git的“user.email”配置设置为机器人的电子邮件地址来解决问题-根据GitLab文档中的模式描述 - Philipp Rieber
1
这是个非常好的答案,谢谢 :) - Anjana Silva
显示剩余3条评论

23

简述

如果你只是想在代码合并到 main 分支时进行版本发布或版本号提升的提交,那么可能已经有现成的 CLI 工具可以更加简单易懂地使用。
(例如使用 gitlab 的 api)

如何操作

必要步骤:
commitingJob:
  stage: some_stage
  script:
    # in order to commit we have to have a user set
    # this would also make it easy to distinct the CI-made commits
    - git config user.name "CI Pipeline"
    - git config user.email "cipipeline@example.com"
    # stage some changes
    - git add src/*
    # We can use `-o ci.skip`, but AFAIK it doesn't work for Merge Request pipelines, while having it inside the commit message works there as well
    - git commit -m "A commit message [skip ci]"
    # we're on a detached head but we can push the commits we made to the remote branch like so:
    - git push HEAD:$CI_COMMIT_REF_NAME

这里缺少的一件事是与远程进行身份验证。可以通过多种方式设置(ssh、token、api)。其他答案讨论了几种不同的选项。我更喜欢使用ACCESS_TOKEN,下面将看到如何以及为什么。

可用身份验证选项的总结和评论

在CI步骤中添加或复制ssh密钥

与仅使用访问令牌相比,采用ssh方式似乎需要付出太多的努力才能获得相同的结果。

  • 生成ssh密钥
  • 将公钥保存为项目部署令牌
  • 将私钥保存为项目CI变量
  • 专门用于CI步骤
    • 添加私钥
    • 添加已知主机
    • 添加/更新ssh远程

总体而言,这似乎是你为用户做的事情,而不是为管道做的事情。

您必须将私钥添加为项目级CI变量-由于模式限制,该变量无法屏蔽。它可以添加为文件,但然后您必须进行chmod以获取适当的权限...

我在这里看到的积极因素是ssh密钥仅限于您将其作为部署令牌添加到的项目。


最简单的选项似乎是使用ACCESS_TOKEN

使用令牌推送提交与更新push远程一样容易:

git remote set-url --push origin "https://$TOKEN_NAME:$ACCESS_TOKEN@gitlab.com/<project>.git"

TOKEN_NAME - 在GitLab中为令牌设置的名称(最好选择没有空格字符的名称)。 ACCESS_TOKEN - 这可以是个人访问令牌(免费版)项目访问令牌(高级版)

它必须存储为项目级CI变量,并且可以被掩盖。 我只需要保存一个包含<token_name>:<access_token>内容的变量,如CI_COMMITTER_USER_AND_TOKEN

⚠ 有关个人访问令牌的警告

这是给CI Runner的太广泛的权限,即使Runner应该提交到多个存储库,我也会更喜欢每个项目单独使用访问令牌,只授予所需的最低权限

请记住,如果有人获得了您的个人访问令牌,则可以代表您执行操作(在本例中进行提交)。授予write_repository权限意味着您可以使用令牌写入任何您帐户可以访问的存储库。

CI_JOB_TOKEN

如果您想知道是否可以使用CI_JOB_TOKEN提交推送 - 您不能。您只能使用它进行pull
(您也可以使用它来发布软件包和镜像,但无法提交推送)

为什么使用git remote set-url --push origin

我们可以为CI生成的提交添加单独的远程,但请记住它可能会被缓存,因此如果我们只是这样做

script: 
  - git remote add ci "https://$TOKEN_NAME:$ACCESS_TOKEN@gitlab.com/<project>.git"

下次运行管道时,我们将从git remote add得到错误,因为我们尝试添加的远程已经添加了。 对现有的origin使用set-url可以确保您使用最新的url。 要使用git remote add实现相同的效果,我们需要先尝试删除ci远程,然后再添加回来。
script: 
  - git remote remove ci || true
  - git remote add ci "https://$TOKEN_NAME:$ACCESS_TOKEN@gitlab.com/<project>.git"

使用GitLab的API推送更改
这与ACCESS_TOKEN的方式基本相同,但令牌具有api权限而不是write_repository。
如果在CI作为发布新版本合并到main的一部分进行提交和推送,则使用CLI工具和此类指南可能比设置提交和推送自己更好:https://docs.gitlab.com/ee/ci/examples/semantic-release.html 上述指南设置了版本增量、包更新和发布说明。请注意,此步骤指示我们创建一个带有API权限的Access Token,以便底层的CLIs提交更改:https://docs.gitlab.com/ee/ci/examples/semantic-release.html#set-up-cicd-variables

22

需要进行一些手指俯卧撑,但这是我在日常工作中使用的从CI推送到仓库的一种较不脆弱的方法。它会直接从一个分离的头推送到主分支:

enter image description here

  1. 生成一个RSA密钥并将其添加为项目部署密钥(公共部分需具有写入权限)。
  2. 将私钥放入项目设置中的CI / CD变量中,命名为SSH_PUSH_KEY。确保将其设置为受保护状态。
  3. 添加一个CI_KNOWN_HOSTS变量,其中包含您的GitLab实例的SSH指纹(记住第一次尝试连接到主机时ssh要求您提供的信息?是那个)。

使用ssh-keyscan <gitlab-host>获取它。它看起来类似于以下内容:

my.gitlab.instance.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArlUMUmNj59PpoLyy4EsKbwhPUfXxuAzFN7dMKDXVvKMmN8344HqQV1tRx6fcmH+0BXK1JAP4f10V0VnYti3e1c5f9dhpl8pIqKLMJgdGDq3MLqjihL3bp5xm8nDsPTm5FoEPPYK1I3M2wr18pBB19evz64NHrK5R/HO5LyTrybVasFumt8cZoH6crnCFgfQWV1mHAG3j41Q0z4yxu6g8zBWESZcVVn90HxQH7+LDHx11122233344491MQGl5fZcKqVWsWQVEssaK87iBsWUxvsuoeVUrj4YRcmbi6F4+ZZZZZZZwwww3ZboWsSWxTk5ESR6WWHccBm8GQflXyY3ZQ==
  1. 按如下步骤在.gitlab-ci.yml文件中设置您的工作。适当设置stageresource_group选项-如果没有后者,可能会遇到竞争条件。还要确保正确设置only,否则您的CI可能会触发自身:
"This CI job pushes to its own repo":
    stage: my_push_stage
    resource_group: this_option_comes_handy_when_pushing
    only:
        - triggers
    before_script:
        - mkdir ~/.ssh/
        - echo "${CI_KNOWN_HOSTS}" > ~/.ssh/known_hosts
        - echo "${SSH_PUSH_KEY}" > ~/.ssh/id_rsa
        - chmod 600 ~/.ssh/id_rsa
        - git config user.email "ci@example.com"
        - git config user.name "CI"
        - git remote remove ssh_origin || true  # Local repo state may be cached
        - git remote add ssh_origin "git@$CI_SERVER_HOST:$CI_PROJECT_PATH.git"
    script:
        - touch "xyz"  # Make an edit
        - git add "xyz"
        - git commit -m "My CI commit"
        - git push ssh_origin HEAD:master  # ❗ this pushes to master, 
                                           # use $CI_COMMIT_REF_NAME if you want to push to current branch
        - git tag MyCiTag  # If you need to add a tag you can do that too
        - git push --tags ssh_origin

1
你可以使用以下命令:ssh-keyscan $CI_SERVER_HOST > ~/.ssh/known_hosts而不是一个变量。 - Tom Chiverton
2
@TomChiverton 这样做虽然有效,但基本上等同于设置GIT_SSH_COMMAND="ssh -o StrictHostKeyChecking=no" - Błażej Michalik
部署密钥无法写入存储库。因此,这个解决方案不再起作用。 - M.K. aka Grisu
2
@M.K.akaGrisu,你把部署的“密钥”和“令牌”搞混了。请参考https://docs.gitlab.com/ee/user/project/deploy_keys/。“可读写的部署密钥可以从存储库中读取和写入”。 - Błażej Michalik

11
您可以将CI_SERVER_CLS_CA_FILE添加到sslCAInfo Git配置中。
checkout alchemy:
    stage: prepare
    script:
        - git config --global "http.${CI_SERVER_URL}.sslCAInfo" "$CI_SERVER_TLS_CA_FILE"
        - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@${CI_SERVER_HOST}/sparklemuffin/alchemy.git

@Sjoerd在评论中提出了将GIT_SSL_CAINFO导出的方法,这种方法更为简短。

export GIT_SSL_CAINFO=$CI_SERVER_TLS_CA_FILE

Gitlab创建CI_SERVER_TLS_CA_FILE并配置git在最初克隆存储库时使用它。由于某种原因,此配置稍后不再可用。


2
我使用了类似的解决方案:我在脚本顶部放置了 export GIT_SSL_CAINFO="$CI_SERVER_TLS_CA_FILE"。如果你把它放在 variables 下面,它是不起作用的 - Sjoerd
...脚本的第二行可以简化为 git clone ${CI_REPOSITORY_URL}... - Trentium
非常优雅,但不幸的是无法用于推送到受保护分支。 - Martin D

11

我发现这个GitLab论坛链接很有用。如该用户所建议的,您需要生成SSH密钥,将其与专门为此工作而创建的新GitLab用户相关联,并将密钥添加到runner中。小缺点是您需要在gitlab中使用swap origin以获取原始的ssh源(而不是在作业内部使用的沙箱源),这会导致提交者更改为提到的新帐户,而不是触发管道的人。

# for your information
whoami
printenv

# we need to extract the ssh/git URL as the runner uses a tokenized URL
export CI_PUSH_REPO=`echo $CI_REPOSITORY_URL | perl -pe 's#.*@(.+?(\:\d+)?)/#git@\1:#'`

# runner runs on a detached HEAD, create a temporary local branch for editing
git checkout -b ci_processing
git config --global user.name "My Runner"
git config --global user.email "runner@gitlab.example.org"
git remote set-url --push origin "${CI_PUSH_REPO}"

# make your changes
touch test.txt

# push changes
# always return true so that the build does not fail if there are no changes
git push origin ci_processing:${CI_COMMIT_REF_NAME} || true

只需要使用当前版本的 GitLab,你就需要按照以下方式更改源变量名称:

export CI_PUSH_REPO=`echo $CI_REPOSITORY_URL | perl -pe 's#.*@(.+?(\:\d+)?)/#git@\1:#'`

我使用 git checkout -b "${CI_COMMIT_REF_NAME}" 命令创建了一个与远程分支同名的分支。 - Axel Guilmin

3

不需要声明CI_KNOWN_HOST,你可以尝试实时获取它:

- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- ssh-keyscan -t rsa $CI_SERVER_HOST >> ~/.ssh/known_hosts

3

问题已解决。在推送之前发出git config --global http.sslverify "false"命令解决了这个特定的问题(它暴露了另一个问题,但那是另一个主题 :))


34
好问题,但是对这个答案打-1,因为它鼓励避免问题而不是修复问题。现在有很好的方法可以获得免费的SSL证书,没有理由完全禁用它而不是修复SSL问题。 - Caleb

1

我可以通过Gitlab-CI以选定的用户提交,基于tsr的答案进行了小改动 https://dev59.com/-FUK5IYBdhLWcg3whwKJ#57800614

# set remote URL to https://oauth2:<AccessToken>@server.com/project.git
CI_PUSH_REPO=`echo "$CI_REPOSITORY_URL $ACCESS_TOKEN_PARAM" | sed 's/^.*\(@.*\)\s\(.*\)/https:\/\/oauth2:\2\1/g'`
git config http.sslverify false
git remote set-url --push origin "${CI_PUSH_REPO}"
git config user.name "Token Owner"
git config user.email "tokenowner@email.com"

# runner runs on a detached HEAD, create a temporary local branch for editing
git checkout -b ci_processing
# make your changes

# push changes
# always return true so that the build does not fail if there are no changes
git push origin ci_processing:${CI_COMMIT_REF_NAME} || true
ACCESS_TOKEN_PARAM 必须在项目的 CI/CD 变量配置中进行配置。
使用 Oauth2 和访问令牌的想法来自于 https://dev59.com/mVgQ5IYBdhLWcg3w4n7z#52074198https://dev59.com/k18e5IYBdhLWcg3wwMhW#52154378
此外,推送更改可能会触发新的流水线!

0

对我来说,这些都没有立即起作用,但我将它们全部合并在一起,得出了这个结果:

- git config user.email "example@example.com"
- git config user.name "example"
- git remote remove dev || true
- git remote add dev https://example:$PROJECT_ACCESS_TOKEN@gitlab.com/demo/versioning-test.git
- git remote set-url --push origin "https://example:$PROJECT_ACCESS_TOKEN@gitlab.com/demo/versioning-test.git"
- git pull origin dev
- git checkout dev 
- git reset --hard origin/dev
- git rm build_number.json || true
#grabs the build number from tags and pipes it into text file json formatted
- echo "`echo {'"build_number"':` `git describe --tags` `echo }`" | sed -e 's/\s\+/"/g' >> build_number.json
- git add build_number.json
- git commit -m "push back from pipeline [skip ci]"
- git push origin dev 

我的使用情况是我们想要将一个版本文件提升,并将其推送回仓库。

0

这是今天的一个工作示例 环境:k8s gitlab:13.x gitlab runner:13.x 此作业的目标名为convert:将Excel文件转换为JSON文件,并提交并更新存储库的分支。

convert:
  variables:
    REPO: "gitlab.com/group/myproject.git" # example
    BRANCH: "BRANCHNAME" # example
    # recommended to store the following as project variables to hide secrets away from the gitlab ci file.
    GITLAB_USER_ID: "gitlab_user" # example
    CI_USERNAME: "gitlab_user" # example
    CI_PUSH_TOKEN: "<api token from gitlab" # example
    GITLAB_USER_EMAIL: "gitlab_user@company.com" # example
  stage: convert
  image:
    name: python:3.7-buster
    entrypoint: ["/bin/ash"]
  before_script:
    - pip3 install openpyxl
    - ls -altr
  script:
    - echo 'converting excel to json'
    - python excel2json.py
    - git remote set-url origin https://${CI_USERNAME}:${CI_PUSH_TOKEN}@$REPO
    - git config --global user.email '${GITLAB_USER_EMAIL}'
    - git config --global user.name '${GITLAB_USER_ID}'
    - git add -A && git commit -m 'added/updated json files'
    - git push origin HEAD:$BRANCH

注意:CI_USERNAME == GITLAB_USER_ID。在我的情况下,两者是相同的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接