d6a7496f1f50b8a6d7928a0da3b3654483a774bd
[platform/upstream/nodejs.git] / deps / npm / node_modules / npm-registry-client / lib / publish.js
1
2 module.exports = publish
3
4 var path = require("path")
5   , url = require("url")
6
7 function publish (data, tarball, cb) {
8
9   var email = this.conf.get('email')
10   var auth = this.conf.get('_auth')
11   var username = this.conf.get('username')
12
13   if (!email || !auth || !username) {
14     var er = new Error("auth and email required for publishing")
15     er.code = 'ENEEDAUTH'
16     return cb(er)
17   }
18
19   // add the dist-url to the data, pointing at the tarball.
20   // if the {name} isn't there, then create it.
21   // if the {version} is already there, then fail.
22   // then:
23   // PUT the data to {config.registry}/{data.name}/{data.version}
24   var registry = this.conf.get('registry')
25
26   var fullData =
27     { _id : data.name
28     , name : data.name
29     , description : data.description
30     , "dist-tags" : {}
31     , versions : {}
32     , readme: data.readme || ""
33     , maintainers :
34       [ { name : username
35         , email : email
36         }
37       ]
38     }
39
40   var tbName = data.name + "-" + data.version + ".tgz"
41     , tbURI = data.name + "/-/" + tbName
42
43   data._id = data.name+"@"+data.version
44   data.dist = data.dist || {}
45   data.dist.tarball = url.resolve(registry, tbURI)
46                          .replace(/^https:\/\//, "http://")
47
48
49   // first try to just PUT the whole fullData, and this will fail if it's
50   // already there, because it'll be lacking a _rev, so couch'll bounce it.
51   this.request("PUT", encodeURIComponent(data.name), fullData,
52       function (er, parsed, json, response) {
53     // get the rev and then upload the attachment
54     // a 409 is expected here, if this is a new version of an existing package.
55     if (er
56         && !(response && response.statusCode === 409)
57         && !( parsed
58             && parsed.reason ===
59               "must supply latest _rev to update existing package" )) {
60       this.log.error("publish", "Failed PUT response "
61                     +(response && response.statusCode))
62       return cb(er)
63     }
64     var dataURI = encodeURIComponent(data.name)
65                 + "/" + encodeURIComponent(data.version)
66
67     var tag = data.tag || this.conf.get('tag') || "latest"
68     dataURI += "/-tag/" + tag
69
70     // let's see what versions are already published.
71     // could be that we just need to update the bin dist values.
72     this.request("GET", data.name, function (er, fullData) {
73       if (er) return cb(er)
74
75       function handle(er) {
76         if (er.message.indexOf("conflict Document update conflict.") === 0) {
77           return cb(conflictError.call(this, data._id));
78         }
79         this.log.error("publish", "Error uploading package");
80         return cb(er)
81       }
82
83       var exists = fullData.versions && fullData.versions[data.version]
84       if (exists) return cb(conflictError.call(this, data._id))
85
86       var rev = fullData._rev;
87       attach.call(this, data.name, tarball, tbName, rev, function (er) {
88         if (er) return handle.call(this, er)
89         this.log.verbose("publish", "attached", [data.name, tarball, tbName])
90         this.request("PUT", dataURI, data, function (er) {
91           if (er) return handle.call(this, er)
92           return cb(er)
93         }.bind(this))
94       }.bind(this))
95     }.bind(this))
96   }.bind(this)) // pining for fat arrows.
97 }
98
99 function conflictError (pkgid) {
100   var e = new Error("publish fail")
101   e.code = "EPUBLISHCONFLICT"
102   e.pkgid = pkgid
103   return e
104 }
105
106 function attach (doc, file, filename, rev, cb) {
107   doc = encodeURIComponent(doc)
108   var revu = "-rev/"+rev
109     , attURI = doc + "/-/" + encodeURIComponent(filename) + "/" + revu
110   this.log.verbose("uploading", [attURI, file])
111   this.upload(attURI, file, cb)
112 }