diff --git a/.github/workflows/autolabler.yml b/.github/workflows/autolabler.yml
index 88370966..626482b0 100644
--- a/.github/workflows/autolabler.yml
+++ b/.github/workflows/autolabler.yml
@@ -1,4 +1,4 @@
-name: Auto Label Issues and PRs
+name: Auto Label & Populate Issue Fields and PRs
on:
pull_request_target:
@@ -9,37 +9,392 @@ on:
permissions:
issues: write
pull-requests: write
+ repository-projects: write
jobs:
- add-labels:
+ add-labels-and-populate:
runs-on: ubuntu-latest
steps:
- - name: Add labels to PR
+ - name: Add labels and populate PR fields
if: github.event_name == 'pull_request_target'
uses: actions/github-script@v7
with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const prNumber = context.payload.pull_request.number;
+ const prNodeId = context.payload.pull_request.node_id;
+ const prTitle = context.payload.pull_request.title.toLowerCase();
+ const prBody = (context.payload.pull_request.body || '').toLowerCase();
+
+ function detectPRType(title, body) {
+ const bugfixKeywords = ['fix', 'bug', 'hotfix', 'patch', 'resolve', 'correct'];
+ const featureKeywords = ['feat', 'add', 'new', 'implement', 'feature'];
+ const docKeywords = ['doc', 'docs', 'documentation', 'readme'];
+ const refactorKeywords = ['refactor', 'cleanup', 'restructure', 'reorganize'];
+ const styleKeywords = ['style', 'css', 'ui', 'design', 'formatting'];
+ const testKeywords = ['test', 'tests', 'testing', 'spec'];
+ const choreKeywords = ['chore', 'deps', 'dependency', 'update', 'upgrade'];
+
+ const text = `${title} ${body}`;
+
+ if (bugfixKeywords.some(keyword => text.includes(keyword))) return 'bugfix';
+ if (featureKeywords.some(keyword => text.includes(keyword))) return 'feature';
+ if (docKeywords.some(keyword => text.includes(keyword))) return 'documentation';
+ if (refactorKeywords.some(keyword => text.includes(keyword))) return 'refactor';
+ if (styleKeywords.some(keyword => text.includes(keyword))) return 'ui/ux';
+ if (testKeywords.some(keyword => text.includes(keyword))) return 'testing';
+ if (choreKeywords.some(keyword => text.includes(keyword))) return 'chore';
+
+ return 'enhancement';
+ }
+
+ function detectPRSize(additions, deletions) {
+ const totalChanges = additions + deletions;
+
+ if (totalChanges < 10) return 'size/XS';
+ if (totalChanges < 30) return 'size/S';
+ if (totalChanges < 100) return 'size/M';
+ if (totalChanges < 500) return 'size/L';
+ return 'size/XL';
+ }
+
+ const prType = detectPRType(prTitle, prBody);
+ const prSize = detectPRSize(
+ context.payload.pull_request.additions || 0,
+ context.payload.pull_request.deletions || 0
+ );
+
+ let labelsToAdd = ["recode", "hacktoberfest-accepted"];
+
+ labelsToAdd.push(prType);
+ labelsToAdd.push(prSize);
+
+ if (prType === 'documentation' || prType === 'chore') {
+ labelsToAdd.push('level 1');
+ } else if (prType === 'feature' || prType === 'ui/ux') {
+ labelsToAdd.push('level 2');
+ } else if (prType === 'refactor' || prSize === 'size/XL') {
+ labelsToAdd.push('level 3');
+ } else {
+ labelsToAdd.push('level 1');
+ }
+
+ const files = await github.rest.pulls.listFiles({
+ ...context.repo,
+ pull_number: prNumber
+ });
+
+ const changedFiles = files.data.map(file => file.filename);
+
+ if (changedFiles.some(file => file.includes('.md'))) {
+ labelsToAdd.push('documentation');
+ }
+ if (changedFiles.some(file => file.includes('.css') || file.includes('.scss'))) {
+ labelsToAdd.push('ui/ux');
+ }
+ if (changedFiles.some(file => file.includes('test') || file.includes('.test.'))) {
+ labelsToAdd.push('testing');
+ }
+ if (changedFiles.some(file => file.includes('package.json'))) {
+ labelsToAdd.push('dependencies');
+ }
+ if (changedFiles.some(file => file.includes('.github'))) {
+ labelsToAdd.push('workflow');
+ }
+
+
+ labelsToAdd = [...new Set(labelsToAdd)];
await github.rest.issues.addLabels({
...context.repo,
issue_number: prNumber,
- labels: ["recode", "level 1", "hacktoberfest-accepted"]
+ labels: labelsToAdd
});
+ console.log(`Added labels to PR #${prNumber}: ${labelsToAdd.join(', ')}`);
+
+ try {
+ const projectsQuery = `
+ query($owner: String!, $repo: String!) {
+ repository(owner: $owner, name: $repo) {
+ projectsV2(first: 10) {
+ nodes {
+ id
+ title
+ }
+ }
+ }
+ }
+ `;
+
+ const projectsResult = await github.graphql(projectsQuery, {
+ owner: context.repo.owner,
+ repo: context.repo.repo
+ });
+
+ const project = projectsResult.repository.projectsV2.nodes.find(
+ p => p.title.includes("recode-web") || p.title.includes("recode")
+ );
- console.log(`Added labels [recode, level 1,hacktoberfest-accepted] to PR #${prNumber}`);
+ if (project) {
+ const addToProjectMutation = `
+ mutation($projectId: ID!, $contentId: ID!) {
+ addProjectV2ItemById(input: {
+ projectId: $projectId
+ contentId: $contentId
+ }) {
+ item {
+ id
+ }
+ }
+ }
+ `;
- - name: Add labels to Issue
+ const addResult = await github.graphql(addToProjectMutation, {
+ projectId: project.id,
+ contentId: prNodeId
+ });
+
+ console.log(`Added PR #${prNumber} to project: ${project.title}`);
+
+ const itemId = addResult.addProjectV2ItemById.item.id;
+
+ } else {
+ console.log("No matching project found");
+ }
+ } catch (error) {
+ console.error("Error adding to project:", error);
+ }
+
+ - name: Add labels and populate Issue fields
if: github.event_name == 'issues'
uses: actions/github-script@v7
with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const issueNumber = context.payload.issue.number;
+ const issueNodeId = context.payload.issue.node_id;
+ const issueTitle = context.payload.issue.title.toLowerCase();
+ const issueBody = (context.payload.issue.body || '').toLowerCase();
+
+ function detectIssueType(title, body) {
+ const bugKeywords = ['bug', 'error', 'fix', 'broken', 'issue', 'problem', 'crash', 'fail'];
+ const featureKeywords = ['feature', 'add', 'new', 'implement', 'enhancement', 'improve'];
+ const docKeywords = ['doc', 'documentation', 'readme', 'guide', 'tutorial', 'explain'];
+ const uiKeywords = ['ui', 'ux', 'design', 'style', 'css', 'layout', 'responsive'];
+ const performanceKeywords = ['performance', 'speed', 'optimize', 'slow', 'fast', 'memory'];
+ const testKeywords = ['test', 'testing', 'unit test', 'integration', 'e2e'];
+ const securityKeywords = ['security', 'vulnerability', 'auth', 'permission', 'secure'];
+
+ const text = `${title} ${body}`;
+
+ if (bugKeywords.some(keyword => text.includes(keyword))) return 'bug';
+ if (featureKeywords.some(keyword => text.includes(keyword))) return 'feature';
+ if (docKeywords.some(keyword => text.includes(keyword))) return 'documentation';
+ if (uiKeywords.some(keyword => text.includes(keyword))) return 'ui/ux';
+ if (performanceKeywords.some(keyword => text.includes(keyword))) return 'performance';
+ if (testKeywords.some(keyword => text.includes(keyword))) return 'testing';
+ if (securityKeywords.some(keyword => text.includes(keyword))) return 'security';
+
+ return 'enhancement';
+ }
+
+ function detectPriority(title, body) {
+ const text = `${title} ${body}`;
+ const criticalKeywords = ['critical', 'urgent', 'severe', 'crash', 'security'];
+ const highKeywords = ['high', 'important', 'major', 'blocking'];
+ const lowKeywords = ['minor', 'low', 'nice to have', 'cosmetic'];
+
+ if (criticalKeywords.some(keyword => text.includes(keyword))) return 'critical';
+ if (highKeywords.some(keyword => text.includes(keyword))) return 'high priority';
+ if (lowKeywords.some(keyword => text.includes(keyword))) return 'low priority';
+
+ return 'medium priority';
+ }
+
+ function detectDifficulty(title, body) {
+ const text = `${title} ${body}`;
+ const beginnerKeywords = ['typo', 'readme', 'documentation', 'simple', 'easy'];
+ const intermediateKeywords = ['feature', 'enhancement', 'improvement'];
+ const advancedKeywords = ['refactor', 'architecture', 'performance', 'security', 'complex'];
+
+ if (beginnerKeywords.some(keyword => text.includes(keyword))) return 'good first issue';
+ if (advancedKeywords.some(keyword => text.includes(keyword))) return 'level 3';
+ if (intermediateKeywords.some(keyword => text.includes(keyword))) return 'level 2';
+
+ return 'level 1';
+ }
+
+ const issueType = detectIssueType(issueTitle, issueBody);
+ const priority = detectPriority(issueTitle, issueBody);
+ const difficulty = detectDifficulty(issueTitle, issueBody);
+
+ let labelsToAdd = ["recode", "hacktoberfest-accepted"];
+
+ labelsToAdd.push(issueType);
+ labelsToAdd.push(priority);
+ labelsToAdd.push(difficulty);
+
+ if (issueTitle.includes('mobile') || issueBody.includes('mobile')) {
+ labelsToAdd.push('mobile');
+ }
+ if (issueTitle.includes('desktop') || issueBody.includes('desktop')) {
+ labelsToAdd.push('desktop');
+ }
+ if (issueTitle.includes('api') || issueBody.includes('api')) {
+ labelsToAdd.push('api');
+ }
+ if (issueTitle.includes('database') || issueBody.includes('database')) {
+ labelsToAdd.push('database');
+ }
await github.rest.issues.addLabels({
...context.repo,
issue_number: issueNumber,
- labels: ["recode", "level 1", "hacktoberfest-accepted"]
+ labels: labelsToAdd
});
+ console.log(`Added labels to Issue #${issueNumber}: ${labelsToAdd.join(', ')}`);
+
+ try {
+ const milestones = await github.rest.issues.listMilestones({
+ ...context.repo,
+ state: 'open'
+ });
+
+ const targetMilestone = milestones.data.find(
+ m => m.title.includes("launch 3.0") || m.title.includes("recode:launch")
+ );
+
+ if (targetMilestone) {
+ await github.rest.issues.update({
+ ...context.repo,
+ issue_number: issueNumber,
+ milestone: targetMilestone.number
+ });
+ console.log(`Set milestone to: ${targetMilestone.title}`);
+ }
+ } catch (error) {
+ console.error("Error setting milestone:", error);
+ }
+
+ try {
+ const projectsQuery = `
+ query($owner: String!, $repo: String!) {
+ repository(owner: $owner, name: $repo) {
+ projectsV2(first: 10) {
+ nodes {
+ id
+ title
+ fields(first: 20) {
+ nodes {
+ ... on ProjectV2Field {
+ id
+ name
+ }
+ ... on ProjectV2SingleSelectField {
+ id
+ name
+ options {
+ id
+ name
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ `;
+
+ const projectsResult = await github.graphql(projectsQuery, {
+ owner: context.repo.owner,
+ repo: context.repo.repo
+ });
+
+ const project = projectsResult.repository.projectsV2.nodes.find(
+ p => p.title.includes("recode-web") || p.title.includes("recode")
+ );
+
+ if (project) {
+ const addToProjectMutation = `
+ mutation($projectId: ID!, $contentId: ID!) {
+ addProjectV2ItemById(input: {
+ projectId: $projectId
+ contentId: $contentId
+ }) {
+ item {
+ id
+ }
+ }
+ }
+ `;
+
+ const addResult = await github.graphql(addToProjectMutation, {
+ projectId: project.id,
+ contentId: issueNodeId
+ });
+
+ const itemId = addResult.addProjectV2ItemById.item.id;
+ console.log(`Added Issue #${issueNumber} to project: ${project.title}`);
+
+ const statusField = project.fields.nodes.find(f => f.name === "Status");
+ if (statusField && statusField.options) {
+ const todoOption = statusField.options.find(o => o.name === "Todo");
+
+ if (todoOption) {
+ const updateFieldMutation = `
+ mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $value: ProjectV2FieldValue!) {
+ updateProjectV2ItemFieldValue(input: {
+ projectId: $projectId
+ itemId: $itemId
+ fieldId: $fieldId
+ value: $value
+ }) {
+ projectV2Item {
+ id
+ }
+ }
+ }
+ `;
+
+ await github.graphql(updateFieldMutation, {
+ projectId: project.id,
+ itemId: itemId,
+ fieldId: statusField.id,
+ value: { singleSelectOptionId: todoOption.id }
+ });
+
+ console.log(`Set Status to "Todo"`);
+ }
+ }
- console.log(`Added labels [recode, level 1, hacktoberfest-accepted] to Issue #${issueNumber}`);
+ const typeField = project.fields.nodes.find(f => f.name === "Type");
+ if (typeField && typeField.options) {
+ const typeOption = typeField.options.find(o => labelsToAdd.includes(o.name.toLowerCase()));
+ if (typeOption) {
+ await github.graphql(`
+ mutation($projectId: ID!, $itemId: ID!, $fieldId: ID!, $value: ProjectV2FieldValue!) {
+ updateProjectV2ItemFieldValue(input: {
+ projectId: $projectId
+ itemId: $itemId
+ fieldId: $fieldId
+ value: $value
+ }) {
+ projectV2Item { id }
+ }
+ }
+ `, {
+ projectId: project.id,
+ itemId: itemId,
+ fieldId: typeField.id,
+ value: { singleSelectOptionId: typeOption.id }
+ });
+ console.log(`Set Type to "${typeOption.name}"`);
+ }
+ }
+ } else {
+ console.log("No matching project found");
+ }
+ } catch (error) {
+ console.error("Error adding to project:", error);
+ }
diff --git a/.github/workflows/issue-auto-reply.yml b/.github/workflows/issue-auto-reply.yml
new file mode 100644
index 00000000..5903e5a2
--- /dev/null
+++ b/.github/workflows/issue-auto-reply.yml
@@ -0,0 +1,28 @@
+name: Auto Reply to New Issues
+
+on:
+ issues:
+ types: [opened]
+
+permissions:
+ issues: write
+
+jobs:
+ comment:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Post Welcome Comment
+ uses: actions/github-script@v7
+ with:
+ script: |
+ const issue_number = context.issue.number;
+ const body = `
+ Thank you for raising this issue! Our team will review it soon.
+
+ For more queries, discussions, or approval of requests, please join our Discord server for further discussion: [Discord Link](https://discord.com/invite/Yn9g6KuWyA)
+ `;
+ await github.rest.issues.createComment({
+ ...context.repo,
+ issue_number,
+ body
+ });
diff --git a/src/pages/dashboard/giveaway/index.tsx b/src/pages/dashboard/giveaway/index.tsx
index 28b47d0c..f7232cdb 100644
--- a/src/pages/dashboard/giveaway/index.tsx
+++ b/src/pages/dashboard/giveaway/index.tsx
@@ -750,13 +750,13 @@ const GiveawayPage: React.FC = () => {
valueText={leaderboard.length.toString()}
description="Total participants"
/>
-
+ /> */}