UNPKG

5.57 kBJavaScriptView Raw
1const extend = require('deep-extend')
2const format = require('util').format
3const path = require('path')
4const ghReleaseAssets = require('gh-release-assets')
5const getDefaults = require('./bin/lib/get-defaults')
6const Emitter = require('events').EventEmitter
7const { Octokit } = require('@octokit/rest')
8
9const clientId = '04dac3c40b7e49b11f38'
10
11const OPTIONS = {
12 required: [
13 'auth',
14 'owner',
15 'repo',
16 'body',
17 'target_commitish',
18 'tag_name',
19 'name'
20 ],
21 valid: [],
22 defaults: {
23 dryRun: false,
24 yes: false,
25 draft: false,
26 endpoint: 'https://api.github.com',
27 prerelease: false,
28 workpath: process.cwd()
29 },
30 whitelist: [
31 'auth',
32 'owner',
33 'repo',
34 'body',
35 'target_commitish',
36 'tag_name',
37 'name',
38 'dryRun',
39 'yes',
40 'draft',
41 'endpoint',
42 'prerelease',
43 'workpath',
44 'assets'
45 ]
46}
47
48function Release (options, callback) {
49 const emitter = new Emitter()
50 if (options.cli) {
51 _Release(options, emitter, callback)
52 return emitter
53 }
54 const workpath = options.workpath || OPTIONS.defaults.workpath
55 const isEnterprise = !!options.endpoint && options.endpoint !== OPTIONS.defaults.endpoint
56 getDefaults(workpath, isEnterprise, function (err, defaults) {
57 options = extend(defaults, options)
58 if (err) {
59 return callback(err)
60 }
61 return _Release(options, emitter, callback)
62 })
63 return emitter
64}
65
66// attempt to create release
67// err if release already exists
68// return release url
69function _Release (options, emitter, callback) {
70 // validate release options
71 const errors = validate(options)
72 let err
73 if (errors.missing.length) {
74 err = new Error('missing required options: ' + errors.missing.join(', '))
75 return callback(err)
76 }
77
78 if (errors.invalid.length) {
79 err = new Error('invalid options: ' + errors.invalid.join(', '))
80 return callback(err)
81 }
82
83 // err if auth info not provided (token or user/pass)
84 if (!getToken(options)) return callback(new Error('missing auth info'))
85
86 const octokit = new Octokit({
87 auth: getToken(options),
88 baseUrl: options.endpoint
89 })
90
91 octokit.repos.getCommit({
92 owner: options.owner,
93 repo: options.repo,
94 ref: options.target_commitish
95 }).then(results => {
96 if (options.dryRun) return callback(null, options)
97
98 octokit.repos.createRelease({
99 tag_name: options.tag_name,
100 target_commitish: options.target_commitish,
101 name: options.name,
102 body: options.body,
103 draft: options.draft,
104 prerelease: options.prerelease,
105 repo: options.repo,
106 owner: options.owner
107 }).then(results => {
108 if (options.assets) {
109 const assets = options.assets.map(function (asset) {
110 if (typeof asset === 'object') {
111 return {
112 name: asset.name,
113 path: path.join(options.workpath, asset.path)
114 }
115 }
116
117 return path.join(options.workpath, asset)
118 })
119
120 const assetOptions = {
121 url: results.data.upload_url,
122 assets
123 }
124
125 if (options.auth.token) {
126 assetOptions.token = options.auth.token
127 } else {
128 assetOptions.auth = options.auth
129 }
130
131 const assetUpload = ghReleaseAssets(assetOptions, function (err) {
132 if (err) return callback(err)
133 return callback(null, results.data)
134 })
135 assetUpload.on('upload-asset', function (name) {
136 emitter.emit('upload-asset', name)
137 })
138 assetUpload.on('upload-progress', function (name, progress) {
139 emitter.emit('upload-progress', name, progress)
140 })
141 assetUpload.on('uploaded-asset', function (name) {
142 emitter.emit('uploaded-asset', name)
143 })
144 } else {
145 callback(null, results.data)
146 }
147 }).catch(err => {
148 // Create Release error handling
149 if (err.status === 404) {
150 const notFoundError = new Error(format('404 Not Found. Review gh-release oAuth Organization access: https://github.com/settings/connections/applications/%s', clientId))
151 notFoundError.wrapped = err
152 return callback(notFoundError)
153 }
154 if (err.errors) {
155 if (err.errors[0].code !== 'already_exists') {
156 return callback(err)
157 }
158
159 const errorMessage = format('Release already exists for tag %s in %s/%s', options.tag_name, options.owner, options.repo)
160 return callback(new Error(errorMessage))
161 }
162 return callback(err)
163 })
164 }).catch(err => {
165 // Check target error handling
166 if (err.status === 404) {
167 const errorMessage = format('Target commitish %s not found in %s/%s', options.target_commitish, options.owner, options.repo)
168 return callback(new Error(errorMessage))
169 } else {
170 return callback(err)
171 }
172 })
173}
174
175function validate (options) {
176 const missing = []
177 const invalid = []
178
179 function checkMissing (opt) {
180 if (!options[opt]) missing.push(opt)
181 }
182
183 function validateInput (opt) {
184 if (OPTIONS.valid[opt].indexOf(options[opt]) === -1) {
185 invalid.push(opt)
186 }
187 }
188
189 OPTIONS.required.forEach(checkMissing)
190 Object.keys(OPTIONS.valid).forEach(validateInput)
191
192 return {
193 invalid,
194 missing
195 }
196}
197
198function getToken (options) {
199 if (options.auth.token) {
200 return options.auth.token
201 } else if (options.auth.username && options.auth.password) {
202 return options.auth
203 } else {
204 return false
205 }
206}
207
208Release.OPTIONS = OPTIONS
209Release.validate = validate
210Release.clientId = clientId
211
212module.exports = Release