Compare commits

...

42 Commits

Author SHA1 Message Date
chris48s
b0ef5046d0 use credentials if available 2023-01-15 12:36:30 +00:00
chris48s
f60c2058fa fix PAT check 2023-01-15 11:32:01 +00:00
chris48s
39e4d9bcbc rename GithubGist to Gist 2023-01-15 11:22:41 +00:00
chris48s
2bf863fb09 use a PAT if available 2023-01-12 20:40:49 +00:00
chris48s
ed277d4e79 give token contents:read 2023-01-11 20:33:03 +00:00
chris48s
fdc81c2e3a give token gist:read 2023-01-11 20:24:50 +00:00
chris48s
6ef9dcaba5 use workflow token for github service tests 2023-01-11 20:05:58 +00:00
chris48s
541cb9acf2 update triggers 2023-01-11 19:53:56 +00:00
chris48s
2492ff79f7 Merge branch 'master' into services-gha 2023-01-11 19:42:32 +00:00
Prashant Rawat
0c73b35915 Add docstrings for pipenv helpers (#8787)
* add docstrings for pipenv helpers

* update param description

Co-authored-by: chris48s <chris48s@users.noreply.github.com>
2023-01-10 20:49:52 +00:00
chris48s
f1d151e963 migrate danger CI job to GHA (#8477)
* migrate danger CI job to GHA

* constrain triggers

* prettier
2023-01-10 19:39:11 +00:00
James Chen-Smith
a0149a8f8f deprecate [apm] service (#8773)
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2023-01-07 18:43:10 +00:00
dependabot[bot]
e843d4eac1 chore(deps): bump json5 from v2.2.1 to v2.2.3 (#8785)
Bumps [json5](https://github.com/json5/json5)
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 18:27:36 +00:00
dependabot[bot]
830b5d8a1f chore(deps-dev): bump @babel/core from 7.20.7 to 7.20.12 (#8779)
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.20.7 to 7.20.12.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.20.12/packages/babel-core)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-07 18:18:30 +00:00
dependabot[bot]
8afb034a58 chore(deps-dev): bump cypress from 12.2.0 to 12.3.0 (#8778)
Bumps [cypress](https://github.com/cypress-io/cypress) from 12.2.0 to 12.3.0.
- [Release notes](https://github.com/cypress-io/cypress/releases)
- [Changelog](https://github.com/cypress-io/cypress/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/cypress-io/cypress/compare/v12.2.0...v12.3.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2023-01-07 18:11:01 +00:00
dependabot[bot]
2f915a7b45 chore(deps-dev): bump @typescript-eslint/eslint-plugin (#8781)
Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 5.47.1 to 5.48.0.
- [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases)
- [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md)
- [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v5.48.0/packages/eslint-plugin)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2023-01-06 21:24:46 +00:00
dependabot[bot]
7dbfd0d049 chore(deps): bump @sentry/node from 7.28.1 to 7.29.0 (#8780)
Bumps [@sentry/node](https://github.com/getsentry/sentry-javascript) from 7.28.1 to 7.29.0.
- [Release notes](https://github.com/getsentry/sentry-javascript/releases)
- [Changelog](https://github.com/getsentry/sentry-javascript/blob/master/CHANGELOG.md)
- [Commits](https://github.com/getsentry/sentry-javascript/compare/7.28.1...7.29.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2023-01-06 21:21:08 +00:00
dependabot[bot]
493fdb76af chore(deps-dev): bump danger from 11.2.0 to 11.2.1 (#8775)
Bumps [danger](https://github.com/danger/danger-js) from 11.2.0 to 11.2.1.
- [Release notes](https://github.com/danger/danger-js/releases)
- [Changelog](https://github.com/danger/danger-js/blob/main/CHANGELOG.md)
- [Commits](https://github.com/danger/danger-js/compare/11.2.0...11.2.1)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2023-01-06 21:16:16 +00:00
dependabot[bot]
abb1bbf8d4 chore(deps): bump prom-client from 14.1.0 to 14.1.1 (#8777)
Bumps [prom-client](https://github.com/siimon/prom-client) from 14.1.0 to 14.1.1.
- [Release notes](https://github.com/siimon/prom-client/releases)
- [Changelog](https://github.com/siimon/prom-client/blob/master/CHANGELOG.md)
- [Commits](https://github.com/siimon/prom-client/compare/v14.1.0...v14.1.1)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2023-01-06 21:11:39 +00:00
dependabot[bot]
a4911dac33 chore(deps-dev): bump eslint-config-prettier from 8.5.0 to 8.6.0 (#8774)
Bumps [eslint-config-prettier](https://github.com/prettier/eslint-config-prettier) from 8.5.0 to 8.6.0.
- [Release notes](https://github.com/prettier/eslint-config-prettier/releases)
- [Changelog](https://github.com/prettier/eslint-config-prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/eslint-config-prettier/compare/v8.5.0...v8.6.0)

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

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: repo-ranger[bot] <39074581+repo-ranger[bot]@users.noreply.github.com>
2023-01-06 21:06:11 +00:00
Prashant Rawat
5e6583c530 Docstrings for php-version service (#8766)
* add docstrings for php-version service

* update docstring for resource-cache service

* update docstring param type
2023-01-06 21:01:59 +00:00
Paula Barszcz
bb1fda2aa7 deprecate lgtm; lgtm service tests should be passing (#8771) 2023-01-04 19:48:47 +00:00
chris48s
9c692cd53a fail + report if tests are pending 2022-09-19 18:25:51 +01:00
chris48s
7410bf2e97 do nothing 2022-09-19 18:16:02 +01:00
chris48s
a83cfa4fb6 do nothing 2022-09-19 18:11:29 +01:00
chris48s
4d64969738 do nothing 2022-09-19 18:08:14 +01:00
chris48s
ade213c6d3 pass PR title safely 2022-09-19 17:26:14 +01:00
chris48s
1135fba9f6 remove spurious debug 2022-09-19 17:09:09 +01:00
chris48s
0234fb077f improve report 2022-09-19 17:04:57 +01:00
chris48s
ed86c1de21 debug statement 2022-09-19 17:00:53 +01:00
chris48s
a47f770c82 better summary 2022-09-19 16:30:45 +01:00
chris48s
3176d6f7f3 Revert "break some stuff on purpose"
This reverts commit 0763d8ec66.
2022-09-19 16:20:00 +01:00
chris48s
0763d8ec66 break some stuff on purpose 2022-09-19 16:14:17 +01:00
chris48s
3fcb959ed2 single-quote the PR title 2022-09-19 16:08:42 +01:00
chris48s
f562dfe868 Revert "pass the PR title from the workflow to the action"
This reverts commit 22aee48544.
2022-09-19 16:06:44 +01:00
chris48s
22aee48544 pass the PR title from the workflow to the action 2022-09-19 16:02:56 +01:00
chris48s
098a24cae5 do nothing 2022-09-19 15:56:46 +01:00
chris48s
a2c8ed27b8 only run summary if there were any tests to run 2022-09-19 15:35:51 +01:00
chris48s
2dccd3d040 only run summary if there were any tests to run 2022-09-19 15:27:14 +01:00
chris48s
b8ce38a041 fix titles 2022-09-19 15:25:01 +01:00
chris48s
0784a14153 fix runs-on 2022-09-19 15:21:11 +01:00
chris48s
afc7b283bc migrate service tests to GH actions 2022-09-19 15:16:09 +01:00
29 changed files with 731 additions and 1284 deletions

View File

@@ -1,110 +1,3 @@
version: 2
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
jobs:
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
services:
docker:
- image: cimg/node:16.15
<<: *services_steps
services@node-17:
docker:
- image: cimg/node:17.9
environment:
NPM_CONFIG_ENGINE_STRICT: 'false'
<<: *services_steps
workflows:
version: 2
on-commit:
jobs:
- services:
filters:
branches:
ignore:
- master
- gh-pages
- services@node-17:
filters:
branches:
ignore:
- master
- gh-pages
- danger:
filters:
branches:
ignore:
- master
- gh-pages
- /dependabot\/.*/
# on-commit-with-cache:
# jobs:
# - npm-install:
# filters:
# branches:
# ignore: gh-pages
# - 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\/.*/
# Do nothing
# TODO: disable Circle

View File

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

29
.github/workflows/danger.yml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: Danger
on:
pull_request_target:
types: [opened, edited, reopened, synchronize]
permissions:
checks: write
pull-requests: write
statuses: write
jobs:
danger:
runs-on: ubuntu-latest
if: github.actor != 'dependabot[bot]'
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup
with:
node-version: 16
- name: Danger
run: npm run danger ci
env:
# https://github.com/gatsbyjs/gatsby/pull/11555
NODE_ENV: test
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'

40
.github/workflows/test-services-17.yml vendored Normal file
View File

@@ -0,0 +1,40 @@
name: Services@node 17
on:
pull_request:
types: [opened, edited, reopened, synchronize]
jobs:
test-services-17:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup
with:
node-version: 17
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 }}'

38
.github/workflows/test-services.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: Services
on:
pull_request:
types: [opened, edited, reopened, synchronize]
jobs:
test-services:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup
with:
node-version: 16
- 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 }}'

View File

@@ -19,9 +19,6 @@
<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>

View File

@@ -14,14 +14,16 @@ let resourceCache = Object.create(null)
/**
* Make a HTTP request using an in-memory cache
*
* @param {object} attrs Refer to individual attrs
* @param {string} attrs.url URL to request
* @param {number} attrs.ttl Number of milliseconds to keep cached value for
* @param {boolean} [attrs.json=true] True if we expect to parse the response as JSON
* @param {Function} [attrs.scraper=buffer => buffer] Function to extract value from the response
* @param {object} [attrs.options={}] Options to pass to got
* @param {Function} [attrs.requestFetcher=fetch] Custom fetch function
* @returns {*} Parsed response
* @async
* @param {object} attrs - Refer to individual attrs
* @param {string} attrs.url - URL to request
* @param {number} attrs.ttl - Number of milliseconds to keep cached value for
* @param {boolean} [attrs.json=true] - True if we expect to parse the response as JSON
* @param {Function} [attrs.scraper=buffer => buffer] - Function to extract value from the response
* @param {object} [attrs.options={}] - Options to pass to got
* @param {Function} [attrs.requestFetcher=fetch] - Custom fetch function
* @throws {InvalidResponse} - Error if unable to parse response
* @returns {Promise<*>} Promise that resolves to parsed response
*/
async function getCachedResource({
url,

View File

@@ -1,102 +0,0 @@
/**
* @module
*/
import { URL, format as urlFormat } from 'url'
function formatSlug(owner, repo, pullRequest) {
return `${owner}/${repo}#${pullRequest}`
}
function parseGithubPullRequestUrl(url, options = {}) {
const { verifyBaseUrl } = options
const parsed = new URL(url)
const components = parsed.pathname.substr(1).split('/')
if (components[2] !== 'pull' || components.length !== 4) {
throw Error(`Invalid GitHub pull request URL: ${url}`)
}
const [owner, repo, , pullRequest] = components
parsed.pathname = ''
const baseUrl = urlFormat(parsed, {
auth: false,
fragment: false,
search: false,
}).replace(/\/$/, '')
if (verifyBaseUrl && baseUrl !== verifyBaseUrl) {
throw Error(`Expected base URL to be ${verifyBaseUrl} but got ${baseUrl}`)
}
return {
baseUrl,
owner,
repo,
pullRequest: +pullRequest,
slug: formatSlug(owner, repo, pullRequest),
}
}
function parseGithubRepoSlug(slug) {
const components = slug.split('/')
if (components.length !== 2) {
throw Error(`Invalid GitHub repo slug: ${slug}`)
}
const [owner, repo] = components
return { owner, repo }
}
function _inferPullRequestFromTravisEnv(env) {
const { owner, repo } = parseGithubRepoSlug(env.TRAVIS_REPO_SLUG)
const pullRequest = +env.TRAVIS_PULL_REQUEST
return {
owner,
repo,
pullRequest,
slug: formatSlug(owner, repo, pullRequest),
}
}
function _inferPullRequestFromCircleEnv(env) {
return parseGithubPullRequestUrl(
env.CI_PULL_REQUEST || env.CIRCLE_PULL_REQUEST
)
}
/**
* When called inside a CI build, infer the details
* of a pull request from the environment variables.
*
* @param {object} [env=process.env] Environment variables
* @returns {module:core/service-test-runner/infer-pull-request~PullRequest}
* Pull Request
*/
function inferPullRequest(env = process.env) {
if (env.TRAVIS) {
return _inferPullRequestFromTravisEnv(env)
} else if (env.CIRCLECI) {
return _inferPullRequestFromCircleEnv(env)
} else if (env.CI) {
throw Error(
'Unsupported CI system. Unable to obtain pull request information from the environment.'
)
} else {
throw Error(
'Unable to obtain pull request information from the environment. Is this running in CI?'
)
}
}
/**
* Pull Request
*
* @typedef PullRequest
* @property {string} pr.baseUrl (returned for travis CI only)
* @property {string} owner
* @property {string} repo
* @property {string} pullRequest PR/issue number
* @property {string} slug owner/repo/#pullRequest
*/
export { parseGithubPullRequestUrl, parseGithubRepoSlug, inferPullRequest }

View File

@@ -1,48 +0,0 @@
import { test, given, forCases } from 'sazerac'
import {
parseGithubPullRequestUrl,
inferPullRequest,
} from './infer-pull-request.js'
describe('Pull request inference', function () {
test(parseGithubPullRequestUrl, () => {
forCases([
given('https://github.com/badges/shields/pull/1234'),
given('https://github.com/badges/shields/pull/1234', {
verifyBaseUrl: 'https://github.com',
}),
]).expect({
baseUrl: 'https://github.com',
owner: 'badges',
repo: 'shields',
pullRequest: 1234,
slug: 'badges/shields#1234',
})
given('https://github.com/badges/shields/pull/1234', {
verifyBaseUrl: 'https://example.com',
}).expectError(
'Expected base URL to be https://example.com but got https://github.com'
)
})
test(inferPullRequest, () => {
const expected = {
owner: 'badges',
repo: 'shields',
pullRequest: 1234,
slug: 'badges/shields#1234',
}
given({
CIRCLECI: '1',
CI_PULL_REQUEST: 'https://github.com/badges/shields/pull/1234',
}).expect(Object.assign({ baseUrl: 'https://github.com' }, expected))
given({
TRAVIS: '1',
TRAVIS_REPO_SLUG: 'badges/shields',
TRAVIS_PULL_REQUEST: '1234',
}).expect(expected)
})
})

View File

@@ -1,5 +1,5 @@
// Infer the current PR from the Travis environment, and look for bracketed,
// space-separated service names in the pull request title.
// Derive a list of service tests to run based on
// space-separated service names in the PR title.
//
// Output the list of services.
//
@@ -8,54 +8,26 @@
// Output:
// travis
// sonar
//
// Example:
//
// TRAVIS=1 TRAVIS_REPO_SLUG=badges/shields TRAVIS_PULL_REQUEST=1108 npm run test:services:pr:prepare
import got from 'got'
import { inferPullRequest } from './infer-pull-request.js'
import servicesForTitle from './services-for-title.js'
async function getTitle(owner, repo, pullRequest) {
const {
body: { title },
} = await got(
`https://api.github.com/repos/${owner}/${repo}/pulls/${pullRequest}`,
{
headers: {
'User-Agent': 'badges/shields',
Authorization: `token ${process.env.GITHUB_TOKEN}`,
},
responseType: 'json',
}
)
return title
let title
try {
if (process.argv.length < 3) {
throw new Error()
}
title = process.argv[2]
} catch (e) {
console.error('Error processing arguments')
process.exit(1)
}
async function main() {
const { owner, repo, pullRequest, slug } = inferPullRequest()
console.error(`PR: ${slug}`)
const title = await getTitle(owner, repo, pullRequest)
console.error(`Title: ${title}\n`)
const services = servicesForTitle(title)
if (services.length === 0) {
console.error('No services found. Nothing to do.')
} else {
console.error(
`Services: (${services.length} found) ${services.join(', ')}\n`
)
console.log(services.join('\n'))
}
console.error(`Title: ${title}\n`)
const services = servicesForTitle(title)
if (services.length === 0) {
console.error('No services found. Nothing to do.')
} else {
console.error(`Services: (${services.length} found) ${services.join(', ')}\n`)
console.log(services.join('\n'))
}
;(async () => {
try {
await main()
} catch (e) {
console.error(e)
process.exit(1)
}
})()

View File

@@ -106,9 +106,6 @@ export default function SponsorsPage(): JSX.Element {
<li>
<a href="https://github.com/">GitHub</a>
</li>
<li>
<a href="https://lgtm.com/">LGTM</a>
</li>
<li>
<a href="https://uptimerobot.com/">Uptime Robot</a>
</li>

761
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -25,7 +25,7 @@
"@fontsource/lekton": "^4.5.11",
"@renovate/pep440": "^1.0.0",
"@renovatebot/ruby-semver": "^1.1.7",
"@sentry/node": "^7.28.1",
"@sentry/node": "^7.29.0",
"@shields_io/camp": "^18.1.1",
"badge-maker": "file:badge-maker",
"bytes": "^3.1.2",
@@ -58,7 +58,7 @@
"path-to-regexp": "^6.2.1",
"pretty-bytes": "^6.0.0",
"priorityqueuejs": "^2.0.0",
"prom-client": "^14.1.0",
"prom-client": "^14.1.1",
"qs": "^6.11.0",
"query-string": "^8.1.0",
"semver": "~7.3.8",
@@ -142,7 +142,7 @@
]
},
"devDependencies": {
"@babel/core": "^7.20.7",
"@babel/core": "^7.20.12",
"@babel/polyfill": "^7.12.1",
"@babel/register": "7.18.9",
"@istanbuljs/schema": "^0.1.3",
@@ -156,7 +156,7 @@
"@types/react-modal": "^3.13.1",
"@types/react-select": "^4.0.17",
"@types/styled-components": "5.1.26",
"@typescript-eslint/eslint-plugin": "^5.47.1",
"@typescript-eslint/eslint-plugin": "^5.48.0",
"@typescript-eslint/parser": "^5.46.0",
"babel-plugin-inline-react-svg": "^2.0.1",
"babel-preset-gatsby": "^2.22.0",
@@ -169,13 +169,13 @@
"child-process-promise": "^2.2.1",
"clipboard-copy": "^4.0.1",
"concurrently": "^7.6.0",
"cypress": "^12.2.0",
"cypress": "^12.3.0",
"cypress-wait-for-stable-dom": "^0.1.0",
"danger": "^11.2.0",
"danger": "^11.2.1",
"danger-plugin-no-test-shortcuts": "^2.0.0",
"deepmerge": "^4.2.2",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-config-prettier": "^8.6.0",
"eslint-config-standard": "^16.0.3",
"eslint-config-standard-jsx": "^10.0.0",
"eslint-config-standard-react": "^11.0.1",

View File

@@ -22,6 +22,10 @@ if (data.stats.passes > 0) {
if (data.stats.failures > 0) {
process.stdout.write(`${data.stats.failures} failed\n\n`)
}
if (data.stats.pending > 0) {
process.stdout.write(`${data.stats.pending} pending\n\n`)
process.exit(2)
}
if (data.stats.failures > 0) {
for (const test of data.tests) {

View File

@@ -1,117 +1,33 @@
import Joi from 'joi'
import { renderLicenseBadge } from '../licenses.js'
import { renderVersionBadge } from '../version.js'
import { renderDownloadsBadge } from '../downloads.js'
import { nonNegativeInteger } from '../validators.js'
import { BaseJsonService, InvalidResponse } from '../index.js'
import { deprecatedService } from '../index.js'
const keywords = ['atom']
const schema = Joi.object({
downloads: nonNegativeInteger,
releases: Joi.object({
latest: Joi.string().required(),
}),
metadata: Joi.object({
license: Joi.string().required(),
}),
const APMDownloads = deprecatedService({
category: 'downloads',
route: {
base: 'apm/dm',
pattern: ':various*',
},
label: 'downloads',
dateAdded: new Date('2023-01-04'),
})
class BaseAPMService extends BaseJsonService {
static defaultBadgeData = { label: 'apm' }
const APMVersion = deprecatedService({
category: 'version',
route: {
base: 'apm/v',
pattern: ':various*',
},
label: 'apm',
dateAdded: new Date('2023-01-04'),
})
async fetch({ packageName }) {
return this._requestJson({
schema,
url: `https://atom.io/api/packages/${packageName}`,
errorMessages: { 404: 'package not found' },
})
}
}
class APMDownloads extends BaseAPMService {
static category = 'downloads'
static route = { base: 'apm/dm', pattern: ':packageName' }
static examples = [
{
title: 'APM',
namedParams: { packageName: 'vim-mode' },
staticPreview: this.render({ downloads: '60043' }),
keywords,
},
]
static defaultBadgeData = { label: 'downloads' }
static render({ downloads }) {
return renderDownloadsBadge({ downloads, colorOverride: 'green' })
}
async handle({ packageName }) {
const json = await this.fetch({ packageName })
return this.constructor.render({ downloads: json.downloads })
}
}
class APMVersion extends BaseAPMService {
static category = 'version'
static route = { base: 'apm/v', pattern: ':packageName' }
static examples = [
{
title: 'APM',
namedParams: { packageName: 'vim-mode' },
staticPreview: this.render({ version: '0.6.0' }),
keywords,
},
]
static render({ version }) {
return renderVersionBadge({ version })
}
async handle({ packageName }) {
const json = await this.fetch({ packageName })
const version = json.releases.latest
if (!version)
throw new InvalidResponse({
underlyingError: new Error('version is invalid'),
})
return this.constructor.render({ version })
}
}
class APMLicense extends BaseAPMService {
static category = 'license'
static route = { base: 'apm/l', pattern: ':packageName' }
static examples = [
{
title: 'APM',
namedParams: { packageName: 'vim-mode' },
staticPreview: this.render({ license: 'MIT' }),
keywords,
},
]
static defaultBadgeData = { label: 'license' }
static render({ license }) {
return renderLicenseBadge({ license })
}
async handle({ packageName }) {
const json = await this.fetch({ packageName })
const license = json.metadata.license
if (!license)
throw new InvalidResponse({
underlyingError: new Error('licence is invalid'),
})
return this.constructor.render({ license })
}
}
const APMLicense = deprecatedService({
category: 'license',
route: {
base: 'apm/l',
pattern: ':various*',
},
label: 'license',
dateAdded: new Date('2023-01-04'),
})
export { APMDownloads, APMVersion, APMLicense }

View File

@@ -1,57 +1,19 @@
import { ServiceTester } from '../tester.js'
import { invalidJSON } from '../response-fixtures.js'
import { isMetric, isVPlusTripleDottedVersion } from '../test-validators.js'
export const t = new ServiceTester({
id: 'apm',
title: 'Atom Package Manager',
pathPrefix: '/apm',
})
t.create('Downloads')
.get('/dm/vim-mode.json')
.expectBadge({ label: 'downloads', message: isMetric })
.expectBadge({ label: 'downloads', message: 'no longer available' })
t.create('Version')
.get('/v/vim-mode.json')
.expectBadge({ label: 'apm', message: isVPlusTripleDottedVersion })
.expectBadge({ label: 'apm', message: 'no longer available' })
t.create('License')
.get('/l/vim-mode.json')
.expectBadge({ label: 'license', message: 'MIT' })
t.create('Downloads | Package not found')
.get('/dm/notapackage.json')
.expectBadge({ label: 'downloads', message: 'package not found' })
t.create('Version | Package not found')
.get('/v/notapackage.json')
.expectBadge({ label: 'apm', message: 'package not found' })
t.create('License | Package not found')
.get('/l/notapackage.json')
.expectBadge({ label: 'license', message: 'package not found' })
t.create('Invalid version')
.get('/dm/vim-mode.json')
.intercept(nock =>
nock('https://atom.io')
.get('/api/packages/vim-mode')
.reply(200, '{"releases":{}}')
)
.expectBadge({ label: 'downloads', message: 'invalid response data' })
t.create('Invalid License')
.get('/l/vim-mode.json')
.intercept(nock =>
nock('https://atom.io')
.get('/api/packages/vim-mode')
.reply(200, '{"metadata":{}}')
)
.expectBadge({ label: 'license', message: 'invalid response data' })
t.create('Unexpected response')
.get('/dm/vim-mode.json')
.intercept(nock =>
nock('https://atom.io').get('/api/packages/vim-mode').reply(invalidJSON)
)
.expectBadge({ label: 'downloads', message: 'unparseable json response' })
.expectBadge({ label: 'license', message: 'no longer available' })

View File

@@ -1,6 +1,7 @@
import { ServiceTester } from '../../tester.js'
export const t = new ServiceTester({
id: 'GithubGistLastCommitRedirect',
id: 'GistLastCommitRedirect',
title: 'Github Gist Last Commit Redirect',
pathPrefix: '/github-gist',
})

View File

@@ -8,7 +8,7 @@ const schema = Joi.object({
updated_at: Joi.string().required(),
}).required()
export default class GithubGistLastCommit extends GithubAuthV3Service {
export default class GistLastCommit extends GithubAuthV3Service {
static category = 'activity'
static route = { base: 'github/gist/last-commit', pattern: ':gistId' }
static examples = [

View File

@@ -1,6 +1,6 @@
import { ServiceTester } from '../../tester.js'
export const t = new ServiceTester({
id: 'GithubGistStarsRedirect',
id: 'GistStarsRedirect',
title: 'Github Gist Stars Redirect',
pathPrefix: '/github',
})

View File

@@ -24,7 +24,7 @@ const documentation = `${commonDocumentation}
<p>This badge shows the number of stargazers for a gist. Gist id is accepted as input and 'gist not found' is returned if the gist is not found for the given gist id.
</p>`
export default class GithubGistStars extends GithubAuthV4Service {
export default class GistStars extends GithubAuthV4Service {
static category = 'social'
static route = {

View File

@@ -1,45 +1,11 @@
import { metric } from '../text-formatters.js'
import LgtmBaseService from './lgtm-base.js'
import { deprecatedService } from '../index.js'
export default class LgtmAlerts extends LgtmBaseService {
static route = {
export default deprecatedService({
category: 'analysis',
route: {
base: 'lgtm/alerts',
pattern: this.pattern,
}
static examples = [
{
title: 'LGTM Alerts',
namedParams: {
host: 'github',
user: 'apache',
repo: 'cloudstack',
},
staticPreview: this.render({ alerts: 2488 }),
},
]
static defaultBadgeData = {
label: 'lgtm alerts',
}
static getColor({ alerts }) {
let color = 'yellow'
if (alerts === 0) {
color = 'brightgreen'
}
return color
}
static render({ alerts }) {
return {
message: metric(alerts),
color: this.getColor({ alerts }),
}
}
async handle({ host, user, repo }) {
const { alerts } = await this.fetch({ host, user, repo })
return this.constructor.render({ alerts })
}
}
pattern: ':various*',
},
label: 'lgtm alerts',
dateAdded: new Date('2023-01-03'),
})

View File

@@ -1,61 +1,11 @@
import Joi from 'joi'
import { createServiceTester } from '../tester.js'
import { data } from './lgtm-test-helpers.js'
export const t = await createServiceTester()
import { ServiceTester } from '../tester.js'
t.create('alerts: total alerts for a project')
export const t = new ServiceTester({
id: 'lgtmAlerts',
title: 'LgtmAlerts',
pathPrefix: '/lgtm/alerts',
})
t.create('Lgtm')
.get('/github/apache/cloudstack.json')
.expectBadge({
label: 'lgtm alerts',
message: Joi.string().regex(/^[0-9kM.]+$/),
})
t.create('alerts: missing project')
.get('/github/some-org/this-project-doesnt-exist.json')
.expectBadge({
label: 'lgtm alerts',
message: 'project not found',
})
t.create('alerts: no alerts')
.get('/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, { alerts: 0, languages: data.languages })
)
.expectBadge({ label: 'lgtm alerts', message: '0' })
t.create('alerts: single alert')
.get('/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, { alerts: 1, languages: data.languages })
)
.expectBadge({ label: 'lgtm alerts', message: '1' })
t.create('alerts: multiple alerts')
.get('/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, { alerts: 123, languages: data.languages })
)
.expectBadge({ label: 'lgtm alerts', message: '123' })
t.create('alerts: json missing alerts')
.get('/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, {})
)
.expectBadge({ label: 'lgtm alerts', message: 'invalid response data' })
t.create('alerts: total alerts for a project with a github mapped host')
.get('/github/apache/cloudstack.json')
.expectBadge({
label: 'lgtm alerts',
message: Joi.string().regex(/^[0-9kM.]+$/),
})
.expectBadge({ label: 'lgtm alerts', message: 'no longer available' })

View File

@@ -1,42 +0,0 @@
import Joi from 'joi'
import { BaseJsonService } from '../index.js'
const schema = Joi.object({
alerts: Joi.number().required(),
languages: Joi.array()
.items(
Joi.object({
lang: Joi.string().required(),
grade: Joi.string(),
})
)
.required(),
}).required()
const hostMappings = {
github: 'g',
bitbucket: 'b',
gitlab: 'gl',
}
export default class LgtmBaseService extends BaseJsonService {
static category = 'analysis'
static defaultBadgeData = { label: 'lgtm' }
static pattern = `:host(${Object.keys(hostMappings).join('|')})/:user/:repo`
async fetch({ host, user, repo }) {
const mappedHost = hostMappings[host]
const url = `https://lgtm.com/api/v0.1/project/${mappedHost}/${user}/${repo}/details`
return this._requestJson({
schema,
url,
errorMessages: {
404: 'project not found',
},
})
}
}

View File

@@ -1,88 +1,11 @@
import LgtmBaseService from './lgtm-base.js'
import { deprecatedService } from '../index.js'
export default class LgtmGrade extends LgtmBaseService {
static route = {
export default deprecatedService({
category: 'analysis',
route: {
base: 'lgtm/grade',
pattern: `:language/${this.pattern}`,
}
static examples = [
{
title: 'LGTM Grade',
namedParams: {
language: 'java',
host: 'github',
user: 'apache',
repo: 'cloudstack',
},
staticPreview: this.render({
language: 'java',
data: {
languages: [
{
lang: 'java',
grade: 'C',
},
],
},
}),
},
]
static getLabel({ language }) {
const languageLabel = (() => {
switch (language) {
case 'cpp':
return 'c/c++'
case 'csharp':
return 'c#'
// Javascript analysis on LGTM also includes TypeScript
case 'javascript':
return 'js/ts'
default:
return language
}
})()
return languageLabel
}
static getGradeAndColor({ language, data }) {
let grade = 'no language data'
let color = 'red'
for (const languageData of data.languages) {
if (languageData.lang === language && 'grade' in languageData) {
// Pretty label for the language
grade = languageData.grade
// Pick colour based on grade
if (languageData.grade === 'A+') {
color = 'brightgreen'
} else if (languageData.grade === 'A') {
color = 'green'
} else if (languageData.grade === 'B') {
color = 'yellowgreen'
} else if (languageData.grade === 'C') {
color = 'yellow'
} else if (languageData.grade === 'D') {
color = 'orange'
}
}
}
return { grade, color }
}
static render({ language, data }) {
const { grade, color } = this.getGradeAndColor({ language, data })
return {
label: `code quality: ${this.getLabel({ language })}`,
message: grade,
color,
}
}
async handle({ language, host, user, repo }) {
const data = await this.fetch({ host, user, repo })
return this.constructor.render({ language, data })
}
}
pattern: ':various*',
},
label: 'lgtm grade',
dateAdded: new Date('2023-01-03'),
})

View File

@@ -1,106 +1,11 @@
import Joi from 'joi'
import { createServiceTester } from '../tester.js'
import { data } from './lgtm-test-helpers.js'
export const t = await createServiceTester()
import { ServiceTester } from '../tester.js'
t.create('grade: missing project')
.get('/java/github/some-org/this-project-doesnt-exist.json')
.expectBadge({
label: 'lgtm',
message: 'project not found',
})
export const t = new ServiceTester({
id: 'lgtmGrade',
title: 'LgtmGrade',
pathPrefix: '/lgtm/grade',
})
t.create('grade: json missing languages')
.get('/java/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, {})
)
.expectBadge({ label: 'lgtm', message: 'invalid response data' })
t.create('grade: grade for a project (java)')
.get('/java/github/apache/cloudstack.json')
.expectBadge({
label: 'code quality: java',
message: Joi.string().regex(/^(?:A\+)|A|B|C|D|E$/),
})
t.create('grade: grade for missing language')
.get('/foo/github/apache/cloudstack.json')
.expectBadge({
label: 'code quality: foo',
message: 'no language data',
})
t.create('grade: grade for a project with a mapped host')
.get('/java/github/apache/cloudstack.json')
.expectBadge({
label: 'code quality: java',
message: Joi.string().regex(/^(?:A\+)|A|B|C|D|E$/),
})
// Test display of languages
t.create('grade: cpp')
.get('/cpp/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data)
)
.expectBadge({ label: 'code quality: c/c++', message: 'A+' })
t.create('grade: javascript')
.get('/javascript/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data)
)
.expectBadge({ label: 'code quality: js/ts', message: 'A' })
t.create('grade: java')
.get('/java/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data)
)
.expectBadge({ label: 'code quality: java', message: 'B' })
t.create('grade: python')
.get('/python/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data)
)
.expectBadge({ label: 'code quality: python', message: 'C' })
t.create('grade: csharp')
.get('/csharp/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data)
)
.expectBadge({ label: 'code quality: c#', message: 'D' })
t.create('grade: other')
.get('/other/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data)
)
.expectBadge({ label: 'code quality: other', message: 'E' })
t.create('grade: foo (no grade for valid language)')
.get('/foo/github/apache/cloudstack.json')
.intercept(nock =>
nock('https://lgtm.com')
.get('/api/v0.1/project/g/apache/cloudstack/details')
.reply(200, data)
)
.expectBadge({ label: 'code quality: foo', message: 'no language data' })
t.create('Lgtm')
.get('/github/apache/cloudstack.json')
.expectBadge({ label: 'lgtm grade', message: 'no longer available' })

View File

@@ -7,9 +7,9 @@ export const t = new ServiceTester({
})
t.create('alerts')
.get('/alerts/g/badges/shields.svg')
.expectRedirect('/lgtm/alerts/github/badges/shields.svg')
.get('/alerts/g/badges/shields.json')
.expectBadge({ label: 'lgtm alerts', message: 'no longer available' })
t.create('grade')
.get('/grade/java/g/apache/cloudstack.svg')
.expectRedirect('/lgtm/grade/java/github/apache/cloudstack.svg')
.get('/grade/java/g/apache/cloudstack.json')
.expectBadge({ label: 'lgtm grade', message: 'no longer available' })

View File

@@ -1,14 +0,0 @@
const data = {
alerts: 0,
languages: [
{ lang: 'cpp', grade: 'A+' },
{ lang: 'javascript', grade: 'A' },
{ lang: 'java', grade: 'B' },
{ lang: 'python', grade: 'C' },
{ lang: 'csharp', grade: 'D' },
{ lang: 'other', grade: 'E' },
{ lang: 'foo' },
],
}
export { data }

View File

@@ -2,14 +2,23 @@
* Utilities relating to PHP version numbers. This compares version numbers
* using the algorithm followed by Composer (see
* https://getcomposer.org/doc/04-schema.md#version).
*
* @module
*/
import { fetch } from '../core/base-service/got.js'
import { getCachedResource } from '../core/base-service/resource-cache.js'
import { listCompare } from './version.js'
import { omitv } from './text-formatters.js'
// Return a negative value if v1 < v2,
// zero if v1 = v2, a positive value otherwise.
/**
* Return a negative value if v1 < v2,
* zero if v1 = v2, a positive value otherwise.
*
* @param {string} v1 - First version for comparison
* @param {string} v2 - Second version for comparison
* @returns {number} Comparison result (-1, 0 or 1)
*/
function asciiVersionCompare(v1, v2) {
if (v1 < v2) {
return -1
@@ -20,9 +29,14 @@ function asciiVersionCompare(v1, v2) {
}
}
// Take a version without the starting v.
// eg, '1.0.x-beta'
// Return { numbers: [1,0,something big], modifier: 2, modifierCount: 1 }
/**
* Take a version without the starting v.
* eg, '1.0.x-beta'
* Return { numbers: [1,0,something big], modifier: 2, modifierCount: 1 }
*
* @param {string} version - Version number string
* @returns {object} Object containing version details
*/
function numberedVersionData(version) {
// A version has a numbered part and a modifier part
// (eg, 1.0.0-patch, 2.0.x-dev).
@@ -96,7 +110,12 @@ function numberedVersionData(version) {
}
}
// Try to convert to a list of numbers.
/**
* Try to convert to a list of numbers.
*
* @param {string} s - Version number string
* @returns {number} Version number interger
*/
function toNum(s) {
let n = +s
if (Number.isNaN(n)) {
@@ -113,12 +132,15 @@ function numberedVersionData(version) {
}
}
// Return a negative value if v1 < v2,
// zero if v1 = v2,
// a positive value otherwise.
//
// See https://getcomposer.org/doc/04-schema.md#version
// and https://github.com/badges/shields/issues/319#issuecomment-74411045
/**
* Compares two versions and return an interger based on the result.
* See https://getcomposer.org/doc/04-schema.md#version
* and https://github.com/badges/shields/issues/319#issuecomment-74411045
*
* @param {string} v1 - First version
* @param {string} v2 - Second version
* @returns {number} Negative value if v1 < v2, zero if v1 = v2, else a positive value
*/
function compare(v1, v2) {
// Omit the starting `v`.
const rawv1 = omitv(v1)
@@ -154,6 +176,12 @@ function compare(v1, v2) {
return 0
}
/**
* Determines the latest version from a list of versions.
*
* @param {string[]} versions - List of versions
* @returns {string} Latest version
*/
function latest(versions) {
let latest = versions[0]
for (let i = 1; i < versions.length; i++) {
@@ -164,6 +192,12 @@ function latest(versions) {
return latest
}
/**
* Determines if a version is stable or not.
*
* @param {string} version - Version number
* @returns {boolean} true if version is stable, else false
*/
function isStable(version) {
const rawVersion = omitv(version)
let versionData
@@ -176,6 +210,12 @@ function isStable(version) {
return versionData.modifier === 3 || versionData.modifier === 4
}
/**
* Checks if a version is valid and returns the minor version.
*
* @param {string} version - Version number
* @returns {string} Minor version
*/
function minorVersion(version) {
const result = version.match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/)
@@ -186,6 +226,13 @@ function minorVersion(version) {
return `${result[1]}.${result[2] ? result[2] : '0'}`
}
/**
* Reduces the list of php versions that intersect with release versions to a version range (for eg. '5.4 - 7.1', '>= 5.5').
*
* @param {string[]} versions - List of php versions
* @param {string[]} phpReleases - List of php release versions
* @returns {string[]} Reduced Version Range (for eg. ['5.4 - 7.1'], ['>= 5.5'])
*/
function versionReduction(versions, phpReleases) {
if (!versions.length) {
return []
@@ -216,6 +263,13 @@ function versionReduction(versions, phpReleases) {
return versions
}
/**
* Fetches the PHP release versions from cache if exists, else fetch from the souce url and save in cache.
*
* @async
* @param {object} githubApiProvider - Github API provider
* @returns {Promise<*>} Promise that resolves to parsed response
*/
async function getPhpReleases(githubApiProvider) {
return getCachedResource({
url: '/repos/php/php-src/git/refs/tags',

View File

@@ -1,11 +1,28 @@
/**
* Common functions and utilities for tasks related to pipenv
*
* @module
*/
import Joi from 'joi'
import { InvalidParameter } from './index.js'
/**
* Joi schema for validating dependency.
*
* @type {Joi}
*/
const isDependency = Joi.object({
version: Joi.string(),
ref: Joi.string(),
}).required()
/**
* Joi schema for validating lock file object.
* Checks if the lock file object has required properties and the properties are valid.
*
* @type {Joi}
*/
const isLockfile = Joi.object({
_meta: Joi.object({
requires: Joi.object({
@@ -16,6 +33,18 @@ const isLockfile = Joi.object({
develop: Joi.object().pattern(Joi.string(), isDependency),
}).required()
/**
* Determines the dependency version based on the dependency type.
*
* @param {object} attrs - Refer to individual attributes
* @param {string} attrs.kind - Wanted dependency type ('dev' or 'default'), defaults to 'default'
* @param {string} attrs.wantedDependency - Name of the wanted dependency
* @param {object} attrs.lockfileData - Object containing lock file data
* @throws {Error} - Error if unknown dependency type provided
* @throws {InvalidParameter} - Error if wanted dependency is not present in lock file data
* @throws {InvalidParameter} - Error if version or ref is not present for the wanted dependency
* @returns {object} Object containing wanted dependency version or ref
*/
function getDependencyVersion({
kind = 'default',
wantedDependency,