[GithubCommitsSince GithubDownloads GithubRelease] GithubDownloads supports latest release by SemVer (#5756)
* Latest release by SemVer * Tests covering altered behavior Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
79e7d2f7f2
commit
c6d97de22e
@@ -1,11 +1,18 @@
|
||||
'use strict'
|
||||
|
||||
const Joi = require('joi')
|
||||
const { nonNegativeInteger } = require('../validators')
|
||||
const { latest } = require('../version')
|
||||
const { NotFound } = require('..')
|
||||
const { errorMessagesFor } = require('./github-helpers')
|
||||
|
||||
const releaseInfoSchema = Joi.object({
|
||||
assets: Joi.array()
|
||||
.items({
|
||||
name: Joi.string().required(),
|
||||
download_count: nonNegativeInteger,
|
||||
})
|
||||
.required(),
|
||||
tag_name: Joi.string().required(),
|
||||
prerelease: Joi.boolean().required(),
|
||||
}).required()
|
||||
@@ -42,16 +49,11 @@ async function fetchReleases(serviceInstance, { user, repo }) {
|
||||
|
||||
function getLatestRelease({ releases, sort, includePrereleases }) {
|
||||
if (sort === 'semver') {
|
||||
const latestRelease = latest(
|
||||
const latestTagName = latest(
|
||||
releases.map(release => release.tag_name),
|
||||
{
|
||||
pre: includePrereleases,
|
||||
}
|
||||
{ pre: includePrereleases }
|
||||
)
|
||||
const kvpairs = Object.assign(
|
||||
...releases.map(release => ({ [release.tag_name]: release.prerelease }))
|
||||
)
|
||||
return { tag_name: latestRelease, prerelease: kvpairs[latestRelease] }
|
||||
return releases.find(({ tag_name }) => tag_name === latestTagName)
|
||||
}
|
||||
|
||||
if (!includePrereleases) {
|
||||
|
||||
@@ -6,8 +6,13 @@ const { nonNegativeInteger } = require('../validators')
|
||||
const { downloadCount: downloadCountColor } = require('../color-formatters')
|
||||
const { NotFound } = require('..')
|
||||
const { GithubAuthV3Service } = require('./github-auth-service')
|
||||
const { fetchLatestRelease } = require('./github-common-release')
|
||||
const { documentation, errorMessagesFor } = require('./github-helpers')
|
||||
|
||||
const queryParamSchema = Joi.object({
|
||||
sort: Joi.string().valid('date', 'semver').default('date'),
|
||||
}).required()
|
||||
|
||||
const releaseSchema = Joi.object({
|
||||
assets: Joi.array()
|
||||
.items({
|
||||
@@ -27,11 +32,12 @@ module.exports = class GithubDownloads extends GithubAuthV3Service {
|
||||
static route = {
|
||||
base: 'github',
|
||||
pattern: ':kind(downloads|downloads-pre)/:user/:repo/:tag*/:assetName',
|
||||
queryParamSchema,
|
||||
}
|
||||
|
||||
static examples = [
|
||||
{
|
||||
title: 'GitHub All Releases',
|
||||
title: 'GitHub all releases',
|
||||
pattern: 'downloads/:user/:repo/total',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
@@ -44,7 +50,7 @@ module.exports = class GithubDownloads extends GithubAuthV3Service {
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'GitHub Releases',
|
||||
title: 'GitHub release (latest by date)',
|
||||
pattern: 'downloads/:user/:repo/:tag/total',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
@@ -59,7 +65,23 @@ module.exports = class GithubDownloads extends GithubAuthV3Service {
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'GitHub Pre-Releases',
|
||||
title: 'GitHub release (latest by SemVer)',
|
||||
pattern: 'downloads/:user/:repo/:tag/total',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
repo: 'atom',
|
||||
tag: 'latest',
|
||||
},
|
||||
queryParams: { sort: 'semver' },
|
||||
staticPreview: this.render({
|
||||
tag: 'latest',
|
||||
assetName: 'total',
|
||||
downloadCount: 27000,
|
||||
}),
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'GitHub release (latest by date including pre-releases)',
|
||||
pattern: 'downloads-pre/:user/:repo/:tag/total',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
@@ -74,7 +96,23 @@ module.exports = class GithubDownloads extends GithubAuthV3Service {
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'GitHub Releases (by Release)',
|
||||
title: 'GitHub release (latest by SemVer including pre-releases)',
|
||||
pattern: 'downloads-pre/:user/:repo/:tag/total',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
repo: 'atom',
|
||||
tag: 'latest',
|
||||
},
|
||||
queryParams: { sort: 'semver' },
|
||||
staticPreview: this.render({
|
||||
tag: 'latest',
|
||||
assetName: 'total',
|
||||
downloadCount: 2000,
|
||||
}),
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'GitHub release (by tag)',
|
||||
pattern: 'downloads/:user/:repo/:tag/total',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
@@ -89,13 +127,13 @@ module.exports = class GithubDownloads extends GithubAuthV3Service {
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'GitHub Releases (by Asset)',
|
||||
pattern: 'downloads/:user/:repo/:tag/:path',
|
||||
title: 'GitHub release (latest by date and asset)',
|
||||
pattern: 'downloads/:user/:repo/:tag/:assetName',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
repo: 'atom',
|
||||
tag: 'latest',
|
||||
path: 'atom-amd64.deb',
|
||||
assetName: 'atom-amd64.deb',
|
||||
},
|
||||
staticPreview: this.render({
|
||||
tag: 'latest',
|
||||
@@ -105,14 +143,49 @@ module.exports = class GithubDownloads extends GithubAuthV3Service {
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'GitHub Pre-Releases (by Asset)',
|
||||
pattern: 'downloads-pre/:user/:repo/:tag/:path',
|
||||
title: 'GitHub release (latest by SemVer and asset)',
|
||||
pattern: 'downloads/:user/:repo/:tag/:assetName',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
repo: 'atom',
|
||||
tag: 'latest',
|
||||
path: 'atom-amd64.deb',
|
||||
assetName: 'atom-amd64.deb',
|
||||
},
|
||||
queryParams: { sort: 'semver' },
|
||||
staticPreview: this.render({
|
||||
tag: 'latest',
|
||||
assetName: 'atom-amd64.deb',
|
||||
downloadCount: 3000,
|
||||
}),
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title: 'GitHub release (latest by date and asset including pre-releases)',
|
||||
pattern: 'downloads-pre/:user/:repo/:tag/:assetName',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
repo: 'atom',
|
||||
tag: 'latest',
|
||||
assetName: 'atom-amd64.deb',
|
||||
},
|
||||
staticPreview: this.render({
|
||||
tag: 'latest',
|
||||
assetName: 'atom-amd64.deb',
|
||||
downloadCount: 237,
|
||||
}),
|
||||
documentation,
|
||||
},
|
||||
{
|
||||
title:
|
||||
'GitHub release (latest by SemVer and asset including pre-releases)',
|
||||
pattern: 'downloads-pre/:user/:repo/:tag/:assetName',
|
||||
namedParams: {
|
||||
user: 'atom',
|
||||
repo: 'atom',
|
||||
tag: 'latest',
|
||||
assetName: 'atom-amd64.deb',
|
||||
},
|
||||
queryParams: { sort: 'semver' },
|
||||
staticPreview: this.render({
|
||||
tag: 'latest',
|
||||
assetName: 'atom-amd64.deb',
|
||||
@@ -154,29 +227,16 @@ module.exports = class GithubDownloads extends GithubAuthV3Service {
|
||||
return { downloadCount }
|
||||
}
|
||||
|
||||
async handle({ kind, user, repo, tag, assetName }) {
|
||||
async handle({ kind, user, repo, tag, assetName }, { sort }) {
|
||||
let releases
|
||||
if (tag === 'latest' && kind === 'downloads') {
|
||||
const latestRelease = await this._requestJson({
|
||||
schema: releaseSchema,
|
||||
url: `/repos/${user}/${repo}/releases/latest`,
|
||||
errorMessages: errorMessagesFor('repo not found'),
|
||||
})
|
||||
if (tag === 'latest') {
|
||||
const include_prereleases = kind === 'downloads-pre' || undefined
|
||||
const latestRelease = await fetchLatestRelease(
|
||||
this,
|
||||
{ user, repo },
|
||||
{ sort, include_prereleases }
|
||||
)
|
||||
releases = [latestRelease]
|
||||
} else if (tag === 'latest') {
|
||||
// Keep only the latest release.
|
||||
const [latestReleaseIncludingPrereleases] = await this._requestJson({
|
||||
schema: releaseArraySchema,
|
||||
url: `/repos/${user}/${repo}/releases`,
|
||||
options: { qs: { per_page: 1 } },
|
||||
errorMessages: errorMessagesFor('repo not found'),
|
||||
})
|
||||
// Note that the API will return an empty array if there are no releases
|
||||
// https://github.com/badges/shields/issues/3786
|
||||
if (!latestReleaseIncludingPrereleases) {
|
||||
throw new NotFound({ prettyMessage: 'no releases' })
|
||||
}
|
||||
releases = [latestReleaseIncludingPrereleases]
|
||||
} else if (tag) {
|
||||
const wantedRelease = await this._requestJson({
|
||||
schema: releaseSchema,
|
||||
|
||||
@@ -4,6 +4,16 @@ const Joi = require('joi')
|
||||
const { isMetric } = require('../test-validators')
|
||||
const t = (module.exports = require('../tester').createServiceTester())
|
||||
|
||||
const mockLatestRelease = release => nock =>
|
||||
nock('https://api.github.com')
|
||||
.get('/repos/photonstorm/phaser/releases/latest')
|
||||
.reply(200, release)
|
||||
|
||||
const mockReleases = releases => nock =>
|
||||
nock('https://api.github.com')
|
||||
.get('/repos/photonstorm/phaser/releases')
|
||||
.reply(200, releases)
|
||||
|
||||
t.create('Downloads all releases')
|
||||
.get('/downloads/photonstorm/phaser/total.json')
|
||||
.expectBadge({ label: 'downloads', message: isMetric })
|
||||
@@ -28,6 +38,116 @@ t.create('downloads for latest release')
|
||||
.get('/downloads/photonstorm/phaser/latest/total.json')
|
||||
.expectBadge({ label: 'downloads@latest', message: isMetric })
|
||||
|
||||
t.create('downloads for latest release (sort by date)')
|
||||
.get('/downloads/photonstorm/phaser/latest/total.json')
|
||||
.intercept(
|
||||
mockLatestRelease({
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 5 },
|
||||
{ name: 'phaser.min.js', download_count: 7 },
|
||||
],
|
||||
tag_name: 'v3.15.1',
|
||||
prerelease: false,
|
||||
})
|
||||
)
|
||||
.expectBadge({ label: 'downloads@latest', message: '12' })
|
||||
|
||||
t.create('downloads for latest release (sort by SemVer)')
|
||||
.get('/downloads/photonstorm/phaser/latest/total.json?sort=semver')
|
||||
.intercept(
|
||||
mockReleases([
|
||||
{
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 1 },
|
||||
{ name: 'phaser.min.js', download_count: 3 },
|
||||
],
|
||||
tag_name: 'v3.16.0-rc1',
|
||||
prerelease: true,
|
||||
},
|
||||
{
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 5 },
|
||||
{ name: 'phaser.min.js', download_count: 7 },
|
||||
],
|
||||
tag_name: 'v3.15.0',
|
||||
prerelease: false,
|
||||
},
|
||||
{
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 9 },
|
||||
{ name: 'phaser.min.js', download_count: 11 },
|
||||
],
|
||||
tag_name: 'v3.15.1',
|
||||
prerelease: false,
|
||||
},
|
||||
])
|
||||
)
|
||||
.expectBadge({ label: 'downloads@latest', message: '20' })
|
||||
|
||||
t.create('downloads for latest release (sort by date including pre-releases)')
|
||||
.get('/downloads-pre/photonstorm/phaser/latest/total.json')
|
||||
.intercept(
|
||||
mockReleases([
|
||||
{
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 1 },
|
||||
{ name: 'phaser.min.js', download_count: 3 },
|
||||
],
|
||||
tag_name: 'v3.16.0-rc1',
|
||||
prerelease: true,
|
||||
},
|
||||
{
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 5 },
|
||||
{ name: 'phaser.min.js', download_count: 7 },
|
||||
],
|
||||
tag_name: 'v3.15.0',
|
||||
prerelease: false,
|
||||
},
|
||||
{
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 9 },
|
||||
{ name: 'phaser.min.js', download_count: 11 },
|
||||
],
|
||||
tag_name: 'v3.15.1',
|
||||
prerelease: false,
|
||||
},
|
||||
])
|
||||
)
|
||||
.expectBadge({ label: 'downloads@latest', message: '4' })
|
||||
|
||||
t.create('downloads for latest release (sort by SemVer including pre-releases)')
|
||||
.get('/downloads-pre/photonstorm/phaser/latest/total.json?sort=semver')
|
||||
.intercept(
|
||||
mockReleases([
|
||||
{
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 9 },
|
||||
{ name: 'phaser.min.js', download_count: 11 },
|
||||
],
|
||||
tag_name: 'v3.15.1',
|
||||
prerelease: false,
|
||||
},
|
||||
{
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 1 },
|
||||
{ name: 'phaser.min.js', download_count: 3 },
|
||||
],
|
||||
tag_name: 'v3.16.0-rc1',
|
||||
prerelease: true,
|
||||
},
|
||||
{
|
||||
assets: [
|
||||
{ name: 'phaser.js', download_count: 5 },
|
||||
{ name: 'phaser.min.js', download_count: 7 },
|
||||
],
|
||||
tag_name: 'v3.15.0',
|
||||
prerelease: false,
|
||||
},
|
||||
])
|
||||
)
|
||||
.expectBadge({ label: 'downloads@latest', message: '4' })
|
||||
|
||||
t.create('downloads-pre for latest release')
|
||||
.get('/downloads-pre/photonstorm/phaser/latest/total.json')
|
||||
.expectBadge({ label: 'downloads@latest', message: isMetric })
|
||||
|
||||
Reference in New Issue
Block a user