├── Procfile ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── 4_Feature_request.md │ └── 1_Bug_report.md ├── workflows │ └── auto-approve.yml └── probot.js ├── .mocharc.yml ├── config ├── production.yml ├── development.yml ├── test.yml ├── local-shields-io-production.template.yml ├── local.template.yml ├── shields-io-production.yml └── default.yml ├── frontend ├── constants.ts ├── pages │ └── index.tsx ├── images │ └── favicon.png ├── types │ ├── assets.d.ts │ └── mapbox__react-click-to-select │ │ └── index.d.ts ├── mocha-ignore-pngs.js ├── enzyme-conf.spec.js ├── lib │ ├── supported-features.ts │ ├── redirect-legacy-routes.ts │ └── service-definitions │ │ └── index.spec.ts └── components │ ├── donate.tsx │ └── header.tsx ├── cypress └── .eslintrc.yml ├── doc └── flamegraph.png ├── spec └── proportions.png ├── badge-maker ├── .npmignore ├── index.d.ts └── index.test-d.ts ├── core ├── base-service │ ├── loader-test-fixtures │ │ ├── empty-array.fixture.js │ │ ├── empty-object.fixture.js │ │ ├── empty-undefined.fixture.js │ │ ├── empty-no-export.fixture.js │ │ ├── invalid-no-base.fixture.js │ │ ├── invalid-wrong-base.fixture.js │ │ ├── invalid-mixed.fixture.js │ │ ├── valid-class.fixture.js │ │ ├── valid-array.fixture.js │ │ └── valid-object.fixture.js │ ├── coalesce.js │ ├── to-array.js │ ├── categories.js │ ├── coalesce.spec.js │ └── json.js ├── register-chai-plugins.spec.js ├── got-test-client.js ├── unhandled-rejection.spec.js ├── server │ ├── instance-id-generator.js │ ├── error-pages │ │ ├── 500.html │ │ └── 404.html │ ├── secret-is-valid.js │ └── in-process-server-test-helpers.js ├── badge-urls │ └── path-helpers.js └── service-test-runner │ └── services-for-title.spec.js ├── .eslintignore ├── .prettierrc.yml ├── services ├── index.js ├── codacy │ └── codacy-helpers.js ├── jira │ ├── jira-common.js │ ├── jira-sprint-redirect.tester.js │ ├── jira-issue-redirect.tester.js │ ├── jira-issue-redirect.service.js │ ├── jira-sprint-redirect.service.js │ └── jira-test-helpers.js ├── gitter │ └── gitter.tester.js ├── node │ ├── testUtils │ │ ├── packageJsonTemplate.json │ │ └── packageJsonVersionsTemplate.json │ ├── node-lts.service.js │ ├── node-current.service.js │ ├── node-lts.spec.js │ └── node-current.spec.js ├── github │ ├── auth │ │ └── is-valid-token.js │ ├── github-labels.tester.js │ ├── github-followers.tester.js │ ├── github-license.spec.js │ ├── github-search.tester.js │ ├── github-repo-size.tester.js │ ├── github-all-contributors.tester.js │ ├── github-forks.tester.js │ ├── github-watchers.tester.js │ ├── github-language-count.tester.js │ ├── github-hacktoberfest.spec.js │ ├── github-issue-detail-redirect.service.js │ ├── github-size.tester.js │ ├── github-top-language.tester.js │ ├── github-contributors.tester.js │ ├── github-code-size.tester.js │ ├── github-deployments.tester.js │ ├── github-last-commit.tester.js │ └── github-lerna-json.tester.js ├── nexus │ ├── nexus-version.js │ └── nexus-redirect.service.js ├── wordpress │ ├── wordpress-platform-redirect.tester.js │ └── wordpress-platform-redirect.service.js ├── response-fixtures.js ├── tester.js ├── bstats │ ├── bstats-players.tester.js │ └── bstats-servers.tester.js ├── deprecation-helpers.js ├── nsp │ └── nsp.service.js ├── security-headers │ └── security-headers.tester.js ├── dockbit │ ├── dockbit.service.js │ └── dockbit.tester.js ├── cauditor │ ├── cauditor.service.js │ └── cauditor.tester.js ├── libscore │ ├── libscore.service.js │ └── libscore.tester.js ├── bithound │ ├── bithound.service.js │ └── bithound.tester.js ├── endpoint │ ├── endpoint-redirect.service.js │ └── endpoint-redirect.tester.js ├── magnumci │ ├── magnumci.service.js │ └── magnumci.tester.js ├── gemnasium │ ├── gemnasium.service.js │ └── gemnasium.tester.js ├── imagelayers │ ├── imagelayers.service.js │ └── imagelayers.tester.js ├── waffle │ ├── waffle-label.service.js │ ├── waffle-label-redirect.tester.js │ └── waffle-label.tester.js ├── issuestats │ ├── issuestats.service.js │ └── issuestats.tester.js ├── leanpub │ ├── leanpub-book-summary.service.js │ └── leanpub-book-summary.tester.js ├── versioneye │ ├── versioneye.service.js │ └── versioneye.tester.js ├── coverity │ ├── coverity-on-demand.service.js │ └── coverity-on-demand.tester.js ├── dotnetstatus │ ├── dotnetstatus.service.js │ └── dotnetstatus.tester.js ├── gratipay │ ├── gratipay.tester.js │ └── gratipay.service.js ├── jitpack │ ├── jitpack-downloads.service.js │ ├── jitpack-version-redirector.tester.js │ ├── jitpack-version-redirector.service.js │ ├── jitpack-version.tester.js │ └── jitpack-downloads.tester.js ├── php-eye │ ├── php-eye-hhvm.service.js │ ├── php-eye-php-version.service.js │ ├── php-eye-hhvm.tester.js │ └── php-eye-php-version.tester.js ├── cocoapods │ ├── cocoapods-apps.service.js │ ├── cocoapods-downloads.service.js │ ├── cocoapods-version.tester.js │ ├── cocoapods-apps.tester.js │ ├── cocoapods-license.tester.js │ ├── cocoapods-docs.tester.js │ └── cocoapods-base.js ├── dub │ ├── dub-license.tester.js │ └── dub-version.tester.js ├── cpan │ ├── cpan-license.tester.js │ ├── cpan.js │ └── cpan-version.service.js ├── symfony │ ├── sensiolabs-redirect.tester.js │ ├── sensiolabs-redirect.service.js │ ├── symfony-insight-base.spec.js │ ├── symfony-insight-stars.tester.js │ ├── symfony-insight-violations.tester.js │ └── symfony-insight-grade.tester.js ├── snap-ci │ ├── snap-ci.tester.js │ └── snap-ci.service.js ├── amo │ ├── amo-users.tester.js │ ├── amo-version.tester.js │ ├── amo-rating.tester.js │ └── amo-downloads.tester.js ├── lgtm │ ├── lgtm-test-helpers.js │ ├── lgtm-redirector.tester.js │ └── lgtm-redirector.service.js ├── crates │ ├── crates-version.tester.js │ ├── crates-license.tester.js │ └── crates-version.spec.js ├── twitter │ ├── twitter-redirect.tester.js │ └── twitter-redirect.service.js ├── spiget │ ├── spiget-downloads.tester.js │ ├── spiget-download-size.tester.js │ ├── spiget-latest-version.tester.js │ └── spiget-rating.tester.js ├── conda │ ├── conda-license.tester.js │ ├── conda-version.tester.js │ ├── conda-platform.tester.js │ ├── conda-downloads.tester.js │ └── conda-base.js ├── gem │ ├── gem-owner.tester.js │ └── gem-version.tester.js ├── sonar │ ├── sonar-generic.tester.js │ ├── sonar-quality-gate.spec.js │ └── sonar-documented-api-density.spec.js ├── liberapay │ ├── liberapay-patrons.tester.js │ ├── liberapay-goal.tester.js │ ├── liberapay-gives.tester.js │ ├── liberapay-receives.tester.js │ └── liberapay-goal.spec.js ├── appveyor │ ├── appveyor-build-redirect.service.js │ ├── appveyor-build-redirect.tester.js │ └── appveyor-job-build.tester.js ├── cdnjs │ └── cdnjs.tester.js ├── puppetforge │ ├── puppetforge-user-module-count.tester.js │ ├── puppetforge-user-release-count.tester.js │ ├── puppetforge-module-downloads.tester.js │ ├── puppetforge-module-version.tester.js │ └── puppetforge-module-pdk-version.tester.js ├── bit │ └── bit-components.tester.js ├── clojars │ ├── clojars-downloads.tester.js │ └── clojars-base.js ├── spack │ └── spack.tester.js ├── codeclimate │ ├── codeclimate-analysis-redirector.tester.js │ └── codeclimate-analysis-redirector.service.js ├── teamcity │ ├── teamcity-test-helpers.js │ ├── teamcity-coverage-redirect.tester.js │ └── teamcity-coverage-redirect.service.js ├── elm-package │ └── elm-package.tester.js ├── hackage │ ├── hackage-deps.tester.js │ └── hackage-version.tester.js ├── check-services.spec.js ├── chocolatey │ └── chocolatey.service.js ├── netlify │ ├── netlify.tester.js │ └── netlify.spec.js ├── maven-metadata │ ├── maven-metadata-redirect.service.js │ └── maven-metadata-redirect.tester.js ├── repology │ └── repology-repositories.tester.js ├── debug │ └── debug.tester.js ├── eclipse-marketplace │ ├── eclipse-marketplace-favorites.tester.js │ ├── eclipse-marketplace-update.tester.js │ ├── eclipse-marketplace-base.js │ └── eclipse-marketplace-version.tester.js ├── luarocks │ └── luarocks.spec.js ├── stackexchange │ ├── stackexchange-helpers.js │ ├── stackexchange-taginfo.tester.js │ ├── stackexchange-monthlyquestions.tester.js │ └── stackexchange-reputation.tester.js ├── vaadin-directory │ ├── vaadin-directory-status.tester.js │ ├── vaadin-directory-rating-count.tester.js │ ├── vaadin-directory-version.tester.js │ └── vaadin-directory-release-date.tester.js ├── discourse │ └── discourse-redirect.service.js ├── continuousphp │ ├── continuousphp.spec.js │ └── continuousphp.tester.js ├── date │ └── date.tester.js ├── offset-earth │ ├── offset-earth-carbon.tester.js │ └── offset-earth-trees.tester.js ├── beerpay │ └── beerpay.tester.js ├── cirrus │ └── cirrus.tester.js ├── resharper │ └── resharper.service.js ├── bugzilla │ └── bugzilla.spec.js ├── deprecation-helpers.spec.js ├── ansible │ ├── ansible-quality.tester.js │ └── ansible-role.tester.js ├── swagger │ └── swagger-redirect.service.js ├── contributor-count.js ├── bountysource │ └── bountysource.tester.js ├── itunes │ └── itunes.tester.js ├── npm │ ├── npm-downloads.spec.js │ └── npm-collaborators.tester.js ├── opm │ └── opm-version.tester.js ├── static-badge │ ├── static-badge.service.js │ └── query-string-static.service.js ├── depfu │ └── depfu.tester.js ├── dynamic │ ├── dynamic-helpers.js │ ├── dynamic-json.service.js │ ├── dynamic-yaml.service.js │ └── json-path.spec.js ├── pypi │ ├── pypi-wheel.tester.js │ ├── pypi-status.tester.js │ ├── pypi-implementation.tester.js │ └── pypi-format.tester.js ├── sourceforge │ └── sourceforge-open-tickets.tester.js ├── codefactor │ ├── codefactor-grade.tester.js │ └── codefactor-helpers.js ├── codecov │ ├── codecov.spec.js │ └── codecov-redirect.service.js ├── docker │ ├── docker-cloud-common-fetch.js │ ├── docker-pulls.tester.js │ └── docker-stars.tester.js ├── travis │ ├── travis-php-version-redirect.tester.js │ └── travis-php-version-redirect.service.js ├── librariesio │ ├── librariesio-sourcerank.tester.js │ ├── librariesio-dependents.tester.js │ ├── librariesio-dependent-repos.tester.js │ └── librariesio-dependencies-helpers.spec.js ├── bower │ └── bower-license.tester.js ├── keybase │ ├── keybase-pgp.tester.js │ ├── keybase-zec.tester.js │ ├── keybase-xlm.tester.js │ └── keybase-btc.tester.js ├── fedora │ └── fedora.tester.js ├── chrome-web-store │ ├── chrome-web-store-price.tester.js │ └── chrome-web-store-version.tester.js ├── jenkins │ ├── jenkins-base.js │ └── jenkins-tests-redirector.service.js ├── contributor-count.spec.js ├── youtube │ ├── youtube-views.tester.js │ └── youtube-comments.tester.js ├── homebrew │ ├── homebrew-cask.tester.js │ └── homebrew.tester.js ├── pkgreview │ └── package-rating.tester.js ├── packagist │ └── packagist-license.spec.js ├── requires │ └── requires.tester.js ├── maintenance │ └── maintenance.tester.js ├── website-status.spec.js ├── cran │ └── cran.tester.js ├── sourcegraph │ └── sourcegraph.tester.js └── cookbook │ └── cookbook.tester.js ├── .dockerignore ├── .vscode ├── extensions.json └── launch.json ├── logo ├── npm.svg ├── stackexchange.svg ├── telegram.svg ├── dependabot.svg ├── gitlab.svg └── serverfault.svg ├── .mocharc-frontend.yml ├── lib ├── svg-helpers.js ├── svg-helpers.spec.js ├── server-secrets.js └── load-simple-icons.js ├── now.json ├── cypress.json ├── .editorconfig ├── .prettierignore ├── .nycrc-frontend.json ├── jsdoc.json ├── tsconfig.json ├── scripts ├── export-service-definitions-cli.js ├── github_token_backup.fish ├── export-supported-features-cli.js ├── redis-connectivity-test.js └── benchmark-performance.js ├── gatsby-browser.js ├── .nycrc.json ├── Dockerfile └── entrypoint.spec.js /Procfile: -------------------------------------------------------------------------------- 1 | web: npm run start:server:prod 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | open_collective: shields 2 | -------------------------------------------------------------------------------- /.mocharc.yml: -------------------------------------------------------------------------------- 1 | reporter: mocha-env-reporter 2 | -------------------------------------------------------------------------------- /config/production.yml: -------------------------------------------------------------------------------- 1 | public: 2 | bind: 3 | address: '0.0.0.0' 4 | -------------------------------------------------------------------------------- /frontend/constants.ts: -------------------------------------------------------------------------------- 1 | export const baseUrl = process.env.GATSBY_BASE_URL || '' 2 | -------------------------------------------------------------------------------- /cypress/.eslintrc.yml: -------------------------------------------------------------------------------- 1 | plugins: 2 | - cypress 3 | env: 4 | cypress/globals: true 5 | -------------------------------------------------------------------------------- /doc/flamegraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bs/shields/master/doc/flamegraph.png -------------------------------------------------------------------------------- /spec/proportions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bs/shields/master/spec/proportions.png -------------------------------------------------------------------------------- /frontend/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Main from '../components/main' 2 | export default Main 3 | -------------------------------------------------------------------------------- /badge-maker/.npmignore: -------------------------------------------------------------------------------- 1 | lib/make-badge-test-helpers.js 2 | lib/**/*.spec.js 3 | index.test-d.ts 4 | -------------------------------------------------------------------------------- /frontend/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bs/shields/master/frontend/images/favicon.png -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/empty-array.fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = [] 4 | -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/empty-object.fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = {} 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /api-docs/ 2 | /build 3 | /coverage 4 | /__snapshots__ 5 | /public 6 | badge-maker/node_modules/ 7 | -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/empty-undefined.fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = undefined 4 | -------------------------------------------------------------------------------- /.prettierrc.yml: -------------------------------------------------------------------------------- 1 | semi: false 2 | singleQuote: true 3 | bracketSpacing: true 4 | endOfLine: lf 5 | arrowParens: avoid 6 | -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/empty-no-export.fixture.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | 'use strict' 3 | 4 | class BadService {} 5 | -------------------------------------------------------------------------------- /services/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const base = require('../core/base-service') 4 | 5 | module.exports = { 6 | ...base, 7 | } 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | shields.env 3 | .git/ 4 | .gitignore 5 | .vscode/ 6 | 7 | # Improve layer cacheability. 8 | Dockerfile 9 | -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/invalid-no-base.fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class BadService {} 4 | 5 | module.exports = BadService 6 | -------------------------------------------------------------------------------- /frontend/types/assets.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' 2 | declare module '*.json' 3 | 4 | // Handled by js-yaml-loader. 5 | declare module '*.yml' 6 | -------------------------------------------------------------------------------- /core/register-chai-plugins.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { use } = require('chai') 4 | 5 | use(require('chai-string')) 6 | use(require('sinon-chai')) 7 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "esbenp.prettier-vscode", 4 | "EditorConfig.EditorConfig", 5 | "dbaeumer.vscode-eslint" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /core/base-service/coalesce.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function coalesce(...candidates) { 4 | return candidates.find(c => c !== undefined && c !== null) 5 | } 6 | -------------------------------------------------------------------------------- /core/got-test-client.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const got = require('got') 4 | 5 | // https://github.com/nock/nock/issues/1523 6 | module.exports = got.extend({ retry: 0 }) 7 | -------------------------------------------------------------------------------- /logo/npm.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.mocharc-frontend.yml: -------------------------------------------------------------------------------- 1 | reporter: mocha-env-reporter 2 | require: 3 | - '@babel/polyfill' 4 | - '@babel/register' 5 | - mocha-yaml-loader 6 | - frontend/mocha-ignore-pngs 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Support Question 3 | url: https://github.com/badges/shields/discussions 4 | about: Ask a question about Shields.io 5 | -------------------------------------------------------------------------------- /lib/svg-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function svg2base64(svg) { 4 | return `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}` 5 | } 6 | 7 | module.exports = { svg2base64 } 8 | -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/invalid-wrong-base.fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | class BadBaseService {} 4 | class BadService extends BadBaseService {} 5 | 6 | module.exports = BadService 7 | -------------------------------------------------------------------------------- /services/codacy/codacy-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | 5 | const codacyGrade = Joi.equal('A', 'B', 'C', 'D', 'E', 'F') 6 | 7 | module.exports = { codacyGrade } 8 | -------------------------------------------------------------------------------- /now.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "name": "shields", 4 | "env": { 5 | "PERSISTENCE_DIR": "/tmp/persistence" 6 | }, 7 | "type": "npm", 8 | "engines": { 9 | "node": "8.x" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /services/jira/jira-common.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const authConfig = { 4 | userKey: 'jira_user', 5 | passKey: 'jira_pass', 6 | serviceKey: 'jira', 7 | } 8 | 9 | module.exports = { authConfig } 10 | -------------------------------------------------------------------------------- /config/development.yml: -------------------------------------------------------------------------------- 1 | public: 2 | bind: 3 | address: 'localhost' 4 | 5 | cors: 6 | allowedOrigin: ['http://localhost:3000'] 7 | 8 | rateLimit: false 9 | 10 | handleInternalErrors: false 11 | -------------------------------------------------------------------------------- /core/unhandled-rejection.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Cause unhandled promise rejections to fail unit tests, and print with stack 4 | // traces. 5 | process.on('unhandledRejection', error => { 6 | throw error 7 | }) 8 | -------------------------------------------------------------------------------- /frontend/mocha-ignore-pngs.js: -------------------------------------------------------------------------------- 1 | import requireHacker from 'require-hacker' 2 | 3 | // https://diessi.ca/blog/how-to-exclude-css-images-anything-from-unit-tests/ 4 | 5 | requireHacker.hook('png', () => 'module.exports = ""') 6 | -------------------------------------------------------------------------------- /cypress.json: -------------------------------------------------------------------------------- 1 | { 2 | "baseUrl": "http://localhost:3000", 3 | "fixturesFolder": false, 4 | "pluginsFile": false, 5 | "supportFile": false, 6 | "env": { 7 | "backend_url": "http://localhost:8080" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = space 9 | 10 | [*.{js,json,html,css}] 11 | charset = utf-8 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /core/server/instance-id-generator.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function generateInstanceId() { 4 | // from https://gist.github.com/gordonbrander/2230317 5 | return Math.random().toString(36).substr(2, 9) 6 | } 7 | 8 | module.exports = generateInstanceId 9 | -------------------------------------------------------------------------------- /services/gitter/gitter.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('on gitter').get('/nwjs/nw.js.json').expectBadge({ 6 | label: 'chat', 7 | message: 'on gitter', 8 | }) 9 | -------------------------------------------------------------------------------- /services/node/testUtils/packageJsonTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "engines": { 3 | "node": ">= 0.4.0" 4 | }, 5 | "maintainers": [ 6 | { 7 | "name": "jaredhanson", 8 | "email": "jaredhanson@gmail.com" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /core/server/error-pages/500.html: -------------------------------------------------------------------------------- 1 | A server error occurred 2 | 3 |

I have nothing to offer for this request

4 |

… but blood, toil, tears and sweat. 5 |

6 |

And trying again later.

