github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/artifactory/commands/npm/publish.go (about) 1 package npm 2 3 import ( 4 "archive/tar" 5 "compress/gzip" 6 "errors" 7 "fmt" 8 "io" 9 "io/ioutil" 10 "os" 11 "os/exec" 12 "path/filepath" 13 "strings" 14 15 "github.com/jfrog/jfrog-cli-go/artifactory/utils" 16 "github.com/jfrog/jfrog-cli-go/artifactory/utils/npm" 17 "github.com/jfrog/jfrog-cli-go/utils/config" 18 "github.com/jfrog/jfrog-client-go/artifactory/buildinfo" 19 "github.com/jfrog/jfrog-client-go/artifactory/services" 20 specutils "github.com/jfrog/jfrog-client-go/artifactory/services/utils" 21 clientutils "github.com/jfrog/jfrog-client-go/utils" 22 "github.com/jfrog/jfrog-client-go/utils/errorutils" 23 "github.com/jfrog/jfrog-client-go/utils/log" 24 ) 25 26 type NpmPublishCommandArgs struct { 27 NpmCommand 28 executablePath string 29 workingDirectory string 30 collectBuildInfo bool 31 packedFilePath string 32 packageInfo *npm.PackageInfo 33 publishPath string 34 tarballProvided bool 35 artifactData []specutils.FileInfo 36 } 37 38 type NpmPublishCommand struct { 39 configFilePath string 40 commandName string 41 *NpmPublishCommandArgs 42 } 43 44 func NewNpmPublishCommand() *NpmPublishCommand { 45 return &NpmPublishCommand{NpmPublishCommandArgs: NewNpmPublishCommandArgs(), commandName: "rt_npm_publish"} 46 } 47 48 func NewNpmPublishCommandArgs() *NpmPublishCommandArgs { 49 return &NpmPublishCommandArgs{} 50 } 51 52 func (npc *NpmPublishCommand) RtDetails() (*config.ArtifactoryDetails, error) { 53 return npc.rtDetails, nil 54 } 55 56 func (npc *NpmPublishCommand) SetConfigFilePath(configFilePath string) *NpmPublishCommand { 57 npc.configFilePath = configFilePath 58 return npc 59 } 60 61 func (nic *NpmPublishCommand) SetArgs(args []string) *NpmPublishCommand { 62 nic.NpmPublishCommandArgs.npmArgs = args 63 return nic 64 } 65 66 func (npc *NpmPublishCommand) Run() error { 67 if npc.configFilePath != "" { 68 // Read config file. 69 log.Debug("Preparing to read the config file", npc.configFilePath) 70 vConfig, err := utils.ReadConfigFile(npc.configFilePath, utils.YAML) 71 if err != nil { 72 return err 73 } 74 deployerParams, err := utils.GetRepoConfigByPrefix(npc.configFilePath, utils.ProjectConfigDeployerPrefix, vConfig) 75 if err != nil { 76 return err 77 } 78 _, _, filteredNpmArgs, buildConfiguration, err := npm.ExtractNpmOptionsFromArgs(npc.NpmPublishCommandArgs.npmArgs) 79 if err != nil { 80 return err 81 } 82 rtDetails, err := deployerParams.RtDetails() 83 if err != nil { 84 return errorutils.CheckError(err) 85 } 86 npc.SetBuildConfiguration(buildConfiguration).SetRepo(deployerParams.TargetRepo()).SetNpmArgs(filteredNpmArgs).SetRtDetails(rtDetails) 87 } 88 return npc.run() 89 } 90 91 func (npc *NpmPublishCommand) run() error { 92 log.Info("Running npm Publish") 93 if err := npc.preparePrerequisites(); err != nil { 94 return err 95 } 96 97 if !npc.tarballProvided { 98 if err := npc.pack(); err != nil { 99 return err 100 } 101 } 102 103 if err := npc.deploy(); err != nil { 104 if npc.tarballProvided { 105 return err 106 } 107 // We should delete the tarball we created 108 return deleteCreatedTarballAndError(npc.packedFilePath, err) 109 } 110 111 if !npc.tarballProvided { 112 if err := deleteCreatedTarball(npc.packedFilePath); err != nil { 113 return err 114 } 115 } 116 117 if !npc.collectBuildInfo { 118 log.Info("npm publish finished successfully.") 119 return nil 120 } 121 122 if err := npc.saveArtifactData(); err != nil { 123 return err 124 } 125 126 log.Info("npm publish finished successfully.") 127 return nil 128 } 129 130 func (npc *NpmPublishCommand) CommandName() string { 131 return npc.commandName 132 } 133 134 func (npc *NpmPublishCommand) preparePrerequisites() error { 135 log.Debug("Preparing prerequisites.") 136 npmExecPath, err := exec.LookPath("npm") 137 if err != nil { 138 return errorutils.CheckError(err) 139 } 140 141 if npmExecPath == "" { 142 return errorutils.CheckError(errors.New("Could not find 'npm' executable")) 143 } 144 145 npc.executablePath = npmExecPath 146 log.Debug("Using npm executable:", npc.executablePath) 147 currentDir, err := os.Getwd() 148 if err != nil { 149 return errorutils.CheckError(err) 150 } 151 152 currentDir, err = filepath.Abs(currentDir) 153 if err != nil { 154 return errorutils.CheckError(err) 155 } 156 157 npc.workingDirectory = currentDir 158 log.Debug("Working directory set to:", npc.workingDirectory) 159 npc.collectBuildInfo = len(npc.buildConfiguration.BuildName) > 0 && len(npc.buildConfiguration.BuildNumber) > 0 160 if err = npc.setPublishPath(); err != nil { 161 return err 162 } 163 164 artDetails, err := npc.rtDetails.CreateArtAuthConfig() 165 if err != nil { 166 return err 167 } 168 169 if err = utils.CheckIfRepoExists(npc.repo, artDetails); err != nil { 170 return err 171 } 172 173 return npc.setPackageInfo() 174 } 175 176 func (npc *NpmPublishCommand) pack() error { 177 log.Debug("Creating npm package.") 178 if err := npm.Pack(npc.npmArgs, npc.executablePath); err != nil { 179 return err 180 } 181 182 npc.packedFilePath = filepath.Join(npc.workingDirectory, npc.packageInfo.GetExpectedPackedFileName()) 183 log.Debug("Created npm package at", npc.packedFilePath) 184 return nil 185 } 186 187 func (npc *NpmPublishCommand) deploy() (err error) { 188 log.Debug("Deploying npm package.") 189 if err = npc.readPackageInfoFromTarball(); err != nil { 190 return err 191 } 192 193 target := fmt.Sprintf("%s/%s", npc.repo, npc.packageInfo.GetDeployPath()) 194 artifactsFileInfo, err := npc.doDeploy(target, npc.rtDetails) 195 if err != nil { 196 return err 197 } 198 199 npc.artifactData = artifactsFileInfo 200 return nil 201 } 202 203 func (npc *NpmPublishCommand) doDeploy(target string, artDetails *config.ArtifactoryDetails) (artifactsFileInfo []specutils.FileInfo, err error) { 204 servicesManager, err := utils.CreateServiceManager(artDetails, false) 205 if err != nil { 206 return nil, err 207 } 208 up := services.UploadParams{} 209 up.ArtifactoryCommonParams = &specutils.ArtifactoryCommonParams{Pattern: npc.packedFilePath, Target: target} 210 if npc.collectBuildInfo { 211 utils.SaveBuildGeneralDetails(npc.buildConfiguration.BuildName, npc.buildConfiguration.BuildNumber) 212 props, err := utils.CreateBuildProperties(npc.buildConfiguration.BuildName, npc.buildConfiguration.BuildNumber) 213 if err != nil { 214 return nil, err 215 } 216 up.ArtifactoryCommonParams.Props = props 217 } 218 artifactsFileInfo, _, failed, err := servicesManager.UploadFiles(up) 219 if err != nil { 220 return nil, err 221 } 222 223 // We deploying only one Artifact which have to be deployed, in case of failure we should fail 224 if failed > 0 { 225 return nil, errorutils.CheckError(errors.New("Failed to upload the npm package to Artifactory. See Artifactory logs for more details.")) 226 } 227 return artifactsFileInfo, nil 228 } 229 230 func (npc *NpmPublishCommand) saveArtifactData() error { 231 log.Debug("Saving npm package artifact build info data.") 232 var buildArtifacts []buildinfo.Artifact 233 for _, artifact := range npc.artifactData { 234 buildArtifacts = append(buildArtifacts, artifact.ToBuildArtifacts()) 235 } 236 237 populateFunc := func(partial *buildinfo.Partial) { 238 partial.Artifacts = buildArtifacts 239 if npc.buildConfiguration.Module == "" { 240 npc.buildConfiguration.Module = npc.packageInfo.BuildInfoModuleId() 241 } 242 partial.ModuleId = npc.buildConfiguration.Module 243 } 244 return utils.SavePartialBuildInfo(npc.buildConfiguration.BuildName, npc.buildConfiguration.BuildNumber, populateFunc) 245 } 246 247 func (npc *NpmPublishCommand) setPublishPath() error { 248 log.Debug("Reading Package Json.") 249 250 npc.publishPath = npc.workingDirectory 251 if len(npc.npmArgs) > 0 && !strings.HasPrefix(strings.TrimSpace(npc.npmArgs[0]), "-") { 252 path := strings.TrimSpace(npc.npmArgs[0]) 253 path = clientutils.ReplaceTildeWithUserHome(path) 254 255 if filepath.IsAbs(path) { 256 npc.publishPath = path 257 } else { 258 npc.publishPath = filepath.Join(npc.workingDirectory, path) 259 } 260 } 261 return nil 262 } 263 264 func (npc *NpmPublishCommand) setPackageInfo() error { 265 log.Debug("Setting Package Info.") 266 fileInfo, err := os.Stat(npc.publishPath) 267 if err != nil { 268 return errorutils.CheckError(err) 269 } 270 271 if fileInfo.IsDir() { 272 npc.packageInfo, err = npm.ReadPackageInfoFromPackageJson(npc.publishPath) 273 return err 274 } 275 log.Debug("The provided path is not a directory, we assume this is a compressed npm package") 276 npc.tarballProvided = true 277 npc.packedFilePath = npc.publishPath 278 return npc.readPackageInfoFromTarball() 279 } 280 281 func (npc *NpmPublishCommand) readPackageInfoFromTarball() error { 282 log.Debug("Extracting info from npm package:", npc.packedFilePath) 283 tarball, err := os.Open(npc.packedFilePath) 284 if err != nil { 285 return errorutils.CheckError(err) 286 } 287 defer tarball.Close() 288 gZipReader, err := gzip.NewReader(tarball) 289 if err != nil { 290 return errorutils.CheckError(err) 291 } 292 293 tarReader := tar.NewReader(gZipReader) 294 for { 295 hdr, err := tarReader.Next() 296 if err != nil { 297 if err == io.EOF { 298 return errorutils.CheckError(errors.New("Could not find 'package.json' in the compressed npm package: " + npc.packedFilePath)) 299 } 300 return errorutils.CheckError(err) 301 } 302 parent := filepath.Dir(hdr.Name) 303 if filepath.Base(parent) == "package" && strings.HasSuffix(hdr.Name, "package.json") { 304 packageJson, err := ioutil.ReadAll(tarReader) 305 if err != nil { 306 return errorutils.CheckError(err) 307 } 308 309 npc.packageInfo, err = npm.ReadPackageInfo(packageJson) 310 return err 311 } 312 } 313 } 314 315 func deleteCreatedTarballAndError(packedFilePath string, currentError error) error { 316 if err := deleteCreatedTarball(packedFilePath); err != nil { 317 errorText := fmt.Sprintf("Two errors occurred: \n%s \n%s", currentError, err) 318 return errorutils.CheckError(errors.New(errorText)) 319 } 320 return currentError 321 } 322 323 func deleteCreatedTarball(packedFilePath string) error { 324 if err := os.Remove(packedFilePath); err != nil { 325 return errorutils.CheckError(err) 326 } 327 log.Debug("Successfully deleted the created npm package:", packedFilePath) 328 return nil 329 }