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
164
165
166
167
168
169
170
|
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 = ""
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)
readme = readme ? "" + readme : ""
var fullData =
{ _id : data.name
, name : data.name
, description : data.description
, "dist-tags" : {}
, versions : {}
, readme: readme
, 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)
})
}
// this way, it'll also get attached to packages that were previously
// published with a version of npm that lacked this feature.
if (!fullData.readme) {
data.readme = readme
}
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)
})
}}
|