Skip to content

fix: 🐛 Update to the 2.0 codemeta schema json#182

Merged
slugb0t merged 6 commits intomainfrom
staging
Mar 9, 2026
Merged

fix: 🐛 Update to the 2.0 codemeta schema json#182
slugb0t merged 6 commits intomainfrom
staging

Conversation

@slugb0t
Copy link
Member

@slugb0t slugb0t commented Feb 27, 2026

Summary by Sourcery

Refactor GitHub App event handling into dedicated handler modules, enhance archival and license compliance logic, and integrate support for the Codemeta 2.0 schema in the validator and dashboard.

New Features:

  • Infer and display FAIR release archival guidance based on DOIs discovered in codemeta and citation metadata, including Zenodo-aware badges and messaging.
  • Add UI flow for confirming or changing an existing license when the content is present but needs SPDX verification, with options to keep content or fetch a standard template.

Bug Fixes:

  • Ensure license compliance checks preserve valid SPDX identifiers, handle unchanged content correctly, and respect pending license pull requests when files are not yet in the default branch.
  • Fix dashboard re-rendering to use structured license metadata from the database and correct the code of conduct flag key when rebuilding the issue body.
  • Restore copying of all required bot resources in the Dockerfile, including compliance checks, handlers, and commands, and ensure the validator image ships the Codemeta 2.0 schema file.

Enhancements:

  • Modularize Probot event registration by extracting installation, push, pull request, and issue logic into dedicated handler modules and centralizing shared constants and helpers.
  • Improve archival template behavior to fall back to metadata-based DOI detection when no prior Codefair Zenodo release exists, with prioritized handling for Zenodo DOIs, other DOIs, and non-DOI identifiers.
  • Normalize license content when comparing changes to better distinguish true edits from formatting differences and to drive more accurate status messages in the dashboard.

Build:

  • Update bot and validator Dockerfiles to include new handler modules and Codemeta 2.0 schema assets in their images.

Documentation:

  • Clarify dashboard messaging around license verification, SPDX requirements, and the impact of custom licenses on Zenodo archival.

Tests:

  • Expose archival DOI parsing and rendering helpers for easier unit testing of edge cases around identifiers and badge rendering.

* fix: 🐛 docker volume for postgres data

* feat: ✨ Abstract bot event listeners to files

* fix: 🐛 improve SPDX validation and add verification workflow

* feat: ✨ detect existing archivals for fair release status

* refactor: ♻️ Add line break after pr badge in issue dashboard

* fix: 🐛 add await to db call

* refactor: ♻️ Sourcery suggestion

* fix(sourcery): 🐛 Edge case with doi format and badge generation
@fairdataihub-bot
Copy link

Thank you for submitting this pull request! We appreciate your contribution to the project. Before we can merge it, we need to review the changes you've made to ensure they align with our code standards and meet the requirements of the project. We'll get back to you as soon as we can with feedback. Thanks again!

@sourcery-ai
Copy link

sourcery-ai bot commented Feb 27, 2026

Reviewer's Guide

Refactors the Probot app into event-specific handlers, enhances FAIR archival and license validation logic (including DOI/Zenodo handling and SPDX/custom license flows), updates the dashboard UI and re-rendering pipeline to work off database-backed metadata, and adds support for the codemeta 2.0 schema in the validator container.

Sequence diagram for bot PR lifecycle and dashboard re-render

