Skip to content

Invalid version breaks registry for composer

Summary

Composer 2 cannot consume packages published with a version .x-dev suffix.

Note: according to comments, this also applies to composer 1.

Steps to reproduce

  1. make a branch called 1.5-composer-test
  2. publish this branch into the composer registry

What is the current bug behavior?

This will create a package with version 1.5-composer-test.x-dev.

Trying to install any version of this package from this registry with composer 2 now results in:

  [UnexpectedValueException]                         
  Invalid version string "1.5-composer-test.x-dev"  

What is the expected correct behavior?

I can use packages from this registry.

Workarounds

  1. manually remove all invalid versions from your registry
  2. to prevent creating invalid versions in the future, constrain publishing packages to valid versions:
publish:
  stage: deploy
  dependencies: []
  rules:
    - if: '$CI_COMMIT_TAG =~ /^v?\d+([.]\d+){2}(-(patch|p|alpha|a|beta|b|RC)\d*)?$/'
      variables:
        DATA: 'tag=$CI_COMMIT_TAG'
    - if: $CI_COMMIT_TAG
      when: never
    - if: '$CI_COMMIT_REF_NAME =~ /^(v?(\d+)([.](\d+|[xX*]))*|v?\D.*)$/'
      variables:
        DATA: 'branch=$CI_COMMIT_REF_NAME'
  cache: {}
  before_script: []
  variables:
    # yamllint disable-line rule:line-length
    URL: '$CI_SERVER_PROTOCOL://$CI_SERVER_HOST:$CI_SERVER_PORT/api/v4/projects/$CI_PROJECT_ID/packages/composer?job_token=$CI_JOB_TOKEN'
  script:
    - insecure=$([ "$CI_SERVER_PROTOCOL" = "http" ] && echo "--insecure" || echo "")
    - response=$(curl -s -w "\n%{http_code}" $insecure --data "$DATA" $URL)
    - code=$(echo "$response" | tail -n 1)
    - body=$(echo "$response" | head -n 1)
    # Output state information
    - if [ $code -eq 201 ]; then
      echo "Package created - Code $code - $body";
      else
      echo "Could not create package - Code $code - $body";
      exit 1;
      fi

Possible fixes

See the documentation:

When branch names look like versions, we have to clarify for composer that we're trying to check out a branch and not a tag. In the above example, we have two version branches: v1 and v2. To get Composer to check out one of these branches, you must specify a version constraint that looks like this: v1.x-dev. The .x is an arbitrary string that Composer requires to tell it that we're talking about the v1 branch and not a v1 tag (alternatively, you can name the branch v1.x instead of v1). In the case of a branch with a version-like name (v1, in this case), you append -dev as a suffix, rather than using dev- as a prefix.

IFAIU, this is saying that only branches that exactly look like versions should be treated with a -dev suffix. So, in this case the -composer-test part rules that branch name out of this case and hence it should be treated with a dev- prefix instead.

See https://github.com/composer/semver/blob/dd61cb4efbd0cff1700b217faf24ce596af4fc4e/src/VersionParser.php#L168-L170

// a branch ending with -dev is only valid if it is numeric
// if it gets prefixed with dev- it means the branch name should
// have had a dev- prefix already when passed to normalize
Edited by Claudio Bley