UNPKG

electron-simple-publisher

Version:

Simple way to publish releases for electron-simple-updater

198 lines (180 loc) 5.69 kB
'use strict'; const https = require('https'); const url = require('url'); const path = require('path'); const fs = require('fs'); class GithubApi { /** * @param {string} repository in format user-name/repo-name * @param {string} token github api token * @param {boolean} verbose Show debug information */ constructor(repository, token, verbose = false) { this.token = token; this.verbose = verbose; const repo = repository.replace('https://github.com/', ''); this.owner = repo.split('/')[0]; this.repo = repo.split('/')[1]; } request(route, data, reqOptions = {}) { const options = this.normalizeOptions(route, data, reqOptions); return new Promise((resolve, reject) => { if (this.verbose) { console.info(Object.assign({}, { data }, options)); } const req = https.request(options, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { let json; if (body) { try { json = JSON.parse(body); } catch (e) { if (this.verbose) { console.warn('Error parsing: ' + body); } reject(e); } } else { json = { code: res.statusCode }; } if (this.verbose) { console.info(json); } return resolve(json); }); }); req.on('error', reject); if (data) { if (typeof data.pipe === 'function') { data.on('error', reject); data.pipe(req); } else { req.write(JSON.stringify(data)); req.end(); } } }); } normalizeOptions(route, data = {}, reqOptions = {}) { const urlParams = Object.assign({}, { owner: this.owner, repo: this.repo }); const options = { host: 'api.github.com', headers: { 'User-Agent': 'electron-simple-publisher', 'authorization': 'token ' + this.token } }; if (reqOptions.headers) { Object.assign(options.headers, reqOptions.headers) } if (typeof data.pipe !== 'function') { for (let field in data) { if (!data.hasOwnProperty(field)) continue; if (field.startsWith('_')) { urlParams[field.substring(1)] = data[field]; delete data[field]; } } } const paths = route.split(' '); options.method = paths[0]; options.path = paths[1]; if (options.path.startsWith('https://')) { const urlObj = url.parse(options.path); options.host = urlObj.host; options.path = urlObj.path; } else { for (let field in urlParams) { if (!urlParams.hasOwnProperty(field)) continue; const value = urlParams[field]; options.path = options.path.replace(':' + field, value); } } return options; } /** * Upload a file to Github Releases * * Some ideas are from https://github.com/remixz/publish-release * @param {string} filePath * @param {string} tag * @return {Promise.<string>} */ releaseFile(filePath, tag) { const fileName = path.basename(filePath); let uploadUrl; return this.request('GET /repos/:owner/:repo/releases/tags/:tag', { _tag: tag }) .then((result) => { if (result && result.tag_name) { return result; } return this.request('POST /repos/:owner/:repo/releases', { tag_name: tag, name: tag }); }) .then((result) => { if (!result.upload_url) { throw new Error( 'Could not get upload_url from response: ' + JSON.stringify(result) ) } const asset = result.assets.filter(a => a.name === fileName)[0]; if (!asset) { return result; } return this.request('DELETE /repos/:owner/:repo/releases/assets/:id', { _id: asset.id }) .then((deleteResult) => { if (deleteResult.code === 204) { return result; } else { throw new Error(`Error while deleting existing ${fileName}`); } }); }) .then((result) => { uploadUrl = result.upload_url.split('{')[0] + '?name=' + fileName; const size = fs.statSync(filePath).size; const fileStream = fs.createReadStream(filePath); const options = { headers: { 'Content-Length': size, 'Content-Type': getContentTypeByFileName(filePath), }}; return this.request('POST ' + uploadUrl, fileStream, options); }) .then((result) => { if (!result.browser_download_url) { throw new Error( `Unable to upload file ${fileName} ` + JSON.stringify(result.errors) ); } return result.browser_download_url; }); } } module.exports = GithubApi; function getContentTypeByFileName(fileName) { const name = path.basename(fileName).toLowerCase(); const ext = path.extname(fileName).toLowerCase(); switch(ext) { case '.json': return 'application/json'; case '.dmg': return 'application/x-apple-diskimage'; case '.zip': return 'application/zip'; case '.exe': return 'application/x-msdownload'; case '.nupkg': return 'application/zip'; case '.appimage': return 'application/x-executable'; case '.deb': return 'application/vnd.debian.binary-package'; case '.rmp': return 'application/x-xz'; case '': return name === 'releases' ? 'text/plain' : 'application/octet-stream'; default: return 'application/octet-stream'; } }