Compare commits

..

38 Commits

Author SHA1 Message Date
github-actions[bot]
0054a4c30b Changelog for Release server-2021-10-04 (#7104)
* Update Changelog

* Update CHANGELOG.md

Co-authored-by: release[bot] <actions@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2021-10-04 19:46:11 +01:00
Caleb Cartwright
42a9ca6444 feat: add 2021 support to GitHub Hacktoberfest (#7086)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-04 15:00:14 +00:00
dependabot[bot]
4ad822c42e chore(deps-dev): bump @typescript-eslint/parser from 4.30.0 to 4.32.0 (#7097)
Bumps [@typescript-eslint/parser](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/parser) from 4.30.0 to 4.32.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.32.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-10-02 16:17:05 +00:00
dependabot[bot]
a3601c9b3e chore(deps-dev): bump mocha from 9.1.1 to 9.1.2 (#7099)
Bumps [mocha](https://github.com/mochajs/mocha) from 9.1.1 to 9.1.2.
- [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.1...v9.1.2)

---
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-10-02 15:58:18 +00:00
dependabot[bot]
1593793c27 chore(deps): bump decamelize from 5.0.0 to 6.0.0 (#7098)
Bumps [decamelize](https://github.com/sindresorhus/decamelize) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/sindresorhus/decamelize/releases)
- [Commits](https://github.com/sindresorhus/decamelize/compare/v5.0.0...v6.0.0)

---
updated-dependencies:
- dependency-name: decamelize
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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-10-02 15:42:37 +00:00
dependabot[bot]
1ff1fc0c58 chore(deps): bump @actions/core in /.github/actions/close-bot (#7095)
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.5.0 to 1.6.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-10-02 15:27:02 +00:00
dependabot[bot]
c02f9d9396 chore(deps-dev): bump cypress from 8.4.1 to 8.5.0 (#7092)
Bumps [cypress](https://github.com/cypress-io/cypress) from 8.4.1 to 8.5.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.4.1...v8.5.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-10-02 10:12:07 -05:00
dependabot[bot]
a3c2ada96e chore(deps): bump fast-xml-parser from 3.20.0 to 3.20.3 (#7090)
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 3.20.0 to 3.20.3.
- [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/v3.20.0...v3.20.3)

---
updated-dependencies:
- dependency-name: fast-xml-parser
  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-10-02 01:05:09 +00:00
dependabot[bot]
403bb557c7 chore(deps): bump simple-icons from 5.15.0 to 5.16.0 (#7094)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.15.0 to 5.16.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.15.0...5.16.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-10-02 00:49:57 +00:00
dependabot[bot]
db8d556671 chore(deps-dev): bump concurrently from 6.2.1 to 6.2.2 (#7093)
Bumps [concurrently](https://github.com/open-cli-tools/concurrently) from 6.2.1 to 6.2.2.
- [Release notes](https://github.com/open-cli-tools/concurrently/releases)
- [Commits](https://github.com/open-cli-tools/concurrently/compare/v6.2.1...v6.2.2)

---
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-10-02 00:28:54 +00:00
dependabot[bot]
3471a99edf chore(deps-dev): bump @typescript-eslint/eslint-plugin (#7089)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.31.0 to 4.32.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.32.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-10-01 18:48:55 -05:00
Marc Bernard
ca927fd5d8 Add [ClearlyDefined] service (#6944)
* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* [ClearlyDefined] New badge

* Add HTTP 500 error message

Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com>

* Update error message for test

* Handle incomplete response

* Make file count optional

* Use NotFound

Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-29 02:10:01 +00:00
Paul Melnikow
4a8c1d1d0f Update Shields production access doc (#7079) 2021-09-28 16:28:03 -04:00
Caleb Cartwright
aa38625b86 refactor: cleanup AppveyorTests with by using shared helpers (#7077)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-27 23:09:00 +00:00
Caleb Cartwright
a3e2b2ff28 handle null licenses in crates.io response schema, run [crates] (#7074)
* fix: handle null licenses in crates.io response schema

* more tests for crates license badge

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-27 22:53:09 +00:00
Caleb Cartwright
28cf98fff4 add documentation guide for adding new secrets (#7072)
* docs: add guide for adding new secrets

* generalize docs for adding secrets/settinsg

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-27 22:34:37 +00:00
Caleb Cartwright
14a65ba38f change [JenkinsTests] targets to get service tests working again (#7068)
* fix: change JenkinsTests targets to get service tests working again

* update example to match working job url

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-26 21:16:13 +00:00
Caleb Cartwright
ca0defa0a7 refactor: switch to David status API and update error handling (#7070)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-26 20:59:44 +00:00
Caleb Cartwright
5e96598fcb refactor: cleanup legacy sinon sandboxing (#7066)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-26 20:34:51 +00:00
Caleb Cartwright
926e62f927 fix: annotate false lgtm hardcoded creds alert (#7069)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-26 20:05:00 +00:00
dependabot[bot]
0fe5839757 chore(deps-dev): bump gatsby-plugin-catch-links from 3.13.0 to 3.14.0 (#7057)
Bumps [gatsby-plugin-catch-links](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-catch-links) from 3.13.0 to 3.14.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.14.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-26 13:26:34 +00:00
dependabot[bot]
b69b13a72d chore(deps): bump graphql from 15.5.3 to 15.6.0 (#7062)
Bumps [graphql](https://github.com/graphql/graphql-js) from 15.5.3 to 15.6.0.
- [Release notes](https://github.com/graphql/graphql-js/releases)
- [Commits](https://github.com/graphql/graphql-js/compare/v15.5.3...v15.6.0)

---
updated-dependencies:
- dependency-name: graphql
  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-26 03:35:55 +00:00
Florian "sp1rit
8a9efb2fc9 [OBS] add Open Build Service service-badge (#6993)
* service: add obs service

* service: obs: replaced replaceAll with replace and global regex

* service: obs: added space between class members

* service: obs: support for multiple instances

* service: obs: removed user prefix from auth vars

obs_userName is now called obs_user and obs_userPass is called obs_pass

Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com>

* service: obs: removed constructor hack in favour of serviceKey

* service: obs: apply suggestions from @calebcartwright

* service: obs: remove unneccesary http status mappings

Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-25 17:44:56 +00:00
dependabot[bot]
e9153ab97a chore(deps): bump simple-icons from 5.14.0 to 5.15.0 (#7061)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.14.0 to 5.15.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.14.0...5.15.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-25 16:13:45 +00:00
dependabot[bot]
d79cf79f8a chore(deps-dev): bump nodemon from 2.0.12 to 2.0.13 (#7060)
Bumps [nodemon](https://github.com/remy/nodemon) from 2.0.12 to 2.0.13.
- [Release notes](https://github.com/remy/nodemon/releases)
- [Commits](https://github.com/remy/nodemon/compare/v2.0.12...v2.0.13)

---
updated-dependencies:
- dependency-name: nodemon
  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-25 15:54:27 +00:00
dependabot[bot]
d0734a313a chore(deps): bump prom-client from 13.2.0 to 14.0.0 (#7063)
Bumps [prom-client](https://github.com/siimon/prom-client) from 13.2.0 to 14.0.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.2.0...v14.0.0)

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

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-25 15:36:45 +00:00
dependabot[bot]
2c9068e976 chore(deps): bump @sentry/node from 6.12.0 to 6.13.2 (#7059)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 6.12.0 to 6.13.2.
- [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.12.0...6.13.2)

---
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-25 15:12:51 +00:00
dependabot[bot]
8b6a61c764 chore(deps-dev): bump gatsby-plugin-page-creator from 3.13.0 to 3.14.0 (#7056)
Bumps [gatsby-plugin-page-creator](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-page-creator) from 3.13.0 to 3.14.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.14.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-25 14:52:17 +00:00
dependabot[bot]
2ae4d179df chore(deps-dev): bump gatsby-plugin-typescript from 3.13.0 to 3.14.0 (#7052)
Bumps [gatsby-plugin-typescript](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-typescript) from 3.13.0 to 3.14.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/master/packages/gatsby-plugin-typescript/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/commits/gatsby-plugin-typescript@3.14.0/packages/gatsby-plugin-typescript)

---
updated-dependencies:
- dependency-name: gatsby-plugin-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-09-25 06:01:54 +00:00
dependabot[bot]
b171af4f82 chore(deps): bump glob from 7.1.7 to 7.2.0 (#7050)
Bumps [glob](https://github.com/isaacs/node-glob) from 7.1.7 to 7.2.0.
- [Release notes](https://github.com/isaacs/node-glob/releases)
- [Changelog](https://github.com/isaacs/node-glob/blob/master/changelog.md)
- [Commits](https://github.com/isaacs/node-glob/compare/v7.1.7...v7.2.0)

---
updated-dependencies:
- dependency-name: glob
  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-25 05:53:12 +00:00
dependabot[bot]
74ffb87012 chore(deps-dev): bump babel-preset-gatsby from 1.13.0 to 1.14.0 (#7054)
Bumps [babel-preset-gatsby](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/babel-preset-gatsby) from 1.13.0 to 1.14.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.14.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-25 05:30:32 +00:00
dependabot[bot]
16620695e2 chore(deps-dev): bump gatsby-plugin-styled-components (#7047)
Bumps [gatsby-plugin-styled-components](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-styled-components) from 4.13.0 to 4.14.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.14.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-25 05:18:34 +00:00
dependabot[bot]
3d6c7971d1 chore(deps-dev): bump gatsby-plugin-react-helmet from 4.13.0 to 4.14.0 (#7046)
Bumps [gatsby-plugin-react-helmet](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-react-helmet) from 4.13.0 to 4.14.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.14.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-25 05:08:10 +00:00
dependabot[bot]
f222668682 chore(deps-dev): bump node-mocks-http from 1.10.1 to 1.11.0 (#7044)
Bumps [node-mocks-http](https://github.com/howardabrams/node-mocks-http) from 1.10.1 to 1.11.0.
- [Release notes](https://github.com/howardabrams/node-mocks-http/releases)
- [Changelog](https://github.com/howardabrams/node-mocks-http/blob/master/HISTORY.md)
- [Commits](https://github.com/howardabrams/node-mocks-http/compare/v1.10.1...v1.11.0)

---
updated-dependencies:
- dependency-name: node-mocks-http
  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-25 03:29:51 +00:00
cnico
1f94f8d571 Correction of badges url in self-hosting configuration with a custom port. Issue 7025 (#7036)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-09-25 03:19:44 +00:00
dependabot[bot]
16813841b7 chore(deps-dev): bump cypress from 8.4.0 to 8.4.1 (#7051)
Bumps [cypress](https://github.com/cypress-io/cypress) from 8.4.0 to 8.4.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.4.0...v8.4.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-25 03:08:39 +00:00
dependabot[bot]
6dbee4457f chore(deps-dev): bump @types/chai from 4.2.21 to 4.2.22 (#7049)
Bumps [@types/chai](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/chai) from 4.2.21 to 4.2.22.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/chai)

---
updated-dependencies:
- dependency-name: "@types/chai"
  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-25 02:48:13 +00:00
dependabot[bot]
c3dd1fbc5c chore(deps-dev): bump gatsby-plugin-remove-trailing-slashes (#7042)
Bumps [gatsby-plugin-remove-trailing-slashes](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/gatsby-plugin-remove-trailing-slashes) from 3.13.0 to 3.14.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.14.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>
2021-09-24 17:19:44 -05:00
46 changed files with 1767 additions and 2122 deletions

View File

@@ -9,14 +9,17 @@
"version": "0.0.0",
"license": "CC0",
"dependencies": {
"@actions/core": "^1.5.0",
"@actions/core": "^1.6.0",
"@actions/github": "^5.0.0"
}
},
"node_modules/@actions/core": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.5.0.tgz",
"integrity": "sha512-eDOLH1Nq9zh+PJlYLqEMkS/jLQxhksPNmUGNBHfa4G+tQmnIhzpctxmchETtVGyBOvXgOVVpYuE40+eS4cUnwQ=="
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz",
"integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==",
"dependencies": {
"@actions/http-client": "^1.0.11"
}
},
"node_modules/@actions/github": {
"version": "5.0.0",
@@ -193,9 +196,12 @@
},
"dependencies": {
"@actions/core": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.5.0.tgz",
"integrity": "sha512-eDOLH1Nq9zh+PJlYLqEMkS/jLQxhksPNmUGNBHfa4G+tQmnIhzpctxmchETtVGyBOvXgOVVpYuE40+eS4cUnwQ=="
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.6.0.tgz",
"integrity": "sha512-NB1UAZomZlCV/LmJqkLhNTqtKfFXJZAUPcfl/zqG7EfsQdeUJtaWO98SGbuQ3pydJ3fHl2CvI/51OKYlCYYcaw==",
"requires": {
"@actions/http-client": "^1.0.11"
}
},
"@actions/github": {
"version": "5.0.0",

View File

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

View File

@@ -4,6 +4,20 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
---
## server-2021-10-04
- feat: add 2021 support to GitHub Hacktoberfest [#7086](https://github.com/badges/shields/issues/7086)
- Add [ClearlyDefined] service [#6944](https://github.com/badges/shields/issues/6944)
- handle null licenses in crates.io response schema, run [crates] [#7074](https://github.com/badges/shields/issues/7074)
- [OBS] add Open Build Service service-badge [#6993](https://github.com/badges/shields/issues/6993)
- Correction of badges url in self-hosting configuration with a custom port. Issue 7025 [#7036](https://github.com/badges/shields/issues/7036)
- fix: support gitlab token via env var [#7023](https://github.com/badges/shields/issues/7023)
- Add API-based support for [GitLab] badges, add new GitLab Tag badge [#6988](https://github.com/badges/shields/issues/6988)
- [freecodecamp]: allow + symbol in username [#7016](https://github.com/badges/shields/issues/7016)
- Rename Riot to Element in Matrix badge help [#6996](https://github.com/badges/shields/issues/6996)
- Fixed Reddit Negative Karma Issue [#6992](https://github.com/badges/shields/issues/6992)
- Dependency updates
## server-2021-09-01
- use multi-stage build to reduce size of docker images [#6938](https://github.com/badges/shields/issues/6938)

View File

@@ -50,6 +50,8 @@ public:
authorizedOrigins: 'NEXUS_ORIGINS'
npm:
authorizedOrigins: 'NPM_ORIGINS'
obs:
authorizedOrigins: 'OBS_ORIGINS'
sonar:
authorizedOrigins: 'SONAR_ORIGINS'
teamcity:
@@ -87,6 +89,8 @@ private:
nexus_user: 'NEXUS_USER'
nexus_pass: 'NEXUS_PASS'
npm_token: 'NPM_TOKEN'
obs_user: 'OBS_USER'
obs_pass: 'OBS_PASS'
redis_url: 'REDIS_URL'
sentry_dsn: 'SENTRY_DSN'
shields_secret: 'SHIELDS_SECRET'

View File

@@ -22,6 +22,8 @@ public:
debug:
enabled: false
intervalSeconds: 200
obs:
authorizedOrigins: 'https://api.opensuse.org'
weblate:
authorizedOrigins: 'https://hosted.weblate.org'
trace: false

View File

@@ -6,6 +6,8 @@ private:
# preferable for self hosting.
gh_token: '...'
gitlab_token: '...'
obs_user: '...'
obs_pass: '...'
twitch_client_id: '...'
twitch_client_secret: '...'
weblate_api_key: '...'

View File

@@ -124,15 +124,11 @@ describe('BaseService', function () {
})
describe('Logging', function () {
let sandbox
beforeEach(function () {
sandbox = sinon.createSandbox()
sinon.stub(trace, 'logTrace')
})
afterEach(function () {
sandbox.restore()
})
beforeEach(function () {
sandbox.stub(trace, 'logTrace')
sinon.restore()
})
it('Invokes the logger as expected', async function () {
await DummyService.invoke(
@@ -426,15 +422,11 @@ describe('BaseService', function () {
})
describe('request', function () {
let sandbox
beforeEach(function () {
sandbox = sinon.createSandbox()
sinon.stub(trace, 'logTrace')
})
afterEach(function () {
sandbox.restore()
})
beforeEach(function () {
sandbox.stub(trace, 'logTrace')
sinon.restore()
})
it('logs appropriate information', async function () {

View File

@@ -99,14 +99,11 @@ describe('Cache header functions', function () {
})
describe('setHeadersForCacheLength', function () {
let sandbox
beforeEach(function () {
sandbox = sinon.createSandbox()
sandbox.useFakeTimers()
sinon.useFakeTimers()
})
afterEach(function () {
sandbox.restore()
sandbox = undefined
sinon.restore()
})
it('should set the correct Date header', function () {

View File

@@ -10,15 +10,11 @@ describe('validate', function () {
requiredString: Joi.string().required(),
}).required()
let sandbox
beforeEach(function () {
sandbox = sinon.createSandbox()
sinon.stub(trace, 'logTrace')
})
afterEach(function () {
sandbox.restore()
})
beforeEach(function () {
sandbox.stub(trace, 'logTrace')
sinon.restore()
})
const ErrorClass = InvalidParameter

View File

@@ -134,6 +134,7 @@ const publicConfigSchema = Joi.object({
}).default({ authorizedOrigins: [] }),
nexus: defaultService,
npm: defaultService,
obs: defaultService,
sonar: defaultService,
teamcity: defaultService,
weblate: defaultService,
@@ -172,6 +173,8 @@ const privateConfigSchema = Joi.object({
nexus_user: Joi.string(),
nexus_pass: Joi.string(),
npm_token: Joi.string(),
obs_user: Joi.string(),
obs_pass: Joi.string(),
redis_url: Joi.string().uri({ scheme: ['redis', 'rediss'] }),
sentry_dsn: Joi.string(),
shields_secret: Joi.string(),

View File

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

View File

@@ -0,0 +1,22 @@
# Adding New Config Values
The Badge Server supports a [variety of methods for defining configuration settings and secrets](./server-secrets.md), and provides a framework for loading those values during bootstrapping.
Any new configuration setting or secret must be correctly registered so that it will be loaded at startup along with the others.
This generally includes adding the corresponding information for your new setting(s)/secret(s) to the following locations:
- [core/server/server.js](https://github.com/badges/shields/blob/master/core/server/server.js) - Add the new values to the [schemas](https://github.com/badges/shields/blob/master/core/server/server.js#L118-L193). Secrets/tokens/etc. should go in the `privateConfigSchema` while non-secret configuration settings should go in the `publicConfigSchema`.
- [config/custom-environment-variables.yml](https://github.com/badges/shields/blob/master/config/custom-environment-variables.yml)
- [docs/server-secrets.md](https://github.com/badges/shields/blob/master/doc/server-secrets.md) (only applicable for secrets)
- [config/default.yml](https://github.com/badges/shields/blob/master/config/default.yml) (optional)
- Any other template config files (e.g. `config/local.template.yml`) (optional)
The exact values needed will depend on what type of secret/setting you are adding, but for reference a few commits are included below which added secrets and or settings:
- (secret) [8a9efb2fc99f97e78ab133c836ab1685803bf4df](https://github.com/badges/shields/commit/8a9efb2fc99f97e78ab133c836ab1685803bf4df)
- (secret) [bd6f4ee1465d14a8f188c37823748a21b6a46762](https://github.com/badges/shields/commit/bd6f4ee1465d14a8f188c37823748a21b6a46762)
- (secret) [0fd557d7bb623e3852c92cebac586d5f6d6d89d8](https://github.com/badges/shields/commit/0fd557d7bb623e3852c92cebac586d5f6d6d89d8)
- (configuration setting) [b1fc4925928c061234e9492f3794c0797467e123](https://github.com/badges/shields/commit/b1fc4925928c061234e9492f3794c0797467e123)
Don't hesitate to reach out if you're unsure of the exact values needed for your new secret/setting, or have any other questions. Feel free to post questions on your corresponding Issue/Pull Request, and/or ping us on the `contributing` channel on our Discord server.

View File

@@ -16,7 +16,7 @@ Production hosting is managed by the Shields ops team:
| Component | Subcomponent | People with access |
| ----------------------------- | ------------------------------- | --------------------------------------------------------------- |
| shields-production-us | Account owner | @paulmelnikow |
| shields-production-us | Account owner | @calebcartwright, @paulmelnikow |
| shields-production-us | Full access | @calebcartwright, @chris48s, @paulmelnikow, @pyvesb |
| shields-production-us | Access management | @calebcartwright, @chris48s, @paulmelnikow, @pyvesb |
| Compose.io Redis | Account owner | @paulmelnikow |

View File

@@ -193,6 +193,21 @@ installation access to private npm packages
[npm token]: https://docs.npmjs.com/getting-started/working_with_tokens
## Open Build Service
- `OBS_USER` (yml: `private.obs_user`)
- `OBS_PASS` (yml: `private.obs_user`)
Only authenticated users are allowed to access the Open Build Service API.
Authentication is done by sending a Basic HTTP Authorisation header. A user
account for the [reference instance](https://build.opensuse.org) is a SUSE
IdP account, which can be created [here](https://idp-portal.suse.com/univention/self-service/#page=createaccount).
While OBS supports [API tokens](https://openbuildservice.org/help/manuals/obs-user-guide/cha.obs.authorization.token.html#id-1.5.10.16.4),
they can only be scoped to execute specific actions on a POST request. This
means however, that an actual account is required to read the build status
of a package.
### SymfonyInsight (formerly Sensiolabs)
- `SL_INSIGHT_USER_UUID` (yml: `private.sl_insight_userUuid`)

View File

@@ -21,11 +21,11 @@ export function getBaseUrl(): string {
https://img.shields.io/
*/
try {
const { protocol, hostname } = window.location
const { protocol, hostname, port } = window.location
if (['shields.io', 'www.shields.io'].includes(hostname)) {
return 'https://img.shields.io'
}
return `${protocol}//${hostname}`
return `${protocol}//${hostname}:${port}`
} catch (e) {
// server-side rendering
return ''

2597
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -24,7 +24,7 @@
"dependencies": {
"@fontsource/lato": "^4.5.0",
"@fontsource/lekton": "^4.5.0",
"@sentry/node": "^6.12.0",
"@sentry/node": "^6.13.2",
"@shields_io/camp": "^18.1.1",
"badge-maker": "file:badge-maker",
"bytes": "^3.1.0",
@@ -34,14 +34,14 @@
"cloudflare-middleware": "^1.0.4",
"config": "^3.3.6",
"cross-env": "^7.0.3",
"decamelize": "^5.0.0",
"decamelize": "^6.0.0",
"emojic": "^1.1.16",
"escape-string-regexp": "^4.0.0",
"fast-xml-parser": "^3.20.0",
"glob": "^7.1.7",
"fast-xml-parser": "^3.20.3",
"glob": "^7.2.0",
"global-agent": "^3.0.0",
"got": "11.8.2",
"graphql": "^15.5.3",
"graphql": "^15.6.0",
"graphql-tag": "^2.12.5",
"ioredis": "4.27.9",
"joi": "17.4.2",
@@ -57,12 +57,12 @@
"path-to-regexp": "^6.2.0",
"pretty-bytes": "^5.6.0",
"priorityqueuejs": "^2.0.0",
"prom-client": "^13.2.0",
"prom-client": "^14.0.0",
"qs": "^6.10.1",
"query-string": "^7.0.1",
"request": "~2.88.2",
"semver": "~7.3.5",
"simple-icons": "5.14.0",
"simple-icons": "5.16.0",
"webextension-store-meta": "^1.0.4",
"xmldom": "~0.6.0",
"xpath": "~0.0.32"
@@ -146,7 +146,7 @@
"@babel/polyfill": "^7.12.1",
"@babel/register": "7.15.3",
"@mapbox/react-click-to-select": "^2.2.1",
"@types/chai": "^4.2.21",
"@types/chai": "^4.2.22",
"@types/lodash.debounce": "^4.0.6",
"@types/lodash.groupby": "^4.6.6",
"@types/mocha": "^9.0.0",
@@ -155,11 +155,11 @@
"@types/react-modal": "^3.12.1",
"@types/react-select": "^4.0.17",
"@types/styled-components": "5.1.14",
"@typescript-eslint/eslint-plugin": "^4.31.0",
"@typescript-eslint/parser": "^4.30.0",
"@typescript-eslint/eslint-plugin": "^4.32.0",
"@typescript-eslint/parser": "^4.32.0",
"babel-plugin-inline-react-svg": "^2.0.1",
"babel-plugin-istanbul": "^6.0.0",
"babel-preset-gatsby": "^1.13.0",
"babel-preset-gatsby": "^1.14.0",
"c8": "^7.9.0",
"caller": "^1.0.1",
"chai": "^4.3.4",
@@ -168,8 +168,8 @@
"chai-string": "^1.4.0",
"child-process-promise": "^2.2.1",
"clipboard-copy": "^4.0.1",
"concurrently": "^6.2.1",
"cypress": "^8.4.0",
"concurrently": "^6.2.2",
"cypress": "^8.5.0",
"danger": "^10.6.6",
"danger-plugin-no-test-shortcuts": "^2.0.0",
"deepmerge": "^4.2.2",
@@ -192,12 +192,12 @@
"fetch-ponyfill": "^7.1.0",
"form-data": "^4.0.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",
"gatsby-plugin-catch-links": "^3.14.0",
"gatsby-plugin-page-creator": "^3.14.0",
"gatsby-plugin-react-helmet": "^4.14.0",
"gatsby-plugin-remove-trailing-slashes": "^3.14.0",
"gatsby-plugin-styled-components": "^4.14.0",
"gatsby-plugin-typescript": "^3.14.0",
"humanize-string": "^2.1.0",
"icedfrisby": "4.0.0",
"icedfrisby-nock": "^2.1.0",
@@ -208,13 +208,13 @@
"lodash.debounce": "^4.0.8",
"lodash.difference": "^4.5.0",
"minimist": "^1.2.5",
"mocha": "^9.1.1",
"mocha": "^9.1.2",
"mocha-env-reporter": "^4.0.0",
"mocha-junit-reporter": "^2.0.0",
"mocha-yaml-loader": "^1.0.3",
"nock": "13.1.3",
"node-mocks-http": "^1.10.1",
"nodemon": "^2.0.12",
"node-mocks-http": "^1.11.0",
"nodemon": "^2.0.13",
"npm-run-all": "^4.1.5",
"open-cli": "^7.0.1",
"portfinder": "^1.0.28",

View File

@@ -1,31 +1,10 @@
import {
testResultQueryParamSchema,
renderTestResultBadge,
documentation,
} from '../test-results.js'
import AppVeyorBase from './appveyor-base.js'
const documentation = `
<p>
You may change the "passed", "failed" and "skipped" text on this badge by supplying query parameters <code>&passed_label=</code>, <code>&failed_label=</code> and <code>&skipped_label=</code> respectively.
</p>
<p>
For example, if you want to use a different terminology:
<br>
<code>/appveyor/tests/NZSmartie/coap-net-iu0to.svg?passed_label=good&failed_label=bad&skipped_label=n%2Fa</code>
</p>
<p>
Or symbols:
<br>
<code>/appveyor/tests/NZSmartie/coap-net-iu0to.svg?compact_message&passed_label=%F0%9F%8E%89&failed_label=%F0%9F%92%A2&skipped_label=%F0%9F%A4%B7</code>
</p>
<p>
There is also a <code>&compact_message</code> query parameter, which will default to displaying ✔, ✘ and ➟, separated by a horizontal bar |.
</p>
`
const commonPreviewProps = {
passed: 477,
failed: 2,

View File

@@ -1,38 +1,26 @@
import queryString from 'querystring'
import Joi from 'joi'
import {
isDefaultTestTotals,
isDefaultCompactTestTotals,
isCustomTestTotals,
isCustomCompactTestTotals,
} from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
const isAppveyorTestTotals = Joi.string().regex(
/^[0-9]+ passed(, [0-9]+ failed)?(, [0-9]+ skipped)?$/
)
const isCompactAppveyorTestTotals = Joi.string().regex(
/^✔ [0-9]+( \| ✘ [0-9]+)?( \| ➟ [0-9]+)?$/
)
const isCustomAppveyorTestTotals = Joi.string().regex(
/^[0-9]+ good(, [0-9]+ bad)?(, [0-9]+ n\/a)?$/
)
const isCompactCustomAppveyorTestTotals = Joi.string().regex(
/^💃 [0-9]+( \| 🤦‍♀️ [0-9]+)?( \| 🤷 [0-9]+)?$/
)
t.create('Test status')
.timeout(10000)
.get('/NZSmartie/coap-net-iu0to.json')
.expectBadge({ label: 'tests', message: isAppveyorTestTotals })
.expectBadge({ label: 'tests', message: isDefaultTestTotals })
t.create('Test status on branch')
.timeout(10000)
.get('/NZSmartie/coap-net-iu0to/master.json')
.expectBadge({ label: 'tests', message: isAppveyorTestTotals })
.expectBadge({ label: 'tests', message: isDefaultTestTotals })
t.create('Test status with compact message')
.timeout(10000)
.get('/NZSmartie/coap-net-iu0to.json?compact_message')
.expectBadge({ label: 'tests', message: isCompactAppveyorTestTotals })
.expectBadge({ label: 'tests', message: isDefaultCompactTestTotals })
t.create('Test status with custom labels')
.timeout(10000)
@@ -43,21 +31,21 @@ t.create('Test status with custom labels')
skipped_label: 'n/a',
},
})
.expectBadge({ label: 'tests', message: isCustomAppveyorTestTotals })
.expectBadge({ label: 'tests', message: isCustomTestTotals })
t.create('Test status with compact message and custom labels')
.timeout(10000)
.get(
`/NZSmartie/coap-net-iu0to.json?${queryString.stringify({
.get('/NZSmartie/coap-net-iu0to.json', {
qs: {
compact_message: null,
passed_label: '💃',
failed_label: '🤦‍♀️',
skipped_label: '🤷',
})}`
)
},
})
.expectBadge({
label: 'tests',
message: isCompactCustomAppveyorTestTotals,
message: isCustomCompactTestTotals,
})
t.create('Test status on non-existent project')

View File

@@ -12,6 +12,7 @@ const greenStatuses = [
const orangeStatuses = ['partially succeeded', 'unstable', 'timeout']
const redStatuses = [
'broken',
'error',
'errored',
'failed',

View File

@@ -53,6 +53,7 @@ test(renderBuildStatusBadge, () => {
test(renderBuildStatusBadge, () => {
forCases([
given({ status: 'broken' }),
given({ status: 'error' }),
given({ status: 'errored' }),
given({ status: 'failed' }),

View File

@@ -0,0 +1,74 @@
import Joi from 'joi'
import {
nonNegativeInteger,
optionalNonNegativeInteger,
} from '../validators.js'
import { floorCount as floorCountColor } from '../color-formatters.js'
import { BaseJsonService, NotFound } from '../index.js'
const schema = Joi.object({
scores: Joi.object({
effective: nonNegativeInteger,
}).required(),
described: Joi.object({
files: optionalNonNegativeInteger,
}),
}).required()
// This service based on the REST API for clearlydefined.io
// https://api.clearlydefined.io/api-docs/
export default class ClearlyDefinedService extends BaseJsonService {
static category = 'analysis'
static route = {
base: 'clearlydefined',
pattern: 'score/:type/:provider/:namespace/:name/:revision',
}
static examples = [
{
title: 'ClearlyDefined Score',
namedParams: {
type: 'npm',
provider: 'npmjs',
namespace: '-',
name: 'jquery',
revision: '3.4.1',
},
staticPreview: this.render({ score: 88 }),
},
]
static defaultBadgeData = { label: 'score' }
static render({ score }) {
score = Math.round(score)
return {
label: 'score',
message: `${score}/100`,
color: floorCountColor(score, 40, 60, 100),
}
}
async fetch({ type, provider, namespace, name, revision }) {
return this._requestJson({
schema,
url: `https://api.clearlydefined.io/definitions/${type}/${provider}/${namespace}/${name}/${revision}`,
errorMessages: {
500: 'unknown type, provider, or upstream issue',
},
})
}
async handle({ type, provider, namespace, name, revision }) {
const data = await this.fetch({ type, provider, namespace, name, revision })
// Return score only if definition contains some files,
// else it was an incomplete response due to unknown coordinates
if (data.described.files > 0) {
return this.constructor.render({ score: data.scores.effective })
} else {
throw new NotFound({
prettyMessage: 'unknown namespace, name, or revision',
})
}
}
}

View File

@@ -0,0 +1,24 @@
import Joi from 'joi'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('ClearlyDefined Score')
.get('/score/npm/npmjs/-/jquery/3.4.1.json')
.expectBadge({
label: 'score',
message: Joi.string().regex(/^\d+\/\d+$/),
})
t.create('ClearlyDefined Score (name not found)')
.get('/score/npm/npmjs/-/not-a-real-package/0.0.0.json')
.expectBadge({
label: 'score',
message: 'unknown namespace, name, or revision',
})
t.create('ClearlyDefined Score (type not found)')
.get('/score/abc/xyz/-/not-a-real-package/0.0.0.json')
.expectBadge({
label: 'score',
message: 'unknown type, provider, or upstream issue',
})

View File

@@ -14,7 +14,7 @@ const crateSchema = Joi.object({
.items(
Joi.object({
downloads: nonNegativeInteger,
license: Joi.string().required(),
license: Joi.string().required().allow(null),
})
)
.min(1)
@@ -25,7 +25,7 @@ const versionSchema = Joi.object({
version: Joi.object({
downloads: nonNegativeInteger,
num: Joi.string().required(),
license: Joi.string().required(),
license: Joi.string().required().allow(null),
}).required(),
}).required()

View File

@@ -1,11 +1,6 @@
import { ServiceTester } from '../tester.js'
import { isMetric } from '../test-validators.js'
export const t = new ServiceTester({
id: 'crates',
title: 'crates.io',
pathPrefix: '/crates',
})
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('total downloads')
.get('/d/libc.json')

View File

@@ -1,3 +1,4 @@
import { InvalidResponse } from '../index.js'
import { BaseCratesService, keywords } from './crates-base.js'
export default class CratesLicense extends BaseCratesService {
@@ -21,28 +22,30 @@ export default class CratesLicense extends BaseCratesService {
},
]
static render({ license }) {
return {
label: 'license',
message: license,
color: 'blue',
static defaultBadgeData = { label: 'license', color: 'blue' }
static render({ license: message }) {
return { message }
}
static transform({ errors, version, versions }) {
// crates.io returns a 200 response with an errors object in
// error scenarios, e.g. https://crates.io/api/v1/crates/libc/0.1
if (errors) {
throw new InvalidResponse({ prettyMessage: errors[0].detail })
}
const license = version ? version.license : versions[0].license
if (!license) {
throw new InvalidResponse({ prettyMessage: 'invalid null license' })
}
return { license }
}
async handle({ crate, version }) {
const json = await this.fetch({ crate, version })
if (json.errors) {
/* a call like
https://crates.io/api/v1/crates/libc/0.1
or
https://crates.io/api/v1/crates/libc/0.1.76
returns a 200 OK with an errors object */
return { message: json.errors[0].detail }
}
return this.constructor.render({
license: json.version ? json.version.license : json.versions[0].license,
})
const { license } = this.constructor.transform(json)
return this.constructor.render({ license })
}
}

View File

@@ -0,0 +1,38 @@
import { expect } from 'chai'
import { test, given } from 'sazerac'
import { InvalidResponse } from '../index.js'
import CratesLicense from './crates-license.service.js'
describe('CratesLicense', function () {
test(CratesLicense.transform, () => {
given({
version: { num: '1.0.0', license: 'MIT' },
versions: [{ license: 'MIT/Apache 2.0' }],
}).expect({ license: 'MIT' })
given({
versions: [{ license: 'MIT/Apache 2.0' }],
}).expect({ license: 'MIT/Apache 2.0' })
})
it('throws InvalidResponse on error response', function () {
expect(() =>
CratesLicense.transform({ errors: [{ detail: 'invalid semver' }] })
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'invalid semver')
})
it('throws InvalidResponse on null license with specific version', function () {
expect(() =>
CratesLicense.transform({ version: { num: '1.2.3', license: null } })
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'invalid null license')
})
it('throws InvalidResponse on null license with latest version', function () {
expect(() => CratesLicense.transform({ versions: [{ license: null }] }))
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'invalid null license')
})
})

View File

@@ -1,10 +1,5 @@
import { ServiceTester } from '../tester.js'
export const t = new ServiceTester({
id: 'crates',
title: 'crates.io',
pathPrefix: '/crates/l',
})
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
t.create('license')
.get('/libc.json')
@@ -16,4 +11,13 @@ t.create('license (with version)')
t.create('license (not found)')
.get('/not-a-real-package.json')
.expectBadge({ label: 'crates.io', message: 'not found' })
.expectBadge({ label: 'license', message: 'not found' })
// https://github.com/badges/shields/issues/7073
t.create('license (null licenses in history)')
.get('/stun.json')
.expectBadge({ label: 'license', message: 'MIT/Apache-2.0' })
t.create('license (version with null license)')
.get('/stun/0.0.1.json')
.expectBadge({ label: 'license', message: 'invalid null license' })

View File

@@ -71,22 +71,19 @@ export default class David extends BaseJsonService {
}
async fetch({ kind, user, repo, path }) {
const url = `https://david-dm.org/${user}/${repo}/${
kind ? `${kind}-` : ''
}info.json`
// Note: David does not return canonical 404 response codes for 'not found'
// cases, but will instead return various 50x errors. Accordingly we account
// for both 'not found' as well as typical/real internal server errors.
const notFoundError = 'repo or path not found or david internal error'
return this._requestJson({
schema,
url,
options: { qs: { path } },
url: `https://status.david-dm.org/gh/${user}/${repo}`,
options: { qs: { path, type: kind } },
errorMessages: {
/* note:
david returns a 504 response for 'not found'
e.g: https://david-dm.org/foo/barbaz/info.json
not a 404 so we can't handle 'not found' cleanly
because this might also be some other error.
*/
504: 'repo or path not found or david internal error',
502: notFoundError,
503: notFoundError,
504: notFoundError,
},
})
}

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, tokenScopes }) {
function setRoutes({ server, authHelper, onTokenAccepted }) {
const baseUrl = process.env.GATSBY_BASE_URL || 'https://img.shields.io'
server.route(/^\/github-auth$/, (data, match, end, ask) => {
@@ -15,7 +15,6 @@ function setRoutes({ server, authHelper, onTokenAccepted, tokenScopes }) {
// 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,7 +41,6 @@ describe('Github token acceptor', function () {
server: camp,
authHelper: oauthHelper,
onTokenAccepted,
tokenScopes: 'read:packages',
})
})
@@ -53,7 +52,6 @@ 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,10 +30,11 @@ 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,
url: '/repos/rust-lang/rust',
})
'/repos/rust-lang/rust',
{}
)
}
})
})
@@ -51,10 +52,11 @@ describe('Github API provider', function () {
const headers = []
async function performOneRequest() {
const { res } = await githubApiProvider.requestAsPromise({
const { res } = await githubApiProvider.requestAsPromise(
request,
url: '/repos/rust-lang/rust',
})
'/repos/rust-lang/rust',
{}
)
expect(res.statusCode).to.equal(200)
headers.push(res.headers)
}

View File

@@ -38,7 +38,6 @@ class GithubApiProvider {
onTokenInvalidated = tokenString => {},
globalToken,
reserveFraction = 0.25,
tokenScopeNames = {},
}) {
Object.assign(this, {
baseUrl,
@@ -46,14 +45,12 @@ 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 })
}
}
@@ -63,41 +60,17 @@ class GithubApiProvider {
standardTokens: this.standardTokens.serializeDebugInfo({ sanitize }),
searchTokens: this.searchTokens.serializeDebugInfo({ sanitize }),
graphqlTokens: this.graphqlTokens.serializeDebugInfo({ sanitize }),
packageScopedTokens: this.packageScopedTokens.serializeDebugInfo({
sanitize,
}),
}
} else {
return {}
}
}
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) {
addToken(tokenString) {
if (this.withPooling) {
this.standardTokens.add(tokenString, data)
this.searchTokens.add(tokenString, data)
this.graphqlTokens.add(tokenString, data)
this.standardTokens.add(tokenString)
this.searchTokens.add(tokenString)
this.graphqlTokens.add(tokenString)
} else {
throw Error('When not using a token pool, do not provide tokens')
}
@@ -168,11 +141,7 @@ class GithubApiProvider {
this.onTokenInvalidated(token.id)
}
tokenForUrl(url, { needsPackageScope }) {
if (needsPackageScope) {
return this.packageScopedTokens.next()
}
tokenForUrl(url) {
if (url.startsWith('/search')) {
return this.searchTokens.next()
} else if (url.startsWith('/graphql')) {
@@ -185,14 +154,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 = {}, neededScopes = {}, callback }) {
request(request, url, options = {}, callback) {
const { baseUrl } = this
let token
let tokenString
if (this.withPooling) {
try {
token = this.tokenForUrl(url, neededScopes)
token = this.tokenForUrl(url)
} catch (e) {
callback(e)
return
@@ -229,20 +198,14 @@ class GithubApiProvider {
})
}
requestAsPromise({ request, url, options, neededScopes }) {
requestAsPromise(request, url, options) {
return new Promise((resolve, reject) => {
this.request({
request,
url,
options,
neededScopes,
callback: (err, res, buffer) => {
if (err) {
reject(err)
} else {
resolve({ res, buffer })
}
},
this.request(request, url, options, (err, res, buffer) => {
if (err) {
reject(err)
} else {
resolve({ res, buffer })
}
})
})
}

View File

@@ -6,11 +6,7 @@ describe('Github API provider', function () {
const baseUrl = 'https://github-api.example.com'
const reserveFraction = 0.333
let mockStandardToken,
mockSearchToken,
mockGraphqlToken,
mockPackagesScopedToken,
provider
let mockStandardToken, mockSearchToken, mockGraphqlToken, provider
beforeEach(function () {
provider = new GithubApiProvider({ baseUrl, reserveFraction })
@@ -22,11 +18,6 @@ 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 () {
@@ -34,16 +25,12 @@ describe('Github API provider', function () {
callback()
}
it('should obtain an appropriate token', function (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()
},
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()
})
})
})
@@ -53,37 +40,12 @@ describe('Github API provider', function () {
callback()
}
it('should obtain an appropriate token', function (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()
},
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()
})
})
})
@@ -93,16 +55,12 @@ describe('Github API provider', function () {
callback()
}
it('should obtain an appropriate token', function (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()
},
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()
})
})
})
@@ -126,33 +84,25 @@ describe('Github API provider', function () {
}
it('should invoke the callback', function (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()
},
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()
})
})
it('should update the token with the expected values', function (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()
},
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()
})
})
})
@@ -182,33 +132,25 @@ describe('Github API provider', function () {
}
it('should invoke the callback', function (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()
},
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()
})
})
it('should update the token with the expected values', function (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()
},
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()
})
})
})
@@ -222,15 +164,11 @@ describe('Github API provider', function () {
}
it('should invoke the callback and update the token with the expected values', function (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()
},
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()
})
})
})
@@ -242,14 +180,10 @@ describe('Github API provider', function () {
}
it('should pass the error to the callback', function (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()
},
provider.request(mockRequest, '/foo', {}, (err, res, buffer) => {
expect(err).to.be.an.instanceof(Error)
expect(err.message).to.equal('connection timeout')
done()
})
})
})

View File

@@ -2,22 +2,21 @@ import gql from 'graphql-tag'
import { mergeQueries } from '../../core/base-service/graphql.js'
import { BaseGraphqlService, BaseJsonService } from '../index.js'
function createRequestFetcher(context, config, neededScopes) {
function createRequestFetcher(context, config) {
const { sendAndCacheRequestWithCallbacks, githubApiProvider } = context
return async (url, options) =>
githubApiProvider.requestAsPromise({
request: sendAndCacheRequestWithCallbacks,
githubApiProvider.requestAsPromise(
sendAndCacheRequestWithCallbacks,
url,
options,
neededScopes,
})
options
)
}
class GithubAuthV3Service extends BaseJsonService {
constructor(context, config, neededScopes) {
constructor(context, config) {
super(context, config)
this._requestFetcher = createRequestFetcher(context, config, neededScopes)
this._requestFetcher = createRequestFetcher(context, config)
this.staticAuthConfigured = true
}
}
@@ -28,10 +27,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, neededScopes) {
constructor(context, config) {
super(context, config)
if (context.githubApiProvider.globalToken) {
this._requestFetcher = createRequestFetcher(context, config, neededScopes)
this._requestFetcher = createRequestFetcher(context, config)
this.staticAuthConfigured = true
} else {
this.staticAuthConfigured = false
@@ -40,9 +39,9 @@ class ConditionalGithubAuthV3Service extends BaseJsonService {
}
class GithubAuthV4Service extends BaseGraphqlService {
constructor(context, config, neededScopes) {
constructor(context, config) {
super(context, config)
this._requestFetcher = createRequestFetcher(context, config, neededScopes)
this._requestFetcher = createRequestFetcher(context, config)
this.staticAuthConfigured = true
}

View File

@@ -25,32 +25,17 @@ describe('GithubAuthV3Service', function () {
}
}
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(
it('forwards custom Accept header', async function () {
const 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 () {
const githubApiProvider = new GithubApiProvider({
baseUrl: 'https://github-api.example.com',
})
const mockToken = { update: sinon.mock(), invalidate: sinon.mock() }
sinon.stub(githubApiProvider.standardTokens, 'next').returns(mockToken)
DummyGithubAuthV3Service.invoke({
@@ -62,26 +47,7 @@ describe('GithubAuthV3Service', function () {
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',
Authorization: 'token undefined',
},
url: 'https://github-api.example.com/repos/badges/shields/check-runs',
baseUrl: 'https://github-api.example.com',

View File

@@ -5,11 +5,6 @@ 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 {
@@ -29,8 +24,6 @@ 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) {
@@ -45,9 +38,6 @@ class GithubConstellation {
baseUrl: process.env.GITHUB_URL || 'https://api.github.com',
globalToken,
withPooling: !globalToken,
tokenScopeNames: {
readPackages: readPackagesScope,
},
onTokenInvalidated: tokenString => this.onTokenInvalidated(tokenString),
})
@@ -80,21 +70,8 @@ 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 => {
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)
}
this.apiProvider.addToken(tokenString)
})
const { shieldsSecret, apiProvider } = this
@@ -104,53 +81,19 @@ class GithubConstellation {
setAcceptorRoutes({
server,
authHelper: this.oauthHelper,
tokenScopes,
onTokenAccepted: tokenString =>
this.onTokenAdded(tokenString, tokenScopes),
onTokenAccepted: tokenString => this.onTokenAdded(tokenString),
})
}
}
onTokenAdded(tokenString, tokenScopes) {
onTokenAdded(tokenString) {
if (!this.persistence) {
throw Error('Token persistence is not configured')
}
const data = { scopes: tokenScopes }
const numReserved = this.apiProvider.numReservedScopedTokens()
if (numReserved < this._maxNumReservedScopedTokens) {
this.apiProvider.addReservedScopedToken(tokenString, data)
} else {
this.apiProvider.addToken(tokenString, data)
}
this.apiProvider.addToken(tokenString)
process.nextTick(async () => {
try {
// 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
await this.persistence.noteTokenAdded(tokenString)
} catch (e) {
log.error(e)
}
@@ -161,12 +104,7 @@ class GithubConstellation {
if (this.persistence) {
process.nextTick(async () => {
try {
const scopes = this._tokenScopes[tokenString]
const token = scopes
? `${tokenString}${persistenceScopeDelimiter}${scopes}`
: tokenString
await this.persistence.noteTokenRemoved(token)
delete this._tokenScopes[tokenString]
await this.persistence.noteTokenRemoved(tokenString)
} catch (e) {
log.error(e)
}

View File

@@ -1,208 +0,0 @@
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

@@ -60,7 +60,7 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
static category = 'issue-tracking'
static route = {
base: 'github/hacktoberfest',
pattern: ':year(2019|2020)/:user/:repo',
pattern: ':year(2019|2020|2021)/:user/:repo',
queryParamSchema,
}
@@ -68,7 +68,7 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
{
title: 'GitHub Hacktoberfest combined status',
namedParams: {
year: '2020',
year: '2021',
user: 'snyk',
repo: 'snyk',
},
@@ -82,7 +82,7 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
{
title: 'GitHub Hacktoberfest combined status (suggestion label override)',
namedParams: {
year: '2020',
year: '2021',
user: 'tmrowco',
repo: 'tmrowapp-contrib',
},
@@ -90,7 +90,7 @@ export default class GithubHacktoberfestCombinedStatus extends GithubAuthV4Servi
suggestion_label: 'help wanted',
},
staticPreview: this.render({
year: '2020',
year: '2021',
suggestedIssueCount: 12,
contributionCount: 8,
daysLeft: 15,

View File

@@ -51,7 +51,7 @@ export default class JenkinsTests extends JenkinsBase {
passed_label: 'passed',
failed_label: 'failed',
skipped_label: 'skipped',
jobUrl: 'https://jenkins.sqlalchemy.org/job/alembic_coverage',
jobUrl: 'https://jenkins.sqlalchemy.org/job/alembic_gerrit',
},
staticPreview: this.render({
passed: 477,

View File

@@ -13,43 +13,34 @@ export const t = await createServiceTester()
// https://wiki.jenkins.io/pages/viewpage.action?pageId=58001258
t.create('Test status')
.get('/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage')
.get('/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_gerrit')
.expectBadge({ label: 'tests', message: isDefaultTestTotals })
t.create('Test status with compact message')
.get(
'/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage',
{
qs: { compact_message: null },
}
)
.get('/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_gerrit', {
qs: { compact_message: null },
})
.expectBadge({ label: 'tests', message: isDefaultCompactTestTotals })
t.create('Test status with custom labels')
.get(
'/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage',
{
qs: {
passed_label: 'good',
failed_label: 'bad',
skipped_label: 'n/a',
},
}
)
.get('/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_gerrit', {
qs: {
passed_label: 'good',
failed_label: 'bad',
skipped_label: 'n/a',
},
})
.expectBadge({ label: 'tests', message: isCustomTestTotals })
t.create('Test status with compact message and custom labels')
.get(
'/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_coverage',
{
qs: {
compact_message: null,
passed_label: '💃',
failed_label: '🤦‍♀️',
skipped_label: '🤷',
},
}
)
.get('/tests.json?jobUrl=https://jenkins.sqlalchemy.org/job/alembic_gerrit', {
qs: {
compact_message: null,
passed_label: '💃',
failed_label: '🤦‍♀️',
skipped_label: '🤷',
},
})
.expectBadge({
label: 'tests',
message: isCustomCompactTestTotals,

View File

@@ -0,0 +1,34 @@
import Joi from 'joi'
import {
isBuildStatus as gIsBuildStatus,
renderBuildStatusBadge as gRenderBuildStatusBadge,
} from '../build-status.js'
const localStatuses = {
blocked: 'inactive',
disabled: 'inactive',
finished: 'orange',
'scheduled-warning': 'orange',
signing: 'orange',
unknown: 'inactive',
unresolvable: 'red',
}
const isBuildStatus = Joi.alternatives().try(
gIsBuildStatus,
Joi.equal(...Object.keys(localStatuses))
)
function renderBuildStatusBadge({ repository, status }) {
const color = localStatuses[status]
if (color) {
return {
message: status.toLowerCase(),
color,
}
} else {
return gRenderBuildStatusBadge({ status: status.toLowerCase() })
}
}
export { isBuildStatus, renderBuildStatusBadge }

View File

@@ -0,0 +1,81 @@
import Joi from 'joi'
import { BaseXmlService } from '../index.js'
import { optionalUrl } from '../validators.js'
import { isBuildStatus, renderBuildStatusBadge } from './obs-build-status.js'
const schema = Joi.object({
status: Joi.object({
'@_code': isBuildStatus,
}).required(),
}).required()
export default class ObsService extends BaseXmlService {
static category = 'build'
static route = {
base: 'obs',
pattern: ':project/:packageName/:repository/:arch',
queryParamSchema: Joi.object({
instance: optionalUrl,
}).required(),
}
static auth = {
userKey: 'obs_user',
passKey: 'obs_pass',
serviceKey: 'obs',
isRequired: true,
}
static examples = [
{
title: 'OBS package build status',
namedParams: {
project: 'openSUSE:Tools',
packageName: 'osc',
repository: 'Debian_11',
arch: 'x86_64',
},
queryParams: { instance: 'https://api.opensuse.org' },
staticPreview: this.render({
repository: 'Debian_11',
status: 'succeeded',
}),
keywords: ['open build service'],
},
]
static defaultBadgeData = { label: 'build' }
static render({ repository, status }) {
return renderBuildStatusBadge({ repository, status })
}
async fetch({ instance, project, packageName, repository, arch }) {
return this._requestXml(
this.authHelper.withBasicAuth({
schema,
url: `${instance}/build/${project}/${repository}/${arch}/${packageName}/_status`,
parserOptions: {
ignoreAttributes: false,
},
})
)
}
async handle(
{ project, packageName, repository, arch },
{ instance = 'https://api.opensuse.org' }
) {
const resp = await this.fetch({
instance,
project,
packageName,
repository,
arch,
})
return this.constructor.render({
repository,
status: resp.status['@_code'],
})
}
}

View File

@@ -0,0 +1,25 @@
import { ServiceTester } from '../tester.js'
import { noToken } from '../test-helpers.js'
import ObsService from './obs.service.js'
import { isBuildStatus } from './obs-build-status.js'
export const t = new ServiceTester({
id: 'obs',
title: 'openSUSE Open Build Service',
})
t.create('status (valid)')
.skipWhen(noToken(ObsService))
.get('/openSUSE:Factory/aaa_base/standard/x86_64.json?label=standard')
.expectBadge({
label: 'standard',
message: isBuildStatus,
})
t.create('status (invalid)')
.skipWhen(noToken(ObsService))
.get('/home:sp1rit/this_package_will_never_exist/repo/arch.json')
.expectBadge({
label: 'build',
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,
url: `/repos/${repoSlug}/license`,
})
`/repos/${repoSlug}/license`
)
try {
const data = JSON.parse(buffer)
if ('html_url' in data) {

View File

@@ -15,7 +15,10 @@ export default class WeblateBase extends BaseJsonService {
async fetch(requestParams) {
return this._requestJson(
this.authHelper.withBearerAuthHeader(requestParams, 'Token')
this.authHelper.withBearerAuthHeader(
requestParams,
'Token' // lgtm [js/hardcoded-credentials]
)
)
}
}