Compare commits

..

1 Commits

Author SHA1 Message Date
Caleb Cartwright
afd23abd1d tests(reuse): increase service test timeout 2022-06-12 19:14:43 -05:00
38 changed files with 385 additions and 809 deletions

View File

@@ -9,14 +9,14 @@
"version": "0.0.0",
"license": "CC0",
"dependencies": {
"@actions/core": "^1.9.0",
"@actions/core": "^1.8.2",
"@actions/github": "^5.0.3"
}
},
"node_modules/@actions/core": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz",
"integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==",
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.2.tgz",
"integrity": "sha512-FXcBL7nyik8K5ODeCKlxi+vts7torOkoDAKfeh61EAkAy1HAvwn9uVzZBY0f15YcQTcZZ2/iSGBFHEuioZWfDA==",
"dependencies": {
"@actions/http-client": "^2.0.1"
}
@@ -226,9 +226,9 @@
},
"dependencies": {
"@actions/core": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz",
"integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==",
"version": "1.8.2",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.8.2.tgz",
"integrity": "sha512-FXcBL7nyik8K5ODeCKlxi+vts7torOkoDAKfeh61EAkAy1HAvwn9uVzZBY0f15YcQTcZZ2/iSGBFHEuioZWfDA==",
"requires": {
"@actions/http-client": "^2.0.1"
}

View File

@@ -10,7 +10,7 @@
"author": "chris48s",
"license": "CC0",
"dependencies": {
"@actions/core": "^1.9.0",
"@actions/core": "^1.8.2",
"@actions/github": "^5.0.3"
}
}

View File

@@ -8,4 +8,4 @@ jobs:
- name: 'Checkout Repository'
uses: actions/checkout@v3
- name: 'Dependency Review'
uses: actions/dependency-review-action@v2
uses: actions/dependency-review-action@v1

View File

