How do I push to a repo from within a gitlab CI pipeline?

Some finger push-ups still required, but here's a less brittle way of pushing to the repository from its own CI, that I use in my daily work. It pushes to master directly from a detached head:

enter image description here

  1. Generate an RSA key and add it as a Project Deploy Key with write access (the public part).
  2. Put the private part into your CI/CD variables from inside of your project settings as SSH_PUSH_KEY. Make sure to set it as protected.
  3. Add a CI_KNOWN_HOSTS variable, with the SSH fingerprint of your GitLab instance (remember that thing ssh asks you about the first time you try to connect to a host? That.).

Use ssh-keyscan <gitlab-host> to get it. It will look similar to this:

my.gitlab.instance.com ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEArlUMUmNj59PpoLyy4EsKbwhPUfXxuAzFN7dMKDXVvKMmN8344HqQV1tRx6fcmH+0BXK1JAP4f10V0VnYti3e1c5f9dhpl8pIqKLMJgdGDq3MLqjihL3bp5xm8nDsPTm5FoEPPYK1I3M2wr18pBB19evz64NHrK5R/HO5LyTrybVasFumt8cZoH6crnCFgfQWV1mHAG3j41Q0z4yxu6g8zBWESZcVVn90HxQH7+LDHx11122233344491MQGl5fZcKqVWsWQVEssaK87iBsWUxvsuoeVUrj4YRcmbi6F4+ZZZZZZZwwww3ZboWsSWxTk5ESR6WWHccBm8GQflXyY3ZQ==
  1. Set up your job inside .gitlab-ci.yml as follows. Set stage and resource_group options appropriately - without the latter you might run into race conditions. Also, make sure to set only properly, as otherwise your CI might trigger itself:
"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 "[email protected]"
        - 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

You can add the CI_SERVER_CLS_CA_FILE to sslCAInfo git config.

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's approach, in the comments, to export GIT_SSL_CAINFO instead, is a bit shorter.

export GIT_SSL_CAINFO=$CI_SERVER_TLS_CA_FILE

Gitlab creates CI_SERVER_TLS_CA_FILE and configures git to use it for initially cloning the repository. For some reason this configuration is no longer available later on.


I found this GitLab forum link helpful As suggested by the user you need to generate SSH key, associate it with new GitLab user dedicated for this job and add key to the runner. Small drawback is you need to use swap origin in gitlab for original ssh source (instead of sandboxed one used inside the job) which leads to committer being changed to mentioned new account instead of person who triggered pipeline. Source from link:

# 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 "[email protected]"
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

Just with current version of GitLab you need to change source variable name as follows:

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