Compare commits

...

112 Commits

Author SHA1 Message Date
github-actions[bot]
e74285ac04 Changelog for Release server-2021-11-04 (#7217)
* Update Changelog

Co-authored-by: release[bot] <actions@users.noreply.github.com>
Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2021-11-04 18:51:37 +00:00
chris48s
b79e00db14 migrate regularUpdate() from request-->got (#7215)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-11-03 19:53:18 +00:00
chris48s
32dd99c540 Two minor docstring fixes (#7216)
* fix XmlElement ref in docstring

* make sure jsdoc picks up renderDownloadsBadge() docstring
2021-11-03 18:49:10 +00:00
chris48s
eb07b60cf0 migrate github badges to use got instead of request; affects [github librariesio] (#7212)
* migrate github badges to use got instead of request

* simplify creation of requestFetcher in libraries.io base

* improve libraries.io connection error test

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-31 16:19:09 +00:00
Caleb Cartwright
8fcde9de85 refactor: use renderDownloadsBadge helper in remaining classes (#7211)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-31 16:10:05 +00:00
Caleb Cartwright
c3d213c65c deprecate David badges (#7197) 2021-10-31 11:01:31 -05:00
Caleb Cartwright
89410389e0 use downloads renderer in next batch of classes, run [hexpm homebrewdownloads jenkinsplugininstalls jetbrainsdownloads jsdelivr modrinth]] (#7210)
* refactor: use downloads renderer in next batch of classes

* chore: run prettier
2021-10-31 10:55:25 -05:00
dependabot[bot]
ee544d13b6 chore(deps): bump fast-xml-parser from 3.20.3 to 3.21.0 (#7201)
Bumps [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) from 3.20.3 to 3.21.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/v3.20.3...v3.21.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-10-29 17:57:38 +00:00
dependabot[bot]
753eae6d57 chore(deps-dev): bump simple-git-hooks from 2.6.1 to 2.7.0 (#7205)
Bumps [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks) from 2.6.1 to 2.7.0.
- [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.6.1...2.7.0)

---
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-10-29 14:40:48 +00:00
dependabot[bot]
54d1612fb1 chore(deps): bump simple-icons from 5.19.0 to 5.20.0 (#7208)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.19.0 to 5.20.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.19.0...5.20.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-29 14:34:51 +00:00
dependabot[bot]
ff14c337bd chore(deps-dev): bump gatsby from 3.14.4 to 3.14.5 (#7207)
Bumps [gatsby](https://github.com/gatsbyjs/gatsby) from 3.14.4 to 3.14.5.
- [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.14.4...gatsby@3.14.5)

---
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-10-29 14:27:06 +00:00
dependabot[bot]
9754e1257e chore(deps-dev): bump cypress from 8.6.0 to 8.7.0 (#7204)
Bumps [cypress](https://github.com/cypress-io/cypress) from 8.6.0 to 8.7.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.6.0...v8.7.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-29 14:18:44 +00:00
dependabot[bot]
4803bf2f13 chore(deps-dev): bump eslint-plugin-jsdoc from 37.0.0 to 37.0.3 (#7206)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 37.0.0 to 37.0.3.
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v37.0.0...v37.0.3)

---
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-10-29 14:10:09 +00:00
dependabot[bot]
56b4088753 chore(deps-dev): bump lint-staged from 11.2.3 to 11.2.6 (#7203)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 11.2.3 to 11.2.6.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v11.2.3...v11.2.6)

---
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>
2021-10-29 14:03:44 +00:00
Caleb Cartwright
c294b12f42 fix: GitHub Package Json test (#7198)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-29 02:19:31 +00:00
Caleb Cartwright
1011cd5483 fix: ensure libraries.io header values are processed numerically (#7196) 2021-10-28 21:09:46 -05:00
Caleb Cartwright
ae58e4a211 Add authentication for Libraries.io-based badges, run [Libraries Bower] (#7080)
* feat: support authentication on Libraries.io requests

* feat: wire up libraries.io config and api provider instantiation

* feat: create libraries.io and bower base classes

* refactor: tweak libraries/bower service classes and tests

* rename request fetcher function/arg

* throw exception when no tokens available

* cleanup old value

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-28 19:21:24 -05:00
chris48s
6e100bf274 fixes and tests for pipenv helpers (#7194) 2021-10-28 17:54:09 +01:00
Caleb Cartwright
e4e7b09009 Add a render helper for downloads badges, run [amo ansible apm chromewebstore clojars conda crates docker dub eclipse gem githubdownloads] (#7163)
* refactor: add render helper for downloads badges

* refactor: use new helper in some download badge classes

* doc renderer function

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-26 23:19:20 +00:00
dependabot[bot]
6df390e43f chore(deps-dev): bump eslint-plugin-jsdoc from 36.1.1 to 37.0.0 (#7193)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 36.1.1 to 37.0.0.
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v36.1.1...v37.0.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-jsdoc
  dependency-type: direct:development
  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-23 03:41:53 +00:00
dependabot[bot]
a2a9780f98 chore(deps-dev): bump gatsby from 3.13.1 to 3.14.4 (#7190)
Bumps [gatsby](https://github.com/gatsbyjs/gatsby) from 3.13.1 to 3.14.4.
- [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.1...gatsby@3.14.4)

---
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>
Co-authored-by: Caleb Cartwright <calebcartwright@users.noreply.github.com>
2021-10-23 03:15:41 +00:00
Caleb Cartwright
50c0be0029 add GitLab Release badge, run all [GitLab] (#7021)
* feat: add GitLabRelease badge

* use single project route param

* add query param for date ordering

* add test for nested subgroup

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-22 22:08:42 -05:00
dependabot[bot]
1dc40b83d8 chore(deps-dev): bump nock from 13.1.3 to 13.1.4 (#7184)
Bumps [nock](https://github.com/nock/nock) from 13.1.3 to 13.1.4.
- [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.3...v13.1.4)

---
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-10-22 21:41:41 -05:00
dependabot[bot]
e30a7f2735 chore(deps-dev): bump nodemon from 2.0.13 to 2.0.14 (#7186)
Bumps [nodemon](https://github.com/remy/nodemon) from 2.0.13 to 2.0.14.
- [Release notes](https://github.com/remy/nodemon/releases)
- [Commits](https://github.com/remy/nodemon/compare/v2.0.13...v2.0.14)

---
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-10-23 02:35:21 +00:00
dependabot[bot]
d1da25b808 chore(deps-dev): bump styled-components from 5.3.1 to 5.3.3 (#7180)
Bumps [styled-components](https://github.com/styled-components/styled-components) from 5.3.1 to 5.3.3.
- [Release notes](https://github.com/styled-components/styled-components/releases)
- [Changelog](https://github.com/styled-components/styled-components/blob/v5.3.3/CHANGELOG.md)
- [Commits](https://github.com/styled-components/styled-components/compare/v5.3.1...v5.3.3)

---
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-10-22 18:23:08 +00:00
dependabot[bot]
379a721ab9 chore(deps-dev): bump babel-preset-gatsby from 1.14.0 to 2.0.0 (#7185)
Bumps [babel-preset-gatsby](https://github.com/gatsbyjs/gatsby/tree/HEAD/packages/babel-preset-gatsby) from 1.14.0 to 2.0.0.
- [Release notes](https://github.com/gatsbyjs/gatsby/releases)
- [Changelog](https://github.com/gatsbyjs/gatsby/blob/babel-preset-gatsby@2.0.0/packages/babel-preset-gatsby/CHANGELOG.md)
- [Commits](https://github.com/gatsbyjs/gatsby/commits/babel-preset-gatsby@2.0.0/packages/babel-preset-gatsby)

---
updated-dependencies:
- dependency-name: babel-preset-gatsby
  dependency-type: direct:development
  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-22 18:14:45 +00:00
dependabot[bot]
222c66a106 chore(deps-dev): bump eslint-plugin-sort-class-members (#7182)
Bumps [eslint-plugin-sort-class-members](https://github.com/bryanrsmith/eslint-plugin-sort-class-members) from 1.11.0 to 1.12.0.
- [Release notes](https://github.com/bryanrsmith/eslint-plugin-sort-class-members/releases)
- [Commits](https://github.com/bryanrsmith/eslint-plugin-sort-class-members/compare/v1.11.0...v1.12.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-sort-class-members
  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-22 18:04:30 +00:00
dependabot[bot]
7725c7d488 chore(deps-dev): bump eslint-plugin-promise from 5.1.0 to 5.1.1 (#7181)
Bumps [eslint-plugin-promise](https://github.com/xjamundx/eslint-plugin-promise) from 5.1.0 to 5.1.1.
- [Release notes](https://github.com/xjamundx/eslint-plugin-promise/releases)
- [Changelog](https://github.com/xjamundx/eslint-plugin-promise/blob/development/CHANGELOG.md)
- [Commits](https://github.com/xjamundx/eslint-plugin-promise/commits)

---
updated-dependencies:
- dependency-name: eslint-plugin-promise
  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-22 17:55:59 +00:00
chris48s
f62dbed843 set content-length header on badge responses (#7179)
closes #7171

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-21 17:09:45 +00:00
chris48s
14f64d17a8 Revert "got --> node-fetch" (#7175)
This reverts commit b209f5c63a.
2021-10-21 18:01:10 +01:00
chris48s
a8214b74cf fix [github] release/tag/download schema (#7170)
closes #7169
2021-10-18 19:20:25 -05:00
cnico
bab818b1c6 Doc self hosting correction (#7024)
* Correction of documentation for configuration

* Better content

* prettier correction

* Review asked to ignore local config file

* Apply suggestions from code review

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-10-17 21:03:07 +00:00
dependabot[bot]
46a366840b chore(deps-dev): bump babel-plugin-istanbul from 6.0.0 to 6.1.1 (#7164)
Bumps [babel-plugin-istanbul](https://github.com/istanbuljs/babel-plugin-istanbul) from 6.0.0 to 6.1.1.
- [Release notes](https://github.com/istanbuljs/babel-plugin-istanbul/releases)
- [Changelog](https://github.com/istanbuljs/babel-plugin-istanbul/blob/master/CHANGELOG.md)
- [Commits](https://github.com/istanbuljs/babel-plugin-istanbul/compare/v6.0.0...v6.1.1)

---
updated-dependencies:
- dependency-name: babel-plugin-istanbul
  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-17 12:52:41 -05:00
dependabot[bot]
f36c34cffa chore(deps): bump simple-icons from 5.18.0 to 5.19.0 (#7165)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.18.0 to 5.19.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.18.0...5.19.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-10-17 12:00:46 -05:00
chris48s
4109d36ad9 Switch callbacks to React.useCallback (#7160)
* upgrade to eslint-plugin-react@7.26.1

* switch callbacks to React.useCallback
2021-10-17 11:42:08 +01:00
Caleb Cartwright
a43c98a89a Supported nested groups on [GitLabPipeline] badge (#7159)
* feat: supported nested groups on gitlab pipeline status badge

* satiate the formatting gods

* use main as default branch

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-16 19:12:02 +00:00
Caleb Cartwright
f8f1286dbb fix: use updated beetle emoji name (#7162)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-16 19:00:35 +00:00
Caleb Cartwright
694b07d636 Support nested groups on [GitLabTag] badge (#7158)
* refactor: support groups on gitlab tag badge

* fix mocked test

* add nested subgroup test

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-16 18:47:03 +00:00
dependabot[bot]
d7316f98b7 chore(deps-dev): bump @types/react-modal from 3.12.1 to 3.13.1 (#7157)
Bumps [@types/react-modal](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-modal) from 3.12.1 to 3.13.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-modal)

---
updated-dependencies:
- dependency-name: "@types/react-modal"
  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-16 15:57:38 +00:00
dependabot[bot]
99cad0f362 chore(deps): bump simple-icons from 5.17.0 to 5.18.0 (#7155)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.17.0 to 5.18.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.17.0...5.18.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-16 15:43:25 +00:00
dependabot[bot]
c0895f1963 chore(deps): bump ioredis from 4.27.10 to 4.28.0 (#7154)
Bumps [ioredis](https://github.com/luin/ioredis) from 4.27.10 to 4.28.0.
- [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.10...v4.28.0)

---
updated-dependencies:
- dependency-name: ioredis
  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-16 15:26:57 +00:00
dependabot[bot]
796bd963d3 chore(deps-dev): bump lint-staged from 11.2.1 to 11.2.3 (#7150)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 11.2.1 to 11.2.3.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v11.2.1...v11.2.3)

---
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: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-16 15:03:37 +00:00
dependabot[bot]
833cd0fb60 chore(deps-dev): bump cypress from 8.5.0 to 8.6.0 (#7152)
Bumps [cypress](https://github.com/cypress-io/cypress) from 8.5.0 to 8.6.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.5.0...v8.6.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-10-16 09:50:20 -05:00
dependabot[bot]
90eee44a70 chore(deps-dev): bump danger from 10.6.6 to 10.7.0 (#7149)
Bumps [danger](https://github.com/danger/danger-js) from 10.6.6 to 10.7.0.
- [Release notes](https://github.com/danger/danger-js/releases)
- [Changelog](https://github.com/danger/danger-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/danger/danger-js/compare/10.6.6...10.7.0)

---
updated-dependencies:
- dependency-name: danger
  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-16 03:39:44 +00:00
dependabot[bot]
e4812ea136 chore(deps-dev): bump eslint-plugin-import from 2.24.2 to 2.25.2 (#7144)
Bumps [eslint-plugin-import](https://github.com/import-js/eslint-plugin-import) from 2.24.2 to 2.25.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.2...v2.25.2)

---
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-10-16 03:22:13 +00:00
dependabot[bot]
8732e3ca8f chore(deps-dev): bump tsd from 0.17.0 to 0.18.0 (#7147)
Bumps [tsd](https://github.com/SamVerschueren/tsd) from 0.17.0 to 0.18.0.
- [Release notes](https://github.com/SamVerschueren/tsd/releases)
- [Commits](https://github.com/SamVerschueren/tsd/compare/v0.17.0...v0.18.0)

---
updated-dependencies:
- dependency-name: tsd
  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-16 03:02:22 +00:00
dependabot[bot]
f546f104b4 chore(deps-dev): bump @types/react-helmet from 6.1.3 to 6.1.4 (#7148)
Bumps [@types/react-helmet](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-helmet) from 6.1.3 to 6.1.4.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-helmet)

---
updated-dependencies:
- dependency-name: "@types/react-helmet"
  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-16 02:50:52 +00:00
dependabot[bot]
0854e90e90 chore(deps-dev): bump humanize-string from 2.1.0 to 3.0.0 (#7145)
Bumps [humanize-string](https://github.com/sindresorhus/humanize-string) from 2.1.0 to 3.0.0.
- [Release notes](https://github.com/sindresorhus/humanize-string/releases)
- [Commits](https://github.com/sindresorhus/humanize-string/compare/v2.1.0...v3.0.0)

---
updated-dependencies:
- dependency-name: humanize-string
  dependency-type: direct:development
  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-16 02:36:16 +00:00
dependabot[bot]
8fcdb7642c chore(deps-dev): bump mocha from 9.1.2 to 9.1.3 (#7146)
Bumps [mocha](https://github.com/mochajs/mocha) from 9.1.2 to 9.1.3.
- [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.2...v9.1.3)

---
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>
2021-10-15 21:24:09 -05:00
dependabot[bot]
03d1e2d741 chore(deps-dev): bump typescript from 4.4.3 to 4.4.4 (#7143)
Bumps [typescript](https://github.com/Microsoft/TypeScript) from 4.4.3 to 4.4.4.
- [Release notes](https://github.com/Microsoft/TypeScript/releases)
- [Commits](https://github.com/Microsoft/TypeScript/compare/v4.4.3...v4.4.4)

---
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>
2021-10-15 20:58:18 -05:00
Chris Carini
f04826a661 Fixing incorrect JetBrains Plugin rating values for [JetBrainsRating] (#7140)
* Fixing incorrect JetBrains Plugin rating values for [JetBrainsRating]

Fixes #7139

* Improving the `votes` `Joi` schema

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-15 04:13:29 +00:00
Caleb Cartwright
bfc2f96dc0 support using release or tag name in [GitHub] Release version badge (#7075)
* feat: support using release or tag name in GitHubRelease version badge

* add comment to query param test

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-14 22:41:27 +00:00
Caleb Cartwright
8a6eba330e feat: support branches in sonar badges (#7065)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-13 21:31:45 +00:00
Dmitriy Fishman
4c8a211feb Typo fix in releases.md (#7137) 2021-10-11 12:54:01 -04:00
Dominik Grzelak
5b5ffce5b2 Add [Modrinth] total downloads badge (#7132)
* Add [Modrinth] total downloads badge

* Check that [Modrinth] downloads value is non-negative

* Remove unnecessary test for negative downloads value

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-10 18:43:10 +00:00
Dominik Grzelak
887ec5b441 Fix hyphens in docs for running code coverage (#7136) 2021-10-10 18:05:22 +00:00
Caleb Cartwright
b3199b23b3 refactor and simplify [AzureDevOpsTests] (#7076)
* refactor: simplify AzureDevOpsTests service tests via helpers

* refactor: apply standard service class patterns to AzureDevOpsTests class

Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-09 16:39:09 +00:00
Caleb Cartwright
de5bd01557 tests: update requires.io service test targets (#7081)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-09 15:36:59 +00:00
dependabot[bot]
4d8a5fdbd1 chore(deps-dev): bump lint-staged from 11.2.0 to 11.2.1 (#7126)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 11.2.0 to 11.2.1.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v11.2.0...v11.2.1)

---
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: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2021-10-09 14:52:34 +00:00
dependabot[bot]
835e69d63d chore(deps-dev): bump @typescript-eslint/eslint-plugin (#7121)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 4.32.0 to 4.33.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.33.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-10-09 14:22:08 +00:00
dependabot[bot]
5a982f62e7 chore(deps): bump graphql from 15.6.0 to 15.6.1 (#7112)
Bumps [graphql](https://github.com/graphql/graphql-js) from 15.6.0 to 15.6.1.
- [Release notes](https://github.com/graphql/graphql-js/releases)
- [Commits](https://github.com/graphql/graphql-js/compare/v15.6.0...v15.6.1)

---
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>
2021-10-09 13:52:00 +00:00
dependabot[bot]
8bcf4e35d6 chore(deps): bump ioredis from 4.27.9 to 4.27.10 (#7108)
Bumps [ioredis](https://github.com/luin/ioredis) from 4.27.9 to 4.27.10.
- [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.9...v4.27.10)

---
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-10-09 05:32:56 +00:00
dependabot[bot]
6ed79b74d8 chore(deps-dev): bump concurrently from 6.2.2 to 6.3.0 (#7109)
Bumps [concurrently](https://github.com/open-cli-tools/concurrently) from 6.2.2 to 6.3.0.
- [Release notes](https://github.com/open-cli-tools/concurrently/releases)
- [Commits](https://github.com/open-cli-tools/concurrently/compare/v6.2.2...v6.3.0)

---
updated-dependencies:
- dependency-name: concurrently
  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-09 04:30:47 +00:00
dependabot[bot]
bc4a61b9e9 chore(deps-dev): bump eslint-plugin-jsdoc from 36.1.0 to 36.1.1 (#7125)
Bumps [eslint-plugin-jsdoc](https://github.com/gajus/eslint-plugin-jsdoc) from 36.1.0 to 36.1.1.
- [Release notes](https://github.com/gajus/eslint-plugin-jsdoc/releases)
- [Commits](https://github.com/gajus/eslint-plugin-jsdoc/compare/v36.1.0...v36.1.1)

---
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-10-08 22:56:58 -05:00
dependabot[bot]
abaf0fe65d chore(deps-dev): bump @babel/core from 7.15.5 to 7.15.8 (#7113)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.15.5 to 7.15.8.
- [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.8/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-10-09 02:43:16 +00:00
dependabot[bot]
b4364303bd chore(deps): bump simple-icons from 5.16.0 to 5.17.0 (#7119)
Bumps [simple-icons](https://github.com/simple-icons/simple-icons) from 5.16.0 to 5.17.0.
- [Release notes](https://github.com/simple-icons/simple-icons/releases)
- [Commits](https://github.com/simple-icons/simple-icons/compare/5.16.0...5.17.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-09 01:28:35 +00:00
dependabot[bot]
47d6135a0c chore(deps-dev): bump @types/react-helmet from 6.1.2 to 6.1.3 (#7115)
Bumps [@types/react-helmet](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/react-helmet) from 6.1.2 to 6.1.3.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/react-helmet)

---
updated-dependencies:
- dependency-name: "@types/react-helmet"
  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-09 00:35:53 +00:00
dependabot[bot]
2e3e619da4 chore(deps-dev): bump c8 from 7.9.0 to 7.10.0 (#7120)
Bumps [c8](https://github.com/bcoe/c8) from 7.9.0 to 7.10.0.
- [Release notes](https://github.com/bcoe/c8/releases)
- [Changelog](https://github.com/bcoe/c8/blob/main/CHANGELOG.md)
- [Commits](https://github.com/bcoe/c8/compare/v7.9.0...v7.10.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-10-09 00:05:27 +00:00
dependabot[bot]
3da7e194d2 chore(deps-dev): bump mocha-junit-reporter from 2.0.0 to 2.0.2 (#7122)
Bumps [mocha-junit-reporter](https://github.com/michaelleeallen/mocha-junit-reporter) from 2.0.0 to 2.0.2.
- [Release notes](https://github.com/michaelleeallen/mocha-junit-reporter/releases)
- [Commits](https://github.com/michaelleeallen/mocha-junit-reporter/compare/v2.0.0...v2.0.2)

---
updated-dependencies:
- dependency-name: mocha-junit-reporter
  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-08 23:36:06 +00:00
dependabot[bot]
b191b3bb8a chore(deps): bump @sentry/node from 6.13.2 to 6.13.3 (#7114)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 6.13.2 to 6.13.3.
- [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.13.2...6.13.3)

---
updated-dependencies:
- dependency-name: "@sentry/node"
  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-08 22:23:28 +00:00
dependabot[bot]
67555ef2d2 chore(deps-dev): bump @types/styled-components from 5.1.14 to 5.1.15 (#7118)
Bumps [@types/styled-components](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/styled-components) from 5.1.14 to 5.1.15.
- [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-10-08 20:54:38 +00:00
dependabot[bot]
b12a4a9964 chore(deps-dev): bump lint-staged from 11.1.2 to 11.2.0 (#7110)
Bumps [lint-staged](https://github.com/okonet/lint-staged) from 11.1.2 to 11.2.0.
- [Release notes](https://github.com/okonet/lint-staged/releases)
- [Commits](https://github.com/okonet/lint-staged/compare/v11.1.2...v11.2.0)

---
updated-dependencies:
- dependency-name: lint-staged
  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-08 19:53:30 +00:00
chris48s
b209f5c63a got --> node-fetch (#6914) 2021-10-08 17:27:50 +01:00
chris48s
0ce98396d7 remove [github] admin routes (#7105) 2021-10-05 17:17:26 +01:00
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
163 changed files with 8392 additions and 6176 deletions

View File

@@ -22,7 +22,7 @@ labels: 'keep-service-tests-green'
<!-- Provide a link to the failing test in CircleCI. -->
:beetle: **Stack trace**
:lady_beetle: **Stack trace**
```
<!-- Provide the complete stack trace from the CircleCI test summary. -->

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,40 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
---
## server-2021-11-04
- migrate regularUpdate() from request-->got [#7215](https://github.com/badges/shields/issues/7215)
- migrate github badges to use got instead of request; affects [github librariesio] [#7212](https://github.com/badges/shields/issues/7212)
- deprecate David badges [#7197](https://github.com/badges/shields/issues/7197)
- fix: ensure libraries.io header values are processed numerically [#7196](https://github.com/badges/shields/issues/7196)
- Add authentication for Libraries.io-based badges, run [Libraries Bower] [#7080](https://github.com/badges/shields/issues/7080)
- fixes and tests for pipenv helpers [#7194](https://github.com/badges/shields/issues/7194)
- add GitLab Release badge, run all [GitLab] [#7021](https://github.com/badges/shields/issues/7021)
- set content-length header on badge responses [#7179](https://github.com/badges/shields/issues/7179)
- fix [github] release/tag/download schema [#7170](https://github.com/badges/shields/issues/7170)
- Supported nested groups on [GitLabPipeline] badge [#7159](https://github.com/badges/shields/issues/7159)
- Support nested groups on [GitLabTag] badge [#7158](https://github.com/badges/shields/issues/7158)
- Fixing incorrect JetBrains Plugin rating values for [JetBrainsRating] [#7140](https://github.com/badges/shields/issues/7140)
- support using release or tag name in [GitHub] Release version badge [#7075](https://github.com/badges/shields/issues/7075)
- feat: support branches in sonar badges [#7065](https://github.com/badges/shields/issues/7065)
- Add [Modrinth] total downloads badge [#7132](https://github.com/badges/shields/issues/7132)
- remove [github] admin routes [#7105](https://github.com/badges/shields/issues/7105)
- Dependency updates
## 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

@@ -33,7 +33,7 @@ class XmlElement {
* @param {object} attrs Refer to individual attrs
* @param {string} attrs.name
* Name of the XML tag
* @param {Array.<string|module:badge-maker/lib/xml-element~XmlElement>} [attrs.content=[]]
* @param {Array.<string|module:badge-maker/lib/xml~XmlElement>} [attrs.content=[]]
* Array of objects to render inside the tag. content may contain a mix of
* string and XmlElement objects. If content is `[]` or ommitted the
* element will be rendered as a self-closing element.

View File

@@ -50,6 +50,8 @@ public:
authorizedOrigins: 'NEXUS_ORIGINS'
npm:
authorizedOrigins: 'NPM_ORIGINS'
obs:
authorizedOrigins: 'OBS_ORIGINS'
sonar:
authorizedOrigins: 'SONAR_ORIGINS'
teamcity:
@@ -84,12 +86,14 @@ private:
jenkins_pass: 'JENKINS_PASS'
jira_user: 'JIRA_USER'
jira_pass: 'JIRA_PASS'
librariesio_tokens: 'LIBRARIESIO_TOKENS'
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'
sl_insight_userUuid: 'SL_INSIGHT_USER_UUID'
sl_insight_apiToken: 'SL_INSIGHT_API_TOKEN'
sonarqube_token: 'SONARQUBE_TOKEN'

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

@@ -420,7 +420,13 @@ class BaseService {
}
static register(
{ camp, handleRequest, githubApiProvider, metricInstance },
{
camp,
handleRequest,
githubApiProvider,
librariesIoApiProvider,
metricInstance,
},
serviceConfig
) {
const { cacheHeaders: cacheHeaderConfig, fetchLimitBytes } = serviceConfig
@@ -444,9 +450,9 @@ class BaseService {
const namedParams = namedParamsForMatch(captureNames, match, this)
const serviceData = await this.invoke(
{
sendAndCacheRequest: fetcher,
sendAndCacheRequestWithCallbacks: request,
sendAndCacheRequest: fetcher, // TODO: rename sendAndCacheRequest
githubApiProvider,
librariesIoApiProvider,
metricHelper,
},
serviceConfig,

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

@@ -64,7 +64,8 @@ async function sendRequest(gotWrapper, url, options) {
}
}
function fetchFactory(fetchLimitBytes) {
const TEN_MB = 10485760
function fetchFactory(fetchLimitBytes = TEN_MB) {
const gotWithLimit = got.extend({
handlers: [
(options, next) => {

View File

@@ -13,6 +13,7 @@ import {
Inaccessible,
InvalidParameter,
Deprecated,
ImproperlyConfigured,
} from './errors.js'
export {
@@ -29,5 +30,6 @@ export {
InvalidResponse,
Inaccessible,
InvalidParameter,
ImproperlyConfigured,
Deprecated,
}

View File

@@ -11,12 +11,14 @@ function streamFromString(str) {
function sendSVG(res, askres, end) {
askres.setHeader('Content-Type', 'image/svg+xml;charset=utf-8')
askres.setHeader('Content-Length', Buffer.byteLength(res, 'utf8'))
end(null, { template: streamFromString(res) })
}
function sendJSON(res, askres, end) {
askres.setHeader('Content-Type', 'application/json')
askres.setHeader('Access-Control-Allow-Origin', '*')
askres.setHeader('Content-Length', Buffer.byteLength(res, 'utf8'))
end(null, { template: streamFromString(res) })
}

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

@@ -1,93 +1,62 @@
import requestModule from 'request'
import { Inaccessible, InvalidResponse } from '../base-service/errors.js'
/**
* @module
*/
import { InvalidResponse } from '../base-service/errors.js'
import { fetchFactory } from '../../core/base-service/got.js'
import checkErrorResponse from '../../core/base-service/check-error-response.js'
const fetcher = fetchFactory()
// Map from URL to { timestamp: last fetch time, data: data }.
let regularUpdateCache = Object.create(null)
// url: a string, scraper: a function that takes string data at that URL.
// interval: number in milliseconds.
// cb: a callback function that takes an error and data returned by the scraper.
//
// To use this from a service:
//
// import { promisify } from 'util'
// import { regularUpdate } from '../../core/legacy/regular-update.js'
//
// function getThing() {
// return promisify(regularUpdate)({
// url: ...,
// ...
// })
// }
//
// in handle():
//
// const thing = await getThing()
function regularUpdate(
{
url,
intervalMillis,
json = true,
scraper = buffer => buffer,
options = {},
request = requestModule,
},
cb
) {
/**
* Make a HTTP request using an in-memory cache
*
* @param {object} attrs Refer to individual attrs
* @param {string} attrs.url URL to request
* @param {number} attrs.intervalMillis Number of milliseconds to keep cached value for
* @param {boolean} [attrs.json=true] True if we expect to parse the response as JSON
* @param {Function} [attrs.scraper=buffer => buffer] Function to extract value from the response
* @param {object} [attrs.options={}] Options to pass to got
* @param {Function} [attrs.requestFetcher=fetcher] Custom fetch function
* @returns {*} Parsed response
*/
async function regularUpdate({
url,
intervalMillis,
json = true,
scraper = buffer => buffer,
options = {},
requestFetcher = fetcher,
}) {
const timestamp = Date.now()
const cached = regularUpdateCache[url]
if (cached != null && timestamp - cached.timestamp < intervalMillis) {
cb(null, cached.data)
return
return cached.data
}
request(url, options, (err, res, buffer) => {
if (err != null) {
cb(
new Inaccessible({
prettyMessage: 'intermediate resource inaccessible',
underlyingError: err,
})
)
return
}
if (res.statusCode < 200 || res.statusCode >= 300) {
cb(
new InvalidResponse({
prettyMessage: 'intermediate resource inaccessible',
})
)
}
const { buffer } = await checkErrorResponse({})(
await requestFetcher(url, options)
)
let reqData
if (json) {
try {
reqData = JSON.parse(buffer)
} catch (e) {
cb(
new InvalidResponse({
prettyMessage: 'unparseable intermediate json response',
underlyingError: e,
})
)
return
}
} else {
reqData = buffer
}
let data
let reqData
if (json) {
try {
data = scraper(reqData)
reqData = JSON.parse(buffer)
} catch (e) {
cb(e)
return
throw new InvalidResponse({
prettyMessage: 'unparseable intermediate json response',
underlyingError: e,
})
}
} else {
reqData = buffer
}
regularUpdateCache[url] = { timestamp, data }
cb(null, data)
})
const data = scraper(reqData)
regularUpdateCache[url] = { timestamp, data }
return data
}
function clearRegularUpdateCache() {

View File

@@ -1,18 +0,0 @@
function constEq(a, b) {
if (a.length !== b.length) {
return false
}
let zero = 0
for (let i = 0; i < a.length; i++) {
zero |= a.charCodeAt(i) ^ b.charCodeAt(i)
}
return zero === 0
}
function makeSecretIsValid(shieldsSecret) {
return function secretIsValid(secret = '') {
return shieldsSecret && constEq(secret, shieldsSecret)
}
}
export { makeSecretIsValid }

View File

@@ -11,6 +11,7 @@ import Camp from '@shields_io/camp'
import originalJoi from 'joi'
import makeBadge from '../../badge-maker/lib/make-badge.js'
import GithubConstellation from '../../services/github/github-constellation.js'
import LibrariesIoConstellation from '../../services/librariesio/librariesio-constellation.js'
import { setRoutes } from '../../services/suggest.js'
import { loadServiceClasses } from '../base-service/loader.js'
import { makeSend } from '../base-service/legacy-result-sender.js'
@@ -134,6 +135,7 @@ const publicConfigSchema = Joi.object({
}).default({ authorizedOrigins: [] }),
nexus: defaultService,
npm: defaultService,
obs: defaultService,
sonar: defaultService,
teamcity: defaultService,
weblate: defaultService,
@@ -169,12 +171,14 @@ const privateConfigSchema = Joi.object({
jira_pass: Joi.string(),
bitbucket_server_username: Joi.string(),
bitbucket_server_password: Joi.string(),
librariesio_tokens: Joi.arrayFromString().items(Joi.string()),
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(),
sl_insight_userUuid: Joi.string(),
sl_insight_apiToken: Joi.string(),
sonarqube_token: Joi.string(),
@@ -239,6 +243,10 @@ class Server {
private: privateConfig,
})
this.librariesioConstellation = new LibrariesIoConstellation({
private: privateConfig,
})
if (publicConfig.metrics.prometheus.enabled) {
this.metricInstance = new PrometheusMetrics()
if (publicConfig.metrics.influx.enabled) {
@@ -411,10 +419,17 @@ class Server {
async registerServices() {
const { config, camp, metricInstance } = this
const { apiProvider: githubApiProvider } = this.githubConstellation
const { apiProvider: librariesIoApiProvider } =
this.librariesioConstellation
;(await loadServiceClasses()).forEach(serviceClass =>
serviceClass.register(
{ camp, handleRequest, githubApiProvider, metricInstance },
{
camp,
handleRequest,
githubApiProvider,
librariesIoApiProvider,
metricInstance,
},
{
handleInternalErrors: config.public.handleInternalErrors,
cacheHeaders: config.public.cacheHeaders,

View File

@@ -87,12 +87,28 @@ describe('The server', function () {
)
})
it('should produce json badges', async function () {
it('should produce SVG badges with expected headers', async function () {
const { statusCode, headers } = await got(
`${baseUrl}:fruit-apple-green.svg`
)
expect(statusCode).to.equal(200)
expect(headers['content-type']).to.equal('image/svg+xml;charset=utf-8')
expect(headers['content-length']).to.equal('1130')
})
it('correctly calculates the content-length header for multi-byte unicode characters', async function () {
const { headers } = await got(`${baseUrl}:fruit-apple🍏-green.json`)
expect(headers['content-length']).to.equal('100')
})
it('should produce JSON badges with expected headers', async function () {
const { statusCode, body, headers } = await got(
`${baseUrl}twitter/follow/_Pyves.json`
`${baseUrl}:fruit-apple-green.json`
)
expect(statusCode).to.equal(200)
expect(headers['content-type']).to.equal('application/json')
expect(headers['access-control-allow-origin']).to.equal('*')
expect(headers['content-length']).to.equal('92')
expect(() => JSON.parse(body)).not.to.throw()
})

View File

@@ -80,6 +80,10 @@ class Token {
return this.usesRemaining <= 0 && !this.hasReset
}
get decrementedUsesRemaining() {
return this._usesRemaining - 1
}
/**
* Update the uses remaining and next reset time for a token.
*
@@ -328,29 +332,6 @@ class TokenPool {
this.fifoQueue.forEach(visit)
this.priorityQueue.forEach(visit)
}
allValidTokenIds() {
const result = []
this.forEach(({ id }) => result.push(id))
return result
}
serializeDebugInfo({ sanitize = true } = {}) {
const maybeSanitize = sanitize ? id => sanitizeToken(id) : id => id
const priorityQueue = []
this.priorityQueue.forEach(t =>
priorityQueue.push(t.getDebugInfo({ sanitize }))
)
return {
utcEpochSeconds: getUtcEpochSeconds(),
allValidTokenIds: this.allValidTokenIds().map(maybeSanitize),
fifoQueue: this.fifoQueue.map(t => t.getDebugInfo({ sanitize })),
priorityQueue,
sanitized: sanitize,
}
}
}
export { sanitizeToken, Token, TokenPool }

View File

@@ -19,10 +19,6 @@ describe('The token pool', function () {
ids.forEach(id => tokenPool.add(id))
})
it('allValidTokenIds() should return the full list', function () {
expect(tokenPool.allValidTokenIds()).to.deep.equal(ids)
})
it('should yield the expected tokens', function () {
ids.forEach(id =>
times(batchSize, () => expect(tokenPool.next().id).to.equal(id))
@@ -38,67 +34,6 @@ describe('The token pool', function () {
)
})
describe('serializeDebugInfo should initially return the expected', function () {
beforeEach(function () {
sinon.useFakeTimers({ now: 1544307744484 })
})
afterEach(function () {
sinon.restore()
})
context('sanitize is not specified', function () {
it('returns fully sanitized results', function () {
// This is `sha()` of '1', '2', '3', '4', '5'. These are written
// literally for avoidance of doubt as to whether sanitization is
// happening.
const sanitizedIds = [
'6b86b273ff34fce19d6b804eff5a3f5747ada4eaa22f1d49c01e52ddb7875b4b',
'd4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35',
'4e07408562bedb8b60ce05c1decfe3ad16b72230967de01f640b7e4729b49fce',
'4b227777d4dd1fc61c6f884f48641d02b4d121d3fd328cb08b5531fcacdabf8a',
'ef2d127de37b942baad06145e54b0c619a1f22327b2ebbcfbec78f5564afe39d',
]
expect(tokenPool.serializeDebugInfo()).to.deep.equal({
allValidTokenIds: sanitizedIds,
priorityQueue: [],
fifoQueue: sanitizedIds.map(id => ({
data: '[redacted]',
id,
isFrozen: false,
isValid: true,
nextReset: Token.nextResetNever,
usesRemaining: batchSize,
})),
sanitized: true,
utcEpochSeconds: 1544307744,
})
})
})
context('with sanitize: false', function () {
it('returns unsanitized results', function () {
expect(tokenPool.serializeDebugInfo({ sanitize: false })).to.deep.equal(
{
allValidTokenIds: ids,
priorityQueue: [],
fifoQueue: ids.map(id => ({
data: undefined,
id,
isFrozen: false,
isValid: true,
nextReset: Token.nextResetNever,
usesRemaining: batchSize,
})),
sanitized: false,
utcEpochSeconds: 1544307744,
}
)
})
})
})
context('tokens are marked exhausted immediately', function () {
it('should be exhausted', function () {
ids.forEach(() => {

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

@@ -2,7 +2,7 @@
Shields is a community project that is stewarded by a handful of core maintainers who contribute on a volunteer basis. We do our best to maintain the availability and reliability of the service, and enhance and improve the project overall. However, if you've spotted something wrong or would like to see a specific feature implemented, please consider helping us resolve it by submitting a pull request. All community contributions, even documentation improvements, are welcome!
https://github.com/badges/shields is a monorepo and hosts the Shields frontend and server code as well as the [badge-maker][npm package] NPM library (and the [badge design specification](https://github.com/badges/shields/tree/master/spec)). The packaging and release processes for these items is described in the respective sections below.
https://github.com/badges/shields is a monorepo and hosts the Shields frontend and server code as well as the [badge-maker][npm package] NPM library (and the [badge design specification](https://github.com/badges/shields/tree/master/spec)). The packaging and release processes for these items are described in the respective sections below.
## badge-maker package

View File

@@ -94,20 +94,18 @@ Sending build context to Docker daemon 3.923 MB
Successfully built 4471b442c220
```
Optionally, create a file called `shields.env` that contains the needed
configuration. See [server-secrets.md](server-secrets.md) and [config/custom-environment-variables.yml](/config/custom-environment-variables.yml) for examples.
Optionally, alter the default values for configuration by setting them via [environment variables](https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file).
See [server-secrets.md](server-secrets.md) and [config/custom-environment-variables.yml](/config/custom-environment-variables.yml) for possible values.
In [config/custom-environment-variables.yml](/config/custom-environment-variables.yml), environment variable names are specified as the quoted, uppercase key values (e.g. `GH_TOKEN`).
Then run the container:
Then run the container, and be sure to specify the same mapped port as the one Shields is listening on :
```console
$ docker run --rm -p 8080:80 --name shields shields
# or if you have shields.env file, run the following instead
$ docker run --rm -p 8080:80 --env-file shields.env --name shields shields
$ docker run --rm -p 8080:8080 --env PORT=8080 --name shields shieldsio/shields:next
> badge-maker@3.0.0 start /usr/src/app
> node server.js
http://[::1]/
Configuration:
...
0916211515 Server is starting up: http://0.0.0.0:8080/
```
Assuming Docker is running locally, you should be able to get to the
@@ -117,8 +115,6 @@ If you run Docker in a virtual machine (such as boot2docker or Docker Machine)
then you will need to replace `localhost` with the IP address of that virtual
machine.
[shields.example.env]: ../shields.example.env
## Raster server
If you want to host PNG badges, you can also self-host a [raster server][]

View File

@@ -174,6 +174,24 @@ access to a private Jenkins CI instance.
Provide a username and password to give your self-hosted Shields installation
access to a private JIRA instance.
### Libraries.io/Bower
- `LIBRARIESIO_TOKENS` (yml: `private.librariesio_tokens`)
Note that the Bower badges utilize the Libraries.io API, so use this secret for both Libraries.io badges and/or Bower badges.
Just like the `*_ORIGINS` type secrets, this value can accept a single token as a string, or a group of tokens provided as an array of strings. For example:
```yaml
private:
librariesio_tokens: my-token
## Or
private:
librariesio_tokens: [my-token some-other-token]
```
When using the environment variable with multiple tokens, be sure to use a space to separate the tokens, e.g. `LIBRARIESIO_TOKENS="my-token some-other-token"`
### Nexus
- `NEXUS_ORIGINS` (yml: `public.services.nexus.authorizedOrigins`)
@@ -193,6 +211,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

@@ -254,7 +254,7 @@ By checking code coverage, we can make sure we've covered all our bases.
We can generate a coverage report and open it:
```
npm run coverage:test:services -- --only=wercker
npm run coverage:test:services -- -- --only=wercker
npm run coverage:report:open
```

View File

@@ -43,9 +43,12 @@ function Example({
exampleData: RenderableExample
isBadgeSuggestion: boolean
}): JSX.Element {
function handleClick(): void {
onClick(exampleData, isBadgeSuggestion)
}
const handleClick = React.useCallback(
function (): void {
onClick(exampleData, isBadgeSuggestion)
},
[exampleData, isBadgeSuggestion, onClick]
)
let exampleUrl, previewUrl
if (isBadgeSuggestion) {

View File

@@ -50,13 +50,16 @@ function _CopiedContentIndicator(
},
}))
function handlePoseComplete(): void {
if (pose === 'effectStart') {
setPose('effectEnd')
} else {
setPose('hidden')
}
}
const handlePoseComplete = React.useCallback(
function (): void {
if (pose === 'effectStart') {
setPose('effectEnd')
} else {
setPose('hidden')
}
},
[pose, setPose]
)
return (
<ContentAnchor>

View File

@@ -40,10 +40,13 @@ export default function Customizer({
const [markup, setMarkup] = useState<string>()
const [message, setMessage] = useState<string>()
function generateBuiltBadgeUrl(): string {
const suffix = queryString ? `?${queryString}` : ''
return `${baseUrl}${path}${suffix}`
}
const generateBuiltBadgeUrl = React.useCallback(
function (): string {
const suffix = queryString ? `?${queryString}` : ''
return `${baseUrl}${path}${suffix}`
},
[baseUrl, path, queryString]
)
function renderLivePreview(): JSX.Element {
// There are some usability issues here. It would be better if the message
@@ -67,28 +70,31 @@ export default function Customizer({
)
}
async function copyMarkup(markupFormat: MarkupFormat): Promise<void> {
const builtBadgeUrl = generateBuiltBadgeUrl()
const markup = generateMarkup({
badgeUrl: builtBadgeUrl,
link,
title,
markupFormat,
})
const copyMarkup = React.useCallback(
async function (markupFormat: MarkupFormat): Promise<void> {
const builtBadgeUrl = generateBuiltBadgeUrl()
const markup = generateMarkup({
badgeUrl: builtBadgeUrl,
link,
title,
markupFormat,
})
try {
await clipboardCopy(markup)
} catch (e) {
setMessage('Copy failed')
setMarkup(markup)
return
}
try {
await clipboardCopy(markup)
} catch (e) {
setMessage('Copy failed')
setMarkup(markup)
return
}
setMarkup(markup)
if (indicatorRef.current) {
indicatorRef.current.trigger()
}
}
if (indicatorRef.current) {
indicatorRef.current.trigger()
}
},
[generateBuiltBadgeUrl, link, title, setMessage, setMarkup]
)
function renderMarkupAndLivePreview(): JSX.Element {
return (
@@ -110,26 +116,32 @@ export default function Customizer({
)
}
function handlePathChange({
path,
isComplete,
}: {
path: string
isComplete: boolean
}): void {
setPath(path)
setPathIsComplete(isComplete)
}
const handlePathChange = React.useCallback(
function ({
path,
isComplete,
}: {
path: string
isComplete: boolean
}): void {
setPath(path)
setPathIsComplete(isComplete)
},
[setPath, setPathIsComplete]
)
function handleQueryStringChange({
queryString,
isComplete,
}: {
queryString: string
isComplete: boolean
}): void {
setQueryString(queryString)
}
const handleQueryStringChange = React.useCallback(
function ({
queryString,
isComplete,
}: {
queryString: string
isComplete: boolean
}): void {
setQueryString(queryString)
},
[setQueryString]
)
return (
<form action="">

View File

@@ -149,14 +149,17 @@ export default function PathBuilder({
}
}, [tokens, namedParams, onChange])
function handleTokenChange({
target: { name, value },
}: ChangeEvent<HTMLInputElement | HTMLSelectElement>): void {
setNamedParams({
...namedParams,
[name]: value,
})
}
const handleTokenChange = React.useCallback(
function ({
target: { name, value },
}: ChangeEvent<HTMLInputElement | HTMLSelectElement>): void {
setNamedParams({
...namedParams,
[name]: value,
})
},
[setNamedParams, namedParams]
)
function renderLiteral(
literal: string,

View File

@@ -270,18 +270,24 @@ export default function QueryStringBuilder({
}, {} as Record<BadgeOptionName, string>)
)
function handleServiceQueryParamChange({
target: { name, type: targetType, checked, value },
}: ChangeEvent<HTMLInputElement>): void {
const outValue = targetType === 'checkbox' ? checked : value
setQueryParams({ ...queryParams, [name]: outValue })
}
const handleServiceQueryParamChange = React.useCallback(
function ({
target: { name, type: targetType, checked, value },
}: ChangeEvent<HTMLInputElement>): void {
const outValue = targetType === 'checkbox' ? checked : value
setQueryParams({ ...queryParams, [name]: outValue })
},
[setQueryParams, queryParams]
)
function handleBadgeOptionChange({
target: { name, value },
}: ChangeEvent<HTMLInputElement>): void {
setBadgeOptions({ ...badgeOptions, [name]: value })
}
const handleBadgeOptionChange = React.useCallback(
function ({
target: { name, value },
}: ChangeEvent<HTMLInputElement>): void {
setBadgeOptions({ ...badgeOptions, [name]: value })
},
[setBadgeOptions, badgeOptions]
)
useEffect(() => {
if (onChange) {

View File

@@ -86,24 +86,30 @@ export default function GetMarkupButton({
Select<Option>
>
async function onControlMouseDown(event: MouseEvent): Promise<void> {
if (onMarkupRequested) {
await onMarkupRequested('link')
}
if (selectRef.current) {
selectRef.current.blur()
}
}
const onControlMouseDown = React.useCallback(
async function (event: MouseEvent): Promise<void> {
if (onMarkupRequested) {
await onMarkupRequested('link')
}
if (selectRef.current) {
selectRef.current.blur()
}
},
[onMarkupRequested, selectRef]
)
async function onOptionClick(
// Eeesh.
value: Option | readonly Option[] | null | undefined
): Promise<void> {
const { value: markupFormat } = value as Option
if (onMarkupRequested) {
await onMarkupRequested(markupFormat)
}
}
const onOptionClick = React.useCallback(
async function onOptionClick(
// Eeesh.
value: Option | readonly Option[] | null | undefined
): Promise<void> {
const { value: markupFormat } = value as Option
if (onMarkupRequested) {
await onMarkupRequested(markupFormat)
}
},
[onMarkupRequested]
)
return (
// TODO It doesn't seem to be possible to check the types and wrap with

View File

@@ -44,33 +44,39 @@ export default function DynamicBadgeMaker({
const isValid =
values.datatype && values.label && values.dataUrl && values.query
function onChange({
target: { name, value },
}: ChangeEvent<HTMLInputElement | HTMLSelectElement>): void {
setValues({
...values,
[name]: value,
})
}
const onChange = React.useCallback(
function ({
target: { name, value },
}: ChangeEvent<HTMLInputElement | HTMLSelectElement>): void {
setValues({
...values,
[name]: value,
})
},
[values]
)
function onSubmit(e: React.FormEvent): void {
e.preventDefault()
const onSubmit = React.useCallback(
function onSubmit(e: React.FormEvent): void {
e.preventDefault()
const { datatype, label, dataUrl, query, color, prefix, suffix } = values
window.open(
dynamicBadgeUrl({
baseUrl,
datatype,
label,
dataUrl,
query,
color,
prefix,
suffix,
}),
'_blank'
)
}
const { datatype, label, dataUrl, query, color, prefix, suffix } = values
window.open(
dynamicBadgeUrl({
baseUrl,
datatype,
label,
dataUrl,
query,
color,
prefix,
suffix,
}),
'_blank'
)
},
[baseUrl, values]
)
return (
<form onSubmit={onSubmit}>

View File

@@ -54,24 +54,28 @@ export default function Main({
const searchTimeout = useRef(0)
const baseUrl = getBaseUrl()
function performSearch(query: string): void {
setSearchIsInProgress(false)
const performSearch = React.useCallback(
function (query: string): void {
setSearchIsInProgress(false)
setQueryIsTooShort(query.length === 1)
setQueryIsTooShort(query.length === 1)
if (query.length >= 2) {
const flat = ServiceDefinitionSetHelper.create(services)
.notDeprecated()
.search(query)
.toArray()
setSearchResults(groupBy(flat, 'category'))
} else {
setSearchResults(undefined)
}
}
if (query.length >= 2) {
const flat = ServiceDefinitionSetHelper.create(services)
.notDeprecated()
.search(query)
.toArray()
setSearchResults(groupBy(flat, 'category'))
} else {
setSearchResults(undefined)
}
},
[setSearchIsInProgress, setQueryIsTooShort, setSearchResults]
)
function searchQueryChanged(query: string): void {
/*
const searchQueryChanged = React.useCallback(
function (query: string): void {
/*
Add a small delay before showing search results
so that we wait until the user has stopped typing
before we start loading stuff.
@@ -81,22 +85,27 @@ export default function Main({
b) stops the page from 'flashing' as the user types, like this:
https://user-images.githubusercontent.com/7288322/42600206-9b278470-85b5-11e8-9f63-eb4a0c31cb4a.gif
*/
setSearchIsInProgress(true)
window.clearTimeout(searchTimeout.current)
searchTimeout.current = window.setTimeout(() => performSearch(query), 500)
}
setSearchIsInProgress(true)
window.clearTimeout(searchTimeout.current)
searchTimeout.current = window.setTimeout(() => performSearch(query), 500)
},
[setSearchIsInProgress, performSearch]
)
function exampleClicked(
example: RenderableExample,
isSuggestion: boolean
): void {
setSelectedExample(example)
setSelectedExampleIsSuggestion(isSuggestion)
}
const exampleClicked = React.useCallback(
function (example: RenderableExample, isSuggestion: boolean): void {
setSelectedExample(example)
setSelectedExampleIsSuggestion(isSuggestion)
},
[setSelectedExample, setSelectedExampleIsSuggestion]
)
function dismissMarkupModal(): void {
setSelectedExample(undefined)
}
const dismissMarkupModal = React.useCallback(
function (): void {
setSelectedExample(undefined)
},
[setSelectedExample]
)
function Category({
category,

View File

@@ -18,21 +18,27 @@ export default function StaticBadgeMaker({
const isValid = values.message && values.color
function onChange({
target: { name, value },
}: ChangeEvent<HTMLInputElement | HTMLSelectElement>): void {
setValues({
...values,
[name]: value,
})
}
const onChange = React.useCallback(
function onChange({
target: { name, value },
}: ChangeEvent<HTMLInputElement | HTMLSelectElement>): void {
setValues({
...values,
[name]: value,
})
},
[setValues, values]
)
function onSubmit(e: React.FormEvent): void {
e.preventDefault()
const onSubmit = React.useCallback(
function (e: React.FormEvent): void {
e.preventDefault()
const { label, message, color } = values
window.open(staticBadgeUrl({ baseUrl, label, message, color }), '_blank')
}
const { label, message, color } = values
window.open(staticBadgeUrl({ baseUrl, label, message, color }), '_blank')
},
[baseUrl, values]
)
return (
<form onSubmit={onSubmit}>

View File

@@ -41,41 +41,47 @@ export default function SuggestionAndSearch({
const [projectUrl, setProjectUrl] = useState<string>()
const [suggestions, setSuggestions] = useState<SuggestionItem[]>([])
function onQueryChanged({
target: { value: query },
}: ChangeEvent<HTMLInputElement>): void {
const isUrl = query.startsWith('https://') || query.startsWith('http://')
setIsUrl(isUrl)
setProjectUrl(isUrl ? query : undefined)
const onQueryChanged = React.useCallback(
function ({
target: { value: query },
}: ChangeEvent<HTMLInputElement>): void {
const isUrl = query.startsWith('https://') || query.startsWith('http://')
setIsUrl(isUrl)
setProjectUrl(isUrl ? query : undefined)
queryChangedDebounced.current(query)
}
queryChangedDebounced.current(query)
},
[setIsUrl, setProjectUrl, queryChangedDebounced]
)
async function getSuggestions(): Promise<void> {
if (!projectUrl) {
setSuggestions([])
return
}
const getSuggestions = React.useCallback(
async function (): Promise<void> {
if (!projectUrl) {
setSuggestions([])
return
}
setInProgress(true)
setInProgress(true)
const fetch = window.fetch || fetchPonyfill
const res = await fetch(
`${baseUrl}/$suggest/v1?url=${encodeURIComponent(projectUrl)}`
)
let suggestions = [] as SuggestionItem[]
try {
const json = (await res.json()) as SuggestionResponse
// This doesn't validate the response. The default value here prevents
// a crash if the server returns {"err":"Disallowed"}.
suggestions = json.suggestions || []
} catch (e) {
suggestions = []
}
const fetch = window.fetch || fetchPonyfill
const res = await fetch(
`${baseUrl}/$suggest/v1?url=${encodeURIComponent(projectUrl)}`
)
let suggestions = [] as SuggestionItem[]
try {
const json = (await res.json()) as SuggestionResponse
// This doesn't validate the response. The default value here prevents
// a crash if the server returns {"err":"Disallowed"}.
suggestions = json.suggestions || []
} catch (e) {
suggestions = []
}
setInProgress(false)
setSuggestions(suggestions)
}
setInProgress(false)
setSuggestions(suggestions)
},
[setSuggestions, setInProgress, baseUrl, projectUrl]
)
function renderSuggestions(): JSX.Element | null {
if (suggestions.length === 0) {
@@ -105,6 +111,8 @@ export default function SuggestionAndSearch({
)
}
// TODO: Warning: A future version of React will block javascript: URLs as a security precaution
// how else to do this?
return (
<section>
<form action="javascript:void 0" autoComplete="off">

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 ''

9723
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.3",
"@shields_io/camp": "^18.1.1",
"badge-maker": "file:badge-maker",
"bytes": "^3.1.0",
@@ -34,16 +34,16 @@
"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.21.0",
"glob": "^7.2.0",
"global-agent": "^3.0.0",
"got": "11.8.2",
"graphql": "^15.5.3",
"graphql": "^15.6.1",
"graphql-tag": "^2.12.5",
"ioredis": "4.27.9",
"ioredis": "4.28.0",
"joi": "17.4.2",
"joi-extension-semver": "5.0.0",
"js-yaml": "^4.1.0",
@@ -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.20.0",
"webextension-store-meta": "^1.0.4",
"xmldom": "~0.6.0",
"xpath": "~0.0.32"
@@ -142,25 +142,25 @@
]
},
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/core": "^7.15.8",
"@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",
"@types/node": "^16.7.10",
"@types/react-helmet": "^6.1.2",
"@types/react-modal": "^3.12.1",
"@types/react-helmet": "^6.1.4",
"@types/react-modal": "^3.13.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",
"@types/styled-components": "5.1.15",
"@typescript-eslint/eslint-plugin": "^4.33.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",
"c8": "^7.9.0",
"babel-plugin-istanbul": "^6.1.1",
"babel-preset-gatsby": "^2.0.0",
"c8": "^7.10.0",
"caller": "^1.0.1",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
@@ -168,9 +168,9 @@
"chai-string": "^1.4.0",
"child-process-promise": "^2.2.1",
"clipboard-copy": "^4.0.1",
"concurrently": "^6.2.1",
"cypress": "^8.4.0",
"danger": "^10.6.6",
"concurrently": "^6.3.0",
"cypress": "^8.7.0",
"danger": "^10.7.0",
"danger-plugin-no-test-shortcuts": "^2.0.0",
"deepmerge": "^4.2.2",
"eslint": "^7.32.0",
@@ -180,41 +180,41 @@
"eslint-config-standard-react": "^11.0.1",
"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-import": "^2.25.2",
"eslint-plugin-jsdoc": "^37.0.3",
"eslint-plugin-mocha": "^9.0.0",
"eslint-plugin-no-extension-in-require": "^0.2.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-react": "^7.24.0",
"eslint-plugin-promise": "^5.1.1",
"eslint-plugin-react": "^7.26.1",
"eslint-plugin-react-hooks": "^4.2.0",
"eslint-plugin-sort-class-members": "^1.11.0",
"eslint-plugin-sort-class-members": "^1.12.0",
"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",
"humanize-string": "^2.1.0",
"gatsby": "3.14.5",
"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": "^3.0.0",
"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.2",
"lint-staged": "^11.2.6",
"lodash.debounce": "^4.0.8",
"lodash.difference": "^4.5.0",
"minimist": "^1.2.5",
"mocha": "^9.1.1",
"mocha": "^9.1.3",
"mocha-env-reporter": "^4.0.0",
"mocha-junit-reporter": "^2.0.0",
"mocha-junit-reporter": "^2.0.2",
"mocha-yaml-loader": "^1.0.3",
"nock": "13.1.3",
"node-mocks-http": "^1.10.1",
"nodemon": "^2.0.12",
"nock": "13.1.4",
"node-mocks-http": "^1.11.0",
"nodemon": "^2.0.14",
"npm-run-all": "^4.1.5",
"open-cli": "^7.0.1",
"portfinder": "^1.0.28",
@@ -230,15 +230,15 @@
"redis-server": "^1.2.2",
"rimraf": "^3.0.2",
"sazerac": "^2.0.0",
"simple-git-hooks": "^2.6.1",
"simple-git-hooks": "^2.7.0",
"sinon": "^11.1.2",
"sinon-chai": "^3.7.0",
"snap-shot-it": "^7.9.6",
"start-server-and-test": "1.14.0",
"styled-components": "^5.3.1",
"styled-components": "^5.3.3",
"ts-mocha": "^8.0.0",
"tsd": "^0.17.0",
"typescript": "^4.4.3"
"tsd": "^0.18.0",
"typescript": "^4.4.4"
},
"engines": {
"node": "^14.17.1",

View File

@@ -1,19 +0,0 @@
#!/usr/bin/env fish
#
# Back up the GitHub tokens from each production server.
#
if test (count $argv) -lt 1
echo Usage: (basename (status -f)) shields_secret
end
set shields_secret $argv[1]
function do_backup
set server $argv[1]
curl --insecure -u ":$shields_secret" "https://$server.servers.shields.io/\$github-auth/tokens" > "$server""_tokens.json"
end
for server in s0 s1 s2
do_backup $server
end

View File

@@ -1,5 +1,4 @@
import { metric } from '../text-formatters.js'
import { downloadCount } from '../color-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { redirector } from '../index.js'
import { BaseAmoService, keywords } from './amo-base.js'
@@ -28,10 +27,7 @@ class AmoWeeklyDownloads extends BaseAmoService {
static defaultBadgeData = { label: 'downloads' }
static render({ downloads }) {
return {
message: `${metric(downloads)}/week`,
color: downloadCount(downloads),
}
return renderDownloadsBadge({ downloads, interval: 'week' })
}
async handle({ addonId }) {

View File

@@ -1,4 +1,4 @@
import { metric } from '../text-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { BaseAmoService, keywords } from './amo-base.js'
export default class AmoUsers extends BaseAmoService {
@@ -16,11 +16,8 @@ export default class AmoUsers extends BaseAmoService {
static defaultBadgeData = { label: 'users' }
static render({ users }) {
return {
message: metric(users),
color: 'blue',
}
static render({ users: downloads }) {
return renderDownloadsBadge({ downloads, colorOverride: 'blue' })
}
async handle({ addonId }) {

View File

@@ -1,6 +1,5 @@
import Joi from 'joi'
import { downloadCount } from '../color-formatters.js'
import { metric } from '../text-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
@@ -32,22 +31,15 @@ class AnsibleGalaxyRoleDownloads extends AnsibleGalaxyRole {
{
title: 'Ansible Role',
namedParams: { roleId: '3078' },
staticPreview: this.render({ downloads: 76 }),
staticPreview: renderDownloadsBadge({ downloads: 76 }),
},
]
static defaultBadgeData = { label: 'role downloads' }
static render({ downloads }) {
return {
message: metric(downloads),
color: downloadCount(downloads),
}
}
async handle({ roleId }) {
const json = await this.fetch({ roleId })
return this.constructor.render({ downloads: json.download_count })
return renderDownloadsBadge({ downloads: json.download_count })
}
}

View File

@@ -1,7 +1,7 @@
import Joi from 'joi'
import { renderLicenseBadge } from '../licenses.js'
import { renderVersionBadge } from '../version.js'
import { metric } from '../text-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService, InvalidResponse } from '../index.js'
@@ -45,7 +45,7 @@ class APMDownloads extends BaseAPMService {
static defaultBadgeData = { label: 'downloads' }
static render({ downloads }) {
return { message: metric(downloads), color: 'green' }
return renderDownloadsBadge({ downloads, colorOverride: 'green' })
}
async handle({ packageName }) {

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

@@ -2,11 +2,24 @@ import Joi from 'joi'
import {
testResultQueryParamSchema,
renderTestResultBadge,
documentation as commonDocumentation,
} from '../test-results.js'
import AzureDevOpsBase from './azure-devops-base.js'
const commonAttrs = {
keywords: ['vso', 'vsts', 'azure-devops'],
namedParams: {
organization: 'azuredevops-powershell',
project: 'azuredevops-powershell',
definitionId: '1',
branch: 'master',
},
queryParams: {
passed_label: 'passed',
failed_label: 'failed',
skipped_label: 'skipped',
compact_message: null,
},
documentation: `
<p>
To obtain your own badge, you need to get 3 pieces of information:
@@ -26,19 +39,7 @@ const commonAttrs = {
Optionally, you can specify a named branch:
<code>https://img.shields.io/azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID/NAMED_BRANCH.svg</code>.
</p>
<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.
<br>
There is also a <code>&compact_message</code> query parameter, which will default to displaying ✔, ✘ and ➟, separated by a horizontal bar |.
<br>
For example, if you want to use a different terminology:
<br>
<code>/azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID.svg?passed_label=good&failed_label=bad&skipped_label=n%2Fa</code>
<br>
Or, use symbols:
<br>
<code>/azure-devops/tests/ORGANIZATION/PROJECT/DEFINITION_ID.svg?compact_message&passed_label=%F0%9F%8E%89&failed_label=%F0%9F%92%A2&skipped_label=%F0%9F%A4%B7</code>
</p>
${commonDocumentation}
`,
}
@@ -71,29 +72,6 @@ export default class AzureDevOpsTests extends AzureDevOpsBase {
static examples = [
{
title: 'Azure DevOps tests',
pattern: ':organization/:project/:definitionId',
namedParams: {
organization: 'azuredevops-powershell',
project: 'azuredevops-powershell',
definitionId: '1',
},
staticPreview: this.render({
passed: 20,
failed: 1,
skipped: 1,
total: 22,
}),
...commonAttrs,
},
{
title: 'Azure DevOps tests (branch)',
pattern: ':organization/:project/:definitionId/:branch',
namedParams: {
organization: 'azuredevops-powershell',
project: 'azuredevops-powershell',
definitionId: '1',
branch: 'master',
},
staticPreview: this.render({
passed: 20,
failed: 1,
@@ -104,15 +82,6 @@ export default class AzureDevOpsTests extends AzureDevOpsBase {
},
{
title: 'Azure DevOps tests (compact)',
pattern: ':organization/:project/:definitionId',
namedParams: {
organization: 'azuredevops-powershell',
project: 'azuredevops-powershell',
definitionId: '1',
},
queryParams: {
compact_message: null,
},
staticPreview: this.render({
passed: 20,
failed: 1,
@@ -124,16 +93,11 @@ export default class AzureDevOpsTests extends AzureDevOpsBase {
},
{
title: 'Azure DevOps tests with custom labels',
pattern: ':organization/:project/:definitionId',
namedParams: {
organization: 'azuredevops-powershell',
project: 'azuredevops-powershell',
definitionId: '1',
},
queryParams: {
passed_label: 'good',
failed_label: 'bad',
skipped_label: 'n/a',
compact_message: null,
},
staticPreview: this.render({
passed: 20,
@@ -172,15 +136,16 @@ export default class AzureDevOpsTests extends AzureDevOpsBase {
})
}
async handle(
{ organization, project, definitionId, branch },
{
compact_message: compactMessage,
passed_label: passedLabel,
failed_label: failedLabel,
skipped_label: skippedLabel,
}
) {
static transform({ aggregatedResultsAnalysis }) {
const { totalTests: total, resultsByOutcome } = aggregatedResultsAnalysis
const passed = resultsByOutcome.Passed ? resultsByOutcome.Passed.count : 0
const failed = resultsByOutcome.Failed ? resultsByOutcome.Failed.count : 0
// assume the rest has been skipped
const skipped = total - passed - failed
return { passed, failed, skipped, total }
}
async fetchTestResults({ organization, project, definitionId, branch }) {
const errorMessages = {
404: 'build pipeline or test result summary not found',
}
@@ -193,8 +158,7 @@ export default class AzureDevOpsTests extends AzureDevOpsBase {
)
// https://dev.azure.com/azuredevops-powershell/azuredevops-powershell/_apis/test/ResultSummaryByBuild?buildId=20
const json = await this.fetch({
return await this.fetch({
url: `https://dev.azure.com/${organization}/${project}/_apis/test/ResultSummaryByBuild`,
options: {
qs: { buildId },
@@ -202,24 +166,24 @@ export default class AzureDevOpsTests extends AzureDevOpsBase {
schema: buildTestResultSummarySchema,
errorMessages,
})
}
const total = json.aggregatedResultsAnalysis.totalTests
let passed = 0
const passedTests = json.aggregatedResultsAnalysis.resultsByOutcome.Passed
if (passedTests) {
passed = passedTests.count
async handle(
{ organization, project, definitionId, branch },
{
compact_message: compactMessage,
passed_label: passedLabel,
failed_label: failedLabel,
skipped_label: skippedLabel,
}
let failed = 0
const failedTests = json.aggregatedResultsAnalysis.resultsByOutcome.Failed
if (failedTests) {
failed = failedTests.count
}
// assume the rest has been skipped
const skipped = total - passed - failed
const isCompact = compactMessage !== undefined
) {
const json = await this.fetchTestResults({
organization,
project,
definitionId,
branch,
})
const { passed, failed, skipped, total } = this.constructor.transform(json)
return this.constructor.render({
passed,
failed,
@@ -228,7 +192,7 @@ export default class AzureDevOpsTests extends AzureDevOpsBase {
passedLabel,
failedLabel,
skippedLabel,
isCompact,
isCompact: compactMessage !== undefined,
})
}
}

View File

@@ -1,149 +1,27 @@
import Joi from 'joi'
import {
isDefaultTestTotals,
isDefaultCompactTestTotals,
isCustomTestTotals,
isCustomCompactTestTotals,
} from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
const org = 'azuredevops-powershell'
const project = 'azuredevops-powershell'
const definitionId = 1
const nonExistentDefinitionId = 9999
const buildId = 20
const uriPrefix = `/${org}/${project}`
const azureDevOpsApiBaseUri = `https://dev.azure.com/${org}/${project}/_apis`
const mockBadgeUriPath = `${uriPrefix}/${definitionId}`
const mockBadgeUri = `${mockBadgeUriPath}.json`
const mockBranchBadgeUri = `${mockBadgeUriPath}/master.json`
const mockLatestBuildApiUriPath = `/build/builds?definitions=${definitionId}&%24top=1&statusFilter=completed&api-version=5.0-preview.4`
const mockLatestBranchBuildApiUriPath = `/build/builds?definitions=${definitionId}&%24top=1&statusFilter=completed&api-version=5.0-preview.4&branchName=refs%2Fheads%2Fmaster`
const mockNonExistentBuildApiUriPath = `/build/builds?definitions=${nonExistentDefinitionId}&%24top=1&statusFilter=completed&api-version=5.0-preview.4`
const mockTestResultSummaryApiUriPath = `/test/ResultSummaryByBuild?buildId=${buildId}`
const latestBuildResponse = {
count: 1,
value: [{ id: buildId }],
}
const mockEmptyTestResultSummaryResponse = {
aggregatedResultsAnalysis: {
totalTests: 0,
resultsByOutcome: {},
},
}
const mockTestResultSummaryResponse = {
aggregatedResultsAnalysis: {
totalTests: 3,
resultsByOutcome: {
Passed: {
count: 1,
},
Failed: {
count: 1,
},
Skipped: {
count: 1,
},
},
},
}
const mockTestResultSummarySetup = nock =>
nock(azureDevOpsApiBaseUri)
.get(mockLatestBuildApiUriPath)
.reply(200, latestBuildResponse)
.get(mockTestResultSummaryApiUriPath)
.reply(200, mockTestResultSummaryResponse)
const mockBranchTestResultSummarySetup = nock =>
nock(azureDevOpsApiBaseUri)
.get(mockLatestBranchBuildApiUriPath)
.reply(200, latestBuildResponse)
.get(mockTestResultSummaryApiUriPath)
.reply(200, mockTestResultSummaryResponse)
const expectedDefaultAzureDevOpsTestTotals = '1 passed, 1 failed, 1 skipped'
const expectedCompactAzureDevOpsTestTotals = '✔ 1 | ✘ 1 | ➟ 1'
const expectedCustomAzureDevOpsTestTotals = '1 good, 1 bad, 1 n/a'
const expectedCompactCustomAzureDevOpsTestTotals = '💃 1 | 🤦‍♀️ 1 | 🤷 1'
function getLabelRegex(label, isCompact) {
return isCompact ? `(?:${label} [0-9]*)` : `(?:[0-9]* ${label})`
}
function isAzureDevOpsTestTotals(
passedLabel,
failedLabel,
skippedLabel,
isCompact
) {
const passedRegex = getLabelRegex(passedLabel, isCompact)
const failedRegex = getLabelRegex(failedLabel, isCompact)
const skippedRegex = getLabelRegex(skippedLabel, isCompact)
const separator = isCompact ? ' | ' : ', '
const regexStrings = [
`^${passedRegex}$`,
`^${failedRegex}$`,
`^${skippedRegex}$`,
`^${passedRegex}${separator}${failedRegex}$`,
`^${failedRegex}${separator}${skippedRegex}$`,
`^${passedRegex}${separator}${skippedRegex}$`,
`^${passedRegex}${separator}${failedRegex}${separator}${skippedRegex}$`,
`^no tests$`,
]
return Joi.alternatives().try(
...regexStrings.map(regexStr => Joi.string().regex(new RegExp(regexStr)))
)
}
const isDefaultAzureDevOpsTestTotals = isAzureDevOpsTestTotals(
'passed',
'failed',
'skipped'
)
const isCompactAzureDevOpsTestTotals = isAzureDevOpsTestTotals(
'✔',
'✘',
'➟',
true
)
const isCustomAzureDevOpsTestTotals = isAzureDevOpsTestTotals(
'good',
'bad',
'n\\/a'
)
const isCompactCustomAzureDevOpsTestTotals = isAzureDevOpsTestTotals(
'💃',
'🤦‍♀️',
'🤷',
true
)
t.create('unknown build definition')
.get(`${uriPrefix}/${nonExistentDefinitionId}.json`)
.get(`/swellaby/opensource/99999999.json`)
.expectBadge({ label: 'tests', message: 'build pipeline not found' })
t.create('404 latest build error response')
.get(mockBadgeUri)
.get('/swellaby/fake/14.json')
.intercept(nock =>
nock(azureDevOpsApiBaseUri).get(mockLatestBuildApiUriPath).reply(404)
)
.expectBadge({
label: 'tests',
message: 'build pipeline or test result summary not found',
})
t.create('no build response')
.get(`${uriPrefix}/${nonExistentDefinitionId}.json`)
.intercept(nock =>
nock(azureDevOpsApiBaseUri).get(mockNonExistentBuildApiUriPath).reply(200, {
count: 0,
value: [],
})
)
.expectBadge({ label: 'tests', message: 'build pipeline not found' })
t.create('no test result summary response')
.get(mockBadgeUri)
.intercept(nock =>
nock(azureDevOpsApiBaseUri)
.get(mockLatestBuildApiUriPath)
.reply(200, latestBuildResponse)
.get(mockTestResultSummaryApiUriPath)
nock('https://dev.azure.com/swellaby/fake/_apis')
.get('/build/builds')
.query({
definitions: 14,
$top: 1,
statusFilter: 'completed',
'api-version': '5.0-preview.4',
})
.reply(404)
)
.expectBadge({
@@ -151,113 +29,73 @@ t.create('no test result summary response')
message: 'build pipeline or test result summary not found',
})
t.create('invalid test result summary response')
.get(mockBadgeUri)
t.create('no test result summary response')
.get('/swellaby/fake/14.json')
.intercept(nock =>
nock(azureDevOpsApiBaseUri)
.get(mockLatestBuildApiUriPath)
.reply(200, latestBuildResponse)
.get(mockTestResultSummaryApiUriPath)
.reply(200, {})
nock('https://dev.azure.com/swellaby/fake/_apis')
.get('/build/builds')
.query({
definitions: 14,
$top: 1,
statusFilter: 'completed',
'api-version': '5.0-preview.4',
})
.reply(200, { count: 1, value: [{ id: 1234 }] })
.get('/test/ResultSummaryByBuild')
.query({ buildId: 1234 })
.reply(404)
)
.expectBadge({ label: 'tests', message: 'invalid response data' })
t.create('no tests in test result summary response')
.get(mockBadgeUri)
.intercept(nock =>
nock(azureDevOpsApiBaseUri)
.get(mockLatestBuildApiUriPath)
.reply(200, latestBuildResponse)
.get(mockTestResultSummaryApiUriPath)
.reply(200, mockEmptyTestResultSummaryResponse)
)
.expectBadge({ label: 'tests', message: 'no tests' })
t.create('test status')
.get(mockBadgeUri)
.intercept(mockTestResultSummarySetup)
.expectBadge({
label: 'tests',
message: expectedDefaultAzureDevOpsTestTotals,
message: 'build pipeline or test result summary not found',
})
t.create('no build response')
.get(`/swellaby/opensource/174.json`)
.expectBadge({ label: 'tests', message: 'build pipeline not found' })
t.create('no tests in test result summary response')
.get('/swellaby/opensource/14.json')
.expectBadge({ label: 'tests', message: 'no tests' })
t.create('test status').get('/swellaby/opensource/25.json').expectBadge({
label: 'tests',
message: isDefaultTestTotals,
})
t.create('test status on branch')
.get(mockBranchBadgeUri)
.intercept(mockBranchTestResultSummarySetup)
.get('/swellaby/opensource/25/master.json')
.expectBadge({
label: 'tests',
message: expectedDefaultAzureDevOpsTestTotals,
message: isDefaultTestTotals,
})
t.create('test status with compact message')
.get(mockBadgeUri, {
.get('/swellaby/opensource/25.json', {
qs: {
compact_message: null,
},
})
.intercept(mockTestResultSummarySetup)
.expectBadge({
label: 'tests',
message: expectedCompactAzureDevOpsTestTotals,
message: isDefaultCompactTestTotals,
})
t.create('test status with custom labels')
.get(mockBadgeUri, {
.get('/swellaby/opensource/25.json', {
qs: {
passed_label: 'good',
failed_label: 'bad',
skipped_label: 'n/a',
},
})
.intercept(mockTestResultSummarySetup)
.expectBadge({
label: 'tests',
message: expectedCustomAzureDevOpsTestTotals,
message: isCustomTestTotals,
})
t.create('test status with compact message and custom labels')
.get(mockBadgeUri, {
qs: {
compact_message: null,
passed_label: '💃',
failed_label: '🤦‍♀️',
skipped_label: '🤷',
},
})
.intercept(mockTestResultSummarySetup)
.expectBadge({
label: 'tests',
message: expectedCompactCustomAzureDevOpsTestTotals,
})
t.create('live test status')
.get(mockBadgeUri)
.expectBadge({ label: 'tests', message: isDefaultAzureDevOpsTestTotals })
t.create('live test status on branch')
.get(mockBranchBadgeUri)
.expectBadge({ label: 'tests', message: isDefaultAzureDevOpsTestTotals })
t.create('live test status with compact message')
.get(mockBadgeUri, {
qs: {
compact_message: null,
},
})
.expectBadge({ label: 'tests', message: isCompactAzureDevOpsTestTotals })
t.create('live test status with custom labels')
.get(mockBadgeUri, {
qs: {
passed_label: 'good',
failed_label: 'bad',
skipped_label: 'n/a',
},
})
.expectBadge({ label: 'tests', message: isCustomAzureDevOpsTestTotals })
t.create('live test status with compact message and custom labels')
.get(mockBadgeUri, {
.get('/swellaby/opensource/25.json', {
qs: {
compact_message: null,
passed_label: '💃',
@@ -267,5 +105,5 @@ t.create('live test status with compact message and custom labels')
})
.expectBadge({
label: 'tests',
message: isCompactCustomAzureDevOpsTestTotals,
message: isCustomCompactTestTotals,
})

View File

@@ -1,5 +1,5 @@
import Joi from 'joi'
import { BaseJsonService } from '../index.js'
import LibrariesIoBase from '../librariesio/librariesio-base.js'
const schema = Joi.object()
.keys({
@@ -17,11 +17,11 @@ const schema = Joi.object()
})
.required()
export default class BaseBowerService extends BaseJsonService {
export default class BaseBowerService extends LibrariesIoBase {
async fetch({ packageName }) {
return this._requestJson({
schema,
url: `https://libraries.io/api/bower/${packageName}`,
url: `/bower/${packageName}`,
errorMessages: {
404: 'package not found',
},

View File

@@ -6,15 +6,6 @@ t.create('licence')
.get('/bootstrap.json')
.expectBadge({ label: 'license', message: 'MIT' })
t.create('license not declared')
.get('/bootstrap.json')
.intercept(nock =>
nock('https://libraries.io')
.get('/api/bower/bootstrap')
.reply(200, { normalized_licenses: [] })
)
.expectBadge({ label: 'license', message: 'missing' })
t.create('licence for Invalid Package')
.timeout(10000)
.get('/it-is-a-invalid-package-should-error.json')

View File

@@ -27,9 +27,7 @@ class BowerVersion extends BaseBowerService {
static defaultBadgeData = { label: 'bower' }
async handle({ packageName }, queryParams) {
const data = await this.fetch({ packageName })
const includePrereleases = queryParams.include_prereleases !== undefined
static transform(data, includePrereleases) {
const version = includePrereleases
? data.latest_release_number
: data.latest_stable_release_number
@@ -38,6 +36,14 @@ class BowerVersion extends BaseBowerService {
throw new InvalidResponse({ prettyMessage: 'no releases' })
}
return version
}
async handle({ packageName }, queryParams) {
const data = await this.fetch({ packageName })
const includePrereleases = queryParams.include_prereleases !== undefined
const version = this.constructor.transform(data, includePrereleases)
return renderVersionBadge({ version })
}
}

View File

@@ -0,0 +1,92 @@
import { expect } from 'chai'
import { test, given } from 'sazerac'
import nock from 'nock'
import { cleanUpNockAfterEach, defaultContext } from '../test-helpers.js'
import { InvalidResponse } from '../index.js'
import LibrariesIoApiProvider from '../librariesio/librariesio-api-provider.js'
import { BowerVersion } from './bower-version.service.js'
describe('BowerVersion', function () {
test(BowerVersion.transform, () => {
given(
{
latest_release_number: '2.0.0-beta',
latest_stable_release_number: '1.8.3',
},
false
).expect('1.8.3')
given(
{
latest_release_number: '2.0.0-beta',
latest_stable_release_number: '1.8.3',
},
true
).expect('2.0.0-beta')
})
it('throws `no releases` InvalidResponse if no stable version', function () {
expect(() =>
BowerVersion.transform({ latest_release_number: 'panda' }, false)
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'no releases')
})
it('throws `no releases` InvalidResponse if no prereleases', function () {
expect(() =>
BowerVersion.transform({ latest_stable_release_number: 'penguin' }, true)
)
.to.throw(InvalidResponse)
.with.property('prettyMessage', 'no releases')
})
context('auth', function () {
cleanUpNockAfterEach()
const fakeApiKey = 'fakeness'
const response = {
normalized_licenses: [],
latest_release_number: '2.0.0-beta',
latest_stable_release_number: '1.8.3',
}
const config = {
private: {
librariesio_tokens: fakeApiKey,
},
}
const librariesIoApiProvider = new LibrariesIoApiProvider({
baseUrl: 'https://libraries.io/api',
tokens: [fakeApiKey],
})
it('sends the auth information as configured', async function () {
const scope = nock('https://libraries.io/api')
// 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.
.get(`/bower/bootstrap?api_key=${fakeApiKey}`)
.reply(200, response)
expect(
await BowerVersion.invoke(
{
...defaultContext,
librariesIoApiProvider,
},
config,
{
platform: 'bower',
packageName: 'bootstrap',
},
{
include_prereleases: '',
}
)
).to.deep.equal({
message: 'v2.0.0-beta',
color: 'orange',
label: undefined,
})
scope.done()
})
})
})

View File

@@ -34,24 +34,6 @@ t.create('Pre Version for Invalid Package')
.get('/v/it-is-a-invalid-package-should-error.json?include_prereleases')
.expectBadge({ label: 'bower', message: 'package not found' })
t.create('Version label should be `no releases` if no stable version')
.get('/v/bootstrap.json')
.intercept(nock =>
nock('https://libraries.io')
.get('/api/bower/bootstrap')
.reply(200, { normalized_licenses: [], latest_stable_release: null })
)
.expectBadge({ label: 'bower', message: 'no releases' })
t.create('Version label should be `no releases` if no pre-release')
.get('/v/bootstrap.json?include_prereleases')
.intercept(nock =>
nock('https://libraries.io')
.get('/api/bower/bootstrap')
.reply(200, { normalized_licenses: [], latest_release_number: null })
)
.expectBadge({ label: 'bower', message: 'no releases' })
t.create('Version (legacy redirect: vpre)')
.get('/vpre/bootstrap.svg')
.expectRedirect('/bower/v/bootstrap.svg?include_prereleases')

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

@@ -1,5 +1,4 @@
import { metric } from '../text-formatters.js'
import { downloadCount } from '../color-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { redirector, NotFound } from '../index.js'
import BaseChromeWebStoreService from './chrome-web-store-base.js'
@@ -11,26 +10,19 @@ class ChromeWebStoreUsers extends BaseChromeWebStoreService {
{
title: 'Chrome Web Store',
namedParams: { storeId: 'ogffaloegjglncjfehdfplabnoondfjo' },
staticPreview: this.render({ downloads: 573 }),
staticPreview: renderDownloadsBadge({ downloads: 573 }),
},
]
static defaultBadgeData = { label: 'users' }
static render({ downloads }) {
return {
message: `${metric(downloads)}`,
color: downloadCount(downloads),
}
}
async handle({ storeId }) {
const chromeWebStore = await this.fetch({ storeId })
const downloads = chromeWebStore.users()
if (downloads == null) {
throw new NotFound({ prettyMessage: 'not found' })
}
return this.constructor.render({ downloads })
return renderDownloadsBadge({ downloads })
}
}

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

@@ -1,5 +1,4 @@
import { metric } from '../text-formatters.js'
import { downloadCount as downloadsColor } from '../color-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { BaseClojarsService } from './clojars-base.js'
export default class ClojarsDownloads extends BaseClojarsService {
@@ -9,22 +8,14 @@ export default class ClojarsDownloads extends BaseClojarsService {
static examples = [
{
namedParams: { clojar: 'prismic' },
staticPreview: this.render({ downloads: 117 }),
staticPreview: renderDownloadsBadge({ downloads: 117 }),
},
]
static defaultBadgeData = { label: 'downloads' }
static render({ downloads }) {
return {
label: 'downloads',
message: metric(downloads),
color: downloadsColor(downloads),
}
}
async handle({ clojar }) {
const json = await this.fetch({ clojar })
return this.constructor.render({ downloads: json.downloads })
return renderDownloadsBadge({ downloads: json.downloads })
}
}

View File

@@ -1,5 +1,4 @@
import { metric } from '../text-formatters.js'
import { downloadCount } from '../color-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import BaseCondaService from './conda-base.js'
export default class CondaDownloads extends BaseCondaService {
@@ -16,11 +15,8 @@ export default class CondaDownloads extends BaseCondaService {
]
static render({ variant, downloads }) {
return {
label: variant === 'dn' ? 'downloads' : 'conda|downloads',
message: metric(downloads),
color: downloadCount(downloads),
}
const labelOverride = variant === 'dn' ? 'downloads' : 'conda|downloads'
return renderDownloadsBadge({ downloads, labelOverride })
}
async handle({ variant, channel, pkg }) {

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,5 +1,4 @@
import { downloadCount as downloadCountColor } from '../color-formatters.js'
import { metric } from '../text-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { InvalidParameter, NotFound } from '../index.js'
import { BaseCratesService, keywords } from './crates-base.js'
@@ -54,23 +53,16 @@ export default class CratesDownloads extends BaseCratesService {
},
]
static _getLabel(version, variant) {
switch (variant) {
case 'dv':
return version ? `downloads@${version}` : 'downloads@latest'
case 'dr':
return 'recent downloads'
default:
return version ? `downloads@${version}` : 'downloads'
}
}
static render({ variant, downloads, version }) {
return {
label: this._getLabel(version, variant),
message: metric(downloads),
color: downloadCountColor(downloads),
let labelOverride
if (variant === 'dr') {
labelOverride = 'recent downloads'
} else if (variant === 'dv' && !version) {
version = 'latest'
} else if (!version) {
labelOverride = 'downloads'
}
return renderDownloadsBadge({ downloads, labelOverride, version })
}
transform({ variant, json }) {

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

@@ -1,98 +1,13 @@
import Joi from 'joi'
import { BaseJsonService } from '../index.js'
import { deprecatedService } from '../index.js'
const schema = Joi.object({
status: Joi.allow(
'insecure',
'outofdate',
'notsouptodate',
'uptodate',
'none'
).required(),
}).required()
const queryParamSchema = Joi.object({
path: Joi.string(),
}).required()
const statusMap = {
insecure: {
color: 'red',
message: 'insecure',
},
outofdate: {
color: 'red',
message: 'out of date',
},
notsouptodate: {
color: 'yellow',
message: 'up to date',
},
uptodate: {
color: 'brightgreen',
message: 'up to date',
},
none: {
color: 'brightgreen',
message: 'none',
},
}
export default class David extends BaseJsonService {
static category = 'dependencies'
static route = {
base: 'david',
pattern: ':kind(dev|optional|peer)?/:user/:repo',
queryParamSchema,
}
static examples = [
{
title: 'David',
namedParams: { user: 'expressjs', repo: 'express' },
staticPreview: this.render({ status: 'uptodate' }),
export default [
deprecatedService({
category: 'dependencies',
route: {
base: 'david',
pattern: ':various+',
},
{
title: 'David (path)',
namedParams: { user: 'babel', repo: 'babel' },
queryParams: { path: 'packages/babel-core' },
staticPreview: this.render({ status: 'uptodate' }),
},
]
static defaultBadgeData = { label: 'dependencies' }
static render({ status, kind }) {
return {
message: statusMap[status].message,
color: statusMap[status].color,
label: `${kind ? `${kind} ` : ''}dependencies`,
}
}
async fetch({ kind, user, repo, path }) {
const url = `https://david-dm.org/${user}/${repo}/${
kind ? `${kind}-` : ''
}info.json`
return this._requestJson({
schema,
url,
options: { qs: { path } },
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',
},
})
}
async handle({ kind, user, repo }, { path }) {
const json = await this.fetch({ kind, user, repo, path })
return this.constructor.render({ status: json.status, kind })
}
}
label: 'david',
dateAdded: new Date('2021-10-30'),
}),
]

View File

@@ -1,70 +1,20 @@
import Joi from 'joi'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
import { ServiceTester } from '../tester.js'
const isDependencyStatus = Joi.string().valid(
'insecure',
'up to date',
'out of date'
)
export const t = new ServiceTester({
id: 'david',
title: 'David',
})
t.create('david dependencies (valid)')
t.create('no longer available (previously dependencies)')
.get('/expressjs/express.json')
.timeout(15000)
.expectBadge({
label: 'dependencies',
message: isDependencyStatus,
label: 'david',
message: 'no longer available',
})
t.create('david dev dependencies (valid)')
t.create('no longer available (previously dev dependencies)')
.get('/dev/expressjs/express.json')
.timeout(15000)
.expectBadge({
label: 'dev dependencies',
message: isDependencyStatus,
})
t.create('david optional dependencies (valid)')
.get('/optional/elnounch/byebye.json')
.timeout(15000)
.expectBadge({
label: 'optional dependencies',
message: isDependencyStatus,
})
t.create('david peer dependencies (valid)')
.get('/peer/webcomponents/generator-element.json')
.timeout(15000)
.expectBadge({
label: 'peer dependencies',
message: isDependencyStatus,
})
t.create('david dependencies with path (valid)')
.get('/babel/babel.json?path=packages/babel-core')
.timeout(15000)
.expectBadge({
label: 'dependencies',
message: isDependencyStatus,
})
t.create('david dependencies (none)')
.get('/peer/expressjs/express.json') // express does not specify peer dependencies
.timeout(15000)
.expectBadge({ label: 'peer dependencies', message: 'none' })
t.create('david dependencies (repo not found)')
.get('/pyvesb/emptyrepo.json')
.timeout(15000)
.expectBadge({
label: 'dependencies',
message: 'repo or path not found or david internal error',
})
t.create('david dependencies (path not found')
.get('/babel/babel.json?path=invalid/path')
.timeout(15000)
.expectBadge({
label: 'dependencies',
message: 'repo or path not found or david internal error',
label: 'david',
message: 'no longer available',
})

View File

@@ -1,5 +1,5 @@
import Joi from 'joi'
import { metric } from '../text-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
import {
@@ -28,11 +28,8 @@ export default class DockerPulls extends BaseJsonService {
static defaultBadgeData = { label: 'docker pulls' }
static render({ count }) {
return {
message: metric(count),
color: dockerBlue,
}
static render({ count: downloads }) {
return renderDownloadsBadge({ downloads, colorOverride: dockerBlue })
}
async fetch({ user, repo }) {

59
services/downloads.js Normal file
View File

@@ -0,0 +1,59 @@
/**
* @module
*/
import { downloadCount } from './color-formatters.js'
import { metric } from './text-formatters.js'
/**
* Handles rendering concerns of badges that display
* download counts, with override/customization support
*
* @param {object} attrs Refer to individual attrs
* @param {number} attrs.downloads Number of downloads
* @param {string} [attrs.interval] Period or interval the downloads occurred
* (e.g. day, week, month). If provided then this Will be reflected
* in the badge message unless overridden by other message-related parameters
* @param {string} [attrs.version] Version or tag that was downloaded
* which will be reflected in the badge label (unless the label is overridden)
* @param {string} [attrs.labelOverride] If provided then the badge label is set to this
* value overriding any other label-related parameters
* @param {string} [attrs.colorOverride] If provided then the badge color is set to this
* value instead of the color being based on the count of downloads
* @param {string} [attrs.messageSuffixOverride] If provided then the badge message will
* will have this value added to the download count, separated with a space
* @param {string} [attrs.versionedLabelPrefix] If provided then the badge label will use
* this value as the prefix for versioned badges, e.g. `foobar@v1.23`. Defaults to 'downloads'
* @returns {object} Badge
*/
function renderDownloadsBadge({
downloads,
interval,
version,
labelOverride,
colorOverride,
messageSuffixOverride,
versionedLabelPrefix = 'downloads',
}) {
let messageSuffix = ''
if (messageSuffixOverride) {
messageSuffix = ` ${messageSuffixOverride}`
} else if (interval) {
messageSuffix = `/${interval}`
}
let label
if (labelOverride) {
label = labelOverride
} else if (version) {
label = `${versionedLabelPrefix}@${version}`
}
return {
label,
color: colorOverride || downloadCount(downloads),
message: `${metric(downloads)}${messageSuffix}`,
}
}
export { renderDownloadsBadge }

View File

@@ -0,0 +1,52 @@
import { test, given } from 'sazerac'
import { renderDownloadsBadge } from './downloads.js'
import { downloadCount } from './color-formatters.js'
import { metric } from './text-formatters.js'
const downloads = 2345
const message = metric(downloads)
const color = downloadCount(downloads)
describe('downloads', function () {
test(renderDownloadsBadge, () => {
given({ downloads }).expect({ label: undefined, color, message })
given({ downloads, labelOverride: 'recent downloads' }).expect({
label: 'recent downloads',
color,
message,
})
given({ downloads, version: 'v1.0.0' }).expect({
label: 'downloads@v1.0.0',
color,
message,
})
given({
downloads,
versionedLabelPrefix: 'installs',
version: 'v1.0.0',
}).expect({
label: 'installs@v1.0.0',
color,
message,
})
given({
downloads,
messageSuffixOverride: '[foo.tar.gz]',
interval: 'week',
}).expect({
label: undefined,
color,
message: `${message} [foo.tar.gz]`,
})
given({ downloads, interval: 'year' }).expect({
label: undefined,
color,
message: `${message}/year`,
})
given({ downloads, colorOverride: 'pink' }).expect({
label: undefined,
color: 'pink',
message,
})
})
})

View File

@@ -1,6 +1,5 @@
import Joi from 'joi'
import { metric } from '../text-formatters.js'
import { downloadCount as downloadCountColor } from '../color-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService } from '../index.js'
@@ -16,19 +15,19 @@ const schema = Joi.object({
const intervalMap = {
dd: {
transform: json => json.downloads.daily,
messageSuffix: '/day',
interval: 'day',
},
dw: {
transform: json => json.downloads.weekly,
messageSuffix: '/week',
interval: 'week',
},
dm: {
transform: json => json.downloads.monthly,
messageSuffix: '/month',
interval: 'month',
},
dt: {
transform: json => json.downloads.total,
messageSuffix: '',
interval: '',
},
}
@@ -43,7 +42,7 @@ export default class DubDownloads extends BaseJsonService {
{
title: 'DUB',
namedParams: { interval: 'dm', packageName: 'vibe-d' },
staticPreview: this.render({ interval: 'dm', downloadCount: 5000 }),
staticPreview: this.render({ interval: 'dm', downloads: 5000 }),
},
{
title: 'DUB (version)',
@@ -55,7 +54,7 @@ export default class DubDownloads extends BaseJsonService {
staticPreview: this.render({
interval: 'dm',
version: '0.8.4',
downloadCount: 100,
downloads: 100,
}),
},
{
@@ -68,21 +67,19 @@ export default class DubDownloads extends BaseJsonService {
staticPreview: this.render({
interval: 'dm',
version: 'latest',
downloadCount: 100,
downloads: 100,
}),
},
]
static defaultBadgeData = { label: 'downloads' }
static render({ interval, version, downloadCount }) {
const { messageSuffix } = intervalMap[interval]
return {
label: version ? `downloads@${version}` : 'downloads',
message: `${metric(downloadCount)}${messageSuffix}`,
color: downloadCountColor(downloadCount),
}
static render({ interval, version, downloads }) {
return renderDownloadsBadge({
downloads,
version,
interval: intervalMap[interval].interval,
})
}
async fetch({ packageName, version }) {
@@ -98,7 +95,7 @@ export default class DubDownloads extends BaseJsonService {
const { transform } = intervalMap[interval]
const json = await this.fetch({ packageName, version })
const downloadCount = transform(json)
return this.constructor.render({ interval, downloadCount, version })
const downloads = transform(json)
return this.constructor.render({ interval, downloads, version })
}
}

View File

@@ -1,6 +1,5 @@
import Joi from 'joi'
import { metric } from '../text-formatters.js'
import { downloadCount as downloadCountColor } from '../color-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import EclipseMarketplaceBase from './eclipse-marketplace-base.js'
@@ -20,16 +19,16 @@ const totalResponseSchema = Joi.object({
}),
}).required()
function DownloadsForInterval(interval) {
function DownloadsForInterval(downloadsInterval) {
const {
base,
schema,
messageSuffix = '',
interval = '',
name,
} = {
month: {
base: 'eclipse-marketplace/dm',
messageSuffix: '/month',
interval: 'month',
schema: monthlyResponseSchema,
name: 'EclipseMarketplaceDownloadsMonth',
},
@@ -38,7 +37,7 @@ function DownloadsForInterval(interval) {
schema: totalResponseSchema,
name: 'EclipseMarketplaceDownloadsTotal',
},
}[interval]
}[downloadsInterval]
return class EclipseMarketplaceDownloads extends EclipseMarketplaceBase {
static name = name
@@ -53,16 +52,13 @@ function DownloadsForInterval(interval) {
]
static render({ downloads }) {
return {
message: `${metric(downloads)}${messageSuffix}`,
color: downloadCountColor(downloads),
}
return renderDownloadsBadge({ downloads, interval })
}
async handle({ name }) {
const { marketplace } = await this.fetch({ name, schema })
const downloads =
interval === 'total'
downloadsInterval === 'total'
? marketplace.node.installstotal
: marketplace.node.installsrecent
return this.constructor.render({ downloads })

View File

@@ -1,7 +1,6 @@
import semver from 'semver'
import Joi from 'joi'
import { downloadCount } from '../color-formatters.js'
import { metric } from '../text-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { latest as latestVersion } from '../version.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService, InvalidParameter, InvalidResponse } from '../index.js'
@@ -81,18 +80,8 @@ export default class GemDownloads extends BaseJsonService {
static defaultBadgeData = { label: 'downloads' }
static render({ variant, version, downloads }) {
let label
if (version) {
label = `downloads@${version}`
} else if (variant === 'dtv') {
label = 'downloads@latest'
}
return {
label,
message: metric(downloads),
color: downloadCount(downloads),
}
version = !version && variant === 'dtv' ? 'latest' : version
return renderDownloadsBadge({ downloads, version })
}
async fetchDownloadCountForVersion({ gem, version }) {

View File

@@ -1,32 +0,0 @@
import { makeSecretIsValid } from '../../../core/server/secret-is-valid.js'
function setRoutes({ shieldsSecret }, { apiProvider, server }) {
const secretIsValid = makeSecretIsValid(shieldsSecret)
// Allow the admin to obtain the tokens for operational and debugging
// purposes. This could be used to:
//
// - Ensure tokens have been propagated to all servers
// - Debug GitHub badge failures
//
// The admin can authenticate with HTTP Basic Auth, with an empty/any
// username and the shields secret in the password and an empty/any
// password.
//
// e.g.
// curl --insecure -u ':very-very-secret' 'https://img.shields.io/$github-auth/tokens'
server.ajax.on('github-auth/tokens', (json, end, ask) => {
if (!secretIsValid(ask.password)) {
// An unknown entity tries to connect. Let the connection linger for a minute.
return setTimeout(() => {
ask.res.statusCode = 401
ask.res.setHeader('Cache-Control', 'private')
end('Invalid secret.')
}, 10000)
}
ask.res.setHeader('Cache-Control', 'private')
end(apiProvider.serializeDebugInfo({ sanitize: false }))
})
}
export { setRoutes }

View File

@@ -1,71 +0,0 @@
import { expect } from 'chai'
import Camp from '@shields_io/camp'
import portfinder from 'portfinder'
import got from '../../../core/got-test-client.js'
import GithubApiProvider from '../github-api-provider.js'
import { setRoutes } from './admin.js'
describe('GitHub admin route', function () {
const shieldsSecret = '7'.repeat(40)
let port, baseUrl
before(async function () {
port = await portfinder.getPortPromise()
baseUrl = `http://127.0.0.1:${port}`
})
let camp
before(async function () {
camp = Camp.start({ port, hostname: '::' })
await new Promise(resolve => camp.on('listening', () => resolve()))
})
after(async function () {
if (camp) {
await new Promise(resolve => camp.close(resolve))
camp = undefined
}
})
before(function () {
const apiProvider = new GithubApiProvider({ withPooling: true })
setRoutes({ shieldsSecret }, { apiProvider, server: camp })
})
context('the password is correct', function () {
it('returns a valid JSON response', async function () {
const { statusCode, body, headers } = await got(
`${baseUrl}/$github-auth/tokens`,
{
username: '',
password: shieldsSecret,
responseType: 'json',
}
)
expect(statusCode).to.equal(200)
expect(body).to.be.ok
expect(headers['cache-control']).to.equal('private')
})
})
// Disabled because this code isn't modified often and the test is very
// slow. To run it, run `SLOW=true npm run test:core`
//
// I wasn't able to make this work with fake timers:
// https://github.com/sinonjs/sinon/issues/1739
if (process.env.SLOW) {
context('the password is missing', function () {
it('returns the expected message', async function () {
this.timeout(11000)
const { statusCode, body, headers } = await got(
`${baseUrl}/$github-auth/tokens`,
{
throwHttpErrors: false,
}
)
expect(statusCode).to.equal(401)
expect(body).to.equal('"Invalid secret."')
expect(headers['cache-control']).to.equal('private')
})
})
}
})

View File

@@ -1,7 +1,8 @@
import { expect } from 'chai'
import config from 'config'
import request from 'request'
import { fetchFactory } from '../../core/base-service/got.js'
import GithubApiProvider from './github-api-provider.js'
const requestFetcher = fetchFactory()
describe('Github API provider', function () {
const baseUrl = process.env.GITHUB_URL || 'https://api.github.com'
@@ -30,8 +31,8 @@ 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(
request,
await githubApiProvider.fetch(
requestFetcher,
'/repos/rust-lang/rust',
{}
)
@@ -52,8 +53,8 @@ describe('Github API provider', function () {
const headers = []
async function performOneRequest() {
const { res } = await githubApiProvider.requestAsPromise(
request,
const { res } = await githubApiProvider.fetch(
requestFetcher,
'/repos/rust-lang/rust',
{}
)

View File

@@ -3,6 +3,7 @@ import log from '../../core/server/log.js'
import { TokenPool } from '../../core/token-pooling/token-pool.js'
import { userAgent } from '../../core/base-service/legacy-request-handler.js'
import { nonNegativeInteger } from '../validators.js'
import { ImproperlyConfigured } from '../index.js'
const headerSchema = Joi.object({
'x-ratelimit-limit': nonNegativeInteger,
@@ -54,18 +55,6 @@ class GithubApiProvider {
}
}
serializeDebugInfo({ sanitize = true } = {}) {
if (this.withPooling) {
return {
standardTokens: this.standardTokens.serializeDebugInfo({ sanitize }),
searchTokens: this.searchTokens.serializeDebugInfo({ sanitize }),
graphqlTokens: this.graphqlTokens.serializeDebugInfo({ sanitize }),
}
} else {
return {}
}
}
addToken(tokenString) {
if (this.withPooling) {
this.standardTokens.add(tokenString)
@@ -151,10 +140,7 @@ 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) {
async fetch(requestFetcher, url, options = {}) {
const { baseUrl } = this
let token
@@ -163,8 +149,10 @@ class GithubApiProvider {
try {
token = this.tokenForUrl(url)
} catch (e) {
callback(e)
return
log.error(e)
throw new ImproperlyConfigured({
prettyMessage: 'Unable to select next Github token from pool',
})
}
tokenString = token.id
} else {
@@ -174,8 +162,6 @@ class GithubApiProvider {
const mergedOptions = {
...options,
...{
url,
baseUrl,
headers: {
'User-Agent': userAgent,
Authorization: `token ${tokenString}`,
@@ -183,31 +169,15 @@ class GithubApiProvider {
},
},
}
request(mergedOptions, (err, res, buffer) => {
if (err === null) {
if (this.withPooling) {
if (res.statusCode === 401) {
this.invalidateToken(token)
} else if (res.statusCode < 500) {
this.updateToken({ token, url, res })
}
}
const response = await requestFetcher(`${baseUrl}${url}`, mergedOptions)
if (this.withPooling) {
if (response.res.statusCode === 401) {
this.invalidateToken(token)
} else if (response.res.statusCode < 500) {
this.updateToken({ token, url, res: response.res })
}
callback(err, res, buffer)
})
}
requestAsPromise(request, url, options) {
return new Promise((resolve, reject) => {
this.request(request, url, options, (err, res, buffer) => {
if (err) {
reject(err)
} else {
resolve({ res, buffer })
}
})
})
}
return response
}
}

View File

@@ -21,47 +21,35 @@ describe('Github API provider', function () {
})
context('a search API request', function () {
const mockRequest = (options, callback) => {
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()
})
it('should obtain an appropriate token', async function () {
const mockResponse = { res: { headers: {} } }
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/search', {})
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
})
})
context('a graphql API request', function () {
const mockRequest = (options, callback) => {
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()
})
it('should obtain an appropriate token', async function () {
const mockResponse = { res: { headers: {} } }
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/graphql', {})
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
})
})
context('a core API request', function () {
const mockRequest = (options, callback) => {
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()
})
it('should obtain an appropriate token', async function () {
const mockResponse = { res: { headers: {} } }
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/repo', {})
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
})
})
@@ -70,40 +58,32 @@ describe('Github API provider', function () {
const remaining = 7955
const nextReset = 123456789
const mockResponse = {
statusCode: 200,
headers: {
'x-ratelimit-limit': rateLimit,
'x-ratelimit-remaining': remaining,
'x-ratelimit-reset': nextReset,
res: {
statusCode: 200,
headers: {
'x-ratelimit-limit': rateLimit,
'x-ratelimit-remaining': remaining,
'x-ratelimit-reset': nextReset,
},
buffer: Buffer.alloc(0),
},
}
const mockBuffer = Buffer.alloc(0)
const mockRequest = (...args) => {
const callback = args.pop()
callback(null, mockResponse, mockBuffer)
}
const mockRequest = sinon.stub().resolves(mockResponse)
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()
})
it('should return the response', async function () {
const res = await provider.fetch(mockRequest, '/repo', {})
expect(Object.is(res, mockResponse)).to.be.true
})
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()
})
it('should update the token with the expected values', async function () {
await provider.fetch(mockRequest, '/foo', {})
const expectedUsesRemaining =
remaining - Math.ceil(reserveFraction * rateLimit)
expect(mockStandardToken.update).to.have.been.calledWith(
expectedUsesRemaining,
nextReset
)
expect(mockStandardToken.invalidate).not.to.have.been.called
})
})
@@ -112,9 +92,10 @@ describe('Github API provider', function () {
const remaining = 7955
const nextReset = 123456789
const mockResponse = {
statusCode: 200,
headers: {},
body: `{
res: {
statusCode: 200,
headers: {},
body: `{
"data": {
"rateLimit": {
"limit": 12500,
@@ -124,67 +105,46 @@ describe('Github API provider', function () {
}
}
}`,
},
}
const mockBuffer = Buffer.alloc(0)
const mockRequest = (...args) => {
const callback = args.pop()
callback(null, mockResponse, mockBuffer)
}
const mockRequest = sinon.stub().resolves(mockResponse)
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()
})
it('should return the response', async function () {
const res = await provider.fetch(mockRequest, '/graphql', {})
expect(Object.is(res, mockResponse)).to.be.true
})
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()
})
it('should update the token with the expected values', async function () {
await provider.fetch(mockRequest, '/graphql', {})
const expectedUsesRemaining =
remaining - Math.ceil(reserveFraction * rateLimit)
expect(mockGraphqlToken.update).to.have.been.calledWith(
expectedUsesRemaining,
nextReset
)
expect(mockGraphqlToken.invalidate).not.to.have.been.called
})
})
context('an unauthorized response', function () {
const mockResponse = { statusCode: 401 }
const mockBuffer = Buffer.alloc(0)
const mockRequest = (...args) => {
const callback = args.pop()
callback(null, mockResponse, mockBuffer)
}
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()
})
it('should invoke the callback and update the token with the expected values', async function () {
const mockResponse = { res: { statusCode: 401, headers: {} } }
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/foo', {})
expect(mockStandardToken.invalidate).to.have.been.calledOnce
expect(mockStandardToken.update).not.to.have.been.called
})
})
context('a connection error', function () {
const mockRequest = (...args) => {
const callback = args.pop()
callback(Error('connection timeout'))
}
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()
})
it('should throw an exception', function () {
const msg = 'connection timeout'
const requestError = new Error(msg)
const mockRequest = sinon.stub().rejects(requestError)
return expect(provider.fetch(mockRequest, '/foo', {})).to.be.rejectedWith(
Error,
'connection timeout'
)
})
})
})

View File

@@ -2,21 +2,15 @@ import gql from 'graphql-tag'
import { mergeQueries } from '../../core/base-service/graphql.js'
import { BaseGraphqlService, BaseJsonService } from '../index.js'
function createRequestFetcher(context, config) {
const { sendAndCacheRequestWithCallbacks, githubApiProvider } = context
return async (url, options) =>
githubApiProvider.requestAsPromise(
sendAndCacheRequestWithCallbacks,
url,
options
)
function createRequestFetcher(context) {
const { sendAndCacheRequest, githubApiProvider } = context
return githubApiProvider.fetch.bind(githubApiProvider, sendAndCacheRequest)
}
class GithubAuthV3Service extends BaseJsonService {
constructor(context, config) {
super(context, config)
this._requestFetcher = createRequestFetcher(context, config)
this._requestFetcher = createRequestFetcher(context)
this.staticAuthConfigured = true
}
}
@@ -30,7 +24,7 @@ class ConditionalGithubAuthV3Service extends BaseJsonService {
constructor(context, config) {
super(context, config)
if (context.githubApiProvider.globalToken) {
this._requestFetcher = createRequestFetcher(context, config)
this._requestFetcher = createRequestFetcher(context)
this.staticAuthConfigured = true
} else {
this.staticAuthConfigured = false
@@ -41,7 +35,7 @@ class ConditionalGithubAuthV3Service extends BaseJsonService {
class GithubAuthV4Service extends BaseGraphqlService {
constructor(context, config) {
super(context, config)
this._requestFetcher = createRequestFetcher(context, config)
this._requestFetcher = createRequestFetcher(context)
this.staticAuthConfigured = true
}

View File

@@ -14,7 +14,7 @@ describe('GithubAuthV3Service', function () {
schema: Joi.object({
requiredString: Joi.string().required(),
}).required(),
url: 'https://github-api.example.com/repos/badges/shields/check-runs',
url: '/repos/badges/shields/check-runs',
options: {
headers: {
Accept: 'application/vnd.github.antiope-preview+json',
@@ -26,10 +26,17 @@ describe('GithubAuthV3Service', function () {
}
it('forwards custom Accept header', async function () {
const sendAndCacheRequestWithCallbacks = sinon.stub().returns(
const sendAndCacheRequest = sinon.stub().returns(
Promise.resolve({
buffer: '{"requiredString": "some-string"}',
res: { statusCode: 200 },
res: {
statusCode: 200,
headers: {
'x-ratelimit-limit': 12500,
'x-ratelimit-remaining': 7955,
'x-ratelimit-reset': 123456789,
},
},
})
)
const githubApiProvider = new GithubApiProvider({
@@ -39,18 +46,19 @@ describe('GithubAuthV3Service', function () {
sinon.stub(githubApiProvider.standardTokens, 'next').returns(mockToken)
DummyGithubAuthV3Service.invoke({
sendAndCacheRequestWithCallbacks,
sendAndCacheRequest,
githubApiProvider,
})
expect(sendAndCacheRequestWithCallbacks).to.have.been.calledOnceWith({
headers: {
'User-Agent': 'Shields.io/2003a',
Accept: 'application/vnd.github.antiope-preview+json',
Authorization: 'token undefined',
},
url: 'https://github-api.example.com/repos/badges/shields/check-runs',
baseUrl: 'https://github-api.example.com',
})
expect(sendAndCacheRequest).to.have.been.calledOnceWith(
'https://github-api.example.com/repos/badges/shields/check-runs',
{
headers: {
'User-Agent': 'Shields.io/2003a',
Accept: 'application/vnd.github.antiope-preview+json',
Authorization: 'token undefined',
},
}
)
})
})

View File

@@ -12,6 +12,7 @@ const releaseInfoSchema = Joi.object({
})
.required(),
tag_name: Joi.string().required(),
name: Joi.string().allow(null).allow(''),
prerelease: Joi.boolean().required(),
}).required()

View File

@@ -2,7 +2,6 @@ import { AuthHelper } from '../../core/base-service/auth-helper.js'
import RedisTokenPersistence from '../../core/token-pooling/redis-token-persistence.js'
import log from '../../core/server/log.js'
import GithubApiProvider from './github-api-provider.js'
import { setRoutes as setAdminRoutes } from './auth/admin.js'
import { setRoutes as setAcceptorRoutes } from './auth/acceptor.js'
// Convenience class with all the stuff related to the Github API and its
@@ -23,7 +22,6 @@ class GithubConstellation {
constructor(config) {
this._debugEnabled = config.service.debug.enabled
this._debugIntervalSeconds = config.service.debug.intervalSeconds
this.shieldsSecret = config.private.shields_secret
const { redis_url: redisUrl, gh_token: globalToken } = config.private
if (redisUrl) {
@@ -74,9 +72,6 @@ class GithubConstellation {
this.apiProvider.addToken(tokenString)
})
const { shieldsSecret, apiProvider } = this
setAdminRoutes({ shieldsSecret }, { apiProvider, server })
if (this.oauthHelper.isConfigured) {
setAcceptorRoutes({
server,

View File

@@ -1,7 +1,6 @@
import Joi from 'joi'
import { metric } from '../text-formatters.js'
import { nonNegativeInteger } from '../validators.js'
import { downloadCount as downloadCountColor } from '../color-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { NotFound } from '../index.js'
import { GithubAuthV3Service } from './github-auth-service.js'
import { fetchLatestRelease } from './github-common-release.js'
@@ -43,7 +42,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
},
staticPreview: this.render({
assetName: 'total',
downloadCount: 857000,
downloads: 857000,
}),
documentation,
},
@@ -58,7 +57,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloadCount: 27000,
downloads: 27000,
}),
documentation,
},
@@ -74,7 +73,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloadCount: 27000,
downloads: 27000,
}),
documentation,
},
@@ -89,7 +88,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloadCount: 2000,
downloads: 2000,
}),
documentation,
},
@@ -105,7 +104,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
staticPreview: this.render({
tag: 'latest',
assetName: 'total',
downloadCount: 2000,
downloads: 2000,
}),
documentation,
},
@@ -120,7 +119,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
staticPreview: this.render({
tag: 'v0.190.0',
assetName: 'total',
downloadCount: 490000,
downloads: 490000,
}),
documentation,
},
@@ -136,7 +135,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloadCount: 3000,
downloads: 3000,
}),
documentation,
},
@@ -153,7 +152,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloadCount: 3000,
downloads: 3000,
}),
documentation,
},
@@ -169,7 +168,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloadCount: 237,
downloads: 237,
}),
documentation,
},
@@ -187,7 +186,7 @@ export default class GithubDownloads extends GithubAuthV3Service {
staticPreview: this.render({
tag: 'latest',
assetName: 'atom-amd64.deb',
downloadCount: 237,
downloads: 237,
}),
documentation,
},
@@ -195,19 +194,14 @@ export default class GithubDownloads extends GithubAuthV3Service {
static defaultBadgeData = { label: 'downloads', namedLogo: 'github' }
static render({ tag, assetName, downloadCount }) {
return {
label: tag ? `downloads@${tag}` : 'downloads',
message:
assetName === 'total'
? metric(downloadCount)
: `${metric(downloadCount)} [${assetName}]`,
color: downloadCountColor(downloadCount),
}
static render({ tag: version, assetName, downloads }) {
const messageSuffixOverride =
assetName !== 'total' ? `[${assetName}]` : undefined
return renderDownloadsBadge({ downloads, messageSuffixOverride, version })
}
static transform({ releases, assetName }) {
const downloadCount = releases.reduce((accum1, { assets }) => {
const downloads = releases.reduce((accum1, { assets }) => {
const filteredAssets =
assetName === 'total'
? assets
@@ -217,12 +211,12 @@ export default class GithubDownloads extends GithubAuthV3Service {
return (
accum1 +
filteredAssets.reduce(
(accum2, { download_count: downloadCount }) => accum2 + downloadCount,
(accum2, { download_count: downloads }) => accum2 + downloads,
0
)
)
}, 0)
return { downloadCount }
return { downloads }
}
async handle({ kind, user, repo, tag, assetName }, { sort }) {
@@ -256,11 +250,11 @@ export default class GithubDownloads extends GithubAuthV3Service {
throw new NotFound({ prettyMessage: 'no releases' })
}
const { downloadCount } = this.constructor.transform({
const { downloads } = this.constructor.transform({
releases,
assetName,
})
return this.constructor.render({ tag, assetName, downloadCount })
return this.constructor.render({ tag, assetName, downloads })
}
}

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

@@ -89,9 +89,9 @@ t.create('Scoped dependency')
})
t.create('Scoped dependency on branch')
.get('/dependency-version/zeit/next.js/dev/babel-eslint/alpha.json')
.get('/dependency-version/zeit/next.js/dev/@babel/eslint-parser/canary.json')
.expectBadge({
label: 'babel-eslint',
label: '@babel/eslint-parser',
message: semverRange,
})

View File

@@ -1,3 +1,4 @@
import Joi from 'joi'
import { addv } from '../text-formatters.js'
import { version as versionColor } from '../color-formatters.js'
import { redirector } from '../index.js'
@@ -8,19 +9,23 @@ import {
} from './github-common-release.js'
import { documentation } from './github-helpers.js'
const extendedQueryParamSchema = Joi.object({
display_name: Joi.string().valid('tag', 'release').default('tag'),
})
class GithubRelease extends GithubAuthV3Service {
static category = 'version'
static route = {
base: 'github/v/release',
pattern: ':user/:repo',
queryParamSchema,
queryParamSchema: queryParamSchema.concat(extendedQueryParamSchema),
}
static examples = [
{
title: 'GitHub release (latest by date)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: {},
queryParams: { display_name: 'tag' },
staticPreview: this.render({
version: 'v4.16.4',
sort: 'date',
@@ -31,7 +36,7 @@ class GithubRelease extends GithubAuthV3Service {
{
title: 'GitHub release (latest by date including pre-releases)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: { include_prereleases: null },
queryParams: { include_prereleases: null, display_name: 'tag' },
staticPreview: this.render({
version: 'v5.0.0-alpha.7',
sort: 'date',
@@ -42,7 +47,7 @@ class GithubRelease extends GithubAuthV3Service {
{
title: 'GitHub release (latest SemVer)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: { sort: 'semver' },
queryParams: { sort: 'semver', display_name: 'tag' },
staticPreview: this.render({
version: 'v4.16.4',
sort: 'semver',
@@ -53,7 +58,11 @@ class GithubRelease extends GithubAuthV3Service {
{
title: 'GitHub release (latest SemVer including pre-releases)',
namedParams: { user: 'expressjs', repo: 'express' },
queryParams: { sort: 'semver', include_prereleases: null },
queryParams: {
sort: 'semver',
include_prereleases: null,
display_name: 'tag',
},
staticPreview: this.render({
version: 'v5.0.0-alpha.7',
sort: 'semver',
@@ -61,6 +70,21 @@ class GithubRelease extends GithubAuthV3Service {
}),
documentation,
},
{
title: 'GitHub release (release name instead of tag name)',
namedParams: { user: 'gooddata', repo: 'gooddata-java' },
queryParams: {
sort: 'date',
include_prereleases: null,
display_name: 'release',
},
staticPreview: this.render({
version: '3.7.0+api3',
sort: 'date',
isPrerelease: true,
}),
documentation,
},
]
static defaultBadgeData = { label: 'release', namedLogo: 'github' }
@@ -72,16 +96,29 @@ class GithubRelease extends GithubAuthV3Service {
return { message: addv(version), color }
}
static transform(latestRelease, display) {
const { name, tag_name: tagName, prerelease: isPrerelease } = latestRelease
if (display === 'tag') {
return { isPrerelease, version: tagName }
}
return { version: name || tagName, isPrerelease }
}
async handle({ user, repo }, queryParams) {
const latestRelease = await fetchLatestRelease(
this,
{ user, repo },
queryParams
)
const { version, isPrerelease } = this.constructor.transform(
latestRelease,
queryParams.display_name
)
return this.constructor.render({
version: latestRelease.tag_name,
version,
sort: queryParams.sort,
isPrerelease: latestRelease.prerelease,
isPrerelease,
})
}
}

View File

@@ -0,0 +1,25 @@
import { test, given } from 'sazerac'
import { GithubRelease } from './github-release.service.js'
describe('GithubRelease', function () {
test(GithubRelease.transform, () => {
given({ name: null, tag_name: '0.1.2', prerelease: true }, 'tag').expect({
version: '0.1.2',
isPrerelease: true,
})
given(
{ name: null, tag_name: '0.1.3', prerelease: true },
'release'
).expect({
version: '0.1.3',
isPrerelease: true,
})
given(
{ name: 'fun name', tag_name: '1.0.0', prerelease: false },
'release'
).expect({
version: 'fun name',
isPrerelease: false,
})
})
})

View File

@@ -20,6 +20,12 @@ t.create('Prerelease')
color: Joi.string().allow('blue', 'orange').required(),
})
// basic query parameter testing. application of param in transform
// logic is tested via unit tests in github-release.spec.js
t.create('Release (release name instead of tag name)')
.get('/v/release/expressjs/express.json?display_name=release')
.expectBadge({ label: 'release', message: isSemver, color: 'blue' })
t.create('Release (No releases)')
.get('/v/release/badges/daily-tests.json')
.expectBadge({ label: 'release', message: 'no releases or repo not found' })

View File

@@ -16,4 +16,50 @@ export default class GitLabBase extends BaseJsonService {
})
)
}
async fetchPage({ page, requestParams, schema }) {
const { res, buffer } = await this._request({
...requestParams,
...{ options: { qs: { page } } },
})
const json = this._parseJson(buffer)
const data = this.constructor._validate(json, schema)
return { res, data }
}
async fetchPaginatedArrayData({
url,
options,
schema,
errorMessages,
firstPageOnly = false,
}) {
const requestParams = this.authHelper.withBasicAuth({
url,
options: {
headers: { Accept: 'application/json' },
qs: { per_page: 100 },
...options,
},
errorMessages,
})
const {
res: { headers },
data,
} = await this.fetchPage({ page: 1, requestParams, schema })
const numberOfPages = headers['x-total-pages']
if (numberOfPages === 1 || firstPageOnly) {
return data
}
const pageData = await Promise.all(
[...Array(numberOfPages - 1).keys()].map((_, i) =>
this.fetchPage({ page: ++i + 1, requestParams, schema })
)
)
return [...data].concat(...pageData)
}
}

View File

@@ -11,11 +11,12 @@ const badgeSchema = Joi.object({
const queryParamSchema = Joi.object({
gitlab_url: optionalUrl,
branch: Joi.string(),
}).required()
const documentation = `
<p>
Important: If your project is publicly visible, but the badge is like this:
Important: You must use the Project Path, not the Project Id. Additionally, if your project is publicly visible, but the badge is like this:
<img src="https://img.shields.io/badge/build-not&nbsp;found-red" alt="build not found"/>
</p>
<p>
@@ -39,26 +40,23 @@ class GitlabPipelineStatus extends BaseSvgScrapingService {
static category = 'build'
static route = {
base: 'gitlab/pipeline',
pattern: ':user/:repo/:branch+',
base: 'gitlab/pipeline-status',
pattern: ':project+',
queryParamSchema,
}
static examples = [
{
title: 'Gitlab pipeline status',
namedParams: {
user: 'gitlab-org',
repo: 'gitlab',
branch: 'master',
},
namedParams: { project: 'gitlab-org/gitlab' },
queryParams: { branch: 'master' },
staticPreview: this.render({ status: 'passed' }),
documentation,
},
{
title: 'Gitlab pipeline status (self-hosted)',
namedParams: { user: 'GNOME', repo: 'pango', branch: 'master' },
queryParams: { gitlab_url: 'https://gitlab.gnome.org' },
namedParams: { project: 'GNOME/pango' },
queryParams: { gitlab_url: 'https://gitlab.gnome.org', branch: 'master' },
staticPreview: this.render({ status: 'passed' }),
documentation,
},
@@ -68,33 +66,67 @@ class GitlabPipelineStatus extends BaseSvgScrapingService {
return renderBuildStatusBadge({ status })
}
async handle(
{ user, repo, branch },
{ gitlab_url: baseUrl = 'https://gitlab.com' }
) {
const { message: status } = await this._requestSvg({
async fetch({ project, branch, baseUrl }) {
return this._requestSvg({
schema: badgeSchema,
url: `${baseUrl}/${user}/${repo}/badges/${branch}/pipeline.svg`,
url: `${baseUrl}/${decodeURIComponent(
project
)}/badges/${branch}/pipeline.svg`,
errorMessages: {
401: 'repo not found',
404: 'repo not found',
},
})
}
static transform(data) {
const { message: status } = data
if (status === 'unknown') {
throw new NotFound({ prettyMessage: 'branch not found' })
}
return { status }
}
async handle(
{ project },
{ gitlab_url: baseUrl = 'https://gitlab.com', branch = 'main' }
) {
const data = await this.fetch({
project,
branch,
baseUrl,
})
const { status } = this.constructor.transform(data)
return this.constructor.render({ status })
}
}
const GitlabPipelineStatusRedirector = redirector({
category: 'build',
name: 'GitlabPipelineStatusRedirector',
route: {
base: 'gitlab/pipeline',
pattern: ':user/:repo',
},
transformPath: ({ user, repo }) => `/gitlab/pipeline/${user}/${repo}/master`,
transformPath: ({ user, repo }) => `/gitlab/pipeline-status/${user}/${repo}`,
transformQueryParams: ({ _b }) => ({ branch: 'master' }),
dateAdded: new Date('2020-07-12'),
})
export { GitlabPipelineStatus, GitlabPipelineStatusRedirector }
const GitlabPipelineStatusBranchRouteParamRedirector = redirector({
category: 'build',
name: 'GitlabPipelineStatusBranchRouteParamRedirector',
route: {
base: 'gitlab/pipeline',
pattern: ':user/:repo/:branch+',
},
transformPath: ({ user, repo }) => `/gitlab/pipeline-status/${user}/${repo}`,
transformQueryParams: ({ branch }) => ({ branch }),
dateAdded: new Date('2021-10-20'),
})
export {
GitlabPipelineStatus,
GitlabPipelineStatusRedirector,
GitlabPipelineStatusBranchRouteParamRedirector,
}

View File

@@ -3,16 +3,27 @@ import { ServiceTester } from '../tester.js'
export const t = new ServiceTester({
id: 'GitlabPipeline',
title: 'Gitlab Pipeline',
pathPrefix: '/gitlab/pipeline',
pathPrefix: '/gitlab',
})
t.create('Pipeline status').get('/gitlab-org/gitlab/v10.7.6.json').expectBadge({
label: 'build',
message: isBuildStatus,
})
t.create('Pipeline status')
.get('/pipeline-status/gitlab-org/gitlab.json?branch=v10.7.6')
.expectBadge({
label: 'build',
message: isBuildStatus,
})
t.create('Pipeline status (nested groups)')
.get(
'/pipeline-status/megabyte-labs/dockerfile/ci-pipeline/ansible-lint.json?branch=master'
)
.expectBadge({
label: 'build',
message: isBuildStatus,
})
t.create('Pipeline status (nonexistent branch)')
.get('/gitlab-org/gitlab/nope-not-a-branch.json')
.get('/pipeline-status/gitlab-org/gitlab.json?branch=nope-not-a-branch')
.expectBadge({
label: 'build',
message: 'branch not found',
@@ -26,19 +37,25 @@ t.create('Pipeline status (nonexistent branch)')
// error message, we will simply display inaccessible
// https://github.com/badges/shields/pull/5538
t.create('Pipeline status (nonexistent repo)')
.get('/this-repo/does-not-exist/master.json')
.get('/pipeline-status/this-repo/does-not-exist.json?branch=master')
.expectBadge({
label: 'build',
message: 'inaccessible',
})
t.create('Pipeline status (custom gitlab URL)')
.get('/GNOME/pango/main.json?gitlab_url=https://gitlab.gnome.org')
.get('/pipeline-status/GNOME/pango.json?gitlab_url=https://gitlab.gnome.org')
.expectBadge({
label: 'build',
message: isBuildStatus,
})
t.create('Pipeline no branch redirect')
.get('/gitlab-org/gitlab.svg')
.expectRedirect('/gitlab/pipeline/gitlab-org/gitlab/master.svg')
.get('/pipeline/gitlab-org/gitlab.svg')
.expectRedirect('/gitlab/pipeline-status/gitlab-org/gitlab.svg?branch=master')
t.create('Pipeline legacy route with branch redirect')
.get('/pipeline/gitlab-org/gitlab/v10.7.6?style=flat')
.expectRedirect(
'/gitlab/pipeline-status/gitlab-org/gitlab.svg?branch=v10.7.6&style=flat'
)

View File

@@ -0,0 +1,152 @@
import Joi from 'joi'
import { optionalUrl } from '../validators.js'
import { latest, renderVersionBadge } from '../version.js'
import { NotFound } from '../index.js'
import GitLabBase from './gitlab-base.js'
const schema = Joi.array().items(
Joi.object({
name: Joi.string().required(),
tag_name: Joi.string().required(),
})
)
const queryParamSchema = Joi.object({
gitlab_url: optionalUrl,
include_prereleases: Joi.equal(''),
sort: Joi.string().valid('date', 'semver').default('date'),
display_name: Joi.string().valid('tag', 'release').default('tag'),
date_order_by: Joi.string()
.valid('created_at', 'released_at')
.default('created_at'),
}).required()
const documentation = `
<p>
You may use your GitLab Project Id (e.g. 25813592) or your Project Path (e.g. megabyte-labs/dockerfile/ci-pipeline/ansible-lint)
</p>
`
const commonProps = {
namedParams: {
project: 'shields-ops-group/tag-test',
},
documentation,
}
export default class GitLabRelease extends GitLabBase {
static category = 'version'
static route = {
base: 'gitlab/v/release',
pattern: ':project+',
queryParamSchema,
}
static examples = [
{
title: 'GitLab Release (latest by date)',
...commonProps,
queryParams: { sort: 'date', date_order_by: 'created_at' },
staticPreview: renderVersionBadge({ version: 'v2.0.0' }),
},
{
title: 'GitLab Release (latest by SemVer)',
...commonProps,
queryParams: { sort: 'semver' },
staticPreview: renderVersionBadge({ version: 'v4.0.0' }),
},
{
title: 'GitLab Release (latest by SemVer pre-release)',
...commonProps,
queryParams: {
sort: 'semver',
include_prereleases: null,
},
staticPreview: renderVersionBadge({ version: 'v5.0.0-beta.1' }),
},
{
title: 'GitLab Release (custom instance)',
namedParams: {
project: 'GNOME/librsvg',
},
documentation,
queryParams: {
sort: 'semver',
include_prereleases: null,
gitlab_url: 'https://gitlab.gnome.org',
date_order_by: 'created_at',
},
staticPreview: renderVersionBadge({ version: 'v2.51.4' }),
},
{
title: 'GitLab Release (by release name)',
namedParams: {
project: 'gitlab-org/gitlab',
},
documentation,
queryParams: {
sort: 'semver',
include_prereleases: null,
gitlab_url: 'https://gitlab.com',
display_name: 'release',
date_order_by: 'created_at',
},
staticPreview: renderVersionBadge({ version: 'GitLab 14.2' }),
},
]
static defaultBadgeData = { label: 'release' }
async fetch({ project, baseUrl, isSemver, orderBy }) {
// https://docs.gitlab.com/ee/api/releases/
return this.fetchPaginatedArrayData({
schema,
url: `${baseUrl}/api/v4/projects/${encodeURIComponent(project)}/releases`,
errorMessages: {
404: 'project not found',
},
options: {
qs: { order_by: orderBy },
},
firstPageOnly: !isSemver,
})
}
static transform({ releases, isSemver, includePrereleases, displayName }) {
if (releases.length === 0) {
throw new NotFound({ prettyMessage: 'no releases found' })
}
const displayKey = displayName === 'tag' ? 'tag_name' : 'name'
if (!isSemver) {
return releases[0][displayKey]
}
return latest(
releases.map(t => t[displayKey]),
{ pre: includePrereleases }
)
}
async handle(
{ project },
{
gitlab_url: baseUrl = 'https://gitlab.com',
include_prereleases: pre,
sort,
display_name: displayName,
date_order_by: orderBy,
}
) {
const isSemver = sort === 'semver'
const releases = await this.fetch({ project, baseUrl, isSemver, orderBy })
const version = this.constructor.transform({
releases,
isSemver,
includePrereleases: pre !== undefined,
displayName,
})
return renderVersionBadge({ version })
}
}

View File

@@ -0,0 +1,49 @@
import { isSemver, withRegex } from '../test-validators.js'
import { createServiceTester } from '../tester.js'
export const t = await createServiceTester()
const isGitLabDisplayVersion = withRegex(/^GitLab [1-9][0-9]*.[0-9]*/)
t.create('Release (latest by date)')
.get('/shields-ops-group/tag-test.json')
.expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' })
t.create('Release (nested groups latest by date)')
.get('/gitlab-org/frontend/eslint-plugin.json')
.expectBadge({ label: 'release', message: isSemver, color: 'blue' })
t.create('Release (latest by date, order by created_at)')
.get('/shields-ops-group/tag-test.json?date_order_by=created_at')
.expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' })
t.create('Release (latest by date, order by released_at)')
.get('/shields-ops-group/tag-test.json?date_order_by=released_at')
.expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' })
t.create('Release (project id latest by date)')
.get('/29538796.json')
.expectBadge({ label: 'release', message: 'v2.0.0', color: 'blue' })
t.create('Release (latest by semver)')
.get('/shields-ops-group/tag-test.json?sort=semver')
.expectBadge({ label: 'release', message: 'v4.0.0', color: 'blue' })
t.create('Release (latest by semver pre-release)')
.get('/shields-ops-group/tag-test.json?sort=semver&include_prereleases')
.expectBadge({ label: 'release', message: 'v5.0.0-beta.1', color: 'orange' })
t.create('Release (release display name)')
.get('/gitlab-org/gitlab.json?display_name=release')
.expectBadge({ label: 'release', message: isGitLabDisplayVersion })
t.create('Release (custom instance')
.get('/GNOME/librsvg.json?gitlab_url=https://gitlab.gnome.org')
.expectBadge({ label: 'release', message: isSemver, color: 'blue' })
t.create('Release (project not found)')
.get('/fdroid/nonexistant.json')
.expectBadge({ label: 'release', message: 'project not found' })
t.create('Release (no tags)')
.get('/fdroid/fdroiddata.json')
.expectBadge({ label: 'release', message: 'no releases found' })

View File

@@ -18,40 +18,43 @@ const queryParamSchema = Joi.object({
sort: Joi.string().valid('date', 'semver').default('date'),
}).required()
const documentation = `
<p>
You may use your GitLab Project Id (e.g. 25813592) or your Project Path (e.g. megabyte-labs/dockerfile/ci-pipeline/ansible-lint)
</p>
`
const commonProps = {
namedParams: {
project: 'shields-ops-group/tag-test',
},
documentation,
}
export default class GitlabTag extends GitLabBase {
static category = 'version'
static route = {
base: 'gitlab/v/tag',
pattern: ':user/:repo',
pattern: ':project+',
queryParamSchema,
}
static examples = [
{
title: 'GitLab tag (latest by date)',
namedParams: {
user: 'shields-ops-group',
repo: 'tag-test',
},
...commonProps,
queryParams: { sort: 'date' },
staticPreview: this.render({ version: 'v2.0.0' }),
},
{
title: 'GitLab tag (latest by SemVer)',
namedParams: {
user: 'shields-ops-group',
repo: 'tag-test',
},
...commonProps,
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',
},
...commonProps,
queryParams: {
sort: 'semver',
include_prereleases: null,
@@ -61,9 +64,9 @@ export default class GitlabTag extends GitLabBase {
{
title: 'GitLab tag (custom instance)',
namedParams: {
user: 'GNOME',
repo: 'librsvg',
project: 'GNOME/librsvg',
},
documentation,
queryParams: {
sort: 'semver',
include_prereleases: null,
@@ -82,14 +85,16 @@ export default class GitlabTag extends GitLabBase {
}
}
async fetch({ user, repo, baseUrl }) {
async fetch({ project, 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`,
url: `${baseUrl}/api/v4/projects/${encodeURIComponent(
project
)}/repository/tags`,
options: { qs: { order_by: 'updated' } },
errorMessages: {
404: 'repo not found',
@@ -113,14 +118,14 @@ export default class GitlabTag extends GitLabBase {
}
async handle(
{ user, repo },
{ project },
{
gitlab_url: baseUrl = 'https://gitlab.com',
include_prereleases: pre,
sort,
}
) {
const tags = await this.fetch({ user, repo, baseUrl })
const tags = await this.fetch({ project, baseUrl })
const version = this.constructor.transform({
tags,
sort,

View File

@@ -33,7 +33,7 @@ describe('GitLabTag', function () {
await GitLabTag.invoke(
defaultContext,
config,
{ user: 'foo', repo: 'bar' },
{ project: 'foo/bar' },
{}
)
).to.deep.equal({

View File

@@ -6,6 +6,14 @@ 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 (nested groups)')
.get('/megabyte-labs/dockerfile/ci-pipeline/ansible-lint.json')
.expectBadge({ label: 'tag', message: isSemver, color: 'blue' })
t.create('Tag (project id latest by date)')
.get('/29538796.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' })
@@ -14,7 +22,7 @@ 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')
t.create('Tag (custom instance)')
.get('/GNOME/librsvg.json?gitlab_url=https://gitlab.gnome.org')
.expectBadge({ label: 'tag', message: isSemver, color: 'blue' })

View File

@@ -1,6 +1,7 @@
import Joi from 'joi'
import { metric, addv, maybePluralize } from '../text-formatters.js'
import { downloadCount, version as versionColor } from '../color-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { addv, maybePluralize } from '../text-formatters.js'
import { version as versionColor } from '../color-formatters.js'
import { BaseJsonService } from '../index.js'
const hexSchema = Joi.object({
@@ -92,24 +93,23 @@ class HexPmVersion extends BaseHexPmService {
}
}
function DownloadsForInterval(interval) {
const { base, messageSuffix, name } = {
function DownloadsForInterval(downloadInterval) {
const { base, interval, name } = {
day: {
base: 'hexpm/dd',
messageSuffix: '/day',
interval: 'day',
name: 'HexPmDownloadsDay',
},
week: {
base: 'hexpm/dw',
messageSuffix: '/week',
interval: 'week',
name: 'HexPmDownloadsWeek',
},
all: {
base: 'hexpm/dt',
messageSuffix: '',
name: 'HexPmDownloadsTotal',
},
}[interval]
}[downloadInterval]
return class HexPmDownloads extends BaseHexPmService {
static name = name
@@ -125,22 +125,16 @@ function DownloadsForInterval(interval) {
{
title: 'Hex.pm',
namedParams: { packageName: 'plug' },
staticPreview: this.render({ downloads: 85000 }),
staticPreview: renderDownloadsBadge({ downloads: 85000 }),
},
]
static defaultBadgeData = { label: 'downloads' }
static render({ downloads }) {
return {
message: `${metric(downloads)}${messageSuffix}`,
color: downloadCount(downloads),
}
}
async handle({ packageName }) {
const json = await this.fetch({ packageName })
return this.constructor.render({ downloads: json.downloads[interval] })
const downloads = json.downloads[downloadInterval]
return renderDownloadsBadge({ downloads, interval })
}
}
}

View File

@@ -1,6 +1,5 @@
import Joi from 'joi'
import { downloadCount } from '../color-formatters.js'
import { metric } from '../text-formatters.js'
import { renderDownloadsBadge } from '../downloads.js'
import { BaseJsonService } from '../index.js'
import { nonNegativeInteger } from '../validators.js'
@@ -19,15 +18,15 @@ function getSchema({ formula }) {
const periodMap = {
dm: {
api_field: '30d',
suffix: '/month',
interval: 'month',
},
dq: {
api_field: '90d',
suffix: '/quarter',
interval: 'quarter',
},
dy: {
api_field: '365d',
suffix: '/year',
interval: 'year',
},
}
@@ -43,19 +42,12 @@ export default class HomebrewDownloads extends BaseJsonService {
{
title: 'homebrew downloads',
namedParams: { interval: 'dm', formula: 'cake' },
staticPreview: this.render({ interval: 'dm', downloads: 93 }),
staticPreview: renderDownloadsBadge({ interval: 'month', downloads: 93 }),
},
]
static defaultBadgeData = { label: 'downloads' }
static render({ interval, downloads }) {
return {
message: `${metric(downloads)}${periodMap[interval].suffix}`,
color: downloadCount(downloads),
}
}
async fetch({ formula }) {
const schema = getSchema({ formula })
return this._requestJson({
@@ -66,10 +58,12 @@ export default class HomebrewDownloads extends BaseJsonService {
}
async handle({ interval, formula }) {
const data = await this.fetch({ formula })
return this.constructor.render({
interval,
downloads: data.analytics.install[periodMap[interval].api_field][formula],
const {
analytics: { install },
} = await this.fetch({ formula })
return renderDownloadsBadge({
downloads: install[periodMap[interval].api_field][formula],
interval: periodMap[interval].interval,
})
}
}

Some files were not shown because too many files have changed in this diff Show More