Detached head in gitlab ci pipeline - how to push correctly
Asked Answered
V

4

18

I got some problems with a detached head for my repository in a CI pipeline. In the build stage of the pipeline I am running a script, which changes a specific file. After changing this file get pushed to the repository.

before_script:
  - git config --global user.name "Bot"
  - git config --global user.email "[email protected]"
  - git status
script:
  - npx ts-node ./generate.ts
  - git push "https://git:${GIT_PUSH_TOKEN}@${CI_REPOSITORY_URL#*@}" "HEAD:main"
  - git status

Running the script give me the output

Fetching changes with git depth set to 50...
Initialized empty Git repository in /builds/fKSu5-y_/0/project/.git/
Created fresh repository.
Checking out 9b4a88be as main...

$ git status
HEAD detached at 9b4a88be

$ git push "https://git:${GIT_PUSH_TOKEN}@${CI_REPOSITORY_URL#*@}" "HEAD:main"
To https://gitlab.domain.com/project.git
9b4a88b..98be83e  HEAD -> main

$ git status
HEAD detached from 9b4a88b

I do not understand why the first git status in the before_script already gives me a detached head.

I think the pipeline creates a detached repository by initial fetching. So the question is how to handle my push in a correct way. In my release stage I'm running semantic-release which fails because of detached head if I do the previous push. If I disable the push, semantic release is working as expected. But of course I need the push. I do not see, what I am doing wrong.


Update

Adding git checkout main in the before_script give me for the first git status the expected result. But after the push command I still have the detached head - which I do not understand.

$ git checkout main
Branch 'main' set up to track remote branch 'main' from 'origin'.
Switched to a new branch 'main'

$ git status
On branch main
Your branch is up to date with 'origin/main'.

$ git push "https://git:${GIT_PUSH_TOKEN}@${CI_REPOSITORY_URL#*@}" "HEAD:main"
To https://gitlab.domain.com/project.git
  336b065..8299e43  HEAD -> main

$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

Although everything the push is working on the release state semantic-release is still not working. I do get: The local branch main is behind the remote one, therefore a new version won't be published.

Volturno answered 21/9, 2021 at 10:4 Comment(4)
"after the push command I still have the detached head" - your updated output doesn't show that, it shows "on branch main". It shows you as "ahead of origin/main by 1 commit", but that's a different issue (it's probably because you pushed to an explicit URL rather than the "origin" remote)Hildegardehildesheim
@Hildegardehildesheim So I should set the remote origin instead of using the repo url?Volturno
The message is just informational. It is not saying that you are on a detached head, or that the push failed. It's just comparing the current branch with the local cache of "origin/main". Pushing to "origin" rather than the URL would probably update that cache, as would running git fetch origin, but to re-emphasise nothing has failed.Hildegardehildesheim
#46472750 #51716544 #40123280 as for I think the pipeline creates a detached repository by initial fetching see docs.gitlab.com/ee/ci/runners/…Rhaetic
L
17

I do not understand why the first git status in the before_script already gives me a detached head.

I think the pipeline creates a detached repository by initial fetching.

You are right, GitLab CI does checkout a specific commit as opposed to a certain branch. You could however add git checkout "$CI_COMMIT_REF_NAME" to your before_script:

before_script:
  - git config --global user.name "Bot"
  - git config --global user.email "[email protected]"
  - git checkout "$CI_COMMIT_REF_NAME"
  - git status
Lakia answered 21/9, 2021 at 11:27 Comment(1)
I tried that just a few seconds before your answer. I've updated the post.Volturno
E
6

I spend a lot of time to try to resolve the same problem and finally I found that there are git strategy: https://docs.gitlab.com/ee/ci/runners/configure_runners.html#git-strategy

When there is set fetch then it re-uses the local working copy (and falls back to clone if it doesn’t exist). So this is the reason why it worked me for the first time and then it stopped working and my checkouted branch was behind and number of commits ahead increased with another commit.

Then the solution is pretty easy. You just need to define the GIT_STRATEGY variable to clone.

variables:
  GIT_STRATEGY: clone

And my working publish job example:

publish:
  stage: publish
  rules:
    - if: $CI_COMMIT_BRANCH == "master"
      when: manual
  variables:
    GIT_STRATEGY: clone
  script:
    - git checkout -b $CI_COMMIT_BRANCH
    - echo "define what you want to change"
    - 'git commit -am "your commit message" || echo "No changes to commit"'
    - git push --set-upstream origin $CI_COMMIT_BRANCH

You can also define clone strategy in global variables or pipeline configuration https://docs.gitlab.com/ee/ci/pipelines/settings.html#choose-the-default-git-strategy.

Eerie answered 26/4, 2022 at 23:13 Comment(2)
git checkout -b creates a new branch, why did you need the -b flag?Constitution
The advantage of using git checkout -b is that it ensures you stay on the same commit - so if the server branch has moved on, this should be detected when you try to push. Otherwise, if you did git checkout $CI_COMMIT_BRANCH and it had moved on, your local code would no longer represent the commit you thought it was running on, but the new commit at the head of $CI_COMMIT_BRANCHMalloch
A
0

This might help someone. Based on gitlab.

The detached state is actually intended, as the runner is specifically designed to checkout the repository using a specific commit (the one that triggered the pipeline). When a specific commit is checked out, the repository is then considered to be in a "detached HEAD" state.

Here is an example from my pipeline that after changing some files, it pushes them to master (change master to main in new repos).

git remote set-url origin http://$user:[email protected]/<group>/<group>/$service.git
git add <file that changed>
git config --global user.email "<user email>"
git config --global user.name "<user name>"
git commit -m "Updated service $service"
git push -o ci.skip origin HEAD:master

-o ci.skip - is to stop running another pipeline after push to repo.

In gitlab you can do below to select the default branch from gitlab variables:

git push -o ci.skip origin HEAD:$CI_DEFAULT_BRANCH

To show all variables add CI_DEBUG_TRACE: "true" to variables section.

Apportion answered 6/12, 2021 at 15:58 Comment(0)
M
0

A workaround solution:

git switch $CI_COMMIT_REF_NAME
git pull
git reset --hard $CI_COMMIT_SHA

Make sure you reset back to your current commit. Or you may run your latest branch changes on an old commit pipeline.

Mouthy answered 6/5 at 15:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.