Skip to content

Unexpected failure with version command #1046

Description

@clarkecheckr

Question

I need some help troubleshooting an issue I'm having. Versioning is working great up until the point where psr tries to create the version tag.

  • The merge request runs the pipeline successfully but does not create a version - good
  • The merge to main runs the pipeline where the only changes are the commits - good
  • The pipeline is run for the version creation and pyproject.toml/GHANGELOG.md are modified - good
  • Version tag is created and the pipeline run and fails. - bad

image

The failure just says ERROR: Job failed: exit code 1 with no other clue as to what's wrong.

image

How do I further troubleshoot this?

Configuration

Semantic Release Configuration
[tool.semantic_release]
version_variables = ["pyproject.toml:version"]

[tool.semantic_release.branches.main]
match = "(main|master)"
prerelease = false

[tool.semantic_release.commit_author]
env = "GIT_COMMIT_AUTHOR"
default = "semantic-release <semantic-release>"

[tool.semantic_release.remote]
type = "gitlab"

Activity

  1. codejedi365 commented on Oct 1, 2024

    @codejedi365
    Contributor

    Hi @clarkecheckr, the primary issue is actually right there on line 142. You are in a detached head state. Because of this, we cannot determine which release branch configuration to use and therefore we give up (not ideal, I know but the best I have at the moment).

    GitLab uses a detached head state in the way they checkout a small git tree to make it faster to execute the pipelines as most do not need the entire git tree. PSR does need the full git tree as well as being checked out to a specific branch.

    My recommendation is you should run the following:

    # make sure you have the full accurate history
    git fetch --unshallow --prune-tags --prune --all --force
    
    # make sure the branch Head is at the commit that was tested/started the workflow
    git checkout -B $CI_COMMIT_BRANCH HEAD
    
    semantic-release -vv version
  2. clarkecheckr commented on Oct 1, 2024

    @clarkecheckr
    Author

    Thanks for the prompt response. I didn't think the detached head state was the issue as I get a similar message in my branch yet there's no error.

    image

    I'll try these changes and report back.

  3. clarkecheckr commented on Oct 1, 2024

    @clarkecheckr
    Author

    I'm working on a repo that is just for getting this working in Gitlab and have an absolute minimal configuration for psr. I'd like to contribute my working .gitlab-ci.yml and pyproject.toml configuration to be used as an example in the docs as they would have helped me out immensely. How would I go about that?

  4. codejedi365 commented on Oct 1, 2024

    @codejedi365
    Contributor

    I'm not sure how it isn't a problem in your branch environment as a detached head state is an immediate abort for semantic release

  5. clarkecheckr commented on Oct 1, 2024

    @clarkecheckr
    Author

    It's not a detached head, it's just the same message that the branch isn't in any release groups.

  6. codejedi365 commented on Oct 1, 2024

    @codejedi365
    Contributor

    Well that message is at the exact same part of the code, it is also an immediate abort. It succeeds though because it is not in --strict mode.

  7. codejedi365 commented on Oct 1, 2024

    @codejedi365
    Contributor

    I'm working on a repo that is just for getting this working in Gitlab and have an absolute minimal configuration for psr. I'd like to contribute my working .gitlab-ci.yml and pyproject.toml configuration to be used as an example in the docs as they would have helped me out immensely. How would I go about that?

    Generally for a GitHub contribution, you would fork the repository, create a branch on your fork, make your changes and then open a PR from your fork's branch to this repo's master branch. The docs are in the docs folder. Review the Contributors.rst as well for info about how to build the docs and general info.

    However, I have a GitLab instruction guide in the works (70% complete) but I haven't published it yet. Ultimately I will have an examples section in that area like I do for github-actions (docs/automatic-releases/github-actions.rst).

    If you want to just put your examples in a gitlab.rst in that folder and explain the rationale, then I will fold yours into my format.

  8. clarkecheckr commented on Oct 1, 2024

    @clarkecheckr
    Author

    BTW, git checkout -B $CI_COMMIT_BRANCH fails with the error fatal: 'HEAD' is not a valid branch name. I get the idea and I'm looking up a way to do it.

  9. clarkecheckr commented on Oct 1, 2024

    @clarkecheckr
    Author

    Also, it only fails for the tag build.

    image

    image

  10. clarkecheckr commented on Oct 1, 2024

    @clarkecheckr
    Author

    The reason this is happening on the tag creation is that the value of $CI_COMMIT_BRANCH is "", so the command reads git checkout -B HEAD and that's not valid. I tried to add a rule to the .gitlab-ci.yml, but then my pipeline didn't run at all, so I have a little work to do to figure this out.

  11. clarkecheckr commented on Oct 1, 2024

    @clarkecheckr
    Author

    Adding a rule to skip the pipeline if $CI_COMMIT_BRANCH is null kinda worked, but now I'm not creating a tag. I thought maybe it would create the tag, just not run the pipeline but that's not the case.

    image

    stages:
      - release
    
    variables:
      GITLAB_TOKEN: $gitlab_pat
    
    release:
        stage: release
        image: python:latest
        before_script:
          - pip install python-semantic-release
        script:
          - git fetch --unshallow --prune-tags --prune --all --force
          - git checkout -B $CI_COMMIT_BRANCH HEAD
          - semantic-release -v version
        rules:
          - if: $CI_COMMIT_BRANCH != null
    
  12. clarkecheckr commented on Oct 1, 2024

    @clarkecheckr
    Author

    I was able to get this working with the following config. The reason it was failing when using git checkout -B $CI_COMMIT_BRANCH HEAD was because when we get to the tag creation step the value of $CI_COMMIT_BRANCH is null. The following config checks to see if it's null, if it is then we set the variable to $CI_COMMIT_REF_NAME otherwise we use $CI_COMMIT_BRANCH. This seems to work but I will be testing it more to confirm. Can you see anything wrong with this method?

    stages:
      - release
    
    variables:
      GITLAB_TOKEN: $gitlab_pat
    
    release:
        stage: release
        image: python:latest
        before_script:
          - pip install python-semantic-release
        script:
          - git fetch --unshallow --prune-tags --prune --all --force
          - echo "CI_COMMIT_REF_NAME" $CI_COMMIT_REF_NAME
          - git checkout -B $SOURCE_BRANCH HEAD
          - semantic-release -v version
        rules:
          # if the commit branch is not null, use the commit branch name
          - if: $CI_COMMIT_BRANCH != null
            variables:
              SOURCE_BRANCH: $CI_COMMIT_BRANCH
          # if the commit branch is null, use the commit ref name
          # this is to account for the fact that the version tag has a null value for $CI_COMMIT_BRANCH
          - if: $CI_COMMIT_BRANCH == null
            variables:
              SOURCE_BRANCH: $CI_COMMIT_REF_NAME
    
  13. codejedi365 commented on Oct 2, 2024

    @codejedi365
    Contributor

    @clarkecheckr, sorry I was unavailable while you worked the issue.

    There isn't anything particularly wrong with your approach but I can't say it would be my way I would solve the problem. I will try to provide some conceptual rationale behind my approach first. In my solution, I utilize workflow:rules to restrict the types of pipelines that occur. I generally recommend to never run semantic-release on a tag triggered pipeline. This defeats the purpose of using semantic-release. The idea behind PSR and conventional commit strategies is to eliminate the emotional attachment to release versions, which crudely means no human makes a tag anymore--PSR handles that based on what you documented that changed. Secondly, I think its a bit of a waste of resources to spin up another pipeline which has no comprehension of what was accomplished prior to the tagged release and rather just set a deploy stage (set of deploy jobs, many times there is more than one deployment target) that is dependent on release job outcomes.

    Also note, that I generally use GitHub Flow branching strategy coupled with a Squash commit merge strategy and in my projects, I rarely create pre-releases (more of a result of my environment, but small amount of preference). Lastly, I haven't done too much of multi-version release support (ie v2 in maintenance, v3 is stable, v4 is unstable) so my example below doesn't handle this.

    So in my workflows, I run when there is a (1) push to the default branch, (2) a merge request event, (3) a pipeline source that is manual or deliberately triggered. Regardless of the workflow trigger, I run a check stage that simultaneously runs a build, code linting, commit-linting, unit testing. Then I move into a test stage which focuses on runtime (system, integration, etc) testing. If this is a MR/PR, then the pipeline ends. If it is the push to the default branch (ie my release branch), then release job & deploy jobs are enabled depending on if I either set it to manually be triggered or allow auto-delivery/deployment. The release job is using semantic-release to determine if a release is required and then the deployment jobs depend on the result of that job.

    Python Semantic Release Configuration
    [tool.semantic_release.branches.release]
    match = "^(main|master)$"
    prerelease = false
    
    [tool.semantic_release.branches.other]
    # Used for enabling build jobs during PRs where a pre-release version is created on any
    # branch name but not published (ie. internal use from workflow artifacts)
    match = ".*"
    prerelease_token = "dev"
    prerelease = true
    
    [tool.semantic_release.remote]
    type = "gitlab"
    ignore_token_for_push = true
    token = { 'env': "GITLAB_TOKEN" }
    GitLab Workflow
    workflow:
      rules:
        - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
        - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
        - if: '$CI_PIPELINE_SOURCE =~ "/api|web|schedule|web|trigger|pipeline/"'
        - when: never
    
    
    stages:
      - .pre
      - check
      - test
      - release
      - deploy
    
    
    commit-lint:
      stage: check
    
    
    build:
      stage: check
      needs: 
        - job: commit-lint
      script: 
        - if git rev-parse --is-shallow-repository | grep -q "true"; then
             git fetch --unshallow --prune-tags --prune --all --force;
          fi
        - git checkout -B "$CI_COMMIT_BRANCH" HEAD
        - semantic-release version --no-commit --no-tag 
      artifacts:
        paths:
          - dist/*.whl
          - dist/*.tar.gz
        expire_in: 1 week
    
    
    test:
      stage: test
    
    
    release:
      stage: release
      rules:
        - if: '$CI_DEFAULT_BRANCH == $CI_COMMIT_BRANCH'
          when: manual       # <--- leave this out if you want auto-deployment
      allow_failure: false      # must have when it is set to manual otherwise GitLab will move on to deploy
      environment:
        name: repository       # custom environment that holds the PAT (and only available to this job)
      script:
        - if git rev-parse --is-shallow-repository | grep -q "true"; then
             git fetch --unshallow --prune-tags --prune --all --force;
          fi
        - git checkout -B "$CI_COMMIT_BRANCH" HEAD
        
        # set the remote url to the PAT (write repository access)
        - git remote set-url origin "https://__token__:$GITLAB_TOKEN@${CI_REPOSITORY_URL#*@}"
        
        # This provides an exit code of whether or not to release, on exitcode 0 it runs the release,
        # on non-zero it writes a status to a file that is passed on to the next job as an environment variable
        - if semantic-release -vv --strict version --print; then
             semantic-release version --skip-build;
          else
             printf '%s\n' "PSR_RELEASED=false" >> deploy.env;
          fi
      artifacts:
        reports:
          dotenv: deploy.env
    
    
    deploy-gl-pypi:
      stage: deploy
      needs:
        - job: release
          artifacts: true
        - job: build
          artifacts: true
      rules:
        - if: '$CI_DEFAULT_BRANCH == $CI_COMMIT_BRANCH'
      variables:
        TWINE_USERNAME: gitlab-ci-token
        TWINE_PASSWORD: $CI_JOB_TOKEN
      script:
        - if [ "PSR_RELEASED" = "true" ]; then
            twine upload --repository-url "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi" dist/*;
          fi

    This pipeline handles the different version situations without the pipeline jobs failing (on a no release) because technically they did work as intended and there is no need for human intervention or review. OR you can just have the deploy fail when the version has not been bumped. It's a matter of preference.

    One additional nuance that I do is actually run semantic-release in the build job but in a non-persistent way because I can gain the nicety to only have to have 1 place where the build command is defined. Secondly when building the artifact (versions stamped and everything) at the beginning of the pipeline I can now actually test the exact (hashed) item that is going to be released. Generally, using semantic-release to build at the end actually builds a new artifact (hashes are different) that wasn't tested--many people wave this risk. Since I have previously built and tested the built distribution, I just import the artifact from the build job during deployment while the release job skips the build_command with --skip-build

  14. 9 remaining items

  15. codejedi365 commented on Oct 7, 2024

    @codejedi365
    Contributor

    @daniel-albuschat, I haven't gotten around to that but I did have it as an action item one of these days.

    There is a way to check if the repo is already unshallowed and should be inluded as a bash conditional before the execution but I can't remember what it is at the moment. Will check later.

  16. codejedi365 commented on Oct 8, 2024

    @codejedi365
    Contributor

    I use a bash conditional around the --unshallow command because of the failure that @daniel-albuschat mentioned above. Happens with small repositories under 50 commits (50 is the default fetch depth for GitLab) or if someone has already increased the fetch depth elsewhere.

    if git rev-parse --is-shallow-repository | grep -q "true"; then
        git fetch --unshallow --prune-tags --prune --all --force
    fi

    @clarkecheckr, I recommend updating your workflow to handle this situation that I didn't include in my above example. (I did update the example above)

  17. github-actions commented on Oct 23, 2024

    @github-actions

    This issue has not received a response in 14 days. If no response is received in 7 days, it will be closed. We look forward to hearing from you.

  18. github-actions commented on Oct 31, 2024

    @github-actions

    This issue was closed because no response was received.

  19. cfxegbert commented on Jan 3, 2025

    @cfxegbert
    Contributor

    Sorry about commenting on a closed issue. There are mechanisms in GitLab to clone the whole repository instead of doing a shallow clone. You can do

    release:
      stage: release
      variables:
        GIT_STRATEGY: clone
        GIT_DEPTH: "0"
    ...

    instead of

    release:
      stage: release
      script:
        - if git rev-parse --is-shallow-repository | grep -q "true"; then
             git fetch --unshallow --prune-tags --prune --all --force;
          fi
    ...

    See:

  20. codejedi365 commented on Jan 3, 2025

    @codejedi365
    Contributor

    @cfxegbert, it's a good thing to note for others. Do you know which version of GitLab that was introduced? I feel like it was pretty recent.

    Also can you validate that it actually prunes or clones all the tags if the build directory is cached? That is a problem I had with GitLab was that deleted tags do not get cleaned up from a repo without --prune-tags.

  21. cfxegbert commented on Jan 3, 2025

    @cfxegbert
    Contributor

    The GIT_ variables have been available for several years. I've been using it for over 3 years. The --prune-tags may be able to be set with GIT_FETCH_EXTRA_FLAGS but I'm not sure if the flags are applied to clones or only fetch. Would you need --prune-tag on a clone?

    From the docs:
    clone is the slowest option. It clones the repository from scratch for every job, ensuring that the local working copy is always pristine. If an existing worktree is found, it is removed before cloning.

    So it will not use the cached worktree like fetch does

  22. cfxegbert commented on Jan 3, 2025

    @cfxegbert
    Contributor

    Looking at the source
    https://gitlab.com/gitlab-org/gitlab-runner/-/blob/main/shells/abstract.go#L535-540
    https://gitlab.com/gitlab-org/gitlab-runner/-/blob/main/shells/abstract.go#L629-639

    to replicate your setup would be:

    variables:
      GIT_STRATEGY: fetch
      GIT_DEPTH: "0"
      GIT_FETCH_EXTRA_FLAGS: --prune-tags --prune --all --force

    With the clone strategy since it removes the directory I don't think you need to prune.

  23. codejedi365 commented on Jan 3, 2025

    @codejedi365
    Contributor

    The GIT_ variables have been available for several years. I've been using it for over 3 years.

    I haven't tried to solve this problem in over 3 years so that's probably why it seems recent.

    Would you need --prune-tag on a clone?

    I doubt it because clone would fail if the directory exists.

    And looks like you found another solution for my env, why thanks!

  24. cfxegbert commented on Jan 3, 2025

    @cfxegbert
    Contributor

    I haven't tried to solve this problem in over 3 years so that's probably why it seems recent.

    FYI GIT_STRATEGY and GIT_DEPTH were added in v1.3.0 (2016). GIT_FETCH_EXTRA_FLAGS was added in v13.1 (2021).

  25. codejedi365 commented on Jan 3, 2025

    @codejedi365
    Contributor

    Well there's a million ways to solve everything and I chose git rather than finding it in GitLab Docs.

  26. hhaidrr commented on Mar 6, 2025

    @hhaidrr

    Thank you all for your insights.

    This issue thread along with the comments in #666 allowed me to setup my Gitlab pipeline in a short-ish amount of time.

    The perfect addition for others now would be to add all this very helpful information as an official page in the docs automatic-releases.

  27. codejedi365 commented on Mar 6, 2025

    @codejedi365
    Contributor

    That is the premise of #666.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions