github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/pkg/platform/model/checkpoints.go (about) 1 package model 2 3 import ( 4 "strings" 5 6 "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" 7 "github.com/go-openapi/strfmt" 8 9 "github.com/ActiveState/cli/pkg/sysinfo" 10 11 "github.com/ActiveState/cli/internal/constants" 12 "github.com/ActiveState/cli/internal/errs" 13 "github.com/ActiveState/cli/internal/locale" 14 "github.com/ActiveState/cli/internal/logging" 15 "github.com/ActiveState/cli/pkg/platform/api/graphql" 16 gqlModel "github.com/ActiveState/cli/pkg/platform/api/graphql/model" 17 "github.com/ActiveState/cli/pkg/platform/api/graphql/request" 18 "github.com/ActiveState/cli/pkg/platform/api/inventory/inventory_models" 19 "github.com/ActiveState/cli/pkg/platform/authentication" 20 ) 21 22 var ( 23 ErrNoData = errs.New("no data") 24 ) 25 26 // Checkpoint represents a collection of requirements 27 type Checkpoint []*mono_models.Checkpoint 28 29 // Language represents a language requirement 30 type Language struct { 31 Name string `json:"name"` 32 Version string `json:"version"` 33 } 34 35 // GetRequirement searches a commit for a requirement by name. 36 func GetRequirement(commitID strfmt.UUID, namespace Namespace, requirement string, auth *authentication.Auth) (*gqlModel.Requirement, error) { 37 chkPt, _, err := FetchCheckpointForCommit(commitID, auth) 38 if err != nil { 39 return nil, err 40 } 41 42 chkPt = FilterCheckpointNamespace(chkPt, namespace.Type()) 43 44 for _, req := range chkPt { 45 if req.Namespace == namespace.String() && req.Requirement == requirement { 46 return req, nil 47 } 48 } 49 50 return nil, nil 51 } 52 53 // FetchLanguagesForCommit fetches a list of language names for the given commit 54 func FetchLanguagesForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([]Language, error) { 55 checkpoint, _, err := FetchCheckpointForCommit(commitID, auth) 56 if err != nil { 57 return nil, err 58 } 59 60 languages := []Language{} 61 for _, requirement := range checkpoint { 62 if NamespaceMatch(requirement.Namespace, NamespaceLanguageMatch) { 63 version := MonoConstraintsToString(requirement.VersionConstraints) 64 lang := Language{ 65 Name: requirement.Requirement, 66 Version: version, 67 } 68 languages = append(languages, lang) 69 } 70 } 71 72 return languages, nil 73 } 74 75 // FetchCheckpointForCommit fetches the checkpoint for the given commit 76 func FetchCheckpointForCommit(commitID strfmt.UUID, auth *authentication.Auth) ([]*gqlModel.Requirement, strfmt.DateTime, error) { 77 logging.Debug("fetching checkpoint (%s)", commitID.String()) 78 79 request := request.CheckpointByCommit(commitID) 80 81 gql := graphql.New(auth) 82 response := gqlModel.Checkpoint{} 83 err := gql.Run(request, &response) 84 if err != nil { 85 return nil, strfmt.DateTime{}, errs.Wrap(err, "gql.Run failed") 86 } 87 88 logging.Debug("Returning %d requirements", len(response.Requirements)) 89 90 if response.Commit == nil { 91 return nil, strfmt.DateTime{}, locale.WrapError(ErrNoData, "err_no_data_found") 92 } 93 94 return response.Requirements, response.Commit.AtTime, nil 95 } 96 97 func GqlReqsToMonoCheckpoint(requirements []*gqlModel.Requirement) []*mono_models.Checkpoint { 98 var result = make([]*mono_models.Checkpoint, 0) 99 for _, req := range requirements { 100 result = append(result, &req.Checkpoint) 101 } 102 return result 103 } 104 105 // FilterCheckpointNamespace filters a Checkpoint removing requirements that do not match the given namespace. 106 func FilterCheckpointNamespace(chkPt []*gqlModel.Requirement, nsType ...NamespaceType) []*gqlModel.Requirement { 107 if chkPt == nil { 108 return nil 109 } 110 111 checkpoint := []*gqlModel.Requirement{} 112 for _, ns := range nsType { 113 for _, requirement := range chkPt { 114 if NamespaceMatch(requirement.Namespace, ns.Matchable()) { 115 checkpoint = append(checkpoint, requirement) 116 } 117 } 118 } 119 120 return checkpoint 121 } 122 123 // CheckpointToRequirements converts a checkpoint to a list of requirements for use with the head-chef 124 func CheckpointToRequirements(checkpoint Checkpoint) []*inventory_models.OrderRequirement { 125 result := []*inventory_models.OrderRequirement{} 126 127 for _, req := range checkpoint { 128 if NamespaceMatch(req.Namespace, NamespacePlatformMatch) { 129 continue 130 } 131 if NamespaceMatch(req.Namespace, NamespaceCamelFlagsMatch) { 132 continue 133 } 134 135 result = append(result, &inventory_models.OrderRequirement{ 136 Feature: &req.Requirement, 137 Namespace: &req.Namespace, 138 VersionRequirements: versionRequirement(req.VersionConstraint), 139 }) 140 } 141 142 return result 143 } 144 145 // CheckpointToCamelFlags converts a checkpoint to camel flags 146 func CheckpointToCamelFlags(checkpoint Checkpoint) []string { 147 result := []string{} 148 149 for _, req := range checkpoint { 150 if !NamespaceMatch(req.Namespace, NamespaceCamelFlagsMatch) { 151 continue 152 } 153 154 result = append(result, req.Requirement) 155 } 156 157 return result 158 } 159 160 // versionRequirement returns nil if the version constraint is empty otherwise it will return a valid 161 // list for a V1OrderRequirements' VersionRequirements. The VersionRequirements can be omitted however 162 // if it is present then the Version string must be populated with at least one character. 163 func versionRequirement(versionConstraint string) []*inventory_models.VersionRequirement { 164 if versionConstraint == "" { 165 return nil 166 } 167 168 var eq = "eq" 169 return []*inventory_models.VersionRequirement{{ 170 Comparator: &eq, 171 Version: &versionConstraint, 172 }} 173 } 174 175 // CheckpointToPlatforms strips platforms from a checkpoint 176 func CheckpointToPlatforms(requirements []*gqlModel.Requirement) []strfmt.UUID { 177 result := []strfmt.UUID{} 178 179 for _, req := range requirements { 180 if !NamespaceMatch(req.Namespace, NamespacePlatformMatch) { 181 continue 182 } 183 result = append(result, strfmt.UUID(req.Requirement)) 184 } 185 186 return result 187 } 188 189 // CheckpointToLanguage returns the language from a checkpoint 190 func CheckpointToLanguage(requirements []*gqlModel.Requirement, auth *authentication.Auth) (*Language, error) { 191 for _, req := range requirements { 192 if !NamespaceMatch(req.Namespace, NamespaceLanguageMatch) { 193 continue 194 } 195 lang, err := FetchLanguageByDetails(req.Requirement, req.VersionConstraint, auth) 196 if err != nil { 197 return nil, err 198 } 199 return lang, nil 200 } 201 202 return nil, locale.NewError("err_fetch_languages") 203 } 204 205 func PlatformNameToPlatformID(name string) (string, error) { 206 name = strings.ToLower(name) 207 if name == "darwin" { 208 name = "macos" 209 } 210 id, err := hostPlatformToPlatformID(name) 211 return id, err 212 } 213 214 func hostPlatformToPlatformID(os string) (string, error) { 215 switch strings.ToLower(os) { 216 case strings.ToLower(sysinfo.Linux.String()): 217 return constants.LinuxBit64UUID, nil 218 case strings.ToLower(sysinfo.Mac.String()): 219 return constants.MacBit64UUID, nil 220 case strings.ToLower(sysinfo.Windows.String()): 221 return constants.Win10Bit64UUID, nil 222 default: 223 return "", locale.NewExternalError("err_unsupported_platform", "", os) 224 } 225 } 226 227 func HostPlatformToKernelName(os string) string { 228 switch strings.ToLower(os) { 229 case strings.ToLower(sysinfo.Linux.String()): 230 return "Linux" 231 case strings.ToLower(sysinfo.Mac.String()): 232 return "Darwin" 233 case strings.ToLower(sysinfo.Windows.String()): 234 return "Windows" 235 default: 236 return "" 237 } 238 } 239 240 func platformArchToHostArch(arch, bits string) string { 241 switch bits { 242 case "32": 243 switch arch { 244 case "IA64": 245 return "nonexistent" 246 case "PA-RISC": 247 return "unsupported" 248 case "PowerPC": 249 return "ppc" 250 case "Sparc": 251 return "sparc" 252 case "x86": 253 return "386" 254 } 255 case "64": 256 switch arch { 257 case "IA64": 258 return "unsupported" 259 case "PA-RISC": 260 return "unsupported" 261 case "PowerPC": 262 return "ppc64" 263 case "Sparc": 264 return "sparc64" 265 case "x86": 266 return "amd64" 267 } 268 } 269 return "unrecognized" 270 } 271 272 func fallbackArch(platform, arch string) string { 273 // On the M1 Mac platform we default to 274 // amd64 as the platform does not support arm. 275 if arch == "arm64" && platform == sysinfo.Mac.String() { 276 return "amd64" 277 } 278 return arch 279 }