From e26feabed6d62d1726987c1e2c2e654ce451f13f Mon Sep 17 00:00:00 2001 From: slugb0t Date: Thu, 15 Aug 2024 13:53:32 -0700 Subject: [PATCH 1/6] feat: :sparkles: license_status key added to db --- bot/utils/license/index.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/bot/utils/license/index.js b/bot/utils/license/index.js index e275f62b..596d7a7b 100644 --- a/bot/utils/license/index.js +++ b/bot/utils/license/index.js @@ -186,6 +186,7 @@ export async function applyLicenseTemplate( const newDate = Date.now(); await licenseCollection.insertOne({ contains_license: false, + license_status: "", created_at: newDate, identifier, open: true, @@ -199,7 +200,13 @@ export async function applyLicenseTemplate( // Update the database await licenseCollection.updateOne( { repositoryId: repository.id }, - { $set: { contains_license: false, updated_at: Date.now() } }, + { + $set: { + contains_license: false, + updated_at: Date.now(), + license_status: "", + }, + }, ); url = `${CODEFAIR_DOMAIN}/add/license/${existingLicense.identifier}`; } @@ -225,6 +232,8 @@ export async function applyLicenseTemplate( licenseId = null; licenseContent = null; } + const licenseContentNotEmpty = + licenseContent && licenseContent.trim().length > 0; // License file found text const identifier = createId(); @@ -239,6 +248,7 @@ export async function applyLicenseTemplate( const newDate = Date.now(); await licenseCollection.insertOne({ contains_license: true, + license_status: licenseContentNotEmpty ? "valid" : "invalid", created_at: newDate, identifier, licenseContent, @@ -257,7 +267,7 @@ export async function applyLicenseTemplate( { $set: { contains_license: true, - licenseContent, + license_status: licenseContentNotEmpty ? "valid" : "invalid", licenseId, updated_at: Date.now(), }, From 40a6b4e374defd8de69015ae23ff11ef68d0adbb Mon Sep 17 00:00:00 2001 From: slugb0t Date: Thu, 15 Aug 2024 14:21:45 -0700 Subject: [PATCH 2/6] refactor: :recycle: better variable name and removed unused fns --- bot/utils/renderer/index.js | 51 +++++++------------------------------ 1 file changed, 9 insertions(+), 42 deletions(-) diff --git a/bot/utils/renderer/index.js b/bot/utils/renderer/index.js index 8ff1ce16..ad653596 100644 --- a/bot/utils/renderer/index.js +++ b/bot/utils/renderer/index.js @@ -1,45 +1,12 @@ -import url from "url"; import { consola } from "consola"; import { applyGitHubIssueToDatabase, createId } from "../tools/index.js"; import { applyCWLTemplate } from "../cwl/index.js"; import { applyMetadataTemplate } from "../metadata/index.js"; import { applyLicenseTemplate } from "../license/index.js"; -const GITHUB_APP_NAME = process.env.GITHUB_APP_NAME; +const { GITHUB_APP_NAME } = process.env; const CODEFAIR_DOMAIN = process.env.CODEFAIR_APP_DOMAIN; -/** - * * Removes the token from the URL in the validation message - * @param {String} inputString - The string to remove the token from - * @returns {String} - The string with the token removed - */ -function removeTokenFromUrlInString(inputString) { - // Regex to find the GitHub raw URL with an optional token - const urlRegex = - /https:\/\/raw\.githubusercontent\.com\/[^\s:]+(\?token=[^:\s]+)?/g; - - // Replace each found URL in the string after removing the token - return inputString.replace(urlRegex, (url) => { - return url.replace(/\?token=[^:]+/, ""); - }); -} - -/** - * * Removes the token from the URL in the validation message - * @param {String} inputString - The string to remove the token from - * @returns {String} - The string with the token removed - */ -function removeTimestampFromUrlInString(inputString) { - // Regex to find the GitHub raw URL with an optional token - const urlRegex = - /https:\/\/raw\.githubusercontent\.com\/[^\s:]+(\?token=[^:\s]+)?/g; - - // Replace each found URL in the string after removing the token - return inputString.replace(urlRegex, (url) => { - return url.replace(/\?timestamp=[^:]+/, ""); - }); -} - /** * * Renders the body of the dashboard issue message * @@ -204,7 +171,7 @@ export async function applyCodemetaTemplate( // License was found but no codemeta.json exists const identifier = createId(); - let url = `${CODEFAIR_DOMAIN}/add/codemeta/${identifier}`; + let badgeURL = `${CODEFAIR_DOMAIN}/add/codemeta/${identifier}`; const codemetaCollection = db.collection("codeMetadata"); const existingCodemeta = await codemetaCollection.findOne({ @@ -229,10 +196,10 @@ export async function applyCodemetaTemplate( { repositoryId: repository.id }, { $set: { updated_at: Date.now() } }, ); - url = `${CODEFAIR_DOMAIN}/add/codemeta/${existingCodemeta.identifier}`; + badgeURL = `${CODEFAIR_DOMAIN}/add/codemeta/${existingCodemeta.identifier}`; } - const codemetaBadge = `[![Citation](https://img.shields.io/badge/Add_Codemeta-dc2626.svg)](${url})`; + const codemetaBadge = `[![Citation](https://img.shields.io/badge/Add_Codemeta-dc2626.svg)](${badgeURL})`; baseTemplate += `\n\n## codemeta.json\n\nA codemeta.json file was not found in the repository. To make your software reusable a codemetada.json is expected at the root level of your repository, as recommended in the [FAIR-BioRS Guidelines](https://fair-biors.org).\n\n${codemetaBadge}`; } else if (subjects.codemeta && subjects.license) { // License was found and codemetata.json also exists @@ -257,9 +224,9 @@ export async function applyCodemetaTemplate( { repositoryId: repository.id }, { $set: { updated_at: Date.now() } }, ); - url = `${CODEFAIR_DOMAIN}/add/license/${existingLicense.identifier}`; + badgeURL = `${CODEFAIR_DOMAIN}/add/license/${existingLicense.identifier}`; } - const codemetaBadge = `[![Citation](https://img.shields.io/badge/Edit_Codemeta-dc2626.svg)](${url})`; + const codemetaBadge = `[![Citation](https://img.shields.io/badge/Edit_Codemeta-dc2626.svg)](${badgeURL})`; baseTemplate += `\n\n## codemeta.json\n\nA codemeta.json file found in the repository.\n\n${codemetaBadge}`; } else { // codemeta and license does not exist @@ -292,7 +259,7 @@ export async function applyCitationTemplate( // License was found but no citation file was found const identifier = createId(); - let url = `${CODEFAIR_DOMAIN}/add/citation/${identifier}`; + let badgeURL = `${CODEFAIR_DOMAIN}/add/citation/${identifier}`; const citationCollection = db.collection("citationRequests"); const existingCitation = await citationCollection.findOne({ repositoryId: repository.id, @@ -316,10 +283,10 @@ export async function applyCitationTemplate( { repositoryId: repository.id }, { $set: { updated_at: Date.now() } }, ); - url = `${CODEFAIR_DOMAIN}/add/citation/${existingCitation.identifier}`; + badgeURL = `${CODEFAIR_DOMAIN}/add/citation/${existingCitation.identifier}`; } - const citationBadge = `[![Citation](https://img.shields.io/badge/Add_Citation-dc2626.svg)](${url})`; + const citationBadge = `[![Citation](https://img.shields.io/badge/Add_Citation-dc2626.svg)](${badgeURL})`; baseTemplate += `\n\n## CITATION.cff\n\nA CITATION.cff file was not found in the repository. The [FAIR-BioRS guidelines](https://fair-biors.org/docs/guidelines) suggests to include that file for providing metadata about your software and make it FAIR.\n\n${citationBadge}`; } else if (subjects.citation && subjects.license) { // Citation file was found and license was found From 0147f10d643de40d5b4822900147a0d3b832484e Mon Sep 17 00:00:00 2001 From: slugb0t Date: Thu, 15 Aug 2024 14:23:07 -0700 Subject: [PATCH 3/6] logs: :loud_sound: better logging on bot side --- bot/utils/tools/index.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bot/utils/tools/index.js b/bot/utils/tools/index.js index 65791547..54817297 100644 --- a/bot/utils/tools/index.js +++ b/bot/utils/tools/index.js @@ -398,6 +398,9 @@ export async function isRepoPrivate(context, owner, repoName) { repo: repoName, }); + consola.info( + `Repository ${repoName} is private: ${repoDetails.data.private}`, + ); return repoDetails.data.private; } catch (error) { consola.error("Error verifying if the repository is private:", error); @@ -421,7 +424,6 @@ export async function applyGitHubIssueToDatabase(issueNumber, repoId) { }, }, ); - consola.info("Issue number appended to database"); } /** From 7664b37ddc77b296825e1df8fdac3d89ff94d708 Mon Sep 17 00:00:00 2001 From: slugb0t Date: Thu, 15 Aug 2024 15:26:24 -0700 Subject: [PATCH 4/6] feat: :sparkles: last updated info applied to issue dashboard + rerun full repo validation command listener added --- bot/index.js | 52 ++++++++++++++++++++++++--- bot/package.json | 1 + bot/utils/renderer/index.js | 8 ++++- bot/utils/tools/index.js | 18 ++++++++++ pnpm-lock.yaml | 71 ++++++++++++++++++------------------- 5 files changed, 109 insertions(+), 41 deletions(-) diff --git a/bot/index.js b/bot/index.js index 43dbf8eb..8a9c7909 100644 --- a/bot/index.js +++ b/bot/index.js @@ -480,6 +480,8 @@ export default async (app, { getRouter }) => { app.on("issues.edited", async (context) => { const issueBody = context.payload.issue.body; const issueTitle = context.payload.issue.title; + const { repository } = context.payload; + const owner = context.payload.repository.owner.login; if (issueTitle === ISSUE_TITLE) { const installationCollection = db.collection("installation"); @@ -523,8 +525,6 @@ export default async (app, { getRouter }) => { issueTitle === ISSUE_TITLE ) { consola.start("Rerunning CWL Validation..."); - const owner = context.payload.repository.owner.login; - const repository = context.payload.repository; const cwl = await getCWLFiles(context, owner, repository.name); @@ -571,11 +571,55 @@ export default async (app, { getRouter }) => { consola.success("CWL Validation rerun successfully!"); } + + if ( + issueBody.includes("") && + issueTitle === ISSUE_TITLE + ) { + consola.start("Rerunning full repository validation..."); + + const license = await checkForLicense(context, owner, repository.name); + const citation = await checkForCitation(context, owner, repository.name); + const codemeta = await checkForCodeMeta(context, owner, repository.name); + const cwl = await getCWLFiles(context, owner, repository.name); + + const cwlObject = { + contains_cwl: cwl.length > 0 || false, + files: cwl, + removed_files: [], + }; + + // If existing cwl validation exists, update the contains_cwl value + const cwlExists = await db.collection("cwlValidation").findOne({ + repositoryId: repository.id, + }); + + if (cwlExists?.contains_cwl_files) { + cwlObject.contains_cwl = cwlExists.contains_cwl_files; + } + + const subjects = { + citation, + codemeta, + cwl: cwlObject, + license, + }; + + const issueBody = await renderIssues( + context, + owner, + repository, + false, + subjects, + ); + + await createIssue(context, owner, repository, ISSUE_TITLE, issueBody); + } }); // When an issue is deleted or closed app.on(["issues.deleted", "issues.closed"], async (context) => { - const repository = context.payload.repository; + const { repository } = context.payload; const issueTitle = context.payload.issue.title; if (issueTitle === ISSUE_TITLE) { @@ -607,7 +651,7 @@ export default async (app, { getRouter }) => { // When an issue is reopened app.on("issues.reopened", async (context) => { - const repository = context.payload.repository; + const { repository } = context.payload; const owner = context.payload.repository.owner.login; const issueTitle = context.payload.issue.title; diff --git a/bot/package.json b/bot/package.json index 8f0b6eca..6c5bbcec 100644 --- a/bot/package.json +++ b/bot/package.json @@ -28,6 +28,7 @@ "axios": "^1.6.8", "consola": "^3.2.3", "cwl-ts-auto": "^0.1.3", + "dayjs": "^1.11.11", "humanparser": "^2.7.0", "js-yaml": "^4.1.0", "mongodb": "^6.5.0", diff --git a/bot/utils/renderer/index.js b/bot/utils/renderer/index.js index ad653596..a8362154 100644 --- a/bot/utils/renderer/index.js +++ b/bot/utils/renderer/index.js @@ -1,5 +1,9 @@ import { consola } from "consola"; -import { applyGitHubIssueToDatabase, createId } from "../tools/index.js"; +import { + applyGitHubIssueToDatabase, + createId, + applyLastModifiedTemplate, +} from "../tools/index.js"; import { applyCWLTemplate } from "../cwl/index.js"; import { applyMetadataTemplate } from "../metadata/index.js"; import { applyLicenseTemplate } from "../license/index.js"; @@ -72,6 +76,8 @@ export async function renderIssues( context, ); + baseTemplate = applyLastModifiedTemplate(baseTemplate); + return baseTemplate; } diff --git a/bot/utils/tools/index.js b/bot/utils/tools/index.js index 54817297..94cdd1ab 100644 --- a/bot/utils/tools/index.js +++ b/bot/utils/tools/index.js @@ -5,6 +5,12 @@ import { consola } from "consola"; import { init } from "@paralleldrive/cuid2"; import human from "humanparser"; import dbInstance from "../../db.js"; +import dayjs from "dayjs"; +import timezone from "dayjs/plugin/timezone.js"; +import utc from "dayjs/plugin/utc.js"; + +dayjs.extend(utc); +dayjs.extend(timezone); /** * * Initialize the database connection @@ -456,3 +462,15 @@ export function replaceRawGithubUrl(inputString, oldUrl, newUrl) { return [modifiedString, firstLineNumber, secondLineNumber]; } + +export function applyLastModifiedTemplate(baseTemplate) { + const lastModified = dayjs() + .tz("America/Los_Angeles") + .format("MMM D YYYY, HH:mm:ss"); + + consola.info( + `GitHub Issue updated at: ${lastModified} (timezone: America/Los_Angeles)`, + ); + + return `${baseTemplate}\n\nLast updated ${lastModified} (timezone: America/Los_Angeles)`; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a0824df5..1fc61c6c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: cwl-ts-auto: specifier: ^0.1.3 version: 0.1.3 + dayjs: + specifier: ^1.11.11 + version: 1.11.11 humanparser: specifier: ^2.7.0 version: 2.7.0 @@ -7079,7 +7082,7 @@ snapshots: '@babel/traverse': 7.24.7 '@babel/types': 7.24.7 convert-source-map: 2.0.0 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -7358,7 +7361,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.24.7 '@babel/types': 7.24.7 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -7818,7 +7821,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) espree: 9.6.1 globals: 13.24.0 ignore: 5.3.1 @@ -7838,7 +7841,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -7874,7 +7877,7 @@ snapshots: '@antfu/install-pkg': 0.1.1 '@antfu/utils': 0.7.10 '@iconify/types': 2.0.0 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) kolorist: 1.8.0 local-pkg: 0.5.0 mlly: 1.7.1 @@ -8095,7 +8098,7 @@ snapshots: '@koa/router@12.0.1': dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) http-errors: 2.0.0 koa-compose: 4.1.0 methods: 1.1.2 @@ -8105,7 +8108,7 @@ snapshots: '@kwsites/file-exists@1.1.1': dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -8711,7 +8714,7 @@ snapshots: '@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-vue: 9.27.0(eslint@8.57.0) transitivePeerDependencies: - eslint-import-resolver-node @@ -8722,8 +8725,8 @@ snapshots: '@nuxtjs/eslint-config@12.0.0(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)': dependencies: eslint: 8.57.0 - eslint-config-standard: 17.1.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0))(eslint-plugin-n@15.7.0(eslint@8.57.0))(eslint-plugin-promise@6.4.0(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-config-standard: 17.1.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint-plugin-n@15.7.0(eslint@8.57.0))(eslint-plugin-promise@6.4.0(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-n: 15.7.0(eslint@8.57.0) eslint-plugin-node: 11.1.0(eslint@8.57.0) eslint-plugin-promise: 6.4.0(eslint@8.57.0) @@ -9565,7 +9568,7 @@ snapshots: '@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.4) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -9583,7 +9586,7 @@ snapshots: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.4) '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) eslint: 8.57.0 optionalDependencies: typescript: 5.5.4 @@ -9599,7 +9602,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 6.21.0(typescript@5.5.4) '@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.5.4) - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.5.4) optionalDependencies: @@ -9613,7 +9616,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.21.0 '@typescript-eslint/visitor-keys': 6.21.0 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 @@ -10178,7 +10181,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -10959,10 +10962,6 @@ snapshots: dependencies: ms: 2.1.2 - debug@4.3.5: - dependencies: - ms: 2.1.2 - debug@4.3.5(supports-color@5.5.0): dependencies: ms: 2.1.2 @@ -11256,10 +11255,10 @@ snapshots: dependencies: eslint: 8.57.0 - eslint-config-standard@17.1.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0))(eslint-plugin-n@15.7.0(eslint@8.57.0))(eslint-plugin-promise@6.4.0(eslint@8.57.0))(eslint@8.57.0): + eslint-config-standard@17.1.0(eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0))(eslint-plugin-n@15.7.0(eslint@8.57.0))(eslint-plugin-promise@6.4.0(eslint@8.57.0))(eslint@8.57.0): dependencies: eslint: 8.57.0 - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) eslint-plugin-n: 15.7.0(eslint@8.57.0) eslint-plugin-promise: 6.4.0(eslint@8.57.0) @@ -11273,11 +11272,11 @@ snapshots: eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) enhanced-resolve: 5.17.0 eslint: 8.57.0 eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.14.0 @@ -11311,7 +11310,7 @@ snapshots: eslint-utils: 2.1.0 regexpp: 3.2.0 - eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -11461,7 +11460,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -12031,7 +12030,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -12112,7 +12111,7 @@ snapshots: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -12294,7 +12293,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -12721,7 +12720,7 @@ snapshots: koa-send@5.0.1: dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) http-errors: 1.8.1 resolve-path: 1.4.0 transitivePeerDependencies: @@ -12741,7 +12740,7 @@ snapshots: content-disposition: 0.5.4 content-type: 1.0.5 cookies: 0.9.1 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) delegates: 1.0.0 depd: 2.0.0 destroy: 1.2.0 @@ -13113,7 +13112,7 @@ snapshots: mquery@5.0.0: dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -13261,7 +13260,7 @@ snapshots: nock@13.5.4: dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) json-stringify-safe: 5.0.1 propagate: 2.0.1 transitivePeerDependencies: @@ -14467,7 +14466,7 @@ snapshots: dependencies: '@kwsites/file-exists': 1.1.1 '@kwsites/promise-deferred': 1.1.1 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -15170,7 +15169,7 @@ snapshots: vite-node@1.6.0(@types/node@20.14.10)(terser@5.31.1): dependencies: cac: 6.7.14 - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) pathe: 1.1.2 picocolors: 1.0.1 vite: 5.3.3(@types/node@20.14.10)(terser@5.31.1) @@ -15211,7 +15210,7 @@ snapshots: dependencies: '@antfu/utils': 0.7.10 '@rollup/pluginutils': 5.1.0(rollup@4.18.0) - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) error-stack-parser-es: 0.1.4 fs-extra: 11.2.0 open: 10.1.0 @@ -15290,7 +15289,7 @@ snapshots: vue-eslint-parser@9.4.3(eslint@8.57.0): dependencies: - debug: 4.3.5 + debug: 4.3.5(supports-color@5.5.0) eslint: 8.57.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 From 4dabec90179b65f8981994a3340dd9449761020e Mon Sep 17 00:00:00 2001 From: slugb0t Date: Fri, 16 Aug 2024 11:40:42 -0700 Subject: [PATCH 5/6] refactor: :recycle: improved workflow for license template renderer --- bot/utils/license/index.js | 151 +++++++++++++++++-------------------- 1 file changed, 69 insertions(+), 82 deletions(-) diff --git a/bot/utils/license/index.js b/bot/utils/license/index.js index 596d7a7b..8b07b8d0 100644 --- a/bot/utils/license/index.js +++ b/bot/utils/license/index.js @@ -173,110 +173,97 @@ export async function applyLicenseTemplate( owner, context, ) { - if (!subjects.license) { - const identifier = createId(); - let url = `${CODEFAIR_DOMAIN}/add/license/${identifier}`; - const licenseCollection = dbInstance.getDb().collection("licenseRequests"); - const existingLicense = await licenseCollection.findOne({ - repositoryId: repository.id, - }); + const licenseCollection = dbInstance.getDb().collection("licenseRequests"); + const identifier = createId(); + let badgeURL = `${CODEFAIR_DOMAIN}/add/license/${identifier}`; + const existingLicense = await licenseCollection.findOne({ + repositoryId: repository.id, + }); + let licenseId = null; + let licenseContent = null; + let licenseContentNotEmpty = null; - if (!existingLicense) { - // Entry does not exist in db, create a new one - const newDate = Date.now(); - await licenseCollection.insertOne({ - contains_license: false, - license_status: "", - created_at: newDate, - identifier, - open: true, - owner, - repo: repository.name, - repositoryId: repository.id, - updated_at: newDate, - }); - } else { - // Get the identifier of the existing license request - // Update the database - await licenseCollection.updateOne( - { repositoryId: repository.id }, - { - $set: { - contains_license: false, - updated_at: Date.now(), - license_status: "", - }, - }, - ); - url = `${CODEFAIR_DOMAIN}/add/license/${existingLicense.identifier}`; - } - // No license file found text - const licenseBadge = `[![License](https://img.shields.io/badge/Add_License-dc2626.svg)](${url})`; - baseTemplate += `## LICENSE ❌\n\nTo make your software reusable a license file is expected at the root level of your repository, as recommended in the [FAIR-BioRS Guidelines](https://fair-biors.org). If you would like Codefair to add a license file, click the "Add license" button below to go to our interface for selecting and adding a license. You can also add a license file yourself and Codefair will update the the dashboard when it detects it on the main branch.\n\n${licenseBadge}`; - } else { + if (subjects.license) { // Get the license identifier const licenseRequest = await context.octokit.rest.licenses.getForRepo({ owner, repo: repository.name, }); - let licenseId = licenseRequest.data.license.spdx_id; - let licenseContent = Buffer.from( + licenseId = licenseRequest.data.license.spdx_id; + licenseContent = Buffer.from( licenseRequest.data.content, "base64", ).toString("utf-8"); + if ( licenseRequest.data.license.spdx_id === "no-license" || licenseRequest.data.license.spdx_id === "NOASSERTION" ) { + consola.info("Resetting license id and content back to null"); licenseId = null; licenseContent = null; } - const licenseContentNotEmpty = - licenseContent && licenseContent.trim().length > 0; - // License file found text - const identifier = createId(); - let url = `${CODEFAIR_DOMAIN}/add/license/${identifier}`; - const licenseCollection = dbInstance.getDb().collection("licenseRequests"); - const existingLicense = await licenseCollection.findOne({ + licenseContentNotEmpty = licenseContent && licenseContent.trim().length > 0; + } + + if (existingLicense) { + // Determine if the existing license is still valid + const isExistingLicenseValid = + existingLicense?.licenseContent && existingLicense.licenseId; + + // Use the new license data if the existing license is invalid or the license has changed + const finalLicenseId = + isExistingLicenseValid && licenseId === null && !subjects.license + ? existingLicense?.licenseId + : licenseId; + const finalLicenseContent = + isExistingLicenseValid && licenseContent === null && !subjects.license + ? existingLicense?.licenseContent + : licenseContent; + + badgeURL = `${CODEFAIR_DOMAIN}/add/license/${existingLicense.identifier}`; + await licenseCollection.updateOne( + { repositoryId: repository.id }, + { + $set: { + contains_license: subjects.license, + updated_at: Date.now(), + license_status: + finalLicenseContent && + finalLicenseContent.trim().length > 0 && + subjects.license + ? "valid" + : "invalid", + licenseId: finalLicenseId, + licenseContent: finalLicenseContent, + }, + }, + ); + } else { + const newDate = Date.now(); + await licenseCollection.insertOne({ + contains_license: subjects.license, + license_status: licenseContentNotEmpty ? "valid" : "invalid", + created_at: newDate, + licenseId, + licenseContent, + identifier, + open: true, + owner, + repo: repository.name, repositoryId: repository.id, + updated_at: newDate, }); + } - if (!existingLicense) { - // Entry does not exist in db, create a new one - const newDate = Date.now(); - await licenseCollection.insertOne({ - contains_license: true, - license_status: licenseContentNotEmpty ? "valid" : "invalid", - created_at: newDate, - identifier, - licenseContent, - licenseId, - open: true, - owner, - repo: repository.name, - repositoryId: repository.id, - updated_at: newDate, - }); - } else { - // Get the identifier of the existing license request - // Update the database - await licenseCollection.updateOne( - { repositoryId: repository.id }, - { - $set: { - contains_license: true, - license_status: licenseContentNotEmpty ? "valid" : "invalid", - licenseId, - updated_at: Date.now(), - }, - }, - ); - url = `${CODEFAIR_DOMAIN}/add/license/${existingLicense.identifier}`; - } - const licenseBadge = `[![License](https://img.shields.io/badge/Edit_License-0ea5e9.svg)](${url})`; + const licenseBadge = `[![License](https://img.shields.io/badge/${subjects.license ? "Edit_License-0ea5e9" : "Add_License-dc2626"}.svg)](${badgeURL})`; + + if (subjects.license) { baseTemplate += `## LICENSE ✔️\n\nA LICENSE file is found at the root level of the repository.\n\n${licenseBadge}`; + } else { + baseTemplate += `## LICENSE ❌\n\nTo make your software reusable a license file is expected at the root level of your repository, as recommended in the [FAIR-BioRS Guidelines](https://fair-biors.org). If you would like Codefair to add a license file, click the "Add license" button below to go to our interface for selecting and adding a license. You can also add a license file yourself and Codefair will update the the dashboard when it detects it on the main branch.\n\n${licenseBadge}`; } return baseTemplate; From 283ee90bf058ee61d383cef86d5c45763de65891 Mon Sep 17 00:00:00 2001 From: slugb0t Date: Fri, 16 Aug 2024 12:53:22 -0700 Subject: [PATCH 6/6] feat: :sparkles: validate metadata files formatting --- bot/utils/metadata/index.js | 38 ++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/bot/utils/metadata/index.js b/bot/utils/metadata/index.js index 890733a4..7f2c7913 100644 --- a/bot/utils/metadata/index.js +++ b/bot/utils/metadata/index.js @@ -322,11 +322,35 @@ export async function applyMetadataTemplate( if (subjects.codemeta && subjects.citation && subjects.license) { // Download the codemeta.json file from the repo - const codemetaFile = await context.octokit.repos.getContent({ - owner, - path: "codemeta.json", - repo: repository.name, - }); + let validCodemeta = false; + let validCitation = false; + + try { + const codemetaFile = await context.octokit.repos.getContent({ + owner, + path: "codemeta.json", + repo: repository.name, + }); + + JSON.parse(Buffer.from(codemetaFile.data.content, "base64").toString()); + + validCodemeta = true; + } catch (error) { + consola.error("Error getting codemeta.json file", error); + } + + try { + const citationFile = await context.octokit.repos.getContent({ + owner, + path: "CITATION.cff", + repo: repository.name, + }); + + yaml.load(Buffer.from(citationFile.data.content, "base64").toString()); + validCitation = true; + } catch (error) { + consola.error("Error getting CITATION.cff file", error); + } // Convert the content to a json object const codemetaContent = JSON.parse( @@ -351,6 +375,8 @@ export async function applyMetadataTemplate( const newDate = Date.now(); // const gatheredMetadata = await gatherMetadata(context, owner, repository); await metadataCollection.insertOne({ + citation_status: validCitation ? "valid" : "invalid", + codemeta_status: validCodemeta ? "valid" : "invalid", contains_citation: subjects.citation, contains_codemeta: subjects.codemeta, contains_metadata: subjects.codemeta && subjects.citation, @@ -369,6 +395,8 @@ export async function applyMetadataTemplate( { repositoryId: repository.id }, { $set: { + citation_status: validCitation ? "valid" : "invalid", + codemeta_status: validCodemeta ? "valid" : "invalid", contains_citation: subjects.citation, contains_codemeta: subjects.codemeta, contains_metadata: subjects.codemeta && subjects.citation,