From d5b5adceb1243f67ce10badb793e93b17ba510d2 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Thu, 17 Jan 2019 10:02:18 +0100 Subject: [PATCH 1/5] fixes audit errors and deprecation warnings --- .babelrc | 2 +- package.json | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.babelrc b/.babelrc index 48d4846..e609001 100644 --- a/.babelrc +++ b/.babelrc @@ -1,5 +1,5 @@ { - "presets": ["es2015", "stage-0"], + "presets": ["env", "stage-0"], "plugins": [ "transform-runtime" ] diff --git a/package.json b/package.json index fa3e281..5857c0a 100644 --- a/package.json +++ b/package.json @@ -28,16 +28,16 @@ "cli" ], "dependencies": { - "babel-runtime": "^6.22.0", - "commander": "^2.9.0", - "github": "^8.1.1", - "minimatch": "^3.0.3" + "@octokit/rest": "^16.9.0", + "babel-runtime": "^6.26.0", + "commander": "^2.19.0", + "minimatch": "^3.0.4" }, "devDependencies": { - "babel-cli": "^6.22.2", - "babel-plugin-transform-runtime": "^6.22.0", - "babel-preset-es2015": "^6.22.0", - "babel-preset-stage-0": "^6.22.0", - "tap": "^12.0.1" + "babel-cli": "^6.26.0", + "babel-plugin-transform-runtime": "^6.23.0", + "babel-preset-env": "^1.7.0", + "babel-preset-stage-0": "^6.24.1", + "tap": "^12.1.1" } } From fe7942ca80155ae878bf13d1ebb9110e62df73fc Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Thu, 17 Jan 2019 14:07:08 +0100 Subject: [PATCH 2/5] port to @octokit/rest --- package.json | 8 +- src/index.js | 227 ++++++++++++++++++--------------------------------- 2 files changed, 85 insertions(+), 150 deletions(-) diff --git a/package.json b/package.json index 5857c0a..51e946c 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,8 @@ "scripts": { "prepublish": "npm run build", "build": "babel --out-dir ./lib ./src", - "test": "tap test/*.js --no-timeout --node-arg=--require --node-arg=babel-register --node-arg=--require --node-arg=babel-polyfill" + "_test": "tap test/*.js --no-timeout --node-arg=--require --node-arg=babel-register --node-arg=--require --node-arg=babel-polyfill", + "test": "npm run build && node lib/index.js --owner cheton --repo github-release-cli list" }, "files": [ "bin", @@ -31,7 +32,10 @@ "@octokit/rest": "^16.9.0", "babel-runtime": "^6.26.0", "commander": "^2.19.0", - "minimatch": "^3.0.4" + "http-link-header": "^1.0.2", + "mime-types": "^2.1.21", + "minimatch": "^3.0.4", + "url-parse": "^1.4.4" }, "devDependencies": { "babel-cli": "^6.26.0", diff --git a/src/index.js b/src/index.js index af50518..6c713cc 100755 --- a/src/index.js +++ b/src/index.js @@ -1,10 +1,16 @@ /* eslint no-console: 0 */ /* eslint max-len: 0 */ import path from 'path'; -import GitHubApi from 'github'; + import program from 'commander'; import minimatch from 'minimatch'; import pkg from '../package.json'; +import * as LinkHeader from 'http-link-header'; +const URL = require('url-parse'); +import * as mime from 'mime-types'; + +import Octokit from '@octokit/rest'; +const octokit = new Octokit(); program .version(pkg.version) @@ -32,115 +38,20 @@ program.parse(process.argv); const [command, ...args] = program.args; -const github = new GitHubApi({ - version: '3.0.0', - timeout: 5000, - headers: { - 'user-agent': 'GitHub-Release-App' - } -}); - -github.authenticate({ +octokit.authenticate({ type: 'oauth', token: program.token || process.env.GITHUB_TOKEN }); -const getReleaseByTag = (options) => { - return new Promise(async (resolve, reject) => { - try { - let page = 1; - let lastPage = 1; - let foundRelease = false; +function next(response) { + if (!response.headers || !response.headers.link) return false; - do { - const releases = await getReleases({ - owner: options.owner, - repo: options.repo, - page: page, - per_page: 30 - }); - - const searchedReleases = releases.filter(r => (r.tag_name === options.tag) || (r.name === options.tag)); - if (searchedReleases.length) { - resolve(releases[0]); - foundRelease = true; - break; - } - - const pagination = (releases.meta.link || '').split(',') - .reduce((acc, link) => { - const r = link.match(/\?page=(\d)+.*rel="(\w+)"/); - if (r && r[1] && r[2]) { - const key = r[2]; - const value = Number(r[1]) || 0; - acc[key] = value; - } - return acc; - }, {}); - - if (pagination.last > 0) { - lastPage = pagination.last; - } - - ++page; - } while (page <= lastPage); - - if (!foundRelease) { - reject('Cannot find release'); - } - } catch (err) { - reject(err); - } - }); -}; - -const getReleases = (options) => { - return new Promise((resolve, reject) => { - github.repos.getReleases(options, (err, res) => { - err ? reject(err) : resolve(res); - }); - }); -}; - -const createRelease = (options) => { - return new Promise((resolve, reject) => { - github.repos.createRelease(options, (err, res) => { - err ? reject(err) : resolve(res); - }); - }); -}; - -const editRelease = (options) => { - return new Promise((resolve, reject) => { - github.repos.editRelease(options, (err, res) => { - err ? reject(err) : resolve(res); - }); - }); -}; - -const getAssets = (options) => { - return new Promise((resolve, reject) => { - github.repos.getAssets(options, (err, res) => { - err ? reject(err) : resolve(res); - }); - }); -}; - -const deleteAsset = (options) => { - return new Promise((resolve, reject) => { - github.repos.deleteAsset(options, (err, res) => { - err ? reject(err) : resolve(res); - }); - }); -}; - -const uploadAsset = (options) => { - return new Promise((resolve, reject) => { - github.repos.uploadAsset(options, (err, res) => { - err ? reject(err) : resolve(res); - }); - }); -}; + let link = LinkHeader.parse(response.headers.link).rel('next'); + if (!link) return false; + link = URL(link[0].uri, null, true).query + if (!link) return false; + return parseInt(link.page); +} const fn = { 'upload': async () => { @@ -150,7 +61,7 @@ const fn = { try { console.log('> releases#getReleaseByTag'); - release = await getReleaseByTag({ + release = await octokit.repos.getReleaseByTag({ owner: owner, repo: repo, tag: tag @@ -162,47 +73,43 @@ const fn = { try { if (!release) { console.log('> releases#createRelease'); - release = await createRelease({ - owner: owner, - repo: repo, + result = await octokit.repos.createRelease({ + owner, + repo, tag_name: tag, name: name || tag, body: body || '', draft: !!draft, prerelease: !!prerelease - }); + }) } else { - console.log('> releases#editRelease'); - let releaseOptions = { - owner: owner, - repo: repo, - id: release.id, + console.log('> releases#updateRelease'); + release = await octokit.repos.updateRelease({ + owner, + repo, + release_id: release.id, tag_name: tag, name: name || tag, - body: (body === undefined) - ? release.body || '' - : body || '', - draft: (draft === undefined) - ? !!release.draft - : false, - prerelease: (prerelease === undefined) - ? !!release.prerelease - : false - }; - release = await editRelease(releaseOptions); + body: (body === undefined) ? release.body || '' : body || '', + draft: (draft === undefined) ? !!release.draft : false, + prerelease: (prerelease === undefined) ? !!release.prerelease : false, + }) } if (files.length > 0) { - console.log('> releases#uploadAsset'); + console.log('> releases#uploadReleaseAsset'); for (let i = 0; i < files.length; ++i) { const file = files[i]; console.log('#%d name="%s" filePath="%s"', i + 1, path.basename(file), file); - await uploadAsset({ - owner: owner, - repo: repo, - id: release.id, - filePath: file, - name: path.basename(file) + + await octokit.repos.uploadReleaseAsset({ + url: release.data.upload_url, + file: fs.createReadStream(file), + headers: { + 'content-type': mime.lookup('file') || 'application/octet-stream', + 'content-length': fs.statSync(file).size, + }, + name: path.basename(asset), }); } } @@ -217,7 +124,7 @@ const fn = { try { console.log('> releases#getReleaseByTag'); - release = await getReleaseByTag({ + release = await octokit.repos.getReleaseByTag({ owner: owner, repo: repo, tag: tag @@ -228,19 +135,24 @@ const fn = { } try { - console.log('> releases#getAssets'); - const assets = await getAssets({ - owner: owner, - repo: repo, - id: release.id - }); + console.log('> releases#listAssetsForRelease'); + + let assets = []; + let _assets = null; + let page = 1; + do { + let _assets = await octokit.repos.listAssetsForRelease({owner, repo, release_id, per_page, page}) + assets = assets.concat(_assets.data) + page = next(_assets) + } while (page) + const deleteAssets = assets.filter(asset => { return patterns.some(pattern => minimatch(asset.name, pattern)); }); console.log('assets=%d, deleteAssets=%d', assets.length, deleteAssets.length); if (deleteAssets.length > 0) { - console.log('> releases#deleteAsset'); + console.log('> releases#deleteReleaseAsset'); for (let i = 0; i < deleteAssets.length; ++i) { const asset = deleteAssets[i]; console.log('#%d', i + 1, { @@ -253,17 +165,36 @@ const fn = { created_at: asset.created_at, updated_at: asset.updated_at }); - await deleteAsset({ - owner: owner, - repo: repo, - id: asset.id - }); + await octokit.repos.deleteReleaseAsset({owner, repo, asset_id: asset.id }) } } } catch (err) { console.error(err); } - } + }, + 'list': async () => { + let releases = null; + let page = 1; + do { + releases = await octokit.repos.listReleases({ owner: program.owner, repo: program.repo, page }); + for (const release of releases.data) { + console.log(`${release.name} (${release.tag_name})`); + } + + page = next(releases) + } while (page) + }, }[command]; -typeof fn === 'function' && fn(); +async function main() { + try { + typeof fn === 'function' && await fn(); + } catch (err) { + // message has token in the response + const message = err.message.replace(/https?:[^\s]*/g, (match) => match.replace(/\?.*/, '')) + console.log(message); + process.exit(1); + } +} + +main() From cae95e06d05f3e9e4186e2716f343872a96e0c46 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Thu, 17 Jan 2019 14:10:55 +0100 Subject: [PATCH 3/5] add fs --- src/index.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/index.js b/src/index.js index 6c713cc..5df302c 100755 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ /* eslint no-console: 0 */ /* eslint max-len: 0 */ import path from 'path'; +import fs from 'fs'; import program from 'commander'; import minimatch from 'minimatch'; From e1078fe064d6d9dd62158f16dbfb770c02ba4301 Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Thu, 17 Jan 2019 14:15:10 +0100 Subject: [PATCH 4/5] allow anonymous for tests --- package.json | 2 +- src/index.js | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 51e946c..8d01514 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "prepublish": "npm run build", "build": "babel --out-dir ./lib ./src", "_test": "tap test/*.js --no-timeout --node-arg=--require --node-arg=babel-register --node-arg=--require --node-arg=babel-polyfill", - "test": "npm run build && node lib/index.js --owner cheton --repo github-release-cli list" + "test": "npm run build && node lib/index.js -a --owner cheton --repo github-release-cli list" }, "files": [ "bin", diff --git a/src/index.js b/src/index.js index 5df302c..0b1fb85 100755 --- a/src/index.js +++ b/src/index.js @@ -22,6 +22,7 @@ program .option('-t, --tag ', 'tag') .option('-n, --name ', 'name') .option('-b, --body ', 'body', false) + .option('-a, --anonymous', 'Use github API without token mainly for testing', false) .option('-d, --draft [value]', 'draft', function(val) { if (String(val).toLowerCase() === 'false') { return false; @@ -39,10 +40,12 @@ program.parse(process.argv); const [command, ...args] = program.args; -octokit.authenticate({ - type: 'oauth', - token: program.token || process.env.GITHUB_TOKEN -}); +if (!program.anonymous) { + octokit.authenticate({ + type: 'oauth', + token: program.token || process.env.GITHUB_TOKEN + }); +} function next(response) { if (!response.headers || !response.headers.link) return false; From 96455bf64a62b9dc1a69438d6dc83e6abd44b3fd Mon Sep 17 00:00:00 2001 From: Emiliano Heyns Date: Thu, 17 Jan 2019 15:48:54 +0100 Subject: [PATCH 5/5] list -> list latest --- src/index.js | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/index.js b/src/index.js index 0b1fb85..6ceb73f 100755 --- a/src/index.js +++ b/src/index.js @@ -177,16 +177,10 @@ const fn = { } }, 'list': async () => { - let releases = null; - let page = 1; - do { - releases = await octokit.repos.listReleases({ owner: program.owner, repo: program.repo, page }); - for (const release of releases.data) { + const releases = await octokit.repos.listReleases({ owner: program.owner, repo: program.repo, page: 1 }); + for (const release of releases.data) { console.log(`${release.name} (${release.tag_name})`); - } - - page = next(releases) - } while (page) + } }, }[command];