add --pindigests flag

This commit is contained in:
2026-07-27 05:51:36 +00:00
parent 8839ad17ba
commit 9b7952bdf4
3 changed files with 97 additions and 3 deletions
+1
View File
@@ -19,5 +19,6 @@ var (
Minor bool
NoDomainChecks bool
Patch bool
PinDigests bool
ShowUnchanged bool
)
+29 -2
View File
@@ -293,12 +293,31 @@ interface.`),
}
}
if upgradeTag != "skip" {
ok, err := recipe.UpdateTag(image, upgradeTag)
var ok bool
var err error
var resolvedDigest string
if internal.PinDigests {
digest, dErr := client.GetImageDigest(img, upgradeTag)
if dErr != nil {
log.Fatal(i18n.G("failed to resolve digest for %s:%s: %s", image, upgradeTag, dErr.Error()))
}
resolvedDigest = digest
ok, err = recipe.UpdatePinnedTag(image, upgradeTag, digest)
} else {
ok, err = recipe.UpdateTag(image, upgradeTag)
}
if err != nil {
log.Fatal(err)
}
if ok {
log.Info(i18n.G("tag upgraded from %s to %s for %s", tag.String(), upgradeTag, image))
if internal.PinDigests {
log.Info(i18n.G("tag upgraded and pinned from %s to %s@%s for %s", tag.String(), upgradeTag, resolvedDigest, image))
} else {
log.Info(i18n.G("tag upgraded from %s to %s for %s", tag.String(), upgradeTag, image))
}
}
} else {
if !internal.NoInput {
@@ -417,4 +436,12 @@ func init() {
false,
i18n.G("commit changes"),
)
RecipeUpgradeCommand.Flags().BoolVarP(
&internal.PinDigests,
i18n.G("pindigests"),
i18n.GC("p", "pin container image by digest"),
false,
i18n.G("pin the container image version by manifest digest (image:tag@sha256:...)"),
)
}
+67 -1
View File
@@ -122,6 +122,7 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) {
log.Debug(i18n.G("considering %s config(s) for tag update", strings.Join(composeFiles, ", ")))
updated := false
for _, composeFile := range composeFiles {
opts := stack.Deploy{Composefiles: []string{composeFile}}
@@ -166,6 +167,10 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) {
old := fmt.Sprintf("%s:%s", composeImage, composeTag)
new := fmt.Sprintf("%s:%s", composeImage, tag)
if old == new {
continue
}
replacedBytes := strings.Replace(string(bytes), old, new, -1)
log.Debug(i18n.G("updating %s to %s in %s", old, new, compose.Filename))
@@ -173,11 +178,72 @@ func (r Recipe) UpdateTag(image, tag string) (bool, error) {
if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil {
return false, err
}
updated = true
}
}
}
return false, nil
return updated, nil
}
// UpdatePinnedTag updates an image reference to a pinned version (image:tag@sha256:...) in-place on local compose files.
func (r Recipe) UpdatePinnedTag(image, tag, digest string) (bool, error) {
glob := fmt.Sprintf("%s/compose**yml", r.Dir)
image = formatter.StripTagMeta(image)
composeFiles, err := filepath.Glob(glob)
if err != nil {
return false, err
}
updated := false
for _, composeFile := range composeFiles {
opts := stack.Deploy{Composefiles: []string{composeFile}}
sampleEnv, err := r.SampleEnv()
if err != nil {
return false, err
}
compose, err := loader.LoadComposefile(opts, sampleEnv)
if err != nil {
return false, err
}
for _, service := range compose.Services {
if service.Image == "" {
continue
}
img, _ := reference.ParseNormalizedNamed(formatter.StripDigest(service.Image))
composeImage := formatter.StripTagMeta(reference.Path(img))
if image == composeImage {
bytes, err := ioutil.ReadFile(composeFile)
if err != nil {
return false, err
}
old := service.Image
new := fmt.Sprintf("%s:%s@%s", composeImage, tag, digest)
if old == new {
continue
}
replacedBytes := strings.Replace(string(bytes), old, new, -1)
log.Debug(i18n.G("updating pinned reference %s to %s in %s", old, new, compose.Filename))
if err := os.WriteFile(compose.Filename, []byte(replacedBytes), 0o764); err != nil {
return false, err
}
updated = true
}
}
}
return updated, nil
}
// UpdateLabel updates a label in-place on file system local compose files.