Skip to content

Conversation

@thaJeztah
Copy link

The SetAnnotation method returns an error if the given flag does not exist. While handling the error should normally be the recommendation, in many cases the list of flags is static, and adding error handling for each annotation can either introduce unwanted boilerplating, or may not be possible in cases where there's no error return.

Unfortunately, also makes this method prone to copy/paste mistakes where either the flag doesn't exist, or an annotation is already set, potentially overwriting another flag's annotation (which is a bug I recently discovered in one of our projects (see 1)). Such bugs can be hard to discover if they have no immediate impact.

This patch introduces a MustSetAnnotation that can be used as a replacement for SetAnnotation. Unlike SetAnnotation, it produces a panic instead of returning an error, and uses stricter validation; not allowing an annotation to be set if the flag already has the annotation set.

The SetAnnotation method returns an error if the given flag does not exist.
While handling the error should normally be the recommendation, in many cases
the list of flags is static, and adding error handling for each annotation can
either introduce unwanted boilerplating, or may not be possible in cases where
there's no error return.

Unfortunately, also makes this method prone to copy/paste mistakes where either
the flag doesn't exist, or an annotation is already set, potentially overwriting
another flag's annotation (which is a bug I recently discovered in one of our
projects (see [1])). Such bugs can be hard to discover if they have no immediate
impact.

This patch introduces a `MustSetAnnotation` that can be used as a replacement
for `SetAnnotation`. Unlike `SetAnnotation`, it produces a panic instead of
returning an error, and uses stricter validation; not allowing an annotation
to be set if the flag already has the annotation set.

[1]: https://github.com/docker/cli/blob/93fa57bbcd08f2f5be7f6cf22f4273a2b5a49e71/cli/command/service/opts.go#L905-L910

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
@thaJeztah thaJeztah force-pushed the add_MustSetAnnotation branch from 06e04a2 to 918e819 Compare December 22, 2025 11:27
Copy link
Collaborator

@tomasaschan tomasaschan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't want to argue against the usefulness of erroring if an annotation is already set, but I do think that this PR is a problem in terms of naming and user expectations on function pairs like func Foo() error/func MustFoo(). It is a strong convention that the MustFoo variant is basically equivalent to

func MustFoo() {
    if err := Foo(); err != nil {
        panic(err)
    }    
}

to ensure that behavior is consistent except in terms of error handling.

I propose either splitting out the duplicate detection functionality (it can be added to SetAnnotation in a backwards compatible way using functional options!) or choosing a different name for this variant (maybe something like SetAnnotationIfUnset?).

PS: Sorry this took so long to come back to. I haven't sat at a computer since before Christmas, and some bug in the phone app left me with a review draft that I couldn't find but also couldn't ignore to post a new review. Hopefully this will be less of an issue going forward, as I am going back to work after nine months of parental leave over the next couple of weeks.

Comment on lines +530 to +532
if _, ok := flag.Annotations[key]; ok {
panic(fmt.Errorf("annotation %q is already set for flag %q", key, name))
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be explicit: this behavior differs between SetAnnotation and MustSetAnnotation, and seems like something that would both be valuable to expose without panicking, and like something that would be confusing for users of SetAnnotation who don't want to bother with checking the returned error.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants