diff --git a/service/appstore_management.go b/service/appstore_management.go index b06415d8..e3151097 100644 --- a/service/appstore_management.go +++ b/service/appstore_management.go @@ -15,6 +15,7 @@ import ( "github.com/IceWhaleTech/CasaOS-Common/utils/file" "github.com/IceWhaleTech/CasaOS-Common/utils/logger" "github.com/bluele/gcache" + "github.com/docker/docker/client" "github.com/samber/lo" "go.uber.org/zap" ) @@ -530,6 +531,14 @@ func (a *AppStoreManagement) isUpdateAvailable(composeApp *ComposeApp) (bool, er return a.IsUpdateAvailableWith(composeApp, storeComposeApp) } +// the patch is have no choice +// the digest compare is not work for these images +// I don't know why, but I have to do this +// I will remove the patch after I rewrite the digest compare +var NoUpdateBlacklist = []string{ + "johnguan/stable-diffusion-webui:latest", +} + func (a *AppStoreManagement) IsUpdateAvailableWith(composeApp *ComposeApp, storeComposeApp *ComposeApp) (bool, error) { currentTag, err := composeApp.MainTag() if err != nil { @@ -542,12 +551,33 @@ func (a *AppStoreManagement) IsUpdateAvailableWith(composeApp *ComposeApp, store return false, err } if lo.Contains(common.NeedCheckDigestTags, currentTag) { + ctx := context.Background() + cli, clientErr := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) + if clientErr != nil { + logger.Error("failed to create docker client", zap.Error(clientErr)) + return false, clientErr + } + defer cli.Close() - _, _ = docker.ExtractImageAndTag(mainService.Image) - // TODO: refactor this. - // need to check the digest of the image - // to see if the image is updated - return false, nil + if lo.Contains(NoUpdateBlacklist, mainService.Image) { + return false, nil + } + + image, _ := docker.ExtractImageAndTag(mainService.Image) + + imageInfo, _, clientErr := cli.ImageInspectWithRaw(ctx, image) + if clientErr != nil { + logger.Error("failed to inspect image", zap.Error(clientErr)) + return false, clientErr + } + + match, clientErr := docker.CompareDigest(mainService.Image, imageInfo.RepoDigests) + if clientErr != nil { + logger.Error("failed to compare digest", zap.Error(clientErr)) + return false, clientErr + } + // match means no update available + return !match, nil } storeTag, err := storeComposeApp.MainTag() return currentTag != storeTag, err diff --git a/service/compose_app.go b/service/compose_app.go index 7a17467f..3e223105 100644 --- a/service/compose_app.go +++ b/service/compose_app.go @@ -211,9 +211,19 @@ func (a *ComposeApp) Update(ctx context.Context) error { for _, service := range storeComposeApp.Services { localComposeAppService := a.App(service.Name) - localComposeAppService.Image = service.Image + + for _, tag := range common.NeedCheckDigestTags { + if strings.HasSuffix(service.Image, tag) { + // keep latest + } else { + localComposeAppService.Image = service.Image + } + } } + // the code is need by stable diffusion. + removeRuntime(a) + newComposeYAML, err := yaml.Marshal(a) if err != nil { return err @@ -914,14 +924,30 @@ func LoadComposeAppFromConfigFile(appID string, configFile string) (*ComposeApp, return (*ComposeApp)(project), err } +var gpuCache *([]external.GPUInfo) = nil + func removeRuntime(a *ComposeApp) { - for i := range a.Services { - a.Services[i].Runtime = "" + if config.RemoveRuntimeIfNoNvidiaGPUFlag { + + // if gpuCache is nil, it means it is first time fetching gpu info + if gpuCache == nil { + value, err := external.GPUInfoList() + if err != nil { + gpuCache = &([]external.GPUInfo{}) + } else { + gpuCache = &value + } + + // without nvidia-smi // no gpu or first time fetching gpu info failed + } + if len(*gpuCache) == 0 { + for i := range a.Services { + a.Services[i].Runtime = "" + } + } } } -var gpuCache *([]external.GPUInfo) = nil - func NewComposeAppFromYAML(yaml []byte, skipInterpolation, skipValidation bool) (*ComposeApp, error) { tmpWorkingDir, err := os.MkdirTemp("", "casaos-compose-app-*") if err != nil { @@ -1002,22 +1028,7 @@ func NewComposeAppFromYAML(yaml []byte, skipInterpolation, skipValidation bool) composeApp.SetTitle(composeApp.Name, common.DefaultLanguage) } - if config.RemoveRuntimeIfNoNvidiaGPUFlag { - // if gpuCache is nil, it means it is first time fetching gpu info - if gpuCache == nil { - value, err := external.GPUInfoList() - if err != nil { - gpuCache = &([]external.GPUInfo{}) - } else { - gpuCache = &value - } - } - - // without nvidia-smi // no gpu or first time fetching gpu info failed - if err != nil || len(*gpuCache) == 0 { - removeRuntime(composeApp) - } - } + removeRuntime(composeApp) // pass icon information to v1 label for backward compatibility, because we are // still using `func getContainerStats()` from `container.go` to get container stats