1 | const extend = require('deep-extend')
|
2 | const format = require('util').format
|
3 | const path = require('path')
|
4 | const ghReleaseAssets = require('gh-release-assets')
|
5 | const getDefaults = require('./bin/lib/get-defaults')
|
6 | const Emitter = require('events').EventEmitter
|
7 | const { Octokit } = require('@octokit/rest')
|
8 |
|
9 | const clientId = '04dac3c40b7e49b11f38'
|
10 |
|
11 | const 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 |
|
48 | function 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 |
|
67 |
|
68 |
|
69 | function _Release (options, emitter, callback) {
|
70 |
|
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 |
|
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 |
|
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 |
|
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 |
|
175 | function 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 |
|
198 | function 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 |
|
208 | Release.OPTIONS = OPTIONS
|
209 | Release.validate = validate
|
210 | Release.clientId = clientId
|
211 |
|
212 | module.exports = Release
|