Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test Files #208

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
342 changes: 342 additions & 0 deletions github-comment-module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,342 @@
import { Value } from "@sinclair/typebox/value";
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@UbiquityOS explain this pr

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@UbiquityOS explain this pr

Copy link

Choose a reason for hiding this comment

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

@UbiquityOS whats this import value about

Copy link

Choose a reason for hiding this comment

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

The 'import { Value } from "@sinclair/typebox/value";' statement is used to import the Value utility from the @sinclair/typebox library.

Context in the Repository:

  • TypeBox Integration: TypeBox is a runtime type system for JavaScript and TypeScript that allows you to define and validate data structures. By importing Value, you're leveraging TypeBox's capabilities to handle and manipulate type-checked values within your project.

  • Usage in Code:

    import { Value } from "@sinclair/typebox/value";
    
    // Example usage
    const validatedData = Value.Create(schema, data);

    In this example, Value.Create would validate data against the defined schema, ensuring type safety and consistency across the application.

Purpose in the PR:

  • Enhancing Type Safety: By introducing Value, the PR aims to enhance type safety within the github-comment-module.ts file. This ensures that the data being processed adheres to the expected structure, reducing potential runtime errors.

  • Facilitating Data Validation: Utilizing Value from TypeBox allows for robust validation of data, especially when interacting with external sources or APIs. This is crucial for maintaining data integrity throughout the application's lifecycle.

Benefits:

  1. Improved Reliability: Ensures that data structures are consistent and validated, minimizing bugs related to type mismatches.
  2. Enhanced Maintainability: Clear type definitions make the codebase easier to understand and maintain.
  3. Seamless Integration: TypeBox integrates smoothly with TypeScript, providing both runtime and compile-time type checking.

If you have specific questions about how Value is implemented or its role in certain functions within the repository, feel free to ask!

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

some example content

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

some example content meant to be ignored

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@UbiquityOS explain this PR?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

/ask explain this PR?

Copy link

Choose a reason for hiding this comment

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