7 | -------------------------------------------------------------------------------- /services/github/auth/is-valid-token.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This is only used by the TokenProviders, though probably the acceptor 4 | // should use it too. 5 | 6 | const isValidToken = t => /^[0-9a-f]{40}$/.test(t) 7 | 8 | module.exports = isValidToken 9 | -------------------------------------------------------------------------------- /services/nexus/nexus-version.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | function isSnapshotVersion(version) { 4 | const pattern = /(\d+\.)*\d-SNAPSHOT/ 5 | return version && version.match(pattern) 6 | } 7 | 8 | module.exports = { 9 | isSnapshotVersion, 10 | } 11 | -------------------------------------------------------------------------------- /frontend/enzyme-conf.spec.js: -------------------------------------------------------------------------------- 1 | import Enzyme from 'enzyme' 2 | import Adapter from 'enzyme-adapter-react-16' 3 | import chai from 'chai' 4 | import chaiEnzyme from 'chai-enzyme' 5 | 6 | Enzyme.configure({ adapter: new Adapter() }) 7 | 8 | chai.use(chaiEnzyme()) 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | package-lock.json 3 | /__snapshots__ 4 | /.next 5 | /.cache 6 | /api-docs 7 | /build 8 | /public 9 | /coverage 10 | private/*.json 11 | /.nyc_output 12 | analytics.json 13 | supported-features.json 14 | service-definitions.yml 15 | -------------------------------------------------------------------------------- /config/test.yml: -------------------------------------------------------------------------------- 1 | public: 2 | bind: 3 | address: 'localhost' 4 | port: 1111 5 | 6 | rateLimit: false 7 | 8 | redirectUrl: 'http://frontend.example.test' 9 | 10 | rasterUrl: 'http://raster.example.test' 11 | 12 | handleInternalErrors: false 13 | -------------------------------------------------------------------------------- /.nycrc-frontend.json: -------------------------------------------------------------------------------- 1 | { 2 | "reporter": ["lcov"], 3 | "all": false, 4 | "silent": true, 5 | "clean": false, 6 | "sourceMap": false, 7 | "instrument": false, 8 | "include": ["frontend/**/*.js"], 9 | "exclude": ["**/*.spec.js", "**/mocha-*.js"] 10 | } 11 | -------------------------------------------------------------------------------- /core/base-service/to-array.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function toArray(val) { 4 | if (val === undefined) { 5 | return [] 6 | } else if (Object(val) instanceof Array) { 7 | return val 8 | } else { 9 | return [val] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/auto-approve.yml: -------------------------------------------------------------------------------- 1 | name: Auto approve 2 | on: pull_request 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: chris48s/approve-bot@1.0.0 9 | with: 10 | github-token: '${{ secrets.GITHUB_TOKEN }}' 11 | -------------------------------------------------------------------------------- /services/wordpress/wordpress-platform-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('Plugin Tested WP Version (Alias)') 6 | .get('/akismet.svg') 7 | .expectRedirect('/wordpress/plugin/tested/akismet.svg') 8 | -------------------------------------------------------------------------------- /services/response-fixtures.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const invalidJSONString = '{{{{{invalid json}}' 4 | 5 | module.exports = { 6 | invalidJSON: () => [ 7 | 200, 8 | invalidJSONString, 9 | { 'Content-Type': 'application/json' }, 10 | ], 11 | invalidJSONString, 12 | } 13 | -------------------------------------------------------------------------------- /services/tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const createServiceTester = require('../core/service-test-runner/create-service-tester') 4 | const ServiceTester = require('../core/service-test-runner/service-tester') 5 | 6 | module.exports = { 7 | createServiceTester, 8 | ServiceTester, 9 | } 10 | -------------------------------------------------------------------------------- /services/bstats/bstats-players.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Players').get('/1.json').expectBadge({ 7 | label: 'players', 8 | message: isMetric, 9 | }) 10 | -------------------------------------------------------------------------------- /services/bstats/bstats-servers.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Servers').get('/1.json').expectBadge({ 7 | label: 'servers', 8 | message: isMetric, 9 | }) 10 | -------------------------------------------------------------------------------- /services/deprecation-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { Deprecated } = require('.') 4 | 5 | function enforceDeprecation(effectiveDate) { 6 | if (Date.now() >= effectiveDate.getTime()) { 7 | throw new Deprecated() 8 | } 9 | } 10 | 11 | module.exports = { 12 | enforceDeprecation, 13 | } 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "attach", 7 | "name": "Node: Nodemon", 8 | "processId": "${command:PickProcess}", 9 | "restart": true, 10 | "protocol": "inspector" 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/invalid-mixed.fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const BaseJsonService = require('../base-json') 4 | 5 | class BadBaseService {} 6 | class GoodService extends BaseJsonService {} 7 | class BadService extends BadBaseService {} 8 | 9 | module.exports = [GoodService, BadService] 10 | -------------------------------------------------------------------------------- /services/nsp/nsp.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | route: { 7 | base: 'nsp/npm', 8 | pattern: ':various*', 9 | }, 10 | label: 'nsp', 11 | category: 'other', 12 | dateAdded: new Date('2018-12-13'), 13 | }) 14 | -------------------------------------------------------------------------------- /frontend/lib/supported-features.ts: -------------------------------------------------------------------------------- 1 | import supportedFeatures from '../../supported-features.json' 2 | 3 | export const shieldsLogos = supportedFeatures.shieldsLogos as string[] 4 | export const simpleIcons = supportedFeatures.simpleIcons as string[] 5 | export const advertisedStyles = supportedFeatures.advertisedStyles as string[] 6 | -------------------------------------------------------------------------------- /services/security-headers/security-headers.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('grade of http://shields.io') 6 | .get('/security-headers.json?url=https://shields.io') 7 | .expectBadge({ label: 'security headers', message: 'F', color: 'red' }) 8 | -------------------------------------------------------------------------------- /services/dockbit/dockbit.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'build', 7 | route: { 8 | base: 'dockbit', 9 | pattern: ':various+', 10 | }, 11 | label: 'dockbit', 12 | dateAdded: new Date('2017-12-31'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/cauditor/cauditor.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'other', 7 | route: { 8 | base: 'cauditor', 9 | pattern: ':various*', 10 | }, 11 | label: 'cauditor', 12 | dateAdded: new Date('2018-02-15'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/libscore/libscore.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'rating', 7 | route: { 8 | base: 'libscore/s', 9 | pattern: ':various+', 10 | }, 11 | label: 'libscore', 12 | dateAdded: new Date('2018-09-22'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/bithound/bithound.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'dependencies', 7 | route: { 8 | base: 'bithound', 9 | pattern: ':various*', 10 | }, 11 | label: 'bithound', 12 | dateAdded: new Date('2018-07-08'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/endpoint/endpoint-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = redirector({ 6 | category: 'other', 7 | route: { 8 | base: 'badge/endpoint', 9 | pattern: '', 10 | }, 11 | transformPath: () => '/endpoint', 12 | dateAdded: new Date('2019-02-19'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/magnumci/magnumci.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'build', 7 | route: { 8 | base: 'magnumci/ci', 9 | pattern: ':various+', 10 | }, 11 | label: 'magnum ci', 12 | dateAdded: new Date('2018-07-08'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/gemnasium/gemnasium.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'dependencies', 7 | route: { 8 | base: 'gemnasium', 9 | pattern: ':various+', 10 | }, 11 | label: 'gemnasium', 12 | dateAdded: new Date('2018-05-15'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/imagelayers/imagelayers.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'size', 7 | route: { 8 | base: 'imagelayers', 9 | pattern: ':various+', 10 | }, 11 | label: 'imagelayers', 12 | dateAdded: new Date('2018-11-18'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/waffle/waffle-label.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | route: { 7 | base: 'waffle/label', 8 | pattern: ':various*', 9 | }, 10 | label: 'waffle', 11 | category: 'issue-tracking', 12 | dateAdded: new Date('2019-05-21'), 13 | }) 14 | -------------------------------------------------------------------------------- /badge-maker/index.d.ts: -------------------------------------------------------------------------------- 1 | interface Format { 2 | label?: string 3 | message: string 4 | labelColor?: string 5 | color?: string 6 | style?: 'plastic' | 'flat' | 'flat-square' | 'for-the-badge' | 'social' 7 | } 8 | 9 | export declare class ValidationError extends Error {} 10 | 11 | export declare function makeBadge(format: Format): string 12 | -------------------------------------------------------------------------------- /services/issuestats/issuestats.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'issue-tracking', 7 | route: { 8 | base: 'issuestats', 9 | format: '(?:.*?)', 10 | }, 11 | label: 'issue stats', 12 | dateAdded: new Date('2018-09-01'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/leanpub/leanpub-book-summary.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | route: { 7 | base: 'leanpub/book', 8 | pattern: ':various+', 9 | }, 10 | category: 'funding', 11 | label: 'leanpub', 12 | dateAdded: new Date('2019-12-30'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/versioneye/versioneye.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'downloads', 7 | route: { 8 | base: 'versioneye/d', 9 | pattern: ':various+', 10 | }, 11 | label: 'versioneye', 12 | dateAdded: new Date('2018-08-20'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/coverity/coverity-on-demand.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | route: { 7 | base: 'coverity/ondemand', 8 | pattern: ':various+', 9 | }, 10 | label: 'coverity', 11 | category: 'analysis', 12 | dateAdded: new Date('2018-12-18'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/dotnetstatus/dotnetstatus.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | category: 'dependencies', 7 | route: { 8 | base: 'dotnetstatus', 9 | pattern: ':various+', 10 | }, 11 | label: 'dotnet status', 12 | dateAdded: new Date('2018-04-01'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/gratipay/gratipay.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'gratipay', 7 | title: 'Gratipay', 8 | })) 9 | 10 | t.create('Receiving').get('/Gratipay.json').expectBadge({ 11 | label: 'gratipay', 12 | message: 'no longer available', 13 | }) 14 | -------------------------------------------------------------------------------- /services/jitpack/jitpack-downloads.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | route: { 7 | base: 'jitpack', 8 | pattern: ':interval(dw|dm)/:various*', 9 | }, 10 | label: 'jitpack', 11 | category: 'downloads', 12 | dateAdded: new Date('2020-04-05'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/php-eye/php-eye-hhvm.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = [ 6 | deprecatedService({ 7 | category: 'platform-support', 8 | label: 'hhvm', 9 | route: { 10 | base: 'hhvm', 11 | pattern: ':various*', 12 | }, 13 | dateAdded: new Date('2018-04-20'), 14 | }), 15 | ] 16 | -------------------------------------------------------------------------------- /services/cocoapods/cocoapods-apps.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | name: 'CocoapodsApps', 7 | category: 'other', 8 | route: { 9 | base: 'cocoapods', 10 | pattern: ':interval(aw|at)/:spec', 11 | }, 12 | label: 'apps', 13 | dateAdded: new Date('2018-01-06'), 14 | }) 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/4_Feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 💡 Feature Request 3 | about: Ideas for other new features or improvements 4 | --- 5 | 6 | :clipboard: **Description** 7 | 8 | 9 | 10 | 12 | -------------------------------------------------------------------------------- /frontend/types/mapbox__react-click-to-select/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module '@mapbox/react-click-to-select' { 2 | import * as React from 'react' 3 | 4 | export type ContainerElementType = 'span' | 'div' 5 | 6 | export interface Props { 7 | containerElement?: ContainerElementType 8 | } 9 | 10 | export default class ClickToSelect extends React.Component {} 11 | } 12 | -------------------------------------------------------------------------------- /services/dub/dub-license.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('license (valid)') 6 | .get('/vibe-d.json') 7 | .expectBadge({ label: 'license', message: 'MIT' }) 8 | 9 | t.create('license (not found)') 10 | .get('/not-a-package.json') 11 | .expectBadge({ label: 'license', message: 'not found' }) 12 | -------------------------------------------------------------------------------- /services/php-eye/php-eye-php-version.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = [ 6 | deprecatedService({ 7 | category: 'platform-support', 8 | label: 'php tested', 9 | route: { 10 | base: 'php-eye', 11 | pattern: ':various*', 12 | }, 13 | dateAdded: new Date('2018-04-20'), 14 | }), 15 | ] 16 | -------------------------------------------------------------------------------- /services/wordpress/wordpress-platform-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = redirector({ 6 | category: 'platform-support', 7 | route: { 8 | base: 'wordpress/v', 9 | pattern: ':slug', 10 | }, 11 | transformPath: ({ slug }) => `/wordpress/plugin/tested/${slug}`, 12 | dateAdded: new Date('2019-04-17'), 13 | }) 14 | -------------------------------------------------------------------------------- /services/cocoapods/cocoapods-downloads.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | module.exports = deprecatedService({ 6 | name: 'CocoapodsDownloads', 7 | category: 'downloads', 8 | route: { 9 | base: 'cocoapods', 10 | pattern: ':interval(dm|dw|dt)/:spec', 11 | }, 12 | label: 'downloads', 13 | dateAdded: new Date('2018-01-06'), 14 | }) 15 | -------------------------------------------------------------------------------- /services/magnumci/magnumci.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = new ServiceTester({ id: 'magnumci', title: 'Magnum CI' }) 6 | module.exports = t 7 | 8 | t.create('no longer available') 9 | .get('/ci/96ffb83fa700f069024921b0702e76ff.json') 10 | .expectBadge({ 11 | label: 'magnum ci', 12 | message: 'no longer available', 13 | }) 14 | -------------------------------------------------------------------------------- /lib/svg-helpers.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const { svg2base64 } = require('./svg-helpers') 5 | 6 | describe('SVG helpers', function () { 7 | test(svg2base64, () => { 8 | given('').expect( 9 | 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciLz4=' 10 | ) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /services/cpan/cpan-license.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('license (valid)').get('/Config-Augeas.json').expectBadge({ 6 | label: 'license', 7 | message: 'lgpl_2_1', 8 | }) 9 | 10 | t.create('license (not found)').get('/not-a-package.json').expectBadge({ 11 | label: 'cpan', 12 | message: 'not found', 13 | }) 14 | -------------------------------------------------------------------------------- /services/libscore/libscore.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = new ServiceTester({ id: 'libscore', title: 'libscore' }) 6 | module.exports = t 7 | 8 | t.create('no longer available (previously usage statistics)') 9 | .get('/s/jQuery.json') 10 | .expectBadge({ 11 | label: 'libscore', 12 | message: 'no longer available', 13 | }) 14 | -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/valid-class.fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const BaseJsonService = require('../base-json') 4 | 5 | class GoodService extends BaseJsonService { 6 | static get category() { 7 | return 'build' 8 | } 9 | 10 | static get route() { 11 | return { 12 | base: 'it/is', 13 | pattern: 'good', 14 | } 15 | } 16 | } 17 | 18 | module.exports = GoodService 19 | -------------------------------------------------------------------------------- /services/cauditor/cauditor.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'cauditor', 7 | title: 'Cauditor', 8 | })) 9 | 10 | t.create('no longer available') 11 | .get('/mi/matthiasmullie/scrapbook/master.json') 12 | .expectBadge({ 13 | label: 'cauditor', 14 | message: 'no longer available', 15 | }) 16 | -------------------------------------------------------------------------------- /services/symfony/sensiolabs-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'sensiolabs', 7 | title: 'SensioLabs', 8 | })) 9 | 10 | t.create('sensiolabs insight') 11 | .get('/i/45afb680-d4e6-4e66-93ea-bcfa79eb8a87.svg') 12 | .expectRedirect('/symfony/i/grade/45afb680-d4e6-4e66-93ea-bcfa79eb8a87.svg') 13 | -------------------------------------------------------------------------------- /config/local-shields-io-production.template.yml: -------------------------------------------------------------------------------- 1 | private: 2 | # These are the keys which are set on the production servers. 3 | gh_client_id: ... 4 | gh_client_secret: ... 5 | redis_url: ... 6 | sentry_dsn: ... 7 | shields_secret: ... 8 | sl_insight_userUuid: ... 9 | sl_insight_apiToken: ... 10 | twitch_client_id: ... 11 | twitch_client_secret: ... 12 | wheelmap_token: ... 13 | youtube_api_key: ... 14 | -------------------------------------------------------------------------------- /jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "exclude": [ 4 | "__snapshots__", 5 | "build", 6 | "coverage", 7 | "logo", 8 | "node_modules", 9 | "private", 10 | "public" 11 | ] 12 | }, 13 | "plugins": ["plugins/markdown"], 14 | "opts": { 15 | "destination": "api-docs/", 16 | "readme": "README.md", 17 | "recurse": true, 18 | "tutorials": "doc/" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /services/versioneye/versioneye.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = new ServiceTester({ id: 'versioneye', title: 'VersionEye' }) 6 | module.exports = t 7 | 8 | t.create('no longer available (previously dependencies status)') 9 | .get('/d/ruby/rails.json') 10 | .expectBadge({ 11 | label: 'versioneye', 12 | message: 'no longer available', 13 | }) 14 | -------------------------------------------------------------------------------- /services/gemnasium/gemnasium.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'gemnasium', 7 | title: 'gemnasium', 8 | })) 9 | 10 | t.create('no longer available (previously dependencies)') 11 | .get('/mathiasbynens/he.json') 12 | .expectBadge({ 13 | label: 'gemnasium', 14 | message: 'no longer available', 15 | }) 16 | -------------------------------------------------------------------------------- /services/snap-ci/snap-ci.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = new ServiceTester({ id: 'snap-ci', title: 'Snap CI' }) 6 | module.exports = t 7 | 8 | t.create('no longer available (previously build state)') 9 | .get('/snap-ci/ThoughtWorksStudios/eb_deployer/master.json') 10 | .expectBadge({ 11 | label: 'snap ci', 12 | message: 'no longer available', 13 | }) 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["frontend/**/*"], 3 | "exclude": [], 4 | "compilerOptions": { 5 | "target": "esnext", 6 | "module": "commonjs", 7 | "declaration": true, 8 | "outDir": "unused_ts_output", 9 | "strict": true, 10 | "allowSyntheticDefaultImports": true, 11 | "esModuleInterop": true, 12 | "jsx": "react", 13 | "typeRoots": ["node_modules/@types", "frontend/types"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /services/waffle/waffle-label-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'WaffleLabelRedirect', 7 | title: 'WaffleLabelRedirect', 8 | pathPrefix: '/waffle/label', 9 | })) 10 | 11 | t.create('waffle label redirect') 12 | .get('/waffleio/waffle.io.svg') 13 | .expectRedirect('/waffle/label/waffleio/waffle.io/ready.svg') 14 | -------------------------------------------------------------------------------- /services/amo/amo-users.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Users') 7 | .get('/IndieGala-Helper.json') 8 | .expectBadge({ label: 'users', message: isMetric }) 9 | 10 | t.create('Users (not found)') 11 | .get('/not-a-real-plugin.json') 12 | .expectBadge({ label: 'users', message: 'not found' }) 13 | -------------------------------------------------------------------------------- /services/lgtm/lgtm-test-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const data = { 4 | alerts: 0, 5 | languages: [ 6 | { lang: 'cpp', grade: 'A+' }, 7 | { lang: 'javascript', grade: 'A' }, 8 | { lang: 'java', grade: 'B' }, 9 | { lang: 'python', grade: 'C' }, 10 | { lang: 'csharp', grade: 'D' }, 11 | { lang: 'other', grade: 'E' }, 12 | { lang: 'foo' }, 13 | ], 14 | } 15 | 16 | module.exports = { 17 | data, 18 | } 19 | -------------------------------------------------------------------------------- /frontend/components/donate.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styled from 'styled-components' 3 | 4 | const Donate = styled.div` 5 | padding: 25px 50px; 6 | ` 7 | 8 | export default function DonateBox(): JSX.Element { 9 | return ( 10 | 11 | Love Shields? Please consider{' '} 12 | donating to sustain our 13 | activities 14 | 15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /logo/stackexchange.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/crates/crates-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isSemver } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('version') 7 | .get('/libc.json') 8 | .expectBadge({ label: 'crates.io', message: isSemver }) 9 | 10 | t.create('version (not found)') 11 | .get('/not-a-real-package.json') 12 | .expectBadge({ label: 'crates.io', message: 'not found' }) 13 | -------------------------------------------------------------------------------- /services/endpoint/endpoint-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'EndpointRedirect', 7 | title: 'EndpointRedirect', 8 | pathPrefix: '/badge/endpoint', 9 | })) 10 | 11 | t.create('Build: default branch') 12 | .get('.svg?url=https://example.com/badge.json') 13 | .expectRedirect('/endpoint.svg?url=https://example.com/badge.json') 14 | -------------------------------------------------------------------------------- /services/jitpack/jitpack-version-redirector.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'JitpackVersionRedirect', 7 | title: 'JitpackVersionRedirect', 8 | pathPrefix: '/jitpack/v', 9 | })) 10 | 11 | t.create('jitpack version redirect') 12 | .get('/jitpack/maven-simple.svg') 13 | .expectRedirect('/jitpack/v/github/jitpack/maven-simple.svg') 14 | -------------------------------------------------------------------------------- /services/waffle/waffle-label.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'Waffle', 7 | title: 'WaffleLabel', 8 | pathPrefix: '/waffle/label', 9 | })) 10 | 11 | t.create('no longer available') 12 | .get('/ritwickdey/vscode-live-server/bug.json') 13 | .expectBadge({ 14 | label: 'waffle', 15 | message: 'no longer available', 16 | }) 17 | -------------------------------------------------------------------------------- /scripts/export-service-definitions-cli.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const yaml = require('js-yaml') 4 | const { collectDefinitions } = require('../core/base-service/loader') 5 | 6 | const definitions = collectDefinitions() 7 | 8 | // Omit undefined 9 | // https://github.com/nodeca/js-yaml/issues/356#issuecomment-312430599 10 | const cleaned = JSON.parse(JSON.stringify(definitions)) 11 | 12 | process.stdout.write(yaml.safeDump(cleaned, { flowLevel: 5 })) 13 | -------------------------------------------------------------------------------- /services/twitter/twitter-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'TwitterUrlRedirect', 7 | title: 'TwitterUrlRedirect', 8 | pathPrefix: '/twitter/url', 9 | })) 10 | 11 | t.create('twitter') 12 | .get('/https/shields.io.svg') 13 | .expectRedirect( 14 | `/twitter/url.svg?url=${encodeURIComponent('https://shields.io')}` 15 | ) 16 | -------------------------------------------------------------------------------- /services/jitpack/jitpack-version-redirector.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | redirector({ 7 | category: 'version', 8 | route: { 9 | base: 'jitpack/v', 10 | pattern: ':groupId/:artifactId', 11 | }, 12 | transformPath: ({ groupId, artifactId }) => 13 | `/jitpack/v/github/${groupId}/${artifactId}`, 14 | dateAdded: new Date('2019-03-31'), 15 | }), 16 | ] 17 | -------------------------------------------------------------------------------- /services/spiget/spiget-downloads.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('EssentialsX (id 9089)').get('/9089.json').expectBadge({ 7 | label: 'downloads', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('Invalid Resource (id 1)').get('/1.json').expectBadge({ 12 | label: 'downloads', 13 | message: 'not found', 14 | }) 15 | -------------------------------------------------------------------------------- /config/local.template.yml: -------------------------------------------------------------------------------- 1 | # Copy this file to `config/local.yml` and fill it in! It will be gitignored. 2 | 3 | private: 4 | # The possible values are documented in `doc/server-secrets.md`. Note that 5 | # you can also set these values through environment variables, which may be 6 | # preferable for self hosting. 7 | gh_token: '...' 8 | twitch_client_id: '...' 9 | twitch_client_secret: '...' 10 | wheelmap_token: '...' 11 | youtube_api_key: '...' 12 | -------------------------------------------------------------------------------- /services/conda/conda-license.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('license') 6 | .get('/l/conda-forge/setuptools.json') 7 | .expectBadge({ label: 'license', message: 'MIT', color: 'green' }) 8 | 9 | t.create('license (invalid)') 10 | .get('/l/conda-forge/some-bogus-package-that-never-exists.json') 11 | .expectBadge({ label: 'license', message: 'not found', color: 'red' }) 12 | -------------------------------------------------------------------------------- /services/dotnetstatus/dotnetstatus.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'dotnetstatus', 7 | title: 'dotnet-status', 8 | })) 9 | 10 | t.create('no longer available (previously get package status)') 11 | .get('/gh/jaredcnance/dotnet-status/API.json') 12 | .expectBadge({ 13 | label: 'dotnet status', 14 | message: 'no longer available', 15 | }) 16 | -------------------------------------------------------------------------------- /services/gem/gem-owner.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('users (valid)') 7 | .get('/raphink.json') 8 | .expectBadge({ 9 | label: 'gems', 10 | message: Joi.string().regex(/^[0-9]+$/), 11 | }) 12 | 13 | t.create('users (not found)') 14 | .get('/not-a-package.json') 15 | .expectBadge({ label: 'gems', message: 'not found' }) 16 | -------------------------------------------------------------------------------- /services/github/github-labels.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('labels').get('/badges/shields/bug.json').expectBadge({ 6 | message: 'bug', 7 | color: '#e11d21', 8 | }) 9 | 10 | t.create('labels (repo or label not found)') 11 | .get('/badges/shields/somenonexistentlabelthatwouldneverexist.json') 12 | .expectBadge({ 13 | message: 'repo or label not found', 14 | }) 15 | -------------------------------------------------------------------------------- /services/sonar/sonar-generic.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Security Rating') 7 | .timeout(10000) 8 | .get( 9 | '/security_rating/com.luckybox:luckybox.json?server=https://sonarcloud.io' 10 | ) 11 | .expectBadge({ 12 | label: 'security rating', 13 | message: isMetric, 14 | color: 'blue', 15 | }) 16 | -------------------------------------------------------------------------------- /services/github/github-followers.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Followers').get('/webcaetano.json').expectBadge({ 7 | label: 'followers', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('Followers (user not found)').get('/PyvesB2.json').expectBadge({ 12 | label: 'followers', 13 | message: 'user not found', 14 | }) 15 | -------------------------------------------------------------------------------- /services/github/github-license.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const GithubLicense = require('./github-license.service') 5 | 6 | test(GithubLicense.render, () => { 7 | given({ license: undefined }).expect({ message: 'not specified' }) 8 | given({ license: 'NOASSERTION' }).expect({ 9 | message: 'not identifiable by github', 10 | }) 11 | given({ license: 'MIT' }).expect({ message: 'MIT', color: 'green' }) 12 | }) 13 | -------------------------------------------------------------------------------- /services/liberapay/liberapay-patrons.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Patrons (valid)').get('/Liberapay.json').expectBadge({ 7 | label: 'patrons', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('Patrons (not found)') 12 | .get('/does-not-exist.json') 13 | .expectBadge({ label: 'liberapay', message: 'not found' }) 14 | -------------------------------------------------------------------------------- /services/appveyor/appveyor-build-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | redirector({ 7 | category: 'build', 8 | route: { 9 | base: 'appveyor/ci', 10 | pattern: ':user/:repo/:branch*', 11 | }, 12 | transformPath: ({ user, repo, branch }) => 13 | `/appveyor/build/${user}/${repo}${branch ? `/${branch}` : ''}`, 14 | dateAdded: new Date('2019-12-10'), 15 | }), 16 | ] 17 | -------------------------------------------------------------------------------- /services/cdnjs/cdnjs.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusTripleDottedVersion } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('cdnjs (valid)').get('/jquery.json').expectBadge({ 7 | label: 'cdnjs', 8 | message: isVPlusTripleDottedVersion, 9 | }) 10 | 11 | t.create('cdnjs (not found)') 12 | .get('/not-a-library.json') 13 | .expectBadge({ label: 'cdnjs', message: 'not found' }) 14 | -------------------------------------------------------------------------------- /logo/telegram.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/puppetforge/puppetforge-user-module-count.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('modules by user').get('/camptocamp.json').expectBadge({ 7 | label: 'modules', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('modules by user').get('/not-a-real-user.json').expectBadge({ 12 | label: 'modules', 13 | message: 'not found', 14 | }) 15 | -------------------------------------------------------------------------------- /services/bit/bit-components.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { isMetric } = require('../test-validators') 5 | 6 | t.create('collection (valid)').get('/ramda/ramda.json').expectBadge({ 7 | label: 'components', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('collection (valid)') 12 | .get('/bit/no-collection-test.json') 13 | .expectBadge({ label: 'components', message: 'collection not found' }) 14 | -------------------------------------------------------------------------------- /services/clojars/clojars-downloads.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('clojars downloads (valid)').get('/prismic.json').expectBadge({ 7 | label: 'downloads', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('clojars downloads (not found)') 12 | .get('/not-a-package.json') 13 | .expectBadge({ label: 'downloads', message: 'not found' }) 14 | -------------------------------------------------------------------------------- /services/puppetforge/puppetforge-user-release-count.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('releases by user').get('/camptocamp.json').expectBadge({ 7 | label: 'releases', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('releases by user').get('/not-a-real-user.json').expectBadge({ 12 | label: 'releases', 13 | message: 'not found', 14 | }) 15 | -------------------------------------------------------------------------------- /frontend/lib/redirect-legacy-routes.ts: -------------------------------------------------------------------------------- 1 | import { navigate } from 'gatsby' 2 | 3 | export default function redirectLegacyRoutes(): void { 4 | const { hash } = window.location 5 | if (hash && hash.startsWith('#/examples/')) { 6 | const category = hash.replace('#/examples/', '') 7 | navigate(`category/${category}`, { 8 | replace: true, 9 | }) 10 | } else if (hash === '#/endpoint') { 11 | navigate('endpoint', { 12 | replace: true, 13 | }) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /services/gem/gem-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('version (valid)').get('/formatador.json').expectBadge({ 7 | label: 'gem', 8 | message: isVPlusDottedVersionAtLeastOne, 9 | }) 10 | 11 | t.create('version (not found)') 12 | .get('/not-a-package.json') 13 | .expectBadge({ label: 'gem', message: 'not found' }) 14 | -------------------------------------------------------------------------------- /services/spack/spack.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('version (valid)').get('/adios2.json').expectBadge({ 7 | label: 'spack', 8 | message: isVPlusDottedVersionAtLeastOne, 9 | }) 10 | 11 | t.create('version (not found)') 12 | .get('/not-a-package.json') 13 | .expectBadge({ label: 'spack', message: 'package not found' }) 14 | -------------------------------------------------------------------------------- /services/codeclimate/codeclimate-analysis-redirector.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'CodeclimateCoverageRedirector', 7 | title: 'Code Climate Coverage Redirector', 8 | pathPrefix: '/codeclimate', 9 | })) 10 | 11 | t.create('Maintainability letter alias') 12 | .get('/maintainability-letter/jekyll/jekyll.svg') 13 | .expectRedirect('/codeclimate/maintainability/jekyll/jekyll.svg') 14 | -------------------------------------------------------------------------------- /services/jira/jira-sprint-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'JiraSprintRedirect', 7 | title: 'JiraSprintRedirect', 8 | pathPrefix: '/jira/sprint', 9 | })) 10 | 11 | t.create('jira sprint') 12 | .get('/https/jira.spring.io/94.svg') 13 | .expectRedirect( 14 | `/jira/sprint/94.svg?baseUrl=${encodeURIComponent( 15 | 'https://jira.spring.io' 16 | )}` 17 | ) 18 | -------------------------------------------------------------------------------- /services/cocoapods/cocoapods-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('version (valid)').get('/AFNetworking.json').expectBadge({ 7 | label: 'pod', 8 | message: isVPlusDottedVersionAtLeastOne, 9 | }) 10 | 11 | t.create('version (not found)') 12 | .get('/not-a-package.json') 13 | .expectBadge({ label: 'pod', message: 'not found' }) 14 | -------------------------------------------------------------------------------- /services/liberapay/liberapay-goal.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isIntegerPercentage } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Goal Progress (valid)').get('/Liberapay.json').expectBadge({ 7 | label: 'goal progress', 8 | message: isIntegerPercentage, 9 | }) 10 | 11 | t.create('Goal Progress (not found)') 12 | .get('/does-not-exist.json') 13 | .expectBadge({ label: 'liberapay', message: 'not found' }) 14 | -------------------------------------------------------------------------------- /services/teamcity/teamcity-test-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const user = 'admin' 4 | const pass = 'password' 5 | const host = 'mycompany.teamcity.com' 6 | const config = { 7 | public: { 8 | services: { 9 | teamcity: { 10 | authorizedOrigins: [`https://${host}`], 11 | }, 12 | }, 13 | }, 14 | private: { 15 | teamcity_user: user, 16 | teamcity_pass: pass, 17 | }, 18 | } 19 | 20 | module.exports = { 21 | user, 22 | pass, 23 | host, 24 | config, 25 | } 26 | -------------------------------------------------------------------------------- /services/amo/amo-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Version').get('/IndieGala-Helper.json').expectBadge({ 7 | label: 'mozilla add-on', 8 | message: isVPlusDottedVersionAtLeastOne, 9 | }) 10 | 11 | t.create('Version (not found)') 12 | .get('/not-a-real-plugin.json') 13 | .expectBadge({ label: 'mozilla add-on', message: 'not found' }) 14 | -------------------------------------------------------------------------------- /services/elm-package/elm-package.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isSemver } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('gets the package version of elm/core') 7 | .get('/elm/core.json') 8 | .expectBadge({ label: 'elm package', message: isSemver }) 9 | 10 | t.create('invalid package name') 11 | .get('/elm-community/frodo-is-not-a-package.json') 12 | .expectBadge({ label: 'elm package', message: 'package not found' }) 13 | -------------------------------------------------------------------------------- /services/github/github-search.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('hit counter') 7 | .get('/badges/shields/async%20handle.json') 8 | .expectBadge({ label: 'async handle counter', message: isMetric }) 9 | 10 | t.create('hit counter for nonexistent repo') 11 | .get('/badges/puppets/async%20handle.json') 12 | .expectBadge({ label: 'counter', message: 'repo not found' }) 13 | -------------------------------------------------------------------------------- /services/hackage/hackage-deps.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('hackage deps (valid)') 7 | .get('/lens.json') 8 | .expectBadge({ 9 | label: 'dependencies', 10 | message: Joi.string().regex(/^(up to date|outdated)$/), 11 | }) 12 | 13 | t.create('hackage deps (not found)') 14 | .get('/not-a-package.json') 15 | .expectBadge({ label: 'dependencies', message: 'not found' }) 16 | -------------------------------------------------------------------------------- /services/check-services.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { 4 | checkNames, 5 | collectDefinitions, 6 | } = require('../core/base-service/loader') 7 | 8 | // When these tests fail, they will throw AssertionErrors. Wrapping them in an 9 | // `expect().not.to.throw()` makes the error output unreadable. 10 | 11 | it('Services have unique names', function () { 12 | this.timeout(30000) 13 | checkNames() 14 | }) 15 | 16 | it('Can collect the service definitions', function () { 17 | collectDefinitions() 18 | }) 19 | -------------------------------------------------------------------------------- /services/chocolatey/chocolatey.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { createServiceFamily } = require('../nuget/nuget-v2-service-family') 4 | 5 | module.exports = createServiceFamily({ 6 | defaultLabel: 'chocolatey', 7 | serviceBaseUrl: 'chocolatey', 8 | apiBaseUrl: 'https://www.chocolatey.org/api/v2', 9 | odataFormat: 'json', 10 | title: 'Chocolatey', 11 | examplePackageName: 'git', 12 | exampleVersion: '2.19.2', 13 | examplePrereleaseVersion: '2.19.2', 14 | exampleDownloadCount: 2.2e6, 15 | }) 16 | -------------------------------------------------------------------------------- /services/github/github-repo-size.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isFileSize } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('repository size').get('/badges/shields.json').expectBadge({ 7 | label: 'repo size', 8 | message: isFileSize, 9 | }) 10 | 11 | t.create('repository size (repo not found)') 12 | .get('/badges/helmets.json') 13 | .expectBadge({ 14 | label: 'repo size', 15 | message: 'repo not found', 16 | }) 17 | -------------------------------------------------------------------------------- /services/netlify/netlify.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isBuildStatus } = require('../build-status') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('netlify (valid, no branch)') 7 | .get('/e6d5a4e0-dee1-4261-833e-2f47f509c68f.json') 8 | .expectBadge({ 9 | label: 'netlify', 10 | message: isBuildStatus, 11 | }) 12 | 13 | t.create('netlify (repo not found)') 14 | .get('/not-a-repo.json') 15 | .expectBadge({ label: 'netlify', message: 'not found' }) 16 | -------------------------------------------------------------------------------- /scripts/github_token_backup.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | # 3 | # Back up the GitHub tokens from each production server. 4 | # 5 | 6 | if test (count $argv) -lt 1 7 | echo Usage: (basename (status -f)) shields_secret 8 | end 9 | 10 | set shields_secret $argv[1] 11 | 12 | function do_backup 13 | set server $argv[1] 14 | curl --insecure -u ":$shields_secret" "https://$server.servers.shields.io/\$github-auth/tokens" > "$server""_tokens.json" 15 | end 16 | 17 | for server in s0 s1 s2 18 | do_backup $server 19 | end 20 | -------------------------------------------------------------------------------- /services/jira/jira-issue-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'JiraIssueRedirect', 7 | title: 'JiraIssueRedirect', 8 | pathPrefix: '/jira/issue', 9 | })) 10 | 11 | t.create('jira issue') 12 | .get('/https/issues.apache.org/jira/kafka-2896.svg') 13 | .expectRedirect( 14 | `/jira/issue/kafka-2896.svg?baseUrl=${encodeURIComponent( 15 | 'https://issues.apache.org/jira' 16 | )}` 17 | ) 18 | -------------------------------------------------------------------------------- /services/maven-metadata/maven-metadata-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = redirector({ 6 | category: 'version', 7 | route: { 8 | base: 'maven-metadata/v', 9 | pattern: ':protocol(http|https)/:hostAndPath+', 10 | }, 11 | transformPath: () => '/maven-metadata/v', 12 | transformQueryParams: ({ protocol, hostAndPath }) => ({ 13 | metadataUrl: `${protocol}://${hostAndPath}`, 14 | }), 15 | dateAdded: new Date('2019-09-16'), 16 | }) 17 | -------------------------------------------------------------------------------- /services/sonar/sonar-quality-gate.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const SonarQualityGate = require('./sonar-quality-gate.service') 5 | 6 | describe('SonarQualityGate', function () { 7 | test(SonarQualityGate.render, () => { 8 | given({ qualityState: 'OK' }).expect({ 9 | message: 'passed', 10 | color: 'success', 11 | }) 12 | given({ qualityState: 'ERROR' }).expect({ 13 | message: 'failed', 14 | color: 'critical', 15 | }) 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /services/repology/repology-repositories.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { nonNegativeInteger } = require('../validators') 5 | 6 | t.create('Existing project').get('/starship.json').expectBadge({ 7 | label: 'repositories', 8 | message: nonNegativeInteger, 9 | }) 10 | 11 | t.create('Non-existent project') 12 | .get('/invalidprojectthatshouldnotexist.json') 13 | .expectBadge({ 14 | label: 'repositories', 15 | message: '0', 16 | }) 17 | -------------------------------------------------------------------------------- /services/conda/conda-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusTripleDottedVersion } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('version').get('/v/conda-forge/zlib.json').expectBadge({ 7 | label: 'conda|conda-forge', 8 | message: isVPlusTripleDottedVersion, 9 | }) 10 | 11 | t.create('version (skip prefix)').get('/vn/conda-forge/zlib.json').expectBadge({ 12 | label: 'conda-forge', 13 | message: isVPlusTripleDottedVersion, 14 | }) 15 | -------------------------------------------------------------------------------- /lib/server-secrets.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const path = require('path') 5 | const config = require('config').util.toObject() 6 | 7 | const legacySecretsPath = path.join(__dirname, '..', 'private', 'secret.json') 8 | if (fs.existsSync(legacySecretsPath)) { 9 | console.error( 10 | `Legacy secrets file found at ${legacySecretsPath}. It should be deleted and secrets replaced with environment variables or config/local.yml` 11 | ) 12 | process.exit(1) 13 | } 14 | 15 | module.exports = config.private 16 | -------------------------------------------------------------------------------- /services/puppetforge/puppetforge-module-downloads.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('module downloads').get('/camptocamp/openssl.json').expectBadge({ 7 | label: 'downloads', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('module downloads (not found)') 12 | .get('/notarealuser/notarealpackage.json') 13 | .expectBadge({ 14 | label: 'downloads', 15 | message: 'not found', 16 | }) 17 | -------------------------------------------------------------------------------- /services/puppetforge/puppetforge-module-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isSemver } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('module version').get('/camptocamp/openssl.json').expectBadge({ 7 | label: 'puppetforge', 8 | message: isSemver, 9 | }) 10 | 11 | t.create('module version (not found)') 12 | .get('/notarealuser/notarealpackage.json') 13 | .expectBadge({ 14 | label: 'puppetforge', 15 | message: 'not found', 16 | }) 17 | -------------------------------------------------------------------------------- /services/debug/debug.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('start time') 7 | .get('/starttime.json') 8 | .expectBadge({ label: 'start time', message: Joi.date().required() }) 9 | 10 | t.create('Flip: first request') 11 | .get('/flip.json') 12 | .expectBadge({ label: 'flip', message: 'on' }) 13 | 14 | t.create('Flip: second request') 15 | .get('/flip.json') 16 | .expectBadge({ label: 'flip', message: 'off' }) 17 | -------------------------------------------------------------------------------- /services/lgtm/lgtm-redirector.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'LgtmRedirect', 7 | title: 'LgtmRedirect', 8 | pathPrefix: '/lgtm', 9 | })) 10 | 11 | t.create('alerts') 12 | .get('/alerts/g/badges/shields.svg') 13 | .expectRedirect('/lgtm/alerts/github/badges/shields.svg') 14 | 15 | t.create('grade') 16 | .get('/grade/java/g/apache/cloudstack.svg') 17 | .expectRedirect('/lgtm/grade/java/github/apache/cloudstack.svg') 18 | -------------------------------------------------------------------------------- /services/eclipse-marketplace/eclipse-marketplace-favorites.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('favorites count').get('/notepad4e.json').expectBadge({ 7 | label: 'favorites', 8 | message: Joi.number().integer().positive(), 9 | }) 10 | 11 | t.create('favorites for unknown solution') 12 | .get('/this-does-not-exist.json') 13 | .expectBadge({ 14 | label: 'favorites', 15 | message: 'solution not found', 16 | }) 17 | -------------------------------------------------------------------------------- /services/luarocks/luarocks.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const Luarocks = require('./luarocks.service') 5 | 6 | test(Luarocks.render, () => { 7 | given({ version: 'dev-1' }).expect({ message: 'dev-1', color: 'yellow' }) 8 | given({ version: 'scm-1' }).expect({ message: 'scm-1', color: 'orange' }) 9 | given({ version: 'cvs-1' }).expect({ message: 'cvs-1', color: 'orange' }) 10 | given({ version: '0.1-1' }).expect({ 11 | message: 'v0.1-1', 12 | color: 'brightgreen', 13 | }) 14 | }) 15 | -------------------------------------------------------------------------------- /services/stackexchange/stackexchange-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { metric } = require('../text-formatters') 4 | const { floorCount: floorCountColor } = require('../color-formatters') 5 | 6 | module.exports = function renderQuestionsBadge({ 7 | suffix, 8 | stackexchangesite, 9 | query, 10 | numValue, 11 | }) { 12 | const label = `${stackexchangesite} ${query} questions` 13 | return { 14 | label, 15 | message: `${metric(numValue)}${suffix}`, 16 | color: floorCountColor(numValue, 1000, 10000, 20000), 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /services/vaadin-directory/vaadin-directory-status.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('publish status of the component') 7 | .get('/vaadinvaadin-grid.json') 8 | .expectBadge({ 9 | label: 'vaadin directory', 10 | message: Joi.equal('published', 'unpublished'), 11 | }) 12 | 13 | t.create('not found').get('/does-not-exist.json').expectBadge({ 14 | label: 'vaadin directory', 15 | message: 'not found', 16 | }) 17 | -------------------------------------------------------------------------------- /core/server/secret-is-valid.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const serverSecrets = require('../../lib/server-secrets') 4 | 5 | function constEq(a, b) { 6 | if (a.length !== b.length) { 7 | return false 8 | } 9 | let zero = 0 10 | for (let i = 0; i < a.length; i++) { 11 | zero |= a.charCodeAt(i) ^ b.charCodeAt(i) 12 | } 13 | return zero === 0 14 | } 15 | 16 | module.exports = function secretIsValid(secret = '') { 17 | return ( 18 | serverSecrets.shields_secret && 19 | constEq(secret, serverSecrets.shields_secret) 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /services/eclipse-marketplace/eclipse-marketplace-update.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isFormattedDate } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('last update date').get('/notepad4e.json').expectBadge({ 7 | label: 'updated', 8 | message: isFormattedDate, 9 | }) 10 | 11 | t.create('last update for unknown solution') 12 | .get('/this-does-not-exist.json') 13 | .expectBadge({ 14 | label: 'updated', 15 | message: 'solution not found', 16 | }) 17 | -------------------------------------------------------------------------------- /services/discourse/discourse-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | redirector({ 7 | category: 'chat', 8 | route: { 9 | base: 'discourse', 10 | pattern: ':protocol(http|https)/:hostAndPath(.+)/:metric', 11 | }, 12 | transformPath: ({ metric }) => `/discourse/${metric}`, 13 | transformQueryParams: ({ protocol, hostAndPath }) => ({ 14 | server: `${protocol}://${hostAndPath}`, 15 | }), 16 | dateAdded: new Date('2019-09-15'), 17 | }), 18 | ] 19 | -------------------------------------------------------------------------------- /services/continuousphp/continuousphp.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const ContinuousPhp = require('./continuousphp.service') 5 | 6 | describe('ContinuousPhp', function () { 7 | test(ContinuousPhp.render, () => { 8 | given({ status: 'unstable' }).expect({ 9 | label: 'build', 10 | message: 'unstable', 11 | color: 'yellow', 12 | }) 13 | given({ status: 'running' }).expect({ 14 | label: 'build', 15 | message: 'running', 16 | color: 'blue', 17 | }) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /services/eclipse-marketplace/eclipse-marketplace-base.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { BaseXmlService } = require('..') 4 | 5 | module.exports = class EclipseMarketplaceBase extends BaseXmlService { 6 | static buildRoute(base) { 7 | return { 8 | base, 9 | pattern: ':name', 10 | } 11 | } 12 | 13 | async fetch({ name, schema }) { 14 | return this._requestXml({ 15 | schema, 16 | url: `https://marketplace.eclipse.org/content/${name}/api/p`, 17 | errorMessages: { 404: 'solution not found' }, 18 | }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /services/twitter/twitter-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | redirector({ 7 | category: 'social', 8 | name: 'TwitterUrlRedirect', 9 | route: { 10 | base: 'twitter/url', 11 | pattern: ':protocol(https|http)/:hostAndPath+', 12 | }, 13 | transformPath: () => `/twitter/url`, 14 | transformQueryParams: ({ protocol, hostAndPath }) => ({ 15 | url: `${protocol}://${hostAndPath}`, 16 | }), 17 | dateAdded: new Date('2019-09-17'), 18 | }), 19 | ] 20 | -------------------------------------------------------------------------------- /services/date/date.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | const { isRelativeFormattedDate } = require('../test-validators') 5 | 6 | const t = (module.exports = new ServiceTester({ 7 | id: 'date', 8 | title: 'Relative Date Tests', 9 | })) 10 | 11 | t.create('Relative date') 12 | .get('/1540814400.json') 13 | .expectBadge({ label: 'date', message: isRelativeFormattedDate }) 14 | 15 | t.create('Relative date - Invalid') 16 | .get('/9999999999999.json') 17 | .expectBadge({ label: 'date', message: 'invalid date' }) 18 | -------------------------------------------------------------------------------- /services/snap-ci/snap-ci.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | const commonAttrs = { 6 | category: 'build', 7 | label: 'snap ci', 8 | dateAdded: new Date('2018-01-23'), 9 | } 10 | 11 | module.exports = [ 12 | deprecatedService({ 13 | route: { 14 | base: 'snap', 15 | pattern: ':various*', 16 | }, 17 | ...commonAttrs, 18 | }), 19 | deprecatedService({ 20 | route: { 21 | base: 'snap-ci', 22 | pattern: ':various*', 23 | }, 24 | ...commonAttrs, 25 | }), 26 | ] 27 | -------------------------------------------------------------------------------- /core/server/error-pages/404.html: -------------------------------------------------------------------------------- 1 | These aren't the pages you're looking 2 | for 3 | 4 |

