88 "path/filepath"
99 "reflect"
1010 "runtime"
11+ "slices"
1112 "strconv"
1213 "strings"
1314 "time"
@@ -105,6 +106,13 @@ var BuildParamsConfig = map[string]common.Parameter{
105106 TypeKind : reflect .Slice ,
106107 Usage : "Annotations to apply to the image using buildah's --annotation option." ,
107108 },
109+ "annotations-file" : {
110+ Name : "annotations-file" ,
111+ ShortName : "" ,
112+ EnvVarName : "KBC_BUILD_ANNOTATIONS_FILE" ,
113+ TypeKind : reflect .String ,
114+ Usage : "Path to a file with annotations, same file format as --build-args-file." ,
115+ },
108116 "image-source" : {
109117 Name : "image-source" ,
110118 ShortName : "" ,
@@ -177,6 +185,7 @@ type BuildParams struct {
177185 Envs []string `paramName:"envs"`
178186 Labels []string `paramName:"labels"`
179187 Annotations []string `paramName:"annotations"`
188+ AnnotationsFile string `paramName:"annotations-file"`
180189 ImageSource string `paramName:"image-source"`
181190 ImageRevision string `paramName:"image-revision"`
182191 LegacyBuildTimestamp string `paramName:"legacy-build-timestamp"`
@@ -205,6 +214,8 @@ type Build struct {
205214
206215 containerfilePath string
207216 buildahSecrets []cliWrappers.BuildahSecret
217+ mergedLabels []string
218+ mergedAnnotations []string
208219}
209220
210221func NewBuild (cmd * cobra.Command , extraArgs []string ) (* Build , error ) {
@@ -255,6 +266,10 @@ func (c *Build) Run() error {
255266 return err
256267 }
257268
269+ if err := c .processLabelsAndAnnotations (); err != nil {
270+ return err
271+ }
272+
258273 if err := c .setSecretArgs (); err != nil {
259274 return err
260275 }
@@ -317,6 +332,9 @@ func (c *Build) logParams() {
317332 if len (c .Params .Annotations ) > 0 {
318333 l .Logger .Infof ("[param] Annotations: %v" , c .Params .Annotations )
319334 }
335+ if c .Params .AnnotationsFile != "" {
336+ l .Logger .Infof ("[param] AnnotationsFile: %s" , c .Params .AnnotationsFile )
337+ }
320338 if c .Params .ImageSource != "" {
321339 l .Logger .Infof ("[param] ImageSource: %s" , c .Params .ImageSource )
322340 }
@@ -609,7 +627,7 @@ func processKeyValueEnvs(args []string) map[string]string {
609627 return values
610628}
611629
612- // Prepends default labels and annotations to the user-provided arrays .
630+ // Prepends default labels and annotations to the user-provided values .
613631// User-provided values override defaults via buildah's "last value wins" behavior.
614632//
615633// The default annotations are primarily based on the OCI annotation spec:
@@ -621,13 +639,13 @@ func processKeyValueEnvs(args []string) map[string]string {
621639//
622640// In addition to the OCI annotations (and labels), if AddLegacyLabels is enabled,
623641// adds labels based on https://github.com/projectatomic/ContainerApplicationGenericLabels.
624- func (c * Build ) mergeDefaultLabelsAndAnnotations () ([] string , [] string , error ) {
642+ func (c * Build ) processLabelsAndAnnotations () error {
625643 var defaultLabels []string
626644 var defaultAnnotations []string
627645
628646 buildTimeStr , err := c .getBuildTimeRFC3339 ()
629647 if err != nil {
630- return nil , nil , fmt .Errorf ("determining build timestamp: %w" , err )
648+ return fmt .Errorf ("determining build timestamp: %w" , err )
631649 }
632650 ociCreated := "org.opencontainers.image.created=" + buildTimeStr
633651 defaultAnnotations = append (defaultAnnotations , ociCreated )
@@ -674,8 +692,22 @@ func (c *Build) mergeDefaultLabelsAndAnnotations() ([]string, []string, error) {
674692 }
675693
676694 mergedLabels := append (defaultLabels , c .Params .Labels ... )
677- mergedAnnotations := append (defaultAnnotations , c .Params .Annotations ... )
678- return mergedLabels , mergedAnnotations , nil
695+
696+ mergedAnnotations := defaultAnnotations
697+ if c .Params .AnnotationsFile != "" {
698+ fileAnnotations , err := parseAnnotationsFile (c .Params .AnnotationsFile )
699+ if err != nil {
700+ return fmt .Errorf ("parsing annotations file: %w" , err )
701+ }
702+ // --annotations-file takes precedence over defaults
703+ mergedAnnotations = append (mergedAnnotations , fileAnnotations ... )
704+ }
705+ // --annotations take precedence over --annotations-file
706+ mergedAnnotations = append (mergedAnnotations , c .Params .Annotations ... )
707+
708+ c .mergedLabels = mergedLabels
709+ c .mergedAnnotations = mergedAnnotations
710+ return nil
679711}
680712
681713func (c * Build ) getBuildTimeRFC3339 () (string , error ) {
@@ -698,6 +730,19 @@ func (c *Build) getBuildTimeRFC3339() (string, error) {
698730 return buildTime .Format (time .RFC3339 ), nil
699731}
700732
733+ func parseAnnotationsFile (filePath string ) ([]string , error ) {
734+ annotations , err := buildargs .ParseBuildArgFile (filePath )
735+ if err != nil {
736+ return nil , err
737+ }
738+ annotationStrings := []string {}
739+ for k , v := range annotations {
740+ annotationStrings = append (annotationStrings , k + "=" + v )
741+ }
742+ slices .Sort (annotationStrings )
743+ return annotationStrings , nil
744+ }
745+
701746// Convert Go's GOARCH value to the value used for the 'architecture' label.
702747//
703748// Historically, the 'architecture' label used the RPM architecture names rather
@@ -725,11 +770,6 @@ func (c *Build) buildImage() error {
725770 }
726771 defer os .Chdir (originalCwd )
727772
728- mergedLabels , mergedAnnotations , err := c .mergeDefaultLabelsAndAnnotations ()
729- if err != nil {
730- return err
731- }
732-
733773 buildArgs := & cliWrappers.BuildahBuildArgs {
734774 Containerfile : c .containerfilePath ,
735775 ContextDir : c .Params .Context ,
@@ -738,8 +778,8 @@ func (c *Build) buildImage() error {
738778 BuildArgs : c .Params .BuildArgs ,
739779 BuildArgsFile : c .Params .BuildArgsFile ,
740780 Envs : c .Params .Envs ,
741- Labels : mergedLabels ,
742- Annotations : mergedAnnotations ,
781+ Labels : c . mergedLabels ,
782+ Annotations : c . mergedAnnotations ,
743783 SourceDateEpoch : c .Params .SourceDateEpoch ,
744784 RewriteTimestamp : c .Params .RewriteTimestamp ,
745785 ExtraArgs : c .Params .ExtraArgs ,
0 commit comments