metadata-action icon indicating copy to clipboard operation
metadata-action copied to clipboard

Enable Flag in Raw Tag does not work as expected

Open baxterjo opened this issue 1 year ago • 9 comments

Contributing guidelines

I've found a bug, and:

  • [x] The documentation does not mention anything about my problem
  • [x] There are no open or closed issues that are related to my problem

Description

When using a raw tag with the enable flag, the tag is still in the metadata output when enable is set to false.

Expected behaviour

Run docker/metadata-action@v5
Context info
Processing images input
Processing tags input
  type=semver,pattern={{raw}},value=v0.0.1+test,enable=true,priority=900
  type=raw,value=latest,enable=false,priority=200
Processing flavor input
Docker image version
Docker tags
  ghcr.io/{org}/{image}:v0.0.1-test
Docker labels
Annotations
JSON output
Bake file definition (tags)
Bake file definition (labels)
Bake file definition (annotations)

Actual behaviour

Run docker/metadata-action@v5
Context info
Processing images input
Processing tags input
  type=semver,pattern={{raw}},value=v0.0.1+test,enable=true,priority=900
  type=raw,value=latest,enable=false,priority=200
Processing flavor input
Docker image version
Docker tags
  ghcr.io/{org}/{image}:v0.0.1-test
  ghcr.io/{org}/{image}:latest
Docker labels
Annotations
JSON output
Bake file definition (tags)
Bake file definition (labels)
Bake file definition (annotations)

Repository URL

No response

Workflow run URL

No response

YAML workflow

- name: Docker Meta
  id: meta
  uses: docker/metadata-action@v5
  with:
    images: ${{ needs.set-workflow-vars.outputs.image-name }}
    tags: |
      type=semver,pattern={{raw}},value=${{ needs.set-workflow-vars.outputs.version }}
      type=raw,value=latest,enable=${{ inputs.is-latest }} # This input is typed as a boolean in the callable workflow, maybe that's it?

Workflow logs

Run docker/metadata-action@v5
Context info
Processing images input
Processing tags input
  type=semver,pattern={{raw}},value=v0.0.1+test,enable=true,priority=900
  type=raw,value=latest,enable=false,priority=200
Processing flavor input
Docker image version
Docker tags
  ghcr.io/{org}/{image}:v0.0.1-test
  ghcr.io/{org}/{image}:latest
Docker labels
Annotations
JSON output
Bake file definition (tags)
Bake file definition (labels)
Bake file definition (annotations)

BuildKit logs

No response

Additional info

No response

baxterjo avatar Oct 07 '24 21:10 baxterjo

type=raw,value=latest,enable=${{ inputs.is-latest }}

Do you repro as well with type=raw,value=latest,enable=false? If not then that might be related to how GitHub handles boolean inputs.

crazy-max avatar Dec 09 '24 11:12 crazy-max

type=raw,value=latest,enable=${{ inputs.is-latest }}

Do you repro as well with type=raw,value=latest,enable=false? If not then that might be related to how GitHub handles boolean inputs.

Yea that question is in the comment of the line where the bug originates from. It is a GHA typed boolean for sure. The peculiar thing is the fix I ended up going with.

Intsead of

- name: Docker Meta
  id: meta
  uses: docker/metadata-action@v5
  with:
    images: ${{ needs.set-workflow-vars.outputs.image-name }}
    tags: |
      type=semver,pattern={{raw}},value=${{ needs.set-workflow-vars.outputs.version }}
      type=raw,value=latest,enable=${{ inputs.is-latest }} # This input is typed as a boolean in the callable workflow, maybe that's it?

I ended up using the flavor input. Which is documented the same as tags as far as booleans go:

- name: Docker Meta
   id: meta
   uses: docker/metadata-action@v5
   with:
     images: ${{ steps.set-vars.outputs.image-name }}
     flavor: |
       latest=${{ inputs.is-latest }}
     tags: |
       type=semver,pattern={{raw}},value=${{ inputs.version }}

baxterjo avatar Dec 09 '24 19:12 baxterjo

Looks like this happened because you previously had the auto flavor paired with your semver type tag trigger:

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/meta.ts#L171-L186

At the start latest is initially false, and when the semver tag is considered a release tag it'll set that to true. Finally at the end of that snippet it'll check the latest flavor, which auto will use that latest=true that was set in the prior condition, otherwise it'll go based on the latest flavor set.