sequenceDiagram
  actor Dev as Developer
  participant GH as GitHub
  participant App as ProbotApp
  participant PRH as pullRequest_handlers
  participant DB as PrismaClient
  participant ACT as actions_reRenderDashboard
  participant REN as renderer_renderIssues

  Dev->>GH: Open PR (e.g. LICENSE or metadata PR)
  GH->>App: pull_request.opened
  App->>PRH: handle pull_request.opened
  PRH->>DB: findUnique_installation(repository_id)
  alt installation missing or disabled or rate_limited
    PRH-->>App: return (ignore)
  else valid installation
    alt LICENSE PR
      PRH->>DB: update licenseRequest.pull_request_url
    else METADATA PR
      PRH->>DB: update codeMetadata.pull_request_url
    end
    PRH->>ACT: reRenderDashboard(context, owner, repository, empty_issue_body)
    ACT->>DB: fetch licenseRequest, codeMetadata, cwl, readme, contributing, cofc
    ACT->>REN: renderIssues(context, owner, repository, emptyRepoFlag, subjectsFromDB)
    REN->>DB: update dashboard issue body
  end

  Dev->>GH: Close or merge PR
  GH->>App: pull_request.closed
  App->>PRH: handle pull_request.closed
  PRH->>DB: clear pull_request_url on licenseRequest or codeMetadata
  PRH->>ACT: reRenderDashboard(context, owner, repository, empty_issue_body)
  ACT->>REN: renderIssues(...)
  REN->>DB: update dashboard issue body (remove PR badge)
  PRH->>GH: octokit.git.deleteRef(branch)
Loading

Class diagram for new handler and helper modules

classDiagram
  class ProbotApp {
  }

  class InstallationHandlers {
    +registerInstallationHandlers(app, db)
  }

  class PushHandler {
    +registerPushHandler(app, db)
  }

  class PullRequestHandlers {
    +registerPullRequestHandlers(app, db)
  }

  class IssueHandlers {
    +registerIssueHandlers(app, db)
  }

  class Helpers {
    +ISSUE_TITLE
    +PR_TITLES
    +createEmptyCommitInfo()
  }

  class ArchivalModule {
    +applyArchivalTemplate(context, baseTemplate, repository, owner, subjects)
    +extractDOIFromString(value)
    +classifyIdentifier(identifier)
    +extractIdentifiersFromCodemeta(codemetaContent)
    +extractIdentifiersFromCitation(citationContent)
    +fetchAndExtractIdentifiers(context, owner, repository)
    +prioritizeIdentifiers(identifiers)
    +createShieldsLabelFromDOI(doi)
    +createZenodoDOIBadge(doi, zenodoId)
    +createOtherDOIBadge(doi)
  }

  class LicenseModule {
    +isValidSpdxLicense(licenseId)
    +normalizeContent(content)
    +validateLicense(license, existingLicense)
    +updateLicenseDatabase(repository, license)
    +applyLicenseTemplate(baseTemplate, repository, subjects, licenseId, customTitle)
  }

  ProbotApp --> InstallationHandlers
  ProbotApp --> PushHandler
  ProbotApp --> PullRequestHandlers
  ProbotApp --> IssueHandlers

  InstallationHandlers --> Helpers
  PushHandler --> Helpers
  PullRequestHandlers --> Helpers
  IssueHandlers --> Helpers

  InstallationHandlers --> ArchivalModule
  PushHandler --> ArchivalModule
  IssueHandlers --> ArchivalModule

  InstallationHandlers --> LicenseModule
  PushHandler --> LicenseModule
  IssueHandlers --> LicenseModule
Loading

File-Level Changes

Change Details Files
Refactor Probot entrypoint into modular event handlers for installations, pushes, pull requests, and issues.
  • Strip most event-handling logic out of the main bot entrypoint and rely on handler registration functions to wire events to logic.
  • Introduce dedicated handler modules for installation, push, pull request, and issue events that encapsulate their previous inline behaviors.
  • Use shared helper constants/utilities (e.g., ISSUE_TITLE, PR_TITLES, createEmptyCommitInfo) to remove duplication across handlers and simplify orchestration.
bot/index.js
bot/handlers/installation.js
bot/handlers/push.js
bot/handlers/pullRequest.js
bot/handlers/issue.js
bot/utils/helpers.js
bot/Dockerfile
Enhance archival compliance logic to detect and prioritize DOIs/Zenodo records from metadata files and render richer FAIR release guidance in the dashboard.
  • Add DOI extraction and classification utilities that normalize identifiers from codemeta.json and CITATION.cff, distinguishing Zenodo DOIs, other DOIs, and non-DOI identifiers.
  • Fetch metadata contents via existing helpers, aggregate and de-duplicate identifiers, and prioritize them (Zenodo first, then other DOIs, then non-DOIs).
  • Extend applyArchivalTemplate to accept context, consult the Zenodo deposition database first, and fall back to identifier-based messaging with badges and expandable details when no prior Codefair release exists.
  • Introduce shields.io-safe label builders and badge renderers for Zenodo and non-Zenodo DOIs, and export archival helpers for testing.