I have nothing to offer for this request

5 |

… but blood, toil, tears and sweat. 6 |

7 |

Fortunately, the main page can offer rainbows instead! 8 |

9 | 10 | 11 | (You probably landed on this page because a link was broken, because someone 12 | mistyped its URL, or because of an irrelevant mistake. Don't worry, though: 13 | there is all of the rest of the Web to explore!) 14 | 15 | -------------------------------------------------------------------------------- /services/gratipay/gratipay.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { deprecatedService } = require('..') 4 | 5 | const commonAttrs = { 6 | category: 'funding', 7 | label: 'gratipay', 8 | dateAdded: new Date('2017-12-29'), 9 | } 10 | 11 | module.exports = [ 12 | deprecatedService({ 13 | route: { 14 | base: 'gittip', 15 | pattern: ':various*', 16 | }, 17 | ...commonAttrs, 18 | }), 19 | deprecatedService({ 20 | route: { 21 | base: 'gratipay', 22 | pattern: ':various*', 23 | }, 24 | ...commonAttrs, 25 | }), 26 | ] 27 | -------------------------------------------------------------------------------- /services/jira/jira-issue-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | redirector({ 7 | category: 'issue-tracking', 8 | route: { 9 | base: 'jira/issue', 10 | pattern: ':protocol(http|https)/:hostAndPath(.+)/:issueKey', 11 | }, 12 | transformPath: ({ issueKey }) => `/jira/issue/${issueKey}`, 13 | transformQueryParams: ({ protocol, hostAndPath }) => ({ 14 | baseUrl: `${protocol}://${hostAndPath}`, 15 | }), 16 | dateAdded: new Date('2019-09-14'), 17 | }), 18 | ] 19 | -------------------------------------------------------------------------------- /services/symfony/sensiolabs-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | // The SymfonyInsight service was previously branded as SensioLabs, and 7 | // accordingly the badge path used to be /sensiolabs/i/projectUuid'. 8 | redirector({ 9 | category: 'analysis', 10 | route: { 11 | base: 'sensiolabs/i', 12 | pattern: ':projectUuid', 13 | }, 14 | transformPath: ({ projectUuid }) => `/symfony/i/grade/${projectUuid}`, 15 | dateAdded: new Date('2019-02-08'), 16 | }), 17 | ] 18 | -------------------------------------------------------------------------------- /gatsby-browser.js: -------------------------------------------------------------------------------- 1 | import redirectLegacyRoutes from './frontend/lib/redirect-legacy-routes' 2 | 3 | // Adapted from https://github.com/gatsbyjs/gatsby/issues/8413 4 | function scrollToElementId(id) { 5 | const el = document.querySelector(id) 6 | if (el) { 7 | return window.scrollTo(0, el.offsetTop - 20) 8 | } else { 9 | return false 10 | } 11 | } 12 | 13 | export function onRouteUpdate({ location: { hash } }) { 14 | if (hash) { 15 | if (!redirectLegacyRoutes()) { 16 | window.setTimeout(() => scrollToElementId(hash), 10) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /services/conda/conda-platform.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const isCondaPlatform = Joi.string().regex(/^\w+-[\w\d]+( \| \w+-[\w\d]+)*$/) 5 | const t = (module.exports = require('../tester').createServiceTester()) 6 | 7 | t.create('platform').get('/p/conda-forge/zlib.json').expectBadge({ 8 | label: 'conda|platform', 9 | message: isCondaPlatform, 10 | }) 11 | 12 | t.create('platform (skip prefix)') 13 | .get('/pn/conda-forge/zlib.json') 14 | .expectBadge({ 15 | label: 'platform', 16 | message: isCondaPlatform, 17 | }) 18 | -------------------------------------------------------------------------------- /services/jira/jira-sprint-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | redirector({ 7 | category: 'issue-tracking', 8 | route: { 9 | base: 'jira/sprint', 10 | pattern: ':protocol(http|https)/:hostAndPath(.+)/:sprintId', 11 | }, 12 | transformPath: ({ sprintId }) => `/jira/sprint/${sprintId}`, 13 | transformQueryParams: ({ protocol, hostAndPath }) => ({ 14 | baseUrl: `${protocol}://${hostAndPath}`, 15 | }), 16 | dateAdded: new Date('2019-09-14'), 17 | }), 18 | ] 19 | -------------------------------------------------------------------------------- /services/offset-earth/offset-earth-carbon.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { withRegex } = require('../test-validators') 5 | 6 | t.create('request for existing username') 7 | .get('/offsetearth.json') 8 | .expectBadge({ 9 | label: 'carbon offset', 10 | message: withRegex(/[\d.]+ tonnes/), 11 | }) 12 | 13 | t.create('invalid username').get('/non-existent-username.json').expectBadge({ 14 | label: 'carbon offset', 15 | message: 'username not found', 16 | color: 'red', 17 | }) 18 | -------------------------------------------------------------------------------- /services/teamcity/teamcity-coverage-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'TeamCityCoverageRedirect', 7 | title: 'TeamCityCoverageRedirect', 8 | pathPrefix: '/teamcity/coverage', 9 | })) 10 | 11 | t.create('coverage') 12 | .get('/https/teamcity.jetbrains.com/ReactJSNet_PullRequests.svg') 13 | .expectRedirect( 14 | `/teamcity/coverage/ReactJSNet_PullRequests.svg?server=${encodeURIComponent( 15 | 'https://teamcity.jetbrains.com' 16 | )}` 17 | ) 18 | -------------------------------------------------------------------------------- /core/server/in-process-server-test-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const merge = require('deepmerge') 4 | const config = require('config').util.toObject() 5 | const portfinder = require('portfinder') 6 | const Server = require('./server') 7 | 8 | async function createTestServer(customConfig = {}) { 9 | const mergedConfig = merge(config, customConfig) 10 | if (!mergedConfig.public.bind.port) { 11 | mergedConfig.public.bind.port = await portfinder.getPortPromise() 12 | } 13 | return new Server(mergedConfig) 14 | } 15 | 16 | module.exports = { 17 | createTestServer, 18 | } 19 | -------------------------------------------------------------------------------- /services/jitpack/jitpack-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | // Github allows versions with chars, etc. 7 | const isAnyV = Joi.string().regex(/^v.+$/) 8 | 9 | t.create('version (groupId)') 10 | .get('/github/erayerdin/kappdirs.json') 11 | .expectBadge({ label: 'jitpack', message: isAnyV }) 12 | 13 | t.create('unknown package') 14 | .get('/github/some-bogus-user/project.json') 15 | .expectBadge({ label: 'jitpack', message: 'project not found or private' }) 16 | -------------------------------------------------------------------------------- /services/teamcity/teamcity-coverage-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | redirector({ 7 | category: 'coverage', 8 | route: { 9 | base: 'teamcity/coverage', 10 | pattern: ':protocol(http|https)/:hostAndPath(.+)/:buildId', 11 | }, 12 | transformPath: ({ buildId }) => `/teamcity/coverage/${buildId}`, 13 | transformQueryParams: ({ protocol, hostAndPath }) => ({ 14 | server: `${protocol}://${hostAndPath}`, 15 | }), 16 | dateAdded: new Date('2019-09-15'), 17 | }), 18 | ] 19 | -------------------------------------------------------------------------------- /services/beerpay/beerpay.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { withRegex } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | const amountOfMoney = withRegex(/^\$[0-9]+(\.[0-9]+)?/) 7 | 8 | t.create('funding').get('/hashdog/scrapfy-chrome-extension.json').expectBadge({ 9 | label: 'beerpay', 10 | message: amountOfMoney, 11 | }) 12 | 13 | t.create('funding (unknown project)') 14 | .get('/hashdog/not-a-real-project.json') 15 | .expectBadge({ 16 | label: 'beerpay', 17 | message: 'project not found', 18 | }) 19 | -------------------------------------------------------------------------------- /services/cirrus/cirrus.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { isBuildStatus } = require('../build-status') 5 | const t = (module.exports = require('../tester').createServiceTester()) 6 | 7 | t.create('cirrus bad repo') 8 | .get('/github/unknown-identifier/unknown-repo.json') 9 | .expectBadge({ label: 'build', message: 'unknown' }) 10 | 11 | t.create('cirrus fully.valid') 12 | .get('/github/flutter/flutter.json') 13 | .expectBadge({ 14 | label: 'build', 15 | message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')), 16 | }) 17 | -------------------------------------------------------------------------------- /services/stackexchange/stackexchange-taginfo.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('JavaScript Questions') 7 | .get('/stackoverflow/t/javascript.json') 8 | .expectBadge({ 9 | label: 'stackoverflow javascript questions', 10 | message: isMetric, 11 | }) 12 | 13 | t.create('Tex Programming Questions') 14 | .get('/tex/t/programming.json') 15 | .expectBadge({ 16 | label: 'tex programming questions', 17 | message: isMetric, 18 | }) 19 | -------------------------------------------------------------------------------- /services/resharper/resharper.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { createServiceFamily } = require('../nuget/nuget-v2-service-family') 4 | 5 | module.exports = createServiceFamily({ 6 | name: 'ResharperPlugin', 7 | defaultLabel: 'resharper', 8 | serviceBaseUrl: 'resharper', 9 | apiBaseUrl: 'https://resharper-plugins.jetbrains.com/api/v2', 10 | odataFormat: 'xml', 11 | title: 'JetBrains ReSharper plugins', 12 | examplePackageName: 'StyleCop.StyleCop', 13 | exampleVersion: '2017.2.0', 14 | examplePrereleaseVersion: '2017.3.0-pre0001', 15 | exampleDownloadCount: 9e4, 16 | }) 17 | -------------------------------------------------------------------------------- /core/base-service/categories.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const categories = require('../../services/categories') 5 | 6 | const isRealCategory = Joi.equal(...categories.map(({ id }) => id)).required() 7 | 8 | const isValidCategory = Joi.alternatives() 9 | .try(isRealCategory, Joi.equal('debug', 'dynamic', 'static').required()) 10 | .required() 11 | 12 | function assertValidCategory(category, message = undefined) { 13 | Joi.assert(category, isValidCategory, message) 14 | } 15 | 16 | module.exports = { 17 | isValidCategory, 18 | assertValidCategory, 19 | } 20 | -------------------------------------------------------------------------------- /frontend/lib/service-definitions/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect } from 'chai' 2 | import { test, given } from 'sazerac' 3 | import { findCategory, getDefinitionsForCategory } from '.' 4 | 5 | describe('Service definition helpers', function () { 6 | test(findCategory, () => { 7 | given('build').expect({ id: 'build', name: 'Build', keywords: ['build'] }) 8 | given('foo').expect(undefined) 9 | }) 10 | 11 | it('getDefinitionsForCategory', function () { 12 | expect(getDefinitionsForCategory('build')) 13 | .to.have.length.greaterThan(10) 14 | .and.lessThan(75) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /services/github/github-all-contributors.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { isMetric } = require('../test-validators') 5 | 6 | t.create('all-contributors repo') 7 | .get('/all-contributors/all-contributors.json') 8 | .expectBadge({ 9 | label: 'all contributors', 10 | message: isMetric, 11 | }) 12 | 13 | t.create('shields repo (not found)').get('/badges/shields.json').expectBadge({ 14 | label: 'all contributors', 15 | message: 'repo not found, branch not found, or .all-contributorsrc missing', 16 | }) 17 | -------------------------------------------------------------------------------- /services/eclipse-marketplace/eclipse-marketplace-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('marketplace version').get('/notepad4e.json').expectBadge({ 7 | label: 'eclipse marketplace', 8 | message: isVPlusDottedVersionAtLeastOne, 9 | }) 10 | 11 | t.create('last update for unknown solution') 12 | .get('/this-does-not-exist.json') 13 | .expectBadge({ 14 | label: 'eclipse marketplace', 15 | message: 'solution not found', 16 | }) 17 | -------------------------------------------------------------------------------- /services/bugzilla/bugzilla.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const Bugzilla = require('./bugzilla.service') 5 | 6 | describe('getDisplayStatus function', function () { 7 | it('formats status correctly', async function () { 8 | test(Bugzilla.getDisplayStatus, () => { 9 | given({ status: 'RESOLVED', resolution: 'WORKSFORME' }).expect( 10 | 'works for me' 11 | ) 12 | given({ status: 'RESOLVED', resolution: 'WONTFIX' }).expect("won't fix") 13 | given({ status: 'ASSIGNED', resolution: '' }).expect('assigned') 14 | }) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /services/github/github-forks.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Forks') 7 | .get('/badges/shields.json') 8 | .expectBadge({ 9 | label: 'forks', 10 | message: isMetric, 11 | link: [ 12 | 'https://github.com/badges/shields/fork', 13 | 'https://github.com/badges/shields/network', 14 | ], 15 | }) 16 | 17 | t.create('Forks (repo not found)').get('/badges/helmets.json').expectBadge({ 18 | label: 'forks', 19 | message: 'repo not found', 20 | }) 21 | -------------------------------------------------------------------------------- /services/spiget/spiget-download-size.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isFileSize } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('EssentialsX (id 9089)') 7 | .get('/9089.json') 8 | .expectBadge({ label: 'size', message: isFileSize }) 9 | 10 | t.create('Advanced Achievements (id 6239)').get('/6239.json').expectBadge({ 11 | lavel: 'size', 12 | message: 'resource hosted externally', 13 | }) 14 | 15 | t.create('Invalid Resource (id 1)').get('/1.json').expectBadge({ 16 | label: 'size', 17 | message: 'not found', 18 | }) 19 | -------------------------------------------------------------------------------- /core/badge-urls/path-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // Escapes `t` using the format specified in 4 | // 5 | function escapeFormat(t) { 6 | return ( 7 | t 8 | // Inline single underscore. 9 | .replace(/([^_])_([^_])/g, '$1 $2') 10 | // Leading or trailing underscore. 11 | .replace(/([^_])_$/, '$1 ') 12 | .replace(/^_([^_])/, ' $1') 13 | // Double underscore and double dash. 14 | .replace(/__/g, '_') 15 | .replace(/--/g, '-') 16 | ) 17 | } 18 | 19 | module.exports = { 20 | escapeFormat, 21 | } 22 | -------------------------------------------------------------------------------- /services/deprecation-helpers.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { expect } = require('chai') 4 | const { Deprecated } = require('../core/base-service/errors') 5 | const { enforceDeprecation } = require('./deprecation-helpers') 6 | 7 | describe('enforceDeprecation', function () { 8 | it('throws Deprecated for a date in the past', function () { 9 | expect(() => enforceDeprecation(new Date())).to.throw(Deprecated) 10 | }) 11 | 12 | it('does not throw for a date in the future', function () { 13 | expect(() => 14 | enforceDeprecation(new Date(Date.now() + 10000)) 15 | ).not.to.throw() 16 | }) 17 | }) 18 | -------------------------------------------------------------------------------- /services/ansible/ansible-quality.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { nonNegativeInteger } = require('../validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('quality score (valid)') 7 | .get('/432.json') 8 | .expectBadge({ label: 'quality', message: nonNegativeInteger }) 9 | 10 | t.create('quality score (project not found)') 11 | .get('/0101.json') 12 | .expectBadge({ label: 'quality', message: 'not found' }) 13 | 14 | t.create('quality score (no score available)') 15 | .get('/2504.json') 16 | .expectBadge({ label: 'quality', message: 'no score available' }) 17 | -------------------------------------------------------------------------------- /services/dub/dub-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { 5 | isVPlusDottedVersionNClausesWithOptionalSuffix, 6 | } = require('../test-validators') 7 | const t = (module.exports = require('../tester').createServiceTester()) 8 | 9 | t.create('version (valid)') 10 | .get('/vibe-d.json') 11 | .expectBadge({ 12 | label: 'dub', 13 | message: isVPlusDottedVersionNClausesWithOptionalSuffix, 14 | color: Joi.equal('blue', 'orange'), 15 | }) 16 | 17 | t.create('version (not found)') 18 | .get('/not-a-package.json') 19 | .expectBadge({ label: 'dub', message: 'not found' }) 20 | -------------------------------------------------------------------------------- /services/spiget/spiget-latest-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { withRegex } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | // Note that Spigot versions can be anything (including just a string), so we'll make sure it's not returning 'not found' 7 | 8 | t.create('EssentialsX (id 9089)') 9 | .get('/9089.json') 10 | .expectBadge({ 11 | label: 'spiget', 12 | message: withRegex(/^(?!not found$)/), 13 | }) 14 | 15 | t.create('Invalid Resource (id 1)').get('/1.json').expectBadge({ 16 | label: 'spiget', 17 | message: 'not found', 18 | }) 19 | -------------------------------------------------------------------------------- /services/swagger/swagger-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | redirector({ 7 | category: 'other', 8 | name: 'SwaggerRedirect', 9 | route: { 10 | base: 'swagger/valid/2.0', 11 | pattern: ':scheme(http|https)/:url*', 12 | }, 13 | transformPath: () => `/swagger/valid/3.0`, 14 | transformQueryParams: ({ scheme, url }) => { 15 | const suffix = /(yaml|yml|json)$/.test(url) ? '' : '.json' 16 | return { specUrl: `${scheme}://${url}${suffix}` } 17 | }, 18 | dateAdded: new Date('2019-11-03'), 19 | }), 20 | ] 21 | -------------------------------------------------------------------------------- /services/dockbit/dockbit.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'dockbit', 7 | title: 'Dockbit', 8 | })) 9 | 10 | t.create('no longer available (previously image size)') 11 | .get('/image-size/_/ubuntu/latest.json') 12 | .expectBadge({ 13 | label: 'dockbit', 14 | message: 'no longer available', 15 | }) 16 | 17 | t.create('no longer available (previously number of layers)') 18 | .get('/layers/_/ubuntu/latest.json') 19 | .expectBadge({ 20 | label: 'dockbit', 21 | message: 'no longer available', 22 | }) 23 | -------------------------------------------------------------------------------- /services/github/github-watchers.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Watchers') 7 | .get('/badges/shields.json') 8 | .expectBadge({ 9 | label: 'watchers', 10 | message: Joi.number().integer().positive(), 11 | link: [ 12 | 'https://github.com/badges/shields', 13 | 'https://github.com/badges/shields/watchers', 14 | ], 15 | }) 16 | 17 | t.create('Watchers (repo not found)').get('/badges/helmets.json').expectBadge({ 18 | label: 'watchers', 19 | message: 'repo not found', 20 | }) 21 | -------------------------------------------------------------------------------- /services/contributor-count.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { metric } = require('./text-formatters') 4 | 5 | function contributorColor(contributorCount) { 6 | if (contributorCount > 2) { 7 | return 'brightgreen' 8 | } else if (contributorCount === 2) { 9 | return 'yellow' 10 | } else { 11 | return 'red' 12 | } 13 | } 14 | 15 | function renderContributorBadge({ label, contributorCount }) { 16 | return { 17 | label, 18 | message: metric(contributorCount), 19 | color: contributorColor(contributorCount), 20 | } 21 | } 22 | 23 | module.exports = { 24 | contributorColor, 25 | renderContributorBadge, 26 | } 27 | -------------------------------------------------------------------------------- /services/github/github-language-count.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('language count').get('/badges/shields.json').expectBadge({ 7 | label: 'languages', 8 | message: Joi.number().integer().positive(), 9 | }) 10 | 11 | t.create('language count (empty repo)') 12 | .get('/pyvesb/emptyrepo.json') 13 | .expectBadge({ label: 'languages', message: '0' }) 14 | 15 | t.create('language count (repo not found)') 16 | .get('/badges/helmets.json') 17 | .expectBadge({ label: 'languages', message: 'repo not found' }) 18 | -------------------------------------------------------------------------------- /services/php-eye/php-eye-hhvm.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'hhvm', 7 | title: 'hhvm', 8 | pathPrefix: '/hhvm', 9 | })) 10 | 11 | t.create('no longer available (previously default branch)') 12 | .get('/symfony/symfony.json') 13 | .expectBadge({ 14 | label: 'hhvm', 15 | message: 'no longer available', 16 | }) 17 | 18 | t.create('no longer available (get specific branch)') 19 | .get('/yiisoft/yii/1.1.19.json') 20 | .expectBadge({ 21 | label: 'hhvm', 22 | message: 'no longer available', 23 | }) 24 | -------------------------------------------------------------------------------- /services/bountysource/bountysource.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const { ServiceTester } = require('../tester') 5 | 6 | const t = (module.exports = new ServiceTester({ 7 | id: 'bountysource', 8 | title: 'Bountysource', 9 | })) 10 | 11 | t.create('bounties (valid)') 12 | .get('/team/mozilla-core/activity.json') 13 | .expectBadge({ 14 | label: 'bounties', 15 | message: isMetric, 16 | }) 17 | 18 | t.create('bounties (invalid team)') 19 | .get('/team/not-a-real-team/activity.json') 20 | .expectBadge({ 21 | label: 'bounties', 22 | message: 'not found', 23 | }) 24 | -------------------------------------------------------------------------------- /services/itunes/itunes.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('iTunes version (valid)').get('/324684580.json').expectBadge({ 7 | label: 'itunes app store', 8 | message: isVPlusDottedVersionAtLeastOne, 9 | }) 10 | 11 | t.create('iTunes version (not found)') 12 | .get('/9.json') 13 | .expectBadge({ label: 'itunes app store', message: 'not found' }) 14 | 15 | t.create('iTunes version (invalid)') 16 | .get('/x.json') 17 | .expectBadge({ label: 'itunes app store', message: 'invalid' }) 18 | -------------------------------------------------------------------------------- /services/github/github-hacktoberfest.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const GitHubHacktoberfest = require('./github-hacktoberfest.service') 5 | 6 | describe('GitHubHacktoberfest', function () { 7 | test(GitHubHacktoberfest.render, () => { 8 | given({ 9 | daysLeft: -1, 10 | contributionCount: 12, 11 | }).expect({ 12 | message: 'is over! (12 PRs opened)', 13 | }) 14 | given({ 15 | daysLeft: 10, 16 | contributionCount: 27, 17 | suggestedIssueCount: 54, 18 | }).expect({ 19 | message: '54 open issues, 27 PRs, 10 days left', 20 | }) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /services/github/github-issue-detail-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | const variantMap = { 6 | s: 'state', 7 | u: 'author', 8 | } 9 | 10 | module.exports = [ 11 | redirector({ 12 | category: 'issue-tracking', 13 | route: { 14 | base: 'github', 15 | pattern: 16 | ':issueKind(issues|pulls)/detail/:variant(s|u)/:user/:repo/:number([0-9]+)', 17 | }, 18 | transformPath: ({ issueKind, variant, user, repo, number }) => 19 | `/github/${issueKind}/detail/${variantMap[variant]}/${user}/${repo}/${number}`, 20 | dateAdded: new Date('2019-04-04'), 21 | }), 22 | ] 23 | -------------------------------------------------------------------------------- /services/imagelayers/imagelayers.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'imagelayers', 7 | title: 'ImageLayers', 8 | })) 9 | 10 | t.create('no longer available (previously image size)') 11 | .get('/image-size/_/ubuntu/latest.json') 12 | .expectBadge({ 13 | label: 'imagelayers', 14 | message: 'no longer available', 15 | }) 16 | 17 | t.create('no longer available (previously number of layers)') 18 | .get('/layers/_/ubuntu/latest.json') 19 | .expectBadge({ 20 | label: 'imagelayers', 21 | message: 'no longer available', 22 | }) 23 | -------------------------------------------------------------------------------- /services/node/testUtils/packageJsonVersionsTemplate.json: -------------------------------------------------------------------------------- 1 | { 2 | "dist-tags": { 3 | "latest": "0.0.91" 4 | }, 5 | "versions": { 6 | "0.0.90": { 7 | "engines": { 8 | "node": ">= 0.4.0" 9 | }, 10 | "maintainers": [ 11 | { 12 | "name": "jaredhanson", 13 | "email": "jaredhanson@gmail.com" 14 | } 15 | ] 16 | }, 17 | "0.0.91": { 18 | "engines": { 19 | "node": ">= 0.4.0" 20 | }, 21 | "maintainers": [ 22 | { 23 | "name": "jaredhanson", 24 | "email": "jaredhanson@gmail.com" 25 | } 26 | ] 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "reporter": ["lcov"], 3 | "all": true, 4 | "silent": true, 5 | "clean": false, 6 | "exclude": [ 7 | "**/*.spec.js", 8 | "**/*.integration.js", 9 | "**/test-helpers.js", 10 | "**/*-test-helpers.js", 11 | "**/*-fixtures.js", 12 | "**/mocha-*.js", 13 | "dangerfile.js", 14 | "gatsby-*.js", 15 | "core/service-test-runner", 16 | "core/got-test-client.js", 17 | "services/**/*.tester.js", 18 | "services/test-validators.js", 19 | "services/tester.js", 20 | "core/base-service/loader-test-fixtures", 21 | "scripts", 22 | "coverage", 23 | "build", 24 | ".github" 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /services/cpan/cpan.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { BaseJsonService } = require('..') 5 | 6 | const schema = Joi.object({ 7 | version: Joi.alternatives(Joi.string().required(), Joi.number().required()), 8 | license: Joi.array().items(Joi.string()).min(1).required(), 9 | }).required() 10 | 11 | module.exports = class BaseCpanService extends BaseJsonService { 12 | static get defaultBadgeData() { 13 | return { label: 'cpan' } 14 | } 15 | 16 | async fetch({ packageName }) { 17 | const url = `https://fastapi.metacpan.org/v1/release/${packageName}` 18 | return this._requestJson({ schema, url }) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /services/jitpack/jitpack-downloads.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'JitPackDownloads', 7 | title: 'JitPackDownloads', 8 | pathPrefix: '/jitpack', 9 | })) 10 | 11 | t.create('no longer available (dw)') 12 | .get('/dw/github/jitpack/maven-simple.json') 13 | .expectBadge({ 14 | label: 'jitpack', 15 | message: 'no longer available', 16 | }) 17 | 18 | t.create('no longer available (dm)') 19 | .get('/dm/github/jitpack/maven-simple.json') 20 | .expectBadge({ 21 | label: 'jitpack', 22 | message: 'no longer available', 23 | }) 24 | -------------------------------------------------------------------------------- /services/leanpub/leanpub-book-summary.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'LeanPub', 7 | title: 'LeanPub', 8 | pathPrefix: '/leanpub/book', 9 | })) 10 | 11 | t.create('no longer available (previously book pages)') 12 | .get('/pages/juice-shop.json') 13 | .expectBadge({ 14 | label: 'leanpub', 15 | message: 'no longer available', 16 | }) 17 | 18 | t.create('no longer available (previously books sold)') 19 | .get('/sold/juice-shop.json') 20 | .expectBadge({ 21 | label: 'leanpub', 22 | message: 'no longer available', 23 | }) 24 | -------------------------------------------------------------------------------- /services/npm/npm-downloads.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const NpmDownloads = require('./npm-downloads.service') 5 | 6 | describe('NpmDownloads', function () { 7 | test(NpmDownloads._intervalMap.dt.transform, () => { 8 | given({ 9 | downloads: [ 10 | { downloads: 2, day: '2018-01-01' }, 11 | { downloads: 3, day: '2018-01-02' }, 12 | ], 13 | }).expect(5) 14 | }) 15 | 16 | test(NpmDownloads.render, () => { 17 | given({ 18 | interval: 'dt', 19 | downloadCount: 0, 20 | }).expect({ 21 | message: '0', 22 | color: 'red', 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /services/php-eye/php-eye-php-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'php-eye', 7 | title: 'php-eye', 8 | pathPrefix: '/php-eye', 9 | })) 10 | 11 | t.create('no longer available (previously default branch)') 12 | .get('/symfony/symfony.json') 13 | .expectBadge({ 14 | label: 'php tested', 15 | message: 'no longer available', 16 | }) 17 | 18 | t.create('no longer available (get specific branch)') 19 | .get('/yiisoft/yii/1.1.19.json') 20 | .expectBadge({ 21 | label: 'php tested', 22 | message: 'no longer available', 23 | }) 24 | -------------------------------------------------------------------------------- /services/stackexchange/stackexchange-monthlyquestions.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetricOverTimePeriod } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Monthly Questions for StackOverflow Momentjs') 7 | .get('/stackoverflow/qm/momentjs.json') 8 | .expectBadge({ 9 | label: 'stackoverflow momentjs questions', 10 | message: isMetricOverTimePeriod, 11 | }) 12 | 13 | t.create('Monthly Questions for Tex Spacing') 14 | .get('/tex/qm/spacing.json') 15 | .expectBadge({ 16 | label: 'tex spacing questions', 17 | message: isMetricOverTimePeriod, 18 | }) 19 | -------------------------------------------------------------------------------- /services/codeclimate/codeclimate-analysis-redirector.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | // http://github.com/badges/shields/issues/1387 7 | // https://github.com/badges/shields/pull/3320#issuecomment-483795000 8 | redirector({ 9 | name: 'CodeclimateCoverageMaintainabilityRedirect', 10 | category: 'analysis', 11 | route: { 12 | base: 'codeclimate/maintainability-letter', 13 | pattern: ':user/:repo', 14 | }, 15 | transformPath: ({ user, repo }) => 16 | `/codeclimate/maintainability/${user}/${repo}`, 17 | dateAdded: new Date('2019-04-16'), 18 | }), 19 | ] 20 | -------------------------------------------------------------------------------- /services/issuestats/issuestats.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = new ServiceTester({ id: 'issuestats', title: 'Issue Stats' }) 6 | module.exports = t 7 | 8 | t.create('no longer available (previously issue analysis)') 9 | .get('/i/github/expressjs/express.json') 10 | .expectBadge({ 11 | label: 'issue stats', 12 | message: 'no longer available', 13 | }) 14 | 15 | t.create('no longer available (previously pull request analysis, long form)') 16 | .get('/p/long/github/expressjs/express.json') 17 | .expectBadge({ 18 | label: 'issue stats', 19 | message: 'no longer available', 20 | }) 21 | -------------------------------------------------------------------------------- /services/crates/crates-license.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'crates', 7 | title: 'crates.io', 8 | pathPrefix: '/crates/l', 9 | })) 10 | 11 | t.create('license') 12 | .get('/libc.json') 13 | .expectBadge({ label: 'license', message: 'MIT OR Apache-2.0' }) 14 | 15 | t.create('license (with version)') 16 | .get('/libc/0.2.44.json') 17 | .expectBadge({ label: 'license', message: 'MIT OR Apache-2.0' }) 18 | 19 | t.create('license (not found)') 20 | .get('/not-a-real-package.json') 21 | .expectBadge({ label: 'crates.io', message: 'not found' }) 22 | -------------------------------------------------------------------------------- /services/netlify/netlify.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const Netlify = require('./netlify.service') 5 | 6 | const building = { message: 'building', label: undefined, color: 'yellow' } 7 | const notBuilt = { message: 'not built', label: undefined, color: undefined } 8 | 9 | describe('Netlify', function () { 10 | test(Netlify.render, () => { 11 | given({ status: 'building' }).expect(building) 12 | given({ status: 'stopped' }).expect(notBuilt) 13 | given({ status: 'infrastructure_failure' }).expect({ 14 | message: 'failing', 15 | color: 'red', 16 | label: undefined, 17 | }) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/valid-array.fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const BaseJsonService = require('../base-json') 4 | 5 | class GoodServiceOne extends BaseJsonService { 6 | static get category() { 7 | return 'build' 8 | } 9 | 10 | static get route() { 11 | return { 12 | base: 'good', 13 | pattern: 'one', 14 | } 15 | } 16 | } 17 | class GoodServiceTwo extends BaseJsonService { 18 | static get category() { 19 | return 'build' 20 | } 21 | 22 | static get route() { 23 | return { 24 | base: 'good', 25 | pattern: 'two', 26 | } 27 | } 28 | } 29 | 30 | module.exports = [GoodServiceOne, GoodServiceTwo] 31 | -------------------------------------------------------------------------------- /core/base-service/loader-test-fixtures/valid-object.fixture.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const BaseJsonService = require('../base-json') 4 | 5 | class GoodServiceOne extends BaseJsonService { 6 | static get category() { 7 | return 'build' 8 | } 9 | 10 | static get route() { 11 | return { 12 | base: 'good', 13 | pattern: 'one', 14 | } 15 | } 16 | } 17 | class GoodServiceTwo extends BaseJsonService { 18 | static get category() { 19 | return 'build' 20 | } 21 | 22 | static get route() { 23 | return { 24 | base: 'good', 25 | pattern: 'two', 26 | } 27 | } 28 | } 29 | 30 | module.exports = { GoodServiceOne, GoodServiceTwo } 31 | -------------------------------------------------------------------------------- /services/github/github-size.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isFileSize } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('File size') 7 | .get('/webcaetano/craft/build/phaser-craft.min.js.json') 8 | .expectBadge({ label: 'size', message: isFileSize }) 9 | 10 | t.create('File size 404') 11 | .get('/webcaetano/craft/build/does-not-exist.min.js.json') 12 | .expectBadge({ label: 'size', message: 'repo or file not found' }) 13 | 14 | t.create('File size for "not a regular file"') 15 | .get('/webcaetano/craft/build.json') 16 | .expectBadge({ label: 'size', message: 'not a regular file' }) 17 | -------------------------------------------------------------------------------- /services/nexus/nexus-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | module.exports = [ 6 | redirector({ 7 | category: 'version', 8 | route: { 9 | base: 'nexus', 10 | pattern: 11 | ':repo(r|s|[^/]+)/:scheme(http|https)/:hostAndPath+/:groupId/:artifactId([^/:]+?):queryOpt(:.+?)?', 12 | }, 13 | transformPath: ({ repo, groupId, artifactId }) => 14 | `/nexus/${repo}/${groupId}/${artifactId}`, 15 | transformQueryParams: ({ scheme, hostAndPath, queryOpt }) => ({ 16 | server: `${scheme}://${hostAndPath}`, 17 | queryOpt, 18 | }), 19 | dateAdded: new Date('2019-07-26'), 20 | }), 21 | ] 22 | -------------------------------------------------------------------------------- /services/amo/amo-rating.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { isStarRating } = require('../test-validators') 5 | const t = (module.exports = require('../tester').createServiceTester()) 6 | 7 | t.create('Rating') 8 | .get('/rating/IndieGala-Helper.json') 9 | .expectBadge({ 10 | label: 'rating', 11 | message: Joi.string().regex(/^\d\/\d$/), 12 | }) 13 | 14 | t.create('Stars') 15 | .get('/stars/IndieGala-Helper.json') 16 | .expectBadge({ label: 'stars', message: isStarRating }) 17 | 18 | t.create('Rating (not found)') 19 | .get('/rating/not-a-real-plugin.json') 20 | .expectBadge({ label: 'mozilla add-on', message: 'not found' }) 21 | -------------------------------------------------------------------------------- /services/coverity/coverity-on-demand.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'CoverityOnDemand', 7 | title: 'Coverity On Demand', 8 | pathPrefix: '/coverity/ondemand', 9 | })) 10 | 11 | t.create('no longer available (streams)') 12 | .get('/streams/44b25sjc9l3ntc2ngfi29tngro.json') 13 | .expectBadge({ 14 | label: 'coverity', 15 | message: 'no longer available', 16 | }) 17 | 18 | t.create('no longer available (jobs)') 19 | .get('/jobs/p4tmm8031t4i971r0im4s7lckk.json') 20 | .expectBadge({ 21 | label: 'coverity', 22 | message: 'no longer available', 23 | }) 24 | -------------------------------------------------------------------------------- /services/opm/opm-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | const isValidVersion = Joi.string() 7 | .regex(/^v[\d+.]+$/) 8 | .required() 9 | 10 | t.create('version').get('/openresty/lua-resty-lrucache.json').expectBadge({ 11 | label: 'opm', 12 | message: isValidVersion, 13 | }) 14 | 15 | t.create('unknown module') 16 | .get('/openresty/does-not-exist.json') 17 | .expectBadge({ label: 'opm', message: 'module not found' }) 18 | 19 | t.create('unknown user') 20 | .get('/nil/does-not-exist.json') 21 | .expectBadge({ label: 'opm', message: 'module not found' }) 22 | -------------------------------------------------------------------------------- /services/static-badge/static-badge.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { escapeFormat } = require('../../core/badge-urls/path-helpers') 4 | const { BaseStaticService } = require('..') 5 | 6 | module.exports = class StaticBadge extends BaseStaticService { 7 | static get category() { 8 | return 'static' 9 | } 10 | 11 | static get route() { 12 | return { 13 | base: '', 14 | format: '(?::|badge/)((?:[^-]|--)*?)-?((?:[^-]|--)*)-((?:[^-.]|--)+)', 15 | capture: ['label', 'message', 'color'], 16 | } 17 | } 18 | 19 | handle({ label, message, color }) { 20 | return { label: escapeFormat(label), message: escapeFormat(message), color } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /frontend/components/header.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'gatsby' 2 | import React from 'react' 3 | import styled from 'styled-components' 4 | import Logo from '../images/logo.svg' 5 | import { VerticalSpace } from './common' 6 | 7 | const Highlights = styled.p` 8 | font-style: italic; 9 | ` 10 | 11 | export default function Header(): JSX.Element { 12 | return ( 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Pixel-perfect   Retina-ready   Fast   Consistent   22 | Hackable   No tracking 23 | 24 |
25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /services/github/github-top-language.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('top language') 7 | .get('/badges/shields.json') 8 | .expectBadge({ 9 | label: 'javascript', 10 | message: Joi.string().regex(/^([1-9]?[0-9]\.[0-9]|100\.0)%$/), 11 | }) 12 | 13 | t.create('top language (empty repo)') 14 | .get('/pyvesb/emptyrepo.json') 15 | .expectBadge({ label: 'language', message: 'none' }) 16 | 17 | t.create('top language (repo not found)') 18 | .get('/not-a-real-user/not-a-real-repo.json') 19 | .expectBadge({ label: 'language', message: 'repo not found' }) 20 | -------------------------------------------------------------------------------- /config/shields-io-production.yml: -------------------------------------------------------------------------------- 1 | public: 2 | metrics: 3 | prometheus: 4 | enabled: true 5 | influx: 6 | enabled: true 7 | url: https://metrics.shields.io/telegraf 8 | instanceIdFrom: env-var 9 | instanceIdEnvVarName: HEROKU_DYNO_ID 10 | envLabel: shields-production 11 | 12 | ssl: 13 | isSecure: true 14 | 15 | cors: 16 | allowedOrigin: ['http://shields.io', 'https://shields.io'] 17 | 18 | redirectUrl: 'https://shields.io/' 19 | 20 | rasterUrl: 'https://raster.shields.io' 21 | 22 | private: 23 | # These are not really private; they should be moved to `public`. 24 | shields_ips: ['192.99.59.72', '51.254.114.150', '149.56.96.133'] 25 | -------------------------------------------------------------------------------- /services/node/node-lts.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const NodeVersionBase = require('./node-base') 4 | const { versionColorForRangeLts } = require('./node-version-color') 5 | 6 | module.exports = class NodeLtsVersion extends NodeVersionBase { 7 | static get path() { 8 | return 'v-lts' 9 | } 10 | 11 | static get defaultBadgeData() { 12 | return { label: 'node-lts' } 13 | } 14 | 15 | static get type() { 16 | return 'lts' 17 | } 18 | 19 | static get colorResolver() { 20 | return versionColorForRangeLts 21 | } 22 | 23 | static get documentation() { 24 | return `This badge indicates whether the package supports all LTS node versions` 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /scripts/export-supported-features-cli.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const logos = require('../lib/load-logos')() 4 | const simpleIcons = require('../lib/load-simple-icons')() 5 | 6 | const shieldsLogos = Object.keys(logos) 7 | 8 | const simpleIconSet = new Set(Object.keys(simpleIcons)) 9 | shieldsLogos.forEach(logo => simpleIconSet.delete(logo)) 10 | const simpleIconNames = Array.from(simpleIconSet) 11 | 12 | const supportedFeatures = { 13 | shieldsLogos, 14 | simpleIcons: simpleIconNames, 15 | advertisedStyles: [ 16 | 'plastic', 17 | 'flat', 18 | 'flat-square', 19 | 'for-the-badge', 20 | 'social', 21 | ], 22 | } 23 | 24 | console.log(JSON.stringify(supportedFeatures, null, 2)) 25 | -------------------------------------------------------------------------------- /services/cocoapods/cocoapods-apps.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | const t = (module.exports = new ServiceTester({ 5 | id: 'CocoapodsApps', 6 | title: 'CocoapodsApps', 7 | pathPrefix: '/cocoapods', 8 | })) 9 | 10 | t.create('apps (valid, weekly)') 11 | .get('/aw/AFNetworking.json') 12 | .expectBadge({ label: 'apps', message: 'no longer available' }) 13 | 14 | t.create('apps (valid, total)') 15 | .get('/at/AFNetworking.json') 16 | .expectBadge({ label: 'apps', message: 'no longer available' }) 17 | 18 | t.create('apps (not found)') 19 | .get('/at/not-a-package.json') 20 | .expectBadge({ label: 'apps', message: 'no longer available' }) 21 | -------------------------------------------------------------------------------- /services/depfu/depfu.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { ServiceTester } = require('../tester') 5 | 6 | const isDependencyStatus = Joi.string().valid( 7 | 'insecure', 8 | 'latest', 9 | 'recent', 10 | 'stale' 11 | ) 12 | 13 | const t = (module.exports = new ServiceTester({ id: 'depfu', title: 'Depfu' })) 14 | 15 | t.create('depfu dependencies (valid)') 16 | .get('/depfu/example-ruby.json') 17 | .expectBadge({ 18 | label: 'dependencies', 19 | message: isDependencyStatus, 20 | }) 21 | 22 | t.create('depfu dependencies (repo not found)') 23 | .get('/pyvesb/emptyrepo.json') 24 | .expectBadge({ label: 'dependencies', message: 'not found' }) 25 | -------------------------------------------------------------------------------- /services/jira/jira-test-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const sprintId = 8 4 | const sprintQueryString = { 5 | jql: `sprint=${sprintId} AND type IN (Bug,Improvement,Story,"Technical task")`, 6 | fields: 'resolution', 7 | maxResults: 500, 8 | } 9 | 10 | const user = 'admin' 11 | const pass = 'password' 12 | const host = 'myprivatejira.test' 13 | const config = { 14 | public: { 15 | services: { 16 | jira: { 17 | authorizedOrigins: [`https://${host}`], 18 | }, 19 | }, 20 | }, 21 | private: { jira_user: user, jira_pass: pass }, 22 | } 23 | 24 | module.exports = { 25 | sprintId, 26 | sprintQueryString, 27 | user, 28 | pass, 29 | host, 30 | config, 31 | } 32 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10-alpine 2 | 3 | RUN mkdir -p /usr/src/app 4 | RUN mkdir /usr/src/app/private 5 | WORKDIR /usr/src/app 6 | 7 | COPY package.json package-lock.json /usr/src/app/ 8 | # Without the badge-maker package.json and CLI script in place, `npm ci` will fail. 9 | COPY badge-maker /usr/src/app/badge-maker/ 10 | 11 | # We need dev deps to build the front end. We don't need Cypress, though. 12 | RUN NODE_ENV=development CYPRESS_INSTALL_BINARY=0 npm ci 13 | 14 | COPY . /usr/src/app 15 | RUN npm run build 16 | RUN npm prune --production 17 | RUN npm cache clean --force 18 | 19 | # Run the server using production configs. 20 | ENV NODE_ENV production 21 | 22 | CMD node server 23 | 24 | EXPOSE 80 25 | -------------------------------------------------------------------------------- /core/base-service/coalesce.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const coalesce = require('./coalesce') 5 | 6 | // Sticking with our one-line spread implementation, and defaulting to 7 | // `undefined` instead of `null`, though h/t to 8 | // https://github.com/royriojas/coalescy for these tests! 9 | 10 | describe('coalesce', function () { 11 | test(coalesce, function () { 12 | given().expect(undefined) 13 | given(null, []).expect([]) 14 | given(null, [], {}).expect([]) 15 | given(null, undefined, 0, {}).expect(0) 16 | 17 | const a = null 18 | const c = 0 19 | const d = 1 20 | let b 21 | given(a, b, c, d).expect(0) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /services/dynamic/dynamic-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { optionalUrl } = require('../validators') 5 | 6 | const queryParamSchema = Joi.object({ 7 | url: optionalUrl.required(), 8 | query: Joi.string().required(), 9 | prefix: Joi.alternatives().try(Joi.string(), Joi.number()), 10 | suffix: Joi.alternatives().try(Joi.string(), Joi.number()), 11 | }) 12 | .rename('uri', 'url', { ignoreUndefined: true, override: true }) 13 | .required() 14 | 15 | function createRoute(which) { 16 | return { 17 | base: `badge/dynamic/${which}`, 18 | pattern: '', 19 | queryParamSchema, 20 | } 21 | } 22 | 23 | module.exports = { 24 | createRoute, 25 | } 26 | -------------------------------------------------------------------------------- /services/dynamic/dynamic-json.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { MetricNames } = require('../../core/base-service/metric-helper') 4 | const { BaseJsonService } = require('..') 5 | const { createRoute } = require('./dynamic-helpers') 6 | const jsonPath = require('./json-path') 7 | 8 | module.exports = class DynamicJson extends jsonPath(BaseJsonService) { 9 | static get enabledMetrics() { 10 | return [MetricNames.SERVICE_RESPONSE_SIZE] 11 | } 12 | 13 | static get route() { 14 | return createRoute('json') 15 | } 16 | 17 | async fetch({ schema, url, errorMessages }) { 18 | return this._requestJson({ 19 | schema, 20 | url, 21 | errorMessages, 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /services/dynamic/dynamic-yaml.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { MetricNames } = require('../../core/base-service/metric-helper') 4 | const { BaseYamlService } = require('..') 5 | const { createRoute } = require('./dynamic-helpers') 6 | const jsonPath = require('./json-path') 7 | 8 | module.exports = class DynamicYaml extends jsonPath(BaseYamlService) { 9 | static get enabledMetrics() { 10 | return [MetricNames.SERVICE_RESPONSE_SIZE] 11 | } 12 | 13 | static get route() { 14 | return createRoute('yaml') 15 | } 16 | 17 | async fetch({ schema, url, errorMessages }) { 18 | return this._requestYaml({ 19 | schema, 20 | url, 21 | errorMessages, 22 | }) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /services/node/node-current.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const NodeVersionBase = require('./node-base') 4 | const { versionColorForRangeCurrent } = require('./node-version-color') 5 | 6 | module.exports = class NodeCurrentVersion extends NodeVersionBase { 7 | static get path() { 8 | return 'v' 9 | } 10 | 11 | static get defaultBadgeData() { 12 | return { label: 'node' } 13 | } 14 | 15 | static get type() { 16 | return 'current' 17 | } 18 | 19 | static get colorResolver() { 20 | return versionColorForRangeCurrent 21 | } 22 | 23 | static get documentation() { 24 | return `This badge indicates whether the package supports the latest release of node` 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /services/pypi/pypi-wheel.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('wheel (has wheel, package version in request)') 6 | .get('/requests/2.18.4.json') 7 | .expectBadge({ label: 'wheel', message: 'yes' }) 8 | 9 | t.create('wheel (has wheel, no package version specified)') 10 | .get('/requests.json') 11 | .expectBadge({ label: 'wheel', message: 'yes' }) 12 | 13 | t.create('wheel (no wheel)') 14 | .get('/chai/1.1.2.json') 15 | .expectBadge({ label: 'wheel', message: 'no' }) 16 | 17 | t.create('wheel (invalid)') 18 | .get('/not-a-package.json') 19 | .expectBadge({ label: 'wheel', message: 'package or version not found' }) 20 | -------------------------------------------------------------------------------- /services/appveyor/appveyor-build-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'AppveyorBuildRedirect', 7 | title: 'AppveyorBuildRedirect', 8 | pathPrefix: '/appveyor/ci', 9 | })) 10 | 11 | t.create('Appveyor CI') 12 | .get('/gruntjs/grunt', { 13 | followRedirect: false, 14 | }) 15 | .expectStatus(301) 16 | .expectHeader('Location', '/appveyor/build/gruntjs/grunt.svg') 17 | 18 | t.create('Appveyor CI (branch)') 19 | .get('/gruntjs/grunt/develop', { 20 | followRedirect: false, 21 | }) 22 | .expectStatus(301) 23 | .expectHeader('Location', '/appveyor/build/gruntjs/grunt/develop.svg') 24 | -------------------------------------------------------------------------------- /services/github/github-contributors.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { isMetric } = require('../test-validators') 5 | 6 | t.create('Contributors').get('/contributors/badges/shields.json').expectBadge({ 7 | label: 'contributors', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('1 contributor') 12 | .get('/contributors/badges/shields-tests.json') 13 | .expectBadge({ 14 | label: 'contributors', 15 | message: '1', 16 | }) 17 | 18 | t.create('Contributors (repo not found)') 19 | .get('/contributors/badges/helmets.json') 20 | .expectBadge({ 21 | label: 'contributors', 22 | message: 'repo not found', 23 | }) 24 | -------------------------------------------------------------------------------- /services/puppetforge/puppetforge-module-pdk-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isSemver } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('PDK version').get('/tragiccode/azure_key_vault.json').expectBadge({ 7 | label: 'pdk version', 8 | message: isSemver, 9 | }) 10 | 11 | t.create("PDK version (library doesn't use the PDK)") 12 | .get('/puppet/yum.json') 13 | .expectBadge({ 14 | label: 'pdk version', 15 | message: 'none', 16 | }) 17 | 18 | t.create('PDK version (not found)') 19 | .get('/notarealuser/notarealpackage.json') 20 | .expectBadge({ 21 | label: 'pdk version', 22 | message: 'not found', 23 | }) 24 | -------------------------------------------------------------------------------- /services/sourceforge/sourceforge-open-tickets.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('bugs') 7 | .get('/sevenzip/bugs.json') 8 | .expectBadge({ 9 | label: 'open tickets', 10 | message: isMetric, 11 | }) 12 | .timeout(10000) 13 | 14 | t.create('feature requests') 15 | .get('/sevenzip/feature-requests.json') 16 | .expectBadge({ 17 | label: 'open tickets', 18 | message: isMetric, 19 | }) 20 | .timeout(10000) 21 | 22 | t.create('invalid project').get('/invalid/bugs.json').expectBadge({ 23 | label: 'open tickets', 24 | message: 'project not found', 25 | }) 26 | -------------------------------------------------------------------------------- /scripts/redis-connectivity-test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const config = require('config').util.toObject() 4 | console.log(config) 5 | const GithubConstellation = require('../services/github/github-constellation') 6 | 7 | const { persistence } = new GithubConstellation({ 8 | persistence: config.public.persistence, 9 | service: config.public.services.github, 10 | private: config.private, 11 | }) 12 | 13 | async function main() { 14 | const tokens = await persistence.initialize() 15 | console.log(`${tokens.length} tokens loaded`) 16 | await persistence.stop() 17 | } 18 | 19 | ;(async () => { 20 | try { 21 | await main() 22 | } catch (e) { 23 | console.error(e) 24 | process.exit(1) 25 | } 26 | })() 27 | -------------------------------------------------------------------------------- /services/codefactor/codefactor-grade.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { isValidGrade } = require('./codefactor-helpers') 5 | 6 | t.create('Grade').get('/github/google/guava.json').expectBadge({ 7 | label: 'code quality', 8 | message: isValidGrade, 9 | }) 10 | 11 | t.create('Grade (branch)') 12 | .get('/github/pallets/flask/master.json') 13 | .expectBadge({ 14 | label: 'code quality', 15 | message: isValidGrade, 16 | }) 17 | 18 | t.create('Grade (nonexistent repo)') 19 | .get('/github/badges/asdfasdfasdfasdfasfae.json') 20 | .expectBadge({ 21 | label: 'code quality', 22 | message: 'repo or branch not found', 23 | }) 24 | -------------------------------------------------------------------------------- /services/stackexchange/stackexchange-reputation.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Invalid parameters') 7 | .get('/stackoverflow/r/invalidimage.json') 8 | .expectBadge({ label: 'stackoverflow', message: 'invalid parameters' }) 9 | 10 | t.create('Reputation for StackOverflow user 22656') 11 | .get('/stackoverflow/r/22656.json') 12 | .expectBadge({ 13 | label: 'stackoverflow reputation', 14 | message: isMetric, 15 | }) 16 | 17 | t.create('Reputation for Tex user 22656').get('/tex/r/226.json').expectBadge({ 18 | label: 'tex reputation', 19 | message: isMetric, 20 | }) 21 | -------------------------------------------------------------------------------- /scripts/benchmark-performance.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const config = require('config').util.toObject() 4 | const got = require('got') 5 | const minimist = require('minimist') 6 | const Server = require('../core/server/server') 7 | 8 | async function main() { 9 | const server = new Server(config) 10 | await server.start() 11 | const args = minimist(process.argv) 12 | const iterations = parseInt(args.iterations) || 10000 13 | for (let i = 0; i < iterations; ++i) { 14 | await got(`${server.baseUrl}badge/coverage-${i}-green.svg`) 15 | } 16 | await server.stop() 17 | } 18 | 19 | ;(async () => { 20 | try { 21 | await main() 22 | } catch (e) { 23 | console.error(e) 24 | process.exit(1) 25 | } 26 | })() 27 | -------------------------------------------------------------------------------- /services/pypi/pypi-status.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('status (valid, stable, package version in request)') 6 | .get('/django/1.11.json') 7 | .expectBadge({ label: 'status', message: 'stable' }) 8 | 9 | t.create('status (valid, no package version specified)') 10 | .get('/typing.json') 11 | .expectBadge({ label: 'status', message: 'stable' }) 12 | 13 | t.create('status (valid, beta)') 14 | .get('/django/2.0rc1.json') 15 | .expectBadge({ label: 'status', message: 'beta' }) 16 | 17 | t.create('status (invalid)') 18 | .get('/not-a-package.json') 19 | .expectBadge({ label: 'status', message: 'package or version not found' }) 20 | -------------------------------------------------------------------------------- /services/symfony/symfony-insight-base.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { expect } = require('chai') 4 | const { NotFound } = require('..') 5 | const { SymfonyInsightBase } = require('./symfony-insight-base') 6 | 7 | describe('SymfonyInsightBase', function () { 8 | context('transform()', function () { 9 | it('throws NotFound error when there is no coverage data', function () { 10 | try { 11 | SymfonyInsightBase.prototype.transform({ 12 | data: { project: {} }, 13 | }) 14 | expect.fail('Expected to throw') 15 | } catch (e) { 16 | expect(e).to.be.an.instanceof(NotFound) 17 | expect(e.prettyMessage).to.equal('no analyses found') 18 | } 19 | }) 20 | }) 21 | }) 22 | -------------------------------------------------------------------------------- /services/amo/amo-downloads.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | const { isMetricOverTimePeriod } = require('../test-validators') 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'AmoDownloads', 7 | title: 'AmoDownloads', 8 | pathPrefix: '/amo', 9 | })) 10 | 11 | t.create('Weekly Downloads') 12 | .get('/dw/dustman.json') 13 | .expectBadge({ label: 'downloads', message: isMetricOverTimePeriod }) 14 | 15 | t.create('Weekly Downloads (not found)') 16 | .get('/dw/not-a-real-plugin.json') 17 | .expectBadge({ label: 'downloads', message: 'not found' }) 18 | 19 | t.create('/d URL should redirect to /dw') 20 | .get('/d/dustman.svg') 21 | .expectRedirect('/amo/dw/dustman.svg') 22 | -------------------------------------------------------------------------------- /services/codecov/codecov.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, forCases, given } = require('sazerac') 4 | const Codecov = require('./codecov.service') 5 | 6 | describe('Codecov', function () { 7 | test(Codecov.prototype.legacyTransform, () => { 8 | forCases([given({ json: {} }), given({ json: { commit: {} } })]).expect({ 9 | coverage: 'unknown', 10 | }) 11 | }) 12 | 13 | test(Codecov.prototype.transform, () => { 14 | forCases([given({ data: { message: 'unknown' } })]).expect({ 15 | coverage: 'unknown', 16 | }) 17 | }) 18 | 19 | test(Codecov.render, () => { 20 | given({ coverage: 'unknown' }).expect({ 21 | message: 'unknown', 22 | color: 'lightgrey', 23 | }) 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /services/crates/crates-version.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const { expect } = require('chai') 5 | const { InvalidResponse } = require('..') 6 | const CratesVersion = require('./crates-version.service') 7 | 8 | describe('CratesVersion', function () { 9 | test(CratesVersion.prototype.transform, () => { 10 | given({ version: { num: '1.0.0' } }).expect({ version: '1.0.0' }) 11 | given({ crate: { max_version: '1.1.0' } }).expect({ version: '1.1.0' }) 12 | }) 13 | 14 | it('throws InvalidResponse on error response', function () { 15 | expect(() => 16 | CratesVersion.prototype.transform({ errors: [{ detail: 'idk how...' }] }) 17 | ).to.throw(InvalidResponse) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /services/docker/docker-cloud-common-fetch.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | 5 | const cloudBuildSchema = Joi.object({ 6 | objects: Joi.array() 7 | .items( 8 | Joi.object({ 9 | state: Joi.string(), 10 | build_settings: Joi.array(), 11 | }).required() 12 | ) 13 | .required(), 14 | }).required() 15 | 16 | async function fetchBuild(serviceInstance, { user, repo }) { 17 | return serviceInstance._requestJson({ 18 | schema: cloudBuildSchema, 19 | url: `https://cloud.docker.com/api/build/v1/source`, 20 | options: { qs: { image: `${user}/${repo}` } }, 21 | errorMessages: { 404: 'repo not found' }, 22 | }) 23 | } 24 | 25 | module.exports = { 26 | fetchBuild, 27 | } 28 | -------------------------------------------------------------------------------- /services/dynamic/json-path.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const chai = require('chai') 4 | const { expect } = chai 5 | const jsonPath = require('./json-path') 6 | 7 | chai.use(require('chai-as-promised')) 8 | 9 | describe('JSON Path service factory', function () { 10 | describe('fetch()', function () { 11 | it('should throw error if it is not overridden', function () { 12 | class BaseService {} 13 | class JsonPathService extends jsonPath(BaseService) {} 14 | const jsonPathServiceInstance = new JsonPathService() 15 | 16 | return expect(jsonPathServiceInstance.fetch({})).to.be.rejectedWith( 17 | Error, 18 | 'fetch() function not implemented for JsonPathService' 19 | ) 20 | }) 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /services/lgtm/lgtm-redirector.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | const commonAttrs = { 6 | category: 'analysis', 7 | dateAdded: new Date('2019-04-30'), 8 | } 9 | 10 | module.exports = [ 11 | redirector({ 12 | route: { 13 | base: 'lgtm/alerts/g', 14 | pattern: ':user/:repo', 15 | }, 16 | transformPath: ({ user, repo }) => `/lgtm/alerts/github/${user}/${repo}`, 17 | ...commonAttrs, 18 | }), 19 | redirector({ 20 | route: { 21 | base: 'lgtm/grade', 22 | pattern: ':language/g/:user/:repo', 23 | }, 24 | transformPath: ({ language, user, repo }) => 25 | `/lgtm/grade/${language}/github/${user}/${repo}`, 26 | ...commonAttrs, 27 | }), 28 | ] 29 | -------------------------------------------------------------------------------- /services/travis/travis-php-version-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'TravisPhpVersionRedirect', 7 | title: 'TravisPhpVersionRedirect', 8 | pathPrefix: '/', 9 | })) 10 | 11 | t.create('travis-ci no branch') 12 | .get('travis-ci/php-v/symfony/symfony.svg') 13 | .expectRedirect('/travis/php-v/symfony/symfony/master.svg') 14 | 15 | t.create('travis-ci branch') 16 | .get('travis-ci/php-v/symfony/symfony/2.8.svg') 17 | .expectRedirect('/travis/php-v/symfony/symfony/2.8.svg') 18 | 19 | t.create('travis no branch') 20 | .get('travis/php-v/symfony/symfony.svg') 21 | .expectRedirect('/travis/php-v/symfony/symfony/master.svg') 22 | -------------------------------------------------------------------------------- /logo/dependabot.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/librariesio/librariesio-sourcerank.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { anyInteger } = require('../validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('sourcerank').timeout(10000).get('/npm/got.json').expectBadge({ 7 | label: 'sourcerank', 8 | message: anyInteger, 9 | }) 10 | 11 | t.create('sourcerank (scoped npm package)') 12 | .timeout(10000) 13 | .get('/npm/@babel/core.json') 14 | .expectBadge({ 15 | label: 'sourcerank', 16 | message: anyInteger, 17 | }) 18 | 19 | t.create('sourcerank (not a package)') 20 | .timeout(10000) 21 | .get('/npm/foobar-is-not-package.json') 22 | .expectBadge({ 23 | label: 'sourcerank', 24 | message: 'package not found', 25 | }) 26 | -------------------------------------------------------------------------------- /services/bower/bower-license.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('licence') 6 | .timeout(10000) 7 | .get('/bootstrap.json') 8 | .expectBadge({ label: 'license', message: 'MIT' }) 9 | 10 | t.create('license not declared') 11 | .get('/bootstrap.json') 12 | .intercept(nock => 13 | nock('https://libraries.io') 14 | .get('/api/bower/bootstrap') 15 | .reply(200, { normalized_licenses: [] }) 16 | ) 17 | .expectBadge({ label: 'license', message: 'missing' }) 18 | 19 | t.create('licence for Invalid Package') 20 | .timeout(10000) 21 | .get('/it-is-a-invalid-package-should-error.json') 22 | .expectBadge({ label: 'license', message: 'package not found' }) 23 | -------------------------------------------------------------------------------- /services/vaadin-directory/vaadin-directory-rating-count.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('rating count of component') 7 | .get('/rating-count/vaadinvaadin-grid.json') 8 | .expectBadge({ 9 | label: 'rating count', 10 | message: Joi.string().regex(/^\d+?\stotal$/), 11 | }) 12 | 13 | t.create('rating count of component') 14 | .get('/rc/vaadinvaadin-grid.json') 15 | .expectBadge({ 16 | label: 'rating count', 17 | message: Joi.string().regex(/^\d+?\stotal$/), 18 | }) 19 | 20 | t.create('not found').get('/rating-count/does-not-exist.json').expectBadge({ 21 | label: 'rating count', 22 | message: 'not found', 23 | }) 24 | -------------------------------------------------------------------------------- /services/keybase/keybase-pgp.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('existing key fingerprint') 7 | .get('/skyplabs.json') 8 | .expectBadge({ 9 | label: 'pgp', 10 | message: Joi.string().hex().length(16), 11 | }) 12 | 13 | t.create('unknown username').get('/skyplabsssssss.json').expectBadge({ 14 | label: 'pgp', 15 | message: 'profile not found', 16 | }) 17 | 18 | t.create('invalid username').get('/s.json').expectBadge({ 19 | label: 'pgp', 20 | message: 'invalid username', 21 | }) 22 | 23 | t.create('missing key fingerprint').get('/skyp.json').expectBadge({ 24 | label: 'pgp', 25 | message: 'no key fingerprint found', 26 | }) 27 | -------------------------------------------------------------------------------- /services/fedora/fedora.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { 4 | isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch, 5 | } = require('../test-validators') 6 | const t = (module.exports = require('../tester').createServiceTester()) 7 | 8 | t.create('Fedora package (default branch, valid)') 9 | .get('/rpm.json') 10 | .expectBadge({ 11 | label: 'fedora', 12 | message: isVPlusDottedVersionNClausesWithOptionalSuffixAndEpoch, 13 | }) 14 | 15 | t.create('Fedora package (not found)') 16 | .get('/not-a-package/rawhide.json') 17 | .expectBadge({ label: 'fedora', message: 'not found' }) 18 | 19 | t.create('Fedora package (branch not found)') 20 | .get('/not-a-package/not-a-branch.json') 21 | .expectBadge({ label: 'fedora', message: 'branch not found' }) 22 | -------------------------------------------------------------------------------- /services/clojars/clojars-base.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { nonNegativeInteger } = require('../validators') 5 | const { BaseJsonService } = require('..') 6 | 7 | const clojarsSchema = Joi.object({ 8 | downloads: nonNegativeInteger, 9 | latest_release: Joi.string().allow(null), 10 | latest_version: Joi.string().required(), 11 | }).required() 12 | 13 | class BaseClojarsService extends BaseJsonService { 14 | async fetch({ clojar }) { 15 | // Clojars API Doc: https://github.com/clojars/clojars-web/wiki/Data 16 | const url = `https://clojars.org/api/artifacts/${clojar}` 17 | return this._requestJson({ 18 | url, 19 | schema: clojarsSchema, 20 | }) 21 | } 22 | } 23 | 24 | module.exports = { BaseClojarsService } 25 | -------------------------------------------------------------------------------- /services/docker/docker-pulls.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | const { dockerBlue } = require('./docker-helpers') 6 | 7 | t.create('docker pulls (valid, library)') 8 | .get('/_/ubuntu.json') 9 | .expectBadge({ 10 | label: 'docker pulls', 11 | message: isMetric, 12 | color: `#${dockerBlue}`, 13 | }) 14 | 15 | t.create('docker pulls (valid, user)') 16 | .get('/jrottenberg/ffmpeg.json') 17 | .expectBadge({ 18 | label: 'docker pulls', 19 | message: isMetric, 20 | }) 21 | 22 | t.create('docker pulls (not found)') 23 | .get('/_/not-a-real-repo.json') 24 | .expectBadge({ label: 'docker pulls', message: 'repo not found' }) 25 | -------------------------------------------------------------------------------- /services/docker/docker-stars.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | const { dockerBlue } = require('./docker-helpers') 6 | 7 | t.create('docker stars (valid, library)') 8 | .get('/_/ubuntu.json') 9 | .expectBadge({ 10 | label: 'docker stars', 11 | message: isMetric, 12 | color: `#${dockerBlue}`, 13 | }) 14 | 15 | t.create('docker stars (valid, user)') 16 | .get('/jrottenberg/ffmpeg.json') 17 | .expectBadge({ 18 | label: 'docker stars', 19 | message: isMetric, 20 | }) 21 | 22 | t.create('docker stars (not found)') 23 | .get('/_/not-a-real-repo.json') 24 | .expectBadge({ label: 'docker stars', message: 'repo not found' }) 25 | -------------------------------------------------------------------------------- /services/keybase/keybase-zec.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { withRegex } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('existing zcash address') 7 | .get('/skyplabs.json') 8 | .expectBadge({ 9 | label: 'zec', 10 | message: withRegex(/^(?!not found$)/), 11 | }) 12 | 13 | t.create('unknown username').get('/skyplabsssssss.json').expectBadge({ 14 | label: 'zec', 15 | message: 'profile not found', 16 | }) 17 | 18 | t.create('invalid username').get('/s.json').expectBadge({ 19 | label: 'zec', 20 | message: 'invalid username', 21 | }) 22 | 23 | t.create('missing zcash address').get('/test.json').expectBadge({ 24 | label: 'zec', 25 | message: 'no zcash addresses found', 26 | }) 27 | -------------------------------------------------------------------------------- /services/librariesio/librariesio-dependents.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('dependent count').timeout(10000).get('/npm/got.json').expectBadge({ 7 | label: 'dependents', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('dependent count (scoped npm package)') 12 | .timeout(10000) 13 | .get('/npm/@babel/core.json') 14 | .expectBadge({ 15 | label: 'dependents', 16 | message: isMetric, 17 | }) 18 | 19 | t.create('dependent count (nonexistent package)') 20 | .timeout(10000) 21 | .get('/npm/foobar-is-not-package.json') 22 | .expectBadge({ 23 | label: 'dependents', 24 | message: 'package not found', 25 | }) 26 | -------------------------------------------------------------------------------- /services/github/github-code-size.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isFileSize } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('code size in bytes for all languages') 7 | .get('/badges/shields.json') 8 | .expectBadge({ 9 | label: 'code size', 10 | message: isFileSize, 11 | }) 12 | 13 | t.create('code size in bytes for all languages (empty repo)') 14 | .get('/pyvesb/emptyrepo.json') 15 | .expectBadge({ 16 | label: 'code size', 17 | message: '0 B', 18 | }) 19 | 20 | t.create('code size in bytes for all languages (repo not found)') 21 | .get('/not-a-real-user/not-a-real-repo.json') 22 | .expectBadge({ 23 | label: 'code size', 24 | message: 'repo not found', 25 | }) 26 | -------------------------------------------------------------------------------- /services/github/github-deployments.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | const validMessages = [ 7 | 'success', 8 | 'error', 9 | 'failure', 10 | 'inactive', 11 | 'in progress', 12 | 'queued', 13 | 'pending', 14 | ] 15 | const isValidMessages = Joi.equal(...validMessages).required() 16 | 17 | t.create('Deployments') 18 | .get('/badges/shields/shields-staging.json') 19 | .expectBadge({ 20 | label: 'state', 21 | message: isValidMessages, 22 | }) 23 | 24 | t.create('Deployments (environment not found)') 25 | .get('/badges/shields/does-not-exist.json') 26 | .expectBadge({ 27 | label: 'state', 28 | message: 'environment not found', 29 | }) 30 | -------------------------------------------------------------------------------- /services/keybase/keybase-xlm.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { withRegex } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('existing stellar address') 7 | .get('/skyplabs.json') 8 | .expectBadge({ 9 | label: 'xlm', 10 | message: withRegex(/^(?!not found$)/), 11 | }) 12 | 13 | t.create('unknown username').get('/skyplabsssssss.json').expectBadge({ 14 | label: 'xlm', 15 | message: 'profile not found', 16 | }) 17 | 18 | t.create('invalid username').get('/s.json').expectBadge({ 19 | label: 'xlm', 20 | message: 'invalid username', 21 | }) 22 | 23 | t.create('missing stellar address').get('/test.json').expectBadge({ 24 | label: 'xlm', 25 | message: 'no stellar address found', 26 | }) 27 | -------------------------------------------------------------------------------- /entrypoint.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { expect } = require('chai') 4 | const isSvg = require('is-svg') 5 | const got = require('./core/got-test-client') 6 | 7 | let server 8 | before(function () { 9 | this.timeout('5s') 10 | // remove args coming from mocha 11 | // https://github.com/badges/shields/issues/3365 12 | process.argv = [] 13 | server = require('./server') 14 | }) 15 | 16 | after('shut down the server', async function () { 17 | await server.stop() 18 | }) 19 | 20 | it('should render a badge', async function () { 21 | const { statusCode, body } = await got( 22 | 'http://localhost:1111/badge/fruit-apple-green.svg' 23 | ) 24 | expect(statusCode).to.equal(200) 25 | expect(body).to.satisfy(isSvg).and.to.include('fruit').and.to.include('apple') 26 | }) 27 | -------------------------------------------------------------------------------- /services/cocoapods/cocoapods-license.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('license (valid)') 6 | .get('/AFNetworking.json') 7 | .expectBadge({ label: 'license', message: 'MIT' }) 8 | 9 | t.create('missing license') 10 | .get('/TespoTextField.json') 11 | .intercept(nock => 12 | nock('https://trunk.cocoapods.org') 13 | .get('/api/v1/pods/TespoTextField/specs/latest') 14 | .reply(200, { 15 | version: '1.0.7', 16 | platforms: { ios: '8.0' }, 17 | }) 18 | ) 19 | .expectBadge({ label: 'license', message: 'not specified' }) 20 | 21 | t.create('license (not found)') 22 | .get('/not-a-package.json') 23 | .expectBadge({ label: 'license', message: 'not found' }) 24 | -------------------------------------------------------------------------------- /services/npm/npm-collaborators.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { nonNegativeInteger } = require('../validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('gets the contributor count') 7 | .get('/prettier.json') 8 | .expectBadge({ label: 'npm collaborators', message: nonNegativeInteger }) 9 | 10 | t.create('gets the contributor count from a custom registry') 11 | .get('/prettier.json?registry_uri=https://registry.npmjs.com') 12 | .expectBadge({ label: 'npm collaborators', message: nonNegativeInteger }) 13 | 14 | t.create('contributor count for unknown package') 15 | .get('/npm-registry-does-not-have-this-package.json') 16 | .expectBadge({ 17 | label: 'npm collaborators', 18 | message: 'package not found', 19 | }) 20 | -------------------------------------------------------------------------------- /services/symfony/symfony-insight-stars.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { withRegex } = require('../test-validators') 5 | const { sampleProjectUuid, noSymfonyToken } = require('./symfony-test-helpers') 6 | 7 | t.create('valid project stars') 8 | .skipWhen(noSymfonyToken) 9 | .get(`/${sampleProjectUuid}.json`) 10 | .timeout(15000) 11 | .expectBadge({ 12 | label: 'stars', 13 | message: withRegex( 14 | /^(?=.{4}$)(\u2605{0,4}[\u00BC\u00BD\u00BE]?\u2606{0,4})$/ 15 | ), 16 | }) 17 | 18 | t.create('stars: nonexistent project') 19 | .skipWhen(noSymfonyToken) 20 | .get('/abc.json') 21 | .expectBadge({ 22 | label: 'symfony insight', 23 | message: 'project not found', 24 | }) 25 | -------------------------------------------------------------------------------- /services/chrome-web-store/chrome-web-store-price.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Price') 7 | .get('/alhjnofcnnpeaphgeakdhkebafjcpeae.json') 8 | .expectBadge({ 9 | label: 'price', 10 | message: Joi.string().regex(/^\$\d+(.\d{1,2})?$/), 11 | }) 12 | 13 | t.create('Price (not found)') 14 | .get('/invalid-name-of-addon.json') 15 | .expectBadge({ label: 'price', message: 'not found' }) 16 | 17 | // Keep this "inaccessible" test, since this service does not use BaseService#_request. 18 | t.create('Price (inaccessible)') 19 | .get('/alhjnofcnnpeaphgeakdhkebafjcpeae.json') 20 | .networkOff() 21 | .expectBadge({ label: 'price', message: 'inaccessible' }) 22 | -------------------------------------------------------------------------------- /services/codecov/codecov-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | const vcsSNameShortFormMap = { 6 | bb: 'bitbucket', 7 | gh: 'github', 8 | gl: 'gitlab', 9 | } 10 | 11 | module.exports = [ 12 | redirector({ 13 | category: 'coverage', 14 | route: { 15 | base: 'codecov/c', 16 | pattern: 17 | 'token/:token/:vcsName(github|gh|bitbucket|bb|gl|gitlab)/:user/:repo/:branch*', 18 | }, 19 | transformPath: ({ vcsName, user, repo, branch }) => { 20 | const vcs = vcsSNameShortFormMap[vcsName] || vcsName 21 | return `/codecov/c/${vcs}/${user}/${repo}${branch ? `/${branch}` : ''}` 22 | }, 23 | transformQueryParams: ({ token }) => ({ token }), 24 | dateAdded: new Date('2019-03-04'), 25 | }), 26 | ] 27 | -------------------------------------------------------------------------------- /services/jenkins/jenkins-base.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { BaseJsonService } = require('..') 4 | 5 | module.exports = class JenkinsBase extends BaseJsonService { 6 | static get auth() { 7 | return { 8 | userKey: 'jenkins_user', 9 | passKey: 'jenkins_pass', 10 | serviceKey: 'jenkins', 11 | } 12 | } 13 | 14 | async fetch({ 15 | url, 16 | schema, 17 | qs, 18 | errorMessages = { 404: 'instance or job not found' }, 19 | disableStrictSSL, 20 | }) { 21 | return this._requestJson( 22 | this.authHelper.withBasicAuth({ 23 | url, 24 | options: { 25 | qs, 26 | strictSSL: disableStrictSSL === undefined, 27 | }, 28 | schema, 29 | errorMessages, 30 | }) 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /services/vaadin-directory/vaadin-directory-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isSemver } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('latest version of the component (can have v prefixed or without)') 7 | .get('/v/vaadinvaadin-grid.json') 8 | .expectBadge({ 9 | label: 'vaadin directory', 10 | message: isSemver, 11 | }) 12 | 13 | t.create('latest version of the component (can have v prefixed or without)') 14 | .get('/version/vaadinvaadin-grid.json') 15 | .expectBadge({ 16 | label: 'vaadin directory', 17 | message: isSemver, 18 | }) 19 | 20 | t.create('not found').get('/v/does-not-exist.json').expectBadge({ 21 | label: 'vaadin directory', 22 | message: 'not found', 23 | }) 24 | -------------------------------------------------------------------------------- /services/cocoapods/cocoapods-docs.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isIntegerPercentage } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('doc percent (valid)').get('/AFNetworking.json').expectBadge({ 7 | label: 'docs', 8 | message: isIntegerPercentage, 9 | }) 10 | 11 | t.create('doc percent (null)') 12 | .get('/AFNetworking.json') 13 | .intercept(nock => 14 | nock('https://metrics.cocoapods.org') 15 | .get('/api/v1/pods/AFNetworking') 16 | .reply(200, '{"cocoadocs": {"doc_percent": null}}') 17 | ) 18 | .expectBadge({ label: 'docs', message: '0%' }) 19 | 20 | t.create('doc percent (not found)') 21 | .get('/not-a-package.json') 22 | .expectBadge({ label: 'docs', message: 'not found' }) 23 | -------------------------------------------------------------------------------- /services/contributor-count.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const { renderContributorBadge } = require('./contributor-count') 5 | 6 | describe('Contributor count helpers', function () { 7 | test(renderContributorBadge, () => { 8 | given({ label: 'maintainers', contributorCount: 1 }).expect({ 9 | label: 'maintainers', 10 | message: '1', 11 | color: 'red', 12 | }) 13 | given({ label: 'collaborators', contributorCount: 2 }).expect({ 14 | label: 'collaborators', 15 | message: '2', 16 | color: 'yellow', 17 | }) 18 | given({ label: 'collaborators', contributorCount: 3000 }).expect({ 19 | label: 'collaborators', 20 | message: '3k', 21 | color: 'brightgreen', 22 | }) 23 | }) 24 | }) 25 | -------------------------------------------------------------------------------- /services/youtube/youtube-views.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { noToken } = require('../test-helpers') 5 | const { isMetric } = require('../test-validators') 6 | const noYouTubeToken = noToken(require('./youtube-views.service')) 7 | 8 | t.create('video view count') 9 | .skipWhen(noYouTubeToken) 10 | .get('/abBdk8bSPKU.json') 11 | .expectBadge({ 12 | label: 'views', 13 | message: isMetric, 14 | color: 'red', 15 | link: ['https://www.youtube.com/watch?v=abBdk8bSPKU'], 16 | }) 17 | 18 | t.create('video not found') 19 | .skipWhen(noYouTubeToken) 20 | .get('/doesnotexist.json') 21 | .expectBadge({ 22 | label: 'youtube', 23 | message: 'video not found', 24 | color: 'red', 25 | }) 26 | -------------------------------------------------------------------------------- /core/base-service/json.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // See available emoji at http://emoji.muan.co/ 4 | const emojic = require('emojic') 5 | const { InvalidResponse } = require('./errors') 6 | const trace = require('./trace') 7 | 8 | function parseJson(buffer) { 9 | const logTrace = (...args) => trace.logTrace('fetch', ...args) 10 | let json 11 | try { 12 | json = JSON.parse(buffer) 13 | } catch (err) { 14 | logTrace(emojic.dart, 'Response JSON (unparseable)', buffer) 15 | throw new InvalidResponse({ 16 | prettyMessage: 'unparseable json response', 17 | underlyingError: err, 18 | }) 19 | } 20 | logTrace(emojic.dart, 'Response JSON (before validation)', json, { 21 | deep: true, 22 | }) 23 | return json 24 | } 25 | 26 | module.exports = { 27 | parseJson, 28 | } 29 | -------------------------------------------------------------------------------- /services/keybase/keybase-btc.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { withRegex } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('existing bitcoin address') 7 | .get('/skyplabs.json') 8 | .expectBadge({ 9 | label: 'btc', 10 | message: withRegex(/^[13][a-km-zA-HJ-NP-Z1-9]{25,34}$/), 11 | }) 12 | 13 | t.create('unknown username').get('/skyplabsssssss.json').expectBadge({ 14 | label: 'btc', 15 | message: 'profile not found', 16 | }) 17 | 18 | t.create('invalid username').get('/s.json').expectBadge({ 19 | label: 'btc', 20 | message: 'invalid username', 21 | }) 22 | 23 | t.create('missing bitcoin address').get('/test.json').expectBadge({ 24 | label: 'btc', 25 | message: 'no bitcoin addresses found', 26 | }) 27 | -------------------------------------------------------------------------------- /badge-maker/index.test-d.ts: -------------------------------------------------------------------------------- 1 | import { expectType, expectError, expectAssignable } from 'tsd' 2 | import { makeBadge, ValidationError } from '.' 3 | 4 | expectError(makeBadge('string is invalid')) 5 | expectError(makeBadge({})) 6 | expectError( 7 | makeBadge({ 8 | message: 'passed', 9 | style: 'invalid style', 10 | }) 11 | ) 12 | 13 | expectType( 14 | makeBadge({ 15 | message: 'passed', 16 | }) 17 | ) 18 | expectType( 19 | makeBadge({ 20 | label: 'build', 21 | message: 'passed', 22 | }) 23 | ) 24 | expectType( 25 | makeBadge({ 26 | label: 'build', 27 | message: 'passed', 28 | labelColor: 'green', 29 | color: 'red', 30 | style: 'flat', 31 | }) 32 | ) 33 | 34 | const error = new ValidationError() 35 | expectAssignable(error) 36 | -------------------------------------------------------------------------------- /services/conda/conda-downloads.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('downloads').get('/d/conda-forge/zlib.json').expectBadge({ 7 | label: 'conda|downloads', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('downloads (skip prefix)') 12 | .get('/dn/conda-forge/zlib.json') 13 | .expectBadge({ label: 'downloads', message: isMetric }) 14 | 15 | t.create('unknown package') 16 | .get('/d/conda-forge/some-bogus-package-that-never-exists.json') 17 | .expectBadge({ label: 'conda', message: 'not found' }) 18 | 19 | t.create('unknown channel') 20 | .get('/d/some-bogus-channel-that-never-exists/zlib.json') 21 | .expectBadge({ label: 'conda', message: 'not found' }) 22 | -------------------------------------------------------------------------------- /services/github/github-last-commit.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isFormattedDate } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('last commit (recent)') 7 | .get('/eslint/eslint.json') 8 | .expectBadge({ label: 'last commit', message: isFormattedDate }) 9 | 10 | t.create('last commit (ancient)') 11 | .get('/badges/badgr.co.json') 12 | .expectBadge({ label: 'last commit', message: 'january 2014' }) 13 | 14 | t.create('last commit (on branch)') 15 | .get('/badges/badgr.co/shielded.json') 16 | .expectBadge({ label: 'last commit', message: 'july 2013' }) 17 | 18 | t.create('last commit (repo not found)') 19 | .get('/badges/helmets.json') 20 | .expectBadge({ label: 'last commit', message: 'repo not found' }) 21 | -------------------------------------------------------------------------------- /services/homebrew/homebrew-cask.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusTripleDottedVersion } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('homebrew cask (valid)').get('/iterm2.json').expectBadge({ 7 | label: 'homebrew cask', 8 | message: isVPlusTripleDottedVersion, 9 | }) 10 | 11 | t.create('homebrew cask (valid)') 12 | .get('/iterm2.json') 13 | .intercept(nock => 14 | nock('https://formulae.brew.sh') 15 | .get('/api/cask/iterm2.json') 16 | .reply(200, { version: '3.3.6' }) 17 | ) 18 | .expectBadge({ label: 'homebrew cask', message: 'v3.3.6' }) 19 | 20 | t.create('homebrew cask (not found)') 21 | .get('/not-a-package.json') 22 | .expectBadge({ label: 'homebrew cask', message: 'not found' }) 23 | -------------------------------------------------------------------------------- /services/homebrew/homebrew.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusTripleDottedVersion } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('homebrew (valid)').get('/cake.json').expectBadge({ 7 | label: 'homebrew', 8 | message: isVPlusTripleDottedVersion, 9 | }) 10 | 11 | t.create('homebrew (valid)') 12 | .get('/cake.json') 13 | .intercept(nock => 14 | nock('https://formulae.brew.sh') 15 | .get('/api/formula/cake.json') 16 | .reply(200, { versions: { stable: '0.23.0', devel: null, head: null } }) 17 | ) 18 | .expectBadge({ label: 'homebrew', message: 'v0.23.0' }) 19 | 20 | t.create('homebrew (not found)') 21 | .get('/not-a-package.json') 22 | .expectBadge({ label: 'homebrew', message: 'not found' }) 23 | -------------------------------------------------------------------------------- /services/symfony/symfony-insight-violations.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { withRegex } = require('../test-validators') 5 | const { sampleProjectUuid, noSymfonyToken } = require('./symfony-test-helpers') 6 | 7 | t.create('valid project violations') 8 | .skipWhen(noSymfonyToken) 9 | .get(`/${sampleProjectUuid}.json`) 10 | .timeout(15000) 11 | .expectBadge({ 12 | label: 'violations', 13 | message: withRegex( 14 | /\d* critical|\d* critical, \d* major|\d* critical, \d* major, \d* minor|\d* critical, \d* major, \d* minor, \d* info|\d* critical, \d* minor|\d* critical, \d* info|\d* major|\d* major, \d* minor|\d* major, \d* minor, \d* info|\d* major, \d* info|\d* minor|\d* minor, \d* info/ 15 | ), 16 | }) 17 | -------------------------------------------------------------------------------- /services/youtube/youtube-comments.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { noToken } = require('../test-helpers') 5 | const { isMetric } = require('../test-validators') 6 | const noYouTubeToken = noToken(require('./youtube-comments.service')) 7 | 8 | t.create('video comment count') 9 | .skipWhen(noYouTubeToken) 10 | .get('/wGJHwc5ksMA.json') 11 | .expectBadge({ 12 | label: 'comments', 13 | message: isMetric, 14 | color: 'red', 15 | link: ['https://www.youtube.com/watch?v=wGJHwc5ksMA'], 16 | }) 17 | 18 | t.create('video not found') 19 | .skipWhen(noYouTubeToken) 20 | .get('/doesnotexist.json') 21 | .expectBadge({ 22 | label: 'youtube', 23 | message: 'video not found', 24 | color: 'red', 25 | }) 26 | -------------------------------------------------------------------------------- /services/pypi/pypi-implementation.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('implementation (valid, package version in request)') 6 | .get('/beehive/1.0.json') 7 | .expectBadge({ label: 'implementation', message: 'cpython | jython | pypy' }) 8 | 9 | t.create('implementation (valid, no package version specified)') 10 | .get('/numpy.json') 11 | .expectBadge({ label: 'implementation', message: 'cpython' }) 12 | 13 | t.create('implementation (not specified)') 14 | .get('/chai/1.1.2.json') 15 | .expectBadge({ label: 'implementation', message: 'cpython' }) 16 | 17 | t.create('implementation (invalid)').get('/not-a-package.json').expectBadge({ 18 | label: 'implementation', 19 | message: 'package or version not found', 20 | }) 21 | -------------------------------------------------------------------------------- /config/default.yml: -------------------------------------------------------------------------------- 1 | public: 2 | bind: 3 | address: '::' 4 | 5 | metrics: 6 | prometheus: 7 | enabled: false 8 | endpointEnabled: false 9 | influx: 10 | enabled: false 11 | timeoutMilliseconds: 1000 12 | intervalSeconds: 15 13 | ssl: 14 | isSecure: false 15 | 16 | cors: 17 | allowedOrigin: [] 18 | 19 | persistence: 20 | dir: './private' 21 | 22 | services: 23 | github: 24 | baseUri: 'https://api.github.com/' 25 | debug: 26 | enabled: false 27 | intervalSeconds: 200 28 | trace: false 29 | 30 | cacheHeaders: 31 | defaultCacheLengthSeconds: 120 32 | 33 | rateLimit: true 34 | 35 | handleInternalErrors: true 36 | 37 | fetchLimit: '10MB' 38 | 39 | shieldsProductionHerokuHacks: false 40 | 41 | private: {} 42 | -------------------------------------------------------------------------------- /services/appveyor/appveyor-job-build.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isBuildStatus } = require('../build-status') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Job CI status') 7 | .timeout(10000) 8 | .get('/wpmgprostotema/voicetranscoder/Windows.json') 9 | .expectBadge({ label: 'build', message: isBuildStatus }) 10 | 11 | t.create('Job CI status on branch') 12 | .timeout(10000) 13 | .get('/wpmgprostotema/voicetranscoder/Linux/master.json') 14 | .expectBadge({ label: 'build', message: isBuildStatus }) 15 | 16 | t.create('Job CI status on nonexistent project') 17 | .timeout(10000) 18 | .get('/somerandomproject/thatdoesntexist/foo.json') 19 | .expectBadge({ 20 | label: 'build', 21 | message: 'project not found or access denied', 22 | }) 23 | -------------------------------------------------------------------------------- /services/hackage/hackage-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('hackage version (valid)').get('/lens.json').expectBadge({ 7 | label: 'hackage', 8 | message: isVPlusDottedVersionAtLeastOne, 9 | }) 10 | 11 | t.create('hackage version (not found)') 12 | .get('/not-a-package.json') 13 | .expectBadge({ label: 'hackage', message: 'not found' }) 14 | 15 | t.create('hackage version (unexpected response)') 16 | .get('/lens.json') 17 | .intercept(nock => 18 | nock('https://hackage.haskell.org') 19 | .get('/package/lens/lens.cabal') 20 | .reply(200, '') 21 | ) 22 | .expectBadge({ label: 'hackage', message: 'invalid response data' }) 23 | -------------------------------------------------------------------------------- /services/node/node-lts.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const NodeVersion = require('./node-lts.service') 5 | 6 | describe('node-lts renderStaticPreview', function () { 7 | it('should have parity with render()', async function () { 8 | const nodeVersionRange = '>= 6.0.0' 9 | 10 | const expectedNoTag = await NodeVersion.renderStaticPreview({ 11 | nodeVersionRange, 12 | }) 13 | const expectedLatestTag = await NodeVersion.renderStaticPreview({ 14 | nodeVersionRange, 15 | tag: 'latest', 16 | }) 17 | 18 | test(NodeVersion.renderStaticPreview.bind(NodeVersion), () => { 19 | given({ nodeVersionRange }).expect(expectedNoTag) 20 | given({ nodeVersionRange, tag: 'latest' }).expect(expectedLatestTag) 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /services/pkgreview/package-rating.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { withRegex, isStarRating } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | const isRatingWithReviews = withRegex( 7 | /^(([0-4](.?([0-9]))?)|5)\/5?\s*\([0-9]*\)$/ 8 | ) 9 | 10 | t.create('Stars Badge renders') 11 | .get('/stars/npm/react.json') 12 | .expectBadge({ label: 'stars', message: isStarRating }) 13 | 14 | t.create('Rating Badge renders') 15 | .get('/rating/npm/react.json') 16 | .expectBadge({ label: 'rating', message: isRatingWithReviews }) 17 | 18 | t.create('nonexistent package') 19 | .get('/rating/npm/ohlolweallknowthispackagewontexist.json') 20 | .expectBadge({ 21 | label: 'rating', 22 | message: 'package not found', 23 | color: 'red', 24 | }) 25 | -------------------------------------------------------------------------------- /services/node/node-current.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const NodeVersion = require('./node-current.service') 5 | 6 | describe('node static renderStaticPreview', function () { 7 | it('should have parity with render()', async function () { 8 | const nodeVersionRange = '>= 6.0.0' 9 | 10 | const expectedNoTag = await NodeVersion.renderStaticPreview({ 11 | nodeVersionRange, 12 | }) 13 | const expectedLatestTag = await NodeVersion.renderStaticPreview({ 14 | nodeVersionRange, 15 | tag: 'latest', 16 | }) 17 | 18 | test(NodeVersion.renderStaticPreview.bind(NodeVersion), () => { 19 | given({ nodeVersionRange }).expect(expectedNoTag) 20 | given({ nodeVersionRange, tag: 'latest' }).expect(expectedLatestTag) 21 | }) 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /services/packagist/packagist-license.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { expect } = require('chai') 4 | const { NotFound } = require('..') 5 | const PackagistLicense = require('./packagist-license.service') 6 | 7 | describe('PackagistLicense', function () { 8 | it('should throw NotFound when default branch is missing', function () { 9 | const json = { 10 | packages: { 11 | 'doctrine/orm': {}, 12 | 'elhadraoui/doctrine-orm': { 13 | 'dev-master': { license: 'MIT' }, 14 | }, 15 | }, 16 | } 17 | expect(() => 18 | PackagistLicense.prototype.transform({ 19 | json, 20 | user: 'doctrine', 21 | repo: 'orm', 22 | }) 23 | ) 24 | .to.throw(NotFound) 25 | .with.property('prettyMessage', 'default branch not found') 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /services/requires/requires.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | const isRequireStatus = Joi.string().regex( 7 | /^(up to date|outdated|insecure|unknown)$/ 8 | ) 9 | 10 | t.create('requirements (valid, without branch)') 11 | .get('/github/celery/celery.json') 12 | .expectBadge({ 13 | label: 'requirements', 14 | message: isRequireStatus, 15 | }) 16 | 17 | t.create('requirements (valid, with branch)') 18 | .get('/github/celery/celery/master.json') 19 | .expectBadge({ 20 | label: 'requirements', 21 | message: isRequireStatus, 22 | }) 23 | 24 | t.create('requirements (not found)') 25 | .get('/github/PyvesB/EmptyRepo.json') 26 | .expectBadge({ label: 'requirements', message: 'not found' }) 27 | -------------------------------------------------------------------------------- /services/static-badge/query-string-static.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { BaseStaticService } = require('..') 5 | 6 | const queryParamSchema = Joi.object({ 7 | message: Joi.string().required(), 8 | }).required() 9 | 10 | module.exports = class QueryStringStaticBadge extends BaseStaticService { 11 | static get category() { 12 | return 'static' 13 | } 14 | 15 | static get route() { 16 | return { 17 | base: '', 18 | pattern: 'static/:schemaVersion(v1)', 19 | // All but one of the parameters are parsed via coalesceBadge. This 20 | // reuses what is the override behaviour for other badges. 21 | queryParamSchema, 22 | } 23 | } 24 | 25 | handle(namedParams, queryParams) { 26 | return { message: queryParams.message } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /services/vaadin-directory/vaadin-directory-release-date.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isFormattedDate } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('latest release date of the component (format: yyyy-mm-dd)') 7 | .get('/rd/vaadinvaadin-grid.json') 8 | .expectBadge({ 9 | label: 'latest release date', 10 | message: isFormattedDate, 11 | }) 12 | 13 | t.create('latest release date of the component (format: yyyy-mm-dd)') 14 | .get('/release-date/vaadinvaadin-grid.json') 15 | .expectBadge({ 16 | label: 'latest release date', 17 | message: isFormattedDate, 18 | }) 19 | 20 | t.create('not found') 21 | .get('/release-date/does-not-exist.json') 22 | .expectBadge({ label: 'latest release date', message: 'not found' }) 23 | -------------------------------------------------------------------------------- /lib/load-simple-icons.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const originalSimpleIcons = require('simple-icons') 4 | const { svg2base64 } = require('./svg-helpers') 5 | 6 | function loadSimpleIcons() { 7 | const simpleIcons = {} 8 | Object.keys(originalSimpleIcons).forEach(key => { 9 | const k = key.toLowerCase().replace(/ /g, '-') 10 | simpleIcons[k] = originalSimpleIcons[key] 11 | simpleIcons[k].base64 = { 12 | default: svg2base64( 13 | simpleIcons[k].svg.replace(' -------------------------------------------------------------------------------- /services/cocoapods/cocoapods-base.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { BaseJsonService } = require('..') 5 | 6 | const schema = Joi.object({ 7 | version: Joi.string().required(), 8 | // https://github.com/badges/shields/issues/4688 9 | license: Joi.alternatives( 10 | Joi.string().required(), 11 | Joi.object({ 12 | type: Joi.string().required(), 13 | }).required() 14 | ), 15 | // https://github.com/badges/shields/pull/209 16 | platforms: Joi.object().default({ ios: '5.0', osx: '10.7' }), 17 | }).required() 18 | 19 | module.exports = class BaseCocoaPodsService extends BaseJsonService { 20 | async fetch({ spec }) { 21 | return this._requestJson({ 22 | schema, 23 | url: `https://trunk.cocoapods.org/api/v1/pods/${spec}/specs/latest`, 24 | }) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /services/librariesio/librariesio-dependent-repos.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isMetric } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('dependent repo count') 7 | .timeout(10000) 8 | .get('/npm/got.json') 9 | .expectBadge({ 10 | label: 'dependent repos', 11 | message: isMetric, 12 | }) 13 | 14 | t.create('dependent repo count (scoped npm package)') 15 | .timeout(10000) 16 | .get('/npm/@babel/core.json') 17 | .expectBadge({ 18 | label: 'dependent repos', 19 | message: isMetric, 20 | }) 21 | 22 | t.create('dependent repo count (not a package)') 23 | .timeout(10000) 24 | .get('/npm/foobar-is-not-package.json') 25 | .expectBadge({ 26 | label: 'dependent repos', 27 | message: 'package not found', 28 | }) 29 | -------------------------------------------------------------------------------- /core/service-test-runner/services-for-title.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const servicesForTitle = require('./services-for-title') 5 | 6 | describe('Services from PR title', function () { 7 | test(servicesForTitle, () => { 8 | given('[Travis] Fix timeout issues').expect(['travis']) 9 | given('[Travis Sonar] Support user token authentication').expect([ 10 | 'travis', 11 | 'sonar', 12 | ]) 13 | given('[CRAN CPAN CTAN] Add test coverage').expect(['cran', 'cpan', 'ctan']) 14 | given( 15 | '[RFC] Add Joi-based request validation to BaseJsonService and rewrite [NPM] badges' 16 | ).expect(['npm']) 17 | given('make changes to [CRAN] and [CPAN]').expect(['cran', 'cpan']) 18 | given('[github appveyor ]').expect(['github', 'appveyor']) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /services/maintenance/maintenance.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | const currentYear = new Date().getUTCFullYear() 6 | 7 | t.create('yes last maintained 2016 (no)') 8 | .get('/yes/2016.json') 9 | .expectBadge({ label: 'maintained', message: 'no! (as of 2016)' }) 10 | 11 | t.create('no longer maintained 2017 (no)') 12 | .get('/no/2017.json') 13 | .expectBadge({ label: 'maintained', message: 'no! (as of 2017)' }) 14 | 15 | t.create('yes this year (yes)') 16 | .get(`/yes/${currentYear}.json`) 17 | .expectBadge({ label: 'maintained', message: 'yes' }) 18 | 19 | t.create(`until end of ${currentYear} (yes)`) 20 | .get(`/until end of ${currentYear}/${currentYear}.json`) 21 | .expectBadge({ label: 'maintained', message: `until end of ${currentYear}` }) 22 | -------------------------------------------------------------------------------- /services/chrome-web-store/chrome-web-store-version.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Version').get('/alhjnofcnnpeaphgeakdhkebafjcpeae.json').expectBadge({ 7 | label: 'chrome web store', 8 | message: isVPlusDottedVersionAtLeastOne, 9 | }) 10 | 11 | t.create('Version (not found)') 12 | .get('/invalid-name-of-addon.json') 13 | .expectBadge({ label: 'chrome web store', message: 'not found' }) 14 | 15 | // Keep this "inaccessible" test, since this service does not use BaseService#_request. 16 | t.create('Version (inaccessible)') 17 | .get('/alhjnofcnnpeaphgeakdhkebafjcpeae.json') 18 | .networkOff() 19 | .expectBadge({ label: 'chrome web store', message: 'inaccessible' }) 20 | -------------------------------------------------------------------------------- /services/liberapay/liberapay-gives.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { isCurrencyOverTime } = require('./liberapay-base') 5 | 6 | t.create('Giving (valid)').get('/Changaco.json').expectBadge({ 7 | label: 'gives', 8 | message: isCurrencyOverTime, 9 | }) 10 | 11 | t.create('Giving (not found)') 12 | .get('/does-not-exist.json') 13 | .expectBadge({ label: 'liberapay', message: 'not found' }) 14 | 15 | t.create('Giving (null)') 16 | .get('/Liberapay.json') 17 | .intercept(nock => 18 | nock('https://liberapay.com').get('/Liberapay/public.json').reply(200, { 19 | npatrons: 0, 20 | giving: null, 21 | receiving: null, 22 | goal: null, 23 | }) 24 | ) 25 | .expectBadge({ label: 'liberapay', message: 'no public giving stats' }) 26 | -------------------------------------------------------------------------------- /services/symfony/symfony-insight-grade.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | const { sampleProjectUuid, noSymfonyToken } = require('./symfony-test-helpers') 6 | 7 | t.create('valid project grade') 8 | .skipWhen(noSymfonyToken) 9 | .get(`/${sampleProjectUuid}.json`) 10 | .timeout(15000) 11 | .expectBadge({ 12 | label: 'grade', 13 | message: Joi.equal( 14 | 'platinum', 15 | 'gold', 16 | 'silver', 17 | 'bronze', 18 | 'no medal' 19 | ).required(), 20 | }) 21 | 22 | t.create('nonexistent project') 23 | .skipWhen(noSymfonyToken) 24 | .get('/45afb680-d4e6-4e66-93ea-bcfa79eb8a88.json') 25 | .expectBadge({ 26 | label: 'symfony insight', 27 | message: 'project not found', 28 | }) 29 | -------------------------------------------------------------------------------- /services/website-status.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const { renderWebsiteStatus } = require('./website-status') 5 | 6 | describe('Website status helpers', function () { 7 | const customOptions = { 8 | upMessage: 'groovy', 9 | upColor: 'papayawhip', 10 | downMessage: 'no good', 11 | downColor: 'gray', 12 | } 13 | 14 | test(renderWebsiteStatus, () => { 15 | given({ isUp: true }).expect({ message: 'up', color: 'brightgreen' }) 16 | given({ isUp: false }).expect({ message: 'down', color: 'red' }) 17 | given({ isUp: true, ...customOptions }).expect({ 18 | message: 'groovy', 19 | color: 'papayawhip', 20 | }) 21 | given({ isUp: false, ...customOptions }).expect({ 22 | message: 'no good', 23 | color: 'gray', 24 | }) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /services/cran/cran.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | const { isVPlusTripleDottedVersion } = require('../test-validators') 5 | 6 | const t = (module.exports = new ServiceTester({ 7 | id: 'cran', 8 | title: 'CRAN/METACRAN', 9 | })) 10 | 11 | t.create('version (valid)').get('/v/devtools.json').expectBadge({ 12 | label: 'cran', 13 | message: isVPlusTripleDottedVersion, 14 | }) 15 | 16 | t.create('version (not found)') 17 | .get('/v/some-bogus-package.json') 18 | .expectBadge({ label: 'cran', message: 'not found' }) 19 | 20 | t.create('license (valid)') 21 | .get('/l/devtools.json') 22 | .expectBadge({ label: 'license', message: 'GPL (>= 2)' }) 23 | 24 | t.create('license (not found)') 25 | .get('/l/some-bogus-package.json') 26 | .expectBadge({ label: 'cran', message: 'not found' }) 27 | -------------------------------------------------------------------------------- /services/pypi/pypi-format.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('format (wheel, package version in request)') 6 | .get('/requests/2.18.4.json') 7 | .expectBadge({ label: 'format', message: 'wheel' }) 8 | 9 | t.create('format (wheel, no package version specified)') 10 | .get('/requests.json') 11 | .expectBadge({ label: 'format', message: 'wheel' }) 12 | 13 | t.create('format (source)') 14 | .get('/chai/1.1.2.json') 15 | .expectBadge({ label: 'format', message: 'source' }) 16 | 17 | t.create('format (egg)') 18 | .get('/virtualenv/0.8.2.json') 19 | .expectBadge({ label: 'format', message: 'egg' }) 20 | 21 | t.create('format (invalid)') 22 | .get('/not-a-package.json') 23 | .expectBadge({ label: 'format', message: 'package or version not found' }) 24 | -------------------------------------------------------------------------------- /services/spiget/spiget-rating.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isStarRating, withRegex } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Stars - EssentialsX (id 9089)').get('/stars/9089.json').expectBadge({ 7 | label: 'rating', 8 | message: isStarRating, 9 | }) 10 | 11 | t.create('Stars - Invalid Resource (id 1)').get('/stars/1.json').expectBadge({ 12 | label: 'rating', 13 | message: 'not found', 14 | }) 15 | 16 | t.create('Rating - EssentialsX (id 9089)') 17 | .get('/rating/9089.json') 18 | .expectBadge({ 19 | label: 'rating', 20 | message: withRegex(/^(\d*\.\d+)(\/5 \()(\d+)(\))$/), 21 | }) 22 | 23 | t.create('Rating - Invalid Resource (id 1)').get('/rating/1.json').expectBadge({ 24 | label: 'rating', 25 | message: 'not found', 26 | }) 27 | -------------------------------------------------------------------------------- /services/bithound/bithound.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | 5 | const t = (module.exports = new ServiceTester({ 6 | id: 'bithound', 7 | title: 'BitHound', 8 | })) 9 | 10 | t.create('no longer available (code)') 11 | .get('/code/github/rexxars/sse-channel.json') 12 | .expectBadge({ 13 | label: 'bithound', 14 | message: 'no longer available', 15 | }) 16 | 17 | t.create('no longer available (dependencies)') 18 | .get('/dependencies/github/rexxars/sse-channel.json') 19 | .expectBadge({ 20 | label: 'bithound', 21 | message: 'no longer available', 22 | }) 23 | 24 | t.create('no longer available (devDpendencies)') 25 | .get('/devDependencies/github/rexxars/sse-channel.json') 26 | .expectBadge({ 27 | label: 'bithound', 28 | message: 'no longer available', 29 | }) 30 | -------------------------------------------------------------------------------- /services/offset-earth/offset-earth-trees.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { isMetric } = require('../test-validators') 5 | 6 | t.create('request for existing username').get('/offsetearth.json').expectBadge({ 7 | label: 'trees', 8 | message: isMetric, 9 | }) 10 | 11 | t.create('request for existing username') 12 | .get('/offsetearth.json') 13 | .intercept(nock => 14 | nock('https://public.offset.earth') 15 | .get('/users/offsetearth/trees') 16 | .reply(200, { total: 50 }) 17 | ) 18 | .expectBadge({ 19 | label: 'trees', 20 | message: '50', 21 | color: 'green', 22 | }) 23 | 24 | t.create('invalid username') 25 | .get('/non-existent-username.json') 26 | .expectBadge({ label: 'trees', message: 'username not found', color: 'red' }) 27 | -------------------------------------------------------------------------------- /services/continuousphp/continuousphp.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { isBuildStatus } = require('../build-status') 5 | const t = (module.exports = require('../tester').createServiceTester()) 6 | 7 | t.create('build status on default branch') 8 | .get('/git-hub/doctrine/dbal.json') 9 | .expectBadge({ 10 | label: 'build', 11 | message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')), 12 | }) 13 | 14 | t.create('build status on named branch') 15 | .get('/git-hub/doctrine/dbal/develop.json') 16 | .expectBadge({ 17 | label: 'build', 18 | message: Joi.alternatives().try(isBuildStatus, Joi.equal('unknown')), 19 | }) 20 | 21 | t.create('unknown repo') 22 | .get('/git-hub/this-repo/does-not-exist.json') 23 | .expectBadge({ label: 'continuousphp', message: 'project not found' }) 24 | -------------------------------------------------------------------------------- /services/sourcegraph/sourcegraph.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { withRegex } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | // Matches API responses such as "0 projects", "1 projects", "182 projects", "14.0k projects". 7 | // There may be other cases not covered by this regex, but hopefully the tested projects won't vary much. 8 | const projectsCount = withRegex(/^[0-9]*(\.[0-9]k)?\sprojects$/) 9 | 10 | t.create('project usage count') 11 | .get('/github.com/theupdateframework/notary.json') 12 | .expectBadge({ 13 | label: 'used by', 14 | message: projectsCount, 15 | }) 16 | 17 | t.create('project without any available information') 18 | .get('/github.com/badges/daily-tests.json') 19 | .expectBadge({ 20 | label: 'used by', 21 | message: '0 projects', 22 | }) 23 | -------------------------------------------------------------------------------- /services/liberapay/liberapay-receives.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | const { isCurrencyOverTime } = require('./liberapay-base') 5 | 6 | t.create('Receiving (valid)').get('/Changaco.json').expectBadge({ 7 | label: 'receives', 8 | message: isCurrencyOverTime, 9 | }) 10 | 11 | t.create('Receiving (not found)') 12 | .get('/does-not-exist.json') 13 | .expectBadge({ label: 'liberapay', message: 'not found' }) 14 | 15 | t.create('Receiving (null)') 16 | .get('/Liberapay.json') 17 | .intercept(nock => 18 | nock('https://liberapay.com').get('/Liberapay/public.json').reply(200, { 19 | npatrons: 0, 20 | giving: null, 21 | receiving: null, 22 | goal: null, 23 | }) 24 | ) 25 | .expectBadge({ label: 'liberapay', message: 'no public receiving stats' }) 26 | -------------------------------------------------------------------------------- /services/travis/travis-php-version-redirect.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | 5 | const ciRedirect = redirector({ 6 | category: 'platform-support', 7 | route: { 8 | base: 'travis-ci/php-v', 9 | pattern: ':user/:repo/:branch*', 10 | }, 11 | transformPath: ({ user, repo, branch }) => 12 | branch 13 | ? `/travis/php-v/${user}/${repo}/${branch}` 14 | : `/travis/php-v/${user}/${repo}/master`, 15 | dateAdded: new Date('2019-04-22'), 16 | }) 17 | 18 | const branchRedirect = redirector({ 19 | category: 'platform-support', 20 | route: { 21 | base: 'travis/php-v', 22 | pattern: ':user/:repo', 23 | }, 24 | transformPath: ({ user, repo }) => `/travis/php-v/${user}/${repo}/master`, 25 | dateAdded: new Date('2020-07-12'), 26 | }) 27 | 28 | module.exports = { ciRedirect, branchRedirect } 29 | -------------------------------------------------------------------------------- /services/sonar/sonar-documented-api-density.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given } = require('sazerac') 4 | const SonarDocumentedApiDensity = require('./sonar-documented-api-density.service') 5 | 6 | describe('SonarDocumentedApiDensity', function () { 7 | test(SonarDocumentedApiDensity.render, () => { 8 | given({ density: 0 }).expect({ 9 | message: '0%', 10 | color: 'red', 11 | }) 12 | given({ density: 10 }).expect({ 13 | message: '10%', 14 | color: 'orange', 15 | }) 16 | given({ density: 20 }).expect({ 17 | message: '20%', 18 | color: 'yellow', 19 | }) 20 | given({ density: 50 }).expect({ 21 | message: '50%', 22 | color: 'yellowgreen', 23 | }) 24 | given({ density: 100 }).expect({ 25 | message: '100%', 26 | color: 'brightgreen', 27 | }) 28 | }) 29 | }) 30 | -------------------------------------------------------------------------------- /services/ansible/ansible-role.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { ServiceTester } = require('../tester') 4 | const { isMetric } = require('../test-validators') 5 | 6 | const t = (module.exports = new ServiceTester({ 7 | id: 'AnsibleRole', 8 | title: 'AnsibleRole', 9 | pathPrefix: '/ansible/role', 10 | })) 11 | 12 | t.create('role name (valid)') 13 | .get('/14542.json') 14 | .expectBadge({ label: 'role', message: 'openwisp.openwisp2' }) 15 | 16 | t.create('role name (not found)') 17 | .get('/000.json') 18 | .expectBadge({ label: 'role', message: 'not found' }) 19 | 20 | t.create('role downloads (valid)') 21 | .get('/d/14542.json') 22 | .expectBadge({ label: 'role downloads', message: isMetric }) 23 | 24 | t.create('role downloads (not found)') 25 | .get('/d/does-not-exist.json') 26 | .expectBadge({ label: 'role downloads', message: 'not found' }) 27 | -------------------------------------------------------------------------------- /services/github/github-lerna-json.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isSemver } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('Lerna version').get('/babel/babel.json').expectBadge({ 7 | label: 'lerna', 8 | message: isSemver, 9 | }) 10 | 11 | t.create('Lerna version (independent)') 12 | .get('/jneander/jneander.json') 13 | .expectBadge({ 14 | label: 'lerna', 15 | message: 'independent', 16 | }) 17 | 18 | t.create('Lerna version (branch)').get('/babel/babel/master.json').expectBadge({ 19 | label: 'lerna@master', 20 | message: isSemver, 21 | }) 22 | 23 | t.create('Lerna version (lerna.json missing)') 24 | .get('/PyvesB/empty-repo.json') 25 | .expectBadge({ 26 | label: 'lerna', 27 | message: 'repo not found, branch not found, or lerna.json missing', 28 | }) 29 | -------------------------------------------------------------------------------- /services/jenkins/jenkins-tests-redirector.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { redirector } = require('..') 4 | const { buildRedirectUrl } = require('./jenkins-common') 5 | 6 | const commonProps = { 7 | category: 'build', 8 | transformPath: () => '/jenkins/tests', 9 | transformQueryParams: ({ protocol, host, job }) => ({ 10 | jobUrl: buildRedirectUrl({ protocol, host, job }), 11 | }), 12 | } 13 | 14 | module.exports = [ 15 | redirector({ 16 | route: { 17 | base: 'jenkins/t', 18 | pattern: ':protocol(http|https)/:host/:job+', 19 | }, 20 | dateAdded: new Date('2019-04-20'), 21 | ...commonProps, 22 | }), 23 | redirector({ 24 | route: { 25 | base: 'jenkins/tests', 26 | pattern: ':protocol(http|https)/:host/:job+', 27 | }, 28 | dateAdded: new Date('2019-11-29'), 29 | ...commonProps, 30 | }), 31 | ] 32 | -------------------------------------------------------------------------------- /services/liberapay/liberapay-goal.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { expect } = require('chai') 4 | const { test, given } = require('sazerac') 5 | const { InvalidResponse } = require('..') 6 | const LiberapayGoal = require('./liberapay-goal.service') 7 | 8 | describe('LiberapayGoal', function () { 9 | test(LiberapayGoal.prototype.transform, () => { 10 | given({ goal: {}, receiving: null }).expect({ 11 | percentAchieved: 0, 12 | }) 13 | given({ goal: { amount: 100 }, receiving: { amount: 89 } }).expect({ 14 | percentAchieved: 89, 15 | }) 16 | }) 17 | 18 | it('throws InvalidResponse on missing goals', function () { 19 | expect(() => 20 | LiberapayGoal.prototype.transform({ goal: null, receiving: null }) 21 | ) 22 | .to.throw(InvalidResponse) 23 | .with.property('prettyMessage', 'no public goals') 24 | }) 25 | }) 26 | -------------------------------------------------------------------------------- /services/librariesio/librariesio-dependencies-helpers.spec.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { test, given, forCases } = require('sazerac') 4 | const { 5 | renderDependenciesBadge, 6 | } = require('./librariesio-dependencies-helpers') 7 | 8 | describe('Libraries.io dependency helpers', function () { 9 | test(renderDependenciesBadge, () => { 10 | forCases([ 11 | given({ deprecatedCount: 10, outdatedCount: 0 }), 12 | given({ deprecatedCount: 10, outdatedCount: 5 }), 13 | ]).expect({ 14 | message: '10 deprecated', 15 | color: 'red', 16 | }) 17 | given({ deprecatedCount: 0, outdatedCount: 5 }).expect({ 18 | message: '5 out of date', 19 | color: 'orange', 20 | }) 21 | given({ deprecatedCount: 0, outdatedCount: 0 }).expect({ 22 | message: 'up to date', 23 | color: 'brightgreen', 24 | }) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /services/maven-metadata/maven-metadata-redirect.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const t = (module.exports = require('../tester').createServiceTester()) 4 | 5 | t.create('maven metadata (badge extension)') 6 | .get( 7 | '/http/central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml.json' 8 | ) 9 | .expectRedirect( 10 | `/maven-metadata/v.json?metadataUrl=${encodeURIComponent( 11 | 'http://central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml' 12 | )}` 13 | ) 14 | 15 | t.create('maven metadata (no badge extension)') 16 | .get( 17 | '/http/central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml' 18 | ) 19 | .expectRedirect( 20 | `/maven-metadata/v.svg?metadataUrl=${encodeURIComponent( 21 | 'http://central.maven.org/maven2/com/google/code/gson/gson/maven-metadata.xml' 22 | )}` 23 | ) 24 | -------------------------------------------------------------------------------- /.github/probot.js: -------------------------------------------------------------------------------- 1 | on('pull_request.closed') 2 | .filter(context => context.payload.pull_request.merged) 3 | .filter( 4 | context => 5 | context.payload.pull_request.head.ref.slice(0, 11) !== 'dependabot/' 6 | ) 7 | .filter(context => context.payload.pull_request.base.ref === 'master') 8 | .comment(`This pull request was merged to [{{ pull_request.base.ref }}]({{ repository.html_url }}/tree/{{ pull_request.base.ref }}) branch. This change is now waiting for deployment, which will usually happen within a few days. Stay tuned by joining our \`#ops\` channel on [Discord](https://discordapp.com/invite/HjJCwm5)! 9 | 10 | After deployment, changes are copied to [gh-pages]({{ repository.html_url }}/tree/gh-pages) branch: ![](https://img.shields.io/github/commit-status/{{ repository.full_name }}/gh-pages/{{ pull_request.merge_commit_sha }}.svg?label=deploy%20status)`) 11 | -------------------------------------------------------------------------------- /logo/serverfault.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /services/codefactor/codefactor-helpers.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | 5 | // https://support.codefactor.io/i14-glossary 6 | // https://github.com/badges/shields/issues/4269 7 | const colorMap = { 8 | 'A+': 'brightgreen', 9 | A: 'brightgreen', 10 | 'A-': 'green', 11 | 'B+': 'yellowgreen', 12 | B: 'yellowgreen', 13 | 'B-': 'yellowgreen', 14 | 'C+': 'yellow', 15 | C: 'yellow', 16 | 'C-': 'yellow', 17 | 'D+': 'orange', 18 | D: 'orange', 19 | 'D-': 'orange', 20 | F: 'red', 21 | '-': 'lightgrey', 22 | } 23 | 24 | const isValidGrade = Joi.valid(...Object.keys(colorMap)).required() 25 | 26 | function gradeColor(grade) { 27 | const color = colorMap[grade] 28 | if (color === undefined) { 29 | throw Error(`Unknown grade: ${grade}`) 30 | } 31 | return color 32 | } 33 | 34 | module.exports = { isValidGrade, gradeColor } 35 | -------------------------------------------------------------------------------- /services/conda/conda-base.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const Joi = require('@hapi/joi') 4 | const { nonNegativeInteger } = require('../validators') 5 | const { BaseJsonService } = require('..') 6 | 7 | const condaSchema = Joi.object({ 8 | latest_version: Joi.string().required(), 9 | conda_platforms: Joi.array().items(Joi.string()).required(), 10 | files: Joi.array() 11 | .items( 12 | Joi.object({ 13 | ndownloads: nonNegativeInteger, 14 | }) 15 | ) 16 | .required(), 17 | }).required() 18 | 19 | module.exports = class BaseCondaService extends BaseJsonService { 20 | static get defaultBadgeData() { 21 | return { label: 'conda' } 22 | } 23 | 24 | async fetch({ channel, pkg }) { 25 | return this._requestJson({ 26 | schema: condaSchema, 27 | url: `https://api.anaconda.org/package/${channel}/${pkg}`, 28 | }) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /services/cookbook/cookbook.tester.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { isVPlusDottedVersionAtLeastOne } = require('../test-validators') 4 | const t = (module.exports = require('../tester').createServiceTester()) 5 | 6 | t.create('version').get('/chef-sugar.json').expectBadge({ 7 | label: 'cookbook', 8 | message: isVPlusDottedVersionAtLeastOne, 9 | }) 10 | 11 | t.create('version') 12 | .get('/chef-sugar.json') 13 | .intercept(nock => 14 | nock('https://supermarket.getchef.com') 15 | .get('/api/v1/cookbooks/chef-sugar/versions/latest') 16 | .reply(200, { 17 | version: '4.1.0', 18 | }) 19 | ) 20 | .expectBadge({ 21 | label: 'cookbook', 22 | message: 'v4.1.0', 23 | color: 'blue', 24 | }) 25 | 26 | t.create('version (not found)') 27 | .get('/not-a-cookbook.json') 28 | .expectBadge({ label: 'cookbook', message: 'not found' }) 29 | -------------------------------------------------------------------------------- /services/cpan/cpan-version.service.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const { renderVersionBadge } = require('../version') 4 | const BaseCpanService = require('./cpan') 5 | 6 | module.exports = class CpanVersion extends BaseCpanService { 7 | static get category() { 8 | return 'version' 9 | } 10 | 11 | static get route() { 12 | return { 13 | base: 'cpan/v', 14 | pattern: ':packageName', 15 | } 16 | } 17 | 18 | static get examples() { 19 | return [ 20 | { 21 | title: 'CPAN', 22 | namedParams: { packageName: 'Config-Augeas' }, 23 | staticPreview: renderVersionBadge({ version: '1.000' }), 24 | keywords: ['perl'], 25 | }, 26 | ] 27 | } 28 | 29 | async handle({ packageName }) { 30 | const { version } = await this.fetch({ packageName }) 31 | return renderVersionBadge({ version }) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/1_Bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug Report 3 | about: Report errors and problems 4 | labels: 'question' 5 | --- 6 | 7 | Are you experiencing an issue with... 8 | 9 | - [ ] [shields.io](https://shields.io/#/) 10 | - [ ] My own instance 11 | - [ ] [badge-maker NPM package](https://www.npmjs.com/package/badge-maker) 12 | 13 | :beetle: **Description** 14 | 15 | 16 | 17 | :link: **Link to the badge** 18 | 19 | 23 | 24 | :bulb: **Possible Solution** 25 | 26 | 27 | 28 | 30 | --------------------------------------------------------------------------------