@@ -4,28 +4,6 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
---
## server-2022-07-01
- change routes for [gitlab] license and contributors badges [#8140](https://github.com/badges/shields/issues/8140)
- ci: support new circle PR url variable [#8135](https://github.com/badges/shields/issues/8135)
- increase cache length on AUR version badge, run [AUR] [#8110](https://github.com/badges/shields/issues/8110)
- Use GraphQL to fix GitHub file count badges [github] [#8112](https://github.com/badges/shields/issues/8112)
- update targets for [bower] service tests [#8107](https://github.com/badges/shields/issues/8107)
- accept version with suffix in [ore] service tests [#8106](https://github.com/badges/shields/issues/8106)
- style: unified self-managed gitlab instance name [#8105](https://github.com/badges/shields/issues/8105)
- feat: add [gitlab] contributors service [#8084](https://github.com/badges/shields/issues/8084)
- fix: display greasy fork dd correctly [#8088](https://github.com/badges/shields/issues/8088)
- [greasyfork] Add Greasy Fork service badges [#8080](https://github.com/badges/shields/issues/8080)
- Fix typos in endpoint badge docs [#8085](https://github.com/badges/shields/issues/8085)
- fix: gitlab licence service docs and example [#8083](https://github.com/badges/shields/issues/8083)
- add docstrings for endpoint-common service [#8079](https://github.com/badges/shields/issues/8079)
- fix(gitlab service test): fix gitlab pipeline & tag(nested group) service test, run [gitlabtag gitlabpipeline] [#8076](https://github.com/badges/shields/issues/8076)
- Add [gitlablicense] services [#8024](https://github.com/badges/shields/issues/8024)
- [Spack] Package Manager: Update Domain [#8046](https://github.com/badges/shields/issues/8046)
- Docstrings for dynamic-common service [#8027](https://github.com/badges/shields/issues/8027)
- switch [jitpack] to use latestOk endpoint [#8041](https://github.com/badges/shields/issues/8041)
- Dependency updates
## server-2022-06-01
- Update GitLab logo (2022) [#7984](https://github.com/badges/shields/issues/7984)

View File

@@ -59,9 +59,7 @@ function _inferPullRequestFromTravisEnv(env) {
}
function _inferPullRequestFromCircleEnv(env) {
return parseGithubPullRequestUrl(
env.CI_PULL_REQUEST || env.CIRCLE_PULL_REQUEST
)
return parseGithubPullRequestUrl(env.CI_PULL_REQUEST)
}
/**

View File

@@ -134,7 +134,7 @@ export default function EndpointPage(): JSX.Element {
</p>
<p>
The endpoint badge is a better alternative than redirecting to the
static badge endpoint or generating SVG on your server:
static badge enpoint or generating SVG on your server:
</p>
<ol>
<li>
@@ -142,7 +142,7 @@ export default function EndpointPage(): JSX.Element {
Content and presentation are separate.
</a>{' '}
The service provider authors the badge, and Shields takes input from
the user to format it. As a service provider, you author the badge
the user to format it. As a service provider you author the badge
but don't have to concern yourself with styling. You don't even have
to pass the formatting options through to Shields.
</li>
@@ -152,12 +152,12 @@ export default function EndpointPage(): JSX.Element {
</li>
<li>
A JSON response is easy to implement; easier than an HTTP redirect.
It is trivial in almost any framework and is more compatible with
It is trivial in almost any framework, and is more compatible with
hosting environments such as{' '}
<a href="https://runkit.com/docs/endpoint">RunKit endpoints</a>.
</li>
<li>
As a service provider, you can rely on the Shields CDN. There's no
As a service provider you can rely on the Shields CDN. There's no
need to study the HTTP headers. Adjusting cache behavior is as
simple as setting a property in the JSON response.
</li>
@@ -197,7 +197,7 @@ export default function EndpointPage(): JSX.Element {
<dd>
Default: <code>false</code>. <code>true</code> to treat this as an
error badge. This prevents the user from overriding the color. In the
future, it may affect cache behavior.
future it may affect cache behavior.
</dd>
<dt>namedLogo</dt>
<dd>

View File

@@ -1,4 +1,4 @@
import * as originalSimpleIcons from 'simple-icons/icons'
import originalSimpleIcons from 'simple-icons'
import { svg2base64 } from './svg-helpers.js'
function loadSimpleIcons() {
@@ -14,10 +14,10 @@ function loadSimpleIcons() {
// https://github.com/badges/shields/issues/4273
Object.keys(originalSimpleIcons).forEach(key => {
const icon = originalSimpleIcons[key]
const { title, slug, hex } = icon
const title = icon.title.toLowerCase()
const legacyTitle = title.replace(/ /g, '-')
icon.base64 = {
default: svg2base64(icon.svg.replace('<svg', `<svg fill="#${hex}"`)),
default: svg2base64(icon.svg.replace('<svg', `<svg fill="#${icon.hex}"`)),
light: svg2base64(icon.svg.replace('<svg', `<svg fill="whitesmoke"`)),
dark: svg2base64(icon.svg.replace('<svg', `<svg fill="#333"`)),
}
@@ -26,17 +26,14 @@ function loadSimpleIcons() {
// (e.g. 'Hive'). If a by-title reference we generate for
// backwards compatibility collides with a proper slug from Simple Icons
// then do nothing, so that the proper slug will always map to the correct icon.
// Starting in v7, the exported object with the full icon set has updated the keys
// to include a lowercase `si` prefix, and utilizes proper case naming conventions.
if (!(`si${title}` in originalSimpleIcons)) {
simpleIcons[title.toLowerCase()] = icon
if (!(title in originalSimpleIcons)) {
simpleIcons[title] = icon
}
const legacyTitle = title.replace(/ /g, '-')
if (!(`si${legacyTitle}` in originalSimpleIcons)) {
simpleIcons[legacyTitle.toLowerCase()] = icon
if (!(legacyTitle in originalSimpleIcons)) {
simpleIcons[legacyTitle] = icon
}
simpleIcons[slug] = icon
simpleIcons[key] = icon
})
return simpleIcons
}

572
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@
"@fontsource/lato": "^4.5.8",
"@fontsource/lekton": "^4.5.9",
"@renovate/pep440": "^1.0.0",
"@sentry/node": "^7.2.0",
"@sentry/node": "^7.1.1",
"@shields_io/camp": "^18.1.1",
"badge-maker": "file:badge-maker",
"bytes": "^3.1.2",
@@ -61,7 +61,7 @@
"qs": "^6.10.5",
"query-string": "^7.1.1",
"semver": "~7.3.7",
"simple-icons": "7.2.0",
"simple-icons": "6.23.0",
"webextension-store-meta": "^1.0.5",
"xmldom": "~0.6.0",
"xpath": "~0.0.32"
@@ -141,7 +141,7 @@
]
},
"devDependencies": {
"@babel/core": "^7.18.5",
"@babel/core": "^7.18.2",
"@babel/polyfill": "^7.12.1",
"@babel/register": "7.17.7",
"@istanbuljs/schema": "^0.1.3",
@@ -155,7 +155,7 @@
"@types/react-modal": "^3.13.1",
"@types/react-select": "^4.0.17",
"@types/styled-components": "5.1.25",
"@typescript-eslint/eslint-plugin": "^5.28.0",
"@typescript-eslint/eslint-plugin": "^5.27.1",
"@typescript-eslint/parser": "^5.27.0",
"babel-plugin-inline-react-svg": "^2.0.1",
"babel-preset-gatsby": "^2.14.0",
@@ -167,8 +167,8 @@
"chai-string": "^1.4.0",
"child-process-promise": "^2.2.1",
"clipboard-copy": "^4.0.1",
"concurrently": "^7.2.2",
"cypress": "^10.2.0",
"concurrently": "^7.2.1",
"cypress": "^10.0.3",
"danger": "^11.0.7",
"danger-plugin-no-test-shortcuts": "^2.0.0",
"deepmerge": "^4.2.2",
@@ -180,13 +180,13 @@
"eslint-plugin-chai-friendly": "^0.7.2",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsdoc": "^39.3.3",
"eslint-plugin-jsdoc": "^39.3.2",
"eslint-plugin-mocha": "^10.0.5",
"eslint-plugin-no-extension-in-require": "^0.2.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.2.0",
"eslint-plugin-react": "^7.30.1",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react": "^7.30.0",
"eslint-plugin-react-hooks": "^4.5.0",
"eslint-plugin-sort-class-members": "^1.14.1",
"fetch-ponyfill": "^7.1.0",
"form-data": "^4.0.0",
@@ -203,7 +203,7 @@
"is-svg": "^4.3.2",
"js-yaml-loader": "^1.2.2",
"jsdoc": "^3.6.10",
"lint-staged": "^13.0.2",
"lint-staged": "^13.0.1",
"lodash.debounce": "^4.0.8",
"lodash.difference": "^4.5.0",
"minimist": "^1.2.6",
@@ -211,13 +211,13 @@
"mocha-env-reporter": "^4.0.0",
"mocha-junit-reporter": "^2.0.2",
"mocha-yaml-loader": "^1.0.3",
"nock": "13.2.7",
"nock": "13.2.6",
"node-mocks-http": "^1.11.0",
"nodemon": "^2.0.18",
"nodemon": "^2.0.16",
"npm-run-all": "^4.1.5",
"open-cli": "^7.0.1",
"portfinder": "^1.0.28",
"prettier": "2.7.1",
"prettier": "2.6.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-error-overlay": "^6.0.11",
@@ -236,8 +236,8 @@
"start-server-and-test": "1.14.0",
"styled-components": "^5.3.5",
"ts-mocha": "^10.0.0",
"tsd": "^0.21.0",
"typescript": "^4.7.4",
"tsd": "^0.20.0",
"typescript": "^4.7.3",
"url": "^0.11.0"
},
"engines": {

View File

@@ -108,7 +108,9 @@ class AurVotes extends BaseAurService {
class AurVersion extends BaseAurService {
static category = 'version'
static route = { base: 'aur/version', pattern: ':packageName' }
static examples = [
{
title: 'AUR version',
@@ -117,8 +119,6 @@ class AurVersion extends BaseAurService {
},
]
static _cacheLength = 3600
static render({ version, outOfDate }) {
const color = outOfDate === null ? 'blue' : 'orange'
return { message: addv(version), color }

View File

@@ -1,7 +1,5 @@
import {
isVPlusDottedVersionAtLeastOne,
isVPlusDottedVersionNClausesWithOptionalSuffix,
} from '../test-validators.js'
import Joi from 'joi'
import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
import { ServiceTester } from '../tester.js'
export const t = new ServiceTester({
id: 'BowerVersion',
@@ -9,17 +7,21 @@ export const t = new ServiceTester({
pathPrefix: '/bower',
})
t.create('version').timeout(10000).get('/v/angular.json').expectBadge({
const isBowerPrereleaseVersion = Joi.string().regex(
/^v\d+(\.\d+)?(\.\d+)?(-?[.\w\d])+?$/
)
t.create('version').timeout(10000).get('/v/bootstrap.json').expectBadge({
label: 'bower',
message: isVPlusDottedVersionAtLeastOne,
})
t.create('pre version')
t.create('pre version') // e.g. bower|v0.2.5-alpha-rc-pre
.timeout(10000)
.get('/v/angular.json?include_prereleases')
.get('/v/bootstrap.json?include_prereleases')
.expectBadge({
label: 'bower',
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
message: isBowerPrereleaseVersion,
})
t.create('Version for Invalid Package')

View File

@@ -1,9 +1,3 @@
/**
* Common functions and utilities for tasks related to endpoint badges.
*
* @module
*/
import Joi from 'joi'
import validate from '../core/base-service/validate.js'
import { InvalidResponse } from './index.js'
@@ -20,11 +14,6 @@ const optionalNumberWhenAnyLogoPresent = Joi.alternatives()
.conditional('namedLogo', { is: Joi.string().required(), then: Joi.number() })
.conditional('logoSvg', { is: Joi.string().required(), then: Joi.number() })
/**
* Joi schema for validating endpoint.
*
* @type {object}
*/
const endpointSchema = Joi.object({
schemaVersion: 1,
label: Joi.string().allow('').required(),
@@ -44,18 +33,9 @@ const endpointSchema = Joi.object({
.oxor('namedLogo', 'logoSvg')
.required()
/**
* Strictly validate the data according to the endpoint schema.
* This rejects unknown/invalid keys.
* Optionally it prints those keys in the message to provide detailed feedback.
*
* @param {object} data Object containing the data for validation
* @param {object} attrs Refer to individual attributes
* @param {string} [attrs.prettyErrorMessage] If provided then error message is set to this value
* @param {boolean} [attrs.includeKeys] If true then includes error details in error message, defaults to false
* @throws {InvalidResponse|Error} Error if Joi validation fails due to invalid or no schema
* @returns {object} Value if Joi validation is success
*/
// Strictly validate according to the endpoint schema. This rejects unknown /
// invalid keys. Optionally it prints those keys in the message in order to
// provide detailed feedback.
function validateEndpointData(
data,
{ prettyErrorMessage = 'invalid response data', includeKeys = false } = {}
@@ -76,17 +56,6 @@ function validateEndpointData(
const anySchema = Joi.any()
/**
* Fetches data from the endpoint and validates the data.
*
* @param {object} serviceInstance Instance of Endpoint class
* @param {object} attrs Refer to individual attributes
* @param {string} attrs.url Endpoint URL
* @param {object} attrs.errorMessages Object containing error messages for different error codes
* @param {string} attrs.validationPrettyErrorMessage If provided then the error message is set to this value
* @param {boolean} attrs.includeKeys If true then includes error details in error message
* @returns {object} Data fetched from endpoint
*/
async function fetchEndpointData(
serviceInstance,
{ url, errorMessages, validationPrettyErrorMessage, includeKeys }

View File

@@ -1,11 +1,11 @@
import path from 'path'
import Joi from 'joi'
import gql from 'graphql-tag'
import { metric } from '../text-formatters.js'
import { InvalidParameter } from '../index.js'
import { GithubAuthV4Service } from './github-auth-service.js'
import { ConditionalGithubAuthV3Service } from './github-auth-service.js'
import {
documentation as commonDocumentation,
transformErrors,
errorMessagesFor,
} from './github-helpers.js'
const documentation = `${commonDocumentation}
@@ -22,29 +22,28 @@ const documentation = `${commonDocumentation}
</p>
`
const schema = Joi.object({
data: Joi.object({
repository: Joi.object({
object: Joi.object({
entries: Joi.array().items(
Joi.object({
type: Joi.string().required(),
extension: Joi.string().allow('').required(),
})
),
const schema = Joi.alternatives(
/*
alternative empty object schema to provide a custom error message
in the event a file path is provided by the user instead of a directory
*/
Joi.object({}).required(),
Joi.array()
.items(
Joi.object({
path: Joi.string().required(),
type: Joi.string().required(),
})
.allow(null)
.required(),
}).required(),
}).required(),
}).required()
)
.required()
)
const queryParamSchema = Joi.object({
type: Joi.any().valid('dir', 'file'),
extension: Joi.string(),
})
export default class GithubDirectoryFileCount extends GithubAuthV4Service {
export default class GithubDirectoryFileCount extends ConditionalGithubAuthV3Service {
static category = 'size'
static route = {
@@ -104,7 +103,7 @@ export default class GithubDirectoryFileCount extends GithubAuthV4Service {
title: 'GitHub repo file count (file extension)',
pattern: ':user/:repo/:path',
namedParams: { user: 'badges', repo: 'shields', path: 'services' },
queryParams: { type: 'file', extension: 'js' },
queryParams: { extension: 'js' },
staticPreview: this.render({ count: 1 }),
documentation,
},
@@ -119,25 +118,10 @@ export default class GithubDirectoryFileCount extends GithubAuthV4Service {
}
async fetch({ user, repo, path = '' }) {
const expression = `HEAD:${path}`
return this._requestGraphql({
query: gql`
query RepoFiles($user: String!, $repo: String!, $expression: String!) {
repository(owner: $user, name: $repo) {
object(expression: $expression) {
... on Tree {
entries {
type
extension
}
}
}
}
}
`,
variables: { user, repo, expression },
return this._requestJson({
url: `/repos/${user}/${repo}/contents/${path}`,
schema,
transformErrors,
errorMessages: errorMessagesFor('repo or directory not found'),
})
}
@@ -153,12 +137,11 @@ export default class GithubDirectoryFileCount extends GithubAuthV4Service {
}
if (type) {
const objectType = type === 'dir' ? 'tree' : 'blob'
files = files.filter(file => file.type === objectType)
files = files.filter(file => file.type === type)
}
if (extension) {
files = files.filter(file => file.extension === `.${extension}`)
files = files.filter(file => path.extname(file.path) === `.${extension}`)
}
return {
@@ -167,13 +150,7 @@ export default class GithubDirectoryFileCount extends GithubAuthV4Service {
}
async handle({ user, repo, path }, { type, extension }) {
const json = await this.fetch({ user, repo, path })
if (json.data.repository.object === null) {
throw new InvalidParameter({
prettyMessage: 'directory not found',
})
}
const content = json.data.repository.object.entries
const content = await this.fetch({ user, repo, path })
const { count } = this.constructor.transform(content, { type, extension })
return this.constructor.render({ count })
}

View File

@@ -5,12 +5,12 @@ import GithubDirectoryFileCount from './github-directory-file-count.service.js'
describe('GithubDirectoryFileCount', function () {
const contents = [
{ extension: '', type: 'tree' },
{ extension: '', type: 'tree' },
{ extension: '.js', type: 'blob' },
{ extension: '.js', type: 'blob' },
{ extension: '.txt', type: 'blob' },
{ extension: '', type: 'commit' },
{ path: 'a', type: 'dir' },
{ path: 'b', type: 'dir' },
{ path: 'c.js', type: 'file' },
{ path: 'd.js', type: 'file' },
{ path: 'e.txt', type: 'file' },
{ path: 'f', type: 'submodule' },
]
test(GithubDirectoryFileCount.transform, () => {

View File

@@ -14,18 +14,11 @@ t.create('directory file count (custom path)')
message: isMetric,
})
t.create('directory file count (repo not found)')
.get('/badges/not_existing_repository.json')
.expectBadge({
label: 'files',
message: 'repo not found',
})
t.create('directory file count (directory not found)')
.get('/badges/shields/not_existing_directory.json')
.expectBadge({
label: 'files',
message: 'directory not found',
message: 'repo or directory not found',
})
t.create('directory file count (not a directory)')

View File

@@ -1,12 +0,0 @@
import { redirector } from '../index.js'
// https://github.com/badges/shields/issues/8138
export default redirector({
category: 'build',
route: {
base: 'gitlab/v/contributor',
pattern: ':project+',
},
transformPath: ({ project }) => `/gitlab/contributors/${project}`,
dateAdded: new Date('2022-06-29'),
})

View File

@@ -1,9 +0,0 @@
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Contributors redirect')
.get('/gitlab-org/gitlab', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader('Location', '/gitlab/contributors/gitlab-org/gitlab.svg')

View File

@@ -1,76 +0,0 @@
import Joi from 'joi'
import { optionalUrl, nonNegativeInteger } from '../validators.js'
import { renderContributorBadge } from '../contributor-count.js'
import GitLabBase from './gitlab-base.js'
const schema = Joi.object({ 'x-total': nonNegativeInteger }).required()
const queryParamSchema = Joi.object({
gitlab_url: optionalUrl,
}).required()
const documentation = `
<p>
You may use your GitLab Project Id (e.g. 278964) or your Project Path (e.g. gitlab-org/gitlab )
</p>
`
const customDocumentation = `
<p>
Note that only network-accessible jihulab.com and other self-managed GitLab instances are supported.
You may use your GitLab Project Id (e.g. 13953) or your Project Path (e.g. gitlab-cn/gitlab ) in <a href="https://jihulab.com">https://jihulab.com</a>
</p>
`
export default class GitlabContributors extends GitLabBase {
static category = 'activity'
static route = {
base: 'gitlab/contributors',
pattern: ':project+',
queryParamSchema,
}
static examples = [
{
title: 'GitLab contributors',
namedParams: {
project: 'gitlab-org/gitlab',
},
staticPreview: this.render({ contributorCount: 418 }),
documentation,
},
{
title: 'GitLab (self-managed) contributors',
queryParams: { gitlab_url: 'https://jihulab.com' },
namedParams: {
project: 'gitlab-cn/gitlab',
},
staticPreview: this.render({ contributorCount: 415 }),
documentation: customDocumentation,
},
]
static defaultBadgeData = { label: 'contributors' }
static render({ contributorCount }) {
return renderContributorBadge({ contributorCount })
}
async handle({ project }, { gitlab_url: baseUrl = 'https://gitlab.com' }) {
// https://docs.gitlab.com/ee/api/repositories.html#contributors
const { res } = await this._request({
url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
project
)}/repository/contributors`,
options: { searchParams: { page: '1', per_page: '1' } },
errorMessages: {
404: 'project not found',
},
})
const data = this.constructor._validate(res.headers, schema)
// The total number of contributors is in the `x-total` field in the headers.
// https://docs.gitlab.com/ee/api/index.html#other-pagination-headers
const contributorCount = data['x-total']
return this.constructor.render({ contributorCount })
}
}

View File

@@ -1,31 +0,0 @@
import { createServiceTester } from '../tester.js'
import { isMetric } from '../test-validators.js'
export const t = await createServiceTester()
t.create('Contributors')
.get('/guoxudong.io/shields-test/licenced-test.json')
.expectBadge({
label: 'contributors',
message: isMetric,
})
t.create('Contributors (repo not found)')
.get('/guoxudong.io/shields-test/do-not-exist.json')
.expectBadge({
label: 'contributors',
message: 'project not found',
})
t.create('Mocking the missing x-total header')
.get('/group/project.json')
.intercept(nock =>
nock('https://gitlab.com')
.get(
'/api/v4/projects/group%2Fproject/repository/contributors?page=1&per_page=1'
)
.reply(200)
)
.expectBadge({
label: 'contributors',
message: 'invalid response data',
})

View File

@@ -68,14 +68,14 @@ export default class GitlabCoverage extends BaseSvgScrapingService {
documentation,
},
{
title: 'Gitlab code coverage (self-managed)',
title: 'Gitlab code coverage (self-hosted)',
namedParams: { user: 'GNOME', repo: 'at-spi2-core', branch: 'master' },
queryParams: { gitlab_url: 'https://gitlab.gnome.org' },
staticPreview: this.render({ coverage: 93 }),
documentation,
},
{
title: 'Gitlab code coverage (self-managed, specific job)',
title: 'Gitlab code coverage (self-hosted, specific job)',
namedParams: { user: 'GNOME', repo: 'libhandy', branch: 'master' },
queryParams: {
gitlab_url: 'https://gitlab.gnome.org',

View File

@@ -1,12 +0,0 @@
import { redirector } from '../index.js'
// https://github.com/badges/shields/issues/8138
export default redirector({
category: 'build',
route: {
base: 'gitlab/v/license',
pattern: ':project+',
},
transformPath: ({ project }) => `/gitlab/license/${project}`,
dateAdded: new Date('2022-06-29'),
})

View File

@@ -1,9 +0,0 @@
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('License redirect')
.get('/gitlab-org/gitlab', {
followRedirect: false,
})
.expectStatus(301)
.expectHeader('Location', '/gitlab/license/gitlab-org/gitlab.svg')

View File

@@ -15,22 +15,21 @@ const queryParamSchema = Joi.object({
const documentation = `
<p>
You may use your GitLab Project Id (e.g. 278964) or your Project Path (e.g. gitlab-org/gitlab )
</p>
`
const customDocumentation = `
<p>
Note that only internet-accessible GitLab instances are supported, for example https://jihulab.com, https://gitlab.gnome.org, or https://gitlab.com/.
You may use your GitLab Project Id (e.g. 13953) or your Project Path (e.g. gitlab-cn/gitlab ) in <a href="https://jihulab.com">https://jihulab.com</a>
You may use your GitLab Project Id (e.g. 13083) or your Project Path (e.g. gitlab-org/gitlab-foss )
</p>
`
const commonProps = {
namedParams: {
project: 'gitlab-org/gitlab-foss',
},
documentation,
}
export default class GitlabLicense extends GitLabBase {
static category = 'license'
static route = {
base: 'gitlab/license',
base: 'gitlab/v/license',
pattern: ':project+',
queryParamSchema,
}
@@ -38,28 +37,22 @@ export default class GitlabLicense extends GitLabBase {
static examples = [
{
title: 'GitLab',
namedParams: {
project: 'gitlab-org/gitlab',
},
...commonProps,
staticPreview: {
label: 'license',
message: 'MIT License',
color: 'green',
},
documentation,
},
{
title: 'GitLab (self-managed)',
namedParams: {
project: 'gitlab-cn/gitlab',
},
title: 'GitLab (custom server)',
...commonProps,
queryParams: { gitlab_url: 'https://jihulab.com' },
staticPreview: {
label: 'license',
message: 'MIT License',
color: 'green',
},
documentation: customDocumentation,
},
]

View File

@@ -54,7 +54,7 @@ class GitlabPipelineStatus extends BaseSvgScrapingService {
documentation,
},
{
title: 'Gitlab pipeline status (self-managed)',
title: 'Gitlab pipeline status (self-hosted)',
namedParams: { project: 'GNOME/pango' },
queryParams: { gitlab_url: 'https://gitlab.gnome.org', branch: 'master' },
staticPreview: this.render({ status: 'passed' }),

View File

@@ -15,7 +15,7 @@ t.create('Pipeline status')
t.create('Pipeline status (nested groups)')
.get(
'/pipeline-status/megabyte-labs/docker/ci-pipeline/ansible.json?branch=master'
'/pipeline-status/megabyte-labs/dockerfile/ci-pipeline/ansible-lint.json?branch=master'
)
.expectBadge({
label: 'build',

View File

@@ -65,7 +65,7 @@ export default class GitLabRelease extends GitLabBase {
staticPreview: renderVersionBadge({ version: 'v5.0.0-beta.1' }),
},
{
title: 'GitLab Release (self-managed)',
title: 'GitLab Release (custom instance)',
namedParams: {
project: 'GNOME/librsvg',
},

View File

@@ -36,7 +36,7 @@ t.create('Release (release display name)')
.get('/gitlab-org/gitlab.json?display_name=release')
.expectBadge({ label: 'release', message: isGitLabDisplayVersion })
t.create('Release (custom instance)')
t.create('Release (custom instance')
.get('/GNOME/librsvg.json?gitlab_url=https://gitlab.gnome.org')
.expectBadge({ label: 'release', message: isSemver, color: 'blue' })

View File

@@ -62,7 +62,7 @@ export default class GitlabTag extends GitLabBase {
staticPreview: this.render({ version: 'v5.0.0-beta.1', sort: 'semver' }),
},
{
title: 'GitLab tag (self-managed)',
title: 'GitLab tag (custom instance)',
namedParams: {
project: 'GNOME/librsvg',
},

View File

@@ -7,7 +7,7 @@ t.create('Tag (latest by date)')
.expectBadge({ label: 'tag', message: 'v2.0.0', color: 'blue' })
t.create('Tag (nested groups)')
.get('/megabyte-labs/docker/ci-pipeline/ansible.json')
.get('/megabyte-labs/docker/ci-pipeline/ansible-lint.json')
.expectBadge({ label: 'tag', message: isSemver, color: 'blue' })
t.create('Tag (project id latest by date)')

View File

@@ -1,24 +0,0 @@
import Joi from 'joi'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
const schema = Joi.object({
daily_installs: nonNegativeInteger,
total_installs: nonNegativeInteger,
good_ratings: nonNegativeInteger,
ok_ratings: nonNegativeInteger,
bad_ratings: nonNegativeInteger,
version: Joi.string().required(),
license: Joi.string().allow(null).required(),
}).required()
export default class BaseGreasyForkService extends BaseJsonService {
static defaultBadgeData = { label: 'greasy fork' }
async fetch({ scriptId }) {
return this._requestJson({
schema,
url: `https://greasyfork.org/scripts/${scriptId}.json`,
})
}
}

View File

@@ -1,35 +0,0 @@
import { renderDownloadsBadge } from '../downloads.js'
import BaseGreasyForkService from './greasyfork-base.js'
export default class GreasyForkInstalls extends BaseGreasyForkService {
static category = 'downloads'
static route = { base: 'greasyfork', pattern: ':variant(dt|dd)/:scriptId' }
static examples = [
{
title: 'Greasy Fork',
pattern: 'dd/:scriptId',
namedParams: { scriptId: '407466' },
staticPreview: renderDownloadsBadge({ downloads: 17, interval: 'day' }),
},
{
title: 'Greasy Fork',
pattern: 'dt/:scriptId',
namedParams: { scriptId: '407466' },
staticPreview: renderDownloadsBadge({ downloads: 3420 }),
},
]
static defaultBadgeData = { label: 'installs' }
async handle({ variant, scriptId }) {
const data = await this.fetch({ scriptId })
if (variant === 'dd') {
const downloads = data.daily_installs
const interval = 'day'
return renderDownloadsBadge({ downloads, interval })
}
const downloads = data.total_installs
return renderDownloadsBadge({ downloads })
}
}

View File

@@ -1,19 +0,0 @@
import { createServiceTester } from '../tester.js'
import { isMetric, isMetricOverTimePeriod } from '../test-validators.js'
export const t = await createServiceTester()
t.create('Daily Installs')
.get('/dd/407466.json')
.expectBadge({ label: 'installs', message: isMetricOverTimePeriod })
t.create('Daily Installs (not found)')
.get('/dd/000000.json')
.expectBadge({ label: 'installs', message: 'not found' })
t.create('Total Installs')
.get('/dt/407466.json')
.expectBadge({ label: 'installs', message: isMetric })
t.create('Total Installs (not found)')
.get('/dt/000000.json')
.expectBadge({ label: 'installs', message: 'not found' })

View File

@@ -1,34 +0,0 @@
import { renderLicenseBadge } from '../licenses.js'
import { InvalidResponse } from '../index.js'
import BaseGreasyForkService from './greasyfork-base.js'
export default class GreasyForkLicense extends BaseGreasyForkService {
static category = 'license'
static route = { base: 'greasyfork', pattern: 'l/:scriptId' }
static examples = [
{
title: 'Greasy Fork',
namedParams: { scriptId: '407466' },
staticPreview: renderLicenseBadge({ licenses: ['MIT'] }),
},
]
static defaultBadgeData = { label: 'license' }
transform({ data }) {
if (data.license === null) {
throw new InvalidResponse({
prettyMessage: 'license not found',
})
}
// remove suffix " License" from data.license
return { license: data.license.replace(/ License$/, '') }
}
async handle({ scriptId }) {
const data = await this.fetch({ scriptId })
const { license } = this.transform({ data })
return renderLicenseBadge({ licenses: [license] })
}
}

View File

@@ -1,11 +0,0 @@
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('License (valid)').get('/l/407466.json').expectBadge({
label: 'license',
message: 'MIT',
})
t.create('License (not found)')
.get('/l/000000.json')
.expectBadge({ label: 'license', message: 'not found' })

View File

@@ -1,20 +0,0 @@
import { renderVersionBadge } from '../version.js'
import BaseGreasyForkService from './greasyfork-base.js'
export default class GreasyForkVersion extends BaseGreasyForkService {
static category = 'version'
static route = { base: 'greasyfork', pattern: 'v/:scriptId' }
static examples = [
{
title: 'Greasy Fork',
namedParams: { scriptId: '407466' },
staticPreview: renderVersionBadge({ version: '3.9.3' }),
},
]
async handle({ scriptId }) {
const data = await this.fetch({ scriptId })
return renderVersionBadge({ version: data.version })
}
}

View File

@@ -1,12 +0,0 @@
import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Version').get('/v/407466.json').expectBadge({
label: 'greasy fork',
message: isVPlusDottedVersionAtLeastOne,
})
t.create('Version (not found)')
.get('/v/000000.json')
.expectBadge({ label: 'greasy fork', message: 'not found' })

View File

@@ -1,10 +1,10 @@
import { isVPlusDottedVersionNClausesWithOptionalSuffix } from '../test-validators.js'
import { isVPlusDottedVersionAtLeastOne } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Nucleus (pluginId nucleus)').get('/nucleus.json').expectBadge({
label: 'version',
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
message: isVPlusDottedVersionAtLeastOne,
})
t.create('Invalid Plugin (pluginId 1)').get('/1.json').expectBadge({

View File

@@ -62,7 +62,10 @@ t.create('valid repo -- unregistered')
color: COLOR_MAP.unregistered,
})
t.create('invalid repo').get('/github.com/repo/invalid-repo.json').expectBadge({
label: 'reuse',
message: 'Not a Git repository',
})
t.create('invalid repo')
.timeout(10000)
.get('/github.com/repo/invalid-repo.json')
.expectBadge({
label: 'reuse',
message: 'Not a Git repository',
})