Compare commits
1 Commits
server-202
...
integratio
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7da38f8354 |
369
.circleci/config.yml
Normal file
369
.circleci/config.yml
Normal file
@@ -0,0 +1,369 @@
|
||||
version: 2
|
||||
|
||||
main_steps: &main_steps
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
npm ci
|
||||
environment:
|
||||
# https://docs.cypress.io/guides/getting-started/installing-cypress.html#Skipping-installation
|
||||
# We don't need to install the Cypress binary in jobs that aren't actually running Cypress.
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
|
||||
- run:
|
||||
name: Linter
|
||||
when: always
|
||||
command: npm run lint
|
||||
|
||||
- run:
|
||||
name: Core tests
|
||||
when: always
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/core/results.xml
|
||||
command: npm run test:core
|
||||
|
||||
- run:
|
||||
name: Entrypoint tests
|
||||
when: always
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/entrypoint/results.xml
|
||||
command: npm run test:entrypoint
|
||||
|
||||
- store_test_results:
|
||||
path: junit
|
||||
|
||||
- run:
|
||||
name: 'Prettier check (quick fix: `npm run prettier`)'
|
||||
when: always
|
||||
command: npm run prettier:check
|
||||
|
||||
integration_steps: &integration_steps
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
npm ci
|
||||
environment:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
|
||||
- run:
|
||||
name: Integration tests
|
||||
when: always
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/integration/results.xml
|
||||
command: npm run test:integration
|
||||
|
||||
- store_test_results:
|
||||
path: junit
|
||||
|
||||
services_steps: &services_steps
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
npm ci
|
||||
environment:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
|
||||
- run:
|
||||
name: Identify services tagged in the PR title
|
||||
command: npm run test:services:pr:prepare
|
||||
|
||||
- run:
|
||||
name: Run tests for tagged services
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/services/results.xml
|
||||
command: RETRY_COUNT=3 npm run test:services:pr:run
|
||||
|
||||
- store_test_results:
|
||||
path: junit
|
||||
|
||||
package_steps: &package_steps
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install node and npm
|
||||
command: |
|
||||
set +e
|
||||
export NVM_DIR="/opt/circleci/.nvm"
|
||||
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
|
||||
nvm install v14
|
||||
nvm use v14
|
||||
npm install -g npm
|
||||
|
||||
# Run the package tests on each currently supported node version. See:
|
||||
# https://github.com/badges/shields/blob/master/badge-maker/README.md#node-version-support
|
||||
# https://nodejs.org/en/about/releases/
|
||||
|
||||
- run:
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/badge-maker/v12/results.xml
|
||||
NODE_VERSION: v12
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
NPM_CONFIG_ENGINE_STRICT: 'false'
|
||||
name: Run package tests on Node 12
|
||||
command: scripts/run_package_tests.sh
|
||||
|
||||
- run:
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/badge-maker/v14/results.xml
|
||||
NODE_VERSION: v14
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
NPM_CONFIG_ENGINE_STRICT: 'false'
|
||||
name: Run package tests on Node 14
|
||||
command: scripts/run_package_tests.sh
|
||||
|
||||
- run:
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/badge-maker/v16/results.xml
|
||||
NODE_VERSION: v16
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
name: Run package tests on Node 16
|
||||
command: scripts/run_package_tests.sh
|
||||
|
||||
- store_test_results:
|
||||
path: junit
|
||||
|
||||
jobs:
|
||||
main:
|
||||
docker:
|
||||
- image: cimg/node:16.15
|
||||
|
||||
<<: *main_steps
|
||||
|
||||
main@node-17:
|
||||
docker:
|
||||
- image: cimg/node:17.9
|
||||
|
||||
<<: *main_steps
|
||||
|
||||
integration:
|
||||
docker:
|
||||
- image: cimg/node:16.15
|
||||
- image: redis
|
||||
|
||||
<<: *integration_steps
|
||||
|
||||
integration@node-17:
|
||||
docker:
|
||||
- image: cimg/node:17.9
|
||||
- image: redis
|
||||
|
||||
<<: *integration_steps
|
||||
|
||||
danger:
|
||||
docker:
|
||||
- image: cimg/node:16.15
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: npm ci
|
||||
environment:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
|
||||
- run:
|
||||
name: Danger
|
||||
when: always
|
||||
environment:
|
||||
# https://github.com/gatsbyjs/gatsby/pull/11555
|
||||
NODE_ENV: test
|
||||
command: npm run danger ci
|
||||
|
||||
frontend:
|
||||
docker:
|
||||
- image: cimg/node:16.15
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
npm ci
|
||||
environment:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
|
||||
- run:
|
||||
name: Prepare frontend tests
|
||||
command: npm run defs && npm run features
|
||||
|
||||
- run:
|
||||
name: Check types
|
||||
command: npm run check-types:frontend
|
||||
|
||||
- run:
|
||||
name: Frontend unit tests
|
||||
environment:
|
||||
mocha_reporter: mocha-junit-reporter
|
||||
MOCHA_FILE: junit/frontend/results.xml
|
||||
when: always
|
||||
command: npm run test:frontend
|
||||
|
||||
- store_test_results:
|
||||
path: junit
|
||||
|
||||
- run:
|
||||
name: Frontend build completes successfully
|
||||
when: always
|
||||
command: npm run build
|
||||
|
||||
package:
|
||||
machine:
|
||||
image: 'ubuntu-2004:202111-02'
|
||||
|
||||
<<: *package_steps
|
||||
|
||||
services:
|
||||
docker:
|
||||
- image: cimg/node:16.15
|
||||
|
||||
<<: *services_steps
|
||||
|
||||
services@node-17:
|
||||
docker:
|
||||
- image: cimg/node:17.9
|
||||
|
||||
<<: *services_steps
|
||||
|
||||
e2e:
|
||||
docker:
|
||||
- image: cypress/base:16.14.0
|
||||
|
||||
steps:
|
||||
- checkout
|
||||
|
||||
- restore_cache:
|
||||
name: Restore Cypress binary
|
||||
keys:
|
||||
- v2-cypress-dependencies-{{ checksum "package-lock.json" }}
|
||||
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
npm ci
|
||||
|
||||
- run:
|
||||
name: Frontend build
|
||||
command: GATSBY_BASE_URL=http://localhost:8080 npm run build
|
||||
|
||||
- run:
|
||||
name: Run tests
|
||||
environment:
|
||||
CYPRESS_REPORTER: junit
|
||||
MOCHA_FILE: junit/e2e/results.xml
|
||||
command: npm run e2e-on-build
|
||||
|
||||
- store_test_results:
|
||||
path: junit
|
||||
|
||||
- store_artifacts:
|
||||
path: cypress/videos
|
||||
|
||||
- store_artifacts:
|
||||
path: cypress/screenshots
|
||||
|
||||
- save_cache:
|
||||
name: Cache Cypress binary
|
||||
paths:
|
||||
# https://docs.cypress.io/guides/getting-started/installing-cypress.html#Binary-cache
|
||||
- ~/.cache/Cypress
|
||||
key: v2-cypress-dependencies-{{ checksum "package-lock.json" }}
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
|
||||
on-commit:
|
||||
jobs:
|
||||
- main:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
- main@node-17:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
- integration@node-17:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
- frontend:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
- package:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
- services:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- master
|
||||
- gh-pages
|
||||
- services@node-17:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- master
|
||||
- gh-pages
|
||||
- danger:
|
||||
filters:
|
||||
branches:
|
||||
ignore:
|
||||
- master
|
||||
- gh-pages
|
||||
- /dependabot\/.*/
|
||||
- e2e:
|
||||
filters:
|
||||
branches:
|
||||
ignore: gh-pages
|
||||
# on-commit-with-cache:
|
||||
# jobs:
|
||||
# - npm-install:
|
||||
# filters:
|
||||
# branches:
|
||||
# ignore: gh-pages
|
||||
# - main:
|
||||
# requires:
|
||||
# - npm-install
|
||||
# - main@node-latest:
|
||||
# requires:
|
||||
# - npm-install
|
||||
# - frontend:
|
||||
# requires:
|
||||
# - npm-install
|
||||
# - services:
|
||||
# requires:
|
||||
# - npm-install
|
||||
# filters:
|
||||
# branches:
|
||||
# ignore: master
|
||||
# - services@node-latest:
|
||||
# requires:
|
||||
# - npm-install
|
||||
# filters:
|
||||
# branches:
|
||||
# ignore: master
|
||||
# - danger:
|
||||
# requires:
|
||||
# - npm-install
|
||||
# filters:
|
||||
# branches:
|
||||
# ignore: /dependabot\/.*/
|
||||
@@ -3,7 +3,6 @@ shields.env
|
||||
.git/
|
||||
.gitignore
|
||||
.vscode/
|
||||
fly.toml
|
||||
|
||||
# Improve layer cacheability.
|
||||
Dockerfile
|
||||
|
||||
@@ -2,6 +2,7 @@ extends:
|
||||
- standard
|
||||
- standard-jsx
|
||||
- standard-react
|
||||
- plugin:@typescript-eslint/recommended
|
||||
- prettier
|
||||
- eslint:recommended
|
||||
|
||||
@@ -17,13 +18,12 @@ settings:
|
||||
react:
|
||||
version: '16.8'
|
||||
jsdoc:
|
||||
mode: typescript
|
||||
mode: jsdoc
|
||||
|
||||
plugins:
|
||||
- chai-friendly
|
||||
- jsdoc
|
||||
- mocha
|
||||
- icedfrisby
|
||||
- no-extension-in-require
|
||||
- sort-class-members
|
||||
- import
|
||||
@@ -35,34 +35,40 @@ overrides:
|
||||
# list of rules, even if they only apply to certain files. That way the
|
||||
# rules listed here are only ones which conflict.
|
||||
|
||||
- files:
|
||||
- 'badge-maker/**/*.js'
|
||||
- '**/*.cjs'
|
||||
env:
|
||||
node: true
|
||||
es6: true
|
||||
|
||||
- files:
|
||||
- '**/*.js'
|
||||
- '!frontend/**/*.js'
|
||||
- '!badge-maker/**/*.js'
|
||||
env:
|
||||
node: true
|
||||
es6: true
|
||||
rules:
|
||||
no-console: 'off'
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off'
|
||||
|
||||
- files:
|
||||
- '**/*.@(ts|tsx)'
|
||||
parserOptions:
|
||||
sourceType: 'module'
|
||||
parser: '@typescript-eslint/parser'
|
||||
rules:
|
||||
no-console: 'off'
|
||||
# Argh.
|
||||
'@typescript-eslint/explicit-function-return-type':
|
||||
['error', { 'allowExpressions': true }]
|
||||
'@typescript-eslint/no-empty-function': 'error'
|
||||
'@typescript-eslint/no-var-requires': 'error'
|
||||
'@typescript-eslint/no-object-literal-type-assertion': 'off'
|
||||
'@typescript-eslint/no-explicit-any': 'error'
|
||||
'@typescript-eslint/ban-ts-ignore': 'off'
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off'
|
||||
|
||||
- files:
|
||||
- '**/*.ts'
|
||||
- core/**/*.ts
|
||||
parserOptions:
|
||||
sourceType: 'module'
|
||||
parser: '@typescript-eslint/parser'
|
||||
|
||||
- files:
|
||||
- 'frontend/**/*.js'
|
||||
- gatsby-browser.js
|
||||
- 'frontend/**/*.@(js|ts|tsx)'
|
||||
parserOptions:
|
||||
sourceType: 'module'
|
||||
env:
|
||||
@@ -107,20 +113,21 @@ overrides:
|
||||
mocha: true
|
||||
rules:
|
||||
mocha/no-exclusive-tests: 'error'
|
||||
mocha/no-skipped-tests: 'error'
|
||||
mocha/no-mocha-arrows: 'error'
|
||||
mocha/prefer-arrow-callback: 'error'
|
||||
|
||||
- files:
|
||||
- 'services/**/*.tester.js'
|
||||
rules:
|
||||
icedfrisby/no-exclusive-tests: 'error'
|
||||
icedfrisby/no-skipped-tests: 'error'
|
||||
|
||||
rules:
|
||||
# Disable some rules from eslint:recommended.
|
||||
no-empty: ['error', { 'allowEmptyCatch': true }]
|
||||
|
||||
# Allow unused parameters. In callbacks, removing them seems to obscure
|
||||
# what the functions are doing.
|
||||
'@typescript-eslint/no-unused-vars': ['error', { 'args': 'none' }]
|
||||
no-unused-vars: 'off'
|
||||
|
||||
'@typescript-eslint/no-var-requires': 'off'
|
||||
|
||||
'@typescript-eslint/no-use-before-define': 'error'
|
||||
no-use-before-define: 'off'
|
||||
|
||||
# These should be disabled by eslint-config-prettier, but are not.
|
||||
@@ -137,8 +144,6 @@ rules:
|
||||
func-style: ['error', 'declaration', { 'allowArrowFunctions': true }]
|
||||
new-cap: ['error', { 'capIsNew': true }]
|
||||
import/order: ['error', { 'newlines-between': 'never' }]
|
||||
quotes:
|
||||
['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }]
|
||||
|
||||
# Account for destructuring responses from upstream services,
|
||||
# many of which do not follow camelcase
|
||||
@@ -171,7 +176,7 @@ rules:
|
||||
jsdoc/check-tag-names: 'error'
|
||||
jsdoc/check-types: 'error'
|
||||
jsdoc/implements-on-classes: 'error'
|
||||
jsdoc/tag-lines: ['error', 'any', { 'startLines': 1 }]
|
||||
jsdoc/newline-after-description: 'error'
|
||||
jsdoc/require-param: 'error'
|
||||
jsdoc/require-param-description: 'error'
|
||||
jsdoc/require-param-name: 'error'
|
||||
@@ -182,7 +187,11 @@ rules:
|
||||
jsdoc/require-returns-type: 'error'
|
||||
jsdoc/valid-types: 'error'
|
||||
|
||||
react/prop-types: 'off'
|
||||
# Disable some from TypeScript.
|
||||
'@typescript-eslint/camelcase': off
|
||||
'@typescript-eslint/explicit-function-return-type': 'off'
|
||||
'@typescript-eslint/no-empty-function': 'off'
|
||||
|
||||
react/jsx-sort-props: 'error'
|
||||
react-hooks/rules-of-hooks: 'error'
|
||||
react-hooks/exhaustive-deps: 'error'
|
||||
|
||||
37
.github/ISSUE_TEMPLATE/3_Badge_request.md
vendored
Normal file
37
.github/ISSUE_TEMPLATE/3_Badge_request.md
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
name: 💡 Badge Request
|
||||
about: Ideas for new badges
|
||||
labels: 'service-badge'
|
||||
---
|
||||
|
||||
:clipboard: **Description**
|
||||
|
||||
<!--
|
||||
A clear and concise description of the new badge.
|
||||
|
||||
- Which service is this badge for e.g: GitHub, Travis CI
|
||||
- What sort of information should this badge show?
|
||||
Provide an example in plain text e.g: "version | v1.01" or as a static badge
|
||||
(static badge generator can be found at https://shields.io)
|
||||
-->
|
||||
|
||||
:link: **Data**
|
||||
|
||||
<!--
|
||||
Where can we get the data from?
|
||||
|
||||
- Is there a public API?
|
||||
- Does the API requires an API key?
|
||||
- Link to the API documentation.
|
||||
-->
|
||||
|
||||
:microphone: **Motivation**
|
||||
|
||||
<!--
|
||||
Please explain why this feature should be implemented and how it would be used.
|
||||
|
||||
- What is the specific use case?
|
||||
-->
|
||||
|
||||
<!-- Love Shields? Please consider donating $10 to sustain our activities:
|
||||
👉 https://opencollective.com/shields -->
|
||||
62
.github/ISSUE_TEMPLATE/3_Badge_request.yml
vendored
62
.github/ISSUE_TEMPLATE/3_Badge_request.yml
vendored
@@ -1,62 +0,0 @@
|
||||
name: '💡 Badge Request'
|
||||
description: Ideas for new badges
|
||||
labels: ['service-badge']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: >
|
||||
## Ideas for new badges
|
||||
|
||||
|
||||
This issue template is for suggesting new badges which
|
||||
**fetch and display data from an upstream service**.
|
||||
If your suggestion is for a static badge
|
||||
(which shows the same information every time it is requested), it is
|
||||
[already possible to make these](https://shields.io/docs/static-badges).
|
||||
We don't add specific routes for badges which only show static information.
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: '📋 Description'
|
||||
description: |
|
||||
A clear and concise description of the new badge.
|
||||
|
||||
- Which service is this badge for e.g: GitHub, Travis CI
|
||||
- What sort of information should this badge show?
|
||||
Provide an example in plain text e.g: "version | v1.01" or as a static badge
|
||||
(static badge generator can be found at https://shields.io/badges/static-badge )
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: data
|
||||
attributes:
|
||||
label: '🔗 Data'
|
||||
description: |
|
||||
Where can we get the data from?
|
||||
|
||||
Please consider and cover details like:
|
||||
- Is there a public API?
|
||||
- Does the API require authentication or an API key?
|
||||
If so, please review our documentation on [Badges Requiring Authentication](https://github.com/badges/shields/blob/master/doc/authentication.md)
|
||||
- Link to the API documentation.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: motivation
|
||||
attributes:
|
||||
label: '🎤 Motivation'
|
||||
description: |
|
||||
Please explain why this feature should be implemented and how it would be used.
|
||||
|
||||
- What is the specific use case?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## :heart: Love Shields?
|
||||
Please consider donating $10 to sustain our activities: [https://opencollective.com/shields](https://opencollective.com/shields)
|
||||
12
.github/actions/close-bot/action.yml
vendored
Normal file
12
.github/actions/close-bot/action.yml
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
name: 'Auto Approve'
|
||||
description: 'Automatically approve/close selected pull requests for shields.io'
|
||||
branding:
|
||||
icon: 'check-circle'
|
||||
color: 'green'
|
||||
inputs:
|
||||
github-token:
|
||||
description: 'The GITHUB_TOKEN secret'
|
||||
required: true
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'index.js'
|
||||
68
.github/actions/close-bot/helpers.js
vendored
Normal file
68
.github/actions/close-bot/helpers.js
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
'use strict'
|
||||
|
||||
function findChangelogStart(lines) {
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i]
|
||||
if (
|
||||
line === '<summary>Changelog</summary>' &&
|
||||
lines[i + 2] === '<blockquote>'
|
||||
) {
|
||||
return i + 3
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function findChangelogEnd(lines, start) {
|
||||
for (let i = start; i < lines.length; i++) {
|
||||
const line = lines[i]
|
||||
if (line === '</blockquote>') {
|
||||
return i
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
function allChangelogLinesAreVersionBump(changelogLines) {
|
||||
return (
|
||||
changelogLines.length > 0 &&
|
||||
changelogLines.length ===
|
||||
changelogLines.filter(line =>
|
||||
line.includes('Version bump only for package')
|
||||
).length
|
||||
)
|
||||
}
|
||||
|
||||
function isPointlessVersionBump(body) {
|
||||
const pointlessBumpLinks = [
|
||||
'https://github.com/gatsbyjs/gatsby',
|
||||
'https://github.com/typescript-eslint/typescript-eslint',
|
||||
]
|
||||
|
||||
const lines = body.split(/\r?\n/)
|
||||
if (!pointlessBumpLinks.some(link => lines[0].includes(link))) {
|
||||
return false
|
||||
}
|
||||
const start = findChangelogStart(lines)
|
||||
const end = findChangelogEnd(lines, start)
|
||||
if (!start || !end) {
|
||||
return false
|
||||
}
|
||||
const changelogLines = lines
|
||||
.slice(start, end)
|
||||
.filter(line => !line.startsWith('<h'))
|
||||
.filter(line => !line.startsWith('<p>All notable changes'))
|
||||
.filter(
|
||||
line => !line.startsWith('See <a href="https://conventionalcommits.org">')
|
||||
)
|
||||
.filter(line => !line.startsWith('<!--'))
|
||||
.filter(
|
||||
line =>
|
||||
!line.startsWith(
|
||||
'<p><a href="https://www.gatsbyjs.com/docs/reference/release-notes/'
|
||||
)
|
||||
)
|
||||
return allChangelogLinesAreVersionBump(changelogLines)
|
||||
}
|
||||
|
||||
module.exports = { isPointlessVersionBump }
|
||||
38
.github/actions/close-bot/index.js
vendored
Normal file
38
.github/actions/close-bot/index.js
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
'use strict'
|
||||
|
||||
const core = require('@actions/core')
|
||||
const github = require('@actions/github')
|
||||
const { isPointlessVersionBump } = require('./helpers')
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const token = core.getInput('github-token', { required: true })
|
||||
|
||||
const { pull_request: pr } = github.context.payload
|
||||
if (!pr) {
|
||||
throw new Error('Event payload missing `pull_request`')
|
||||
}
|
||||
|
||||
const client = github.getOctokit(token)
|
||||
|
||||
if (
|
||||
['dependabot[bot]', 'dependabot-preview[bot]'].includes(pr.user.login)
|
||||
) {
|
||||
if (isPointlessVersionBump(pr.body)) {
|
||||
core.debug(`Closing pull request #${pr.number}`)
|
||||
await client.rest.pulls.update({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
pull_number: pr.number,
|
||||
state: 'closed',
|
||||
})
|
||||
|
||||
core.debug(`Done.`)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
core.setFailed(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
416
.github/actions/close-bot/package-lock.json
generated
vendored
Normal file
416
.github/actions/close-bot/package-lock.json
generated
vendored
Normal file
@@ -0,0 +1,416 @@
|
||||
{
|
||||
"name": "close-bot",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "close-bot",
|
||||
"version": "0.0.0",
|
||||
"license": "CC0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.9.0",
|
||||
"@actions/github": "^5.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz",
|
||||
"integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.3.tgz",
|
||||
"integrity": "sha512-myjA/pdLQfhUGLtRZC/J4L1RXOG4o6aYdiEq+zr5wVVKljzbFld+xv10k1FX6IkIJtNxbAq44BdwSNpQ015P0A==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"@octokit/core": "^3.6.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.17.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
|
||||
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
|
||||
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
|
||||
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.6.3",
|
||||
"@octokit/request-error": "^2.0.5",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "6.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
|
||||
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
|
||||
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^5.6.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz",
|
||||
"integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA=="
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "2.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz",
|
||||
"integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.34.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz",
|
||||
"integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.34.0",
|
||||
"deprecation": "^2.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
|
||||
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.1.0",
|
||||
"@octokit/types": "^6.16.1",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
|
||||
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "6.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz",
|
||||
"integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^11.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz",
|
||||
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ=="
|
||||
},
|
||||
"node_modules/deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"node_modules/is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
||||
"engines": {
|
||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.9.0.tgz",
|
||||
"integrity": "sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA==",
|
||||
"requires": {
|
||||
"@actions/http-client": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"@actions/github": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.0.3.tgz",
|
||||
"integrity": "sha512-myjA/pdLQfhUGLtRZC/J4L1RXOG4o6aYdiEq+zr5wVVKljzbFld+xv10k1FX6IkIJtNxbAq44BdwSNpQ015P0A==",
|
||||
"requires": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"@octokit/core": "^3.6.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.17.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
|
||||
}
|
||||
},
|
||||
"@actions/http-client": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
|
||||
"integrity": "sha512-PIXiMVtz6VvyaRsGY268qvj57hXQEpsYogYOu2nrQhlf+XCGmZstmuZBbAybUl1nQGnvS1k1eEsQ69ZoD7xlSw==",
|
||||
"requires": {
|
||||
"tunnel": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"@octokit/auth-token": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
|
||||
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"@octokit/core": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
|
||||
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
|
||||
"requires": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.6.3",
|
||||
"@octokit/request-error": "^2.0.5",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/endpoint": {
|
||||
"version": "6.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
|
||||
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/graphql": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
|
||||
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
|
||||
"requires": {
|
||||
"@octokit/request": "^5.6.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/openapi-types": {
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz",
|
||||
"integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA=="
|
||||
},
|
||||
"@octokit/plugin-paginate-rest": {
|
||||
"version": "2.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz",
|
||||
"integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.34.0"
|
||||
}
|
||||
},
|
||||
"@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.13.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz",
|
||||
"integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.34.0",
|
||||
"deprecation": "^2.3.1"
|
||||
}
|
||||
},
|
||||
"@octokit/request": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
|
||||
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
|
||||
"requires": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.1.0",
|
||||
"@octokit/types": "^6.16.1",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"@octokit/request-error": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
|
||||
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
|
||||
"requires": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"@octokit/types": {
|
||||
"version": "6.34.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz",
|
||||
"integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==",
|
||||
"requires": {
|
||||
"@octokit/openapi-types": "^11.2.0"
|
||||
}
|
||||
},
|
||||
"before-after-hook": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz",
|
||||
"integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ=="
|
||||
},
|
||||
"deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q=="
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "2.6.7",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz",
|
||||
"integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==",
|
||||
"requires": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
|
||||
"requires": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o="
|
||||
},
|
||||
"tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
|
||||
},
|
||||
"universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE="
|
||||
},
|
||||
"whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=",
|
||||
"requires": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
|
||||
}
|
||||
}
|
||||
}
|
||||
16
.github/actions/close-bot/package.json
vendored
Normal file
16
.github/actions/close-bot/package.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "close-bot",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "chris48s",
|
||||
"license": "CC0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.9.0",
|
||||
"@actions/github": "^5.0.3"
|
||||
}
|
||||
}
|
||||
21
.github/actions/core-tests/action.yml
vendored
21
.github/actions/core-tests/action.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: 'Core tests'
|
||||
description: 'Run core and entrypoint tests'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Core tests
|
||||
if: always()
|
||||
run: npm run test:core -- --reporter json --reporter-option 'output=reports/core.json'
|
||||
shell: bash
|
||||
|
||||
- name: Entrypoint tests
|
||||
if: always()
|
||||
run: npm run test:entrypoint -- --reporter json --reporter-option 'output=reports/entrypoint.json'
|
||||
shell: bash
|
||||
|
||||
- name: Write Markdown Summary
|
||||
if: always()
|
||||
run: |
|
||||
node scripts/mocha2md.js Core reports/core.json >> $GITHUB_STEP_SUMMARY
|
||||
node scripts/mocha2md.js Entrypoint reports/entrypoint.json >> $GITHUB_STEP_SUMMARY
|
||||
shell: bash
|
||||
@@ -1,12 +0,0 @@
|
||||
name: 'docusaurus-theme-openapi swizzled component changes warning'
|
||||
description: 'Check for changes in docusaurus-theme-openapi components which are swizzled and prints out a warning'
|
||||
branding:
|
||||
icon: 'alert-triangle'
|
||||
color: 'yellow'
|
||||
inputs:
|
||||
github-token:
|
||||
description: 'The GITHUB_TOKEN secret'
|
||||
required: true
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'index.js'
|
||||
@@ -1,105 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Returns info about all files changed in a PR (max 3000 results)
|
||||
*
|
||||
* @param {object} client hydrated octokit ready to use for GitHub Actions
|
||||
* @param {string} owner repo owner
|
||||
* @param {string} repo repo name
|
||||
* @param {number} pullNumber pull request number
|
||||
* @returns {object[]} array of object that describe pr changed files - see https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#list-pull-requests-files
|
||||
*/
|
||||
async function getAllFilesForPullRequest(client, owner, repo, pullNumber) {
|
||||
const perPage = 100 // Max number of items per page
|
||||
let page = 1 // Start with the first page
|
||||
let allFiles = []
|
||||
while (true) {
|
||||
const response = await client.rest.pulls.listFiles({
|
||||
owner,
|
||||
repo,
|
||||
pull_number: pullNumber,
|
||||
per_page: perPage,
|
||||
page,
|
||||
})
|
||||
|
||||
if (response.data.length === 0) {
|
||||
// Break the loop if no more results
|
||||
break
|
||||
}
|
||||
|
||||
allFiles = allFiles.concat(response.data)
|
||||
page++ // Move to the next page
|
||||
}
|
||||
return allFiles
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of files changed betwen two tags for a github repo
|
||||
*
|
||||
* @param {object} client hydrated octokit ready to use for GitHub Actions
|
||||
* @param {string} owner repo owner
|
||||
* @param {string} repo repo name
|
||||
* @param {string} baseTag base tag
|
||||
* @param {string} headTag head tag
|
||||
* @returns {string[]} Array listing all changed files betwen the base tag and the head tag
|
||||
*/
|
||||
async function getChangedFilesBetweenTags(
|
||||
client,
|
||||
owner,
|
||||
repo,
|
||||
baseTag,
|
||||
headTag,
|
||||
) {
|
||||
const response = await client.rest.repos.compareCommits({
|
||||
owner,
|
||||
repo,
|
||||
base: baseTag,
|
||||
head: headTag,
|
||||
})
|
||||
|
||||
return response.data.files.map(file => file.filename)
|
||||
}
|
||||
|
||||
function findKeyEndingWith(obj, ending) {
|
||||
for (const key in obj) {
|
||||
if (key.endsWith(ending)) {
|
||||
return key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get large (>1MB) JSON file from git repo on at ref as a json object
|
||||
*
|
||||
* @param {object} client Hydrated octokit ready to use for GitHub Actions
|
||||
* @param {string} owner Repo owner
|
||||
* @param {string} repo Repo name
|
||||
* @param {string} path Path of the file in repo relative to root directory
|
||||
* @param {string} ref Git refrence (commit, branch, tag)
|
||||
* @returns {string[]} Array listing all changed files betwen the base tag and the head tag
|
||||
*/
|
||||
async function getLargeJsonAtRef(client, owner, repo, path, ref) {
|
||||
const fileSha = (
|
||||
await client.rest.repos.getContent({
|
||||
owner,
|
||||
repo,
|
||||
path,
|
||||
ref,
|
||||
})
|
||||
).data.sha
|
||||
const fileBlob = (
|
||||
await client.rest.git.getBlob({
|
||||
owner,
|
||||
repo,
|
||||
file_sha: fileSha,
|
||||
})
|
||||
).data.content
|
||||
return JSON.parse(Buffer.from(fileBlob, 'base64').toString())
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAllFilesForPullRequest,
|
||||
getChangedFilesBetweenTags,
|
||||
findKeyEndingWith,
|
||||
getLargeJsonAtRef,
|
||||
}
|
||||
148
.github/actions/docusaurus-swizzled-warning/index.js
vendored
148
.github/actions/docusaurus-swizzled-warning/index.js
vendored
@@ -1,148 +0,0 @@
|
||||
'use strict'
|
||||
|
||||
const core = require('@actions/core')
|
||||
const github = require('@actions/github')
|
||||
const {
|
||||
getAllFilesForPullRequest,
|
||||
getChangedFilesBetweenTags,
|
||||
findKeyEndingWith,
|
||||
getLargeJsonAtRef,
|
||||
} = require('./helpers')
|
||||
|
||||
async function run() {
|
||||
try {
|
||||
const token = core.getInput('github-token', { required: true })
|
||||
|
||||
const { pull_request: pr } = github.context.payload
|
||||
if (!pr) {
|
||||
throw new Error('Event payload missing `pull_request`')
|
||||
}
|
||||
|
||||
const client = github.getOctokit(token)
|
||||
const packageName = 'docusaurus-theme-openapi'
|
||||
const packageParentName = 'docusaurus-preset-openapi'
|
||||
const overideComponents = ['Curl', 'Response']
|
||||
const messageTemplate = `<table><thead><tr><th colspan="2">
|
||||
⚠️ This PR contains changes to components of ${packageName} we've overridden
|
||||
</th></tr>
|
||||
<tr><th colspan="2">
|
||||
We need to watch out for changes to the ${overideComponents.join(
|
||||
', ',
|
||||
)} components
|
||||
</th></tr></thead>
|
||||
`
|
||||
|
||||
if (
|
||||
!['dependabot[bot]', 'dependabot-preview[bot]'].includes(pr.user.login)
|
||||
) {
|
||||
return
|
||||
}
|
||||
const files = await getAllFilesForPullRequest(
|
||||
client,
|
||||
github.context.repo.owner,
|
||||
github.context.repo.repo,
|
||||
pr.number,
|
||||
)
|
||||
|
||||
const file = files.filter(f => f.filename === 'package-lock.json')[0]
|
||||
if (file === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const prCommitRefForFile = file.contents_url.split('ref=')[1]
|
||||
const pkgLockNewJson = await getLargeJsonAtRef(
|
||||
client,
|
||||
github.context.repo.owner,
|
||||
github.context.repo.repo,
|
||||
file.filename,
|
||||
prCommitRefForFile,
|
||||
)
|
||||
const pkgLockOldJson = await getLargeJsonAtRef(
|
||||
client,
|
||||
github.context.repo.owner,
|
||||
github.context.repo.repo,
|
||||
file.filename,
|
||||
'master',
|
||||
)
|
||||
|
||||
const oldVesionModuleKey = findKeyEndingWith(
|
||||
pkgLockOldJson.packages,
|
||||
`node_modules/${packageName}`,
|
||||
)
|
||||
const newVesionModuleKey = findKeyEndingWith(
|
||||
pkgLockNewJson.packages,
|
||||
`node_modules/${packageName}`,
|
||||
)
|
||||
let oldVersion = pkgLockOldJson.packages[oldVesionModuleKey].version
|
||||
let newVersion = pkgLockNewJson.packages[newVesionModuleKey].version
|
||||
|
||||
const oldVesionModuleKeyParent = findKeyEndingWith(
|
||||
pkgLockOldJson.packages,
|
||||
`node_modules/${packageParentName}`,
|
||||
)
|
||||
const newVesionModuleKeyParent = findKeyEndingWith(
|
||||
pkgLockNewJson.packages,
|
||||
`node_modules/${packageParentName}`,
|
||||
)
|
||||
const oldVersionParent =
|
||||
pkgLockOldJson.packages[oldVesionModuleKeyParent].dependencies[
|
||||
packageName
|
||||
].substring(1)
|
||||
const newVersionParent =
|
||||
pkgLockNewJson.packages[newVesionModuleKeyParent].dependencies[
|
||||
packageName
|
||||
].substring(1)
|
||||
|
||||
// if parent dependency is higher version then existing
|
||||
// npm install will retrive the newer version from the parent dependency
|
||||
if (oldVersionParent > oldVersion) {
|
||||
oldVersion = oldVersionParent
|
||||
}
|
||||
if (newVersionParent > newVersion) {
|
||||
newVersion = newVersionParent
|
||||
}
|
||||
core.info(`oldVersion=${oldVersion}`)
|
||||
core.info(`newVersion=${newVersion}`)
|
||||
|
||||
if (newVersion !== oldVersion) {
|
||||
const pkgChangedFiles = await getChangedFilesBetweenTags(
|
||||
client,
|
||||
'cloud-annotations',
|
||||
'docusaurus-openapi',
|
||||
`v${oldVersion}`,
|
||||
`v${newVersion}`,
|
||||
)
|
||||
const changedComponents = overideComponents.filter(
|
||||
componenet =>
|
||||
pkgChangedFiles.filter(
|
||||
path =>
|
||||
path.includes('docusaurus-theme-openapi/src/theme') &&
|
||||
path.includes(componenet),
|
||||
).length > 0,
|
||||
)
|
||||
const versionReport = `<tbody><tr><td> Old version </td><td> ${oldVersion} </td></tr>
|
||||
<tr><td> New version </td><td> ${newVersion} </td></tr>
|
||||
`
|
||||
const changedComponentsReport = `<tr><td> Overide components changed </td><td> ${changedComponents.join(
|
||||
', ',
|
||||
)} </td></tr></tbody></table>
|
||||
`
|
||||
const body = messageTemplate + versionReport + changedComponentsReport
|
||||
await client.rest.issues.createComment({
|
||||
owner: github.context.repo.owner,
|
||||
repo: github.context.repo.repo,
|
||||
issue_number: pr.number,
|
||||
body,
|
||||
})
|
||||
|
||||
core.info('Found changes and posted comment, done.')
|
||||
return
|
||||
}
|
||||
|
||||
core.info('No changes found, done.')
|
||||
} catch (error) {
|
||||
core.setFailed(error.message)
|
||||
}
|
||||
}
|
||||
|
||||
run()
|
||||
236
.github/actions/docusaurus-swizzled-warning/package-lock.json
generated
vendored
236
.github/actions/docusaurus-swizzled-warning/package-lock.json
generated
vendored
@@ -1,236 +0,0 @@
|
||||
{
|
||||
"name": "docusaurus-swizzled-warning",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "docusaurus-swizzled-warning",
|
||||
"version": "0.0.0",
|
||||
"license": "CC0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/github": "^5.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
|
||||
"integrity": "sha512-2aZDDa3zrrZbP5ZYg159sNoLRb61nQ7awl5pSvIq5Qpj81vwDzdMRKzkWJGJuwVvWpvZKx7vspJALyvaaIQyug==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/github": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/github/-/github-5.1.1.tgz",
|
||||
"integrity": "sha512-Nk59rMDoJaV+mHCOJPXuvB1zIbomlKS0dmSIqPGxd0enAXBnOfn4VWF+CGtRCwXZG9Epa54tZA7VIRlJDS8A6g==",
|
||||
"dependencies": {
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"@octokit/core": "^3.6.0",
|
||||
"@octokit/plugin-paginate-rest": "^2.17.0",
|
||||
"@octokit/plugin-rest-endpoint-methods": "^5.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@actions/http-client": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.1.1.tgz",
|
||||
"integrity": "sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw==",
|
||||
"dependencies": {
|
||||
"tunnel": "^0.0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/auth-token": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz",
|
||||
"integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/core": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz",
|
||||
"integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==",
|
||||
"dependencies": {
|
||||
"@octokit/auth-token": "^2.4.4",
|
||||
"@octokit/graphql": "^4.5.8",
|
||||
"@octokit/request": "^5.6.3",
|
||||
"@octokit/request-error": "^2.0.5",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"before-after-hook": "^2.2.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/endpoint": {
|
||||
"version": "6.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz",
|
||||
"integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/graphql": {
|
||||
"version": "4.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz",
|
||||
"integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==",
|
||||
"dependencies": {
|
||||
"@octokit/request": "^5.6.0",
|
||||
"@octokit/types": "^6.0.3",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/openapi-types": {
|
||||
"version": "12.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz",
|
||||
"integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ=="
|
||||
},
|
||||
"node_modules/@octokit/plugin-paginate-rest": {
|
||||
"version": "2.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz",
|
||||
"integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.40.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=2"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/plugin-rest-endpoint-methods": {
|
||||
"version": "5.16.2",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz",
|
||||
"integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.39.0",
|
||||
"deprecation": "^2.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@octokit/core": ">=3"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request": {
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz",
|
||||
"integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==",
|
||||
"dependencies": {
|
||||
"@octokit/endpoint": "^6.0.1",
|
||||
"@octokit/request-error": "^2.1.0",
|
||||
"@octokit/types": "^6.16.1",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"universal-user-agent": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/request-error": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz",
|
||||
"integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==",
|
||||
"dependencies": {
|
||||
"@octokit/types": "^6.0.3",
|
||||
"deprecation": "^2.0.0",
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@octokit/types": {
|
||||
"version": "6.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz",
|
||||
"integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==",
|
||||
"dependencies": {
|
||||
"@octokit/openapi-types": "^12.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/before-after-hook": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz",
|
||||
"integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ=="
|
||||
},
|
||||
"node_modules/deprecation": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz",
|
||||
"integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ=="
|
||||
},
|
||||
"node_modules/is-plain-object": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
|
||||
"integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/node-fetch": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
|
||||
"integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
|
||||
"dependencies": {
|
||||
"whatwg-url": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "4.x || >=6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"encoding": "^0.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"encoding": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/tr46": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
|
||||
},
|
||||
"node_modules/tunnel": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
|
||||
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
|
||||
"engines": {
|
||||
"node": ">=0.6.11 <=0.7.0 || >=0.7.3"
|
||||
}
|
||||
},
|
||||
"node_modules/universal-user-agent": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz",
|
||||
"integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w=="
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "8.3.2",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
|
||||
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/webidl-conversions": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
|
||||
},
|
||||
"node_modules/whatwg-url": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
|
||||
"dependencies": {
|
||||
"tr46": "~0.0.3",
|
||||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"name": "docusaurus-swizzled-warning",
|
||||
"version": "0.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "jNullj",
|
||||
"license": "CC0",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.10.0",
|
||||
"@actions/github": "^5.1.1"
|
||||
}
|
||||
}
|
||||
2
.github/actions/draft-release/Dockerfile
vendored
2
.github/actions/draft-release/Dockerfile
vendored
@@ -1,4 +1,4 @@
|
||||
FROM node:18-bullseye
|
||||
FROM node:12-buster
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y jq
|
||||
|
||||
9
.github/actions/draft-release/entrypoint.sh
vendored
9
.github/actions/draft-release/entrypoint.sh
vendored
@@ -2,17 +2,14 @@
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
# mark workspace dir as 'safe'
|
||||
git config --system --add safe.directory '/github/workspace'
|
||||
# Set up a git user
|
||||
git config user.name "release[bot]"
|
||||
git config user.email "actions@users.noreply.github.com"
|
||||
|
||||
# Find last server-YYYY-MM-DD tag
|
||||
git fetch --unshallow --tags
|
||||
LAST_TAG=$(git tag | grep server | tail -n 1)
|
||||
|
||||
# Set up a git user
|
||||
git config user.name "release[bot]"
|
||||
git config user.email "actions@users.noreply.github.com"
|
||||
|
||||
# Find the marker in CHANGELOG.md
|
||||
INSERT_POINT=$(grep -n "^\-\-\-$" CHANGELOG.md | cut -f1 -d:)
|
||||
INSERT_POINT=$((INSERT_POINT+1))
|
||||
|
||||
28
.github/actions/integration-tests/action.yml
vendored
28
.github/actions/integration-tests/action.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: 'Integration tests'
|
||||
description: 'Run integration tests'
|
||||
inputs:
|
||||
github-token:
|
||||
description: 'The GITHUB_TOKEN secret'
|
||||
required: true
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Migrate DB
|
||||
if: always()
|
||||
run: npm run migrate up
|
||||
env:
|
||||
POSTGRES_URL: postgresql://postgres:postgres@localhost:5432/ci_test
|
||||
shell: bash
|
||||
|
||||
- name: Integration Tests
|
||||
if: always()
|
||||
run: npm run test:integration -- --reporter json --reporter-option 'output=reports/integration-tests.json'
|
||||
env:
|
||||
GH_TOKEN: '${{ inputs.github-token }}'
|
||||
POSTGRES_URL: postgresql://postgres:postgres@localhost:5432/ci_test
|
||||
shell: bash
|
||||
|
||||
- name: Write Markdown Summary
|
||||
if: always()
|
||||
run: node scripts/mocha2md.js Integration reports/integration-tests.json >> $GITHUB_STEP_SUMMARY
|
||||
shell: bash
|
||||
26
.github/actions/package-tests/action.yml
vendored
26
.github/actions/package-tests/action.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: 'Package tests'
|
||||
description: 'Run package tests and check types'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Tests
|
||||
if: always()
|
||||
run: npm run test:package -- --reporter json --reporter-option 'output=reports/package-tests.json'
|
||||
shell: bash
|
||||
|
||||
- name: Type Checks
|
||||
if: always()
|
||||
run: |
|
||||
set -o pipefail
|
||||
npm run check-types:package 2>&1 | tee reports/package-types.txt
|
||||
shell: bash
|
||||
|
||||
- name: Write Markdown Summary
|
||||
if: always()
|
||||
run: |
|
||||
node scripts/mocha2md.js 'Package Tests' reports/package-tests.json >> $GITHUB_STEP_SUMMARY
|
||||
echo '# Package Types' >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
cat reports/package-types.txt >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
shell: bash
|
||||
86
.github/actions/service-tests/action.yml
vendored
86
.github/actions/service-tests/action.yml
vendored
@@ -1,86 +0,0 @@
|
||||
name: 'Service tests'
|
||||
description: 'Run tests for selected services'
|
||||
inputs:
|
||||
github-token:
|
||||
description: 'The GITHUB_TOKEN secret'
|
||||
required: true
|
||||
librariesio-tokens:
|
||||
description: 'The SERVICETESTS_LIBRARIESIO_TOKENS secret'
|
||||
required: false
|
||||
default: ''
|
||||
obs-user:
|
||||
description: 'The SERVICETESTS_OBS_USER secret'
|
||||
required: false
|
||||
default: ''
|
||||
obs-pass:
|
||||
description: 'The SERVICETESTS_OBS_PASS secret'
|
||||
required: false
|
||||
default: ''
|
||||
sl-insight-user-uuid:
|
||||
description: 'The SERVICETESTS_SL_INSIGHT_USER_UUID secret'
|
||||
required: false
|
||||
default: ''
|
||||
sl-insight-api-token:
|
||||
description: 'The SERVICETESTS_SL_INSIGHT_API_TOKEN secret'
|
||||
required: false
|
||||
default: ''
|
||||
twitch-client-id:
|
||||
description: 'The SERVICETESTS_TWITCH_CLIENT_ID secret'
|
||||
required: false
|
||||
default: ''
|
||||
twitch-client-secret:
|
||||
description: 'The SERVICETESTS_TWITCH_CLIENT_SECRET secret'
|
||||
required: false
|
||||
default: ''
|
||||
wheelmap-token:
|
||||
description: 'The SERVICETESTS_WHEELMAP_TOKEN secret'
|
||||
required: false
|
||||
default: ''
|
||||
youtube-api-key:
|
||||
description: 'The SERVICETESTS_YOUTUBE_API_KEY secret'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Derive list of service tests to run
|
||||
# Note: In this step we are using an intermediate env var instead of
|
||||
# passing github.event.pull_request.title as an argument
|
||||
# to prevent a shell injection attack. Further reading:
|
||||
# https://securitylab.github.com/research/github-actions-untrusted-input/#exploitability-and-impact
|
||||
# https://securitylab.github.com/research/github-actions-untrusted-input/#remediation
|
||||
if: always()
|
||||
env:
|
||||
TITLE: ${{ github.event.pull_request.title }}
|
||||
run: npm run test:services:pr:prepare "$TITLE"
|
||||
shell: bash
|
||||
|
||||
- name: Run service tests
|
||||
if: always()
|
||||
run: npm run test:services:pr:run -- --reporter json --reporter-option 'output=reports/service-tests.json'
|
||||
shell: bash
|
||||
env:
|
||||
RETRY_COUNT: 3
|
||||
GH_TOKEN: '${{ inputs.github-token }}'
|
||||
LIBRARIESIO_TOKENS: '${{ inputs.librariesio-tokens }}'
|
||||
OBS_USER: '${{ inputs.obs-user }}'
|
||||
OBS_PASS: '${{ inputs.obs-pass }}'
|
||||
SL_INSIGHT_USER_UUID: '${{ inputs.sl-insight-user-uuid }}'
|
||||
SL_INSIGHT_API_TOKEN: '${{ inputs.sl-insight-api-token }}'
|
||||
TWITCH_CLIENT_ID: '${{ inputs.twitch-client-id }}'
|
||||
TWITCH_CLIENT_SECRET: '${{ inputs.twitch-client-secret }}'
|
||||
WHEELMAP_TOKEN: '${{ inputs.wheelmap-token }}'
|
||||
YOUTUBE_API_KEY: '${{ inputs.youtube-api-key }}'
|
||||
|
||||
- name: Write Markdown Summary
|
||||
if: always()
|
||||
run: |
|
||||
if test -f 'reports/service-tests.json'; then
|
||||
echo '# Services' >> $GITHUB_STEP_SUMMARY
|
||||
sed -e 's/^/- /' pull-request-services.log >> $GITHUB_STEP_SUMMARY
|
||||
node scripts/mocha2md.js Report reports/service-tests.json >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo 'No services found. Nothing to do.' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
shell: bash
|
||||
40
.github/actions/setup/action.yml
vendored
40
.github/actions/setup/action.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: 'Set up project'
|
||||
description: 'Set up project'
|
||||
inputs:
|
||||
node-version:
|
||||
description: 'Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0.'
|
||||
required: true
|
||||
cypress:
|
||||
description: 'Install Cypress binary (boolean)'
|
||||
type: boolean
|
||||
# https://docs.cypress.io/guides/getting-started/installing-cypress.html#Skipping-installation
|
||||
# We don't need to install the Cypress binary in jobs that aren't actually running Cypress.
|
||||
required: false
|
||||
default: false
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: Install Node JS ${{ inputs.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ inputs.node-version }}
|
||||
|
||||
- name: Install NPM 9
|
||||
run: npm install -g npm@^9.0.0
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies
|
||||
if: ${{ inputs.cypress == 'false' }}
|
||||
env:
|
||||
CYPRESS_INSTALL_BINARY: 0
|
||||
run: |
|
||||
echo "skipping cypress binary"
|
||||
npm ci
|
||||
shell: bash
|
||||
|
||||
- name: Install dependencies (including cypress binary)
|
||||
if: ${{ inputs.cypress == 'true' }}
|
||||
run: |
|
||||
echo "installing cypress binary"
|
||||
npm ci
|
||||
shell: bash
|
||||
23
.github/dependabot.yml
vendored
23
.github/dependabot.yml
vendored
@@ -8,7 +8,6 @@ updates:
|
||||
day: friday
|
||||
time: '12:00'
|
||||
open-pull-requests-limit: 99
|
||||
rebase-strategy: disabled
|
||||
ignore:
|
||||
# https://github.com/badges/shields/issues/7324
|
||||
# https://github.com/badges/shields/issues/7447
|
||||
@@ -17,6 +16,9 @@ updates:
|
||||
- dependency-name: 'decamelize'
|
||||
- dependency-name: 'humanize-string'
|
||||
|
||||
# https://github.com/badges/shields/pull/7288#issuecomment-974699240
|
||||
- dependency-name: '@types/node'
|
||||
|
||||
# badge-maker package dependencies
|
||||
- package-ecosystem: npm
|
||||
directory: '/badge-maker'
|
||||
@@ -25,22 +27,17 @@ updates:
|
||||
day: friday
|
||||
time: '12:00'
|
||||
open-pull-requests-limit: 99
|
||||
rebase-strategy: disabled
|
||||
|
||||
# GH actions
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 99
|
||||
rebase-strategy: disabled
|
||||
|
||||
# docusaurus-swizzled-warning package dependencies
|
||||
# close-bot package dependencies
|
||||
- package-ecosystem: npm
|
||||
directory: '/.github/actions/docusaurus-swizzled-warning'
|
||||
directory: '/.github/actions/close-bot'
|
||||
schedule:
|
||||
interval: weekly
|
||||
day: friday
|
||||
time: '12:00'
|
||||
open-pull-requests-limit: 99
|
||||
rebase-strategy: disabled
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: weekly
|
||||
open-pull-requests-limit: 99
|
||||
|
||||
10
.github/scripts/cleanup-review-apps.sh
vendored
10
.github/scripts/cleanup-review-apps.sh
vendored
@@ -1,10 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
apps=$(flyctl apps list --json | jq -r .[].ID | grep -E "pr-[0-9]+-badges-shields") || exit 0
|
||||
|
||||
for app in $apps
|
||||
do
|
||||
flyctl apps destroy "$app" -y
|
||||
done
|
||||
34
.github/scripts/deploy-review-app.sh
vendored
34
.github/scripts/deploy-review-app.sh
vendored
@@ -1,34 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euxo pipefail
|
||||
|
||||
app="pr-$PR_NUMBER-badges-shields"
|
||||
region="ewr"
|
||||
org="shields-io"
|
||||
|
||||
# Get PR JSON from the API
|
||||
# This will fail if $PR_NUMBER is not a valid PR
|
||||
pr_json=$(curl --fail "https://api.github.com/repos/badges/shields/pulls/$PR_NUMBER")
|
||||
|
||||
# Checkout the PR branch
|
||||
git config user.name "actions[bot]"
|
||||
git config user.email "actions@users.noreply.github.com"
|
||||
git fetch origin "pull/$PR_NUMBER/head:pr-$PR_NUMBER"
|
||||
git checkout "pr-$PR_NUMBER"
|
||||
|
||||
# If the app does not already exist, create it
|
||||
if ! flyctl status --app "$app"; then
|
||||
flyctl launch --no-deploy --copy-config --name "$app" --region "$region" --org "$org"
|
||||
echo $SECRETS | tr " " "\n" | flyctl secrets import --app "$app"
|
||||
fi
|
||||
|
||||
# Deploy
|
||||
flyctl deploy --app "$app" --region "$region"
|
||||
|
||||
# Post a comment on the PR
|
||||
app_url=$(flyctl status --app "$app" --json | jq -r .Hostname)
|
||||
comment_url=$(echo "$pr_json" | jq .comments_url -r)
|
||||
curl "$comment_url" \
|
||||
-X POST \
|
||||
-H "Authorization: token $GITHUB_TOKEN" \
|
||||
--data "{\"body\":\"🚀 Updated review app: https://$app_url\"}"
|
||||
@@ -1,13 +1,11 @@
|
||||
name: Docusaurus swizzled component changes warning
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
name: Auto close
|
||||
on: pull_request_target
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
docusaurus-swizzled-warning:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
@@ -15,8 +13,8 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install action dependencies
|
||||
run: cd .github/actions/docusaurus-swizzled-warning && npm ci
|
||||
run: cd .github/actions/close-bot && npm ci
|
||||
|
||||
- uses: ./.github/actions/docusaurus-swizzled-warning
|
||||
- uses: ./.github/actions/close-bot
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
11
.github/workflows/build-docker-image.yml
vendored
11
.github/workflows/build-docker-image.yml
vendored
@@ -1,12 +1,9 @@
|
||||
name: Build Docker Image
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- 'gh-readonly-queue/**'
|
||||
|
||||
jobs:
|
||||
build-docker-image:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -14,17 +11,15 @@ jobs:
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
version: v0.9.1
|
||||
|
||||
- name: Set Git Short SHA
|
||||
run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: false
|
||||
tags: ghcr.io/badges/shields:pr-validation
|
||||
tags: shieldsio/shields:pr-validation
|
||||
build-args: |
|
||||
version=${{ env.SHORT_SHA }}
|
||||
|
||||
24
.github/workflows/cleanup-review-apps.yml
vendored
24
.github/workflows/cleanup-review-apps.yml
vendored
@@ -1,24 +0,0 @@
|
||||
name: Cleanup Review Apps
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 7 * * *'
|
||||
# At 07:00, daily
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
cleanup-review-apps:
|
||||
runs-on: ubuntu-latest
|
||||
environment: 'Review Apps'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: superfly/flyctl-actions/setup-flyctl@master
|
||||
|
||||
- name: install jq
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -y jq
|
||||
|
||||
- run: .github/scripts/cleanup-review-apps.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||
21
.github/workflows/create-release.yml
vendored
21
.github/workflows/create-release.yml
vendored
@@ -6,7 +6,6 @@ on:
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
@@ -36,8 +35,6 @@ jobs:
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
version: v0.9.1
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
@@ -46,26 +43,10 @@ jobs:
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push snapshot release to DockerHub
|
||||
uses: docker/build-push-action@v4
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: shieldsio/shields:server-${{ steps.date.outputs.date }}
|
||||
build-args: |
|
||||
version=server-${{ steps.date.outputs.date }}
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push snapshot release to GHCR
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ghcr.io/badges/shields:server-${{ steps.date.outputs.date }}
|
||||
build-args: |
|
||||
version=server-${{ steps.date.outputs.date }}
|
||||
|
||||
29
.github/workflows/danger.yml
vendored
29
.github/workflows/danger.yml
vendored
@@ -1,29 +0,0 @@
|
||||
name: Danger
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
|
||||
permissions:
|
||||
checks: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
|
||||
jobs:
|
||||
danger:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor != 'dependabot[bot]' && github.actor != 'repo-ranger[bot]'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Danger
|
||||
run: npm run danger ci
|
||||
env:
|
||||
# https://github.com/gatsbyjs/gatsby/pull/11555
|
||||
NODE_ENV: test
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
11
.github/workflows/deploy-docs.yml
vendored
11
.github/workflows/deploy-docs.yml
vendored
@@ -8,7 +8,7 @@ permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
deploy-docs:
|
||||
build-and-deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -16,13 +16,10 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Build
|
||||
run: npm run build-docs
|
||||
run: |
|
||||
npm ci
|
||||
npm run build-docs
|
||||
|
||||
- name: Deploy
|
||||
uses: JamesIves/github-pages-deploy-action@v4
|
||||
|
||||
43
.github/workflows/deploy-review-app.yml
vendored
43
.github/workflows/deploy-review-app.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Create/Update Review App
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR Number to deploy e.g: 1234'
|
||||
required: true
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
deploy-review-app:
|
||||
runs-on: ubuntu-latest
|
||||
environment: 'Review Apps'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: superfly/flyctl-actions/setup-flyctl@master
|
||||
|
||||
- name: install jq
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -y jq
|
||||
|
||||
- run: .github/scripts/deploy-review-app.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
|
||||
PR_NUMBER: ${{ github.event.inputs.pr_number }}
|
||||
# credentials to set when we create the review app
|
||||
SECRETS: |
|
||||
GH_TOKEN=${{ secrets.GH_PAT }}
|
||||
LIBRARIESIO_TOKENS=${{ secrets.SERVICETESTS_LIBRARIESIO_TOKENS }}
|
||||
OBS_USER=${{ secrets.SERVICETESTS_OBS_USER }}
|
||||
OBS_PASS=${{ secrets.SERVICETESTS_OBS_PASS }}
|
||||
SL_INSIGHT_API_TOKEN=${{ secrets.SERVICETESTS_SL_INSIGHT_USER_UUID }}
|
||||
SL_INSIGHT_USER_UUID=${{ secrets.SERVICETESTS_SL_INSIGHT_API_TOKEN }}
|
||||
TWITCH_CLIENT_ID=${{ secrets.SERVICETESTS_TWITCH_CLIENT_ID }}
|
||||
TWITCH_CLIENT_SECRET=${{ secrets.SERVICETESTS_TWITCH_CLIENT_SECRET }}
|
||||
WHEELMAP_TOKEN=${{ secrets.SERVICETESTS_WHEELMAP_TOKEN }}
|
||||
YOUTUBE_API_KEY=${{ secrets.SERVICETESTS_YOUTUBE_API_KEY }}
|
||||
2
.github/workflows/draft-release.yml
vendored
2
.github/workflows/draft-release.yml
vendored
@@ -10,7 +10,7 @@ permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
draft-release:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
name: 'Dependency Review'
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
enforce-dependency-review:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@v3
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@v3
|
||||
uses: actions/dependency-review-action@v2
|
||||
|
||||
31
.github/workflows/publish-docker-next.yml
vendored
31
.github/workflows/publish-docker-next.yml
vendored
@@ -4,11 +4,8 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
permissions:
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
publish-docker-next:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -16,8 +13,6 @@ jobs:
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
version: v0.9.1
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
@@ -28,31 +23,11 @@ jobs:
|
||||
- name: Set Git Short SHA
|
||||
run: echo "SHORT_SHA=${GITHUB_SHA::7}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push to DockerHub
|
||||
id: docker_build_push
|
||||
uses: docker/build-push-action@v4
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: shieldsio/shields:next
|
||||
build-args: |
|
||||
version=${{ env.SHORT_SHA }}
|
||||
|
||||
- name: Output Image Digest
|
||||
run: echo ${{ steps.docker_build_push.outputs.digest }} >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Login to GHCR
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push to GHCR
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ghcr.io/badges/shields:next
|
||||
build-args: |
|
||||
version=${{ env.SHORT_SHA }}
|
||||
|
||||
67
.github/workflows/test-bug-run-badge.yml
vendored
67
.github/workflows/test-bug-run-badge.yml
vendored
@@ -1,67 +0,0 @@
|
||||
name: Test new bug report badge
|
||||
run-name: Test bug report on issue ${{ github.event.issue.number }}
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
jobs:
|
||||
extract-bug-badge-url:
|
||||
if: ${{ contains(github.event.issue.labels.*.name, 'question') }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
runBadgeTest: ${{ steps.testCondition.outputs.runNext }}
|
||||
link: ${{ steps.testCondition.outputs.link }}
|
||||
steps:
|
||||
- name: Test badge test run conditions
|
||||
id: testCondition
|
||||
run: |
|
||||
product=$(echo "${{ github.event.issue.body }}" | grep -A2 "Are you experiencing an issue with.*" | tail -n 1)
|
||||
link=$(echo "${{ github.event.issue.body }}" | grep -A2 "Link to the badge.*" | tail -n 1)
|
||||
|
||||
if [[ "$product" == "shields.io" && "$link" == "https://img.shields.io"* ]]; then
|
||||
echo "runNext=true" >> "$GITHUB_OUTPUT"
|
||||
echo "link=$link" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Conditions not met. Skipping the workflow..."
|
||||
echo "runNext=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
run-bug-badge-url-test:
|
||||
needs: extract-bug-badge-url
|
||||
if: needs.extract-bug-badge-url.outputs.runBadgeTest == 'true'
|
||||
permissions:
|
||||
issues: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
cypress: false
|
||||
|
||||
- name: Output debug info
|
||||
env:
|
||||
TEST_BADGE_LINK: '${{ needs.extract-bug-badge-url.outputs.link }}'
|
||||
run: npm run badge $TEST_BADGE_LINK
|
||||
|
||||
- name: Add Comment to Issue
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
script: |
|
||||
const issueNumber = context.issue.number;
|
||||
const owner = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const runId = context.runId;
|
||||
const jobUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}`;
|
||||
const issueComment = `
|
||||
Badge tested using \`npm run badge ${{ needs.extract-bug-badge-url.outputs.link }}\`
|
||||
Output is available [here](${jobUrl})
|
||||
`;
|
||||
github.rest.issues.createComment({
|
||||
issue_number: issueNumber,
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
body: issueComment
|
||||
});
|
||||
49
.github/workflows/test-e2e.yml
vendored
49
.github/workflows/test-e2e.yml
vendored
@@ -1,49 +0,0 @@
|
||||
name: E2E
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
- 'dependabot/**'
|
||||
|
||||
jobs:
|
||||
test-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Cache Cypress binary
|
||||
id: cache-cypress
|
||||
uses: actions/cache@v3
|
||||
env:
|
||||
cache-name: cache-cypress
|
||||
with:
|
||||
path: ~/.cache/Cypress
|
||||
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
cypress: true
|
||||
|
||||
- name: Run tests
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: npm run e2e
|
||||
|
||||
- name: Archive videos
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: videos
|
||||
path: cypress/videos
|
||||
|
||||
- name: Archive screenshots
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: screenshots
|
||||
path: cypress/screenshots
|
||||
52
.github/workflows/test-integration-20.yml
vendored
52
.github/workflows/test-integration-20.yml
vendored
@@ -1,52 +0,0 @@
|
||||
name: Integration@node 20
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
- 'dependabot/**'
|
||||
|
||||
jobs:
|
||||
test-integration-20:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PAT_EXISTS: ${{ secrets.GH_PAT != '' }}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: ci_test
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 20
|
||||
env:
|
||||
NPM_CONFIG_ENGINE_STRICT: 'false'
|
||||
|
||||
- name: Integration Tests (with PAT)
|
||||
if: ${{ env.PAT_EXISTS == 'true' }}
|
||||
uses: ./.github/actions/integration-tests
|
||||
with:
|
||||
github-token: '${{ secrets.GH_PAT }}'
|
||||
|
||||
- name: Integration Tests (with workflow token)
|
||||
if: ${{ env.PAT_EXISTS == 'false' }}
|
||||
uses: ./.github/actions/integration-tests
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
50
.github/workflows/test-integration.yml
vendored
50
.github/workflows/test-integration.yml
vendored
@@ -1,50 +0,0 @@
|
||||
name: Integration
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
- 'dependabot/**'
|
||||
|
||||
jobs:
|
||||
test-integration:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
PAT_EXISTS: ${{ secrets.GH_PAT != '' }}
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: ci_test
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Integration Tests (with PAT)
|
||||
if: ${{ env.PAT_EXISTS == 'true' }}
|
||||
uses: ./.github/actions/integration-tests
|
||||
with:
|
||||
github-token: '${{ secrets.GH_PAT }}'
|
||||
|
||||
- name: Integration Tests (with workflow token)
|
||||
if: ${{ env.PAT_EXISTS == 'false' }}
|
||||
uses: ./.github/actions/integration-tests
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
28
.github/workflows/test-lint.yml
vendored
28
.github/workflows/test-lint.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: Lint
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
- 'dependabot/**'
|
||||
|
||||
jobs:
|
||||
test-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: ESLint
|
||||
if: always()
|
||||
run: npm run lint
|
||||
|
||||
- name: 'Prettier check (quick fix: `npm run prettier`)'
|
||||
if: always()
|
||||
run: npm run prettier:check
|
||||
25
.github/workflows/test-main-20.yml
vendored
25
.github/workflows/test-main-20.yml
vendored
@@ -1,25 +0,0 @@
|
||||
name: Main@node 20
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
- 'dependabot/**'
|
||||
|
||||
jobs:
|
||||
test-main-20:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 20
|
||||
env:
|
||||
NPM_CONFIG_ENGINE_STRICT: 'false'
|
||||
|
||||
- name: Core tests
|
||||
uses: ./.github/actions/core-tests
|
||||
28
.github/workflows/test-main.yml
vendored
28
.github/workflows/test-main.yml
vendored
@@ -1,28 +0,0 @@
|
||||
name: Main
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
- 'dependabot/**'
|
||||
|
||||
jobs:
|
||||
test-main:
|
||||
strategy:
|
||||
matrix:
|
||||
os: ['ubuntu-latest', 'windows-latest']
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Core tests
|
||||
uses: ./.github/actions/core-tests
|
||||
40
.github/workflows/test-package-cli.yml
vendored
40
.github/workflows/test-package-cli.yml
vendored
@@ -1,40 +0,0 @@
|
||||
name: Package CLI
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
- 'dependabot/**'
|
||||
|
||||
# Smoke test (render a badge with the CLI) with only the package
|
||||
# dependencies installed.
|
||||
|
||||
jobs:
|
||||
test-package-cli:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- node: '16'
|
||||
- node: '18'
|
||||
- node: '20'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Node JS ${{ inputs.node-version }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd badge-maker
|
||||
npm install
|
||||
npm link
|
||||
|
||||
- name: Render a badge with the CLI
|
||||
run: |
|
||||
cd badge-maker
|
||||
badge cactus grown :green @flat
|
||||
34
.github/workflows/test-package-lib.yml
vendored
34
.github/workflows/test-package-lib.yml
vendored
@@ -1,34 +0,0 @@
|
||||
name: Package Library
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened, synchronize]
|
||||
push:
|
||||
branches-ignore:
|
||||
- 'gh-pages'
|
||||
- 'dependabot/**'
|
||||
|
||||
jobs:
|
||||
test-package-lib:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- node: '16'
|
||||
engine-strict: 'false'
|
||||
- node: '18'
|
||||
engine-strict: 'true'
|
||||
- node: '20'
|
||||
engine-strict: 'false'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
env:
|
||||
NPM_CONFIG_ENGINE_STRICT: ${{ matrix.engine-strict }}
|
||||
|
||||
- name: Package tests
|
||||
uses: ./.github/actions/package-tests
|
||||
43
.github/workflows/test-services-20.yml
vendored
43
.github/workflows/test-services-20.yml
vendored
@@ -1,43 +0,0 @@
|
||||
name: Services@node 20
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
push:
|
||||
branches:
|
||||
- 'gh-readonly-queue/**'
|
||||
|
||||
jobs:
|
||||
test-services-20:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 20
|
||||
env:
|
||||
NPM_CONFIG_ENGINE_STRICT: 'false'
|
||||
|
||||
- name: Service tests (triggered from local branch)
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
uses: ./.github/actions/service-tests
|
||||
with:
|
||||
github-token: '${{ secrets.GH_PAT }}'
|
||||
librariesio-tokens: '${{ secrets.SERVICETESTS_LIBRARIESIO_TOKENS }}'
|
||||
obs-user: '${{ secrets.SERVICETESTS_OBS_USER }}'
|
||||
obs-pass: '${{ secrets.SERVICETESTS_OBS_PASS }}'
|
||||
sl-insight-user-uuid: '${{ secrets.SERVICETESTS_SL_INSIGHT_USER_UUID }}'
|
||||
sl-insight-api-token: '${{ secrets.SERVICETESTS_SL_INSIGHT_API_TOKEN }}'
|
||||
twitch-client-id: '${{ secrets.SERVICETESTS_TWITCH_CLIENT_ID }}'
|
||||
twitch-client-secret: '${{ secrets.SERVICETESTS_TWITCH_CLIENT_SECRET }}'
|
||||
wheelmap-token: '${{ secrets.SERVICETESTS_WHEELMAP_TOKEN }}'
|
||||
youtube-api-key: '${{ secrets.SERVICETESTS_YOUTUBE_API_KEY }}'
|
||||
|
||||
- name: Service tests (triggered from fork)
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
uses: ./.github/actions/service-tests
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
41
.github/workflows/test-services.yml
vendored
41
.github/workflows/test-services.yml
vendored
@@ -1,41 +0,0 @@
|
||||
name: Services
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, edited, reopened, synchronize]
|
||||
push:
|
||||
branches:
|
||||
- 'gh-readonly-queue/**'
|
||||
|
||||
jobs:
|
||||
test-services:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Service tests (triggered from local branch)
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
uses: ./.github/actions/service-tests
|
||||
with:
|
||||
github-token: '${{ secrets.GH_PAT }}'
|
||||
librariesio-tokens: '${{ secrets.SERVICETESTS_LIBRARIESIO_TOKENS }}'
|
||||
obs-user: '${{ secrets.SERVICETESTS_OBS_USER }}'
|
||||
obs-pass: '${{ secrets.SERVICETESTS_OBS_PASS }}'
|
||||
sl-insight-user-uuid: '${{ secrets.SERVICETESTS_SL_INSIGHT_USER_UUID }}'
|
||||
sl-insight-api-token: '${{ secrets.SERVICETESTS_SL_INSIGHT_API_TOKEN }}'
|
||||
twitch-client-id: '${{ secrets.SERVICETESTS_TWITCH_CLIENT_ID }}'
|
||||
twitch-client-secret: '${{ secrets.SERVICETESTS_TWITCH_CLIENT_SECRET }}'
|
||||
wheelmap-token: '${{ secrets.SERVICETESTS_WHEELMAP_TOKEN }}'
|
||||
youtube-api-key: '${{ secrets.SERVICETESTS_YOUTUBE_API_KEY }}'
|
||||
|
||||
- name: Service tests (triggered from fork)
|
||||
if: github.event.pull_request.head.repo.full_name != github.repository
|
||||
uses: ./.github/actions/service-tests
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
33
.github/workflows/update-github-api.yml
vendored
33
.github/workflows/update-github-api.yml
vendored
@@ -1,33 +0,0 @@
|
||||
name: Update GitHub API Version
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 7 * * 6'
|
||||
# At 07:00 on Saturday
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-github-api:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Check for new GitHub API version
|
||||
run: node scripts/update-github-api.js
|
||||
|
||||
- name: Create Pull Request if config has changed
|
||||
uses: peter-evans/create-pull-request@v5
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
commit-message: Update GitHub API Version
|
||||
title: Update [GitHub] API Version
|
||||
branch-suffix: random
|
||||
18
.gitignore
vendored
18
.gitignore
vendored
@@ -92,7 +92,10 @@ typings/
|
||||
|
||||
# Temporary build artifacts.
|
||||
/build
|
||||
frontend/categories/*.yaml
|
||||
.next
|
||||
badge-examples.json
|
||||
supported-features.json
|
||||
service-definitions.yml
|
||||
|
||||
# Local runtime configuration.
|
||||
/config/local*.yml
|
||||
@@ -100,6 +103,11 @@ frontend/categories/*.yaml
|
||||
# Template for the local runtime configuration.
|
||||
!/config/local*.template.yml
|
||||
|
||||
# Gatsby
|
||||
/frontend/.cache
|
||||
/frontend/public
|
||||
/public
|
||||
|
||||
# Cypress
|
||||
/cypress/videos/
|
||||
/cypress/screenshots/
|
||||
@@ -109,11 +117,3 @@ frontend/categories/*.yaml
|
||||
|
||||
# Flamebearer
|
||||
flamegraph.html
|
||||
|
||||
# config file for node-pg-migrate
|
||||
migrations-config.json
|
||||
|
||||
# Frontend/Docusaurus
|
||||
frontend/.docusaurus
|
||||
frontend/.cache-loader
|
||||
/public
|
||||
|
||||
5
.mocharc-frontend.yml
Normal file
5
.mocharc-frontend.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
reporter: mocha-env-reporter
|
||||
require:
|
||||
- '@babel/polyfill'
|
||||
- '@babel/register'
|
||||
- mocha-yaml-loader
|
||||
10
.nycrc-frontend.json
Normal file
10
.nycrc-frontend.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"reporter": ["lcov"],
|
||||
"all": false,
|
||||
"silent": true,
|
||||
"clean": false,
|
||||
"sourceMap": false,
|
||||
"instrument": false,
|
||||
"include": ["frontend/**/*.js"],
|
||||
"exclude": ["**/*.spec.js", "**/mocha-*.js"]
|
||||
}
|
||||
@@ -10,5 +10,5 @@ public
|
||||
private/*.json
|
||||
/.nyc_output
|
||||
analytics.json
|
||||
frontend/.docusaurus
|
||||
frontend/categories
|
||||
supported-features.json
|
||||
service-definitions.yml
|
||||
|
||||
221
CHANGELOG.md
221
CHANGELOG.md
@@ -4,227 +4,6 @@ Note: this changelog is for the shields.io server. The changelog for the badge-m
|
||||
|
||||
---
|
||||
|
||||
## server-2023-09-01
|
||||
|
||||
- migrate examples to openApi part 8; affects [ubuntu uptimerobot vaadin vcpkg visualstudiomarketplace wheelmap] [#9463](https://github.com/badges/shields/issues/9463)
|
||||
- Add dynamic TOML support via [DynamicToml] Service [#9517](https://github.com/badges/shields/issues/9517)
|
||||
- migrate `examples` to `openApi` part 11: enums; affects [codefactor conda depfu homebrew jsdelivr reddit sourceforge testspace vaadin github] [#9437](https://github.com/badges/shields/issues/9437)
|
||||
- Revert "log an error to sentry if an upstream service responds with `429 Too Many Requests` (#9505)" [#9523](https://github.com/badges/shields/issues/9523)
|
||||
- cache [pypi] downloads for longer [#9522](https://github.com/badges/shields/issues/9522)
|
||||
- log an error to sentry if an upstream service responds with `429 Too Many Requests` [#9505](https://github.com/badges/shields/issues/9505)
|
||||
- Docs: update TUTORIAL.md add BaseTomlService [#9518](https://github.com/badges/shields/issues/9518)
|
||||
- Replace localhost with domain in example link. [#9506](https://github.com/badges/shields/issues/9506)
|
||||
- [twitter] --> x [#9496](https://github.com/badges/shields/issues/9496)
|
||||
- use defaultBadgeData as object instead of function in tutorial docs [#9502](https://github.com/badges/shields/issues/9502)
|
||||
- add docstrings for website status service [#9495](https://github.com/badges/shields/issues/9495)
|
||||
- migrate `examples` to `openApi` part5; affects [itunes jetbrains jitpack keybase lemmy luarocks maintenance openvsx] [#9431](https://github.com/badges/shields/issues/9431)
|
||||
- migrate `examples` to `openApi` part 4; affects [ecologi elm flathub gem gitter GithubTotalDiscussions greasyfork hackage hackernews homebrew] [#9430](https://github.com/badges/shields/issues/9430)
|
||||
- [bundlejs] add badge for the npm package size [#9055](https://github.com/badges/shields/issues/9055)
|
||||
- Switch [OpenCollective] badges to use GraphQL and auth [#9387](https://github.com/badges/shields/issues/9387)
|
||||
- migrate examples to openApi part 6; affects [opencollective opm ossf powershell pub pypi reddit repology] [#9462](https://github.com/badges/shields/issues/9462)
|
||||
- devops: Add ci warning for swizzled docusaurus components [#9467](https://github.com/badges/shields/issues/9467)
|
||||
- migrate `examples` to `openApi` part 3; affects [conan cookbook coverity cpan debian docker docsrs dub eclipse] [#9429](https://github.com/badges/shields/issues/9429)
|
||||
- Add TOML support with BaseTomlService [#9438](https://github.com/badges/shields/issues/9438)
|
||||
- [Pulsar] Add Pulsar Badges for Stargazers & Downloads [#8767](https://github.com/badges/shields/issues/8767)
|
||||
- Add [CurseForge] badges [#9252](https://github.com/badges/shields/issues/9252)
|
||||
- update npm prune command [#9470](https://github.com/badges/shields/issues/9470)
|
||||
- remove obsolete entries from dependabot.yml [#9469](https://github.com/badges/shields/issues/9469)
|
||||
- output DockerHub digest in publish-docker-next workflow [#9471](https://github.com/badges/shields/issues/9471)
|
||||
- delete close-bot [#9468](https://github.com/badges/shields/issues/9468)
|
||||
- Add docstrings for version service [#9411](https://github.com/badges/shields/issues/9411)
|
||||
- deploy on node 18 [#9385](https://github.com/badges/shields/issues/9385)
|
||||
- migrate `examples` to `openApi` part 2; affects [archlinux bitcomponents bountysource cdnjs chrome clearlydefined clojars cocoapods coincap] [#9428](https://github.com/badges/shields/issues/9428)
|
||||
- allow calling [github] without auth [#9427](https://github.com/badges/shields/issues/9427)
|
||||
- fix [github] service tests [#9425](https://github.com/badges/shields/issues/9425)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-08-01
|
||||
|
||||
- Convert `examples` arrays to `openApi` objects (part 1) [#9320](https://github.com/badges/shields/issues/9320)
|
||||
- Migrate from docs.rs' builds API to status API [#9422](https://github.com/badges/shields/issues/9422)
|
||||
- [OpenVSX] Fix OpenVSX API call for unversioned package URLs [#9408](https://github.com/badges/shields/issues/9408)
|
||||
- Add support for [Lemmy] [#9368](https://github.com/badges/shields/issues/9368)
|
||||
- upgrade to npm 9 [#9323](https://github.com/badges/shields/issues/9323)
|
||||
- Go back to default YouTube cache [#9372](https://github.com/badges/shields/issues/9372)
|
||||
- Add [GitHubDiscussionsSearch] and GitHubRepoDiscussionsSearch service [#9340](https://github.com/badges/shields/issues/9340)
|
||||
- Allow user to filter github tags and releases [#9193](https://github.com/badges/shields/issues/9193)
|
||||
- don't URL encode slash in [githubactionsworkflow] badge [#9322](https://github.com/badges/shields/issues/9322)
|
||||
- add a bit of border to select boxes [#9348](https://github.com/badges/shields/issues/9348)
|
||||
- deprecate [snyk] badges [#9349](https://github.com/badges/shields/issues/9349)
|
||||
- increase max-age on [docker] badges, again [#9350](https://github.com/badges/shields/issues/9350) [#9369](https://github.com/badges/shields/issues/9369)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-07-02
|
||||
|
||||
By far the most significant change in this release is the long-awaited launch of the re-designed frontend:
|
||||
|
||||
- migrate frontend to docusaurus [#9014](https://github.com/badges/shields/issues/9014)
|
||||
- fix a load of spacing issues in frontend content [#9281](https://github.com/badges/shields/issues/9281)
|
||||
- set a sensible meta description [#9283](https://github.com/badges/shields/issues/9283)
|
||||
- chore(frontend): open homepage feature links in new tab [#9300](https://github.com/badges/shields/issues/9300)
|
||||
- adapt opencollective images to theme background [#9298](https://github.com/badges/shields/issues/9298)
|
||||
- temp fix: wrap code examples tabs in narrow browser windows [#9302](https://github.com/badges/shields/issues/9302)
|
||||
- add a bit of border to text boxes [#9324](https://github.com/badges/shields/issues/9324)
|
||||
|
||||
Other changes in this release:
|
||||
|
||||
- cache [dockerpulls] badges for an hour [#9343](https://github.com/badges/shields/issues/9343)
|
||||
- Mention YouTube API services and link to Google Privacy Policy [#9339](https://github.com/badges/shields/issues/9339)
|
||||
- allow negative timestamps in relative [date] badge [#9321](https://github.com/badges/shields/issues/9321)
|
||||
- upgrade to graphql 16 [#9290](https://github.com/badges/shields/issues/9290)
|
||||
- remove obsolete travis .org examples [#9284](https://github.com/badges/shields/issues/9284)
|
||||
- increase max age on reddit badges [#9282](https://github.com/badges/shields/issues/9282)
|
||||
- feat: Add author filter option for [GithubCommitActivity] [#9251](https://github.com/badges/shields/issues/9251)
|
||||
- Fix: [GithubCommitActivity] invalid branch error handling [#9258](https://github.com/badges/shields/issues/9258)
|
||||
- Implement a pattern for dealing with upstream APIs which are slow on the first hit; affects [endpoint] [#9233](https://github.com/badges/shields/issues/9233)
|
||||
- Delete old deprecated services [#9254](https://github.com/badges/shields/issues/9254)
|
||||
- feat: add 'canceled' status to netlify deploy badge [#9240](https://github.com/badges/shields/issues/9240)
|
||||
- increase default cache on youtube badges [#9238](https://github.com/badges/shields/issues/9238)
|
||||
- embiggen youtube cache, again [#9250](https://github.com/badges/shields/issues/9250)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-06-01
|
||||
|
||||
- feat: Add total commits to [GitHubCommitActivity] [#9196](https://github.com/badges/shields/issues/9196)
|
||||
- set a custom error on 429 [#9159](https://github.com/badges/shields/issues/9159)
|
||||
- deprecate [travis].org badges [#9171](https://github.com/badges/shields/issues/9171)
|
||||
- count private sponsors on [GithubSponsors] badge [#9170](https://github.com/badges/shields/issues/9170)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-05-01
|
||||
|
||||
** Removal:** For users who need to maintain a Github Token pool, storage has been provided via the `RedisTokenPersistence` and `REDIS_URL` settings. This feature was deprecated in `server-2023-03-01`. As of this release, the `RedisTokenPersistence` backend is now removed. If you are using this feature, you will need to migrate to using the `SQLTokenPersistence` backend for storage and provide a postgres connection string via the `POSTGRES_URL` setting. [#8922](https://github.com/badges/shields/issues/8922)
|
||||
|
||||
- fail to start server if there are duplicate service names [#9099](https://github.com/badges/shields/issues/9099)
|
||||
- [SourceForge] Added badges for SourceForge [#9078](https://github.com/badges/shields/issues/9078) [#9102](https://github.com/badges/shields/issues/9102)
|
||||
- crates: Use `?include=` to reduce crates.io backend load [#9081](https://github.com/badges/shields/issues/9081)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-04-02
|
||||
|
||||
- [JenkinsCoverage] Update Jenkins Code Coverage API for new plugin version [#9010](https://github.com/badges/shields/issues/9010)
|
||||
- [CTAN] fallback to date if version is empty [#9036](https://github.com/badges/shields/issues/9036)
|
||||
- Update to [CTAN] API version 2.0 [#9016](https://github.com/badges/shields/issues/9016)
|
||||
- handle missing statistics array in [VisualStudioMarketplace] badges [#8985](https://github.com/badges/shields/issues/8985)
|
||||
- [Netlify] upgrade colors for SVG parsing [#8971](https://github.com/badges/shields/issues/8971)
|
||||
- Fix [Vcpkg] version service for different version fields [#8945](https://github.com/badges/shields/issues/8945)
|
||||
- only try to close pool if one exists [#8947](https://github.com/badges/shields/issues/8947)
|
||||
- misc minor fixes to [githubsize node pypi] [#8946](https://github.com/badges/shields/issues/8946)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-03-01
|
||||
|
||||
**Deprecation:** For users who need to maintain a Github Token pool, storage has been provided via the `RedisTokenPersistence` and `REDIS_URL` settings. As of this release, the `RedisTokenPersistence` backend is now deprecated and will be removed in a future release. If you are using this feature, you will need to migrate to using the `SQLTokenPersistence` backend for storage and provide a postgres connection string via the `POSTGRES_URL` setting. [#8922](https://github.com/badges/shields/issues/8922)
|
||||
|
||||
- fix: for crates.io versions, use max_stable_version if it exists [#8687](https://github.com/badges/shields/issues/8687)
|
||||
- don't autofocus search [#8927](https://github.com/badges/shields/issues/8927)
|
||||
- Add [Vcpkg] version service [#8923](https://github.com/badges/shields/issues/8923)
|
||||
- fix: Set uid/gid in docker image to 0 [#8908](https://github.com/badges/shields/issues/8908)
|
||||
- expose port 443 in Dockerfile [#8889](https://github.com/badges/shields/issues/8889)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-02-01
|
||||
|
||||
- replace [twitter] badge with static fallback [#8842](https://github.com/badges/shields/issues/8842)
|
||||
- Add various [Polymart] badges [#8811](https://github.com/badges/shields/issues/8811)
|
||||
- update [githubpipenv] tests/examples [#8797](https://github.com/badges/shields/issues/8797)
|
||||
- deprecate [apm] service [#8773](https://github.com/badges/shields/issues/8773)
|
||||
- deprecate lgtm [#8771](https://github.com/badges/shields/issues/8771)
|
||||
- Dependency updates
|
||||
|
||||
## server-2023-01-01
|
||||
|
||||
- Breaking change: Routes for GitHub workflows badge have changed. See https://github.com/badges/shields/issues/8671 for more details
|
||||
- Behaviour change: In this release we fixed a long standing bug. GitHub badges were previously not reading the base URL from the `config.service.baseUri`.
|
||||
This release fixes that bug, bringing the code into line with the documented behaviour. This should not cause a behaviour change for most users,
|
||||
but users who had previously set a value in `config.service.baseUri` which was previously ignored could see this now have an effect.
|
||||
Users who configure their instance using env vars rather than yaml should see no change.
|
||||
- Send `X-GitHub-Api-Version` when calling [GitHub] v3 API [#8669](https://github.com/badges/shields/issues/8669)
|
||||
- add [VpmVersion] badge [#8755](https://github.com/badges/shields/issues/8755)
|
||||
- Add [modrinth] game versions [#8673](https://github.com/badges/shields/issues/8673)
|
||||
- fix debug logging of undefined query params [#8540](https://github.com/badges/shields/issues/8540), [#8757](https://github.com/badges/shields/issues/8757)
|
||||
- fall back to classifiers if [pypi] license text is really long [#8690](https://github.com/badges/shields/issues/8690)
|
||||
- allow passing key to [stackexchange] [#8539](https://github.com/badges/shields/issues/8539)
|
||||
- Dependency updates
|
||||
|
||||
## server-2022-12-01
|
||||
|
||||
- fix: support logoColor to shield icons. [#8263](https://github.com/badges/shields/issues/8263)
|
||||
- handle missing properties array in [VisualStudioMarketplaceVersion] [#8603](https://github.com/badges/shields/issues/8603)
|
||||
- deprecate [wercker] service [#8642](https://github.com/badges/shields/issues/8642)
|
||||
- Add [Coincap] Cryptocurrency badges [#8623](https://github.com/badges/shields/issues/8623)
|
||||
- Add [modrinth] version [#8604](https://github.com/badges/shields/issues/8604)
|
||||
- [factorio-mod-portal] services [#8625](https://github.com/badges/shields/issues/8625)
|
||||
- [Coveralls] for GitLab [#8584](https://github.com/badges/shields/issues/8584), [#8644](https://github.com/badges/shields/issues/8644)
|
||||
- Remove 'suggest badges' feature [#8311](https://github.com/badges/shields/issues/8311)
|
||||
- Add [modrinth] followers [#8601](https://github.com/badges/shields/issues/8601)
|
||||
- Update the [modrinth] API to v2 [#8600](https://github.com/badges/shields/issues/8600)
|
||||
- tidy up [GitHubGist] routes [#8510](https://github.com/badges/shields/issues/8510)
|
||||
- fix [flathub] version error handling [#8500](https://github.com/badges/shields/issues/8500)
|
||||
- Dependency updates
|
||||
|
||||
## server-2022-11-01
|
||||
|
||||
- [Ansible] Add collection badge [#8578](https://github.com/badges/shields/issues/8578)
|
||||
- [VisualStudioMarketplace] Add support to prerelease extensions version (Issue #8207) [#8561](https://github.com/badges/shields/issues/8561)
|
||||
- feat: add [GitlabLastCommit] service [#8508](https://github.com/badges/shields/issues/8508)
|
||||
- fix [swagger] service tests (allow 0 items in array) [#8564](https://github.com/badges/shields/issues/8564)
|
||||
- fix codecov badge for non-default branch [#8565](https://github.com/badges/shields/issues/8565)
|
||||
- Add [GitHubLastCommit] by committer badge [#8537](https://github.com/badges/shields/issues/8537)
|
||||
- [GitHubReleaseDate] - published_at field [#8543](https://github.com/badges/shields/issues/8543)
|
||||
- Fix [Testspace] with new "untested" value in case_counts array [#8544](https://github.com/badges/shields/issues/8544)
|
||||
- fix: Support WAITING status for GitHub deployments [#8521](https://github.com/badges/shields/issues/8521)
|
||||
- [Whatpulse] badge for a user and for a team [#8466](https://github.com/badges/shields/issues/8466)
|
||||
- deprecate [pkgreview] service [#8499](https://github.com/badges/shields/issues/8499)
|
||||
- Dependency updates
|
||||
|
||||
## server-2022-10-08
|
||||
|
||||
- deprecate [criterion] service [#8501](https://github.com/badges/shields/issues/8501)
|
||||
- fix formatRelativeDate error handling; run [date] [#8497](https://github.com/badges/shields/issues/8497)
|
||||
- allow/validate bitbucket_username / bitbucket_password in private config schema [#8472](https://github.com/badges/shields/issues/8472)
|
||||
- fix [pub] points badge test and example [#8498](https://github.com/badges/shields/issues/8498)
|
||||
- feat: add [GitlabLanguageCount] service [#8377](https://github.com/badges/shields/issues/8377)
|
||||
- [GitHubGistStars] add GitHub Gist Stars [#8471](https://github.com/badges/shields/issues/8471)
|
||||
- fix display/search of CII badge examples [#8473](https://github.com/badges/shields/issues/8473)
|
||||
- feat: add 2022 support to GitHub Hacktoberfest [#8468](https://github.com/badges/shields/issues/8468)
|
||||
- fix [GitLabCoverage] subgroup bug [#8401](https://github.com/badges/shields/issues/8401)
|
||||
- implement ruby gems-specific version sort/color functions [#8434](https://github.com/badges/shields/issues/8434)
|
||||
- Add `rc` to pre-release identifiers [#8435](https://github.com/badges/shields/issues/8435)
|
||||
- add [GitHub] Number of commits between branches/tags/commits [#8394](https://github.com/badges/shields/issues/8394)
|
||||
- add [Packagist] dependency version [#8371](https://github.com/badges/shields/issues/8371)
|
||||
- fix Docker build status invalid response data bug [#8392](https://github.com/badges/shields/issues/8392)
|
||||
- Dependency updates
|
||||
|
||||
## server-2022-09-04
|
||||
|
||||
- fix frontend compile for users running on Windows [#8350](https://github.com/badges/shields/issues/8350)
|
||||
- [DockerSize] Docker image size multi arch [#8290](https://github.com/badges/shields/issues/8290)
|
||||
- upgrade gatsby [#8334](https://github.com/badges/shields/issues/8334)
|
||||
- Custom domains for [JitPack] artifacts [#8333](https://github.com/badges/shields/issues/8333)
|
||||
- fix [dockerstars] service [#8316](https://github.com/badges/shields/issues/8316)
|
||||
- [BountySource] Fix: Broken Badge generation for decimal activity values [#8315](https://github.com/badges/shields/issues/8315)
|
||||
- feat: add [gitlabmergerequests] service [#8166](https://github.com/badges/shields/issues/8166)
|
||||
- Fix terminology for [ROS] version service [#8292](https://github.com/badges/shields/issues/8292)
|
||||
- feat: add [GitlabStars] service [#8209](https://github.com/badges/shields/issues/8209)
|
||||
- Fix invalid `rst` format when `alt` or `target` is present [#8275](https://github.com/badges/shields/issues/8275)
|
||||
- [GithubGistLastCommit] GitHub gist last commit [#8272](https://github.com/badges/shields/issues/8272)
|
||||
- [GitHub] GitHub file size for a specific branch [#8262](https://github.com/badges/shields/issues/8262)
|
||||
- Dependency updates
|
||||
|
||||
## server-2022-08-01
|
||||
|
||||
- [pypi] Add Framework Version Badges support [#8261](https://github.com/badges/shields/issues/8261)
|
||||
- feat: add [GitlabForks] server [#8208](https://github.com/badges/shields/issues/8208)
|
||||
- Update PyPI api according to https://warehouse.pypa.io/api-reference/json.html [#8251](https://github.com/badges/shields/issues/8251)
|
||||
- Add [galaxytoolshed] Activity [#8164](https://github.com/badges/shields/issues/8164)
|
||||
- [greasyfork] Add Greasy Fork rating badges [#8087](https://github.com/badges/shields/issues/8087)
|
||||
- refactor(deps): Replace moment with dayjs [#8192](https://github.com/badges/shields/issues/8192)
|
||||
- add spaces round pipe in [conda] badge [#8189](https://github.com/badges/shields/issues/8189)
|
||||
- Add [ROS] version service [#8169](https://github.com/badges/shields/issues/8169)
|
||||
- feat: add [gitlabissues] service [#8108](https://github.com/badges/shields/issues/8108)
|
||||
- Dependency updates
|
||||
|
||||
## server-2022-07-03
|
||||
|
||||
- Add [galaxytoolshed] services [#8114](https://github.com/badges/shields/issues/8114)
|
||||
|
||||
@@ -134,19 +134,9 @@ Prettier before a commit by default.
|
||||
When adding or changing a service [please write tests][service-tests], and ensure the [title of your Pull Requests follows the required conventions](#running-service-tests-in-pull-requests) to ensure your tests are executed.
|
||||
When changing other code, please add unit tests.
|
||||
|
||||
The integration tests are not run by default. For most contributions it is OK to skip these unless you're working directly on the code for storing the GitHub token pool in postgres.
|
||||
|
||||
To run the integration tests:
|
||||
|
||||
- You must have PostgreSQL installed. Use `brew install postgresql`, `apt-get install postgresql`, etc.
|
||||
- Set a connection string either with an env var `POSTGRES_URL=postgresql://user:pass@127.0.0.1:5432/db_name` or by using
|
||||
```yaml
|
||||
private:
|
||||
postgres_url: 'postgresql://user:pass@127.0.0.1:5432/db_name'
|
||||
```
|
||||
in a yaml config file.
|
||||
- Run `npm run migrate up` to apply DB migrations
|
||||
- Run `npm run test:integration` to run the tests
|
||||
To run the integration tests, you must have redis installed and in your PATH.
|
||||
Use `brew install redis`, `yum install redis`, etc. The test runner will
|
||||
start the server automatically.
|
||||
|
||||
[service-tests]: https://github.com/badges/shields/blob/master/doc/service-tests.md
|
||||
|
||||
|
||||
12
Dockerfile
12
Dockerfile
@@ -1,4 +1,4 @@
|
||||
FROM node:18-alpine AS Builder
|
||||
FROM node:16-alpine AS Builder
|
||||
|
||||
RUN mkdir -p /usr/src/app
|
||||
RUN mkdir /usr/src/app/private
|
||||
@@ -9,17 +9,17 @@ COPY package.json package-lock.json /usr/src/app/
|
||||
COPY badge-maker /usr/src/app/badge-maker/
|
||||
|
||||
RUN apk add python3 make g++
|
||||
RUN npm install -g "npm@^9.0.0"
|
||||
RUN npm install -g "npm@>=8"
|
||||
# We need dev deps to build the front end. We don't need Cypress, though.
|
||||
RUN NODE_ENV=development CYPRESS_INSTALL_BINARY=0 npm ci
|
||||
|
||||
COPY . /usr/src/app
|
||||
RUN npm run build
|
||||
RUN npm prune --omit=dev
|
||||
RUN npm prune --production
|
||||
RUN npm cache clean --force
|
||||
|
||||
# Use multi-stage build to reduce size
|
||||
FROM node:18-alpine
|
||||
FROM node:16-alpine
|
||||
|
||||
ARG version=dev
|
||||
ENV DOCKER_SHIELDS_VERSION=$version
|
||||
@@ -30,8 +30,8 @@ LABEL fly.version=$version
|
||||
ENV NODE_ENV production
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY --from=Builder --chown=0:0 /usr/src/app /usr/src/app
|
||||
COPY --from=Builder /usr/src/app /usr/src/app
|
||||
|
||||
CMD node server
|
||||
|
||||
EXPOSE 80 443
|
||||
EXPOSE 80
|
||||
|
||||
11
README.md
11
README.md
@@ -19,6 +19,9 @@
|
||||
<a href="https://coveralls.io/github/badges/shields">
|
||||
<img src="https://img.shields.io/coveralls/github/badges/shields"
|
||||
alt="coverage"></a>
|
||||
<a href="https://lgtm.com/projects/g/badges/shields/alerts/">
|
||||
<img src="https://img.shields.io/lgtm/alerts/g/badges/shields"
|
||||
alt="Total alerts"/></a>
|
||||
<a href="https://discord.gg/HjJCwm5">
|
||||
<img src="https://img.shields.io/discord/308323056592486420?logo=discord"
|
||||
alt="chat on Discord"></a>
|
||||
@@ -70,7 +73,7 @@ This repo hosts:
|
||||
[Make your own badges!][custom badges]
|
||||
(Quick example: `https://img.shields.io/badge/left-right-f39f37`)
|
||||
|
||||
[custom badges]: https://img.shields.io/badges/static-badge
|
||||
[custom badges]: https://shields.io/#your-badge
|
||||
|
||||
### Quickstart
|
||||
|
||||
@@ -98,8 +101,8 @@ You can read a [tutorial on how to add a badge][tutorial].
|
||||
|
||||
## Development
|
||||
|
||||
1. Install Node 18 or later. You can use the [package manager][] of your choice.
|
||||
Tests need to pass in Node 18 and 20.
|
||||
1. Install Node 16 or later. You can use the [package manager][] of your choice.
|
||||
Tests need to pass in Node 16 and 17.
|
||||
2. Clone this repository.
|
||||
3. Run `npm ci` to install the dependencies.
|
||||
4. Run `npm start` to start the badge server and the frontend dev server.
|
||||
@@ -107,7 +110,7 @@ You can read a [tutorial on how to add a badge][tutorial].
|
||||
|
||||
When server source files change, the badge server should automatically restart
|
||||
itself (using [nodemon][]). When the frontend files change, the frontend dev
|
||||
server (`docusaurus start`) should also automatically reload. However the badge
|
||||
server (`gatsby dev`) should also automatically reload. However the badge
|
||||
definitions are built only before the server first starts. To regenerate those,
|
||||
either run `npm run defs` or manually restart the server.
|
||||
|
||||
|
||||
10
app.json
10
app.json
@@ -35,16 +35,6 @@
|
||||
"WEBLATE_API_KEY": {
|
||||
"description": "Configure the API key to be used for the Weblate service.",
|
||||
"required": false
|
||||
},
|
||||
"METRICS_INFLUX_ENABLED": {
|
||||
"description": "Disable influx metrics",
|
||||
"value": "false",
|
||||
"required": false
|
||||
},
|
||||
"REQUIRE_CLOUDFLARE": {
|
||||
"description": "Allow direct traffic",
|
||||
"value": "false",
|
||||
"required": false
|
||||
}
|
||||
},
|
||||
"formation": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 4.0.0 [WIP]
|
||||
|
||||
- Drop compatibility with Node < 16
|
||||
- Drop compatibility with Node 10
|
||||
|
||||
## 3.3.1
|
||||
|
||||
@@ -275,7 +275,7 @@ badge.loadFont('/path/to/Verdana.ttf', err => {
|
||||
{ text: ['build', 'passed'], colorscheme: 'green', template: 'flat' },
|
||||
(svg, err) => {
|
||||
// svg is a string containing your badge
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -7,19 +7,19 @@ expectError(
|
||||
makeBadge({
|
||||
message: 'passed',
|
||||
style: 'invalid style',
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
expectType<string>(
|
||||
makeBadge({
|
||||
message: 'passed',
|
||||
}),
|
||||
})
|
||||
)
|
||||
expectType<string>(
|
||||
makeBadge({
|
||||
label: 'build',
|
||||
message: 'passed',
|
||||
}),
|
||||
})
|
||||
)
|
||||
expectType<string>(
|
||||
makeBadge({
|
||||
@@ -28,7 +28,7 @@ expectType<string>(
|
||||
labelColor: 'green',
|
||||
color: 'red',
|
||||
style: 'flat',
|
||||
}),
|
||||
})
|
||||
)
|
||||
|
||||
const error = new ValidationError()
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
const path = require('path')
|
||||
const isSvg = require('is-svg')
|
||||
const { spawn } = require('child-process-promise')
|
||||
const { expect, use } = require('chai')
|
||||
use(require('chai-string'))
|
||||
@@ -19,7 +20,6 @@ describe('The CLI', function () {
|
||||
})
|
||||
|
||||
it('should produce default badges', async function () {
|
||||
const { default: isSvg } = await import('is-svg')
|
||||
const { stdout } = await runCli(['cactus', 'grown'])
|
||||
expect(stdout)
|
||||
.to.satisfy(isSvg)
|
||||
@@ -28,13 +28,11 @@ describe('The CLI', function () {
|
||||
})
|
||||
|
||||
it('should produce colorschemed badges', async function () {
|
||||
const { default: isSvg } = await import('is-svg')
|
||||
const { stdout } = await runCli(['cactus', 'grown', ':green'])
|
||||
expect(stdout).to.satisfy(isSvg)
|
||||
})
|
||||
|
||||
it('should produce right-color badges', async function () {
|
||||
const { default: isSvg } = await import('is-svg')
|
||||
const { stdout } = await runCli(['cactus', 'grown', '#abcdef'])
|
||||
expect(stdout).to.satisfy(isSvg).and.to.include('#abcdef')
|
||||
})
|
||||
|
||||
@@ -69,7 +69,7 @@ function getLogoElement({ logo, horizPadding, badgeHeight, logoWidth }) {
|
||||
|
||||
function renderBadge(
|
||||
{ links, leftWidth, rightWidth, height, accessibleText },
|
||||
content,
|
||||
content
|
||||
) {
|
||||
const width = leftWidth + rightWidth
|
||||
const leftLink = links[0]
|
||||
@@ -397,7 +397,7 @@ class Plastic extends Badge {
|
||||
accessibleText: this.accessibleText,
|
||||
height: this.constructor.height,
|
||||
},
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement],
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -446,7 +446,7 @@ class Flat extends Badge {
|
||||
accessibleText: this.accessibleText,
|
||||
height: this.constructor.height,
|
||||
},
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement],
|
||||
[gradient, clipPath, backgroundGroup, this.foregroundGroupElement]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -478,7 +478,7 @@ class FlatSquare extends Badge {
|
||||
accessibleText: this.accessibleText,
|
||||
height: this.constructor.height,
|
||||
},
|
||||
[backgroundGroup, this.foregroundGroupElement],
|
||||
[backgroundGroup, this.foregroundGroupElement]
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -748,7 +748,7 @@ function social({
|
||||
accessibleText,
|
||||
height: externalHeight,
|
||||
},
|
||||
[style, gradients, backgroundGroup, logoElement, foregroundGroup],
|
||||
[style, gradients, backgroundGroup, logoElement, foregroundGroup]
|
||||
)
|
||||
}
|
||||
|
||||
@@ -978,7 +978,7 @@ function forTheBadge({
|
||||
accessibleText: createAccessibleText({ label, message }),
|
||||
height: BADGE_HEIGHT,
|
||||
},
|
||||
[backgroundGroup, foregroundGroup],
|
||||
[backgroundGroup, foregroundGroup]
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ test(toSvgColor, () => {
|
||||
given('papayawhip').expect('papayawhip')
|
||||
given('purple').expect('purple')
|
||||
forCases([given(''), given(undefined), given('not-a-color')]).expect(
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
given('lightgray').expect('#9f9f9f')
|
||||
given('informational').expect('#007ec6')
|
||||
|
||||
@@ -32,7 +32,7 @@ function _validate(format) {
|
||||
]
|
||||
if ('style' in format && !styleValues.includes(format.style)) {
|
||||
throw new ValidationError(
|
||||
`Field \`style\` must be one of (${styleValues.toString()})`,
|
||||
`Field \`style\` must be one of (${styleValues.toString()})`
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ function _clean(format) {
|
||||
cleaned[key] = format[key]
|
||||
} else {
|
||||
throw new ValidationError(
|
||||
`Unexpected field '${key}'. Allowed values are (${expectedKeys.toString()})`,
|
||||
`Unexpected field '${key}'. Allowed values are (${expectedKeys.toString()})`
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
'use strict'
|
||||
|
||||
const { expect } = require('chai')
|
||||
const isSvg = require('is-svg')
|
||||
const { makeBadge, ValidationError } = require('.')
|
||||
|
||||
describe('makeBadge function', function () {
|
||||
it('should produce badge with valid input', async function () {
|
||||
const { default: isSvg } = await import('is-svg')
|
||||
it('should produce badge with valid input', function () {
|
||||
expect(
|
||||
makeBadge({
|
||||
label: 'build',
|
||||
message: 'passed',
|
||||
}),
|
||||
})
|
||||
).to.satisfy(isSvg)
|
||||
expect(
|
||||
makeBadge({
|
||||
message: 'passed',
|
||||
}),
|
||||
})
|
||||
).to.satisfy(isSvg)
|
||||
expect(
|
||||
makeBadge({
|
||||
@@ -23,7 +23,7 @@ describe('makeBadge function', function () {
|
||||
message: 'passed',
|
||||
color: 'green',
|
||||
style: 'flat',
|
||||
}),
|
||||
})
|
||||
).to.satisfy(isSvg)
|
||||
})
|
||||
|
||||
@@ -32,44 +32,44 @@ describe('makeBadge function', function () {
|
||||
console.log(x)
|
||||
expect(() => makeBadge(x)).to.throw(
|
||||
ValidationError,
|
||||
'makeBadge takes an argument of type object',
|
||||
'makeBadge takes an argument of type object'
|
||||
)
|
||||
})
|
||||
expect(() => makeBadge({})).to.throw(
|
||||
ValidationError,
|
||||
'Field `message` is required',
|
||||
'Field `message` is required'
|
||||
)
|
||||
expect(() => makeBadge({ label: 'build' })).to.throw(
|
||||
ValidationError,
|
||||
'Field `message` is required',
|
||||
'Field `message` is required'
|
||||
)
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', labelColor: 7 }),
|
||||
makeBadge({ label: 'build', message: 'passed', labelColor: 7 })
|
||||
).to.throw(ValidationError, 'Field `labelColor` must be of type string')
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', format: 'png' }),
|
||||
makeBadge({ label: 'build', message: 'passed', format: 'png' })
|
||||
).to.throw(ValidationError, "Unexpected field 'format'")
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', template: 'flat' }),
|
||||
makeBadge({ label: 'build', message: 'passed', template: 'flat' })
|
||||
).to.throw(ValidationError, "Unexpected field 'template'")
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', foo: 'bar' }),
|
||||
makeBadge({ label: 'build', message: 'passed', foo: 'bar' })
|
||||
).to.throw(ValidationError, "Unexpected field 'foo'")
|
||||
expect(() =>
|
||||
makeBadge({
|
||||
label: 'build',
|
||||
message: 'passed',
|
||||
style: 'something else',
|
||||
}),
|
||||
})
|
||||
).to.throw(
|
||||
ValidationError,
|
||||
'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)',
|
||||
'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)'
|
||||
)
|
||||
expect(() =>
|
||||
makeBadge({ label: 'build', message: 'passed', style: 'popout' }),
|
||||
makeBadge({ label: 'build', message: 'passed', style: 'popout' })
|
||||
).to.throw(
|
||||
ValidationError,
|
||||
'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)',
|
||||
'Field `style` must be one of (plastic,flat,flat-square,for-the-badge,social)'
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -58,6 +58,6 @@ module.exports = function makeBadge({
|
||||
logoPadding: logo && label.length ? 3 : 0,
|
||||
color: toSvgColor(color),
|
||||
labelColor: toSvgColor(labelColor),
|
||||
}),
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
const { test, given, forCases } = require('sazerac')
|
||||
const { expect } = require('chai')
|
||||
const snapshot = require('snap-shot-it')
|
||||
const isSvg = require('is-svg')
|
||||
const prettier = require('prettier')
|
||||
const makeBadge = require('./make-badge')
|
||||
|
||||
async function expectBadgeToMatchSnapshot(format) {
|
||||
snapshot(await prettier.format(makeBadge(format), { parser: 'html' }))
|
||||
function expectBadgeToMatchSnapshot(format) {
|
||||
snapshot(prettier.format(makeBadge(format), { parser: 'html' }))
|
||||
}
|
||||
|
||||
function testColor(color = '', colorAttr = 'color') {
|
||||
@@ -17,7 +18,7 @@ function testColor(color = '', colorAttr = 'color') {
|
||||
message: 'Bob',
|
||||
[colorAttr]: color,
|
||||
format: 'json',
|
||||
}),
|
||||
})
|
||||
).color
|
||||
}
|
||||
|
||||
@@ -67,7 +68,7 @@ describe('The badge generator', function () {
|
||||
given('bluish'),
|
||||
given('almostred'),
|
||||
given('brightmaroon'),
|
||||
given('cactus'),
|
||||
given('cactus')
|
||||
).expect(undefined)
|
||||
})
|
||||
})
|
||||
@@ -79,16 +80,15 @@ describe('The badge generator', function () {
|
||||
})
|
||||
|
||||
describe('SVG', function () {
|
||||
it('should produce SVG', async function () {
|
||||
const { default: isSvg } = await import('is-svg')
|
||||
it('should produce SVG', function () {
|
||||
expect(makeBadge({ label: 'cactus', message: 'grown', format: 'svg' }))
|
||||
.to.satisfy(isSvg)
|
||||
.and.to.include('cactus')
|
||||
.and.to.include('grown')
|
||||
})
|
||||
|
||||
it('should match snapshot', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshot', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -113,8 +113,7 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should replace undefined svg badge style with "flat"', async function () {
|
||||
const { default: isSvg } = await import('is-svg')
|
||||
it('should replace undefined svg badge style with "flat"', function () {
|
||||
const jsonBadgeWithUnknownStyle = makeBadge({
|
||||
label: 'name',
|
||||
message: 'Bob',
|
||||
@@ -138,14 +137,14 @@ describe('The badge generator', function () {
|
||||
message: 'Bob',
|
||||
format: 'svg',
|
||||
style: 'unknown_style',
|
||||
}),
|
||||
})
|
||||
).to.throw(Error, "Unknown badge style: 'unknown_style'")
|
||||
})
|
||||
})
|
||||
|
||||
describe('"flat" template badge generation', function () {
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -155,8 +154,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -167,8 +166,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -177,8 +176,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -188,8 +187,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -200,8 +199,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -212,8 +211,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -223,8 +222,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -236,8 +235,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
|
||||
describe('"flat-square" template badge generation', function () {
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -247,8 +246,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -259,8 +258,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -269,8 +268,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -280,8 +279,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -292,8 +291,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -304,8 +303,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -315,8 +314,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -328,8 +327,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
|
||||
describe('"plastic" template badge generation', function () {
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -339,8 +338,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -351,8 +350,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -361,8 +360,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -372,8 +371,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -384,8 +383,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -396,8 +395,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -407,8 +406,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -428,7 +427,7 @@ describe('The badge generator', function () {
|
||||
message: 1999,
|
||||
format: 'svg',
|
||||
style: 'for-the-badge',
|
||||
}),
|
||||
})
|
||||
)
|
||||
.to.include('1998')
|
||||
.and.to.include('1999')
|
||||
@@ -441,14 +440,14 @@ describe('The badge generator', function () {
|
||||
message: '1 string',
|
||||
format: 'svg',
|
||||
style: 'for-the-badge',
|
||||
}),
|
||||
})
|
||||
)
|
||||
.to.include('LABEL')
|
||||
.and.to.include('1 STRING')
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -458,8 +457,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -470,8 +469,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -480,8 +479,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -491,8 +490,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -503,8 +502,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -515,8 +514,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the label color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the label color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -526,8 +525,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: black text when the message color is light', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: black text when the message color is light', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -546,7 +545,7 @@ describe('The badge generator', function () {
|
||||
message: 'some-value',
|
||||
format: 'svg',
|
||||
style: 'social',
|
||||
}),
|
||||
})
|
||||
)
|
||||
.to.include('Some-key')
|
||||
.and.to.include('some-value')
|
||||
@@ -560,14 +559,14 @@ describe('The badge generator', function () {
|
||||
message: 'some-value',
|
||||
format: 'json',
|
||||
style: 'social',
|
||||
}),
|
||||
})
|
||||
)
|
||||
.to.include('""')
|
||||
.and.to.include('some-value')
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -577,8 +576,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -589,8 +588,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, no logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, no logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -599,8 +598,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -610,8 +609,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message only, with logo and labelColor', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message only, with logo and labelColor', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: '',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -622,8 +621,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
})
|
||||
|
||||
it('should match snapshots: message/label, with links', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('should match snapshots: message/label, with links', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'cactus',
|
||||
message: 'grown',
|
||||
format: 'svg',
|
||||
@@ -636,8 +635,8 @@ describe('The badge generator', function () {
|
||||
})
|
||||
|
||||
describe('badges with logos should always produce the same badge', function () {
|
||||
it('badge with logo', async function () {
|
||||
await expectBadgeToMatchSnapshot({
|
||||
it('badge with logo', function () {
|
||||
expectBadgeToMatchSnapshot({
|
||||
label: 'label',
|
||||
message: 'message',
|
||||
format: 'svg',
|
||||
|
||||
@@ -66,7 +66,7 @@ class XmlElement {
|
||||
})
|
||||
.join(' ')
|
||||
return stripXmlWhitespace(
|
||||
`<${this.name}${attrsStr}>${content}</${this.name}>`,
|
||||
`<${this.name}${attrsStr}>${content}</${this.name}>`
|
||||
)
|
||||
}
|
||||
return stripXmlWhitespace(`<${this.name}${attrsStr}/>`)
|
||||
@@ -88,7 +88,7 @@ class ElementList {
|
||||
typeof el.render === 'function'
|
||||
? acc + el.render()
|
||||
: acc + escapeXml(el),
|
||||
'',
|
||||
''
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
"badge": "lib/badge-cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
"node": ">= 12",
|
||||
"npm": ">= 6"
|
||||
},
|
||||
"collective": {
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -77,7 +77,6 @@ private:
|
||||
bitbucket_password: 'BITBUCKET_PASS'
|
||||
bitbucket_server_username: 'BITBUCKET_SERVER_USER'
|
||||
bitbucket_server_password: 'BITBUCKET_SERVER_PASS'
|
||||
curseforge_api_key: 'CURSEFORGE_API_KEY'
|
||||
discord_bot_token: 'DISCORD_BOT_TOKEN'
|
||||
drone_token: 'DRONE_TOKEN'
|
||||
gh_client_id: 'GH_CLIENT_ID'
|
||||
@@ -95,13 +94,10 @@ private:
|
||||
obs_user: 'OBS_USER'
|
||||
obs_pass: 'OBS_PASS'
|
||||
redis_url: 'REDIS_URL'
|
||||
opencollective_token: 'OPENCOLLECTIVE_TOKEN'
|
||||
postgres_url: 'POSTGRES_URL'
|
||||
sentry_dsn: 'SENTRY_DSN'
|
||||
sl_insight_userUuid: 'SL_INSIGHT_USER_UUID'
|
||||
sl_insight_apiToken: 'SL_INSIGHT_API_TOKEN'
|
||||
sonarqube_token: 'SONARQUBE_TOKEN'
|
||||
stackapps_api_key: 'STACKAPPS_API_KEY'
|
||||
teamcity_user: 'TEAMCITY_USER'
|
||||
teamcity_pass: 'TEAMCITY_PASS'
|
||||
twitch_client_id: 'TWITCH_CLIENT_ID'
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
public:
|
||||
bind:
|
||||
address: '::'
|
||||
|
||||
metrics:
|
||||
prometheus:
|
||||
enabled: false
|
||||
@@ -11,26 +12,33 @@ public:
|
||||
intervalSeconds: 15
|
||||
ssl:
|
||||
isSecure: false
|
||||
|
||||
cors:
|
||||
allowedOrigin: []
|
||||
|
||||
services:
|
||||
github:
|
||||
baseUri: 'https://api.github.com'
|
||||
baseUri: 'https://api.github.com/'
|
||||
debug:
|
||||
enabled: false
|
||||
intervalSeconds: 200
|
||||
restApiVersion: '2022-11-28'
|
||||
obs:
|
||||
authorizedOrigins: 'https://api.opensuse.org'
|
||||
weblate:
|
||||
authorizedOrigins: 'https://hosted.weblate.org'
|
||||
trace: false
|
||||
|
||||
cacheHeaders:
|
||||
defaultCacheLengthSeconds: 120
|
||||
|
||||
handleInternalErrors: true
|
||||
|
||||
fetchLimit: '10MB'
|
||||
userAgentBase: 'shields (self-hosted)'
|
||||
|
||||
requestTimeoutSeconds: 120
|
||||
requestTimeoutMaxAgeSeconds: 30
|
||||
|
||||
requireCloudflare: false
|
||||
|
||||
private: {}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
private:
|
||||
# These are the keys which are set on the production servers.
|
||||
curseforge_api_key: ...
|
||||
discord_bot_token: ...
|
||||
gh_client_id: ...
|
||||
gh_client_secret: ...
|
||||
gitlab_token: ...
|
||||
redis_url: ...
|
||||
sentry_dsn: ...
|
||||
shields_secret: ...
|
||||
sl_insight_userUuid: ...
|
||||
|
||||
@@ -4,7 +4,6 @@ private:
|
||||
# The possible values are documented in `doc/server-secrets.md`. Note that
|
||||
# you can also set these values through environment variables, which may be
|
||||
# preferable for self hosting.
|
||||
curseforge_api_key: '...'
|
||||
gh_token: '...'
|
||||
gitlab_token: '...'
|
||||
obs_user: '...'
|
||||
|
||||
112
core/badge-urls/make-badge-url.d.ts
vendored
Normal file
112
core/badge-urls/make-badge-url.d.ts
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
export function badgeUrlFromPath({
|
||||
baseUrl,
|
||||
path,
|
||||
queryParams,
|
||||
style,
|
||||
format,
|
||||
longCache,
|
||||
}: {
|
||||
baseUrl?: string
|
||||
path: string
|
||||
queryParams: { [k: string]: string | number | boolean }
|
||||
style?: string
|
||||
format?: string
|
||||
longCache?: boolean
|
||||
}): string
|
||||
|
||||
export function badgeUrlFromPattern({
|
||||
baseUrl,
|
||||
pattern,
|
||||
namedParams,
|
||||
queryParams,
|
||||
style,
|
||||
format,
|
||||
longCache,
|
||||
}: {
|
||||
baseUrl?: string
|
||||
pattern: string
|
||||
namedParams: { [k: string]: string }
|
||||
queryParams: { [k: string]: string | number | boolean }
|
||||
style?: string
|
||||
format?: string
|
||||
longCache?: boolean
|
||||
}): string
|
||||
|
||||
export function encodeField(s: string): string
|
||||
|
||||
export function staticBadgeUrl({
|
||||
baseUrl,
|
||||
label,
|
||||
message,
|
||||
labelColor,
|
||||
color,
|
||||
style,
|
||||
namedLogo,
|
||||
format,
|
||||
links,
|
||||
}: {
|
||||
baseUrl?: string
|
||||
label: string
|
||||
message: string
|
||||
labelColor?: string
|
||||
color?: string
|
||||
style?: string
|
||||
namedLogo?: string
|
||||
format?: string
|
||||
links?: string[]
|
||||
}): string
|
||||
|
||||
export function queryStringStaticBadgeUrl({
|
||||
baseUrl,
|
||||
label,
|
||||
message,
|
||||
color,
|
||||
labelColor,
|
||||
style,
|
||||
namedLogo,
|
||||
logoColor,
|
||||
logoWidth,
|
||||
logoPosition,
|
||||
format,
|
||||
}: {
|
||||
baseUrl?: string
|
||||
label: string
|
||||
message: string
|
||||
color?: string
|
||||
labelColor?: string
|
||||
style?: string
|
||||
namedLogo?: string
|
||||
logoColor?: string
|
||||
logoWidth?: number
|
||||
logoPosition?: number
|
||||
format?: string
|
||||
}): string
|
||||
|
||||
export function dynamicBadgeUrl({
|
||||
baseUrl,
|
||||
datatype,
|
||||
label,
|
||||
dataUrl,
|
||||
query,
|
||||
prefix,
|
||||
suffix,
|
||||
color,
|
||||
style,
|
||||
format,
|
||||
}: {
|
||||
baseUrl?: string
|
||||
datatype: string
|
||||
label: string
|
||||
dataUrl: string
|
||||
query: string
|
||||
prefix: string
|
||||
suffix: string
|
||||
color?: string
|
||||
style?: string
|
||||
format?: string
|
||||
}): string
|
||||
|
||||
export function rasterRedirectUrl(
|
||||
{ rasterUrl }: { rasterUrl: string },
|
||||
badgeUrl: string
|
||||
): string
|
||||
@@ -1,5 +1,147 @@
|
||||
// Avoid "Attempted import error: 'URL' is not exported from 'url'" in frontend.
|
||||
import url from 'url'
|
||||
import queryString from 'query-string'
|
||||
import { compile } from 'path-to-regexp'
|
||||
|
||||
function badgeUrlFromPath({
|
||||
baseUrl = '',
|
||||
path,
|
||||
queryParams,
|
||||
style,
|
||||
format = '',
|
||||
longCache = false,
|
||||
}) {
|
||||
const outExt = format.length ? `.${format}` : ''
|
||||
|
||||
const outQueryString = queryString.stringify({
|
||||
cacheSeconds: longCache ? '2592000' : undefined,
|
||||
style,
|
||||
...queryParams,
|
||||
})
|
||||
const suffix = outQueryString ? `?${outQueryString}` : ''
|
||||
|
||||
return `${baseUrl}${path}${outExt}${suffix}`
|
||||
}
|
||||
|
||||
function badgeUrlFromPattern({
|
||||
baseUrl = '',
|
||||
pattern,
|
||||
namedParams,
|
||||
queryParams,
|
||||
style,
|
||||
format = '',
|
||||
longCache = false,
|
||||
}) {
|
||||
const toPath = compile(pattern, {
|
||||
strict: true,
|
||||
sensitive: true,
|
||||
encode: encodeURIComponent,
|
||||
})
|
||||
|
||||
const path = toPath(namedParams)
|
||||
|
||||
return badgeUrlFromPath({
|
||||
baseUrl,
|
||||
path,
|
||||
queryParams,
|
||||
style,
|
||||
format,
|
||||
longCache,
|
||||
})
|
||||
}
|
||||
|
||||
function encodeField(s) {
|
||||
return encodeURIComponent(s.replace(/-/g, '--').replace(/_/g, '__'))
|
||||
}
|
||||
|
||||
function staticBadgeUrl({
|
||||
baseUrl = '',
|
||||
label,
|
||||
message,
|
||||
labelColor,
|
||||
color = 'lightgray',
|
||||
style,
|
||||
namedLogo,
|
||||
format = '',
|
||||
links = [],
|
||||
}) {
|
||||
const path = [label, message, color].map(encodeField).join('-')
|
||||
const outQueryString = queryString.stringify({
|
||||
labelColor,
|
||||
style,
|
||||
logo: namedLogo,
|
||||
link: links,
|
||||
})
|
||||
const outExt = format.length ? `.${format}` : ''
|
||||
const suffix = outQueryString ? `?${outQueryString}` : ''
|
||||
return `${baseUrl}/badge/${path}${outExt}${suffix}`
|
||||
}
|
||||
|
||||
function queryStringStaticBadgeUrl({
|
||||
baseUrl = '',
|
||||
label,
|
||||
message,
|
||||
color,
|
||||
labelColor,
|
||||
style,
|
||||
namedLogo,
|
||||
logoColor,
|
||||
logoWidth,
|
||||
logoPosition,
|
||||
format = '',
|
||||
}) {
|
||||
// schemaVersion could be a parameter if we iterate on it,
|
||||
// for now it's hardcoded to the only supported version.
|
||||
const schemaVersion = '1'
|
||||
const suffix = `?${queryString.stringify({
|
||||
label,
|
||||
message,
|
||||
color,
|
||||
labelColor,
|
||||
style,
|
||||
logo: namedLogo,
|
||||
logoColor,
|
||||
logoWidth,
|
||||
logoPosition,
|
||||
})}`
|
||||
const outExt = format.length ? `.${format}` : ''
|
||||
return `${baseUrl}/static/v${schemaVersion}${outExt}${suffix}`
|
||||
}
|
||||
|
||||
function dynamicBadgeUrl({
|
||||
baseUrl,
|
||||
datatype,
|
||||
label,
|
||||
dataUrl,
|
||||
query,
|
||||
prefix,
|
||||
suffix,
|
||||
color,
|
||||
style,
|
||||
format = '',
|
||||
}) {
|
||||
const outExt = format.length ? `.${format}` : ''
|
||||
|
||||
const queryParams = {
|
||||
label,
|
||||
url: dataUrl,
|
||||
query,
|
||||
style,
|
||||
}
|
||||
|
||||
if (color) {
|
||||
queryParams.color = color
|
||||
}
|
||||
if (prefix) {
|
||||
queryParams.prefix = prefix
|
||||
}
|
||||
if (suffix) {
|
||||
queryParams.suffix = suffix
|
||||
}
|
||||
|
||||
const outQueryString = queryString.stringify(queryParams)
|
||||
return `${baseUrl}/badge/dynamic/${datatype}${outExt}?${outQueryString}`
|
||||
}
|
||||
|
||||
function rasterRedirectUrl({ rasterUrl }, badgeUrl) {
|
||||
// Ensure we're always using the `rasterUrl` by using just the path from
|
||||
@@ -10,4 +152,12 @@ function rasterRedirectUrl({ rasterUrl }, badgeUrl) {
|
||||
return result
|
||||
}
|
||||
|
||||
export { rasterRedirectUrl }
|
||||
export {
|
||||
badgeUrlFromPath,
|
||||
badgeUrlFromPattern,
|
||||
encodeField,
|
||||
staticBadgeUrl,
|
||||
queryStringStaticBadgeUrl,
|
||||
dynamicBadgeUrl,
|
||||
rasterRedirectUrl,
|
||||
}
|
||||
|
||||
155
core/badge-urls/make-badge-url.spec.js
Normal file
155
core/badge-urls/make-badge-url.spec.js
Normal file
@@ -0,0 +1,155 @@
|
||||
import { test, given } from 'sazerac'
|
||||
import {
|
||||
badgeUrlFromPath,
|
||||
badgeUrlFromPattern,
|
||||
encodeField,
|
||||
staticBadgeUrl,
|
||||
queryStringStaticBadgeUrl,
|
||||
dynamicBadgeUrl,
|
||||
} from './make-badge-url.js'
|
||||
|
||||
describe('Badge URL generation functions', function () {
|
||||
test(badgeUrlFromPath, () => {
|
||||
given({
|
||||
baseUrl: 'http://example.com',
|
||||
path: '/npm/v/gh-badges',
|
||||
style: 'flat-square',
|
||||
longCache: true,
|
||||
}).expect(
|
||||
'http://example.com/npm/v/gh-badges?cacheSeconds=2592000&style=flat-square'
|
||||
)
|
||||
})
|
||||
|
||||
test(badgeUrlFromPattern, () => {
|
||||
given({
|
||||
baseUrl: 'http://example.com',
|
||||
pattern: '/npm/v/:packageName',
|
||||
namedParams: { packageName: 'gh-badges' },
|
||||
style: 'flat-square',
|
||||
longCache: true,
|
||||
}).expect(
|
||||
'http://example.com/npm/v/gh-badges?cacheSeconds=2592000&style=flat-square'
|
||||
)
|
||||
})
|
||||
|
||||
test(encodeField, () => {
|
||||
given('foo').expect('foo')
|
||||
given('').expect('')
|
||||
given('happy go lucky').expect('happy%20go%20lucky')
|
||||
given('do-right').expect('do--right')
|
||||
given('it_is_a_snake').expect('it__is__a__snake')
|
||||
})
|
||||
|
||||
test(staticBadgeUrl, () => {
|
||||
given({
|
||||
label: 'foo',
|
||||
message: 'bar',
|
||||
color: 'blue',
|
||||
style: 'flat-square',
|
||||
}).expect('/badge/foo-bar-blue?style=flat-square')
|
||||
given({
|
||||
label: 'foo',
|
||||
message: 'bar',
|
||||
color: 'blue',
|
||||
style: 'flat-square',
|
||||
format: 'png',
|
||||
namedLogo: 'github',
|
||||
}).expect('/badge/foo-bar-blue.png?logo=github&style=flat-square')
|
||||
given({
|
||||
label: 'Hello World',
|
||||
message: 'Привет Мир',
|
||||
color: '#aabbcc',
|
||||
}).expect(
|
||||
'/badge/Hello%20World-%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%9C%D0%B8%D1%80-%23aabbcc'
|
||||
)
|
||||
given({
|
||||
label: '123-123',
|
||||
message: 'abc-abc',
|
||||
color: 'blue',
|
||||
}).expect('/badge/123--123-abc--abc-blue')
|
||||
given({
|
||||
label: '123-123',
|
||||
message: '',
|
||||
color: 'blue',
|
||||
style: 'social',
|
||||
}).expect('/badge/123--123--blue?style=social')
|
||||
given({
|
||||
label: '',
|
||||
message: 'blue',
|
||||
color: 'blue',
|
||||
}).expect('/badge/-blue-blue')
|
||||
})
|
||||
|
||||
test(queryStringStaticBadgeUrl, () => {
|
||||
// the query-string library sorts parameters by name
|
||||
given({
|
||||
label: 'foo',
|
||||
message: 'bar',
|
||||
color: 'blue',
|
||||
style: 'flat-square',
|
||||
}).expect('/static/v1?color=blue&label=foo&message=bar&style=flat-square')
|
||||
given({
|
||||
label: 'foo Bar',
|
||||
message: 'bar Baz',
|
||||
color: 'blue',
|
||||
style: 'flat-square',
|
||||
format: 'png',
|
||||
namedLogo: 'github',
|
||||
}).expect(
|
||||
'/static/v1.png?color=blue&label=foo%20Bar&logo=github&message=bar%20Baz&style=flat-square'
|
||||
)
|
||||
given({
|
||||
label: 'Hello World',
|
||||
message: 'Привет Мир',
|
||||
color: '#aabbcc',
|
||||
}).expect(
|
||||
'/static/v1?color=%23aabbcc&label=Hello%20World&message=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%20%D0%9C%D0%B8%D1%80'
|
||||
)
|
||||
})
|
||||
|
||||
test(dynamicBadgeUrl, () => {
|
||||
const dataUrl = 'http://example.com/foo.json'
|
||||
const query = '$.bar'
|
||||
const prefix = 'value: '
|
||||
given({
|
||||
baseUrl: 'http://img.example.com',
|
||||
datatype: 'json',
|
||||
label: 'foo',
|
||||
dataUrl,
|
||||
query,
|
||||
prefix,
|
||||
style: 'plastic',
|
||||
}).expect(
|
||||
[
|
||||
'http://img.example.com/badge/dynamic/json',
|
||||
'?label=foo',
|
||||
`&prefix=${encodeURIComponent(prefix)}`,
|
||||
`&query=${encodeURIComponent(query)}`,
|
||||
'&style=plastic',
|
||||
`&url=${encodeURIComponent(dataUrl)}`,
|
||||
].join('')
|
||||
)
|
||||
const suffix = '<- value'
|
||||
const color = 'blue'
|
||||
given({
|
||||
baseUrl: 'http://img.example.com',
|
||||
datatype: 'json',
|
||||
label: 'foo',
|
||||
dataUrl,
|
||||
query,
|
||||
suffix,
|
||||
color,
|
||||
style: 'plastic',
|
||||
}).expect(
|
||||
[
|
||||
'http://img.example.com/badge/dynamic/json',
|
||||
'?color=blue',
|
||||
'&label=foo',
|
||||
`&query=${encodeURIComponent(query)}`,
|
||||
'&style=plastic',
|
||||
`&suffix=${encodeURIComponent(suffix)}`,
|
||||
`&url=${encodeURIComponent(dataUrl)}`,
|
||||
].join('')
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -7,7 +7,7 @@ describe('Badge URL helper functions', function () {
|
||||
given('single trailing underscore_').expect('single trailing underscore ')
|
||||
given('__double leading underscores').expect('_double leading underscores')
|
||||
given('double trailing underscores__').expect(
|
||||
'double trailing underscores_',
|
||||
'double trailing underscores_'
|
||||
)
|
||||
given('treble___underscores').expect('treble_ underscores')
|
||||
given('fourfold____underscores').expect('fourfold__underscores')
|
||||
|
||||
@@ -11,7 +11,7 @@ class AuthHelper {
|
||||
isRequired = false,
|
||||
defaultToEmptyStringForUser = false,
|
||||
},
|
||||
config,
|
||||
config
|
||||
) {
|
||||
if (!userKey && !passKey) {
|
||||
throw Error('Expected userKey or passKey to be set')
|
||||
@@ -142,7 +142,7 @@ class AuthHelper {
|
||||
|
||||
withBasicAuth(requestParams) {
|
||||
return this._withAnyAuth(requestParams, requestParams =>
|
||||
this.constructor._mergeAuth(requestParams, this._basicAuth),
|
||||
this.constructor._mergeAuth(requestParams, this._basicAuth)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -153,11 +153,6 @@ class AuthHelper {
|
||||
: undefined
|
||||
}
|
||||
|
||||
_apiKeyHeader(apiKeyHeader) {
|
||||
const { _pass: pass } = this
|
||||
return this.isConfigured ? { [apiKeyHeader]: pass } : undefined
|
||||
}
|
||||
|
||||
static _mergeHeaders(requestParams, headers) {
|
||||
const {
|
||||
options: { headers: existingHeaders, ...restOptions } = {},
|
||||
@@ -175,21 +170,15 @@ class AuthHelper {
|
||||
}
|
||||
}
|
||||
|
||||
withApiKeyHeader(requestParams, header = 'x-api-key') {
|
||||
return this._withAnyAuth(requestParams, requestParams =>
|
||||
this.constructor._mergeHeaders(requestParams, this._apiKeyHeader(header)),
|
||||
)
|
||||
}
|
||||
|
||||
withBearerAuthHeader(
|
||||
requestParams,
|
||||
bearerKey = 'Bearer', // lgtm [js/hardcoded-credentials]
|
||||
bearerKey = 'Bearer' // lgtm [js/hardcoded-credentials]
|
||||
) {
|
||||
return this._withAnyAuth(requestParams, requestParams =>
|
||||
this.constructor._mergeHeaders(
|
||||
requestParams,
|
||||
this._bearerAuthHeader(bearerKey),
|
||||
),
|
||||
this._bearerAuthHeader(bearerKey)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -215,7 +204,7 @@ class AuthHelper {
|
||||
this.constructor._mergeQueryParams(requestParams, {
|
||||
...(userKey ? { [userKey]: this._user } : undefined),
|
||||
...(passKey ? { [passKey]: this._pass } : undefined),
|
||||
}),
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,13 +8,12 @@ describe('AuthHelper', function () {
|
||||
it('throws without userKey or passKey', function () {
|
||||
expect(() => new AuthHelper({}, {})).to.throw(
|
||||
Error,
|
||||
'Expected userKey or passKey to be set',
|
||||
'Expected userKey or passKey to be set'
|
||||
)
|
||||
})
|
||||
it('throws without serviceKey or authorizedOrigins', function () {
|
||||
expect(
|
||||
() =>
|
||||
new AuthHelper({ userKey: 'myci_user', passKey: 'myci_pass' }, {}),
|
||||
() => new AuthHelper({ userKey: 'myci_user', passKey: 'myci_pass' }, {})
|
||||
).to.throw(Error, 'Expected authorizedOrigins or serviceKey to be set')
|
||||
})
|
||||
it('throws when authorizedOrigins is not an array', function () {
|
||||
@@ -26,8 +25,8 @@ describe('AuthHelper', function () {
|
||||
passKey: 'myci_pass',
|
||||
authorizedOrigins: true,
|
||||
},
|
||||
{ private: {} },
|
||||
),
|
||||
{ private: {} }
|
||||
)
|
||||
).to.throw(Error, 'Expected authorizedOrigins to be an array of origins')
|
||||
})
|
||||
})
|
||||
@@ -36,7 +35,7 @@ describe('AuthHelper', function () {
|
||||
function validate(config, privateConfig) {
|
||||
return new AuthHelper(
|
||||
{ authorizedOrigins: ['https://example.test'], ...config },
|
||||
{ private: privateConfig },
|
||||
{ private: privateConfig }
|
||||
).isValid
|
||||
}
|
||||
test(validate, () => {
|
||||
@@ -44,20 +43,20 @@ describe('AuthHelper', function () {
|
||||
// Fully configured user + pass.
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass', isRequired: true },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' }
|
||||
),
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' }
|
||||
),
|
||||
// Fully configured user or pass.
|
||||
given(
|
||||
{ userKey: 'myci_user', isRequired: true },
|
||||
{ myci_user: 'admin' },
|
||||
{ myci_user: 'admin' }
|
||||
),
|
||||
given(
|
||||
{ passKey: 'myci_pass', isRequired: true },
|
||||
{ myci_pass: 'abc123' },
|
||||
{ myci_pass: 'abc123' }
|
||||
),
|
||||
given({ userKey: 'myci_user' }, { myci_user: 'admin' }),
|
||||
given({ passKey: 'myci_pass' }, { myci_pass: 'abc123' }),
|
||||
@@ -71,16 +70,16 @@ describe('AuthHelper', function () {
|
||||
// Partly configured.
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass', isRequired: true },
|
||||
{ myci_user: 'admin' },
|
||||
{ myci_user: 'admin' }
|
||||
),
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass' },
|
||||
{ myci_user: 'admin' },
|
||||
{ myci_user: 'admin' }
|
||||
),
|
||||
// Missing required config.
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass', isRequired: true },
|
||||
{},
|
||||
{}
|
||||
),
|
||||
given({ userKey: 'myci_user', isRequired: true }, {}),
|
||||
given({ passKey: 'myci_pass', isRequired: true }, {}),
|
||||
@@ -92,18 +91,18 @@ describe('AuthHelper', function () {
|
||||
function validate(config, privateConfig) {
|
||||
return new AuthHelper(
|
||||
{ authorizedOrigins: ['https://example.test'], ...config },
|
||||
{ private: privateConfig },
|
||||
{ private: privateConfig }
|
||||
)._basicAuth
|
||||
}
|
||||
test(validate, () => {
|
||||
forCases([
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass', isRequired: true },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' }
|
||||
),
|
||||
given(
|
||||
{ userKey: 'myci_user', passKey: 'myci_pass' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' },
|
||||
{ myci_user: 'admin', myci_pass: 'abc123' }
|
||||
),
|
||||
]).expect({ username: 'admin', password: 'abc123' })
|
||||
given({ userKey: 'myci_user' }, { myci_user: 'admin' }).expect({
|
||||
@@ -115,11 +114,11 @@ describe('AuthHelper', function () {
|
||||
password: 'abc123',
|
||||
})
|
||||
given({ userKey: 'myci_user', passKey: 'myci_pass' }, {}).expect(
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
given(
|
||||
{ passKey: 'myci_pass', defaultToEmptyStringForUser: true },
|
||||
{ myci_pass: 'abc123' },
|
||||
{ myci_pass: 'abc123' }
|
||||
).expect({
|
||||
username: '',
|
||||
password: 'abc123',
|
||||
@@ -169,7 +168,7 @@ describe('AuthHelper', function () {
|
||||
expect(() =>
|
||||
authHelper.enforceStrictSsl({
|
||||
options: { https: { rejectUnauthorized: false } },
|
||||
}),
|
||||
})
|
||||
).to.throw(InvalidParameter)
|
||||
})
|
||||
})
|
||||
@@ -193,7 +192,7 @@ describe('AuthHelper', function () {
|
||||
expect(() =>
|
||||
authHelper.enforceStrictSsl({
|
||||
options: { https: { rejectUnauthorized: false } },
|
||||
}),
|
||||
})
|
||||
).not.to.throw()
|
||||
})
|
||||
})
|
||||
@@ -319,7 +318,7 @@ describe('AuthHelper', function () {
|
||||
},
|
||||
},
|
||||
private: { myci_user: 'admin', myci_pass: 'abc123' },
|
||||
},
|
||||
}
|
||||
)
|
||||
const withBasicAuth = requestOptions =>
|
||||
authHelper.withBasicAuth(requestOptions)
|
||||
@@ -377,7 +376,7 @@ describe('AuthHelper', function () {
|
||||
withBasicAuth({
|
||||
url: 'https://myci.test/api',
|
||||
options: { https: { rejectUnauthorized: false } },
|
||||
}),
|
||||
})
|
||||
).to.throw(InvalidParameter)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -44,12 +44,6 @@ class BaseGraphqlService extends BaseService {
|
||||
* and custom error messages e.g: `{ 404: 'package not found' }`.
|
||||
* This can be used to extend or override the
|
||||
* [default](https://github.com/badges/shields/blob/master/core/base-service/check-error-response.js#L5)
|
||||
* @param {object} [attrs.systemErrors={}] Key-value map of got network exception codes
|
||||
* and an object of params to pass when we construct an Inaccessible exception object
|
||||
* e.g: `{ ECONNRESET: { prettyMessage: 'connection reset' } }`.
|
||||
* See {@link https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md#errorcodes got error codes}
|
||||
* for allowed keys
|
||||
* and {@link module:core/base-service/errors~RuntimeErrorProps} for allowed values
|
||||
* @param {Function} [attrs.transformJson=data => data] Function which takes the raw json and transforms it before
|
||||
* further procesing. In case of multiple query in a single graphql call and few of them
|
||||
* throw error, partial data might be used ignoring the error.
|
||||
@@ -68,7 +62,6 @@ class BaseGraphqlService extends BaseService {
|
||||
variables = {},
|
||||
options = {},
|
||||
httpErrorMessages = {},
|
||||
systemErrors = {},
|
||||
transformJson = data => data,
|
||||
transformErrors = defaultTransformErrors,
|
||||
}) {
|
||||
@@ -81,8 +74,7 @@ class BaseGraphqlService extends BaseService {
|
||||
const { buffer } = await this._request({
|
||||
url,
|
||||
options: mergedOptions,
|
||||
httpErrors: httpErrorMessages,
|
||||
systemErrors,
|
||||
errorMessages: httpErrorMessages,
|
||||
})
|
||||
const json = transformJson(this._parseJson(buffer))
|
||||
if (json.errors) {
|
||||
@@ -91,7 +83,7 @@ class BaseGraphqlService extends BaseService {
|
||||
throw exception
|
||||
} else {
|
||||
throw Error(
|
||||
`transformErrors() must return a ShieldsRuntimeError; got ${exception}`,
|
||||
`transformErrors() must return a ShieldsRuntimeError; got ${exception}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,23 +35,23 @@ describe('BaseGraphqlService', function () {
|
||||
Promise.resolve({
|
||||
buffer: '{"some": "json"}',
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher', async function () {
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/graphql',
|
||||
{
|
||||
body: '{"query":"{\\n requiredString\\n}","variables":{}}',
|
||||
body: '{"query":"{\\n requiredString\\n}\\n","variables":{}}',
|
||||
headers: { Accept: 'application/json' },
|
||||
method: 'POST',
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -74,17 +74,17 @@ describe('BaseGraphqlService', function () {
|
||||
|
||||
await WithOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/graphql',
|
||||
{
|
||||
body: '{"query":"{\\n requiredString\\n}","variables":{}}',
|
||||
body: '{"query":"{\\n requiredString\\n}\\n","variables":{}}',
|
||||
headers: { Accept: 'application/json' },
|
||||
method: 'POST',
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -98,8 +98,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string',
|
||||
})
|
||||
@@ -113,8 +113,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -130,8 +130,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -149,8 +149,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await DummyGraphqlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -188,8 +188,8 @@ describe('BaseGraphqlService', function () {
|
||||
expect(
|
||||
await WithErrorHandler.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -30,26 +30,14 @@ class BaseJsonService extends BaseService {
|
||||
* @param {string} attrs.url URL to request
|
||||
* @param {object} [attrs.options={}] Options to pass to got. See
|
||||
* [documentation](https://github.com/sindresorhus/got/blob/main/documentation/2-options.md)
|
||||
* @param {object} [attrs.httpErrors={}] Key-value map of status codes
|
||||
* @param {object} [attrs.errorMessages={}] Key-value map of status codes
|
||||
* and custom error messages e.g: `{ 404: 'package not found' }`.
|
||||
* This can be used to extend or override the
|
||||
* [default](https://github.com/badges/shields/blob/master/core/base-service/check-error-response.js#L5)
|
||||
* @param {object} [attrs.systemErrors={}] Key-value map of got network exception codes
|
||||
* and an object of params to pass when we construct an Inaccessible exception object
|
||||
* e.g: `{ ECONNRESET: { prettyMessage: 'connection reset' } }`.
|
||||
* See {@link https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md#errorcodes got error codes}
|
||||
* for allowed keys
|
||||
* and {@link module:core/base-service/errors~RuntimeErrorProps} for allowed values
|
||||
* @returns {object} Parsed response
|
||||
* @see https://github.com/sindresorhus/got/blob/main/documentation/2-options.md
|
||||
*/
|
||||
async _requestJson({
|
||||
schema,
|
||||
url,
|
||||
options = {},
|
||||
httpErrors = {},
|
||||
systemErrors = {},
|
||||
}) {
|
||||
async _requestJson({ schema, url, options = {}, errorMessages = {} }) {
|
||||
const mergedOptions = {
|
||||
...{ headers: { Accept: 'application/json' } },
|
||||
...options,
|
||||
@@ -57,8 +45,7 @@ class BaseJsonService extends BaseService {
|
||||
const { buffer } = await this._request({
|
||||
url,
|
||||
options: mergedOptions,
|
||||
httpErrors,
|
||||
systemErrors,
|
||||
errorMessages,
|
||||
})
|
||||
const json = this._parseJson(buffer)
|
||||
return this.constructor._validate(json, schema)
|
||||
|
||||
@@ -28,21 +28,21 @@ describe('BaseJsonService', function () {
|
||||
Promise.resolve({
|
||||
buffer: '{"some": "json"}',
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher', async function () {
|
||||
await DummyJsonService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/foo.json',
|
||||
{
|
||||
headers: { Accept: 'application/json' },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -60,7 +60,7 @@ describe('BaseJsonService', function () {
|
||||
|
||||
await WithOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -69,7 +69,7 @@ describe('BaseJsonService', function () {
|
||||
headers: { Accept: 'application/json' },
|
||||
method: 'POST',
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -83,8 +83,8 @@ describe('BaseJsonService', function () {
|
||||
expect(
|
||||
await DummyJsonService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string',
|
||||
})
|
||||
@@ -98,8 +98,8 @@ describe('BaseJsonService', function () {
|
||||
expect(
|
||||
await DummyJsonService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -115,8 +115,8 @@ describe('BaseJsonService', function () {
|
||||
expect(
|
||||
await DummyJsonService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -33,14 +33,14 @@ export default class BaseStaticService extends BaseService {
|
||||
{},
|
||||
serviceConfig,
|
||||
namedParams,
|
||||
queryParams,
|
||||
queryParams
|
||||
)
|
||||
|
||||
const badgeData = coalesceBadge(
|
||||
queryParams,
|
||||
serviceData,
|
||||
this.defaultBadgeData,
|
||||
this,
|
||||
this
|
||||
)
|
||||
|
||||
// The final capture group is the extension.
|
||||
|
||||
@@ -53,16 +53,10 @@ class BaseSvgScrapingService extends BaseService {
|
||||
* @param {string} attrs.url URL to request
|
||||
* @param {object} [attrs.options={}] Options to pass to got. See
|
||||
* [documentation](https://github.com/sindresorhus/got/blob/main/documentation/2-options.md)
|
||||
* @param {object} [attrs.httpErrors={}] Key-value map of status codes
|
||||
* @param {object} [attrs.errorMessages={}] Key-value map of status codes
|
||||
* and custom error messages e.g: `{ 404: 'package not found' }`.
|
||||
* This can be used to extend or override the
|
||||
* [default](https://github.com/badges/shields/blob/master/core/base-service/check-error-response.js#L5)
|
||||
* @param {object} [attrs.systemErrors={}] Key-value map of got network exception codes
|
||||
* and an object of params to pass when we construct an Inaccessible exception object
|
||||
* e.g: `{ ECONNRESET: { prettyMessage: 'connection reset' } }`.
|
||||
* See {@link https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md#errorcodes got error codes}
|
||||
* for allowed keys
|
||||
* and {@link module:core/base-service/errors~RuntimeErrorProps} for allowed values
|
||||
* @returns {object} Parsed response
|
||||
* @see https://github.com/sindresorhus/got/blob/main/documentation/2-options.md
|
||||
*/
|
||||
@@ -71,8 +65,7 @@ class BaseSvgScrapingService extends BaseService {
|
||||
valueMatcher,
|
||||
url,
|
||||
options = {},
|
||||
httpErrors = {},
|
||||
systemErrors = {},
|
||||
errorMessages = {},
|
||||
}) {
|
||||
const logTrace = (...args) => trace.logTrace('fetch', ...args)
|
||||
const mergedOptions = {
|
||||
@@ -82,8 +75,7 @@ class BaseSvgScrapingService extends BaseService {
|
||||
const { buffer } = await this._request({
|
||||
url,
|
||||
options: mergedOptions,
|
||||
httpErrors,
|
||||
systemErrors,
|
||||
errorMessages,
|
||||
})
|
||||
logTrace(emojic.dart, 'Response SVG', buffer)
|
||||
const data = {
|
||||
|
||||
@@ -28,7 +28,7 @@ describe('BaseSvgScrapingService', function () {
|
||||
describe('valueFromSvgBadge', function () {
|
||||
it('should find the correct value', function () {
|
||||
expect(BaseSvgScrapingService.valueFromSvgBadge(exampleSvg)).to.equal(
|
||||
exampleMessage,
|
||||
exampleMessage
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -40,21 +40,21 @@ describe('BaseSvgScrapingService', function () {
|
||||
Promise.resolve({
|
||||
buffer: exampleSvg,
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher with the expected header', async function () {
|
||||
await DummySvgScrapingService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/foo.svg',
|
||||
{
|
||||
headers: { Accept: 'image/svg+xml' },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -75,7 +75,7 @@ describe('BaseSvgScrapingService', function () {
|
||||
|
||||
await WithCustomOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -84,7 +84,7 @@ describe('BaseSvgScrapingService', function () {
|
||||
method: 'POST',
|
||||
headers: { Accept: 'image/svg+xml' },
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -98,8 +98,8 @@ describe('BaseSvgScrapingService', function () {
|
||||
expect(
|
||||
await DummySvgScrapingService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: exampleMessage,
|
||||
})
|
||||
@@ -124,8 +124,8 @@ describe('BaseSvgScrapingService', function () {
|
||||
expect(
|
||||
await WithValueMatcher.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'a different message',
|
||||
})
|
||||
@@ -139,8 +139,8 @@ describe('BaseSvgScrapingService', function () {
|
||||
expect(
|
||||
await DummySvgScrapingService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/**
|
||||
* @module
|
||||
*/
|
||||
|
||||
import emojic from 'emojic'
|
||||
import { parse } from 'smol-toml'
|
||||
import BaseService from './base.js'
|
||||
import { InvalidResponse } from './errors.js'
|
||||
import trace from './trace.js'
|
||||
|
||||
/**
|
||||
* Services which query a TOML endpoint should extend BaseTomlService
|
||||
*
|
||||
* @abstract
|
||||
*/
|
||||
class BaseTomlService extends BaseService {
|
||||
/**
|
||||
* Request data from an upstream API serving TOML,
|
||||
* parse it and validate against a schema
|
||||
*
|
||||
* @param {object} attrs Refer to individual attrs
|
||||
* @param {Joi} attrs.schema Joi schema to validate the response against
|
||||
* @param {string} attrs.url URL to request
|
||||
* @param {object} [attrs.options={}] Options to pass to got. See
|
||||
* [documentation](https://github.com/sindresorhus/got/blob/main/documentation/2-options.md)
|
||||
* @param {object} [attrs.httpErrors={}] Key-value map of status codes
|
||||
* and custom error messages e.g: `{ 404: 'package not found' }`.
|
||||
* This can be used to extend or override the
|
||||
* [default](https://github.com/badges/shields/blob/master/core/base-service/check-error-response.js#L5)
|
||||
* @param {object} [attrs.systemErrors={}] Key-value map of got network exception codes
|
||||
* and an object of params to pass when we construct an Inaccessible exception object
|
||||
* e.g: `{ ECONNRESET: { prettyMessage: 'connection reset' } }`.
|
||||
* See {@link https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md#errorcodes got error codes}
|
||||
* for allowed keys
|
||||
* and {@link module:core/base-service/errors~RuntimeErrorProps} for allowed values
|
||||
* @returns {object} Parsed response
|
||||
* @see https://github.com/sindresorhus/got/blob/main/documentation/2-options.md
|
||||
*/
|
||||
async _requestToml({
|
||||
schema,
|
||||
url,
|
||||
options = {},
|
||||
httpErrors = {},
|
||||
systemErrors = {},
|
||||
}) {
|
||||
const logTrace = (...args) => trace.logTrace('fetch', ...args)
|
||||
const mergedOptions = {
|
||||
...{
|
||||
headers: {
|
||||
Accept:
|
||||
// the official header should be application/toml - see https://toml.io/en/v1.0.0#mime-type
|
||||
// but as this is not registered here https://www.iana.org/assignments/media-types/media-types.xhtml
|
||||
// some apps use other mime-type like application/x-toml, text/plain etc....
|
||||
'text/x-toml, text/toml, application/x-toml, application/toml, text/plain',
|
||||
},
|
||||
},
|
||||
...options,
|
||||
}
|
||||
const { buffer } = await this._request({
|
||||
url,
|
||||
options: mergedOptions,
|
||||
httpErrors,
|
||||
systemErrors,
|
||||
})
|
||||
let parsed
|
||||
try {
|
||||
parsed = parse(buffer.toString())
|
||||
} catch (err) {
|
||||
logTrace(emojic.dart, 'Response TOML (unparseable)', buffer)
|
||||
throw new InvalidResponse({
|
||||
prettyMessage: 'unparseable toml response',
|
||||
underlyingError: err,
|
||||
})
|
||||
}
|
||||
logTrace(emojic.dart, 'Response TOML (before validation)', parsed, {
|
||||
deep: true,
|
||||
})
|
||||
return this.constructor._validate(parsed, schema)
|
||||
}
|
||||
}
|
||||
|
||||
export default BaseTomlService
|
||||
@@ -1,150 +0,0 @@
|
||||
import Joi from 'joi'
|
||||
import { expect } from 'chai'
|
||||
import sinon from 'sinon'
|
||||
import BaseTomlService from './base-toml.js'
|
||||
|
||||
const dummySchema = Joi.object({
|
||||
requiredString: Joi.string().required(),
|
||||
}).required()
|
||||
|
||||
class DummyTomlService extends BaseTomlService {
|
||||
static category = 'cat'
|
||||
static route = { base: 'foo' }
|
||||
|
||||
async handle() {
|
||||
const { requiredString } = await this._requestToml({
|
||||
schema: dummySchema,
|
||||
url: 'http://example.com/foo.toml',
|
||||
})
|
||||
return { message: requiredString }
|
||||
}
|
||||
}
|
||||
|
||||
const expectedToml = `
|
||||
# example toml
|
||||
requiredString = "some-string"
|
||||
`
|
||||
|
||||
const invalidSchemaToml = `
|
||||
# example toml - legal toml syntax but invalid schema
|
||||
unexpectedKey = "some-string"
|
||||
`
|
||||
|
||||
const invalidTomlSyntax = `
|
||||
# example illegal toml syntax that can't be parsed
|
||||
missing= "space"
|
||||
colonsCantBeUsed: 42
|
||||
missing "assignment"
|
||||
`
|
||||
|
||||
describe('BaseTomlService', function () {
|
||||
describe('Making requests', function () {
|
||||
let requestFetcher
|
||||
beforeEach(function () {
|
||||
requestFetcher = sinon.stub().returns(
|
||||
Promise.resolve({
|
||||
buffer: expectedToml,
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher', async function () {
|
||||
await DummyTomlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/foo.toml',
|
||||
{
|
||||
headers: {
|
||||
Accept:
|
||||
'text/x-toml, text/toml, application/x-toml, application/toml, text/plain',
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
|
||||
it('forwards options to _requestFetcher', async function () {
|
||||
class WithOptions extends DummyTomlService {
|
||||
async handle() {
|
||||
const { requiredString } = await this._requestToml({
|
||||
schema: dummySchema,
|
||||
url: 'http://example.com/foo.toml',
|
||||
options: { method: 'POST', searchParams: { queryParam: 123 } },
|
||||
})
|
||||
return { message: requiredString }
|
||||
}
|
||||
}
|
||||
|
||||
await WithOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/foo.toml',
|
||||
{
|
||||
headers: {
|
||||
Accept:
|
||||
'text/x-toml, text/toml, application/x-toml, application/toml, text/plain',
|
||||
},
|
||||
method: 'POST',
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Making badges', function () {
|
||||
it('handles valid toml responses', async function () {
|
||||
const requestFetcher = async () => ({
|
||||
buffer: expectedToml,
|
||||
res: { statusCode: 200 },
|
||||
})
|
||||
expect(
|
||||
await DummyTomlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
).to.deep.equal({
|
||||
message: 'some-string',
|
||||
})
|
||||
})
|
||||
|
||||
it('handles toml responses which do not match the schema', async function () {
|
||||
const requestFetcher = async () => ({
|
||||
buffer: invalidSchemaToml,
|
||||
res: { statusCode: 200 },
|
||||
})
|
||||
expect(
|
||||
await DummyTomlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
message: 'invalid response data',
|
||||
})
|
||||
})
|
||||
|
||||
it('handles unparseable toml responses', async function () {
|
||||
const requestFetcher = async () => ({
|
||||
buffer: invalidTomlSyntax,
|
||||
res: { statusCode: 200 },
|
||||
})
|
||||
expect(
|
||||
await DummyTomlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
message: 'unparseable toml response',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -24,16 +24,10 @@ class BaseXmlService extends BaseService {
|
||||
* @param {string} attrs.url URL to request
|
||||
* @param {object} [attrs.options={}] Options to pass to got. See
|
||||
* [documentation](https://github.com/sindresorhus/got/blob/main/documentation/2-options.md)
|
||||
* @param {object} [attrs.httpErrors={}] Key-value map of status codes
|
||||
* @param {object} [attrs.errorMessages={}] Key-value map of status codes
|
||||
* and custom error messages e.g: `{ 404: 'package not found' }`.
|
||||
* This can be used to extend or override the
|
||||
* [default](https://github.com/badges/shields/blob/master/core/base-service/check-error-response.js#L5)
|
||||
* @param {object} [attrs.systemErrors={}] Key-value map of got network exception codes
|
||||
* and an object of params to pass when we construct an Inaccessible exception object
|
||||
* e.g: `{ ECONNRESET: { prettyMessage: 'connection reset' } }`.
|
||||
* See {@link https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md#errorcodes got error codes}
|
||||
* for allowed keys
|
||||
* and {@link module:core/base-service/errors~RuntimeErrorProps} for allowed values
|
||||
* @param {object} [attrs.parserOptions={}] Options to pass to fast-xml-parser. See
|
||||
* [documentation](https://github.com/NaturalIntelligence/fast-xml-parser#xml-to-json)
|
||||
* @returns {object} Parsed response
|
||||
@@ -44,8 +38,7 @@ class BaseXmlService extends BaseService {
|
||||
schema,
|
||||
url,
|
||||
options = {},
|
||||
httpErrors = {},
|
||||
systemErrors = {},
|
||||
errorMessages = {},
|
||||
parserOptions = {},
|
||||
}) {
|
||||
const logTrace = (...args) => trace.logTrace('fetch', ...args)
|
||||
@@ -56,8 +49,7 @@ class BaseXmlService extends BaseService {
|
||||
const { buffer } = await this._request({
|
||||
url,
|
||||
options: mergedOptions,
|
||||
httpErrors,
|
||||
systemErrors,
|
||||
errorMessages,
|
||||
})
|
||||
const validateResult = XMLValidator.validate(buffer)
|
||||
if (validateResult !== true) {
|
||||
|
||||
@@ -28,21 +28,21 @@ describe('BaseXmlService', function () {
|
||||
Promise.resolve({
|
||||
buffer: '<requiredString>some-string</requiredString>',
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher', async function () {
|
||||
await DummyXmlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
'http://example.com/foo.xml',
|
||||
{
|
||||
headers: { Accept: 'application/xml, text/xml' },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('BaseXmlService', function () {
|
||||
|
||||
await WithCustomOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -71,7 +71,7 @@ describe('BaseXmlService', function () {
|
||||
headers: { Accept: 'application/xml, text/xml' },
|
||||
method: 'POST',
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -85,8 +85,8 @@ describe('BaseXmlService', function () {
|
||||
expect(
|
||||
await DummyXmlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string',
|
||||
})
|
||||
@@ -112,8 +112,8 @@ describe('BaseXmlService', function () {
|
||||
expect(
|
||||
await DummyXmlServiceWithParserOption.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string with trailing whitespace ',
|
||||
})
|
||||
@@ -127,8 +127,8 @@ describe('BaseXmlService', function () {
|
||||
expect(
|
||||
await DummyXmlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -144,8 +144,8 @@ describe('BaseXmlService', function () {
|
||||
expect(
|
||||
await DummyXmlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -23,16 +23,10 @@ class BaseYamlService extends BaseService {
|
||||
* @param {string} attrs.url URL to request
|
||||
* @param {object} [attrs.options={}] Options to pass to got. See
|
||||
* [documentation](https://github.com/sindresorhus/got/blob/main/documentation/2-options.md)
|
||||
* @param {object} [attrs.httpErrors={}] Key-value map of status codes
|
||||
* @param {object} [attrs.errorMessages={}] Key-value map of status codes
|
||||
* and custom error messages e.g: `{ 404: 'package not found' }`.
|
||||
* This can be used to extend or override the
|
||||
* [default](https://github.com/badges/shields/blob/master/core/base-service/check-error-response.js#L5)
|
||||
* @param {object} [attrs.systemErrors={}] Key-value map of got network exception codes
|
||||
* and an object of params to pass when we construct an Inaccessible exception object
|
||||
* e.g: `{ ECONNRESET: { prettyMessage: 'connection reset' } }`.
|
||||
* See {@link https://github.com/sindresorhus/got/blob/main/documentation/7-retry.md#errorcodes got error codes}
|
||||
* for allowed keys
|
||||
* and {@link module:core/base-service/errors~RuntimeErrorProps} for allowed values
|
||||
* @param {object} [attrs.encoding='utf8'] Character encoding
|
||||
* @returns {object} Parsed response
|
||||
* @see https://github.com/sindresorhus/got/blob/main/documentation/2-options.md
|
||||
@@ -41,8 +35,7 @@ class BaseYamlService extends BaseService {
|
||||
schema,
|
||||
url,
|
||||
options = {},
|
||||
httpErrors = {},
|
||||
systemErrors = {},
|
||||
errorMessages = {},
|
||||
encoding = 'utf8',
|
||||
}) {
|
||||
const logTrace = (...args) => trace.logTrace('fetch', ...args)
|
||||
@@ -58,8 +51,7 @@ class BaseYamlService extends BaseService {
|
||||
const { buffer } = await this._request({
|
||||
url,
|
||||
options: mergedOptions,
|
||||
httpErrors,
|
||||
systemErrors,
|
||||
errorMessages,
|
||||
})
|
||||
let parsed
|
||||
try {
|
||||
|
||||
@@ -44,14 +44,14 @@ describe('BaseYamlService', function () {
|
||||
Promise.resolve({
|
||||
buffer: expectedYaml,
|
||||
res: { statusCode: 200 },
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it('invokes _requestFetcher', async function () {
|
||||
await DummyYamlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -61,7 +61,7 @@ describe('BaseYamlService', function () {
|
||||
Accept:
|
||||
'text/x-yaml, text/yaml, application/x-yaml, application/yaml, text/plain',
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@@ -79,7 +79,7 @@ describe('BaseYamlService', function () {
|
||||
|
||||
await WithOptions.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
|
||||
expect(requestFetcher).to.have.been.calledOnceWith(
|
||||
@@ -91,7 +91,7 @@ describe('BaseYamlService', function () {
|
||||
},
|
||||
method: 'POST',
|
||||
searchParams: { queryParam: 123 },
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -105,8 +105,8 @@ describe('BaseYamlService', function () {
|
||||
expect(
|
||||
await DummyYamlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'some-string',
|
||||
})
|
||||
@@ -120,8 +120,8 @@ describe('BaseYamlService', function () {
|
||||
expect(
|
||||
await DummyYamlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -137,8 +137,8 @@ describe('BaseYamlService', function () {
|
||||
expect(
|
||||
await DummyYamlService.invoke(
|
||||
{ requestFetcher },
|
||||
{ handleInternalErrors: false },
|
||||
),
|
||||
{ handleInternalErrors: false }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
} from './errors.js'
|
||||
import { validateExample, transformExample } from './examples.js'
|
||||
import { fetch } from './got.js'
|
||||
import { getEnum } from './openapi.js'
|
||||
import {
|
||||
makeFullUrl,
|
||||
assertValidRoute,
|
||||
@@ -45,7 +44,7 @@ const optionalStringWhenNamedLogoPresent = Joi.alternatives().conditional(
|
||||
{
|
||||
is: Joi.string().required(),
|
||||
then: Joi.string(),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const optionalNumberWhenAnyLogoPresent = Joi.alternatives()
|
||||
@@ -103,26 +102,6 @@ class BaseService {
|
||||
throw new Error(`Route not defined for ${this.name}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract an array of allowed values from this service's route pattern
|
||||
* for a given route parameter
|
||||
*
|
||||
* @param {string} param The name of a param in this service's route pattern
|
||||
* @returns {string[]} Array of allowed values for this param
|
||||
*/
|
||||
static getEnum(param) {
|
||||
if (!('pattern' in this.route)) {
|
||||
throw new Error('getEnum() requires route to have a .pattern property')
|
||||
}
|
||||
const enumeration = getEnum(this.route.pattern, param)
|
||||
if (!Array.isArray(enumeration)) {
|
||||
throw new Error(
|
||||
`Could not extract enum for param ${param} from pattern ${this.route.pattern}`,
|
||||
)
|
||||
}
|
||||
return enumeration
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration for the authentication helper that prepares credentials
|
||||
* for upstream requests.
|
||||
@@ -161,15 +140,6 @@ class BaseService {
|
||||
*/
|
||||
static examples = []
|
||||
|
||||
/**
|
||||
* Optional: an OpenAPI Paths Object describing this service's
|
||||
* route or routes in OpenAPI format.
|
||||
*
|
||||
* @see https://swagger.io/specification/#paths-object
|
||||
* @abstract
|
||||
*/
|
||||
static openApi = undefined
|
||||
|
||||
static get _cacheLength() {
|
||||
const cacheLengths = {
|
||||
build: 30,
|
||||
@@ -204,37 +174,21 @@ class BaseService {
|
||||
Joi.assert(
|
||||
this.defaultBadgeData,
|
||||
defaultBadgeDataSchema,
|
||||
`Default badge data for ${this.name}`,
|
||||
`Default badge data for ${this.name}`
|
||||
)
|
||||
|
||||
this.examples.forEach((example, index) =>
|
||||
validateExample(example, index, this),
|
||||
validateExample(example, index, this)
|
||||
)
|
||||
|
||||
// ensure openApi spec matches route
|
||||
if (this.openApi) {
|
||||
const preparedRoute = prepareRoute(this.route)
|
||||
for (const [key, value] of Object.entries(this.openApi)) {
|
||||
let example = key
|
||||
for (const param of value.get.parameters) {
|
||||
example = example.replace(`{${param.name}}`, param.example)
|
||||
}
|
||||
if (!example.match(preparedRoute.regex)) {
|
||||
throw new Error(
|
||||
`Inconsistent Open Api spec and Route found for service ${this.name}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static getDefinition() {
|
||||
const { category, name, isDeprecated, openApi } = this
|
||||
const { category, name, isDeprecated } = this
|
||||
const { base, format, pattern } = this.route
|
||||
const queryParams = getQueryParamNames(this.route)
|
||||
|
||||
const examples = this.examples.map((example, index) =>
|
||||
transformExample(example, index, this),
|
||||
transformExample(example, index, this)
|
||||
)
|
||||
|
||||
let route
|
||||
@@ -246,7 +200,7 @@ class BaseService {
|
||||
route = undefined
|
||||
}
|
||||
|
||||
const result = { category, name, isDeprecated, route, examples, openApi }
|
||||
const result = { category, name, isDeprecated, route, examples }
|
||||
|
||||
assertValidServiceDefinition(result, `getDefinition() for ${this.name}`)
|
||||
|
||||
@@ -255,7 +209,7 @@ class BaseService {
|
||||
|
||||
constructor(
|
||||
{ requestFetcher, authHelper, metricHelper },
|
||||
{ handleInternalErrors },
|
||||
{ handleInternalErrors }
|
||||
) {
|
||||
this._requestFetcher = requestFetcher
|
||||
this.authHelper = authHelper
|
||||
@@ -263,34 +217,24 @@ class BaseService {
|
||||
this._metricHelper = metricHelper
|
||||
}
|
||||
|
||||
async _request({ url, options = {}, httpErrors = {}, systemErrors = {} }) {
|
||||
async _request({ url, options = {}, errorMessages = {} }) {
|
||||
const logTrace = (...args) => trace.logTrace('fetch', ...args)
|
||||
let logUrl = url
|
||||
const logOptions = Object.assign({}, options)
|
||||
if ('searchParams' in options && options.searchParams != null) {
|
||||
const params = new URLSearchParams(
|
||||
Object.fromEntries(
|
||||
Object.entries(options.searchParams).filter(
|
||||
([k, v]) => v !== undefined,
|
||||
),
|
||||
),
|
||||
)
|
||||
if ('searchParams' in options) {
|
||||
const params = new URLSearchParams(options.searchParams)
|
||||
logUrl = `${url}?${params.toString()}`
|
||||
delete logOptions.searchParams
|
||||
}
|
||||
logTrace(
|
||||
emojic.bowAndArrow,
|
||||
'Request',
|
||||
`${logUrl}\n${JSON.stringify(logOptions, null, 2)}`,
|
||||
)
|
||||
const { res, buffer } = await this._requestFetcher(
|
||||
url,
|
||||
options,
|
||||
systemErrors,
|
||||
`${logUrl}\n${JSON.stringify(logOptions, null, 2)}`
|
||||
)
|
||||
const { res, buffer } = await this._requestFetcher(url, options)
|
||||
await this._meterResponse(res, buffer)
|
||||
logTrace(emojic.dart, 'Response status code', res.statusCode)
|
||||
return checkErrorResponse(httpErrors)({ buffer, res })
|
||||
return checkErrorResponse(errorMessages)({ buffer, res })
|
||||
}
|
||||
|
||||
static enabledMetrics = []
|
||||
@@ -316,7 +260,7 @@ class BaseService {
|
||||
prettyErrorMessage = 'invalid response data',
|
||||
includeKeys = false,
|
||||
allowAndStripUnknownKeys = true,
|
||||
} = {},
|
||||
} = {}
|
||||
) {
|
||||
return validate(
|
||||
{
|
||||
@@ -328,7 +272,7 @@ class BaseService {
|
||||
allowAndStripUnknownKeys,
|
||||
},
|
||||
data,
|
||||
schema,
|
||||
schema
|
||||
)
|
||||
}
|
||||
|
||||
@@ -369,22 +313,18 @@ class BaseService {
|
||||
error instanceof Deprecated
|
||||
) {
|
||||
trace.logTrace('outbound', emojic.noGoodWoman, 'Handled error', error)
|
||||
const serviceData = {
|
||||
return {
|
||||
isError: true,
|
||||
message: error.prettyMessage,
|
||||
color: 'lightgray',
|
||||
}
|
||||
if (error.cacheSeconds !== undefined) {
|
||||
serviceData.cacheSeconds = error.cacheSeconds
|
||||
}
|
||||
return serviceData
|
||||
} else if (this._handleInternalErrors) {
|
||||
if (
|
||||
!trace.logTrace(
|
||||
'unhandledError',
|
||||
emojic.boom,
|
||||
'Unhandled internal error',
|
||||
error,
|
||||
error
|
||||
)
|
||||
) {
|
||||
// This is where we end up if an unhandled exception is thrown in
|
||||
@@ -402,7 +342,7 @@ class BaseService {
|
||||
'unhandledError',
|
||||
emojic.boom,
|
||||
'Unhandled internal error',
|
||||
error,
|
||||
error
|
||||
)
|
||||
throw error
|
||||
}
|
||||
@@ -412,7 +352,7 @@ class BaseService {
|
||||
context = {},
|
||||
config = {},
|
||||
namedParams = {},
|
||||
queryParams = {},
|
||||
queryParams = {}
|
||||
) {
|
||||
trace.logTrace('inbound', emojic.womanCook, 'Service class', this.name)
|
||||
trace.logTrace('inbound', emojic.ticket, 'Named params', namedParams)
|
||||
@@ -446,13 +386,13 @@ class BaseService {
|
||||
traceSuccessMessage: 'Query params after validation',
|
||||
},
|
||||
queryParams,
|
||||
queryParamSchema,
|
||||
queryParamSchema
|
||||
)
|
||||
trace.logTrace(
|
||||
'inbound',
|
||||
emojic.crayon,
|
||||
'Query params after validation',
|
||||
queryParams,
|
||||
queryParams
|
||||
)
|
||||
} catch (error) {
|
||||
serviceError = error
|
||||
@@ -466,7 +406,7 @@ class BaseService {
|
||||
try {
|
||||
serviceData = await serviceInstance.handle(
|
||||
namedParams,
|
||||
transformedQueryParams,
|
||||
transformedQueryParams
|
||||
)
|
||||
serviceInstance._validateServiceData(serviceData)
|
||||
} catch (error) {
|
||||
@@ -491,7 +431,7 @@ class BaseService {
|
||||
librariesIoApiProvider,
|
||||
metricInstance,
|
||||
},
|
||||
serviceConfig,
|
||||
serviceConfig
|
||||
) {
|
||||
const { cacheHeaders: cacheHeaderConfig } = serviceConfig
|
||||
const { regex, captureNames } = prepareRoute(this.route)
|
||||
@@ -519,14 +459,14 @@ class BaseService {
|
||||
},
|
||||
serviceConfig,
|
||||
namedParams,
|
||||
queryParams,
|
||||
queryParams
|
||||
)
|
||||
|
||||
const badgeData = coalesceBadge(
|
||||
queryParams,
|
||||
serviceData,
|
||||
this.defaultBadgeData,
|
||||
this,
|
||||
this
|
||||
)
|
||||
// The final capture group is the extension.
|
||||
const format = (match.slice(-1)[0] || '.svg').replace(/^\./, '')
|
||||
@@ -535,7 +475,7 @@ class BaseService {
|
||||
metricHandle.noteResponseSent()
|
||||
},
|
||||
cacheLength: this._cacheLength,
|
||||
}),
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ describe('BaseService', function () {
|
||||
{},
|
||||
defaultConfig,
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ queryParamA: '!' },
|
||||
),
|
||||
{ queryParamA: '!' }
|
||||
)
|
||||
).to.deep.equal({
|
||||
message: 'Hello namedParamA: bar.bar.bar with queryParamA: !',
|
||||
})
|
||||
@@ -85,8 +85,8 @@ describe('BaseService', function () {
|
||||
{},
|
||||
defaultConfig,
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ queryParamA: ['foo', 'bar'] },
|
||||
),
|
||||
{ queryParamA: ['foo', 'bar'] }
|
||||
)
|
||||
).to.deep.equal({
|
||||
color: 'red',
|
||||
isError: true,
|
||||
@@ -97,13 +97,13 @@ describe('BaseService', function () {
|
||||
describe('Required overrides', function () {
|
||||
it('Should throw if render() is not overridden', function () {
|
||||
expect(() => BaseService.render()).to.throw(
|
||||
/^render\(\) function not implemented for BaseService$/,
|
||||
/^render\(\) function not implemented for BaseService$/
|
||||
)
|
||||
})
|
||||
|
||||
it('Should throw if route is not overridden', function () {
|
||||
return expect(BaseService.invoke({}, {}, {})).to.be.rejectedWith(
|
||||
/^Route not defined for BaseService$/,
|
||||
/^Route not defined for BaseService$/
|
||||
)
|
||||
})
|
||||
|
||||
@@ -112,13 +112,13 @@ describe('BaseService', function () {
|
||||
}
|
||||
it('Should throw if handle() is not overridden', function () {
|
||||
return expect(WithRoute.invoke({}, {}, {})).to.be.rejectedWith(
|
||||
/^Handler not implemented for WithRoute$/,
|
||||
/^Handler not implemented for WithRoute$/
|
||||
)
|
||||
})
|
||||
|
||||
it('Should throw if category is not overridden', function () {
|
||||
expect(() => BaseService.category).to.throw(
|
||||
/^Category not set for BaseService$/,
|
||||
/^Category not set for BaseService$/
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -135,25 +135,25 @@ describe('BaseService', function () {
|
||||
{},
|
||||
defaultConfig,
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ queryParamA: '!' },
|
||||
{ queryParamA: '!' }
|
||||
)
|
||||
expect(trace.logTrace).to.be.calledWithMatch(
|
||||
'inbound',
|
||||
sinon.match.string,
|
||||
'Service class',
|
||||
'DummyService',
|
||||
'DummyService'
|
||||
)
|
||||
expect(trace.logTrace).to.be.calledWith(
|
||||
'inbound',
|
||||
sinon.match.string,
|
||||
'Named params',
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
expect(trace.logTrace).to.be.calledWith(
|
||||
'inbound',
|
||||
sinon.match.string,
|
||||
'Query params after validation',
|
||||
{ queryParamA: '!' },
|
||||
{ queryParamA: '!' }
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -171,7 +171,7 @@ describe('BaseService', function () {
|
||||
const serviceData = await LinkService.invoke(
|
||||
{},
|
||||
{ handleInternalErrors: false },
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
|
||||
expect(serviceData).to.deep.equal({
|
||||
@@ -194,7 +194,7 @@ describe('BaseService', function () {
|
||||
await ThrowingService.invoke(
|
||||
{},
|
||||
{ handleInternalErrors: false },
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
expect.fail('Expected to throw')
|
||||
} catch (e) {
|
||||
@@ -212,7 +212,7 @@ describe('BaseService', function () {
|
||||
await ThrowingService.invoke(
|
||||
{},
|
||||
{ handleInternalErrors: false },
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
expect.fail('Expected to throw')
|
||||
} catch (e) {
|
||||
@@ -233,8 +233,8 @@ describe('BaseService', function () {
|
||||
await ThrowingService.invoke(
|
||||
{},
|
||||
{ handleInternalErrors: true },
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
),
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -251,7 +251,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'red',
|
||||
@@ -266,7 +266,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -281,7 +281,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -296,7 +296,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'lightgray',
|
||||
@@ -311,7 +311,7 @@ describe('BaseService', function () {
|
||||
}
|
||||
}
|
||||
expect(
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' }),
|
||||
await ThrowingService.invoke({}, {}, { namedParamA: 'bar.bar.bar' })
|
||||
).to.deep.equal({
|
||||
isError: true,
|
||||
color: 'red',
|
||||
@@ -336,7 +336,7 @@ describe('BaseService', function () {
|
||||
mockHandleRequest = sinon.spy()
|
||||
DummyService.register(
|
||||
{ camp: mockCamp, handleRequest: mockHandleRequest },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
})
|
||||
|
||||
@@ -413,8 +413,8 @@ describe('BaseService', function () {
|
||||
expect(() =>
|
||||
DummyService._validate(
|
||||
{ requiredString: ['this', "shouldn't", 'work'] },
|
||||
dummySchema,
|
||||
),
|
||||
dummySchema
|
||||
)
|
||||
)
|
||||
.to.throw()
|
||||
.instanceof(InvalidResponse)
|
||||
@@ -436,31 +436,24 @@ describe('BaseService', function () {
|
||||
})
|
||||
const serviceInstance = new DummyService(
|
||||
{ requestFetcher },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
const url = 'some-url'
|
||||
const options = {
|
||||
headers: { Cookie: 'some-cookie' },
|
||||
searchParams: { param1: 'foobar', param2: undefined },
|
||||
}
|
||||
const options = { headers: { Cookie: 'some-cookie' } }
|
||||
await serviceInstance._request({ url, options })
|
||||
|
||||
expect(trace.logTrace).to.be.calledWithMatch(
|
||||
'fetch',
|
||||
sinon.match.string,
|
||||
'Request',
|
||||
`${url}?param1=foobar\n${JSON.stringify(
|
||||
{ headers: options.headers },
|
||||
null,
|
||||
2,
|
||||
)}`,
|
||||
`${url}\n${JSON.stringify(options, null, 2)}`
|
||||
)
|
||||
expect(trace.logTrace).to.be.calledWithMatch(
|
||||
'fetch',
|
||||
sinon.match.string,
|
||||
'Response status code',
|
||||
200,
|
||||
200
|
||||
)
|
||||
})
|
||||
|
||||
@@ -471,7 +464,7 @@ describe('BaseService', function () {
|
||||
})
|
||||
const serviceInstance = new DummyService(
|
||||
{ requestFetcher },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
try {
|
||||
@@ -504,17 +497,17 @@ describe('BaseService', function () {
|
||||
const serviceInstance =
|
||||
new DummyServiceWithServiceResponseSizeMetricEnabled(
|
||||
{ requestFetcher, metricHelper },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
await serviceInstance._request({ url })
|
||||
|
||||
expect(await register.getSingleMetricAsString('service_response_bytes'))
|
||||
.to.contain(
|
||||
'service_response_bytes_bucket{le="65536",category="other",family="undefined",service="dummy_service_with_service_response_size_metric_enabled"} 0\n',
|
||||
'service_response_bytes_bucket{le="65536",category="other",family="undefined",service="dummy_service_with_service_response_size_metric_enabled"} 0\n'
|
||||
)
|
||||
.and.to.contain(
|
||||
'service_response_bytes_bucket{le="131072",category="other",family="undefined",service="dummy_service_with_service_response_size_metric_enabled"} 1\n',
|
||||
'service_response_bytes_bucket{le="131072",category="other",family="undefined",service="dummy_service_with_service_response_size_metric_enabled"} 1\n'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -529,17 +522,16 @@ describe('BaseService', function () {
|
||||
})
|
||||
const serviceInstance = new DummyService(
|
||||
{ requestFetcher, metricHelper },
|
||||
defaultConfig,
|
||||
defaultConfig
|
||||
)
|
||||
|
||||
await serviceInstance._request({ url })
|
||||
|
||||
expect(
|
||||
await register.getSingleMetricAsString('service_response_bytes'),
|
||||
await register.getSingleMetricAsString('service_response_bytes')
|
||||
).to.not.contain('service_response_bytes_bucket')
|
||||
})
|
||||
})
|
||||
|
||||
describe('auth', function () {
|
||||
class AuthService extends DummyService {
|
||||
static auth = {
|
||||
@@ -566,8 +558,8 @@ describe('BaseService', function () {
|
||||
},
|
||||
private: { myci_pass: 'abc123' },
|
||||
},
|
||||
{ namedParamA: 'bar.bar.bar' },
|
||||
),
|
||||
{ namedParamA: 'bar.bar.bar' }
|
||||
)
|
||||
).to.deep.equal({ message: 'The CI password is abc123' })
|
||||
})
|
||||
|
||||
@@ -584,8 +576,8 @@ describe('BaseService', function () {
|
||||
},
|
||||
{
|
||||
namedParamA: 'bar.bar.bar',
|
||||
},
|
||||
),
|
||||
}
|
||||
)
|
||||
).to.deep.equal({
|
||||
color: 'lightgray',
|
||||
isError: true,
|
||||
@@ -593,44 +585,4 @@ describe('BaseService', function () {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getEnum', function () {
|
||||
class EnumService extends DummyService {
|
||||
static route = {
|
||||
base: 'foo',
|
||||
pattern: ':namedParamA/:namedParamB(this|that)',
|
||||
queryParamSchema,
|
||||
}
|
||||
}
|
||||
|
||||
it('returns an array of allowed values', async function () {
|
||||
expect(EnumService.getEnum('namedParamB')).to.deep.equal(['this', 'that'])
|
||||
})
|
||||
|
||||
it('throws if param name is invalid', async function () {
|
||||
expect(() => EnumService.getEnum('notAValidParam')).to.throw(
|
||||
'Could not extract enum for param notAValidParam from pattern :namedParamA/:namedParamB(this|that)',
|
||||
)
|
||||
})
|
||||
|
||||
it('throws if param name is not an enum', async function () {
|
||||
expect(() => EnumService.getEnum('namedParamA')).to.throw(
|
||||
'Could not extract enum for param namedParamA from pattern :namedParamA/:namedParamB(this|that)',
|
||||
)
|
||||
})
|
||||
|
||||
it('throws if route does not have a pattern', async function () {
|
||||
class FormatService extends DummyService {
|
||||
static route = {
|
||||
base: 'foo',
|
||||
format: '([^/]+?)',
|
||||
queryParamSchema,
|
||||
}
|
||||
}
|
||||
|
||||
expect(() => FormatService.getEnum('notAValidParam')).to.throw(
|
||||
'getEnum() requires route to have a .pattern property',
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -39,14 +39,14 @@ function coalesceCacheLength({
|
||||
assert(defaultCacheLengthSeconds !== undefined)
|
||||
|
||||
const cacheLength = coalesce(
|
||||
serviceOverrideCacheLengthSeconds,
|
||||
serviceDefaultCacheLengthSeconds,
|
||||
defaultCacheLengthSeconds,
|
||||
defaultCacheLengthSeconds
|
||||
)
|
||||
|
||||
// Overrides can apply _more_ caching, but not less. Query param overriding
|
||||
// can request more overriding than service override, but not less.
|
||||
const candidateOverrides = [
|
||||
serviceOverrideCacheLengthSeconds,
|
||||
overrideCacheLengthFromQueryParams(queryParams),
|
||||
].filter(x => x !== undefined)
|
||||
|
||||
|
||||
@@ -74,12 +74,12 @@ describe('Cache header functions', function () {
|
||||
serviceDefaultCacheLengthSeconds: 900,
|
||||
serviceOverrideCacheLengthSeconds: 400,
|
||||
queryParams: {},
|
||||
}).expect(400)
|
||||
}).expect(900)
|
||||
given({
|
||||
cacheHeaderConfig,
|
||||
serviceOverrideCacheLengthSeconds: 400,
|
||||
queryParams: {},
|
||||
}).expect(400)
|
||||
}).expect(777)
|
||||
given({
|
||||
cacheHeaderConfig,
|
||||
serviceOverrideCacheLengthSeconds: 900,
|
||||
@@ -125,7 +125,7 @@ describe('Cache header functions', function () {
|
||||
|
||||
it('should set the expected Cache-Control header', function () {
|
||||
expect(res._headers['cache-control']).to.equal(
|
||||
'no-cache, no-store, must-revalidate',
|
||||
'no-cache, no-store, must-revalidate'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -141,7 +141,7 @@ describe('Cache header functions', function () {
|
||||
|
||||
it('should set the expected Cache-Control header', function () {
|
||||
expect(res._headers['cache-control']).to.equal(
|
||||
'max-age=123, s-maxage=123',
|
||||
'max-age=123, s-maxage=123'
|
||||
)
|
||||
})
|
||||
|
||||
@@ -156,7 +156,7 @@ describe('Cache header functions', function () {
|
||||
it('sets the expected fields', function () {
|
||||
const expectedFields = ['date', 'cache-control', 'expires']
|
||||
expectedFields.forEach(field =>
|
||||
expect(res._headers[field]).to.equal(undefined),
|
||||
expect(res._headers[field]).to.equal(undefined)
|
||||
)
|
||||
|
||||
setCacheHeaders({
|
||||
@@ -169,7 +169,7 @@ describe('Cache header functions', function () {
|
||||
expectedFields.forEach(field =>
|
||||
expect(res._headers[field])
|
||||
.to.be.a('string')
|
||||
.and.have.lengthOf.at.least(1),
|
||||
.and.have.lengthOf.at.least(1)
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -181,7 +181,7 @@ describe('Cache header functions', function () {
|
||||
|
||||
it('should set the expected Cache-Control header', function () {
|
||||
expect(res._headers['cache-control']).to.equal(
|
||||
`max-age=${24 * 3600}, s-maxage=${24 * 3600}`,
|
||||
`max-age=${24 * 3600}, s-maxage=${24 * 3600}`
|
||||
)
|
||||
})
|
||||
|
||||
@@ -190,7 +190,7 @@ describe('Cache header functions', function () {
|
||||
expect(new Date(lastModified)).to.be.withinTime(
|
||||
// Within the last 60 seconds.
|
||||
new Date(Date.now() - 60 * 1000),
|
||||
new Date(),
|
||||
new Date()
|
||||
)
|
||||
})
|
||||
})
|
||||
@@ -221,7 +221,7 @@ describe('Cache header functions', function () {
|
||||
})
|
||||
expect(serverHasBeenUpSinceResourceCached(req)).to.equal(false)
|
||||
})
|
||||
},
|
||||
}
|
||||
)
|
||||
context(
|
||||
'when the If-Modified-Since header is after the process started',
|
||||
@@ -233,7 +233,7 @@ describe('Cache header functions', function () {
|
||||
})
|
||||
expect(serverHasBeenUpSinceResourceCached(req)).to.equal(true)
|
||||
})
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,22 +2,21 @@ import { NotFound, InvalidResponse, Inaccessible } from './errors.js'
|
||||
|
||||
const defaultErrorMessages = {
|
||||
404: 'not found',
|
||||
429: 'rate limited by upstream service',
|
||||
}
|
||||
|
||||
export default function checkErrorResponse(httpErrors = {}) {
|
||||
export default function checkErrorResponse(errorMessages = {}) {
|
||||
return async function ({ buffer, res }) {
|
||||
let error
|
||||
httpErrors = { ...defaultErrorMessages, ...httpErrors }
|
||||
errorMessages = { ...defaultErrorMessages, ...errorMessages }
|
||||
if (res.statusCode === 404) {
|
||||
error = new NotFound({ prettyMessage: httpErrors[404] })
|
||||
error = new NotFound({ prettyMessage: errorMessages[404] })
|
||||
} else if (res.statusCode !== 200) {
|
||||
const underlying = Error(
|
||||
`Got status code ${res.statusCode} (expected 200)`,
|
||||
`Got status code ${res.statusCode} (expected 200)`
|
||||
)
|
||||
const props = { underlyingError: underlying }
|
||||
if (httpErrors[res.statusCode] !== undefined) {
|
||||
props.prettyMessage = httpErrors[res.statusCode]
|
||||
if (errorMessages[res.statusCode] !== undefined) {
|
||||
props.prettyMessage = errorMessages[res.statusCode]
|
||||
}
|
||||
if (res.statusCode >= 500) {
|
||||
error = new Inaccessible(props)
|
||||
|
||||
@@ -45,42 +45,6 @@ describe('async error handler', function () {
|
||||
})
|
||||
})
|
||||
|
||||
context('when status is 429', function () {
|
||||
const buffer = Buffer.from('some stuff')
|
||||
const res = { statusCode: 429 }
|
||||
|
||||
it('throws InvalidResponse', async function () {
|
||||
try {
|
||||
await checkErrorResponse()({ res, buffer })
|
||||
expect.fail('Expected to throw')
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidResponse)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Response: Got status code 429 (expected 200)',
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('rate limited by upstream service')
|
||||
expect(e.response).to.equal(res)
|
||||
expect(e.buffer).to.equal(buffer)
|
||||
}
|
||||
})
|
||||
|
||||
it('displays the custom too many requests', async function () {
|
||||
const notFoundMessage = "terribly sorry but that's one too many requests"
|
||||
try {
|
||||
await checkErrorResponse({ 429: notFoundMessage })({ res, buffer })
|
||||
expect.fail('Expected to throw')
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidResponse)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Response: Got status code 429 (expected 200)',
|
||||
)
|
||||
expect(e.prettyMessage).to.equal(
|
||||
"terribly sorry but that's one too many requests",
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
context('when status is 4xx', function () {
|
||||
it('throws InvalidResponse', async function () {
|
||||
const res = { statusCode: 499 }
|
||||
@@ -90,7 +54,7 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidResponse)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Response: Got status code 499 (expected 200)',
|
||||
'Invalid Response: Got status code 499 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('invalid')
|
||||
expect(e.response).to.equal(res)
|
||||
@@ -106,7 +70,7 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(InvalidResponse)
|
||||
expect(e.message).to.equal(
|
||||
'Invalid Response: Got status code 403 (expected 200)',
|
||||
'Invalid Response: Got status code 403 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('access denied')
|
||||
}
|
||||
@@ -122,7 +86,7 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(Inaccessible)
|
||||
expect(e.message).to.equal(
|
||||
'Inaccessible: Got status code 503 (expected 200)',
|
||||
'Inaccessible: Got status code 503 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('inaccessible')
|
||||
expect(e.response).to.equal(res)
|
||||
@@ -138,7 +102,7 @@ describe('async error handler', function () {
|
||||
} catch (e) {
|
||||
expect(e).to.be.an.instanceof(Inaccessible)
|
||||
expect(e.message).to.equal(
|
||||
'Inaccessible: Got status code 500 (expected 200)',
|
||||
'Inaccessible: Got status code 500 (expected 200)'
|
||||
)
|
||||
expect(e.prettyMessage).to.equal('server overloaded')
|
||||
}
|
||||
|
||||
@@ -16,15 +16,16 @@ import toArray from './to-array.js'
|
||||
//
|
||||
// Logos are resolved in this manner:
|
||||
//
|
||||
// 1. When `?logo=` contains a named logo or the name of one of the Shields
|
||||
// logos or contains base64-encoded SVG, that logo is used. When a
|
||||
// `&logoColor=` is specified, that color is used (except for the
|
||||
// base64-encoded logos). Otherwise the default color is used. If the color
|
||||
// is specified for a multicolor Shield logo, the named logo will be used and
|
||||
// colored. The appearance of the logo can be customized using `logoWidth`,
|
||||
// and in the case of the popout badge, `logoPosition`. When `?logo=` is
|
||||
// specified, any logo-related parameters specified dynamically by the
|
||||
// service, or by default in the service, are ignored.
|
||||
// 1. When `?logo=` contains the name of one of the Shields logos, or contains
|
||||
// base64-encoded SVG, that logo is used. In the case of a named logo, when
|
||||
// a `&logoColor=` is specified, that color is used. Otherwise the default
|
||||
// color is used. `logoColor` will not be applied to a custom
|
||||
// (base64-encoded) logo; if a custom color is desired the logo should be
|
||||
// recolored prior to making the request. The appearance of the logo can be
|
||||
// customized using `logoWidth`, and in the case of the popout badge,
|
||||
// `logoPosition`. When `?logo=` is specified, any logo-related parameters
|
||||
// specified dynamically by the service, or by default in the service, are
|
||||
// ignored.
|
||||
// 2. The second precedence is the dynamic logo returned by a service. This is
|
||||
// used only by the Endpoint badge. The `logoColor` can be overridden by the
|
||||
// query string.
|
||||
@@ -36,7 +37,7 @@ export default function coalesceBadge(
|
||||
serviceData,
|
||||
// These two parameters were kept separate to make tests clearer.
|
||||
defaultBadgeData,
|
||||
{ category, _cacheLength: defaultCacheSeconds } = {},
|
||||
{ category, _cacheLength: defaultCacheSeconds } = {}
|
||||
) {
|
||||
// The "overrideX" naming is based on services that provide badge
|
||||
// parameters themselves, which can be overridden by a query string
|
||||
@@ -141,7 +142,7 @@ export default function coalesceBadge(
|
||||
} else {
|
||||
namedLogo = coalesce(
|
||||
serviceNamedLogo,
|
||||
style === 'social' ? defaultNamedLogo : undefined,
|
||||
style === 'social' ? defaultNamedLogo : undefined
|
||||
)
|
||||
namedLogoColor = coalesce(overrideLogoColor, serviceLogoColor)
|
||||
}
|
||||
@@ -166,13 +167,13 @@ export default function coalesceBadge(
|
||||
isError ? undefined : overrideColor,
|
||||
serviceColor,
|
||||
defaultColor,
|
||||
'lightgrey',
|
||||
'lightgrey'
|
||||
),
|
||||
labelColor: coalesce(
|
||||
// In case of an error, disregard user's color override.
|
||||
isError ? undefined : overrideLabelColor,
|
||||
serviceLabelColor,
|
||||
defaultLabelColor,
|
||||
defaultLabelColor
|
||||
),
|
||||
style,
|
||||
namedLogo,
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('overrides the label', function () {
|
||||
expect(
|
||||
coalesceBadge({ label: 'purr count' }, { label: 'purrs' }, {}),
|
||||
coalesceBadge({ label: 'purr count' }, { label: 'purrs' }, {})
|
||||
).to.include({ label: 'purr count' })
|
||||
})
|
||||
})
|
||||
@@ -54,11 +54,11 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('overrides the color', function () {
|
||||
expect(
|
||||
coalesceBadge({ color: '10ADED' }, { color: 'red' }, {}),
|
||||
coalesceBadge({ color: '10ADED' }, { color: 'red' }, {})
|
||||
).to.include({ color: '10ADED' })
|
||||
// also expected for legacy name
|
||||
expect(
|
||||
coalesceBadge({ colorB: 'B0ADED' }, { color: 'red' }, {}),
|
||||
coalesceBadge({ colorB: 'B0ADED' }, { color: 'red' }, {})
|
||||
).to.include({ color: 'B0ADED' })
|
||||
})
|
||||
|
||||
@@ -68,16 +68,16 @@ describe('coalesceBadge', function () {
|
||||
coalesceBadge(
|
||||
{ color: '10ADED' },
|
||||
{ isError: true, color: 'lightgray' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ color: 'lightgray' })
|
||||
// also expected for legacy name
|
||||
expect(
|
||||
coalesceBadge(
|
||||
{ colorB: 'B0ADED' },
|
||||
{ isError: true, color: 'lightgray' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ color: 'lightgray' })
|
||||
})
|
||||
})
|
||||
@@ -102,11 +102,11 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('overrides the label color', function () {
|
||||
expect(
|
||||
coalesceBadge({ labelColor: '42f483' }, { color: 'green' }, {}),
|
||||
coalesceBadge({ labelColor: '42f483' }, { color: 'green' }, {})
|
||||
).to.include({ labelColor: '42f483' })
|
||||
// also expected for legacy name
|
||||
expect(
|
||||
coalesceBadge({ colorA: 'B2f483' }, { color: 'green' }, {}),
|
||||
coalesceBadge({ colorA: 'B2f483' }, { color: 'green' }, {})
|
||||
).to.include({ labelColor: 'B2f483' })
|
||||
})
|
||||
|
||||
@@ -116,8 +116,8 @@ describe('coalesceBadge', function () {
|
||||
// Scoutcamp converts numeric query params to numbers.
|
||||
{ color: 123 },
|
||||
{ color: 'green' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ color: '123' })
|
||||
// also expected for legacy name
|
||||
expect(
|
||||
@@ -125,8 +125,8 @@ describe('coalesceBadge', function () {
|
||||
// Scoutcamp converts numeric query params to numbers.
|
||||
{ colorB: 123 },
|
||||
{ color: 'green' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ color: '123' })
|
||||
})
|
||||
})
|
||||
@@ -140,7 +140,7 @@ describe('coalesceBadge', function () {
|
||||
it('when a social badge, uses the default named logo', function () {
|
||||
// .not.be.empty for confidence that nothing has changed with `getShieldsIcon()`.
|
||||
expect(
|
||||
coalesceBadge({ style: 'social' }, {}, { namedLogo: 'appveyor' }).logo,
|
||||
coalesceBadge({ style: 'social' }, {}, { namedLogo: 'appveyor' }).logo
|
||||
).to.equal(getSimpleIcon({ name: 'appveyor' })).and.not.be.empty
|
||||
})
|
||||
|
||||
@@ -149,50 +149,32 @@ describe('coalesceBadge', function () {
|
||||
namedLogo: 'npm',
|
||||
})
|
||||
expect(coalesceBadge({}, { namedLogo: 'npm' }, {}).logo).to.equal(
|
||||
getShieldsIcon({ name: 'npm' }),
|
||||
getShieldsIcon({ name: 'npm' })
|
||||
).and.not.to.be.empty
|
||||
})
|
||||
|
||||
it('applies the named monochrome logo with color', function () {
|
||||
it('applies the named logo with color', function () {
|
||||
expect(
|
||||
coalesceBadge({}, { namedLogo: 'dependabot', logoColor: 'blue' }, {})
|
||||
.logo,
|
||||
).to.equal(getShieldsIcon({ name: 'dependabot', color: 'blue' })).and.not
|
||||
.to.be.empty
|
||||
})
|
||||
|
||||
it('applies the named multicolored logo with color', function () {
|
||||
expect(
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoColor: 'blue' }, {}).logo,
|
||||
).to.equal(getSimpleIcon({ name: 'npm', color: 'blue' })).and.not.to.be
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoColor: 'blue' }, {}).logo
|
||||
).to.equal(getShieldsIcon({ name: 'npm', color: 'blue' })).and.not.to.be
|
||||
.empty
|
||||
})
|
||||
|
||||
it('overrides the logo', function () {
|
||||
expect(
|
||||
coalesceBadge({ logo: 'npm' }, { namedLogo: 'appveyor' }, {}).logo,
|
||||
coalesceBadge({ logo: 'npm' }, { namedLogo: 'appveyor' }, {}).logo
|
||||
).to.equal(getShieldsIcon({ name: 'npm' })).and.not.be.empty
|
||||
})
|
||||
|
||||
it('overrides the monochrome logo with a color', function () {
|
||||
expect(
|
||||
coalesceBadge(
|
||||
{ logo: 'dependabot', logoColor: 'blue' },
|
||||
{ namedLogo: 'appveyor' },
|
||||
{},
|
||||
).logo,
|
||||
).to.equal(getShieldsIcon({ name: 'dependabot', color: 'blue' })).and.not
|
||||
.be.empty
|
||||
})
|
||||
|
||||
it('overrides multicolored logo with a color', function () {
|
||||
it('overrides the logo with a color', function () {
|
||||
expect(
|
||||
coalesceBadge(
|
||||
{ logo: 'npm', logoColor: 'blue' },
|
||||
{ namedLogo: 'appveyor' },
|
||||
{},
|
||||
).logo,
|
||||
).to.equal(getSimpleIcon({ name: 'npm', color: 'blue' })).and.not.be.empty
|
||||
{}
|
||||
).logo
|
||||
).to.equal(getShieldsIcon({ name: 'npm', color: 'blue' })).and.not.be
|
||||
.empty
|
||||
})
|
||||
|
||||
it("when the logo is overridden, it ignores the service's logo color, position, and width", function () {
|
||||
@@ -205,37 +187,27 @@ describe('coalesceBadge', function () {
|
||||
logoPosition: -3,
|
||||
logoWidth: 100,
|
||||
},
|
||||
{},
|
||||
).logo,
|
||||
{}
|
||||
).logo
|
||||
).to.equal(getShieldsIcon({ name: 'npm' })).and.not.be.empty
|
||||
})
|
||||
|
||||
it("overrides the service monochome logo's color", function () {
|
||||
expect(
|
||||
coalesceBadge(
|
||||
{ logoColor: 'blue' },
|
||||
{ namedLogo: 'dependabot', logoColor: 'red' },
|
||||
{},
|
||||
).logo,
|
||||
).to.equal(getShieldsIcon({ name: 'dependabot', color: 'blue' })).and.not
|
||||
.be.empty
|
||||
})
|
||||
|
||||
it("overrides the service multicolored logo's color", function () {
|
||||
it("overrides the service logo's color", function () {
|
||||
expect(
|
||||
coalesceBadge(
|
||||
{ logoColor: 'blue' },
|
||||
{ namedLogo: 'npm', logoColor: 'red' },
|
||||
{},
|
||||
).logo,
|
||||
).to.equal(getSimpleIcon({ name: 'npm', color: 'blue' })).and.not.be.empty
|
||||
{}
|
||||
).logo
|
||||
).to.equal(getShieldsIcon({ name: 'npm', color: 'blue' })).and.not.be
|
||||
.empty
|
||||
})
|
||||
|
||||
// https://github.com/badges/shields/issues/2998
|
||||
it('overrides logoSvg', function () {
|
||||
const logoSvg = 'data:image/svg+xml;base64,PHN2ZyB4bWxu'
|
||||
expect(coalesceBadge({ logo: 'npm' }, { logoSvg }, {}).logo).to.equal(
|
||||
getShieldsIcon({ name: 'npm' }),
|
||||
getShieldsIcon({ name: 'npm' })
|
||||
).and.not.be.empty
|
||||
})
|
||||
})
|
||||
@@ -244,7 +216,7 @@ describe('coalesceBadge', function () {
|
||||
it('overrides the logo with custom svg', function () {
|
||||
const logoSvg = 'data:image/svg+xml;base64,PHN2ZyB4bWxu'
|
||||
expect(
|
||||
coalesceBadge({ logo: logoSvg }, { namedLogo: 'appveyor' }, {}),
|
||||
coalesceBadge({ logo: logoSvg }, { namedLogo: 'appveyor' }, {})
|
||||
).to.include({ logo: logoSvg })
|
||||
})
|
||||
|
||||
@@ -254,8 +226,8 @@ describe('coalesceBadge', function () {
|
||||
coalesceBadge(
|
||||
{ logo: logoSvg, logoColor: 'brightgreen' },
|
||||
{ namedLogo: 'appveyor' },
|
||||
{},
|
||||
),
|
||||
{}
|
||||
)
|
||||
).to.include({ logo: logoSvg })
|
||||
})
|
||||
})
|
||||
@@ -269,7 +241,7 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('applies the logo width', function () {
|
||||
expect(
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoWidth: 275 }, {}),
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoWidth: 275 }, {})
|
||||
).to.include({ logoWidth: 275 })
|
||||
})
|
||||
})
|
||||
@@ -283,7 +255,7 @@ describe('coalesceBadge', function () {
|
||||
|
||||
it('applies the logo position', function () {
|
||||
expect(
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoPosition: -10 }, {}),
|
||||
coalesceBadge({}, { namedLogo: 'npm', logoPosition: -10 }, {})
|
||||
).to.include({ logoPosition: -10 })
|
||||
})
|
||||
})
|
||||
@@ -296,8 +268,8 @@ describe('coalesceBadge', function () {
|
||||
{
|
||||
link: 'https://circleci.com/workflow-run/184ef3de-4836-4805-a2e4-0ceba099f92d',
|
||||
},
|
||||
{},
|
||||
).links,
|
||||
{}
|
||||
).links
|
||||
).to.deep.equal(['https://circleci.com/gh/badges/daily-tests'])
|
||||
})
|
||||
})
|
||||
@@ -328,7 +300,7 @@ describe('coalesceBadge', function () {
|
||||
describe('Cache length', function () {
|
||||
it('overrides the cache length', function () {
|
||||
expect(
|
||||
coalesceBadge({ style: 'pill' }, { cacheSeconds: 123 }, {}),
|
||||
coalesceBadge({ style: 'pill' }, { cacheSeconds: 123 }, {})
|
||||
).to.include({ cacheLengthSeconds: 123 })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -20,7 +20,7 @@ function deprecatedService(attrs) {
|
||||
const { route, name, label, category, examples, message } = Joi.attempt(
|
||||
attrs,
|
||||
attrSchema,
|
||||
`Deprecated service for ${attrs.route.base}`,
|
||||
`Deprecated service for ${attrs.route.base}`
|
||||
)
|
||||
|
||||
return class DeprecatedService extends BaseService {
|
||||
|
||||
@@ -42,7 +42,6 @@ class ShieldsRuntimeError extends Error {
|
||||
if (props.underlyingError) {
|
||||
this.stack = props.underlyingError.stack
|
||||
}
|
||||
this.cacheSeconds = props.cacheSeconds
|
||||
}
|
||||
}
|
||||
|
||||
@@ -207,9 +206,6 @@ class Deprecated extends ShieldsRuntimeError {
|
||||
* @property {string} prettyMessage User-facing error message to override the
|
||||
* value of `defaultPrettyMessage()`. This is the text that will appear on the
|
||||
* badge when we catch and render the exception (Optional)
|
||||
* @property {number} cacheSeconds Length of time to cache this error response
|
||||
* for. Defaults to the cacheLength of the service class throwing the error
|
||||
* (Optional)
|
||||
*/
|
||||
|
||||
export {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user