diff --git a/.github/workflows/build-and-publish-reports.yml b/.github/workflows/build-and-publish-reports.yml index 6100fe2..a4e77cd 100644 --- a/.github/workflows/build-and-publish-reports.yml +++ b/.github/workflows/build-and-publish-reports.yml @@ -1,6 +1,16 @@ name: Build and publish compliance reports on: workflow_dispatch: + inputs: + type: + description: 'Emulate either schedule, push, or pull_request' + required: true + default: 'schedule' + type: choice + options: + - schedule + - push + - pull_request schedule: - cron: "0 0 * * 6" # Run at 00:00 on every saturday push: @@ -10,57 +20,137 @@ on: pull_request: branches: [ main ] jobs: - build-and-publish: - if: secrets.PINATA_API_ENDPOINT != null && secrets.PINATA_API_TOKEN != null && secrets.ESTUARY_API_ENDPOINT != null && secrets.ESTUARY_API_TOKEN != null && secrets.NFT_API_ENDPOINT != null && secrets.NFT_API_TOKEN != null && secrets.WEB3_API_ENDPOINT != null && secrets.PINATA_API_TOKEN != null - strategy: - fail-fast: false - matrix: - service: [pinata, estuary, nft, web3] - include: - - service: pinata - endpoint: ${{ secrets.PINATA_API_ENDPOINT }} - token: ${{secrets.PINATA_API_TOKEN}} - - service: estuary - endpoint: ${{ secrets.ESTUARY_API_ENDPOINT }} - token: ${{secrets.ESTUARY_API_TOKEN}} - - service: nft - endpoint: ${{ secrets.NFT_API_ENDPOINT }} - token: ${{secrets.NFT_API_TOKEN}} - - service: web3 - endpoint: ${{ secrets.WEB3_API_ENDPOINT }} - token: ${{secrets.WEB3_API_TOKEN}} + checkout: + runs-on: ubuntu-latest + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + - uses: ipfs/aegir/actions/cache-node-modules@master - concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession. + check-pinata-compliance: runs-on: ubuntu-latest + needs: [checkout] steps: - name: Checkout 🛎️ uses: actions/checkout@v3 + - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Reports Cache + uses: actions/cache@v3 + with: + path: docs + key: ${{ github.sha }}-pinata + - run: npm run dev-start -- -s ${{ secrets.PINATA_API_ENDPOINT }} ${{secrets.PINATA_API_TOKEN}} + - uses: actions/upload-artifact@v2 + with: + name: pinata-logs + path: docs/api.pinata.cloud + - uses: actions/upload-artifact@v2 + with: + name: pinata-report + path: docs/api.pinata.cloud.md - - name: Install dependencies - run: npm ci + check-estuary-compliance: + runs-on: ubuntu-latest + needs: [checkout] + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Reports Cache + uses: actions/cache@v3 + with: + path: docs + key: ${{ github.sha }}-estuary + - run: npm run dev-start -- -s ${{ secrets.ESTUARY_API_ENDPOINT }} ${{secrets.ESTUARY_API_TOKEN}} + - uses: actions/upload-artifact@v2 + with: + name: estuary-logs + path: docs/api.estuary.tech + - uses: actions/upload-artifact@v2 + with: + name: estuary-report + path: docs/api.estuary.tech.md - # Migrate reports to use npx - - name: Generate reports - run: npm dev-start -- -s ${{ secrets[matrix.endpoint] }} ${{ secrets[matrix.token] }} + check-nft-dot-storage-compliance: + runs-on: ubuntu-latest + needs: [checkout] + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Reports Cache + uses: actions/cache@v3 + with: + path: docs + key: ${{ github.sha }}-nft + - run: npm run dev-start -- -s ${{ secrets.NFT_API_ENDPOINT }} ${{secrets.NFT_API_TOKEN}} + - uses: actions/upload-artifact@v2 + with: + name: nft-logs + path: docs/nft.storage + - uses: actions/upload-artifact@v2 + with: + name: nft-report + path: docs/nft.storage.md - # Deploy to gh pages branch + check-web3-dot-storage-compliance: + runs-on: ubuntu-latest + needs: [checkout] + steps: + - name: Checkout 🛎️ + uses: actions/checkout@v3 + - uses: ipfs/aegir/actions/cache-node-modules@master + - name: Reports Cache + uses: actions/cache@v3 + with: + path: docs + key: ${{ github.sha }}-web3 + - run: npm run dev-start -- -s ${{ secrets.WEB3_API_ENDPOINT }} ${{secrets.WEB3_API_TOKEN}} + - uses: actions/upload-artifact@v2 + with: + name: web3-logs + path: docs/api.web3.storage + - uses: actions/upload-artifact@v2 + with: + name: web3-report + path: docs/api.web3.storage.md + + # Deploy to gh pages branch - ## Cron jobs + ## Cron jobs + deploy-from-schedule: + if: success() && (github.event.schedule != null || github.event.pusher != null || github.event.inputs.type == 'schedule' || github.event.inputs.type == 'push') + runs-on: ubuntu-latest + needs: [check-pinata-compliance, check-estuary-compliance, check-nft-dot-storage-compliance, check-web3-dot-storage-compliance] + concurrency: ci-${{ github.ref }} # Recommended if you intend to make multiple deployments in quick succession. + steps: + - name: Reports Cache + uses: actions/cache@v3 + with: + path: docs + key: ${{ github.sha }}-pinata + - name: Reports Cache + uses: actions/cache@v3 + with: + path: docs + key: ${{ github.sha }}-estuary + - name: Reports Cache + uses: actions/cache@v3 + with: + path: docs + key: ${{ github.sha }}-nft + - name: Reports Cache + uses: actions/cache@v3 + with: + path: docs + key: ${{ github.sha }}-web3 - name: Scheduled deployment - if: success() && (github.event.schedule != null || github.event.pusher != null) uses: s0/git-publish-subdir-action@399aab378450f99b7de6767f62b0d1dbfcb58b53 env: REPO: self BRANCH: gh-pages FOLDER: docs GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - ## PRs should push to branch other than primary `gh-pages` branch - - name: Deployment for PR - if: success() && github.event.pull_request != null - uses: s0/git-publish-subdir-action@399aab378450f99b7de6767f62b0d1dbfcb58b53 - env: - REPO: self - BRANCH: gh-pages-${github.sha} - FOLDER: docs - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SQUASH_HISTORY: false + MESSAGE: "Update published reports with changes from {sha} with message:\n{msg}" + SKIP_EMPTY_COMMITS: false diff --git a/package-lock.json b/package-lock.json index 90e1d62..a580a0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@hapi/joi": "^17.1.1", "@ipfs-shipyard/pinning-service-client": "^1.0.1", "fetch-ponyfill": "^7.1.0", + "git-rev": "^0.2.1", "go-ipfs": "^0.12.1", "ipfs": "^0.62.3", "ipfs-core": "^0.14.3", @@ -32,6 +33,7 @@ "pinning-service-compliance": "dist/src/index.js" }, "devDependencies": { + "@types/git-rev": "^0.2.0", "@types/hapi__joi": "^17.1.8", "@types/marked": "^4.0.3", "@types/marked-terminal": "^3.1.3", @@ -3380,6 +3382,12 @@ "@types/ms": "*" } }, + "node_modules/@types/git-rev": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@types/git-rev/-/git-rev-0.2.0.tgz", + "integrity": "sha512-BF+uvTxHCxPpf2BlArsIn8eHVTr7oUqq2oEA3F2Os6X9UGohHE6m7jKpHzsEqvQTQVOeBCyFMxjp7YvGk748ow==", + "dev": true + }, "node_modules/@types/hapi__joi": { "version": "17.1.8", "resolved": "https://registry.npmjs.org/@types/hapi__joi/-/hapi__joi-17.1.8.tgz", @@ -10854,6 +10862,11 @@ "xtend": "~4.0.1" } }, + "node_modules/git-rev": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/git-rev/-/git-rev-0.2.1.tgz", + "integrity": "sha512-p6OU8kZpeGHYqGpwnSD5/8IIERooiQp0p6On3T7ngcugnjhbmihvgMwCK2iun8ytn7FynsCPN+jRclR29hgOBg==" + }, "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", @@ -30138,6 +30151,12 @@ "@types/ms": "*" } }, + "@types/git-rev": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@types/git-rev/-/git-rev-0.2.0.tgz", + "integrity": "sha512-BF+uvTxHCxPpf2BlArsIn8eHVTr7oUqq2oEA3F2Os6X9UGohHE6m7jKpHzsEqvQTQVOeBCyFMxjp7YvGk748ow==", + "dev": true + }, "@types/hapi__joi": { "version": "17.1.8", "resolved": "https://registry.npmjs.org/@types/hapi__joi/-/hapi__joi-17.1.8.tgz", @@ -35713,6 +35732,11 @@ } } }, + "git-rev": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/git-rev/-/git-rev-0.2.1.tgz", + "integrity": "sha512-p6OU8kZpeGHYqGpwnSD5/8IIERooiQp0p6On3T7ngcugnjhbmihvgMwCK2iun8ytn7FynsCPN+jRclR29hgOBg==" + }, "glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", diff --git a/package.json b/package.json index a2785f9..d5714ce 100644 --- a/package.json +++ b/package.json @@ -126,6 +126,7 @@ "rebuild": "npm run clean && npm install && npm run build" }, "devDependencies": { + "@types/git-rev": "^0.2.0", "@types/hapi__joi": "^17.1.8", "@types/marked": "^4.0.3", "@types/marked-terminal": "^3.1.3", @@ -146,6 +147,7 @@ "@hapi/joi": "^17.1.1", "@ipfs-shipyard/pinning-service-client": "^1.0.1", "fetch-ponyfill": "^7.1.0", + "git-rev": "^0.2.1", "go-ipfs": "^0.12.1", "ipfs": "^0.62.3", "ipfs-core": "^0.14.3", diff --git a/src/ApiCall.ts b/src/ApiCall.ts index a6e72fd..1310af7 100644 --- a/src/ApiCall.ts +++ b/src/ApiCall.ts @@ -16,6 +16,7 @@ import { Icons } from './utils/constants.js' import { globalReport } from './utils/report.js' import { isError } from './guards/isError.js' import { getTextAndJson } from './utils/fetchSafe/getTextAndJson.js' +import { isResponse } from './guards/isResponse.js' interface ApiCallOptions { pair: ServiceAndTokenPair @@ -115,12 +116,19 @@ class ApiCall { this.result = result diff --git a/src/index.ts b/src/index.ts index c124d1a..c7b97c7 100755 --- a/src/index.ts +++ b/src/index.ts @@ -46,14 +46,10 @@ const main = async () => { } const getUncaughtListener = (type: 'unhandledRejection' | 'uncaughtException' | 'uncaughtExceptionMonitor'): NodeJS.UncaughtExceptionListener => (err, origin) => { - logger.error(type, { error: err }) + logger.error(type, err, origin) writeSync( process.stderr.fd, - `Caught exception: ${JSON.stringify(err, null, 2)}\n` - ) - writeSync( - process.stdout.fd, - `Caught exception: ${JSON.stringify(err, null, 2)}\n` + `Caught exception: ${JSON.stringify({ err, origin }, null, 2)}\n` ) } process.on('uncaughtExceptionMonitor', getUncaughtListener('uncaughtExceptionMonitor')) diff --git a/src/output/complianceCheckHeader.ts b/src/output/complianceCheckHeader.ts new file mode 100644 index 0000000..aad5259 --- /dev/null +++ b/src/output/complianceCheckHeader.ts @@ -0,0 +1,9 @@ +import { Icons } from '../utils/constants.js' + +interface ComplianceCheckHeaderProps { + title: string + successful: boolean +} +const complianceCheckHeader = ({ title, successful }: ComplianceCheckHeaderProps) => `${title} - ${successful ? `${Icons.SUCCESS} SUCCESS` : `${Icons.FAILURE} FAILED`}` + +export { complianceCheckHeader } diff --git a/src/output/getErrorsMarkdown.ts b/src/output/getErrorsMarkdown.ts new file mode 100644 index 0000000..8bfb430 --- /dev/null +++ b/src/output/getErrorsMarkdown.ts @@ -0,0 +1,19 @@ +import { inspect } from 'util' + +import { Icons } from '../utils/constants.js' + +const errorToMarkdown = (error: Error) => { + let errorOutput = '' + if (error.stack != null) { + errorOutput = ` + ${Icons.ERROR} ${error.stack}` + } else if (error.name != null && error.message != null) { + errorOutput = `${Icons.ERROR} ${error.name} - ${error.message}` + } else { + errorOutput = `${Icons.ERROR} ${inspect(error)}` + } + return errorOutput +} +const getErrorsMarkdown = (errors: Error[]) => errors.map(errorToMarkdown).join('\n') + +export { getErrorsMarkdown } diff --git a/src/output/getExpectationsMarkdown.ts b/src/output/getExpectationsMarkdown.ts index 22196fa..e4988c1 100644 --- a/src/output/getExpectationsMarkdown.ts +++ b/src/output/getExpectationsMarkdown.ts @@ -1,4 +1,5 @@ import type { ComplianceCheckDetails, PinsApiResponseTypes } from '../types.js' +import { Icons } from '../utils/constants.js' const getExpectationsMarkdown = (details: ComplianceCheckDetails): string => { let checks = 0 @@ -7,14 +8,14 @@ const getExpectationsMarkdown = (details: Compli checks++ if (success) { successes++ - return `✓ ${title} (success)` + return `${Icons.SUCCESS} ${title} (success)` } - return `✘ ${title} (failure)` + return `${Icons.FAILURE} ${title} (failure)` }) return `### Expectations (${successes}/${checks} successful) - ${lineItems.join('\n ')} + ${lineItems.join('\n\n ')} ` } diff --git a/src/output/getHeader.ts b/src/output/getHeader.ts index 5a04c3d..c166337 100644 --- a/src/output/getHeader.ts +++ b/src/output/getHeader.ts @@ -1,27 +1,64 @@ import type { ComplianceCheckDetails, PinsApiResponseTypes } from '../types.js' +import { Icons } from '../utils/constants.js' +import { getHostnameFromUrl } from '../utils/getHostnameFromUrl.js' +import { gitHash } from '../utils/gitHash.js' +import { logger } from '../utils/logs.js' +import { markdownLinkToTextLabel } from '../utils/markdownLinkToTextLabel.js' +import { complianceCheckHeader } from './complianceCheckHeader.js' +import { linkToCommit } from './linkToCommit.js' +import { linkToGithubRepo } from './linkToGithubRepo.js' +import { linkToHeading } from './linkToHeading.js' +import { linkToNpm } from './linkToNpm.js' type RequiredHeaderProps = Pick, 'title' | 'successful' | 'pair'> -const getHeader = (details: Array>) => { +interface HeaderOptions { + markdownLinks: boolean +} +const getHeader = async (details: Array>, options: HeaderOptions = { markdownLinks: true }) => { const endpointUrl = details[0].pair[0] + const useMarkdownLinks = options.markdownLinks + const hostname = getHostnameFromUrl(endpointUrl) let checks = 0 let successes = 0 + const dateString = (new Date()).toISOString() + let revisionString: string | null = null + + try { + const currentRevision = await gitHash() + + revisionString = useMarkdownLinks ? linkToCommit(currentRevision) : currentRevision + } catch (err) { + logger.error('Could not obtain latest git hash', err) + logger.info('No git repository, using npm version') + revisionString = useMarkdownLinks ? linkToNpm() : process.env.npm_package_version as string + } + const titles = details.map(({ title, successful }) => { checks++ + const titleLink = useMarkdownLinks ? linkToHeading(title, complianceCheckHeader({ title, successful })) : title if (successful) { successes++ - return `✓ ${title}` + return `${Icons.SUCCESS} ${titleLink}` } - return `✘ ${title}` + return `${Icons.FAILURE} ${titleLink}` }) + const reportHistory = linkToGithubRepo('Report History', `commits/gh-pages/${hostname}.md`) + return ` # ${endpointUrl} compliance: +Execution Date: ${dateString ?? '(Error getting date)'} + +Revision: ${revisionString ?? '(Error getting revision)'} + +${useMarkdownLinks ? reportHistory : markdownLinkToTextLabel(reportHistory)} + ## Summary (${successes}/${checks} successful) - ${titles.join('\n ')} + ${titles.join('\n\n ')} ` } diff --git a/src/output/getReportEntry.ts b/src/output/getReportEntry.ts index 7123552..f285576 100644 --- a/src/output/getReportEntry.ts +++ b/src/output/getReportEntry.ts @@ -1,40 +1,37 @@ - -import { inspect } from 'util' - import type { ComplianceCheckDetails, PinsApiResponseTypes } from '../types.js' -import { Icons } from '../utils/constants.js' import { stringifyHeaders } from '../utils/stringifyHeaders.js' +import { complianceCheckHeader } from './complianceCheckHeader.js' +import { getErrorsMarkdown } from './getErrorsMarkdown.js' import { getExpectationsMarkdown } from './getExpectationsMarkdown.js' import { joiValidationAsMarkdown } from './joiValidationAsMarkdown.js' const getReportEntry = (details: ComplianceCheckDetails): string => { - const { request, response, title, url, method, validationResult, result: clientParsedResult } = details - - const reportEntry = `## ${title} - ${details.successful ? `${Icons.SUCCESS} SUCCESS` : `${Icons.FAILURE} FAILED`} + const { request, response, title, url, method, validationResult, result: clientParsedResult, successful, errors } = details + const joiValidationMarkdown = joiValidationAsMarkdown(validationResult) + const reportEntry = `## ${complianceCheckHeader({ title, successful })} ${getExpectationsMarkdown(details)} -${details.errors.map((error) => { - let errorOutput = '' - if (error.name != null && error.message != null) { - errorOutput = `* ${error.name} - ${error.message}` - if (error.stack != null) { - errorOutput += ` - * ${error.stack}` - } - } else { - errorOutput = `* ${inspect(error)}` - } - return errorOutput -}).join('\n')} - -#### Joi validation failures -${joiValidationAsMarkdown(validationResult)} +${ + errors.length > 0 + ? `### Errors during run +${getErrorsMarkdown(errors)}` + : '' +} +${ + joiValidationMarkdown !== 'No failures' + ? `#### Response object doesn't match expected schema: +${joiValidationMarkdown} + ` + : '' +} ### Details -#### Request - ${method}: ${url} - +#### Request +\`\`\` +${method} ${url} +\`\`\` ##### Headers \`\`\`json ${stringifyHeaders(request.headers)} @@ -43,15 +40,11 @@ ${stringifyHeaders(request.headers)} \`\`\`json ${request.body} \`\`\` -#### Response data from ${url} -\`\`\` -${inspect(response.json, { depth: 4 })} -\`\`\` -#### Response data after being parsed by RemotePinningServiceClient + +#### Response \`\`\` -${inspect(clientParsedResult, { depth: 4 })} +${response.status} ${response.statusText} \`\`\` -#### Response - ${response.statusText} (${response.status}) ##### Headers \`\`\`json ${stringifyHeaders(response.headers)} @@ -60,6 +53,15 @@ ${stringifyHeaders(response.headers)} \`\`\`json ${response.body} \`\`\` + +##### Body (as JSON) +\`\`\`json +${JSON.stringify(response.json, null, 2)} +\`\`\` +##### Body (parsed by [pinning-service-client](https://www.npmjs.com/package/@ipfs-shipyard/pinning-service-client)) +\`\`\`json +${JSON.stringify(clientParsedResult, null, 2)} +\`\`\` ` return reportEntry diff --git a/src/output/linkToCommit.ts b/src/output/linkToCommit.ts new file mode 100644 index 0000000..fee0f8f --- /dev/null +++ b/src/output/linkToCommit.ts @@ -0,0 +1,5 @@ +import { sourceRepoUrl } from '../utils/constants.js' + +const linkToCommit = (revision: string) => `[${revision}](${sourceRepoUrl}/commit/${revision})` + +export { linkToCommit } diff --git a/src/output/linkToGithubRepo.ts b/src/output/linkToGithubRepo.ts new file mode 100644 index 0000000..884fe07 --- /dev/null +++ b/src/output/linkToGithubRepo.ts @@ -0,0 +1,5 @@ +import { sourceRepoUrl } from '../utils/constants.js' + +const linkToGithubRepo = (displayText: string, pathSuffix?: string) => `[${displayText}](${sourceRepoUrl}${pathSuffix != null ? `/${pathSuffix}` : ''})` + +export { linkToGithubRepo } diff --git a/src/output/linkToHeading.ts b/src/output/linkToHeading.ts new file mode 100644 index 0000000..94b6fd9 --- /dev/null +++ b/src/output/linkToHeading.ts @@ -0,0 +1,25 @@ +/** + * Create a markdown link to a heading on the same page (hash link) + * + * This function attempts to create a link that matches the generated links occurring automatically when github renders a markdown document. + * + * @example + * https://github.com/ipfs-shipyard/pinning-service-compliance/blob/f19db8c708b9a9ef72437db2510eaa45840d382c/nft.storage.md#can-create-and-then-delete-a-new-pin----success + * + * @param text - The text to use for the link + * @param headerText - The text of the header we want to link to + * + * @returns {string} A Markdown link to the header in the form of [text](converted_headerText) + */ +const linkToHeading = (text: string, headerText = text) => { + const link = headerText + .replace(/['=()/\\:,]/g, '') // remove invalid characters first + .replace(/[🟢]/gu, '') // remove invalid characters first + .replace(/[❌]/gu, '') // remove invalid characters first + .replace(/[^a-zA-Z0-9]/g, '-') // replace any remaining non-alphanumeric characters with hyphens + .toLowerCase() + + return `[${text}](#${link})` +} + +export { linkToHeading } diff --git a/src/output/linkToNpm.ts b/src/output/linkToNpm.ts new file mode 100644 index 0000000..f1dbc28 --- /dev/null +++ b/src/output/linkToNpm.ts @@ -0,0 +1,5 @@ + +const packageName = process.env.npm_package_name as string +const linkToNpm = (version = process.env.npm_package_version as string) => `[${version}](https://www.npmjs.com/package/${packageName}/v/${version})` + +export { linkToNpm } diff --git a/src/output/reporting.ts b/src/output/reporting.ts index 058d37a..4804eb5 100644 --- a/src/output/reporting.ts +++ b/src/output/reporting.ts @@ -112,11 +112,15 @@ const addApiCallToReport = async (apiCall: ApiCa const createReport = async (hostname: string, details: Array>) => { const reportFilePath = getReportFilePath(hostname) - const header = getHeader(details) + // Write console output first + const noMarkdownLinksHeader = await getHeader(details, { markdownLinks: false }) // Don't give npx users cluttered output. + const consoleHeader = `${noMarkdownLinksHeader.replace(/#+ /gm, '')}\nSee the full report at ${reportFilePath}` + logger.info(consoleHeader, { messageOnly: true }) + + // write file content + const header = await getHeader(details) const fileContents = await readFile(reportFilePath, 'utf8') await writeFile(reportFilePath, header + fileContents) - const consoleHeader = `${header.replace(/#+ /gm, '')}\nSee the full report at ${reportFilePath}` - logger.info(consoleHeader, { messageOnly: true }) } const writeHeaders = async () => { diff --git a/src/types.d.ts b/src/types.d.ts index 335f63d..31c5cae 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -94,6 +94,8 @@ interface ComplianceCheckResponse { body: string } +type Revision = string + interface ComplianceCheckDetails { pair: ServiceAndTokenPair errors: Error[] @@ -127,5 +129,6 @@ export type { PinningSpecJoiSchema, ServiceAndTokenPair, ImplementableMethods, - PinsApiResponseTypes + PinsApiResponseTypes, + Revision } diff --git a/src/utils/constants.ts b/src/utils/constants.ts index 324abd4..681dc53 100644 --- a/src/utils/constants.ts +++ b/src/utils/constants.ts @@ -16,10 +16,13 @@ const downloadDir = resolve(_dirname, '..', '..', 'downloaded') const generatedDir = resolve(_dirname, '..', '..', 'generated') const docsDir = resolve(_dirname, '..', '..', 'docs') +const publishedReportsUrl = 'https://ipfs-shipyard.github.io/pinning-service-compliance' +const sourceRepoUrl = 'https://github.com/ipfs-shipyard/pinning-service-compliance' + enum Icons { - SUCCESS = '✓', - FAILURE = '✘', - ERROR = '⚠' + SUCCESS = '🟢', + FAILURE = '❌', + ERROR = '⚠️' } export { @@ -30,5 +33,7 @@ export { specFile, specLocation, specVersion, - Icons + Icons, + publishedReportsUrl, + sourceRepoUrl } diff --git a/src/utils/fetchSafe/getText.ts b/src/utils/fetchSafe/getText.ts index 332cdd0..089b183 100644 --- a/src/utils/fetchSafe/getText.ts +++ b/src/utils/fetchSafe/getText.ts @@ -9,7 +9,13 @@ const handleLargeRequests = async () => await sleep(TIMEOUT_SECONDS * 1000).then const getText = async (response: ApiCall['response']): Promise => { const actualTextPromise = new Promise((resolve, reject) => { response.clone().text().then((result) => { - resolve(result) + try { + const obj = JSON.parse(result) + const text = JSON.stringify(obj, null, 2) + resolve(text) + } catch { + resolve(result) + } }, (error) => { reject(error) }) diff --git a/src/utils/getQueue.ts b/src/utils/getQueue.ts index c13c095..30ad33c 100644 --- a/src/utils/getQueue.ts +++ b/src/utils/getQueue.ts @@ -1,18 +1,13 @@ -// import { TokenBucketLimiter } from '@dutu/rate-limiter' import PQueue from 'p-queue' type QueueInstance = InstanceType const queues: Map = new Map() -// const getQueue = (endpointUrl: string, options: ConstructorParameters[0] = { concurrency: 1, intervalCap: 1, interval: 1000 }): QueueInstance => { const getQueue = (endpointUrl: string, options: ConstructorParameters[0] = { concurrency: 1, intervalCap: 1, interval: 1000 }): PQueue => { if (queues.has(endpointUrl)) { return queues.get(endpointUrl) as QueueInstance } - let newQueueLimiter = new PQueue(options) - if (endpointUrl.includes('pinata')) { - newQueueLimiter = new PQueue({ ...options, interval: 5000 }) - } + const newQueueLimiter = new PQueue(options) queues.set(endpointUrl, newQueueLimiter) diff --git a/src/utils/gitHash.ts b/src/utils/gitHash.ts new file mode 100644 index 0000000..f985601 --- /dev/null +++ b/src/utils/gitHash.ts @@ -0,0 +1,36 @@ +import git from 'git-rev' + +import type { Revision } from '../types.js' + +/** + * Provides the git hash of HEAD by default, in short format. + * + * This function will not work when called from npx, because the npm package doesn't come with the git repo. Please + * catch any errors and display npm package version instead. + * + * @param {number} [fromHead=0] - How many commits from the head you want the hash of + * + * @returns {Revision} The hash of the requested commit + */ +const gitHash = async (fromHead = 0): Promise => { + return await new Promise((resolve, reject) => { + try { + if (fromHead === 0) { + git.short((commitHash) => resolve(commitHash)) + } else { + // @see https://www.npmjs.com/package/git-rev#logfunction-array--- + git.log((log) => { + try { + resolve(log[fromHead][0].slice(0, 7)) + } catch (err) { + reject(err) + } + }) + } + } catch (err) { + reject(err) + } + }) +} + +export { gitHash } diff --git a/src/utils/markdownLinkToTextLabel.ts b/src/utils/markdownLinkToTextLabel.ts new file mode 100644 index 0000000..9e9d1d1 --- /dev/null +++ b/src/utils/markdownLinkToTextLabel.ts @@ -0,0 +1,9 @@ +/** + * @example + * var foo = '[Report History](https://github.com/ipfs-shipyard/pinning-service-compliance/commits/gh-pages/api.pinata.cloud.md)' + * var bar = markdownLinkToTextLabel(foo) + * // bar = 'Report History: https://github.com/ipfs-shipyard/pinning-service-compliance/commits/gh-pages/api.pinata.cloud.md' + */ +const markdownLinkToTextLabel = (markdownLink: string) => markdownLink.replace(/\[([^\]]+)\]\((.+)\)/, '$1: $2') + +export { markdownLinkToTextLabel }