This pull request (#208) introduces the GithubCommentModule, a new class designed to handle the creation and posting of structured comments on GitHub issues and pull requests. This module plays a crucial role in automating and managing comment workflows, ensuring consistency, proper formatting, and integration with other system components.

Key Features and Functionalities:

  1. HTML Encoding:

    • Utilizes the jsdom library to safely encode strings containing special characters. This ensures that comments are rendered correctly on GitHub without unintended formatting issues.
    _encodeHTML(str: string) {
      const dom = new JSDOM();
      const div = dom.window.document.createElement("div");
      div.appendChild(dom.window.document.createTextNode(str));
      return div.innerHTML;
    }
  2. Content Management and Truncation:

    • Implements logic to manage the length of comment content. If the content exceeds predefined limits (GITHUB_COMMENT_PAYLOAD_LIMIT), the module can strip excessive parts and append a note indicating that the content has been truncated.
    • This feature ensures that comments remain within GitHub’s character limits, preventing potential issues with very long comments.
    if (stripContent) {
      logger.info("Stripping content due to excessive length.");
      bodyArray.push("> [!NOTE]\n");
      bodyArray.push("> This output has been truncated due to the comment length limit.\n\n");
      // Additional logic to handle content stripping
    }
  3. Structured Metadata Insertion:

    • Adds structured metadata to each comment, including the workflow run URL and processed results. This metadata enhances traceability and integration with other system components, such as GitHub Actions.
    bodyArray.push(
      createStructuredMetadata("GithubCommentModule", {
        workflowUrl: this._encodeHTML(getGithubWorkflowRunUrl()),
        output: JSON.parse(JSON.stringify(metadataResult, typeReplacer, 2)),
      })
    );
  4. Configuration Handling:

    • Reads and applies configuration settings related to GitHub comments from the project's configuration files. This allows for customizable behavior based on different project requirements and environments.
    private readonly _configuration: GithubCommentConfiguration | null = configuration.incentives.githubComment;
  5. Permit Management:

    • Keeps track of the last comment ID using environment variables. This facilitates the association of new comments with their corresponding workflow runs, ensuring accurate tracking and management.
    private _lastCommentId: number | null = process.env.COMMENT_ID ? Number(process.env.COMMENT_ID) : null;
  6. Integration with Helper Functions and Libraries:

    • Leverages various helper functions and external libraries (like decimal.js, yaml, and custom helpers) to process and format data before posting comments. This modular approach promotes maintainability and scalability.

Impact on the Project:

By introducing the GithubCommentModule, this PR enhances the project's ability to automate and manage GitHub comments efficiently. The module ensures that comments are consistently formatted, properly encoded, and integrated with the project's CI/CD workflows. Additionally, it handles edge cases related to content length and maintains structured metadata, which is essential for traceability and further automation tasks.

Overall, this PR contributes to building a more robust and maintainable system for managing GitHub interactions, aligning with the project's goals of automation, code quality, and seamless integration with development workflows.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ignroe

import Decimal from "decimal.js";
import * as fs from "fs";
import { JSDOM } from "jsdom";
import { stringify } from "yaml";
import { CommentAssociation, CommentKind } from "../configuration/comment-types";
import configuration from "../configuration/config-reader";
import { GithubCommentConfiguration, githubCommentConfigurationType } from "../configuration/github-comment-config";
import { getGithubWorkflowRunUrl } from "../helpers/github";
import logger from "../helpers/logger";
import { createStructuredMetadata } from "../helpers/metadata";
import { removeKeyFromObject, typeReplacer } from "../helpers/result-replacer";
import { getErc20TokenSymbol } from "../helpers/web3";
import { IssueActivity } from "../issue-activity";
import { getOctokitInstance } from "../octokit";
import program from "./command-line";
import { GithubCommentScore, Module, Result } from "./processor";
import { GITHUB_COMMENT_PAYLOAD_LIMIT } from "../helpers/constants";
import { generateFeeString } from "../helpers/fee";

interface SortedTasks {
issues: { specification: GithubCommentScore | null; comments: GithubCommentScore[] };
reviews: GithubCommentScore[];
}

/**
* Posts a GitHub comment according to the given results.
*/
export class GithubCommentModule implements Module {
private readonly _configuration: GithubCommentConfiguration | null = configuration.incentives.githubComment;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

/ask explain this pr ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

/ask explain this pr ?

Copy link

Choose a reason for hiding this comment

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

Tip

ubosshivaditya is thinking...

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

/ask explain this pr ?

Copy link

@test-app-ubo test-app-ubo bot Feb 14, 2025

Choose a reason for hiding this comment

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

This pull request (#208) introduces a new TypeScript module named GithubCommentModule to the project. The primary objective of this module is to automate the process of posting comments on GitHub issues and pull requests based on the results generated by the project's workflows. Here's a detailed breakdown of the changes and their functionalities:

Key Features of GithubCommentModule:

  1. Integration with GitHub APIs:

    • Octokit Usage: The module leverages Octokit to interact seamlessly with GitHub's REST APIs. This allows the bot to create, update, and manage comments programmatically.
  2. Configuration Management:

    • Dynamic Configuration: It reads configurations from github-comment-config to determine how comments should be formatted and under what conditions they should be posted. This ensures flexibility and adaptability to different project requirements.
  3. HTML Encoding:

    • Safe Content Rendering: Utilizing JSDOM, the module encodes strings containing special characters to ensure that comments are rendered correctly on GitHub without formatting issues or potential security vulnerabilities.
  4. Structured Metadata Inclusion:

    • Enhanced Context: The module appends structured metadata to each comment, such as the workflow run URL and summarized results. This metadata provides additional context and can be used for tracking and debugging purposes.
  5. Payload Management:

    • Handling Large Comments: To adhere to GitHub's comment length limits, the module includes logic to truncate excessively long content. When truncation occurs, it notifies users with a note indicating that the output has been shortened to fit within the permissible limits.
  6. Result Processing:

    • SortedTasks Interface: Defines a structure (SortedTasks) to categorize results into issues and reviews, allowing for organized and targeted commenting based on the type of feedback or result.
    • HTML Generation: Processes results to generate HTML content for comments, ensuring that the information is presented in a clear and readable format.
  7. Error Handling and Logging:

    • Robust Logging: Integrates with the project's logging system to record important events and errors, facilitating easier monitoring and troubleshooting.
    • Error Management: Implements mechanisms to handle potential errors gracefully, ensuring that the commenting process does not disrupt the overall workflow.

Impact on the Project:

  • Enhanced Automation: By automating the commenting process, the module reduces the manual effort required to provide feedback on issues and pull requests, thereby streamlining the development workflow.

  • Improved Communication: Structured and well-formatted comments enhance clarity and ensure that all stakeholders receive consistent and actionable information.

  • Scalability: The module's ability to handle large payloads and its configurable nature make it scalable, accommodating future growth and evolving project needs.

Conclusion:

The addition of the GithubCommentModule significantly bolsters the project's automation capabilities, particularly in managing and communicating through GitHub comments. By integrating seamlessly with GitHub's APIs and adhering to best practices in code formatting and error handling, this module ensures efficient and reliable interactions within the project's CI/CD pipelines.

private readonly _debugFilePath = "./output.html";
/**
* COMMENT_ID can be set in the environment to reference the id of the last comment created during this workflow.
* See also compute.yml to understand how it is set.
*/
private _lastCommentId: number | null = process.env.COMMENT_ID ? Number(process.env.COMMENT_ID) : null;

/**
* Ensures that a string containing special characters get HTML encoded.
*/
_encodeHTML(str: string) {
const dom = new JSDOM();
const div = dom.window.document.createElement("div");
div.appendChild(dom.window.document.createTextNode(str));
return div.innerHTML;
}

async getBodyContent(result: Result, stripContent = false): Promise<string> {
const keysToRemove: string[] = [];
const bodyArray: (string | undefined)[] = [];

if (stripContent) {
logger.info("Stripping content due to excessive length.");
bodyArray.push("> [!NOTE]\n");
bodyArray.push("> This output has been truncated due to the comment length limit.\n\n");
for (const [key, value] of Object.entries(result)) {
// Remove result with 0 total from being displayed
if (result[key].total <= 0) continue;
result[key].evaluationCommentHtml = await this._generateHtml(key, value, true);
bodyArray.push(result[key].evaluationCommentHtml);
}
bodyArray.push(
createStructuredMetadata("GithubCommentModule", {
workflowUrl: this._encodeHTML(getGithubWorkflowRunUrl()),
})
);
return bodyArray.join("");
}

for (const [key, value] of Object.entries(result)) {
// Remove result with 0 total from being displayed
if (result[key].total <= 0) {
keysToRemove.push(key);
continue;
}
result[key].evaluationCommentHtml = await this._generateHtml(key, value);
bodyArray.push(result[key].evaluationCommentHtml);
}
// Remove evaluationCommentHtml because it is superfluous
let metadataResult = removeKeyFromObject(result, "evaluationCommentHtml");
// Remove user with 0 result from metadataResult
for (const key of keysToRemove) {
metadataResult = removeKeyFromObject(metadataResult, key);
}
// Add the workflow run url and the metadata in the GitHub's comment
bodyArray.push(
createStructuredMetadata("GithubCommentModule", {
workflowUrl: this._encodeHTML(getGithubWorkflowRunUrl()),
output: JSON.parse(JSON.stringify(metadataResult, typeReplacer, 2)),
})
);

const body = bodyArray.join("");
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

/ask explain this pr?

Copy link

Choose a reason for hiding this comment

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

This pull request (#208) introduces the GithubCommentModule, a new TypeScript class designed to automate the posting of GitHub comments based on specific results. Key functionalities include:

  1. Configuration Management: The module loads its configuration from GithubCommentConfiguration, allowing customization of comment behaviors through the ubiquibot-config.
  2. HTML Encoding: Utilizes jsdom to safely encode special characters in comment strings, ensuring that comments render correctly on GitHub.
  3. Content Generation: The getBodyContent method constructs the comment body, supporting both full and truncated content based on length constraints. It intelligently includes or excludes specific data points to adhere to GitHub's comment size limits.
  4. Metadata Integration: Adds structured metadata to comments using helper functions, embedding information like workflow URLs to provide context and traceability.
  5. GitHub API Interaction: Leverages the @octokit/rest package to interact with GitHub APIs, enabling actions like posting comments and handling workflow events.
  6. Logging and Error Handling: Incorporates custom logging via @ubiquity-dao/ubiquibot-logger to track operations and potential issues, enhancing maintainability and debugging capabilities.
  7. Environment Management: Manages environment variables securely with dotenv, ensuring sensitive information like COMMENT_ID is handled appropriately.

Overall, this module enhances the repository's ability to programmatically manage GitHub comments, streamlining processes like notifying contributors, reporting statuses, and integrating AI-driven functionalities.

// We check this length because GitHub has a comment length limit
if (body.length > GITHUB_COMMENT_PAYLOAD_LIMIT) {
// First, we try to diminish the metadata content to only contain the URL
bodyArray[bodyArray.length - 1] = `${createStructuredMetadata("GithubCommentModule", {
workflowUrl: this._encodeHTML(getGithubWorkflowRunUrl()),
})}`;
const newBody = bodyArray.join("");
if (newBody.length <= GITHUB_COMMENT_PAYLOAD_LIMIT) {
return newBody;
} else {
return this.getBodyContent(result, true);
}
}
return body;
}

async transform(data: Readonly<IssueActivity>, result: Result): Promise<Result> {
const body = await this.getBodyContent(result);
if (this._configuration?.debug) {
fs.writeFileSync(this._debugFilePath, body);
}
if (this._configuration?.post) {
try {
await this.postComment(body);
} catch (e) {
logger.error(`Could not post GitHub comment: ${e}`);
}
}
return result;
}

get enabled(): boolean {
if (!Value.Check(githubCommentConfigurationType, this._configuration)) {
logger.error("Invalid / missing configuration detected for GithubContentModule, disabling.");
return false;
}
return true;
}

async postComment(body: string, updateLastComment = true) {
const { eventPayload } = program;
if (!this._configuration?.post) {
logger.debug("Won't post a comment since posting is disabled.", { body });
return;
}
if (updateLastComment && this._lastCommentId !== null) {
await getOctokitInstance().issues.updateComment({
body,
repo: eventPayload.repository.name,
owner: eventPayload.repository.owner.login,
issue_number: eventPayload.issue.number,
comment_id: this._lastCommentId,
});
} else {
const comment = await getOctokitInstance().issues.createComment({
body,
repo: eventPayload.repository.name,
owner: eventPayload.repository.owner.login,
issue_number: eventPayload.issue.number,
});
this._lastCommentId = comment.data.id;
}
}

_createContributionRows(result: Result[0], sortedTasks: SortedTasks | undefined) {
const content: string[] = [];

if (result.task?.reward) {
content.push(buildContributionRow("Issue", "Task", result.task.multiplier, result.task.reward));
}

if (!sortedTasks) {
return content.join("");
}

function buildContributionRow(
view: string,
contribution: string,
count: number,
reward: number | Decimal | undefined
) {
const fee = generateFeeString(reward, result.feeRate);
return `
<tr>
<td>${view}</td>
<td>${contribution}</td>
<td>${count}</td>
<td>${reward || "-"}</td>
<td>${fee}</td>
</tr>`;
}

if (sortedTasks.issues.specification) {
content.push(buildContributionRow("Issue", "Specification", 1, sortedTasks.issues.specification.score?.reward));
}
if (sortedTasks.issues.comments.length) {
content.push(
buildContributionRow(
"Issue",
"Comment",
sortedTasks.issues.comments.length,
sortedTasks.issues.comments.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))
)
);
}
if (sortedTasks.reviews.length) {
content.push(
buildContributionRow(
"Review",
"Comment",
sortedTasks.reviews.length,
sortedTasks.reviews.reduce((acc, curr) => acc.add(curr.score?.reward ?? 0), new Decimal(0))
)
);
}
return content.join("");
}

_createIncentiveRows(sortedTasks: SortedTasks | undefined, feeRate: number | Decimal | undefined = undefined) {
const content: string[] = [];

if (!sortedTasks) {
return content.join("");
}

function buildIncentiveRow(commentScore: GithubCommentScore) {
// Properly escape carriage returns for HTML rendering
const formatting = stringify({
content: commentScore.score?.formatting,
regex: commentScore.score?.words,
}).replace(/[\n\r]/g, "&#13;");
// Makes sure any HTML injected in the templated is not rendered itself
const sanitizedContent = commentScore.content
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll("`", "&#96;")
.replace(/([\s\S]{64}).[\s\S]+/, "$1&hellip;");
const fee = generateFeeString(commentScore.score?.reward, feeRate);
return `
<tr>
<td>
<h6>
<a href="${commentScore.url}" target="_blank" rel="noopener">${sanitizedContent}</a>
</h6>
</td>
<td>
<details>
<summary>
${new Decimal(commentScore.score?.words?.result ?? 0).add(new Decimal(commentScore.score?.formatting?.result ?? 0))}
</summary>
<pre>${formatting}</pre>
</details>
</td>
<td>${commentScore.score?.relevance ?? "-"}</td>
<td>${commentScore.score?.reward ?? "-"}</td>
<td>${fee}</td>
</tr>`;
}

if (sortedTasks.issues.specification) {
content.push(buildIncentiveRow(sortedTasks.issues.specification));
}
for (const issueComment of sortedTasks.issues.comments) {
content.push(buildIncentiveRow(issueComment));
}
for (const reviewComment of sortedTasks.reviews) {
content.push(buildIncentiveRow(reviewComment));
}
return content.join("");
}

async _generateHtml(username: string, result: Result[0], stripComments = false) {
const sortedTasks = result.comments?.reduce<SortedTasks>(
(acc, curr) => {
if (curr.type & CommentKind.ISSUE) {
if (curr.type & CommentAssociation.SPECIFICATION) {
acc.issues.specification = curr;
} else {
acc.issues.comments.push(curr);
}
} else if (curr.type & CommentKind.PULL) {
acc.reviews.push(curr);
}
return acc;
},
{ issues: { specification: null, comments: [] }, reviews: [] }
);

const tokenSymbol = await getErc20TokenSymbol(configuration.evmNetworkId, configuration.erc20RewardToken);

return `
<details>
<summary>
<b>
<h3>
&nbsp;
<a href="${result.permitUrl}" target="_blank" rel="noopener">
[ ${result.total} ${tokenSymbol} ]
</a>
&nbsp;
</h3>
<h6>
@${username}
</h6>
</b>
</summary>
${result.feeRate !== undefined ? `<h6>⚠️ ${result.feeRate}% fee rate has been applied. Consider using the&nbsp;<a href="https://dao.ubq.fi/dollar" target="_blank" rel="noopener">Ubiquity Dollar</a>&nbsp;for no fees.</h6>` : ""}
<h6>Contributions Overview</h6>
<table>
<thead>
<tr>
<th>View</th>
<th>Contribution</th>
<th>Count</th>
<th>Reward</th>
<th>Fee</th>
</tr>
</thead>
<tbody>
${this._createContributionRows(result, sortedTasks)}
</tbody>
</table>
${
!stripComments
? `<h6>Conversation Incentives</h6>
<table>
<thead>
<tr>
<th>Comment</th>
<th>Formatting</th>
<th>Relevance</th>
<th>Reward</th>
<th>Fee</th>
</tr>
</thead>
<tbody>
${this._createIncentiveRows(sortedTasks, result.feeRate)}
</tbody>
</table>`
: ""
}
</details>
`
.replace(/(\r?\n|\r)\s*/g, "") // Remove newlines and leading spaces/tabs after them
.replace(/\s*(<\/?[^>]+>)\s*/g, "$1") // Trim spaces around HTML tags
.trim();
}
}
291 changes: 291 additions & 0 deletions index.js

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions resr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Important Details about the project:

1. We would use C11 for general language support
2. If someone is facing issues with CMAKE then should try switching their LLVM compilers especially on Mac Silicon
3. I think we should start using Qt6 for UI, 4.5 is not good
4. GDCOM has not been added to vsproj fix that
1 change: 1 addition & 0 deletions static/dist/test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
randomtest