Compare commits

..

114 Commits

Author SHA1 Message Date
Caleb Cartwright
c6e31d7f32 load and manage persisted tokens with scope support 2021-09-19 11:41:26 -05:00
Caleb Cartwright
3aadb79325 allow github service classes to define scope requirements 2021-09-19 11:40:21 -05:00
Caleb Cartwright
b8412fd80b support scoped and unscoped tokens in API Provider 2021-09-19 11:39:48 -05:00
Caleb Cartwright
345188e34b expose token pools internal counts of held tokens 2021-09-19 11:38:53 -05:00
Caleb Cartwright
a92dc72ff5 support including scopes on oauth authorization 2021-09-19 11:37:18 -05:00
dependabot[bot]
bf469f10df chore(deps-dev): bump typescript from 4.4.2 to 4.4.3 (#7035)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.4.2 to 4.4.3.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.4.2...v4.4.3)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-18 16:36:35 +00:00
dependabot[bot]
994e752fd1 chore(deps): bump simple-icons from 5.13.0 to 5.14.0 (#7032)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.13.0 to 5.14.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.13.0...5.14.0)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-18 16:19:20 +00:00
dependabot[bot]
f6fd8eac4a chore(deps-dev): bump gatsby from 3.13.0 to 3.13.1 (#7031)
Bumps [gatsby](https://github.com/gatsbyjs/gatsby) from 3.13.0 to 3.13.1.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/compare/gatsby@3.13.0...gatsby@3.13.1)

---
updated-dependencies:
- dependency-name: gatsby
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-18 10:57:11 -05:00
dependabot[bot]
c41d45100e chore(deps-dev): bump prettier from 2.4.0 to 2.4.1 (#7029)
Bumps [prettier](https://github.com/prettier/prettier) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.4.0...2.4.1)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-17 23:54:08 +00:00
dependabot[bot]
e66b266800 chore(deps-dev): bump eslint-plugin-cypress from 2.11.3 to 2.12.1 (#7027)
Bumps [eslint-plugin-cypress](https://github.com/cypress-io/eslint-plugin-cypress) from 2.11.3 to 2.12.1.
- [Release notes](https://github.com/cypress-io/eslint-plugin-cypress/releases)
- [Commits](https://github.com/cypress-io/eslint-plugin-cypress/compare/v2.11.3...v2.12.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-cypress
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-17 23:15:44 +00:00
dependabot[bot]
1ac7ccc231 chore(deps-dev): bump cypress from 8.3.1 to 8.4.0 (#7026)
Bumps [cypress](https://github.com/cypress-io/cypress) from 8.3.1 to 8.4.0.
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Changelog](https://github.com/cypress-io/cypress/blob/develop/.releaserc.base.js)
- [Commits](https://github.com/cypress-io/cypress/compare/v8.3.1...v8.4.0)

---
updated-dependencies:
- dependency-name: cypress
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-17 18:01:15 -05:00
Caleb Cartwright
d728749886 fix: support gitlab token via env var (#7023) 2021-09-16 22:28:54 +00:00
Caleb Cartwright
354fb7db99 ensure docker image builds on PRs (#7019)
* ci: ensure docker image builds on PRs

* ci: intentionally make docker build fail to ensure github workflow fails

* ci: unset forced docker failure

* fix docker build step name

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-15 22:38:06 +00:00
Caleb Cartwright
961e13b229 Add API-based support for [GitLab] badges, add new GitLab Tag badge (#6988)
* Added GitLab Tag service

* Added prettyMessage for when repo has no tags

* Added pretty message for repo not found

* core: esm-ify gitlab tag service

* feat: support gitlab auth

* feat: support custom gitlab url on tag badges

* tests: add auth test for gitlab

* docs: fix gitlab config key references

* feat: support gitlab tag sorting options

* docs: add custom gitlab instance example for tags badge

* use v in gitlab route

* fix: gitlab tag examples

Co-authored-by: Ideotec <guille@ideotec.es>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-14 23:06:57 +00:00
Caleb Cartwright
6bb62e4c0b tests: fix dockerversion service test (#7014)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-14 22:46:31 +00:00
Caleb Cartwright
721d0142ff tests: fix spigetdownloadsize service test (#7018) 2021-09-14 22:30:14 +00:00
Seth Falco
13a53f123f [freecodecamp]: allow + symbol in username (#7016) 2021-09-13 18:21:26 -05:00
Pierre-Yves B
ca63f21113 Stop attempting to override Accept header in [GitHub] API provider (#7013) 2021-09-12 20:48:23 +01:00
chris48s
042ae1c45f update branch in [githublernajson] test (#7012)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-12 18:17:58 +00:00
dependabot[bot]
2f52b1617d chore(deps): bump fast-xml-parser from 3.19.0 to 3.20.0 (#7009)
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 3.19.0 to 3.20.0.
- [Release notes](https://github.com/NaturalIntelligence/fast-xml-parser/releases)
- [Changelog](https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/NaturalIntelligence/fast-xml-parser/compare/3.19.0...v3.20.0)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-11 16:46:07 +00:00
dependabot[bot]
e91da33016 chore(deps): bump graphql from 15.5.2 to 15.5.3 (#7008)
Bumps [graphql](https://github.com/graphql/graphql-js) from 15.5.2 to 15.5.3.
- [Release notes](https://github.com/graphql/graphql-js/releases)
- [Commits](https://github.com/graphql/graphql-js/compare/v15.5.2...v15.5.3)

---
updated-dependencies:
- dependency-name: graphql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-11 16:33:50 +00:00
dependabot[bot]
a76df09c35 chore(deps-dev): bump eslint-plugin-jsdoc from 36.0.8 to 36.1.0 (#7007)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 36.0.8 to 36.1.0.
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v36.0.8...v36.1.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-11 16:19:30 +00:00
dependabot[bot]
70874e2d5b chore(deps-dev): bump @babel/core from 7.15.4 to 7.15.5 (#7004)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.15.4 to 7.15.5.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.5/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-11 15:59:59 +00:00
dependabot[bot]
68dbf71d42 chore(deps-dev): bump prettier from 2.3.2 to 2.4.0 (#7002)
Bumps [prettier](https://github.com/prettier/prettier) from 2.3.2 to 2.4.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.3.2...2.4.0)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-11 15:39:24 +00:00
dependabot[bot]
f4bddb9964 chore(deps-dev): bump c8 from 7.8.0 to 7.9.0 (#7001)
Bumps [c8](https://github.com/bcoe/c8) from 7.8.0 to 7.9.0.
- [Release notes](https://github.com/bcoe/c8/releases)
- [Changelog](https://github.com/bcoe/c8/blob/master/CHANGELOG.md)
- [Commits](https://github.com/bcoe/c8/compare/v7.8.0...v7.9.0)

---
updated-dependencies:
- dependency-name: c8
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-11 04:23:39 +00:00
dependabot[bot]
cb52deec1c chore(deps-dev): bump @types/styled-components from 5.1.13 to 5.1.14 (#7006)
Bumps [@types/styled-components](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/styled-components) from 5.1.13 to 5.1.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/styled-components)

---
updated-dependencies:
- dependency-name: "@types/styled-components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-10 23:09:33 -05:00
dependabot[bot]
047b14b52a chore(deps-dev): bump @typescript-eslint/eslint-plugin (#7000)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.30.0 to 4.31.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.31.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-11 03:48:17 +00:00
dependabot[bot]
dfb68efffb chore(deps): bump simple-icons from 5.12.0 to 5.13.0 (#7010)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.12.0 to 5.13.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.12.0...5.13.0)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-10 17:01:57 -05:00
J. Ryan Stinnett
8284545e22 Rename Riot to Element in Matrix badge help (#6996)
The Riot Matrix client was renamed to Element in July 2020 (https://element.io/blog/welcome-to-element/). This updates the Matrix badge help text to match.
2021-09-08 17:07:53 +00:00
Rohit Sah
8a1c69ead6 Fixed Reddit Negative Karma Issue (#6992) 2021-09-07 19:49:02 +01:00
Caleb Cartwright
1b871a97b4 refactor: update VS Marketplace Ratings badges for unrated extensions (#6986)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 17:33:01 +00:00
dependabot[bot]
0342a3d7c6 chore(deps-dev): bump gatsby-plugin-catch-links from 3.6.0 to 3.13.0 (#6984)
Bumps [gatsby-plugin-catch-links](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-catch-links) from 3.6.0 to 3.13.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-catch-links/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/commits/gatsby-plugin-catch-links@3.13.0/packages/gatsby-plugin-catch-links)

---
updated-dependencies:
- dependency-name: gatsby-plugin-catch-links
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 17:21:48 +00:00
dependabot[bot]
77871a9f7b chore(deps): bump graphql from 15.5.1 to 15.5.2 (#6983)
Bumps [graphql](https://github.com/graphql/graphql-js) from 15.5.1 to 15.5.2.
- [Release notes](https://github.com/graphql/graphql-js/releases)
- [Commits](https://github.com/graphql/graphql-js/compare/v15.5.1...v15.5.2)

---
updated-dependencies:
- dependency-name: graphql
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 17:10:03 +00:00
dependabot[bot]
15be262ba5 chore(deps-dev): bump gatsby-plugin-react-helmet from 4.6.0 to 4.13.0 (#6980)
Bumps [gatsby-plugin-react-helmet](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-react-helmet) from 4.6.0 to 4.13.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-react-helmet/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/commits/gatsby-plugin-react-helmet@4.13.0/packages/gatsby-plugin-react-helmet)

---
updated-dependencies:
- dependency-name: gatsby-plugin-react-helmet
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 16:58:36 +00:00
dependabot[bot]
05fe731290 chore(deps-dev): bump mocha from 9.1.0 to 9.1.1 (#6981)
Bumps [mocha](https://github.com/mochajs/mocha) from 9.1.0 to 9.1.1.
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v9.1.0...v9.1.1)

---
updated-dependencies:
- dependency-name: mocha
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 16:44:56 +00:00
dependabot[bot]
58310f7363 chore(deps-dev): bump babel-preset-gatsby from 1.12.0 to 1.13.0 (#6971)
Bumps [babel-preset-gatsby](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/babel-preset-gatsby) from 1.12.0 to 1.13.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/packages/babel-preset-gatsby/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/commits/babel-preset-gatsby@1.13.0/packages/babel-preset-gatsby)

---
updated-dependencies:
- dependency-name: babel-preset-gatsby
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 05:58:44 +00:00
dependabot[bot]
570c2750e2 chore(deps-dev): bump @types/node from 16.7.2 to 16.7.10 (#6977)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.7.2 to 16.7.10.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 05:49:00 +00:00
dependabot[bot]
62af78c488 chore(deps-dev): bump @typescript-eslint/parser from 4.29.0 to 4.30.0 (#6978)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.29.0 to 4.30.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.30.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 05:22:49 +00:00
dependabot[bot]
c48cd071fe chore(deps): bump @sentry/node from 6.11.0 to 6.12.0 (#6976)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 6.11.0 to 6.12.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.11.0...6.12.0)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 04:58:16 +00:00
dependabot[bot]
a9c9e7d679 chore(deps): bump ioredis from 4.27.8 to 4.27.9 (#6975)
Bumps [ioredis](https://github.com/luin/ioredis) from 4.27.8 to 4.27.9.
- [Release notes](https://github.com/luin/ioredis/releases)
- [Changelog](https://github.com/luin/ioredis/blob/master/Changelog.md)
- [Commits](https://github.com/luin/ioredis/compare/v4.27.8...v4.27.9)

---
updated-dependencies:
- dependency-name: ioredis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 04:36:45 +00:00
dependabot[bot]
4a47b9a364 chore(deps-dev): bump gatsby-plugin-remove-trailing-slashes (#6970)
Bumps [gatsby-plugin-remove-trailing-slashes](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-remove-trailing-slashes) from 3.6.0 to 3.13.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-remove-trailing-slashes/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/commits/gatsby-plugin-remove-trailing-slashes@3.13.0/packages/gatsby-plugin-remove-trailing-slashes)

---
updated-dependencies:
- dependency-name: gatsby-plugin-remove-trailing-slashes
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 04:27:14 +00:00
dependabot[bot]
901dd7b9b6 chore(deps-dev): bump @babel/core from 7.15.0 to 7.15.4 (#6967)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.15.0 to 7.15.4.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.4/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-03 23:14:09 -05:00
dependabot[bot]
6dff73065a chore(deps-dev): bump open-cli from 7.0.0 to 7.0.1 (#6966)
Bumps [open-cli](https://github.com/sindresorhus/open-cli) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/sindresorhus/open-cli/releases)
- [Commits](https://github.com/sindresorhus/open-cli/compare/v7.0.0...v7.0.1)

---
updated-dependencies:
- dependency-name: open-cli
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-04 04:03:41 +00:00
dependabot[bot]
a5f803ff2b chore(deps): bump simple-icons from 5.11.0 to 5.12.0 (#6972)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.11.0 to 5.12.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.11.0...5.12.0)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-03 21:59:11 +00:00
dependabot[bot]
9780da024e chore(deps-dev): bump gatsby-plugin-styled-components (#6985)
Bumps [gatsby-plugin-styled-components](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-styled-components) from 4.6.0 to 4.13.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-styled-components/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/commits/gatsby-plugin-styled-components@4.13.0/packages/gatsby-plugin-styled-components)

---
updated-dependencies:
- dependency-name: gatsby-plugin-styled-components
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-03 21:37:49 +00:00
dependabot[bot]
12b5e8891f chore(deps-dev): bump gatsby-plugin-page-creator from 3.12.0 to 3.13.0 (#6969)
Bumps [gatsby-plugin-page-creator](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-page-creator) from 3.12.0 to 3.13.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-page-creator/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/commits/gatsby-plugin-page-creator@3.13.0/packages/gatsby-plugin-page-creator)

---
updated-dependencies:
- dependency-name: gatsby-plugin-page-creator
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-03 21:19:05 +00:00
dependabot[bot]
5472c733a6 chore(deps-dev): bump gatsby from 3.12.1 to 3.13.0 (#6965)
Bumps [gatsby](https://github.com/gatsbyjs/gatsby) from 3.12.1 to 3.13.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/compare/gatsby@3.12.1...gatsby@3.13.0)

---
updated-dependencies:
- dependency-name: gatsby
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-03 21:02:05 +00:00
dependabot[bot]
fcab8a52dc chore(deps-dev): bump cypress from 8.3.0 to 8.3.1 (#6973)
Bumps [cypress](https://github.com/cypress-io/cypress) from 8.3.0 to 8.3.1.
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Changelog](https://github.com/cypress-io/cypress/blob/develop/.releaserc.base.js)
- [Commits](https://github.com/cypress-io/cypress/compare/v8.3.0...v8.3.1)

---
updated-dependencies:
- dependency-name: cypress
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-03 20:43:22 +00:00
dependabot[bot]
a111e9cba8 chore(deps-dev): bump start-server-and-test from 1.13.1 to 1.14.0 (#6968)
Bumps [start-server-and-test](https://github.com/bahmutov/start-server-and-test) from 1.13.1 to 1.14.0.
- [Release notes](https://github.com/bahmutov/start-server-and-test/releases)
- [Commits](https://github.com/bahmutov/start-server-and-test/compare/v1.13.1...v1.14.0)

---
updated-dependencies:
- dependency-name: start-server-and-test
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-09-03 20:30:25 +00:00
github-actions[bot]
7a7cda6d4b Changelog for Release server-2021-09-01 (#6964)
* Update Changelog

Co-authored-by: release[bot] <actions@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2021-09-01 18:03:29 +01:00
Caleb Cartwright
76ca283775 refactor(GitHubCommitActivity): switch to v4/GraphQL API (#6959)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-29 17:59:36 +00:00
Seth Falco
b02471ff42 feat: add freecodecamp badge (#6958)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-28 16:55:11 +00:00
dependabot[bot]
7b9d1d340b chore(deps-dev): bump nock from 13.1.1 to 13.1.3 (#6949)
Bumps [nock](https://github.com/nock/nock) from 13.1.1 to 13.1.3.
- [Release notes](https://github.com/nock/nock/releases)
- [Changelog](https://github.com/nock/nock/blob/main/CHANGELOG.md)
- [Commits](https://github.com/nock/nock/compare/v13.1.1...v13.1.3)

---
updated-dependencies:
- dependency-name: nock
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-28 16:13:50 +01:00
dependabot[bot]
48faded3f7 chore(deps-dev): bump eslint-plugin-import from 2.24.1 to 2.24.2 (#6952)
Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.24.1 to 2.24.2.
- [Release notes](https://github.com/import-js/eslint-plugin-import/releases)
- [Changelog](https://github.com/import-js/eslint-plugin-import/blob/main/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.24.1...v2.24.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-import
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-28 14:46:44 +00:00
dependabot[bot]
be3fa207b5 chore(deps-dev): bump gatsby from 3.12.0 to 3.12.1 (#6953)
Bumps [gatsby](https://github.com/gatsbyjs/gatsby) from 3.12.0 to 3.12.1.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/compare/gatsby@3.12.0...gatsby@3.12.1)

---
updated-dependencies:
- dependency-name: gatsby
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-27 21:14:37 +00:00
dependabot[bot]
2624b0fb00 chore(deps-dev): bump typescript from 4.3.5 to 4.4.2 (#6951)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.3.5 to 4.4.2.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.3.5...v4.4.2)

---
updated-dependencies:
- dependency-name: typescript
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-27 21:01:58 +00:00
dependabot[bot]
b0b986f31f chore(deps): bump simple-icons from 5.10.0 to 5.11.0 (#6945)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.10.0 to 5.11.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.10.0...5.11.0)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-27 20:53:21 +00:00
dependabot[bot]
61139bbccb chore(deps-dev): bump @types/styled-components from 5.1.12 to 5.1.13 (#6950)
Bumps [@types/styled-components](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/styled-components) from 5.1.12 to 5.1.13.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/styled-components)

---
updated-dependencies:
- dependency-name: "@types/styled-components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-27 20:43:50 +00:00
dependabot[bot]
c5ee05c3fd chore(deps-dev): bump @types/node from 16.6.2 to 16.7.2 (#6948)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.6.2 to 16.7.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-27 20:34:20 +00:00
dependabot[bot]
c8fcee3bff chore(deps-dev): bump styled-components from 5.3.0 to 5.3.1 (#6954)
Bumps [styled-components](https://github.com/styled-components/styled-components) from 5.3.0 to 5.3.1.
- [Release notes](https://github.com/styled-components/styled-components/releases)
- [Changelog](https://github.com/styled-components/styled-components/blob/v5.3.1/CHANGELOG.md)
- [Commits](https://github.com/styled-components/styled-components/compare/v5.3.0...v5.3.1)

---
updated-dependencies:
- dependency-name: styled-components
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-27 20:14:49 +00:00
dependabot[bot]
54844c0892 chore(deps-dev): bump eslint-plugin-jsdoc from 36.0.7 to 36.0.8 (#6955)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 36.0.7 to 36.0.8.
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v36.0.7...v36.0.8)

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-27 20:07:06 +00:00
Su Yang
22995e4e35 use multi-stage build to reduce size (#6938)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-25 17:19:18 +00:00
chris48s
92772de68e use the right version of NPM in docker build (#6941) 2021-08-24 22:06:09 +00:00
Pierre-Yves B
1aea78a5d0 Fix [MyGet] version example and test (#6940) 2021-08-23 21:06:29 -05:00
Pierre-Yves B
6919b0a49c Tidy up server imports (#6937) 2021-08-22 17:30:10 +01:00
Pierre-Yves B
8fd54b1b8d Switch all shields.io links to https (#6935) 2021-08-22 17:20:59 +01:00
chris48s
779c1ffaad fix incorrect colorsForBackground detection (#6939)
* pass an actual color to colorsForBackground (closes #6936)

* improve test coverage for non-default font color
2021-08-22 16:59:25 +01:00
chris48s
e8c78d55b3 Migrate flat, flat-square, plastic and social to use XmlElement (#6883)
* start changelog entry for v4

* migrate Flat/FlatSquare/Plastic to use XmlElement

* move brightnessThreshold into colorsForBackground

* move old renderLogo function inline into social()

this is the only place it is now used

* use XmlElement in social()

* don't quote numbers if we don't need to

* remove intermediate calls to .render()

leave everything as XmlElement objects right till the end
then make one final call to .render()
which cascades aaall the way through the tree

* factor out code for assembling logo element

* use scale consts in social

* remove NullElement

now we've removed all the intermediate calls to render()
we can just use an empty string

* write leftlink so it doesn't look like a bool

Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com>
2021-08-21 20:44:14 +01:00
dependabot[bot]
1c36dd2bd0 chore(deps-dev): bump eslint-plugin-import from 2.24.0 to 2.24.1 (#6932)
Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.24.0 to 2.24.1.
- [Release notes](https://github.com/import-js/eslint-plugin-import/releases)
- [Changelog](https://github.com/import-js/eslint-plugin-import/blob/master/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.24.0...v2.24.1)

---
updated-dependencies:
- dependency-name: eslint-plugin-import
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-20 14:46:28 +00:00
dependabot[bot]
45def37a0d chore(deps): bump ioredis from 4.27.7 to 4.27.8 (#6926)
Bumps [ioredis](https://github.com/luin/ioredis) from 4.27.7 to 4.27.8.
- [Release notes](https://github.com/luin/ioredis/releases)
- [Changelog](https://github.com/luin/ioredis/blob/master/Changelog.md)
- [Commits](https://github.com/luin/ioredis/compare/v4.27.7...v4.27.8)

---
updated-dependencies:
- dependency-name: ioredis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-20 14:31:44 +00:00
dependabot[bot]
8c3b20b5b8 chore(deps-dev): bump gatsby from 3.11.1 to 3.12.0 (#6925)
Bumps [gatsby](https://github.com/gatsbyjs/gatsby) from 3.11.1 to 3.12.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/compare/gatsby@3.11.1...gatsby@3.12.0)

---
updated-dependencies:
- dependency-name: gatsby
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-20 14:19:55 +00:00
dependabot[bot]
dd63a0b71f chore(deps-dev): bump gatsby-plugin-page-creator from 3.11.0 to 3.12.0 (#6919)
Bumps [gatsby-plugin-page-creator](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-page-creator) from 3.11.0 to 3.12.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-page-creator/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/commits/gatsby-plugin-page-creator@3.12.0/packages/gatsby-plugin-page-creator)

---
updated-dependencies:
- dependency-name: gatsby-plugin-page-creator
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-20 14:07:22 +00:00
dependabot[bot]
96a13b8749 chore(deps): bump @actions/core in /.github/actions/close-bot (#6923)
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.4.0 to 1.5.0.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-20 13:54:39 +00:00
dependabot[bot]
f28ba52562 chore(deps-dev): bump @types/node from 16.6.1 to 16.6.2 (#6921)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.6.1 to 16.6.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-20 13:45:07 +00:00
dependabot[bot]
9a78f0a0c2 chore(deps-dev): bump simple-git-hooks from 2.5.1 to 2.6.1 (#6927)
Bumps [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks) from 2.5.1 to 2.6.1.
- [Release notes](https://github.com/toplenboren/simple-git-hooks/releases)
- [Changelog](https://github.com/toplenboren/simple-git-hooks/blob/master/Changelog.md)
- [Commits](https://github.com/toplenboren/simple-git-hooks/compare/2.5.1...2.6.1)

---
updated-dependencies:
- dependency-name: simple-git-hooks
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-20 13:32:35 +00:00
dependabot[bot]
11bf09bf0f chore(deps): bump simple-icons from 5.9.0 to 5.10.0 (#6918)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.9.0 to 5.10.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.9.0...5.10.0)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-20 13:18:46 +00:00
dependabot[bot]
44a3e9abb6 chore(deps-dev): bump mocha from 9.0.3 to 9.1.0 (#6917)
Bumps [mocha](https://github.com/mochajs/mocha) from 9.0.3 to 9.1.0.
- [Release notes](https://github.com/mochajs/mocha/releases)
- [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mochajs/mocha/compare/v9.0.3...v9.1.0)

---
updated-dependencies:
- dependency-name: mocha
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-20 13:07:24 +00:00
dependabot[bot]
66dbcb5ca6 chore(deps-dev): bump cypress from 8.2.0 to 8.3.0 (#6915)
Bumps [cypress](https://github.com/cypress-io/cypress) from 8.2.0 to 8.3.0.
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Changelog](https://github.com/cypress-io/cypress/blob/develop/.releaserc.base.js)
- [Commits](https://github.com/cypress-io/cypress/compare/v8.2.0...v8.3.0)

---
updated-dependencies:
- dependency-name: cypress
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-20 12:57:48 +00:00
chris48s
dabc907fc5 update [requires] test/example (#6913)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-19 17:21:59 +00:00
Pierre-Yves B
71bbcd527b Fix badge classes links in tutorial (#6911) 2021-08-19 18:04:00 +01:00
nixxquality
70947e1ff0 [TwitchExtensionVersion] New badge (#6900)
* Create Twitch Extension Version badge

* skip tests when there's no token

* use renderVersionBadge

* improve joi schema

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-19 16:13:29 +00:00
chris48s
48b7f70ef2 remove disableStrictSsl param from [jenkins] (#6887)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-18 19:40:41 +00:00
chris48s
96c27c2627 switch from husky to simple-git-hooks (#6904) 2021-08-18 19:33:20 +00:00
dependabot[bot]
17beba4ebf chore(deps-dev): bump @babel/register from 7.14.5 to 7.15.3 (#6898)
Bumps [@babel/register](https://github.com/babel/babel/tree/HEAD/packages/babel-register) from 7.14.5 to 7.15.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.3/packages/babel-register)

---
updated-dependencies:
- dependency-name: "@babel/register"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-14 14:28:35 +00:00
dependabot[bot]
95f6611837 chore(deps): bump @sentry/node from 6.10.0 to 6.11.0 (#6897)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 6.10.0 to 6.11.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/6.10.0...6.11.0)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-14 14:20:03 +00:00
dependabot[bot]
eb7352e5ae chore(deps-dev): bump eslint-plugin-import from 2.23.4 to 2.24.0 (#6893)
Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.23.4 to 2.24.0.
- [Release notes](https://github.com/import-js/eslint-plugin-import/releases)
- [Changelog](https://github.com/import-js/eslint-plugin-import/blob/master/CHANGELOG.md)
- [Commits](https://github.com/import-js/eslint-plugin-import/compare/v2.23.4...v2.24.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-import
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-14 14:09:56 +00:00
dependabot[bot]
c81a1c6196 chore(deps-dev): bump @types/node from 16.4.13 to 16.6.1 (#6894)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.4.13 to 16.6.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-14 13:51:30 +00:00
dependabot[bot]
64c6533849 chore(deps-dev): bump gatsby from 3.11.0 to 3.11.1 (#6890)
Bumps [gatsby](https://github.com/gatsbyjs/gatsby) from 3.11.0 to 3.11.1.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/compare/gatsby@3.11.0...gatsby@3.11.1)

---
updated-dependencies:
- dependency-name: gatsby
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-14 04:50:14 +00:00
dependabot[bot]
8468ddbb07 chore(deps-dev): bump concurrently from 6.2.0 to 6.2.1 (#6891)
Bumps [concurrently](https://github.com/open-cli-tools/concurrently) from 6.2.0 to 6.2.1.
- [Release notes](https://github.com/open-cli-tools/concurrently/releases)
- [Commits](https://github.com/open-cli-tools/concurrently/compare/v6.2.0...v6.2.1)

---
updated-dependencies:
- dependency-name: concurrently
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-14 04:39:47 +00:00
dependabot[bot]
169d51412a chore(deps-dev): bump eslint-plugin-jsdoc from 36.0.6 to 36.0.7 (#6892)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 36.0.6 to 36.0.7.
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v36.0.6...v36.0.7)

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-14 04:28:04 +00:00
dependabot[bot]
51e70bae8d chore(deps): bump prom-client from 13.1.0 to 13.2.0 (#6888)
Bumps [prom-client](https://github.com/siimon/prom-client) from 13.1.0 to 13.2.0.
- [Release notes](https://github.com/siimon/prom-client/releases)
- [Changelog](https://github.com/siimon/prom-client/blob/master/CHANGELOG.md)
- [Commits](https://github.com/siimon/prom-client/compare/v13.1.0...v13.2.0)

---
updated-dependencies:
- dependency-name: prom-client
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-14 04:18:30 +00:00
dependabot[bot]
8be2da17ed chore(deps): bump simple-icons from 5.8.1 to 5.9.0 (#6889)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.8.1 to 5.9.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.8.1...5.9.0)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-13 23:53:16 +00:00
chris48s
8e0788ccdc enforce strict SSL checking for [coverity] (#6886)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-12 19:04:11 +00:00
dependabot[bot]
36d80af111 chore(deps-dev): bump eslint from 7.31.0 to 7.32.0 (#6861)
Bumps [eslint](https://github.com/eslint/eslint) from 7.31.0 to 7.32.0.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Changelog](https://github.com/eslint/eslint/blob/master/CHANGELOG.md)
- [Commits](https://github.com/eslint/eslint/compare/v7.31.0...v7.32.0)

---
updated-dependencies:
- dependency-name: eslint
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-12 13:10:15 +00:00
chris48s
5b583cc9ef fix failing [gitlab] test (#6885)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-11 22:11:29 +00:00
chris48s
1444b5624c update [bugzilla] not found test (#6884) 2021-08-11 17:03:37 -05:00
chris48s
45a9c90782 Revert "prefer release.name in [Github] release badge (#6879)" (#6880)
This reverts commit a4c93c56e7.
2021-08-10 07:48:27 -05:00
chris48s
a4c93c56e7 prefer release.name in [Github] release badge (#6879) 2021-08-09 14:59:29 +01:00
chris48s
f4781804f6 Update self hosting docs (#6877)
* update node version

* remove notes on redis
2021-08-08 21:11:09 +01:00
chris48s
21742a50b4 fix auto-close action (#6876) 2021-08-08 20:10:20 +01:00
chris48s
2cb52494c3 fix DockerHub snapshot build (#6878) 2021-08-08 20:02:10 +01:00
dependabot[bot]
fbef4a5d5d chore(deps-dev): bump @types/styled-components from 5.1.11 to 5.1.12 (#6873)
Bumps [@types/styled-components](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/styled-components) from 5.1.11 to 5.1.12.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/styled-components)

---
updated-dependencies:
- dependency-name: "@types/styled-components"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2021-08-07 20:12:02 +01:00
dependabot[bot]
64b747605c chore(deps-dev): bump lint-staged from 11.1.1 to 11.1.2 (#6872)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 11.1.1 to 11.1.2.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v11.1.1...v11.1.2)

---
updated-dependencies:
- dependency-name: lint-staged
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2021-08-07 20:02:08 +01:00
dependabot[bot]
ea071535e3 chore(deps-dev): bump @types/node from 16.4.7 to 16.4.13 (#6871)
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 16.4.7 to 16.4.13.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-06 23:41:59 -05:00
dependabot[bot]
3b5f915443 chore(deps-dev): bump @typescript-eslint/parser from 4.28.4 to 4.29.0 (#6874)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.28.4 to 4.29.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/parser/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.29.0/packages/parser)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/parser"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-06 23:06:21 -05:00
dependabot[bot]
8b17c7b175 chore(deps-dev): bump @typescript-eslint/eslint-plugin (#6865)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.28.4 to 4.29.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v4.29.0/packages/eslint-plugin)

---
updated-dependencies:
- dependency-name: "@typescript-eslint/eslint-plugin"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-06 22:50:24 -05:00
dependabot[bot]
ec028c5158 chore(deps-dev): bump eslint-plugin-chai-friendly from 0.7.1 to 0.7.2 (#6864)
Bumps [eslint-plugin-chai-friendly](https://github.com/ihordiachenko/eslint-plugin-chai-friendly) from 0.7.1 to 0.7.2.
- [Release notes](https://github.com/ihordiachenko/eslint-plugin-chai-friendly/releases)
- [Commits](https://github.com/ihordiachenko/eslint-plugin-chai-friendly/compare/v0.7.1...v0.7.2)

---
updated-dependencies:
- dependency-name: eslint-plugin-chai-friendly
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-06 22:40:59 -05:00
dependabot[bot]
b901b2b216 chore(deps-dev): bump @babel/core from 7.14.8 to 7.15.0 (#6858)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.14.8 to 7.15.0.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.15.0/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-06 22:04:22 -05:00
dependabot[bot]
ff8f4432b3 chore(deps): bump ioredis from 4.27.6 to 4.27.7 (#6857)
Bumps [ioredis](https://github.com/luin/ioredis) from 4.27.6 to 4.27.7.
- [Release notes](https://github.com/luin/ioredis/releases)
- [Changelog](https://github.com/luin/ioredis/blob/master/Changelog.md)
- [Commits](https://github.com/luin/ioredis/compare/v4.27.6...v4.27.7)

---
updated-dependencies:
- dependency-name: ioredis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-06 21:32:09 -05:00
dependabot[bot]
ed07b61e7d chore(deps): bump joi from 17.4.1 to 17.4.2 (#6856)
Bumps [joi](https://github.com/sideway/joi) from 17.4.1 to 17.4.2.
- [Release notes](https://github.com/sideway/joi/releases)
- [Commits](https://github.com/sideway/joi/compare/v17.4.1...v17.4.2)

---
updated-dependencies:
- dependency-name: joi
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-06 15:43:51 +01:00
dependabot[bot]
087a824a85 chore(deps-dev): bump gatsby from 3.10.2 to 3.11.0 (#6860)
Bumps [gatsby](https://github.com/gatsbyjs/gatsby) from 3.10.2 to 3.11.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/compare/gatsby@3.10.2...gatsby@3.11.0)

---
updated-dependencies:
- dependency-name: gatsby
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2021-08-06 15:31:33 +01:00
dependabot[bot]
e18de8c8be chore(deps-dev): bump cypress from 8.1.0 to 8.2.0 (#6863)
Bumps [cypress](https://github.com/cypress-io/cypress) from 8.1.0 to 8.2.0.
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Changelog](https://github.com/cypress-io/cypress/blob/develop/.releaserc.base.js)
- [Commits](https://github.com/cypress-io/cypress/compare/v8.1.0...v8.2.0)

---
updated-dependencies:
- dependency-name: cypress
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-06 15:08:31 +01:00
dependabot[bot]
63fd51b428 chore(deps): bump simple-icons from 5.8.0 to 5.8.1 (#6867)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.8.0 to 5.8.1.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.8.0...5.8.1)

---
updated-dependencies:
- dependency-name: simple-icons
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-08-06 14:53:54 +01:00
Paul Melnikow
37e83641ab Support optionalDependencies in [GithubPackageJson] (#6749)
Closes #6044

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-08-03 17:25:09 -05:00
77 changed files with 3928 additions and 5234 deletions

View File

@@ -9,14 +9,14 @@
"version": "0.0.0",
"license": "CC0",
"dependencies": {
"@actions/core": "^1.4.0",
"@actions/core": "^1.5.0",
"@actions/github": "^5.0.0"
}
},
"node_modules/@actions/core": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.4.0.tgz",
"integrity": "sha512-CGx2ilGq5i7zSLgiiGUtBCxhRRxibJYU6Fim0Q1Wg2aQL2LTnF27zbqZOrxfvFQ55eSBW0L8uVStgtKMpa0Qlg=="
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.5.0.tgz",
"integrity": "sha512-eDOLH1Nq9zh+PJlYLqEMkS/jLQxhksPNmUGNBHfa4G+tQmnIhzpctxmchETtVGyBOvXgOVVpYuE40+eS4cUnwQ=="
},
"node_modules/@actions/github": {
"version": "5.0.0",
@@ -193,9 +193,9 @@
},
"dependencies": {
"@actions/core": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.4.0.tgz",
"integrity": "sha512-CGx2ilGq5i7zSLgiiGUtBCxhRRxibJYU6Fim0Q1Wg2aQL2LTnF27zbqZOrxfvFQ55eSBW0L8uVStgtKMpa0Qlg=="
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.5.0.tgz",
"integrity": "sha512-eDOLH1Nq9zh+PJlYLqEMkS/jLQxhksPNmUGNBHfa4G+tQmnIhzpctxmchETtVGyBOvXgOVVpYuE40+eS4cUnwQ=="
},
"@actions/github": {
"version": "5.0.0",

View File

@@ -10,7 +10,7 @@
"author": "chris48s",
"license": "CC0",
"dependencies": {
"@actions/core": "^1.4.0",
"@actions/core": "^1.5.0",
"@actions/github": "^5.0.0"
}
}

View File

@@ -2,7 +2,7 @@ name: Auto close
on: pull_request_target
permissions:
pull_request: write
pull-requests: write
jobs:
build:

View File

@@ -0,0 +1,20 @@
name: Build Docker Image
on:
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Build
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: shieldsio/shields:pr-validation

View File

@@ -1,11 +1,11 @@
name: Tag Release
name: Create Release
on:
pull_request:
types: [closed]
jobs:
tag-release:
create-release:
if: |
github.event_name == 'pull_request' &&
github.event.action == 'closed' &&
@@ -24,8 +24,24 @@ jobs:
with:
ref: 'master'
- name: Tag Release
- name: Tag release in GitHub
uses: tvdias/github-tagger@v0.0.2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
tag: server-${{ steps.date.outputs.date }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push snapshot release to DockerHub
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: shieldsio/shields:server-${{ steps.date.outputs.date }}

View File

@@ -1,30 +0,0 @@
name: Build and Publish Snapshot Docker Image
on: create
jobs:
build:
runs-on: ubuntu-latest
if: ${{ github.event.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/server-') }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Get tag name
id: get_version
run: echo ::set-output name=TAG_NAME::$(echo $GITHUB_REF | cut -d / -f 3)
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: shieldsio/shields:${{ steps.get_version.outputs.TAG_NAME }}

View File

@@ -4,6 +4,19 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
---
## server-2021-09-01
- use multi-stage build to reduce size of docker images [#6938](https://github.com/badges/shields/issues/6938)
- remove disableStrictSsl param from [jenkins] [#6887](https://github.com/badges/shields/issues/6887)
- refactor(GitHubCommitActivity): switch to v4/GraphQL API [#6959](https://github.com/badges/shields/issues/6959)
- feat: add freecodecamp badge [#6958](https://github.com/badges/shields/issues/6958)
- use the right version of NPM in docker build [#6941](https://github.com/badges/shields/issues/6941)
- [TwitchExtensionVersion] New badge [#6900](https://github.com/badges/shields/issues/6900)
- enforce strict SSL checking for [coverity] [#6886](https://github.com/badges/shields/issues/6886)
- Update self hosting docs [#6877](https://github.com/badges/shields/issues/6877)
- Support optionalDependencies in [GithubPackageJson] [#6749](https://github.com/badges/shields/issues/6749)
- Dependency updates
## server-2021-08-01
- use v5 API for [AUR] badges [#6836](https://github.com/badges/shields/issues/6836)

View File

@@ -1,4 +1,4 @@
FROM node:14-alpine
FROM node:14-alpine AS Builder
RUN mkdir -p /usr/src/app
RUN mkdir /usr/src/app/private
@@ -8,6 +8,7 @@ COPY package.json package-lock.json /usr/src/app/
# Without the badge-maker package.json and CLI script in place, `npm ci` will fail.
COPY badge-maker /usr/src/app/badge-maker/
RUN npm install -g "npm@>=7"
# We need dev deps to build the front end. We don't need Cypress, though.
RUN NODE_ENV=development CYPRESS_INSTALL_BINARY=0 npm ci
@@ -16,9 +17,14 @@ RUN npm run build
RUN npm prune --production
RUN npm cache clean --force
# Use multi-stage build to reduce size
FROM node:14-alpine
# Run the server using production configs.
ENV NODE_ENV production
WORKDIR /usr/src/app
COPY --from=Builder /usr/src/app /usr/src/app
CMD node server
EXPOSE 80

View File

@@ -73,7 +73,7 @@ This repo hosts:
[Make your own badges!][custom badges]
(Quick example: `https://img.shields.io/badge/left-right-f39f37`)
[custom badges]: http://shields.io/#your-badge
[custom badges]: https://shields.io/#your-badge
### Quickstart

View File

@@ -402,6 +402,126 @@ exports['The badge generator "flat" template badge generation should match snaps
`
exports['The badge generator "flat" template badge generation should match snapshots: black text when the label color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="90"
height="20"
role="img"
aria-label="cactus: grown"
>
<title>cactus: grown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
<stop offset="1" stop-opacity=".1" />
</linearGradient>
<clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff" /></clipPath>
<g clip-path="url(#r)">
<rect width="45" height="20" fill="#f3f3f3" />
<rect x="45" width="45" height="20" fill="#000" />
<rect width="90" height="20" fill="url(#s)" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="110"
>
<text
aria-hidden="true"
x="235"
y="150"
fill="#ccc"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
cactus
</text>
<text x="235" y="140" transform="scale(.1)" fill="#333" textLength="350">
cactus
</text>
<text
aria-hidden="true"
x="665"
y="150"
fill="#010101"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
grown
</text>
<text x="665" y="140" transform="scale(.1)" fill="#fff" textLength="350">
grown
</text>
</g>
</svg>
`
exports['The badge generator "flat" template badge generation should match snapshots: black text when the message color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="90"
height="20"
role="img"
aria-label="cactus: grown"
>
<title>cactus: grown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
<stop offset="1" stop-opacity=".1" />
</linearGradient>
<clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff" /></clipPath>
<g clip-path="url(#r)">
<rect width="45" height="20" fill="#000" />
<rect x="45" width="45" height="20" fill="#e2ffe1" />
<rect width="90" height="20" fill="url(#s)" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="110"
>
<text
aria-hidden="true"
x="235"
y="150"
fill="#010101"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
cactus
</text>
<text x="235" y="140" transform="scale(.1)" fill="#fff" textLength="350">
cactus
</text>
<text
aria-hidden="true"
x="665"
y="150"
fill="#ccc"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
grown
</text>
<text x="665" y="140" transform="scale(.1)" fill="#333" textLength="350">
grown
</text>
</g>
</svg>
`
exports['The badge generator "flat-square" template badge generation should match snapshots: message/label, no logo 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -609,6 +729,70 @@ exports['The badge generator "flat-square" template badge generation should matc
`
exports['The badge generator "flat-square" template badge generation should match snapshots: black text when the label color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="90"
height="20"
role="img"
aria-label="cactus: grown"
>
<title>cactus: grown</title>
<g shape-rendering="crispEdges">
<rect width="45" height="20" fill="#f3f3f3" />
<rect x="45" width="45" height="20" fill="#000" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="110"
>
<text x="235" y="140" transform="scale(.1)" fill="#333" textLength="350">
cactus
</text>
<text x="665" y="140" transform="scale(.1)" fill="#fff" textLength="350">
grown
</text>
</g>
</svg>
`
exports['The badge generator "flat-square" template badge generation should match snapshots: black text when the message color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="90"
height="20"
role="img"
aria-label="cactus: grown"
>
<title>cactus: grown</title>
<g shape-rendering="crispEdges">
<rect width="45" height="20" fill="#000" />
<rect x="45" width="45" height="20" fill="#e2ffe1" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="110"
>
<text x="235" y="140" transform="scale(.1)" fill="#fff" textLength="350">
cactus
</text>
<text x="665" y="140" transform="scale(.1)" fill="#333" textLength="350">
grown
</text>
</g>
</svg>
`
exports['The badge generator "plastic" template badge generation should match snapshots: message/label, no logo 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -965,6 +1149,130 @@ exports['The badge generator "plastic" template badge generation should match sn
`
exports['The badge generator "plastic" template badge generation should match snapshots: black text when the label color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="90"
height="18"
role="img"
aria-label="cactus: grown"
>
<title>cactus: grown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#fff" stop-opacity=".7" />
<stop offset=".1" stop-color="#aaa" stop-opacity=".1" />
<stop offset=".9" stop-color="#000" stop-opacity=".3" />
<stop offset="1" stop-color="#000" stop-opacity=".5" />
</linearGradient>
<clipPath id="r"><rect width="90" height="18" rx="4" fill="#fff" /></clipPath>
<g clip-path="url(#r)">
<rect width="45" height="18" fill="#f3f3f3" />
<rect x="45" width="45" height="18" fill="#000" />
<rect width="90" height="18" fill="url(#s)" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="110"
>
<text
aria-hidden="true"
x="235"
y="140"
fill="#ccc"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
cactus
</text>
<text x="235" y="130" transform="scale(.1)" fill="#333" textLength="350">
cactus
</text>
<text
aria-hidden="true"
x="665"
y="140"
fill="#010101"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
grown
</text>
<text x="665" y="130" transform="scale(.1)" fill="#fff" textLength="350">
grown
</text>
</g>
</svg>
`
exports['The badge generator "plastic" template badge generation should match snapshots: black text when the message color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="90"
height="18"
role="img"
aria-label="cactus: grown"
>
<title>cactus: grown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#fff" stop-opacity=".7" />
<stop offset=".1" stop-color="#aaa" stop-opacity=".1" />
<stop offset=".9" stop-color="#000" stop-opacity=".3" />
<stop offset="1" stop-color="#000" stop-opacity=".5" />
</linearGradient>
<clipPath id="r"><rect width="90" height="18" rx="4" fill="#fff" /></clipPath>
<g clip-path="url(#r)">
<rect width="45" height="18" fill="#000" />
<rect x="45" width="45" height="18" fill="#e2ffe1" />
<rect width="90" height="18" fill="url(#s)" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="110"
>
<text
aria-hidden="true"
x="235"
y="140"
fill="#010101"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
cactus
</text>
<text x="235" y="130" transform="scale(.1)" fill="#fff" textLength="350">
cactus
</text>
<text
aria-hidden="true"
x="665"
y="140"
fill="#ccc"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
grown
</text>
<text x="665" y="130" transform="scale(.1)" fill="#333" textLength="350">
grown
</text>
</g>
</svg>
`
exports['The badge generator "for-the-badge" template badge generation should match snapshots: message/label, no logo 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -1218,6 +1526,84 @@ exports['The badge generator "for-the-badge" template badge generation should ma
`
exports['The badge generator "for-the-badge" template badge generation should match snapshots: black text when the label color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="146.75"
height="28"
role="img"
aria-label="CACTUS: GROWN"
>
<title>CACTUS: GROWN</title>
<g shape-rendering="crispEdges">
<rect width="72.5" height="28" fill="#f3f3f3" />
<rect x="72.5" width="74.25" height="28" fill="#000" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="100"
>
<text transform="scale(.1)" x="362.5" y="175" textLength="485" fill="#333">
CACTUS
</text>
<text
transform="scale(.1)"
x="1096.25"
y="175"
textLength="502.5"
fill="#fff"
font-weight="bold"
>
GROWN
</text>
</g>
</svg>
`
exports['The badge generator "for-the-badge" template badge generation should match snapshots: black text when the message color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="146.75"
height="28"
role="img"
aria-label="CACTUS: GROWN"
>
<title>CACTUS: GROWN</title>
<g shape-rendering="crispEdges">
<rect width="72.5" height="28" fill="#000" />
<rect x="72.5" width="74.25" height="28" fill="#e2ffe1" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="100"
>
<text transform="scale(.1)" x="362.5" y="175" textLength="485" fill="#fff">
CACTUS
</text>
<text
transform="scale(.1)"
x="1096.25"
y="175"
textLength="502.5"
fill="#333"
font-weight="bold"
>
GROWN
</text>
</g>
</svg>
`
exports['The badge generator "social" template badge generation should match snapshots: message/label, no logo 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
@@ -1843,102 +2229,3 @@ exports['The badge generator badges with logos should always produce the same ba
</svg>
`
exports['The badge generator text colors should use black text when the label color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="90"
height="20"
role="img"
aria-label="cactus: grown"
>
<title>cactus: grown</title>
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1" />
<stop offset="1" stop-opacity=".1" />
</linearGradient>
<clipPath id="r"><rect width="90" height="20" rx="3" fill="#fff" /></clipPath>
<g clip-path="url(#r)">
<rect width="45" height="20" fill="#f3f3f3" />
<rect x="45" width="45" height="20" fill="#000" />
<rect width="90" height="20" fill="url(#s)" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="110"
>
<text
aria-hidden="true"
x="235"
y="150"
fill="#ccc"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
cactus
</text>
<text x="235" y="140" transform="scale(.1)" fill="#333" textLength="350">
cactus
</text>
<text
aria-hidden="true"
x="665"
y="150"
fill="#010101"
fill-opacity=".3"
transform="scale(.1)"
textLength="350"
>
grown
</text>
<text x="665" y="140" transform="scale(.1)" fill="#fff" textLength="350">
grown
</text>
</g>
</svg>
`
exports['The badge generator text colors should use black text when the message color is light 1'] = `
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="146.75"
height="28"
role="img"
aria-label="CACTUS: GROWN"
>
<title>CACTUS: GROWN</title>
<g shape-rendering="crispEdges">
<rect width="72.5" height="28" fill="#000" />
<rect x="72.5" width="74.25" height="28" fill="#e2ffe1" />
</g>
<g
fill="#fff"
text-anchor="middle"
font-family="Verdana,Geneva,DejaVu Sans,sans-serif"
text-rendering="geometricPrecision"
font-size="100"
>
<text transform="scale(.1)" x="362.5" y="175" textLength="485" fill="#fff">
CACTUS
</text>
<text
transform="scale(.1)"
x="1096.25"
y="175"
textLength="502.5"
fill="#333"
font-weight="bold"
>
GROWN
</text>
</g>
</svg>
`

View File

@@ -4,7 +4,7 @@
"keywords": ["badge", "github", "svg", "status"],
"website": "https://shields.io/",
"repository": "https://github.com/badges/shields",
"logo": "http://shields.io/favicon.png",
"logo": "https://shields.io/favicon.png",
"env": {
"CYPRESS_INSTALL_BINARY": {
"description": "Disable the cypress binary installation",

View File

@@ -1,5 +1,9 @@
# Changelog
## 4.0.0 [WIP]
- Drop compatibility with Node 10
## 3.3.1
- Improve font measuring in for-the-badge and social styles

View File

@@ -2,23 +2,22 @@
const anafanafo = require('anafanafo')
const { brightness } = require('./color')
const { XmlElement, escapeXml } = require('./xml')
const { XmlElement, ElementList } = require('./xml')
// https://github.com/badges/shields/pull/1132
const FONT_SCALE_UP_FACTOR = 10
const FONT_SCALE_DOWN_VALUE = 'scale(.1)'
const FONT_FAMILY = 'Verdana,Geneva,DejaVu Sans,sans-serif'
const fontFamily = `font-family="${FONT_FAMILY}"`
const socialFontFamily =
'font-family="Helvetica Neue,Helvetica,Arial,sans-serif"'
const brightnessThreshold = 0.69
const WIDTH_FONT = '11px Verdana'
const SOCIAL_FONT_FAMILY = 'Helvetica Neue,Helvetica,Arial,sans-serif'
function capitalize(s) {
return `${s.charAt(0).toUpperCase()}${s.slice(1)}`
}
function colorsForBackground(color) {
const brightnessThreshold = 0.69
if (brightness(color) <= brightnessThreshold) {
return { textColor: '#fff', shadowColor: '#010101' }
} else {
@@ -53,127 +52,61 @@ function shouldWrapBodyWithLink({ links }) {
return hasLeftLink && !hasRightLink
}
function renderAriaAttributes({ accessibleText, links }) {
const { hasLink } = hasLinks({ links })
return hasLink ? '' : `role="img" aria-label="${escapeXml(accessibleText)}"`
}
function renderTitle({ accessibleText, links }) {
const { hasLink } = hasLinks({ links })
return hasLink ? '' : `<title>${escapeXml(accessibleText)}</title>`
}
function renderLogo({
logo,
badgeHeight,
horizPadding,
logoWidth = 14,
logoPadding = 0,
}) {
if (logo) {
const logoHeight = 14
const y = (badgeHeight - logoHeight) / 2
const x = horizPadding
return {
hasLogo: true,
totalLogoWidth: logoWidth + logoPadding,
renderedLogo: `<image x="${x}" y="${y}" width="${logoWidth}" height="${logoHeight}" xlink:href="${escapeXml(
logo
)}"/>`,
}
} else {
return { hasLogo: false, totalLogoWidth: 0, renderedLogo: '' }
}
}
function renderLink({
link,
height,
textLength,
horizPadding,
leftMargin,
renderedText,
}) {
const rectHeight = height
const rectWidth = textLength + horizPadding * 2
const rectX = leftMargin > 1 ? leftMargin + 1 : 0
return `<a target="_blank" xlink:href="${escapeXml(link)}">
<rect width="${rectWidth}" x="${rectX}" height="${rectHeight}" fill="rgba(0,0,0,0)" />
${renderedText}
</a>`
}
function renderText({
leftMargin,
horizPadding = 0,
content,
link,
height,
verticalMargin = 0,
shadow = false,
color,
}) {
if (!content.length) {
return { renderedText: '', width: 0 }
}
const textLength = preferredWidthOf(content, { font: '11px Verdana' })
const escapedContent = escapeXml(content)
const shadowMargin = 150 + verticalMargin
const textMargin = 140 + verticalMargin
const outTextLength = 10 * textLength
const x = 10 * (leftMargin + 0.5 * textLength + horizPadding)
let renderedText = ''
const { textColor, shadowColor } = colorsForBackground(color)
if (shadow) {
renderedText = `<text aria-hidden="true" x="${x}" y="${shadowMargin}" fill="${shadowColor}" fill-opacity=".3" transform="scale(.1)" textLength="${outTextLength}">${escapedContent}</text>`
}
renderedText += `<text x="${x}" y="${textMargin}" transform="scale(.1)" fill="${textColor}" textLength="${outTextLength}">${escapedContent}</text>`
return {
renderedText: link
? renderLink({
link,
height,
textLength,
horizPadding,
leftMargin,
renderedText,
})
: renderedText,
width: textLength,
}
function getLogoElement({ logo, horizPadding, badgeHeight, logoWidth }) {
const logoHeight = 14
if (!logo) return ''
return new XmlElement({
name: 'image',
attrs: {
x: horizPadding,
y: 0.5 * (badgeHeight - logoHeight),
width: logoWidth,
height: logoHeight,
'xlink:href': logo,
},
})
}
function renderBadge(
{ links, leftWidth, rightWidth, height, accessibleText },
main
content
) {
const width = leftWidth + rightWidth
const leftLink = escapeXml(links[0])
const leftLink = links[0]
const { hasLink } = hasLinks({ links })
return `
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${width}" height="${height}" ${renderAriaAttributes(
{ links, accessibleText }
)}>
const title = hasLink
? ''
: new XmlElement({ name: 'title', content: [accessibleText] })
${renderTitle({ accessibleText, links })}
${
shouldWrapBodyWithLink({ links })
? `<a target="_blank" xlink:href="${leftLink}">${main}</a>`
: main
}
</svg>`
const body = shouldWrapBodyWithLink({ links })
? new XmlElement({
name: 'a',
content,
attrs: { target: '_blank', 'xlink:href': leftLink },
})
: new ElementList({ content })
const svgAttrs = {
xmlns: 'http://www.w3.org/2000/svg',
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
width,
height,
}
if (!hasLink) {
svgAttrs.role = 'img'
svgAttrs['aria-label'] = accessibleText
}
const svg = new XmlElement({
name: 'svg',
content: [title, body],
attrs: svgAttrs,
})
return svg.render()
}
class Badge {
static get fontFamily() {
throw new Error('Not implemented')
}
static get height() {
throw new Error('Not implemented')
}
@@ -197,41 +130,25 @@ class Badge {
labelColor,
}) {
const horizPadding = 5
const { hasLogo, totalLogoWidth, renderedLogo } = renderLogo({
logo,
badgeHeight: this.constructor.height,
horizPadding,
logoWidth,
logoPadding,
})
const hasLogo = !!logo
const totalLogoWidth = logoWidth + logoPadding
const accessibleText = createAccessibleText({ label, message })
const hasLabel = label.length || labelColor
if (labelColor == null) {
labelColor = '#555'
}
const [leftLink, rightLink] = links
labelColor = hasLabel || hasLogo ? labelColor : color
labelColor = escapeXml(labelColor)
color = escapeXml(color)
const labelMargin = totalLogoWidth + 1
const { renderedText: renderedLabel, width: labelWidth } = renderText({
leftMargin: labelMargin,
horizPadding,
content: label,
link: !shouldWrapBodyWithLink({ links }) && leftLink,
height: this.constructor.height,
verticalMargin: this.constructor.verticalMargin,
shadow: this.constructor.shadow,
color: labelColor,
})
const labelWidth = label.length
? preferredWidthOf(label, { font: WIDTH_FONT })
: 0
const leftWidth = hasLabel
? labelWidth + 2 * horizPadding + totalLogoWidth
: 0
const messageWidth = preferredWidthOf(message, { font: WIDTH_FONT })
let messageMargin = leftWidth - (message.length ? 1 : 0)
if (!hasLabel) {
if (hasLogo) {
@@ -240,18 +157,6 @@ class Badge {
messageMargin = messageMargin + 1
}
}
const { renderedText: renderedMessage, width: messageWidth } = renderText({
leftMargin: messageMargin,
horizPadding,
content: message,
link: rightLink,
height: this.constructor.height,
verticalMargin: this.constructor.verticalMargin,
shadow: this.constructor.shadow,
color,
})
let rightWidth = messageWidth + 2 * horizPadding
if (hasLogo && !hasLabel) {
rightWidth += totalLogoWidth + horizPadding - 1
@@ -259,9 +164,12 @@ class Badge {
const width = leftWidth + rightWidth
const accessibleText = createAccessibleText({ label, message })
this.horizPadding = horizPadding
this.labelMargin = labelMargin
this.messageMargin = messageMargin
this.links = links
this.labelWidth = labelWidth
this.messageWidth = messageWidth
this.leftWidth = leftWidth
this.rightWidth = rightWidth
this.width = width
@@ -270,25 +178,174 @@ class Badge {
this.label = label
this.message = message
this.accessibleText = accessibleText
this.renderedLogo = renderedLogo
this.renderedLabel = renderedLabel
this.renderedMessage = renderedMessage
this.logoElement = getLogoElement({
logo,
horizPadding,
badgeHeight: this.constructor.height,
logoWidth,
})
this.foregroundGroupElement = this.getForegroundGroupElement()
}
static render(params) {
return new this(params).render()
}
getTextElement({ leftMargin, content, link, color, textWidth, linkWidth }) {
if (!content.length) return ''
const { textColor, shadowColor } = colorsForBackground(color)
const x =
FONT_SCALE_UP_FACTOR * (leftMargin + 0.5 * textWidth + this.horizPadding)
const text = new XmlElement({
name: 'text',
content: [content],
attrs: {
x,
y: 140 + this.constructor.verticalMargin,
transform: FONT_SCALE_DOWN_VALUE,
fill: textColor,
textLength: FONT_SCALE_UP_FACTOR * textWidth,
},
})
const shadowText = new XmlElement({
name: 'text',
content: [content],
attrs: {
'aria-hidden': 'true',
x,
y: 150 + this.constructor.verticalMargin,
fill: shadowColor,
'fill-opacity': '.3',
transform: FONT_SCALE_DOWN_VALUE,
textLength: FONT_SCALE_UP_FACTOR * textWidth,
},
})
const shadow = this.constructor.shadow ? shadowText : ''
if (!link) {
return new ElementList({ content: [shadow, text] })
}
const rect = new XmlElement({
name: 'rect',
attrs: {
width: linkWidth,
x: leftMargin > 1 ? leftMargin + 1 : 0,
height: this.constructor.height,
fill: 'rgba(0,0,0,0)',
},
})
return new XmlElement({
name: 'a',
content: [rect, shadow, text],
attrs: { target: '_blank', 'xlink:href': link },
})
}
getLabelElement() {
const leftLink = this.links[0]
return this.getTextElement({
leftMargin: this.labelMargin,
content: this.label,
link: !shouldWrapBodyWithLink({ links: this.links })
? leftLink
: undefined,
color: this.labelColor,
textWidth: this.labelWidth,
linkWidth: this.leftWidth,
})
}
getMessageElement() {
const rightLink = this.links[1]
return this.getTextElement({
leftMargin: this.messageMargin,
content: this.message,
link: rightLink,
color: this.color,
textWidth: this.messageWidth,
linkWidth: this.rightWidth,
})
}
getClipPathElement(rx) {
return new XmlElement({
name: 'clipPath',
content: [
new XmlElement({
name: 'rect',
attrs: {
width: this.width,
height: this.constructor.height,
rx,
fill: '#fff',
},
}),
],
attrs: { id: 'r' },
})
}
getBackgroundGroupElement({ withGradient, attrs }) {
const leftRect = new XmlElement({
name: 'rect',
attrs: {
width: this.leftWidth,
height: this.constructor.height,
fill: this.labelColor,
},
})
const rightRect = new XmlElement({
name: 'rect',
attrs: {
x: this.leftWidth,
width: this.rightWidth,
height: this.constructor.height,
fill: this.color,
},
})
const gradient = new XmlElement({
name: 'rect',
attrs: {
width: this.width,
height: this.constructor.height,
fill: 'url(#s)',
},
})
const content = withGradient
? [leftRect, rightRect, gradient]
: [leftRect, rightRect]
return new XmlElement({ name: 'g', content, attrs })
}
getForegroundGroupElement() {
return new XmlElement({
name: 'g',
content: [
this.logoElement,
this.getLabelElement(),
this.getMessageElement(),
],
attrs: {
fill: '#fff',
'text-anchor': 'middle',
'font-family': FONT_FAMILY,
'text-rendering': 'geometricPrecision',
'font-size': 110,
},
})
}
render() {
throw new Error('Not implemented')
}
}
class Plastic extends Badge {
static get fontFamily() {
return fontFamily
}
static get height() {
return 18
}
@@ -302,6 +359,36 @@ class Plastic extends Badge {
}
render() {
const gradient = new XmlElement({
name: 'linearGradient',
content: [
new XmlElement({
name: 'stop',
attrs: { offset: 0, 'stop-color': '#fff', 'stop-opacity': '.7' },
}),
new XmlElement({
name: 'stop',
attrs: { offset: '.1', 'stop-color': '#aaa', 'stop-opacity': '.1' },
}),
new XmlElement({
name: 'stop',
attrs: { offset: '.9', 'stop-color': '#000', 'stop-opacity': '.3' },
}),
new XmlElement({
name: 'stop',
attrs: { offset: 1, 'stop-color': '#000', 'stop-opacity': '.5' },
}),
],
attrs: { id: 's', x2: 0, y2: '100%' },
})
const clipPath = this.getClipPathElement(4)
const backgroundGroup = this.getBackgroundGroupElement({
withGradient: true,
attrs: { 'clip-path': 'url(#r)' },
})
return renderBadge(
{
links: this.links,
@@ -310,38 +397,12 @@ class Plastic extends Badge {
accessibleText: this.accessibleText,
height: this.constructor.height,
},
`
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#fff" stop-opacity=".7"/>
<stop offset=".1" stop-color="#aaa" stop-opacity=".1"/>
<stop offset=".9" stop-color="#000" stop-opacity=".3"/>
<stop offset="1" stop-color="#000" stop-opacity=".5"/>
</linearGradient>
<clipPath id="r">
<rect width="${this.width}" height="${this.constructor.height}" rx="4" fill="#fff"/>
</clipPath>
<g clip-path="url(#r)">
<rect width="${this.leftWidth}" height="${this.constructor.height}" fill="${this.labelColor}"/>
<rect x="${this.leftWidth}" width="${this.rightWidth}" height="${this.constructor.height}" fill="${this.color}"/>
<rect width="${this.width}" height="${this.constructor.height}" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" ${this.constructor.fontFamily} text-rendering="geometricPrecision" font-size="110">
${this.renderedLogo}
${this.renderedLabel}
${this.renderedMessage}
</g>`
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement]
)
}
}
class Flat extends Badge {
static get fontFamily() {
return fontFamily
}
static get height() {
return 20
}
@@ -355,6 +416,28 @@ class Flat extends Badge {
}
render() {
const gradient = new XmlElement({
name: 'linearGradient',
content: [
new XmlElement({
name: 'stop',
attrs: { offset: 0, 'stop-color': '#bbb', 'stop-opacity': '.1' },
}),
new XmlElement({
name: 'stop',
attrs: { offset: 1, 'stop-opacity': '.1' },
}),
],
attrs: { id: 's', x2: 0, y2: '100%' },
})
const clipPath = this.getClipPathElement(3)
const backgroundGroup = this.getBackgroundGroupElement({
withGradient: true,
attrs: { 'clip-path': 'url(#r)' },
})
return renderBadge(
{
links: this.links,
@@ -363,36 +446,12 @@ class Flat extends Badge {
accessibleText: this.accessibleText,
height: this.constructor.height,
},
`
<linearGradient id="s" x2="0" y2="100%">
<stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<clipPath id="r">
<rect width="${this.width}" height="${this.constructor.height}" rx="3" fill="#fff"/>
</clipPath>
<g clip-path="url(#r)">
<rect width="${this.leftWidth}" height="${this.constructor.height}" fill="${this.labelColor}"/>
<rect x="${this.leftWidth}" width="${this.rightWidth}" height="${this.constructor.height}" fill="${this.color}"/>
<rect width="${this.width}" height="${this.constructor.height}" fill="url(#s)"/>
</g>
<g fill="#fff" text-anchor="middle" ${this.constructor.fontFamily} text-rendering="geometricPrecision" font-size="110">
${this.renderedLogo}
${this.renderedLabel}
${this.renderedMessage}
</g>`
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement]
)
}
}
class FlatSquare extends Badge {
static get fontFamily() {
return fontFamily
}
static get height() {
return 20
}
@@ -406,6 +465,11 @@ class FlatSquare extends Badge {
}
render() {
const backgroundGroup = this.getBackgroundGroupElement({
withGradient: false,
attrs: { 'shape-rendering': 'crispEdges' },
})
return renderBadge(
{
links: this.links,
@@ -414,17 +478,7 @@ class FlatSquare extends Badge {
accessibleText: this.accessibleText,
height: this.constructor.height,
},
`
<g shape-rendering="crispEdges">
<rect width="${this.leftWidth}" height="${this.constructor.height}" fill="${this.labelColor}"/>
<rect x="${this.leftWidth}" width="${this.rightWidth}" height="${this.constructor.height}" fill="${this.color}"/>
</g>
<g fill="#fff" text-anchor="middle" ${this.constructor.fontFamily} text-rendering="geometricPrecision" font-size="110">
${this.renderedLogo}
${this.renderedLabel}
${this.renderedMessage}
</g>`
[backgroundGroup, this.foregroundGroupElement]
)
}
}
@@ -448,13 +502,7 @@ function social({
const labelHorizPadding = 5
const messageHorizPadding = 4
const horizGutter = 6
const { totalLogoWidth, renderedLogo } = renderLogo({
logo,
badgeHeight: externalHeight,
horizPadding: labelHorizPadding,
logoWidth,
logoPadding,
})
const totalLogoWidth = logoWidth + logoPadding
const hasMessage = message.length
const font = 'bold 11px Helvetica'
@@ -463,75 +511,235 @@ function social({
const labelRectWidth = labelTextWidth + totalLogoWidth + 2 * labelHorizPadding
const messageRectWidth = messageTextWidth + 2 * messageHorizPadding
let [leftLink, rightLink] = links
leftLink = escapeXml(leftLink)
rightLink = escapeXml(rightLink)
const [leftLink, rightLink] = links
const { hasLeftLink, hasRightLink, hasLink } = hasLinks({ links })
const accessibleText = createAccessibleText({ label, message })
function renderMessageBubble() {
function getMessageBubble() {
if (!hasMessage) return ''
const messageBubbleMainX = labelRectWidth + horizGutter + 0.5
const messageBubbleNotchX = labelRectWidth + horizGutter
return `
<rect x="${messageBubbleMainX}" y="0.5" width="${messageRectWidth}" height="${internalHeight}" rx="2" fill="#fafafa"/>
<rect x="${messageBubbleNotchX}" y="7.5" width="0.5" height="5" stroke="#fafafa"/>
<path d="M${messageBubbleMainX} 6.5 l-3 3v1 l3 3" stroke="d5d5d5" fill="#fafafa"/>
`
const content = [
new XmlElement({
name: 'rect',
attrs: {
x: messageBubbleMainX,
y: 0.5,
width: messageRectWidth,
height: internalHeight,
rx: 2,
fill: '#fafafa',
},
}),
new XmlElement({
name: 'rect',
attrs: {
x: messageBubbleNotchX,
y: 7.5,
width: 0.5,
height: 5,
stroke: '#fafafa',
},
}),
new XmlElement({
name: 'path',
attrs: {
d: `M${messageBubbleMainX} 6.5 l-3 3v1 l3 3`,
stroke: 'd5d5d5',
fill: '#fafafa',
},
}),
]
return new ElementList({ content })
}
function renderLabelText() {
function getLabelText() {
const labelTextX =
10 * (totalLogoWidth + labelTextWidth / 2 + labelHorizPadding)
const labelTextLength = 10 * labelTextWidth
const escapedLabel = escapeXml(label)
FONT_SCALE_UP_FACTOR *
(totalLogoWidth + labelTextWidth / 2 + labelHorizPadding)
const labelTextLength = FONT_SCALE_UP_FACTOR * labelTextWidth
const shouldWrapWithLink = hasLeftLink && !shouldWrapBodyWithLink({ links })
const rect = `<rect id="llink" stroke="#d5d5d5" fill="url(#a)" x=".5" y=".5" width="${labelRectWidth}" height="${internalHeight}" rx="2" />`
const shadow = `<text aria-hidden="true" x="${labelTextX}" y="150" fill="#fff" transform="scale(.1)" textLength="${labelTextLength}">${escapedLabel}</text>`
const text = `<text x="${labelTextX}" y="140" transform="scale(.1)" textLength="${labelTextLength}">${escapedLabel}</text>`
const rect = new XmlElement({
name: 'rect',
attrs: {
id: 'llink',
stroke: '#d5d5d5',
fill: 'url(#a)',
x: '.5',
y: '.5',
width: labelRectWidth,
height: internalHeight,
rx: 2,
},
})
const shadow = new XmlElement({
name: 'text',
content: [label],
attrs: {
'aria-hidden': 'true',
x: labelTextX,
y: 150,
fill: '#fff',
transform: FONT_SCALE_DOWN_VALUE,
textLength: labelTextLength,
},
})
const text = new XmlElement({
name: 'text',
content: [label],
attrs: {
x: labelTextX,
y: 140,
transform: FONT_SCALE_DOWN_VALUE,
textLength: labelTextLength,
},
})
return shouldWrapWithLink
? `
<a target="_blank" xlink:href="${leftLink}">
${shadow}
${text}
${rect}
</a>
`
: `
${rect}
${shadow}
${text}
`
? new XmlElement({
name: 'a',
content: [shadow, text, rect],
attrs: { target: '_blank', 'xlink:href': leftLink },
})
: new ElementList({ content: [rect, shadow, text] })
}
function renderMessageText() {
const messageTextX =
10 * (labelRectWidth + horizGutter + messageRectWidth / 2)
const messageTextLength = 10 * messageTextWidth
const escapedMessage = escapeXml(message)
function getMessageText() {
if (!hasMessage) return ''
const rect = `<rect width="${messageRectWidth + 1}" x="${
labelRectWidth + horizGutter
}" height="${internalHeight + 1}" fill="rgba(0,0,0,0)" />`
const shadow = `<text aria-hidden="true" x="${messageTextX}" y="150" fill="#fff" transform="scale(.1)" textLength="${messageTextLength}">${escapedMessage}</text>`
const text = `<text id="rlink" x="${messageTextX}" y="140" transform="scale(.1)" textLength="${messageTextLength}">${escapedMessage}</text>`
const messageTextX =
FONT_SCALE_UP_FACTOR *
(labelRectWidth + horizGutter + messageRectWidth / 2)
const messageTextLength = FONT_SCALE_UP_FACTOR * messageTextWidth
const rect = new XmlElement({
name: 'rect',
attrs: {
width: messageRectWidth + 1,
x: labelRectWidth + horizGutter,
height: internalHeight + 1,
fill: 'rgba(0,0,0,0)',
},
})
const shadow = new XmlElement({
name: 'text',
content: [message],
attrs: {
'aria-hidden': 'true',
x: messageTextX,
y: 150,
fill: '#fff',
transform: FONT_SCALE_DOWN_VALUE,
textLength: messageTextLength,
},
})
const text = new XmlElement({
name: 'text',
content: [message],
attrs: {
id: 'rlink',
x: messageTextX,
y: 140,
transform: FONT_SCALE_DOWN_VALUE,
textLength: messageTextLength,
},
})
return hasRightLink
? `
<a target="_blank" xlink:href="${rightLink}">
${rect}
${shadow}
${text}
</a>
`
: `
${shadow}
${text}
`
? new XmlElement({
name: 'a',
content: [rect, shadow, text],
attrs: { target: '_blank', 'xlink:href': rightLink },
})
: new ElementList({ content: [shadow, text] })
}
const style = new XmlElement({
name: 'style',
content: [
'a:hover #llink{fill:url(#b);stroke:#ccc}a:hover #rlink{fill:#4183c4}',
],
})
const gradients = new ElementList({
content: [
new XmlElement({
name: 'linearGradient',
content: [
new XmlElement({
name: 'stop',
attrs: {
offset: 0,
'stop-color': '#fcfcfc',
'stop-opacity': 0,
},
}),
new XmlElement({
name: 'stop',
attrs: { offset: 1, 'stop-opacity': '.1' },
}),
],
attrs: { id: 'a', x2: 0, y2: '100%' },
}),
new XmlElement({
name: 'linearGradient',
content: [
new XmlElement({
name: 'stop',
attrs: { offset: 0, 'stop-color': '#ccc', 'stop-opacity': '.1' },
}),
new XmlElement({
name: 'stop',
attrs: { offset: 1, 'stop-opacity': '.1' },
}),
],
attrs: { id: 'b', x2: 0, y2: '100%' },
}),
],
})
const labelRect = new XmlElement({
name: 'rect',
attrs: {
stroke: 'none',
fill: '#fcfcfc',
x: 0.5,
y: 0.5,
width: labelRectWidth,
height: internalHeight,
rx: 2,
},
})
const messageBubble = getMessageBubble()
const labelText = getLabelText()
const messageText = getMessageText()
const backgroundGroup = new XmlElement({
name: 'g',
content: [labelRect, messageBubble],
attrs: { stroke: '#d5d5d5' },
})
const foregroundGroup = new XmlElement({
name: 'g',
content: [labelText, messageText],
attrs: {
'aria-hidden': `${!hasLink}`,
fill: '#333',
'text-anchor': 'middle',
'font-family': SOCIAL_FONT_FAMILY,
'text-rendering': 'geometricPrecision',
'font-weight': 700,
'font-size': '110px',
'line-height': '14px',
},
})
const logoElement = getLogoElement({
logo,
horizPadding: labelHorizPadding,
badgeHeight: externalHeight,
logoWidth,
})
return renderBadge(
{
links,
@@ -540,26 +748,7 @@ function social({
accessibleText,
height: externalHeight,
},
`
<style>a:hover #llink{fill:url(#b);stroke:#ccc}a:hover #rlink{fill:#4183c4}</style>
<linearGradient id="a" x2="0" y2="100%">
<stop offset="0" stop-color="#fcfcfc" stop-opacity="0"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<linearGradient id="b" x2="0" y2="100%">
<stop offset="0" stop-color="#ccc" stop-opacity=".1"/>
<stop offset="1" stop-opacity=".1"/>
</linearGradient>
<g stroke="#d5d5d5">
<rect stroke="none" fill="#fcfcfc" x="0.5" y="0.5" width="${labelRectWidth}" height="${internalHeight}" rx="2"/>
${hasMessage ? renderMessageBubble() : ''}
</g>
${renderedLogo}
<g aria-hidden="${!hasLink}" fill="#333" text-anchor="middle" ${socialFontFamily} text-rendering="geometricPrecision" font-weight="700" font-size="110px" line-height="14px">
${renderLabelText()}
${hasMessage ? renderMessageText() : ''}
</g>
`
[style, gradients, backgroundGroup, logoElement, foregroundGroup]
)
}
@@ -574,7 +763,6 @@ function forTheBadge({
}) {
const FONT_SIZE = 10
const BADGE_HEIGHT = 28
const LOGO_HEIGHT = 14
const TEXT_MARGIN = 12
const LOGO_MARGIN = 9
const LOGO_TEXT_GUTTER = 6
@@ -641,15 +829,11 @@ function forTheBadge({
}
}
const logoElement = new XmlElement({
name: 'image',
attrs: {
x: logoMinX,
y: 0.5 * (BADGE_HEIGHT - LOGO_HEIGHT),
width: logoWidth,
height: LOGO_HEIGHT,
'xlink:href': logo,
},
const logoElement = getLogoElement({
logo,
horizPadding: logoMinX,
badgeHeight: BADGE_HEIGHT,
logoWidth,
})
function getLabelElement() {
@@ -772,7 +956,7 @@ function forTheBadge({
const foregroundGroup = new XmlElement({
name: 'g',
content: [
logo ? logoElement : '',
logoElement,
hasLabel ? getLabelElement() : '',
getMessageElement(),
],
@@ -794,7 +978,7 @@ function forTheBadge({
accessibleText: createAccessibleText({ label, message }),
height: BADGE_HEIGHT,
},
[backgroundGroup.render(), foregroundGroup.render()].join('')
[backgroundGroup, foregroundGroup]
)
}

View File

@@ -210,6 +210,28 @@ describe('The badge generator', function () {
links: ['https://shields.io/', 'https://www.google.co.uk/'],
})
})
it('should match snapshots: black text when the label color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'flat',
color: '#000',
labelColor: '#f3f3f3',
})
})
it('should match snapshots: black text when the message color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'flat',
color: '#e2ffe1',
labelColor: '#000',
})
})
})
describe('"flat-square" template badge generation', function () {
@@ -280,6 +302,28 @@ describe('The badge generator', function () {
links: ['https://shields.io/', 'https://www.google.co.uk/'],
})
})
it('should match snapshots: black text when the label color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'flat-square',
color: '#000',
labelColor: '#f3f3f3',
})
})
it('should match snapshots: black text when the message color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'flat-square',
color: '#e2ffe1',
labelColor: '#000',
})
})
})
describe('"plastic" template badge generation', function () {
@@ -350,6 +394,28 @@ describe('The badge generator', function () {
links: ['https://shields.io/', 'https://www.google.co.uk/'],
})
})
it('should match snapshots: black text when the label color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'plastic',
color: '#000',
labelColor: '#f3f3f3',
})
})
it('should match snapshots: black text when the message color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'plastic',
color: '#e2ffe1',
labelColor: '#000',
})
})
})
describe('"for-the-badge" template badge generation', function () {
@@ -447,6 +513,28 @@ describe('The badge generator', function () {
links: ['https://shields.io/', 'https://www.google.co.uk/'],
})
})
it('should match snapshots: black text when the label color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'for-the-badge',
color: '#000',
labelColor: '#f3f3f3',
})
})
it('should match snapshots: black text when the message color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'for-the-badge',
color: '#e2ffe1',
labelColor: '#000',
})
})
})
describe('"social" template badge generation', function () {
@@ -556,28 +644,4 @@ describe('The badge generator', function () {
})
})
})
describe('text colors', function () {
it('should use black text when the label color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'flat',
color: '#000',
labelColor: '#f3f3f3',
})
})
it('should use black text when the message color is light', function () {
expectBadgeToMatchSnapshot({
label: 'cactus',
message: 'grown',
format: 'svg',
style: 'for-the-badge',
color: '#e2ffe1',
labelColor: '#000',
})
})
})
})

View File

@@ -58,7 +58,7 @@ class XmlElement {
if (this.content.length > 0) {
const content = this.content
.map(function (el) {
if (el instanceof XmlElement) {
if (typeof el.render === 'function') {
return el.render()
} else {
return escapeXml(el)
@@ -73,4 +73,24 @@ class XmlElement {
}
}
module.exports = { escapeXml, stripXmlWhitespace, XmlElement }
/**
* Convenience class. Sometimes it is useful to return an object that behaves
* like an XmlElement but renders multiple XML tags (not wrapped in a <g>).
*/
class ElementList {
constructor({ content = [] }) {
this.content = content
}
render() {
return this.content.reduce(
(acc, el) =>
typeof el.render === 'function'
? acc + el.render()
: acc + escapeXml(el),
''
)
}
}
module.exports = { escapeXml, stripXmlWhitespace, XmlElement, ElementList }

View File

@@ -21,7 +21,7 @@
"bugs": {
"url": "https://github.com/badges/shields/issues"
},
"homepage": "http://shields.io",
"homepage": "https://shields.io",
"bin": {
"badge": "lib/badge-cli.js"
},

View File

@@ -40,6 +40,8 @@ public:
debug:
enabled: 'GITHUB_DEBUG_ENABLED'
intervalSeconds: 'GITHUB_DEBUG_INTERVAL_SECONDS'
gitlab:
authorizedOrigins: 'GITLAB_ORIGINS'
jenkins:
authorizedOrigins: 'JENKINS_ORIGINS'
jira:
@@ -77,6 +79,7 @@ private:
gh_client_id: 'GH_CLIENT_ID'
gh_client_secret: 'GH_CLIENT_SECRET'
gh_token: 'GH_TOKEN'
gitlab_token: 'GITLAB_TOKEN'
jenkins_user: 'JENKINS_USER'
jenkins_pass: 'JENKINS_PASS'
jira_user: 'JIRA_USER'

View File

@@ -3,6 +3,7 @@ private:
discord_bot_token: ...
gh_client_id: ...
gh_client_secret: ...
gitlab_token: ...
redis_url: ...
sentry_dsn: ...
shields_secret: ...

View File

@@ -5,6 +5,7 @@ private:
# you can also set these values through environment variables, which may be
# preferable for self hosting.
gh_token: '...'
gitlab_token: '...'
twitch_client_id: '...'
twitch_client_secret: '...'
weblate_api_key: '...'

View File

@@ -87,7 +87,7 @@ describe('Influx metrics', function () {
})
it('should send metrics', async function () {
const scope = nock('http://shields-metrics.io/', {
const scope = nock('https://shields-metrics.io/', {
reqheaders: {
'Content-Type': 'application/x-www-form-urlencoded',
},
@@ -101,7 +101,7 @@ describe('Influx metrics', function () {
.reply(200)
process.env.INSTANCE_ID = 'instance2'
influxMetrics = new InfluxMetrics(metricInstance, {
url: 'http://shields-metrics.io/metrics',
url: 'https://shields-metrics.io/metrics',
timeoutMillseconds: 100,
intervalSeconds: 0.001,
username: 'metrics-username',
@@ -132,7 +132,7 @@ describe('Influx metrics', function () {
})
const influxMetrics = new InfluxMetrics(metricInstance, {
url: 'http://shields-metrics.io/metrics',
url: 'https://shields-metrics.io/metrics',
timeoutMillseconds: 50,
intervalSeconds: 0,
username: 'metrics-username',
@@ -149,14 +149,14 @@ describe('Influx metrics', function () {
.and(
sinon.match.has(
'message',
'Cannot push metrics. Cause: RequestError: Nock: Disallowed net connect for "shields-metrics.io:80/metrics"'
'Cannot push metrics. Cause: RequestError: Nock: Disallowed net connect for "shields-metrics.io:443/metrics"'
)
)
)
})
it('should log error responses', async function () {
nock('http://shields-metrics.io/').persist().post('/metrics').reply(400)
nock('https://shields-metrics.io/').persist().post('/metrics').reply(400)
await influxMetrics.sendMetrics()
@@ -166,7 +166,7 @@ describe('Influx metrics', function () {
.and(
sinon.match.has(
'message',
'Cannot push metrics. http://shields-metrics.io/metrics responded with status code 400'
'Cannot push metrics. https://shields-metrics.io/metrics responded with status code 400'
)
)
)

View File

@@ -125,6 +125,7 @@ const publicConfigSchema = Joi.object({
intervalSeconds: Joi.number().integer().min(1).required(),
},
},
gitlab: defaultService,
jira: defaultService,
jenkins: Joi.object({
authorizedOrigins: origins,
@@ -161,6 +162,7 @@ const privateConfigSchema = Joi.object({
gh_client_id: Joi.string(),
gh_client_secret: Joi.string(),
gh_token: Joi.string(),
gitlab_token: Joi.string(),
jenkins_user: Joi.string(),
jenkins_pass: Joi.string(),
jira_user: Joi.string(),

View File

@@ -188,6 +188,10 @@ class TokenPool {
this.priorityQueue = new PriorityQueue(this.constructor.compareTokens)
}
count() {
return this.tokenIds.size
}
/**
* compareTokens
*

View File

@@ -90,20 +90,20 @@ Each service has a directory for its files:
All service badge classes inherit from [BaseService] or another class which extends it.
Other classes implement useful behavior on top of [BaseService].
- [BaseJsonService](https://contributing.shields.io/module-core_base-service_base-json-basejsonservice)
- [BaseJsonService](https://contributing.shields.io/module-core_base-service_base-json-BaseJsonService.html)
implements methods for performing requests to a JSON API and schema validation.
- [BaseXmlService](https://contributing.shields.io/module-core_base-service_base-xml-basexmlservice)
- [BaseXmlService](https://contributing.shields.io/module-core_base-service_base-xml-BaseXmlService.html)
implements methods for performing requests to an XML API and schema validation.
- [BaseYamlService](https://contributing.shields.io/module-core_base-service_base-yaml-baseyamlservice)
- [BaseYamlService](https://contributing.shields.io/module-core_base-service_base-yaml-BaseYamlService.html)
implements methods for performing requests to a YAML API and schema validation.
- [BaseSvgScrapingService](https://contributing.shields.io/module-core_base-service_base-svg-scraping-basesvgscrapingservice)
- [BaseSvgScrapingService](https://contributing.shields.io/module-core_base-service_base-svg-scraping-BaseSvgScrapingService.html)
implements methods for retrieving information from existing third-party badges.
- [BaseGraphqlService](https://contributing.shields.io/module-core_base-service_base-graphql-basegraphqlservice)
- [BaseGraphqlService](https://contributing.shields.io/module-core_base-service_base-graphql-BaseGraphqlService.html)
implements methods for performing requests to a GraphQL API and schema validation.
- If you are contributing to a _service family_, you may define a common super
class for the badges or one may already exist.
[baseservice]: https://contributing.shields.io/module-core_base-service_base-baseservice
[baseservice]: https://contributing.shields.io/module-core_base-service_base.html
As a first step we will look at the code for an example which generates a badge without contacting an API.

View File

@@ -32,6 +32,8 @@ Production hosting is managed by the Shields ops team:
| Twitch | OAuth app | @PyvesB |
| Discord | OAuth app | @PyvesB |
| YouTube | Account owner | @PyvesB |
| GitLab | Account owner | @calebcartwright |
| GitLab | Account access | @calebcartwright, @chris48s, @paulmelnikow, @PyvesB |
| OpenStreetMap (for Wheelmap) | Account owner | @paulmelnikow |
| DNS | Account owner | @olivierlacan |
| DNS | Read-only account access | @espadrine, @paulmelnikow, @chris48s |
@@ -102,7 +104,7 @@ hosted on [Zeit Now][]. It's managed in the
Both the badge server and frontend are served from Heroku.
After merging a commit to master, heroku should create a staging deploy. Check this has deployed correctly in the `shields-staging` pipeline and review http://shields-staging.herokuapp.com/
After merging a commit to master, heroku should create a staging deploy. Check this has deployed correctly in the `shields-staging` pipeline and review https://shields-staging.herokuapp.com/
If we're happy with it, "promote to production". This will deploy what's on staging to the `shields-production-eu` and `shields-production-us` pieplines.

View File

@@ -10,7 +10,7 @@ You will need Node 14 or later, which you can install using a
On Ubuntu / Debian:
```sh
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -; sudo apt-get install -y nodejs
curl -sL https://deb.nodesource.com/setup_14.x | sudo -E bash -; sudo apt-get install -y nodejs
```
```sh
@@ -140,11 +140,6 @@ preconfigured raster server.
[raster server]: https://github.com/badges/svg-to-image-proxy
[micro]: https://github.com/zeit/micro
## Persistence
To enable Redis-backed GitHub token persistence, point `REDIS_URL` to your
Redis installation.
## Server secrets
You can add your own server secrets in environment variables or `config/local.yml`.

View File

@@ -147,6 +147,15 @@ These settings are used by shields.io for GitHub OAuth app authorization
but will not be necessary for most self-hosted installations. See
[production-hosting.md](./production-hosting.md).
### GitLab
- `GITLAB_ORIGINS` (yml: `public.services.gitlab.authorizedOrigins`)
- `GITLAB_TOKEN` (yml: `private.gitlab_token`)
A GitLab [Personal Access Token][gitlab-pat] is required for accessing private content. If you need a GitLab token for your self-hosted Shields server then we recommend limiting the scopes to the minimal set necessary for the badges you are using.
[gitlab-pat]: https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html
### Jenkins CI
- `JENKINS_ORIGINS` (yml: `public.services.jenkins.authorizedOrigins`)

View File

@@ -139,7 +139,7 @@ function StyleTable({ style }: { style: string }): JSX.Element {
<td>
<Badges
badges={badges}
baseUrl="http://img.shields.io"
baseUrl="https://img.shields.io"
style={style}
/>
</td>

View File

@@ -3,7 +3,7 @@
"version": "0.0.0",
"description": "Shields.io frontend",
"private": true,
"homepage": "http://shields.io",
"homepage": "https://shields.io",
"license": "CC0-1.0",
"repository": {
"type": "git",

6048
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,7 +10,7 @@
"image",
"shields.io"
],
"homepage": "http://shields.io",
"homepage": "https://shields.io",
"bugs": {
"url": "https://github.com/badges/shields/issues",
"email": "thaddee.tyl@gmail.com"
@@ -24,7 +24,7 @@
"dependencies": {
"@fontsource/lato": "^4.5.0",
"@fontsource/lekton": "^4.5.0",
"@sentry/node": "^6.10.0",
"@sentry/node": "^6.12.0",
"@shields_io/camp": "^18.1.1",
"badge-maker": "file:badge-maker",
"bytes": "^3.1.0",
@@ -37,14 +37,14 @@
"decamelize": "^5.0.0",
"emojic": "^1.1.16",
"escape-string-regexp": "^4.0.0",
"fast-xml-parser": "^3.19.0",
"fast-xml-parser": "^3.20.0",
"glob": "^7.1.7",
"global-agent": "^3.0.0",
"got": "11.8.2",
"graphql": "^15.5.1",
"graphql": "^15.5.3",
"graphql-tag": "^2.12.5",
"ioredis": "4.27.6",
"joi": "17.4.1",
"ioredis": "4.27.9",
"joi": "17.4.2",
"joi-extension-semver": "5.0.0",
"js-yaml": "^4.1.0",
"jsonpath": "~1.1.1",
@@ -57,12 +57,12 @@
"path-to-regexp": "^6.2.0",
"pretty-bytes": "^5.6.0",
"priorityqueuejs": "^2.0.0",
"prom-client": "^13.1.0",
"prom-client": "^13.2.0",
"qs": "^6.10.1",
"query-string": "^7.0.1",
"request": "~2.88.2",
"semver": "~7.3.5",
"simple-icons": "5.8.0",
"simple-icons": "5.14.0",
"webextension-store-meta": "^1.0.4",
"xmldom": "~0.6.0",
"xpath": "~0.0.32"
@@ -142,25 +142,25 @@
]
},
"devDependencies": {
"@babel/core": "^7.14.8",
"@babel/core": "^7.15.5",
"@babel/polyfill": "^7.12.1",
"@babel/register": "7.14.5",
"@babel/register": "7.15.3",
"@mapbox/react-click-to-select": "^2.2.1",
"@types/chai": "^4.2.21",
"@types/lodash.debounce": "^4.0.6",
"@types/lodash.groupby": "^4.6.6",
"@types/mocha": "^9.0.0",
"@types/node": "^16.4.7",
"@types/node": "^16.7.10",
"@types/react-helmet": "^6.1.2",
"@types/react-modal": "^3.12.1",
"@types/react-select": "^4.0.17",
"@types/styled-components": "5.1.11",
"@typescript-eslint/eslint-plugin": "^4.28.0",
"@typescript-eslint/parser": "^4.27.0",
"@types/styled-components": "5.1.14",
"@typescript-eslint/eslint-plugin": "^4.31.0",
"@typescript-eslint/parser": "^4.30.0",
"babel-plugin-inline-react-svg": "^2.0.1",
"babel-plugin-istanbul": "^6.0.0",
"babel-preset-gatsby": "^1.2.0",
"c8": "^7.8.0",
"babel-preset-gatsby": "^1.13.0",
"c8": "^7.9.0",
"caller": "^1.0.1",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
@@ -168,20 +168,20 @@
"chai-string": "^1.4.0",
"child-process-promise": "^2.2.1",
"clipboard-copy": "^4.0.1",
"concurrently": "^6.2.0",
"cypress": "^8.1.0",
"concurrently": "^6.2.1",
"cypress": "^8.4.0",
"danger": "^10.6.6",
"danger-plugin-no-test-shortcuts": "^2.0.0",
"deepmerge": "^4.2.2",
"eslint": "^7.31.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-config-standard": "^16.0.3",
"eslint-config-standard-jsx": "^10.0.0",
"eslint-config-standard-react": "^11.0.1",
"eslint-plugin-chai-friendly": "^0.7.1",
"eslint-plugin-cypress": "^2.11.3",
"eslint-plugin-import": "^2.23.4",
"eslint-plugin-jsdoc": "^36.0.6",
"eslint-plugin-chai-friendly": "^0.7.2",
"eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-import": "^2.24.2",
"eslint-plugin-jsdoc": "^36.1.0",
"eslint-plugin-mocha": "^9.0.0",
"eslint-plugin-no-extension-in-require": "^0.2.0",
"eslint-plugin-node": "^11.1.0",
@@ -191,35 +191,34 @@
"eslint-plugin-sort-class-members": "^1.11.0",
"fetch-ponyfill": "^7.1.0",
"form-data": "^4.0.0",
"gatsby": "3.10.2",
"gatsby-plugin-catch-links": "^3.1.0",
"gatsby-plugin-page-creator": "^3.10.0",
"gatsby-plugin-react-helmet": "^4.1.0",
"gatsby-plugin-remove-trailing-slashes": "^3.1.0",
"gatsby-plugin-styled-components": "^4.6.0",
"gatsby": "3.13.1",
"gatsby-plugin-catch-links": "^3.13.0",
"gatsby-plugin-page-creator": "^3.13.0",
"gatsby-plugin-react-helmet": "^4.13.0",
"gatsby-plugin-remove-trailing-slashes": "^3.13.0",
"gatsby-plugin-styled-components": "^4.13.0",
"gatsby-plugin-typescript": "^3.2.0",
"humanize-string": "^2.1.0",
"husky": "^4.3.8",
"icedfrisby": "4.0.0",
"icedfrisby-nock": "^2.1.0",
"is-svg": "^4.3.1",
"js-yaml-loader": "^1.2.2",
"jsdoc": "^3.6.7",
"lint-staged": "^11.1.1",
"lint-staged": "^11.1.2",
"lodash.debounce": "^4.0.8",
"lodash.difference": "^4.5.0",
"minimist": "^1.2.5",
"mocha": "^9.0.3",
"mocha": "^9.1.1",
"mocha-env-reporter": "^4.0.0",
"mocha-junit-reporter": "^2.0.0",
"mocha-yaml-loader": "^1.0.3",
"nock": "13.1.1",
"nock": "13.1.3",
"node-mocks-http": "^1.10.1",
"nodemon": "^2.0.12",
"npm-run-all": "^4.1.5",
"open-cli": "^7.0.0",
"open-cli": "^7.0.1",
"portfinder": "^1.0.28",
"prettier": "2.3.2",
"prettier": "2.4.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-error-overlay": "^6.0.9",
@@ -231,14 +230,15 @@
"redis-server": "^1.2.2",
"rimraf": "^3.0.2",
"sazerac": "^2.0.0",
"simple-git-hooks": "^2.6.1",
"sinon": "^11.1.2",
"sinon-chai": "^3.7.0",
"snap-shot-it": "^7.9.6",
"start-server-and-test": "1.13.1",
"styled-components": "^5.3.0",
"start-server-and-test": "1.14.0",
"styled-components": "^5.3.1",
"ts-mocha": "^8.0.0",
"tsd": "^0.17.0",
"typescript": "^4.3.5"
"typescript": "^4.4.3"
},
"engines": {
"node": "^14.17.1",
@@ -259,9 +259,7 @@
"url": "https://opencollective.com/shields",
"logo": "https://opencollective.com/opencollective/logo.txt"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
"simple-git-hooks": {
"pre-commit": "npx lint-staged"
}
}

View File

@@ -1,14 +1,11 @@
/* eslint-disable import/order */
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
// Set up Sentry reporting as early in the process as possible.
import configModule from 'config'
import Sentry from '@sentry/node'
import Server from './core/server/server.js'
// Set up Sentry reporting as early in the process as possible.
const config = configModule.util.toObject()
const disabledIntegrations = ['Console', 'Http']
Sentry.init({

View File

@@ -27,5 +27,5 @@ t.create('Bugzilla valid bug status with custom baseUrl')
})
t.create('Bugzilla invalid bug status')
.get('/102.json?baseUrl=https://bugzilla.gnome.org')
.get('/001.json')
.expectBadge({ label: 'bugzilla', message: 'not found' })

View File

@@ -48,14 +48,6 @@ export default class CoverityScan extends BaseJsonService {
const json = await this._requestJson({
url,
schema,
options: {
// Coverity has an issue in their certificate chain that requires
// disabling the default strict SSL check in order to call their API.
// For more information see:
// https://github.com/badges/shields/issues/3334
// https://github.com/badges/shields/pull/3336
strictSSL: false,
},
errorMessages: {
// At the moment Coverity returns an HTTP 200 with an HTML page
// displaying the text 404 when project is not found.

View File

@@ -15,7 +15,7 @@ t.create('docker version (valid, library with tag)')
})
t.create('docker version (valid, user)')
.get('/datadog/agent.json')
.get('/datadog/dogstatsd.json')
.expectBadge({
label: 'version',
message: isSemver,

View File

@@ -0,0 +1,76 @@
import Joi from 'joi'
import { metric } from '../text-formatters.js'
import { BaseJsonService, InvalidResponse, NotFound } from '../index.js'
/**
* Validates that the schema response is what we're expecting.
* The username pattern should match the freeCodeCamp repository.
*
* @see https://github.com/freeCodeCamp/freeCodeCamp/blob/main/utils/validate.js#L14
*/
const schema = Joi.object({
entities: Joi.object({
user: Joi.object()
.required()
.pattern(/^[a-zA-Z0-9\-_+]*$/, {
points: Joi.number().allow(null).required(),
}),
}).optional(),
}).required()
/**
* This badge displays the total number of points a student has accumulated
* from completing challenges on freeCodeCamp.
*/
export default class FreeCodeCampPoints extends BaseJsonService {
static category = 'other'
static route = {
base: 'freecodecamp/points',
pattern: ':username',
}
static examples = [
{
title: 'freeCodeCamp points',
namedParams: { username: 'sethi' },
staticPreview: this.render({ points: 934 }),
},
]
static defaultBadgeData = { label: 'points', color: 'info' }
static render({ points }) {
return { message: metric(points) }
}
async fetch({ username }) {
return this._requestJson({
schema,
url: `https://api.freecodecamp.org/api/users/get-public-profile`,
options: {
qs: {
username,
},
},
})
}
static transform(response, username) {
const { entities } = response
if (entities === undefined)
throw new NotFound({ prettyMessage: 'profile not found' })
const { points } = entities.user[username]
if (points === null) throw new InvalidResponse({ prettyMessage: 'private' })
return points
}
async handle({ username }) {
const response = await this.fetch({ username })
const points = this.constructor.transform(response, username)
return this.constructor.render({ points })
}
}

View File

@@ -0,0 +1,15 @@
import { createServiceTester } from '../tester.js'
import { isMetric } from '../test-validators.js'
export const t = await createServiceTester()
t.create('Total Points Valid')
.get('/sethi.json')
.expectBadge({ label: 'points', message: isMetric })
t.create('Total Points Private')
.get('/set.json')
.expectBadge({ label: 'points', message: 'private' })
t.create('Total Points Invalid')
.get('/invalid@username.json')
.expectBadge({ label: 'points', message: 'profile not found' })

View File

@@ -3,7 +3,7 @@ import request from 'request'
import { userAgent } from '../../../core/base-service/legacy-request-handler.js'
import log from '../../../core/server/log.js'
function setRoutes({ server, authHelper, onTokenAccepted }) {
function setRoutes({ server, authHelper, onTokenAccepted, tokenScopes }) {
const baseUrl = process.env.GATSBY_BASE_URL || 'https://img.shields.io'
server.route(/^\/github-auth$/, (data, match, end, ask) => {
@@ -15,6 +15,7 @@ function setRoutes({ server, authHelper, onTokenAccepted }) {
// it's not setting a bad example.
client_id: authHelper._user,
redirect_uri: `${baseUrl}/github-auth/done`,
scope: tokenScopes,
})
ask.res.setHeader(
'Location',

View File

@@ -41,6 +41,7 @@ describe('Github token acceptor', function () {
server: camp,
authHelper: oauthHelper,
onTokenAccepted,
tokenScopes: 'read:packages',
})
})
@@ -52,6 +53,7 @@ describe('Github token acceptor', function () {
const qs = queryString.stringify({
client_id: fakeClientId,
redirect_uri: 'https://img.shields.io/github-auth/done',
scope: 'read:packages',
})
const expectedLocationHeader = `https://github.com/login/oauth/authorize?${qs}`
expect(res.headers.location).to.equal(expectedLocationHeader)

View File

@@ -30,11 +30,10 @@ describe('Github API provider', function () {
it('should be able to run 10 requests', async function () {
this.timeout('20s')
for (let i = 0; i < 10; ++i) {
await githubApiProvider.requestAsPromise(
await githubApiProvider.requestAsPromise({
request,
'/repos/rust-lang/rust',
{}
)
url: '/repos/rust-lang/rust',
})
}
})
})
@@ -52,11 +51,10 @@ describe('Github API provider', function () {
const headers = []
async function performOneRequest() {
const { res } = await githubApiProvider.requestAsPromise(
const { res } = await githubApiProvider.requestAsPromise({
request,
'/repos/rust-lang/rust',
{}
)
url: '/repos/rust-lang/rust',
})
expect(res.statusCode).to.equal(200)
headers.push(res.headers)
}

View File

@@ -38,6 +38,7 @@ class GithubApiProvider {
onTokenInvalidated = tokenString => {},
globalToken,
reserveFraction = 0.25,
tokenScopeNames = {},
}) {
Object.assign(this, {
baseUrl,
@@ -45,12 +46,14 @@ class GithubApiProvider {
onTokenInvalidated,
globalToken,
reserveFraction,
tokenScopeNames,
})
if (this.withPooling) {
this.standardTokens = new TokenPool({ batchSize: 25 })
this.searchTokens = new TokenPool({ batchSize: 5 })
this.graphqlTokens = new TokenPool({ batchSize: 25 })
this.packageScopedTokens = new TokenPool({ batchSize: 25 })
}
}
@@ -60,17 +63,41 @@ class GithubApiProvider {
standardTokens: this.standardTokens.serializeDebugInfo({ sanitize }),
searchTokens: this.searchTokens.serializeDebugInfo({ sanitize }),
graphqlTokens: this.graphqlTokens.serializeDebugInfo({ sanitize }),
packageScopedTokens: this.packageScopedTokens.serializeDebugInfo({
sanitize,
}),
}
} else {
return {}
}
}
addToken(tokenString) {
numReservedScopedTokens() {
return this.packageScopedTokens.count()
}
addReservedScopedToken(tokenString, data) {
if (!this.withPooling) {
throw Error('When not using a token pool, do not provide tokens')
}
const { scopes } = data
if (!scopes) {
throw new Error('Cannot add unscoped token to reserved token pools')
}
scopes.split('%20').forEach(scope => {
if (scope === this.tokenScopeNames.readPackages) {
this.packageScopedTokens.add(tokenString, data)
}
})
}
addToken(tokenString, data) {
if (this.withPooling) {
this.standardTokens.add(tokenString)
this.searchTokens.add(tokenString)
this.graphqlTokens.add(tokenString)
this.standardTokens.add(tokenString, data)
this.searchTokens.add(tokenString, data)
this.graphqlTokens.add(tokenString, data)
} else {
throw Error('When not using a token pool, do not provide tokens')
}
@@ -141,7 +168,11 @@ class GithubApiProvider {
this.onTokenInvalidated(token.id)
}
tokenForUrl(url) {
tokenForUrl(url, { needsPackageScope }) {
if (needsPackageScope) {
return this.packageScopedTokens.next()
}
if (url.startsWith('/search')) {
return this.searchTokens.next()
} else if (url.startsWith('/graphql')) {
@@ -154,14 +185,14 @@ class GithubApiProvider {
// Act like request(), but tweak headers and query to avoid hitting a rate
// limit. Inject `request` so we can pass in `cachingRequest` from
// `request-handler.js`.
request(request, url, options = {}, callback) {
request({ request, url, options = {}, neededScopes = {}, callback }) {
const { baseUrl } = this
let token
let tokenString
if (this.withPooling) {
try {
token = this.tokenForUrl(url)
token = this.tokenForUrl(url, neededScopes)
} catch (e) {
callback(e)
return
@@ -178,7 +209,6 @@ class GithubApiProvider {
baseUrl,
headers: {
'User-Agent': userAgent,
Accept: 'application/vnd.github.v3+json',
Authorization: `token ${tokenString}`,
...options.headers,
},
@@ -199,14 +229,20 @@ class GithubApiProvider {
})
}
requestAsPromise(request, url, options) {
requestAsPromise({ request, url, options, neededScopes }) {
return new Promise((resolve, reject) => {
this.request(request, url, options, (err, res, buffer) => {
if (err) {
reject(err)
} else {
resolve({ res, buffer })
}
this.request({
request,
url,
options,
neededScopes,
callback: (err, res, buffer) => {
if (err) {
reject(err)
} else {
resolve({ res, buffer })
}
},
})
})
}

View File

@@ -6,7 +6,11 @@ describe('Github API provider', function () {
const baseUrl = 'https://github-api.example.com'
const reserveFraction = 0.333
let mockStandardToken, mockSearchToken, mockGraphqlToken, provider
let mockStandardToken,
mockSearchToken,
mockGraphqlToken,
mockPackagesScopedToken,
provider
beforeEach(function () {
provider = new GithubApiProvider({ baseUrl, reserveFraction })
@@ -18,6 +22,11 @@ describe('Github API provider', function () {
mockGraphqlToken = { update: sinon.spy(), invalidate: sinon.spy() }
sinon.stub(provider.graphqlTokens, 'next').returns(mockGraphqlToken)
mockPackagesScopedToken = { update: sinon.spy(), invalidate: sinon.spy() }
sinon
.stub(provider.packageScopedTokens, 'next')
.returns(mockPackagesScopedToken)
})
context('a search API request', function () {
@@ -25,12 +34,16 @@ describe('Github API provider', function () {
callback()
}
it('should obtain an appropriate token', function (done) {
provider.request(mockRequest, '/search', {}, (err, res, buffer) => {
expect(err).to.be.undefined
expect(provider.searchTokens.next).to.have.been.calledOnce
expect(provider.standardTokens.next).not.to.have.been.called
expect(provider.graphqlTokens.next).not.to.have.been.called
done()
provider.request({
request: mockRequest,
url: '/search',
callback: (err, res, buffer) => {
expect(err).to.be.undefined
expect(provider.searchTokens.next).to.have.been.calledOnce
expect(provider.standardTokens.next).not.to.have.been.called
expect(provider.graphqlTokens.next).not.to.have.been.called
done()
},
})
})
})
@@ -40,12 +53,37 @@ describe('Github API provider', function () {
callback()
}
it('should obtain an appropriate token', function (done) {
provider.request(mockRequest, '/graphql', {}, (err, res, buffer) => {
expect(err).to.be.undefined
expect(provider.searchTokens.next).not.to.have.been.called
expect(provider.standardTokens.next).not.to.have.been.called
expect(provider.graphqlTokens.next).to.have.been.calledOnce
done()
provider.request({
request: mockRequest,
url: '/graphql',
callback: (err, res, buffer) => {
expect(err).to.be.undefined
expect(provider.searchTokens.next).not.to.have.been.called
expect(provider.standardTokens.next).not.to.have.been.called
expect(provider.graphqlTokens.next).to.have.been.calledOnce
done()
},
})
})
})
context('a request requiring the read:packages scope', function () {
const mockRequest = (options, callback) => {
callback()
}
it('should obtain an appropriate token', function (done) {
provider.request({
request: mockRequest,
url: '/graphql',
neededScopes: { needsPackageScope: true },
callback: (err, res, buffer) => {
expect(err).to.be.undefined
expect(provider.searchTokens.next).not.to.have.been.called
expect(provider.standardTokens.next).not.to.have.been.called
expect(provider.graphqlTokens.next).not.to.have.been.called
expect(provider.packageScopedTokens.next).to.have.been.calledOnce
done()
},
})
})
})
@@ -55,12 +93,16 @@ describe('Github API provider', function () {
callback()
}
it('should obtain an appropriate token', function (done) {
provider.request(mockRequest, '/repo', {}, (err, res, buffer) => {
expect(err).to.be.undefined
expect(provider.searchTokens.next).not.to.have.been.called
expect(provider.standardTokens.next).to.have.been.calledOnce
expect(provider.graphqlTokens.next).not.to.have.been.called
done()
provider.request({
request: mockRequest,
url: '/repo',
callback: (err, res, buffer) => {
expect(err).to.be.undefined
expect(provider.searchTokens.next).not.to.have.been.called
expect(provider.standardTokens.next).to.have.been.calledOnce
expect(provider.graphqlTokens.next).not.to.have.been.called
done()
},
})
})
})
@@ -84,25 +126,33 @@ describe('Github API provider', function () {
}
it('should invoke the callback', function (done) {
provider.request(mockRequest, '/foo', {}, (err, res, buffer) => {
expect(err).to.equal(null)
expect(Object.is(res, mockResponse)).to.be.true
expect(Object.is(buffer, mockBuffer)).to.be.true
done()
provider.request({
request: mockRequest,
url: '/foo',
callback: (err, res, buffer) => {
expect(err).to.equal(null)
expect(Object.is(res, mockResponse)).to.be.true
expect(Object.is(buffer, mockBuffer)).to.be.true
done()
},
})
})
it('should update the token with the expected values', function (done) {
provider.request(mockRequest, '/foo', {}, (err, res, buffer) => {
expect(err).to.equal(null)
const expectedUsesRemaining =
remaining - Math.ceil(reserveFraction * rateLimit)
expect(mockStandardToken.update).to.have.been.calledWith(
expectedUsesRemaining,
nextReset
)
expect(mockStandardToken.invalidate).not.to.have.been.called
done()
provider.request({
request: mockRequest,
url: '/foo',
callback: (err, res, buffer) => {
expect(err).to.equal(null)
const expectedUsesRemaining =
remaining - Math.ceil(reserveFraction * rateLimit)
expect(mockStandardToken.update).to.have.been.calledWith(
expectedUsesRemaining,
nextReset
)
expect(mockStandardToken.invalidate).not.to.have.been.called
done()
},
})
})
})
@@ -132,25 +182,33 @@ describe('Github API provider', function () {
}
it('should invoke the callback', function (done) {
provider.request(mockRequest, '/graphql', {}, (err, res, buffer) => {
expect(err).to.equal(null)
expect(Object.is(res, mockResponse)).to.be.true
expect(Object.is(buffer, mockBuffer)).to.be.true
done()
provider.request({
request: mockRequest,
url: '/graphql',
callback: (err, res, buffer) => {
expect(err).to.equal(null)
expect(Object.is(res, mockResponse)).to.be.true
expect(Object.is(buffer, mockBuffer)).to.be.true
done()
},
})
})
it('should update the token with the expected values', function (done) {
provider.request(mockRequest, '/graphql', {}, (err, res, buffer) => {
expect(err).to.equal(null)
const expectedUsesRemaining =
remaining - Math.ceil(reserveFraction * rateLimit)
expect(mockGraphqlToken.update).to.have.been.calledWith(
expectedUsesRemaining,
nextReset
)
expect(mockGraphqlToken.invalidate).not.to.have.been.called
done()
provider.request({
request: mockRequest,
url: '/graphql',
callback: (err, res, buffer) => {
expect(err).to.equal(null)
const expectedUsesRemaining =
remaining - Math.ceil(reserveFraction * rateLimit)
expect(mockGraphqlToken.update).to.have.been.calledWith(
expectedUsesRemaining,
nextReset
)
expect(mockGraphqlToken.invalidate).not.to.have.been.called
done()
},
})
})
})
@@ -164,11 +222,15 @@ describe('Github API provider', function () {
}
it('should invoke the callback and update the token with the expected values', function (done) {
provider.request(mockRequest, '/foo', {}, (err, res, buffer) => {
expect(err).to.equal(null)
expect(mockStandardToken.invalidate).to.have.been.calledOnce
expect(mockStandardToken.update).not.to.have.been.called
done()
provider.request({
request: mockRequest,
url: '/foo',
callback: (err, res, buffer) => {
expect(err).to.equal(null)
expect(mockStandardToken.invalidate).to.have.been.calledOnce
expect(mockStandardToken.update).not.to.have.been.called
done()
},
})
})
})
@@ -180,10 +242,14 @@ describe('Github API provider', function () {
}
it('should pass the error to the callback', function (done) {
provider.request(mockRequest, '/foo', {}, (err, res, buffer) => {
expect(err).to.be.an.instanceof(Error)
expect(err.message).to.equal('connection timeout')
done()
provider.request({
request: mockRequest,
url: '/foo',
callback: (err, res, buffer) => {
expect(err).to.be.an.instanceof(Error)
expect(err.message).to.equal('connection timeout')
done()
},
})
})
})

View File

@@ -2,21 +2,22 @@ import gql from 'graphql-tag'
import { mergeQueries } from '../../core/base-service/graphql.js'
import { BaseGraphqlService, BaseJsonService } from '../index.js'
function createRequestFetcher(context, config) {
function createRequestFetcher(context, config, neededScopes) {
const { sendAndCacheRequestWithCallbacks, githubApiProvider } = context
return async (url, options) =>
githubApiProvider.requestAsPromise(
sendAndCacheRequestWithCallbacks,
githubApiProvider.requestAsPromise({
request: sendAndCacheRequestWithCallbacks,
url,
options
)
options,
neededScopes,
})
}
class GithubAuthV3Service extends BaseJsonService {
constructor(context, config) {
constructor(context, config, neededScopes) {
super(context, config)
this._requestFetcher = createRequestFetcher(context, config)
this._requestFetcher = createRequestFetcher(context, config, neededScopes)
this.staticAuthConfigured = true
}
}
@@ -27,10 +28,10 @@ class GithubAuthV3Service extends BaseJsonService {
// useful when consuming GitHub endpoints which are not rate-limited: it
// avoids wasting API quota on them in production.
class ConditionalGithubAuthV3Service extends BaseJsonService {
constructor(context, config) {
constructor(context, config, neededScopes) {
super(context, config)
if (context.githubApiProvider.globalToken) {
this._requestFetcher = createRequestFetcher(context, config)
this._requestFetcher = createRequestFetcher(context, config, neededScopes)
this.staticAuthConfigured = true
} else {
this.staticAuthConfigured = false
@@ -39,9 +40,9 @@ class ConditionalGithubAuthV3Service extends BaseJsonService {
}
class GithubAuthV4Service extends BaseGraphqlService {
constructor(context, config) {
constructor(context, config, neededScopes) {
super(context, config)
this._requestFetcher = createRequestFetcher(context, config)
this._requestFetcher = createRequestFetcher(context, config, neededScopes)
this.staticAuthConfigured = true
}

View File

@@ -0,0 +1,90 @@
import Joi from 'joi'
import { expect } from 'chai'
import sinon from 'sinon'
import { GithubAuthV3Service } from './github-auth-service.js'
import GithubApiProvider from './github-api-provider.js'
describe('GithubAuthV3Service', function () {
class DummyGithubAuthV3Service extends GithubAuthV3Service {
static category = 'build'
static route = { base: 'runs' }
async handle() {
const { requiredString } = await this._requestJson({
schema: Joi.object({
requiredString: Joi.string().required(),
}).required(),
url: 'https://github-api.example.com/repos/badges/shields/check-runs',
options: {
headers: {
Accept: 'application/vnd.github.antiope-preview+json',
},
},
})
return { message: requiredString }
}
}
class ScopedDummyGithubAuthV3Service extends DummyGithubAuthV3Service {
constructor(context, config) {
super(context, config, { needsPackageScope: true })
}
}
let sendAndCacheRequestWithCallbacks, mockToken
const githubApiProvider = new GithubApiProvider({
baseUrl: 'https://github-api.example.com',
})
beforeEach(function () {
sendAndCacheRequestWithCallbacks = sinon.stub().returns(
Promise.resolve({
buffer: '{"requiredString": "some-string"}',
res: { statusCode: 200 },
})
)
mockToken = { id: 'abc123', update: sinon.mock(), invalidate: sinon.mock() }
})
afterEach(function () {
sinon.restore()
})
it('forwards custom Accept header', async function () {
sinon.stub(githubApiProvider.standardTokens, 'next').returns(mockToken)
DummyGithubAuthV3Service.invoke({
sendAndCacheRequestWithCallbacks,
githubApiProvider,
})
expect(sendAndCacheRequestWithCallbacks).to.have.been.calledOnceWith({
headers: {
'User-Agent': 'Shields.io/2003a',
Accept: 'application/vnd.github.antiope-preview+json',
Authorization: 'token abc123',
},
url: 'https://github-api.example.com/repos/badges/shields/check-runs',
baseUrl: 'https://github-api.example.com',
})
})
it('uses token with correct read scope', function () {
sinon.stub(githubApiProvider.packageScopedTokens, 'next').returns(mockToken)
ScopedDummyGithubAuthV3Service.invoke({
sendAndCacheRequestWithCallbacks,
githubApiProvider,
})
expect(sendAndCacheRequestWithCallbacks).to.have.been.calledOnceWith({
headers: {
'User-Agent': 'Shields.io/2003a',
Accept: 'application/vnd.github.antiope-preview+json',
Authorization: 'token abc123',
},
url: 'https://github-api.example.com/repos/badges/shields/check-runs',
baseUrl: 'https://github-api.example.com',
})
})
})

View File

@@ -1,22 +1,28 @@
import gql from 'graphql-tag'
import Joi from 'joi'
import { InvalidResponse } from '../index.js'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import { errorMessagesFor, documentation } from './github-helpers.js'
import { GithubAuthV4Service } from './github-auth-service.js'
import { transformErrors, documentation } from './github-helpers.js'
const schema = Joi.array()
.items(
Joi.object({
total: nonNegativeInteger,
})
)
.required()
const schema = Joi.object({
data: Joi.object({
repository: Joi.object({
object: Joi.object({
history: Joi.object({
totalCount: nonNegativeInteger,
}).required(),
}),
}).required(),
}).required(),
}).required()
export default class GithubCommitActivity extends GithubAuthV3Service {
export default class GitHubCommitActivity extends GithubAuthV4Service {
static category = 'activity'
static route = {
base: 'github/commit-activity',
pattern: ':interval(y|m|4w|w)/:user/:repo',
pattern: ':interval(y|m|4w|w)/:user/:repo/:branch*',
}
static examples = [
@@ -29,6 +35,20 @@ export default class GithubCommitActivity extends GithubAuthV3Service {
keywords: ['commits'],
documentation,
},
{
title: 'GitHub commit activity (branch)',
// Override the pattern to omit the deprecated interval "4w".
pattern: ':interval(y|m|w)/:user/:repo/:branch*',
namedParams: {
interval: 'm',
user: 'badges',
repo: 'squint',
branch: 'main',
},
staticPreview: this.render({ interval: 'm', commitCount: 5 }),
keywords: ['commits'],
documentation,
},
]
static defaultBadgeData = { label: 'commit activity', color: 'blue' }
@@ -46,47 +66,67 @@ export default class GithubCommitActivity extends GithubAuthV3Service {
}
}
static transform({ interval, weekData }) {
const weekTotals = weekData.map(({ total }) => total)
if (interval === 'm') {
// To approximate the value for the past month, get the sum for the last
// four weeks and add a weighted value for the fifth week.
const fourWeeksValue = weekTotals
.slice(-4)
.reduce((sum, weekTotal) => sum + weekTotal, 0)
const fifthWeekValue = weekTotals.slice(-5)[0]
const averageWeeksPerMonth = 365 / 12 / 7
return (
fourWeeksValue + Math.round((averageWeeksPerMonth - 4) * fifthWeekValue)
)
}
let wantedWeekData
switch (interval) {
case 'y':
wantedWeekData = weekTotals
break
case '4w':
wantedWeekData = weekTotals.slice(-4)
break
case 'w':
wantedWeekData = weekTotals.slice(-2, -1)
break
default:
throw Error('Unhandled case')
}
return wantedWeekData.reduce((sum, weekTotal) => sum + weekTotal, 0)
async fetch({ interval, user, repo, branch = 'HEAD' }) {
const since = this.constructor.getIntervalQueryStartDate({ interval })
return this._requestGraphql({
query: gql`
query (
$user: String!
$repo: String!
$branch: String!
$since: GitTimestamp!
) {
repository(owner: $user, name: $repo) {
object(expression: $branch) {
... on Commit {
history(since: $since) {
totalCount
}
}
}
}
}
`,
variables: {
user,
repo,
branch,
since,
},
schema,
transformErrors,
})
}
async handle({ interval, user, repo }) {
const weekData = await this._requestJson({
url: `/repos/${user}/${repo}/stats/commit_activity`,
schema,
errorMessages: errorMessagesFor(),
})
const commitCount = this.constructor.transform({ interval, weekData })
static transform({ data }) {
const {
repository: { object: repo },
} = data
if (!repo) {
throw new InvalidResponse({ prettyMessage: 'invalid branch' })
}
return repo.history.totalCount
}
static getIntervalQueryStartDate({ interval }) {
const now = new Date()
if (interval === 'y') {
now.setUTCFullYear(now.getUTCFullYear() - 1)
} else if (interval === 'm' || interval === '4w') {
now.setUTCDate(now.getUTCDate() - 30)
} else {
now.setUTCDate(now.getUTCDate() - 7)
}
return now.toISOString()
}
async handle({ interval, user, repo, branch }) {
const json = await this.fetch({ interval, user, repo, branch })
const commitCount = this.constructor.transform(json)
return this.constructor.render({ interval, commitCount })
}
}

View File

@@ -0,0 +1,64 @@
import { expect } from 'chai'
import sinon from 'sinon'
import { InvalidResponse } from '../index.js'
import GitHubCommitActivity from './github-commit-activity.service.js'
describe('GitHubCommitActivity', function () {
describe('transform', function () {
it('throws InvalidResponse on invalid branch and null object', function () {
expect(() =>
GitHubCommitActivity.transform({
data: { repository: { object: null } },
})
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'invalid branch')
})
})
describe('getIntervalQueryStartDate', function () {
/** @type {sinon.SinonFakeTimers} */
let clock
beforeEach(function () {
clock = sinon.useFakeTimers()
})
afterEach(function () {
clock.restore()
})
it('provides correct value for yearly interval', function () {
clock.tick(new Date('2021-08-28T02:21:34.000Z').getTime())
expect(
GitHubCommitActivity.getIntervalQueryStartDate({
interval: 'y',
})
).to.equal('2020-08-28T02:21:34.000Z')
})
it('provides correct value for simple monthly interval', function () {
clock.tick(new Date('2021-03-31T02:21:34.000Z').getTime())
expect(
GitHubCommitActivity.getIntervalQueryStartDate({
interval: 'm',
})
).to.equal('2021-03-01T02:21:34.000Z')
})
it('provides correct value for fun monthly interval', function () {
clock.tick(new Date('2021-03-07T02:21:34.000Z').getTime())
expect(
GitHubCommitActivity.getIntervalQueryStartDate({
interval: '4w',
})
).to.equal('2021-02-05T02:21:34.000Z')
})
it('provides correct value for weekly interval', function () {
clock.tick(new Date('2021-12-31T23:59:34.000Z').getTime())
expect(
GitHubCommitActivity.getIntervalQueryStartDate({
interval: 'w',
})
).to.equal('2021-12-24T23:59:34.000Z')
})
})
})

View File

@@ -33,6 +33,13 @@ t.create('commit activity (1 week)').get('/w/eslint/eslint.json').expectBadge({
message: isCommitActivity,
})
t.create('commit activity (custom branch)')
.get('/y/badges/squint/main.json')
.expectBadge({
label: 'commit activity',
message: isCommitActivity,
})
t.create('commit activity (repo not found)')
.get('/w/badges/helmets.json')
.expectBadge({

View File

@@ -5,6 +5,11 @@ import GithubApiProvider from './github-api-provider.js'
import { setRoutes as setAdminRoutes } from './auth/admin.js'
import { setRoutes as setAcceptorRoutes } from './auth/acceptor.js'
const readPackagesScope = 'read:packages'
// Multiple scopes need to be uri-encoded space delimited
const tokenScopes = `${readPackagesScope}`
const persistenceScopeDelimiter = '.scopes.'
// Convenience class with all the stuff related to the Github API and its
// authorization tokens, to simplify server initialization.
class GithubConstellation {
@@ -24,6 +29,8 @@ class GithubConstellation {
this._debugEnabled = config.service.debug.enabled
this._debugIntervalSeconds = config.service.debug.intervalSeconds
this.shieldsSecret = config.private.shields_secret
this._tokenScopes = {}
this._maxNumReservedScopedTokens = 0
const { redis_url: redisUrl, gh_token: globalToken } = config.private
if (redisUrl) {
@@ -38,6 +45,9 @@ class GithubConstellation {
baseUrl: process.env.GITHUB_URL || 'https://api.github.com',
globalToken,
withPooling: !globalToken,
tokenScopeNames: {
readPackages: readPackagesScope,
},
onTokenInvalidated: tokenString => this.onTokenInvalidated(tokenString),
})
@@ -70,8 +80,21 @@ class GithubConstellation {
log.error(e)
}
// Reserve a subset of scoped tokens from the total set
// to be used for queries which require an explicit scope,
// while leaving a sufficient amount of tokens (scoped or unscoped)
// for the bulk of our requests which don't care about scopes.
this._maxNumReservedScopedTokens = Math.floor(tokens.length * 0.15)
tokens.forEach(tokenString => {
this.apiProvider.addToken(tokenString)
const [token, scopes] = tokenString.split(persistenceScopeDelimiter)
this._tokenScopes[token] = scopes || null
const data = { scopes }
const numReserved = this.apiProvider.numReservedScopedTokens()
if (scopes && numReserved < this._maxNumReservedScopedTokens) {
this.apiProvider.addReservedScopedToken(token, data)
} else {
this.apiProvider.addToken(token, data)
}
})
const { shieldsSecret, apiProvider } = this
@@ -81,19 +104,53 @@ class GithubConstellation {
setAcceptorRoutes({
server,
authHelper: this.oauthHelper,
onTokenAccepted: tokenString => this.onTokenAdded(tokenString),
tokenScopes,
onTokenAccepted: tokenString =>
this.onTokenAdded(tokenString, tokenScopes),
})
}
}
onTokenAdded(tokenString) {
onTokenAdded(tokenString, tokenScopes) {
if (!this.persistence) {
throw Error('Token persistence is not configured')
}
this.apiProvider.addToken(tokenString)
const data = { scopes: tokenScopes }
const numReserved = this.apiProvider.numReservedScopedTokens()
if (numReserved < this._maxNumReservedScopedTokens) {
this.apiProvider.addReservedScopedToken(tokenString, data)
} else {
this.apiProvider.addToken(tokenString, data)
}
process.nextTick(async () => {
try {
await this.persistence.noteTokenAdded(tokenString)
// To avoid having multiple set entries for re-authorized/re-scoped
// tokens we need to first remove the previous entry that had different scopes
if (
Object.prototype.hasOwnProperty.call(this._tokenScopes, tokenString)
) {
const currentScopes = this._tokenScopes[tokenString]
// These scopes shouldn't match in practice, as that would
// indicate the function has somehow been invoked with an existing
// token but without any scope changes. Nevertheless, the conditional
// guard is here in case there are circumstances that assumption fails
// to be upheld.
if (currentScopes !== tokenScopes) {
const token = currentScopes
? `${tokenString}${persistenceScopeDelimiter}${currentScopes}`
: tokenString
await this.persistence.noteTokenRemoved(token)
}
}
// It's unlikely that we'd evert revert back to no longer requesting any scopes
// but handling that scenario regardless so we don't end up
// with junk like `abc123.scopes.undefined` in redis
const token = tokenScopes
? `${tokenString}${persistenceScopeDelimiter}${tokenScopes}`
: tokenString
await this.persistence.noteTokenAdded(token)
this._tokenScopes[tokenString] = tokenScopes || null
} catch (e) {
log.error(e)
}
@@ -104,7 +161,12 @@ class GithubConstellation {
if (this.persistence) {
process.nextTick(async () => {
try {
await this.persistence.noteTokenRemoved(tokenString)
const scopes = this._tokenScopes[tokenString]
const token = scopes
? `${tokenString}${persistenceScopeDelimiter}${scopes}`
: tokenString
await this.persistence.noteTokenRemoved(token)
delete this._tokenScopes[tokenString]
} catch (e) {
log.error(e)
}

View File

@@ -0,0 +1,208 @@
import { expect } from 'chai'
import sinon from 'sinon'
import log from '../../core/server/log.js'
import RedisTokenPersistence from '../../core/token-pooling/redis-token-persistence.js'
import GithubConstellation from './github-constellation.js'
import GithubApiProvider from './github-api-provider.js'
describe('GithubConstellation', function () {
const tokens = [
'abc123',
'def4567.scopes.read:packages%20read:user',
'def789.scopes.read:packages',
'ghi012',
'fff444.scopes.read:packages',
'555eee.scopes.read:packages',
'ddd666',
'777ccc',
'bbb888',
'999aaa',
'000111.scopes.read:packages',
'222333.scopes.read:packages',
'111111',
'888888',
]
const config = {
private: {
redis_url: 'localhost',
},
service: {
debug: {
enabled: false,
},
},
}
const server = { ajax: { on: sinon.stub() } }
beforeEach(function () {
sinon.stub(log, 'log')
sinon
.stub(GithubConstellation, '_createOauthHelper')
.returns({ isConfigured: false })
sinon.stub(GithubConstellation.prototype, 'scheduleDebugLogging')
sinon.stub(RedisTokenPersistence.prototype, 'initialize').returns(tokens)
sinon.stub(RedisTokenPersistence.prototype, 'noteTokenAdded')
sinon.stub(RedisTokenPersistence.prototype, 'noteTokenRemoved')
sinon.spy(GithubApiProvider.prototype, 'addToken')
sinon.spy(GithubApiProvider.prototype, 'addReservedScopedToken')
})
afterEach(function () {
sinon.restore()
})
context('initialize', function () {
it('does not fetch tokens when pooling disabled', async function () {
const constellation = new GithubConstellation({
...config,
...{ private: { gh_token: 'secret' } },
})
await constellation.initialize(server)
expect(RedisTokenPersistence.prototype.initialize).not.to.have.been.called
})
it('loads both scoped and unscoped tokens', async function () {
const constellation = new GithubConstellation(config)
await constellation.initialize(server)
expect(constellation.apiProvider.graphqlTokens.count()).to.equal(12)
expect(constellation.apiProvider.searchTokens.count()).to.equal(12)
expect(constellation.apiProvider.standardTokens.count()).to.equal(12)
expect(constellation.apiProvider.packageScopedTokens.count()).to.equal(2)
expect(
GithubApiProvider.prototype.addReservedScopedToken
).to.be.calledWithExactly('def4567', {
scopes: 'read:packages%20read:user',
})
expect(
GithubApiProvider.prototype.addReservedScopedToken
).to.be.calledWithExactly('def789', {
scopes: 'read:packages',
})
})
})
context('onTokenAdded', function () {
it('adds new scoped token with met reserves', async function () {
const token = 'shh_secret'
sinon
.stub(GithubApiProvider.prototype, 'numReservedScopedTokens')
.returns(2)
const clock = sinon.useFakeTimers()
const constellation = new GithubConstellation(config)
await constellation.initialize(server)
constellation._maxNumReservedScopedTokens = 2
constellation.onTokenAdded(token, 'read:packages')
await clock.tickAsync()
expect(GithubApiProvider.prototype.addToken).to.be.calledWithExactly(
token,
{ scopes: 'read:packages' }
)
expect(
GithubApiProvider.prototype.addReservedScopedToken
).to.not.be.calledWith(token)
expect(RedisTokenPersistence.prototype.noteTokenAdded).to.be.calledWith(
`${token}.scopes.read:packages`
)
expect(RedisTokenPersistence.prototype.noteTokenRemoved).to.not.be.called
expect(Object.keys(constellation._tokenScopes).length).to.equal(15)
expect(constellation._tokenScopes[token]).to.equal('read:packages')
})
it('adds new scoped token with unmet reserves', async function () {
const token = 'shh_secret'
sinon
.stub(GithubApiProvider.prototype, 'numReservedScopedTokens')
.returns(2)
const clock = sinon.useFakeTimers()
const constellation = new GithubConstellation(config)
await constellation.initialize(server)
constellation._maxNumReservedScopedTokens = 3
constellation.onTokenAdded(token, 'read:packages')
await clock.tickAsync()
expect(
GithubApiProvider.prototype.addReservedScopedToken
).to.be.calledWithExactly(token, { scopes: 'read:packages' })
expect(GithubApiProvider.prototype.addToken).to.not.be.calledWith(token)
expect(RedisTokenPersistence.prototype.noteTokenAdded).to.be.calledWith(
`${token}.scopes.read:packages`
)
expect(RedisTokenPersistence.prototype.noteTokenRemoved).to.not.be.called
expect(Object.keys(constellation._tokenScopes).length).to.equal(15)
expect(constellation._tokenScopes[token]).to.equal('read:packages')
})
it('adds new unscoped token', async function () {
const token = '1234567890987654321'
const clock = sinon.useFakeTimers()
const constellation = new GithubConstellation(config)
await constellation.initialize(server)
constellation.onTokenAdded(token)
await clock.tickAsync()
expect(GithubApiProvider.prototype.addToken).to.be.calledWithExactly(
token,
{ scopes: undefined }
)
expect(
GithubApiProvider.prototype.addReservedScopedToken
).to.not.be.calledWith(token)
expect(RedisTokenPersistence.prototype.noteTokenAdded).to.be.calledWith(
token
)
expect(RedisTokenPersistence.prototype.noteTokenRemoved).to.not.be.called
expect(Object.keys(constellation._tokenScopes).length).to.equal(15)
expect(constellation._tokenScopes[token]).to.equal(null)
})
it('updates scopes on existing token', async function () {
const existingToken = 'abc123'
const clock = sinon.useFakeTimers()
const constellation = new GithubConstellation(config)
await constellation.initialize(server)
sinon
.stub(GithubApiProvider.prototype, 'numReservedScopedTokens')
.returns(1)
constellation.onTokenAdded(existingToken, 'read:packages')
await clock.tickAsync()
expect(
GithubApiProvider.prototype.addReservedScopedToken
).to.be.calledWithExactly(existingToken, { scopes: 'read:packages' })
expect(GithubApiProvider.prototype.addToken.callCount).to.equal(12)
expect(RedisTokenPersistence.prototype.noteTokenAdded).to.be.calledWith(
`${existingToken}.scopes.read:packages`
)
expect(RedisTokenPersistence.prototype.noteTokenRemoved).to.be.calledWith(
existingToken
)
expect(Object.keys(constellation._tokenScopes).length).to.equal(14)
expect(constellation._tokenScopes[existingToken]).to.equal(
'read:packages'
)
})
})
context('onTokenInvalidated', function () {
it('removes scoped token', async function () {
const clock = sinon.useFakeTimers()
const constellation = new GithubConstellation(config)
await constellation.initialize(server)
constellation.onTokenInvalidated('def789')
await clock.tickAsync()
expect(RedisTokenPersistence.prototype.noteTokenRemoved).to.be.calledWith(
'def789.scopes.read:packages'
)
expect(Object.keys(constellation._tokenScopes).length).to.equal(13)
})
it('removes unscoped token', async function () {
const clock = sinon.useFakeTimers()
const constellation = new GithubConstellation(config)
await constellation.initialize(server)
constellation.onTokenInvalidated('888888')
await clock.tickAsync()
expect(
RedisTokenPersistence.prototype.noteTokenRemoved
).to.be.calledWithExactly('888888')
expect(Object.keys(constellation._tokenScopes).length).to.equal(13)
})
})
})

View File

@@ -14,12 +14,10 @@ t.create('Lerna version (independent)')
message: 'independent',
})
t.create('Lerna version (branch)')
.get('/facebook/jest/master.json')
.expectBadge({
label: 'lerna@master',
message: isSemver,
})
t.create('Lerna version (branch)').get('/facebook/jest/main.json').expectBadge({
label: 'lerna@main',
message: isSemver,
})
t.create('Lerna version (lerna.json missing)')
.get('/PyvesB/empty-repo.json')

View File

@@ -75,7 +75,7 @@ class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthV3Service
static route = {
base: 'github/package-json/dependency-version',
pattern:
':user/:repo/:kind(dev|peer)?/:scope(@[^/]+)?/:packageName/:branch*',
':user/:repo/:kind(dev|peer|optional)?/:scope(@[^/]+)?/:packageName/:branch*',
queryParamSchema: dependencyQueryParamSchema,
}
@@ -146,14 +146,18 @@ class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthV3Service
{ user, repo, kind, branch = 'HEAD', scope, packageName },
{ filename = 'package.json' }
) {
const { dependencies, devDependencies, peerDependencies } =
await fetchJsonFromRepo(this, {
schema: isPackageJsonWithDependencies,
user,
repo,
branch,
filename,
})
const {
dependencies,
devDependencies,
peerDependencies,
optionalDependencies,
} = await fetchJsonFromRepo(this, {
schema: isPackageJsonWithDependencies,
user,
repo,
branch,
filename,
})
const wantedDependency = scope ? `${scope}/${packageName}` : packageName
const { range } = getDependencyVersion({
@@ -162,6 +166,7 @@ class GithubPackageJsonDependencyVersion extends ConditionalGithubAuthV3Service
dependencies,
devDependencies,
peerDependencies,
optionalDependencies,
})
return this.constructor.render({

View File

@@ -47,6 +47,13 @@ t.create('Peer dependency version')
message: semverRange,
})
t.create('Optional dependency version')
.get('/dependency-version/IcedFrisby/IcedFrisby/optional/@hapi/joi.json')
.expectBadge({
label: '@hapi/joi',
message: semverRange,
})
t.create('Dev dependency version')
.get(
'/dependency-version/paulmelnikow/react-boxplot/dev/react.json?label=react%20tested'

View File

@@ -0,0 +1,19 @@
import { BaseJsonService } from '../index.js'
export default class GitLabBase extends BaseJsonService {
static auth = {
passKey: 'gitlab_token',
serviceKey: 'gitlab',
}
async fetch({ url, options, schema, errorMessages }) {
return this._requestJson(
this.authHelper.withBasicAuth({
schema,
url,
options,
errorMessages,
})
)
}
}

View File

@@ -33,7 +33,7 @@ t.create('Pipeline status (nonexistent repo)')
})
t.create('Pipeline status (custom gitlab URL)')
.get('/GNOME/pango/master.json?gitlab_url=https://gitlab.gnome.org')
.get('/GNOME/pango/main.json?gitlab_url=https://gitlab.gnome.org')
.expectBadge({
label: 'build',
message: isBuildStatus,

View File

@@ -0,0 +1,131 @@
import Joi from 'joi'
import { version as versionColor } from '../color-formatters.js'
import { optionalUrl } from '../validators.js'
import { latest } from '../version.js'
import { addv } from '../text-formatters.js'
import { NotFound } from '../index.js'
import GitLabBase from './gitlab-base.js'
const schema = Joi.array().items(
Joi.object({
name: Joi.string().required(),
})
)
const queryParamSchema = Joi.object({
gitlab_url: optionalUrl,
include_prereleases: Joi.equal(''),
sort: Joi.string().valid('date', 'semver').default('date'),
}).required()
export default class GitlabTag extends GitLabBase {
static category = 'version'
static route = {
base: 'gitlab/v/tag',
pattern: ':user/:repo',
queryParamSchema,
}
static examples = [
{
title: 'GitLab tag (latest by date)',
namedParams: {
user: 'shields-ops-group',
repo: 'tag-test',
},
queryParams: { sort: 'date' },
staticPreview: this.render({ version: 'v2.0.0' }),
},
{
title: 'GitLab tag (latest by SemVer)',
namedParams: {
user: 'shields-ops-group',
repo: 'tag-test',
},
queryParams: { sort: 'semver' },
staticPreview: this.render({ version: 'v4.0.0' }),
},
{
title: 'GitLab tag (latest by SemVer pre-release)',
namedParams: {
user: 'shields-ops-group',
repo: 'tag-test',
},
queryParams: {
sort: 'semver',
include_prereleases: null,
},
staticPreview: this.render({ version: 'v5.0.0-beta.1', sort: 'semver' }),
},
{
title: 'GitLab tag (custom instance)',
namedParams: {
user: 'GNOME',
repo: 'librsvg',
},
queryParams: {
sort: 'semver',
include_prereleases: null,
gitlab_url: 'https://gitlab.gnome.org',
},
staticPreview: this.render({ version: 'v2.51.4' }),
},
]
static defaultBadgeData = { label: 'tag' }
static render({ version, sort }) {
return {
message: addv(version),
color: sort === 'semver' ? versionColor(version) : 'blue',
}
}
async fetch({ user, repo, baseUrl }) {
// https://docs.gitlab.com/ee/api/tags.html
// N.B. the documentation has contradictory information about default sort order.
// As of 2020-10-11 the default is by date, but we add the `order_by` query param
// explicitly in case that changes upstream.
return super.fetch({
schema,
url: `${baseUrl}/api/v4/projects/${user}%2F${repo}/repository/tags`,
options: { qs: { order_by: 'updated' } },
errorMessages: {
404: 'repo not found',
},
})
}
static transform({ tags, sort, includePrereleases }) {
if (tags.length === 0) {
throw new NotFound({ prettyMessage: 'no tags found' })
}
if (sort === 'date') {
return tags[0].name
}
return latest(
tags.map(t => t.name),
{ pre: includePrereleases }
)
}
async handle(
{ user, repo },
{
gitlab_url: baseUrl = 'https://gitlab.com',
include_prereleases: pre,
sort,
}
) {
const tags = await this.fetch({ user, repo, baseUrl })
const version = this.constructor.transform({
tags,
sort,
includePrereleases: pre !== undefined,
})
return this.constructor.render({ version, sort })
}
}

View File

@@ -0,0 +1,47 @@
import { expect } from 'chai'
import nock from 'nock'
import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
import GitLabTag from './gitlab-tag.service.js'
describe('GitLabTag', function () {
describe('auth', function () {
cleanUpNockAfterEach()
const fakeToken = 'abc123'
const config = {
public: {
services: {
gitlab: {
authorizedOrigins: ['https://gitlab.com'],
},
},
},
private: {
gitlab_token: fakeToken,
},
}
it('sends the auth information as configured', async function () {
const scope = nock('https://gitlab.com/')
.get('/api/v4/projects/foo%2Fbar/repository/tags?order_by=updated')
// This ensures that the expected credentials are actually being sent with the HTTP request.
// Without this the request wouldn't match and the test would fail.
.basicAuth({ user: '', pass: fakeToken })
.reply(200, [{ name: '1.9' }])
expect(
await GitLabTag.invoke(
defaultContext,
config,
{ user: 'foo', repo: 'bar' },
{}
)
).to.deep.equal({
message: 'v1.9',
color: 'blue',
})
scope.done()
})
})
})

View File

@@ -0,0 +1,27 @@
import { isSemver } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('Tag (latest by date)')
.get('/shields-ops-group/tag-test.json')
.expectBadge({ label: 'tag', message: 'v2.0.0', color: 'blue' })
t.create('Tag (latest by SemVer)')
.get('/shields-ops-group/tag-test.json?sort=semver')
.expectBadge({ label: 'tag', message: 'v4.0.0', color: 'blue' })
t.create('Tag (latest by SemVer pre-release)')
.get('/shields-ops-group/tag-test.json?sort=semver&include_prereleases')
.expectBadge({ label: 'tag', message: 'v5.0.0-beta.1', color: 'orange' })
t.create('Tag (custom instance')
.get('/GNOME/librsvg.json?gitlab_url=https://gitlab.gnome.org')
.expectBadge({ label: 'tag', message: isSemver, color: 'blue' })
t.create('Tag (repo not found)')
.get('/fdroid/nonexistant.json')
.expectBadge({ label: 'tag', message: 'repo not found' })
t.create('Tag (no tags)')
.get('/fdroid/fdroiddata.json')
.expectBadge({ label: 'tag', message: 'no tags found' })

View File

@@ -12,15 +12,11 @@ export default class JenkinsBase extends BaseJsonService {
schema,
qs,
errorMessages = { 404: 'instance or job not found' },
disableStrictSSL,
}) {
return this._requestJson(
this.authHelper.withBasicAuth({
url,
options: {
qs,
strictSSL: disableStrictSSL === undefined,
},
options: { qs },
schema,
errorMessages,
})

View File

@@ -68,12 +68,11 @@ export default class JenkinsBuild extends JenkinsBase {
return { status: colorStatusMap[json.color] }
}
async handle(namedParams, { jobUrl, disableStrictSSL }) {
async handle(namedParams, { jobUrl }) {
const json = await this.fetch({
url: buildUrl({ jobUrl, lastCompletedBuild: false }),
schema,
qs: buildTreeParamQueryString('color'),
disableStrictSSL,
})
const { status } = this.transform({ json })
return this.constructor.render({ status })

View File

@@ -1,10 +1,7 @@
import Joi from 'joi'
import { optionalUrl } from '../validators.js'
const queryParamSchema = Joi.object({
disableStrictSSL: Joi.equal(''),
jobUrl: optionalUrl,
}).required()
const queryParamSchema = Joi.object({ jobUrl: optionalUrl }).required()
const buildRedirectUrl = ({ protocol, host, job }) => {
const jobPrefix = job.indexOf('/') > -1 ? '' : 'job/'

View File

@@ -112,14 +112,13 @@ export default class JenkinsCoverage extends JenkinsBase {
}
}
async handle({ format }, { jobUrl, disableStrictSSL }) {
async handle({ format }, { jobUrl }) {
const { schema, transform, treeQueryParam, pluginSpecificPath } =
formatMap[format]
const json = await this.fetch({
url: buildUrl({ jobUrl, plugin: pluginSpecificPath }),
schema,
qs: buildTreeParamQueryString(treeQueryParam),
disableStrictSSL,
errorMessages: {
404: 'job or coverage not found',
},

View File

@@ -107,7 +107,6 @@ export default class JenkinsTests extends JenkinsBase {
async handle(
namedParams,
{
disableStrictSSL,
jobUrl,
compact_message: compactMessage,
passed_label: passedLabel,
@@ -119,7 +118,6 @@ export default class JenkinsTests extends JenkinsBase {
url: buildUrl({ jobUrl }),
schema,
qs: buildTreeParamQueryString('actions[failCount,skipCount,totalCount]'),
disableStrictSSL,
})
const { passed, failed, skipped, total } = this.transform({ json })
return this.constructor.render({

View File

@@ -30,10 +30,10 @@ const documentation = `
<p>
In order for this badge to work, the host of your room must allow guest accounts or dummy accounts to register, and the room must be world readable (chat history visible to anyone).
</br>
The following steps will show you how to setup the badge URL using the Riot.im Matrix client.
The following steps will show you how to setup the badge URL using the Element Matrix client.
</br>
<ul>
<li>Select the desired room inside the Riot.im client</li>
<li>Select the desired room inside the Element client</li>
<li>Click on the room settings button (gear icon) located near the top right of the client</li>
<li>Scroll to the very bottom of the settings page and look under the <code>Addresses</code> section</li>
<li>You should see one or more <code>room addresses (or aliases)</code>, which can be easily identified with their starting hash (<code>#</code>) character (ex: <code>#twim:matrix.org</code>)</li>

View File

@@ -25,11 +25,11 @@ class MyGetVersionService extends Version {
title: 'MyGet tenant',
pattern: ':tenant.myget/:feed/v/:packageName',
namedParams: {
tenant: 'cefsharp',
feed: 'cefsharp',
packageName: 'cef.sdk',
tenant: 'tizen',
feed: 'dotnet',
packageName: 'Tizen.NET',
},
staticPreview: this.render({ version: '91.1.1' }),
staticPreview: this.render({ version: '9.0.0.16564' }),
},
]
}
@@ -48,7 +48,7 @@ class MyGetDownloadService extends Downloads {
namedParams: {
tenant: 'cefsharp',
feed: 'cefsharp',
packageName: 'CefSharp',
packageName: 'CefSharp.Common',
},
staticPreview: this.render({ downloads: 9748 }),
},

View File

@@ -69,9 +69,9 @@ t.create('version (valid)')
})
t.create('version (tenant)')
.get('/cefsharp.myget/cefsharp/v/cef.sdk.json')
.get('/tizen.myget/dotnet/v/Tizen.NET.json')
.expectBadge({
label: 'cefsharp',
label: 'dotnet',
message: isVPlusDottedVersionNClausesWithOptionalSuffix,
})

View File

@@ -13,6 +13,7 @@ const isPackageJsonWithDependencies = Joi.object({
dependencies: isDependencyMap,
devDependencies: isDependencyMap,
peerDependencies: isDependencyMap,
optionalDependencies: isDependencyMap,
}).required()
function getDependencyVersion({
@@ -21,19 +22,19 @@ function getDependencyVersion({
dependencies,
devDependencies,
peerDependencies,
optionalDependencies,
}) {
let dependenciesOfKind
if (kind === 'peer') {
dependenciesOfKind = peerDependencies
} else if (kind === 'dev') {
dependenciesOfKind = devDependencies
} else if (kind === 'prod') {
dependenciesOfKind = dependencies
} else {
throw Error(`Not very kind: ${kind}`)
const dependencyMaps = {
peer: peerDependencies,
optional: optionalDependencies,
dev: devDependencies,
prod: dependencies,
}
const range = dependenciesOfKind[wantedDependency]
if (!(kind in dependencyMaps)) {
throw Error(`Not very kind: ${kind}`)
}
const range = dependencyMaps[kind][wantedDependency]
if (range === undefined) {
throw new InvalidParameter({
prettyMessage: `${kind} dependency not found`,

View File

@@ -1,12 +1,12 @@
import Joi from 'joi'
import { nonNegativeInteger } from '../validators.js'
import { anyInteger } from '../validators.js'
import { metric } from '../text-formatters.js'
import { BaseJsonService } from '../index.js'
const schema = Joi.object({
data: Joi.object({
link_karma: nonNegativeInteger,
comment_karma: nonNegativeInteger,
link_karma: anyInteger,
comment_karma: anyInteger,
}).required(),
}).required()

View File

@@ -17,7 +17,7 @@ export default class RequiresIo extends BaseJsonService {
{
title: 'Requires.io',
pattern: ':service/:user/:repo',
namedParams: { service: 'github', user: 'celery', repo: 'celery' },
namedParams: { service: 'github', user: 'zulip', repo: 'zulip' },
staticPreview: this.render({ status: 'up-to-date' }),
},
{
@@ -25,8 +25,8 @@ export default class RequiresIo extends BaseJsonService {
pattern: ':service/:user/:repo/:branch',
namedParams: {
service: 'github',
user: 'celery',
repo: 'celery',
user: 'zulip',
repo: 'zulip',
branch: 'master',
},
staticPreview: this.render({ status: 'up-to-date' }),

View File

@@ -7,14 +7,14 @@ const isRequireStatus = Joi.string().regex(
)
t.create('requirements (valid, without branch)')
.get('/github/celery/celery.json')
.get('/github/zulip/zulip.json')
.expectBadge({
label: 'requirements',
message: isRequireStatus,
})
t.create('requirements (valid, with branch)')
.get('/github/celery/celery/master.json')
.get('/github/zulip/zulip/master.json')
.expectBadge({
label: 'requirements',
message: isRequireStatus,

View File

@@ -2,16 +2,16 @@ import { isFileSize } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('EssentialsX (id 9089)')
.get('/9089.json')
t.create('EssentialsX (hosted resource)')
.get('/771.json')
.expectBadge({ label: 'size', message: isFileSize })
t.create('Pet Master (id 15904)').get('/15904.json').expectBadge({
t.create('Pet Master (external resource)').get('/15904.json').expectBadge({
lavel: 'size',
message: 'resource hosted externally',
})
t.create('Invalid Resource (id 1)').get('/1.json').expectBadge({
t.create('Invalid Resource').get('/1.json').expectBadge({
label: 'size',
message: 'not found',
})

View File

@@ -75,10 +75,10 @@ async function githubLicense(githubApiProvider, user, repo) {
let link = `https://github.com/${repoSlug}`
const { buffer } = await githubApiProvider.requestAsPromise(
const { buffer } = await githubApiProvider.requestAsPromise({
request,
`/repos/${repoSlug}/license`
)
url: `/repos/${repoSlug}/license`,
})
try {
const data = JSON.parse(buffer)
if ('html_url' in data) {

View File

@@ -93,7 +93,9 @@ const isPercentage = Joi.alternatives().try(
isDecimalPercentage
)
const isFileSize = withRegex(/^[0-9]*[.]?[0-9]+\s(B|kB|MB|GB|TB|PB|EB|ZB|YB)$/)
const isFileSize = withRegex(
/^[0-9]*[.]?[0-9]+\s(B|kB|KB|MB|GB|TB|PB|EB|ZB|YB)$/
)
const isFormattedDate = Joi.alternatives().try(
Joi.equal('today', 'yesterday'),

View File

@@ -0,0 +1,51 @@
import Joi from 'joi'
import { renderVersionBadge } from '../version.js'
import TwitchBase from './twitch-base.js'
const helixSchema = Joi.object({
data: Joi.array()
.items(Joi.object({ version: Joi.string().required() }).required())
.min(1)
.required(),
})
export default class TwitchExtensionVersion extends TwitchBase {
static category = 'version'
static route = {
base: 'twitch/extension/v',
pattern: ':extensionId',
}
static examples = [
{
title: 'Twitch Extension Version',
namedParams: {
extensionId: '2nq5cu1nc9f4p75b791w8d3yo9d195',
},
staticPreview: renderVersionBadge({ version: '1.0.0' }),
},
]
static defaultBadgeData = {
label: 'twitch extension',
}
async fetch({ extensionId }) {
const data = this._requestJson({
schema: helixSchema,
url: `https://api.twitch.tv/helix/extensions/released`,
options: {
qs: { extension_id: extensionId },
},
})
return data
}
async handle({ extensionId }) {
const data = await this.fetch({ extensionId })
return renderVersionBadge({ version: data.data[0].version })
}
}

View File

@@ -0,0 +1,16 @@
import { isSemver } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
import { noToken } from '../test-helpers.js'
import _noTwitchToken from './twitch.service.js'
const noTwitchToken = noToken(_noTwitchToken)
export const t = await createServiceTester()
t.create('gets the released version of Schedule with Google Calendar')
.skipWhen(noTwitchToken)
.get('/2nq5cu1nc9f4p75b791w8d3yo9d195.json')
.expectBadge({ label: 'twitch extension', message: isSemver })
t.create('invalid extension id')
.skipWhen(noTwitchToken)
.get('/will-never-exist.json')
.expectBadge({ label: 'twitch extension', message: 'not found' })

View File

@@ -40,6 +40,13 @@ export default class VisualStudioMarketplaceRating extends VisualStudioMarketpla
}
static render({ format, averageRating, ratingCount }) {
if (ratingCount < 1) {
return {
message: 'no ratings',
color: 'inactive',
}
}
const message =
format === 'r'
? `${averageRating.toFixed(1)}/5 (${ratingCount})`

View File

@@ -83,8 +83,8 @@ t.create('zero rating')
)
.expectBadge({
label: 'rating',
message: '0.0/5 (0)',
color: 'red',
message: 'no ratings',
color: 'lightgrey',
})
t.create('stars')

View File

@@ -12,12 +12,12 @@ t.create('status of https://shields.io')
.expectBadge({ label: 'website', message: 'up', color: 'brightgreen' })
t.create('status of nonexistent domain')
.get('/website.json?url=http://shields.io.io')
.get('/website.json?url=https://shields.io.io')
.timeout(15000)
.expectBadge({ label: 'website', message: 'down', color: 'red' })
t.create('status when network is off')
.get('/website.json?url=http://shields.io')
.get('/website.json?url=https://shields.io')
.networkOff()
.expectBadge({ label: 'website', message: 'down', color: 'red' })

View File

@@ -29,4 +29,4 @@ As you can see below, without increasing the footprint of these badges, I've tri
![Badge design](proportions.png)
This badge design corresponds to an old and now deprecated version which has since been replaced by beautiful and scalable SVG versions that can be found on [shields.io](http://shields.io)
This badge design corresponds to an old and now deprecated version which has since been replaced by beautiful and scalable SVG versions that can be found on [shields.io](https://shields.io)