@@ -3,22 +3,21 @@ import { renderIssues, createIssue } from "../../utils/renderer/index.js";
33import dbInstance from "../../db.js" ;
44import { logwatch } from "../../utils/logwatch.js" ;
55import { applyLastModifiedTemplate } from "../../utils/tools/index.js" ;
6- import { validateLicense } from "../../compliance-checks/license/index.js" ;
6+ import {
7+ checkForLicense ,
8+ validateLicense ,
9+ } from "../../compliance-checks/license/index.js" ;
710import { getCWLFiles } from "../../compliance-checks/cwl/index.js" ;
811import {
9- validateMetadata ,
10- getCitationContent ,
11- getCodemetaContent ,
12- gatherMetadata ,
13- convertDateToUnix ,
14- applyDbMetadata ,
15- applyCodemetaMetadata ,
16- applyCitationMetadata ,
12+ checkMetadataFilesExists ,
13+ updateMetadataDatabase ,
14+ applyMetadataTemplate ,
1715} from "../../compliance-checks/metadata/index.js" ;
1816import { checkForReadme } from "../../compliance-checks/readme/index.js" ;
19- import { createId } from "../../utils/tools/index.js" ;
20- import { checkForCodeofConduct } from "../../compliance-checks/code-of-conduct/index.js" ;
21- import { checkForContributingFile } from "../../compliance-checks/contributing/index.js" ;
17+ import {
18+ checkForContributingFile ,
19+ checkForCodeofConduct ,
20+ } from "../../compliance-checks/additional-checks/index.js" ;
2221
2322const ISSUE_TITLE = `FAIR Compliance Dashboard` ;
2423const db = dbInstance ;
@@ -234,125 +233,131 @@ export async function rerunMetadataValidation(
234233 repository ,
235234 issueBody
236235) {
237- logwatch . start ( "Validating metadata files..." ) ;
238- try {
239- let metadata = await gatherMetadata ( context , owner , repository ) ;
240- let containsCitation = false ,
241- containsCodemeta = false ,
242- validCitation = false ,
243- validCodemeta = false ;
236+ const repoInfo = `${ owner } /${ repository . name } ` ;
237+ logwatch . start (
238+ `Rerunning metadata validation for repo: ${ repository . name } (ID: ${ repository . id } )`
239+ ) ;
244240
245- let existingMetadataEntry = await db . codeMetadata . findUnique ( {
246- where : {
247- repository_id : repository . id ,
241+ try {
242+ // Check which metadata files exist
243+ const subjects = await checkMetadataFilesExists ( context , owner , repository ) ;
244+
245+ // Get license status (needed for metadata checks)
246+ const licenseCheck = await checkForLicense ( context , owner , repository . name ) ;
247+ subjects . license = licenseCheck ?. status ;
248+
249+ // Force revalidation by creating a synthetic context that looks like bot push
250+ const syntheticContext = {
251+ ...context ,
252+ payload : {
253+ ...context . payload ,
254+ pusher : { name : `${ process . env . GH_APP_NAME } [bot]` } , // Triggers full revalidation
248255 } ,
249- } ) ;
256+ } ;
250257
251- if ( existingMetadataEntry ?. metadata ) {
252- // Update the metadata variable
253- containsCitation = existingMetadataEntry . contains_citation ;
254- containsCodemeta = existingMetadataEntry . contains_codemeta ;
255- metadata = applyDbMetadata ( existingMetadataEntry , metadata ) ;
256- } else {
257- // create blank entry to prevent issues down the line
258- existingMetadataEntry = await db . codeMetadata . create ( {
259- data : {
260- identifier : createId ( ) ,
261- repository : {
262- connect : {
263- id : repository . id ,
264- } ,
265- } ,
266- } ,
267- } ) ;
268- }
258+ await updateMetadataDatabase (
259+ repository . id ,
260+ subjects ,
261+ repository ,
262+ owner ,
263+ syntheticContext
264+ ) ;
269265
270- const citation = await getCitationContent ( context , owner , repository ) ;
271- const codemeta = await getCodemetaContent ( context , owner , repository ) ;
266+ // Generate new metadata section
267+ let newMetadataSection = "" ;
268+ newMetadataSection = await applyMetadataTemplate (
269+ subjects ,
270+ newMetadataSection ,
271+ repository ,
272+ owner ,
273+ syntheticContext
274+ ) ;
272275
273- if ( codemeta ) {
274- containsCodemeta = true ;
275- validCodemeta = await validateMetadata ( codemeta , "codemeta" , repository ) ;
276- metadata = await applyCodemetaMetadata ( codemeta , metadata , repository ) ;
277- }
276+ // Parse the existing issue body to replace just the metadata section
277+ const issueBodyWithoutTimestamp = issueBody . substring (
278+ 0 ,
279+ issueBody . indexOf ( `<sub><span style="color: grey;">Last updated` )
280+ ) ;
278281
279- if ( citation ) {
280- containsCitation = true ;
281- validCitation = await validateMetadata ( citation , "citation" , repository ) ;
282- metadata = await applyCitationMetadata ( citation , metadata , repository ) ;
283- // consola.info("Metadata so far after citation update", JSON.stringify(metadata, null, 2));
284- }
282+ // Find the metadata section boundaries
283+ const metadataStartMarker = "## Metadata" ;
284+ const metadataStartIndex =
285+ issueBodyWithoutTimestamp . indexOf ( metadataStartMarker ) ;
285286
286- // Ensure all dates have been converted to ISO strings split by the T
287- if ( metadata . creationDate ) {
288- metadata . creationDate = convertDateToUnix ( metadata . creationDate ) ;
289- }
290- if ( metadata . firstReleaseDate ) {
291- metadata . firstReleaseDate = convertDateToUnix ( metadata . firstReleaseDate ) ;
292- }
293- if ( metadata . currentVersionReleaseDate ) {
294- metadata . currentVersionReleaseDate = convertDateToUnix (
295- metadata . currentVersionReleaseDate
296- ) ;
287+ if ( metadataStartIndex === - 1 ) {
288+ throw new Error ( "Could not find Metadata section in issue body" ) ;
297289 }
298290
299- // update the database with the metadata information
300- if ( existingMetadataEntry ) {
301- await db . codeMetadata . update ( {
302- data : {
303- codemeta_status : validCodemeta ? "valid" : "invalid" ,
304- citation_status : validCitation ? "valid" : "invalid" ,
305- contains_citation : containsCitation ,
306- contains_codemeta : containsCodemeta ,
307- metadata : metadata ,
308- } ,
309- where : {
310- repository_id : repository . id ,
311- } ,
312- } ) ;
291+ // Find the next section (starts with ## or end of string)
292+ const afterMetadataStart = issueBodyWithoutTimestamp . substring (
293+ metadataStartIndex + metadataStartMarker . length
294+ ) ;
295+ const nextSectionMatch = afterMetadataStart . match ( / \n # # / ) ;
296+
297+ let updatedBody ;
298+ if ( nextSectionMatch ) {
299+ // There's another section after Metadata
300+ const nextSectionIndex =
301+ metadataStartIndex +
302+ metadataStartMarker . length +
303+ nextSectionMatch . index ;
304+
305+ updatedBody =
306+ issueBodyWithoutTimestamp . substring ( 0 , metadataStartIndex ) + // Before Metadata
307+ newMetadataSection + // New Metadata section
308+ issueBodyWithoutTimestamp . substring ( nextSectionIndex ) ; // After Metadata
313309 } else {
314- await db . codeMetadata . create ( {
315- data : {
316- codemeta_status : validCodemeta ? "valid" : "invalid" ,
317- citation_status : validCitation ? "valid" : "invalid" ,
318- contains_citation : containsCitation ,
319- contains_codemeta : containsCodemeta ,
320- metadata : metadata ,
321- } ,
322- where : {
323- repository_id : repository . id ,
324- } ,
325- } ) ;
310+ // Metadata is the last section
311+ updatedBody =
312+ issueBodyWithoutTimestamp . substring ( 0 , metadataStartIndex ) + // Before Metadata
313+ newMetadataSection ; // New Metadata section
326314 }
327315
328- const issueBodyRemovedCommand = issueBody . substring (
329- 0 ,
330- issueBody . indexOf ( `<sub><span style="color: grey;">Last updated` )
331- ) ;
332- const lastModified = await applyLastModifiedTemplate (
333- issueBodyRemovedCommand
316+ // Add timestamp
317+ updatedBody = applyLastModifiedTemplate ( updatedBody ) ;
318+
319+ // Update the issue
320+ await createIssue ( context , owner , repository , ISSUE_TITLE , updatedBody ) ;
321+ logwatch . info (
322+ `Metadata validation rerun completed for repo: ${ repository . name } (ID: ${ repository . id } )`
334323 ) ;
335- await createIssue ( context , owner , repository , ISSUE_TITLE , lastModified ) ;
336324 } catch ( error ) {
337- // Remove the command from the issue body
338- const issueBodyRemovedCommand = issueBody . substring (
339- 0 ,
340- issueBody . indexOf ( `<sub><span style="color: grey;">Last updated` )
341- ) ;
342- const lastModified = await applyLastModifiedTemplate (
343- issueBodyRemovedCommand
325+ logwatch . error (
326+ {
327+ message : "Failed to rerun metadata validation" ,
328+ repo : repoInfo ,
329+ error : error . message ,
330+ stack : error . stack ,
331+ } ,
332+ true
344333 ) ;
345- await createIssue ( context , owner , repository , ISSUE_TITLE , lastModified ) ;
346- if ( error . cause ) {
347- logwatch . error (
334+
335+ // rrestore issue body without command
336+ try {
337+ const issueBodyRemovedCommand = issueBody . substring (
338+ 0 ,
339+ issueBody . indexOf ( `<sub><span style="color: grey;">Last updated` )
340+ ) ;
341+ const lastModified = applyLastModifiedTemplate ( issueBodyRemovedCommand ) ;
342+
343+ await context . octokit . issues . update ( {
344+ owner,
345+ repo : repository . name ,
346+ issue_number : context . payload . issue . number ,
347+ body : lastModified ,
348+ } ) ;
349+ } catch ( restoreError ) {
350+ logwatch . warn (
348351 {
349- message : "Error.cause message for Metadata Validation" ,
350- error : error . cause ,
352+ message : "Failed to restore issue body after error" ,
353+ repo : repoInfo ,
354+ error : restoreError . message ,
351355 } ,
352356 true
353357 ) ;
354358 }
355- throw new Error ( "Error rerunning metadata validation" , error ) ;
359+
360+ throw error ;
356361 }
357362}
358363
@@ -365,68 +370,61 @@ export async function rerunLicenseValidation(
365370 // Run the license validation again
366371 logwatch . start ( "Rerunning License Validation..." ) ;
367372 try {
368- const licenseRequest = await context . octokit . rest . licenses . getForRepo ( {
369- owner,
370- repo : repository . name ,
371- } ) ;
372-
373- const existingLicense = await db . licenseRequest . findUnique ( {
374- where : {
375- repository_id : repository . id ,
376- } ,
377- } ) ;
378-
379- const license = ! ! licenseRequest . data . license ;
373+ const license = await checkForLicense ( context , owner , repository . name ) ;
380374
381375 if ( ! license ) {
382376 throw new Error ( "License not found in the repository" ) ;
383377 }
384378
385- const { licenseId, licenseContent, licenseContentEmpty } = validateLicense (
386- licenseRequest ,
387- existingLicense
388- ) ;
389-
390- logwatch . info ( {
391- message : `License validation complete` ,
392- licenseId,
393- licenseContent,
394- licenseContentEmpty,
395- } ) ;
396-
397379 // Update the database with the license information
398- if ( existingLicense ) {
399- await db . licenseRequest . update ( {
400- data : {
401- license_id : licenseId ,
402- license_content : licenseContent ,
403- license_status : licenseContentEmpty ? "invalid" : "valid" ,
404- } ,
405- where : {
406- repository_id : repository . id ,
407- } ,
408- } ) ;
409- } else {
410- await db . licenseRequest . create ( {
411- data : {
412- license_id : licenseId ,
413- license_content : licenseContent ,
414- license_status : licenseContentEmpty ? "invalid" : "valid" ,
415- } ,
416- where : {
417- repository_id : repository . id ,
418- } ,
419- } ) ;
420- }
380+ await updateLicenseDatabase ( repository , license ) ;
421381
422382 // Update the issue body
423383 const issueBodyRemovedCommand = issueBody . substring (
424384 0 ,
425385 issueBody . indexOf ( `<sub><span style="color: grey;">Last updated` )
426386 ) ;
427- const lastModified = await applyLastModifiedTemplate (
428- issueBodyRemovedCommand
387+
388+ // Generate new license section
389+ let newLicenseSection = "" ;
390+ newLicenseSection = await applyLicenseTemplate (
391+ license ,
392+ newLicenseSection ,
393+ repository ,
394+ owner ,
395+ context
396+ ) ;
397+ // Parse the existing issue body to replace just the license section
398+ const issueBodyWithoutTimestamp = issueBodyRemovedCommand ;
399+ const licenseStartMarker = "## LICENSE" ;
400+ const licenseStartIndex =
401+ issueBodyWithoutTimestamp . indexOf ( licenseStartMarker ) ;
402+ if ( licenseStartIndex === - 1 ) {
403+ throw new Error ( "Could not find LICENSE section in issue body" ) ;
404+ }
405+
406+ // Find the next section (starts with ## or end of string)
407+ const afterLicenseStart = issueBodyWithoutTimestamp . substring (
408+ licenseStartIndex + licenseStartMarker . length
429409 ) ;
410+ const nextSectionMatch = afterLicenseStart . match ( / \n # # / ) ;
411+ let updatedBody ;
412+ if ( nextSectionMatch ) {
413+ // There's another section after LICENSE
414+ const nextSectionIndex =
415+ licenseStartIndex + licenseStartMarker . length + nextSectionMatch . index ;
416+ updatedBody =
417+ issueBodyWithoutTimestamp . substring ( 0 , licenseStartIndex ) + // Before LICENSE
418+ newLicenseSection + // New LICENSE section
419+ issueBodyWithoutTimestamp . substring ( nextSectionIndex ) ; // After LICENSE
420+ } else {
421+ // LICENSE is the last section
422+ updatedBody =
423+ issueBodyWithoutTimestamp . substring ( 0 , licenseStartIndex ) + // Before LICENSE
424+ newLicenseSection ; // New LICENSE section
425+ }
426+
427+ const lastModified = await applyLastModifiedTemplate ( updatedBody ) ;
430428 await createIssue ( context , owner , repository , ISSUE_TITLE , lastModified ) ;
431429 } catch ( error ) {
432430 // Remove the command from the issue body
@@ -443,6 +441,7 @@ export async function rerunLicenseValidation(
443441 {
444442 message : "Error.cause message for License Validation" ,
445443 error : error . cause ,
444+ stack : error ?. stack ,
446445 } ,
447446 true
448447 ) ;
0 commit comments