bot/compliance-checks/archival/index.js
bot/utils/renderer/index.js
Improve license validation, persistence, and dashboard rendering to better distinguish SPDX, custom, and pending-verification licenses and preserve PR-driven state.
  • Load SPDX license metadata within the license compliance check and add helpers to validate SPDX IDs and normalize license content for robust comparisons.
  • Update validateLicense to preserve known SPDX IDs when content is unchanged, flag changed or unidentifiable content as Custom for user verification, and handle NOASSERTION and empty content cases more explicitly.
  • Adjust updateLicenseDatabase to reuse existing license data when a license PR is still open but the file is missing from the repo, and to run validation for newly discovered licenses before inserting.
  • Refine applyLicenseTemplate messaging to describe the new verification flow and dependency on SPDX licenses for Zenodo archival.
  • Change reRenderDashboard to build a full license subject object from DB fields and fix the code of conduct flag name to match the schema.
  • Update the dashboard renderer so license PR badges include a trailing newline for cleaner layout.
bot/compliance-checks/license/index.js
bot/commands/actions/index.js
bot/utils/renderer/index.js
Add a confirmation flow in the license editing UI to handle verification of existing content vs switching licenses and ensure correct persistence paths.
  • Track the originally detected license ID and derive a canConfirmExistingContent flag used to distinguish between verifying a previously Custom license and changing to a new SPDX license.
  • When a user selects a new license and existing license content is present, show a confirmation modal that allows them to either keep their current content (confirm type) or fetch a fresh standard template.
  • Implement confirm/cancel handlers that either save the existing content through the custom_title API (triggering dashboard re-render) or retrieve and load the canonical license template, while preventing accidental license ID changes on cancel.
  • Minorly adjust layout and bindings in the license edit page, including using :value binding on the select and formatting tweaks.
ui/pages/dashboard/[owner]/[repo]/edit/license.vue
Tighten dashboard re-render and archival wiring so all checks operate consistently off database state and context-aware templates.
  • Ensure applyArchivalTemplate is called with the GitHub context so it can fetch metadata files when needed.
  • Rebuild subject objects in reRenderDashboard from DB queries (particularly the license object) to align with what compliance templates expect.
  • Use the updated contains_cofc field instead of the previous contains_code flag when wiring code of conduct status.
bot/utils/renderer/index.js
bot/commands/actions/index.js
Add and wire the codemeta 2.0 JSON schema into the validator container image.
  • Include codemeta-schema2.0.json alongside the existing schema and app files in the validator Docker image.
  • Add the new schema file to the repository so it is available at build time.
validator/Dockerfile
validator/codemeta-schema2.0.json
Minor infrastructure and configuration tweaks.
  • Normalize the Postgres volume mapping in the db-docker-compose configuration file.
  • Reorder COPY directives in the bot Dockerfile to include the new handlers directory and avoid duplicates.
db-docker-compose.yaml
bot/Dockerfile

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@fairdataihub-bot
Copy link

Thanks for making updates to your pull request. Our team will take a look and provide feedback as soon as possible. Please wait for any GitHub Actions to complete before editing your pull request. If you have any additional questions or concerns, feel free to let us know. Thank you for your contributions!

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • In ui/pages/dashboard/[owner]/[repo]/edit/license.vue, the <n-select> was changed from v-model:value="licenseId" to :value="licenseId" without wiring an @update:value handler, so user selections will no longer update licenseId or trigger updateLicenseContent; reintroduce v-model:value or add an explicit @update:value to keep the selection reactive.
  • The new archival helpers in bot/compliance-checks/archival/index.js add quite a bit of logic; consider moving the identifier/DOI parsing and badge generation into a separate module (e.g., utils/doi.js) to keep the archival template function focused and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `ui/pages/dashboard/[owner]/[repo]/edit/license.vue`, the `<n-select>` was changed from `v-model:value="licenseId"` to `:value="licenseId"` without wiring an `@update:value` handler, so user selections will no longer update `licenseId` or trigger `updateLicenseContent`; reintroduce `v-model:value` or add an explicit `@update:value` to keep the selection reactive.
