github.com/ActiveState/cli@v0.0.0-20240508170324-6801f60cd051/internal/runbits/checkout/checkout.go (about) 1 package checkout 2 3 import ( 4 "path/filepath" 5 6 "github.com/ActiveState/cli/internal/analytics" 7 "github.com/ActiveState/cli/internal/config" 8 "github.com/ActiveState/cli/internal/locale" 9 "github.com/ActiveState/cli/internal/primer" 10 "github.com/ActiveState/cli/pkg/platform/runtime/buildscript" 11 "github.com/go-openapi/strfmt" 12 13 "github.com/ActiveState/cli/internal/constants" 14 "github.com/ActiveState/cli/internal/errs" 15 "github.com/ActiveState/cli/internal/fileutils" 16 "github.com/ActiveState/cli/internal/language" 17 "github.com/ActiveState/cli/internal/osutils" 18 "github.com/ActiveState/cli/internal/output" 19 "github.com/ActiveState/cli/internal/runbits/git" 20 "github.com/ActiveState/cli/pkg/localcommit" 21 "github.com/ActiveState/cli/pkg/platform/api/mono/mono_models" 22 "github.com/ActiveState/cli/pkg/platform/authentication" 23 "github.com/ActiveState/cli/pkg/platform/model" 24 "github.com/ActiveState/cli/pkg/project" 25 "github.com/ActiveState/cli/pkg/projectfile" 26 ) 27 28 type primeable interface { 29 primer.Outputer 30 primer.Analyticer 31 primer.Configurer 32 primer.Auther 33 } 34 35 // Checkout will checkout the given platform project at the given path 36 // This includes cloning an associated repository and creating the activestate.yaml 37 // It does not activate any environment 38 type Checkout struct { 39 repo git.Repository 40 output.Outputer 41 config *config.Instance 42 analytics analytics.Dispatcher 43 branchName string 44 auth *authentication.Auth 45 } 46 47 type errCommitDoesNotBelong struct { 48 CommitID strfmt.UUID 49 } 50 51 func (e errCommitDoesNotBelong) Error() string { 52 return "commitID does not belong to the given branch" 53 } 54 55 func New(repo git.Repository, prime primeable) *Checkout { 56 return &Checkout{repo, prime.Output(), prime.Config(), prime.Analytics(), "", prime.Auth()} 57 } 58 59 func (r *Checkout) Run(ns *project.Namespaced, branchName, cachePath, targetPath string, noClone bool) (_ string, rerr error) { 60 defer r.rationalizeError(&rerr) 61 62 path, err := r.pathToUse(ns, targetPath) 63 if err != nil { 64 return "", errs.Wrap(err, "Could not get path to use") 65 } 66 67 path, err = filepath.Abs(path) 68 if err != nil { 69 return "", errs.Wrap(err, "Could not get absolute path") 70 } 71 72 // If project does not exist at path then we must checkout 73 // the project and create the project file 74 pj, err := model.FetchProjectByName(ns.Owner, ns.Project, r.auth) 75 if err != nil { 76 return "", locale.WrapError(err, "err_fetch_project", "", ns.String()) 77 } 78 79 var branch *mono_models.Branch 80 commitID := ns.CommitID 81 82 switch { 83 // Fetch the branch the given commitID is on. 84 case commitID != nil: 85 for _, b := range pj.Branches { 86 if belongs, err := model.CommitBelongsToBranch(ns.Owner, ns.Project, b.Label, *commitID, r.auth); err == nil && belongs { 87 branch = b 88 break 89 } else if err != nil { 90 return "", errs.Wrap(err, "Could not determine which branch the given commitID belongs to") 91 } 92 } 93 if branch == nil { 94 return "", &errCommitDoesNotBelong{CommitID: *commitID} 95 } 96 97 // Fetch the given project branch. 98 case branchName != "": 99 branch, err = model.BranchForProjectByName(pj, branchName) 100 if err != nil { 101 return "", locale.WrapError(err, "err_fetch_branch", "", branchName) 102 } 103 commitID = branch.CommitID 104 105 // Fetch the default branch for the given project. 106 default: 107 branch, err = model.DefaultBranchForProject(pj) 108 if err != nil { 109 return "", errs.Wrap(err, "Could not grab branch for project") 110 } 111 commitID = branch.CommitID 112 } 113 114 if commitID == nil { 115 return "", errs.New("commitID is nil") 116 } 117 118 // Clone the related repo, if it is defined 119 if !noClone && pj.RepoURL != nil && *pj.RepoURL != "" { 120 err := r.repo.CloneProject(ns.Owner, ns.Project, path, r.Outputer, r.analytics) 121 if err != nil { 122 return "", locale.WrapError(err, "err_clone_project", "Could not clone associated git repository") 123 } 124 } 125 126 language, err := getLanguage(*commitID, r.auth) 127 if err != nil { 128 return "", errs.Wrap(err, "Could not get language from commitID") 129 } 130 131 if cachePath != "" && !filepath.IsAbs(cachePath) { 132 cachePath, err = filepath.Abs(cachePath) 133 if err != nil { 134 return "", errs.Wrap(err, "Could not get absolute path for cache") 135 } 136 } 137 138 // Match the case of the organization. 139 // Otherwise the incorrect case will be written to the project file. 140 owners, err := model.FetchOrganizationsByIDs([]strfmt.UUID{pj.OrganizationID}, r.auth) 141 if err != nil { 142 return "", errs.Wrap(err, "Unable to get the project's org") 143 } 144 if len(owners) == 0 { 145 return "", locale.NewInputError("err_no_org_name", "Your project's organization name could not be found") 146 } 147 owner := owners[0].URLName 148 149 // Create the config file, if the repo clone didn't already create it 150 configFile := filepath.Join(path, constants.ConfigFileName) 151 if !fileutils.FileExists(configFile) { 152 _, err = projectfile.Create(&projectfile.CreateParams{ 153 Owner: owner, 154 Project: pj.Name, // match case on the Platform 155 BranchName: branch.Label, 156 Directory: path, 157 Language: language.String(), 158 Cache: cachePath, 159 }) 160 if err != nil { 161 if osutils.IsAccessDeniedError(err) { 162 return "", &ErrNoPermission{err, path} 163 } 164 return "", errs.Wrap(err, "Could not create projectfile") 165 } 166 } 167 168 err = localcommit.Set(path, commitID.String()) 169 if err != nil { 170 return "", errs.Wrap(err, "Could not create local commit file") 171 } 172 173 if r.config.GetBool(constants.OptinBuildscriptsConfig) { 174 if err := buildscript.Initialize(path, r.auth); err != nil { 175 return "", errs.Wrap(err, "Unable to initialize buildscript") 176 } 177 } 178 179 return path, nil 180 } 181 182 func getLanguage(commitID strfmt.UUID, auth *authentication.Auth) (language.Language, error) { 183 modelLanguage, err := model.LanguageByCommit(commitID, auth) 184 if err != nil { 185 return language.Unset, locale.WrapError(err, "err_language_by_commit", "", string(commitID)) 186 } 187 188 return language.MakeByNameAndVersion(modelLanguage.Name, modelLanguage.Version), nil 189 }