package entity import ( "strings" "time" ) // ArtifactType Artifact 类型 type ArtifactType string const ( ArtifactTypeChart ArtifactType = "chart" // Helm Chart ArtifactTypeImage ArtifactType = "image" // Docker/OCI Image ArtifactTypeOther ArtifactType = "other" // Other types ) // Artifact OCI Artifact 领域实体 type Artifact struct { RegistryID string Repository string Tag string Digest string Type ArtifactType Size int64 MediaType string ConfigType string // Config layer 的 mediaType (用于更准确的类型判断) Annotations map[string]string CreatedAt time.Time } // Repository 仓库信息 type Repository struct { RegistryID string Name string TagCount int } // NewArtifact 创建新 Artifact func NewArtifact(registryID, repository, tag, digest string) *Artifact { return &Artifact{ RegistryID: registryID, Repository: repository, Tag: tag, Digest: digest, Annotations: make(map[string]string), CreatedAt: time.Now(), } } // SetType 设置 Artifact 类型(根据 mediaType 识别为 chart | image | other) // 已废弃:请使用 DetermineType() 方法,它提供更准确的类型判断 func (a *Artifact) SetType(mediaType string) { lowerMediaType := strings.ToLower(strings.TrimSpace(mediaType)) containsAny := func(target string, keywords ...string) bool { for _, keyword := range keywords { if keyword != "" && strings.Contains(target, keyword) { return true } } return false } switch { case lowerMediaType == "": a.Type = ArtifactTypeOther case containsAny(lowerMediaType, "helm", "cncf.helm", "helm.chart", "helm+", "chart+json", "chart.v1", "helm-package", "helm.config", ): a.Type = ArtifactTypeChart case containsAny(lowerMediaType, "docker", "vnd.docker", "docker.distribution", "docker.container.image", "vnd.oci", "oci.image", "opencontainers", "container.image", ): a.Type = ArtifactTypeImage case strings.Contains(lowerMediaType, "image") || strings.Contains(lowerMediaType, "manifest") || strings.Contains(lowerMediaType, "container"): a.Type = ArtifactTypeImage default: a.Type = ArtifactTypeOther } } // DetermineType 智能判断 Artifact 类型(综合多种信息) // 优先级: // 1. ConfigType (config.mediaType) - 最准确 // 2. Annotations - 可能包含类型标注 // 3. Repository 名称 - charts/ 前缀暗示 // 4. MediaType - 兜底判断 func (a *Artifact) DetermineType() { containsAny := func(target string, keywords ...string) bool { for _, keyword := range keywords { if keyword != "" && strings.Contains(target, keyword) { return true } } return false } // 1. 优先检查 ConfigType(最准确的判断方式) if a.ConfigType != "" { lowerConfigType := strings.ToLower(strings.TrimSpace(a.ConfigType)) // Helm Chart 的 config.mediaType if containsAny(lowerConfigType, "helm.config", "cncf.helm", "helm.chart", "chart.content", ) { a.Type = ArtifactTypeChart return } // Docker/OCI Image 的 config.mediaType if containsAny(lowerConfigType, "docker.container.image", "oci.image.config", ) { a.Type = ArtifactTypeImage return } } // 2. 检查 Annotations for key, value := range a.Annotations { lowerKey := strings.ToLower(key) lowerValue := strings.ToLower(value) if containsAny(lowerKey, "helm", "chart") || containsAny(lowerValue, "helm", "chart") { a.Type = ArtifactTypeChart return } } // 3. 检查 Repository 名称(辅助判断) if strings.HasPrefix(strings.ToLower(a.Repository), "charts/") { // charts/ 开头的仓库很可能是 Helm Chart // 但需要结合 MediaType 进一步确认 lowerMediaType := strings.ToLower(strings.TrimSpace(a.MediaType)) // 如果是 OCI manifest 格式,很可能是以 OCI 格式存储的 Helm Chart if strings.Contains(lowerMediaType, "oci.image.manifest") || strings.Contains(lowerMediaType, "vnd.oci") { a.Type = ArtifactTypeChart return } } // 4. 回退到基于 MediaType 的判断(兜底逻辑) lowerMediaType := strings.ToLower(strings.TrimSpace(a.MediaType)) switch { case lowerMediaType == "": a.Type = ArtifactTypeOther case containsAny(lowerMediaType, "helm", "cncf.helm", "helm.chart", "helm+", "chart+json", "chart.v1", "helm-package", "helm.config", ): a.Type = ArtifactTypeChart case containsAny(lowerMediaType, "docker", "vnd.docker", "docker.distribution", "docker.container.image", ): a.Type = ArtifactTypeImage case strings.Contains(lowerMediaType, "image") || strings.Contains(lowerMediaType, "manifest"): a.Type = ArtifactTypeImage default: a.Type = ArtifactTypeOther } } // IsChart 判断是否为 Helm Chart func (a *Artifact) IsChart() bool { return a.Type == ArtifactTypeChart }