- The new archival helpers in `bot/compliance-checks/archival/index.js` add quite a bit of logic; consider moving the identifier/DOI parsing and badge generation into a separate module (e.g., `utils/doi.js`) to keep the archival template function focused and easier to maintain.

## Individual Comments

### Comment 1
<location path="bot/index.js" line_range="46-50" />
<code_context>
-      });
-    }
-  });
+  // Register all event handlers
+  registerInstallationHandlers(app, db);
+  registerPushHandler(app, db);
+  registerPullRequestHandlers(app, db);
+  registerIssueHandlers(app, db);
 };
</code_context>
<issue_to_address>
**issue (bug_risk):** The `db` variable passed into handler registration is undefined; use `dbInstance` instead.

`dbInstance` is the only database export from `./db.js`, and `db` is not defined in this scope. The calls to `registerInstallationHandlers`, `registerPushHandler`, `registerPullRequestHandlers`, and `registerIssueHandlers` will fail at runtime. Replace `db` with `dbInstance` so each handler receives the actual Prisma client instance.
</issue_to_address>

### Comment 2
<location path="bot/compliance-checks/archival/index.js" line_range="219" />
<code_context>
-      const lastVersion = existingZenodoDep.github_tag_name;
-      const zenodoId = existingZenodoDep.zenodo_id;
-      const zenodoDoi = existingZenodoDep.last_published_zenodo_doi;
-      const zenodoDOIBadge = `[![DOI](https://img.shields.io/badge/DOI-${zenodoDoi}-blue)](${ZENODO_ENDPOINT}/records/${zenodoId})`;
-      baseTemplate += `${archiveTitle} ✔️\n\n***${lastVersion}***${alreadyReleaseText}\n\n${zenodoDOIBadge}\n\nReady to create your next FAIR release? Click the button below:\n\n${releaseBadgeButton}\n\n`;
-    }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Use the new DOI badge helper to avoid label-encoding issues for stored Zenodo DOIs.

This branch still interpolates `zenodoDoi` directly into the shields.io URL, so a stored `last_published_zenodo_doi` containing `/`, `(`, `)`, spaces, or other special characters could break the badge.

To align with the new helpers and centralize encoding, update this to:
```js
const zenodoDOIBadge = createZenodoDOIBadge(zenodoDoi, zenodoId);
```

Suggested implementation:

```javascript
      const lastVersion = existingZenodoDep.github_tag_name;
      const zenodoId = existingZenodoDep.zenodo_id;
      const zenodoDoi = existingZenodoDep.last_published_zenodo_doi;
      const zenodoDOIBadge = createZenodoDOIBadge(zenodoDoi, zenodoId);
      baseTemplate += `${archiveTitle} ✔️\n\n***${lastVersion}***${alreadyReleaseText}\n\n${zenodoDOIBadge}\n\nReady to create your next FAIR release? Click the button below:\n\n${releaseBadgeButton}\n\n`;
    }

```

1. Ensure `createZenodoDOIBadge` is imported or available in this module, matching how it is used elsewhere in the codebase, e.g. an import like:
   `const { createZenodoDOIBadge } = require('./path/to/helpers');`
2. If the helper has a different signature (e.g. expects an object `{ doi, zenodoId }`), adjust the call accordingly to match the existing helper definition.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@slugb0t slugb0t merged commit a7e9b0e into main Mar 9, 2026
5 checks passed
@fairdataihub-bot
Copy link

Thanks for closing this pull request! If you have any further questions, please feel free to open a new issue. We are always happy to help!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant