summaryrefslogtreecommitdiff
path: root/deps/npm/lib/utils/npm-registry-client/publish.js
blob: 86cfdc6a89980a6944769c6f1af9499e93ef3c78 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163

module.exports = publish

var request = require("./request.js")
  , GET = request.GET
  , PUT = request.PUT
  , DELETE = request.DELETE
  , reg = request.reg
  , upload = request.upload
  , log = require("../log.js")
  , path = require("path")
  , npm = require("../../npm.js")
  , url = require("url")

function publish (data, prebuilt, readme, cb) {
  if (typeof readme === "function") cb = readme, readme = null
  if (typeof prebuilt === "function") cb = prebuilt, prebuilt = null
  // add the dist-url to the data, pointing at the tarball.
  // if the {name} isn't there, then create it.
  // if the {version} is already there, then fail.
  // then:
  // PUT the data to {config.registry}/{data.name}/{data.version}
  var registry = reg()
  if (registry instanceof Error) return cb(registry)

  var fullData =
    { _id : data.name
    , name : data.name
    , description : data.description
    , "dist-tags" : {}
    , versions : {}
    , readme: readme ? "" + readme : null
    , maintainers :
      [ { name : npm.config.get("username")
        , email : npm.config.get("email")
        }
      ]
    }

  var tbName = data.name + "-" + data.version + ".tgz"
    , bd = npm.config.get("bindist")
    , pbName = data.name + "-" + data.version + "-" + bd + ".tgz"
    , tbURI = data.name + "/-/" + tbName
    , pbURI = data.name + "/-/" + pbName

  data._id = data.name+"@"+data.version
  data.dist = data.dist || {}
  data.dist.tarball = url.resolve(registry, tbURI)
                         .replace(/^https:\/\//, "http://")

  if (prebuilt && bd) {
    data.dist.bin[bd] = data.dist.bin[bd] || {}
    data.dist.bin[bd].tarball = url.resolve(registry, pbURI)
                                   .replace(/^https:\/\//, "http://")
  }




  // first try to just PUT the whole fullData, and this will fail if it's
  // already there, because it'll be lacking a _rev, so couch'll bounce it.
  PUT(encodeURIComponent(data.name), fullData,
      function (er, parsed, json, response) {
    // get the rev and then upload the attachment
    // a 409 is expected here, if this is a new version of an existing package.
    if (er
        && !(response && response.statusCode === 409)
        && !( parsed
            && parsed.reason ===
              "must supply latest _rev to update existing package" )) {
      return log.er(cb, "Failed PUT response "
                   +(response && response.statusCode))(er)
    }
    var dataURI = encodeURIComponent(data.name)
                + "/" + encodeURIComponent(data.version)

    var tag = data.tag || npm.config.get("tag")
    if (npm.config.get("pre")) dataURI += "/-pre/true"
    else if (tag) dataURI += "/-tag/" + tag
    else dataURI += "/-tag/latest"

    // let's see what verions are already published.
    // could be that we just need to update the bin dist values.
    GET(data.name, function (er, fullData) {
      if (er) return cb(er)

      var exists = fullData.versions && fullData.versions[data.version]
      if (exists) {
        log(exists._id, "Already published")
        var ebin = exists.dist.bin || {}
          , nbin = data.dist.bin || {}
          , needs = Object.keys(nbin).filter(function (bd) {
              return !ebin.hasOwnProperty(bd)
            })
        log.verbose(needs, "uploading bin dists")
        if (!needs.length) return cb(conflictError(data._id))
        // attach the needed bindists, upload the new metadata
        exists.dist.bin = ebin
        needs.forEach(function (bd) { exists.dist.bin[bd] = nbin[bd] })
        return PUT(dataURI + "/-rev/" + fullData._rev, exists, function (er) {
          if (er) return cb(er)
          attach(data.name, prebuilt, pbName, cb)
        })
      }

      PUT(dataURI, data, function (er) {
        if (er) {
          if (er.message.indexOf("conflict Document update conflict.") === 0) {
            return cb(conflictError(data._id))
          }
          return log.er(cb, "Error sending version data")(er)
        }

        var c = path.resolve(npm.cache, data.name, data.version)
          , tb = path.resolve(c, "package.tgz")

        cb = rollbackFailure(data, cb)

        log.verbose([data.name, tb, tbName], "attach 2")
        attach(data.name, tb, tbName, function (er) {
          log.verbose([er, data.name, prebuilt, pbName], "attach 3")
          if (er || !prebuilt) return cb(er)
          attach(data.name, prebuilt, pbName, cb)
        })
      })
    })
  })
}

function conflictError (pkgid) {
  var e = new Error("publish fail")
  e.errno = npm.EPUBLISHCONFLICT
  e.pkgid = pkgid
  return e
}

function attach (doc, file, filename, cb) {
  doc = encodeURIComponent(doc)
  GET(doc, function (er, d) {
    if (er) return cb(er)
    if (!d) return cb(new Error(
      "Attempting to upload to invalid doc "+doc))
    var rev = "-rev/"+d._rev
      , attURI = doc + "/-/" + encodeURIComponent(filename) + "/" + rev
    log.verbose([attURI, file], "uploading")
    upload(attURI, file, cb)
  })
}

function rollbackFailure (data, cb) { return function (er) {
  if (!er) return cb()
  npm.ROLLBACK = true
  log.error(er, "publish failed")
  log("rollback", "publish failed")
  npm.commands.unpublish([data.name+"@"+data.version], function (er_) {
    if (er_) {
      log.error(er_, "rollback failed")
      log.error( "Invalid data in registry! Please report this."
               , "rollback failed" )
    } else log("rolled back", "publish failed")
    cb(er)
  })
}}