Git push from inside a gitlab-runner

It seems like something most people would want to do. You have something in your pipeline changing the project and then you want to push. However whenever I attempt to do a git push from my gitlab runner I get a 403 permission denied. Most things I find on the internet say it is because of it attempting to use the CI token. I have the SSH keys on the machine the runner is running on.

How do people do pushes from a gitlab runner?

Use a git credential helper to push to the repo. Take a look at the the “badge” stage of the following gitlab-ci.yaml for an example:

We did this by adding a before_script task where we install SSH & Git and set the Git credentials.

The var SSH_PRIVATE_KEY_TOOLKIT in the example below is a Deploy Key generated in Gitlab by going to Settings > Repository > Deploy Keys, Make sure to enable write access by editing the deploy key after enabling it.

 before_script:
    - apt-get update -y && apt-get install -yqqf openssh-client git unzip sshpass rsync --fix-missing
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client git -y )'
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY_TOOLKIT" | tr -d '\r' | ssh-add - > /dev/null

    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh

    - ssh-keyscan $GITLAB_URL >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts

    - git config --global user.email "our@email.com"
    - git config --global user.name "Gitlab Runner"

Then in your script you can do this:

  script:
    # Pull repo
    - echo "Pulling external repo into build"
    - ssh git@$GITLAB_URL
    - git clone git@$GITLAB_URL:$EXTERNAL_REPO_URL filelocation/we/expect/the/files
    
    # Do something after pulling your repo
    
    # Push repo changes into current repo
    - git remote rm origin && git remote add origin "git@$GITLAB_URL:${CI_PROJECT_PATH}.git"
    - git add filelocation/we/expect/the/files another/location/can/be/defined
    - git commit -m "Commit message here :)" || echo "No changes, nothing to commit!"
    - git push origin HEAD:$CI_COMMIT_REF_NAME

Hope this helps you out.

2 Likes

My solution wasn’t as complicated, maybe because I am relying on the gitlab-runner machine always having the correct ssh keys set up. I have posted below for anyone else

 script:
- 'standard-version --releaseCommitMessageFormat "chore(release): {{currentTag}}"'
- git remote show origin
- git remote set-url --push origin git@gitlab:$CI_PROJECT_PATH
- git remote show origin
- git push --follow-tags origin HEAD:$CI_COMMIT_REF_NAME
- npm publish

This changes the remote taking out the whole CI token stuff

1 Like

In case others are wondering, I found a nice guide to set this up with shared GitLab runners and compiled it into another answer here.

In short:

deploy:
  stage: deploy
  before_script:
    - 'which ssh-agent || ( apt-get update -qy && apt-get install openssh-client -qqy )'
    - eval `ssh-agent -s`
    - echo "${SSH_PRIVATE_KEY}" | tr -d '\r' | ssh-add - > /dev/null # add ssh ke
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo "$SSH_PUBLIC_KEY" >> ~/.ssh/id_rsa.pub
    - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
  script:
    - git config --global user.email "${CI_EMAIL}"
    - git config --global user.name "${CI_USERNAME}"
    - git add -f *.pdf # Force add PDF since we .gitignored it
    - git commit -m "Compiled PDF from $CI_COMMIT_SHORT_SHA [skip ci]" || echo "No changes, nothing to commit!"
    - git remote rm origin && git remote add origin git@gitlab.com:$CI_PROJECT_PATH.git
    - git push origin HEAD:$CI_COMMIT_REF_NAME # Pushes to the same branch as the trigger
1 Like