github.com/jfrog/jfrog-cli-core/v2@v2.51.0/general/project/projectinit.go (about) 1 package project 2 3 import ( 4 "os" 5 "os/exec" 6 "path" 7 "path/filepath" 8 "strings" 9 10 commonCommands "github.com/jfrog/jfrog-cli-core/v2/common/commands" 11 "github.com/jfrog/jfrog-cli-core/v2/common/project" 12 "github.com/jfrog/jfrog-cli-core/v2/utils/config" 13 "github.com/jfrog/jfrog-cli-core/v2/utils/coreutils" 14 "github.com/jfrog/jfrog-client-go/utils/errorutils" 15 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 16 "gopkg.in/yaml.v3" 17 ) 18 19 const ( 20 buildFileName = "build.yaml" 21 ) 22 23 type ProjectInitCommand struct { 24 projectPath string 25 serverId string 26 serverUrl string 27 } 28 29 func NewProjectInitCommand() *ProjectInitCommand { 30 return &ProjectInitCommand{} 31 } 32 33 func (pic *ProjectInitCommand) SetProjectPath(path string) *ProjectInitCommand { 34 pic.projectPath = path 35 return pic 36 } 37 38 func (pic *ProjectInitCommand) SetServerId(id string) *ProjectInitCommand { 39 pic.serverId = id 40 return pic 41 } 42 43 func (pic *ProjectInitCommand) Run() (err error) { 44 if pic.serverId == "" { 45 defaultServer, err := config.GetSpecificConfig("", true, false) 46 if err != nil { 47 return err 48 } 49 pic.serverId = defaultServer.ServerId 50 pic.serverUrl = defaultServer.Url 51 } 52 technologiesMap, err := pic.detectTechnologies() 53 if err != nil { 54 return err 55 } 56 if _, errNotFound := exec.LookPath("docker"); errNotFound == nil { 57 technologiesMap[coreutils.Docker] = true 58 } 59 // First create repositories for the detected technologies. 60 for techName := range technologiesMap { 61 // First create repositories for the detected technology. 62 err = createDefaultReposIfNeeded(techName, pic.serverId) 63 if err != nil { 64 return err 65 } 66 err = createProjectBuildConfigs(techName, pic.projectPath, pic.serverId) 67 if err != nil { 68 return err 69 } 70 } 71 // Create build config 72 if err = pic.createBuildConfig(); err != nil { 73 return 74 } 75 76 err = coreutils.PrintTable("", "", pic.createSummarizeMessage(technologiesMap), false) 77 return 78 } 79 80 func (pic *ProjectInitCommand) createSummarizeMessage(technologiesMap map[coreutils.Technology]bool) string { 81 return coreutils.PrintBold("This project is initialized!\n") + 82 coreutils.PrintBold("The project config is stored inside the .jfrog directory.") + 83 "\n\n" + 84 coreutils.PrintTitle("🔍 Scan the dependencies of this project for security vulnerabilities by running") + 85 "\n" + 86 "jf audit\n\n" + 87 coreutils.PrintTitle("📦 Scan any software package on you machine for security vulnerabilities by running") + 88 "\n" + 89 "jf scan path/to/dir/or/package\n\n" + 90 coreutils.PrintTitle("🐳 Scan any local docker image on you machine for security vulnerabilities by running") + 91 "\n" + 92 "jf docker scan <image name>:<image tag>\n\n" + 93 coreutils.PrintTitle("💻 If you're using VS Code, IntelliJ IDEA, WebStorm, PyCharm, Android Studio or GoLand") + 94 "\n" + 95 "Open the IDE 👉 Install the JFrog extension or plugin 👉 View the JFrog panel" + 96 "\n\n" + 97 pic.createBuildMessage(technologiesMap) + 98 coreutils.PrintTitle("📚 Read more using this link:") + 99 "\n" + 100 coreutils.PrintLink(coreutils.GettingStartedGuideUrl) 101 } 102 103 // Return a string message, which includes all the build and deployment commands, matching the technologiesMap sent. 104 func (pic *ProjectInitCommand) createBuildMessage(technologiesMap map[coreutils.Technology]bool) string { 105 message := "" 106 for tech := range technologiesMap { 107 switch tech { 108 case coreutils.Maven: 109 message += "jf mvn install deploy\n" 110 case coreutils.Gradle: 111 message += "jf gradle artifactoryP\n" 112 case coreutils.Npm: 113 message += "jf npm install\n" 114 message += "jf npm publish\n" 115 case coreutils.Go: 116 message += 117 "jf go build\n" + 118 "jf go-publish v1.0.0\n" 119 case coreutils.Pip, coreutils.Pipenv: 120 message += 121 "jf " + string(tech) + " install\n" + 122 "jf rt upload path/to/package/file default-pypi-local" + 123 coreutils.PrintComment(" #Publish your "+string(tech)+" package") + 124 "\n" 125 case coreutils.Dotnet: 126 executableName := coreutils.Nuget 127 _, errNotFound := exec.LookPath("dotnet") 128 if errNotFound == nil { 129 // dotnet exists in path, So use it in the instruction message. 130 executableName = coreutils.Dotnet 131 } 132 message += 133 "jf " + string(executableName) + " restore\n" + 134 "jf rt upload '*.nupkg'" + RepoDefaultName[tech][Virtual] + "\n" 135 } 136 } 137 if message != "" { 138 message = coreutils.PrintTitle("🚧 Build the code & deploy the packages by running") + 139 "\n" + 140 message + 141 "\n" 142 } 143 if ok := technologiesMap[coreutils.Docker]; ok { 144 baseurl := strings.TrimPrefix(strings.TrimSpace(pic.serverUrl), "https://") 145 baseurl = strings.TrimPrefix(baseurl, "http://") 146 imageUrl := path.Join(baseurl, DockerVirtualDefaultName, "<image>:<tag>") 147 message += coreutils.PrintTitle("🐳 Pull and push any docker image using Artifactory") + 148 "\n" + 149 "jf docker tag <image>:<tag> " + imageUrl + "\n" + 150 "jf docker push " + imageUrl + "\n" + 151 "jf docker pull " + imageUrl + "\n\n" 152 } 153 154 if message != "" { 155 message += coreutils.PrintTitle("📤 Publish the build-info to Artifactory") + 156 "\n" + 157 "jf rt build-publish\n\n" 158 } 159 return message 160 } 161 162 // Returns all detected technologies found in the project directory. 163 // First, try to return only the technologies that detected according to files in the root directory. 164 // In case no indication found in the root directory, the search continue recursively. 165 func (pic *ProjectInitCommand) detectTechnologies() (technologiesMap map[coreutils.Technology]bool, err error) { 166 technologiesMap, err = coreutils.DetectTechnologies(pic.projectPath, false, false) 167 if err != nil { 168 return 169 } 170 // In case no technologies were detected in the root directory, try again recursively. 171 if len(technologiesMap) == 0 { 172 technologiesMap, err = coreutils.DetectTechnologies(pic.projectPath, false, true) 173 if err != nil { 174 return 175 } 176 } 177 return 178 } 179 180 type BuildConfigFile struct { 181 Version int `yaml:"version,omitempty"` 182 ConfigType string `yaml:"type,omitempty"` 183 BuildName string `yaml:"name,omitempty"` 184 } 185 186 func (pic *ProjectInitCommand) createBuildConfig() error { 187 jfrogProjectDir := filepath.Join(pic.projectPath, ".jfrog", "projects") 188 if err := fileutils.CreateDirIfNotExist(jfrogProjectDir); err != nil { 189 return errorutils.CheckError(err) 190 } 191 configFilePath := filepath.Join(jfrogProjectDir, buildFileName) 192 projectDirName := filepath.Base(filepath.Dir(pic.projectPath)) 193 buildConfigFile := &BuildConfigFile{Version: 1, ConfigType: "build", BuildName: projectDirName} 194 resBytes, err := yaml.Marshal(&buildConfigFile) 195 if err != nil { 196 return errorutils.CheckError(err) 197 } 198 return errorutils.CheckError(os.WriteFile(configFilePath, resBytes, 0644)) 199 } 200 201 func createDefaultReposIfNeeded(tech coreutils.Technology, serverId string) error { 202 err := CreateDefaultLocalRepo(tech, serverId) 203 if err != nil { 204 return err 205 } 206 err = CreateDefaultRemoteRepo(tech, serverId) 207 if err != nil { 208 return err 209 } 210 211 return CreateDefaultVirtualRepo(tech, serverId) 212 } 213 214 func createProjectBuildConfigs(tech coreutils.Technology, projectPath string, serverId string) error { 215 jfrogProjectDir := filepath.Join(projectPath, ".jfrog", "projects") 216 if err := fileutils.CreateDirIfNotExist(jfrogProjectDir); err != nil { 217 return errorutils.CheckError(err) 218 } 219 techName := strings.ToLower(string(tech)) 220 configFilePath := filepath.Join(jfrogProjectDir, techName+".yaml") 221 configFile := commonCommands.ConfigFile{ 222 Version: commonCommands.BuildConfVersion, 223 ConfigType: techName, 224 } 225 configFile.Resolver = project.Repository{ServerId: serverId} 226 configFile.Deployer = project.Repository{ServerId: serverId} 227 switch tech { 228 case coreutils.Maven: 229 configFile.Resolver.ReleaseRepo = MavenVirtualDefaultName 230 configFile.Resolver.SnapshotRepo = MavenVirtualDefaultName 231 configFile.Deployer.ReleaseRepo = MavenVirtualDefaultName 232 configFile.Deployer.SnapshotRepo = MavenVirtualDefaultName 233 case coreutils.Dotnet: 234 fallthrough 235 case coreutils.Nuget: 236 configFile.Resolver.NugetV2 = true 237 fallthrough 238 default: 239 configFile.Resolver.Repo = RepoDefaultName[tech][Virtual] 240 configFile.Deployer.Repo = RepoDefaultName[tech][Virtual] 241 242 } 243 resBytes, err := yaml.Marshal(&configFile) 244 if err != nil { 245 return errorutils.CheckError(err) 246 } 247 248 return errorutils.CheckError(os.WriteFile(configFilePath, resBytes, 0644)) 249 } 250 251 func (pic *ProjectInitCommand) CommandName() string { 252 return "project_init" 253 } 254 255 func (pic *ProjectInitCommand) ServerDetails() (*config.ServerDetails, error) { 256 return config.GetSpecificConfig("", true, false) 257 }