absolute-version/absolute-version-js

The first character is stripped from the git commit hash

agross opened this issue Β· 9 comments

$ git log --oneline -1
d4ec867 (HEAD -> pact, origin/pact) ...

$ npx absolute-version
4ec867-pact+4ec867

4ec867 != d4ec867

$ git log --oneline -1
e6864fd (HEAD -> main, origin/main) ...

$ npx absolute-version
6864fd-main+6864fd

6864fd != e6864fd

I think the culprit is this test

The hash will never start with a g and it seems that the first character is always stripped from the predefined git-describe "hash".

I haven't looked at how git-describe (the package) works, but git describe (the command) returns this when HEAD does not point to a tagged commit:

$ git describe --always
e6864fd

Thanks for these two reports! I really appreciate the thorough investigation.

Looks like this line is the culprit. Probably it should strip it only if present - what do you think?

I didn't realise that git describe doesn't always append the g - it's not obviously defined in the documentation, but it can be kind of inferred - it says --always says that it falls back to the uniquely abbreviated commit object. Good catch.

I can fix this up mid next week, or if a fix is urgent a PR would be welcome - I don't think the change is substantial.

Aside, but using absolute-version without tags will only give you half the value 😎

Probably it should strip it only if present - what do you think?

The g is only included in the output of git describe if it finds an annotated tag on an ancestor commit. Then the format is <tag>-<distance>-g<SHA of the HEAD commit>.

But since this obviously isn’t a SHA, I (wrongly) assumed git-describe (the package) will never return this as the sha property.

This is the result of git-describe (the package) if an annotated tag points to HEAD:

{
  "dirty": false,
  "raw": "v1.0.0-0-g9b1c539",
  "hash": "g9b1c539",
  "distance": 0,
  "tag": "v1.0.0",
  "semver": {
    "options": {
      "loose": false,
      "includePrerelease": false
    },
    "loose": false,
    "raw": "v1.0.0",
    "major": 1,
    "minor": 0,
    "patch": 0,
    "prerelease": [],
    "build": [],
    "version": "1.0.0"
  },
  "suffix": "0-g9b1c539",
  "semverString": "1.0.0"
}

...and when the annotated tag points to an ancestor of HEAD:

{
  "dirty": false,
  "raw": "v1.0.0-3-g9b1c539",
  "hash": "g9b1c539",
  "distance": 3,
  "tag": "v1.0.0",
  "semver": {
    "options": {
      "loose": false,
      "includePrerelease": false
    },
    "loose": false,
    "raw": "v1.0.0",
    "major": 1,
    "minor": 0,
    "patch": 0,
    "prerelease": [],
    "build": [],
    "version": "1.0.0"
  },
  "suffix": "3-g9b1c539",
  "semverString": "1.0.0+3.g9b1c539"
}

And when there is no annotated tag:

{
  "dirty": false,
  "raw": "9b1c539",
  "hash": "9b1c539",
  "distance": null,
  "tag": null,
  "semver": null,
  "suffix": "9b1c539",
  "semverString": null
}

So, yes, removing the g if it's the first character should be the way to go.

We're in agreement - I think I cross-posted on the other issue saying essentially the same thing.

I'm heading to bed here in Australia - but if you're able to raise a PR I'd be happy to release it first thing in the morning. Otherwise if you don't mind waiting, I'll get to this one next week.

Thanks again for the report!

Aside, but using absolute-version without tags will only give you half the value

Yes, I get that. Since my project is pretty new, there are no annotated tags yet.

What I'm trying to do is find a convenient way to map participant versions to docker image tags. As suggested by the docs, I use SHAs for Pacticipant versions in the Pact Broker. But I wouldn't want to tell my customers to docker pull my-app:<40 char git hash>

I'm leaning towards using the output of absolute-version for image tags which turn into annotated tag values if there is one. I also store the image tag as a Pacticipant tag, so when I check whether I can deploy Pacticipant version <40 char git hash> and the answer is yes I can glean the corresponding image from the custom tag (e.g. 1.0.0 or b1c539-main+b1c539 attached to that participant version.

2023-03-16_16-23-49

Perhaps you know of a better way to achieve that, I'm open for suggestions!

As suggested by the docs, I use SHAs for Pacticipant versions in the Pact Broker

Ah! So, I think the Pact Broker docs say you should include the SHA, not necessarily that it should just be the SHA.

Personally, I straight up use the absolute-version output for can-i-deploy - I originally wrote it to use for broker versions. I wanted to include it as the default in Pact, but (quite rightly) it was pointed out that not everyone uses git tags to manage their version numbers.

I'm leaning towards using the output of absolute-version for image tags

This is (one of) the intended use cases - the idea was that you can use absolute-version for all releases (internal and external), and then only publish the git-tagged releases to customers (ie, 1.0.0, 1.0.1 etc, which absolute-version will give version numbers with no additional commit metadata).

However, you will (unfortunately) not be able to use the annotated tag for docker tags, as docker tags are not fully semver compatible(!):

only [A-Za-z0-9_.-] are allowed

You'll have to process the output and remove the +.

This is a common enough use-case that perhaps I should add a --formatDockerTag option (I've also considered adding --strict, which would refuse to generate a number if no tag or the working tree is dirty). I haven't added options, as it kind of defeats the purpose of having one clear format. But, perhaps these two wouldn't be a departure from that philosophy.

not necessarily that it should just be the SHA.

Yes, I could probably use absolute-version instead. As long as tags are protected it'll be as safe as the SHA.

One issue that I'll have with this system though is when a specific provider version needs to be verified. I use the Pact Broker to trigger such verifications on (GitHub) CI, and I need to know which provider revision needs to be verified. The simplest answer to this problem is using the revision/commit hash.

name: Verify pact

on:
  repository_dispatch:
    types:
      - verify-pact

env:
  # This is provided by the Pact Broker webhook.
  GIT_SHA: ${{ github.event.client_payload.pact.providerVersionNumber }}
  GIT_REF_NAME: ${{ github.event.client_payload.pact.providerVersionBranch }}
  PACT_URL: ${{ github.event.client_payload.pact.url }}
# ...

jobs:
  pact-verification:
    name: ${{ github.event.client_payload.message }}

      - uses: actions/checkout@v3
        with:
          # Verify specific provider version, falls back to the empty string
          # (== the default branch) if unset.
          ref: ${{ env.GIT_REF_NAME }}

      # Pretend env.GIT_REF_NAME is at env.GIT_SHA.
      - run: git reset --hard ${{ env.GIT_SHA }}

      # Run the provider verification with PACT_URL makes sure it pulls the correct contract revision from the broker.

In this case I would somehow translate the absolute-version back to the git commit hash. (Without the leading character this becomes a tiny bit harder πŸ˜ƒ)

then only publish the git-tagged releases to customers

Exactly πŸ‘

However, you will (unfortunately) not be able to use the annotated tag for docker tags

Nothing a shell substitution can't solve 😎

I had a spare moment, so I've fixed this. Releasing 1.0.2 now.

Many thanks! πŸ‘πŸ»