That's why your workaround to configure instead with flavor.latest worked 😎

It's a subtle bug that the README should probably highlight better, enable=false skips the tag processing, but for the latest tag itself you have to keep the default flavor behaviour in mind.


Additional context

Earlier in the process that semver type was processed here:

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/meta.ts#L46-L61

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/meta.ts#L66-L69

So the raw tag was in fact skipped due to your enabled=false, the caveat is latest was implicitly added regardless due to flavor.latest=auto + semver, which due to priority is processed first (even if priority was adjusted though, the enabled=false would skip setting version.latest=false, so you'd still get version.latest=true set once the semver tag is processed).

Tags are iterated through an array based on their priority:

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/tag.ts#L43-L52

If the raw tag had a higher priority (and was enabled) it'd have precedence at setting the latest tag to false:

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/meta.ts#L314-L317

latest is decided by the first tag to set it when it's in an undefined state:

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/meta.ts#L333

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/meta.ts#L343-L345


Comparison in source (not relevant)

latest:

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/flavor.ts#L4-L5

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/flavor.ts#L37-L42


enable:

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/tag.ts#L88-L99

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/tag.ts#L1

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/tag.ts#L203-L205

https://github.com/docker/metadata-action/blob/8e1d5461f02b7886d3c1a774bfbd873650445aa2/src/meta.ts#L53-L60

polarathene avatar Feb 25 '25 06:02 polarathene

It's a subtle bug that the README should probably highlight better

I think a louder warning or an action failure when processing special case tags would better. Something that points to a corresponding readme section.

baxterjo avatar Feb 26 '25 18:02 baxterjo

Just to clarify, you specifically mean with type=raw,value=latest,enable=false caveat with the default flavor.latest=auto?

I think there's other tag rules that could run into that latest tag issue depending on the setup, but the logic is a tad messy to sift through 😓 This is the most common combination I've seen though where the mishap happens.

polarathene avatar Feb 26 '25 19:02 polarathene

Yea I think any tags that can be listed in the tags section whose behavior can be influenced by another setting should come with a warning if there are conflicting settings. I think just a quick lint or sanity check before attempting to run the action would be sufficient. I realize teasing out the edge cases could be tough, but the amount of time saved for devs would be worth it IMO. The iteration loop for CI jobs is loooooong, so being able to tighten up the feedback pays off in dividends.

baxterjo avatar Feb 26 '25 21:02 baxterjo

I'm ultimately apathetic about it though as I rarely work on CI these days. Feel free to close this if you don't want to use it as a tracking issue.

baxterjo avatar Feb 26 '25 21:02 baxterjo

I'm short on time, so here's a rough outline for anyone landing here, that may want to contribute a fix to the README:

  • type=raw could mention a caveat with value=latest.
  • The latest tag section references the default flavor.latest=auto behaviour.
    • That section then suggests using a type=raw tag with enable for controlling a conditional latest tag, without clarifying that the flavor.latest=auto default will disregard/override that enable logic.
    • Additionally if flavor.latest=true and a type=raw,value=latest,enable=true tag are both present, the tags output will produce two latest tags (even when there is no suffix/prefix difference involved).
  • flavor setting describes the onlatest condition for flavor.<prefix|suffix>:
    • It is not clarified that onlatest attribute is only applicable to an implicit latest tag generated from flavor.latest=<auto|true> (when auto resolves to true). onlatest has no relation to an explicit type=raw,value=latest tag (which could easily be confused that it does).

    • onlatest is also false by default, but that's not documented either (see this recent bug report from a confused user).

    • flavor.suffix (and equivalent flavor.prefix) applies to all explicit tags, but can also be used as an attribute in tags to opt-out via ,suffix= (suffix unset via an empty value). This override behaviour could be documented more clearly, given the current docs that show a tag types implicit defaults which already include ,prefix=,suffix= it may not be obvious that you can opt-out of the flavor prefix/suffix this way.

      https://github.com/docker/metadata-action/blob/318604b99e75e41977312d83839a89be02ca4893/README.md?plain=1#L703-L708

polarathene avatar Feb 27 '25 01:02 polarathene

Thanks for the convo and analysis on this issue! I will take a closer look

crazy-max avatar Feb 27 '25 14:02 crazy-max